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

Who = [53.976871, -61.523507, 0.13521682]

Напомним, что весовые коэффициенты фактически соответствуют нейронным связям, возникающим в “настоящих” биологических сетях. Тестирование показывает, что обученная сеть действительно воспроизводит функцию XOR практически достоверно, лишь с небольшой погрешностью:

Input [0.0, 0.0], output [0.0000266]

Input [0.0, 1.0], output [0.95193190]

Input [1.0, 0.0], output [0.95074983]

Input [1.0, 1.0], output [0.07137252]

Как показывает тестирование, если увеличить число скрытых нейронов до 3х, обучение происходит гораздо быстрее, и не требует столь большого числа итераций. Уже 50000 итераций достаточно для приемлемой точности, лог программы выглядит примерно так:

Network: input/hidden/output: 2/3/1

Train phase:

Step 0 error 0.418338057819

Step 10000 error 0.00320960246906

Step 20000 error 0.00115714673178

Step 30000 error 0.00176164027913

Step 40000 error 4.34585435915e-05

wIH [[6.243515, -4.35533, 0.1199493], [5.040033, 5.132244, 0.35661004], [-4.4208235, 6.207919, 0.2522050]]

wHO [-11.2227881, 14.0531446, -11.2731065, 0.9389702]

Test phase:

Input [0.0, 0.0], output [0.0436959]

Input [0.0, 1.0], output [0.9712140]

Input [1.0, 0.0], output [0.9721711]

Input [1.0, 1.0], output [0.0075550]

Как можно видеть, даже такая простая нейронная сеть, изначально заполненная случайными коэффициентами, смогла обучившись на наборе данных, корректно воспроизвести его. Разумеется, количество как входов, так и выходов, может быть другим, функция XOR была взята лишь для наглядности.

Разумеется, описанная выше сеть является очень простой, в качестве более сложного примера можно привести архитектуру так называемой “сверточной сети” (convolutional neural network, CNN). Такая сеть “разбивает” входные данные на фрагменты, каждый из которых в свою очередь выделяет на изображении следующими подсетями какие-то ключевые детали. Подобные сети используются например, в задачах распознавания образов, в частности, рукописного текста.

Фото с сайта https://www.kernix.com

Несмотря на всю сложность (сети могут содержать тысячи нейронов, а их обучение может занимать не один день), принципы функционирования модели нейрона в общем, практически те же, что и в вышеописанной функции XOR.

И еще один философский вопрос, который может возникнуть при рассмотрении нейронных сетей - возможен ли искусственный интеллект, сможет ли компьютер мыслить. Ответ не так прост как кажется. С физической точки зрения - ответ однозначно “да”. По отдельности нейроны нашего мозга состоят из весьма простых компонентов, и нет никакого принципиального запрета смоделировать их работу на компьютере. С алгоритмической точки зрения - узнать как работает самосознание, пока что никто не смог, данный вопрос еще открыт, как для математиков, так и для физиологов. Таким образом, запрограммировать на компьютере процесс мышления пока невозможно, но лишь потому, что собственно принцип еще неизвестен. Впрочем, учитывая скорость развития современных технологий, можно предположить что это лишь вопрос времени, причем возможно не очень дальнего. Хорошо это или нет - вопрос другой, выходящий за рамки данного текста. Ну и для понимания сложности данной задачи - для симуляции лишь 1% человеческого мозга (а это 1.7млрд нервных клеток и 10.4трлн синапсов) в течении 1 секунды, в 2013 году понадобилось 40 минут расчетов на суперкомпьютере из 82000 процессоров.

Что касается нейронных сетей, рассмотренных выше (MLP, CNN), то разумеется, никаким сознанием они обладать не могут, такие сети могут лишь научиться выполнять какую-то одну, пусть и весьма сложную, задачу. Тут еще важно не путать два принципиально разных момента - можно создать “симулятор чата”, загрузив в компьютер сотни тысяч фраз и ответов на них, и даже человек не сможет отличить такого “собеседника” от живого (что-то похожее работает например, в Siri), но полноценным интеллектом с собственным сознанием (есть даже термин “strong AI”) это не является.

20. Нейронные сети - распознавание текста

Рассмотрим практический пример - распознавание выше написанной нейронной сетью рукописного текста. Для тренировки сети воспользуемся имеющейся в открытом доступе базой MNIST, содержащей 60000 черно-белых рукописных изображений цифр размером 28x28пкс.

Выглядят изображения примерно так:

Массив изображений каждой цифры хранится в виде чисел float в диапазоне от 0 до 1, длина массива равна 28х28 = 784 значения. Кстати, при выводе массива в консоль форма цифры вполне узнаваема:

(картинка соответствует числу “5”)

Всего таких чисел, как было сказано, 60000, что вполне хватит для тренировки нашей сети.

Воспользуемся уже готовым классом MLP, изменим лишь некоторые параметры:

Число входов будет равно 28х28 = 784, т.к. на вход нейросети будет подаваться массив целиком.

На выходе нейросеть должна давать значения от 0 до 9, однако мы не можем получить значение > 1, так что воспользуемся другим путем: нейросеть будет иметь 10 выходов, где “1” будет соответствовать указанной цифре. Т.е. для числа “0” мы получим [1,0,0,0,0,0,0,0,0,0], для “1” [0,1,0,0,0,0,0,0,0,0], и так далее.

Число скрытых нейронов мы берем наугад, например 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().

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