Основная цель этого проекта — просмотреть набор данных о красном вине и проанализировать его атрибуты, затем я создам несколько моделей машинного обучения для прогнозирования качества красного вина, а затем сравню результаты.
Я выделю результат жирным шрифтом, чтобы он был понятен зрителям.
Импортировать необходимые библиотеки
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)
Что мы можем понять из корреляций?
- Качество вина сильно зависит от летучих кислот.
- Кроме того, качество вина сильно коррелирует с алкоголем.
- pH и лимонная кислота/постоянная кислотность сильно обратно пропорциональны, поскольку все мы знаем, что кислоты имеют меньшие значения pH.
- Самоотношение, т. е. признака к самому себе, равно 1, как и ожидалось.
- можно сделать и другие аналогичные выводы.
Некоторые визуализации
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)