Сегодняшний рассказ о том, как работают нейронные сети. Во-первых, мы собираемся их визуализировать и объяснить их структуру:

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

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

Веса изображены в виде синапсов, а сама идея создания нейронов в информатике изначально была заимствована из биологии. Анализируя нейронную сеть, мы можем утверждать, что она состоит из слоев (в нашем случае 6), причем слои представляют собой просто стопку нейронов. Первый слой обычно называют входным слоем, поскольку он включает в себя информацию из набора данных. Нашим руководящим примером будет классификация изображений с рукописными цифрами как цифрами от 0 до 9. Предположим, что наши изображения имеют представление 28*28 в оттенках серого (0 — полностью черный, а 255 — полностью белый), наш входной слой будет состоять из 784 нейронов, каждый из которых имеет значение от 0 до 1 (после масштабирования значений каждого пиксель):

Последний слой называется выходным слоем, и он содержит столько же нейронов, сколько классов наших данных, для нашего экземпляра 10. Промежуточные слои называются скрытыми слоями, потому что мы не можем вмешиваться в процесс обучения. Еще одним важным фактором является то, как наши слои будут связаны, и один из способов — это плотные соединения, при которых все нейроны слоя n-1 соединены со всеми нейронами слоя n. Давайте проанализируем подробнее, как функционирует вся сеть. На каждом слое мы пытаемся найти некоторые закономерности, такие как петли или края, которые могут помочь нам предсказать, что это изображение представляет собой определенную цифру. Эти закономерности выражаются через веса и смещения каждого синапса, а также в ходе обучения они изменяются для достижения высокой точности прогнозов. Но есть разница между математическим выражением нейрона и нейрона нейронной сети, и она называется функцией активации. Ранее мы объясняли, что нейрон — это просто взвешенная сумма, но в этих структурах выходным сигналом нейрона является значение функции активации с вводом этой взвешенной суммы. Но зачем нам добавлять сложность? Основная причина в том, что указанная выше сумма может иметь любое значение от -∞ до +∞, и мы хотим втиснуть это значение между [0,1] или даже [-1,1], а наиболее часто реализуемые из них изображены ниже:

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

Проанализировав основы нейронных сетей, мы должны понять, как мы их обучаем и, что более важно, что мы тренируем. Что ж, ответ заключается в весах и смещениях, и сначала они инициализируются случайным образом. Учитывая это, я думаю, вполне вероятно, что результатом будет полный беспорядок, поскольку сеть не имеет никаких знаний о данных. Шаг за шагом эти параметры изменяются и изменяются, пока мы не получим надежную модель (помните, что с их помощью выражаются наши шаблоны распознавания, поэтому, изучая их, мы понимаем суть наших данных). Опять же, у нас есть еще одна проблема: что на самом деле означает надежность? Давайте рассмотрим эту концепцию. Как и в машинном обучении с контролируемым или даже неконтролируемым обучением, мы оцениваем наши модели с точностью, учитывая правильные и прогнозируемые значения. В результате у нас есть метрика функции потерь, которая оценивает нашу модель. Давайте возьмем более удобную парадигму: в нашем поясняющем примере мы классифицируем изображения как цифры. После одного сеанса обучения (часто называемого эпохой) мы имеем в выходном слое десять нейронов со значениями от 0 до 1, а значение, близкое к 1 i-го нейрона, означает очень высокую вероятность того, что изображение представляет собой i-ю цифру. Итак, мы используем среднеквадратическую ошибку в качестве функции потерь и предположим, что у нас есть изображение, прогнозы и истинные метки, как это изображение (особая благодарность 3Blue1Brown):

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

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

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

a — это скорость обучения, небольшое плавающее число, показывающее, насколько нам следует двигаться в направлении локального минимума (мы должны искать глобальный минимум, но это практически невозможно). Этот метод называется градиентным спуском, и у него есть много других версий, таких как стохастический градиентный спуск. До сих пор мы приводили формулу процедуры обучения, но не говорили о нахождении градиента 13000 (в реальных примерах даже больше) переменных. Что ж, это самый важный этап обучения. Давайте поговорим об одном нейроне с экземпляром изображения, которое представляет число 4.

Как видно на предыдущем изображении, наша модель в выходном слое на четвертом нейроне имеет цвет очень черный, поэтому у него мало шансов спрогнозировать его как 4. Что мы можем сделать, так это отрегулировать веса предыдущего слоя, чтобы сделать его ярче. вверх. Мы можем значительно уменьшить третий синапс, сильно увеличить второй и немного уменьшить первый. Это довольно хорошее приближение к диплому. Итак, представьте, что во многих примерах это происходит с каждым нейроном каждого слоя. Чтобы быть более конкретным, я задам это более четко: каждый нейрон из выходного слоя и обратно сравнивает свой выход с истинным (помните, что мы находимся в режиме обучения), он объявляет, какие изменения следует сделать в предыдущих слоях и сумму все нейроны N-го слоя применяются к (N-1)-му нейронам вплоть до входного слоя, и весь этот процесс выполняется итеративно до тех пор, пока он не сойдётся (более аналитическое и наглядное объяснение смотрите в этом видео: https://youtu .be/Ilg3gGewQ5U). Этот алгоритм обучения называется обратным распространением ошибки, и это способ подойти к довольно сложному расчету градиента многих тысяч переменных. Для более быстрого и эффективного способа мы используем стохастический градиентный спуск, который предоставляет модели пакеты изображений вместо одного экземпляра за раз. На этом этапе я хотел бы углубиться в код, так как не думаю, что какие-либо другие элементы исчисления необходимы. Сначала я собираюсь объяснить, что делает код, а затем объяснить его реализацию. Сначала я собираюсь импортировать необходимые библиотеки и набор данных fmnist_fashion, который содержит изображения в оттенках серого. Каждое изображение состоит из 28*28 пикселей со значением от 0 до 255 (от черного до белого). После печати формы каждого набора данных мы можем распечатать значение конкретного пикселя и даже изображения (я выбрал пятое):

Последний шаг перед построением модели — это предварительная обработка данных и масштабирование значений пикселей путем погружения на 255, чтобы сжать значение каждого до [0,1]. Модель является последовательной с размером входного слоя (28,28), и мы используем сглаживающий слой, чтобы «растянуть» пиксели и векторизировать их во входной слой. Скрытый слой плотный, а его функция активации — танх (количество нейронов зависит от ваших собственных экспериментов). Выходной слой имеет 10 нейронов, и softmax используется в качестве функции активации для расчета возможностей каждого класса. После этого мы компилируем модель с метриками точности и оптимизатором Адама и обучаем ее в течение 10 эпох (10 итераций для расчета градиента), и результаты приведены ниже:

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

#importing the libraries
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
#loading the dataset
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
print(train_images.shape)
print(test_images.shape)
#we can get a look at a single pixel
print(train_images[3,13,4])#black pixel
print(train_labels[:5])
#this dataset consists of 10 categories of clothing
#the labels are from 0-9
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
#We can plot an image as shown below
plt.figure()
plt.imshow(train_images[5])
plt.colorbar()
plt.grid(False)
plt.show()
#Data preprocessing
train_images, test_images = train_images / 255.0, test_images / 255.0
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(64, activation='tanh'),
    keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=10)
test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=1)
print('Test accuracy:', test_acc)
predictions = model.predict(test_images)
print(predictions[0])
predictions = model.predict(test_images)
print(predictions[2])
print(np.argmax(predictions[2]))
print(test_labels[2])
COLOR = 'white'
plt.rcParams['text.color'] = COLOR
plt.rcParams['axes.labelcolor'] = COLOR

def predict(model, image, correct_label):
  class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
  prediction = model.predict(np.array([image]))
  predicted_class = class_names[np.argmax(prediction)]

  show_image(image, class_names[correct_label], predicted_class)


def show_image(img, label, guess):
  plt.figure()
  plt.imshow(img, cmap=plt.cm.binary)
  plt.title("Excpected: " + label)
  plt.xlabel("Guess: " + guess)
  plt.colorbar()
  plt.grid(False)
  plt.show()


def get_number():
  while True:
    num = input("Pick a number: ")
    if num.isdigit():
      num = int(num)
      if 0 <= num <= 1000:
        return int(num)
    else:
      print("Try again...")

num = get_number()
image = test_images[num]
label = test_labels[num]
predict(model, image, label)

Надеюсь, я предоставил вам основы архитектуры нейронных сетей и всю их концепцию. Не стесняйтесь спрашивать все, что хотите, и следите за новыми историями! (полезные видео как по программированию, так и по объяснению нейронных сетей вы можете найти в «Tech With Tim» и «3Blue1Brown»)

Сообщение от AI Mind

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь: