前言
从零开始学习ai文章系列计划是个人在《动手学深度学习》和《磨菇书》两本书的学习中的个人笔记,文章也会以课本中的章节分开,即每个章节一片笔记。我会尽量的把主要内容以及遇到的难点进行记录与解决,如果哪里有错误的欢迎指正。或者不清晰的可以直接查看原文部分。
《动手学深度学习》原文(课本):https://tangshusen.me/Dive-into-DL-PyTorch/#/
《动手学深度学习》代码:https://github.com/ShusenTang/Dive-into-DL-PyTorch
(由于有时候公式太多,可能会直接贴图片)
1. 获取和读取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def load_data_fashion_mnist (batch_size, resize=None , root='~/Datasets/FashionMNIST' ): """Download the fashion mnist dataset and then load into memory.""" trans = [] if resize: trans.append(torchvision.transforms.Resize(size=resize)) trans.append(torchvision.transforms.ToTensor()) transform = torchvision.transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True , download=True , transform=transform) mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False , download=True , transform=transform) if sys.platform.startswith('win' ): num_workers = 0 else : num_workers = 4 train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True , num_workers=num_workers) test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False , num_workers=num_workers) return train_iter, test_iter batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
2.2定义和初始化模型
在3.4节(softmax回归)中提到,softmax回归的输出层是一个全连接层,所以我们用一个线性模块就可以了。因为前面我们数据返回的每个batch样本x的形状为(batch_size,
1, 28, 28), 所以我们要先用view()将x的形状转换成(batch_size,
784)才送入全连接层。
我们将对x的形状转换的这个功能自定义一个FlattenLayer网络层,但是不做任何网络计算操作,仅仅修改输入x的形状,将x变成扁平。
1 2 3 4 5 6 class FlattenLayer (nn.Module): def __init__ (self ): super (FlattenLayer, self ).__init__() def forward (self, x ): return x.view(x.shape[0 ], -1 )
这样我们就可以更方便地定义我们的模型:
1 2 3 4 5 6 net = nn.Sequential( OrderedDict([ ('flatten' , FlattenLayer()), ('linear' , nn.Linear(num_inputs, num_outputs)) ]) )
然后,我们使用均值为0、标准差为0.01的正态分布随机初始化模型的权重参数。
1 2 init.normal_(net.linear.weight, mean=0 , std=0.01 ) init.constant_(net.linear.bias, val=0 )
这里net.linear指的是OrderedDict中的'linear'关键字。
3. softmax和交叉熵损失函数
PyTorch提供了一个包括softmax运算和交叉熵损失计算的函数。
1 loss = nn.CrossEntropyLoss()
类似于之前手动实现的两个函数的结合
1 2 3 4 5 6 7 def softmax (X ): X_exp = X.exp() partition = X_exp.sum (dim=1 , keepdim=True ) return X_exp / partition def cross_entropy (y_hat, y ): return - torch.log(y_hat.gather(1 , y.view(-1 , 1 )))
结果是一个列向量,即每行样本结果输出一个交叉熵损失
4. 定义优化算法
使用学习率为0.1的小批量随机梯度下降作为优化算法。将net的parameters当作参数填入,然后设定学习率即可
1 optimizer = torch.optim.SGD(net.parameters(), lr=0.1 )
我们看到之前optimizer为None的时候,我们手动对每个参数进行清零,并且获取梯度后,手动修改每个参数的值。
这次设定了optimizer后,直接通过 optimizer.zero_grad()
来清零梯度,通过 optimizer.step() 来使用梯度进行更新。
5. 训练模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def train_ch3 (net, train_iter, test_iter, loss, num_epochs, batch_size, params=None , lr=None , optimizer=None ): for epoch in range (num_epochs): train_l_sum, train_acc_sum, n = 0.0 , 0.0 , 0 for X, y in train_iter: y_hat = net(X) l = loss(y_hat, y).sum () if optimizer is not None : optimizer.zero_grad() elif params is not None and params[0 ].grad is not None : for param in params: param.grad.data.zero_() l.backward() if optimizer is None : sgd(params, lr, batch_size) else : optimizer.step() train_l_sum += l.item() train_acc_sum += (y_hat.argmax(dim=1 ) == y).sum ().item() n += y.shape[0 ] test_acc = evaluate_accuracy(test_iter, net) print ('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1 , train_l_sum / n, train_acc_sum / n, test_acc)) num_epochs = 5 d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None , None , optimizer)