Весы / смещения обновляются только один раз для каждой мини-партии?
Я следую учебному пособию по нейронным сетям, и у меня есть вопрос о функции, которая обновляет веса.
def update_mini_batch(self, mini_batch, eta):
"""Update the network's weights and biases by applying
gradient descent using backpropagation to a single mini batch.
The "mini_batch" is a list of tuples "(x, y)", and "eta"
is the learning rate."""
nabla_b = [np.zeros(b.shape) for b in self.biases] #Initialize bias matrix with 0's
nabla_w = [np.zeros(w.shape) for w in self.weights] #Initialize weights matrix with 0's
for x, y in mini_batch: #For tuples in one mini_batch
delta_nabla_b, delta_nabla_w = self.backprop(x, y) #Calculate partial derivatives of bias/weights with backpropagation, set them to delta_nabla_b
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)] #Generate a list with partial derivatives of bias of every neuron
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)] #Generate a list with partial derivatives of weights for every neuron
self.weights = [w-(eta/len(mini_batch))*nw #Update weights according to update rule
for w, nw in zip(self.weights, nabla_w)] #What author does is he zips 2 lists with values he needs (Current weights and partial derivatives), then do computations with them.
self.biases = [b-(eta/len(mini_batch))*nb #Update biases according to update rule
for b, nb in zip(self.biases, nabla_b)]
Чего я здесь не понимаю, так это того, что для вычисления nabla_b и nabla_w используется цикл for (частные производные от весов / смещений). С обратным распространением для каждого учебного примера в мини-партии, но обновлять веса / смещения только один раз.
Мне кажется, что, скажем, у нас есть мини-пакет размером 10, мы вычисляем nabla_b и nabla_w 10 раз, и после того, как цикл for завершает обновление весов и смещений. Но разве цикл for не сбрасывает списки nabla_b и nabla_b каждый раз? Почему бы нам не обновить self.weights
а также self.biases
внутри цикла?
Нейронная сеть работает отлично, поэтому я думаю, что где-то допускаю небольшую ошибку мышления.
К вашему сведению: соответствующую часть учебника, которому я следую, можно найти здесь
2 ответа
Ключом к пониманию того, как этот цикл добавляет смещения и веса в каждом учебном примере, является примечание порядка оценки в Python. В частности, все справа от =
знак оценивается до того, как он назначен переменной слева от =
подписать.
Это более простой пример, который может быть проще для понимания:
nabla_b = [0, 0, 0, 0, 0]
for x in range(10):
delta_nabla_b = [-1, 2, -3, 4, -5]
nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
В этом примере у нас есть только пять скалярных смещений и постоянный градиент для каждого. В конце этого цикла, что является nabla_b
? Рассмотрим понимание, расширенное с использованием определения zip
и помня, что все справа от =
знак оценивается до того, как он будет записан в имя переменной слева:
nabla_b = [0, 0, 0, 0, 0]
for x in range(10):
# nabla_b is defined outside of this loop
delta_nabla_b = [-1, 2, -3, 4, -5]
# expand the comprehension and the zip() function
temp = []
for i in range(len(nabla_b)):
temp.append(nabla_b[i] + delta_nabla_b[i])
# now that the RHS is calculated, set it to the LHS
nabla_b = temp
На этом этапе должно быть ясно, что каждый элемент nabla_b
суммируется с каждым соответствующим элементом delta_nabla_b
в понимании, и этот результат перезаписывает nabla_b
для следующей итерации цикла.
Итак, в учебном примере, nabla_b
а также nabla_w
являются суммами частных производных, к которым добавлен градиент, один раз за обучающий пример в мини-пакете. Технически они сбрасываются для каждого обучающего примера, но они сбрасываются до их предыдущего значения плюс градиент, который является именно тем, что вы хотите. Более понятный (но менее лаконичный) способ написать это мог бы быть:
def update_mini_batch(self, mini_batch, eta):
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
# expanding the comprehensions
for i in range(len(nabla_b)):
nabla_b[i] += delta_nabla_b[i] # set the value of each element directly
for i in range(len(nabla_w)):
nabla_w[i] += delta_nabla_w[i]
self.weights = [w-(eta/len(mini_batch))*nw # note that this comprehension uses the same trick
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b-(eta/len(mini_batch))*nb
for b, nb in zip(self.biases, nabla_b)]
Нет, обновление происходит после окончания пакета, применяя каждое из обучающих обновлений по очереди. Каноническое описание говорит, что мы вычисляем среднее значение всех обновлений и корректируем это значение; корректировка каждого обновления, в свою очередь, арифметически эквивалентна.
Сначала инициализируйте массивы смещения и веса.
nabla_b = [np.zeros(b.shape) for b in self.biases] #Initialize bias matrix with 0's
nabla_w = [np.zeros(w.shape) for w in self.weights] #Initialize weights matrix with 0's
Для каждого наблюдения в мини-матче вставьте результат тренировки в массивы смещения и веса
for x, y in mini_batch: #For tuples in one mini_batch
delta_nabla_b, delta_nabla_w = self.backprop(x, y) #Calculate partial derivatives of bias/weights with backpropagation, set them to delta_nabla_b
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)] #Generate a list with partial derivatives of bias of every neuron
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)] #Generate a list with partial derivatives of weights for every neuron
Наконец, отрегулируйте каждый вес и уклон, изменяя значение для каждого из результатов тренировки, по очереди.
self.weights = [w-(eta/len(mini_batch))*nw #Update weights according to update rule
for w, nw in zip(self.weights, nabla_w)] #What author does is he zips 2 lists with values he needs (Current weights and partial derivatives), then do computations with them.
self.biases = [b-(eta/len(mini_batch))*nb #Update biases according to update rule
for b, nb in zip(self.biases, nabla_b)]