所以我尝试使用 numpy 构建一个基本的 ANN。问题是我的预测准确率一直在 10% 左右,但我不明白为什么会这样。这是所有代码。
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
data = pd.read_csv('train.csv')
data = np.array(data)
m, n = data.shape
np.random.shuffle(data)
data_train = data.T
X_train = data_train[1:n]
Y_train = data_train[0]
def init_params():
W1 = np.random.randn(10, 784)
b1 = np.random.rand(10, 1)
W2 = np.random.randn(10, 10)
b2 = np.random.randn(10, 1)
return W1, b1, W2, b2
def ReLU(Z):
return np.maximum(0, Z)
def softmax(Z):
e = np.exp(Z - Z.max(axis=0, keepdims=True))
return e/e.sum(axis=0, keepdims=True)
def forward_prop(W1, b1, W2, b2, X):
Z1 = W1.dot(X) + b1
A1 = ReLU(Z1)
Z2 = W2.dot(A1) + b2
A2 = softmax(Z2)
return Z1, A1, Z2, A2
def one_hot(Y):
one_hot_Y = np.zeros((Y.size, Y.max() + 1))
one_hot_Y[np.arange(Y.size), Y] = 1
one_hot_Y = one_hot_Y.T
return one_hot_Y
def deriv_ReLU(Z):
return (Z > 0).astype(int)
def back_prop(Z1, A1, Z2, A2, W2, X, Y):
m = Y.size
one_hot_Y = one_hot(Y)
dZ2 = A2 - one_hot_Y
dW2 = 1 / m * dZ2.dot(A1.T)
db2 = 1 / m * np.sum(dZ2, 1).reshape(-1, 1)
dZ1 = W2.T.dot(dZ2) * deriv_ReLU(Z1)
dW1 = 1 / m * dZ1.dot(X.T)
db1 = 1 / m * np.sum(dZ1, 1).reshape(-1, 1)
return dW1, db1, dW2, db2
def update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha):
W1 = W1 - alpha * dW1
db1 = b1 - alpha * db1
W2 = W2 - alpha * dW2
db2 = b2 - alpha * db2
return W1, b1, W2, b2
def get_predictions(A2):
return np.argmax(A2, 0)
def get_accuracy(predictions, Y):
print(predictions, Y)
return np.sum(predictions == Y) / Y.size
def gradient_descent(X, Y, iterations, alpha):
W1, b1, W2, b2 = init_params()
for i in range(iterations):
Z1, A1, Z2, A2 = forward_prop(W1, b1, W2, b2, X)
dW1, db1, dW2, db2 = back_prop(Z1, A1, Z2, A2, W2, X, Y)
W1, b1, W2, b2 = update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha)
if (i % 50):
print(f'Iteration: {i}')
print(f'Accuracy: {get_accuracy(get_predictions(A2), Y)}')
return W1, b1, W2, b2
W1, b1, W2, b2 = gradient_descent(X_train, Y_train, 100, 0.1)
我认为这可能是我使用的数据的问题,但在尝试新数据后,情况并非如此。很明显,在反向传播的某个地方(我猜)我搞砸了一些东西并切换了一些变量。使用的数据是手写数字的 MNIST 数据集,有 10 个类别。 我做了更多的挖掘,我注意到实际上该模型似乎一直在输出单个数字作为预测。我不明白为什么。
好吧,我在 chatGPT 的帮助下成功解决了自己的问题:
我在那里有一些拼写错误,特别是在 update_params() 函数中,我更新了导数而不是更新实际层。
偏差更新问题: update_params 函数在更新偏差时存在潜在问题:
db1 = b1 - alpha * db1
这行应该是:
b1 = b1 - alpha * db1
同样,检查 db2 以确保:
b2 = b2 - alpha * db2
由于错误的变量被更新,偏差在训练过程中保持不变,从而阻碍了有效的学习。
真正改变游戏的是接下来的两点:
权重初始化: 确保您的权重初始化不会产生太大或太小的值。标准方法是按 sqrt(1/n) 缩放权重,其中:
n 是给定层的输入数量。
W1 = np.random.randn(10, 784) * np.sqrt(1 / 784)
W2 = np.random.randn(10, 10) * np.sqrt(1 / 10)
这可以防止梯度消失/爆炸的问题。
这改变了游戏规则:
数据标准化: 确保您的输入数据 X(本例中为像素)已标准化。通常,像素值的范围是 0 到 255,因此您应该将输入数据除以 255,以使值保持在 0 到 1 之间。
X_train = X_train / 255.0
这种标准化通常有助于稳定学习。
现在就完成了。我能够在 100 次迭代内获得 90% 的准确率。我现在将测试不同的激活函数并找到最合适的。谢谢你聊天GPT。