CS231n简介
详见 CS231n课程笔记1:Introduction。
本文都是作者自己的思考,正确性未经过验证,欢迎指教。
作业笔记
这里实现的是一个两层神经网络(含有一个隐层),激活函数使用的是ReLU,误差函数使用的是softmax,使用了L2正则项。
1. scores计算
需要注意的有
1. 最后一层不使用激活函数
2. 注意加bias的时候,使用了broadcasting,所以在反向传播的时候需要乘以num_class
# Unpack variables from the params dictionary
W1, b1 = self.params['W1'], self.params['b1']
W2, b2 = self.params['W2'], self.params['b2']
N, D = X.shape
# Compute the forward pass
h1_sum = X.dot(W1)+b1
h1 = h1_sum*(h1_sum > 0)
scores = (h1).dot(W2)+b2
2. 误差方程
Softmax + L2 regulization ,详情请参考 CS231n课程作业1.5:Softmax的误差以及梯度计算。
值得注意的有:
1. 为了方便broadcasting,在sum的时候需要keepdims
2. 最后得到的是平均值,而不是综合,而且为了防止遇到数值问题,所以最好使用average,而不是sum/num。
# Compute the loss
exp_scores = np.exp(scores)
sum_exp_scores = np.sum(exp_scores,axis=1,keepdims=True)
dif_exp_scores = exp_scores / sum_exp_scores
log_loss = -np.log(dif_exp_scores)
loss = np.average(log_loss[np.arange(N),y])
loss += 0.5*reg*(np.sum(W1*W1)+np.sum(b1*b1)+np.sum(W2*W2)+np.sum(b2*b2))
3. 梯度计算
3.1. 正则项的梯度
dW1 = reg*W1
db1 = reg*b1
dW2 = reg*W2
db2 = reg*b2
3.2. Softmax的梯度
详情请参考 CS231n课程作业1.5:Softmax的误差以及梯度计算。
值得注意的有:
1. 只对于真值处传播梯度
2. average的部分引入了一个1./num_class的系数
3. 对于sum_exp的梯度计算,需要引入一个sum(接上broadcasting,实际为拓展传播,这部分是sum的反向传播),因为sum_exp是作用于每个class的部分。
dlog_loss = np.zeros(log_loss.shape)
dlog_loss[np.arange(N),y] = 1./N
ddif_exp_scores = -1./dif_exp_scores*dlog_loss
dexp_scores = ddif_exp_scores/sum_exp_scores
dsum_exp_scores = -1.*exp_scores/sum_exp_scores/sum_exp_scores * ddif_exp_scores
dexp_scores += np.sum(dsum_exp_scores,axis=1,keepdims=True)
dscores = exp_scores * dexp_scores
3.2. 神经网络部分的梯度
值得注意的有:
1. max0函数的反向传播只对于值大于0的部分传播。
db2 += np.sum(dscores,axis = 0)
dW2 += h1.T.dot(dscores)
dh1 = dscores.dot(W2.T)
dh1_sum = dh1 * (h1_sum > 0)
db1 += np.sum(dh1_sum,axis=0)
dW1 += X.T.dot(dh1_sum)
3.4. 全部代码
# Backward pass: compute gradients
grads = {}
dloss = 1.
dW1 = reg*W1
db1 = reg*b1
dW2 = reg*W2
db2 = reg*b2
dlog_loss = np.zeros(log_loss.shape)
dlog_loss[np.arange(N),y] = 1./N
ddif_exp_scores = -1./dif_exp_scores*dlog_loss
dexp_scores = ddif_exp_scores/sum_exp_scores
dsum_exp_scores = -1.*exp_scores/sum_exp_scores/sum_exp_scores * ddif_exp_scores
dexp_scores += np.sum(dsum_exp_scores,axis=1,keepdims=True)
dscores = exp_scores * dexp_scores
db2 += np.sum(dscores,axis = 0)
dW2 += h1.T.dot(dscores)
dh1 = dscores.dot(W2.T)
dh1_sum = dh1 * (h1_sum > 0)
db1 += np.sum(dh1_sum,axis=0)
dW1 += X.T.dot(dh1_sum)
grads['W1']=dW1
grads['b1']=db1
grads['W2']=dW2
grads['b2']=db2
4. 训练与预测
详情请参考CS231n作业笔记1.4:随机梯度下降(SGD)。
值得注意的有:
1. 这里的原有代码使用了learning rate decay,对于learning rate进行递减。
4.1. 训练代码
batch_mask = np.random.choice(num_train,batch_size)
X_batch = X[batch_mask]
y_batch = y[batch_mask]
loss, grads = self.loss(X_batch, y=y_batch, reg=reg)
loss_history.append(loss)
self.params['W1'] -= grads['W1'] * learning_rate
self.params['b1'] -= grads['b1'] * learning_rate
self.params['W2'] -= grads['W2'] * learning_rate
self.params['b2'] -= grads['b2'] * learning_rate
4.2. 预测代码
h1 = X.dot(self.params['W1'])+self.params['b1']
h1 *= (h1 > 0)
scores = h1.dot(self.params['W2'])+self.params['b2']
y_pred = np.argmax(scores,axis=1)