美文网首页
[PyTorch]基础大全 附案例-MINST(手写数字 28*

[PyTorch]基础大全 附案例-MINST(手写数字 28*

作者: DDuncan | 来源:发表于2020-02-01 05:35 被阅读0次

一、深度学习框架

  • Caffe
  • Caffe2
  • Chainer
  • CNTK(Microsoft Cognitive Toolkit)
  • Deeplearning4j
  • Keras
  • MATLAB
  • MxNet
  • TensorFlow
  • Theano
  • Torch/PyTorch

二、搭建开发环境(GPU环境)

所有相关软件的资源

  1. GPU环境:NVIDIA CUDAv10.2
    底层基于C/C++开发:VS2015/2017
  2. 库:cuDNN v7.6.5 (for CUDAv10.2)
    系统变量:cuda/bin目录
  3. 开发框架:tensorflow, pytorch等
    + conda:调试很方便
    + pip: 轻量级,纯净
  4. 查看验证
    + CUDA是否正确安装:cmd窗口,输入nvcc -V
    + PyTorch torch.cuda.is_available() ​​返回True
    + GPU使用的设备 variable.cuda() 返回device
  • GPU训练

(model/variable).to('cuda')
(model/variable).to('cpu')

  • 工具

使用:CUDA
查看:GPUz

尽可能使用inplace操作

  • NVIDIA开发人员计划
    使用最新的NVIDIA SDK和工具来加速您在人工智能,深度学习,加速计算和高级图形等关键技术领域中的应用
    包括自动驾驶汽车,大数据,医疗保健,高性能计算,机器人技术,虚拟现实等领域

三、背景知识:CPU与GPU

  1. 核心数
    CPU 4~16个核心
    GPU上千个核心:针对并行计算

  2. 进程与线程

  • GIL Global Interpreter Lock
    解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁
    但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了
    毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序
    解决方案:用multiprocessing替代Thread,多进程代替多线程
  1. GPU使用场合
  • 图像处理:适合并行运算
    openACC
    openCL
  1. 编程实现
    PyTorch框架调用
    C++调用封装好的函数

四、PyTorch框架入门

  1. 知识点
  • 张量Tensor

    向量和矩阵的泛化:类似ndarray,本质都是张量 张量
  • 加法 torch.sum()
    dim=0:跨行相加
    dim=1:跨列相加

  • 乘法

  1. Hadamard product
    x.mul(y) 用法与*乘法相同,两者都是broadcast的
    x.mul_(y) 下划线表示inplace = True
    * 对应元素相乘:如果a与b的size不同,则以某种方式将a或b进行复制,使得复制后的a和b的size相同
    * 标量:数乘
    * 一维向量:广播机制
    Tensor与行向量做乘法的结果是每列乘以行向量对应列的值
    Tensor与列向量做乘法的结果是每行乘以列向量对应行的值

  2. 矩阵相乘 (j, 1, n, m) & (k, m, p) == (j, k, n, p)
    torch.mm(x, y)x.mm(y) 矩阵大小需满足: (i, n)x(n, j)
    torch.matmul(x, y) torch.mm的broadcast版本
    torch.matmul() 支持广播;若均为一维:表示内积

  • 除法:基于列
    a (64, 10) b (64,)
    a/b 报错:若b是列向量(64, 1),则a的每一行除以b的每一行(单个数)
    关注列的维度,10/1能除尽@广播机制
    但是b(64,)默认行向量,即1*64:无法做到10/64

  • 其余运算
    torch.mean() 注意dim的取值
    torch.topk() topk, indices = torch.topk(data, k)

  1. 关键细节
  • 数据类型转换(Tensor&ndarray)
    torch.from_numpy() ndarray转化为Tensor
    torch.numpy() 转化为ndarray
    注意:转换前后共用同一块内存,所以两者同时改变

  • 下划线:表示inplace=True

  • 维度调整
    .shape()
    .flatten()
    .reshape()
    .resize_() 维度小了,则删除多余的数据;维度大了,则新数据先不初始化
    weights.view() 常用,返回与weights维度相同的新的张量(与原始数据相同):如果数量不同,则报错(避免.resize_的问题)


五、实例:识别数字(28*28图像)

基础操作:感知器

#导入模块
import torch

#自定义函数 激活函数
def activation(x):
    return 1/(1+torch.exp(-x))


#单层感知器 5*1
#初始化
torch.manual_seed(7)
features = torch.randn((1, 5)) #1行5列 正态分布随机
weights = torch.randn_like(features) #维度相同randn_like
bias = torch.randn((1, 1))
#前馈过程
h = torch.sum(features.mul(weights)) + bias  #一维向量.mul()表示* Hadamard product
y = activation(h)


#多层感知器 3*2*1
#初始化
torch.manual_seed(7)
features = torch.randn((1, 3))
n_input = features.shape[1]
n_hidden = 2
n_output = 1 
W1 = torch.randn(n_input, n_hidden) #3*2
W2 = torch.randn(n_hidden, n_output) #2*1
B1 = torch.randn((1, n_hidden))
B2 = torch.randn((1, n_output))
#前馈过程
h = activation(features.mm(W1) + B1)
y = activation(h.mm(W2) + B2)

数据大小:[64, 1, 28, 28]
一个Batch:64张图,每张图是28*28像素

from torchvision import datasets, transforms 

#Define a transform to normalize the data 
transform = transforms.Compose([transforms.ToTensor(),
                               transforms.Normalize((0.5, ), (0.5, ))]) 

#Download and load the training data 
trainset = datasets.MNIST('~/.pytorch/NNIST_data/', download=True, train=True, transform=transform) 
testset= datasets.MNIST('~/.pytorch/NNIST_data/', download=True, train=False, transform=transform) 

trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True) 
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True) 

实现1)清晰过程

1.1 建立模型

#导入模块
import torch
from torch import nn, optim
import torch.nn.functional as F

#封装成类
class Classifier(nn.Module):
    def __init__(self):
        super().__init__()
        #3个隐藏层
        self.fc1 = nn.Linear(784, 256) #fc full connect
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, 64)
        self.fc4 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(x.shape[0], -1)

        x= F.relu(self.fc1(x))
        x= F.relu(self.fc2(x))
        x= F.relu(self.fc3(x))
        x= F.log_softmax(self.fc4(x), dim=1)
        return x
  • 前馈测试
model = Classifier()

images, labels = next(iter(testloader)) #测试样本 28*28图像;标签表示具体数字

#计算结果:概率值
#.forward()中含有等价于.flatten的操作
ps = torch.exp(model(images))  #不需要model.forward() 默认为输入(继承nn.Module 且覆写方法.forward())
print(ps.shape)  #结果显示([64, 10])

1.2 训练网络-反向传播 + 1.3 验证测试

#1.2 训练网络-反向传播
model = Classifier()
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)

epochs = 2
steps = 0

train_losses, test_losses = [], []
for e in range(epochs):
    running_loss = 0
    for images, labels in trainloader:
        optimizer.zero_grad()
        log_ps = model.(images)
        loss = criterion(log_ps, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

#1.3 验证测试
    else:
        test_loss = 0
        accuracy = 0
        #验证过程中:关闭梯度跟踪
        with torch.no_grad():
            for images, labels in testloader:
                log_ps = model.(images)
                test_loss += criterion(log_ps, labels)
                
                ps = torch.exp(log_ps)
                top_p, top_class = ps.topk(1, dim=1)
                equals = top_class == labels.view(*top_class.shape)
                accuracy += torch.mean(equals.type(torch.FloatTensor))

        train_losses.append(running_loss/len(trainloader))
        test_losses.append(test_loss/len(testloader))
        print("" .format, ...)
print()

实现2)nn.Module

2.1 建立模型

input_size = 784
hidden_sizes = [128, 64]
output_size = 10

#建立模型
#实现方法(1)逐个列举
model = nn.Sequential(nn.Linear(input_size, hidden_sizes[0]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[0], hidden_sizes[1]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[1], output_size),
                      nn.Softmax(dim=1))

#实现方法(2)有序字典 collections.OrderedDict()
from collections import OrderedDict
model = nn.Sequential(OrderedDict([
                      ('fc1', nn.Linear(input_size, hidden_sizes[0])),
                      ('relu1', nn.ReLU()),
                      ('fc2', nn.Linear(hidden_sizes[0], hidden_sizes[1])),
                      ('relu2', nn.ReLU()),
                      ('output', nn.Linear(hidden_sizes[1], output_size)),
                      ('softmax', nn.Softmax(dim=1))]))

#model[0]等价于model.fc1

2.2 训练网络-反向传播 + 2.3 验证测试

criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.003)

epochs = 5 
for e in range (epochs) : 
    running_loss = 0 
    for images, labels in trainloader: 
    #Flatten WIST images into a 784 long vector 
    images = images. view (images.shape[0], -1) 

    #Training pass 
    optimizer.zero_grad() 

    output = model(images)
    loss = criterion(output, labels)
    loss.backward() 

    optimizer.step() 
    running_loss += loss.item ()
else:
    print (f"Training loss: {running_loss/len (trainloader)}") 
训练结果

六、实例分析(流程梳理)

  1. 模块汇总
import torch
from torch import nn

from torchvision import datasets, transforms
import torch.nn.functional as F

#torch.nn模块
(model).weight
    .weight.data.normal_(std=0.01)
(model).bias
    .bias.data.fill_(0)


torch.randn(, requires_grad=True)  #梯度跟踪
torch.empty()
.random_()
    #建模相关
    criterion()
    loss()
    loss().backword()  #backword() 某变量的偏导(基于梯度跟踪)
    nn.ReLU()
    nn.Logsoftmax() == log(nn.Softmax())
    nn.NLLLoss() == - nn.Logsoftmax()
    nn.CrossEntropyLoss() #交叉熵
    nn.Sequential(nn.Linear(), nn.ReLU(), ...) # OrderedDict()方法


#torch.nn.functional模块
F.relu()
F.sigmoid()
F.softmax()
F.log_softmax()

#autograd模块
requires_grad = True
torch.set_grad_enabled(True|False)
(model).weight.grad

x.requires_grad_(True)  #开启梯度跟踪
torch.zeros(1, requires_grad=True)
with torch.no_grad():
    ......
    ......

#optim模块
optim.SGD()
(optim.SGD()).zero_grad()
  1. 操作细节
  • 验证循环时
    model.eval()将网络设为评估模式,验证环节不必dropout
    model.train() 将其设为训练模式
  • CUDA错误
    错误原因:一部分模型/张量在CPU,另一部分在GPU
    解决方法:涉及所有相关的模型和张量转移到同一device
  1. 评价指标
  • 准确率
  • 精确率、召回率
  • top-5错误
  1. 常见问题

1. 欠拟合&过拟合

@类比-试穿裤子:选择大一点+腰带控制

  • 模型复杂度图表
    提前停止训练
  • 正则化:模型权重的影响
    @ODS-惩罚因子λ:对权重系数增加惩罚因子λ
    本质:避免影响,就针对该影响添加惩罚因子
    L2倾向于选择均匀、非稀疏的向量

2. 训练不均衡

  • dropout
    每个节点有20%的概率(可设置)在前馈-反向传播的一个循环中关闭
    @肌肉训练:左右都训练

3. 陷入局部最小点

1)随机启动random restart
设置不同的初始权值
2)动态步长momentum
动量β:结合步长的历史==动量的惯性
峡谷的例子:左右振荡——>振荡抵消
不用二阶方法:计算量太大
3)随机梯度下降SGD Stochastic gradient descent
梯度的随机近似(随机选择部分数据集),代替真正的梯度下降
更快迭代,但收敛率下降

4. 计算量大

  • 随机梯度下降(选择数据批次)
    程序实现:
    外循环(4个批次)
    内循环(每个批次:6个点)

5. 参数:学习速率选取

  • 实际运用:“动态”学习速率
    陡峭:步长大
    平坦:步长小

6. 选择:不同的误差函数

1)如果参数模型定义了一个分布p(y|x;θ),我们采用最大似然原理得到代价函数:训练数据和模型预测间的交叉熵 交叉熵

对数函数能帮我们避免梯度过小(例如有的输出单元有一个指数函数,取对数后梯度就不那么小了)

2)如果不预测y的完整概率分布,仅仅预测在给定x条件下y的某种统计量,那就用某些专门的损失函数来计算
2.1)均方误差
2.2)平均绝对误差

  1. 均方差损失函数+Sigmoid激活函数(不推荐)
    均方误差和平均绝对误差在梯度下降法表现不好,因为饱和的输出单元梯度非常小。
    (Sigmoid的这个曲线意味着在大多数时候,我们的梯度变化值很小,导致我们的W,b更新到极值的速度较慢,也就是我们的算法收敛速度较慢)

  2. 使用交叉熵损失函数+Sigmoid激活函数(提高收敛速度)
    对比来说交叉熵代价函数更受欢迎

7. 梯度消失 选择:激活函数

  • sigmoid函数两端导数接近0
    Softmax:多个分类
  • 双曲正切tanh(x)
  • 修正线性单元ReLU
  1. 网络优化
  • 网络结构
  • 数据
  • 运行时间

References

选择:不同的误差函数

相关文章

网友评论

      本文标题:[PyTorch]基础大全 附案例-MINST(手写数字 28*

      本文链接:https://www.haomeiwen.com/subject/rsdcthtx.html