Это продолжение моей предыдущей статьи, в которой я нашел переменные, наиболее коррелирующие с кассовым успехом фильма. Недавно я протестировал множество моделей машинного обучения, чтобы узнать, какая модель лучше всего подходит для прогнозирования нашей цели (кассовых сборов фильма). Я подробно расскажу, какие модели я использовал, и связанные с ними результаты ниже.

И если вы хотите увидеть эту развернутую модель в действии и спрогнозировать кассовые сборы своего фильма, нажмите здесь.

Очистка данных

Но сначала мне нужно было очистить и систематизировать свои данные. Я взял данные из открытой базы данных IMDb, которую они обновляют раз в день. Набор данных состоял из миллионов строк, поэтому мне пришлось использовать Dask, чтобы помочь моему бедному MacBook Air обработать данные.

Я сузил набор данных до фильмов, выпущенных в США в период с 1980 по 2020 год, а затем объединил его с другим набором данных из TMDB, который содержал сведения о бюджете и кассовых сборах, которых не было в наборе данных IMDb.

Как и в большинстве проектов машинного обучения, получение, очистка и подготовка данных заняли около 80% всего времени проекта.

Разработка функций

Основываясь на моей предыдущей статье, я знал, что существует корреляция между кассовыми сборами фильма и предыдущим кассовым успехом актеров и режиссеров, связанных с указанным фильмом. Вот простое уравнение, которое я использовал для количественной оценки этого числа:

Я просто подсчитал средние кассовые сборы, связанные с каждым актером и режиссером в их четырех лучших фильмах, а затем сложил эти числа вместе (средний валовой сбор для звездного актера и средний валовой сбор для режиссера) каждого фильма, чтобы получить «Общий валовой сбор». Банковская привлекательность».

Утечка данных

«Общая валовая прибыльность» — единственная функция, которая может привести к утечке данных, поскольку она забирает кассовые сборы предыдущих лучших фильмов актера. Однако это может включать целевые данные в нашу функцию.

Чтобы устранить утечку данных, было бы хорошо вычислить только avg. валовой для актера за все фильмы, выпущенные до целевого фильма (в конце концов, если бы мы использовали это в реальном мире, у нас не было бы доступа к кассовым сборам будущих фильмов, в которых снимался бы актер).

Нахождение нашего базового уровня с помощью фиктивной регрессии

После разделения данных обучения и тестирования я начал с нашего базового уровня.

model_baseline = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
DummyRegressor() 
)

Затем я использовал перекрестную проверку, чтобы помочь оценить показатели.

scoring = ['neg_mean_absolute_error','neg_mean_absolute_percentage_error','r2'] 
cv_baseline = cross_validate( model_baseline, X_train, y_train, scoring=scoring, cv=5 ) 
cv_mae_baseline = -cv_baseline['test_neg_mean_absolute_error'].mean() 
cv_mape_baseline = -cv_baseline['test_neg_mean_absolute_percentage_error'].mean() 
cv_r2_baseline = cv_baseline['test_r2'].mean() 
print('Baseline MAE:', '{:,}'.format(cv_mae_baseline.round(2))) print('Baseline MAPE:', '{:,}'.format(cv_mape_baseline.round(6))) print('Baseline R2 score:', '{:,}'.format(cv_r2_baseline.round(6)))

Ниже были напечатаны результаты:

Baseline MAE: 93,947,168.94 
Baseline MAPE: 282.409473 
Baseline R2 score: -0.000994

Из-за высокой дисперсии целевого столбца я решил, что моей ключевой метрикой будет оценка R2, а не MAE. Тем не менее, я продолжал печатать среднюю абсолютную ошибку и среднюю абсолютную процентную ошибку для справки. Теперь, когда у меня был базовый уровень, пришло время использовать другие известные модели машинного обучения.

Использование линейной регрессии

Я создал свою линейную модель и повторно использовал большую часть приведенного выше кода с небольшими изменениями.

model_lr_1 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), 
LinearRegression() 
)

После перекрестной проверки и подсчета очков ниже были распечатаны результаты:

Linear Regression (1) MAE: 455,712,698.77 
Linear Regression (1) MAPE: 178.76018 
Linear Regression (1) R2 score: -14,205.471755

Линейная регрессия работала хуже, чем базовый уровень! Я попробовал еще раз, на этот раз масштабируя свои данные, чтобы увидеть, имеет ли это значение.

model_lr_2 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), 
StandardScaler(), 
LinearRegression() 
)

Распечатанные результаты:

Linear Regression (2) MAE: 4.039487079675694e+17 
Linear Regression (2) MAPE: 3,207,677,317,315.1016 
Linear Regression (2) R2 score: -3.033014419394398e+22

Масштабирование данных сделало это намного хуже. Вероятно, это связано с большим разбросом данных, поскольку большинство фильмов приносят мало денег, в то время как некоторые зарабатывают более чем в 1000 раз больше, чем другие.

Затем я попробовал Ридж-регрессию.

Использование гребневой регрессии

К сожалению, в случае с Ridge нам пришлось масштабировать данные.

model_ridge_1 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), 
StandardScaler(), 
Ridge() 
)

Но, к счастью, это имело весьма положительное значение. Вот распечатанные результаты:

Ridge Regression (1) MAE: 64,164,252.9 
Ridge Regression (1) MAPE: 130.469053 
Ridge Regression (1) R2 score: 0.5957777

Я попробовал несколько разных значений альфа-канала для Ridge, но это не имело большого значения.

model_ridge_2 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), 
StandardScaler(), 
Ridge(alpha=100) 
)

Вот новые оценки:

Ridge Regression (2) MAE: 63,796,418.02 
Ridge Regression (2) MAPE: 128.287068 
Ridge Regression (2) R2 score: 0.595994

Это почти не имело значения. Я мог бы использовать поиск по сетке, чтобы найти оптимальные гиперпараметры, но я хотел сначала попробовать древовидную модель.

У меня было предчувствие, что древовидные модели превзойдут модели линейной регрессии, потому что получение больших кассовых сборов не обязательно является линейным, и в игре есть другие взаимодействия, такие как история, звездная сила фильма и реклама из уст в уста. Ниже подтверждается моя догадка.

Использование случайного леса

Я построил первую модель, используя порядковый кодировщик для моих категориальных переменных.

model_forest_1 = make_pipeline( 
OrdinalEncoder(), 
SimpleImputer(strategy='mean'), 
RandomForestRegressor() 
)

Вот были баллы:

Random Forest (1) MAE: 45,806,089.2 
Random Forest (1) MAPE: 63.284248 
Random Forest (1) R2 score: 0.6850748

Показатели уже были намного лучше, а я еще даже не настраивал гиперпараметры! Затем я попробовал One Hot Encoding, чтобы увидеть, имеет ли это значение.

model_forest_2 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), 
RandomForestRegressor() 
)

Результаты:

Random Forest (2) MAE: 44,759,313.8 
Random Forest (2) MAPE: 48.350349 
Random Forest (2) R2 score: 0.6904746

One Hot Encoding давал лучшие показатели, чем Ordinal, поэтому я придерживался его.

Использование поиска по сетке

Чтобы найти оптимальные гиперпараметры, я использовал GridSearchCV() для «model_forest_2». Вместо того, чтобы тестировать все параметры в итеративном диапазоне, вместо этого я использовал крайние значения параметров, чтобы сократить время вычислений, но не оставляя все на волю случая, используя случайный поиск.

params = { "simpleimputer__strategy": ['mean','median'], "randomforestregressor__n_estimators": [75, 100, 200], "randomforestregressor__max_depth": [None, 100], "randomforestregressor__min_samples_leaf": [1, 0.1], 
} 
model_grid_rf = GridSearchCV( 
model_forest_2, 
param_grid = params, 
n_jobs=-1, 
cv=5, 
# verbose=3 
) 
model_grid_rf.fit(X_train, y_train) 
print(model_grid_rf.best_params_)

Вот напечатанные гиперпараметры:

{'randomforestregressor__max_depth': None, 'randomforestregressor__min_samples_leaf': 1, 'randomforestregressor__n_estimators': 100, 'simpleimputer__strategy': 'mean'}

Гиперпараметры оказались просто значениями по умолчанию. Для пояснения я построил третью модель и явно указал гиперпараметры:

model_forest_3 = make_pipeline( 
OneHotEncoder(use_cat_names=True), 
SimpleImputer(strategy='mean'), RandomForestRegressor(max_depth=None, 
min_samples_leaf=1, 
n_estimators=100 ) 
)

Вот распечатанные результаты:

Random Forest (3) MAE: 44,809,586.4 
Random Forest (3) MAPE: 53.607505 
Random Forest (3) R2 score: 0.6880779

Все 3 балла были немного хуже. Скорее всего, это связано со случайностью регрессии, так как мы все равно каждый раз будем получать разные результаты. Тем не менее, чтобы быть консервативным, мы будем придерживаться этих значений для отчетности.

XGBoost

Затем я попробовал XGBoost. Я настроил гиперпараметры с помощью GridSearchCV:

xgb_params = { "xgbregressor__max_depth": [3,6,10], "xgbregressor__learning_rate": [0.01,0.1,0.3], "xgbregressor__n_estimators": [100, 500, 1000], "xgbregressor__colsample_bytree": [0.1, 0.5, 1] 
} 
xgb_grid_1 = GridSearchCV(
model_xgb_1, 
param_grid=xgb_params, 
n_jobs=-1, 
scoring='r2', 
cv=2, 
verbose=3) 
xgb_grid_1.fit(X_train, y_train)

Затем я распечатал результаты:

XGBoost (2) MAE: 45,012,162.4 
XGBoost (2) MAPE: 56.752657 
XGBoost (2) R2 score: 0.700572

Показатели MAE и MAPE были немного хуже, но показатель R2 был лучше. Поэтому я использовал эту модель для данных тестирования и сравнил ее с моделью случайного леса:

Random Forest R2 (Test) = 0.68125 
XGBoost R2 (Test) = 0.7136502

XGBoost показал лучшие результаты! Это неудивительно, поскольку это наиболее распространенная модель, используемая для победы в соревнованиях Kaggle.

Полученные результаты

В то время как Random Forest превзошел модели линейной регрессии, XGBoost все же превзошел их все. Это будет еще один пример готовой к адаптации модели XGBoost.

Для тех, кому интересно, какие функции были главными, вот график «важности функций», взятый из модели случайного леса:

Как видите, «бюджет» был самым важным параметром, а «Total_Gross_Bankability» занял второе место.

Дальнейшие исследования

Ограничения моего анализа включают отсутствующие данные во всех строках, которые либо пришлось удалить, либо проигнорировать. В набор данных также включены только фильмы, выпущенные в США в период с 1980 по 2020 год, и исключены все фильмы, выпущенные стриминговыми компаниями (например, оригиналы Netflix, поскольку их бизнес-модель не построена на кассовых сборах).

Область для дальнейших исследований включает в себя расчет общей валовой прибыльности на основе фильмов, выпущенных до того, который мы пытаемся предсказать, лог-преобразование кассовых сборов, чтобы проверить, влияет ли это на оценку R2, и удаление фильмов, классифицированных как выбросы в блок-сюжете. модель (с кассовыми сборами более чем в 1,5 раза превышающими межквартильный диапазон).

Если вы хотите предсказать, какими могут быть кассовые сборы вашего собственного фильма, посмотрите приложение, которое я сделал на основе этой модели!

Первоначально опубликовано на https://www.linkedin.com.

Вы можете проверить мой GitHub. Для удобства вы можете увидеть весь блокнот, упомянутый выше, прямо здесь. Чтобы увидеть больше проектов Data Science, подпишитесь на меня на GitHub или свяжитесь со мной в LinkedIn!