本系列主要是对pytorch基础知识学习的一个记录,尽量保持博客的更新进度和自己的学习进度。本人也处于学习阶段,博客中涉及到的知识可能存在某些问题,希望大家批评指正。
在吴恩达老师深度学习视频中,第一章和第二章的课后作业我们用numpy实现前向传播和反向传播,numpy提供的ndarray数据类型,支持numpy内置的dot运算简化了极大简化了矩阵运算,让我们可以快速的搭建一个简单的神经网络模型。
在深度学习框架pytorch中,其实我们很难见到numpy的使用,准确的说是很难见到ndarray这一数据类型,见到更多的其实是tensor类型的数据。
ndarray和tensor本质上来说都是多维数组,pytorch封装了很多关于tensor类型数据的操作,比如随机初始化,广播等,所以在pytorch框架下,tensor可以完成ndarray在numpy中的几乎所有操作。tensor和ndarray数据类型之间是可以相互转换的。
除此之外,tensor的一个重大优势是可以利用GPU进行加速运算。
导入库:
import torch
刚开始学习的时候其实有点疑惑的,我pip的明明是pytorch,为什么import的库的名字是torch。
构造一个5x3的矩阵,不初始化:
x = torch.empty(5, 3)
print(x)
输出:
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
构造一个随机初始化矩阵:
x = torch.rand(5, 3)
print(x)
输出:
tensor([[0.1033, 0.4686, 0.5900],
[0.1427, 0.8772, 0.3225],
[0.7151, 0.7967, 0.2124],
[0.3208, 0.7758, 0.6411],
[0.9130, 0.7475, 0.6015]])
与numpy一样,随机初始化提供rand和randn两种方法。
构造一个矩阵全为 0,而且数据类型是 long:
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
输出:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
直接传入数据构造张量:
x = torch.tensor([5.5, 3])
print(x)
输出:
tensor([5.5000, 3.0000])
通过已有tensor创建一个新的tensor:
x = torch.rand(5, 3)
x = x.new_ones(5, 3, dtype=torch.double)
# new_* methods take in sizes
print(x)
x = torch.randn_like(x, dtype=torch.float)
# override dtype!
print(x)
# result has the same size
输出:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.6138, 0.8007, 1.6560],
[-0.1160, 1.0738, 0.5042],
[ 0.3081, 0.0404, 0.1803],
[-0.3866, 0.2987, -1.9679],
[-1.0050, 0.9958, -0.5199]])
获取维度信息:
x = torch.rand(5, 3)
print(x.size())
print(x.size()[0])
print(x.size()[1])
输出:
torch.Size([5, 3])
5
3
可以看出,torch.Size()是一个元组,所以它支持左右的元组操作。
加法操作:
x = torch.rand(5, 3)
y = torch.rand(5, 3)
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(x + y)
print(torch.add(x, y))
print(result)
输出:
tensor([[0.2672, 1.6628, 0.6432],
[1.1228, 0.8476, 0.4138],
[1.7469, 0.4409, 1.0035],
[1.2439, 0.6834, 0.9636],
[1.2115, 1.6199, 1.1866]])
tensor([[0.2672, 1.6628, 0.6432],
[1.1228, 0.8476, 0.4138],
[1.7469, 0.4409, 1.0035],
[1.2439, 0.6834, 0.9636],
[1.2115, 1.6199, 1.1866]])
tensor([[0.2672, 1.6628, 0.6432],
[1.1228, 0.8476, 0.4138],
[1.7469, 0.4409, 1.0035],
[1.2439, 0.6834, 0.9636],
[1.2115, 1.6199, 1.1866]])
可以看出,pytorch对加法运算符进行了重载,能够进行tensor类型之间的加法,pytorch也提供了add方法实现两个tensor类型数据相加。add方法可以提供一个输出tensor作为参数,上面使用的result也赋值了相同的运算结果。
加法in-place:
x = torch.rand(5, 3)
y = torch.rand(5, 3)
print(y)
y.add_(x)
print(y)
输出:
tensor([[0.4575, 0.1560, 0.5231],
[0.3615, 0.4968, 0.9983],
[0.6981, 0.8572, 0.3183],
[0.5831, 0.4591, 0.2261],
[0.7476, 0.3067, 0.1562]])
tensor([[0.8333, 0.1667, 1.2496],
[0.8277, 1.4037, 1.9814],
[1.5074, 1.4336, 0.4321],
[0.8253, 0.9932, 0.8753],
[1.2419, 0.9924, 0.3691]])
可以看出使用 add_() 会直接改变y的值,是把x加到y上。
任何使张量会发生变化的操作都有一个前缀 '_'
改变tensor的形状和大小:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # the size -1 is inferred from other dimensions
print(x.size(), y.size(), z.size()
输出:
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
当view()传入的参数为-1时,该位置上的维度由其他维度上的参数决定,在本例中,x的大小为16,确定z的第二个维度是8,那么z的第一个维度上是16/8=2。
如果你有一个元素 tensor ,使用 .item() 来获得这个 value 。
x = torch.randn(1)
print(x)
print(x.item())
输出:
tensor([-0.9010])
-0.9009907841682434
自动求导(autograd)是深度学习框架的重要组成部分。在之前的作业中,我们使用numpy搭建神经网络,我们需要保存每层计算的A和Z的值,另外我们在定义激活函数的同时会定义对应激活函数求导后的函数,方便我们在反向传播的计算。
但是torch.Tensor类中有属性 requires_grad ,将其设置为True时会开始跟踪针对tensor的所有操作,完成计算后,可以调用 .backward() 来自动计算所有梯度。该张量的梯度将累积到 .grad 属性中。接下来通过官方的一个例子来理解以下:
import torch
x = torch.ones(2, 2, requires_grad=True)
y = x + 2
print(y)
z = y * y * 3
print(z)
out = z.mean()
print(out)
out.backward()
print(x.grad)
输出:
tensor([[3., 3.],
[3., 3.]], grad_fn=)
tensor([[27., 27.],
[27., 27.]], grad_fn=)
tensor(27., grad_fn=)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
具体推导过程如下:
在对神经网络进行训练时我们需要将requieres_grad设置为True,记录梯度需要消耗大量内存,但是训练完成对模型评估时并不用记录梯度,可以将代码块使用 with torch.no_grad(): 包装起来。
学了官方文档的这两部分,个人感受是感觉学了挺多东西,又感觉什么也没学。接下来会讲解使用pytorch搭建一个简单的神经网络,动手实践才是王道。