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

Я выделю результат жирным шрифтом, чтобы он был понятен зрителям.

Импортировать необходимые библиотеки

import warnings
warnings.filterwarnings('ignore')
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Preprocessing
from scipy import stats
from sklearn.model_selection import train_test_split, cross_validate, StratifiedShuffleSplit, GridSearchCV
from sklearn.preprocessing import MinMaxScaler, StandardScaler, LabelEncoder
from sklearn.decomposition import PCA

# Machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC, SVC
from sklearn.neighbors import KNeighborsClassifier, LocalOutlierFactor
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier, BaggingClassifier, VotingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB

# Model evaluation
from sklearn import metrics
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score

Набор данных, который я использовал

https://www.kaggle.com/uciml/red-wine-quality-cortez-et-al-2009

Прочитать набор данных

df=pd.read_csv('winequality-red.csv')
print(f'there are {df.shape[0]} number of rows')
there are 1599 number of rows

Число столбцов

print(f'there are {df.shape[1]} number of columns')
there are 12 number of columns

Опишите функцию

df.describe()

df.columns # the quality is the target variable that we have to predict.
Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar', 'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density', 'pH', 'sulphates', 'alcohol', 'quality'], dtype='object')

Информационная функция

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1599 entries, 0 to 1598
Data columns (total 12 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   fixed acidity         1599 non-null   float64
 1   volatile acidity      1599 non-null   float64
 2   citric acid           1599 non-null   float64
 3   residual sugar        1599 non-null   float64
 4   chlorides             1599 non-null   float64
 5   free sulfur dioxide   1599 non-null   float64
 6   total sulfur dioxide  1599 non-null   float64
 7   density               1599 non-null   float64
 8   pH                    1599 non-null   float64
 9   sulphates             1599 non-null   float64
 10  alcohol               1599 non-null   float64
 11  quality               1599 non-null   int64  
dtypes: float64(11), int64(1)
memory usage: 150.0 KB

Проверка пропущенных значений

percent_missing = df.isnull().sum() * 100 / len(df)
missing_value_df = pd.DataFrame({'column_name': df.columns, 'percent_missing': percent_missing})
missing_value_df.sort_values('percent_missing', inplace=True)
missing_value_df

Распределение переменных

df.describe(include='all')

df.hist(figsize=(20,20))

Корреляции

plt.figure(figsize=(15,15))
sns.heatmap(df.corr(),square=True,annot=True,cbar=True)

Что мы можем понять из корреляций?

  1. Качество вина сильно зависит от летучих кислот.
  2. Кроме того, качество вина сильно коррелирует с алкоголем.
  3. pH и лимонная кислота/постоянная кислотность сильно обратно пропорциональны, поскольку все мы знаем, что кислоты имеют меньшие значения pH.
  4. Самоотношение, т. е. признака к самому себе, равно 1, как и ожидалось.
  5. можно сделать и другие аналогичные выводы.

Некоторые визуализации

sns.factorplot(x='quality',y='fixed acidity',data=df,kind='bar',size=5,aspect=1)
sns.factorplot(x='quality',y='fixed acidity',data=df,kind='violin',size=5,aspect=1)

sns.factorplot(x='quality',y='alcohol',data=df,kind='bar',size=5,aspect=1)
sns.factorplot(x='quality',y='alcohol',data=df,kind='violin',size=5,aspect=1)

sns.factorplot(x='quality',y='chlorides',data=df,kind='bar',size=5,aspect=1)

sns.factorplot(x='quality',y='free sulfur dioxide',data=df,kind='bar',size=5,aspect=1)

plt.figure(figsize=(30,30))
ax = sns.boxplot(data=df, orient="h", palette="Set2")

# create distplot
fig,ax=plt.subplots(ncols=6,nrows=2,figsize=(20,10))
index=0
ax=ax.flatten()

for col,value in df.items():
    sns.distplot(value,ax=ax[index])
    index+=1
plt.tight_layout(pad=0.5,w_pad=0.7,h_pad=5.0)

df.corrwith(df.quality).plot.bar(
        figsize = (20, 10), title = "Correlation with quality", fontsize = 15, rot = 45, grid = True)

Часть машинного обучения

df['quality'].value_counts()
5    681
6    638
7    199
4     53
8     18
3     10
Name: quality, dtype: int64

Создание функции для перечисления качеств

def chan(lis):
    if lis<=5:
        return 0
    else:
        return 1

Применение функции к столбцу качества

df['quality']=df['quality'].map(chan)
label_quality = LabelEncoder()
#Bad becomes 0 and good becomes 1 
df['quality'] = label_quality.fit_transform(df['quality'])

Окончательный набор данных

df=df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]
df

X=df.drop(['quality'],axis=1)
y=df['quality']

#Applying Standard scaling to get optimized result
sc = StandardScaler()
X = sc.fit_transform(X)

pca = PCA(n_components=.98)
X=pca.fit_transform(X)

Разделение набора данных

split = StratifiedShuffleSplit( test_size=0.2, random_state=42)
for train_index,test_index in split.split(df,df["quality"]):
    strat_train_set = df.iloc[train_index,:]
    strat_test_set = df.iloc[test_index,:]
X_train=strat_train_set.iloc[:,:-1]
y_train=strat_train_set.iloc[:,-1]
X_test=strat_test_set.iloc[:,:-1]
y_test=strat_test_set.iloc[:,-1]

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

# Creating the model:
rf = RandomForestClassifier() 
params = { 
    'n_estimators': [200, 500],
    'max_features': ['auto', 'sqrt', 'log2'],
    'max_depth' : [4,5,6,7,8],
    'criterion' :['gini', 'entropy']
}

gs = GridSearchCV(rf, param_grid= params, cv = 5,scoring = "roc_auc",verbose=1)
gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_estimator_)
print(gs.best_params_)
Fitting 5 folds for each of 60 candidates, totalling 300 fits
0.8553444427961562
RandomForestClassifier(max_depth=8, max_features='sqrt', n_estimators=500)
{'criterion': 'entropy', 'max_depth': 8, 'max_features': 'sqrt', 'n_estimators': 500}

Оптимальные характеристики Random Forest

rf =RandomForestClassifier(criterion='entropy', max_depth=8, max_features='sqrt', n_estimators=500)
rf.fit(X_train, y_train)
y_predict_rf = rf.predict(X_test)
# confusion_matrix
cm = confusion_matrix(y_test, y_predict_rf)
sns.heatmap(cm, annot=True, fmt="d")
<AxesSubplot:>

print('accuracy',accuracy_score((y_test), y_predict_rf))
print('precision',precision_score(y_test, y_predict_rf))
print('recall',recall_score(y_test, y_predict_rf))
accuracy 0.839041095890411
precision 0.8827586206896552
recall 0.810126582278481

Классификатор упаковки

n_samples = X.shape[0]
n_features = X.shape[1]
bagging = BaggingClassifier() 
params = {
          'n_estimators': [20,50, 100],
          'max_samples': [0.5, 1.0, n_samples// 2],
          'max_features': [0.5, 1.0, n_features// 2],
          'bootstrap': [True, False],
          'bootstrap_features': [True, False]}

gs = GridSearchCV(bagging, param_grid= params, cv = 5, scoring = "roc_auc", verbose=1)
gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_estimator_)
print(gs.best_params_)
Fitting 5 folds for each of 108 candidates, totalling 540 fits
0.8710936244210388
BaggingClassifier(bootstrap=False, bootstrap_features=True, max_samples=729, n_estimators=100)
{'bootstrap': False, 'bootstrap_features': True, 'max_features': 1.0, 'max_samples': 729, 'n_estimators': 100}

Оптимальные характеристики классификатора мешков

bagging = BaggingClassifier(bootstrap= False, bootstrap_features= True, max_features=1.0, max_samples=729, n_estimators= 100)
bagging.fit(X_train, y_train)
y_predict_bagging = bagging.predict(X_test)
# confusion_matrix
cm = confusion_matrix(y_test, y_predict_bagging)
sns.heatmap(cm, annot=True, fmt="d")
<AxesSubplot:>

print('accuracy',accuracy_score((y_test), y_predict_bagging))
print('precision',precision_score(y_test, y_predict_bagging))
print('recall',recall_score(y_test, y_predict_bagging))
accuracy 0.8424657534246576
precision 0.8636363636363636
recall 0.8417721518987342

Классификатор повышения градиента

knn =GradientBoostingClassifier()
knn.fit(X_train, y_train)
y_predict_knn= knn.predict(X_test)
# confusion_matrix
cm = confusion_matrix(y_test, y_predict_knn)
sns.heatmap(cm, annot=True, fmt="d")
<AxesSubplot:>

print('accuracy',accuracy_score((y_test), y_predict_knn))
print('precision',precision_score(y_test, y_predict_knn))
print('recall',recall_score(y_test, y_predict_knn))
accuracy 0.8047945205479452
precision 0.8389261744966443
recall 0.7911392405063291

Классификатор дерева решений и другие модели

models = list()
models.append(('dt', DecisionTreeClassifier()))
models.append(('rf',RandomForestClassifier()))
models.append(('bagging', BaggingClassifier(bootstrap= True,bootstrap_features= True,max_features=1.0, max_samples= 1.0, n_estimators= 100)))
models.append(('gb', GradientBoostingClassifier()))

Определить ансамбль жесткого голосования

ensemble = VotingClassifier(estimators=models, voting='hard')
ensemble.fit(X_train, y_train)
y_predict_ensemble= ensemble.predict(X_test)
#confusion_matrix
cm = confusion_matrix(y_test, y_predict_ensemble)
sns.heatmap(cm, annot=True, fmt="d")
<AxesSubplot:>

print('accuracy',accuracy_score((y_test), y_predict_ensemble))
print('precision',precision_score(y_test, y_predict_ensemble))
print('recall',recall_score(y_test, y_predict_ensemble))
accuracy 0.8561643835616438
precision 0.9027777777777778
recall 0.8227848101265823

Сравнение моделей

fpr_rf, tpr_rf, thresholds_rf = metrics.roc_curve(y_test, y_predict_rf)
fpr_bg, tpr_bg, thresholds_bg = metrics.roc_curve(y_test, y_predict_bagging)
fpr_gb, tpr_gb, thresholds_gb = metrics.roc_curve(y_test, y_predict_knn)
fpr_voting, tpr_voting, thresholds_voting = metrics.roc_curve(y_test, y_predict_ensemble)
plt.plot(fpr_rf, tpr_rf, label="Random Forest")
plt.plot(fpr_bg, tpr_bg, label="Bagging classifier")
plt.plot(fpr_gb, tpr_gb, label="gradient bossting classifier")
plt.plot(fpr_voting, tpr_voting, label="voting classifier")
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.rcParams['font.size'] = 12
plt.title('ROC curve')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity)')
plt.legend(loc="lower right", fontsize=10)
plt.grid(True)

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

Спасибо за просмотр моей работы :D