参考:
- https://pytorch.apachecn.org/docs/1.4[1]
- 李沐《动手学深度学习》
导入
代码语言:javascript复制import torch # 导入整个包
查阅文档
查找模块中的函数与类
为了知道模块中可以调用哪些函数和类,我们调用 dir
函数。例如,我们可以(查询随机数生成模块中的所有属性:)
In [25]: print(dir(torch.distributions))
['AbsTransform', 'AffineTransform', 'Bernoulli', 'Beta', 'Binomial', 'CatTransform', 'Categorical', 'Cauchy', 'Chi2', 'ComposeTransform', 'ContinuousBernoulli', 'Dirichlet', 'Distribution', 'ExpTransform', 'Exponential', 'ExponentialFamily', 'FisherSnedecor', 'Gamma', 'Geometric', 'Gumbel', 'HalfCauchy', 'HalfNormal', 'Independent', 'Laplace', 'LogNormal', 'LogisticNormal', 'LowRankMultivariateNormal', 'LowerCholeskyTransform', 'MixtureSameFamily', 'Multinomial', 'MultivariateNormal', 'NegativeBinomial', 'Normal', 'OneHotCategorical', 'Pareto', 'Poisson', 'PowerTransform', 'RelaxedBernoulli', 'RelaxedOneHotCategorical', 'SigmoidTransform', 'SoftmaxTransform', 'StackTransform', 'StickBreakingTransform', 'StudentT', 'TanhTransform', 'Transform', 'TransformedDistribution', 'Uniform', 'VonMises', 'Weibull', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'bernoulli', 'beta', 'biject_to', 'binomial', 'categorical', 'cauchy', 'chi2', 'constraint_registry', 'constraints', 'continuous_bernoulli', 'dirichlet', 'distribution', 'exp_family', 'exponential', 'fishersnedecor', 'gamma', 'geometric', 'gumbel', 'half_cauchy', 'half_normal', 'identity_transform', 'independent', 'kl', 'kl_divergence', 'laplace', 'log_normal', 'logistic_normal', 'lowrank_multivariate_normal', 'mixture_same_family', 'multinomial', 'multivariate_normal', 'negative_binomial', 'normal', 'one_hot_categorical', 'pareto', 'poisson', 'register_kl', 'relaxed_bernoulli', 'relaxed_categorical', 'studentT', 'transform_to', 'transformed_distribution', 'transforms', 'uniform', 'utils', 'von_mises', 'weibull']
查找特定函数和类的用法
有关如何使用给定函数或类的更具体说明,我们可以调用 help
函数。例如,我们来[查看张量 ones
函数的用法。]
In [26]: help(torch.ones)
In [27]: ?torch.ones # 仅限于 ipython(包括 Jupyter 笔记本)
In [28]: torch.ones? # 仅限于 ipython
Tensor
Tensor
(张量)类似于NumPy
的ndarray
,但还可以在GPU上使用来加速计算。
快速构建 tensor
代码语言:javascript复制In [2]: x = torch.arange(12) # 1 维 tensor
In [3]: x
Out[3]: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [2]: x = torch.empty(5, 3) # 空矩阵
In [3]: x
Out[3]:
tensor([[0.0000e 00, 2.0000e 00, 0.0000e 00],
[2.0000e 00, 1.1735e 36, 4.5769e-41],
[6.5887e 35, 4.5769e-41, 0.0000e 00],
[0.0000e 00, 6.6363e 35, 4.5769e-41],
[6.6386e 35, 4.5769e-41, 1.1840e 36]])
In [4]: x = torch.rand(5, 3) # 随机矩阵
In [5]: x
Out[5]:
tensor([[0.3323, 0.5025, 0.9657],
[0.4815, 0.4241, 0.2857],
[0.1518, 0.6936, 0.1510],
[0.3635, 0.1421, 0.4776],
[0.0721, 0.2776, 0.3314]])
In [2]: x = torch.zeros(5, 3, dtype=torch.long) # 零矩阵,数据类型为 long
In [3]: x
Out[3]:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
In [2]: torch.ones(5, 3)
Out[2]:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
从数据中构造 tensor
代码语言:javascript复制In [4]: torch.tensor([5.4, 3])
Out[4]: tensor([5.4000, 3.0000])
In [5]: torch.tensor((5.4, 3))
Out[5]: tensor([5.4000, 3.0000])
In [6]: torch.tensor((5.4, 3), dtype = torch.double)
Out[6]: tensor([5.4000, 3.0000], dtype=torch.float64)
重用已构建 tensor 的属性创建新的 tensor:
代码语言:javascript复制In [3]: x = torch.rand(5, 3)
In [4]: x.new_ones(5, 3, dtype=torch.double)
Out[4]:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
In [5]: torch.randn_like(x, dtype=torch.float)
Out[5]:
tensor([[ 1.0804, -1.1380, -0.8551],
[-1.1124, 0.6538, 0.4079],
[ 0.5622, 1.4927, 0.9899],
[-0.0126, 1.4290, 1.8898],
[-0.1893, -0.5293, 0.7433]])
tensor 的属性与运算
代码语言:javascript复制In [4]: import torch
In [5]: x = torch.rand(5, 3)
In [6]: x.size()
Out[6]: torch.Size([5, 3])
In [7]: torch.Size([5, 3]) # 本质上是元组
Out[7]: torch.Size([5, 3])
In [8]: x.numel()
Out[8]: 15
In [9]: y = torch.rand(5, 3)
In [10]: x y
Out[10]:
tensor([[1.4320, 1.2021, 0.0620],
[1.0978, 1.0793, 1.2821],
[1.3716, 0.8742, 0.5661],
[0.9134, 0.1798, 1.4293],
[0.7544, 0.7361, 1.3159]])
In [11]: torch.add(x, y) # 另一种形式
Out[11]:
tensor([[1.4320, 1.2021, 0.0620],
[1.0978, 1.0793, 1.2821],
[1.3716, 0.8742, 0.5661],
[0.9134, 0.1798, 1.4293],
[0.7544, 0.7361, 1.3159]])
In [29]: x.shape # 与 size() 方法一致
Out[29]: torch.Size([5, 3])
原位操作(inplace,以 _
为函数后缀,如 copy_()
、 t_()
):
In [12]: y
Out[12]:
tensor([[0.7367, 0.7452, 0.0162],
[0.6207, 0.3872, 0.3586],
[0.5303, 0.3704, 0.0873],
[0.6344, 0.1556, 0.6411],
[0.4608, 0.3672, 0.8274]])
In [13]: y.add_(x)
Out[13]:
tensor([[1.4320, 1.2021, 0.0620],
[1.0978, 1.0793, 1.2821],
[1.3716, 0.8742, 0.5661],
[0.9134, 0.1798, 1.4293],
[0.7544, 0.7361, 1.3159]])
In [14]: y
Out[14]:
tensor([[1.4320, 1.2021, 0.0620],
[1.0978, 1.0793, 1.2821],
[1.3716, 0.8742, 0.5661],
[0.9134, 0.1798, 1.4293],
[0.7544, 0.7361, 1.3159]])
索引(与NumPy一致):
代码语言:javascript复制In [15]: x[:, 1]
Out[15]: tensor([0.4568, 0.6921, 0.5038, 0.0242, 0.3689])
改变形状:
代码语言:javascript复制In [16]: x.view(15)
Out[16]:
tensor([0.6953, 0.4568, 0.0458, 0.4771, 0.6921, 0.9235, 0.8413, 0.5038, 0.4788,
0.2789, 0.0242, 0.7882, 0.2935, 0.3689, 0.4885])
In [17]: x.view(-1, 5) # 当使用 -1 时,行的维度会自动推断
Out[17]:
tensor([[0.6953, 0.4568, 0.0458, 0.4771, 0.6921],
[0.9235, 0.8413, 0.5038, 0.4788, 0.2789],
[0.0242, 0.7882, 0.2935, 0.3689, 0.4885]])
In [30]: x.reshape(-1, 5) # 使用 reshape 与 view 一致
Out[30]:
tensor([[0.6953, 0.4568, 0.0458, 0.4771, 0.6921],
[0.9235, 0.8413, 0.5038, 0.4788, 0.2789],
[0.0242, 0.7882, 0.2935, 0.3689, 0.4885]])
只有一个元素时转换为标量:
代码语言:javascript复制In [20]: x[0, 0].item()
Out[20]: 0.6953406929969788
更多操作在 https://pytorch.org/docs/stable/torch.html[2]。
节省内存
[运行一些操作可能会导致为新结果分配内存]。例如,如果我们用 y= x y
,我们将取消引用 Y
指向的张量,而是指向新分配的内存处的张量。
In [32]: before = id(y)
In [33]: y = y x
In [34]: id(y) == before
Out[34]: False
幸运的是,(执行原地操作)非常简单。我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如 y[:] = <expression>
。为了说明这一点,我们首先创建一个新的矩阵 z,其形状与另一个 y 相同,使用 zeros_like
来分配一个全
的块。
代码语言:javascript复制In [35]: z = torch.zeros_like(y)
In [36]: print('id(z):', id(z))
id(z): 140513782045184
In [37]: z[:] = x y
In [38]: print('id(z):', id(z))
id(z): 140513782045184
上面我们可以使用 x
替换 z
,就可以达到节省内存的目的。
对接NumPy
Torch张量和NumPy数组将共享它们的底层内存位置,因此当一个改变时,另外也会改变。
代码语言:javascript复制In [21]: a = torch.ones(5)
In [22]: a
Out[22]: tensor([1., 1., 1., 1., 1.])
In [23]: a.numpy()
Out[23]: array([1., 1., 1., 1., 1.], dtype=float32)
In [24]: torch.from_numpy(a.numpy())
Out[24]: tensor([1., 1., 1., 1., 1.])
CUDA上的Tensor
代码语言:javascript复制# 当GPU可用时,我们可以运行以下代码
# 我们将使用`torch.device`来将tensor移入和移出GPU
if torch.cuda.is_available():
device = torch.device("cuda") # a CUDA device object
y = torch.ones_like(x, device=device) # 直接在GPU上创建tensor
x = x.to(device) # 或者使用`.to("cuda")`方法
z = x y
print(z)
print(z.to("cpu", torch.double)) # `.to`也能在移动时改变dtype
概率
在统计学中,我们把从概率分布中抽取样本的过程称为_抽样_(sampling)。将概率分配给一些离散选择的分布称为_多项分布_(multinomial distribution)。稍后我们将给出_分布_(distribution)的更正式定义。但笼统来说,可以把它看作是对事件的概率分配。更多查看笔记本:https://github.com/ShixiangWang/d2lNote/blob/main/pytorch/chapter_preliminaries/probability.ipynb[3]
线性代数
- 标量
- 向量
- 矩阵
- 张量
向量是标量的推广,矩阵是向量的推广,张量是任意维度的推广。**
哈达玛积
两个矩阵的按元素乘法称为 哈达玛积(Hadamard product)(数学符号
)
点积
给定两个向量
,它们的 点积(dot product)
(或
)是相同位置的按元素乘积的和:
。
矩阵-向量积
现在我们知道如何计算点积,我们可以开始理解 矩阵-向量积(matrix-vector products)。回顾矩阵
和向量
。让我们从可视化矩阵
开始,用它的行向量表示
其中每个
都是行向量,表示矩阵的
行。[矩阵向量积
是一个长度为
的列向量,其
元素是点积
]:
我们可以把一个矩阵
乘法看作是一个从
到
向量的转换。这些转换证明是非常有用的。例如,我们可以用方阵的乘法来表示旋转。我们将在后续章节中讲到,我们也可以使用矩阵向量乘积来描述在给定前一层的值时计算神经网络的每一层所需要的计算。在代码中使用张量表示矩阵向量积,我们使用与点积相同的 dot
函数。当我们为矩阵 A
和向量 x
调用 np.dot(A, x)
时,会执行矩阵向量积。注意,A
的列维数(沿轴1的长度)必须与 x
的维数(其长度)相同。
矩阵-矩阵乘法
如果你已经掌握了点积和矩阵-向量积的知识,那么 矩阵-矩阵乘法(matrix-matrix multiplication) 应该很简单。
假设我们有两个矩阵
和
:
用行向量
表示矩阵
的
行,并让列向量
作为矩阵
的
列。要生成矩阵积
,最简单的方法是考虑
的行向量和
的列向量:
当我们简单地将每个元素
计算为点积
:
[我们可以将矩阵-矩阵乘法
看作是简单地执行
次矩阵-向量积,并将结果拼接在一起,形成一个
矩阵]。
范数
线性代数中一些最有用的运算符是 范数(norms)。非正式地说,一个向量的_范数_告诉我们一个向量有多大。这里考虑的 大小(size) 概念不涉及维度,而是分量的大小。在线性代数中,向量范数是将向量映射到标量的函数
。向量范数要满足一些属性。给定任意向量
,第一个性质说,如果我们按常数因子
缩放向量的所有元素,其范数也会按相同常数因子的 绝对值 缩放:
第二个性质是我们熟悉的三角不等式:
第三个性质简单地说范数必须是非负的:
这是有道理的,因为在大多数情况下,任何东西的最小的_大小_是0。最后一个性质要求最小范数,并且只有由所有零组成的向量才能达到最小范数。
你可能会注意到,范数听起来很像距离的度量。如果你还记得小学时的欧几里得距离(想想毕达哥拉斯定理),那么非负性的概念和三角不等式可能会给你一些启发。事实上,欧几里得距离是一个范数:具体而言,它是
范数。假设
-维向量
中的元素是
的 [
范数 是向量元素平方和的平方根:]
(
)
其中,在
范数中常常省略下标
,也就是说,
等同于
。
在深度学习中,我们更经常地使用平方
范数。你还会经常遇到 [
范数,它表示为向量元素的绝对值之和:]
(
)
与
范数相比,
范数受异常值的影响较小。为了计算
范数,我们将绝对值函数和按元素求和组合起来。
范数和
范数都是更一般的
范数的特例:
类似于向量的
范数,[矩阵]
(的 弗罗贝尼乌斯范数(Frobenius norm) 是矩阵元素的平方和的平方根:)
(
)
弗罗贝尼乌斯范数满足向量范数的所有性质。它的行为就好像它是矩阵形向量的
范数。
代码语言:javascript复制torch.norm(torch.ones((4, 9)))
在深度学习中,我们经常试图解决优化问题: 最大化 分配给观测数据的概率; 最小化 预测和真实观测之间的距离。为物品(如单词、产品或新闻文章)分配向量表示,以便最小化相似项目之间的距离,最大化不同项目之间的距离。通常,目标,或许是深度学习算法最重要的组成部分(除了数据),被表达为范数。
微分
导数和微分
假设我们有一个函数
,其输入和输出都是标量。(
的 导数 被定义为)
(
)
如果这个极限存在。如果
存在,则称
在
处是_可微_(differentiable)的。如果
在一个区间内的每个数上都是可微的,则此函数在此区间中是可微的。让我们熟悉一下导数的几个等价符号。给定
,其中
和
分别是函数
的自变量和因变量。以下表达式是等价的:
其中符号
和
是_微分运算符_,表示_微分_操作。我们可以使用以下规则来对常见函数求微分:
(
是一个常数)
(幂律(power rule),
是任意实数)
为了微分一个由一些简单函数(如上面的常见函数)组成的函数,下面的法则使用起来很方便。假设函数
和
都是可微的,
是一个常数,我们有:
常数相乘法则
加法法则
乘法法则
除法法则
现在我们可以应用上述几个法则来计算
。因此,通过令
,我们有
。当
时,此导数也是曲线
切线的斜率。
偏导数
在深度学习中,函数通常依赖于许多变量。因此,我们需要将微分的思想推广到这些 多元函数 (multivariate function)上。
设
是一个具有
个变量的函数。
关于第
个参数
的_偏导数_(partial derivative)为:
为了计算
,我们可以简单地将
看作常数,并计算
关于
的导数。对于偏导数的表示,以下是等价的:
梯度
我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的_梯度_(gradient)向量。设函数
的输入是一个
维向量
,并且输出是一个标量。函数
相对于
的梯度是一个包含
个偏导数的向量:
其中
通常在没有歧义时被
取代。
假设
为
维向量,在微分多元函数时经常使用以下规则:
- 对于所有
,都有
- 对于所有
,都有
- 对于所有
,都有
同样,对于任何矩阵
,我们都有
。正如我们之后将看到的,梯度对于设计深度学习中的优化算法有很大用处。
链式法则
然而,上面方法可能很难找到梯度。这是因为在深度学习中,多元函数通常是 复合(composite)的,所以我们可能没法应用上述任何规则来微分这些函数。幸运的是,链式法则使我们能够微分复合函数。
让我们先考虑单变量函数。假设函数
和
都是可微的,根据链式法则:
现在让我们把注意力转向一个更一般的场景,即函数具有任意数量的变量的情况。假设可微分函数
有变量
,其中每个可微分函数
都有变量
。注意,
是
的函数。对于任意
,链式法则给出:
自动求导
深度学习框架通过自动计算导数,即 自动求导 (automatic differentiation),来加快这项工作。实际中,根据我们设计的模型,系统会构建一个 计算图 (computational graph),来跟踪数据通过若干操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。这里,反向传播(backpropagate)只是意味着跟踪整个计算图,填充关于每个参数的偏导数。
autograd
包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义(define-by-run)的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
torch.Tensor
是这个包的核心类。如果设置它的属性 .requires_grad
为 True
,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward()
,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad
属性。
要阻止一个张量被跟踪历史,可以调用 .detach()
方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad():
中。在评估模型时特别有用,因为模型可能具有 requires_grad = True
的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算(评估模型的时候计算已经完成了)。
还有一个类对于autograd的实现非常重要:Function
。
Tensor
和 Function
互相连接生成了一个无圈图(acyclic graph),它编码了完整的计算历史。每个张量都有一个 .grad_fn
属性(梯度函数),该属性引用了创建 Tensor
自身的Function
(除非这个张量是用户手动创建的,即这个张量的 grad_fn
是 None
)。
如果需要计算导数,可以在 Tensor
上调用 .backward()
。如果 Tensor
是一个标量(即它包含一个元素的数据),则不需要为 backward()
指定任何参数,但是如果它有更多的元素,则需要指定一个 gradient
参数,该参数是形状匹配的张量。
image.png
一个例子
代码语言:javascript复制In [1]: import torch
In [2]: x = torch.arange(4.0)
In [3]: x
Out[3]: tensor([0., 1., 2., 3.])
In [4]: x.requires_grad_(True) # inplace 修改
Out[4]: tensor([0., 1., 2., 3.], requires_grad=True)
In [5]: x.grad
In [6]: y = 2 * torch.dot(x, x)
In [7]: y
Out[7]: tensor(28., grad_fn=<MulBackward0>)
In [8]: y.backward() # 反向传播
In [9]: x.grad
Out[9]: tensor([ 0., 4., 8., 12.])
In [10]: x.grad == 4 * x # 验证
Out[10]: tensor([True, True, True, True])
在默认情况下,PyTorch会累积梯度,我们需要清除之前的值:
代码语言:javascript复制In [11]: x.grad.zero_()
Out[11]: tensor([0., 0., 0., 0.])
In [12]: y = x.sum()
In [13]: y.backward()
In [14]: x.grad
Out[14]: tensor([1., 1., 1., 1.])
非标量变量的反向传播
当 y
不是标量时,向量y
关于向量x
的导数的最自然解释是一个矩阵。对于高阶和高维的 y
和 x
,求导的结果可以是一个高阶张量。然而,虽然这些更奇特的对象确实出现在高级机器学习中(包括[深度学习中]),但当我们调用向量的反向计算时,我们通常会试图计算一批训练样本中每个组成部分的损失函数的导数。我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。
代码语言:javascript复制对非标量调用
backward
需要传入一个gradient
参数,该参数指定微分函数关于self
的梯度。在我们的例子中,我们只想求偏导数的和,所以传递一个1的梯度是合适的
In [15]: x.grad.zero_()
Out[15]: tensor([0., 0., 0., 0.])
In [16]: y = x * x
In [17]: y.sum().backward() # 等价于y.backward(torch.ones(len(x)))
In [18]: x.grad
Out[18]: tensor([0., 2., 4., 6.])
分离计算
有时,我们希望[将某些计算移动到记录的计算图之外]。例如,假设y
是作为x
的函数计算的,而z
则是作为y
和x
的函数计算的。现在,想象一下,我们想计算 z
关于 x
的梯度,但由于某种原因,我们希望将 y
视为一个常数,并且只考虑到 x
在y
被计算后发挥的作用。
在这里,我们可以分离 y
来返回一个新变量 u
,该变量与 y
具有相同的值,但丢弃计算图中如何计算 y
的任何信息。换句话说,梯度不会向后流经 u
到 x
。因此,下面的反向传播函数计算 z = u * x
关于 x
的偏导数,同时将 u
作为常数处理(那么导数就是 u
),而不是z = x * x * x
关于 x
的偏导数。
In [19]: x.grad.zero_()
Out[19]: tensor([0., 0., 0., 0.])
In [20]: y = x * x
In [21]: u = y.detach()
In [22]: z = u * x
In [23]: z.sum().backward()
In [24]: x.grad == u
Out[24]: tensor([True, True, True, True])
In [25]: u
Out[25]: tensor([0., 1., 4., 9.])
Python控制流的梯度计算
使用自动求导的一个好处是,[即使构建函数的计算图需要通过 Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度]。在下面的代码中,while
循环的迭代次数和 if
语句的结果都取决于输入 a
的值。
In [26]:
...: def f(a):
...: b = a * 2
...: while b.norm() < 1000:
...: b = b * 2
...: if b.sum() > 0:
...: c = b
...: else:
...: c = 100 * b
...: return c
...:
In [27]: a = torch.randn(size = (), requires_grad = True)
In [28]: a
Out[28]: tensor(-0.0054, requires_grad=True)
In [29]: d = f(a)
In [30]: d.backward()
我们现在可以分析上面定义的 f
函数。请注意,它在其输入 a
中是分段线性的。换言之,对于任何 a
,存在某个常量标量 k
,使得 f(a) = k * a
,其中 k
的值取决于输入 a
。因此,d / a
允许我们验证梯度是否正确。
In [31]: d
Out[31]: tensor(-142314.2812, grad_fn=<MulBackward0>)
In [32]: a
Out[32]: tensor(-0.0054, requires_grad=True)
In [33]: d / a
Out[33]: tensor(26214400., grad_fn=<DivBackward0>)
In [34]: a.grad
Out[34]: tensor(26214400.)
向量积计算
现在我们来看一个雅可比向量积的例子:
代码语言:javascript复制In [38]: x = torch.randn(3, requires_grad = True)
In [39]: y = x * 2
In [40]: while y.data.norm() < 1000:
...: y = y * 2
...:
In [41]: print(y)
tensor([572.4341, 790.8298, 409.4621], grad_fn=<MulBackward0>)
在这种情况下,y
不再是标量。torch.autograd
不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward
:
In [43]: v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
In [44]: y.backward(v)
In [45]: x.grad
Out[45]: tensor([5.1200e 01, 5.1200e 02, 5.1200e-02])
阻止 torch 梯度跟踪
可以通过将代码块包装在 with torch.no_grad():
中,来阻止autograd跟踪设置了 .requires_grad=True
的张量的历史记录。
In [47]: x.requires_grad
Out[47]: True
In [48]: (x ** 2).requires_grad
Out[48]: True
In [50]: with torch.no_grad():
...: print((x ** 2).requires_grad)
...:
False
参考资料
[1]
https://pytorch.apachecn.org/docs/1.4: https://pytorch.apachecn.org/docs/1.4
[2]
https://pytorch.org/docs/stable/torch.html: https://pytorch.org/docs/stable/torch.html
[3]
https://github.com/ShixiangWang/d2lNote/blob/main/pytorch/chapter_preliminaries/probability.ipynb: https://github.com/ShixiangWang/d2lNote/blob/main/pytorch/chapter_preliminaries/probability.ipynb