大家好,我是Kay,小白一个。以下是我完成斯坦福 cs231n-assignment1-two_layer_net 这份作业的做题过程、思路、踩到的哪些坑、还有一些得到的启发和心得。希望下面的文字能对所有像我这样的小白有所帮助。
两层的网络处理方法与之前的SVM/Softmax 的处理方法类似,关键在于函数和梯度的计算。
【思路】公式是:W2 * max(0, W1*x) 用代码实现之即可。
scores_hid = np.dot(X, W1) + b1
scores_hid = np.maximum(0, scores_hid)
scores =np.dot(scores_hid, W2) + b2
结果正确。
【思路】用 softmax 的公式,不要忘记加上正则惩罚项。
scores = scores - np.max(scores, axis = 1,keepdims=True)
exp_sum = np.sum(np.exp(scores), axis = 1,keepdims=True)
loss = -np.sum(scores[range(N), y]) +np.sum(np.log(exp_sum))
loss = loss / N + 0.5 * reg * (np.sum(W1 *W1) + np.sum(W2 * W2))
【开始 Debug】
这个 bug 我是找得真是要气死自己啊!感觉公式都没记错啊!Run 了好几次 loss 还是不够小,郁闷之下跑去百度,贴了别人的代码结果 loss 也是这么多但是他们的结果很小,喵喵喵?我定眼一看,别人的 reg 都是 0.1!凭什么我给的是0.05。想哭、难受。
改成 0.1 后,果然我的代码也是正确的。
【思考提升】
我顺手看了下别人的代码,有的人没有对scores 做处理就开 e 的幂,这是不对的哦~小心数值被爆掉哦~
【思路】画出“计算图”,一步步往回做,靠公式得到:
d_b2= 1 * d_scores
d_W2= h1 * d_scores (h1 是 max(0, f1) )
d_b1 = 1 * d_f1
d_W1= X * d_f1
prob = score / exp_sum
prob[range(N), y] -= 1
d_scores= np.dot(X.T, prob)
d_scores /= N
grads['b2'] = np.sum(d_scores, axis = 0)
grads['W2'] = np.dot(scores_hid.T, d_scores)
d_f1 = np.dot(d_scores, W2.T)
d_f1[scores_hid <= 0] = 0
grads['b1'] = np.sum(d_f1, axis = 0)
grads['W1'] =np.dot(X.T, d_f1)
【开始 Debug】这里我遇到了特别多错误,果然思考还是不够严谨。
1. d_scores 求错了,不是 np.dot(X.T, prob),不能生搬以为是 softmax 里的 dW,d_scores 就是 prob!
2. prob 也求错了,在 softmax 里,我分子上的 scores 是有做 e 幂的,但是这里还没处理就直接拿去用了,还是一处生搬旧思想的错误。
3. 两个 dW 没有加正则项。
【修改代码】
prob = np.exp(scores) / exp_sum
prob[range(N), y] -= 1
d_scores = prob / N
grads['b2'] = np.sum(d_scores, axis = 0)
grads['W2'] = np.dot(scores_hid.T, d_scores) + reg * W2
d_f1 = np.dot(d_scores, W2.T)
d_f1[scores_hid <= 0] = 0
grads['b1'] = np.sum(d_f1, axis = 0)
grads['W1'] =np.dot(X.T, d_f1) + reg * W1
结果正确。
【思考提升】其实像这些所谓的“小错”是很让人沮丧的,错的又不是大方向,找起bug 时又往往是从整体思路开始怀疑自己,因此找到这点小错是很耗费精力的,要怎么加快 debug 的效率呢?错的地方是思路、还是小瑕疵?这是应该训练的地方了。
【思路】
SGD:利用特定一张图像对我们的各个参数进行 update
y_pred就是谁的分数大就取那个标签作为 y_pred
三段代码都贴在这里了。
sample_indices = np.random.choice(range(num_train), batch_size)
X_batch = X[sample_indices]
y_batch =y[sample_indices]
self.params['W1'] -= learning_rate * grads['W1']
self.params['b1'] -= learning_rate * grads['b1']
self.params['W2'] -= learning_rate * grads['W2']
self.params['b2'] -= learning_rate * grads['b2']
y_pred = np.argmax(self.loss(X), axis=1)
结果Final training loss: 0.017143643532923747
【思路】这里的超参数有:hidden layer size, learning rate, numer of training epochs, andregularization strength
首先,我们还是先调最重要的lr 和 reg,接着再考虑其他超参数。
best_val = -1
input_size = 32 * 32 * 3
hidden_size = 100
num_classes = 10
net = TwoLayerNet(input_size, hidden_size,num_classes)
learing_rates = [1e-3, 1.5e-3, 2e-3]
regularizations = [0.2, 0.35, 0.5]
for lr in learing_rates:
for reg in regularizations:
stats = net.train(X_train, y_train, X_val, y_val,
num_iters=1500,batch_size=200,
learning_rate=lr,learning_rate_decay=0.95,
reg=reg, verbose=False)
val_acc = (net.predict(X_val) == y_val).mean()
if val_acc > best_val:
best_val = val_acc
best_net = net
print ("lr ",lr, "reg ", reg, "val accuracy:", val_acc)
print ("best validation accuracyachieved during cross-validation: ", best_val)
结果差强人意。
【思考提升】老师说:“Tuning the hyperparameters and developing intuition for how theyaffect the final performance is a large part of using Neural Networks.”可是就目前而言,我还是没有认识到超参数的“重要性”?所以,我需要训练一种超参如何影响神经网络的直觉。
这份作业要求我们掌握正向传递分数函数和损失函数,同时反向传递梯度给每个变量。
Delta = “本地梯度”*“上沿梯度”
有趣的是,
变量间做“加法”,传回的梯度都是那份“上沿梯度”,相当于是一个广播器;
变量间做“max()”,传回的梯度是那份“上沿梯度”给最大的值,其他的梯度是0,相当于是一个路由器;
变量间做“乘法”,传回的梯度都是那份“上沿梯度”*对方本身的值,相当于是一个(带放大“上沿梯度”倍)交换器。
这三个典例,应该能帮助我们直观地理解 backpropagation。
最后,关于缩进,我已经放弃治疗了。