基本的反向传播实现不起作用

Basic Backpropagation Implementation Not Working

我还处于理解 backpropagation 的早期阶段,我尝试自己实施它。

我尝试使用的数据集是大小为 (150, 4) 的鸢尾花数据集。

我只担心 backpropagation 而不是梯度下降,所以我只是在一个例子上尝试我的算法,看看我是否可以获得看似正确的输出。

但是,我的问题是尝试为我的初始权重矩阵获取梯度,我遇到了形状错误。

我希望我的网络是这样的 - 4 个输入、8 个隐藏神经元和 1 个输出神经元

我的代码如下。 错误出现在最后一行,因为 x 的大小为 (4,1) 而 delta2 的大小为 (8,8) 所以我无法得到点积 我只是不知道如果我根据其他来源正确地遵循算法,我不明白我应该如何获得正确的 delta2 大小。

from sklearn.datasets import load_iris
from keras.utils import to_categorical
import numpy as np

# LOAD DATA
data = load_iris()
X = data.data[:-20]
y = to_categorical(data.target[:-20])
# only 20 samples because we have a small dataset
X_test = data.data[-20:]
y_test = to_categorical(data.target[-20:])

# INIT WEIGHTS  - will try to add bias later on
w1 = np.random.rand(np.shape(X)[1], h_neurons)
w2 = np.random.rand(h_neurons, 3)

def sigmoid(x, deriv=False):
    if deriv:
        return sigmoid(x)*(1-sigmoid(x))
    else:
        return 1/(1+np.exp(-x))

# Feed forward
x = X[1].reshape(4,1)
z1 = w1.T.dot(x) # need to transpose weight matrix
a1 = sigmoid(z1)
z2 = w2.T.dot(a1)
y_hat = sigmoid(z2,deriv=True) # output


# BACKPROP
y_ = y[1].reshape(3,1)
delta3 = np.multiply((y_hat - y_), sigmoid(z2, deriv=True))
dJdW2 = a1.dot(delta3) ## ERROR !!!

delta2 = np.dot(delta3, w2.T) * sigmoid(z1, deriv=True)
dJdW1 = np.dot(x.T, delta2) ## ERROR !!!

我以为我正确地实现了backpropagation,但显然不是,有人可以指出我哪里错了吗?

我被卡住了,我查看了各种来源,计算 dJdW(成本相对于权重的导数)的代码大致相同。

我认为您的代码中存在几个问题。让我们逐步解决它们。首先,这是完整的代码:

from sklearn.preprocessing import StandardScaler

def sigmoid(x, deriv=False):
    if deriv:
        return sigmoid(x)*(1-sigmoid(x))
    else:
        return 1/(1+np.exp(-x))


data = load_iris()
X = data.data[:-20]
X = StandardScaler().fit_transform(X)
y = data.target[:-20]
y = y.reshape(-1,1)

w1 = np.random.rand(np.shape(X)[1], 8)
w2 = np.random.rand(8, 1)

z1 = np.dot(X, w1) #shape (130, 8)
a1 = sigmoid(z1)
z2 = np.dot(a1, w2) #shape (130,1)
y_hat = sigmoid(z2) # l2 should also use sigmoid activation
delta3 = ((y - y_hat) * sigmoid(z2, deriv=True)) #shape (130,1)
dJdW2 = a1.T.dot(delta3) #shape (8,1)
delta2 = np.dot(delta3, w2.T) * sigmoid(z1, deriv=True) #shape (130,8)
dJdW1 = np.dot(X.T, delta2) #shape (4,8)
  1. 这与您的问题并不完全相关,但我建议缩放输入数据
  2. 一开始y的shape是(130,),值得reshape为(130,1),否则会出现一些问题。 重要:我不使用一种热编码并保留形状为 130,1 的 y,因为一种热编码需要 softmax,sigmoid 会更糟。
  3. 我觉得最好用矢量化的版本,不要一个样写代码,这样应该更容易理解。并且你需要在前向传递中使用更少的转置。

所以您输入了形状为 130、4 的 X 和形状为 4、8 的权重 w1。结果应该具有形状 130、8。您可以这样做:

z1 = np.dot(X, w1)
a1 = sigmoid(z1)

然后你从隐藏层移动到输出层,从形状 130,8 到形状 130,1。并且不要忘记将激活函数应用于 y_hat:

z2 = np.dot(a1, w2)
y_hat = sigmoid(z2)

现在我们可以反向传播了。您已正确计算增量:

delta3 = np.multiply((y_hat - y_), sigmoid(z2, deriv=True)) #shape (130,1)

所以你有形状为 (130,1) 的 delta3,形状为 130,8 的 a1 并且需要获取一个值来更新 w2,所以结果应该有形状 (8,1):

dJdW2 = a1.T.dot(delta3) #shape (8,1)

以类似的方式获取更新 w1 的值:

delta2 = np.dot(delta3, w2.T) * sigmoid(z1, deriv=True) #shape (130,8)
dJdW1 = np.dot(X.T, delta2) #shape (4,8)

就是这样。但我想指出,您将无法使用这样的模型对该数据集进行良好的预测:sigmoid 的输出范围从 0 到 1,而 iris 数据集中有 3 classes。您可以采用多种方式:只获取属于 2 classes 的数据;对每个 class 使用单独的 sigmoid 或对输出层使用 softmax 激活。