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相比即可进行测试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容