Как правильно реализовать Backpropogation - PullRequest
2 голосов
/ 21 мая 2019

Я пытался реализовать свою собственную игрушечную библиотеку нейронных сетей в учебных целях.Я пытался протестировать его на различных операциях логического вентиля, таких как Or, And и XOR.Хотя он работает правильно для операций ИЛИ, он не работает для операций И ​​и XOR.Он редко дает правильный вывод для операций AND и XOR.

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


import numpy as np

class myNeuralNet:

    def __init__(self, layers = [2, 2, 1], learningRate = 0.09):
        self.layers = layers
        self.learningRate = learningRate
        self.biasses = [np.random.randn(l, 1)  for l in self.layers[1:]]
        self.weights = [np.random.randn(i, o)  for o, i in zip(self.layers[:-1], self.layers[1:])]
        self.cost = []

    def sigmoid(self, z):
        return (1.0 / (1.0 + np.exp(-z)))

    def sigmoidPrime(self, z):
        return (self.sigmoid(z) * (1 - self.sigmoid(z)))



    def feedForward(self, z, predict = False):
        activations = [z]
        for w, b in zip(self.weights, self.biasses): activations.append(self.sigmoid(np.dot(w, activations[-1]) + b))
        # for activation in activations: print(activation)
        if predict: return np.round(activations[-1])
        return np.array(activations)

    def drawLearningRate(self):
        import matplotlib.pyplot as plt
        plt.xlim(0, len(self.cost))
        plt.ylim(0, 5)
        plt.plot(np.array(self.cost).reshape(-1, 1))
        plt.show()



    def backPropogate(self, x, y):
        bigDW = [np.zeros(w.shape) for w in self.weights]
        bigDB = [np.zeros(b.shape) for b in self.biasses]
        activations = self.feedForward(x)
        delta = activations[-1] - y
        # print(activations[-1])
        # quit()
        self.cost.append(np.sum([- y * np.log(activations[-1]) - (1 - y) * np.log(1 - activations[-1])]))
        for l in range(2, len(self.layers) + 1):
            bigDW[-l + 1] = (1 / len(x)) * np.dot(delta, activations[-l].T)
            bigDB[-l + 1] = (1 / len(x)) * np.sum(delta, axis = 1)
            delta = np.dot(self.weights[-l + 1].T, delta) * self.sigmoidPrime(activations[-l]) 

        for w, dw in zip(self.weights, bigDW): w -= self.learningRate * dw
        for b, db in zip(self.biasses, bigDB): b -= self.learningRate *db.reshape(-1, 1)
        return np.sum(- y * np.log(activations[-1]) - (1 - y) * np.log(1 - activations[-1])) / 2



if __name__ == '__main__':
    nn = myNeuralNet(layers = [2, 2, 1], learningRate = 0.35)
    datasetX = np.array([[1, 1], [0, 1], [1, 0], [0, 0]]).transpose()
    datasetY = np.array([[x ^ y] for x, y in datasetX.T]).reshape(1, -1)
    print(datasetY)
    # print(nn.feedForward(datasetX, predict = True))
    for _ in range(60000): nn.backPropogate(datasetX, datasetY)
    # print(nn.cost)
    print(nn.feedForward(datasetX, predict = True))
    nn.drawLearningRate()

Иногда также выдается «RuntimeWarning: переполнение, встречающееся в exp», которое иногда приводит к сбою конвергенции.

Ответы [ 2 ]

0 голосов
/ 22 мая 2019

Для кросс-энтропийной ошибки необходим корректный выходной слой в сети. Сигмоид обычно не работает и также не должен использоваться.

Ваши формулы выглядят немного не так. Для текущей конфигурации сети вы определили: 3 слоя (2, 2, 1) у вас есть w0 (2x2) и w1 (1x2). Не забудьте найти dw1 у вас есть следующее:

  d1 = (guess - target) * sigmoid_prime(net_inputs[1]) <- when you differentiated da2/dz1 you ended up f'(z1) and not f'(a2)!
  dw1 = d1 * activations[1]
  db1 = np.sum(d1, axis=1)
  d0 = d1 * w1 * sigmoid_prime(net_inputs[0])
  dw0 = d0 * activations[0]
  db0 = np.sum(d0, axis=1)

Следует помнить, что каждый слой имеет net_inputs как

z: = w @ x + b

и активации

a: = f (z)

. Во время обратного распространения, когда вы вычисляете da [i] / dz [i-1], вам нужно применить производную функции активации к z [i-1], а не к [i].

z = w @ x + b

a = f (z)

da / dz = f '(z) !!!

И это для всех слоев. Некоторые незначительные моменты, на которые следует обратить внимание:

  • Переключите расчет ошибки на: np.mean (.5 * (активации [-1] - y) ** 2), если вы не используете функции активаций soft / hardmax для выходного слоя (для один выходной нейрон с чего бы вам).

  • Использование z-s в производных функции активации при вычислении дельты

  • Не используйте сигмоид (это проблематично с точки зрения исчезающих градиентов), попробуйте ReLu: np.where (x <= 0, 0, x) /np.where (x <= 0, 0, 1) или некоторые его варианты. </p>

  • Для скорости обучения по XOR выбор между [.0001, .1] должен быть более чем достаточным с использованием любого вида оптимизации.

  • Если вы инициализируете свои весовые матрицы как: [number_of_input_units x number_of_output_units] вместо [number_of_output_units x number_of_input_units], что у вас есть сейчас, вы можете изменить z = w @ x + b на z = x @ w + b и Вам не нужно будет транспонировать свои входы и выходы.

Вот пример реализации вышеуказанного:

import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)


def cost(guess, target):
    return np.mean(np.sum(.5 * (guess - target)**2, axis=1), axis=0)


datasetX = np.array([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
datasetY = np.array([[0.], [1.], [1.], [0.]])


w0 = np.random.normal(0., 1., size=(2, 4))
w1 = np.random.normal(0., 1., size=(4, 1))
b0 = np.zeros(4)
b1 = np.zeros(1)

f1 = lambda x: np.where(x <= 0, 0, x)
df1 = lambda d: np.where(d <= 0, 0, 1)
f2 = lambda x: np.where(x <= 0, .1*x, x)
df2 = lambda d: np.where(d <= 0, .1, 1)


costs = []
for i in range(250):
    a0 = datasetX
    z0 = a0 @ w0 + b0
    a1 = f1(z0)
    z1 = a1 @ w1 + b1
    a2 = f2(z1)
    costs.append(cost(a2, datasetY))

    d1 = (a2 - datasetY) * df2(z1)
    d0 = d1 @ w1.T * df1(z0)

    dw1 = a1.T @ d1
    db1 = np.sum(d1, axis=0)
    dw0 = a0.T @ d0
    db0 = np.sum(d0, axis=0)

    w0 = w0 - .1 * dw0
    b0 = b0 - .1 * db0
    w1 = w1 - .1 * dw1
    b1 = b1 - .1 * db1

print(f2(f1(datasetX @ w0 + b0) @ w1 + b1))

plt.plot(costs)
plt.show()

Результат, который он дает:

[[0.00342399]
 [0.99856158]
 [0.99983358]
 [0.00156524]]
0 голосов
/ 21 мая 2019

У меня была такая же проблема, когда я создавал нейронную сеть с нуля.Я решил это, используя scipy.special.expit(x) вместо np.exp (x), дайте мне знать, работает ли он для вас!

...