pytorch入门(四)—— Torchvision 数据集

Torchvision 数据集
torchvision.datasets包含数据集:

  • MNIST
  • COCO(用于图像标注和目标检测)(Captioning and Detection)
  • LSUN Classification
  • ImageFolder
  • Imagenet-12
  • CIFAR10 and CIFAR100
  • STL10
  • SVHN
  • PhotoTour

torchvision.models包含预训练的模型结构:

  • AlexNet
  • VGG
  • ResNet
  • SqueezeNet
  • DenseNet

ResNet又名残差网,ResNet引入了残差网络结构(Residual Network),通过残差网络,可以把网络层弄得很深。利用ResNet强大的表征能力,不仅是图像分类,而且很多其他计算机视觉应用(比如物体检测和面部识别)的性能都得到了极大地提升。

VGGNet为深度卷积神经网络。VGGNet反复堆叠 3 × 3 3 \times 3 3×3小型卷积核和 2 × 2 2\times2 2×2最大池化层,成功构筑16 ~ 19层深卷积神经网络。VGGNet虽然模型参数比AlexNet多,但反而只需要较少的迭代次数就可以收敛,主要原因是更深的网络和更小的卷积核带来的隐式的正则化效果。

import torch as tr
import torchvision.models as models

#通过调用构造函数来构造具有随机权重的模型
resnet18 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
#densenet = models.densenet_161()
print(resnet18, alexnet, squeezenet)

#使用torch.utils.model_zoo加载预训练的模型
resnet_18 = models.resnet18(pretrained=True)
print(resnet_18)

#PyTorch提供的预训练的模型
#models.alexnet(pretrained=False, ** kwargs)
alexnet_p = models.alexnet(pretrained=False)
print(alexnet_p)

Torch.nn包里面包含了如何定义Module神经网络模型,各种Functional函数以及如何通过Optim实现各种优化算法。
Module是神经网络的基本组成部分,作为一个抽象类,可以通过定义成员函数实现不同的神经网络结构。
Functional包括Convolution函数、Pooling函数、非线性激活函数、Normalization函数、线性函数、Dropout函数、距离函数(Distance functions)、损失函数(Loss functions)等。
Optim包含了各种优化算法,Momentum算法、NesterovMomentum算法、AdaGrad算法、RMSProp算法、Adam(Adaptive Moment Estimation)算法等。

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))
    
    
model = Model()
print(model)

卷积层

卷积是一种局部操作,通过一定大小的卷积核作用于局部图像区域,从而得到图像的局部信息。

###一维卷积层
class torch.nn.Conv1d(in_channels, out_channels, kerne_size,  stride=1, 
						padding=0, dilation=1, groups=1, bias=True)
import torch as tr
import torch.nn as nn
import torch.autograd as autograd
m = nn.Conv1d(16, 33, 3, stride=2)
inputs = autograd.Variable(tr.randn(20, 16, 50))
output = m(inputs)
print(output)

池化层

###池化层
class torch.nn.MaxPool1d(kerne_size, stride=None, padding=0, dilation=1, 
											return_indices=False, ceil_mode=False)
import torch as tr
import torch.nn as nn
import torch.autograd as autograd
m = nn.MaxPool1d(3, stride=2)
input = autograd.Variable(tr.randn(20, 16, 50))
output = m(inputs)
print(output)

创建一个小型ConvNet

import torch as tr
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F

class MNISTConvNet(nn.Module):
	def __init__(self):
		super(MNISTConvNet, self).__init__()
		self.conv1 = nn.Conv2d(1, 10, 5)
		self.pool1 = nn.MaxPool2d(2, 2)
		self.conv2 = nn.Conv2d(10, 20, 5)
		self.pool2 = nn.MaxPool2d(2, 2)
		self.fc1 = nn.Linear(320, 50)
		self.fc2 = nn.Linear(50, 10)
	
	def forward(self, input):
		x = self.pool1(F.relu(self.conv1(input)))
		x = self.pool2(F.relu(self.conv2(x)))
		return x

net = MNISTConvNet()
print(net)

torch.nn包装仅支持作为小批量样品的输入,而不是单个样品。

input = Variable(tr.randn(1, 1, 28, 28))
out = net(input)
print(out.size())

定义虚拟目标标签并使用损失函数计算错误

target =  Variable(tr.LongTensor([3]))
loss_fn = nn.CrossEntropyLoss()
err = loss_fn(out, target)
err.backward()
print(err)

Functional 函数

torch.nn.functional 包里面提供的函数如下:

  • Convolution 函数
  • Pooling 函数
  • 非线性激活函数
  • Normalization函数
  • 线性函数
  • Dropout函数
  • 距离函数(Distance functions)
  • 损失函数(Loss functions)
  • Vision Functions

Torch.nn 包里面只是包装好了神经网络架构的类,nn.functional 与 Torch.nn 包相比,nn.functional 是可以直接调用函数的。

torch.nn.functional.con1d(input, weight, bias=None, stride=1, padding=0, 
							dilation=1, groups=1)
filters = autograd. Variable(tr.randn(33, 16, 3))
inputs = autograd. Variable(tr.randn(20, 16, 50))
F.conv1d(inputs, filters)
Pooling 函数

Pooling函数主要用于图像处理的卷积神经网络中。
卷积神经网络中的卷积层是对图像的一个邻域进行卷积得到图像的邻域特征。亚采样层就是使用Pooling函数技术将小邻域内的特征点整合得到新的特征。Pooling函数确实起到了整合特征的作用。
池化操作是利用一个矩阵窗口在张量上进行扫描,将每个矩阵通过取最大值或者平均值等方法来减少元素的个数,最大值和平均值的方法可以使得特征提取拥有“平移不变性”,也就说图像有了几个像素的位移情况下,依然可以获得稳定的特征组合,平移不变性对于识别十分重要。
Pooling函数的结果是特征减少,参数减少,但Pooling的目的不仅是如此。Pooling函数的目的是保持某种不变性(旋转、平移、伸缩等),常用的有Mean-Pooling函数,Max-Pooling函数和Stochastic-Pooling函数三种。

torch.nn.functional.avg_pool1d(input, kernel_size, stride=None, padding=0, 
								ceil_mode=False, count_include_pad=True)
input = Variable(tr.Tensor([[[1, 2, 3, 4, 5, 6, 7]]]))
F.avg_pool1d(input, kernel_size=3, stride=2)
Sigmoid 函数
@deprecated
torch.nn.functional.sigmoid(input) 
#已移除,使用torch.sigmoid(input)代替

Sigmoid 函数的定义:
f ( x ) = 1 1 + e − x f(x) = \frac {1} {1+e^{-x}} f(x)=1+ex1
优点:

  • Sigmoid 函数的输出映射在(0, 1)之间,单调连续,输出范围有限,优化稳定,可以用作输出层;
  • 求导容易

缺点:

  • 由于其软饱和性,容易产生梯度消失,导致训练出现问题;
  • 其输出并不是以0为中心的
input = Variable(tr.Tensor([[[1, 2, 3, 4, 5, 6, 7]]]))
m = torch.sigmoid(input)
Tanh 函数

Tanh 函数是双曲函数中的一个,为反曲正切。

@deprecated
torch.nn.functional.tanh(input) 
#已移除,使用torch.sigmoid(input)代替

Tanh 函数的定义:
t a n h ( x ) = 1 − e − 2 x 1 + e − 2 x tanh(x) = \frac {1-e^{-2x}} {1+e^{-2x}} tanh(x)=1+e2x1e2x
优点:

  • 比 Sigmoid 函数收敛速度更快;
  • 相比 Sigmoid 函数,其输出以 0 为中心。

缺点:

  • 还是没有改变 Sigmoid 函数的最大问题——由于饱和性产生的梯度消失。
input = Variable(tr.Tensor([[[1, 2, 3, 4, 5, 6, 7]]]))
m = tf.tanh(input)
ReLU 函数

ReLU 函数用来替代传统的激活函数。

torch.nn.functional.relu(input, inplace=False) 
torch.relu(input, inplace=False)

ReLU 函数的定义:
y = { 0 ( x ≤ 0 ) x ( x > 0 ) y = \left\{ \begin{array}{ll} 0 & \textrm{($x\le 0$)}\\ \\ x & \textrm{($x > 0$)} \end{array} \right. y=0x(x0)(x>0)

input = Variable(tr.Tensor([[[1, 2, 3, 4, 5, 6, 7]]]))
m = tf.relu(input)
m = tf.nn.functional.relu(input)
Dropout 函数

Dropout 函数可以作为一种Trikc选择,用来防止模型过拟合。
在每个训练批次中,通过忽略一半的神经元,即让一半的隐层节点值为0,可以明显减少过拟合现象

torch.nn.functional.dropout(input, p=0.5, training=False, inplace=False) 
torch.nn.functional.alpha_dropout(input, p=0.5, training=False) 
torch.nn.functional.dropout2d(input, p=0.5, training=False, inplace=False) 
torch.nn.functional.dropout3d(input, p=0.5, training=False, inplace=False) 
损失函数(Loss functions)

通常机器学习每一个算法中都会有一个目标函数,算法的求解过程是通过对这个目标函数优化的过程。在分类或者回归问题中,通常使用损失函数(代价函数)作为其目标函数。损失函数用来评价模型的预测值和真实值不一样的程度,损失函数越好,通常模型的性能越好。不同的算法使用的损失函数不一样。
损失函数分为经验风险损失函数和结构风险损失函数。经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是指经验风险损失函数加上正则项。

KL散度损失函数

torch.nn.functional.kl_div(input, target, size_average=True) 

其他常见损失函数

torch.nn.functional.poisson_nll_loss(input, target, log_input=True, full=False, size_average=True) 
torch.nn.functional.nll_loss(input, target, weight=None, size_average=True) 
torch.nn.functional.cosine_embedding_loss(input, input2, target, margin=0,size_average=True) 

优化算法

torch.optim 是实现各种优化算法的包。
在使用 torch.optim 包构建 Optimizer 对象中,可以指定 Optimizer 参数,包括学习速率,权重衰减等。

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr=0.0001)

同时也可为每个参数单独设置选项,利用 dict 定义一组参数,进行传入数据

optim.SGD([
			{'params': model.base.parameters()},
			{'params': model.classifier.parameters(), 'lr': 1e-3}
		], lr=1e-2, momentum=0.9)

所有的 Optimizer 都会实现 step() 更新参数的方法,使用方法如下:

optimizer.step()

一旦梯度被如 backward() 之类的函数计算好之后,调用该函数

for input, target in dataset:
	optimizer.zero_grad()
	output = model(input)
	loss = loss_fn(output, target)
	loss.backward()
	optimizer.step()
optimizer.step(closure)

一些优化算法例如 Conjugate Gradient 和 LBFGS 需要重复多次计算函数,因此需要传入一个闭包来允许它们重新计算你的模型,这个闭包会清空梯度,计算损失,然后返回

for input, target in dataset:
	def closure():
		optimizer.zero_grad()
		output = model(input)
		loss = loss_fn(output, target)
		loss.backward()
		return loss
optimizer.step(closure)
  • 随机梯度优化算法
torch.optim.SGD(params, lr=0.01, momentum=0, dampening=0, weight_decay=0, nesterov=False)

SGD 全名Stochastic Gradient Descent,即随机梯度下降。其特点是训练速度快,对于很大的数据集,也能够以比较快的速度收敛

optimizer = tr.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
optimizer.zero_grad()
loss_fn = (model(input), target).backward()
optimizer.step()

动态学习速率调整算法

torch.optim.lr_scheduler

torch.optim.lr_scheduler 提供了几种方法根据 epoches 的数量调整学习速率。
torch.optim.lr_scheduler.ReduceLROnPlateau 允许基于一些验证测量来降低动态学习速率

torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)

将每个参数组的学习速率设置为初始的 lr 乘以一个给定的函数。当 last_epoch=-1 时,将初始 lr 设置为 lr

lambda1 = lambda epoch: epoch // 30
lambda2 = lambda epoch: 0.95 ** epoch
scheduler = tr.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
for epoch in range(100):
	scheduler.step()
	train(...)
	validate(...)

注意:训练时的常见问题

  • 梯度消失和梯度爆炸
  • 梯度消失问题

自动求导机制

  • requires_grad
    如果一个变量定义 requires_grad 为 True,后续的这个变量的所有操作可以使用requires_grad,如果一个变量定义 requires_grad 为 False,变量不需要梯度,在子图中从不执行向后计算。
x = Variable(tr.randn(5, 5))
y = Variable(tr.randn(5, 5))
z = Variable(tr.randn(5, 5), requires_grad=True)
a = x + y
a.requires_grad
#False
b = a + z
b.requires_grad
#True

使用requires_grad = False 冻结已训练好的模型参数

model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
	param.requires_grad = False
model.fc = nn.Linear(512, 100)
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)

  • volatile (已移除,使用 with torch.no_grad() 代替)
    volatile 代表 requires_grad 为False。在不执行计算,不调用.backward()的时候,volatile参数特别有用,他将使用绝对最小的内存来评估模型。
    如果一个变量定义了volatile的操作,那么他的输出也将是volatile
regular_input = Variable(tr.randn(3, 3, 3, 1))
volatile_input = Variable(tr.randn(3, 3, 3, 1), volatile=True)
model = torchvision.models.resnet18(pretrained=True)
model(regular_input).requires_grad
model(volatile_input).requires_grad #True
model(volatile_input).volatile
#volatile was removed (Variable.volatile is always False)
#model(volatile_input).creator is None

with tr.no_grad():
	volatile_input = Variable(tr.randn(3, 3, 3, 1))
	volatile_input.requires_grad
	#False

@torch.no_grad()
 def doubler(x):
	return x * 2
z = doubler(x)
z.requires_grad
#False

保存和加载模型

序列化和恢复模型的两种主要方法

  • 只保存和加载模型参数
  • 保存和加载整个模型
#第一种
torch.save(the_model.state_dict(), PATH)
the_model = TheModelClass(*args, **kwargs)
the_model.load_state_dict(torch.load(PATH))

#第二种
torch.save(the_model, PATH)
the_model = torch.load(PATH)

GPU加速运算

#返回当前所选设备的索引
torch.cuda.current_device()
#更改所选设备
torch.cuda.device(idx)
import torch as tr
x = tr.cuda.FloatTensor(1)
y = tr.cuda.FloatTensor(1).cuda()
with tr.cuda.device(0):
	a = tr.cuda.FloatTensor(1)
	b = tr.cuda.FloatTensor(1).cuda()
	c = a + b
	z = x + y
	d = tr.randn(2).cuda()

你可能感兴趣的:(机器学习,PyTorch)