系列文章目录
文章目录
pytorch学习笔记(一)pytorch基础知识
本文是在学习DataWhale开源教程《深入浅出PyTorch》过程中做的简单摘抄,原文请点这里第二章:PyTorch基础知识 — 深入浅出PyTorch (datawhalechina.github.io)
1.1 张量
1.1.1 简介
张量是现代机器学习基础,它的核心是一个数据容器,不同维度的张量可以表示不同的数据类型
如:
- 3维:时间序列
- 4维:图像
- 5维:视频
一个图像可以用三个字段表示:
(width, height, channel) = 3D
但是,在机器学习工作中,我们经常要处理不止一张图片或一篇文档——我们要处理一个集合。我们可能有10,000张郁金香的图片,这意味着,我们将用到4D张量:
(batch_size, width, height, channel) = 4D
在PyTorch中, torch.Tensor
是存储和变换数据的主要工具
1.1.2 创建tensor
下面是几种常见方法的示例
-
随即初始化矩阵 torch.rand()
import torch x = torch.rand(4, 3) print(x)
tensor([[0.7569, 0.4281, 0.4722], [0.9513, 0.5168, 0.1659], [0.4493, 0.2846, 0.4363], [0.5043, 0.9637, 0.1469]])
-
全零矩阵 torch.zeros()
import torch x = torch.zeros(4, 3, dtype=torch.long) print(x)
tensor([[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
-
构建张量 torch.tensor()
import torch x = torch.tensor([5.5, 3]) print(x)
tensor([5.5000, 3.0000])
-
基于已经存在的tensor,创建一个tensor
x = x.new_ones(4, 3, dtype=torch.double) # 创建一个新的全1矩阵tensor,返回的tensor默认具有相同的torch.dtype和torch.device # 也可以像之前的写法 x = torch.ones(4, 3, dtype=torch.double) print(x) x = torch.randn_like(x, dtype=torch.float) # 重置数据类型 print(x) # 结果会有一样的size # 获取它的维度信息 print(x.size()) print(x.shape)
tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]], dtype=torch.float64) tensor([[ 2.7311, -0.0720, 0.2497], [-2.3141, 0.0666, -0.5934], [ 1.5253, 1.0336, 1.3859], [ 1.3806, -0.6965, -1.2255]]) torch.Size([4, 3]) torch.Size([4, 3])
1.1.3 张量的操作
-
加法
- x+y
- torch.add(x+y)
- y.add_(x)
-
索引
import torch x = torch.rand(4,3) # 取第二列 print(x[:, 1])
-
维度变换
torch.view()
x = torch.randn(4, 4) y = x.view(16) z = x.view(-1, 8) # -1是指这一维的维数由其他维度决定 print(x.size(), y.size(), z.size())
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
torch.view()
返回的新tensor
与源tensor
共享内存(其实是同一个tensor
),更改其中的一个,另外一个也会跟着改变。(顾名思义,view()仅仅是改变了对这个张量的观察角度)。如果我们希望原始张量和变换后的张量互相不影响,可以先用
clone()
创造一个张量副本然后再使用torch.view()
进行维度变换
1.1.4 广播机制
当对两个形状不同的 Tensor 按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个 Tensor 形状相同后再按元素运算。
x = torch.arange(1, 3).view(1, 2)
print(x)
y = torch.arange(1, 4).view(3, 1)
print(y)
print(x + y)
tensor([[1, 2]])
tensor([[1],
[2],
[3]])
tensor([[2, 3],
[3, 4],
[4, 5]])
由于x和y分别是1行2列和3行1列的矩阵,如果要计算x+y,那么x中第一行的2个元素被广播 (复制)到了第二行和第三行,而y中第⼀列的3个元素被广播(复制)到了第二列。如此,就可以对2个3行2列的矩阵按元素相加。
1.2 自动求导
1.2.1 Autograd简介
PyTorch 中,所有神经网络的核心是 autograd
包。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
tensor是这个包的核心类,设置它的属性 .requires_grad
为 True
,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward()
,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad
属性。
为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad():
中。在评估模型时特别有用,因为模型可能具有 requires_grad = True
的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。
还有一个类对于autograd
的实现非常重要:Function
。Tensor
和 Function
互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn
属性,该属性引用了创建 Tensor
自身的Function
(除非这个张量是用户手动创建的,即这个张量的grad_fn
是 None
)。
eg:
创建一个张量并设置requires_grad=True
用来追踪其计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
对这个张量做一次运算:
y = x ** 2
print(y)
tensor([[1., 1.],
[1., 1.]], grad_fn=<PowBackward0>)
y
是计算的结果,所以它有grad_fn
属性。
print(y.grad_fn)
<PowBackward0 object at 0x000001CB45988C70>
对 y 进行更多操作
z = y * y * 3
out = z.mean()
print(z, out)
tensor([[3., 3.],
[3., 3.]], grad_fn=<MulBackward0>) tensor(3., grad_fn=<MeanBackward0>)
1.2.2 梯度
out.backward()
输出导数 d(out)/dx
print(x.grad)
tensor([[3., 3.],
[3., 3.]])
注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。
如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播), 那么我们可以对 tensor.data 进行操作。
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是一个tensor
print(x.data.requires_grad) # 但是已经是独立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x) # 更改data的值也会影响tensor的值
print(x.grad)
tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])