Список работ

Простая нейронная сеть на Python

Содержание

Введение

Нейронная сеть (НС), решающая, болен человек сахарным диабетом или нет, реализуется на Питоне с применением библиотеки TensorFlow с оболочкой Keras. Вывод графиков потерь и точности осуществлен средствами matplotlib.
Установка TensorFlow, Keras и прочих библиотек выполняется в командном окне, которое в Windows открывается после нажатия Win + X.
Установка обычной или GPU-версий Tensorflow [1]:

<path>/Scripts/pip install --upgrade tensorflow
<path>/Scripts/pip install --upgrade tensorflow-gpu

Например, установка matplotlib:

C:/Users/HP/AppData/Local/Programs/Python/Python36/Scripts/pip install matplotlib

Установка обычной и GPU-версий Keras:

<path>/Scripts/pip install keras

Обучение проводится по следующим данным:

  1. Pregnancies – число беременностей (все наблюдаемые – это женщины).
  2. Glucose – концентрация глюкозы в плазме через 2 часа после введения в пероральном глюкозотолерантном тесте (ммоль/л).
  3. BloodPressure – диастольческое (нижнее) кровяное давление (мм рт. ст.).
  4. SkinThickness – толщина кожной складки в области трицепса (мм).
  5. Insulin – концентрация инсулина в сывортке крови при 2-часовом во`здержании от еды (мкЕд/мл).
  6. BMI – индекс массы тела ((вес, кг / рост, м)^2).
  7. DiabetesPedigreeFunction – наследственность (генетическая предрасположенность к диабету).
  8. Age – возраст (число лет).
  9. Outcome – выход: 0 – если нет диабета, 1 – в противном случае.

Примеры данных приведены на рис. 1.

Диагностика диабета

Рис. 1. Диагностические данные

Всего в таблице 768 строк. Данные разбиваются на две части: обучающую и для тестирования. Каждая часть, в свою очередь, разбивается на матрицу с данными и вектор с выходными данными (последний столбец исходной таблицы). В обучающую выборку (массивы trainData и trainOutput) включаются первые 3/4 строк исходной таблицы, последующие строки попадают в выборку для проверки сети (массивы testData и testOutput).
Данные находятся в csv-файле и описаны в [2]. В качестве разделителя используется точка с запятой.

Структура сети

Взята полносвязная нейронная сеть с тремя скрытыми слоями. Структура сети приведена в табл. 1:

Таблица 1. Структура НС

СлойФункция активацииЧисло нейронов
Входной
Первый скрытый слойReLU16 
Второй скрытый слойТа же32 
Третий скрытый слойТа же16 
ВыходнойSigmoid

Графики функций активации ReLU и Sigmoid показаны на рис. 2.

RelU и Sigmoid

Рис. 2. Функции активации ReLU и Sigmoid

На рис. 3, а показаны схема перцептрона (для иллюстрации идеи архитектуры НС) и архитектура полносвязной НС (б) с одним скрытым слоем.

Архитектура полносвязной НС

Рис. 3. Архитектура полносвязной НС: а – перцептрон; б – полносвязная НС; в – пороговая функция перцептрона

Перцептрон (рис. 3, а) обеспечивает бинарную классификацию – все объекты будут отнесены перцептроном к одному из двух классов; h – пороговая функция (рис. 3, в). Перцептрон можно рассматривать как модель искусственного нейрона, построенную по аналогии с моделью биологического нейрона (рис. 4).

Биологический нейрон

Рис. 4. Модель биологического нейрона

Биологический нейрон передает информацию с помощью электрических и химических сигналов. Нейрон состоит из тела нейрона и отростков: дендритов и аксона, которые связывают нейроны в нейронную сеть. Дендриты отвечают за приём сигналов от других нейронов, а аксон – за передачу сигналов. Аксон заканчивается концевой ветвью (терминалью), которая образует с дендритами другого нейрона соединение – синапс. Синапс способен менять форму при изменении разности потенциалов. Изменение формы влияет на силу передачи сигналов. Выход нейрона распределяется по многим синапсам на множество нейронов и наоборот на данный нейрон сходятся синапсы от множества источников [3].
Дендритами искусственного нейрона являются его связи с другими нейронами или входными данными, которые выражаются в числовом векторе w1, w2, …, wn, называемым вектором весов. Он определяет силу синаптической связи с другими нейронами или входными данными.

Реализация

# Реализуется многослойный перцептрон
import numpy as np
import sys, os, numpy, keras
import matplotlib.pyplot as plt
from tkinter import messagebox # Вывод сообщений
from keras.models import Sequential # Линейный стек слоев
from keras.layers import Dense # Добавление полносвязного слоя
from keras import optimizers
from numpy import array
from sklearn.preprocessing import StandardScaler # Стандартизация данных
#
class LossHistory(keras.callbacks.Callback):
     def on_train_begin(self, logs={}):
         self.losses = [] # Потери и точность
         self.acc = []
     def on_batch_end(self, batch, logs={}):
         self.losses.append(logs.get('loss'))
         self.acc.append(logs.get('acc'))
#
# Задание затравки датчика случайных чисел обеспечит повторяемость результата
np.random.seed(348)
#
file = "G:/python/SimpleNN/pima_indians_diabetes.csv"
if os.path.exists(file):
     print ("Файл найден")
else:
     print ("Файл не найден")
     exit()
# Задание затравки датчика случайных чисел обеспечит повторяемость результата
numpy.random.seed(348)
# Загружаем диагностические данные для обучения сети
# Файл с данными - это таблица из 8-ми столбцов; в последнем стобце 0, если нет диабета, или 1 - в противном случае
allData = numpy.loadtxt(file, delimiter = ";") # allData[0, 0]) - первый элемент матрицы
allDataLen = len(allData[:, 0])
print(allDataLen) # 768
X = allData[:,0:8]
y = allData[:,8]
##print(X)
#
# fit- вычисляет среднее значение и стандартное отклонение для последующей стандартизации
scaler = StandardScaler(copy = False).fit(X)
X = scaler.transform(X) # Стандартизация за счет центрирования и масштабирования
#
##print(X)
##sys.exit()
# X_train - матрица параметров (диагностические данные, первые 8 столбцов в табл. рис. 1);
# y_train - выход сети (0 или 1, см. последний столбец в табл. на рис. 1)
# Размер порция тренировочных данные (обучающей данных)
X_trainLen = int(3 * allDataLen / 4)
# Тренировочные и проверочные данные
X_train, y_train = X[0:X_trainLen, 0:8], y[0:X_trainLen]
X_test, y_test = X[X_trainLen + 1:, 0:8], y[X_trainLen + 1:]
##print(X_test)
##print(y_test)
##sys.exit()
categorical = False # True, False
if categorical:
     num_classes = 2
     y_train = keras.utils.to_categorical(y_train, num_classes)
     y_test = keras.utils.to_categorical(y_test, num_classes)
     lossFun = 'categorical_crossentropy'
else:
     num_classes = 1
     lossFun = 'mean_squared_error'
#
# Виды функцции потерь:
# mean_squared_error; mean_absolute_error; mean_absolute_percentage_error
# mean_squared_logarithmic_error; squared_hinge; hinge;
# categorical_hinge; logcosh; categorical_crossentropy;
# sparse_categorical_crossentropy; binary_crossentropy;
# kullback_leibler_divergence; cosine_proximity
#
# Виды методов оптимизации:
# SGD; RMSprop; Adagrad; Adadelta; Adam; Adamax; Nadam; TFOptimizer
# Вариант задания метода оптимизации
# optKind = optimizers.SGD(lr = 0.01, decay = 1e-6, momentum = 0.9, nesterov = True)
#
# Создаем НС
model = Sequential()
# Для входного слоя необходимо указать input_dim - число входов
# 12 - число выходов первого слоя
# relu - функция активации нейрона
actFun = 'relu'
actFun2 = 'sigmoid' # softmax, sigmoid
model.add(Dense(16, input_dim = 8, activation = actFun))
# 32 - число выходов второго слоя; число входов равно числу выходов предшествующего слоя
model.add(Dense(48, activation = actFun))
##model.add(Dense(16, activation = actFun))
# sigmoid - функция активация нейрона последнего слоя
model.add(Dense(num_classes, activation = actFun2))
# Компиляция НС с функцией потерь lossFun и алгоритма Adam ее минимизации (обновляются веса НС)
optKind = 'Adam'
model.compile(loss = lossFun, optimizer = optKind, metrics = ['accuracy'])
#
# Обучение нейронной сети
print('\nОбучение')
# epochs - число обучающих эпох
# batch_size - параметр, определяющий форму входного массива
# В случае 2D (это наш случай), на входе сети будет массив формы (batch_size, input_dim)
allPoints = True # True or False
if allPoints:
     # CallBack
     history = LossHistory()
     model.fit(X_train, y_train, epochs = 10, batch_size = 30, verbose = 2, callbacks = [history])
else:
     # Получаем историю после завершения эпохи
     history = model.fit(X_train, y_train, epochs = 6, batch_size = 30, verbose = 2)
#
# Оценка результата
print('\nТестирование')
scores = model.evaluate(X_test, y_test, verbose = 2)
#scores = model.evaluate(X_train, y_train)
#
print("%s: %.2f%%" % (model.metrics_names[0], scores[0]*100)) # loss (потери)
print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100)) # acc (точность)
#
predictions = model.predict(X_test)
rounded = [round(x[0]) for x in predictions]
print('Прогноз:')
print(rounded)
#
# messagebox.showinfo(title = "Done", message = "Готово")
#
# Графики изменения потерь и точности в процессе обучения
if allPoints:
     #print(history.losses)
     cnt = len(history.losses)
else:
     cnt = len(history.history['loss'])
rng = numpy.arange(cnt)
#
# Вариант 1 вывода графиков потерь и точности
fig, ax = plt.subplots(figsize = (8, 4))
if allPoints:
     ax.scatter(rng, history.losses, marker = 'o', c = 'red', edgecolor = 'black', label = 'Потери')
     ax.scatter(rng, history.acc, marker = 'o', c = 'blue', edgecolor = 'black', label = 'Точность')
else:
     ax.scatter(rng, history.history['loss'], marker = 'o', c = 'red', edgecolor = 'black', label = 'Потери')
     ax.scatter(rng, history.history['acc'], marker = 'o', c = 'blue', edgecolor = 'black', label = 'Точность')
ax.set_title('Потери и Точность в процессе обучения')
ax.legend(loc = 'upper left')
ax.set_ylabel('Потери, Точность')
ax.set_xlabel('Шаг')
ax.set_xlim([0, cnt])
ax.set_ylim([0, 1])
fig.show()
#
# Другой вариант вывода графиков потерь и точности
"""
plt.title('Потери и Точность в процессе обучения')
if allPoints:
     plt.scatter(rng, history.losses, marker = 'o', c = 'red', edgecolor = 'black', label = 'Потери')
     plt.scatter(rng, history.acc, marker = 'o', c = 'blue', edgecolor = 'black', label = 'Точность')
else:
     plt.scatter(rng, history.history['loss'], marker = 'o', c = 'red', edgecolor = 'black', label = 'Потери')
     plt.scatter(rng, history.history['acc'], marker = 'o', c = 'blue', edgecolor = 'black', label = 'Точность')
plt.legend(loc = 'upper left')
plt.xlim([0, cnt])
plt.ylim([0, 1])
plt.ylabel('Потери, Точность')
plt.xlabel('Шаг')
plt.show()
"""

По ходу обучения выводятся следующие сведения:

Using TensorFlow backend.
Файл найден
768
Обучение
Epoch 1/10
- 1s - loss: 0.2506 - acc: 0.5122
Epoch 2/10
- 0s - loss: 0.2241 - acc: 0.7153
Epoch 3/10
- 0s - loss: 0.2075 - acc: 0.7326
Epoch 4/10
- 0s - loss: 0.1941 - acc: 0.7344
Epoch 5/10
- 0s - loss: 0.1853 - acc: 0.7396
Epoch 6/10
- 0s - loss: 0.1793 - acc: 0.7465
Epoch 7/10
- 0s - loss: 0.1744 - acc: 0.7535
Epoch 8/10
- 0s - loss: 0.1709 - acc: 0.7517
Epoch 9/10
- 0s - loss: 0.1671 - acc: 0.7569
Epoch 10/10
- 0s - loss: 0.1642 - acc: 0.7674

Тестирование
loss: 16.14%
acc: 76.44%
Прогноз:
[0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ...]

При отсутствии стандартизации данных результаты будут хуже:
loss: 36.65%
acc: 63.35%

Потери и точность на каждом шаге обучения показаны на рис. 5 и 6.

Потери и точность

Рис. 5. Графики изменения потерь и точности на этапе обучения сети (выполнена стандартизации данных)

Потери и точность

Рис. 6. Графики изменения потерь и точности на этапе обучения сети (нет стандартизации данных)

Для вывода графиков потерь и точности, иллюстрирующих процесс обучения, создан класс LossHistory.
Экземпляр history этого класса использован в качестве callBack-параметра метода model.fit.
Свойства losses и acc рассматриваемого класса являются массивами, в которых накапливаются потери и точность, вычисляемые в процессе обучения, продолжительность которого определяется параметром batch_size.

Заключение

Приведен пример употребления полносвязной нейронной сети. Использована оболочка Keras, обеспечивающая доступ к TensorFlow. Невысокая точность классификации объясняется непродолжительным обучением, малым размером обучающих данных и неподходящей архитектурой НС.

Литература

  1. TensorFlow. [Электронный ресурс] URL: https://www.tensorflow.org/install/install_windows (Дата обращения: 27.05.2018).
  2. Pima Indians Diabetes Database. [Электронный ресурс] URL: https://www.kaggle.com/uciml/pima-indians-diabetes-database/data (Дата обращения: 27.05.2018).
  3. Шеперд Г. Нейробиология: В 2-х т. Т. 1. Пер. с англ. – М.: Мир, 1987. 454 с.

Список работ

Рейтинг@Mail.ru