1 . torch.nn
是专门为深度学习而设计的模块,它的核心数据结构是 Module
,这是一个抽象的概念,既可以表示神经网络中的某个层,也可以表示一个包含很多层的神经网络。在实际使用中,常见的做法是继承 nn.Module
,撰写自己的网络/层。 下面自定义一个全连接层。
1 | import torch as t |
1 . torch.nn
是专门为深度学习而设计的模块,它的核心数据结构是 Module
,这是一个抽象的概念,既可以表示神经网络中的某个层,也可以表示一个包含很多层的神经网络。在实际使用中,常见的做法是继承 nn.Module
,撰写自己的网络/层。 下面自定义一个全连接层。
1 | import torch as t |
1 . 如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录,则使用 tensor.data 进行操作。
Input:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import torch as t
a = t.ones(3, 4, requires_grad=True)
b = t.ones(3, 4, requires_grad=True)
c = a * b
print(a.data) # 还是同一个 tensor
print(a.data.requires_grad) # 但是已经独立于计算图之外了
d = a.data.sigmoid_() # sigmoid_ 是一个 inplace 操作,会修改 a 自身的值
print(a)
print(d.requires_grad)
print(a.requires_grad)
# 近似于 tensor = a.data,但是如果 tensor 被修改,backward 可能会报错
tensor = a.detach()
print(tensor.requires_grad)
# 统计 tensor 的一些指标,不希望被记录
mean = tensor.mean()
std = tensor.std()
maximum = tensor.max()
print(mean, std, maximum)
tensor[0] = 1
print(a)
# 下面会报错: RuntimeError: one of the variables needed for gradient
# computation has been modified by an inplace operation.
# 因为 c = a * b,b 的梯度取决于 a,现在修改了 tensor,其实也就是修改了 a,梯度不再准确
# c.sum().backward()
1 . torch.autograd
为方便用户使用而专门开发的一套自动求导引擎,它能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
1 | import torch as t |
1 | b = t.zeros(3, 4).requires_grad_() |
2 . 验证 autograd 的计算结果与利用公式手动计算的结果一致。
$y=x^2 \cdot e^x$ 的导函数是:$\frac{d_{y}}{d_{x}}=2x \cdot e^x + x^2 \cdot e^x$,来看看 autograd 的计算结果与手动求导的计算结果是否有误差。
本节实现一个训练线性回归参数的例子,线性回归的损失函数为:$loss=\frac{1}{2} \sum_{i=1}^{N}(y_{i}-(wx_{i}+b))^2$,然后利用随机梯度下降法更新参数 $w$ 和 $b$ 来最小化损失函数,最终学得 $w$ 和 $b$ 的值。
1 . tensor 的数据结构分为头信息区和存储区,信息区主要保存着 tensor 形状、步长、数据类型等信息,而真正的数据则保存成连续数组存放在存储区。一般来说一个 tensor 有着与之对应的 storage,storage 是在 data 之上封装的接口,便于使用,而不同 tensor 的头信息一般不同,但却可能使用相同的数据。
随机梯度下降(SGD)方法的一个缺点是其更新方向完全依赖于当前 batch 计算出的梯度,因而十分不稳定,Momentum 算法借用了物理中的动量概念,它模拟的是物体运动时的惯性,即更新的时候在一定程度上保留之前更新的方向,同时利用当前 batch 的梯度微调最终的更新方向,这样一来,可以在一定程度上增加稳定性,从而学习得更快,并且还有一定摆脱局部最优的能力。数学表达式如下:
$$v_{t}=\gamma v_{t-1}+\alpha \cdot \nabla_{\theta }J(\theta)$$ $$\theta=\theta -v_{t}$$
Momentum 算法会观察历史梯度 $v_{t-1}$,若当前梯度的方向与历史梯度一致,表明当前样本不太可能为异常点,则会增强这个方向的梯度,若当前梯度与历史梯度方向不一致,则梯度会衰减。
1 . 逐元素操作的输入和输出形状一致。常见的操作如下表。
函数 | 功能 |
---|---|
abs/sqrt/div/exp/fmod/log/pow… | 绝对值/平方根/除法/指数/求余/求幂… |
cos/sin/asin/atan2/cosh… | 相关三角函数 |
ceil/round/floor/trunc | 上取整/四舍五入/下取整/只保留整数部分 |
clamp(input, min, max) | 超过 min 和 max 部分截断 |
sigmod/tanh.. | 激活函数 |
对于很多操作,例如 div
、mul
、pow
、fmod
等, PyTorch 都实现了运算符重载,所以可以直接使用运算符。如 a ** 2
等价于 torch.pow(a, 2)
,a * 2
等价于 torch.mul(a, 2)
。
1 . Tensor 支持与 numpy.ndarray
类似的索引操作,如无特殊说明,索引出来的结果与原 tensor 共享内存,也即修改一个,另一个也会跟着修改。
Input:
1 | import torch as t |
1 . 通过 tensor.view
方法可以调整 tensor 的形状,但必须保证调整前后元素总数一致,返回的新 tensor 与源 tensor 共享内存,即更改其中一个,另一个也会跟着改变。
1 | a = t.arange(0, 6) |
1 . 从接口的角度来讲,对 tensor 的操作可分为两类:
torch.function
,如 torch.save
等;tensor.function
,如 tensor.view
等。为了方便使用,对 tensor 的大部分操作同时支持这两类接口,如 torch.sum(torch.sum(a, b))
与 tensor.sum(a.sum(b))
功能等价。