Введение
Красное вино было частью социальных, религиозных и культурных мероприятий на протяжении сотен лет. Средневековые монастыри считали, что их монахи живут дольше отчасти из-за регулярного умеренного употребления вина. Красное вино производится путем дробления и брожения цельных темных ягод винограда.
Есть много видов красного вина, которые различаются по вкусу и цвету. Общие сорта включают шираз, мерло, каберне совиньон, пино нуар и зинфандель. Содержание алкоголя обычно колеблется в пределах 12–15%.
Объективный анализ
- как предсказать качество вина по имеющимся признакам?
- Чтобы предсказать качество вина, мы используем методы машинного обучения, такие как логистическая регрессия, случайный лес и деревья решений, для анализа данных и поиска наилучшего подхода к моделированию.
Набор данных
Набор данных состоит из 1599 строк и 12 столбцов. Тип данных всех переменных - float
Разбираясь в различных характеристиках вина, мы видим, что всего имеется 12 столбцов, включая окончательный параметр качества.
- Фиксированная кислотность: нелетучие кислоты, которые плохо испаряются.
- Летучие кислоты: высокое содержание уксусной кислоты в вине, что приводит к неприятному вкусу уксуса.
- Лимонная кислота действует как консервант, повышая кислотность. В небольших количествах придает вину свежесть и аромат.
- Остаточный сахар: количество сахара, оставшееся после остановки брожения. Главное, чтобы был идеальный баланс между сладостью и кислинкой. Важно отметить, что вина › 45 г/л сладкие.
- Хлориды: количество соли в вине.
- Свободный диоксид серы: предотвращает рост микробов и окисление вина.
- Общее количество диоксида серы: количество свободных и связанных форм SO2.
- Плотность: более сладкие вина имеют более высокую плотность.
- pH: описывает уровень кислотности по шкале от 0 до 14. Большинство вин всегда находятся в пределах 3–4 по шкале pH.
- Алкоголь: доступный в небольших количествах в винах, делает пьющих общительными.
- Сульфаты: добавка к вину, которая способствует повышению уровня SO2 и действует как антимикробное и антиоксидантное средство.
- Качество: выходная переменная/предиктор.
Проверка нулевых или отсутствующих значений
#Check null or missing value df.isnull().sum() output : fixed acidity 0 volatile acidity 0 citric acid 0 residual sugar 0 chlorides 0 free sulfur dioxide 0 total sulfur dioxide 0 density 0 pH 0 sulphates 0 alcohol 0 quality 0 dtype: int64
Похоже, что нет пропущенных значений. Это означает, что набор данных может быть обработан.
Визуализация данных
Распределение переменной целевого качества
sns.countplot(data=df, x= 'quality') plt.title('Distribution Variable Target') plt.show()
Визуализируйте содержание корреляции в красном вине
plt.figure(figsize=(20, 10)) sns.heatmap(df.corr(), annot=True) plt.title("Heatmap for Red Wine Quality") plt.show()
Рисунок выше: аналитика
- Алкоголь положительно коррелирует с качеством красного вина.
- Алкоголь имеет слабую положительную корреляцию со значением рН.
- Лимонная кислота и плотность имеют сильную положительную корреляцию с фиксированной кислотностью.
- pH имеет отрицательную корреляцию с плотностью, фиксированной кислотностью, лимонной кислотой и сульфатами.
Исследовательский анализ данных
Масштабирование функций
Масштабируйте набор данных по качеству
Прочитав описание набора данных качества красного вина, мы находим:
- качество ›= 7 – «хорошее»
- качество ‹= 7 — «плохое»
df['quality'] = df['quality'].apply(lambda x: 1 if x >= 7 else 0) sns.countplot(data = df, x = 'quality') plt.xticks([0,1], ['bad wine','good wine']) plt.title("Types of Wine") plt.show()
- из приведенной выше визуализации мы видим, что набор данных искажен или несбалансирован.
Повторная выборка набора данных
- для искаженного или несбалансированного набора данных мы можем выполнить повторную выборку, используя метод передискретизации синтетического меньшинства для балансировки данных.
#parameter for requires seed random_value = 1000 X = df.drop(['quality'], axis=1) y = df.quality oversample = SMOTE() X_ros, y_ros = oversample.fit_resample(X, y) sns.countplot(x=y_ros) plt.xticks([0,1], ['bad wine','good wine']) plt.title("Types of Wine") plt.show()
Предварительная обработка набора данных
- Разделить набор данных на поезд и тест
# split dataset to train and test variable # use test size of 20% of the data proportion X_train, X_test, y_train, y_test = train_test_split(X_ros, y_ros, test_size=0.2, random_state=random_value) X_train.shape, X_test.shape output: ((2211, 11), (553, 11))
- Масштабирование набора данных с помощью StandardScaler
# scale with StandardScaler scaler = StandardScaler() # fit to data training scaler.fit(X_train) # transform x_train = scaler.transform(X_train) x_test = scaler.transform(X_test)
Тренировочные модели
Логистическая регрессия
# Logistic Regression initialization logreg = LogisticRegression(class_weight='balanced', random_state=random_value) # Cross Validation logreg_score = cross_val_score(estimator = logreg, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 3, n_jobs=-1) # Fit data training logreg.fit(x_train, y_train) # Predict data test y_pred = logreg.predict(x_test) print('Avarage Recall score', np.mean(logreg_score)) print('Test Recall score', recall_score(y_test, y_pred)) [Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers. [Parallel(n_jobs=-1)]: Done 3 out of 10 | elapsed: 3.0s remaining: 7.2s [Parallel(n_jobs=-1)]: Done 7 out of 10 | elapsed: 3.2s remaining: 1.3s Avarage Recall score 0.8348402948402949 Test Recall score 0.8284671532846716 [Parallel(n_jobs=-1)]: Done 10 out of 10 | elapsed: 3.3s finished
- Основываясь на аналогичных результатах, полученных из оценок, можно сделать вывод, что производительность модели хорошая при прогнозировании целевой переменной.
# Confusion Matrix conf_mat = confusion_matrix(y_test, y_pred) # Heatmap Confusion Matrix sns.heatmap(conf_mat, cmap = 'Reds', annot = True, fmt='.1f') plt.title('Confusion Matrix dari Prediksi Logistic Regression') plt.xlabel('Predicted') plt.ylabel('Actual') plt.show()
Предсказание модели:
- модель правильно предсказала «0» 220 раз, а неправильно предсказала «0» 59 раз.
- Также он неправильно предсказал «1» 47 раз, а правильно предсказал «1» 227 раз.
Настройка гиперпараметров
# Grid parameters log_grid = { 'penalty': ['l1', 'l2'], 'C': [0.1, 1, 10], 'solver': ['liblinear'] } # Use RandomizedSearchCV logreg_cv = RandomizedSearchCV(estimator=logreg, param_distributions=log_grid, scoring='recall', cv=10) # Fit to model logreg_cv.fit(X_train, y_train) # Best Score print(f'Best score: {logreg_cv.best_score_}') print(f'Best params: {logreg_cv.best_params_}') output : Best score: 0.8348402948402949 Best params: {'solver': 'liblinear', 'penalty': 'l1', 'C': 10}
Сравнить балл
# Logistic Regression initialization logreg_tuned = LogisticRegression(**logreg_cv.best_params_,class_weight='balanced', random_state=random_value) # Cross Validation logreg_tuned_score = cross_val_score(estimator = logreg_tuned, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 0) # Fit data training logreg_tuned.fit(x_train, y_train) # Predict data test y_pred_tuned = logreg_tuned.predict(x_test) # Cek Score print('Avarage Recall score', np.mean(logreg_score)) print('Test Recall score', recall_score(y_test, y_pred)) print('Avarage Recall score Tuning', np.mean(logreg_tuned_score)) print('Test Recall score Tuning', recall_score(y_test, y_pred_tuned)) output : Avarage Recall score 0.8348402948402949 Test Recall score 0.8284671532846716 Avarage Recall score Tuning 0.833939393939394 Test Recall score Tuning 0.8284671532846716
- Судя по полученным результатам, производительность немного улучшилась.
Случайный лес
# Random Forest Regression initialization rfc = RandomForestClassifier(n_estimators=100, random_state=random_value) # Cross Validation rf_score = cross_val_score(estimator = rfc, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 3, n_jobs=-1) # Fit data training rfc.fit(x_train, y_train) # Predict data test y_pred = rfc.predict(x_test) print('Avarage Recall score', np.mean(rf_score)) print('Test Recall score', recall_score(y_test, y_pred)) output : [Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers. [Parallel(n_jobs=-1)]: Done 3 out of 10 | elapsed: 0.8s remaining: 1.9s [Parallel(n_jobs=-1)]: Done 7 out of 10 | elapsed: 0.8s remaining: 0.3s [Parallel(n_jobs=-1)]: Done 10 out of 10 | elapsed: 1.3s finished Avarage Recall score 0.9638738738738739 Test Recall score 0.9708029197080292
- Основываясь на аналогичных результатах, полученных из оценок, можно сделать вывод, что производительность модели хорошая при прогнозировании целевой переменной.
# Confusion Matrix conf_mat = confusion_matrix(y_test, y_pred) # Heatmap Confusion Matrix sns.heatmap(conf_mat, cmap = 'Reds', annot = True, fmt='.1f') plt.title('Confusion Matrix dari Prediksi Random Forest') plt.xlabel('Predicted') plt.ylabel('Actual') plt.show()
Предсказание модели:
- Наша модель правильно предсказала «0» 253 раза, а неверно предсказала «0» 26 раз.
- Также он неправильно предсказал «1» 8 раз, а правильно предсказал «1» 266 раз.
Настройка гиперпараметров
# Grid parameters rf_grid = { 'n_estimators': [50, 100, 200], 'max_depth': [3, 5, 10], 'min_samples_split': [2, 5, 10] } # Use RandomizedSearchCV rf_cv = RandomizedSearchCV(estimator=rfc, param_distributions=rf_grid, scoring='recall', cv=10) # Fit to model rf_cv.fit(X_train, y_train) # Best Score print(f'Best score: {rf_cv.best_score_}') print(f'Best params: {rf_cv.best_params_}') output : Best score: 0.9701801801801802 Best params: {'n_estimators': 100, 'min_samples_split': 5, 'max_depth': 10}
Сравнить балл
# Random Forest Regression initialization rf_tuned = RandomForestClassifier(**rf_cv.best_params_, random_state=random_value) # Cross Validation rf_tuned_score = cross_val_score(estimator = rf_tuned, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 0) # Fit data training rf_tuned.fit(x_train, y_train) # Predict data test y_pred_tuned = rf_tuned.predict(x_test) # Cek Score print('Avarage Recall score', np.mean(rf_score)) print('Test Recall score', recall_score(y_test, y_pred)) print('Avarage Recall score Tuning', np.mean(rf_tuned_score)) print('Test Recall score Tuning', recall_score(y_test, y_pred_tuned)) output : Avarage Recall score 0.9638738738738739 Test Recall score 0.9708029197080292 Avarage Recall score Tuning 0.9701801801801802 Test Recall score Tuning 0.9708029197080292
- Судя по полученным результатам, производительность немного улучшилась.
Дерево решений
# DecisionTree Regression initialization dtc = DecisionTreeClassifier(random_state=random_value) # Cross Validation dt_score = cross_val_score(estimator = dtc, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 3, n_jobs=-1) # Fit data training dtc.fit(x_train, y_train) # Predict data test y_pred = dtc.predict(x_test) print('Avarage Recall score', np.mean(dt_score)) print('Test Recall score', recall_score(y_test, y_pred)) output: Avarage Recall score 0.925962325962326 Test Recall score 0.9306569343065694 [Parallel(n_jobs=-1)]: Using backend LokyBackend with 8 concurrent workers. [Parallel(n_jobs=-1)]: Done 3 out of 10 | elapsed: 0.0s remaining: 0.0s [Parallel(n_jobs=-1)]: Done 7 out of 10 | elapsed: 0.0s remaining: 0.0s [Parallel(n_jobs=-1)]: Done 10 out of 10 | elapsed: 0.0s finished
- Основываясь на аналогичных результатах, полученных из оценок, можно сделать вывод, что производительность модели хорошая при прогнозировании целевой переменной.
# Confusion Matrix conf_mat = confusion_matrix(y_test, y_pred) # Heatmap Confusion Matrix sns.heatmap(conf_mat, cmap = 'Reds', annot = True, fmt='.1f') plt.title('Confusion Matrix dari Prediksi Decision Tree') plt.xlabel('Predicted') plt.ylabel('Actual') plt.show()
Предсказание модели:
- Наша модель правильно предсказала «0» 250 раз, а неверно предсказала «0» 29 раз.
- Также он неправильно предсказал «1» 19 раз, а правильно предсказал «1» 255 раз.
Настройка гиперпараметров
# Grid parameters dt_grid = { 'max_depth': [3, 5, 10], 'min_samples_split': [2, 5, 10] } # Use RandomizedSearchCV dt_cv = RandomizedSearchCV(estimator=dtc, param_distributions=dt_grid, scoring='recall', cv=10) # Fit to model dt_cv.fit(X_train, y_train) # Best Score print(f'Best score: {dt_cv.best_score_}') print(f'Best params: {dt_cv.best_params_}') output : Best score: 0.9295659295659296 Best params: {'min_samples_split': 2, 'max_depth': 10}
Сравнить балл
# DecisionTree Regression initialization dt_tuned = RandomForestClassifier(**dt_cv.best_params_, random_state=random_value) # Cross Validation dt_tuned_score = cross_val_score(estimator = dt_tuned, X = x_train, y= y_train, scoring = 'recall',cv = 10, verbose = 0) # Fit data training dt_tuned.fit(x_train, y_train) # Predict data test y_pred_tuned = dt_tuned.predict(x_test) # Cek Score print('Avarage Recall score', np.mean(dt_score)) print('Test Recall score', recall_score(y_test, y_pred)) print('Avarage Recall score Tuning', np.mean(dt_tuned_score)) print('Test Recall score Tuning', recall_score(y_test, y_pred_tuned)) output : Avarage Recall score 0.925962325962326 Test Recall score 0.9306569343065694 Avarage Recall score Tuning 0.9692710892710892 Test Recall score Tuning 0.9708029197080292
- Судя по полученным результатам, производительность немного улучшилась.
Заключение и будущие работы
- Мы можем построить довольно хорошую модель с довольно высокой точностью.
- Случайный лес и дерево решений — модели с очень хорошей производительностью и точностью.
- Получите больше данных для тестирования, чтобы наша модель могла учиться и создавать более точные прогнозы.
- Сделайте PCA, уменьшите количество функций данных и упростите процесс в нашей модели.
- Изучите другой метод и найдите наиболее подходящие решения для качества вина.
Ссылки
Набор данных:
Исходный код на GitHub: https://github.com/ariprachmaan/winepredict.git