Merge pull request 'romanova_adelina_lab_3 is ready' (#277) from romanova_adelina_lab_3 into main
Reviewed-on: http://student.git.athene.tech/Alexey/IIS_2023_1/pulls/277
This commit is contained in:
commit
45dc8c70ea
BIN
romanova_adelina_lab_3/1.png
Normal file
BIN
romanova_adelina_lab_3/1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
BIN
romanova_adelina_lab_3/2.png
Normal file
BIN
romanova_adelina_lab_3/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 KiB |
BIN
romanova_adelina_lab_3/3.png
Normal file
BIN
romanova_adelina_lab_3/3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
romanova_adelina_lab_3/4.png
Normal file
BIN
romanova_adelina_lab_3/4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
77
romanova_adelina_lab_3/README.md
Normal file
77
romanova_adelina_lab_3/README.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Лабораторная работа №3. Вариант 21
|
||||||
|
|
||||||
|
## Тема:
|
||||||
|
Деревья решений
|
||||||
|
|
||||||
|
## Модель:
|
||||||
|
|
||||||
|
Decision Tree Classifier
|
||||||
|
|
||||||
|
## Как запустить программу:
|
||||||
|
Установить *python, numpy, matplotlib, sklearn*
|
||||||
|
```
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Какие технологии использовались:
|
||||||
|
Язык программирования Python, библиотеки numpy, matplotlib, sklearn
|
||||||
|
|
||||||
|
Среда разработки VSCode
|
||||||
|
|
||||||
|
# Что делает лабораторная работа:
|
||||||
|
Использует данные из набора "UCI Heart Disease Data" и обучает модель: ```Decision Tree Classifier```
|
||||||
|
|
||||||
|
Датасет UCI Heart Disease Data содержит информацию о различных клинических признаках, таких как возраст, пол, артериальное давление, холестерин, наличие электрокардиографических признаков и другие, а также целевую переменную, отражающую наличие или отсутствие заболевания сердца.
|
||||||
|
|
||||||
|
Для начала нужно предобработать данные, чтобы модель могла принимать их на вход. Изначально данный имеют следующий вид:
|
||||||
|
|
||||||
|
![](1.png "")
|
||||||
|
|
||||||
|
Так как модели машинного обучения умеют работать исключительно с числовыми значениями, то нужно свести все данных к данному формату и использовать только полные строки, значение признаков которых не являются пустыми значениями. Это происходит с использованием функции, представленной ниже:
|
||||||
|
|
||||||
|
![](2.png "")
|
||||||
|
|
||||||
|
Далее нужно привести целевое значение к бинарному виду, т.к изначально данное поле принимает 4 значения. После этого применить подход, называемый “feature engineering”, для получения большего количества признаков, которые возможно помогут модели при решении задачи, т.к. обычно в машинном и глубоком обучении действует следующая логика: Больше данных - лучше результат. Получение новых признаков происходит с помощью функции ниже и далее обновленный набор данных снова преобразовывается к численному формату.
|
||||||
|
|
||||||
|
```
|
||||||
|
def fe_creation(df):
|
||||||
|
# Feature engineering (FE)
|
||||||
|
df['age2'] = df['age']//10
|
||||||
|
df['trestbps2'] = df['trestbps']//10
|
||||||
|
df['chol2'] = df['chol']//60
|
||||||
|
df['thalch2'] = df['thalch']//40
|
||||||
|
df['oldpeak2'] = df['oldpeak']//0.4
|
||||||
|
for i in ['sex', 'age2', 'fbs', 'restecg', 'exang']:
|
||||||
|
for j in ['cp','trestbps2', 'chol2', 'thalch2', 'oldpeak2', 'slope']:
|
||||||
|
df[i + "_" + j] = df[i].astype('str') + "_" + df[j].astype('str')
|
||||||
|
return df
|
||||||
|
```
|
||||||
|
После применения данной функции количество признаков увеличилось с 12 до 47. Далее все признаки стандартизируются с помощью следующей формулы z = (x-mean)/std, где х - текущее значение признак, mean - математическое ожидание столбца с этим признаком, std - стандартное отклонение данного признака, а z - соответственно новое значение признака x. После всех описанных действий данные стали готовыми для их использования для обучения деревьев.
|
||||||
|
|
||||||
|
```Decision Tree Classifier```- это алгоритм машинного обучения, который использует структуру дерева для принятия решений. Каждый узел дерева представляет собой тест по какому-то признаку, а каждая ветвь представляет возможный результат этого теста. Цель - разделить данные на подгруппы так, чтобы в каждой подгруппе преобладал один класс.
|
||||||
|
|
||||||
|
```
|
||||||
|
decision_tree = DecisionTreeClassifier()
|
||||||
|
param_grid = {'min_samples_leaf': [i for i in range(2,12)]}
|
||||||
|
decision_tree_CV = GridSearchCV(decision_tree, param_grid=param_grid, cv=cv_train, verbose=False)
|
||||||
|
decision_tree_CV.fit(train, train_target)
|
||||||
|
print(decision_tree_CV.best_params_)
|
||||||
|
|
||||||
|
acc_all = acc_metrics_calc(0, acc_all, decision_tree_CV, train, valid, train_target, valid_target, title="Decision Tree Classifier")
|
||||||
|
plot_learning_curve(decision_tree_CV, "Decision Tree", train, train_target, cv=cv_train)
|
||||||
|
|
||||||
|
feature_importances_dt = decision_tree_CV.best_estimator_.feature_importances_
|
||||||
|
plot_feature_importance(feature_importances_dt, data.columns, "Decision Tree")
|
||||||
|
```
|
||||||
|
|
||||||
|
Первым был обучен Decision Tree Classifier, который с помощью алгоритма GridSearch нашел наилучшие гиперпараметры для решения задачи. Ниже приведены графики, отображающие качество и процесс обучения данного классификатора.
|
||||||
|
|
||||||
|
![](3.png "")
|
||||||
|
|
||||||
|
На следующем графике мы можем увидеть какие признаки модель посчитала наиболее важными:
|
||||||
|
|
||||||
|
![](4.png "")
|
||||||
|
|
||||||
|
## Вывод
|
||||||
|
|
||||||
|
На обучающихся данных мы в большинстве случаев предсказываем правильно, а в валидационных появляется проблема с выявлением второго класса, которое отображает наличие заболеваний.
|
302
romanova_adelina_lab_3/main.py
Normal file
302
romanova_adelina_lab_3/main.py
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
|
||||||
|
import sklearn
|
||||||
|
from sklearn.preprocessing import (LabelEncoder,
|
||||||
|
StandardScaler,
|
||||||
|
MinMaxScaler,
|
||||||
|
RobustScaler)
|
||||||
|
from sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold, learning_curve, ShuffleSplit
|
||||||
|
from sklearn.model_selection import cross_val_predict as cvp
|
||||||
|
from sklearn import metrics
|
||||||
|
from sklearn.metrics import mean_absolute_error, mean_squared_error, accuracy_score, confusion_matrix, explained_variance_score
|
||||||
|
|
||||||
|
from sklearn.tree import DecisionTreeClassifier, plot_tree
|
||||||
|
from sklearn.ensemble import RandomForestClassifier
|
||||||
|
|
||||||
|
|
||||||
|
def str_features_to_numeric(data):
|
||||||
|
# Преобразовывает все строковые признаки в числовые.
|
||||||
|
|
||||||
|
# Определение категориальных признаков
|
||||||
|
categorical_columns = []
|
||||||
|
numerics = ['int8', 'int16', 'int32', 'int64', 'float16', 'float32', 'float64']
|
||||||
|
features = data.columns.values.tolist()
|
||||||
|
for col in features:
|
||||||
|
if data[col].dtype in numerics: continue
|
||||||
|
categorical_columns.append(col)
|
||||||
|
|
||||||
|
# Кодирование категориальных признаков
|
||||||
|
for col in categorical_columns:
|
||||||
|
if col in data.columns:
|
||||||
|
le = LabelEncoder()
|
||||||
|
le.fit(list(data[col].astype(str).values))
|
||||||
|
data[col] = le.transform(list(data[col].astype(str).values))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def fe_creation(df):
|
||||||
|
# Feature engineering (FE)
|
||||||
|
df['age2'] = df['age']//10
|
||||||
|
df['trestbps2'] = df['trestbps']//10
|
||||||
|
df['chol2'] = df['chol']//60
|
||||||
|
df['thalch2'] = df['thalch']//40
|
||||||
|
df['oldpeak2'] = df['oldpeak']//0.4
|
||||||
|
for i in ['sex', 'age2', 'fbs', 'restecg', 'exang']:
|
||||||
|
for j in ['cp','trestbps2', 'chol2', 'thalch2', 'oldpeak2', 'slope']:
|
||||||
|
df[i + "_" + j] = df[i].astype('str') + "_" + df[j].astype('str')
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def acc_d(y_meas, y_pred):
|
||||||
|
# Относительная погрешность между прогнозируемыми значениями y_pred и измеренными значениями y_meas
|
||||||
|
return mean_absolute_error(y_meas, y_pred)*len(y_meas)/sum(abs(y_meas))
|
||||||
|
|
||||||
|
|
||||||
|
def acc_rmse(y_meas, y_pred):
|
||||||
|
# Среднеквадратичная ошибка между прогнозируемыми значениями y_pred и измеренными значениями y_meas
|
||||||
|
return (mean_squared_error(y_meas, y_pred))**0.5
|
||||||
|
|
||||||
|
|
||||||
|
def plot_cm(train_target, train_target_pred, valid_target, valid_target_pred, title):
|
||||||
|
# Построение матриц ошибок
|
||||||
|
|
||||||
|
def cm_calc(y_true, y_pred):
|
||||||
|
cm = confusion_matrix(y_true, y_pred, labels=np.unique(y_true))
|
||||||
|
cm_sum = np.sum(cm, axis=1, keepdims=True)
|
||||||
|
cm_perc = cm / cm_sum.astype(float) * 100
|
||||||
|
annot = np.empty_like(cm).astype(str)
|
||||||
|
nrows, ncols = cm.shape
|
||||||
|
for i in range(nrows):
|
||||||
|
for j in range(ncols):
|
||||||
|
c = cm[i, j]
|
||||||
|
p = cm_perc[i, j]
|
||||||
|
if i == j:
|
||||||
|
s = cm_sum[i]
|
||||||
|
annot[i, j] = '%.1f%%\n%d/%d' % (p, c, s)
|
||||||
|
elif c == 0:
|
||||||
|
annot[i, j] = ''
|
||||||
|
else:
|
||||||
|
annot[i, j] = '%.1f%%\n%d' % (p, c)
|
||||||
|
cm = pd.DataFrame(cm, index=np.unique(y_true), columns=np.unique(y_true))
|
||||||
|
cm.index.name = 'Actual'
|
||||||
|
cm.columns.name = 'Predicted'
|
||||||
|
return cm, annot
|
||||||
|
|
||||||
|
|
||||||
|
# Построение матриц ошибок
|
||||||
|
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(16, 6), sharex=True)
|
||||||
|
|
||||||
|
# Обучающие данные
|
||||||
|
ax = axes[0]
|
||||||
|
ax.set_title("for training data")
|
||||||
|
cm0, annot0 = cm_calc(train_target, train_target_pred)
|
||||||
|
sns.heatmap(cm0, cmap= "YlGnBu", annot=annot0, fmt='', ax=ax)
|
||||||
|
|
||||||
|
# Тестовые данные
|
||||||
|
ax = axes[1]
|
||||||
|
ax.set_title("for test (validation) data")
|
||||||
|
cm1, annot1 = cm_calc(valid_target, valid_target_pred)
|
||||||
|
sns.heatmap(cm1, cmap= "YlGnBu", annot=annot1, fmt='', ax=ax)
|
||||||
|
|
||||||
|
fig.suptitle(f'CONFUSION MATRICES for {title}')
|
||||||
|
plt.savefig(f'CONFUSION MATRICES for {title}.png')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def acc_metrics_calc(num, acc_all, model, train, valid, train_target, valid_target, title):
|
||||||
|
# Этап выбора моделей
|
||||||
|
# Расчет точности модели по различным показателям
|
||||||
|
|
||||||
|
ytrain = model.predict(train).astype(int)
|
||||||
|
yvalid = model.predict(valid).astype(int)
|
||||||
|
print('train_target = ', train_target[:5].values)
|
||||||
|
print('ytrain = ', ytrain[:5])
|
||||||
|
print('valid_target =', valid_target[:5].values)
|
||||||
|
print('yvalid =', yvalid[:5])
|
||||||
|
|
||||||
|
num_acc = 0
|
||||||
|
for x in metrics_now:
|
||||||
|
if x == 1:
|
||||||
|
#критерий точности score
|
||||||
|
acc_train = round(metrics.accuracy_score(train_target, ytrain), 2)
|
||||||
|
acc_valid = round(metrics.accuracy_score(valid_target, yvalid), 2)
|
||||||
|
elif x == 2:
|
||||||
|
# rmse критерий
|
||||||
|
acc_train = round(acc_rmse(train_target, ytrain), 2)
|
||||||
|
acc_valid = round(acc_rmse(valid_target, yvalid), 2)
|
||||||
|
elif x == 3:
|
||||||
|
# критерий относительной погрешности
|
||||||
|
acc_train = round(acc_d(train_target, ytrain) * 100, 2)
|
||||||
|
acc_valid = round(acc_d(valid_target, yvalid) * 100, 2)
|
||||||
|
|
||||||
|
print('acc of', metrics_all[x], 'for train =', acc_train)
|
||||||
|
print('acc of', metrics_all[x], 'for valid =', acc_valid)
|
||||||
|
acc_all[num_acc].append(acc_train) #train
|
||||||
|
acc_all[num_acc+1].append(acc_valid) #valid
|
||||||
|
num_acc += 2
|
||||||
|
|
||||||
|
# Построение матриц
|
||||||
|
plot_cm(train_target, ytrain, valid_target, yvalid, title)
|
||||||
|
|
||||||
|
return acc_all
|
||||||
|
|
||||||
|
|
||||||
|
def plot_feature_importance(feature_importances, feature_names, model_name):
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
|
||||||
|
# Создание цветовой палитры
|
||||||
|
colors = sns.color_palette('viridis', len(feature_importances))
|
||||||
|
|
||||||
|
# Сортировка индексов важностей признаков
|
||||||
|
indices = feature_importances.argsort()[::-1]
|
||||||
|
|
||||||
|
# Создание стильного барплота
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
ax = sns.barplot(x=feature_importances[indices], y=feature_names[indices], palette=colors)
|
||||||
|
|
||||||
|
# Добавление декораций
|
||||||
|
plt.xlabel('Важность признака', fontsize=14)
|
||||||
|
plt.ylabel('Признаки', fontsize=14)
|
||||||
|
plt.title(f'Важность признаков в модели {model_name}', fontsize=16)
|
||||||
|
plt.xticks(fontsize=12)
|
||||||
|
plt.yticks(fontsize=12)
|
||||||
|
|
||||||
|
# Добавление цветовой шкалы и ее описания
|
||||||
|
cbar = plt.colorbar(plt.cm.ScalarMappable(cmap='viridis'), ax=ax)
|
||||||
|
cbar.set_label('Уровень важности', rotation=270, labelpad=15, fontsize=12)
|
||||||
|
|
||||||
|
# Добавление сетки для лучшей читаемости
|
||||||
|
plt.grid(axis='x', linestyle='--', alpha=0.6)
|
||||||
|
|
||||||
|
# Сохранение графика в файл
|
||||||
|
plt.savefig('feature_importance_plot.png', bbox_inches='tight')
|
||||||
|
|
||||||
|
# Отображение графика
|
||||||
|
plt.savefig(f'feature_importances_{model_name}.png')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def plot_learning_curve(estimator, title, X, y, cv=None, axes=None, ylim=None,
|
||||||
|
n_jobs=None, train_sizes=np.linspace(.1, 1.0, 5), random_state=0):
|
||||||
|
fig, axes = plt.subplots(2, 1, figsize=(20, 10))
|
||||||
|
|
||||||
|
if axes is None:
|
||||||
|
_, axes = plt.subplots(1, 2, figsize=(20, 5))
|
||||||
|
|
||||||
|
axes[0].set_title(title)
|
||||||
|
if ylim is not None:
|
||||||
|
axes[0].set_ylim(*ylim)
|
||||||
|
axes[0].set_xlabel("Training examples")
|
||||||
|
axes[0].set_ylabel("Score")
|
||||||
|
|
||||||
|
cv_train = ShuffleSplit(n_splits=cv_n_split, test_size=test_train_split_part, random_state=random_state)
|
||||||
|
|
||||||
|
train_sizes, train_scores, test_scores, fit_times, _ = \
|
||||||
|
learning_curve(estimator=estimator, X=X, y=y, cv=cv,
|
||||||
|
train_sizes=train_sizes,
|
||||||
|
return_times=True)
|
||||||
|
|
||||||
|
train_scores_mean = np.mean(train_scores, axis=1)
|
||||||
|
train_scores_std = np.std(train_scores, axis=1)
|
||||||
|
test_scores_mean = np.mean(test_scores, axis=1)
|
||||||
|
test_scores_std = np.std(test_scores, axis=1)
|
||||||
|
fit_times_mean = np.mean(fit_times, axis=1)
|
||||||
|
fit_times_std = np.std(fit_times, axis=1)
|
||||||
|
|
||||||
|
# Plot learning curve
|
||||||
|
axes[0].grid()
|
||||||
|
axes[0].fill_between(train_sizes, train_scores_mean - train_scores_std,
|
||||||
|
train_scores_mean + train_scores_std, alpha=0.1,
|
||||||
|
color="r")
|
||||||
|
axes[0].fill_between(train_sizes, test_scores_mean - test_scores_std,
|
||||||
|
test_scores_mean + test_scores_std, alpha=0.1,
|
||||||
|
color="g")
|
||||||
|
axes[0].plot(train_sizes, train_scores_mean, 'o-', color="r",
|
||||||
|
label="Training score")
|
||||||
|
axes[0].plot(train_sizes, test_scores_mean, 'o-', color="g",
|
||||||
|
label="Cross-validation score")
|
||||||
|
axes[0].legend(loc="best")
|
||||||
|
|
||||||
|
# Plot n_samples vs fit_times
|
||||||
|
axes[1].grid()
|
||||||
|
axes[1].plot(train_sizes, fit_times_mean, 'o-')
|
||||||
|
axes[1].fill_between(train_sizes, fit_times_mean - fit_times_std,
|
||||||
|
fit_times_mean + fit_times_std, alpha=0.1)
|
||||||
|
axes[1].set_xlabel("Training examples")
|
||||||
|
axes[1].set_ylabel("fit_times")
|
||||||
|
axes[1].set_title("Scalability of the model")
|
||||||
|
|
||||||
|
plt.savefig(f'{title}.png')
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Загрузка данных
|
||||||
|
# Преобразование данных и предобработка
|
||||||
|
# Обучение моделей Decision Tree Classifier и Random Forest Classifier
|
||||||
|
# Расчет метрик и построение графиков
|
||||||
|
cv_n_split = 5
|
||||||
|
random_state = 42
|
||||||
|
test_train_split_part = 0.25
|
||||||
|
|
||||||
|
metrics_all = {1: 'acc', 2 : 'rmse', 3 : 're'}
|
||||||
|
metrics_now = [1, 2, 3]
|
||||||
|
|
||||||
|
data = pd.read_csv("..//heart_disease_uci.csv")
|
||||||
|
data['target'] = data['num']
|
||||||
|
data = data.drop(columns=['id', 'dataset', 'ca', 'thal', 'num'])
|
||||||
|
|
||||||
|
data = data[(data['chol'] <= 420) & (data['oldpeak'] >=0) & (data['oldpeak'] <=4)].reset_index(drop=True)
|
||||||
|
data = data.dropna().reset_index(drop=True)
|
||||||
|
print(data.info())
|
||||||
|
|
||||||
|
data = str_features_to_numeric(data)
|
||||||
|
data = data[data['target'].isin([0, 1])] # приволим столбец с целевыми значениями к бинарному виду
|
||||||
|
|
||||||
|
data = fe_creation(data)
|
||||||
|
data = str_features_to_numeric(data)
|
||||||
|
|
||||||
|
dataset = data.copy() # original data
|
||||||
|
target_name = 'target'
|
||||||
|
target = data.pop(target_name)
|
||||||
|
|
||||||
|
# Model standartization
|
||||||
|
# The standard score of a sample x is calculated as:
|
||||||
|
# z = (x - мат.ож.) / (стандартное отклонение)
|
||||||
|
scaler = StandardScaler()
|
||||||
|
data = pd.DataFrame(scaler.fit_transform(data), columns = data.columns)
|
||||||
|
|
||||||
|
train, valid, train_target, valid_target = train_test_split(data, target, test_size=test_train_split_part, random_state=random_state)
|
||||||
|
|
||||||
|
# list of accuracy of all model - amount of metrics_now * 2 (train & valid datasets)
|
||||||
|
num_models = 6
|
||||||
|
acc_train = []
|
||||||
|
acc_valid = []
|
||||||
|
acc_all = np.empty((len(metrics_now)*2, 0)).tolist()
|
||||||
|
acc_all
|
||||||
|
|
||||||
|
acc_all_pred = np.empty((len(metrics_now), 0)).tolist()
|
||||||
|
acc_all_pred
|
||||||
|
|
||||||
|
cv_train = ShuffleSplit(n_splits=cv_n_split, test_size=test_train_split_part, random_state=random_state)
|
||||||
|
|
||||||
|
decision_tree = DecisionTreeClassifier()
|
||||||
|
param_grid = {'min_samples_leaf': [i for i in range(2,12)]}
|
||||||
|
decision_tree_CV = GridSearchCV(decision_tree, param_grid=param_grid, cv=cv_train, verbose=False)
|
||||||
|
decision_tree_CV.fit(train, train_target)
|
||||||
|
print(decision_tree_CV.best_params_)
|
||||||
|
|
||||||
|
acc_all = acc_metrics_calc(0, acc_all, decision_tree_CV, train, valid, train_target, valid_target, title="Decision Tree Classifier")
|
||||||
|
plot_learning_curve(decision_tree_CV, "Decision Tree", train, train_target, cv=cv_train)
|
||||||
|
|
||||||
|
feature_importances_dt = decision_tree_CV.best_estimator_.feature_importances_
|
||||||
|
plot_feature_importance(feature_importances_dt, data.columns, "Decision Tree")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user