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

Таблица содержания

  1. "Вступление"
  2. Исследовательский анализ
  3. Предварительная обработка данных
  4. Валидация и выбор модели
  5. "Заключение"

Введение

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

В этом проекте я хотел сравнить несколько алгоритмов классификации, чтобы предсказать качество вина, которое имеет оценку от 0 до 10. Поскольку мне нравится белое вино больше, чем красное, я решил сравнить и выбрать алгоритм, чтобы выяснить, что делает хорошее вино, используя Данные winequality-white.csv взяты из Репозитория машинного обучения UCI. Атрибуты в этом наборе данных:

  • фиксированная кислотность
  • летучая кислотность
  • лимонная кислота
  • остаточный сахар
  • хлориды
  • свободный диоксид серы
  • общий диоксид серы
  • плотность
  • pH
  • сульфаты
  • алкоголь
  • качественный

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

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

Импорт библиотек и модулей

import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns
import pandas as pd
import numpy as np
from sklearn import metrics

Чтение данных

white_wines = pd.read_csv('winequality/winequality-white.csv')

Понимание данных

Чтобы понять данные, я взглянул на первые пару строк. Это кажется довольно чистым, но это 4898 строк данных, и было бы неразумно проверять все строки, просто взглянув на них, чтобы увидеть, есть ли проблемы с данными. Поэтому на следующем шаге я сразу проверю все недостающие значения.

print(white_wines.shape)
white_wines.head()

Проверка отсутствующих значений

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

white_wines.isnull().any()

fixed acidity           False
volatile acidity        False
citric acid             False
residual sugar          False
chlorides               False
free sulfur dioxide     False
total sulfur dioxide    False
density                 False
pH                      False
sulphates               False
alcohol                 False
quality                 False
dtype: bool

Обнаружение выбросов

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

Давайте посмотрим на столбец «остаточный сахар». count говорит, что в этом столбце 4898 строк. В то время как среднее и стандартное означают соответственно среднее значение и стандартное отклонение столбца, 25% значений меньше 1,70 и 75. % из них меньше 9,90. Интересно то, что при среднем значении 6,39 и минимальном значении 0,60 максимальное значение 65,80. Похоже на выброс.

white_wines.describe()

Я использовал коробчатую диаграмму, чтобы визуализировать распределение значений в столбце «остаточный сахар», чтобы лучше понять. Фактическое максимальное значение составляет около 20, а значения выше этого значения являются выбросами, поскольку они не включены в поле наблюдения.

sns.boxplot(white_wines[‘residual sugar’])

Устранение выбросов с помощью Z-показателя

В большинстве случаев важно удалить выбросы, поскольку они, скорее всего, повлияют на производительность моделей машинного обучения. Но предположим, что 30% вашего набора данных - выбросы. Тогда может быть неразумно удалять их все, потому что, вероятно, происходит что-то еще, и это требует дальнейшего изучения. Чтобы найти и удалить выбросы, я использовал z-оценку.

Его интерпретация заключается в том, чтобы взять точку данных или наблюдение, вычесть среднее значение генеральной совокупности и разделить его на стандартное отклонение. Он показывает, на сколько стандартных отклонений точка данных находится от среднего значения. Точки данных, которые слишком далеки от среднего значения, считаются выбросами. В большинстве случаев порог обнаружения выбросов равен z-баллу ›3 или z-баллу‹ -3. Для этого я использовал функцию zscore (), определенную в библиотеке SciPy, и установил порог = 3. После удаления выбросов в наборе данных осталось 4487 строк, что означает, что около 8,4% набора данных было удалено как выбросы.

z = np.abs(stats.zscore(white_wines))
white_wines = white_wines[(z < 3).all(axis=1)]
white_wines.shape
(4487, 12)

Проверка корреляции между атрибутами

Когда я проверил корреляцию между столбцами, я увидел, что некоторые функции сильно коррелируют с качеством, а некоторые - нет.

plt.subplots(figsize=(15, 10))
sns.heatmap(white_wines.corr(), annot = True, cmap = ‘coolwarm’)

Проверка классового дисбаланса

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

white_wines[‘quality’].value_counts()
6    2038
5    1309
7     855
8     161
4     124
Name: quality, dtype: int64

Предварительная обработка данных

На этом этапе анализа я определил функции для обучения и тестирования модели машинного обучения и цель для прогнозирования «качества». А затем я провел стандартизацию (также называемую нормализацией z-показателя) для функций, потому что разные масштабы функций могут влиять на производительность моделей машинного обучения. Для этого я использовал функцию StandardScaler (), определенную в Scikit-learn. И, наконец, я разделил набор данных на обучающий и тестовый наборы 80% и 20% соответственно.

Определение функций и цели

# Define features X
X = np.asarray(white_wines.iloc[:,:-1])
# Define target y
y = np.asarray(white_wines[‘quality’])

Стандартизация набора данных

from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)

Разделение наборов для обучения и тестирования

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=0)
print (‘Train set:’, X_train.shape, y_train.shape)
print (‘Test set:’, X_test.shape, y_test.shape)
Train set: (3589, 11) (3589,)
Test set: (898, 11) (898,)

Проверка и выбор модели

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

K-Ближайшие соседи

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

График точности KNN

Я создал график, чтобы увидеть, как точность меняется с числом К.

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score
# Number of k from 1 to 26
k_range = range(1, 26)
k_scores = []
# Calculate cross validation score for every k number from 1 to 26
for k in k_range:
 knn = KNeighborsClassifier(n_neighbors=k)
# It’s 10 fold cross validation with ‘accuracy’ scoring 
scores = cross_val_score(knn, X, y, cv=10, scoring=’accuracy’) 
 k_scores.append(scores.mean())
%matplotlib inline
# Plot accuracy for every k number between 1 and 26
plt.plot(k_range, k_scores)
plt.xlabel('Value of K for KNN')
plt.ylabel('Cross-validated accuracy')

Перекрестная проверка для KNN

Я решил использовать k = 19, так как с его помощью получилась одна из самых высоких точности. И обучил модель и рассчитал точность с помощью различных методов проверки.

# Train the model and predict for k=19
knn = KNeighborsClassifier(n_neighbors=19)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_auc_score
# classification report for test set
print(metrics.classification_report(y_test, y_pred, digits=3, zero_division = 1))
# Calculate cv score with 'accuracy' scoring and 10 folds
accuracy = cross_val_score(knn, X, y, scoring = 'accuracy',cv=10)
print('cross validation score',accuracy.mean())
# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(knn, X, y, scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc',accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,knn.predict_proba(X_test), multi_class='ovr'))

Когда я смотрю на отчет о классификации, я сразу вижу, что классы 4 и 8 не принимались во внимание при обучении, потому что их результаты вспоминания равны нулю. Это означает, что из всех членов класса 4 и класса 8 он не предсказал правильно ни одного из них. Таким образом, это не подходящая модель для нашего набора данных.

Логистическая регрессия

Логистическая регрессия на самом деле представляет собой алгоритм двоичной классификации, который можно использовать для таких вопросов, как Да / Нет, Истина / Ложь и т. Д.

В этом случае это позволяет нам использовать его для задач классификации нескольких классов, таких как наша. Поскольку в нашем наборе данных есть 5 классов для прогнозирования качества. Чтобы использовать его в качестве алгоритма классификации нескольких классов, я использовал параметры multi_class = ’multinomial’, solver = ’newton-cg’.

Учитывая, что это проблема классификации нескольких классов, я использовал параметр оценки «roc_auc_ovr» вместо «точности» при вычислении оценки перекрестной проверки. Я также рассчитал roc_auc_score с параметром multi_class = ’ovr’. Я объясню это позже в заключение.

# import module
from sklearn.linear_model import LogisticRegression
# Train and fit model
logreg = LogisticRegression(multi_class=’multinomial’,solver =’newton-cg’)
logreg.fit(X_train, y_train)
# Predict out-of-sample test set
y_pred = logreg.predict(X_test)
# classification report
print(metrics.classification_report(y_test, y_pred, digits=3, zero_division = 1))
print(‘accuracy’,accuracy_score(y_test, y_pred))
# Calculate cv score with ‘roc_auc_ovr’ scoring and 10 folds
accuracy = cross_val_score(logreg, X, y, scoring = ‘roc_auc_ovr’,cv=10)
print(‘cross validation score with roc_auc’,accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print(‘roc_auc_score’,roc_auc_score(y_test,logreg.predict_proba(X_test), multi_class=’ovr’))

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

Добавление полиномиальных функций к логистической регрессии

from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
# Add polynomial features to the logistic regression model
def PolynomialRegression(degree=2, **kwargs):
 return make_pipeline(PolynomialFeatures(degree),
 LogisticRegression(multi_class=’multinomial’,solver =’newton-cg’, **kwargs))

Теперь я попытался добавить полиномиальные функции 3-й степени в модель логистической регрессии.

# Train and fit the 3rd degree polynomial regression model
poly = PolynomialRegression(3)
poly.fit(X_train,y_train)
# Test out-of-sample test set
y_pred = poly.predict(X_test)
# Classification report
print(metrics.classification_report(y_test, y_pred, digits=3))
# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(poly, X, y, scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc_ovr scoring',accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,poly.predict_proba(X_test), multi_class='ovr'))

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

Древо решений

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

from sklearn.tree import DecisionTreeClassifier
# Train and fit the Decision Tree Classification model
tree = DecisionTreeClassifier(random_state=0)
tree.fit(X_train, y_train)
# Evaluate the model with out-of-sample test set
y_pred = tree.predict(X_test)
# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))
# Calculate cv score with ‘roc_auc_ovr’ scoring and 10 folds
accuracy = cross_val_score(tree, X, y,scoring = ‘roc_auc_ovr’,cv=10)
print(‘cross validation score with roc_auc_ovr scoring’,accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print(‘roc_auc_score’,roc_auc_score(y_test,tree.predict_proba(X_test), multi_class=’ovr’))

Случайный лес

Случайный лес - это метод ансамблевого обучения, который строит несколько деревьев решений, а затем получает прогноз, основанный на том, что предсказывает большинство деревьев решений. Мне нравится думать об этом как о посещении нескольких врачей по поводу проблемы со здоровьем и принятии решения, следует ли вам делать операцию или нет, в зависимости от того, что говорят большинство врачей.

Итак, давайте посмотрим на результаты модели случайного леса.

from sklearn.ensemble import RandomForestClassifier
# Train and fit the Random Forest Classification model
forest = RandomForestClassifier(n_estimators=100,random_state = 0)
forest.fit(X_train, y_train)
# Test out-of-sample test set
y_pred = forest.predict(X_test)
# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))
# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(forest, X, y,scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc_ovr scoring',accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,forest.predict_proba(X_test), multi_class='ovr'))

Пока что это лучший вариант! Roc_auc_score довольно хорош, оценка перекрестной проверки пока лучшая, и есть некоторые результаты отзыва даже для классов меньшинств. Но этого пока недостаточно. Так что одна из вещей, которые можно сделать для увеличения полноты информации, - это передискретизация классов меньшинств. Для этого я использовал алгоритм случайного леса с реализацией алгоритма SMOTE.

Добавление алгоритма SMOTE

Алгоритм SMOTE (синтетическая передискретизация меньшинства) создает синтетические выборки классов меньшинств для увеличения представления классов меньшинств.

# Import SMOTE module
from imblearn.over_sampling import SMOTE
# Create model and fit the training set to create a new training set
sm = SMOTE(random_state = 2) 
X_train_res, y_train_res = sm.fit_sample(X_train, y_train.ravel())
# Create random forest model
forest = RandomForestClassifier(n_estimators=100,random_state = 0)
# Fit the model to the new train set
forest.fit(X_train_res, y_train_res.ravel())
# # Test out-of-sample test set
y_pred = forest.predict(X_test)
# Classification report
print(metrics.classification_report(y_test, y_pred.round(), digits=3))
# Calculate cv score with 'roc_auc_ovr' scoring and 10 folds
accuracy = cross_val_score(forest, X, y,scoring = 'roc_auc_ovr',cv=10)
print('cross validation score with roc_auc_ovr scoring',accuracy.mean())
# Calculate roc_auc score with multiclass parameter
print('roc_auc_score',roc_auc_score(y_test,forest.predict_proba(X_test), multi_class='ovr'))

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

Заключение

В этом проекте я использовал K-ближайших соседей, логистическую регрессию с полиномиальными функциями, дерево решений и случайный лес. Используя roc_auc_score в Scikit-learn, я рассчитал оценку AUC для каждой модели. Также, используя метод cross_val_score, я нашел оценку AUC, используя метод перекрестной проверки, передав параметр roc_auc_ovr. Несмотря на то, что он обычно используется для оценки производительности в двоичной классификации, с подходом One-vs-Rest я применил его к проблеме классификации нескольких классов. Оценка модели с помощью этого метода полезна при большом дисбалансе. Также не требуется устанавливать порог классификации.

Если мы сравним баллы перекрестной проверки и вспомним результаты всех моделей, то увидим, что наилучшие результаты были получены с помощью Классификатора случайного леса с методом SMOTE с баллом перекрестной проверки 0,7565. Поскольку у нас были сильно несбалансированные классы, SMOTE создал синтетическую выборку меньшинства, чтобы сбалансировать выборки. И обучил модель, как если бы у нас были сбалансированные классы в нашем наборе данных.

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

Спасибо за чтение!

Если у вас есть отзывы или исправления, не стесняйтесь обращаться ко мне через мой LinkedIn!