PyTorch笔记(一)

前言

在深度学习的初始阶段,每个深度学习研究者都需要写大量的重发代码。为了提高工作效率,这些研究者就开发出了很多深度学习框架,我们熟知的就有Tensorflow、PyTorch、Caffe等。

每种框架都有自己的特点和短板,每个人可以根据自己的需求进行选择。

对于我来说,PyTorch的代码比较简洁,很容易理解。

PyTorch的下载:https://pytorch.org/

在这里插入图片描述

一、张量(Tensor)

张量是PyTorch处理的最基本的对象,它表示的是一个多维的矩阵。比如零维就是一个点,一维就是一个向量,二维就是一个普通的矩阵,多维就相当于一个多维的数组。这一点和numpy是非常相似的,并且PyTorch的tensor和numpy的ndarray是可以相互转化的。不同的是PyTorch可以在GPU上运行,而numpy的ndarray只能在cpu上运行。

  • 定义一个矩阵
a = torch.Tensor([[1,2],[3,4]])
print('a is : \n{}'.format(a))

#输出结果如下:
#a is : 
#tensor([[1., 2.],
#        [3., 4.]])

我们输入的数据类型是整型的,输出结果却是浮点型的,这里就涉及到tensor的数据类型。torch.Tensor默认的就是浮点型torch.FloatTensor,torch.FloatTensor表示的是32位浮点型,其它数据类型有64位浮点型torch.DoubleTensor、16位整型torch.ShortTensor、32位整型torch.IntTensor和64位整型torch.LongTensor

a = torch.IntTensor([[1,2],[3,4]])
print('a is : \n{}'.format(a))

#a is : 
#tensor([[1, 2],
#        [3, 4]], dtype=torch.int32)
  • 矩阵大小

在numpy中是通过shape来获取矩阵的大小,而在tensor中,使用size来获得矩阵大小。

a = torch.Tensor([[1,2],[3,4]])
print('a size is : \n{}'.format(a.size()))

#a size is : 
#torch.Size([2, 2])
  • 定义全是0的空tensor
b = torch.zeros((3,2))
print('b is : \n{}'.format(b))

#b is : 
#tensor([[0., 0.],
#        [0., 0.],
#        [0., 0.]])
  • 定义全是1的tensor
a = torch.ones((2,2))
print(a)

#tensor([[1., 1.],
#        [1., 1.]])
  • 定义随机值的tensor
c = torch.randn((3,2))
print('c is : \n{}'.format(c))

#c is : 
#tensor([[-1.0598,  1.2067],
#        [-0.8201, -1.9696],
#        [ 0.9314, -0.6350]])
  • 改变矩阵大小
c = torch.randn((3,2))
print('c is : \n{}'.format(c))
c = c.view(2,3)
print('c is : \n{}'.format(c))

#c is : 
#tensor([[ 0.6022,  0.1773],
#        [-2.2036,  0.1484],
#        [ 1.9957,  1.2197]])

#c is : 
#tensor([[ 0.6022,  0.1773, -2.2036],
#        [ 0.1484,  1.9957,  1.2197]])

#上下两个对比可以发现,改变矩阵大小是按每行从左到右的顺序

b = torch.zeros((3,2))
b = b.view(1,-1)  #-1表示自动补齐
print('b is : \n{}'.format(b))

#b is : 
#tensor([[0., 0., 0., 0., 0., 0.]])
  • 索引

我们可以像numpy一样通过索引获取元素或者改变某个值

b = torch.zeros((3,2))
print(b[0,0])  #tensor(0.)
b[0,0] = 100
print('b is : \n{}'.format(b))

#b is : 
#tensor([[100.,   0.],
#        [  0.,   0.],
#        [  0.,   0.]])
  • 数学运算

torch.Tensor 支持大量数学操作符,如+,-,*,/等

e = torch.Tensor([[3,2],[5,4],[7,6]])
f = torch.Tensor([[1,1],[1,1],[1,1]])
g = e+f
print('g is : \n{}'.format(g))

#g is : 
#tensor([[4., 3.],
#        [6., 5.],
#        [8., 7.]])

也可以使用Tensor的内置函数,如add()和add_()等。这里需要提一下的就是 add 和 add_ 的区别,使用add()函数会生成一个新的Tensor变量, 而add_ 函数会直接在当前Tensor变量上进行操作(对于其它末尾带“_”的函数都是相似的功能。)

e = torch.Tensor([[3,2],[5,4],[7,6]])
h = e.add(10)
print('e is : \n{}'.format(e))

#e is : 
#tensor([[3., 2.],
#        [5., 4.],
#        [7., 6.]])

print('h is : \n{}'.format(h))

#h is : 
#tensor([[13., 12.],
#        [15., 14.],
#        [17., 16.]])

e.add_(10)
print('e is : \n{}'.format(e))

#e is : 
#tensor([[13., 12.],
#        [15., 14.],
#        [17., 16.]])
  • 转置
e = torch.Tensor([[3,2],[5,4],[7,6]])
print('e is : \n{}'.format(e))

#e is : 
#tensor([[3., 2.],
#        [5., 4.],
#        [7., 6.]])

print('e is : \n{}'.format(e.t())) #t()表示转置

#e is : 
#tensor([[3., 5., 7.],
#        [2., 4., 6.]])
  • Tensor和Numpy的相互转换
i = np.ones((2,3))
j = torch.from_numpy(i)  #numpy->tensor
print(j)

#tensor([[1., 1., 1.],
#        [1., 1., 1.]], dtype=torch.float64)

l = j.numpy()  #tensor->numpy
print(l)

#[[1. 1. 1.]
# [1. 1. 1.]]

二、变量(Variable)

调用Variable的方法是torch.autograd.Variable,autograd提供了自动求导的功能,这个概念在numpy里面就没有了,这是神经网络计算图里的特有概念。所谓计算图,是神经网络在做运算的时候构造出来的,然后在里面进行前向传播和反向传播。

Variable和Tensor本质上没有区别,不过Variable会被放入一个计算图中,然后进行前向传播、反向传播、自动求导。

将tensor变为Variable也很简单,如将tensor a变成Variable,只需要Variable(a)就可以。

  • Variable封装了Tensor,并且支持了几乎所有Tensor的操作
#如add函数
a = torch.ones((2,2))
b = Variable(a)
c = b.add(10)
print(c)

#tensor([[11., 11.],
#        [11., 11.]])
  • 一旦完成张量计算之后就可以调用.backward()函数,它会把所有的梯度计算好
  • 通过Variable的.data属性可以获取到张量
  • 通过Variabe的.grad属性可以获取到梯度

值得注意的是,pytorch官方文档指出不推荐使用Variable,但是目前Variable仍然可以继续工作,具体做了哪些改动我不太清楚。

在这里插入图片描述

三、数据集

我们在训练模型的时候,需要将训练集全部输入到模型中去,一般方法是使用迭代。而pytorch内已经定义好了一个数据迭代器—DataLoader,它的调用方法是torch.utils.data.DataLoader。

比较重要的几个参数有batch_size(批大小,表示一次将多少数据输入模型), shuffle(是否将数据打乱), num_workers(多线程读取数据)

train_loader = DataLoader(train_dataset, batch_size = 50, shuffle=True, num_workers=0)
#num_workers=0表示只在主线程中加载数据

另外,torchvision内有一个关于计算机视觉的数据读取类ImageFolder,它的调用方式是torchvision.datasets.ImageFolder,主要功能是读取图片数据,且要求图片是下图这种存放方式。

在这里插入图片描述

然后这样来调用类:

train_dataset = ImageFolder(root='./data/train/',transform=data_transform)

root表示根目录,transform表示数据预处理方式。

这种方式将train目录下的cat和dog文件夹内的所有图片作为训练集,而文件夹名cat和dog作为标签数据进行训练。

四、nn.Module

在pytorch内编写神经网络,所有的层结构和损失函数都来自于torch.nn,所有的模型构建都是从nn.Module继承的,于是有了如下模板:

class net_name(nn.Module):
    def __init__(self, other_arguments):
        super(net_name, self).__init__()
        self.conv1 == nn.Conv2d(in_channels, out_channels, kernel_size)
        #other network layer
        
        def forward(self,x):
            x = self.conv1(x)
            return x

这样就建立了一个计算图,并且这个结构可以复用多次,每次调用相当于用该结构图定义的相同参数做一次前向传播。得益于pytorch的自动求导功能,我们不需要自己编写反向传播。

定义完模型后,我们需要通过nn这个包来定义损失函数,如均方误差、交叉熵等。

#定义交叉熵
criterion = nn.CrossEntropyLoss()
#计算损失函数
loss = criterion(output, target)

五、模型的优化

在深度学习中,我们需要不断调整网络参数来使损失函数最小化(或最大化),而模型的优化是参数更新的策略。

常用的优化算法有SGD, Adam等。

比如我们用随机梯度下降来定义模型优化:

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

这样我们就把模型的参数作为需要更新的参数传入优化器,lr是学习率,momentum表示动量,动量是使模型跳出局部最优解的有效方法。

在模型训练中,优化之前需要将梯度归零,即optimizer.zero_grad(),然后通过loss.backward()反向传播,自动求导得到每个参数的梯度,最后只需要optimizer.step()就可以通过梯度进行参数更新。

六、模型的保存与加载

pytorch中保存模型有两种方式:

  • 保存整个模型的结构信息和参数信息
  • 保存模型的参数
torch.save(model, path)
torch.save(model.state_dict(), path)

第一个参数是保存对象,第二个参数是保存路径及名称。

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

  • 加载整个模型
  • 加载参数信息
load_model=torch.load('model.pth')  #方式一
model.load_state_dic(torch.load('model_state.pth'))  #方式二

第二种方式在加载前需要先导入模型结构。

在网络较大的时候不推荐第一种方式,加载时间很长,存储的空间也很大。

参考

廖星宇《深度学习入门之PyTorch》

PyTorch官方文档

发布了5 篇原创文章 · 获赞 1 · 访问量 366

猜你喜欢

转载自blog.csdn.net/cun_king/article/details/105008835
今日推荐