今天看了pytorch官方的tutorial,好美的教程哦,说是60min的闪电战,结果运行完代码,花了一整天,是我不配,网站官方地址是:https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
这篇tutorial主要分为四个部分:
1、什么是Tensor,和Tensorflow介绍差不多
2、自动求梯度(微分)
3、神经网络架构
4、以一个图像分类作为example实战
是一个基于python的科学计算包,有两个作用
【1】取代numpy,而且可以在GPU 上运行
【2】一个提供了最大灵活性和速度的深度学习研究平台
我的目的肯定是2,才不会为了取代numpy安装torch
关于torch有这么记过方法来生成特殊的Tensor(矩阵)
torch.empty(2,3,4) | 生成一个参数size的矩阵,值为原内存值 |
torch.rand(2,3,4) | 随机值生成一个矩阵 |
zeros(2,3,4) | 所有元素都为0 |
ones(2,3,4) | 所有元素为1 |
eye(2,2) | 对角矩阵 |
tensor([8,2,8]) | 生成值为【8,2,8】的Tensor,指定值 |
randn_like(a) ones_like(a) zeros_like(a) ..._like(a) |
值为'_'前面的属性,比如随机,属性和参数a一致,除非指明 |
Tesor.size()返回的是元组,支持所有的元组操作
【1】加:x+y 或 torch.add(x,y,out=res) 或 y.add_(x) 等价于y+=x
任何改变张量对象的操作都有“_” e.g. x.copy_(y) x.t_()
【2】切片 :x[ : , 1 ]
【3】reshape/resize:y=x.view(16) y=x.view(-1,8)
【4】当tensor只有一个元素,可以使用a.item()来调用这个值
【5】z=a.mean()
【6】求范式:a.norm(p='fro'),fro是2范式
【7】x.eg(y) 判断两个是否相等
【8】y=x.detach() 防止被追踪
tensor操作一个标量时,比如a+1、a*2,tensor里的每个元素都会进行该操作
如果Torch的Tensor在CPU上运行,则Tensor和Numpy互相转化的变量共享一片内存
tensor (a) -------------a.numpy()---------------> numpy(b)
tensor (a) <------tensor.from_numpy(b)------- numpy(b)
听到长名词很高级,其实不然,不就是微分吗?pytorch的中心包就是autograd库,提供了tensor中所有操作的自动微分
重点:如果你设置tensor对象的.requires_grad属性为True【a.requires_grad_=True】,就开始追踪其上所有操作,当完成所有计算之后,你可以调用.backward()来自动计算所有的梯度,tensor的梯度会自动累加到.grad属性上
可以使用x.detach()防止梯度被追踪
也可以把代码加入 with torch.no_grad: ,可以在评估模型使用这种模式,因为评估时不需要计算梯度,y=x.detach()是获的一个有相同值但是不需要梯度的新的tensor,
有一个库 torch.Function,教程里说它和Tensor构成了一个有向无环图,我开始不理解,后面做笔记才理清,表示和存储了完整的计算历史,Tensor有个.grad_fn属性,它是引用创建tensor对象的那个Funcation,(除非这个张量是用户手动创建的,即,这个张量的 grad_fn
是 None
)
tensor.backward()自动计算所有梯度,当其为标量时,就不用指定参数默认参数是out.backward(torch.tensor(1.)),假如不是,就需要指定梯度参数,维度要和tensor匹配,why?我是看的这篇文章理解的https://blog.csdn.net/sinat_28731575/article/details/90342082
https://zhuanlan.zhihu.com/p/33378444
这是官方文档,第一个参数tensor就是对象tensor,第二个参数就是我很好奇的参数了,假如不引用的话,会提示:
为什么在求导的过程中需要引入这个参数,如果我们不引入这个参数的话,则会报下面的错误:
RuntimeError: grad can be implicitly created only for scalar outputs
即为提示我们输出不是一个标量
因为我们平时调用这个函数都是对最后的loss进行backward,而这是一个标量,更普遍的是,它可以tensor对tensor进行backward:
注意:
pytoch构建的计算图是动态图,为了节约内存,所以每次一轮迭代完之后计算图就被在内存释放,所以当你想要多次backward时候就会报错,需要添加标识:retain_graph=True
loss.backward(retain_graph=True) # 添加retain_graph=True标识,让计算图不被立即释放
import torch
from torch.autograd import Variable # torch 中 Variable 模块
x = Variable(torch.FloatTensor([[1, 2]]), requires_grad=True) # 定义一个输入变量
y = Variable(torch.FloatTensor([[3, 4],[5, 6]]))
loss = torch.mm(x, y) # 变量之间的运算
loss.backward(torch.FloatTensor([[1, 0]]), retain_graph=True) # 求梯度,保留图
print(x.grad.data) # 求出 x_1 的梯度
x.grad.data.zero_() # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[0, 1]]), retain_graph=True) # 求出 x_2的梯度
print(x.grad.data) # 求出 x_2的梯度
x.grad.data.zero_() # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[0, 2]]), retain_graph=True) # 求出 x_2的梯度
print(x.grad.data) # 求出 2*x_2的梯度
x.grad.data.zero_() # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[1, 1]]), retain_graph=True) # 求出 x_2的梯度
print(x.grad.data) # 求出 x_2+x_2的梯度
输出:
理解:
最后的可以组成雅克比矩阵,与传入的参数相乘就可以了
作用:This characteristic of vector-Jacobian product makes it very convenient to feed external gradients into a model that has non-scalar output.
使用torch.nn可以构建神经网络,nn.Module类包含了许多层,以及forward()方法来返回输出
神经网络过程就不说了
weight= weight -learning_rate *gradient
三四章内容我就放一起讲了,我主要介绍我的代码,相比较于tutorial上的代码,有些调整。不仅包括了卷积层的kernel size
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
Conv2d(1,6,3):输入的channel大小为1,输出的channel为6,卷积核大小为3*3
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
#
self.conv1=nn.Conv2d(3,6,3)
self.pool=nn.MaxPool2d(2,2)
self.conv2=nn.Conv2d(6,16,4)
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 = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
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 x
def num_flat_features(self,x):
y=x.size()[1:]
s=1
for i in y:
s=s*i
return s
定义的网路结构需要继承nn.Module,而且overide forward函数,backward()不需要重载,自动实现。学习到的参数都存储在net.parameters()
注意!torch.nn只支持mini-batch,不支持单个案例输入,举个例子,nn.Conv2d
will take in a 4D Tensor of nSamples x nChannels x Height x Width
.假如是单个案例,可以通过input.unsqueeze(0)转化为一个batch sample
简要回顾:
torch.Tensor
- A multi-dimensional array with support for autograd operations like backward()
. Also holds the gradient w.r.t. the tensor.nn.Module
- Neural network module. Convenient way of encapsulating parameters, with helpers for moving them to GPU, exporting, loading, etc.nn.Parameter
- A kind of Tensor, that is automatically registered as a parameter when assigned as an attribute to a Module
.autograd.Function
- Implements forward and backward definitions of an autograd operation. Every Tensor
operation creates at least a single Function
node that connects to functions that created a Tensor
and encodes its history.损失函数将the (output, target) pair作为输入,记住output在前,有很多损失函数:https://pytorch.org/docs/stable/nn.html
定义损失函数和优化器
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
假如想得到输出的FN图,可以这样
print(loss.grad_fn) # MSELoss
print(loss.grad_fn.next_functions[0][0]) # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU
反向传播前别忘了net.zero_grad() ,否则会累加
net=Net()
for epoch in range(5):
running_loss=0.0
running_acc=0.0
for i,data in enumerate(trainloader,0):
inputs,labels=data
optimizer.zero_grad()
output=net(inputs)
_,pred=torch.max(output,1)
loss=criterion(output,labels)
loss.backward()
optimizer.step()
running_loss+=loss.item()
running_acc+=torch.sum(pred==labels)
if i%2000==1999:
print('Epoch:{}:{} :loss={},acc={}'.format(
epoch+1,i+1,running_loss/2000,running_acc/12000))
running_loss=0.0
running_acc=0.0
print("training is over")
有个torch.max函数,torch.max有两个参数:输入数组及比较的维度,输出有两个:第一个是结果,即该维度下最大的值是多少,第二个是最大值的索引
x:
tensor([[0.5285, 0.1247, 0.8332, 0.5485],
[0.7917, 0.6138, 0.5881, 0.3381],
[0.4226, 0.6605, 0.8571, 0.0399],
[0.1716, 0.0609, 0.9712, 0.4838]])
torch.max(x,1):
(tensor([0.8332, 0.7917, 0.8571, 0.9712]), tensor([2, 0, 2, 2]))
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
torchvision.transforms.Compose([传入一个数组]):通过compose将各个变换串联起来,参数是各个变换的实例对象
变换包括在PIL格式图片上的转换、torch.Tensor上的转换、类型转换:
1、剪切并返回中间区域、饱和度亮度对比度调整、灰度图、填充、旋转、裁剪等
2、对张量图像进行标准化处理Normalize(mean,std)
3、类型转换ToPILImage(mode=None)、ToTensor()参数可以是PIL或者是np.ndarray
DataLoader本质就是一个迭代对象,可以用iter()访问,不可以用next()访问,参数batch_size:每个batch的大小,每一次next()的数量就是batch_size大小
使用iter(DataLoader对象)返回的是一个迭代器,可以用next()访问
也可以使用for input,label in dataloaders:进行访问
num_workers表示用多少个线程处理data loading,默认为0,表示所有的数据都将load到主线程中
def test_(testloader):
PATH = './cifar_net.pth'
net=Net()
net.load_state_dict(torch.load(PATH))
running_loss = 0.0
running_acc = 0.0
for i, data in enumerate(testloader, 0):
inputs, labels = data
optimizer.zero_grad()
output = net(inputs)
_, pred = torch.max(output, 1)
loss = criterion(output, labels)
running_loss += loss.item()
running_acc += torch.sum(pred == labels)
if i % 100 == 99:
print('Epoch:{} :loss={},acc={}'.format(
i + 1, running_loss / 100, running_acc / 600))
running_loss = 0.0
running_acc = 0.0
完整代码:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
#
self.conv1=nn.Conv2d(3,6,3)
self.pool=nn.MaxPool2d(2,2)
self.conv2=nn.Conv2d(6,16,4)
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 = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
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 x
def num_flat_features(self,x):
y=x.size()[1:]
s=1
for i in y:
s=s*i
return s
def imshow(img):
img=img/2+0.5
npimg=img.numpy()
plt.imshow(np.transpose(npimg,(1,2,0)))
plt.show()
'''
过程:
1、创建一个DataSet对象
2、创建一个DataLoader对象
3、循环这个DataLoader对象,并将data、label加载在模型中进行训练
'''
'''
torchvision.transforms.Compose([传入一个数组]):通过compose将各个变换串联起来
参数是各个变换的实例对象
变换包括在PIL格式图片上的转换、torch.Tensor上的转换、类型转换:
1、剪切并返回中间区域、饱和度亮度对比度调整、灰度图、填充、旋转、裁剪等
2、对张量图像进行标准化处理Normalize(mean,std)
3、类型转换ToPILImage(mode=None)、ToTensor()参数可以是PIL或者是np.ndarray
'''
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset,batch_size=6,shuffle=True,num_workers=2)
'''
DataLoader本质就是一个迭代对象,可以用iter()访问,不可以用next()访问
batch_size:每个batch的大小,每一次next()的数量就是batch_size大小
使用iter(DataLoader对象)返回的是一个迭代器,可以用next()访问
也可以使用for input,label in dataloaders:进行访问
num_workers表示用多少个线程处理data loading,默认为0,表示所有的数据都将load到主线程中
'''
testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=6,shuffle=True,num_workers=2)
str_class='plane car bird cat deer dog frog horse ship truck'
classes=str_class.split()
net=Net()
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
def test_(testloader):
PATH = './cifar_net.pth'
net=Net()
net.load_state_dict(torch.load(PATH))
running_loss = 0.0
running_acc = 0.0
for i, data in enumerate(testloader, 0):
inputs, labels = data
optimizer.zero_grad()
output = net(inputs)
_, pred = torch.max(output, 1)
loss = criterion(output, labels)
running_loss += loss.item()
running_acc += torch.sum(pred == labels)
if i % 100 == 99:
print('Epoch:{} :loss={},acc={}'.format(
i + 1, running_loss / 100, running_acc / 600))
running_loss = 0.0
running_acc = 0.0
'''
torch.max有两个参数:输入数组及比较的维度,输出有两个:第一个是结果,即该维度下最大的值是多少,第二个是最大值的索引
x:
tensor([[0.5285, 0.1247, 0.8332, 0.5485],
[0.7917, 0.6138, 0.5881, 0.3381],
[0.4226, 0.6605, 0.8571, 0.0399],
[0.1716, 0.0609, 0.9712, 0.4838]])
torch.max(x,1):
(tensor([0.8332, 0.7917, 0.8571, 0.9712]), tensor([2, 0, 2, 2]))
'''
#_,predicted=torch.max(outputs,1)
#print("Predicted:", ",".join('%5s' % classes[predicted[j]] for j in range(6)))
if __name__ == '__main__':
for epoch in range(5):
running_loss=0.0
running_acc=0.0
for i,data in enumerate(trainloader,0):
inputs,labels=data
optimizer.zero_grad()
output=net(inputs)
_,pred=torch.max(output,1)
loss=criterion(output,labels)
loss.backward()
optimizer.step()
running_loss+=loss.item()
running_acc+=torch.sum(pred==labels)
if i%2000==1999:
print('Epoch:{}:{} :loss={},acc={}'.format(
epoch+1,i+1,running_loss/2000,running_acc/12000))
running_loss=0.0
running_acc=0.0
print("training is over")
PATH='./cifar_net.pth'
torch.save(net.state_dict(),PATH)
test_(testloader)