Шаг 56.
Основы создания нейросети на Python. Создаем нейронную сеть на Python. Набор рукописных цифр MNIST. Подготовка тренировочных данных MNIST (окончание)

    На этом шаге мы приведем полный код созданной сети с загрузкой тренировочных данных.

    Обновим наш код с учетом проделанной работы. Ниже представлено состояние кода на данном этапе, включая последнее обновление.

[In 1]:
# Код для создания 3-слойной нейронной сети вместе с
# кодом для ее обучения с помощью набора данных MNIST.
# (с) Tariq Rashid, 2016
# лицензия GPLv2
import numpy
# библиотека scipy.special содержит сигмоиду expit() 
import scipy.special
# библиотека для графического отображения массивов 
import matplotlib.pyplot
# гарантировать размещение графики в данном блокноте,
# а не в отдельном окне 
%matplotlib inline

# определение класса нейронной сети 
class neuralNetwork:

    # инициализировать нейронную сеть
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        # задать количество узлов во входном, скрытом и выходном слое 
        self.inodes = inputnodes
        self.hnodes = hiddennodes 
        self.onodes = outputnodes

        # Матрицы весовых коэффициентов связей wih (между входным и скрытым
        # слоями) и who (между скрытым и выходным слоями).
        # Весовые коэффициенты связей между узлом i и узлом j следующего слоя
        # обозначены как w_i_j:
        # w11 w21
        # w12 w22 и т.д. 

        self.wih = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
        self.who = numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))

        # коэффициент обучения 
        self.lr = learningrate 

        # использование сигмоиды в качестве функции активации 
        self.activation_function = lambda x: scipy.special.expit(x)


    # тренировка нейронной сети
    def train(self, inputs_list, targets_list):
        # преобразовать список входных значений в двухмерный массив 
        inputs = numpy.array(inputs_list, ndmin=2).T
        targets = numpy.array(targets_list, ndmin=2).T

        # рассчитать входящие сигналы для скрытого слоя 
        hidden_inputs = numpy.dot(self.wih, inputs)
        # рассчитать исходящие сигналы для скрытого слоя 
        hidden_outputs = self.activation_function(hidden_inputs)

        # рассчитать входящие сигналы для выходного слоя 
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # рассчитать исходящие сигналы для выходного слоя 
        final_outputs = self.activation_function (final_inputs)

        # ошибки выходного слоя =
        # (целевое значение - фактическое значение) 
        output_errors = targets - final_outputs
        # ошибки скрытого слоя - это ошибки output_errors,
        # распределенные пропорционально весовым коэффициентам связей
        # и рекомбинированные на скрытых узлах 
        hidden_errors = numpy.dot(self.who.T, output_errors)
        # обновить весовые коэффициенты для связей между
        # скрытым и выходным слоями
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), 
                                        numpy.transpose (hidden_outputs))

        # обновить весовые коэффициенты для связей между
        # входным и скрытым слоями
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), 
                                        numpy.transpose(inputs))

    # опрос нейронной сети 
    def query(self, inputs_list):
        # преобразовать список входных значений
        # в двухмерный массив
        inputs = numpy.array(inputs_list, ndmin=2).T

        # рассчитать входящие сигналы для скрытого слоя 
        hidden_inputs = numpy.dot(self.wih, inputs)
        # рассчитать исходящие сигналы для скрытого слоя 
        hidden_outputs = self.activation_function(hidden_inputs)

        # рассчитать входящие сигналы для выходного слоя 
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # рассчитать исходящие сигналы для выходного слоя 
        final_outputs = self.асtivation_function(final_inputs)

        return final_outputs
[In 2]:
# количество входных, скрытых и выходных узлов 
input_nodes = 784
hidden_nodes = 100 
output_nodes = 10
# коэффициент обучения равен 0,3 
learning_rate = 0.3
# создать экземпляр нейронной сети
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
[In 3]:
# загрузить в список тестовый набор данных CSV-файла набора MNIST 
training_data_file = open("D:/NN/mnist_train_100.csv", 'r') 
training_data_list = training_data_file.readlines()
training_data_file.close()
[In 4]:
# тренировка нейронной сети

# перебрать все записи в тренировочном наборе данных 
for record in training_data_list:
    # получить список значений, используя символы запятой (',')
    # в качестве разделителей 
	    all_values = record.split(',')
    # масштабировать и сместить входные значения
    inputs = (numpy.asfarray(all_values[1:]) / 255 * 0.99) + 0.01
    # создать целевые выходные значения (все равны 0,01, за исключением
    # желаемого маркерного значения, равного 0,99) 
    targets = numpy.zeros(output_nodes) + 0.01
    
    # all_values[0] - целевое маркерное значение для данной записи 
    targets[int(all_values[0])] = 0.99
    n.train(inputs, targets)
Архив блокнота с созданной сетью можно взять здесь.

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

    Почему мы выбрали 784 входных узла? Вспомните о том, что это число равно произведению 28x28, представляющему количество пикселей, из которых состоит изображение рукописной цифры.

    Выбор ста скрытых узлов не имеет столь же строгого научного обоснования. Мы не выбрали это число большим, чем 784, из тех соображений, что нейронная сеть должна находить во входных значениях такие особенности или шаблоны, которые можно выразить в более короткой форме, чем сами эти значения. Поэтому, выбирая количество узлов меньшим, чем количество входных значений, мы заставляем сеть пытаться находить ключевые особенности путем обобщения информации. В то же время, если выбрать количество скрытых узлов слишком малым, будут ограничены возможности сети в отношении определения достаточного количества отличительных признаков или шаблонов в изображении. Тем самым мы лишили бы сеть возможности выносить собственные суждения относительно данных MNIST. С учетом того, что выходной слой должен обеспечивать вывод 10 маркеров, а значит, должен иметь десять узлов, выбор промежуточного значения 100 для количества узлов скрытого слоя представляется вполне разумным.

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

    На следующем шаге мы рассмотрим тестирование нейронной сети.




Предыдущий шаг Содержание Следующий шаг