Можно ли подобрать прокатные характеристики фильма (жанр, время, деятельность, возрастной рейтинг и т.п.), а также режиссера, сценариста, актеров, оператора (всю творческую группу) так, чтобы существенно повысить его результаты в прокате? Желательно заранее, до начала съемок.
Что происходит «под капотом» у Голливуда?
Мультивселенные. Комиксы. Герои фильмов: клишированные и как будто бы рождённые на одной фабрике клонов, хоть и обладающие при этом разными уникальными способностями. Почему так происходит?
Все дело в том, что Голливуд из «фабрики грез» превратился в масштабную генеративную нейросеть по производству контента. И эта сеть находится в состоянии «переобученности», поскольку начала учиться на своих же продуктах.
Блокбастеры, франшизы, ребуты и ребилты, перезагрузки и перезапуски: массовый зритель требует красивой картинки и «эпичности», а все сюжеты уже разобраны, герои многократно просклонялись, их воскрешали и умертвляли множество раз. В попытке выжать из «пестрой коровы» еще что-нибудь и была «изобретена» та самая мультивселенная – с бесчисленными интеграциями эпох обучения нейросетевого сценарного сюжета, «россомахизацией дедпула» или «дедпулизацией россомахи».
Прокатные характеристики и творческая группа подбираются с помощью алгоритмов машинного и глубокого обучения, сценарий прогоняется через языковые модели и сравнивается с мириадами подобных, реализованных нереализованных вариантов. Разумеется, ни один голливудский режиссёр или продюсер вам в этом не признаются (кино же – это творчество!). А те деятели старой закалки (привет... Стивен Спилберг), которые по умолчанию этим не пользуются - давно вышли в тираж и забыты киностудиями.
Подобрать данные фильма, включая его жанр, длительность, выбрать актеров и других участников творческой группы – вполне возможно. И это ненамного сложнее чем подобрать гиперпараметры 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
В спойлере - таблички производительности моделей на выровненных классах с помощью SMOTE и ADASYN, а также на базовом датасете, в котором не производилась балансировка классов
Скрытый текст
Скрытый текст
Как мы видим, балансировка классов не очень-то улучшает производительность моделей. Разумеется можно попробовать поэкспериментировать со Stacking
и Voting
и их вложениями-алгоритмами, однако остановимся на на двух ансамблях, показавших неплохие результаты по метрике recall
(в большей мере нас интересуют именно успешные проекты) и общей точностиHistGradientBoostingClassifier
и AdaBoostClassifier
.
Подбор гиперпараметров моделей
Далее мы попробуем подобрать гиперпараметры этих моделей, чтобы повысить их качество. При этом мы пойдем двумя путями: AdaBoost
мы будем совершенствовать под "отклик" и идентификацию успешных проектов, а HistGradientBoosting
у нас будет отвечать за общую accuracy
.
Скрытый текст
# Определяем модель
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_)
Скрытый текст
# Определяем сетку поиска гиперпараметров
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
Итоговый эксперимент
Используем валидационый датасет из 180 кинокартин для оценки метрик двух полученных моделей. Напомним, что модели к нему не имели доступа, ни на этапе тренинга, ни на этапе тестирования, какая-либо утечка данных исключена. Кинокартины новые, указаны в хронологическом порядке (тестовые и тренинговые данные - перемешаны). Единственное что у них есть общее - это актеры, режиссеры, сценаристы, продюсеры, операторы, монтажеры, художники-постановщики - категориальные переменные, закодированные одинаковым образом, с уникальным номером для каждой персоналии.
Смотрим на результат
Скрытый текст
|
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% годовых. И все потому что инвестиции шли бы в конкретных людей. В России есть примерно два десятка режиссеров, которые при правильном сочетании "гиперпараметров" картины, работая в своих жанрах получают стабильный результат. Таким же образом определяются "честные" продюсеры, которые зарабатывают не с бюджета, а с результатов проката, такие же "кассовые" актеры, операторы, сценаристы и другие члены творческой группы.
Небольшое замечание: пока мы проиллюстрировали только общий подход. Для по-настоящему точного прогноза окупаемости кинофильма, отбора прибыльных проектов из всего вороха представленных необходимо несколько ансамблевых моделей с разными настройками гиперпараметров — под «точность», «правильность», «чувствительность» или «отклик». такая большая условная «метамодель».
В одном случае расчеты должны идти через категориальные переменные, в другом — через исторические данные проката (у каждого жанра есть своя история свои коэффициенты, у каждого режиссера, сценариста, актера — есть свои средние коэффициенты по сборам, просмотрам, сборам на экран и так далее).
Тем не менее, основной тезис доказан, идея возможности отбора кинокартин и подбора их гиперпараметров проиллюстрирована на простейшем примере. Всем хороших кинофильмов в ленту!