pytorch 1.4.0版本的tutorials提供了三个大块的教程:
- deep learning with pytorch: a 60 minute blitz
- writing custom datasets, dataloaders and transforms
- visualizing models, data and training with tensorboard
根据个人所需将其分为五个篇章,作为pytorch的入门(假设你已经有了一定的tensorflow和深度学习基础):
第一篇章 tensor
tensor是pytorch核心的数据结构,和numpy中的ndarray的区别是,它可以在GPU上实现加速。
tensor需要了解的就是两个部分的内容:(为表述方便,用t
表示某个具体的tensor)
tensor的属性
t.shape/t.size()
创建tensor
创建tensor有很多种方法,和tensorflow中的tensor差不多
- 构造方法:
tensor()
- 特殊张量:
ones()/ones_like()
- 随机函数:
randn()
tensor运算
tensor的计算(包括算术计算、代数运算、维度计算),太多了,和tensorflow几乎没什么两样。
t.view()
:改变tensor的shape并返回t.item()
:如果tensor只有一个元素,直接获取python数字,如果不是请使用tolist()t.tolist()
:直接将tensor转化为python原生的嵌套list
以加法为例,既可以从torch的角度执行:torch.add(t1, t2),也可以从tensor的角度去执行:t1.add(t2),还可以原地执行t1.add_(t2)
原地执行的好处是不用重新赋值。
第二篇章 自动微分
自动微分的这个机制完全吊打tensorflow,只需要设置tensor的requires_grad
参数为True,便可以追踪该tensor的计算,最后可以通过loss调用loss.backward()
自动计算所有被追踪的tensor的梯度值。
除了requires_grad
每个tensor还有grad属性和grad_fn属性,前者是记录了梯度值,后者是梯度函数。
避免追踪
requires_grad
设置为False:t.requires_grad_(False)
detach()
第三篇章 神经网络
自定义的模型需要继承nn.module
(类似keras的自定义模型必须继承keras.Model),然后__init__()
中定义层结构,在forward(inputs)
中定义前向传播的过程,最后返回output即可。
在开始正式的模型之前,需要了解下:
torch.nn.Conv2d(in_channels, out_channels, kernel_size)
:创建的卷积层在接受参数的时候,图像的shape是torch.nn.Linear(in_features, out_features)
接下来看下模型结构:
import torch
import torch.nn as nn
import torch.nn.functional as Fclass Net(nn.module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(1, 6, 3)self.conv2 = nn.Conv2d(6, 16, 3)self.fc1 = nn.Linear(16 * 6 * 6, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))x = F.max_pool2d(F.relu(self.conv2(x)), 2)x = x.view(-1, self.num_flat_features(x))x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xdef num_flat_features(self, x):size = x.size()[1:]num_features = 1for s in size:num_features *= sreturn num_featuresnet = Net()
print(net)
可以通过net.parameters()
查看所有的参数。
损失函数和优化器
和tensorflow一样,损失函数有很多,比如MSELoss()
.
由于每次求梯度值都会累加,所以需要在一开始进行清零:net.zeros_grad()
,然后在计算出loss之后通过loss.backward()
计算。
求出梯度,需要对参数进行更新,最简单的方法是使用梯度下降:
for f in net.parameters():f.data.sub_(f.grad.data * learning_rate)
除此之外,还有很多种其他的优化器:SGD、Nesterous-SGD、Adam、Rmsprop等,都封装到torch.optim
中去了。
所以,一般通过优化器来将参数的梯度值清零:optimizer.zeros_grad()
将以上的过程总结为下面的三个步骤:
- 使用优化器将参数的梯度值清零:
optimizer.zero_grad()
- 根据损失值来计算损失值:
loss.backward()
- 使用优化器来更新参数:
optimizer.step()
第四篇 训练一个分类器
模型的保存分为两种:(1)仅保存参数;(2)保存整个模型
仅保存参数
torch.save(model.state_dict(), PATH) # PATH是某个具体路径
the_model = Model()
the_model.load_state_dict(torch.load(PATH))
保存整个模型
torch.save(the_mode, PATH)
the_model = torch.load(PATH)
如何在GPU上实现训练?
每个tensor都有一个device参数,如果将一个cpu上的tensor转化成gpu上的tensor,只需要t = t.device('cuda:0')
,但是在此之前需要检测cuda是否存在,否则代码在没有cuda上的机器上不能运行:
# generate the device
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")model = model.to(device)
这样模型就可以在显卡上运行了,只不过,输入input和正确答案target也要转一下:input.to(device)
和target.to(device)
除此之外还可以通过torch.cuda.device_count()来查看显卡的数量,从而实现分布式训练:
model = nn.DataParallel(model)
第五篇章 通过tensorboard进行可视化
tensorboard是一个独立的工具,为了对模型训练进行可视化,需要将summary写到指定目录中去(默认为logdir=runs),然后开启本地服务器tensorboard --logdir=xxx
,并在浏览器中(https://localhost:6006)查看。
写入tensorboard需要创建一个SummaryWriter
类对象:
from torch.utils.tensorboard import SummaryWriterwriter = SummaryWriter('runs/fashion_mnist_experiment_1')
第一个部分:图像(images)
对图像不感兴趣,所以这里不深究,通过add_image()
添加图像,并借助make_grid()
来画图。
img_grid = torchvision.utils.make_grid(images) # create grid of images
matplotlib_imshow(img_grid, one_channel=True) # show images
writer.add_image('four_fashion_mnist_images', img_grid)
第二个部分:计算图(graphs)
通过add_graph()
将整个模型写入目录中,可以查看模型结构。
writer.add_graph(net, images)
writer.close()
第三个部分:数据(projector)
通过add_embedding()
来将某个张量加入到tensorboard中去,当然高维数据都会通过降维成低维数据查看。
features = images.view(-1, 28 * 28)
writer.add_embedding(features, metadata=class_labels, label_img=images.unsequeeze(1))
writer.close()
第四个部分:添加标量(scalars)
将损失值等标量值加入到tensorboard中去
# writer down each 1000 batchs, running_loss is the total loss of 1000 batchs
writer.add_scalar('training_loss', running_loss/1000, epoch*len(trainloader)+i))
还可以画个figure上去
writer.add_figure('predictions vs actuals',plot_classes_preds(net, inputs, labels),global_step=epoch*len(trainloader)+i)
除此之外还有几个其他的画图方法,这个后面再慢慢加。