Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шолле Ф. - Глубокое обучение на Python (Библиотека программиста) - 2023.pdf
Скачиваний:
6
Добавлен:
07.04.2024
Размер:
11.34 Mб
Скачать

2.5. Оглядываясь на первый пример    95

GradientTape .работает.с.тензорными.операциями:

x = tf.Variable(tf.random.uniform((2, 2))) with tf.GradientTape() as tape:

y = 2 * x + 3

grad_of_y_wrt_x = tape.gradient(y, x)

Создать экземпляр Variable с формой (2, 2) и с нулевыми

начальными значениями элементов

grad_of_y_wrt_x — тензор с формой (2, 2) как x, описывающий кривизну y = 2 * a + 3 в окрестностях x = [[0, 0], [0, 0]]

И.со.списками.переменных:

W = tf.Variable(tf.random.uniform((2, 2)))

 

b = tf.Variable(tf.zeros((2,)))

 

matmul — так называется скалярное

 

x = tf.random.uniform((2, 2))

 

 

произведение в TensorFlow

with tf.GradientTape() as tape:

 

 

 

grad_of_y_wrt_W_and_b — это

y = tf.matmul(x, W) + b

 

 

 

 

 

 

 

 

grad_of_y_wrt_W_and_b = tape.gradient(y, [W, b])

 

список с двумя тензорами, формы

 

которых совпадают с формами W и b

 

 

 

 

 

 

 

 

 

 

соответственно

 

 

 

 

 

В.следующей.главе.вы.поближе.познакомитесь.с.GradientTape.

2.5. ОГЛЯДЫВАЯСЬ НА ПЕРВЫЙ ПРИМЕР

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

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

Вот.наши.входные.данные:

(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

Теперь.вам.известно,.что.входные.изображения.хранятся.в.тензорах.NumPy. типа.float32,.имеющих.форму.(60000,.784).(обучающие.данные).и.(10000,.784) . (контрольные.данные).соответственно.

96    Глава 2. Математические основы нейронных сетей

Рис. 2.26. Связь между слоями, функцией потерь и оптимизатором в сети

Вот.наша.сеть:

model = keras.Sequential([ layers.Dense(512, activation="relu"), layers.Dense(10, activation="softmax")

])

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

Вот.как.выглядел.этап.компиляции:

model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["accuracy"])

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

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

Наконец,.вот.как.выглядел.цикл.обучения:

model.fit(train_images, train_labels, epochs=5, batch_size=128)

2.5. Оглядываясь на первый пример    97

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

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

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

2.5.1. Повторная реализация первого примера в TensorFlow

Что.лучше.покажет.полное.и.однозначное.понимание.темы,.чем.реализация. с.нуля?.Конечно,.понятие.«с.нуля».здесь.довольно.относительное:.мы.не.будем. повторно.писать.базовые.тензорные.операции.и.реализацию.обратного.распространения..Но.мы.опустимся.на.такой.низкий.уровень,.что.нам.практически. не.понадобятся.функции.из.библиотеки.Keras.

Не.волнуйтесь,.если.что-то.в.примере.останется.непонятым..В.следующей.главе. мы.подробно.рассмотрим.TensorFlow.API..А.пока.просто.попытайтесь.ухватить. суть.происходящего..Цель.этого.примера.—.помочь.кристаллизовать.понимание. математики.глубокого.обучения.с.использованием.конкретной.реализации.. Поехали!

Простой класс Dense

Ранее.вы.узнали,.что.слой.Dense .реализует.следующее.преобразование.входных. данных,.где.W .и.b .—.параметры.модели,.а.activation .—.поэлементная.функция. (обычно.relu,.но.в.последнем.слое.—.softmax):

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

Реализуем.на.Python.простой.класс.NaiveDense,.создающий.две.переменные. TensorFlow,.W .и.b,.и.имеющий.метод.__call__(),.который.применяет.предыдущее.преобразование.

98    Глава 2. Математические основы нейронных сетей

import tensorflow as tf

 

 

 

 

 

 

 

 

 

class NaiveDense:

 

 

 

 

 

 

 

 

 

def __init__(self, input_size, output_size, activation):

 

 

Создать матрицу W с формой

 

self.activation = activation

 

 

 

 

 

 

 

 

(input_size, output_size),

 

 

 

 

 

 

 

 

 

 

инициализированную

w_shape = (input_size, output_size)

 

 

 

 

 

случайными значениями

 

 

 

 

 

 

w_initial_value = tf.random.uniform(w_shape, minval=0, maxval=1e-1)

self.W = tf.Variable(w_initial_value)

 

Создать вектор b с формой (output_size,),

 

 

 

 

 

 

b_shape = (output_size,)

 

 

 

 

инициализированный нулями

 

 

 

 

 

 

 

 

 

b_initial_value = tf.zeros(b_shape)

 

 

 

 

 

 

self.b = tf.Variable(b_initial_value)

 

Выполнить

 

 

 

 

 

 

 

 

 

 

def __call__(self, inputs):

 

 

 

 

прямой проход

 

 

 

 

 

 

 

 

 

return self.activation(tf.matmul(inputs, self.W) + self.b)

@property

 

Вспомогательный метод

 

 

 

 

для получения весов из слоя

 

def weights(self):

 

 

 

 

 

 

 

 

 

 

 

 

 

return [self.W, self.b]

 

 

 

 

 

 

 

 

 

Простой класс Sequential

Теперь.создадим.класс.NaiveSequential,.объединяющий.слои.в.цепочку..Он.обер­ тывает.список.слоев.и.реализует.метод.__call__(),.который.просто.вызывает. по.порядку.слои.в.этом.списке,.передавая.входные.данные..Он.также.имеет. свойство.weights,.упрощающее.наблюдение.за.весами.слоев.

class NaiveSequential:

def __init__(self, layers): self.layers = layers

def __call__(self, inputs): x = inputs

for layer in self.layers: x = layer(x)

return x

@property

def weights(self): weights = []

for layer in self.layers: weights += layer.weights

return weights

Используя.классы.NaiveDense .и.NaiveSequential,.можно.создать.имитацию. модели.Keras:

model = NaiveSequential([

NaiveDense(input_size=28 * 28, output_size=512, activation=tf.nn.relu), NaiveDense(input_size=512, output_size=10, activation=tf.nn.softmax)

])

assert len(model.weights) == 4