Одной из основных задач при разработке модели машинного обучения является выбор оптимальных гиперпараметров модели. Гиперпараметры — это настройки, которые определяют специфику обучения алгоритма модели. В зависимости от модели они могут управлять функциями обучения, такими как то, как алгоритм перебирает решения, вычисляет внутренние функции или взвешивает прогноз.
Хотя большинство моделей машинного обучения хорошо задокументированы, выбор гиперпараметров все еще может быть весьма запутанным. Выбор оптимальных настроек обычно требует перебора различных параметров, чтобы увидеть, как они влияют на точность модели, вызывая процесс Поиск в сетке. К счастью, Scikit-learn разработала GridSearchCV
инструмент для автоматизации этого процесса. GridSearchCV
также сохраняет оценки точности комбинаций гиперпараметров по мере перебора параметров, что позволяет удобно исследовать влияние этих параметров на модель. Давайте посмотрим, как это работает.
Этот рабочий процесс был создан в Python 3.10, и соответствующий блокнот Jupyter можно найти здесь.
1. Импорт и обработка данных
В этом проекте используется общедоступный набор данных Уровень адаптации учащихся в онлайн-образовании на kaggle, опубликованный как Прогнозирование уровня адаптации учащихся в онлайн-образовании с использованием подходов машинного обучения на 12-й Международной конференции 2021 по компьютерным коммуникациям и сетям. Технологии (ICCCNT) материалы конференции (DOI: 10.1109/ICCCNT51525.2021.9579741). Здесь авторы собрали массив демографических, экономических и других исходных данных для студентов, обучающихся онлайн, а также индивидуальные оценки их адаптации к онлайн-обучению. Затем авторы использовали эти данные для прогнозирования уровня адаптивности новых студентов, создав модель классификатора машинного обучения. Они сравнили точность ряда моделей, включая K-Nearest Neighbors
, Decision Tree
, Random Forest
, Support Vector Machine
, Artificial Neural Network
и Naive Bayes
, что дало нам хорошие ориентиры при разработке и сравнении нашего собственного диапазона моделей классификаторов.
Начнем с загрузки и импорта данных в виде кадра данных pandas
.
#download and import data import wget import os.path import pandas as pd file_path = 'data/students_adaptability_level_online_education.csv' if not os.path.isfile(file_path): url = 'https://www.kaggle.com/datasets/mdmahmudulhasansuzan/students-adaptability-level-in-online-education?select=students_adaptability_level_online_education.csv' wget.download(url, out = file_path) df = pd.read_csv(file_path) df.head()
Похоже, что все наши переменные являются объектами. Давайте подтвердим перед обработкой данных.
#check data structure df.dtypes
Чтобы построить и изучить ряд моделей классификатора, нам нужно преобразовать данные в закодированные целые числа. Для начала давайте отсортируем по алфавиту value_counts
каждой переменной и сохраним их в словаре списков переменных. Затем мы будем использовать индекс списка каждого значения как целое число в новом закодированном кадре данных.
#check distribution of all data values and list values in dict values_dict = {} for column in df: values_dict[column] = df[column].value_counts().sort_index().index.to_list() #inspect values values_dict
Возраст, Уровень образования и Финансовое состояние являются порядковыми. То есть они имеют естественный порядок рангов. Поскольку эти значения в настоящее время упорядочены в алфавитном порядке, текущий порядок в списке не соответствует их естественному расположению. Хотя это не повлияет на точность наших моделей классификатора, это усложнит исследование данных и интерпретацию модели. Чтобы упростить задачу в будущем, давайте вручную изменим порядок этих значений перед обработкой в окончательный закодированный кадр данных. Просто помните, что при построении живых моделей все новые данные должны быть предварительно обработаны таким же образом.
#reorder lists order = [0,5,1,2,3,4] values_dict[‘Age’] = [values_dict[‘Age’][i] for i in order] order = [1,0,2] values_dict[‘Education Level’] = [values_dict[‘Education Level’][i] for i in order] values_dict[‘Financial Condition’] = [values_dict[‘Financial Condition’][i] for i in order] order = [1,0] values_dict[‘Institution Type’] = [values_dict[‘Institution Type’][i] for i in order] values_dict[‘Load-shedding’] = [values_dict[‘Load-shedding’][i] for i in order] order = [1,2,0] values_dict[‘Adaptivity Level’] = [values_dict[‘Adaptivity Level’][i] for i in order] #build integer-coded dataframe coded_dict = {} for column in df: coded_dict[column] = [] for i in range(0,df.shape[0]): coded_dict[column].append(values_dict[column].index(df[column][i])) coded_df = pd.DataFrame(coded_dict) coded_df.head()
2. Исследование данных
Теперь, когда данные закодированы целым числом, мы можем искать любые очевидные тенденции в наборе данных. Мне всегда нравится начинать с корреляционной матрицы, которая быстро визуализирует коррелирующие переменные. Здесь мы будем использовать ранговую корреляцию Спирмена, чтобы учесть тот факт, что это ранжированные целые числа, и построим результирующую матрицу в виде тепловой карты.
#plot correlation matrix import seaborn as sns import matplotlib.pyplot as plt cmap = sns.color_palette(“vlag”, as_cmap=True).reversed() sns.heatmap(coded_df.corr(method=’spearman’), cmap=cmap, vmin=-1, vmax=1) plt.show()
Учитывая, что для получения образования требуется время (а большинство из нас прогрессирует с одинаковой скоростью), неудивительно, что возраст и уровень образования довольно сильно коррелируют друг с другом. Однако нет сильной индивидуальной корреляции с Уровнем адаптивности, что указывает на необходимость многомерного исследования. Примечательно, что существует слабая положительная корреляция между переменной ответа и длительностью занятий, финансовым состоянием и местоположением, что, по-видимому, указывает на то, что хорошо обеспеченные ресурсами учащиеся, живущие в городе и получающие больше общения с учителями, адаптируются лучше всего. Существует также слабая отрицательная корреляция с типом учебного заведения, что позволяет предположить, что негосударственные школы обеспечивают более высокую адаптивность, чем государственные школы.
Поскольку ни одна из переменных сильно не коррелирует с Уровнем адаптивности, давайте упорядочим (уменьшим размерность) данные и посмотрим, есть ли какие-либо кластеры в общей структуре данных. Во-первых, мы стандартизируем данные. Затем будем ординировать с Principal Component Analysis
, который проецирует полные данные по измерениям наибольшей изменчивости (подробнее о ординации здесь).
#split X and Y, and standardize X import numpy as np import sklearn from sklearn import preprocessing Y = coded_df[‘Adaptivity Level’].to_numpy() X = coded_df.loc[:, coded_df.columns != ‘Adaptivity Level’].to_numpy() xform = preprocessing.StandardScaler() X_z = xform.fit(X).transform(X) #define pca plot fuction def plot_pca(ordi,lab,y): ''' Generates biplot of 1st and 2nd axes from an ordination model ''' plt.figure() plt.scatter(ordi[y==0, 0], ordi[y==0, 1], color='red', label='Low') plt.scatter(ordi[y==1, 0], ordi[y==1, 1], color='blue', label='Moderate') plt.scatter(ordi[y==2, 0], ordi[y==2, 1], color='green', label='High') plt.xlabel(lab[0]) plt.ylabel(lab[1]) plt.legend() plt.show() #train and plot PCA from sklearn.decomposition import PCA pca = PCA() X_pca = pca.fit_transform(X_z) labels = [f"PCA1 ({pca.explained_variance_ratio_[0]*100:.1f}%)", f"PCA2 ({pca.explained_variance_ratio_[1]*100:.1f}%)"] plot_pca(X_pca,labels,Y)
Несмотря на то, что наблюдается небольшая видимая тенденция Низкая адаптивность к верхнему левому углу и Высокая адаптивность к нижнему правому краю, эта тенденция слабая и сильно смешанная. На первый и второй основные компоненты приходится только 39,0% общей изменчивости данных, поэтому мы не можем добиться значимого разделения данных только по двум измерениям простой ординации. Существуют дополнительные методы ординации, которые применяют более структурированную проекцию данных в попытке максимизировать внутренние структуры данных. t-Distributed Stochastic Neighbor Embedding
— один из таких методов, который пытается максимизировать естественные кластеры данных.
#train and plot tSNE from sklearn.manifold import TSNE tsne = TSNE(n_components=2, learning_rate=300, perplexity=30, early_exaggeration=12, init=’random’, random_state=2019) X_tsne = tsne.fit_transform(X_z) labels = [“tSNE1”,”tSNE2"] plot_pca(X_tsne,labels,Y)
Явно существует ряд похожих студенческих кластеров, но они не разделяются по уровню адаптивности. Давайте посмотрим, что мы можем сделать с некоторыми распространенными алгоритмами классификатора ML. Здесь мы будем использовать Logistic Regression
, K-Nearest Neighbors
, Decision Tree
, Random Forest
, Support Vector Machine
и Artificial Neural Network
. В оригинальной публикации авторы не использовали Logistic Regression
, который часто не работает так же хорошо для n>2 классов, хотя и допускает мультиклассирование. Однако они построили модель Naive Bayes
, которая предполагает, что ни одна из независимых переменных не влияет друг на друга. Тем не менее, мы можем разумно предположить взаимосвязь между некоторыми переменными, такими как вероятность того, что более образованные учащиеся будут старше, и насколько обеспеченные в финансовом отношении учащиеся с большей вероятностью смогут позволить себе более высокую скорость интернета и более дорогие устройства. Следовательно, это неверное предположение для этого набора данных, поэтому мы не будем включать модель Naive Bayes
.
3. Оптимизация модели с помощью GridSearchCV
Все алгоритмы машинного обучения имеют ряд гиперпараметров, которые влияют на то, как они строят модель. К ним относятся regularization parameters
, scaling values
, solver algorithms
, tree depth
и number of neighbors
, а также многие другие. Оптимальная настройка для любого из этих гиперпараметров редко бывает очевидной, поэтому необходимо повторять и измерять точность модели в диапазоне этих настроек. GridSearchCV
автоматизирует этот процесс, запуская репликацию cross-validations
всех комбинаций этих гиперпараметров, а затем выбирая набор гиперпараметров с наивысшей точностью модели.
Мы начнем со случайного разделения данных на тренировочный (80%) и тестовый (20%) наборы. GridSearchCV
также разделит входные обучающие данные на тестовый поезд и, таким образом, сообщенную точность в выборочной точности обучения. Чтобы сравнить и выбрать лучшую общую модель, нам также необходимо измерить точность вне выборки с помощью тестового набора. Мы также отфильтруем предупреждающие сообщения, чтобы ограничить распечатки только сообщениями о ходе выполнения.
#split train and test data sets from sklearn.model_selection import train_test_split X_train, X_test, Y_train, Y_test = train_test_split(X_z, Y, test_size=0.2, random_state=2) #ignore all warnings from warnings import simplefilter simplefilter(action='ignore')
Теперь мы можем приступить к оптимизации нашей первой модели. Начнем с Logistic Regression
. Всегда полезно просмотреть описания гиперпараметров в руководстве пользователя перед построением новой модели. Для нашего набора данных мы оптимизируем гиперпараметры C
, penalty
и solver
, установив для multi_class
значение multinomial
. Это делается с помощью словаря parameters
. Затем мы создаем объект GridSearchCV
, используя parameters
объект модели Logisitic Regression
и определенное количество cross-validations
(здесь мы используем 10). Наконец, мы оптимизируем нашу модель, подгоняя наши обучающие данные.
#train logistic regression from sklearn.model_selection import GridSearchCV from sklearn.linear_model import LogisticRegression parameters = {'C': np.logspace(-2, 0, 20), 'penalty': ['none', 'l2', 'l1', 'elasticnet'], 'solver': ['newton-cg', 'lbfgs', 'sag', 'saga'], 'multi_class': ['multinomial']} lr = LogisticRegression() grid_search = GridSearchCV(lr, parameters, cv=10, verbose=0) logreg_cv = grid_search.fit(X_train, Y_train)
Как только GridSearchCV
завершит оптимизацию нашей модели, мы можем проверить наши окончательные значения гиперпараметров, точность в выборке и точность вне выборки.
#test logistic regression print(“Tuned hpyerparameters (best parameters):”, logreg_cv.best_params_) print(“Train accuracy:”, logreg_cv.best_score_) print(“Test accuracy:”, logreg_cv.best_estimator_.score(X_test, Y_test))
Мы также можем извлечь оценки в выборке для каждой комбинации проверенных гиперпараметров, чтобы изучить, как каждый параметр влияет на точность модели.
#plot logreg scores logreg_cv_df = pd.DataFrame(logreg_cv.cv_results_[‘params’]) logreg_cv_df[‘score’] = logreg_cv.cv_results_[‘mean_test_score’] sns.lineplot(data=logreg_cv_df, x=’C’, y=’score’, hue=’penalty’) plt.show()
Наиболее точная модель Logistic Regression
была достигнута с параметрами l1 и l2 pentalty
и regularization
около 0,4. Тем не менее, при максимальной точности 70% это значительно уступает лучшей модели из оригинальной публикации (точность 89,63% достигается с моделью Random Forest
).
Давайте посмотрим, сможем ли мы получить лучший результат, используя K-Nearest Neighbors
.
#train k-nearest neighbors from sklearn.neighbors import KNeighborsClassifier parameters = {'n_neighbors': list(range(1, 20)), 'weights': ['uniform', 'distance'], 'algorithm': ['auto', 'ball_tree', 'kd_tree', 'brute'], 'p': [1,2]} knn = KNeighborsClassifier() grid_search = GridSearchCV(knn, parameters, cv=10, verbose=0) knn_cv = grid_search.fit(X_train, Y_train) #test k-nearest neighbors print("Tuned hpyerparameters (best parameters):", knn_cv.best_params_) print("Train accuracy:", knn_cv.best_score_) print("Test accuracy:", knn_cv.best_estimator_.score(X_test, Y_test))
#plot knn scores knn_cv_df = pd.DataFrame(knn_cv.cv_results_[‘params’]) knn_cv_df[‘score’] = knn_cv.cv_results_[‘mean_test_score’] sns.lineplot(data=knn_cv_df, x=’n_neighbors’, y=’score’, hue=’weights’, style=’algorithm’) plt.show()
Метод соседства weighting
оказался наиболее влиятельным гиперпараметром на точность модели K-Nearest Neighbors
. Тип вычислений algorithm
также играл большую роль при более низких значениях n_neighbor
, но его влияние уменьшалось выше примерно 10 соседей. Эта модель достигла вневыборочной точности 88,80%, что приближается к точности 89,63%, которой первоначальные авторы достигли с моделью Random Forest
.
Далее, давайте создадим файл Decision Tree
.
#train decision tree from sklearn.tree import DecisionTreeClassifier parameters = {'criterion': ['gini', 'entropy', 'log_loss'], 'splitter': ['best', 'random'], 'max_depth': [2*n for n in range(1,10)], 'max_features': ['auto', 'sqrt', 'log2'], 'min_samples_leaf': [1, 2, 4], 'min_samples_split': [2, 5, 10]} tree = DecisionTreeClassifier() grid_search = GridSearchCV(tree, parameters, cv=10, verbose=0) tree_cv = grid_search.fit(X_train, Y_train) #test decision tree print("Tuned hpyerparameters (best parameters):", tree_cv.best_params_) print("Train accuracy:", tree_cv.best_score_) print("Test accuracy:", tree_cv.best_estimator_.score(X_test, Y_test))
#plot decision tree scores tree_cv_df = pd.DataFrame(tree_cv.cv_results_[‘params’]) tree_cv_df[‘score’] = tree_cv.cv_results_[‘mean_test_score’] sns.lineplot(data=tree_cv_df, x=’max_depth’, y=’score’, hue=’min_samples_leaf’, style=’min_samples_split’) plt.show()
Модель Decision Tree
показала лучшие результаты с минимальными значениями min_samples_leaf
и min_samples_split
и высоким деревом max_depth
. Это указывает на то, что модель должна делать много небольших делений, чтобы классифицировать выборки, что согласуется с трудностью создания кластеров путем ординации. Модель по-прежнему достигла высокой точности 88,38%, хотя это глубокое, мелкозернистое переменное разделение не очень эффективно.
Теперь перейдем к Random Forest
, который является Decision Tree
«метаоценкой». Это также дало первоначальным авторам их лучший результат.
#train random forest from sklearn.ensemble import RandomForestClassifier parameters = {'criterion': ['gini', 'entropy', 'log_loss'], 'max_depth': [2*n for n in range(1,10)], 'max_features': ['auto', 'sqrt', 'log2'], 'min_samples_leaf': [1, 2, 4], 'min_samples_split': [2, 5, 10]} forest = RandomForestClassifier() grid_search = GridSearchCV(forest, parameters, cv=10, verbose=0) forest_cv = grid_search.fit(X_train, Y_train) #test random forest print("Tuned hpyerparameters (best parameters):", forest_cv.best_params_) print("Train accuracy:", forest_cv.best_score_) print("Test accuracy:", forest_cv.best_estimator_.score(X_test, Y_test))
#plot random forest scores forest_cv_df = pd.DataFrame(forest_cv.cv_results_[‘params’]) forest_cv_df[‘score’] = forest_cv.cv_results_[‘mean_test_score’] sns.lineplot(data=forest_cv_df, x=’max_depth’, y=’score’, hue=’min_samples_leaf’, style=’min_samples_split’) plt.show()
Наша модель Random Forest
, основанная на консенсусе голосов от 100 независимых деревьев решений, лишь незначительно улучшила точность исходной модели Decision Tree
. Он по-прежнему полагался на минимальные значения min_samples_leaf
и min_samples_split
, хотя и уменьшил max_depth
до 14. Удивительно, что наш Random Forest
оказался хуже по сравнению с исходной публикацией, хотя авторы не описали, какие гиперпараметры они выбрали, и мы не настроили все возможных параметров здесь.
Мы немного по-другому оптимизируем нашу модель Support Vector Machine
. SVM находят функцию гиперплоскости, чтобы максимизировать разделение между классами, добавляя к данным более высокую размерность в своего рода обратной ординации. Это вычислительно затратная модель по сравнению с другими, и моя машина (32 ГБ ОЗУ, 3600 МГц / 8 ЦП) остановилась при попытке запустить ее через GridSearchCV
сразу. Используемый здесь подход с вложенным циклом for представляет собой ручную итерацию GridSearchCV
, которая выполняет только часть общего поиска по сетке за раз. Это все еще заняло у моей машины 4 дня, поэтому я рекомендую включить некоторое количество сообщений о прогрессе с настройкой verbose
, чтобы сохранить ваше здравомыслие.
#train support vector machine from sklearn.svm import SVC svm_params = [] svm_scores = np.empty(0) for i in np.logspace(-3, 3, 10): for j in range(2,5): for k in np.logspace(-3, 1, 10): parameters = {'C': [i], 'kernel': ['linear', 'poly', 'rbf', 'sigmoid'], 'degree': [j], 'gamma': [k]} svm = SVC() grid_search = GridSearchCV(svm, parameters, cv=10, verbose=1) svm_cv = grid_search.fit(X_train, Y_train) svm_params = svm_params + svm_cv.cv_results_['params'] svm_scores = np.append(svm_scores, svm_cv.cv_results_['mean_test_score']) #test support vector machine print("Tuned hpyerparameters (best parameters):", svm_params[np.argmax(svm_scores)]) print("Train accuracy:", np.max(svm_scores)) svm_cv = SVC(C=svm_params[np.argmax(svm_scores)]['C'], degree=svm_params[np.argmax(svm_scores)]['degree'], gamma=svm_params[np.argmax(svm_scores)]['gamma'], kernel=svm_params[np.argmax(svm_scores)]['kernel']) svm_cv.fit(X_train, Y_train) print("Test accuracy:", svm_cv.score(X_test, Y_test))
#plot svm scores svm_cv_df = pd.DataFrame(svm_params) svm_cv_df['score'] = svm_scores from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_trisurf(svm_cv_df[(svm_cv_df['C']==0.001) & (svm_cv_df['kernel']=='poly')]['degree'], svm_cv_df[(svm_cv_df['C']==0.001) & (svm_cv_df['kernel']=='poly')]['gamma'], svm_cv_df[(svm_cv_df['C']==0.001) & (svm_cv_df['kernel']=='poly')]['score'], cmap=plt.cm.viridis, linewidth=0.2) ax.view_init(18,300) ax.set_xlabel('degree') ax.set_ylabel('gamma') ax.set_zlabel('score') plt.show()
Support Vector Machine
обеспечил лучшую классификацию с точностью 89,21%. Хотя это все еще немного хуже модели в оригинальной публикации. Наиболее важными гиперпараметрами были полиномиальные degree
и gamma
масштабирование.
Наконец, давайте посмотрим на модель Artificial Neural Net
.
#train artificial neural network from sklearn.neural_network import MLPClassifier parameters = {'activation': ['identity', 'logistic', 'tanh', 'relu'], 'solver': ['lbfgs', 'sgd', 'adam'], 'alpha': np.logspace(-5, 0, 10), 'learning_rate': ['adaptive'], 'max_iter': [1000]} ann = MLPClassifier() grid_search = GridSearchCV(ann, parameters, cv=10, verbose=0) ann_cv = grid_search.fit(X_train, Y_train) #test artificial neural network print("Tuned hpyerparameters (best parameters):", ann_cv.best_params_) print("Train accuracy:", ann_cv.best_score_) print("Test accuracy:", ann_cv.best_estimator_.score(X_test, Y_test))
#plot ann scores ann_cv_df = pd.DataFrame(ann_cv.cv_results_['params']) ann_cv_df['score'] = ann_cv.cv_results_['mean_test_score'] sns.lineplot(data=ann_cv_df, x='alpha', y='score', hue='solver', style='activation') plt.legend(loc='lower right') plt.show()
Artificial Neural Network
имел более высокую точность внутри выборки, чем Support Vector Machine
, но такую же точность вне выборки - 89,21%. Гиперпараметр solver
оказал наибольшее влияние на точность модели, при этом lbfgs показали лучшие результаты, особенно с функциями tanh и relu activation
. Это соответствует документации ANN, которая рекомендует lbfgs для более быстрой сходимости на небольших наборах данных.
GridSearchCV
также смог оптимизировать ИНС намного быстрее, чем SVM, за 1 час против 4 дней, что сделало ИНС более эффективным вариантом реализации для этого набора данных.
4. Окончательное сравнение моделей
Наконец, мы напрямую сравним точность моделей и построим матрицы путаницы, чтобы увидеть, в чем каждая модель превосходит другие модели.
#compare models print("Logistic Regression: {}\nKNN: {}\nDecision Tree: {}\nRandom Forest: {}\nSVM: {}\nANN: {}".format( logreg_cv.best_estimator_.score(X_test, Y_test), knn_cv.best_estimator_.score(X_test, Y_test), tree_cv.best_estimator_.score(X_test, Y_test), forest_cv.best_estimator_.score(X_test, Y_test), svm_cv.score(X_test, Y_test), ann_cv.best_estimator_.score(X_test, Y_test)))
#plot confusion matrices using model predictions from sklearn.metrics import confusion_matrix logreg_cm = confusion_matrix(Y_test, logreg_cv.predict(X_test)) knn_cm = confusion_matrix(Y_test, knn_cv.predict(X_test)) tree_cm = confusion_matrix(Y_test, tree_cv.predict(X_test)) forest_cm = confusion_matrix(Y_test, forest_cv.predict(X_test)) svm_cm = confusion_matrix(Y_test, svm_cv.predict(X_test)) ann_cm = confusion_matrix(Y_test, ann_cv.predict(X_test)) vmax = max(np.amax(logreg_cm), np.amax(knn_cm), np.amax(tree_cm), np.amax(forest_cm), np.amax(svm_cm), np.amax(ann_cm)) fig, axs = plt.subplots(2, 3, constrained_layout=True) sns.heatmap(logreg_cm, annot=True, cbar=False, ax=axs[0,0], cmap='mako', fmt='g', vmin=0, vmax=vmax) sns.heatmap(knn_cm, annot=True, cbar=False, ax=axs[1,0], cmap='mako', fmt='g', vmin=0, vmax=vmax) sns.heatmap(tree_cm, annot=True, cbar=False, ax=axs[0,1], cmap='mako', fmt='g', vmin=0, vmax=vmax) sns.heatmap(forest_cm, annot=True, cbar=False, ax=axs[1,1], cmap='mako', fmt='g', vmin=0, vmax=vmax) sns.heatmap(svm_cm, annot=True, cbar=False, ax=axs[0,2], cmap='mako', fmt='g', vmin=0, vmax=vmax) sns.heatmap(ann_cm, annot=True, cbar=False, ax=axs[1,2], cmap='mako', fmt='g', vmin=0, vmax=vmax) axs[0,0].set_title('Logistic Regression') axs[1,0].set_title('K-Nearest Neighbors') axs[0,1].set_title('Decision Tree') axs[1,1].set_title('Random Forest') axs[0,2].set_title('Support Vector Machine') axs[1,2].set_title('Artificial Neural Network') axs[0,0].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,0].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[0,1].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,1].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[0,2].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,2].xaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[0,0].yaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,0].yaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[0,1].yaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,1].yaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[0,2].yaxis.set_ticklabels(['Low', 'Mod', 'High']) axs[1,2].yaxis.set_ticklabels(['Low', 'Mod', 'High']) fig.supxlabel('Predicted Adaptability') fig.supylabel('True Adaptability') plt.show()
Здесь мы видим, что плохой результат Logistic Regression
является результатом склонности к бинарным предсказаниям, классифицируя адаптируемость всех, кроме одного учащегося, как низкую или умеренную несмотря на то, что параметр mutli_class
оптимизирован до multinomial
. Все остальные модели были почти идентичными, отличаясь только количеством недоклассифицированных умеренно адаптируемых учащихся.
Наконец, давайте сравним, как точность внутри и вне выборки меняется для каждой модели.
#compare model accuracies model_acc = {'Model': ['LogReg', 'KNN', 'Tree', 'Forest', 'SVM', 'ANN', 'LogReg', 'KNN', 'Tree', 'Forest', 'SVM', 'ANN'], 'Accuracy': [logreg_cv.best_score_, knn_cv.best_score_, tree_cv.best_score_, forest_cv.best_score_, np.max(svm_scores), ann_cv.best_score_, logreg_cv.best_estimator_.score(X_test, Y_test), knn_cv.best_estimator_.score(X_test, Y_test), tree_cv.best_estimator_.score(X_test, Y_test), forest_cv.best_estimator_.score(X_test, Y_test), svm_cv.score(X_test, Y_test), ann_cv.best_estimator_.score(X_test, Y_test)], 'Data': ['GridSearch', 'GridSearch', 'GridSearch', 'GridSearch', 'GridSearch', 'GridSearch', 'Test', 'Test', 'Test', 'Test', 'Test', 'Test']} model_df = pd.DataFrame(model_acc) sns.catplot(kind='bar', y='Accuracy', x='Model', hue='Data', data=model_df) plt.ylim(0.65, None) plt.xlabel('Model', fontsize=13) plt.ylabel('Accuracy', fontsize=13) plt.show()
Точность внутри выборки была выше, чем точность вне выборки для каждой отдельной модели, что свидетельствует о важности обучения и проверки моделей с использованием разных данных. Помимо Logistic Regression
, точность разных моделей была очень сопоставимой. Наш окончательный выбор модели сводился к выбору эффективности обучения между моделями Support Vector Machine
и Artificial Nueral Network
, хотя каждая из K-Nearest Neighbors
, Decision Tree
или Random Forest
была значительно быстрее, чем ИНС, и лишь незначительно менее точна в этом наборе данных. В зависимости от ваших вычислительных ресурсов, более высокая эффективность обучения может разумно повлиять на окончательный выбор модели в пользу любой из этих трех других.
5. Выводы
Как мы видим, GridSearchCV
— незаменимый инструмент для выбора модели машинного обучения, позволяющий быстро и удобно сравнивать гиперпараметры нескольких моделей. Однако для более ресурсоемких моделей, таких как Support Vector Machines
, иногда необходимо вручную выполнить итерацию GridSearchCV
, чтобы она завершилась. Это легко сделать с помощью for-loops.
Прежде чем остановиться на модели и развернуть ее, всегда рекомендуется провести перекрестную проверку точности вашей модели путем повторной выборки данных обучения и тестирования. Это можно сделать с помощью инструмента cross_val_score
Scikit-learn, о котором я расскажу в следующем посте.