pytorch_study

此生再无相见时 提交于 2020-01-28 11:28:56

文章目录

**1 资源汇总

  1. pytorch 官网
  2. PyTorch 文件 python API
  3. pytorch 中文文档
  4. pytorch Windows 常见问题汇总

** 2 常见深度学习框架中的Tensor的通道顺序

  • caffe2 的Blob通道顺序:NCHW
  • Pytorch中tensor的通道顺序:NCHW
  • Tensorflow的tensor通道顺序:默认是NHWC, 也支持NCHW,使用cuDNN会更快;

其中:N:为batch;C为channel;H为height;W为width

**3 常见颜色通道顺序

原图像为RGB:

  • opencv的颜色通道顺序为BGR; cv2.cvtColor( ) 进行图像颜色空间转换
  • matplotlib的颜色通道顺序为RGB
  • caffe2的颜色通道顺序为BGR
  • skimage的颜色通道顺序为RGB

1 PyTorch Tutorials 1: 入门-1 60分钟闪电战

1.1 什么是PyTorch

PyTorch 是一个基于Python的科学计算软件包,针对两组受众:

  • 替代NumPy以使用GPU的功能
  • 提供最大灵活性和速度的深度学习研究平台

1.1.1 tensor

Tensor与NumPy的ndarrays类似,另外,Tensors也可以在GPU上使用以加速计算

from __future__ import print_function # 在开头加上from __future__ import print_function这句之后,即使在python2.X,使用print就得像python3.X那样加括号使用。python2.X中print不需要括号,而在python3.X中则需要。
import torch

x1 = torch.empty(5,3) # 构造一个未初始化的5*3矩阵
print("构造一个未初始化的5*3矩阵: x1")
print("x1 = {}".format(x1))

x2 = torch.rand(5,3)# 构造一个随机初始化的矩阵
print("构造一个随机初始化的矩阵: x2")
print("x2 = {}".format(x2))

x3 = torch.zeros(5,3,dtype=torch.long) # 构造一个零填充且dtype=long的矩阵
print("构造一个零填充且dtype=long的矩阵:x3 ")
print("x3 = {}".format(x3))

x4 = torch.tensor([5,5,3])
print("直接从数据构造张量:x4 ")
print("x3 = {}".format(x4))

x5 = x4.new_ones(5,3,dtype=torch.double)
x6 = torch.randn_like(x5,dtype=torch.float)
print("基于现有张量创建张量: x5")
print("x5 = {}".format(x5))
print("x6 = {}".format(x6))
print(x6.size())#大小

结果:

构造一个未初始化的5*3矩阵: x1
x1 = tensor([[9.2755e-39, 1.0561e-38, 9.0918e-39],
        [8.4490e-39, 1.0102e-38, 9.0919e-39],
        [1.0102e-38, 8.9082e-39, 8.4489e-39],
        [9.6429e-39, 8.4490e-39, 9.6429e-39],
        [9.2755e-39, 1.0286e-38, 9.0919e-39]])
构造一个随机初始化的矩阵: x2
x2 = tensor([[0.0165, 0.7937, 0.2671],
        [0.2964, 0.9791, 0.1708],
        [0.3662, 0.3443, 0.0226],
        [0.3664, 0.0351, 0.2979],
        [0.0750, 0.5245, 0.5292]])
构造一个零填充且dtype=long的矩阵:x3 
x3 = tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
直接从数据构造张量:x4 
x3 = tensor([5, 5, 3])
基于现有张量创建张量: x5
x5 = tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
x6 = tensor([[-0.7477,  0.5312, -1.4660],
        [-2.5972,  0.2740, -0.7484],
        [-0.4803, -1.9650,  0.1134],
        [ 2.1693, -0.5554, -0.4980],
        [ 0.3304,  0.9787,  0.1518]])
torch.Size([5, 3])

Process finished with exit code 0

torch.Size 实际上是一个元组,因此它支持所有元组操作。

1.1.2 operations

operator有多种语法

(1) 加法的语法:

  1. x+y
  2. torch.add(x,y)
  3. in-place 即 y.add_(x)
# operators :加法
y1 = torch.tensor([[5,3],[3,5]],dtype=torch.float)
y2 = torch.tensor([[1,1],[1,1]],dtype=torch.float)
print("加法+:")
print("y1+y2 = {}".format(y1+y2))

result = torch.empty(2,2)
torch.add(y1,y2,out=result)
print("加法:torch.add()")
print(result)

y2.add_(y1)
print("加法:in-place")
print(y2)

结果:

加法:
y1+y2 = tensor([[6., 4.],
        [4., 6.]])
加法:torch.add()
tensor([[6., 4.],
        [4., 6.]])
加法:in-place
tensor([[6., 4.],
        [4., 6.]])

Process finished with exit code 0

注意:

任何使张量in-place变化的操作都用固定_。例如:x.copy_(y)x.t_(),将改变x

(2) tensor的索引与NumPy的索引类似

y1 = torch.tensor([[5,3],[3,5]],dtype=torch.float)
print(y1[:,1])

结果:

tensor([3., 5.])

(3) 调整大小:torch.view

data1 = torch.randn(4,4)
data2 = data1.view(16)
data3 = data1.view(-1,8)
print("data1 = {}".format(data1))
print("data1_size = {}".format(data1.size()))
print("data2 = {}".format(data2))
print("data2_size = {}".format(data2.size()))
print("data3 = {}".format(data3))
print("data3_size = {}".format(data3.size()))

结果:

data1 = tensor([[-1.1949, -0.2015,  0.1611, -0.7577],
        [ 2.6310, -0.2445, -0.5748, -0.9437],
        [-0.5842, -1.4634, -1.7578,  0.0564],
        [-0.8272,  0.5982, -0.0105,  0.1601]])
data1_size = torch.Size([4, 4])
data2 = tensor([-1.1949, -0.2015,  0.1611, -0.7577,  2.6310, -0.2445, -0.5748, -0.9437,
        -0.5842, -1.4634, -1.7578,  0.0564, -0.8272,  0.5982, -0.0105,  0.1601])
data2_size = torch.Size([16])
data3 = tensor([[-1.1949, -0.2015,  0.1611, -0.7577,  2.6310, -0.2445, -0.5748, -0.9437],
        [-0.5842, -1.4634, -1.7578,  0.0564, -0.8272,  0.5982, -0.0105,  0.1601]])
data3_size = torch.Size([2, 8])

Process finished with exit code 0

(4) .item()

如果您具有一个元素张量,请使用.item()将该值作为Python数字获取

x = torch.randn(1)
print("x={}".format(x))
print("x.item() = {}".format(x.item()))

tensor的其他操作,包括移调、索引、切片、数学运算、线性代数、随机数等

1.1.3 NumPy Bridge

将Torch张量转换为NumPy数组

a = torch.ones(5) # 张量
print("a={}".format(a))
b = a.numpy() # 数组
print("b={}".format(b))

结果:

a=tensor([1., 1., 1., 1., 1.])
b=[1. 1. 1. 1. 1.]

1.1.4 Converting NumPy Array to Torch Tensor 将NumPy数组转换为Torch张量

import torch
import numpy as np
a = np.ones(5)# 数组
print("a={}".format(a))
b = torch.from_numpy(a) # 将数组NumPy转为Tensor
np.add(a,1,out=a)
print("a={}".format(a))
print("b={}".format(b))

结果:

a=[1. 1. 1. 1. 1.]
a=[2. 2. 2. 2. 2.]
b=tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

Process finished with exit code 0

pytorch的tensor对象与numpy数组是可以相互转换的,且numpy数组的默认类型是double

1.1.5 CUDA Tensors

Tensors can be moved onto any device using the .to method. 使用.to方法可以将张量移动到任何设备上

# let us run this cell only if CUDA is available
# We will use ``torch.device`` objects to move tensors in and out of GPU
if torch.cuda.is_available():
    device = torch.device("cuda")          # a CUDA device object
    y = torch.ones_like(x, device=device)  # directly create a tensor on GPU
    x = x.to(device)                       # or just use strings ``.to("cuda")``
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # ``.to`` can also change dtype together!

结果:

tensor([0.8337], device='cuda:0')
tensor([0.8337], dtype=torch.float64)

1.2 Autograd: 自动分化

autograd软件包是PyTorch中所有神经网络的核心。

(1) Tensor

  • torch.Tensor是程序包的中心类。如果将其属性设置 .requires_gradTrue,它将开始跟踪对其的所有操作。完成计算后,您可以调用.backward()并自动计算所有梯度。该张量的梯度将累加到.grad属性中。

  • 要停止张量跟踪历史记录,可以调用.detach()将其从计算历史记录中分离出来,并防止跟踪将来的计算。

  • 为了防止跟踪历史记录(和使用内存),您还可以将代码块包装在中。这在评估模型时特别有用,因为模型可能具有的可训练参数 ,但我们不需要梯度。with torch.no_grad():requires_grad=True

  • 还有一个类对于autograd实现非常重要-a Function

  • TensorFunction相互连接并建立一个无环图,该图对完整的计算历史进行编码。每个张量都有一个.grad_fn属性,该属性引用Function已创建的Tensor(用户创建的张量-他们的 除外)。grad_fn is None

  • 如果你想计算的衍生品,你可以叫.backward()Tensor。如果Tensor为标量(即,它包含一个元素数据),则无需为指定参数backward(),但是,如果它具有更多元素,则需要指定gradient 参数为匹配形状的张量。

#在PyTorch中,集中于所有神经网络的是autograd包。
#autograd.Variable是autograd包的核心类,它封装了一个张量,并支持几乎所有在该张量上定义的操作。
#一旦完成了你的计算,你可以调用.backward(),它会自动计算所有梯度。你可以通过.data属性访问原始的张量,而梯度w.r.t.这个变量被累积到.grad。
#还有一个类对于autograd的实现非常重要——一个函数。变量和函数是相互联系的,并建立一个非循环图,它编码了计算的一个完整历史。
#每个变量都有一个.grad_fn属性,该属性引用了一个创建了该变量的函数(除了由用户创建的变量之外,它们的grad_fn是None)。
#如果你想计算导数,你可以在一个变量上调用.backward()。如果变量是一个标量(也就是说它包含一个元素数据),那么你不需要为backward()指定任何参数,
#但是如果它是矢量,有更多元素,那么你需要指定一个grad_output参数,该参数是一个匹配形状的张量。
from __future__ import print_function
import torch

x = torch.ones(2,2,requires_grad=True)#创建一个张量并设置requires_grad=True为跟踪张量
print("创建一个张量并设置requires_grad=True为跟踪张量: x={}".format(x))
y=x+2#进行张量运算
print(y)
print(y.grad_fn)# y是由于操作而创建,因此具有grad_fn

z=y*y*3#进行更多操作
out=z.mean()
print(z)
print(out)

结果

创建一个张量并设置requires_grad=True为跟踪张量: x=tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x00000268AE837588>
tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>)
tensor(27., grad_fn=<MeanBackward0>)

Process finished with exit code 0

.requires_grad_( ... )requires_grad 就地(in-place)更改现有Tensor的标志。False如果未给出输入标志,则默认为。

a=torch.randn(2,2)
a = ((a*3)/(a-1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b=(a*a).sum()
print(b.grad_fn)

结果

False
True
<SumBackward0 object at 0x000001AEAF717F98>

Process finished with exit code 0

(2) Gradients

现在让我们反向传播。因为out包含单个标量,out.backward()所以等效于out.backward(torch.tensor(1.))

梯度:d(out)/dxx.grad

from __future__ import print_function
import torch

x = torch.ones(2,2,requires_grad=True)#创建一个张量并设置requires_grad=True为跟踪张量
print("创建一个张量并设置requires_grad=True为跟踪张量: x={}".format(x))
y=x+2#进行张量运算
print(y)
print(y.grad_fn)# y是由于操作而创建,因此具有grad_fn

z=y*y*3#进行更多操作
out=z.mean()
print("'z=y*y*3'={}".format(z))
print("out=z.mean()={}".format(out))

out.backward()
print("x.grad = {}".format(x.grad))

结果

创建一个张量并设置requires_grad=True为跟踪张量: x=tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x000001FFB284A2E8>
'z=y*y*3'=tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>)
out=z.mean()=27.0
x.grad = tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

Process finished with exit code 0

在这里插入图片描述
现在, 给出一个vector-Jacobian(向量雅可比布积)的例子

a = torch.randn(3,requires_grad=True)#生成均值为0,标准差为1的3个标准正态分布,一维张量
b = a*2
while b.data.norm()<1000:#括号未写内容默认为求2范数
    b = b*2
print("b={}".format(b))

结果

b=tensor([ -52.1273,  372.6357, 1114.6013], grad_fn=<MulBackward0>)

Process finished with exit code 0

补充:范数

  • 范数(norm)是数学中的一种基本概念。在泛函分析中,它定义在赋范线性空间中,并满足一定的条件,即①非负性;②齐次性;③三角不等式。
  • 它常常被用来度量某个向量空间(或矩阵)中的每个向量的长度或大小。
    在这里插入图片描述
    在这里插入图片描述
  1. 范数
# 范数
a = torch.ones((2,3)) # 建立一个tensor
b = torch.norm(a,p=1)# 指定求1范数,等价于b=a.data.norm(p=1)
c = torch.norm(a) # 默认求2范数,等价于c=a.data.norm()
print("建立一个张量 a={}".format(a))
print("1范数 b={}".format(b))
print("2范数 c={}".format(c))

结果:

建立一个张量 a=tensor([[1., 1., 1.],
        [1., 1., 1.]])
1范数 b=6.0
2范数 c=2.4494898319244385
  1. dim维度,按行/列求范数
# 在指定维度上求范数,返回输入张量给定维dim上每行/列的p范数 dim=0表示按列,dim=1表示案行
a1 = torch.tensor([[1,2,3,4],[1,2,3,4]]).float() # norm仅支持floatTensor, a1 是一个2*4的Tensor
b1 = torch.norm(a1,p=2,dim=0) # 按0维度求2范数,按列
c1 = torch.norm(a1,p=2,dim=1) # 按1维度求2范数,按行
print("张量a1={}".format(a1))
print("b1 = {}".format(b1))
print("c1 = {}".format(c1))

结果:

张量a1=tensor([[1., 2., 3., 4.],
        [1., 2., 3., 4.]])
b1 = tensor([1.4142, 2.8284, 4.2426, 5.6569])
c1 = tensor([5.4772, 5.4772])
  1. keepdim保持输出的维度
# keepdim 保持输出维度
a2 = torch.rand((2,3,4))
b2 = torch.norm(a2,p=2,dim=0,keepdim=True)
c2 = torch.norm(a2,p=2,dim=1,keepdim=True)
print("建立一个张量: a2={}".format(a2))
print("按0维度求2范数,按列,且保持输出维度:")
print("b2 = {}".format(b2))
print("按1维度求2范数,按行,且保持输出维度:")
print("c2 = {}".format(c2))

结果:

建立一个张量: a2=tensor([[[0.1698, 0.1888, 0.2717, 0.9134],
         [0.1803, 0.2694, 0.4204, 0.3550],
         [0.5115, 0.7364, 0.9427, 0.1710]],

        [[0.9369, 0.1454, 0.8053, 0.0939],
         [0.8771, 0.4274, 0.1368, 0.5517],
         [0.0274, 0.1561, 0.8043, 0.4437]]])
按0维度求2范数,按列,且保持输出维度:
b2 = tensor([[[0.9522, 0.2383, 0.8499, 0.9182],
         [0.8955, 0.5053, 0.4421, 0.6561],
         [0.5122, 0.7527, 1.2392, 0.4755]]])
按1维度求2范数,按行,且保持输出维度:
c2 = tensor([[[0.5683, 0.8065, 1.0674, 0.9948]],

        [[1.2837, 0.4777, 1.1464, 0.7142]]])

Process finished with exit code 0

1.3 神经网络

torch.nn模块构建神经网络。一个nn.Module包含层和一种方法forward(input),它返回output
在这里插入图片描述
LeNet是一个简单的前馈网络。它获取输入,将其一层一层地反馈,然后最终给出输出。

神经网络的典型训练过程如下:

  • 定义具有一些可学习参数(或权重)的神经网络
  • 遍历输入数据集
  • 通过网络处理输入
  • 计算损失(输出正确的距离有多远)
  • 反向传播:将梯度传播回网络参数
  • 权重更新:通常使用简单的更新规则来更新网络的权重:
    weight = weight - learning_rate * gradient
from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as f
import torch.optim as optim

# 1 构建模型 :定义网络
class Net(nn.Module):#所定义的Net是从nn.Module框架中继承而来的,而不是一个新玩意
    def __init__(self):#定义一个初始化函数,当调用这个Net的时候,内部会进行初始化
        super(Net,self).__init__()#等价于nn.Module.__init__(self)这里首先初始化(清空)所定义的网络
        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=3)
        self.conv2 = nn.Conv2d(6,16,3)
        self.fc1 = nn.Linear(16*6*6,120) # 6*6 特征图的大小
        self.fc2 = nn.Linear(120,84)# an affine operation: y = Wx + b
        self.fc3 = nn.Linear(84,10)
    def forward(self, x):#定义前向forward函数,引出自变量x,返回计算结果return x,反向(backward)函数使用autograd自动定义,在前向函数中可使用任何张量操作。
        # Max pooling over a (2, 2) window
        x = f.max_pool2d(f.relu(self.conv1(x)),(2,2)) #先调用kernal卷积conv1计算一次,再由激活函数relu计算一次,最后再经过最大池化计算一次
        x = f.max_pool2d(f.relu(self.conv2(x)),2)# If the size is a square you can only specify a single number
        x = x.view(-1,self.num_flat_features(x)) #将x的size统一变成二维,其中用-1表示组数,可以自适应原先的组数,其余的合并,
        x = f.relu(self.fc1(x))
        x = f.relu(self.fc2(x))
        x = self.fc3(x)
        return x
    def num_flat_features(self,x):
        #[1:],其中的1:就是将除了第一维的组数保留外,其余全部通过下面的乘法计算总数,并利用上面的view(-1,)合并成2维
        size = x.size()[1:]# all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

# 2 初始化网络
net = Net() # 初始化网络
print(net)
print("*"*40+"1-2 定义网络-初始化网络"+"*"*40)

#模型的学习参数由net.parameters()返回
params = list(net.parameters())
print("参数长度len(params)={}".format(len(params)))
print(params[0].size())# conv1's.weight
print("*"*40+" 网络参数输出"+"*"*40)

# 随机输入,调用网络
#input = torch.autograd.Variable(torch.randn(1,1,32,32))
input = torch.randn(1,1,32,32)
print("网络输入: input = {}".format(input))
out = net(input)
print("网络输出:out = {}".format(out))
print("*"*40+" 模拟网络输入输出"+"*"*40)

# 3 损失函数 nn模块中有几种不同的损失函数,一个简单的损失是:nn.MSELoss计算输入与目标之间的均方误差
target = torch.randn(10) #  # a dummy target, for example
print("标签:target={}".format(target))
target = target.view(1,-1)  # make it the same shape as output
print("标签:target={}".format(target))
criterion = nn.MSELoss()
loss = criterion(out,target)
print("均方误差loss = {}".format(loss))
print("*"*40+"3 损失函数"+"*"*40)

# 4 反向传播
#要反向传播错误,我们要做的就是loss.backward()。不过,您需要清除现有的梯度,否则梯度将累积到现有的梯度中。
#现在我们将调用loss.backward(),并查看向后前后conv1的偏差梯度。
net.zero_grad()# zeroes the gradient buffers of all parameters 将缓冲特度的参数归零
print('conv1.bais.grad before backward')
print(net.conv1.bias.grad)
loss.backward()#反向传播误差
print('conv1.bais.grad after backward')
print(net.conv1.bias.grad)
print("*"*40+"4 反向传播"+"*"*40)

# 5 更新权重 torch.optim
#实践中使用的最简单的更新规则是随机梯度下降(SGD)即:weight = weight - learning_rate * gradient
optimizer = optim.SGD(net.parameters(),lr=0.01)#创建优化器
# in your training loop:
optimizer.zero_grad()## zero the gradient buffers
output = net(input)
loss = criterion(output,target)
optimizer.step() # 权重更新
print("loss after optimizer:",loss)#经过优化以后打印损失观察一下
print("params[1] after optimizer:",params[1])#经过优化以后打印conv1的偏倚bias和上面未经优化的偏倚bias可以进行对比
#优化以后如何再进一步优化,优化到什么程度算是ok了?
print("*"*40+"5 权重更新"+"*"*40)

结果:

Net(
  (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
  (fc1): Linear(in_features=576, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
****************************************1-2 定义网络-初始化网络****************************************
参数长度len(params)=10
torch.Size([6, 1, 3, 3])
**************************************** 网络参数输出****************************************
网络输入: input = tensor([[[[ 0.5292, -0.1447, -0.0123,  ..., -0.7849, -1.3795, -1.3519],
          [-0.5759, -2.0916,  0.7251,  ...,  0.5552, -0.4991,  1.3427],
          [ 0.4304, -1.2179,  0.3672,  ...,  1.0113,  0.2425, -0.5043],
          ...,
          [-0.5749, -1.5526, -2.0207,  ...,  1.0086,  0.1407,  0.4457],
          [ 0.0684,  0.4982, -1.1754,  ..., -0.7470,  0.6411,  0.1230],
          [-0.5161,  0.3732,  0.1747,  ...,  0.6392,  0.4259,  0.6657]]]])
网络输出:out = tensor([[-0.1596, -0.0666,  0.0333,  0.0355,  0.0198, -0.1662, -0.0140,  0.1774,
          0.0318,  0.0974]], grad_fn=<AddmmBackward>)
**************************************** 模拟网络输入输出****************************************
标签:target=tensor([-0.3667, -1.0788, -1.2884, -0.1630,  0.2900, -0.6252,  0.9165, -1.5528,
         0.5669, -0.2360])
标签:target=tensor([[-0.3667, -1.0788, -1.2884, -0.1630,  0.2900, -0.6252,  0.9165, -1.5528,
          0.5669, -0.2360]])
均方误差loss = 0.7395011782646179
****************************************3 损失函数****************************************
conv1.bais.grad before backward
None
conv1.bais.grad after backward
tensor([-0.0045,  0.0096,  0.0087,  0.0003,  0.0191,  0.0031])
****************************************4 反向传播****************************************
loss after optimizer: tensor(0.7395, grad_fn=<MseLossBackward>)
params[1] after optimizer: Parameter containing:
tensor([-0.0408,  0.2781,  0.3060,  0.2181,  0.2288,  0.1992],
       requires_grad=True)
****************************************5 权重更新****************************************

Process finished with exit code 0

注意

  • 只需要定义forward函数,backward 就可以使用自动定义函数(计算梯度)autograd。您可以在forward函数中使用任何Tensor操作。
  • torch.nn仅支持小批量。整个torch.nn 包仅支持作为微型样本的输入,而不支持单个样本。
    在这里插入图片描述
  • 当我们调用时loss.backward(),整个图与损失是微分的,并且图中的所有张量都requires_grad=True 将具有.grad随梯度累积的张量。
    在这里插入图片描述
  • 在使用神经网络时,您希望使用各种不同的更新规则,例如SGD,Nesterov-SGD,Adam,RMSProp等。为实现此目的,pytorch构建了一个包:torch.optim实现所有这些方法.

1.4 训练分类器

训练图像分类器,我们将按顺序执行以下步骤:

  1. 使用以下命令加载和标准化CIFAR10训练和测试数据集 torchvision
  2. 定义卷积神经网络
  3. 定义损失函数
  4. 根据训练数据训练网络
  5. 在测试数据上测试网络

1.4.1 CPU上网络训练

PyTorch中,关于model.eval()和torch.no_grad()

Use both. They do different things, and have different scopes.

  • with torch.no_grad: disables tracking of gradients in autograd.
  • model.eval(): changes the forward() behaviour of the module it is called upon. eg, it disables dropout and has batch norm use the entire population statistics
# 训练图像分类器
# 我们将按顺序执行以下步骤:
# 1 使用以下命令加载和标准化CIFAR10训练和测试数据集 torchvision
# 2 定义卷积神经网络
# 3 定义损失函数
# 4 根据训练数据训练网络
# 5 在测试数据上测试网络
# coding:utf-8
from __future__ import print_function
import torch
import torchvision # 数据集和模型
import torchvision.transforms as transforms #数据集的预处理

import torch.nn as nn # 定义网络
import torch.nn.functional as f

import torch.optim as optim # 优化器

# 1 加载并标准化CIFAR-10
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)# 下载数据集
trainset = torchvision.datasets.CIFAR10(root = 'D:\code\pytorch_study\data\cifar-10-python',train=True,download=False,transform=transform)
#trainloader = torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True)#数据加载器
trainloader = torch.utils.data.DataLoader(trainset,batch_size=32,shuffle=True,num_workers=0)#数据加载器
print("trainloader={}".format(trainloader))
#testset = torchvision.datasets.CIFAR10(root = './data',train=False,download=True,transform=transform)
testset = torchvision.datasets.CIFAR10(root = 'D:\code\pytorch_study\data\cifar-10-python',train=False,download=False,transform=transform)
testloader = torch.utils.data.DataLoader(testset,batch_size=4,shuffle=False,num_workers=0)
#testloader = torch.utils.data.DataLoader(testset,batch_size=32,shuffle=False)
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# 2 展示训练图像
import matplotlib.pyplot as plt
import numpy as np
def imshow(img):#展示训练图像
    img = img / 2 +0.5 # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg,(1,2,0)))
    plt.show()
# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()
# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

# 3 定义卷积网络
class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3,out_channels=6,kernel_size=5)
        self.pool = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(6,16,5)
        self.fc1 = nn.Linear(16*5*5,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,16*5*5)
        x = f.relu(self.fc1(x))
        x = f.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 初始化网络
net = Net()

# 4 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()# 损失函数为交叉熵
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)# SGD优化器

# 5 训练网络 只需要遍历数据迭代器,然后将输入馈送到网络并进行优化。
for epoch in range(2):# loop over the dataset multiple times
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0 ):
        inputs,labels = data  # get the inputs; data is a list of [inputs, labels]
        optimizer.zero_grad() # zero the parameter gradients
        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs,labels)#损失
        loss.backward()# 网络后传
        optimizer.step()#网络优化
        # 输出统计结果
        running_loss += loss.item()# .item()将tensor转为python数值
        if i % 2000 == 1999: # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f'%(epoch+1,i+1,running_loss/2000))
            running_loss = 0.0
print('Finished Training')

# 6 快速保存训练有素的模型
PATH = './cifar_net.pth'
torch.save(net.state_dict(),PATH)

# 7 在测试数据集上测试网络
# 将通过预测神经网络输出的类别标签并根据实际情况进行检查来进行检查。如果预测正确,则将样本添加到正确预测列表中。

# 7.1 展示测试集上的图片
dataiter = iter(testloader)#list、tuple等都是可迭代对象,可通过iter()函数获取这些可迭代对象的迭代器,然后我们可以对获取到的迭代器不断使⽤next()函数来获取下⼀条数据
images, labels = dataiter.next()
#print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ',' '.join('%5s' % classes[labels[j]] for j in range(4)))

# 7.2 重新加载保存模型(注意:这里不需要保存和重新加载模型,我们只是为了说明如何这样做):
net = Net()
net.load_state_dict(torch.load(PATH))
outputs = net(images)#看看神经网络如何看待以上这些示例:
# 7.3 输出是10类的能量。一个类别的能量越高,网络就认为该图像属于特定类别。因此,让我们获得最高能量的指数
_, predicted = torch.max(outputs,1)
print('Predicted: ',' '.join('%5s' % classes[predicted[j]] for j in range(4)))

# 7.3 看一下网络在整个数据集上的表现
correct = 0
total = 0
with torch.no_grad():#disables tracking of gradients in autograd.
    for data in testloader:
        images,labels = data
        outputs = net(images)
        _,predicted = torch.max(outputs,1)#输出是10类的能量。一个类别的能量越高,网络就认为该图像属于特定类别。因此,让我们获得最高能量的指数
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
print("Accuracy of the network on the 10000 test images: %d %%" % (100 * correct / total))

# 7.5 查看那些类别表现良好,那些表现不佳
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images,labels = data
        outputs = net(images)
        _,predicted = torch.max(outputs,1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
for i in range(10):
    print('Accuracy of %5s : %2d %%' % (classes[i], 100*class_correct[i]/class_total[i]))

运行结果:

trainloader=<torch.utils.data.dataloader.DataLoader object at 0x0000025479E9ABE0>
  cat  deer plane   car
Finished Training
GroundTruth:    cat  ship  ship plane
Predicted:    cat  ship   car  ship
Accuracy of the network on the 10000 test images: 38 %
Accuracy of plane : 49 %
Accuracy of   car : 58 %
Accuracy of  bird :  2 %
Accuracy of   cat : 14 %
Accuracy of  deer : 30 %
Accuracy of   dog : 34 %
Accuracy of  frog : 44 %
Accuracy of horse : 60 %
Accuracy of  ship : 30 %
Accuracy of truck : 56 %

Process finished with exit code 0

在这里插入图片描述
在这里插入图片描述
注意问题:
在这里插入图片描述
解决方法:可以关闭所有进程,在运行本代码

1.4.2 GPU 上训练

在这里插入图片描述

1.5 可选:数据并行

本教程中,将学习如何使用多个GPU DataParallel(数据并行)

2 PyTorch Tutorials 1: 入门-2 编写自定义数据集,数据加载器和转换

PyTorch提供了许多工具来简化数据加载过程,并有望使代码更具可读性。在本教程中,我们将看到如何从非平凡的数据集中加载和预处理/增强数据。

3 PyTorch Tutorials 1: 入门-3 使用TensorBoard 可视化模型、数据和训练

PyTorchTensorBoard集成在一起,TensorBoard是一种工具,用于可视化神经网络训练运行的结果。

  1. TensorBoard设置
  2. 写入TensorBoard
  3. 使用TensorBoard检查模型
  4. TensorBoard中添加一个“”
  5. 使用TensorBoard跟踪模型训练
  6. 使用TensorBoard评估经过训练的模型

4 PyTorch Tutorials 2: 图片

6 PyTorch 中文文档

6.1 package参考

6.1.1 torch

(1) 张量tensor

(2) 创建操作

(3) 索引、切片、连接、换位

2.1.2 torch.nn

(1) 参数

(2) 容器Containers

add_module(name, module)

(3) 卷积层

 self.conv1 = torch.nn.Sequential( # input_size = 227*227*3
            torch.nn.Conv2d(in_channels=3,out_channels=96,kernel_size=11,stride=4,padding=0),#output_size = 55*55*96
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=3,stride=2)#output_size = 27*27 *96
        )

(4) 池化层

(5) Non-Linear Activations

(6) Normalization layers

(7) Recurrent layers

(8) Linear layers

(9) Dropout layers

(10) Sparsel layers

(11) Distance functions

(12) Loss functions 损失函数

  1. 交叉熵损失函数
class torch.nn.CrossEntropyLoss(weight=None, size_average=True)

此标准将LogSoftMaxNLLLoss集成到一个类中。当训练一个多类分类器的时候,这个方法是十分有用的。
在这里插入图片描述

(13) Vision layers

(14) Multi-GPU layers

(15) Utilities

6.1.3 torch.autograd

torch.autograd提供了类和函数用来对任意标量函数进行求导。要想使用自动求导,只需要对已有的代码进行微小的改变。只需要将所有的tensor包含进Variable对象中即可。

6.1.4 torch.utils.data

(1) torch.utils.data.DataLoader()

class torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=<function default_collate>, pin_memory=False, drop_last=False)

数据加载器。组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。
参数:

  • dataset (Dataset) – 加载数据的数据集。
  • batch_size (int, optional) –每个batch加载多少个样本(默认: 1)。
  • shuffle (bool, optional) –设置为True时会在每个epoch重新打乱数据(默认: False).
  • sampler (Sampler, optional) –定义从数据集中提取样本的策略。如果指定,则忽略shuffle参数。
  • num_workers (int, optional) –用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)
  • collate_fn (callable, optional) –
  • pin_memory (bool, optional) –
  • drop_last (bool, optional) –如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch size整除,则最后一个batch将更小。(默认: False)

6.2 torchvision

torchvision 包含了目前流行的数据集,模型结构和常用的图片转换工具

6.2.1 torchvision.datasets

  1. MNIST
  2. COCO (用于图像标注和目标检测)
  3. LSUN Classification 是一个场景理解图像数据集,主要包含了卧室、固房、客厅、教室等场景图像。
  4. ImageFolder (一个通用的数据加载器,)
  5. Imagenet-12
  6. CIFAR-10 and CIFAR-100
  7. STL10
dset.CIFAR10(root, train=True, transform=None, target_transform=None, download=False)

dset.CIFAR100(root, train=True, transform=None, target_transform=None, download=False)

参数说明:
- root : cifar-10-batches-py 的根目录
- train : True = 训练集, False = 测试集
- download : True = 从互联上下载数据,并将其放在root目录下。如果数据集已经下载,什么都不干。
-transform:数据预处理
-target_transform: 一个函数,输入为target,输出对其的转换。例子,输入的是图片标注的string, 输出为word的索引。

示例:

# 预处理
data_tf = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])# 将图像转化成tensor,然后继续标准化,即减均值,除以方差

# 读取数据集
train_dataset = torchvision.datasets.CIFAR10(root = '.\\data1',train=True,transform=data_tf,download=True)
test_dataset = torchvision.datasets.CIFAR10(root='.\\data1',train=False,transform=data_tf)

6.2.2 torchvision.models

  • AlexNet
  • VGG
  • ResNet
  • SqueezeNet
  • DenseNet
    可以使用随机初始化的权重来创建这些模型
import torchvision.models as models
resnet18 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
densenet = models.densenet_161()

或者用预训练(pre-trained)的模型

import torchvision.models as models
#pretrained=True就可以使用预训练的模型
resnet18 = models.resnet18(pretrained=True)
alexnet = models.alexnet(pretrained=True)

在这里插入图片描述

6.2.3 torchvision.transforms

(1) 对PIL.Image进行变换

(注:PIL(Python Image Library)是python的第三方图像处理库,但是由于其强大的功能与众多的使用人数,几乎已经被认为是python官方图像处理库了。)
(1.1) class torchvision.transforms.Compose(transforms)
将多个transform组合起来使用。
transforms : 由transform构成的列表

transforms.Compose([transforms.CenterCrop(10),transforms.ToTensor(),])

示例:

data_tf = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])# 将图像转化成tensor,然后继续标准化,即减均值,除以方差

(1.2) class torchvision.transforms.CenterCrop(size)
将给定的PIL.Image进行中心切割,得到给定的sizesize可以是tuple(target_height, target_width)size也可以是一个Integer,在这种情况下,切出来的图片的形状是正方形。

(1.3) class torchvision.transforms.RandomCrop(size, padding=0)
切割中心点的位置随机选取。size可以是tuple也可以是Integer

(1.4) class torchvision.transforms.RandomHorizontalFlip
随机水平翻转给定的PIL.Image,概率为0.5。即:一半的概率翻转,一半的概率不翻转。

(1.5) class torchvision.transforms.RandomSizedCrop(size, interpolation=2)
先将给定的PIL.Image随机切,然后再resize成给定的size大小。

(1.6) class torchvision.transforms.Pad(padding, fill=0)
将给定的PIL.Image的所有边用给定的pad value填充。 padding:要填充多少像素 fill:用什么值填充

from torchvision import transforms
from PIL import Image
padding_img = transforms.Pad(padding=10, fill=0)#上下左右都要填充10个像素

(1.6) class torchvision.transforms.Scale(size)
对图片进行缩放,若原图“height>width”,则改变大小后的图片是“(size*height/width,size)

(2) 对Tensor进行变换

(2.1)class torchvision.transforms.Normalize(mean, std)
给定均值:(R,G,B) 方差:(R,G,B),将会把Tensor正则化。即:Normalized_image=(image-mean)/std

(2.2)Conversion Transformsclass torchvision.transforms.ToTensor
把一个取值范围是[0,255]PIL.Image或者shape(H,W,C)numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]torch.FloadTensor

(3)通用变换

class torchvision.transforms.Lambda(lambd) 使用lambd作为转换器。

6.2.4 torchvision.utils

(1) torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, range=None, scale_each=False)

给定 4D mini-batch Tensor, 形状为 (B x C x H x W),或者一个a list of image,做成一个size(B / nrow, nrow)sprite image

  • normalize=True ,会将图片的像素值归一化处理
  • 如果 range=(min, max), min和max是数字,那么min,max用来规范化image
  • scale_each=True ,每个图片独立规范化,而不是根据所有图片的像素最大最小值来规范化

(2) torchvision.utils.save_image(tensor, filename, nrow=8, padding=2, normalize=False, range=None, scale_each=False)

将给定的Tensor保存成image文件。如果给定的是mini-batch tensor,那就用make-grid做成sprite image,再保存。

pytorch model

01 pytorch LeNet-5 (98年)

在这里插入图片描述

02 pytorch Alex(2012年)

03 pytorch vgg(2014年)

论文原文:Very Deep Convolutional Networks for Large-Scale Image Recognition
在这里插入图片描述
代码:

04 pytorch ResNet(2015年)

05 pytorch DenseNet(2017年)

论文链接:https://arxiv.org/pdf/1608.06993.pdf

DenseNet采用了高密度的跳连结构,对于每一层,使用先前所有层的输出作为输入,该层的输出将作为之后所有层的输入的一部分。因此对于一个dense模块,假设有 L 层,那么存在 L(L+1)/2 直接的连接。
在这里插入图片描述
dense模块之后会连接一个transition层,由1x1卷积和平均池化构成。
在这里插入图片描述

Pytorch实现DenseNet

06 pytorch SeNet(2018年)

7 python model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!