Pytorch tutorial pytorch 入门

今天看了pytorch官方的tutorial,好美的教程哦,说是60min的闪电战,结果运行完代码,花了一整天,是我不配,网站官方地址是:https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html

这篇tutorial主要分为四个部分:

1、什么是Tensor,和Tensorflow介绍差不多

2、自动求梯度(微分)

3、神经网络架构

4、以一个图像分类作为example实战

 

1、WHAT IS PYTORCH?

是一个基于python的科学计算包,有两个作用

【1】取代numpy,而且可以在GPU 上运行

【2】一个提供了最大灵活性和速度的深度学习研究平台

我的目的肯定是2,才不会为了取代numpy安装torch

1.1 Tensor construction

关于torch有这么记过方法来生成特殊的Tensor(矩阵)

torch.empty(2,3,4) 生成一个参数size的矩阵,值为原内存值
torch.rand(2,3,4) 随机值生成一个矩阵
zeros(2,3,4) 所有元素都为0
ones(2,3,4) 所有元素为1
eye(2,2) 对角矩阵
tensor([8,2,8]) 生成值为【8,2,8】的Tensor,指定值

randn_like(a)

ones_like(a)

zeros_like(a)

..._like(a)

值为'_'前面的属性,比如随机,属性和参数a一致,除非指明

 

 

 

 

 

 

 

 

 

Tesor.size()返回的是元组,支持所有的元组操作

1.2 Tensor Operation

【1】加:x+y  或 torch.add(x,y,out=res)  或   y.add_(x)  等价于y+=x 

任何改变张量对象的操作都有“_”    e.g.  x.copy_(y)    x.t_()

【2】切片 :x[ : , 1 ]

【3】reshape/resize:y=x.view(16)    y=x.view(-1,8)

【4】当tensor只有一个元素,可以使用a.item()来调用这个值 

【5】z=a.mean()

【6】求范式:a.norm(p='fro'),fro是2范式

【7】x.eg(y) 判断两个是否相等

【8】y=x.detach() 防止被追踪

tensor操作一个标量时,比如a+1、a*2,tensor里的每个元素都会进行该操作

 

 

1.3 NumPy Bridge(类型转换)

如果Torch的Tensor在CPU上运行,则Tensor和Numpy互相转化的变量共享一片内存

 

tensor (a) -------------a.numpy()--------------->  numpy(b)

tensor (a) <------tensor.from_numpy(b)-------  numpy(b)

 

2、AUTOGRAD: AUTOMATIC DIFFERENTIATION

听到长名词很高级,其实不然,不就是微分吗?pytorch的中心包就是autograd库,提供了tensor中所有操作的自动微分

重点:如果你设置tensor对象的.requires_grad属性为True【a.requires_grad_=True】,就开始追踪其上所有操作,当完成所有计算之后,你可以调用.backward()来自动计算所有的梯度,tensor的梯度会自动累加到.grad属性上

可以使用x.detach()防止梯度被追踪

也可以把代码加入 with torch.no_grad: ,可以在评估模型使用这种模式,因为评估时不需要计算梯度,y=x.detach()是获的一个有相同值但是不需要梯度的新的tensor,

有一个库 torch.Function,教程里说它和Tensor构成了一个有向无环图,我开始不理解,后面做笔记才理清,表示和存储了完整的计算历史,Tensor有个.grad_fn属性,它是引用创建tensor对象的那个Funcation,(除非这个张量是用户手动创建的,即,这个张量的 grad_fn 是 None

tensor.backward()自动计算所有梯度,当其为标量时,就不用指定参数默认参数是out.backward(torch.tensor(1.)),假如不是,就需要指定梯度参数,维度要和tensor匹配,why?我是看的这篇文章理解的https://blog.csdn.net/sinat_28731575/article/details/90342082

https://zhuanlan.zhihu.com/p/33378444

在这里插入图片描述

这是官方文档,第一个参数tensor就是对象tensor,第二个参数就是我很好奇的参数了,假如不引用的话,会提示:

为什么在求导的过程中需要引入这个参数,如果我们不引入这个参数的话,则会报下面的错误:

RuntimeError: grad can be implicitly created only for scalar outputs

即为提示我们输出不是一个标量

因为我们平时调用这个函数都是对最后的loss进行backward,而这是一个标量,更普遍的是,它可以tensor对tensor进行backward:

注意:

pytoch构建的计算图是动态图,为了节约内存,所以每次一轮迭代完之后计算图就被在内存释放,所以当你想要多次backward时候就会报错,需要添加标识:retain_graph=True

loss.backward(retain_graph=True) # 添加retain_graph=True标识,让计算图不被立即释放
import torch
from torch.autograd import Variable # torch 中 Variable 模块

x = Variable(torch.FloatTensor([[1, 2]]), requires_grad=True)  # 定义一个输入变量
y = Variable(torch.FloatTensor([[3, 4],[5, 6]]))
loss = torch.mm(x, y)    # 变量之间的运算
loss.backward(torch.FloatTensor([[1, 0]]), retain_graph=True)  # 求梯度,保留图                                    
print(x.grad.data)   # 求出 x_1 的梯度

x.grad.data.zero_()  # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[0, 1]]), retain_graph=True)   # 求出 x_2的梯度
print(x.grad.data)        # 求出 x_2的梯度

x.grad.data.zero_()  # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[0, 2]]), retain_graph=True)   # 求出 x_2的梯度
print(x.grad.data)        # 求出 2*x_2的梯度

x.grad.data.zero_()  # 最后的梯度会累加到叶节点,所以叶节点清零
loss.backward(torch.FloatTensor([[1, 1]]), retain_graph=True)   # 求出 x_2的梯度
print(x.grad.data)        # 求出 x_2+x_2的梯度

输出:

理解:

Pytorch tutorial pytorch 入门_第1张图片

最后的可以组成雅克比矩阵,与传入的参数相乘就可以了

作用:This characteristic of vector-Jacobian product makes it very convenient to feed external gradients into a model that has non-scalar output.

 

 

3、NEURAL NETWORKS

使用torch.nn可以构建神经网络,nn.Module类包含了许多层,以及forward()方法来返回输出

Pytorch tutorial pytorch 入门_第2张图片

神经网络过程就不说了

  • Define the neural network that has some learnable parameters (or weights)
  • Iterate over a dataset of inputs
  • Process input through the network
  • Compute the loss (how far is the output from being correct)
  • Propagate gradients back into the network’s parameters
  • Update the weights of the network, typically using a simple update rule: weight= weight -learning_rate *gradient

 

三四章内容我就放一起讲了,我主要介绍我的代码,相比较于tutorial上的代码,有些调整。不仅包括了卷积层的kernel size

第一部分 引入的库/包

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

第二部分 网络定义

Conv2d(1,6,3):输入的channel大小为1,输出的channel为6,卷积核大小为3*3

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #
        self.conv1=nn.Conv2d(3,6,3)

        self.pool=nn.MaxPool2d(2,2)
        self.conv2=nn.Conv2d(6,16,4)

        self.fc1=nn.Linear(16*6*6,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,self.num_flat_features(x))
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x


    def num_flat_features(self,x):
        y=x.size()[1:]
        s=1
        for i in y:
            s=s*i
        return s

定义的网路结构需要继承nn.Module,而且overide forward函数,backward()不需要重载,自动实现。学习到的参数都存储在net.parameters()

注意!torch.nn只支持mini-batch,不支持单个案例输入,举个例子,nn.Conv2d will take in a 4D Tensor of   nSamples x nChannels x Height x Width.假如是单个案例,可以通过input.unsqueeze(0)转化为一个batch sample

简要回顾:

  • torch.Tensor - A multi-dimensional array with support for autograd operations like backward(). Also holds the gradient w.r.t. the tensor.
  • nn.Module - Neural network module. Convenient way of encapsulating parameters, with helpers for moving them to GPU, exporting, loading, etc.
  • nn.Parameter - A kind of Tensor, that is automatically registered as a parameter when assigned as an attribute to a Module.
  • autograd.Function - Implements forward and backward definitions of an autograd operation. Every Tensor operation creates at least a single Function node that connects to functions that created a Tensor and encodes its history.

第三部分 损失函数:

损失函数将the (output, target) pair作为输入,记住output在前,有很多损失函数:https://pytorch.org/docs/stable/nn.html

定义损失函数和优化器

    criterion=nn.CrossEntropyLoss()
    optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

 

假如想得到输出的FN图,可以这样

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

 

第四部分 训练代码

反向传播前别忘了net.zero_grad() ,否则会累加

    
    net=Net()


    for epoch in range(5):
        running_loss=0.0
        running_acc=0.0
        for i,data in enumerate(trainloader,0):
            inputs,labels=data
            optimizer.zero_grad()
            output=net(inputs)
            _,pred=torch.max(output,1)
            loss=criterion(output,labels)
            loss.backward()
            optimizer.step()

            running_loss+=loss.item()
            running_acc+=torch.sum(pred==labels)

            if i%2000==1999:
                print('Epoch:{}:{} :loss={},acc={}'.format(
                    epoch+1,i+1,running_loss/2000,running_acc/12000))
                running_loss=0.0
                running_acc=0.0
    print("training is over")

有个torch.max函数,torch.max有两个参数:输入数组及比较的维度,输出有两个:第一个是结果,即该维度下最大的值是多少,第二个是最大值的索引

        x:
         tensor([[0.5285, 0.1247, 0.8332, 0.5485],
                [0.7917, 0.6138, 0.5881, 0.3381],
                [0.4226, 0.6605, 0.8571, 0.0399],
                [0.1716, 0.0609, 0.9712, 0.4838]])

        torch.max(x,1):
         (tensor([0.8332, 0.7917, 0.8571, 0.9712]), tensor([2, 0, 2, 2]))

第五部分 数据加载/处理:

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


torchvision.transforms.Compose([传入一个数组]):通过compose将各个变换串联起来,参数是各个变换的实例对象
变换包括在PIL格式图片上的转换、torch.Tensor上的转换、类型转换:
1、剪切并返回中间区域、饱和度亮度对比度调整、灰度图、填充、旋转、裁剪等
2、对张量图像进行标准化处理Normalize(mean,std)
3、类型转换ToPILImage(mode=None)、ToTensor()参数可以是PIL或者是np.ndarray

DataLoader本质就是一个迭代对象,可以用iter()访问,不可以用next()访问,参数batch_size:每个batch的大小,每一次next()的数量就是batch_size大小
使用iter(DataLoader对象)返回的是一个迭代器,可以用next()访问
也可以使用for input,label in dataloaders:进行访问
num_workers表示用多少个线程处理data loading,默认为0,表示所有的数据都将load到主线程中

第六部分 测试代码:

def test_(testloader):
    PATH = './cifar_net.pth'
    net=Net()
    net.load_state_dict(torch.load(PATH))
    running_loss = 0.0
    running_acc = 0.0
    for i, data in enumerate(testloader, 0):
        inputs, labels = data
        optimizer.zero_grad()
        output = net(inputs)
        _, pred = torch.max(output, 1)
        loss = criterion(output, labels)

        running_loss += loss.item()
        running_acc += torch.sum(pred == labels)

        if i % 100 == 99:
            print('Epoch:{} :loss={},acc={}'.format(
                 i + 1, running_loss / 100, running_acc / 600))
            running_loss = 0.0
            running_acc = 0.0

 

完整代码:


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



class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #
        self.conv1=nn.Conv2d(3,6,3)

        self.pool=nn.MaxPool2d(2,2)
        self.conv2=nn.Conv2d(6,16,4)

        self.fc1=nn.Linear(16*6*6,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,self.num_flat_features(x))
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x


    def num_flat_features(self,x):
        y=x.size()[1:]
        s=1
        for i in y:
            s=s*i
        return s

def imshow(img):
    img=img/2+0.5
    npimg=img.numpy()
    plt.imshow(np.transpose(npimg,(1,2,0)))
    plt.show()

'''
过程:
1、创建一个DataSet对象
2、创建一个DataLoader对象
3、循环这个DataLoader对象,并将data、label加载在模型中进行训练
'''

'''
torchvision.transforms.Compose([传入一个数组]):通过compose将各个变换串联起来
参数是各个变换的实例对象
变换包括在PIL格式图片上的转换、torch.Tensor上的转换、类型转换:
1、剪切并返回中间区域、饱和度亮度对比度调整、灰度图、填充、旋转、裁剪等
2、对张量图像进行标准化处理Normalize(mean,std)
3、类型转换ToPILImage(mode=None)、ToTensor()参数可以是PIL或者是np.ndarray
'''

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=6,shuffle=True,num_workers=2)

'''
DataLoader本质就是一个迭代对象,可以用iter()访问,不可以用next()访问
batch_size:每个batch的大小,每一次next()的数量就是batch_size大小
使用iter(DataLoader对象)返回的是一个迭代器,可以用next()访问
也可以使用for input,label in dataloaders:进行访问
num_workers表示用多少个线程处理data loading,默认为0,表示所有的数据都将load到主线程中
'''
testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=6,shuffle=True,num_workers=2)


str_class='plane car bird cat deer dog frog horse ship truck'
classes=str_class.split()


net=Net()
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

def test_(testloader):
    PATH = './cifar_net.pth'
    net=Net()
    net.load_state_dict(torch.load(PATH))
    running_loss = 0.0
    running_acc = 0.0
    for i, data in enumerate(testloader, 0):
        inputs, labels = data
        optimizer.zero_grad()
        output = net(inputs)
        _, pred = torch.max(output, 1)
        loss = criterion(output, labels)

        running_loss += loss.item()
        running_acc += torch.sum(pred == labels)

        if i % 100 == 99:
            print('Epoch:{} :loss={},acc={}'.format(
                 i + 1, running_loss / 100, running_acc / 600))
            running_loss = 0.0
            running_acc = 0.0

    '''
        torch.max有两个参数:输入数组及比较的维度,输出有两个:第一个是结果,即该维度下最大的值是多少,第二个是最大值的索引

        x:
         tensor([[0.5285, 0.1247, 0.8332, 0.5485],
                [0.7917, 0.6138, 0.5881, 0.3381],
                [0.4226, 0.6605, 0.8571, 0.0399],
                [0.1716, 0.0609, 0.9712, 0.4838]])

        torch.max(x,1):
         (tensor([0.8332, 0.7917, 0.8571, 0.9712]), tensor([2, 0, 2, 2]))
        '''
    #_,predicted=torch.max(outputs,1)
    #print("Predicted:", ",".join('%5s' % classes[predicted[j]] for j in range(6)))


if __name__ == '__main__':

    for epoch in range(5):
        running_loss=0.0
        running_acc=0.0
        for i,data in enumerate(trainloader,0):
            inputs,labels=data
            optimizer.zero_grad()
            output=net(inputs)
            _,pred=torch.max(output,1)
            loss=criterion(output,labels)
            loss.backward()
            optimizer.step()

            running_loss+=loss.item()
            running_acc+=torch.sum(pred==labels)

            if i%2000==1999:
                print('Epoch:{}:{} :loss={},acc={}'.format(
                    epoch+1,i+1,running_loss/2000,running_acc/12000))
                running_loss=0.0
                running_acc=0.0
    print("training is over")
    
    PATH='./cifar_net.pth'
    torch.save(net.state_dict(),PATH)

    test_(testloader)

 

你可能感兴趣的:(深度学习)