一、简介
PyTorch 是一个基于 Torch 的 Python 开源机器学习库(Python+Torch(深度学习框架)),由Facebook的人工智能研究小组开发。 Pytorch类似于Numpy,可以使用GPU,运行在CUDA上;内置动态图,可以定义深度学习模型,可灵活的进行训练和应用。
(1)PyTorch 基于Python的科学计算包,服务于以下两种场景:
● 作为NumPy的替代品,可以使用GPU的强大计算能力 ● 提供最大的灵活性和高速的深度学习研究平台
(2)PyTorch 是一个 Python 包,提供两个高级功能:
● 具有强大的GPU加速的张量计算 (如NumPy) ● 包含自动求导系统的的深度神经网络
二、pytorch 的基石——Tensor 张量
Tensors 与 Numpy 中的 ndarrays 类似,但是在 PyTorch中 Tensors 可以使用 GPU 进行计算。
要介绍 Tensor 这个数据类型,我觉得有必要扯一下数学。我们都知道:标量(Scalar)是只有大小,没有方向的量,如1,2,3等;向量(Vector)是有大小和方向的量,其实就是一串数字,如(1,2);矩阵(Matrix)是好几个向量拍成一排合并而成的一堆数字,如 [1,2;3,4]。
标量,向量,矩阵它们三个都是张量,标量是零维的张量,向量是一维的张量,矩阵是二维的张量。除此之外,张量还可以是四维的、五维的等等。  张量就是按照任意维排列的一堆数字的推广。如图所示,矩阵不过是三维张量下的一个二维切面。要找到三维张量下的一个标量,需要三个维度的坐标来定位。
1、创建 tensor
可以通过 torch.tensor() 将普通数组转化为 tensor,torch.empty() 或r and() 可以创建随机 tensor,torch.zeros()、ones() 创建数据都是 0 或 1 的 tensor。
import torch x = torch.tensor([5.5, 3]) # 创建一维张量
print(x)
x = torch.rand(5, 3) # 随机生成5*3的矩阵 print(x)

2、tensor 运算
(1) 算术操作
result = torch.empty(5, 3)
x = torch.rand(5, 3)
y = torch.rand(5, 3)
torch.add(x, y, out=result)
print(x)
print(y)
print(result)

(2)索引
我们还可以使?类似NumPy的索引操作来访问 Tensor 的一部分,需要注意的是:索引出来的结果与原数据共享内存,也即修改?个,另?个会跟着修改 。
x = torch.rand(5, 3)
print(x)
y = x[0,:]
y += 1
print(y)
print(x)
print(x[0,:])

(3)改变形状
注意 view() 返回的新 tensor 与源 tensor 没有共享内存(其实是 tensor 的副本),也即更改其中的?个,另外?个不会跟着改变 。(顾名思义,view仅是改变了对这个张量的观察角度)
x = torch.rand(5, 3)
print(x)
z = x.view(-1,5)
print(z)
print(x)

3、张量和 Numpy 的相互转换
张量和Numpy数组之间的转换十分容易。
(1)Tensor 到 Numpy,在使用 Cpu 的情况下,张量和 array 将共享他们的物理位置,改变其中一个的值,另一个也会随之变化。
a = torch.ones(5)
print(a)
"""
tensor([1., 1., 1., 1., 1.])
"""
b = a.numpy()
print(b)
"""
[1. 1. 1. 1. 1.]
"""
a.add_(1)
print(a)
print(b)
"""
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
"""
(2)Numpy 到 Tensor ,在使用 Cpu 的情况下,张量和 array 将共享他们的物理位置,改变其中一个的值,另一个也会随之变化。
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
"""
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
"""
(3)Gpu下的转换
# 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")
y = torch.ones_like(x, device=device)
x = x.to(device)
z = x + y
print(z)
print(z.to("cpu", torch.double))
"""
tensor([-0.4743], device='cuda:0')
tensor([-0.4743], dtype=torch.float64)
"""
三、自动求导机制
在pytorch中,神经网络的核心是自动微分。自动微分包会提供自动微分的操作,它是一个取决于每一轮的运行的库,你的下一次的结果会和你上一轮运行的代码有关,因此,每一轮的结果,有可能都不一样。
PyTorch 中,所有神经网络的核心是 autograd 包,它为张量上的所有操作提供了自动求导机制。Pytorch 通过 Tensor 来储存数据,我们可以把它看作一个节点,通过 Function 实现数据操作,即节点之间转换的路径。
autograd 包为张量上的所有操作提供了自动求导。
它是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行,并且每次迭代可以是不同的。
torch.Tensor 是这个包的核心类。如果设置.requires_grad 为 True,那么将会追踪所有对于该张量的操作 。
当完成计算后通过调用 .backward(),自动计算所有的梯度,这个张量的所有梯度将会自动积累到 .grad 属性 。 这里是引用
在自动梯度计算中还有另外一个重要的类 Function。Tensor 和 Function 互相连接并生成一个非循环图,它表示和存储了完整的计算历史。
每个张量都有一个 .grad_fn 属性,这个属性引用了一个创建了 Tensor 的 Function (除非这个张量是用户手动创建的,即这个张量的 grad_fn 是 None)。
import torch
x=torch.ones(2,2,requires_grad=True)
print(x)
print(x.grad_fn)
 由于x是被直接创建的,也就是说它是一个叶子节点,所以它的grad_fn属性的值为None
如果需要计算导数,你可以在 Tensor 上调用.backward() ;获取求得的导数用 .grad 方法 。
如果 Tensor 是一个标量(即它包含一个元素数据)则不需要为 backward() 指定任何参数,但是如果它有更多的元素,你需要指定一个 gradient 参数来匹配张量的形状。
1、标量对张量的求导
原则上,pytorch 不支持张量对张量的求导,它只支持标量对张量的求导。
先看标量对张量求导的情况:
如下所示为一个训练过程,输入张量x,经过一系列操作之后得到输出y。  如下所示为实现上面过程的代码,首先通过 tensor 定义张量,通过属性 requires_grad 指定是否需要自动求导 。之后执行正向操作 *w1、+b、*w2,最后求平均得到输出 y。接着通过 backward() 执行反向传播 ,就可以得到张量的 grad 值了。
x = torch.ones(5, requires_grad=True)
w1 = torch.tensor(2.0, requires_grad=True)
w2 = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(4.0, requires_grad=False)
l1 = x * w1
l2 = l1 + b
l3 = l2 * w2
y = l3.mean()
y.backward()
print(l1.data, l1.grad, l1.grad_fn)
print(l2.data, l2.grad, l2.grad_fn)
print(y)
print(w1.grad, w2.grad)
print(x.grad)
从上面的输出可以看到 x 一开始为 [1,1,1,1,1],乘以 w1 后为 [2., 2., 2., 2., 2.],+b 之后为 [6., 6., 6., 6., 6.],再乘以 3 为 [18., 18., 18., 18., 18.],求均值得到 y 为 18。

Tensor 的 grad_fn 属性用于记录上一步是经过怎样的操作得到自己的,用于反向传播时进行求导 。例如 l1.grad_fn 为 MulBackward0 代表其反向传播操作为乘法。
反向传播后通过 grade 属性可以获得最后输出对本张量的导数值 。 例如上面的操作中输出为 y,反向传播后,w2.grad 获得的就是 dy/dw2。由链式求导法则可得 dy/dw2=dy/dl3 × dl3/dw2。由于 y 由 l3 求均值得到,即 y=1/5(Σl3),所以 dy/dl3=1/5,又 l3=l2 * w2,所以 dl3/dw2=l2,所以 dy/dw2=1/5*l2=1/5(6., 6., 6., 6., 6.)=6。同理通过反向链式求导得到 w1、x 的 grad。
注意到 l1、l2 的 grade 值为 None,这是由于张量 l1、l2、l3 都是中间计算结果,它们被称为非叶张量,相对地由用户创建的 x、w1、w2 被称为叶张量(leaf tensor)。pytorch为了节约内存并不会保存中间张量的导数值,只会用grad_fn来记录是通过什么操作产生的。如果我们希望查看的话,可以通过retain_grad()来保存非叶张量的导数值 。

2、张量对张量求导的求导
默认情况下 pytorch 不允许张量对张量求导,所以在使用张量对张量求导的时候,必须要传入一个与被求导张量同形的张量,然后pytorch根据传入的张量与被求导张量作加权求和将其转化为标量 。 张量的梯度是一个与原张量同形的张量
首先创建一个叶子节点 x
x = torch.tensor([[1.0,2.0],[3.0,4.0]],requires_grad=True)
print(x)
接下来计算 y=3*x
y = 3*x
print(y)
接下来我们直接用 y 求导 y.backward()。毫无意外,直接报错,这就印证了前面说过的pytorch不支持张量对张量直接求导。 我们必须构建一个与 y 同形的张量 z,把 z 作为 y.backward() 的参数求 y 对 x 的导 。
z = torch.tensor([[1.0,0.1],[0.01,0.001]],dtype=torch.float)
y.backward(z)
print(x.grad)
输出结果:tensor([[3.0000, 0.3000], [0.0300, 0.0030]]) (张量的梯度是一个与原张量同形的张量 )
对于表达式 y.backward(z) (y、z为同形张量)的计算过程,实际上将 y 与 z 加权求和得到标量 m,然后用 m 对 x 求导得到结果,也就是说实际上有这样一步计算 m=torch.sum(y*z)
四、神经网络包 nn 和优化器 optm
使用 torch.nn 包来构建神经网络。nn 包依赖 autograd 包来定义模型并求导。 一个 nn.Module 包含各个层和一个 forward(input) 方法,该方法返回 output。
1、nn.Module(模组)
在 Pytorch 中编写神经网络,所有的层结构和损失结构都来自 torch.nn , 所有模型构建都是从基类 nn.Module 继承的。
2、torch.optim(优化)
(1)一阶优化算法:使用各个参数的梯度值来更新参数,最常用的是梯度下降  (2)二阶优化算法:使用二阶导数 ( Hessian方法 ) 来最大化或者最小化代价函数,主要基于牛顿法。下面举一个例子  这样,我们就得到了学习速率为0.01,动量是0.9的随机梯度下降,在优化之前 要将梯度归零,optimizer.zeros(),然后通过 loss.backward() 反向传播,自动求导得到每个参数的梯度,然后通过 optimizer.step() 进行参数的更新。
3、模型的保存和加载
在 Pytorch 中使用 torch.save 来保存模型的结构和参数,有两种方式:
(1)保存整个模型的结构信息和参数信息, 保存的对象是模型 model (2)保存模型的参数, 保存的对象是模型的状态 model.state_dict() save 的第一个参数是保存的对象, 第二个是保存的路径
加载模型对应两种保存方式也有两种
(1) 加载完整的模型结构和参数信息,使用 load_model = torch.load(‘model.pth’) 在网络较大的时候记载时间教程,存储空间较大 (2)加载模型参数信息,需要先导入模型的结构,然后通过 model.load_state_dict(torch.load(‘model_state.pth’)) 来导入。
4、神经网络的典型训练过程
● 定义包含一些可学习的参数(或者叫权重)神经网络模型; ● 在数据集上迭代; ● 通过神经网络处理输入; ● 计算损失(输出结果和正确值的差值大小); ● 将梯度反向传播回网络的参数; ● 更新网络的参数,主要使用如下简单的更新原则:weight = weight - learning_rate * gradient 
(1)定义神经网络模型
import torch
import torch.nn as nn
import torch.nn.functional as F
# 汉字均为我个人理解,英文为原文标注。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 6, 5)
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 = 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 x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
"""
Net(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, 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)
)
"""
params = list(net.parameters())
print(len(params))
print(params[0].size())
"""
10
torch.Size([6, 1, 5, 5])
"""
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
"""
tensor([[ 0.0114, 0.0476, -0.0647, 0.0381, 0.0088, -0.1024, -0.0354, 0.0220,
-0.0471, 0.0586]], grad_fn=<AddmmBackward>)
"""
net.zero_grad()
out.backward(torch.randn(1, 10))
(2)损失函数
选用nn.MSELoss来计算误差
"""
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
"""
output = net(input)
target = torch.randn(10)
target = target.view(1, -1)
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
print(loss.grad_fn)
print(loss.grad_fn.next_functions[0][0])
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])
"""
<MseLossBackward object at 0x7efbcad51a58>
<AddmmBackward object at 0x7efbcad51b38>
<AccumulateGrad object at 0x7efbcad51b38>
"""
net.zero_grad()
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
"""
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0087, -0.0073, 0.0013, 0.0006, -0.0107, -0.0042])
"""
(3)更新权值
在反向传播计算完所有参数的梯度后,还需要使用优化方法来更新网络的权重和参数,权值更新采用的方法是随机梯度下降法(SGD, Stochastic Gradient Descent )。 
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
以上步骤我们实现了网络的数据完整传播步骤。
五、数据的加载与预处理
要想有一个好的模型你必须有一些好的数据,并将他们转化为模型可以理解的语言,这个工作非常重要。 ?? 当我们需要处理图像,文本,音频或者视频数据的时候,你可以使用标准的 python 库来将这些数据就转化为 numpy array,然后再转化为 Tensor。下面列出一些相应的python库:  特别是对于视觉领域,我们写了一个叫做 torchvision 的包,他可以将很多知名数据的数据即涵盖在内。并且,通过 torchvision.datasets 和 torch.utils.data.DataLoader 进行数据的转化。在本里中我们将会使用 CIFAR10 数据集,它有以下各类: ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。
PyTorch 通过 torch.utils.data 对一般常用的数据加载进行了封装,可以很容易地实现多线程数据预读和批量加载。
torchvision 是 PyTorch 中专门用来处理图像的库,torchvision 已经预先实现了常用图像数据集,包括前面使用过的 CIFAR-10,ImageNet、COCO、MNIST、LSUN 等数据集,可通过 torchvision.datasets 方便的调用。
1、Dataset
(1)Torchvision.datasets
Torchvision.datasets 可以理解为 PyTorch 团队自定义的 dataset, 这些 dataset 帮我们提前处理好了很多的图片数据集,我们拿来就可以直接使用:  (2)自定义 Dataset
Dataset 是一个抽象类,为了能够方便的读取,需要将要使用的数据包装为Dataset类。 自定义的 Dataset 需要继承它并且实现两个成员方法:  定义好数据集类之后使用 dataloader 加载数据。
2、torchvision.models
Torchvision 不仅提供了常用图片数据集,还提供了训练好的模型,可以加载之后,直接使用,或者在进行迁移学习 torchvision.models 模块的子模块中包含以下子模型结构。  
3、torchvision.transforms
transforms 模块提供了一般的图像转换操作类,用作数据处理和数据增强。
data_transforms = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
(1)transforms.Compose(transforms) 方法是将多种变换组合在一起。 上述对data_transforms进行了四种变换,前两个是对PILImage进行的,分别对其进行随机大小和随机宽高比的裁剪,之后resize到指定大小224,以及对原始图像进行随机的水平翻转;第三个transforms.ToTensor() 将PILImage转变为torch.FloatTensor的数据形式;最后一个Normalize则是对tensor进行的。
(2)多种组合变换有一定的先后顺序,处理 PILImage 的变换方法(大多数方法)都需要放在 ToTensor 方法之前,而处理tensor的方法(比如Normalize方法)就要放在ToTensor方法之后。
transforms中的函数
torchvision.transforms.Normalize(mean, std)
用给定的均值和标准差分别对每个通道的数据进行正则化。具体来说,给定均值(M1,…,Mn),给定标准差(S1,…,Sn),其中n是通道数(一般是3),对每个通道进行如下操作: output[channel] = (input[channel] - mean[channel]) / std[channel]
比如:原来的tensor是三个维度的,值在[0,1]之间,经过变换之后就到了[-1,1], 计算如下:((0,1)-0.5)/0.5=(-1,1)
torchvision.transforms.ToTensor
把一个取值范围是[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的torch.FloadTensor
torchvision.transforms.ToPILImage
将shape为(C,H,W)的Tensor或shape为(H,W,C)的numpy.ndarray转换成PIL.Image,值不变。
torchvision.transforms.CenterCrop(size)
将给定的PIL.Image进行中心切割,得到给定的size,size可以是tuple,(target_height, target_width)。size也可以是一个Integer,在这种情况下,切出来的图片的形状是正方形。
torchvision.transforms.RandomCrop(size, padding=0)
切割中心点的位置随机选取。size可以是tuple也可以是Integer。
torchvision.transforms.RandomHorizontalFlip
随机水平翻转给定的PIL.Image,概率为0.5。即:一半的概率翻转,一半的概率不翻转。
torchvision.transforms.RandomSizedCrop(size, interpolation=2)
先将给定的PIL.Image随机切,然后再resize成给定的size大小。
torchvision.transforms.Pad(padding, fill=0)
将给定的PIL.Image的所有边用给定的pad value填充。 padding:要填充多少像素 fill:用什么值填充
4、处理数据和训练模型的技巧
数据预处理
(1)中心化 每个特征维度都减去相应的均值来实现中心化,这样可以使数据变成0均值。 (2)标准化 在数据都变成0均值后,采用标准化的做法是数据不同的特征维度都有相同的规模。有两种常用的方法。一种是除以数据标准差,是数据的分布接近标准高斯分布。二是让每个维度特征的最大值和最小值按比例缩放到-1~1之间。 (3)PCA 在用PCA处理数据前,通常将数据中心化,并计算数据的协方差矩阵。该协方差矩阵是对称半正定的,可以通过矩阵来进行奇异值分解(SVD), 然后对数据进行去相关性,再将其投影到一个特征空间,选取较大的、主要的向量来降低数据的维度,这种方法叫做PCA,主成分分析 (4)白噪声 与PCA相似,首先将数据投影到一个特征空间,然后每个维度除以特征值来标准化这些数据。直观上就是将一个多元高斯分布转化到了一个0均值、协方差为1的多元高斯分布。白噪声的处理增强数据中的噪声,因为其增强了数据中的所有维度。 在实际处理中,中心化和标准化都特别重要,但是白噪声和PCA在卷积神经网络中几乎不使用,因为网络可以自动学些如何提取这些特征。
权重初始化
(1)全0初始化 直观但是不应采取的策略就是全0初始化。在神经网络中如果每个权重都被初始化为相同的值,那个每个神经元都会计算出相同的结果,在反向传播时计算出相同的梯度,最后权重都会有相同的更新。 (2)随机初始化 我们希望权重初始化时能够尽量靠近0,但不都是0。一般随机化策略有高斯随机化和均匀随机化,值得注意的是并不是越小的随机化产生的结果越好。 (3)稀疏初始化 将权重全部初始化为0,然后挑选一些参数赋随机值,这种方法在实际中使用较少。 (4)初始化偏置 对于偏置(bias),通常初始化为0。 (5)批标准化(Batch Normalization) 核心思想是标准化这个过程是可微的,批标准化通常应用在全连接层后面,非线性层前面。批标准化还可以理解为在网络中的每一层前面都会做数据的预处理。
防止过拟合
(1)正则化 L2正则化是正则化中比较常用的手段,其思想是对权重过大的部分进行惩罚,也就是直接在损失函数中加入权重的二范数量级。 (2)Dropout 核心思想是在训练网络的时候依概率P保留每个神经元,也就是每次训练的时候有些神经元会被置为0。
六、训练分类器
● 首先装载数据,并将其统一化; ● 定义CNN; ● 定义损失函数; ● 训练神经网络; ● 测试网络;
1、CPU 版本
import torch
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
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')
import matplotlib.pyplot as plt
import numpy as np
def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
dataiter = iter(trainloader)
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 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()
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
dataiter = iter(testloader)
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))
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]))
2、GPU 版本
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
net.to(device)
inputs, labels = inputs.to(device), labels.to(device)
import torch.optim as optim
import torch
import torchvision
import torchvision.transforms as transforms
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')
import matplotlib.pyplot as plt
import numpy as np
def imshow(img):
img = img / 2 + 0.5
npimg = img.cpu().numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
dataiter = iter(trainloader)
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 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()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
net.to(device)
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
dataiter = iter(testloader)
images, labels = dataiter.next()
images, labels = inputs.to(device), labels.to(device)
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
images, labels = inputs.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))
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
images, labels = inputs.to(device), labels.to(device)
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]))
|