Гиперпараметрическая оптимизация прокатных характеристик фильма и подбор состава творческой группы
Можно ли подобрать прокатные характеристики фильма (жанр, время, деятельность, возрастной рейтинг и т.п.), а также режиссера, сценариста, актеров, оператора (всю творческую группу) так, чтобы существенно повысить его результаты в прокате? Желательно заранее, до начала съемок.
Что происходит «под капотом» у Голливуда?
Мультивселенные. Комиксы. Герои фильмов: клишированные и как будто бы рождённые на одной фабрике клонов, хоть и обладающие при этом разными уникальными способностями. Почему так происходит?
Все дело в том, что Голливуд из «фабрики грез» превратился в масштабную генеративную нейросеть по производству контента. И эта сеть находится в состоянии «переобученности», поскольку начала учиться на своих же продуктах.
Блокбастеры, франшизы, ребуты и ребилты, перезагрузки и перезапуски: массовый зритель требует красивой картинки и «эпичности», а все сюжеты уже разобраны, герои многократно просклонялись, их воскрешали и умертвляли множество раз. В попытке выжать из «пестрой коровы» еще что-нибудь и была «изобретена» та самая мультивселенная — с бесчисленными интеграциями эпох обучения нейросетевого сценарного сюжета, «россомахизацией дедпула» или «дедпулизацией россомахи».
Прокатные характеристики и творческая группа подбираются с помощью алгоритмов машинного и глубокого обучения, сценарий прогоняется через языковые модели и сравнивается с мириадами подобных, реализованных нереализованных вариантов. Разумеется, ни один голливудский режиссёр или продюсер вам в этом не признаются (кино же — это творчество!). А те деятели старой закалки (привет… Стивен Спилберг), которые по умолчанию этим не пользуются — давно вышли в тираж и забыты киностудиями.
Подобрать данные фильма, включая его жанр, длительность, выбрать актеров и других участников творческой группы — вполне возможно. И это ненамного сложнее чем подобрать гиперпараметры ML-модели с помощью GridSearchCV
, RandomizedSearchCV
или Optuna
.
Российский кинематограф, уже не ламповый, но все еще не настолько нейросетевой в погоне за «голливудщиной» тоже рано или поздно примется за оптимизацию прокатных характеристик и состава творческих групп и в этой связи очень бы хотелось, чтобы мы все-таки зафиксировали для него некий ModelCheckpoint
с наилучшим состоянием по производительности и метрикам, до тех пор, пока он не начнет «переобучаться» вслед за голливудской метамоделью-образцом
Разумеется, опытный режиссер с большим числом успешных проектов по сценарию маститого сценариста-ремесленника и под руководством продюсера, заинтересованного в заработке с проекта с приглашением известных и «кассовых» актеров скорее всего снимет хорошее и окупающееся в прокате кино. Кто же даст дорогу молоды и начинающим? кто будет «штрафовать модель отбора», чтобы не допустить ее переобучения? Хотя, может быть, мы зря тревожимся и в России все это не сработает?
Сработает. И вот почему…
Гипотеза применимости гиперпараметрической оптимизации проекта киноконтента
Итак, не раскрывая всех карт, докажем, что «правильный» подбор параметров фильма и состава группы является определяющим фактором успеха. Задолго до выхода в прокат и даже до начала съемок. И сделаем это на материалах российских кинофильмов.
Соответственно, если мы создадим работающую модель для прогнозирования кассового успеха на первом датасете и успешно ее апробируем на втором (у нас условия «проверки реальностью» более жесткие, поэтому незначительное падение метрик качества допустимо) — наша гипотеза будет доказана. Вдобавок мы со временем сможем посмотреть, какие именно факторы и как влияют на результаты проекта в прокате.
В реальной ситуации, когда у нас на руках очень интересный проект, который потенциально не окупается в прокате — мы можем так подобрать его «гиперпараметры»: жанр, длительность, возрастные ограничения, ведущих актеров и даже режиссера со сценаристами (или отправить в отставку продюсера и заменить его новым) — что он если не сорвет всю кассу, то хотя бы соберет два своих бюджета.
И такая «математическая эквилибристика» вполне себе оправдана — даже очень хороший режиссер, набивший руку в «хоррорах» (привет, «Пираты галактики Барракуда»), вряд ли проявит себя в мелодраме и мюзикле. То же самое справедливо для актеров, операторов, сценаристов и ведущих актеров (у последних так вообще есть свое амплуа, в пределах которого они могут эффективно работать).
Источник данных
Для проверки идеи мы воспользуемся датасетом из данных по 1680 российским картинам. (все датасеты и коды по прогнозированию российского кинопроката публикуются в репозитории проекта, однако данный датасет будет опубликован к выходу «тяжелой» научной статьи по объективным редакционным причинам).
Предикторами у нас будут выступать следующие столбцы данных:
Структура датасета
"week", "month", "Screens"
— неделя, месяц выхода в прокат и соответственно количество экранов в прокате.
"budget", "age_R", "time"
— бюджет проекта, возрастной рейтинг, длительность.
'main_genre', 'genre_2', 'genre_3', 'genre_4', 'genre_5'
— до 5 определений жанра проекта.
'dir1', 'dir2', 'dir3', 'dir4'
— персоналии режиссеров проекта (из может быть несколько).
'wr1', 'wr2', 'wr3', 'wr4'
— персоналии сценаристов проекта.
'prod1', 'prod2', 'prod3', 'prod4'
— персоналии продюсеров проекта.
'op1', 'op2', 'op3', 'op4'
— операторы проекта.
'dr1', 'dr2', 'dr3', 'dr4', 'dr5', 'dr6', 'dr7'
— художники-постановщики.
'ed1', 'ed2', 'ed3', 'ed4'
— режиссеры монтажа, монтажеры.
'komp1', 'komp2', 'komp3', 'komp4'
— композиторы, предоставившие свою музыку или написавшие аранжировку для кинопроизведения.
'act1', 'act2', 'act3', 'act4', 'act5', 'act6', 'act7', 'act8', 'act9', 'act10'
— до 10 ведущих актеров в проекте.
Прогнозируемой величиной у нас будет выступать столбец "rezult2"
— двухклассовая разметка, в которой 0 — фильм не окупился в прокате, не собрал два своих бюджета и 1 — фильм окупился в прокате.
Предварительная обработка данных
Самое первое, что мы сделаем — используем LabelEncoder
из sklearn.preprocessing
, чтобы закодировать категориальные переменные (названия жанров и имя с фамилией участников творческой группы в числовые значения. Затем с помощью df.fillna
заменим шестью девятками все пропуски и NaN
в датасете.
Далее мы разделим датасет на два, первый — для обучения и тестирования моделей, второй для валидации. И здесь мы осознанно усложним задачу доказательства гипотезы : модель, естественно, не видит второй датафрейм ни во время тренировки, ни во время тестирования.
При этом данные первого датасета для обучения и тестирования перемешаны в псевдослучайном порядке без учета хронологии выхода картин, а вот второй датасет — это 180 относительно свежих кинокартин, которые вышли в прокат после ухода иностранных дистрибьютеров с российского рынка. И этот факт должен, по идее, существенно сказаться на условиях окупаемости российских кинокартин и «смешать все карты». Если модель с ним справится — наша гипотеза доказана и «гиперпараметрический подбор» характеристик и состава творческой группы возможен.
Следующая проблема, которую нам необходимо решить — несбалансированность классов. Окупившихся в прокате фильмов примерно в 10 раз меньше, чем провалившихся. Здесь мы попробуем три методики выравнивания:
Увеличение выборки с использованием библиотеки imbalanced-learn
, а именно: RandomOverSampler.
Синтетическое генерирование данных — SMOTE
.
ADASYN
, работающий аналогично SMOTE, но с добавлением адаптивности. Он уделяет больше внимания классам, которые более затруднительны для классификации, генерируя больше синтетических примеров для них.
Во всех случаях мы будем сэмплировать только x_train, y_train
x_test и y_test
— остаются без изменений, с текущей пропорцией классов.
Погружение в данные
На первом этапе мы постараемся использовать основные ансамблевые модели машинного обучения в их базовой конфигурации. Почему именно ансамбли? — вот в прошлой публикации мы выяснили, что именно ансамблевые модели дают хороший результат. Поэтому не размениваемся на базовые алгоритмы.
Код ансамблевых моделей
# Создаем модели
models = {
'AdaBoost': AdaBoostClassifier(random_state = 42),
'Bagging': BaggingClassifier(random_state = 42),
'ExtraTrees': ExtraTreesClassifier(random_state = 42),
'GradientBoosting': GradientBoostingClassifier(random_state = 42),
'RandomForest': RandomForestClassifier(random_state = 42),
'Stacking': StackingClassifier(estimators=[('lr', LogisticRegression(random_state = 42)), ('rf', RandomForestClassifier(random_state = 42))]),
'Voting': VotingClassifier(estimators=[('lr', LogisticRegression(random_state = 42)), ('rf', RandomForestClassifier(random_state = 42))]),
'HistGradientBoosting': HistGradientBoostingClassifier(random_state = 42),
'CatBoost': CatBoostClassifier(verbose=0,random_state = 42)
}
# Обучаем модели на базовых настройках и делаем предсказания
predictions = {}
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
predictions[name] = y_pred
# Создаем словарь для метрик
metrics = {
'Accuracy': accuracy_score,
'Precision': precision_score,
'Recall': recall_score,
'F1_score': f1_score,
}
# Вычисляем метрики для каждой модели
results = {}
for name, y_pred in predictions.items():
results[name] = [metric(y_test, y_pred) for metric in metrics.values()]
# Выводим сравнительную таблицу с метриками
print("Метрики для различных моделей:")
print("{:<20} {:<10} {:<10} {:<10} {:<10} ".format("Модель", "Accuracy", "Precision", "Recall", "F1_score"))
for name, metrics in results.items():
print("{:<20} {:<10.4f} {:<10.4f} {:<10.4f} {:<10.4f} ".format(name, *metrics))
Мы экспериментировали с разными механизмами выравнивания баланса классов. Ниже приведена табличка с данными для RandomOverSampler
Метрики моделей на датасете с выровненными классами с помощью RandomOverSampler
В спойлере — таблички производительности моделей на выровненных классах с помощью SMOTE и ADASYN, а также на базовом датасете, в котором не производилась балансировка классов
Балансировка классов SMOTE и ADASYN
Метрики моделей на датасете с выровненными классами с помощью ADASYN
Метрики моделей на датасете с выровненными классами с помощью SMOTE
Без балансировки
Метрики моделей на несбалансированном датасете
Как мы видим, балансировка классов не очень-то улучшает производительность моделей. Разумеется можно попробовать поэкспериментировать со Stacking
и Voting
и их вложениями-алгоритмами, однако остановимся на на двух ансамблях, показавших неплохие результаты по метрике recall
(в большей мере нас интересуют именно успешные проекты) и общей точностиHistGradientBoostingClassifier
и AdaBoostClassifier
.
Подбор гиперпараметров моделей
Далее мы попробуем подобрать гиперпараметры этих моделей, чтобы повысить их качество. При этом мы пойдем двумя путями: AdaBoost
мы будем совершенствовать под «отклик» и идентификацию успешных проектов, а HistGradientBoosting
у нас будет отвечать за общую accuracy
.
Подбор гиперпараметров HistGradientBoosting
# Определяем модель
model = HistGradientBoostingClassifier(random_state=42)
# Задаем параметры для подбора
param_distributions = {
'max_iter': np.arange(500, 1301, 50), # Количество итераций
'max_depth': np.arange(4, 10), # Глубина деревьев
'learning_rate': np.linspace(0.01, 0.3, 10), # Темп обучения
'l2_regularization': np.logspace(-1, 1, 5) # L2-регуляризация
}
# Настраиваем RandomizedSearchCV
random_search = RandomizedSearchCV(
model,
param_distributions,
n_iter=50, # Число проб
scoring='accuracy', # Оценка качества
cv=5, # Кросс-валидация
random_state=42,
n_jobs=-1 # Используем все доступные ядра
)
# Запускаем поиск
random_search.fit(X, y)
# Получаем лучшие параметры и результат
print("Лучшие параметры:", random_search.best_params_)
print("Лучший результат:", random_search.best_score_)
Подбор гиперпараметров AdaBoost
# Определяем сетку поиска гиперпараметров
param_distributions = {
'n_estimators': [100, 120, 130, 140, 150, 160],
'learning_rate': [1.0, 0.1,0.01],
'algorithm': ['SAMME']
}
# Создаем экземпляр модели с пустыми параметрами
model = AdaBoostClassifier(random_state=42)
# Создаем экземпляр RandomizedSearchCV с моделью и сеткой поиска
random_search = RandomizedSearchCV(model, param_distributions, n_iter=10, cv=3, scoring='recall', verbose=0)
random_search.fit(X_train, y_train)
# Выводим результаты
print("Лучшие параметры:", random_search.best_params_)
В результате плясок с бубнами мы получили две модели с подобранными гиперпараметрами:
model = AdaBoostClassifier(n_estimators = 120,
learning_rate =0.01, algorithm= 'SAMME', random_state=42)
model_X = HistGradientBoostingClassifier(max_iter = 1200, max_depth = 6,
learning_rate = 0.2677777777777778,
l2_regularization = 1.0, random_state = 42)
Оценим две модели по их производительности, еще раз отметим, что у каждой из них — своя епархия. И в отношении AdaBoost мы идем на осознанную жертву, «выкручивая» алгоритм именно под выявление положительного класса и жертвуя всеми остальными.
Таблица 1. Метрики AdaBoostClassifier.
precision | recall | f1-score | support | |
0 | 0.993197 | 0.356968 | 0.525180 | 409 |
1 | 0.132013 | 0.975610 | 0.232558 | 41 |
accuracy | 0.413333 | 0.413333 | 0.413333 | 0.413333 |
macro avg | 0.562605 | 0.666289 | 0.378869 | 450 |
weighted avg | 0.914734 | 0.413333 | 0.498519 | 450 |
Таблица 2. Метрики HistGradientBoostingClassifier
precision | recall | f1-score | support | |
0 | 0.950000 | 0.975550 | 0.962606 | 409 |
1 | 0.666667 | 0.487805 | 0.563380 | 41 |
accuracy | 0.931111 | 0.931111 | 0.931111 | 0.931111 |
macro avg | 0.808333 | 0.731678 | 0.762993 | 450 |
weighted avg | 0.924185 | 0.931111 | 0.926232 | 450 |
Казалось бы, HistGradientBoostingClassifier
по всем параметрам делает AdaBoostClassifier
за исключением одного: нам нужны только успешные в прокате фильмы! Причем определенные достаточно точно.
Как мы видим на картинках ниже, AdaBoost
очень точно выцепила 40 успешных кинокартин. В одной, правда, ошиблась. В отличие от HistGradientBoosting
, у которого чувствительность все же ниже, 20 к 21
результаты классификации AdaBoost
результаты классификации HistGradientBoosting
Результаты работы алгоритма AdaBoost
результаты работы алгоритма HistGradientBoosting
Итоговый эксперимент
Используем валидационый датасет из 180 кинокартин для оценки метрик двух полученных моделей. Напомним, что модели к нему не имели доступа, ни на этапе тренинга, ни на этапе тестирования, какая-либо утечка данных исключена. Кинокартины новые, указаны в хронологическом порядке (тестовые и тренинговые данные — перемешаны). Единственное что у них есть общее — это актеры, режиссеры, сценаристы, продюсеры, операторы, монтажеры, художники-постановщики — категориальные переменные, закодированные одинаковым образом, с уникальным номером для каждой персоналии.
Смотрим на результат
Метрики AdaBoost
precision | recall | f1-score | support | |
0 | 1.000000 | 0.418182 | 0.589744 | 165 |
1 | 0.135135 | 1.000000 | 0.238095 | 15 |
accuracy | 0.466667 | 0.466667 | 0.466667 | 0.466667 |
macro avg | 0.567568 | 0.709091 | 0.413919 | 180 |
weighted avg | 0.927928 | 0.466667 | 0.560440 | 180 |
И здесь нас поджидает сюрприз: модель верно определила все 15 успешных фильмов в прокате (с другим классом — полный «расколбас», как и на тренинговых данных). Разумеется все другие метрики у нас не на высоте. За общую точность у нас отвечает другой алгоритм HistGradientBoosting — вот его его метрики:
precision | recall | f1-score | support | |
0 | 0.935294 | 0.963636 | 0.949254 | 165 |
1 | 0.400000 | 0.266667 | 0.320000 | 15 |
accuracy | 0.905556 | 0.905556 | 0.905556 | 0.905556 |
macro avg | 0.667647 | 0.615152 | 0.634627 | 180 |
weighted avg | 0.890686 | 0.905556 | 0.896816 | 180 |
Выводы
Итак, на валидационной выборке мы получили практически такие же как и на тестовой выборке метрики качества. На практике это означает, что если бы мы вложились в 15 предварительно отобранных из 180 кинофильмов, то получили бы нехилую прибыль в X3-X4 от бюджета проектов или 200–300% годовых. И все потому что инвестиции шли бы в конкретных людей. В России есть примерно два десятка режиссеров, которые при правильном сочетании «гиперпараметров» картины, работая в своих жанрах получают стабильный результат. Таким же образом определяются «честные» продюсеры, которые зарабатывают не с бюджета, а с результатов проката, такие же «кассовые» актеры, операторы, сценаристы и другие члены творческой группы.
Небольшое замечание: пока мы проиллюстрировали только общий подход. Для по-настоящему точного прогноза окупаемости кинофильма, отбора прибыльных проектов из всего вороха представленных необходимо несколько ансамблевых моделей с разными настройками гиперпараметров — под «точность», «правильность», «чувствительность» или «отклик». такая большая условная «метамодель».
В одном случае расчеты должны идти через категориальные переменные, в другом — через исторические данные проката (у каждого жанра есть своя история свои коэффициенты, у каждого режиссера, сценариста, актера — есть свои средние коэффициенты по сборам, просмотрам, сборам на экран и так далее).
Тем не менее, основной тезис доказан, идея возможности отбора кинокартин и подбора их гиперпараметров проиллюстрирована на простейшем примере. Всем хороших кинофильмов в ленту!