Введение

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

Цель

Наша цель — эффективно классифицировать грибы как съедобные или ядовитые на основе атрибутов набора данных. Для этого мы будем:

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

Установленные библиотеки

Для проведения нашего анализа мы будем использовать следующие библиотеки:

# import all the required libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.model_selection import train_test_split, KFold
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVC
from collections import defaultdict
from sklearn.feature_selection import chi2, SelectKBest, RFE, SelectFromModel, mutual_info_classif
from sklearn.metrics import mean_squared_error, r2_score, confusion_matrix, accuracy_score
from xgboost import XGBClassifier
from sklearn.ensemble import RandomForestClassifier
import shap
import h2o
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import SimpleImputer, IterativeImputer, KNNImputer

Чтение CSV-файла

# Reading the dataset
data = pd.read_csv('mushrooms.csv')

Исследовательский анализ данных (EDA)

Мы начали наше исследование с EDA в наборе данных UCI Mushroom. Этот процесс позволил нам лучше понять набор данных и извлечь ценную информацию. Во время EDA мы обнаружили:

  1. Набор данных не содержал нулевых значений, что указывает на хорошее качество данных.
# check for the null values
data.isnull().any()

2. Набор данных был сбалансированным, с примерно равным количеством съедобных и ядовитых грибов.

# Lets check the balance of the dataset
counts = data['class'].value_counts()
x_axis = counts.index
y_axis = counts.values

plt.figure(figsize=(8, 5))
sns.barplot(x=x_axis, y=y_axis)

3. После изучения распределения независимой переменной было замечено, что признаки «вуалевидность» и «жаберность» содержат значительное количество уникальных значений. Это указывает на возможность проблем корреляции между этими функциями, которые следует дополнительно изучить, чтобы обеспечить точный анализ и моделирование.

С помощью EDA мы также сделали несколько важных наблюдений:

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

Горячее кодирование

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

# One hot encode the categorical variable 
one_hot_data = pd.get_dummies(data)

n = one_hot_data[['class_e', 'class_p']]   # The label for the machine learning models
M = one_hot_data.drop(['class_e', 'class_p'], axis=1)   #Features

M

Анализ тепловой карты

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

  • Жаберное приложение
  • Кольцевой
  • жаберный цвет
  • синяки

Выбор функции

Чтобы еще больше уточнить наш набор данных, мы выполнили выбор признаков с использованием регрессии обычных наименьших квадратов (OLS). На основе нашего анализа мы выявили несколько несущественных особенностей:

# Looking for most relavant features
import statsmodels.api as sm
independent_variables_for_ols = list(data.columns)
model = sm.OLS(data['class'], data[list(independent_variables_for_ols)]).fit()

# Print out the statistics
model.summary()

Учитывая значение точности 0,05

  • Население (значение p: 0,19)
  • Форма крышки (значение p: 0,22)
  • Форма стебля (значение p: 0,19)
  • Цвет стебля под кольцом (значение p: 0,049)

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

Обучение модели

Мы разделили наш набор данных, используя размер теста 0,2.

# X test data and x train data
X_train, X_test, y_train, y_test = train_test_split(M, n, test_size=0.2)
X_train.head()

МЫ обучили три разные модели: XGBoost, машины опорных векторов (SVM) и деревья решений. Удивительно, но все три модели достигли 100% точности. Мы использовали матрицу путаницы для визуализации результатов.

  1. XGBКлассификатор
# XG boost using the Classifiers
XGB = XGBClassifier(learning_rate=0.005, max_depth=10, n_estimators=30,
                      colsample_bytree=0.3, min_child_weight=0.5, reg_alpha=0.3,
                      )
XGB.fit(X_train, y_train["class_e"])
xgb_prediction = XGB.predict(X_test)
xgb_linear = accuracy_score(y_test['class_e'], xgb_prediction)
print('Accuracy of SVM Model: %', 100 * xgb_linear)

predictions_rf = pd.DataFrame(LR.predict(X_test))
plt.figure(figsize=(12, 8))
conf_mat = confusion_matrix(y_true=y_test['class_e'], y_pred=predictions_rf[0])
sns.heatmap(conf_mat, annot=True, fmt='g')
plt.title('Confusion Matrix of the Test Data (Edible Class Based)', fontsize=14)
plt.ylabel('Real Class', fontsize=12)
plt.xlabel('Predicted Class', fontsize=12)
plt.show()

2. Классификатор SVM

# Classification using one hot encoded data
clf = SVC(kernel='linear', probability=True)
clf.fit(X_train, y_train['class_e'])

predictions_svm = clf.predict(X_test)

acc_svm = accuracy_score(y_test['class_e'], predictions_svm)
print('Accuracy of SVM Model: %', 100 * acc_svm)

predictions_rf = pd.DataFrame(clf.predict(X_test))
plt.figure(figsize=(12, 8))
conf_mat = confusion_matrix(y_true=y_test['class_e'], y_pred=predictions_rf[0])
sns.heatmap(conf_mat, annot=True, fmt='g')
plt.title('Confusion Matrix of the Test Data (Edible Class Based)', fontsize=14)
plt.ylabel('Real Class', fontsize=12)
plt.xlabel('Predicted Class', fontsize=12)
plt.show()

3. Классификатор на основе дерева

# XG boost using the Classifiers
dec = DecisionTreeClassifier()
dec.fit(X_train, y_train["class_e"])
tree_prediction = dec.predict(X_test)
accuracy_tree = accuracy_score(y_test['class_e'], tree_prediction)
print('Accuracy of Decision Tree Model: %', 100 * accuracy_tree)

predictions_rf = pd.DataFrame(tree.predict(X_test))
plt.figure(figsize=(12, 8))
conf_mat = confusion_matrix(y_true=y_test['class_e'], y_pred=predictions_rf[0])
sns.heatmap(conf_mat, annot=True, fmt='g')
plt.title('Confusion Matrix of the Test Data (Edible Class Based)', fontsize=14)
plt.ylabel('Real Class', fontsize=12)
plt.xlabel('Predicted Class', fontsize=12)
plt.show()

Чтобы найти лучшую модель, мы использовали H2OAutoML с максимум 25 моделями, максимальным временем выполнения 800 секунд и начальным значением 1. Затем мы создали таблицу лидеров, отображающую производительность каждой модели, что позволило нам сравнить и выбрать наиболее подходящий для нашей задачи.

from h2o.automl import H2OAutoML
from h2o.estimators import H2OXGBoostEstimator
# Convert the current data frame into H2O dataframe
hf = h2o.H2OFrame(data)

hf['class'] = hf['class'].asfactor()
predictor_variables = hf.drop('class').columns

# Train test split
train, valid = hf.split_frame(ratios=[0.8], seed=1234)
train

aml_model = H2OAutoML(max_models=25, max_runtime_secs=800, seed=1)
aml_model.train(x=predictor_variables, y='class', training_frame=train, validation_frame=valid)

leader_board = aml_model.leaderboard
leader_board.head(rows=leader_board.nrows)

График AUC

# plot the bar graph of AUC of each model 
lb = leader_board.as_data_frame()
lb['model_type'] = lb['model_id'].apply(lambda x: x.split('_')[0])
fig = px.bar(
    lb, 
    x='model_id',
    y='auc',
    color='model_type'
)
fig.update_yaxes(range=[0.999, 1])
fig.show()

Интерпретируемость модели и сводка SHAP

Чтобы интерпретировать результаты нашей модели и понять, как различные функции влияют на съедобность, мы использовали значения SHAP (Shapley Additive ExPlanations). Сводная диаграмма SHAP предоставила следующую информацию:

  • Каждая точка (красная и синяя) представляет собой особенность гриба.
  • Красный цвет обозначает высокие значения характеристик, а синий цвет — низкие значения.
  • Точки справа от оси Y положительно влияли на съедобность, а точки слева — отрицательно.
  • Положение точки на оси x указывает на интенсивность ее воздействия на съедобность, при этом большее расстояние от оси соответствует более высокой интенсивности.

На сводном графике SHAP мы заметили, что:

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

Важность функции

На основании нашего анализа были определены следующие признаки, наиболее важные для определения съедобности грибов:

  1. Запах
  2. Цвет печати спор
  3. Стволовой корень
  4. синяки

Здесь мы получаем важную переменную из automl_mode

# Automl model variable importance graph with maximum feature 10
aml_model.leader.varimp_plot(num_of_features=10)

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

Заключение

В этой статье мы изучили набор данных UCI Mushroom, чтобы классифицировать грибы как съедобные или ядовитые. Благодаря проверке качества данных, выбору признаков, обучению модели и интерпретируемости мы эффективно проанализировали и поняли набор данных. Наши результаты, в том числе наиболее важные признаки для определения съедобности, могут быть использованы для более точного определения грибов, безопасных для употребления в пищу в будущем.

Что мы сделали?

АйСкунс