Tensorflow — глубокие и рекуррентные нейронные сети

TensorFlow
Tensorflow — глубокие и рекуррентные нейронные сети

«Это 26-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г.".

используется в дальнейшемtfсокращенный представительtensorflow.

1. Глубокое обучение и глубокие нейронные сети

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

Глубокие нейронные сети: наиболее распространенный метод реализации глубокого обучения.

Ограничения линейных моделей:

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

Функция активации для достижения линеаризации:

Несколько часто используемых нелинейных функций активации:

  • Функция ReLU:

    f(x)=max(x,0)f(x)=max(x,0)
  • сигмовидная функция:

    f(x)=11+exf(x)=\frac{1}{1+e^{-x}}
  • тан функция:

    f(x)=1e2x1+e2xf(x)=\frac{1-e^{-2x}}{1+e^{-2x}}

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

a = tf.nn.relu(tf.matmul(x, w1) + biases1)
y = tf.nn.relu(tf.matmul(a, w2) + biases2)

Многослойная сеть для решения операции XOR:

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

2. Функция потерь

2.1 Классическая функция потерь

Наиболее распространенный метод для нейронных сетей для решения задач мультиклассификации состоит в том, чтобы задать n выходных узлов, каждый из которых соответствует соответствующей категории, так что можно получить n-мерный массив.В идеале, если выборка принадлежит категории k, тогда выход класса должен иметь значение 1, а остальные классы должны быть 0. Например: [1, 0, 0, 0, 0, 0, 0 , 0, 0, 0]. Как оценить близость выходного вектора к ожидаемому вектору?перекрестная энтропияявляется одним из наиболее часто используемых методов оценки.Перекрестная энтропия характеризует расстояние между двумя распределениями вероятностей., которая является широко используемой функцией потерь в задачах классификации.

перекрестная энтропия: Учитывая вероятность p, q, перекрестная энтропия p, представленная q, равна:

H(p,q)=xp(x)*logq(x)H(p,q)=-x\sum p(x)*logq(x)

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

Кросс-энтропийный анализ кода:

cross_entroy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, le-10, 1.0)))
# 其中,y_代表正确结果,y代表预测结果
# clip_by_value可以将一个张量中的数值限制在一个范围内,本例中小于y的值都被换成y,大于le-10的值都被换成le-10
v1 = tf.constant([1.0, 2.0], [3.0, 4.0])
v2 = tf.constant([5.0, 6.0], [7.0, 8.0])
print tf.matmul(v1, v2).eval()
# 输出矩阵v1 v2相乘的结果

Обычно в tf кросс-энтропия и регрессия softmax используются вместе, поэтому tf инкапсулирует эти две функции и предоставляет функцию tf.nn.softmax_cross_entroy_with_logits:

cross_entroy = tf.nn.softmax_cross_entroy_with_logits(labels = y_, logits = y)
# 其中y代表了原始神经网络的输出结果,而y_给出了标准答案

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

MSE(y,y')=1n(yiyi')2nMSE(y,y')=\sum_1^n \frac{(y_i-y_i')^2}{n}

Где yi — правильный ответ i-ых данных в пакете, а yi' — прогнозируемое значение, заданное нейронной сетью. Следующий код описывает, как реализовать:

mse = tf.reduce_mean(tf.square(y_ - y))
# 这里的减法也是两个矩阵中对应元素的减法

2.2 Пользовательская функция потерь

loss = tf.reduce_sum(tf.where(tf.greater(v1 ,v2), (v1 - v2)*a, (v2 - v1)*b))
# tf.greater函数作用是比较大小
# tf.where有三个参数,第一个为选择依据,真就选第二个,假就选第三个

3. Алгоритм оптимизации нейронной сети

Алгоритм градиентного спуска, алгоритм обратного распространения:

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

3.1 Центральная идея градиентного спуска

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

Давайте используем пример, чтобы показать рабочий процесс алгоритма градиентного спуска: предположим, что начальное значение параметра равно 5, скорость обучения равна 3, а функция оптимизацииJ(x)=x2J(x) = x^2(Градиент является производным):

количество раундов Текущее значение параметра раунда градиент * скорость обучения значение параметра после обновления
1 5 2*5*0.3=3 2
2 2 2*2*0.3=1.2 0.8
3 0.8 2*0.8*0.3=0.48 0.32
4 0.32 2*0.32*0.3=0.192 0.128
5 0.128 2*0.128*0.3=0.0768 0.0512

Процесс оптимизации нейросетевого алгоритма можно разделить на два этапа:

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

Недостатки алгоритма градиентного спуска:

  • Оптимальное решение может быть не достигнуто: только когда функция потерь является выпуклой функцией, алгоритм градиентного спуска может дать глобальное оптимальное решение;
  • Время расчета слишком велико: поскольку потери необходимо минимизировать для всех обучающих данных, функция потерь J(x) представляет собой сумму потерь для всех обучающих данных, поэтому потери для всех обучающих данных необходимо вычислять в каждом функция итерации;
  • Для ускорения процесса обучения можно использовать алгоритм стохастического градиентного спуска, так как алгоритм стохастического градиентного спуска оптимизирует только параметры на определенных обучающих данных;
  • Как правило, в практических приложениях используется комбинация двух вышеуказанных алгоритмов — каждый раз вычисляется функция потерь небольшой части обучающих данных. Эта небольшая часть данных называется пакетом.

Обучение нейронных сетей обычно следует следующему процессу:

batch_size = n
# 每次读取一小部分数据作为当前的训练数据来执行反向传播算法
x = tf.placeholder(tf.float32, shape=(batch_size, 2), name='x-input')
y_ = tf.placeholder(tf.float32, shape=(batch_size, 1), name='y-input')

# 定义神经网络结构和优化算法
loss = ...
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)

# 训练神经网络
with tf.Session() as sses:
# 参数初始化
...
# 迭代的更新参数
for i in range(STEPS)
#准备batch_size个训练数据。一般将所有训练数据随机打乱之后再选取可以得到更好的优化效果
current_X, current_Y = ...
sess.run(train_step, feed_dict={x:current_X, y_:current_Y})

4. Дальнейшая оптимизация нейросети

4.1 Настройка скорости обучения

скорость обучения: Управление скоростью обновления параметров.

Если скорость обучения слишком велика, результаты прогнозирования будут неточными, а скорость обучения будет слишком маленькой.Хотя сходимость очень хорошая, она требует много времени.Чтобы решить эту проблему и лучше установить скорость обучения, tf предоставляет метод экспоненциального затухания: tf Функция train.exponential_decay реализует экспоненциально затухающую скорость обучения. С помощью этой функции можно использовать более высокую скорость обучения для быстрого получения оптимального решения, а затем скорость обучения можно постепенно снижать по мере продолжения итерации, делая модель более стабильной на более позднем этапе обучения.

Функция exponential_dacay экспоненциально снижает скорость обучения, и следующий код реализует ее функцию:

decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
# 其中decayed_learning_rate为每一轮优化时使用的学习率
# learning_rate为事先设定的初始学习率
# decay_rate为衰减系数
# decay_steps为衰减速度

Функция Tf.train.exponential может выбирать различные методы затухания, устанавливая параметры. Лестница по умолчанию имеет значение false, а global_step/decay_steps будут интегрированы, когда для нее установлено значение true, что делает скорость обучения функцией лестницы. При таких настройках DECAY_STEPS обычно представляет собой полное количество итерационных колес, необходимых для использования обучающих данных. Это итеративное число представляет собой общие данные обучающей выборки, разделенные на обучающую выборку в каждой партии. Сценарий применения этого параметра заключается в том, что каждый раз, когда вы обучаете данные, скорость обучения уменьшается один раз, что может сделать все данные в обучающих данных равной роли в обучении модели. При использовании скорости обучения с непрерывным индексом затухания разные данные обучения имеют разные скорости обучения, а когда скорость обучения снижается, соответствующие данные обучения по результатам результатов обучения модели малы.

Ниже приведен код, демонстрирующий использование функции tf.train.exponential_decay:

global_step = tf.Variable(0)
     
# 通过exponential_decay函数生成学习率
learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
#使用指数衰减的学习率,在minimize函数中传入global_step将自动更新global_step参数,从而使得学习率也得到相应的更新
learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(...my loss..., global_step=global_step)
# 上述函数指定了初始学习率为0.1,因为指定了staircase为True,所以每训练100轮后学习率乘以0.96.一般来说初始学习率、衰减系数和衰减速度都是根据经验设置的。

4.2 Проблема переобучения

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

Чтобы избежать проблем с переоснащением, очень распространенным методом является регуляризация. Идея регуляризации заключается в добавлении к функции потерь показателя, характеризующего сложность модели. Предполагая, что функция потерь, используемая для характеристики модели на обучающих данных, равна J (x), тогда вместо прямой оптимизации J (x) оптимизируйте J (x) + bR (w), где R (w) характеризуется как сложность модели, а b представляет собой долю потери сложности модели в общей сумме потерь. Здесь x представляет все параметры нейронной сети, включая вес w и смещение b на ребре. Вообще говоря, сложность модели определяется только весом w. Есть две часто используемые функции: одна — регуляризация L1, а другая — регуляризация L2. Независимо от метода регуляризации, основная идея состоит в том, чтобы ограничить размер весов, чтобы модель не могла произвольно соответствовать шуму листа обучающих данных.

L1 и L2 также различаются: регуляризация L1 сделает параметры разреженными, а L2 — нет. (Разница в том, что один принимает абсолютное значение, а другой принимает квадратное значение. Когда параметр очень мал, влияние квадрата игнорируется.) Следующий код показывает метод регуляризации L2, предоставленный tf:

w = tf.Variable(tf.random_normal([2, 1], stddev = 1, seed  =1))
y = tf.matmul(x, w)

loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.l2_regularizer(lambda)(w)
# loss为定义的损失函数,它由两部分组成
# 一部分是均方误差损失函数,刻画了模型在训练数据上的表现
# 第二部分就是正则化,它防止模型过渡模拟训练数据中的随机噪声
# lambda参数表示了正则化项的权重
# w为需要计算正则化损失的参数
# 以下代码展示了L1正则化
print sses.run(tf.contrib.layers.l1_regularizer(.5))(weights)//其中.5为正则化项的权重

4.3 Модель скользящего среднего

Метод повышения надежности модели на тестовых данных — модель скользящего среднего.

tf.train.ExponentialMovingAverage предоставляется в TensorFlow для реализации модели скользящего среднего. Перед инициализацией этой функции укажите скорость затухания, затухание. Эта скорость затухания используется для управления скоростью обновления модели. ExponentialMovingAverage поддерживаеттеневая переменная(теневая переменная), начальное значение этой теневой переменной является начальным значением соответствующей переменной, и каждый раз, когда запускается обновление переменной, значение теневой переменной будет обновляться следующим образом:

shadow_variable=decay*shadow_variable+(1decay)*variableshadow\_variable = decay*shadow\_variable + (1-decay)*variable

Где shadow_variable — это теневая переменная, переменная — это переменная, которую нужно обновить, а затухание — это скорость затухания. Можно сделать вывод, что чем больше распад, тем стабильнее модель.

ExponentialMovingAverage также предоставляет параметр num_updates для динамической установки размера затухания.

5. Рекуррентные нейронные сети

Рекуррентная нейронная сеть — рекуррентная нейронная сеть, RNN

Сеть долговременной кратковременной памяти — долговременная кратковременная память, LSTM

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

1.png

Приведенный выше рисунок представляет собой типичную модель рекуррентной нейронной сети.Как видно из приведенного выше рисунка, в каждый момент времени tрекуррентная нейронная сеть будет давать выходные данные в соответствии с входными данными в это время в сочетании с состоянием текущей модели, и обновить состояние модели. Помимо входа от xt, вход основного тела A рекуррентной структуры также имеет ребро цикла для обеспечения скрытого состояния ht-1 в предыдущий момент.В каждый момент модуль A рекуррентной нейронной сети считывает xt и ht-1 после чтения xt и ht-1 будет сгенерировано новое скрытое состояние ht и вывод ot в этот момент. Поскольку операции и переменные в модуле А в разное время одни и те же, рекуррентную нейронную сеть теоретически можно рассматривать как результат бесконечного повторения одной и той же структуры нейронной сети. Подобно тому, как сверточные нейронные сети совместно используют параметры в разных пространственных положениях, рекуррентные нейронные сети совместно используют параметры в разное время, тем самым обрабатывая последовательности произвольной длины с ограниченными параметрами.

После расширения циклической нейронной сети ее можно рассматривать как нейронную сеть с прямой связью с промежуточными слоями N. Эта нейронная сеть с прямой связью не имеет циклических связей, поэтому ее можно обучать напрямую с помощью алгоритма обратного распространения без других специальных алгоритмов оптимизации. Такой метод обучения называется «обратное распространение во времени» и является наиболее распространенным методом обучения рекуррентных нейронных сетей. Из структуры рекуррентной нейронной сети видно, что она наиболее подходит для решения задач, связанных с временными рядами, и в основном используется в распознавании речи, языковой модели, машинном переводе и анализе временных рядов. Структурная схема рекуррентной нейронной сети с использованием однослойной полносвязной нейронной сети в качестве тела цикла:

2.png

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

6. Структура долговременной кратковременной памяти (LSTM)

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

3.png

В так называемой структуре «ворот» используется сигмовидная нейронная сеть и операция побитового умножения, которые вместе образуют структуру «ворот». Это называется воротами, потому что полностью подключенный слой нейронной сети, использующий сигмоид в качестве функции активации, будет выводить значение от 0 до 1, описывающее, сколько информации текущий вход может передать через структуру. Когда «ворота» открыты, выход слоя сигмовидной нейронной сети равен 1, и вся информация может пройти. Когда «ворота» закрыты, выход слоя сигмовидной нейронной сети равен 0, и вся информация не может пройти. через. В LSTM ворота забывания и входные ворота являются наиболее важными частями.

Роль ворот забывания состоит в том, чтобы позволить рекуррентной нейронной сети забыть ранее бесполезную информацию. Ворота забывания решат, какую часть памяти нужно забыть, в соответствии с текущим входом Xt и выходом Ht-1 в предыдущий момент. Предположим, что размерность состояния c равна n. Ворота забывания будут использовать сигмоид для вычисления вектора размерности n на основе текущего входа xt и выхода ht-1 в предыдущий момент, и его значение в каждом измерении находится в диапазоне (0, 1). Затем перемножить состояние ct-1 предыдущего момента и эту величину побитно, тогда размерность со значением f, близким к 0, будет забыта, а размерность со значением f, близким к 1, сохранится. Функция входных ворот состоит в том, чтобы дополнить новую память после того, как рекуррентная нейронная сеть забудет часть памяти. Входной вентиль определяет, какая информация добавляется к состоянию ct-1 для генерации нового состояния ct на основе xt и ht-1. Конкретная формула для каждого вентиля определяется следующим образом:

z=tanh(Wz[ht1,xt])(входное значение)z=tanh(W_z[h_{t-1},x_t])(входное значение)
i=sigmoid(Wi[ht1,xt])(входные ворота)i=sigmoid(W_i[h_{t-1},x_t])(входной вентиль)
f=sigmoid(Wf[ht1,xt])(Забытые ворота)f=sigmoid(W_f[h_{t-1},x_t]) (забывая ворота)
o=sigmoid(Wo[ht1,xt])(выходные ворота)o=sigmoid(W_o[h_{t-1},x_t])(выходной вентиль)
ct=f*ct1+i*z(новое состояние)c_t=f*c_{t-1}+i*z (новое состояние)
ht=o*tanhct(вывод)h_t=o*tanhc_t(выход)

вWzW_z,WiW_i,WfW_f,WoW_oпредставляет собой матрицу параметров 4 измерений [2n, n].

import tensorflow as tf
# 定义一个LSTM结构。在TensorFlow中通过一句简单的命令就可以实现一个完整的LSTM结构。
# LSTM中使用的变量也会在函数中自动被声明
lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_hidden_size)
# 将LSTM中的状态初始化为全0数组。BasicLSTMCell类提供了zero_state函数来生成
# 全零的初始状态。state是一个包含两个张量的LSTMStateTuple类,其中state.c和state.h
# 分别对应了c状态和h状态
# 和其他神经网络类似,在优化循环神经网络时,每次也会使用一个batch的训练样本
state = lstm.zero_state(batch_size, tf.float32)
# 定义损失函数
loss = 0.0
# 虽然在测试时循环神经网络可以处理任意长度的序列,但是在训练中为了将循环网络展开成
# 前馈神经网络,我们需要知道训练数据的序列长度。以下代码中的num_steps就表示这个长度
for i in range(num_steps):
    # 在第一个时刻声明LSTM结构中使用的变量,在之后的时刻都需要服用之前定义好的变量
    if i > 0 : tf.get_variable_scope().reuse_variables()
    # 每一步处理时间序列中的一个时刻。将当前输入currnt_input和前一时刻的状态state
    # 传入定义的LSTM结构中可以得到当前LSTM的输出lstm_output和更新后的状态state。lstm_output
    # 用于输出给其他层,state用于输出给下一时刻,它们在dropout等方面可以有不同的处理方式
    lstm_output, state = lstm(current_input, state)
    # 将当前时刻LSTM结构的输出传入一个全连接层得到最后的输出
    final_output = fully_connected(lstm_output)
    # 计算当前时刻输出的损失
    los += calc_loss(final_output, expected_output)
# 接下来就是训练模型

7. Варианты рекуррентных нейронных сетей

7.1 Двунаправленные рекуррентные нейронные сети и глубокие рекуррентные нейронные сети

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

Основная часть двунаправленной рекуррентной нейронной сети представляет собой комбинацию двух одночленных рекуррентных нейронных сетей. В каждый момент времени t эти две рекуррентные нейронные сети одновременно получают входные данные в противоположных направлениях. Две сети выполняют вычисления независимо друг от друга, каждая из которых генерирует новое состояние и выводит результат в данный момент, а конечный результат двунаправленной рекуррентной сети представляет собой простое сращивание двух однонаправленных рекуррентных нейронных сетей. Тело цикла в каждом слое сети может свободно выбирать любую структуру, как было представлено ранее.Простой RNN, LSTMвозможно.

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

import tensorflow as tf
# 定义一个基本的LSTM结构作为循环体的基础结构。深层循环神经网络也支持使用其他循环体结构
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell
# 通过MultiRNNCell类实现深层循环神经网络中每一时刻的前向传播过程。其中
# number_of_layers表示多少层。初始化MutiRNNCell,否则TensorFlow会在每一层之间共享参数
stacked_lstm = tf.nn.rnn_cell.MultiRNNCell(
    lstm_cell(lstm_size) for _ in range(number_of_layers)
)
# 和经典循环神经网络一样,可以通过zero_state函数来获取初始状态
state = stacked_lstm.zero_state(batch_size, tf.float32)
# 计算每一时刻的前向传播结果
for i in range(len(num_steps)):
    if i > 0: tf.get_variable_scope().reuse_variables()
    stacked_lstm_output, state = stacked_lstm(current_input, state)
    final_output = fully_connected(stacked_lstm_output)
    loss += calc_loss(final_output, expected_output)

7.2 Исключение рекуррентных нейронных сетей

Использование метода отсева в структуре нейронной сети сделает нейронную сеть более надежной. Рекуррентные нейронные сети обычно используют отсев только между разными слоями структур цикла, а не между структурами цикла в одном слое. То есть при переходе от момента времени t-l к моменту времени t рекуррентная нейронная сеть не будет выполнять сброс состояния, но в то же время t будет использоваться между разными слоями тел циклов.

В TensorFlow отсев можно реализовать с помощью класса tf.nn.rnn_cell.DropoutWrapper.