张量 ¶
- 0 维张量:标量
- 1 维张量:向量
- 2 维张量:矩阵
- 3 维张量:如一张图片、时间序列树
- 4 维张量:如一批图片
- 5 维张量:如一批视频
- ……
In [ ]:
Copied!
# tensor 创建
import torch
r = torch.rand(4, 3, dtype=torch.double) # 随机矩阵
torch.zeros(4, 3) # 0 矩阵
torch.ones(4, 3) # 1 矩阵
torch.eye(4, 3) # 单位矩阵
torch.empty(4, 3) # 分配空间但是不初始化
torch.arange(0, 10, 2) # [0, 2, ..., 8]
torch.tensor([1, 2, 3]) # 从数组创建
r.new_ones(3, 4) # 继承 r 属性的矩阵,元素全为 1
torch.randn_like(r, dtype=torch.float) # 继承 r 形状的矩阵,随机从 N(0, 1) 正态分布中抽取
r
# tensor 创建
import torch
r = torch.rand(4, 3, dtype=torch.double) # 随机矩阵
torch.zeros(4, 3) # 0 矩阵
torch.ones(4, 3) # 1 矩阵
torch.eye(4, 3) # 单位矩阵
torch.empty(4, 3) # 分配空间但是不初始化
torch.arange(0, 10, 2) # [0, 2, ..., 8]
torch.tensor([1, 2, 3]) # 从数组创建
r.new_ones(3, 4) # 继承 r 属性的矩阵,元素全为 1
torch.randn_like(r, dtype=torch.float) # 继承 r 形状的矩阵,随机从 N(0, 1) 正态分布中抽取
r
Out[ ]:
tensor([[0.4766, 0.8120, 0.1916],
[0.1597, 0.3787, 0.2883],
[0.8513, 0.8320, 0.8854],
[0.8355, 0.8321, 0.8237]], dtype=torch.float64)
In [ ]:
Copied!
# tensor 信息
r.shape # tensor 的形状
r.dtype # tensor 的数据类型
r.size() # tensor 的大小
# tensor 信息
r.shape # tensor 的形状
r.dtype # tensor 的数据类型
r.size() # tensor 的大小
Out[ ]:
torch.Size([4, 3])
In [23]:
Copied!
# tensor 操作
## 加法,广播机制
o = torch.ones(4, 1)
a = torch.arange(0, 4)
print("a + o = ", a + o)
## 索引和视图
r[:, 1] # 获取 所有行,第二列,tensor([0.8120, 0.3787, 0.8320, 0.8321], dtype=torch.float64)
r[2, :] # 获取 第三行,所有列,tensor([0.8513, 0.8320, 0.8854], dtype=torch.float64)
# 索引返回的切片是一个视图,对切片进行修改会影响原张量;可以使用 view() 操作获得改变形状的视图
r.view(12)
r.view(2, -1) # -1 表示自动计算
# 如果不希望修改原数据,使用 clone() 创建一个副本后在进行 view
r.clone().view(3, -1)
# tensor 操作
## 加法,广播机制
o = torch.ones(4, 1)
a = torch.arange(0, 4)
print("a + o = ", a + o)
## 索引和视图
r[:, 1] # 获取 所有行,第二列,tensor([0.8120, 0.3787, 0.8320, 0.8321], dtype=torch.float64)
r[2, :] # 获取 第三行,所有列,tensor([0.8513, 0.8320, 0.8854], dtype=torch.float64)
# 索引返回的切片是一个视图,对切片进行修改会影响原张量;可以使用 view() 操作获得改变形状的视图
r.view(12)
r.view(2, -1) # -1 表示自动计算
# 如果不希望修改原数据,使用 clone() 创建一个副本后在进行 view
r.clone().view(3, -1)
a + o = tensor([[1., 2., 3., 4.],
[1., 2., 3., 4.],
[1., 2., 3., 4.],
[1., 2., 3., 4.]])
Out[23]:
tensor([[0.4766, 0.8120, 0.1916, 0.1597],
[0.3787, 0.2883, 0.8513, 0.8320],
[0.8854, 0.8355, 0.8321, 0.8237]], dtype=torch.float64)
自动求导 ¶
PyTorch 中,所有神经网络的核心是 autograd 包。autograd 包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 (define-by-run) 的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
如果设置 torch.Tensor 的属性 .requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到 .grad 属性。在计算 t.backward() 时:如果 t 是标量,不需要参数;否则需要传入一个同形的 Tensor 作为 gradient 参数。
在数学上,
在评估模型等不希望最终梯度计算的过程中,可以包装在 with torch.no_grad(): 块中。
Tensor 和 Function 互相连接生成了一个无环图 (acyclic graph),这个图编码了完整的计算历史。每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor 自身的 Function (如果这个张量是用户手动创建的,这个张量的 grad_fn 是 None
In [ ]:
Copied!
# 梯度和方向传播
x = torch.eye(2, 2, requires_grad=True)
y = x**2 + 1
z = y * y *3
z_m = z.mean()
print(x, y, z, z_m, "-"*10, sep="\n") # 手动创建的 tensor 没有 grad_fn 属性
z_m.backward(retain_graph=True) # 即计算 d(z_m) / d(x),标量不需要传入参数
print(x.grad)
x.grad.data.zero_() # 注意梯度会累加,计算另一个梯度前要清空梯度
z.backward(torch.ones_like(z), retain_graph=True) # 非标量需要传入同形的 Tensor
print(x.grad)
x.grad.data.zero_()
x.data *= 2 # 通过 x.data 只修改数据,不会影响梯度代数上的计算(结果变了是因为 x.data 的值变了)
z_m.backward(retain_graph=True)
print(x.grad)
# 梯度和方向传播
x = torch.eye(2, 2, requires_grad=True)
y = x**2 + 1
z = y * y *3
z_m = z.mean()
print(x, y, z, z_m, "-"*10, sep="\n") # 手动创建的 tensor 没有 grad_fn 属性
z_m.backward(retain_graph=True) # 即计算 d(z_m) / d(x),标量不需要传入参数
print(x.grad)
x.grad.data.zero_() # 注意梯度会累加,计算另一个梯度前要清空梯度
z.backward(torch.ones_like(z), retain_graph=True) # 非标量需要传入同形的 Tensor
print(x.grad)
x.grad.data.zero_()
x.data *= 2 # 通过 x.data 只修改数据,不会影响梯度代数上的计算(结果变了是因为 x.data 的值变了)
z_m.backward(retain_graph=True)
print(x.grad)
tensor([[1., 0.],
[0., 1.]], requires_grad=True)
tensor([[2., 1.],
[1., 2.]], grad_fn=<AddBackward0>)
tensor([[12., 3.],
[ 3., 12.]], grad_fn=<MulBackward0>)
tensor(7.5000, grad_fn=<MeanBackward0>)
----------
tensor([[6., 0.],
[0., 6.]])
tensor([[24., 0.],
[ 0., 24.]])
tensor([[12., 0.],
[ 0., 12.]])