【PyTorch学习】PyTorch基础知识

目录

一、Pytorch介绍

1.1 什么是Pytorch

1.2 为何要使用Pytorch

1.3 配置Pytorch深度学习环境

二、Pytorch基础

2.1 Tensor(张量)

2.2 Variable(变量)

2.3 Dataset(数据集)

1. torch.utils.data.Dataset

2. torch.utils.data.TensorDataset

3. torch.utils.data.DataLoader

4. torchvision.datasets.ImageFolder

2.4 nn.Module(模组)

2.5 torch.optim(优化)

1. 一阶优化算法

2. 二阶优化算法

2.6 模型的保存和加载


一、Pytorch介绍

1.1 什么是Pytorch

PyTorch 是 Torch7 团队开发的,从它的名字就可以看出,其与 Torch 的不同之处在于 PyTorch 使用了 Python 作为开发语言。

PyTorch 既可以看做加入了 GPU 支持的 numpy,同时也可以看 成一个拥有自动求导功能的强大的深度神经网络,除了 Facebook之外,它还已经被 Twitter、CMU 和 Salesforce 等机构采用。

1.2 为何要使用Pytorch

  1. 掌握一个框架并不能一劳永逸,现在深度学习并没有谁拥 有绝对的垄断地位。多学一个框架,以备不时之需。
  2. Tensorflow 与 Caffe 都是命令式的编程语言,而且是静态的,首先必须构建一个神经网络,然后一次又一次使用同样的结构, 如果想要改变网络的结构,就必须从头开始。但是对于 PyTorch,通过一种反向自动求导的技术,可以让你零延迟地任意改变神经网络的行为,尽管这项技术不是 Pytorch 独有,但目前为止它实现是最快的,能够为你任何疯狂想法的实现获得最高的速度和最佳的灵活性, 这也是 PyTorch 对比 Tensorflow 最大的优势。
  3. Pytorch 的设计思路是线性、直观且易于使用的。
  4. Pytorch 的代码相对于 Tensorflow 而言,更加简洁直观,同 时对于 Tensorflow 高度工业化的很难看懂的底层代码,Pytorch的源代码就要友好得多,更容易看懂。

PyTorch的特点:

  1. 支持GPU
  2. 动态神经网络
  3. Python优先
  4. 命令式体验
  5. 轻松扩展

1.3 配置Pytorch深度学习环境

安装GPU版本的Pytorch链接: 

https://blog.csdn.net/weixin_40431584/article/details/105119633

 

二、Pytorch基础

2.1 Tensor(张量)

PyTorch 里面处理的最基本的操作对象就是 Tensor,Tensor 是张量的英文,表示的是一个多维的矩阵,比如零维就是一个点,一维就是向量,二维就是一般的矩阵,多维就相当于一个多维的数组,这和 numpy 是对应的,而且 PyTorch 的 Tensor 可以和 numpy的ndarray相互转换,唯一不同的是PyTorch可以在GPU上运行,而numpy的ndarray只能在CPU上运行

常用的不同数据类型的Tensor:

  • 32位浮点型torch.FloatTensor
  • 64位浮点型 torch.DoubleTensor
  • 16位整型torch.ShortTensor
  • 32位整型torch.IntTensor
  • 64位整型torch.LongTensor
import torch

# 定义一个三行两列给定元素的矩阵,并且显示出矩阵的元素和大小
a = torch.Tensor([[2, 3], [4, 8], [7, 9]])
print('a is: {}'.format(a))
print('a size is: {}'.format(a.size()))
a is: tensor([[2., 3.],
        [4., 8.],
        [7., 9.]])
a size is: torch.Size([3, 2])

torch.Tensor默认的是torch.FloatTensor数据类型,也可以定义我们想要的数据类型,如下:

b = torch.LongTensor([[2, 3], [4, 8], [7, 9]])
print('b is: {}'.format(b))
b is: tensor([[2, 3],
        [4, 8],
        [7, 9]])

也可以创建一个全是0的空Tensor或者取一个正态分布作为随机初始值:

c = torch.zeros((3, 2))
print('zero tensor: {}'.format(c))

d = torch.randn((3, 2))
print('normal random is:{}'.format(d))
zero tensor: tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])
normal random is:tensor([[-0.7224,  0.6373],
        [-0.0982,  0.1669],
        [-1.4247,  0.5982]])

我们也可以像numpy一样通过索引的方式取得其中的元素,同时也可以改变它的值,比如将a的第一行第二列改变为100。

a[0, 1] = 100
print('changed a is: {}'.format(a))
changed a is: tensor([[  2., 100.],
        [  4.,   8.],
        [  7.,   9.]])

除此之外,还可以在Tensor与numpy.ndarray之间相互转换:

import numpy as np

numpy_b = b.numpy()
print('conver to numpy is \n {}'.format(numpy_b))

e = np.array([[2, 3], [4, 5]])
torch_e = torch.from_numpy(e)
print('from numpy to torch.Tensor is \n {}'.format(torch_e))
f_torch_e = torch_e.float()
print('cahnge data type to float tensor \n {}'.format(f_torch_e))
conver to numpy is 
 [[2 3]
 [4 8]
 [7 9]]
from numpy to torch.Tensor is 
 tensor([[2, 3],
        [4, 5]], dtype=torch.int32)
cahnge data type to float tensor 
 tensor([[2., 3.],
        [4., 5.]])

通过简单的b.numpy(),就能将b转换为numpy数据类型,同时使用 torch.from_numpy()就能将numpy转换为tensor,如果需要更改tensor的数据类型,只需要在转换后的tensor后面加上你需要的类型,比如想将a的类型转换成float,只需a.float()就可以了。

如果你的电脑支持GPU加速,还可以将Tensor放到GPU上。首先通过torch.cuda.is_available()判断一下是否支持GPU,如果想把tensor a放到GPU上,只需a.cuda()就能够将tensor a放到GPU上了。

if torch.cuda.is_available():
    a_cuda = a.cuda()
    print(a_cuda)
tensor([[  2., 100.],
        [  4.,   8.],
        [  7.,   9.]], device='cuda:0')

2.2 Variable(变量)

这个在numpy里面就没有了,是神经网络计算图里特有的一个概念,就是Variable提供了自动求导的功能。

Variable和Tensor本质上没有区别,不过Variable会被放入一个计算图中,然后进行前向传播,反向传播,自动求导。首先Variable是在torch.autograd.Variable中,要将一个tensor变成Variable也非常简单,比如想让一个tensor a变成Variable,只需要 Variable(a)就可以了。

Variable有三个比较重要的组成属性

  • data:通过data可以取出Variable里面的tensor数值。
  • grad_fn:grad_fn表示的是得到这个Variable的操作,比如通过加减还是乘除来得到的。
  • grad:grad是这个Variabel的反向传播梯度。

下面通过例子来具体说明一下:

from torch.autograd import Variable # torch中Variable模块

# 创建变量
x = Variable(torch.Tensor([1]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)

# 建立一个计算图
y = w * x + b   

# 计算梯度
y.backward()

print(x.grad)
print(w.grad)
print(b.grad)
tensor([2.])
tensor([1.])
tensor([1.])

构建Variable,要注意得传入一个参数requires_grad=True,这个参数表示是否对这个变量求梯度,默认的是False,也就是不对这个变量求梯度,这里我们希望得到这些变量的梯度,所以需要传入这个参数。

从上面的代码中,我们注意到了一行y.backward(),这一行代码就是所谓的自动求导,这其实等价y.backward(torch.FloatTensor([1])),只不过对于标量求导里面的参数就可以不写了,自动求导不需要你再去明确地写明哪个函数对哪个函数求导,直接通过这行代码就能对所有的需要梯度的变量进行求导,得到它们的梯度,然后通过x.grad可以得到x的梯度。

上面是标量的求导,同时也可以做矩阵求导,比如:

x = torch.randn(3)
x = Variable(x, requires_grad=True)

y = x * 2
print(y)

y.backward(torch.FloatTensor([1, 0.1, 0.01]))
print(x.grad)
tensor([ 2.3863,  1.3822, -2.5512], grad_fn=<MulBackward0>)
tensor([2.0000, 0.2000, 0.0200])

相当于给出了一个三维向量去做运算,这时候得到的结果y就是一个向量,这里对这个向量求导就不能直接写成 y.backward(),这样程序是会报错的。这个时候需要传入参数声明,比如y.backward(torch.FloatTensor([1, 1, 1])),这样得到的结果就是它们每个分量的梯度,或者可以传入y.backward(torch.FloatTensor([1, 0.1, 0.01])),这样得到的梯度就是它们原本的梯度分别乘上1,0.1和0.01。

2.3 Dataset(数据集)

在处理任何机器学习问题之前都需要数据读取,并进行预处理。PyTorch提供了很多工具使得数据的读取和预处理变得很容易。接下来介绍 Dataset,TensorDataset,DataLoader,ImageFolder的简单用法。

1. torch.utils.data.Dataset

它是代表这一数据的抽象类。你可以自己定义你的数据类,继承和重写这个抽象类,非常简单,只需要定义__len__和__getitem__这两个函数:

from torch.utils.data import Dataset
import pandas as pd

class myDataset(Dataset):
    def __init__(self, csv_file, txt_file, root_dir, other_file):
        self.csv_data = pd.read_csv(csv_file)
        with open(txt_file, 'r') as f:
            data_list = f.readlines()
        self.txt_data = data_list
        self.root_dir = root_dir
        
    def __len__(self):
        return len(self.csv_data)
    
    def __getitem__(self, idx):
        data = (self.csv_data[idx], self.txt_data[idx])
        return data

通过上面的方式,可以定义我们需要的数据类,可以通过迭代的方式来取得每一个数据,但是这样很难实现取batch,shuffle或者是多线程去读取数据。

2. torch.utils.data.TensorDataset

它继承自Dataset,新版把之前的data_tensor和target_tensor去掉了,输入变成了可变参数,也就是我们平常使用*args。

# 原版使用方法

train_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)

# 新版使用方法

train_dataset = Data.TensorDataset(x, y)
import torch
import torch.utils.data as Data

BATCH_SIZE = 5

x = torch.linspace(1, 10, 10)
y = torch.linspace(10, 1, 10)

torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
    dataset=torch_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True,
    num_workers=0,
)

for epoch in range(3):
    for step, (batch_x, batch_y) in enumerate(loader):
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())
Epoch:  0 | Step:  0 | batch x:  [6. 8. 9. 2. 5.] | batch y:  [5. 3. 2. 9. 6.]
Epoch:  0 | Step:  1 | batch x:  [10.  4.  7.  3.  1.] | batch y:  [ 1.  7.  4.  8. 10.]
Epoch:  1 | Step:  0 | batch x:  [ 5. 10.  2.  6.  7.] | batch y:  [6. 1. 9. 5. 4.]
Epoch:  1 | Step:  1 | batch x:  [9. 1. 8. 4. 3.] | batch y:  [ 2. 10.  3.  7.  8.]
Epoch:  2 | Step:  0 | batch x:  [ 5.  7.  1. 10.  9.] | batch y:  [ 6.  4. 10.  1.  2.]
Epoch:  2 | Step:  1 | batch x:  [6. 8. 2. 4. 3.] | batch y:  [5. 3. 9. 7. 8.]

3. torch.utils.data.DataLoader

PyTorch中提供了一个简单的办法来做这个事情,通过torch.utils.data.DataLoader来定义一个新的迭代器,如下:

from torch.utils.data import DataLoader

dataiter = DataLoader(myDataset,batch_size=32,shuffle=True,collate_fn=defaulf_collate)

其中的参数都很清楚,只有collate_fn是标识如何取样本的,我们可以定义自己的函数来准确地实现想要的功能,默认的函数在一般情况下都是可以使用的。

(需要注意的是,Dataset类只相当于一个打包工具,包含了数据的地址。真正把数据读入内存的过程是由Dataloader进行批迭代输入的时候进行的。)

4. torchvision.datasets.ImageFolder

另外在torchvison这个包中还有一个更高级的有关于计算机视觉的数据读取类:ImageFolder,主要功能是处理图片,且要求图片是下面这种存放形式:

root/dog/xxx.png

root/dog/xxy.png

root/dog/xxz.png

root/cat/123.png

root/cat/asd/png

root/cat/zxc.png

之后这样来调用这个类:

from torchvision.datasets import ImageFolder

dset = ImageFolder(root='root_path', transform=None, loader=default_loader)

其中 root 需要是根目录,在这个目录下有几个文件夹,每个文件夹表示一个类别:transform 和 target_transform 是图片增强,后面我们会详细介绍;loader是图片读取的办法,因为我们读取的是图片的名字,然后通过 loader 将图片转换成我们需要的图片类型进入神经网络。

2.4 nn.Module(模组)

在PyTorch里面编写神经网络,所有的层结构和损失函数都来自于torch.nn,所有的模型构建都是从这个基类nn.Module继承的,于是有了下面这个模板。

import torch.nn as nn

class net_name(nn.Module):
    def __init__(self, other_arguments):
        super(net_name, self).__init__()
        self.convl = nn.Conv2d(in_channels, out_channels, kernel_size)
        # 其他网路层
        
    def forward(self, x):
        x = self.convl(x)
        return x

这样就建立了一个计算图,并且这个结构可以复用多次,每次调用就相当于用该计算图定义的相同参数做一次前向传播,这得益于PyTorch的自动求导功能,所以我们不需要自己编写反向传播,而所有的网络层都是由nn这个包得到的,比如线性层nn.Linear。

定义完模型之后,我们需要通过nn这个包来定义损失函数。常见的损失函数都已经定义在了nn中,比如均方误差、多分类的交叉熵,以及二分类的交叉熵等等,调用这些已经定义好的损失函数也很简单:

criterion = nn.CrossEntropyLoss()

loss = criterion(output, target)

criterion = nn.CrossEntropyLoss() loss = criterion(output, target)

2.5 torch.optim(优化)

在机器学习或者深度学习中,我们需要通过修改参数使得损失函数最小化(或最大化),优化算法就是一种调整模型参数更新的策略。优化算法分为两大类。

1. 一阶优化算法

这种算法使用各个参数的梯度值来更新参数,最常用的一阶优化算法是梯度下降。所谓的梯度就是导数的多变量表达式,函数的梯度形成了一个向量场,同时也是一个方向,这个方向上方向导数最大,且等于梯度。梯度下降的功能是通过寻找最小值,控制方差,更新模型参数,最终使模型收敛,网络的参数更新公式是:

\Theta = \Theta - \eta \times \frac{\partial J(\Theta )}{\partial \Theta }

其中 \eta η 是学习率,\frac{\partial J(\Theta )}{\partial \Theta }是函数的梯度。

2. 二阶优化算法

二阶优化算法使用了二阶导数(也叫做Hessian方法)来最小化或最大化损失函数,主要基于牛顿法,但是由于二阶导数的计算成本很高,所以这种方法并没有广泛使用。torch.optim是一个实现各种优化算法的包,大多数常见的算法都能够直接通过这个包来调用,比如随机梯度下降,以及添加动量的随机梯度下降,自适应学习率等。在调用的时候将需要优化的参数传入,这些参数都必须是Variable,然后传入一些基本的设定,比如学习率和动量等。

optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)

这样我们就将模型的参数作为需要更新的参数传入优化器,设定学习率是0.01,动量是0.9随机梯度下降,在优化之前需要先将梯度归零,即optimizer.zeros(),然后通过loss.backward()反向传播,自动求导得到每个参数的梯度,最后只需要optimizer.step()就可以通过梯度作一步参数更新。

import torch
import torch.utils.data as Data
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt

torch.manual_seed(1)   

LR = 0.01
BATCH_SIZE = 32
EPOCH = 12

# 模拟数据
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))

# 绘制数据集
plt.scatter(x.numpy(), y.numpy())
plt.show()

2.6 模型的保存和加载

在PyTorch里面使用torch.save来保存模型的结构和参数,有两种保存方式:

  1. 保存整个模型的结构信息和参数信息,保存的对象是模型 model;
  2. 保存模型的参数,保存的对象是模型的状态model.state_dict()。

可以这样保存,save的第一个参数是保存对象,第二个参数是保存路径及名称:

torch.save(model, './model.pth')

torch.save(model.state_dict(), './model_state.pth')

加载模型有两种方式对应于保存模型的方式:

  1. 加载完整的模型结构和参数信息,使用 load_model=torch.load('model.pth'),在网络较大的时候加载的时间比较长,同时存储空间也比较大;
  2. 加载模型参数信息,需要先导入模型的结构,然后通过 model.load_state_dic(torch.load('model_state.pth'))来导入。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章