PyTorch60分钟教程学习笔记基本概念网络构建分类网络搭建,训练与测试

基本概念

Tensor

tensor是的含义是张量,简单的理解可以将其当成三维矩阵,pytorch中的张量是对数据的一种封装,也是数据结构中最核心的部分之一。对于pytorch中的张量,数组可能是更好的理解方法。

Tensor的定义

  • 直接定义矩阵,使用torch.Tensor(shape)方法定义未初始化的张量,使用torch.rand(shape)torch.randn(shape)定义随机张量
import torch as pt
x = pt.Tensor(2,4)
print(x)
# 1.00000e-23 *
#  0.0000  0.0000  1.2028  0.0000
#  0.0000  0.0000  1.1517  0.0000
# [torch.FloatTensor of size 2x4]
x = pt.rand(5,3)
# 0.7609  0.5925  0.5840
# 0.1949  0.6366  0.3763
# 0.1802  0.8529  0.9373
# 0.6013  0.9685  0.9945
# 0.6555  0.1740  0.9884
# [torch.FloatTensor of size 5x3]
print(x,x.size()[0])
# 5
y = pt.rand(5,3)
  • 从numpy 中定义tensor,使用torch.from_numpy(ndarray)的方法,需要注意的是,这种情况下numpy矩阵和tensor会“绑定”,即修改任何一个的值,另一个的值也会发生变化
a = np.ones(5)
b = pt.from_numpy(a)
print(a,b)
#[ 1.  1.  1.  1.  1.] 
 #1
 #1
 #1
 #1
 #1
#[torch.DoubleTensor of size 5]

Tensor的基本操作

Tensor和numpy中的ndarray相似,可以完成加减乘除等运算,常见的操作方法通常为Tensor.操作(参数)torch.操作(参数,out=输出tensor),以加法为例

result = pt.Tensor(5,3)
test = pt.add(x,y,out=result)
# print(result,test)

y.add_(x)

两种方法test = pt.add(x,y,out=result)y.add_(x)都是相加,前者是相加后将结果交给一个新的Tensorresult,而后者可以理解为y自加x Tensor还可以转换为numpy的对象ndarray,可以使用Tensor.numpy()获得与Tensor绑定的ndarray对象,修改Tensor时,ndarray对象也发生变化

a = pt.ones(5)
b = a.numpy()
# print(a,b)
a.add_(1)
# print(a,b)

使用GPU加速

使用Tensor = Tensor.cuda()的方法可以讲Tensor放到GPU上,通常的运算不支持从CPU到GPU的变换,因此若要在GPU上进行网络运算,网络声明完成后也要调用网络和输入的.cuda()方法将网络和输入放在GPU上

a,b = pt.Tensor(2,2),pt.Tensor(2,2)
a = a.cuda()
b = b.cuda()

Variable

Variable正向传播

Variable与TensorFlow中的Variable一样,是构建神经网络和训练的核心类,使用Variable可以构建计算图,并在图中计算结果(正向传播)和微分(反向传播),Variable的一些运算符重载过,因此可以直接使用+-*/运算符

x = Variable(pt.ones(2,2),requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
# Variable containing:
#27
#[torch.FloatTensor of size 1]

Variable反向传播

以上构建了一个计算图并计算了out的值,完成前向传播,使用out.backward()可以执行反向传播,就是计算微分。

out.backward()
print(x.grad)
#Variable containing:
# 4.5000  4.5000
# 4.5000  4.5000
#[torch.FloatTensor of size 2x2]

网络构建

网络结构构建

class Net(nn.Module):
    """docstring for Net"""
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1,6,5)# input channel,6 output channel,5x5
        self.conv2 = nn.Conv2d(6,16,5)
        self.fc1 = nn.Linear(16*5*5,120)#input 16*5*5,output120
        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)) #input,poolcore shape
        x = F.max_pool2d(F.relu(self.conv2(x)), 2) #(2,2) => 2 because 2=2
        x = x.view(-1, self.num_flat_features(x)) #reshape
        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:] #remove batch size
        num_f = 1
        for s in size:
            num_f *= s
        return num_f

以上为构建一个简单的CNN的例子,其中

  • nn来自import torch.nn as nn这其中封装各种各样的网络层
  • F来自import torch.nn.functional as F,这其中封装了各种各样的神经网络需要使用的函数

在网络结构中

  • nn.Linear(input_size,output_size)为线性连接层,为MLP的线性部分 -nn.Conv2d(input_channel,output_channel,shape)表示卷积核

函数中

  • F.max_pool2d(input,core_shape)为池化层
  • F.relu(input)为ReLu激活函数

另外,Variable.view()为变形函数,其中的-1表示不关心batch,而函数self.num_flat_features(x)是为了获得x的元素数量,这一步直接将x拍扁成向量

网络的前向传播

定义网络后(以上的类)后,声明后可以直接调用

net = Net().cuda()
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 (400 -> 120)
# (fc2): Linear (120 -> 84)
# (fc3): Linear (84 -> 10)
#)

其中.cuda()是将整个网络放到GPU上,如果直接使用net = Net()网络位置在CPU上,将无法使用GPU加速。 定义网络后,直接传入输入即可完成前向传播

net.zero_grad() # Zero the gradient buffers of all parameters 

inputdata = Variable(pt.randn(1,1,32,32)) #?(1,1,32,32) nSamples x nChannels x Height x Width
inputdata = inputdata.cuda()
# 1-batch 1-input channel 32,32
# print(inputdata)
# print(inputdata.unsqueeze(0)) #[torch.FloatTensor of size 1x1x1x32x32]
out = net(inputdata)
print(out)

其中net.zero_grad()是为了清除梯度,起类似于初始化的作用。pytorch要求数据与网络的位置相同,因此若是网络声明在GPU上,数据也必须要GPU上加速。

网络的反向传播(权值更新)

网络的反向传播可以直接使用预先定义的代价函数的.backward()方法实现

net.zero_grad()
print(net.conv1.bias.grad)

loss.backward()
print(net.conv1.bias.grad)

在更新权值的时候,可以手动指定更新的方法

for f in net.parameters():
    f.data.sub_(f.grad.data * 0.01)

其中:

  • net.parameters()是个生成器,可以遍历net中的所有参数
  • f.grad.data为输出(代价函数)到这一参数的梯度

除了手动制定,也可以从import torch.optim as optim中调用优化器

optimizer = optim.SGD(net.parameters(),lr=0.01)
optimizer.zero_grad()
out = net(inputdata)
loss = criterion(out,target)
loss.backward()
optimizer.step()

其中criterion()为代价函数,loss为代价函数的输出值,optimizer.step()为调用一次优化

代价函数

代价函数表示当前结果距离期望输出的“距离”,torch.nn封装了一些代价函数,可以在训练的时候直接调用

target = Variable(pt.arange(1,11)).cuda()
# print(target)
criterion = nn.MSELoss().cuda()
loss = criterion(out,target) #out shape = 1*10 target shape = 10

这里调用的代价函数是MSELoss()平方平均函数

分类网络搭建,训练与测试

分类网络数据准备

教程提供的范例的训练集是CIFAR10数据集,该数据集提供了10种不同类型的图片,引入代码如下图

import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim


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')

这部分仅仅是下载并提供数据集,不必深究,需要注意的是从testloader中获得数据即可

分类网络搭建

分类网络搭建使用两层conv+pool后接3层mlp层的结构,是个基本的卷积神经网络,构建类如下

class Net(nn.Module):
    """docstring for Net"""
    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().cuda()

这里将组件的定义放在了构造函数中,而将网络前馈部分放在了单独的forward()函数中。另外,使用net = Net().cuda()将网络放在了GPU上

分类网络的训练

分类网络的训练需要定义优化器和代价函数,剩下的就是将数据丢进神经网络中了,代码如下

criterion = nn.CrossEntropyLoss()
#声明使用交叉熵函数作为代价函数
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
#声明使用学习率0.001的SGD优化器

for epoch in range(2):
    running_loss = 0
    for i,data in enumerate(trainloader,0):
        inputs,labels = data
        inputs,labels = Variable(inputs).cuda(),Variable(labels).cuda()
        #获得数据并将其放在GPU上
        optimizer.zero_grad()
        #初始化梯度

        outputs = net(inputs)
        #前馈
        loss = criterion(outputs,labels)
        loss.backward()
        optimizer.step()
        #反馈计算梯度并更新权值

        running_loss += loss.data[0]
        if i % 200 == 0:
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0
            #打印平均代价函数值
print('Finished Training')

总结一下,该部分代码总共做了以下几件事

  • 定义优化器与代价函数
  • 执行网络训练

执行网络训练部分,每次迭代包括以下操作

  1. 获取batch数据并将其放在GPU上 2.初始化梯度 3.执行前馈计算代价函数 4.执行反馈计算梯度并更新权值

分类网络的测试

网络测试部分就是将所有的训练数据再投入网络中训练一次,看真实结果与预测结果是否相同,代码如下

corret,total = 0,0
for images,labels in testloader:
    images = images.cuda()
    labels = labels.cuda()
    outputs = net(Variable(images))
    _,predicted = torch.max(outputs.data,1)
    total += labels.size(0)
    corret += (predicted == labels).sum()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * corret / total))

前馈得到预测结果后,使用_,predicted = torch.max(outputs.data,1)在第一维看取出最大的数(丢弃)和最大数的位置(保留)后再与label相比即可进行测试

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习算法工程师

简单易懂的自动编码器

作者:叶虎 编辑:田旭 引言 自动编码器是一种无监督的神经网络模型,它可以学习到输入数据的隐含特征,这称为编码(coding),同时用学习到的新特征可以重构出原...

5066
来自专栏人工智能头条

实战 | 手把手教你用PyTorch实现图像描述(附完整代码)

2.7K7
来自专栏机器之心

基于注意力机制,机器之心带你理解与训练神经机器翻译系统

6908
来自专栏AI科技评论

开发 | 如何把时间序列问题转化为监督学习问题?通俗易懂的 Python 教程

AI科技评论按:本文作者 Jason Brownlee 为澳大利亚知名机器学习专家,对时间序列预测尤有心得。原文发布于其博客。 Jason Brownlee ...

3545
来自专栏机器学习算法与Python学习

机器学习(26)之K-Means实战与调优详解

关键字全网搜索最新排名 【机器学习算法】:排名第一 【机器学习】:排名第一 【Python】:排名第三 【算法】:排名第四 前言 在K-Means聚类算法原理(...

7906
来自专栏文武兼修ing——机器学习与IC设计

基于Python的Rosenblatt感知器模型Rosenblatt感知器Python实现结果

Rosenblatt感知器 Rosenblatt感知器是一种最简单的感知器模型,即输出值为输入与对应权值相乘后取和再累加并加上偏置后通过符号函数的结果,即:Ou...

3479
来自专栏悦思悦读

【文本分析】怎样把文档转换成向量

文本分析 文本分析指从文本中抽取出的特征来量化来表示文本信息,并在此基础上对其进行基于数学模型的处理。它是文本挖掘、信息检索的一个基本问题。 在“大数据”盛行的...

58311
来自专栏AI研习社

手把手教你如何用 TensorFlow 实现基于 DNN 的文本分类

编者按:本文源自一位数据科学开发者的个人博客,主要面对初学者用户,AI 研习社编译。另外,关于 TensorFlow 和 DNN 的更多深度内容,欢迎大家在文末...

3989

在Python中用一个长短期记忆网络来演示记忆

长期短期记忆(LSTM)网络是一种能够在长序列上学习的递归神经网络。

68311
来自专栏Java Web

矩阵乘法问题

问题描述 给定n个矩阵:A1,A2,...,An,其中Ai与Ai+1是可乘的,i=1,2...,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积...

4833

扫码关注云+社区

领取腾讯云代金券