一篇博客带你掌握pytorch基础,学以致用
1.将numpy的 ndarray对象转换为pytorch的张量
在 Pytroch 中,Tensor 是一种包含单一数据类型元素的多维矩阵,译作张量。熟悉 numpy 的同学对这个定义应该感到很熟悉,就像ndarray一样,一维Tensor叫Vector,二维Tensor叫Matrix,三维及以上称为Tensor。而 numpy 和 Tensor 确实有很多的相似之处,两者也可以互转。
实例如下:
import torch
import numpy as np
def a2t():
np_data = np.array([[1, 2],[3,4]])
#/********** Begin *********/
#将np_data转为对应的tensor,赋给变量torch_data
torch_data=torch.from_numpy(np_data)
#/********** End *********/
return(torch_data)
- pytorch张量创建
一个张量tensor可以利用输入其行数和列数进行构建,也可以利用 Python 的list构建
想必大家对 Python 的基本数据类型都有一定的了解,如int、float、double等,与之对应的 Pytorch 提供了多种类型的tensor,以便根据数据的特性进行相应的选择。
Pytorch 定义了七种CPU tensor类型和八种GPU tensor类型:
一个张量tensor可以利用输入张量shape ,即行数和列数进行构建。数据在相应的数据类型的最大最小值之间随机生成。torch.Tensor是默认的tensor类型(torch.FloatTensor)的简称。
实例如下:
下面两行代码等价
t1 = torch.Tensor(2,2)
#t1 = torch.FloatTensor(2,2)
一个张量tensor可以从 Python 的list或序列构建。
torch.Tensor(list)
Pytorch 提供了很多方法用于创建特殊的矩阵,这里只介绍几种常用的矩阵:
torch.ones(n,m):创建nm 维的张量,其元素都被填充了标量值1;
torch.zeros(n,m): 创建nm 维的张量,其元素都被填充了标量值0;
torch.eye(n,m):创建n*m 维的张量,其对角线为1,其余元素均为0;
torch.linspace(start, end, steps=100) :创建值start和 end之间等间距点的一维张量;
torch.rand(sizes):返回一个张量,包含了从区间[0,1)的均匀分布中抽取的一组随机数,其形状由整数序列sizes定义;
torch.randn(sizes):返回一个张量,包含了从标准正态分布(均值为0,方差为1,即高斯白噪声)中抽取一组随机数,其形状由整数序列sizes定义。
利用torch.size()可获得张量的大小,示例如下:
a = torch.randn(2,2)
print(a)
print(a.size()
这里,我们在演示一个创建32位size为(2,2)的整型张量
import torch
def create():
#/********** Begin *********/
#创建一个32位有符号的整数张量t
t=torch.IntTensor(2,2)
#/********** End *********/
return t
3.Tensor 切片及索引
这里我想说的是pytorch的索引和切片语法都沿袭了python容器的语法,所以,我们在对tensor进行索引和切片可以类比list的方法
Python 中的切片非常灵活,一行代码就可以实现多行循环完成的动作。而数据中往往包含着大量的冗余信息,利用切片、索引操作对数据进行预处理,可以为后续的操作提供极大的便利。
Tensor 对象的内容,可以通过索引或切片来访问和修改,就像 Python 的内置容器对象一样。
一维 Tensor
一维 Tensor 的索引和 Python 列表类似。
应用实例:
import torch
t = torch.Tensor(range(5))
print(t)
print(t[1])
print(t[0:3])
还可以进行逆序索引
t[ :,-1]
负索引
t[-3:-1]
二维 Tensor
在多维 Tensor 中,如果省略了后面的索引,则返回的对象会是一个维度低一点的Tensor。
切片
一维 Tensor
一维 Tensor的切片和 Python 列表类似。
如前所述,Tensor 对象中的元素遵循基于零的索引。
基本切片通过将start,stop和step参数提供给内置的slice函数来构造一个 Python slice对象。 此slice对象被传递给Tensor 来提取Tensor 的一部分。
s = slice(2,5,2)
print("Slicing Tensor : {}".format(t[s]))
print(t[2:5:2 ])
二维Tensor也可指定 step 进行切片。
tensor = torch.Tensor([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
print(tensor[0:3:2,:])
上式选取了行位置的起始位置为0,终止位置为3,步长为 2,即第一行和第三行,而列位置则没有进行切片,输出结果如下。
Pytorch 提供了许多用于切片和索引的函数,这里只介绍几种最为常见的函数:
torch.chunk(tensor, chunks, dim=0)
用途:将张量沿给定维度拆分成若干块。
参数:
torch.cat(seq, dim=0, out=None) → Tensor
用途:在给定维度上连接张量的给定序列。
参数:
torch.unsqueeze(input, dim, out=None)
用途:返回在指定位置插入尺寸为1的新张量。
参数:
实例
print("chunk : {}".format(torch.chunk(tensor,2,dim=0)))
print("cat : {}".format(torch.cat((tensor,tensor),1)))
那我们来做一个小测试
生成一个张量,并做如下索引
输出正序索引张量 t 起始位置为2,终止位置为5元素;
输出正序索引张量 t 起始位置为-4,终止位置为-2元素;
输出张量 t 起始位置为2,终止位置为6,步长为3的元素。
import torch
t = torch.Tensor(range(6))
print("Inverted indexing :{}".format(t[2:5]))
print("Negative indexing :{}".format(t[-4:-2]))
print("Slicing :{}".format(t[2:6:3]))
4.pytorch数学运算
(1)两个张量相加
此处 将两个同形矩阵相加有三种语法结构:
import torch
x = torch.Tensor(2, 3)
y = torch.rand(2, 3)
语法一: x + y
应用示例:
print( x+y )
语法二: torch.add (x, y)
应用示例:
print( torch.add(x, y))
语法三: y.add _(x)
应用示例:
y.add_(x)
print(y)
注意: 会改变 tensor的函数操作会用一个下划线后缀来标示。
比如, y.add _(x) 会在原地求和,并返回改变后的 tensor,而 y.add (x)将会在一个新的 tensor 中计算结果, y 本身不改变。
这里还有一些常用的pytorch函数
我们再来做一个小练习:
import torch
t = torch.Tensor([[2.4,-5.6,8.6,2.1,4],[3.5,-9.3,6.7,-5,4.23]])
#/********** Begin *********/
#输出一个新张量,元素为输入的元素四舍五入到最接近的整数
print(torch.round(t))
#输出一个新张量,元素为输入的元素的符号,正为1,负为-1
print(torch.sign(t))
#/********** End *********/
- 改变tensor维度
在编程中,我们常常需要对 Tensor 进行 reshape 的操作,比如转置、扩充张量等操作。
view()
应用实例:
y = torch.randn(5, 10, 15)
print(y.size())
print(y.view(-1, 15).size()) # Same as doing y.view(50, 15)
print(y.view(-1, 15).unsqueeze(1).size()) # Adds a dimension at index 1.
args参数中的-1表示根据另一个维度自动变换。还记得我们在第三节中提到的unsqueeze(),它将返回在指定位置插入一个维度的新张量。
输出结果:
torch.Size([5, 10, 15])
torch.Size([50, 15])
torch.Size([50, 1, 15])
转置
由下面两种转置产生的结果张量,与输入张量共享它的底层存储,所以改变其中的内容会改变另一个的内容。
x.t()
基本语法:
torch.t(input, out=None) → Tensor
用途:将输入的2D张量转置其维度0和1。
x = torch.randn(5, 10)
print(x.t().size()
会将其转换为(10,5)
x.transpose(0,1)
基本语法:
torch.transpose(input, dim0, dim1, out=None) → Tensor
用途:返回一个作为输入转置版本的张量。给定的维度dim0和dim1被交换。
参数:
input (Tensor) : 输入张量
dim0 (int) : 第一个转置的维度
dim1 (int) : 第二个转置的维度
应用示例:
print(y.transpose(0, 1).size())
print(y.transpose(1, 2).size())
这里再举一个小测试:
import torch
t = torch.randn(2, 10, 8)
# 将张量`t`的大小转为`40*4`,并在零位置插入尺寸为`1`的新张量,由此扩充原始张量`t`
#输出变化后的
print(t.view(-1, 4).unsqueeze(0).size())
6.pytorch Variable对象
本质上Variable和Tensor没有区别,多数情况下,将Tensor替换为Variable,代码一样会正常的工作。不过Variabel会放入一个计算图(computational graph),这个图将所有的计算步骤 (节点) 都连接起来,进行前向传播,反向传播以及自动求导。在最后进行误差反向传递时,一次性将所有 Variable 里面的梯度都计算出来,而Tensor 就没有这个能力啦。
(1)创建 Variable
import torch
from torch.autograd import Variable
基本形式:
Variable(data, requires_grad=True)
参数说明:
data: 任意的 tensor 类
requires_grad:是否参与误差反向传播, 是否计算计算梯度
利用variable.data可获取 Variable 中的张量。
应用实例
import torch
from torch.autograd import Variable
tensor = torch.FloatTensor([[1,4,2],[3,1,4]])
#/********** Begin *********/
v=Variable(tensor,requires_grad=True)
#/********** End *********/
print(v)
7.Variable 属性
之前我们学习了利用.data访问 Variable 的 Tensor,可以在此基础上利用 numpy() 转换为 numpy 类型。
应用示例:
print(variable.data.numpy())
Variable 的求导原理
如图,假设我们有一个输入变量input,input是用户输入的,所以其创造者creator为null值,input经过第一个数据操作operation1(比如加减乘除运算)得到output1变量,这个过程中会自动生成一个function1的变量,而output1的创造者就是这个function1。随后,output1再经过一个数据操作生成output2,这个过程也会生成另外一个Function实例function2,output2的创造者creator为function2。
在这个向前传播的过程中,function1和function2记录了数据input的所有操作历史,当output2运行其backward函数时,会使得function2和function1自动反向计算input的导数值并存储在grad属性中。
若把整个操作流看成是一张图(Graph),那么像input这种creator为null的被称之为图的叶子(graph leaf),它不是任何函数(Function)的输出,而是由用户创建的节点,只有叶子节点才能被autograd,返回导数。而creator 非null的变量比如output1和output2,是不能被返回导数的,它们的grad均为None。
Variable 的主要属性如下所示:
.data
任意类型的封装好的张量。
.grad
保存与 data类型和位置相匹配的梯度,此属性难以分配并且不能重新分配。
.requires_grad
标记变量是否已经由一个需要调用到此变量的子图创建的 bool值。只能在叶子变量上进行修改。默认为 False,叶子节点指定 True后,依赖节点都被置为 True。
is_leaf
标记变量是否是子图叶子节点(如由用户创建的变量)的 bool值。
.volatile
标记变量是否能在推理模式下应用的 bool值。只能在叶子变量上更改。
应用示例:
print('Requires Gradient : %s ' % (variable.requires_grad))
print('Volatile : %s ' % (variable.volatile))
print('Gradient : %s ' % (variable.grad))
应用实例:
import torch
from torch.autograd import Variable
x = torch.FloatTensor([1,2,3])
x = Variable(x, requires_grad=True)
#/********** Begin *********/
#创建一个变量 y,由 x 的平方计算得到
y=x*x
print("Variable containing:,y.data)
print('Requires Gradient : %s ' % (y.requires_grad))
print('Gradient : %s ' % (y.grad))
8.梯度初探
根据上面所介绍的Variable属性,我们将进一步学习Variable的反向传播函数backward,从而计算出其梯度的大小。
相关知识
参数说明:
variables (sequence of Variable):将要计算导数的变量
grad_variables (sequence of (Tensor, Variable or None))
相应变量的每个元素的梯度
Tensor:将自动转换为变量,当 create_graph为True时计算梯度
Variable:计算变量的梯度
None:被指定为标量梯度或不需要计算梯度的值
retain_graph (bool, optional)
False:用于计算梯度的图形将被释放
create_graph (bool, optional)
True:图形的梯度将被计算,允许计算更高阶的导数
默认为False。
retain_variable
False: 反向传播之后这个计算图的内存会被释放
True : 反向传播之后这个计算图的内存不会被释放,可进行第二次进行第二次反向传播
该计算图使用链式规则进行求导。如果任何变量是非标量的(即它们的数据具有多于一个元素)并且需要梯度,则该函数另外需要指定grad_variables。
# Create tensors.
x = Variable(torch.Tensor([3]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
# Build a computational graph.
y = w * x + 4 # y =w * x + 4
# Compute gradients.
y.backward(retain_graph=True)
# Print out the gradients.
print(x.grad)
print(w.grad)
下面是一个得到求导结果的代码应用实例
import torch
from torch.autograd import Variable
x = Variable(torch.Tensor(range(4)), requires_grad=True)
#/********** Begin *********/
#在 x 基础上进行运算, y = x + 2
y = x + 2
#y.backward(retain_graph=True)
#在 y 基础上进行运算, z = y * y * 3
z = y * y * 3
#令变量 out 为 z 的平均值并输出
#z.backward(retain_graph=True)
out=torch.sum(z)/len(z)
#计算 out 的梯度并输出x的梯度值
out.backward(retain_graph=True)
print(out.data)
print(x.grad)
#/********** End *********/
9.torch.autograd.grad函数
基本语法:
torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=None, only_inputs=True, allow_unused=False)
用途:计算并返回输入的输出梯度的总和。
参数说明:
outputs(可变序列)
差分函数的输出。
inputs(可变序列)
输入将返回梯度的积分(并不积累.grad)。
grad_outputs(Tensor 或Variable的序列)
每个输出的梯度。任何张量将被自动转换为volatile,除非create_graph为True。
retain_graph(bool,可选)
如果为 False,则用于计算 grad的图形将被释放。
请注意,在几乎所有情况下,将此选项设置为 True不是必需的,通常可以以更有效的方式解决。
默认值为create_graph。
create_graph(bool,可选)
如果为True,则构造导数的图形,允许计算高阶衍生产品。
默认为False,除非grad_variables包含至少一个非易失性变量。
only_inputs(bool,可选)
为True,则渐变 wrt离开是图形的一部分,但不显示inputs不会被计算和累积。
默认为True
allow_unused(bool, 可选)
默认为False.
应用实例:
#计算 z = x*x + 3*y
z = x*x + 3*y
#求dz_dx和dz_dy 并输出
dz_dx=torch.autograd.grad(z, x, grad_outputs=torch.ones(2,3))
dz_dy=torch.autograd.grad(z, y, grad_outputs=torch.ones(2,3))
print("dz_dx: \n",dz_dx)
print("dz_dy: \n",dz_dy)
10.torch.nn.Module
神经网络可以使用torch.nn包构建。它提供了几乎所有与神经网络相关的功能,例如:
线性图层 nn.Linear,nn.Bilinear
卷积层 nn.Conv1d,nn.Conv2d,nn.Conv3d,nn.ConvTranspose2d
非线性 nn.Sigmoid,nn.Tanh,nn.ReLU,nn.LeakyReLU
池化层 nn.MaxPool1d,nn.AveragePool2d
Recurrent网络 nn.LSTM,nn.GRU
标准化 nn.BatchNorm2d
Dropout nn.Dropout,nn.Dropout2d
Embedding - nn.Embedding
损失函数 nn.MSELoss,nn.CrossEntropyLoss,nn.NLLLoss
这些类的实例将具有一个内置的__call__函数,可通过图层运行输入。
torch.nn.Module是所有神经网络模块的基类,用户自定义的神经网络模型同样继承自这个类。它定义了训练神经网络需要的所有基础方法,并且是可以序列化的抽象类。
实例代码`:
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)
def forward(self, x):
x = F.relu(self.conv1(x))
return F.relu(self.conv2(x))
做一个小练习:
声明一个in_features=2,out_features=3的线性模型 l并输出;
变量 net 由三个l 序列构成,并输出 net。
import torch
import torch.nn as nn
from torch.autograd import Variable
#/********** Begin *********/
#声明一个in_features=2,out_features=3的线性模型 l并输出
l = nn.Linear(2, 2)
net = nn.Sequential(l, l,l)
#变量 net 由三个l 序列构成,并输出 net
net = nn.Sequential(l, l)
print(l)
print(net)
# for idx, m in enumerate(net.modules()):
# print(idx, '->', m)
11.线性–Linear layers
现在同学们autograd有了初步的了解,而nn建立在autograd的基础上来进行模型的定义和微分。
我们自定义的网络结构是由若干的layer组成的,我们将其设置为 nn.Module的子类,nn.Module中包含着神经网络的层,只要使用方法forward(input)就能够将output返回。
Linear是module的子类,是参数化module的一种,与其名称一样,表示着一种线性变换。
首先,先导入我们本次实训使用的包。
import torch
import torch.nn as nn
from torch.autograd import Variable
torch.nn.Linear
基本形式:
torch.nn.Linear (in_features, out_features, bias=True)
用途:对输入数据应用一个线性转换,格式如下所示:
维度大小:
Input: (N,∗,in_features) *表示任意大小的附加维度
Output: (N,∗,out_features) 最后一个维度要与输入相同
学习到的变量:
weight– 可学习权重(out_features x in_features)
bias – 可学习偏移量(out_features)
应用实例:
module = nn.Linear(4, 2)
print(module)
输出:
Linear(in_features=4, out_features=2)
可以利用module.weight,module.bias查询模块的权重和偏移量。
print("module.weight:
%s"% module.weight)#W
print("module.bias:
%s"% module.bias)#b
有了权重、偏移量后,随机生成输入样本后,我们就可以利用 公式 y=wx+b进行线性转换啦。
#生成输入样本
input = Variable(torch.randn(8, 4))
# output = input * module
output = module(input)
print(output.size())
小练习:
创建in_features=3, out_features=2线性层变量 linear并输出;
输出linear的 type 属性
import torch
import torch.nn as nn
from torch.autograd import Variable
#/********** Begin *********/
# 创建in_features=3, out_features=2线性层变量 linear
module=nn.Linear(3, 2)
#输出linear
print("linear: ",module)
#输出linear的 type 属性
print("type of linear : ",module.type)
print("\nCongratulation!")
#/********** End *********/
13.非线性–Nonlinearities
请同学们掌握 torch.nn 提供的几个重要的非线性模型,如下所示。由此对数据进行相应的非线性映射,便于之后的处理和应用。
ReLU
Threshold
Sigmoid
Tanh
关于这四个函数,是非常经典的激活函数,我在之前的博客介绍过其中的两个,这里不做详细介绍了,以后博主准备专门写一篇介绍。
当然把图像给大家看一下,了解他们的属性:
那么举一个变换实例:
创建 Tanh 模型 m。
输出经m变化后的input值。
具体请参见后续测试样例。
import torch
import torch.nn as nn
from torch.autograd import Variable
input = Variable(torch.Tensor([2.3,-1.4,0.54]))
#/********** Begin *********/
#创建 Tanh 模型 m
m= nn.Tanh()
#输出经 m 变化后的 input 值
print(m(input))
14.卷积–Convolution Layers
卷积的本质就是用卷积核的参数来提取原始数据的特征,通过矩阵点乘的运算,提取出和卷积核特征一致的值。
卷积层是用一个固定大小的矩形块去席卷原始数据,将原始数据分成一个个和卷积核大小相同的小块,然后将这些小块和卷积核相乘输出一个卷积值(注意这里是一个单独的值,不再是矩阵了)。
这里我们拿最常用的 conv1d举例说明卷积过程的计算。
conv1d
基本形式:
torch.nn.Conv1d (in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
用途:对输入样本应用一维卷积核
参数:
应用示例:
m = nn.Conv1d(16, 33, 3, stride=2)
input = Variable(torch.randn(20, 16, 50))
output = m(input)
print(output.size())
输出结果:
torch.Size([20, 33, 24])
conv2d
基本形式:
torch.nn.Conv2d (in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
参数基本和conv1d差不多
应用实例:
# With square kernels and equal stride
m = nn.Conv2d(16, 33, 3, stride=2)
# non-square kernels and unequal stride and with padding
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
input = Variable(torch.randn(20, 16, 50, 100))
output = m(input)
print(output.size())
下面做一个小练习:
import torch
import torch.nn as nn
from torch.autograd import Variable
input = Variable(torch.randn(10, 16, 40))
#/********** Begin *********/
#创建一个in_channels=16, out_channels=24, kernel_size=4, stride=3的Conv1d变量conv
conv= nn.Conv1d(16, 24,kernel_size=4, stride=3)
#对input应用卷积操作并赋值给变量 output
output=conv(input)
#输出 output 的大小,要求输出不换行
print(output.size())
15.池化–Pooling Layers
Pytorch 中池化分为两种:
最大池化MaxPool
平均池化 AvgPool
下面以MaxPool1d做详细介绍,2维和3维只是在1维的基础上计算了长宽、长宽高的池化。
函数定义:
torch.nn.MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
应用实例:
# pool of size=3, stride=2
m = nn.MaxPool1d(3, stride=2)
input = Variable(torch.Tensor([[[1,2,3,4,5,6,7]]]))
output = m(input)
print(output.size())
还有一种平均池化
下面以AvgPool1d做详细介绍,2维和3维只是在1维的基础上计算了长宽、长宽高的池化。
函数定义:
torch.nn.AvgPool1d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True)
实例:
# pool with window of size=3, stride=2
m = nn.AvgPool1d(3, stride=2)
output = m(Variable(torch.Tensor([[[1,2,3,4,5,6,7]]])))
print(output)
作业小练习:
import torch
import torch.nn as nn
from torch.autograd import Variable
x = Variable(torch.randn(10, 3, 28, 28))
#/********** Begin *********/
#创建一个in_channels=3, out_channels=32, kernel_size=(3, 3), stride=1, padding=1, bias=True的Conv2d变量conv
conv=nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3, 3), stride=1, padding=1, bias=True)
#创建一个kernel_size=(2, 2), stride=2的MaxPool2d变量pool
m = nn.MaxPool2d(kernel_size=(2, 2), stride=2)
#对x应用卷积和最大池化操作并赋值给变量outpout_pool
o1=conv(x)
outpout_pool=m(o1)
#输出 outpout_pool 的大小,要求输出打印不换行
print("Pool output size : " ,outpout_pool.size())
#/********** End *********/
print("\nCongratulation!")
16.正则化
正则化项即罚函数,该项对模型向量进行“惩罚”,从而避免单纯最小二乘问题的过拟合问题。在本节中,我们将介绍正则化方面的知识,这有助于预防过拟合的问题的产生。
首先,先引入我们要使用的包
import torch
import torch.nn
from torch.autograd import Variable
函数定义:
torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True)
应用实例:
input = Variable(torch.Tensor([[1,2,3],[2,3,4]]))
# With Learnable Parameters
m = nn.BatchNorm1d(3)
output = m(input)
print(output)
# Without Learnable Parameters
m1 = nn.BatchNorm1d(3, affine=False)
output1 = m1(input)
print(output1)
函数定义:
torch.nn.InstanceNorm1d(num_features, eps=1e-05, momentum=0.1, affine=False)
应用实例:
input = Variable(torch.randn(1, 5, 2))
# Without Learnable Parameters
m = nn.InstanceNorm1d(5)
output = m(input)
print(output)
# With Learnable Parameters
m1 = nn.InstanceNorm1d(5, affine=True)
output1 = m1(input)
print(output1)
小练习
import torch
import torch.nn as nn
from torch.autograd import Variable
input = Variable(torch.Tensor([[1,2,3,4],[5,6,7,8]]))
#/********** Begin *********/
# 创建一个4维的 带有学习参数的正则化量 m
m = nn.BatchNorm1d(4)
#输出weight和bias
print(m.weight)
print(m.bias)
#在 input 上应用该正则化并输出
out=m(input)
print(out)
#/********** End *********/
17.损失函数
损失函数用来估量模型的预测值f(x)与真实值Y的不一致程度,它是一个非负实值函数,通常使用L(Y, f(x))来表示,损失函数越小,模型的鲁棒性就越好。
本次主要介绍三种常用的损失函数:
L1Loss
MSELoss
CrossEntropyLos
L1Loss
函数定义
torch.nn.L1Loss (size_average=True, reduce=True)
衡量输入 x和目标 y之间样本方差的平均绝对值:
参数说明
size_average(bool,可选) - 默认情况下,对每个 mini -batch的损失取平均值。
设置为False时,则每个 mini -batch的损失将被相加;
当reduce为false时忽略;
默认值:True。
reduce(bool,可选) - 默认情况下,损失是每个 mini -batch的平均或总和。
当reduce为False时,损失函数会返回每个 batch元素的损失,而忽略size_average;
默认值:True
应用实例:
loss = nn.L1Loss()
input1 = Variable(torch.randn(3, 5), requires_grad=True)
target = Variable(torch.randn(3, 5))
output = loss(input1, target)
print(output)
MSELoss
平方损失函数
函数定义
torch.nn.MSELoss (size_average=True, reduce=True)
衡量输入 x和目标 y中 n个元素之间均方误差
参数说明
size_average(bool,可选) - 默认情况下,对每个 mini -batch的损失取平均值。
若设置为False,则每个mini-batch的损失将被相加;
只有在reduce为True时才应用;
默认值: True。
reduce(bool,可选) - 默认情况下,损失取决于每个 mini -batch的观测值的平均值,或总和,取决于size_average。
如果reduce为False,则返回每个 batch元素的损失,而忽略size_average;
默认值:True。
应用实例:
loss = nn.MSELoss()
input1 = Variable(torch.randn(3, 5), requires_grad=True)
target = Variable(torch.randn(3, 5))
output = loss(input1, target)
print(output)
CrossEntropyLoss
Cross Entropy (交叉熵)来自香农的信息论,简单来说,交叉熵是用来衡量在给定的真实分布pk下,使用非真实分布qk所指定的策略f(x)消除系统的不确定性所需要付出的努力的大小。
交叉熵越小,就证明算法所产生的策略越接近最优策略,也就间接证明我们的算法所计算出的非真实分布越接近真实分布。
函数定义
torch.nn.CrossEntropyLoss (weight=None, size_average=True, ignore_index=-100, reduce=True)
参数说明
weight(tensor,可选) - 给每个类手动缩放权重。如果给出,则必须是维度为C的张量。C是类别的总数
size_average(bool`,可选)
ignore_index(int,optional) - 指定被忽略的目标值,不会影响输入梯度。当size_average为真时,损失是对未被忽略的目标进行平均。
reduce(bool,可选)
应用实例:
# 预测值f(x) 构造样本,神经网络输出层
inputs_tensor = torch.FloatTensor( [
[10, 2, 1,-2,-3],
[-1,-6,-0,-3,-5],
[-5, 4, 8, 2, 1]
])
# 真值y
targets_tensor = torch.LongTensor([1,3,2])
# targets_tensor = torch.LongTensor([1])
input1 = Variable(inputs_tensor, requires_grad=True)
target = Variable(targets_tensor)
loss = nn.CrossEntropyLoss()
output = loss(input1, target)
print(output)
小任务:
创建名为 loss 的 L1Loss损失函数;
对 input 和 target应用 loss 赋值给 output;
输出 output的.data属性。
具体请参见后续测试样例。
代码:
import torch
import torch.nn as nn
from torch.autograd import Variable
input = Variable(torch.Tensor([1.1,2.2,2.93,3.8]))
target = Variable(torch.Tensor([1,2,3,4]))
#/********** Begin *********/
#创建名为 loss 的 L1Loss损失函数
loss = nn.L1Loss()
#对 input 和 target应用 loss 并输出
output = loss(input1, target)
print(output)
#/********** End *********/
18 距离函数
距离函数常用来进行相似性度量。采用什么样的方法计算距离是很讲究,甚至关系到分类的正确与否。
CosineSimilarity
torch.nn.CosineSimilarity(dim=1, eps=1e-08)
返回沿着dim计算的x1和x2之间的余弦相似度。
实例:
input1 = Variable(torch.randn(5, 12))
input2 = Variable(torch.randn(5, 12))
cos = nn.CosineSimilarity(dim=1, eps=1e-6)
output = cos(input1, input2)
print(output)
PairwiseDistance
torch.nn.PairwiseDistance(p=2, eps=1e-06)
计算向量v1,v2之间的范数距离
实例:
pdist = nn.PairwiseDistance(p=2)
output = pdist(input1, input2)
print(output)
小练习:
import torch
import torch.nn as nn
from torch.autograd import Variable
input = Variable(torch.Tensor([[1.1,2.2],[2.93,3.8]]))
target = Variable(torch.Tensor([[1,2],[3,4]]))
#/********** Begin *********/
#创建一范数的变量pdist
pdist = nn.PairwiseDistance(p=1)
#对 input 、 target应用该范数并输出
out=pdist(input,target)
print(out)
#/*******pdist*** End *********/
19.使用optimizer
torch.optim是一个实现了各种优化算法的库。大部分常用的方法得到支持,并且接口具备足够的通用性,使得未来能够集成更加复杂的方法。
要使用torch.optim,必须构造一个optimizer对象。这个对象能保存当前的参数状态并且基于计算梯度更新参数。
构建一个优化器
torch.optim.Optimizer (params, defaults)
参数:
params (iterable) – 定义需要优化的变量有哪些,变量或字典的迭代。
defaults – (dict) - 一个包含优化选项默认值的字典(当参数组没有指定它们时使用)。
优化器(optimizers)类的基类。这个类定义了在训练模型的时候添加一个操作的API。
要构造一个Optimizer,你必须给它一个包含参数(必须都是 Variable对象)进行优化。然后,您可以指定optimizer的参数选项,比如学习率lr,权重衰减weight_decay等。
应用实例:
optimizer = optim.SGD(model.parameters(), lr = 0.01, weight_decay=0)
#Adam优化
optimizer = optim.Adam([var1, var2], lr = 0.0001)
所有的optimizer都实现了step()方法,这个方法会更新所有的参数。它能按两种方式来使用:
optimizer.step()
这是大多数optimizer所支持的简化版本。一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数。
优化器的基本使用方法如下:
建立优化器实例
循环:
清空梯度
向前传播
计算 Loss
反向传播
更新参数
应用示例
for input, target in dataset:
# 每次迭代清空上一次的梯度
optimizer.zero_grad()
#运用模型
output = model(input)
# 计算损失
loss = loss_fn(output, target)
# 反向传播
loss.backward()
# 更新梯度
optimizer.step()
查看优化器参数
使用optimizer.param_groups即可查看优化器的各项参数,该函数返回一个列表,其中集合了优化器的各项参数,各项参数的意义将在下一章节详细介绍,输出格式如下所示:
[{‘params’,’lr’, ‘momentum’, ‘dampening’, ‘weight_decay’, ‘nesterov’}]
应用示例:
print(optimizer.param_groups)
做一个小练习:
import torch.nn as nn
import torch
from torch.autograd import Variable
import torch.optim
# Linear Regression Model
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(2, 2) # input and output is 2 dimension
def forward(self, x):
out = self.linear(x)
return out
model = LinearRegression()
#/********** Begin *********/
#声明一个 SGD优化器 optimizer,传入参数
optimizer = torch.optim.SGD(model.parameters(), lr = 0.1, weight_decay=0)
#利用optimizer.param_groups查看优化器的各项参数并输出lr的值。
print("lr:",optimizer.param_groups[0]['lr'])
#/********** End *********/
19.优化
这里讨论的优化问题指的是:给定目标函数f(x),我们需要找到一组参数x,使得f(x)的值最小。
Stochastic Gradient Descent (SGD) 是最基础的优化方法,普通的训练方法, 需要重复不断的把整套数据放入神经网络NN中训练,这样消耗的计算资源会很大。当我们使用SGD会把数据拆分后,再分批不断放入 NN 中计算。 每次使用批数据,虽然不能反映整体数据的情况,不过却很大程度上加速了 NN 的训练过程,而且也不会丢失太多准确率。
SGD是梯度下降的batch版本。对于训练数据集,我们首先将其分成n个batch,每个batch包含m个样本。我们每次更新都利用一个batch的数据,而非整个训练集。即:
函数定义:
torch.optim.SGD (params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)
参数说明:
应用实例:
# SGD 就是随机梯度下降
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
optimizer.zero_grad()
loss_fn(model(input), target).backward()
optimizer.step()
momentum
SGD方法的一个缺点是,其更新方向完全依赖于当前的batch,因而其更新十分不稳定。解决这一问题的一个简单的做法便是引入momentum。
momentum即动量,它模拟的是物体运动时的惯性,即更新的时候在一定程度上保留之前更新的方向,同时利用当前batch的梯度微调最终的更新方向。这样一来,可以在一定程度上增加稳定性,从而学习地更快,并且还有一定摆脱局部最优的能力。就像把一个人从平地上放到了一个斜坡上, 只要他往下坡的方向走一点点, 由于向下的惯性,他不自觉地就一直往下走, 走的弯路也变少了。这就是 Momentum
为每个参数单独设置选项
Optimizer也支持为每个参数单独设置选项。若想这么做,不要直接传入Variable的iterable,而是传入dict的iterable。每一个dict都分别定 义了一组参数,并且包含一个param键,这个键对应参数的列表。其他的键应该optimizer所接受的其他参数的关键字相匹配,并且会被用于对这组参数的优化。
例如,当我们想指定每一层的学习率时,这是非常有用的:
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
这意味着model.base的参数将会使用1e-2的学习率, model.classifier的参数将会使用1e-3的学习率,并且0.9的momentum将会被用于所 有的参数
小练习:
import torch.nn as nn
import torch.optim
import torch
from torch.autograd import Variable
# Linear Regression Model
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(2, 2) # input and output is 1 dimension
self.linear2 = nn.Linear(2, 2)
def forward(self, x):
out = self.linear(x)
out = self.linear2(out)
return out
model = LinearRegression()
#/********** Begin *********/
#声明一个 SGD优化器 optimizer, 按要求设置 lr 的值
optimizer=torch.optim.SGD([
{'params': model.linear.parameters(), 'lr':1e-5},
{'params': model.linear2.parameters(), 'lr': 0.01}
])
#按照格式输出optimizer.param_groups的长度
#print(optim1.param_groups)
print("The len of param_groups list:",len(optimizer.param_groups))
print("linear's lr:",optimizer.param_groups[0]['lr'])
print("linear2's lr:",optimizer.param_groups[1]['lr'])
#按照格式输出linear层的lr
# print(optim1.linear[0]['lr'])
# #按照格式输出linear2层的lr
# print(optim1.linear2[0]['lr'])
#/********** End *********/
20.RMSprop
上面提到的方法对于所有参数都使用了同一个更新速率。但是同一个更新速率不一定适合所有参数。比如有的参数可能已经到了仅需要微调的阶段,但又有些参数由于对应样本少等原因,还需要较大幅度的调动。
Adagrad就是针对这一问题提出的,自适应地为各个参数分配不同学习率的算法。其公式如下:
其中g
t
同样是当前的梯度,连加和开根号都是元素级别的运算。eta 是初始学习率,由于之后会自动调整学习率,所以初始值就不像之前的算法那样重要了。而ϵ是一个比较小的数,用来保证分母非0。
RMSprop
有了 momentum 的惯性原则 ,加上adagrad的对错误方向的阻力, 我们就能合并成这样。让RMSProp同时具备他们两种方法的优势。
函数定义:
class torch.optim.RMSprop (params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
作用:实现RMSprop算法。
参数说明:
closure (callable,optional) – 一个重新评价模型并返回loss的闭包,对于大多数参数来说是可选的。
step(closure=None)函数:执行单一的优化步骤(参数更新)。
与 SGD不同的是,其 lr为可选值。
应用实例:
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=0.001, alpha=0.9)
优点
相比于SGD和Momentum ,这种方法很好的解决了深度学习中过早结束的问题 。
适合处理非平稳目标,对于RNN效果很好。
缺点
算法并不能完全解决局部最小值问题,只是使得参数收敛的速度更快。针对是否能收敛到全局最优解,还与模型的初始化有关。
小练习
声明一个 RMSprop优化器optimizer,设置 lr为0.1,alpha为 0.9;
补充优化器基本使用方法的相关步骤:清空梯度,计算Loss,反向传播,更新参数。其中,计算Loss,反向传播两步骤的相关代码已给出;
按照格式“optimizer’s lr:… ”输出optimizer的lr;
按照格式“optimizer’s alpha:… ”输出optimizer的alpha。
具体请参见后续测试样例。
import torch
from torch import nn, optim
from torch.autograd import Variable
import numpy as np
# Linear Regression Model
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(1,1) # input and output is 1 dimension
def forward(self, x):
out = self.linear(x)
return out
x_train = Variable(torch.randn(1,1))
y_train = Variable(torch.randn(1,1))
criterion = nn.MSELoss()
model = LinearRegression()
#/********** Begin *********/
#声明一个 RMSprop 优化器 optimizer, 按要求设置 lr,alpha 的值
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=0.01, alpha=0.9)
#清空梯度
opt_RMSprop.zero_grad()
#计算Loss
loss = criterion(model(x_train), y_train)
#反向传播
loss.backward()
#更新参数
optmizer.step()
#按照格式输出optimizer的lr
print("linear's lr:",optimizer.param_groups[0]['lr'])
##按照格式输出optimizer的alpha
print("linear's lr:",optimizer.param_groups[0]['alpha'])
#/********** End *********/
21.adam优化算法
Adam
虽然RMSProp融合了momentum和adagrad思想。不过细心的同学们肯定看出来了,似乎在 RMSProp 中少了些什么。原来是我们还没把 momentum合并完全,RMSProp还缺少了momentum中的 这一部分。所以, 我们在 Adam 方法中补上了这种想法。
函数定义:
class torch.optim.Adam (params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
作用:实现Adam算法。
应用实例:
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=0.0001, betas=(0.9, 0.99))
计算m 时有 momentum 下坡的属性, 计算v时有 adagrad 阻力的属性, 然后再更新参数时 把 m 和 v 都考虑进去。实验证明,大多数时候,使用 Adam 都能又快又好的达到目标,迅速收敛。
优点
结合了 Adagrad善于处理稀疏梯度和 RMSprop善于处理非平稳目标的优点;
对内存需求较小;
为不同的参数计算不同的自适应学习率;
也适用于大多非凸优化-适用于大数据集和高维空间。
小练习:
声明一个Adam优化器 optimizer1, 传入model_Adam1的属性值,设置 lr为0.2,betas为(0.9,0.9);
声明一个Adam优化器 optimizer2, 传入model_Adam2的属性值,设置 lr为0.001, betas为(0.9,0.9);
程序对 迭代十次的loss值求和。要求同学们按要求输出相应语句。
若 loss1小于loss2,输出“opt_Adam1 is better than opt_Adam2”;
否则输出“opt_Adam2 is better than opt_Adam1”。
具体请参见后续测试样例。
import torch
from torch import nn, optim
from torch.autograd import Variable
import numpy as np
# Linear Regression Model
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression, self).__init__()
self.linear = nn.Linear(2,2) # input and output is 2 dimension
def forward(self, x):
out = self.linear(x)
return out
x_train = Variable(torch.from_numpy(np.array([[1,2],[3,4]], dtype=np.float32)))
y_train = Variable(torch.from_numpy(np.array([[1,5],[2,8]], dtype=np.float32)))
model_Adam1 = LinearRegression()
model_Adam2 = LinearRegression()
models = [model_Adam1,model_Adam2]
#/********** Begin *********/
#声明一个Adam优化器 optimizer1, 设置 lr为0.2,betas为(0.9,0.9)
optimizer1 = torch.optim.Adam(net_Adam.parameters(), lr=0.2, betas=(0.9, 0.9))
#声明一个Adam优化器 optimizer2, 设置 lr为0.001,betas为(0.9,0.9)
optimizer1 = torch.optim.Adam(net_Adam.parameters(), lr=0.001, betas=(0.9, 0.9))
optimizers = [opt_Adam1,opt_Adam2]
losses_his = [[],[]]
loss_func = nn.MSELoss()
for epoch in range(10):
# 对每个优化器, 优化属于他的神经网络
for model,opt, l_his in zip(models,optimizers, losses_his):
output = model(x_train)
loss = loss_func(output, y_train)
opt.zero_grad()
loss.backward()
opt.step()
l_his.append(loss.data[0])
loss1 = sum(losses_his[0])
loss2 = sum(losses_his[1])
#利用 if-else 结构判断 loss1、loss2的大小
if loss1<loss2:
print("opt_Adam1 is better than opt_Adam2")
else:print"opt_Adam2 is better than opt_Adam1"}
#若loss1小于loss2,输出“opt_Adam1 is better than opt_Adam2”;
#否则输出“opt_Adam2 is better than opt_Adam1”。
#/********** End *********/
22.优化器的比较
优化器的比较
SGD(Stochastic gradient descent)
随机梯度下降算法的参数更新针对每一个样本集 x (i) 和 y (i) 。批量梯度下降算法在大数据量时会产生大量的冗余计算,比如:每次针对相似样本都会重新计算。这种情况时, SGD 算法每次则只更新一次。因此 SGD 算法通过更快,并且适合online。
但是 SGD 以高方差进行快速更新,这会导致目标函数出现严重抖动的情况。一方面,正是因为计算的抖动可以让梯度计算跳出局部最优,最终到达一个更好的最优点;另一方面, SGD 算法也会因此产生过调。
** Momentum **
动量可以加速 SGD 算法的收敛速度,并且降低 SGD 算法收敛时的震荡。
通过添加一个衰减因子到历史更新向量,并加上当前的更新向量。当梯度保持相同方向时,动量因子加速参数更新;而梯度方向改变时,动量因子能降低梯度的更新速度。
RMSprop
RMSPprop 算法是一种自适应参数更新算法,用于解决 adagrad 算法学习率消失的问题。为了进一步优化损失函数在更新中存在摆动幅度过大的问题,并且进一步加快函数的收敛速度, RMSProp 算法对权重 W 和偏置 b 的梯度使用了微分平方加权平均数,使得每个参数的学习率不同。
Adam
Adam 算法是另一种自适应参数更新算法。它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。 Adam 的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
如上所述, SGD 算法能够找到极小值,但是比其他优化器花费的时间更多。和其他算法相比, SGD 算法更加依赖于初始化参数的设置和退火策略,而且 SGD 算法更加容易陷入鞍点。
RMSprop 算法解决了学习率趋近于零的问题。 Adam 则是在 RMSprop 的基础上加入了偏差校正和动量。所以, Adam 可能是最佳的选择。
综上来看,当训练数据特征较为稀疏的时候,采用自适应的优化器通常能获得更好的性能。
为了直观的感受不同优化器的效果,接下来通过画图的方式来比较。首先,构造数据集,具体代码如下所示:
#定义参数
LR = 0.01
BATCH_SIZE = 32
EPOCH = 10
# 生成伪数据
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
y = x.pow(2) + 0.1*torch.randn(x.size())
# 绘制数据图像
plt.scatter(x.numpy(), y.numpy())
plt.show()
其中torch.linspace是为了生成连续间断的数据,第一个参数表示起点,第二个参数表示终点,第三个参数表示将这个区间分成平均几份,即生成几个数据。然后用torch.unsqueeze给伪数据添加一个维度, dim 表示添加在第几维。torch.randn返回一个张量,包含了从标准正态分布(均值为0,方差为1)中抽取的一组随机数。
接下来构造模型Net,设置两个层:隐藏层和输出层,并利用relu作为激活函数,在对输入进行线性处理得到最后的输出。并为每个优化器创建一个Net,将这些模型存在列表 nets中。之后,定义不同的优化器,将这些优化器存在列表 optimizers中,这里对模型和优化器的定义要求同学们在编程中实现。最后定义损失函数。
class Net(torch.nn.Module):
def __init__(self): pass
def forward(self, x): pass
# 为每个优化器创建一个 Net
net_SGD = Net()
...
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
# 定义不同的优化器
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
...
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
#定义损失函数
loss_func = torch.nn.MSELoss()
# 记录 training 时不同神经网络的 loss
losses_his = [[], [], [], []]
这次我们做一次啊大作业:
import torch
import torch.utils.data as Data
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
import os,sys
path = os.path.split(os.path.abspath(os.path.realpath(sys.argv[0])))[0] + os.path.sep
print("validation path:" ,path)
#定义参数
LR = 0.01
BATCH_SIZE = 32
EPOCH = 10
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
y = x.pow(2) + 0.1*torch.randn(x.size())
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
# 默认的 network 形式
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 40)
self.predict = torch.nn.Linear(40, 1)
def forward(self, x):
#隐藏层的激活函数
x = F.relu(self.hidden(x))
#线性输出
x = self.predict(x)
return x
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
#/********** Begin *********/
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
# 声明优化器opt_Momentum,传入对应的模型参数,lr 赋值为 LR,momentum为0.7
opt_Momentum=torch.optim.SGD(net_Momentum.parameters(), lr=LR,momentum=0.7)
# 声明优化器opt_RMSprop,传入对应的模型参数,lr 赋值为 LR,alpha为0.9
opt_RMSprop=torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR,alpha=0.7)
# 声明优化器opt_Adam,传入对应的模型参数,lr 赋值为 LR,betas为(0.9, 0.99)
opt_Adam=torch.optim.Adam(net_Adam.parameters(), lr=LR,betas=(0.9, 0.99))
#/********** End *********/
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch.nn.MSELoss()
losses_his = [[], [], [], []]
#训练循环
for epoch in range(EPOCH):
for step, (batch_x, batch_y) in enumerate(loader):
b_x = Variable(batch_x)
b_y = Variable(batch_y)
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x)
loss = loss_func(output, b_y)
#/********** Begin *********/
#为下一次训练清空梯度
opt.zero_grad()
#反向传播 计算梯度
loss.backward()
#更新参数
opt.step()
#更新梯度
l_his.append(loss.data[0])
#/********** End *********/
#画图
labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']
for i, l_his in enumerate(losses_his):
plt.plot(l_his, label=labels[i])
plt.legend(loc='best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.ylim((0, 0.2))
plt.savefig(path + "outputimages/mylossTest.png")
23.加载数据集
回归分析(Regression Analysis)是确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。线性回归(Linear regression)是利用称为线性回归方程的最小二乘函数,对一个或多个自变量和因变量之间关系,进行建模的一种回归分析。这种函数是一个或多个称为回归系数的模型参数的线性组合。
在回归分析中,只包括一个自变量和一个因变量,且二者的关系可用一条直线近似表示,这种回归分析称为一元线性回归分析。大于一个自变量情况的叫做多元回归。在线性回归中,数据使用线性预测函数来建模,并且未知的模型参数也是通过数据来估计。这些模型被叫做线性模型。
对于常用数据集,可以使用torchvision.datasets直接进行读取。torchvision.dataset是torch.utils.data.Dataset的实现。该包提供了以下数据集的读取:
MNIST
COCO (Captioning and Detection)
LSUN Classification
ImageFolder
Imagenet-12
CIFAR10 and CIFAR100
STL10
应用示例:
import torch
import torchvision
import torchvision.transforms as transforms
cifarSet = torchvision.datasets.CIFAR10(root = "../data/cifar/", train= True,transform=transforms.ToTensor(), download = True)
参数说明:
datasets.CIFAR10()下载的数据集就是数据集CIFAR10,同理若要下载数据集MNIST,使用datasets. MNIST()即可 ;
参数root用来标明,将数据集下载到哪个位置;
transform将数据集转换为Tensor;
train 表明是否训练。
DataLoader
Pytorch 中提供了一种整理数据结构的变量,叫做DataLoader,我们能用它来包装自己的数据,进行训练。
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)
函数定义:
做一个小练习:
import torch
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import os
import sys
path = os.path.split(os.path.abspath(os.path.realpath(sys.argv[0])))[0] + os.path.sep
path = path[:-10] + '/data/'
#/********** Begin *********/
# 下载训练集 MNIST 训练集,设置 root = path,train=False ,download=False,赋值给变量train_dataset
train_dataset = datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)
# 创建dataset=train_dataset,batch_size=100, shuffle=True的DataLoader变量data_loader
dataset=torch.utils.data.DataLoader(train_datase, batch_size=100, shuffle=False, sampler=None, num_workers=0, collate_fn=<function default_collate>, pin_memory=False, drop_last=False)
# 获取数据集中的第四个元素的图片信息和类别
print(dataset[3])
#按照格式“Image Size: ...”输出该图片大小
img, target = test_dataset[3]
print("Image Size:, img)
#按照格式“Image Target: ...”输出该图片的类别
print("Image Target: ",target)
#/********** End *********/
24.建立模型,定义损失和优化函数
在进行网络构建时,主要通过torch.nn包中的已经实现好的卷积层、池化层等进行搭建。例如下面的代码展示了一个具有一个隐含层的MLP网络。nn.Linear负责构建全连接层,需要提供输入和输出的通道数,也就是y = wx+b中x和y的维度。
# 定义简单的前馈神经网络
class Neuralnetwork(nn.Module):
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
#调用父类的初始化函数
super(Neuralnetwork, self).__init__()
# 定义全连接层:线性连接(y = Wx + b),in_dim个节点连接到n_hidden_1个节点上
self.layer1 = nn.Linear(in_dim, n_hidden_1)
# 定义全连接层:线性连接(y = Wx + b),n_hidden_1个节点连接到n_hidden_2个节点上
self.layer2 = nn.Linear(n_hidden_1, n_hidden_2)
# 定义全连接层:线性连接(y = Wx + b),n_hidden_2个节点连接到out_dim个节点上
self.layer3 = nn.Linear(n_hidden_2, out_dim)
def forward(self, x):
# 输入x->layer1,更新到x
x = self.layer1(x)
# 输入x->layer2,更新到x
x = self.layer2(x)
# 输入x->layer3,更新到x
x = self.layer3(x)
return x
model = Neuralnetwork(28 * 28, 300, 100, 10)
由于 Pytorch 可以实现自动求导,所以我们只需实现forward过程即可。这里在 forward 函数中定义了对输入的依次操作,在三个网络层中进行线性映射。
损失函数
这里我们选择利用交叉熵函数作为损失函数。
criterion = nn.CrossEntropyLoss()
优化方法
在训练时,我们确定优化方法为SGD方法。下面代码中的optim.SGD初始化需要接受网络中待优化的Parameter列表(或是迭代器),以及学习率lr。
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
下面我来定义一个卷积神经网络
import torch
import torch.nn as nn
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.autograd import Variable
# CNN Model (2 conv layer)
class CNN(nn.Module):
def __init__(self):
#/********** Begin *********/
#模型初始化__init__
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=5, padding=2),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.MaxPool2d(2))
self.layer2 = nn.Sequential( # (16,14,14)
nn.Conv2d(16, 32, 5, 1, 2), # (32,14,14)
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2) # (32,7,7)
)
self.fc = nn.Linear(32*7*7, 10)
# 定义全连接层:线性连接(y = Wx + b),7*7*32个节点连接到10个节点上
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # 将(batch,32,7,7)展平为(batch,32*7*7)
out = self.fc(x)
return out
#/********** End *********/
cnn = CNN()
params = list(cnn.parameters())
print(len(params))
for name, parameters in cnn.named_parameters():
print(name, ":", parameters.size())
25训练神经网络
接下来,我们只需要遍历数据集,同时在每次迭代中,清空待优化参数的梯度,前向计算,反向传播以及优化器的迭代求解即可。
下面为一个用例
for epoch in range(num_epoches):
print('epoch {}'.format(epoch + 1))
print('*' * 10)
running_loss = 0.0
running_acc = 0.0
for i, data in enumerate(train_loader, 1):
img, label = data
img = img.view(img.size(0), -1)
img = Variable(img)
label = Variable(label)
# 向前传播
out = model(img)
loss = criterion(out, label)
running_loss += loss.data[0] * label.size(0)
_, pred = torch.max(out, 1)
num_correct = (pred == label).sum()
running_acc += num_correct.data[0]
# 向后传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i % 300 == 0:
print('[{}/{}] Loss: {:.6f}, Acc: {:.6f}'.format(
epoch + 1, num_epoches, running_loss / (batch_size * i),
running_acc / (batch_size * i)))
print('Finish {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
epoch + 1, running_loss / (len(train_dataset)), running_acc / (len(train_dataset))))
这个接着24,补充了损失函数,和优化方法,并进行训练
import torch
import torch.nn as nn
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.autograd import Variable
import os
import sys
path = os.path.split(os.path.abspath(os.path.realpath(sys.argv[0])))[0] + os.path.sep
# Hyper Parameters
batch_size = 100
learning_rate = 0.001
num_epochs = 1
# MNIST Dataset
train_dataset = dsets.MNIST(root='./data/',
train=True,
transform=transforms.ToTensor(),
download=False)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
# CNN Model (2 conv layer)
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=5, padding=2),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.MaxPool2d(2))
self.layer2 = nn.Sequential(
nn.Conv2d(16, 32, kernel_size=5, padding=2),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2))
self.fc = nn.Linear(7*7*32, 10)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
cnnmodel = CNN()
#创建输出文件 output.txt
f = open(path + 'output.txt', 'w')
f.seek(0)
f.truncate() #清空文件
#/********** Begin *********/
# 声明一个为交叉熵损失函数的变量criterion
# 声明一个为Adam优化函数的变量optimizer,传入 cnn的参数,并使学习率lr为0.001
optimizer = torch.optim.Adam(cnnmodel.parameters(), lr=0.001)
lcriterion = nn.CrossEntropyLoss()
# 训练模型
for i, (images, labels) in enumerate(train_loader):
# 将images,labels数据转换为Variable类型
# optimizer梯度归零
# 对 images 应用 cnnmodel 模型并赋值给变量 outputs
#Backward
#Optimize
b_x = Variable(images)
b_y = Variable(labels)
output = cnnmodel(b_x)
loss = lcriterion(output, b_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
#共训练60次,分别100次输出一回loss信息,并将输出信息存到文件中
if (i+1) % 10 == 0:
f.writelines('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f \n'
%(1, num_epochs, i+1, len(train_dataset)//1000, loss.data[0]))
print ('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f'
%(1, num_epochs, i+1, len(train_dataset)//1000, loss.data[0]))
if i > 60:
break
f.close()
#/********** End *********/
26.保存和测试模型
保存模型可以通过下面方法实现:
torch.save(model.state_dict(), './neural_network.pth')
最后是我们的测试代码:
import torch
import torch.nn as nn
import torchvision.datasets as dsets
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
from torch.autograd import Variable
import warnings
from PIL import Image
warnings.filterwarnings('ignore')
import os,sys
path = os.path.split(os.path.abspath(os.path.realpath(sys.argv[0])))[0] + os.path.sep
rootpath = path[:-10]
#print("validation path:" ,root)
# MNIST Dataset
test_dataset = dsets.MNIST(root='./data/',
train=False,
transform=transforms.ToTensor(),
download=False)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=100,
shuffle=True)
# CNN Model (2 conv layer)
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=5, padding=2),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.MaxPool2d(2))
self.layer2 = nn.Sequential(
nn.Conv2d(16, 32, kernel_size=5, padding=2),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2))
self.fc = nn.Linear(7*7*32, 10)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
cnnmodel = CNN()
cnnmodel = torch.load( rootpath + 'src/step3/cnnModel.pkl')
#/********** Begin *********/
# 将模型转为测试模式 ["cnnmodel.eval()","cnnmodel(images)","TestAccuracyofthemodelonthe200testimages:"]
cnnmodel.eval()
correct = 0
total = 0
i = 0
for images, labels in test_loader:
images = Variable(images)
#对images 应用cnn模型,将结果赋值给 outputs
outputs= cnnmodel(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
i += 1
# 为了节约时间, 我们测试时只测试前10个
if i> 10 :
break
#按格式输出正确率correct/total 的百分比
print("TestAccuracyofthemodelonthe200testimages:",correct/total)
#/********** End *********/
你好,学习pytorch的同仁,如果你看到这,说明你真的是一个很有耐心的人,祝你日后顺利。