Основы нейронной сети

Нейронные сети

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

  • что такое персептрон
  • что такое нейронная сеть
  • Тензоры и операции
  • дифференциал
  • градиентный спуск

начать с проблемы

Прежде чем начать, я надеюсь, что вы немного разбираетесь в машинном обучении.Предпосылка решения проблем заключается в том, чтобы задавать вопросы.Мы задаем такой вопрос, правильноMNIST数据集Проанализируйте, а затем шаг за шагом в процессе решения проблемы, чтобы прояснить задействованные концепции.

MNIST数据集набор для обучения письму отMNIST, я думаю, вы не незнакомы с ним. Это классический набор данных в области машинного обучения. Я чувствую, что любой учебник может использовать его в качестве примера, но это также доказывает классику этого набора данных. Вот краткое изложение введение:

  • Учебный набор из 60 000 примеров и тестовый набор из 10 000 примеров.
  • Изображения представлены матрицей 28 × 28, и каждое изображение представлено 784-мерным вектором.
  • Картинки разделены на 10 категорий, соответствующих от 0 до 9, всего 10 арабских цифр.

Содержимое сжатого пакета следующее:

  • train-images-idx3-ubyte.gz: training set images (9912422 bytes)
  • train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
  • t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
  • t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)

Выше:

Код генерации изображения выглядит следующим образом:

%matplotlib inline

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

def plot_digits(instances, images_per_row=10, **options):
    size = 28
    images_per_row = min(len(instances), images_per_row)
    images = instances
    n_rows = (len(instances) - 1) // images_per_row + 1
    row_images = []
    n_empty = n_rows * images_per_row - len(instances)
    images.append(np.zeros((size, size * n_empty)))
    for row in range(n_rows):
        rimages = images[row * images_per_row : (row + 1) * images_per_row]
        row_images.append(np.concatenate(rimages, axis=1))
    image = np.concatenate(row_images, axis=0)
    plt.imshow(image, cmap = matplotlib.cm.binary, **options)
    plt.axis("off")

plt.figure(figsize=(9,9))
plot_digits(train_images[:100], images_per_row=10)
plt.show()

Но вам не нужно пробовать это в спешке, Далее мы можем шаг за шагом разобрать набор для обучения письму.

Посмотрите на эту строку кода:

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

MNIST数据集пройти черезkeras.datasetsнагрузка, гдеtrain_imagesиtrain_labelsФормируется обучающая выборка, а две другие являются тестовой выборкой:

  • train_images.shape: (60000, 28, 28)
  • train_labels.shape: (60000,)

Нам нужно сделать очень просто.Закинуть обучающую выборку в нейронную сеть.После обучения генерируется нужная модель нейронной сети,а затем модель предсказывает тестовую выборку.Нам нужно только судить,верны ли предсказанные числа или не может

Прежде чем создавать нейронную сеть с помощью кода, позвольте мне кратко представить, что такое нейронная сеть, начнем с персептрона.

датчик

Персептрон — это искусственная нейронная сеть, состоящая из двух слоев нейронов, предложенная Фрэнком Розенблаттом, ее появление произвело в свое время сенсацию, ведь персептрон был первой нейронной сетью, способной обучаться

Персептрон работает следующим образом:

Три переменные слева представляют соответственно три различных двоичных входа, а выход представляет собой двоичный выход.Для множества входных данных некоторые входные данные могут быть действительными, а некоторые могут быть недействительными.Под влиянием такого количества входных данных, как судить вывод вывод? Розенблатт вводит веса, чтобы представить важность соответствующего входа.

В этот момент выход может быть выражен как:

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


import numpy as np


class Perceptron:
    """
    代码实现 Frank Rosenblatt 提出的感知器的与非门,加深对感知器的理解
    blog: https://www.howie6879.cn/post/33/
    """

    def __init__(self, act_func, input_nums=2):
        """
        实例化一些基本参数
        :param act_func: 激活函数
        """
        # 激活函数
        self.act_func = act_func
        # 权重 已经确定只会有两个二进制输入
        self.w = np.zeros(input_nums)
        # 偏置项
        self.b = 0.0

    def fit(self, input_vectors, labels, learn_nums=10, rate=0.1):
        """
        训练出合适的 w 和 b
        :param input_vectors: 样本训练数据集
        :param labels: 标记值
        :param learn_nums: 学习多少次
        :param rate: 学习率
        """
        for i in range(learn_nums):
            for index, input_vector in enumerate(input_vectors):
                label = labels[index]
                output = self.predict(input_vector)
                delta = label - output
                self.w += input_vector * rate * delta
                self.b += rate * delta
        print("此时感知器权重为{0},偏置项为{1}".format(self.w, self.b))
        return self

    def predict(self, input_vector):
        if isinstance(input_vector, list):
            input_vector = np.array(input_vector)
        return self.act_func(sum(self.w * input_vector) + self.b)


def f(z):
    """
    激活函数
    :param z: (w1*x1+w2*x2+...+wj*xj) + b
    :return: 1 or 0
    """
    return 1 if z > 0 else 0

def get_and_gate_training_data():
    '''
    AND 训练数据集
    '''
    input_vectors = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
    labels = np.array([1, 0, 0, 0])
    return input_vectors, labels


if __name__ == '__main__':
    """
    输出如下:
        此时感知器权重为[ 0.1  0.2],偏置项为-0.2 与门
        1 and 1 = 1
        1 and 0 = 0
        0 and 1 = 0
        0 and 0 = 0
    """
    # 获取样本数据
    and_input_vectors, and_labels = get_and_gate_training_data()
    # 实例化感知器模型
    p = Perceptron(f)
    # 开始学习 AND
    p_and = p.fit(and_input_vectors, and_labels)
    # 开始预测 AND
    print('1 and 1 = %d' % p_and.predict([1, 1]))
    print('1 and 0 = %d' % p_and.predict([1, 0]))
    print('0 and 1 = %d' % p_and.predict([0, 1]))
    print('0 and 0 = %d' % p_and.predict([0, 0]))

сигмовидные нейроны

Нейроны и перцептроны по сути одинаковые, разница между ними в том, что функция активации разная, например функция перехода изменена на сигмовидную функцию

Нейронная сеть может регулировать веса и смещения искусственных нейронов посредством изучения выборок, чтобы сделать выходные результаты более точными, так как же разработать такой алгоритм для нейронной сети?

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

Итак, нам нужен сигмовидный нейрон, потому что сигмовидный нейрон возвращает любое действительное число между [0, 1], так что небольшие изменения весов и смещений вызовут лишь небольшие изменения на выходе. Выход можно выразить как σ(w⋅x +b), а σ — сигмовидная функция.В сигмовидной функции S относится к сигмовидной функции, которая определяется следующим образом:

Нейронные сети

На самом деле нейронная сеть представляет собой множество нейронов, соединенных по определенным правилам.Нейронная сеть состоит из следующих компонентов:

  • Входной слой: принимаем передаваемые данные, здесь должно быть 784 нейрона
  • Скрытые слои: выявление особенностей
  • Веса между слоями: учитываются автоматически
  • Каждый скрытый слой будет иметь тщательно разработанную функцию активации, такую ​​как Sigmoid, функция активации Relu.
  • выходной слой, 10 выходов
  • Выход предыдущего слоя используется как вход следующего слоя, и информация всегда распространяется вперед и никогда не возвращается обратно: нейронная сеть с прямой связью.
  • Существуют циклы, в которых возможны циклы обратной связи: рекуррентные нейронные сети.

поступающие из входного слоя手写字训练集, а затем передать данные обучающего набора вперед через скрытый слой, и, наконец, выходной слой выведет 10 значений вероятности, сумма которых равна 1. Теперь мы можем посмотреть наKerasКод:

Первым шагом является предварительная обработка данных.Мы знаем, что исходная форма данных(60000, 28, 28), диапазон значений[0, 255], который теперь изменен на[0, 1]:

train_images = train_images.reshape((60000, 28 * 28)) 
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28)) 
test_images = test_images.astype('float32') / 255

Затем категорически закодируйте метки:

from keras.utils import to_categorical

train_labels = to_categorical(train_labels) 
test_labels = to_categorical(test_labels)

Второй шаг, напишите модель:

from keras import models 
from keras import layers

network = models.Sequential() 
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,))) 
network.add(layers.Dense(10, activation='softmax')
            
network.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])
network.fit(train_images, train_labels, epochs=5, batch_size=128)

Скрытый слой, функция активации выбранаrelu, выходной слой используетsoftmaxВозвращает массив из 10 значений вероятности, сумма которых равна 1.

При обучении показывается два числа: одно — потери сети на обучающих данныхloss, другой - точность сети на обучающих данныхacc

Очень просто, мы строим и обучаем нейронную сеть, всего несколько строк кода, причина, по которой она написана так коротко, заключается в том, чтоkerasИнтерфейсный пакет проще в использовании, но нам все равно нужно изучить теоретические знания внутри.

Представление данных для нейронных сетей

TensorFlowвнутриTensorЭто означает тензор, Данные, хранящиеся в многомерном массиве Numpy в приведенном выше примере, являются тензором: тензор — это контейнер данных, матрица — двумерный тензор, а тензор — это обобщение матрицы на любой размерности. Размерность тензора называется осью

скаляр

Тензор, содержащий число, называется скаляром (0D-тензором) следующим образом:

x = np.array(12)
print(x, x.ndim)
# 12, 0

Число осей тензора также называют рангом

вектор

Массив чисел называется вектором (одномерным тензором) следующим образом:

x = np.array([12, 3, 6, 14, 7])
print(x, x.ndim)
# [12  3  6 14  7] 1

матрица

Массив векторов называется матрицей (двумерным тензором) следующим образом:

x = np.array([[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]])
print(x, x.ndim)
# [[ 5 78  2 34  0]
# [ 6 79  3 35  1]
# [ 7 80  4 36  2]] 2

Трехмерные тензоры против многомерных тензоров

Объединение нескольких матриц в новый массив представляет собой трехмерный тензор, как показано ниже:

x = np.array([[[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]]])
print(x, x.ndim)
# (array([[[ 5, 78,  2, 34,  0],
#          [ 6, 79,  3, 35,  1]],
#  
#         [[ 5, 78,  2, 34,  0],
#          [ 6, 79,  3, 35,  1]],
#  
#         [[ 5, 78,  2, 34,  0],
#          [ 6, 79,  3, 35,  1]]]), 3)

Объединение нескольких трехмерных тензоров в массив создает четырехмерный тензор.

ключевой атрибут

Тензоры определяются следующими тремя ключевыми свойствами:

  • Количество осей: три оси для трехмерных тензоров, две оси для матриц
  • Форма: это целочисленный кортеж, например, предыдущая матрица (3, 5), вектор (5,) и трехмерный тензор (3, 2, 5)
  • тип данных

Тензор в работе в Numpy

ранее загруженныйtrain_imagesза:

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

например, выбор фрагмента10~100номера:

train_images[10:100].shape
# (90, 28, 28)

Концепция пакетной обработки данных

Модели глубокого обучения случайным образом разбивают набор данных на мини-пакеты для обработки, например:

batch = train_images[:128]
batch.shape
# (128, 28, 28)

тензоры реальных данных

Вот посмотрите на форму данных в реальном мире:

  • Векторные данные: двумерные тензоры (образцы, признаки)
  • Данные временных рядов или данные последовательности: трехмерные тензоры (выборки, временные интервалы, признаки)
  • изображение: тензор 4D (образцы, высота, ширина, каналы) или (образцы, каналы, высота, ширина)
  • видео: тензор 5D (образцы, кадры, высота, ширина, каналы) или (образцы, кадры, каналы, высота, ширина)

Тензорные операции

Расчеты, подобные компьютерным программам, могут быть преобразованы в двоичные вычисления, а вычисления глубокого обучения могут быть преобразованы в некоторые тензоры числовых данных.Тензорные операции(tensor operation)

Код скрытого слоя приведенной выше модели выглядит следующим образом:

keras.layers.Dense(512, activation='relu')

Этот слой можно понимать как функцию, которая вводит двумерный тензор и выводит двумерный тензор, точно так же, как функция вычисления, выводимая в последнем разделе персептрона выше:

output = relu(dot(W, input) + b)

Поэлементный расчет

И Relu, и операции сложения являются поэлементными операциями, такими как:

# 输入示例
input_x = np.array([[2], [3], [1]])
# 权重
W = np.array([[5, 6, 1], [7, 8, 1]])
# 计算输出 z
z = np.dot(W, input_x)

# 实现激活函数
def naive_relu(x):
    assert len(x.shape) == 2
    x = x.copy()
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] = max(x[i, j], 0) 
    return x

# 激活函数对应的输出
output = naive_relu(z)
output

транслировать

В том разделе натяжения есть такой код:

output = relu(dot(W, input) + b)

dot(W, input)является двумерным тензором,bявляется вектором, что произойдет, если добавить два тензора разных форм?

Если нет двусмысленности, меньший тензор передается в соответствии с формой большего тензора:

input_x = np.array([[1], [3]])
# 权重
W = np.array([[5, 6], [7, 8]])
b = np.array([1])
# 计算输出 z
z = np.dot(W, input_x) + b
# array([[24],
#        [32]])

Тензорное скалярное произведение

Операция скалярного произведения, также называемая тензорным произведением, например:

import numpy as np

# 输入示例
input_x = np.array([[2], [3], [1]])
# 权重
W = np.array([[5, 6, 1], [7, 8, 1]])
np.dot(W, input_x)

Скалярное произведение между двумя векторами является скаляром:

def naive_vector_dot(x, y):
    assert len(x.shape) == 1
    assert len(y.shape) == 1 
    assert x.shape[0] == y.shape[0]
    z = 0.
    for i in range(x.shape[0]):
        z += x[i] * y[i] 
    return z

x = np.array([1,2])
y = np.array([1,2])

naive_vector_dot(x, y)

# 5.0

Скалярное произведение матрицы и вектора является вектором:

np.dot(W, [1, 2, 3])
# array([20, 26])

Тензорная деформация

При предварительной обработке данных перед:

train_images = train_images.reshape((60000, 28 * 28)) 
train_images = train_images.astype('float32') / 255

В приведенном выше примере форма входных данных изменяется на (60000, 784). Деформация тензора относится к изменению строк и столбцов тензора для получения желаемой формы. Количество наборов данных до и после не изменяется. Часто встречаются специальные Тензорная деформация представляет собой транспозицию следующим образом:

x = np.zeros((300, 20))
x = np.transpose(x)
x.shape
# (20, 300)

Оптимизация градиента

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

output = relu(dot(W, input_x) + b)

в:

  • relu: функция активации
  • W: тензор, представляющий вес, первый шаг может принимать небольшое случайное значение для случайной инициализации
  • b: представляет собой тензор, представляющий смещение

Теперь нам нужен алгоритм, который позволит нам найти веса и смещения, такие что y = y (x) соответствует выборке входных данных x

вернуться к персептрону

Процесс обучения персептрона - это процесс, в котором веса и смещения постоянно настраиваются и обновляются.Смещения можно понимать как значение веса, вход которого равен 1, так как же обновляются веса?

Во-первых, представим понятие функции потерь, цитируя объяснение из книги «Статистические методы обучения» г-на Ли Ханга:

Задача обучения с супервизором состоит в том, чтобы выбрать в качестве решающей функции в пространстве гипотез модель F. Для данного входа X соответствующий выход Y задается f (x), а прогнозируемое значение f (x) этого выхода также равно согласуется с реальным значением y. Это может быть несовместимо с функцией потерь (функцией потерь) или функцией стоимости, измеряется степень ошибки прогнозирования, функция потерь представляет собой неотрицательную функцию реального значения F (x) и y, не забудьте L (Y, F (X))

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

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

  • Что такое производная
  • что такое градиент

Что такое производная

Предположим, что существует непрерывная гладкая функцияf(x) = y, что такое функциональная непрерывность? Это означает, что небольшие изменения x могут привести только к небольшим изменениям y.

Предположим, что две точки на f(x)a,bдостаточно близко, тоa,bмогут быть аппроксимированы как линейная функция, где их наклон равенk, то можно сказать, что наклон k равен f в точке bПроизводная

Таким образом, производная описывает, как изменяется f(x) при изменении x. Если вы хотите уменьшить значение f(x), просто переместите x на небольшой шаг в направлении, противоположном производной, и наоборот.

что такое градиент

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

Стохастический градиентный спуск

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

Этот раздел кода персептрона:

self.w += input_vector * rate * delta

Это соответствует правилам, полученным из приведенной выше формулы

Суммировать

Давайте посмотрим на весь код модели распознавания рукописных слов:

from keras import models 
from keras import layers
from keras.utils import to_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

train_images = train_images.reshape((60000, 28 * 28)) 
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28)) 
test_images = test_images.astype('float32') / 255

train_labels = to_categorical(train_labels) 
test_labels = to_categorical(test_labels)


network = models.Sequential() 
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,))) 
network.add(layers.Dense(10, activation='softmax'))

network.compile(optimizer='rmsprop',loss='categorical_crossentropy', metrics=['accuracy'])
network.fit(train_images, train_labels, epochs=5, batch_size=128)

test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)
  • Входные данные сохраняются вfloat32форматированныйNumpyВ тензоре формы (60000, 784) и (10000, 784)
  • Структура нейронной сети: 1 входной слой, один скрытый слой и один выходной слой.
  • categorical_crossentropy — функция потерь для моделей классификации.
  • Каждая партия из 128 образцов, всего 5 итераций, всего обновлений (469 * 5) = 2345 раз

инструкция

Книги и статьи, которые повлияли на эту статью, благодаря их вкладу: