Глубокое обучение: понимание обратного распространения в нейронных сетях в одной статье — BackPropagation

машинное обучение глубокое обучение

Недавно я читал о глубоком обучении. Сначала я прочитал учебник UFLDL от Wu Enda. Я прочитал непосредственно китайскую версию. Позже я обнаружил, что некоторые части были не очень ясны. Я снова перешел к английской версии, а затем искал Некоторая информация.Обнаружено, что переводчик китайской версии дополнит пропущенный процесс вывода формулы при переводе, но дополнение неверно, поэтому неудивительно, что есть проблема. Метод обратного распространения на самом деле является основой нейронных сетей, но многие люди всегда сталкиваются с некоторыми проблемами, когда учатся, или вздрагивают, когда видят большие формулы и чувствуют, что это сложно.На самом деле это не сложно. правила используются многократно. Если вы не хотите смотреть на формулу, вы можете напрямую ввести значение, выполнить фактический расчет, а затем вывести формулу после того, как испытаете процесс, что будет очень просто.

Говоря о нейронных сетях, вы должны быть знакомы с этой картинкой:

 

Это базовая структура типичной трехслойной нейронной сети. Слой L1 — это входной слой, Слой L2 — скрытый слой, а Слой L3 — скрытый слой. Теперь у нас есть набор данных {x1, x2, x3, ...,xn}, на выходе тоже куча данных {y1,y2,y3,...,yn}, теперь их нужно сделать в скрытом слое каким-то преобразованием, чтобы можно было получить на выходе вы ожидаете после заливки данных. Если вы хотите, чтобы ваш вывод был таким же, как исходный ввод, то это наиболее распространенная модель автокодировщика (Auto-Encoder). Некоторые люди могут спросить, почему ввод и вывод должны быть одинаковыми? Любые функции? На самом деле, приложение довольно широкое, и оно будет использоваться для распознавания изображений, классификации текста и т. д. Я напишу еще одну статью об Auto-Encoder, чтобы объяснить, включая некоторые варианты. Если ваш вывод отличается от исходного ввода, то это очень распространенная искусственная нейронная сеть, что эквивалентно тому, что исходные данные проходят через сопоставление, чтобы получить нужные выходные данные, о чем мы собираемся поговорить. сегодня.

В этой статье прямо приводится пример, приводящий числовые значения для демонстрации процесса работы метода обратного распространения.Вывод формулы не буду писать до следующего раза, когда я напишу Auto-Encoder.Это на самом деле очень просто.Заинтересованные студенты могут выведите сами и попробуйте :) (Примечание: в этой статье предполагается, что вы уже понимаете базовую структуру нейронной сети. Если вы совсем не понимаете, вы можете обратиться к заметкам, написанным Опросом:[Машинное обучение и алгоритм] Основы нейронных сетей)

Предположим, у вас есть такой сетевой уровень:

Первый слой — это входной слой, содержащий два нейрона i1, i2 и точку пересечения b1, второй слой — скрытый слой, содержащий два нейрона h1, h2 и точку пересечения b2, а третий слой — выходной. o1, o2, wi, отмеченный в каждой строке, представляет собой вес соединения между слоями, а функция активации по умолчанию является сигмовидной функцией.

Теперь присвойте им начальные значения, как показано ниже:

Среди них входные данные i1=0,05, i2=0,10;

Выходные данные o1=0,01, o2=0,99;

Начальный вес w1=0,15,w2=0,20,w3=0,25,w4=0,30;

w5=0,40,w6=0,45,w7=0,50,w8=0,55

 

Цель: Имея входные данные i1, i2 (0,05 и 0,10), максимально приблизить выходные данные к исходным выходным данным o1, o2 (0,01 и 0,99).

 

Шаг 1 Прямое распространение

1. Входной слой ----> скрытый слой:

Вычислить входную взвешенную сумму нейрона h1:

Выход o1 нейрона h1: (здесь используется сигмовидная функция активации):

 

 

Таким же образом можно вычислить выход o2 нейрона h2:

 

2. Скрытый слой ----> выходной слой:

Рассчитаем значения нейронов выходного слоя o1 и o2:

 

Таким образом, процесс прямого распространения завершен, мы получаем выходное значение [0,75136079 , 0,772928465], которое все еще далеко от фактического значения [0,01 , 0,99].Теперь мы распространяем ошибку обратно, обновляем веса и пересчитываем выход .

 

Шаг 2. Обратное распространение

1. Рассчитайте общую ошибку

Общая ошибка: (квадратичная ошибка)

Но есть два выхода, поэтому ошибки o1 и o2 рассчитываются отдельно, а общая ошибка представляет собой сумму двух:

 

2. Скрытый слой ----> обновление веса выходного слоя:

Взяв в качестве примера весовой параметр w5, если мы хотим узнать, какое влияние w5 оказывает на общую ошибку, мы можем использовать общую ошибку, чтобы частично вывести w5: (цепное правило)

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

Теперь посчитаем значение каждой формулы отдельно:

рассчитать:

рассчитать:

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

 

рассчитать:

Умножьте последние три:

Таким образом, мы вычисляем частную производную общей ошибки E(total) по w5.

Возвращаясь к приведенной выше формуле, мы находим:

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

Следовательно, формула частной производной общей ошибки E(total) для w5 может быть записана как:

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

Наконец, мы обновляем значение w5:

(в,скорость обучения, здесь мы берем 0,5)

Точно так же можно обновить w6,w7,w8:

 

3. Скрытый слой ----> обновление веса скрытого слоя:

Метод на самом деле похож на описанный выше, но есть одно место, которое необходимо изменить: при вычислении частной производной полной ошибки для w5 выше, это из out(o1)---->net(o1)- --->w5 , но когда веса между скрытыми слоями будут обновлены, это будет out(h1)---->net(h1)---->w1, а out(h1) примет E(o1) и E(o2) ) из двух мест, так что это место должно вычислять оба.

 

 

рассчитать:

Рассчитать сначала:

Аналогично рассчитать:

Сложите два вместе, чтобы получить общее значение:

пересчитывать:

пересчитывать:

Наконец, умножьте три вместе:

Чтобы упростить формулу, используйте sigma(h1) для представления ошибки единицы скрытого слоя h1:

Наконец, обновите веса w1:

Таким же образом можно обновить веса w2, w3 и w4:

 

Таким образом, метод обратного распространения ошибки завершается. Наконец, мы пересчитываем обновленные веса и непрерывно повторяем. После первой итерации в этом примере общая ошибка E (общая) падает с 0,298371109 до 0,291027924. После 10 000 итераций общая ошибка составляет 0,000035085, а результат — [0,015912196, 0,984065734] (исходный ввод — [0,01, 0,99]), что доказывает, что эффект по-прежнему хорош.

 

Код (Питон):

  1 #coding:utf-8
  2 import random
  3 import math
  4 
  5 #
  6 #   参数解释:
  7 #   "pd_" :偏导的前缀
  8 #   "d_" :导数的前缀
  9 #   "w_ho" :隐含层到输出层的权重系数索引
 10 #   "w_ih" :输入层到隐含层的权重系数的索引
 11 
 12 class NeuralNetwork:
 13     LEARNING_RATE = 0.5
 14 
 15     def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
 16         self.num_inputs = num_inputs
 17 
 18         self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
 19         self.output_layer = NeuronLayer(num_outputs, output_layer_bias)
 20 
 21         self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
 22         self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)
 23 
 24     def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
 25         weight_num = 0
 26         for h in range(len(self.hidden_layer.neurons)):
 27             for i in range(self.num_inputs):
 28                 if not hidden_layer_weights:
 29                     self.hidden_layer.neurons[h].weights.append(random.random())
 30                 else:
 31                     self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
 32                 weight_num += 1
 33 
 34     def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
 35         weight_num = 0
 36         for o in range(len(self.output_layer.neurons)):
 37             for h in range(len(self.hidden_layer.neurons)):
 38                 if not output_layer_weights:
 39                     self.output_layer.neurons[o].weights.append(random.random())
 40                 else:
 41                     self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
 42                 weight_num += 1
 43 
 44     def inspect(self):
 45         print('------')
 46         print('* Inputs: {}'.format(self.num_inputs))
 47         print('------')
 48         print('Hidden Layer')
 49         self.hidden_layer.inspect()
 50         print('------')
 51         print('* Output Layer')
 52         self.output_layer.inspect()
 53         print('------')
 54 
 55     def feed_forward(self, inputs):
 56         hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
 57         return self.output_layer.feed_forward(hidden_layer_outputs)
 58 
 59     def train(self, training_inputs, training_outputs):
 60         self.feed_forward(training_inputs)
 61 
 62         # 1. 输出神经元的值
 63         pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
 64         for o in range(len(self.output_layer.neurons)):
 65 
 66             # ∂E/∂zⱼ
 67             pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])
 68 
 69         # 2. 隐含层神经元的值
 70         pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
 71         for h in range(len(self.hidden_layer.neurons)):
 72 
 73             # dE/dyⱼ = Σ ∂E/∂zⱼ * ∂z/∂yⱼ = Σ ∂E/∂zⱼ * wᵢⱼ
 74             d_error_wrt_hidden_neuron_output = 0
 75             for o in range(len(self.output_layer.neurons)):
 76                 d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]
 77 
 78             # ∂E/∂zⱼ = dE/dyⱼ * ∂zⱼ/∂
 79             pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()
 80 
 81         # 3. 更新输出层权重系数
 82         for o in range(len(self.output_layer.neurons)):
 83             for w_ho in range(len(self.output_layer.neurons[o].weights)):
 84 
 85                 # ∂Eⱼ/∂wᵢⱼ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢⱼ
 86                 pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)
 87 
 88                 # Δw = α * ∂Eⱼ/∂wᵢ
 89                 self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight
 90 
 91         # 4. 更新隐含层的权重系数
 92         for h in range(len(self.hidden_layer.neurons)):
 93             for w_ih in range(len(self.hidden_layer.neurons[h].weights)):
 94 
 95                 # ∂Eⱼ/∂wᵢ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢ
 96                 pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)
 97 
 98                 # Δw = α * ∂Eⱼ/∂wᵢ
 99                 self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight
100 
101     def calculate_total_error(self, training_sets):
102         total_error = 0
103         for t in range(len(training_sets)):
104             training_inputs, training_outputs = training_sets[t]
105             self.feed_forward(training_inputs)
106             for o in range(len(training_outputs)):
107                 total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
108         return total_error
109 
110 class NeuronLayer:
111     def __init__(self, num_neurons, bias):
112 
113         # 同一层的神经元共享一个截距项b
114         self.bias = bias if bias else random.random()
115 
116         self.neurons = []
117         for i in range(num_neurons):
118             self.neurons.append(Neuron(self.bias))
119 
120     def inspect(self):
121         print('Neurons:', len(self.neurons))
122         for n in range(len(self.neurons)):
123             print(' Neuron', n)
124             for w in range(len(self.neurons[n].weights)):
125                 print('  Weight:', self.neurons[n].weights[w])
126             print('  Bias:', self.bias)
127 
128     def feed_forward(self, inputs):
129         outputs = []
130         for neuron in self.neurons:
131             outputs.append(neuron.calculate_output(inputs))
132         return outputs
133 
134     def get_outputs(self):
135         outputs = []
136         for neuron in self.neurons:
137             outputs.append(neuron.output)
138         return outputs
139 
140 class Neuron:
141     def __init__(self, bias):
142         self.bias = bias
143         self.weights = []
144 
145     def calculate_output(self, inputs):
146         self.inputs = inputs
147         self.output = self.squash(self.calculate_total_net_input())
148         return self.output
149 
150     def calculate_total_net_input(self):
151         total = 0
152         for i in range(len(self.inputs)):
153             total += self.inputs[i] * self.weights[i]
154         return total + self.bias
155 
156     # 激活函数sigmoid
157     def squash(self, total_net_input):
158         return 1 / (1 + math.exp(-total_net_input))
159 
160 
161     def calculate_pd_error_wrt_total_net_input(self, target_output):
162         return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();
163 
164     # 每一个神经元的误差是由平方差公式计算的
165     def calculate_error(self, target_output):
166         return 0.5 * (target_output - self.output) ** 2
167 
168     
169     def calculate_pd_error_wrt_output(self, target_output):
170         return -(target_output - self.output)
171 
172     
173     def calculate_pd_total_net_input_wrt_input(self):
174         return self.output * (1 - self.output)
175 
176 
177     def calculate_pd_total_net_input_wrt_weight(self, index):
178         return self.inputs[index]
179 
180 
181 # 文中的例子:
182 
183 nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
184 for i in range(10000):
185     nn.train([0.05, 0.1], [0.01, 0.09])
186     print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))
187 
188 
189 #另外一个例子,可以把上面的例子注释掉再运行一下:
190 
191 # training_sets = [
192 #     [[0, 0], [0]],
193 #     [[0, 1], [1]],
194 #     [[1, 0], [1]],
195 #     [[1, 1], [0]]
196 # ]
197 
198 # nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
199 # for i in range(10000):
200 #     training_inputs, training_outputs = random.choice(training_sets)
201 #     nn.train(training_inputs, training_outputs)
202 #     print(i, nn.calculate_total_error(training_sets))

 

Наконец-то это конец написания. Я до сих пор не использую латекс для редактирования математических формул. Изначально я хотел написать это на черновой бумаге, а затем отсканировать и загрузить, но я чувствовал, что это также повлияет на чтение много. Я буду использовать редактор формул для редактирования формулы в будущем. Сигмовидная функция активации используется с осторожностью.На самом деле существует несколько различных функций активации на выбор.Подробности см. в [3].Наконец, рекомендуется веб-сайт для онлайн-демонстрации изменений нейронной сети: http://www.emergentmind .com/neural-network, вы можете сами заполнить ввод и вывод, а затем наблюдать за изменением весов каждой итерации.Это очень весело~ Если у вас есть какие-либо ошибки или вы не понимаете, пожалуйста, оставьте сообщение: )

 

использованная литература:

1.Примечания опроса:[Машинное обучение и алгоритм] Основы нейронных сетей(HTTP://woowoo.cn blog.com/maybe2030/fear/5597716.HTML#3457159)

2.Rachel_Zhang:http://blog.csdn.net/abcjennifer/article/details/7758797

3.http://www.cedar.buffalo.edu/%7Esrihari/CSE574/Chap5/Chap5.3-BackProp.pdf

4.https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

 

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

 

 

Добавить Автора

Источник: http://www.cnblogs.com/charlotte77/

Эта статья предназначена в основном для изучения, исследования и распространения.Если вам необходимо перепечатать, пожалуйста, свяжитесь со мной, укажите автора и источник, некоммерческое использование!