import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline
import json
import seaborn as sns
1. Описание набора данных и признаков (2 балла) (+) Описан процесс сбора данных (если применимо), есть подробное описание решаемой задачи, в чем ее ценность, дано описание целевого и прочих признаков; (+/-) Сказано, какая задача решается, откуда данные, что есть целевой признак. Даны названия признаков; (-/+) Сказано, какая задача решается, откуда данные и что есть целевой признак; (-) Описание отсутствует и дано только название датасета или решаемой задачи, скажем, "прогноз оттока".
Данные представляют собой базу кинофильмов и оценок пользователей из The Movie Database (TMDb). Данные взяты из Kaggle (https://www.kaggle.com/tmdb/tmdb-movie-metadata).
Из описания известно что данные были введены пользователями вручную, соответственно могут быть ошибки или не соответствия с официальными источниками (All fields are filled out by users so don't expect them to agree on keywords, genres, ratings, or the like). Метаданные были скачаны с использованием парсера используя API TMDb's (https://gist.github.com/SohierDane/4a84cb96d220fc4791f52562be37968b). Как считается рейтинг (целевая переменная), нашёл вот что:
Популярной возможностью IMDb являются онлайн-голосования. Любой зарегистрированный посетитель сайта может голосовать за фильмы, выставляя им рейтинг: от 1 («ужасный фильм» ) до 10 («шедевр» ) баллов
Я решаю задачу предсказания рейтинга фильма (признак vote_average). Ценность заключается в следующем: можно выяснить, как зависит окупаемость фильма, его популярность, делать подбор актёров таким образом, чтобы рейтинг фильма. Хочу понять, возможно ли предсказать рейтинг фильма, зная только его краткое описание, бюджет и другие общедоступные данные.
Описание признаков:
def load_tmdb_movies(path):
df = pd.read_csv(path)
df['release_date'] = pd.to_datetime(df['release_date']).apply(lambda x: x.date())
json_columns = ['genres', 'keywords', 'production_countries', 'production_companies', 'spoken_languages']
for column in json_columns:
df[column] = df[column].apply(json.loads)
return df
def load_tmdb_credits(path):
df = pd.read_csv(path)
json_columns = ['cast', 'crew']
for column in json_columns:
df[column] = df[column].apply(json.loads)
return df
movies = load_tmdb_movies("tmdb_5000_movies.csv")
credits = load_tmdb_credits("tmdb_5000_credits.csv")
Посмотрим что у нас из себя представляют данные.
movies.head(3)
Заключение: Большинство признаков представляют собой json-контейнеры, которые ещё надо разворачивать и делать из них фичи.
print(movies.shape)
print(movies.info())
Заключение: Практически все важные поля имеют значения - это хорошо
movies.describe()
p = movies.original_language.value_counts()
print(p.get_values())
print(movies.original_language.unique())
# распределение голосов в зависимости от языка
val_en_counts = movies[movies['original_language'] == 'en']['vote_average'].value_counts().sort_index()
val_ja_counts = movies[movies['original_language'] == 'ja']['vote_average'].value_counts().sort_index()
val_fr_counts = movies[movies['original_language'] == 'fr']['vote_average'].value_counts().sort_index()
plt.title('Распределение значений рейтинга')
plt.xlabel('vote_average'), plt.ylabel('Количество')
plt.plot(val_en_counts.keys(),np.log(val_en_counts.values))
plt.plot(val_ja_counts.keys(),np.log(val_ja_counts.values))
plt.plot(val_fr_counts.keys(),np.log(val_fr_counts.values))
plt.grid(True)
plt.show()
Заключение. Очевидно, что англоязычных фильмов больше чем других, но распределение голосов похожее.
p = movies.vote_average.value_counts().sort_index()
plt.title('Распределение значений рейтинга')
plt.xlabel('vote_average')
plt.ylabel('Количество')
plt.plot(p.keys(),p.values)
plt.grid(True)
plt.show()
plt.scatter(x=np.log(movies.budget+1), y=np.log(movies.revenue+1), c=movies.vote_average)
# plt.scatter(x=np.log(movies.budget), y=np.log(movies.revenue), c=movies.vote_average)
movies[ (movies['budget']>0) & (movies['revenue']>0) ].plot.scatter(x='budget', y='revenue', c='vote_average', figsize=(10, 10), s=45)
movies[ movies['budget'] <=0].head(3)
movies[ movies['revenue'] <=0 ].head(3)
Заключение. Несмотря на то, что пропусков данных нет, в самих данных есть проблемы. TMDB ничего не знает о бюджете и доходах некоторых фильмов. (например, The Lovers, The Tuxedo). Видимо такие фильмы придётся в дальнейшем просто убирать из обучающей выборки.
Для остальных фильмов наблюдается зависимость вложения и отдачи, но однозначно сказать что дорогой фильм будет очень востребован нельзя.
movs = pd.concat([movies.budget, movies.revenue, movies.popularity, movies.vote_count, movies.vote_average], axis=1)
#movs.vote_average = np.log(movs.vote_average)
movs.popularity = np.log(movs.popularity+1)
movs.vote_count = np.log(movs.vote_count+1)
movs[ (movs.budget>0) & (movs.revenue>0)].plot.scatter(x='popularity', y='vote_average', figsize=(5, 5), c='vote_count')
del movs
Заключение. Сказать что популярный фильм будет иметь высоки рейтинг нельзя. Есть фильмы, которые не так популярны, но имеют высокий рейтинг.
rel_date_time = pd.to_datetime(movies.release_date)
rel_date_time[ rel_date_time > '1963-01-01' ].value_counts().plot(figsize=(20, 5))
del rel_date_time
Заключение. TMDB в основном содержит фильмы 21 века.
Остальные признаки посмотрим, когда возьмём их из JSONa
Вытяним из JSONa все жанры, языки фильмов, страну производителя и сделаем OneHotEnconding вручную (кто знает как сделать это красивее напишите мне в Слаке).
all_genres = []
for i in range(0,movies.shape[0]):
all_genres.append([actor['name'] for actor in movies['genres'].iloc[i][:10]])
all_genres = set(x for l in all_genres for x in l) # множество содержит только уникальные элементы
genres = pd.DataFrame(columns=all_genres)
for i in range(0,movies.shape[0]):
a = [actor['name'] for actor in movies['genres'].iloc[i][:10]]
for j in all_genres:
genres.at[i,j] = 0
for item in a:
genres.at[i,item] = 1
genres.fillna(0, inplace=True)
print(genres.shape)
new_cols = 'genre_'+genres.columns
genres.columns = new_cols
# print(genres.info())
# print(genres.head(5)) # для проверки
# print(genres.tail(5)) # для проверки
genres.to_csv('genres.csv')
# genres = pd.read_csv('genres.csv')
# genres = genres.drop('Unnamed: 0', axis=1)
# for col in genres.columns:
# genres[col] = pd.to_numeric(genres[col], errors='coerce', downcast='unsigned')
# Компаний очень много и комп медленно их обрабатывает, поэтому пока не трогаю
# all_prod_companies = []
# for i in range(0,movies.shape[0]):
# all_prod_companies.append([comp['id'] for comp in movies['production_companies'].iloc[i][:10]])
# all_prod_companies = set(x for l in all_prod_companies for x in l)
# prod_comps = pd.DataFrame(columns=all_prod_companies)
# for i in range(0,movies.shape[0]):
# a = [comp['id'] for comp in movies['production_companies'].iloc[i][:10]]
# for j in all_prod_companies:
# prod_comps.at[i,j] = 0
# for item in a:
# prod_comps.at[i,item] = 1
# prod_comps.fillna(0, inplace=True)
# print(prod_comps.shape)
# print(prod_comps.head(3))
all_prod_countrs = []
for i in range(0,movies.shape[0]):
all_prod_countrs.append([comp['iso_3166_1'] for comp in movies['production_countries'].iloc[i][:10]])
all_prod_countrs = set(x for l in all_prod_countrs for x in l)
print(all_prod_countrs)
prod_countrs = pd.DataFrame(columns=all_prod_countrs)
for i in range(0,movies.shape[0]):
a = [countr['iso_3166_1'] for countr in movies['production_countries'].iloc[i][:10]]
for j in all_prod_countrs:
prod_countrs.at[i,j] = 0
for item in a:
prod_countrs.at[i,item] = 1
prod_countrs.fillna(0, inplace=True)
new_cols = 'country_'+prod_countrs.columns
prod_countrs.columns = new_cols
print(prod_countrs.shape)
# print(prod_countrs.head(3))
prod_countrs.to_csv('prod_countrs.csv')
# prod_countrs = pd.read_csv('prod_countrs.csv')
# prod_countrs = prod_countrs.drop('Unnamed: 0', axis=1)
all_spok_langs = []
for i in range(0,movies.shape[0]):
all_spok_langs.append([comp['iso_639_1'] for comp in movies['spoken_languages'].iloc[i][:10]])
all_spok_langs = set(x for l in all_spok_langs for x in l)
# print(all_spok_langs)
spok_langs = pd.DataFrame(columns=all_spok_langs)
for i in range(0,movies.shape[0]):
a = [lang['iso_639_1'] for lang in movies['spoken_languages'].iloc[i][:10]]
for j in all_spok_langs:
spok_langs.at[i,j] = 0
for item in a:
spok_langs.at[i,item] = 1
spok_langs.fillna(0, inplace=True)
new_cols = 'country_'+spok_langs.columns
spok_langs.columns = new_cols
print(spok_langs.shape)
# print(spok_langs.head(3))
spok_langs.to_csv('spok_langs.csv')
# prod_countrs = pd.read_csv('prod_countrs.csv')
# prod_countrs = prod_countrs.drop('Unnamed: 0', axis=1)
LabelEncoding оригинального языка:
from sklearn.preprocessing import LabelEncoder
labelEnc = LabelEncoder()
movies.original_language = labelEnc.fit_transform(movies.original_language)
# print(dict(enumerate(labelEnc.classes_)))
# print(movies.original_language.head(3)) # для проверки
Из даты релиза достаём год и месяц выпуска. Вдруг окажется что например, фильмы хорошо заходят перед новыми годом, а не перед новым учебным годом.
temp_date_month = []
temp_date = pd.to_datetime(movies.release_date)
temp_date_data = [t.month for t in temp_date]
movies['release_month'] = temp_date_data
temp_date_data = [t.year for t in temp_date]
movies['release_year'] = temp_date_data
Соединяем теперь все столбцы в один DataFrame
movies = pd.concat([movies, genres, prod_countrs, spok_langs], axis=1)
Удаляем все не нужные признаки: страница фильма, ИД в каталоге, оригинальное название, статус.
movies.drop(['homepage', 'status', 'id', 'original_title', 'title', 'release_date'], axis=1, inplace=True)
movies.drop(['genres', 'production_countries', 'spoken_languages'], axis=1, inplace=True)
# с чем я пока не умею работать
movies.drop(['tagline', 'keywords', 'overview', 'production_companies'], axis=1, inplace=True)
Создаём наборы данных с признаками и целевым признаком:
movies.columns[:50]
x_data = movies.copy()
x_data.drop('vote_average', axis=1, inplace=True)
y_data = movies['vote_average']
x_data.dropna(axis=0, inplace=True)
y_data = y_data[x_data.index]
data = pd.concat([x_data, y_data], axis=1)
data.to_csv('data1.csv')
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.33, random_state=43)
x_train_f, x_test_f, y_train_f, y_test_f = train_test_split(x_data[(x_data.budget>0) & (x_data.revenue>0)], y_data[(x_data.budget>0) & (x_data.revenue>0)], test_size=0.33, random_state=43)
3. Первичный визуальный анализ данных (4 балла) (+) Построены визуализации (распределения признаков, матрица корреляций и т.д.), описана связь с анализом данным (п. 2). Присутствуют выводы; (+/-) Построены визуализации (распределения признаков, матрица корреляций и т.д.). Присутствуют выводы с небольшими ошибками; (-/+) Недостает важных визуализаций и/или присутствует много ошибок в выводах; (-) Отсутствует.
# TODO Рейтинг фильмов, которые меняли своё название и не меняли.
movies_log = movies[(movies['budget']>0) & (movies['revenue']>0)][['vote_average', 'popularity', 'revenue', 'budget', 'vote_count','runtime']].copy()
movies_log.popularity = np.log(movies_log.popularity+1)
movies_log.revenue = np.log(movies_log.revenue+1)
movies_log.budget = np.log(movies_log.budget+1)
movies_log.vote_count = np.log(movies_log.vote_count+1)
# movies_log.vote_average = np.log(movies_log.vote_average+1)
sns.pairplot(data=movies_log, hue='vote_average')#, vars=['vote_average', 'popularity', 'revenue', 'budget', 'vote_count'])#,'runtime'])
print(movies.release_month.value_counts())
movies_log = movies[(movies['budget']>0) & (movies['revenue']>0)][['release_year', 'release_month', 'vote_average', 'popularity']].copy()
movies_log.release_year = np.log(movies_log.release_year+1)
movies_log.popularity = np.log(movies_log.popularity+1)
sns.pairplot(data=movies_log, hue='release_month')
f, ax = plt.subplots(nrows=1, ncols=2, figsize=(15, 3))
corr = movies[['vote_average', 'popularity', 'revenue', 'runtime', 'budget', 'vote_count', 'release_year', 'release_month']].corr()
sns.heatmap(corr, cmap='YlGnBu', annot=True, ax=ax[0], fmt='.1f')
corr = movies[['vote_average', 'popularity', 'revenue', 'runtime', 'budget', 'vote_count', 'release_year', 'release_month']].corr(method='spearman')
sns.heatmap(corr, cmap='YlGnBu', annot=True, ax=ax[1], fmt='.1f')
corr = movies[['vote_average','genre_Action', 'genre_Drama', 'genre_Adventure', 'genre_Crime',
'genre_Western', 'genre_Comedy', 'genre_Mystery', 'genre_Music',
'genre_History', 'genre_Documentary', 'genre_Fantasy', 'genre_Romance',
'genre_Animation', 'genre_War', 'genre_Foreign', 'genre_TV Movie',
'genre_Science Fiction', 'genre_Family', 'genre_Horror',
'genre_Thriller']].corr()
f, ax = plt.subplots(figsize=(10, 10))
sns.heatmap(corr, cmap='YlGnBu', annot=True, ax=ax, fmt='.1f')
Заключение. Что касается прогнозируемой величины: наиболее рейтинговые фильмы являются драмами. Рейтинг фильма сильно зависит от количества голосов, популярности и продолжительности.
Просто наблюдения: семейный фильм скорее всего будет мультфильмом, драма скорее всего романтической и в историческом контексте, исторические фильмы чаще про войну, за популярные фильмы чаще всего голосуют. Наибольшее количество фильмов выходят в сентябре, что наверно не совсем логично. Например, в новогодние каникулы все отдыхают и обычно нечем заняться. С другой стороны это конец летнего сезона.
Здесь описание того, что было показано до этого.
Заключение по приведённым выше данным вполне очевидны. Например, семейный фильм - это значит что родители пойдут с детьми на мультфильмы (жанр - анимация). Далее как наблюдение - больше всего исторических фильмов о войне, нежели о великих достижениях и гениях своего времени.
Также очевидно, что люди обсуждают и голосуют за те фильмы, на которые они ходили и возможно не раз или рассказали друзьям, что можно судит по кассовым сборам.
отсутствует, жаль
from sklearn.linear_model import LinearRegression#, RidgeCV, LassoCV
from sklearn.metrics import mean_absolute_error, mean_squared_error
lr = LinearRegression(n_jobs=-1)
lr.fit(x_train, y_train)
prediction = lr.predict(x_train)
print('MAE',mean_absolute_error(y_train, prediction))
print('MSE',mean_squared_error(y_train, prediction))
lr = LinearRegression(n_jobs=-1)
lr.fit(x_train_f, y_train_f)
prediction = lr.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
from sklearn.model_selection import cross_val_predict
prediction = cross_val_predict(lr, x_train, y_train, cv=5, n_jobs=-1)
print('MAE',mean_absolute_error(y_train, prediction))
print('MSE',mean_squared_error(y_train, prediction))
prediction = cross_val_predict(lr, x_train_f, y_train_f, cv=5, n_jobs=-1)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
Вывод. Если в модель не закладывать фильмы, бюджет и доход которых нулевой (здесь больше всего шумов, см. выше), то даже линейная регрессия лучше работает. Это видно по MSE, т.к. она сильнее штрафует за большие ошибки (выбросы). Кросс-валидация этот результат не улучшает.
Выбираем такое количество признаков, которые описывают 98% всех решений и посмотрим на качество прогноза.
from sklearn.decomposition import PCA
# из 9 домашки
def plotPCA(pca, perct=90):
"""
График накопленного процента объясненной дисперсии по компонентам
"""
features = range(pca.n_components_)
variance = np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)
plt.figure(figsize=(15, 7))
plt.bar(features, variance)
# дополнительно отметим уровень, при котором объяснены 90% дисперсии
plt.hlines(y = perct, xmin=0, xmax=len(features), linestyles='dashed', colors='red')
plt.xlabel('PCA components')
plt.ylabel('variance')
plt.xticks(features)
plt.show()
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(x_train)
x_train_scaled = scaler.transform(x_train)
x_test_scaled = scaler.transform(x_test)
prediction = cross_val_predict(lr, x_train_scaled, y_train, cv=5, n_jobs=-1)
print('MAE',mean_absolute_error(y_train, prediction))
print('MSE',mean_squared_error(y_train, prediction))
pca = PCA()
pca.fit(x_train_scaled, y_train)
plotPCA(pca)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(x_train)
x_train_scaled = scaler.transform(x_train_f)
x_test_scaled = scaler.transform(x_test_f)
prediction = cross_val_predict(lr, x_train_scaled, y_train_f, cv=5, n_jobs=-1)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
pca = PCA()
pca.fit(x_train_scaled, y_train_f)
plotPCA(pca)
features = range(pca.n_components_)
variance = np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)
print(variance[16*9+5], features[16*9+5])
pca = PCA(n_components=144)
pca.fit(x_train_scaled, y_train_f)
pca_features_train = pca.transform(x_train_scaled)
pca_features_test = pca.transform(x_test_scaled)
lr.fit(pca_features_train, y_train_f)
prediction = lr.predict(pca_features_test)
print('MAE',mean_absolute_error(y_test_f, prediction))
print('MSE',mean_squared_error(y_test_f, prediction))
Заключение. Уменьшение количества признаков сильно картину не улучшает, возможно это из-за масштабирования. (масштабирование коэффициентов не улучшает картину, либо я где-то ошибся, т.к. МАЕ и MSE были огромными)
from sklearn.linear_model import LassoCV
LS_CV = LassoCV(cv=5, n_jobs=-1)
LS_CV.fit(x_train_f,y_train_f)
prediction = LS_CV.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
from sklearn.linear_model import RidgeCV
Rg_CV = RidgeCV(cv=5)
Rg_CV.fit(x_train_f,y_train_f)
prediction = Rg_CV.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
score_list = [0.01, 0.1, 1, 10, 100, 1000 ] #[ 'svd', 'eigen'] #['explained_variance', 'neg_mean_absolute_error', 'neg_mean_squared_error','neg_mean_squared_log_error','neg_median_absolute_error','r2']
# for score in score_list:
Rg_CV = RidgeCV(cv=5, alphas=score_list)
Rg_CV.fit(x_train_f,y_train_f)
prediction = Rg_CV.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction), 'MSE',mean_squared_error(y_train_f, prediction))
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import GridSearchCV
alphas = [.01, .05, .1, .2, 1.0]
l1_ratios = np.linspace(.05, .15, 10)
# alphas = [0.1, 1.0, 10]
# l1_ratios = np.linspace(.1, .9, 3)
el_net = ElasticNet()
parameters = {'alpha':alphas, 'l1_ratio':l1_ratios}
grid = GridSearchCV(el_net, param_grid=parameters, scoring='mean_absolute_error' ,verbose=1, cv=5, return_train_score=1, n_jobs=-1)
grid.fit(x_train_f, y_train_f)
print(grid.best_score_)
print(grid.best_estimator_)
el_net_best = grid.best_estimator_
prediction = el_net_best.predict(x_train_f)
print(el_net_best.score(x_train_f, y_train_f))
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
Заключение. Из рассмотренных моделей ElasticNet и RidgeCV показали хорошие результаты. Правда у ElasticNet коэффициент детерминации маловат и коэффициент l1_ratio
такой что он по сути является RidgeCV (т.е. применятся Л2-регуляризация). Обратимся к кривым валидации для проверки.
from sklearn.ensemble import RandomForestRegressor
estimators = np.arange(5,60,20)
min_samples_leaf = np.arange(5,20,5)
parameters = {'n_estimators':estimators, 'min_samples_leaf':min_samples_leaf}
rfrr = RandomForestRegressor(criterion='mae', n_jobs=-1)
grid = GridSearchCV(rfrr, param_grid=parameters, scoring='mean_absolute_error', verbose=1, cv=5, return_train_score=1, n_jobs=-1)
grid.fit(x_train_f, y_train_f)
print(grid.best_score_)
print(grid.best_estimator_)
rfrr_best = grid.best_estimator_
prediction = rfrr_best.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
Заключение. Лес дал неплохой результат.
from sklearn.ensemble import AdaBoostRegressor
estimators = np.arange(5,60,20)
loss = ['linear', 'square', 'exponential']
parameters = {'n_estimators':estimators, 'loss':loss}
abrr = AdaBoostRegressor(random_state=42)
grid = GridSearchCV(abrr, param_grid=parameters, scoring='mean_absolute_error', verbose=1, cv=5, return_train_score=1, n_jobs=-1)
grid.fit(x_train_f, y_train_f)
print(grid.best_score_)
print(grid.best_estimator_)
abrr_best = grid.best_estimator_
prediction = abrr_best.predict(x_train_f)
print('MAE',mean_absolute_error(y_train_f, prediction))
print('MSE',mean_squared_error(y_train_f, prediction))
from sklearn.model_selection import learning_curve
# from http://scikit-learn.org/stable/auto_examples/model_selection/plot_learning_curve.html#sphx-glr-auto-examples-model-selection-plot-learning-curve-py
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
"""
Generate a simple plot of the test and training learning curve.
Parameters
----------
estimator : object type that implements the "fit" and "predict" methods
An object of that type which is cloned for each validation.
title : string
Title for the chart.
X : array-like, shape (n_samples, n_features)
Training vector, where n_samples is the number of samples and
n_features is the number of features.
y : array-like, shape (n_samples) or (n_samples, n_features), optional
Target relative to X for classification or regression;
None for unsupervised learning.
ylim : tuple, shape (ymin, ymax), optional
Defines minimum and maximum yvalues plotted.
cv : int, cross-validation generator or an iterable, optional
Determines the cross-validation splitting strategy.
Possible inputs for cv are:
- None, to use the default 3-fold cross-validation,
- integer, to specify the number of folds.
- An object to be used as a cross-validation generator.
- An iterable yielding train/test splits.
For integer/None inputs, if ``y`` is binary or multiclass,
:class:`StratifiedKFold` used. If the estimator is not a classifier
or if ``y`` is neither binary nor multiclass, :class:`KFold` is used.
Refer :ref:`User Guide <cross_validation>` for the various
cross-validators that can be used here.
n_jobs : integer, optional
Number of jobs to run in parallel (default 1).
"""
plt.figure()
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, scoring='mean_absolute_error')
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
label="Cross-validation score")
plt.legend(loc="best")
return plt
plot_learning_curve(lr, 'LR learning curves', x_train, y_train, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5))
plot_learning_curve(lr, 'LR learning curves (filtered data)', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5))
Заключение. Судя по кривой обучения увеличение данных хорошо влияет, т.к. уменьшается вариация (varience).
plot_learning_curve(LS_CV, 'LS learning curves', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5))
Заключение. Использовать эту модель не стоит, т.к. очень большое отклонение; а близкое расположение кривых при увеличении обучающей выборки говорит о высоком смещении оценки.
plot_learning_curve(Rg_CV, 'Rg learning curves', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.01, 1.0, 10))
Заключение. Тут выглядит очень непонятно. Вроде как не надо много данных и модель сразу же получает хорошую оценку. Масштабирование с 0,01 до 0,2 размера выборки ничего хорошего не показало. Поэтому думаю оставить эту модель ии посмотреть, что будет на тесте.
el_net_best = grid.best_estimator_
plot_learning_curve(el_net_best, 'el_net_best learning curves', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.01, .3, 5))
from sklearn.model_selection import cross_val_score
alphas = np.linspace(0.01, 2.0, 15) #[.01, .05, .1, .2, 1.0]
mse_array = []
mae_array = []
scores_array = []
scores_std_array = []
for alph in alphas:
el_net_temp = ElasticNet(alpha=alph, l1_ratio=0.08333)
el_net_temp.fit(x_train_f, y_train_f)
scores = cross_val_score(el_net_temp, x_train_f, y_train_f, scoring='mean_absolute_error', cv=5, n_jobs=-1)
prediction = el_net_temp.predict(x_train_f)
scores_array.append(scores.mean())
scores_std_array.append(scores.std())
mae_array.append(mean_absolute_error(y_train_f, prediction))
mse_array.append(mean_squared_error(y_train_f, prediction))
# print(alph, scores, 'MAE',mean_absolute_error(y_train_f, prediction), 'MSE',mean_squared_error(y_train_f, prediction))
plt.figure()
plt.title('Alpha - regularization')
plt.xlabel("Alpha")
plt.ylabel("Score")
plt.grid()
plt.fill_between(alphas, -np.asarray(scores_array) - np.asarray(scores_std_array),
-np.asarray(scores_array) + np.asarray(scores_std_array), alpha=0.1,
color="r")
plt.plot(alphas, -np.asarray(scores_array), 'o-', color="r", label="Cross-validation score")
plt.plot(alphas, mae_array, 'o-', color="g", label="Train score")
plt.legend(loc="best")
Заключение. По валидационной кривой очень похоже на высокое смещение оценки. Как интерпретировать вторую кривую (скор от регуляризации) не знаю. Думал что тоже покажет мне или смещение или разброс, но не дало.
plot_learning_curve(rfrr_best, 'RandomForest learning curves', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5))
plot_learning_curve(abrr_best, 'RandomForest learning curves', x_train_f, y_train_f, ylim=None, cv=5,
n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5))
Заключение. Более привычный рисунок для проверки. Чем больше данных тем лучше, highbias.
lr = LinearRegression(n_jobs=-1)
lr.fit(x_train, y_train)
prediction = lr.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
По отфильтрованной обучающей выборке:
lr.fit(x_train_f, y_train_f)
prediction = lr.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
LS_CV = LassoCV(cv=5, n_jobs=-1)
LS_CV.fit(x_train_f, y_train_f)
prediction = LS_CV.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
Rg_CV = RidgeCV(cv=5)
Rg_CV.fit(x_train_f, y_train_f)
prediction = Rg_CV.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
el_net_best = ElasticNet(alpha=0.01, copy_X=True, fit_intercept=True,
l1_ratio=0.083333333333333329, max_iter=1000, normalize=False,
positive=False, precompute=False, random_state=None,
selection='cyclic', tol=0.0001, warm_start=False)
el_net_best.fit(x_train_f, y_train_f)
prediction = Rg_CV.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
rfrr = RandomForestRegressor(bootstrap=True, criterion='mae', max_depth=None,
max_features='auto', max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=5, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=45, n_jobs=-1,
oob_score=False, random_state=None, verbose=0, warm_start=False)
rfrr.fit(x_train_f, y_train_f)
prediction = rfrr.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
abrr = AdaBoostRegressor(base_estimator=None, learning_rate=1.0, loss='linear',
n_estimators=25, random_state=42)
abrr.fit(x_train_f, y_train_f)
prediction = abrr.predict(x_test)
print('MAE',mean_absolute_error(y_test, prediction))
print('MSE',mean_squared_error(y_test, prediction))
Заключение. Оценки как отфильтрованные, так и не отфильтрованные вполне соответствуют значениям метрик на обучающей выборке. Победил Случайный лес.
Я выбирал бы случайный лес или линейную регрессию. Первая как-то надёжнее, но вторая проще, разница в качестве между ними небольшая.
Так как не понятно было изначально к чему можно было прийти я пошёл классическим путём: выбрать что-то простое (линейная регрессия) и серебрянную пулю (Случайный лес). Собственно говоря они и хорошо зашли. Если честно, то не знаю что выбрать MSE или MAE. Я думаю что MAE говорит о точности оценки, а MSE что-то вроде разброса.
В целом я результатом доволен, не смотря на то, что я не использую данные об описании (можно было бы построить новые фичи, но как на это время на оставил), ключевые слова или кинокомпании. Даже без этого модели (Лес и ЛР) дают оценку с ошибкой в 100/10*0.58 = 5,8% и 6,7% соответственно. Пожалуй это и было бы основной метрикой, чем меньше процент ошибки, тем лучше - как инженерный подход.
Теперь мы можем предсказывать рейтинг фильма. Это может нам понадобится при проведении подготовительных работ над фильмом - его описание, закладывать бюджет и т.п. Почему результат такой - думаю, потому что основные признаки как популярность и количество голосов влияют больше остальных, да и фильтрация по бюджету и доходу - это было важно.