В этом посте я приведу простой пример мультиклассовой классификации данных с помощью нейронной сети. В качестве сети мы будем использовать многослойный перцептрон, а данными послужит набор о цветках ириса.
В 1936 году известный английский статистик, биолог и генетик Рональд Фишер продемонстрировал свой метод дискриминантного анализа на примере собранных данных о видах ириса, о длине и ширине лепестков и чашелистиков. Этот набор данных уже стал классическим и часто используется для демонстрации работы методов классификации.
Итак, приступим. Для начала инициализируем генератор случайных чисел. Это хорошая практика, позволяющая получить подобные результаты при повторении экспериментов в дальнейшем.
1 2 | seed = 101 np.random.seed(seed) |
Теперь нужно загрузить датасет. Этот сет можно найти по адресу https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data . Для работы с датасетом мы будем использовать pandas.
1 2 3 4 | dataframe = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None) dataset = dataframe.values X = dataset[:, 0:4].astype(float) Y = dataset[:, 4] |
После загрузки датасета, мы делим его на две части – на целевую переменную Y и на данные, от которых она зависит X.
Мы будем определять класс растения по четырем параметрам: длине и ширине лепестка и длине и ширине чашелистика. Результирующих классов в нашем датасете три.
Визуализируем данные, для лучшего понимания:
Целевая переменная – класс, который представлен в датасете текстом. Чтобы нейросеть работала, нам нужно закодировать эти классы в целочисленные категории.
Iris-setosa кодируется как класс 0, Iris-versicolor – класс 1 и Iris-virginica как класс 2. Далее применим one-hot кодирование к полученным категориями. Iris-setosa станет [1, 0, 0], Iris-versicolor – [0, 1, 0] и т.д. Этот метод часто применяется в задачах классификации.
1 2 3 4 | encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) dummy_y = np_utils.to_categorical(encoded_Y) |
Нам нужно отделить часть данных для тестового подмножества для оценки качества работы нашей сети. Это удобно сделать, вызвав метод train_test_split. В качестве параметров укажем размер тестового сета – 25% и передадим наш инициализатор случайных значений.
1 | X_train, X_test, Y_train, Y_test = train_test_split(X, dummy_y, test_size=.25, random_state=seed) |
Теперь опишем нашу нейросеть. Мы используем классы из библиотека Keras. Создадим нейросеть следующей архитектуры: 4 входа под каждый признак, 4 нейрона в скрытом слое, и три выхода — по количеству классов. Вопрос архитектуры сетей до сих пор не является решенным, выбор конфигурации, как правило, основывается на опыте и оптимизируется с помощью экспериментов.
1 2 3 4 5 6 | def my_model(): model = Sequential() model.add(Dense(4, input_dim=4, init=’normal , activation= relu )) model.add(Dense(3, init= normal , activation= sigmoid )) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model |
Мы готовы запустить расчет. Выполним метод fit у нашей модели. Установим количество эпох обучения равное 200 и количество примеров для одновременного обучения равное 7. Параметр verbose отвечает за вывод процесса обучения в терминал.
1 2 | model = my_model() model.fit(X_train, Y_train, batch_size=7, nb_epoch=200, verbose=1) |
Оценим качество нашей модели на тестовом подмножестве. Будем использовать метрику площади под ROC-кривой. Эта метрика чаще всего применяется в задачах мультиклассовой классификации.
1 2 | predictions = model.predict_proba(X_test) print('Accuracy: {}'.format(roc_auc_score(y_true=Y_test, y_score=predictions))) |
Запустив обучение и получив результат, можно обратить внимание на то, что на последней эпохе точность была равна acc: 0.9732, а на тестовом подмножестве она опустилась до 0.8339. Это объясняется переобучением нейросети. Сеть просто запомнила значения признаков и выдала соответственный результат на обучающем подмножестве. А так как про тестовый сет нейросеть ничего не знает, то точность на нем гораздо ниже.
Для борьбы с переобучением существует множество методик, и это темы для отдельных статей. А сейчас, чтобы немного улучшить точность можно добавить один Dropout слой между полносвязными слоями Dense. Этот слой будет вносить случайные искажения в расчет и, таким образом, будет мешать переобучению сети.
1 2 3 | model.add(Dense(4, input_dim=4, init='normal', activation='relu')) model.add(Dropout(0.2)) model.add(Dense(3, init='normal', activation='sigmoid')) |
После запуска вы увидите, что точность неплохо возросла.
Как вы могли заметить, нейросетевой классификатор на keras реализуется довольно просто. C отступами строк, комментариями и импортами мы уложились в 50 строчек кода.
Ниже представлен полный код примера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import numpy as np import pandas as pd from keras.models import Sequential from keras.layers import Dense, Dropout from keras.utils import np_utils from sklearn.metrics import roc_auc_score from sklearn.preprocessing import LabelEncoder from sklearn.cross_validation import train_test_split # random seed seed = 101 np.random.seed(seed) # load and split data dataframe = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None) dataset = dataframe.values X = dataset[:, 0:4].astype(float) Y = dataset[:, 4] # encode class values as integers encoder = LabelEncoder() encoder.fit(Y) encoded_Y = encoder.transform(Y) # one-hot dummy_y = np_utils.to_categorical(encoded_Y) # split to test and train X_train, X_test, Y_train, Y_test = train_test_split(X, dummy_y, test_size=.25, random_state=seed) # define model def my_model(): model = Sequential() model.add(Dense(4, input_dim=4, init='normal', activation='relu')) model.add(Dropout(0.2)) model.add(Dense(3, init='normal', activation='sigmoid')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model # fit model = my_model() model.fit(X_train, Y_train, batch_size=7, nb_epoch=200, verbose=1) # predict predictions = model.predict_proba(X_test) print('Accuracy: {}'.format(roc_auc_score(y_true=Y_test, y_score=predictions))) |