К книге
Рассказы о математике с примерами на языках Python и CСтраница 9
69%
Страница 9
9

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

В итоге, мы имеем такую структуру сети:

Рассмотрим подробнее программу на языке Python.

Тренировка сети

Создаем нейронную сеть и подготавливаем 2 массива тестовых данных (исходные и целевых значения). Массив “digits” загружается из зараннее подготовленного текстового файла базы MNIST. Для каждой цифры содержится исходный массив в 784 цифры, и правильное значение числа, которое и будет использоваться для тренировки.

mlp = MLP(n_in=28*28, n_hidden=48, n_out=10)

print "1. Загрузка данных"

with open("digitsMnist.txt", 'rb') as fp:

digits = cPickle.load(fp)

print "2. Подготовка данных"

batch = 50

inputs = []

targets = []

for p in range(batch):

data = digits[p]["data"]

resulVal = digits[p]["result"]

# Конвертация целевого числа в массив: “3” => [0,0,0,1,0,0,0,0,0,0]

result_flat = [0.0]*10

result_flat[resulVal] = 1.0

inputs.append(data)

targets.append(result_flat)

Собственно тренировка ничем по сути не отличается от предыдущего примера с функцией XOR. Т.к. процесс идет несколько часов, были также добавлены функции сохранения и чтения состояния сети, чтобы процесс можно было прервать и затем продолжить. Прервать тренировку можно в любой момент нажатием Ctrl+C. Для удобства визуализации процесса выводится суммарная ошибка по всем тестируемым цифрам.

print "3. Обучение"

mlp.load()

try:

N = 10000

for p in range(N):

errSum = 0.0

for d in range(batch):

mlp.forwardPass(inputs[d])

mlp.backPass(inputs[d], targets[d])

errSum += mlp.calcError(inputs[d], targets[d])

print "Step", p, "error", errSum

except KeyboardInterrupt:

pass

mlp.save()

Всего было проведено около 50000 итераций на блоке размером 50 цифр, весь массив в 60000 цифр не использовался. Обучение сети заняло порядка 2х часов на компьютере с Core i7. Разумеется, этот проект учебный, и программа никак не оптимизирована по быстродействию. С использованием специальных фреймворков типа Keras тренировка такой сети занимает не более 5 минут.

Тестирование сети

И наконец, тестирование. В качестве теста, “от руки” была нарисована цифра “3”, которая получилась в виде массива примерно такой:

t = [[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] +

[ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]]

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

for digit in t:

printDigit(digit)

mlp.forwardPass(digit)

mlp.printOutput()

Результат весьма интересен:

[0.019443, 0.0102247, 0.0091067, 0.467039, 0.0000103, 0.0150096, 0.0008709, 0.0004573, 0.655264, 0.0867915]

На тренировочных значениях сеть должна была выдавать “1” в том разряде, который соответствует “загаданному” числу. И хотя нигде явным образом это не планировалось и специально не программировалось, сеть выдала максимальные значения в тех разрядах массива, где “по ее мнению”, степень схожести максимальна. Как можно видеть, эти разряды соответствуют числам “3” и “8”, что визуально действительно похоже на нарисованное число. Строго говоря, результат не совсем верный, картинка больше похожа на “3” чем на ”8”, но и тренировка не была закончена полностью.

Для следующего теста число нейронов было увеличено до 128, а количество итераций обучения было увеличено до 75000.

Результаты теста на том же массиве:

[0.0044085, 0.219943, 0.0118179, 0.816858, 0.0002372, 0.06267359, 0.0008674, 0.00020319, 0.55439027, 0.00061541]

Максимальное значение теперь действительно соответствует числу “3”, можно сказать что сеть работает как задумывалось.

Для тех кто захочет поэкспериментировать самостоятельно, исходный текст программы и база чисел MNIST находятся в архиве, ссылка на который есть в начале книги.

21. Использование библиотеки TensorFlow

Говоря о математических вычислениях и нейронных сетях, нельзя не упомянуть библиотеку TensorFlow - эта библиотека была специально создана в Google для расчетов в области машинного обучения. Изначально она предназначалась только для использования внутри Google, но затем исходные коды TensorFlow были опубликованы для всех желающих. Библиотека достаточно сложна в использовании, но позволяет получить неплохие результаты.

Для установки TensorFlow для Windows необходимо:

- Установить 64-битную версию Python (дистрибутив можно скачать по ссылке https://www.python.org/downloads/release/python-365/)

- Запустить из консоли команду pip3 install tensorflow

Рассмотрим использование Tensorflow на простом примере: умножение двух массивов. Исходный код приведен ниже.

import tensorflow as tf

# Initialize two constants

x1 = tf.constant([1,2,3,4])

x2 = tf.constant([5,6,7,8])

# Multiply

task = tf.multiply(x1, x2)

# Intialize the Session

session = tf.Session()

# Print the result

print(session.run(task))

# Close the session

session.close()

Сохраним программу в файле tf1.py и запустим, набрав в консоли python3 tf1.py (обратим внимание на цифру “3” в версии Python).

Разберем код программы подробнее. Функция tf.constant позволяет создать константу, в нашем случае это массив со значениями x1 = [1,2,3,4]. Аналогично создается массив x2. Наконец, команда task = tf.multiply(x1,x2) умножает два массива. Казалось бы все пока просто - но не совсем. Если мы напишем print(task), то никакого результата не увидим, на экран отобразится лишь что-то типа Tensor(“Mul:0”, dtype=int32). На самом деле, в памяти TensorFlow формируется так называемый “граф вычислений” (computation graph). Как уже упоминалось, Python - это интерпретатор, и работает довольно-таки медленно, поэтому библиотека TensorFlow формирует в памяти собственную модель данных для вычислений. Для того, чтобы получить реальный результат, мы должны создать объект Session, и запустить в нем вычисления командой session.run(task). Когда вычисления произведены, сессию можно закрыть командой session.close().

Разумеется, можно вычислять не только константы. Рассмотрим пример использования переменных.

import tensorflow as tf

k = tf.constant(2)

a = tf.placeholder(tf.int32)

b = tf.placeholder(tf.int32)

m = tf.multiply(k, a)

task = tf.add(m, b)

session = tf.Session()

print(session.run(task, feed_dict={a: 2, b: 3}))

print(session.run(task, feed_dict={a: [10,11], b: [3,4]}))

session.close()

Здесь мы задаем граф вычислений по формуле a*k + b, где a и b - это переменные, для указания этого используется специальный тип placeholder. Теперь, при вычислении командой session.run мы можем передать различные параметры, например a=2 и b=3:

print(session.run(task, feed_dict={a: 2, b: 3}))

>> 7

Можно даже передать массивы в качестве параметра:

print(session.run(task, feed_dict={a: [10,11], b: [3,4]}))

> [23, 26]

Библиотека TensorFlow поддерживает множество различных операций, например, вычисления с матрицами, ну и разумеется, большое количество функций предназначено специально для обработки данных и машинного обучения. Вернемся к предыдущей главе, и перепишем нашу нейронную сеть, распознающую текст. За основу исходного кода был взят готовый пример из https://github.com/aymericdamien/TensorFlow-Examples/tree/master/examples желающие могут изучить приведенные там примеры более подробно.

Код целиком приведен ниже:

import tensorflow as tf

import _pickle as cPickle

# Prepare training data

training_epochs = 500

learning_rate = 0.001

inputs = []

targets = []

with open("digitsMnist.txt", 'rb') as fp:

digits = cPickle.load(fp)

print("Digits loaded:", len(digits))

inputs = list(map(lambda x: x["data"], digits))

def arrayFromDigit(data):

result = data["result"]

# Convert value to array: "3" => [0,0,0,1,0,0,0,0,0,0]

result_flat = [0.0]*10

result_flat[result] = 1.0

return result_flat

targets = list(map(arrayFromDigit, digits))

# Network Parameters

n_hidden_1 = 48       # 1st layer number of neurons

n_input = 784        # MNIST data input (img shape: 28*28)

n_classes = 10       # MNIST total classes (0-9 digits)

model_path = "model_mlp.ckpt"

# tf Graph input

X = tf.placeholder("float", [None, n_input])

Y = tf.placeholder("float", [None, n_classes])

# Store layers weight & bias

weights = {

'h1': tf.Variable(tf.random_normal([n_input, n_hidden_1])),

'out': tf.Variable(tf.random_normal([n_hidden_1, n_classes]))

}

biases = {

'b1': tf.Variable(tf.random_normal([n_hidden_1])),

'out': tf.Variable(tf.random_normal([n_classes]))

}

# Create model

def multilayer_perceptron(x):

# Hidden fully connected layer with 256 neurons

layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])

# Output fully connected layer with a neuron for each class

out_layer = tf.matmul(layer_1, weights['out']) + biases['out']

return out_layer

# Construct model

logits = multilayer_perceptron(X)

# Define loss and optimizer

loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=Y))

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)

train_op = optimizer.minimize(loss_op)

# Initializing the variables

init = tf.global_variables_initializer()

# Save/restore model

saver = tf.train.Saver()

session = tf.Session()

session.run(init)

# Training cycle

for p in range(training_epochs):

_, c = session.run([train_op, loss_op], feed_dict={X: inputs, Y: targets})

if p % 50 == 0:

print("Step {} of {}".format(p, training_epochs))

print("Training Finished!")

# Save model (optional)

# save_path = saver.save(sess, './' + model_path)

# print("Model saved in file: %s" % save_path)

# Restore model weights from previously saved model (optional)

# saver.restore(sess, model_path)

# print("Model restored from file: %s" % save_path)

# Use model

pred = tf.nn.softmax(logits) # Apply softmax to logits

print("")

t = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,

0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,

Предыдущая главаГлава 9 из 13Следующая глава