持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
l1 = np.zeros((784,128),dtype=np.float32)
l2 = np.zeros((128,10),dtype=np.float32)
复制代码
这里注意一下默认情况下,numpy 创建一个数组类型为 np.float64
所以需要手动指定一下类型为 np.float32
,然后
l1[:] = model.l1.weight.detach().numpy().transpose()
l2[:] = model.l2.weight.detach().numpy().transpose()
复制代码
用 numpy 实现前向传播
def forward(x):
x = x.dot(l1)
x = np.maximum(x,0)
x = x.dot(l2)
return x
y_test_pred = np.argmax(forward(X_test.reshape((-1,28*28))),axis=1)
(y_test_pred==Y_test).mean()
复制代码
使用 numpy 进行训练
首先我们实现一下交叉熵
y_test_pred_out[sample,Y_test[sample]]
复制代码
sample = 1
-Y_test_pred_out[sample,Y_test[sample]] + np.log(np.exp(Y_test_pred_out[sample]).sum())
复制代码
这里要做就是我们计算所有样本交叉熵损失函数
ret = -Y_test_pred_out[range(Y_test_pred_out.shape[0]),Y_test] + np.log(np.exp(Y_test_pred_out[sample]).sum())
复制代码
imshow(X_test[np.argmax(ret)])
复制代码
不难看出这个就是模型给出判断误差最大的一张图像,的确不好分辨,即使对于我们人类来说也是一张比较难于分辨的图像。
sorted(list(zip(ret,range(ret.shape[0]))),reverse=True)
复制代码
grid = sorted(list(zip(ret,range(ret.shape[0]))),reverse=True)[0:16]
hard_classification_img = X_test[[x[1] for x in grid]]
#hard_classification_img.reshape(4,28*4,28).shape
hard_classification_img.shape
imshow(np.concatenate(hard_classification_img.reshape((4,28*4,28)),axis=1))
复制代码
首先grid
是一个 list 其中每一个元素是一个 tuple 类型,例如 (30.342394, 2607),
其中一个值 loss 值,另一个对应图像需要,我们需要根据图像需要拿到对应的图像,hard_classification_img
的 shape 为
np.concatenate(hard_classification_img.reshape((4,28*4,28)),axis=1)
复制代码
关键是看代码是怎么把 16 个 图像拼接为 排列图像
写一个训练过程
out = model(torch.tensor(X_test[0:1].reshape((-1,28*28))).float())
loss = loss_fun(out,torch.tensor(Y_test[0:1]).long())
loss.backward()
复制代码
从测试集中拿到样本输入到模型中,模型给出预测结果,再将预测结果和标签输入 loss 函数,然后对 loss 进行反向传播回传梯度,用梯度来更新模型参数
model.zero_grad()
out = model(torch.tensor(X_test[0:1].reshape((-1,28*28))).float())
loss = loss_fun(out,torch.tensor(Y_test[0:1]).long())
loss.backward()
figsize(16,16)
imshow(model.l1.weight.grad)
复制代码
将梯度以图像形式输出便于观察,从图上来看大部分梯度都是 0。
figure()
imshow(model.l2.weight.grad)
复制代码
model.zero_grad()
out = model(torch.tensor(X_test[0:1].reshape((-1,28*28))).float())
loss = loss_fun(out,torch.tensor(Y_test[0:1]).long())
print(loss)
loss.retain_grad()
loss.backward()
figsize(16,16)
imshow(model.l1.weight.grad)
figure()
imshow(model.l2.weight.grad)
复制代码
这里retain_grad()
可以保留非叶子结点以外中间结点的梯度,默认情况下为了节省内存空间是不会保留中间结点非叶子结点的梯度的。
#理解梯度下降
model.zero_grad()
out = model(torch.tensor(X_test[0:1].reshape((-1,28*28))).float())
out.retain_grad()
loss = loss_fun(out,torch.tensor(Y_test[0:1]).long())
# print(loss)
loss.retain_grad()
loss.backward()
figsize(16,16)
imshow(model.l1.weight.grad)
figure()
imshow(model.l2.weight.grad)
out.grad,loss.grad
复制代码
输出 out
和 loss
的梯度,retain_grad
表示中间变量
(tensor([[ 8.8083e-11, 7.9277e-13, 4.0970e-04, 4.5061e-05, 1.6520e-12, 4.4796e-08, 9.0004e-15, -4.5484e-04, 1.1185e-10, 3.0328e-08]]), tensor(1.))
复制代码
...
loss_fun = nn.CrossEntropyLoss(reduction='none')
...
for i in tbar:
...
loss = loss_fun(out,Y)
print(loss.shape)
# print(loss.mean())
loss = loss.mean()
loss.backward()
optim.step()
# print(loss)
复制代码
这里将 reduction
设置为 none
在每次迭代时,不会对 loss
进行求均值或者求和,所以需要手动loss.mean()
进行求均值。
将 CrossEntropyLoss
拆分为 LogSoftmax
和 NLLLoss
。
class ANet(torch.nn.Module):
def __init__(self):
...
self.sm = nn.LogSoftmax(dim=1)
def forward(self,x):
...
x = self.sm(x)
return x
model = ANet()
复制代码
loss_fun = nn.NLLLoss(reduction='none')
复制代码