#!/usr/bin/env python # coding: utf-8 # # Задача: классификация стекла # Часто на месте преступления остаются осколки разных видов стекол, которые можно использовать как улики, если определить тип стекла и от каких оно объектов. [Выборка](https://archive.ics.uci.edu/ml/machine-learning-databases/glass/) состоит из 9 признаков - химических параметров образцов и 214 объектов. Необходимо каждому образцу сопоставить один из 6 классов (например: стекло автомобиля, осколок посуды, окно здания) и сравнить качество работы решающего дерева и алгоритма решающего дерева и алгоритма k-ближайших соседей. В качестве функции ошибки использовать долю неправильных ответов классификатора. Дает ли масштабирование признаков значительное улучшение в качестве классификации? # # Домашнее задание 2 # ## Первое знакомство с данными # In[1]: import pandas as pd import sklearn as sc import numpy as np import matplotlib.pyplot as plt from sklearn import tree, model_selection, metrics from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler plt.rcParams['font.family'] = 'serif' plt.rcParams['font.serif'] = 'FreeSerif' plt.rcParams['lines.linewidth'] = 2 plt.rcParams['lines.markersize'] = 12 plt.rcParams['xtick.labelsize'] = 24 plt.rcParams['ytick.labelsize'] = 24 plt.rcParams['legend.fontsize'] = 24 plt.rcParams['axes.titlesize'] = 26 plt.rcParams['axes.labelsize'] = 24 # In[2]: columnNames = ['Id','RI', 'Na', 'Mg', 'Al', 'Si', 'K', 'Ca', 'Ba', 'Fe', 'Type of glass'] data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/glass/glass.data', names = columnNames, header=None) # In[3]: df = data.drop(list(data)[0], 1) df.head() # Посмотрим на соотношение классов. # In[4]: X = df.drop(list(df)[9], 1) target = df['Type of glass'] for i in range(1, 8): print("{0} - {1}".format(i, sum([target[j] == i for j in range(target.shape[0])]))) # ## Обучение моделей # В качестве критерия сравнения моделей решающего дерева и метода ближайших соседей будем сравнивать доли неправильных ошибок, усреднённых по кросс-валидации с 3 фолдами. Для воспроизводимости результата зафиксируем seed. # In[5]: clf1 = tree.DecisionTreeClassifier() np.random.seed(0) c_v_s = model_selection.cross_val_score(clf1, X, target, cv = 3) 1 - c_v_s.mean() # In[6]: res = [] k = [i for i in range(1, 20)] for i in range(1, 20): clf2 = KNeighborsClassifier(n_neighbors=i) c_v_s = model_selection.cross_val_score(clf2, X, target, cv = 3) res.append(1 - c_v_s.mean()) min(res) # ## Масштабирование признаков # Сравним результаты работы моделей после масштабирования признаков. Для метода ближайших соседей построим график зависимости доли ошибок классификатора от числа соседей. # In[7]: np.random.seed(0) scaler = StandardScaler() scaler.fit(X, target) X_scaled = scaler.transform(X) clf1 = tree.DecisionTreeClassifier() c_v_s = model_selection.cross_val_score(clf1, X_scaled, target, cv = 3) 1 - c_v_s.mean() # In[8]: res1 = [] for i in range(1, 20): clf2 = KNeighborsClassifier(n_neighbors=i) c_v_s = model_selection.cross_val_score(clf2, X, target, cv = 3) res1.append(1 - c_v_s.mean()) min(res1) # In[9]: fig, ax = plt.subplots(figsize=(12, 8)) plt.xlabel('Number of neighbors, $k$') plt.ylabel('Error rate, $Q$') plt.plot(k, res, 'g-', marker='o') plt.plot(k, res1, 'b-', marker='o') ax.set_title('Зависимость ошибки от числа соседей') plt.grid() plt.show() fig.savefig('1.svg') # # Выводы # Видим, что качество алгоритма метода ближайших соседей при кросс-валидации с количеством фолдов = 3 будет наилучшим при k = 14 и при таких условиях эффективнее алгоритма решающего дерева с параметрами по умолчанию. Как и следовало ожидать, масштабирование признаков не изменяет качество работы алгоритма ближайших соседей, а для решающего дерева даже немного ухудшает. # # Домашнее задание 3 # Построим график зависимости средней ошибки и его стандартного отклонения алгоритма ближайших соседей от объема выборки на обучении и контроле. В качестве выборки возьмём первые $m$ элементов выборки. Для этого $K = 20$ раз случайно разобьём выборку на обучение и контроль в отношении $1:1$. Модель ближайших соседей имеет единственный параметр - число соседей, так что для каждого разбиения на обучение и контроль подберём нужное число соседей, минимизирующее долю неправильных ответов классификатора на обучении и контроле соответственно. Для этого выберем минимальное целое число в отрезке $[1, size(X_{train})]$, которое будет доставлять минимум функции ошибки на обучении или контроле. Получим два набора из 20 ошибок. Вычислим среднюю ошибку и стандартное отклонение. Повторим процедуру для $m \in [2, 100]$. После этого построим график средней ошибки и стандартного отклонения от $m$. # In[10]: from sklearn.model_selection import train_test_split K = 20 error_mean = [] error_std = [] for m in range(2, 101): X_data = X[0:m] y_data = target[0:m] error_data = [] for k in range(K): X_data_train, X_data_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.5, random_state=k) error = [] for n in range(1, X_data_train.shape[0] + 1): clf = KNeighborsClassifier(n_neighbors=n) clf.fit(X_data_train, y_train) predictions = clf.predict(X_data_test) error.append(1 - metrics.accuracy_score(y_test, predictions)) error_data.append(min(error)) error_mean.append(np.array(error_data).mean()) error_std.append(np.array(error_data).std()) # In[11]: m = [i for i in range(2, 101)] fig, ax = plt.subplots(figsize=(12, 8)) plt.xlabel('Size of train data, $m$') plt.ylabel('Error rate, $Q$') plt.errorbar(x = m, y = error_mean, yerr = np.array(error_std)) ax.set_title('Зависимость ошибки от объема выборки') plt.grid() plt.show() fig.savefig('ModelBreadSw.png')