Tensor与Variable
- 1.Tensor
- 1.1.expand(*sizes)
- 1.2.unfold(dim, size, step) → Tensor
- 1.3.index_add_(dim, index, tensor) → Tensor
- 1.4.view(*args) → Tensor
- 2.Variable
- 2.1.API 兼容性
- 2.2.in-place 正确性检查
- 2.3.class torch.autograd.Variable [source]
- 2.4.backward(gradient=None, retain_variables=False)[source]
- 2.5.detach()[source]
- 2.6.register_hook(hook)[source]
- 2.7.reinforce(reward)[source]
- 2.8.class torch.autograd.Function[source]
- 2.9.backward(* grad_output)[source]
- 2.10.forward(* input)[source]
- 2.11.mark_dirty(* args)[source]
- 2.12.mark_non_differentiable(* args)[source]
- 2.13.mark_shared_storage(* pairs)[source]
- 2.14.save_for_backward(* tensors)[source]
- 3.Tensor 与Variable的区别和联系
- 4.Parameter
1.Tensor
torch.Tensor
是一种包含单一数据类型元素的多维矩阵。
Torch定义了七种CPU tensor类型和八种GPU tensor类型:
Data type | CPU tensor | GPU tensor |
-32-bit floating point | torch.FloatTensor | torch.cuda.FloatTensor |
64-bit floating point | torch.DoubleTensor | torch.cuda.DoubleTensor |
16-bit floating point | N/A | torch.cuda.HalfTensor |
8-bit integer (unsigned) | torch.ByteTensor | torch.cuda.ByteTensor |
8-bit integer (signed) | torch.CharTensor | torch.cuda.CharTensor |
16-bit integer (signed) | torch.ShortTensor | torch.cuda.ShortTensor |
32-bit integer (signed) | torch.IntTensor | torch.cuda.IntTensor |
64-bit integer (signed) | torch.LongTensor | torch.cuda.LongTensor |
|
每一个张量tensor都有一个相应的torch.Storage
用来保存其数据。类tensor提供了一个存储的多维的、横向视图,并且定义了在数值运算。
注意: 会改变tensor的函数操作会用一个下划线后缀来标示。比如,torch.FloatTensor.abs_()
会在原地计算绝对值,并返回改变后的tensor,而tensor.FloatTensor.abs()
将会在一个新的tensor中计算结果
更多关于Tensor的操作
1.1.expand(*sizes)
返回tensor的一个新视图,单个维度扩大为更大的尺寸。 tensor也可以扩大为更高维,新增加的维度将附在前面。 扩大tensor不需要分配新内存,只是仅仅新建一个tensor的视图,其中通过将stride
设为0,一维将会扩展位更高维。任何一个一维的在不分配新内存情况下可扩展为任意的数值。
- 参数:
sizes(torch.Size or int...)
-需要扩展的大小
1.2.unfold(dim, size, step) → Tensor
返回一个tensor,其中含有在dim
维度填充上所有大小为size
的分片。两个分片之间的步长为step
。 如果_sizedim_是dim
维度的原始大小,则在返回tensor中的维度dim大小是_(sizedim-size)/step+1_ 维度大小的附加维度将附加在返回的tensor中。
- 参数:
dim (int)
-需要展开的维度size (int)
-每一个分片需要展开的大小 - step (int)-相邻分片之间的步长
1.3.index_add_(dim, index, tensor) → Tensor
按参数index
中的索引数确定的顺序,将参数tensor中的元素加到原来的tensor中。参数tensor的尺寸必须严格地与原tensor匹配,否则会发生错误。
- 参数:
dim(int)
-索引index所指向的维度index(LongTensor)
-需要从tensor中选取的指数tensor(Tensor)
-含有相加元素的tensor
1.4.view(*args) → Tensor
返回一个有相同数据但大小不同的tensor。 返回的tensor必须有与原tensor相同的数据和相同数目的元素,但可以有不同的大小。一个tensor必须是连续的contiguous()
才能被查看。
2.Variable
2.1.API 兼容性
Variable
API 几乎和 Tenso
r API一致 (除了一些in-place
方法,这些in-place
方法会修改required_grad=True
的 input
的值)。多数情况下,将Tensor
替换为Variable
,代码一样会正常的工作。
2.2.in-place 正确性检查
所有的Variable
都会记录用在他们身上的in-place operations
。如果pytorch
检测到variable
在一个Function
中已经被保存用来backward
,但是之后它又被in-place operations
修改。当这种情况发生时,在backward
的时候,pytorch
就会报错。这种机制保证了,如果你用了in-place operations
,但是在backward
过程中没有报错,那么梯度的计算就是正确的。
2.3.class torch.autograd.Variable [source]
包装一个Tensor
,并记录用在它身上的operations
.Variable
是Tensor
对象的一个thin wrapper
,它同时保存着Variable
的梯度和创建这个Variable
的Function的引用。这个引用可以用来追溯创建这个Variable
的整条链。如果Variable
是被用户所创建的,那么它的creator
是None
,我们称这种对象为leaf Variables
。
由于autograd
只支持标量值的反向求导(即:y是标量),梯度的大小总是和数据的大小匹配。同时,仅仅给leaf variables
分配梯度,其他Variable的梯度总是为0.
- 变量:
data
– 包含的Tensor
grad
– 保存着Variable的
梯度。这个属性是默认分配的,且不能被重新分配。requires_grad
– 布尔值,指示这个Variable
是否是被一个包含Variable的
子图创建的。更多细节请看Excluding subgraphs from backward
。只能改变leaf variable
的这个标签。volatile
– 布尔值,指示这个Variable
是否被用于推断模式(即,不保存历史信息)。更多细节请看Excluding subgraphs from backward
。只能改变leaf variable的
这个标签。creator
– 创建这个Variable
的Function
,对于leaf variable
,这个属性为None
。只读属性。 - 属性:
data (any tensor class)
– 被包含的Tensor
requires_grad (bool)
–requires_grad
标记. 只能通过keyword传入.volatile (bool)
–volatile
标记. 只能通过keyword
传入.
2.4.backward(gradient=None, retain_variables=False)[source]
当前Variable
对leaf variable
求偏导。
计算图可以通过链式法则求导。如果Variable
是非标量(non-scalar)的,且requires_grad=True
。那么此函数需要指定gradient
,它的形状应该和Variable
的长度匹配,里面保存了Variable的梯度。
此函数累积leaf variable
的梯度。你可能需要在调用此函数之前将Variable
的梯度置零。
-参数:gradient (Tensor)
– 其他函数对于此Variable
的导数。仅当Variable不
是标量的时候使用,类型和位形状应该和self.data
一致。retain_variables (bool)
– True
, 计算梯度所必要的buffer
在经历过一次backward
过程后不会被释放。如果你想多次计算某个子图的梯度的时候,设置为True
。在某些情况下,使用autograd.backward()
效率更高。
2.5.detach()[source]
Returns a new Variable, detached from the current graph. 返回一个新的Variable
,从当前图中分离下来的。
返回的Variable``````requires_grad=False
,如果输入 volatile=True
,那么返回的Variable``````volatile=True
。
注意:
返回的Variable
和原始的Variabl
e公用同一个data tensor
。in-place
修改会在两个Variable
上同时体现**(因为它们共享data tensor
),可能会导致错误**。detach_()[source]
将一个Variable
从创建它的图中分离,并把它设置成leaf variable
。
2.6.register_hook(hook)[source]
注册一个backward hook。
每次gradients
被计算的时候,这个hook
都被调用。hook
应该拥有以下签名:hook(grad) -> Variable or None
hook
不应该修改它的输入,但是它可以选择性的返回一个替代当前梯度的新梯度。
这个函数返回一个 句柄(handle
)。它有一个方法 handle.remove()
,可以用这个方法将hook
从module
移除。
例子:
v = Variable(torch.Tensor([0, 0, 0]), requires_grad=True)
h = v.register_hook(lambda grad: grad * 2) # double the gradient
v.backward(torch.Tensor([1, 1, 1]))
#先计算原始梯度,再进hook,获得一个新梯度。
print(v.grad.data)
2
2
2
[torch.FloatTensor of size 3]
>>> h.remove() # removes the hook
def w_hook(grad):
print("hello")
return None
w1 = Variable(torch.FloatTensor([1, 1, 1]),requires_grad=True)
w1.register_hook(w_hook) # 如果hook返回的是None的话,那么梯度还是原来计算的梯度。
w1.backward(gradient=torch.FloatTensor([1, 1, 1]))
print(w1.grad)
hello
Variable containing:
1
1
1
[torch.FloatTensor of size 3]
2.7.reinforce(reward)[source]
注册一个奖励,这个奖励是由一个随机过程得到的。
微分一个随机节点需要提供一个奖励值。如果你的计算图中包含随机 operations
,你需要在他们的输出上调用这个函数。否则的话,会报错。
- 参数:
reward (Tensor)
– 每个元素的reward
。必须和Varaible
形状相同,并在同一个设备上。
2.8.class torch.autograd.Function[source]
Records operation history and defines formulas for differentiating ops. 记录operation的历史,定义微分公式。
每个执行在Varaibles
上的operation
都会创建一个Function
对象,这个Function
对象执行计算工作,同时记录下来。这个历史以有向无环图的形式保存下来,有向图的节点为functions
,有向图的边代表数据依赖关系(input<-output)
。之后,当backward
被调用的时候,计算图以拓扑顺序处理,通过调用每个Function
对象的backward()
,同时将返回的梯度传递给下一个Function
。
通常情况下,用户能和Functions
交互的唯一方法就是创建Function
的子类,定义新的operation
。这是扩展torch.autograd
的推荐方法。
由于Function
逻辑在很多脚本上都是热点,所有我们把几乎所有的Function
都使用C实现,通过这种策略保证框架的开销是最小的。
每个Function
只被使用一次(在forward
过程中)。
- 变量:
saved_tensors
– 调用forward()
时需要被保存的Tensors
的tuple
。needs_input_grad
– 长度为 输入数量的 布尔值组成的tuple
。指示给定的input
是否需要梯度。这个被用来优化用于backward
过程中的buffer
,忽略backward
中的梯度计算。num_inputs
–forward
的输入参数数量。num_outputs
–forward
返回的Tensor
数量。requires_grad
– 布尔值。指示backward
以后会不会被调用。previous_functions
– 长度为num_inputs
的Tuple of (int, Function) pairs
。Tuple
中的每单元保存着创建input
的Function
的引用和索引。
2.9.backward(* grad_output)[source]
定义了operation
的微分公式。
所有的Function
子类都应该重写这个方法。
所有的参数都是Tensor
。他必须接收和forward
的输出 相同个数的参数。而且它需要返回和forward
的输入参数相同个数的Tensor
。 即:backward
的输入参数是此operation
的输出的值的梯度。backward的返回值是此operation输入值的梯度。
2.10.forward(* input)[source]
执行operation。
所有的Function
子类都需要重写这个方法。
可以接收和返回任意个数tensors
2.11.mark_dirty(* args)[source]
将输入的 tensors
标记为被in-place operation
修改过。
这个方法应当至多调用一次,仅仅用在forward
方法里,而且**mark_dirty
的实参只能是forward
的实参**。
每个在forward
方法中被in-place operations
修改的tenso
r都应该传递给这个方法。这样,可以保证检查的正确性。这个方法在tensor
修改前后调用都可以。
2.12.mark_non_differentiable(* args)[source]
将输出标记为不可微。
这个方法至多只能被调用一次,只能在forward
中调用,而且实参只能是forward
的返回值。
这个方法会将输出标记成不可微,会增加backward
过程中的效率。在backward
中,你依旧需要接收forward
输出值的梯度,但是这些梯度一直是None
。
这用于例如从 max 函数返回的索引。
2.13.mark_shared_storage(* pairs)[source]
将给定的tensors pairs
标记为共享存储空间。
这个方法至多只能被调用一次,只能在forward中调用,而且所有的实参必须是(input, output)
对。
如果一些 inputs
和outputs
是共享存储空间的,所有的这样的 (input, output)
对都应该传给这个函数,保证 in-place operations
检查的正确性。唯一的特例就是,当 output
和input
是同一个tensor
(in-place operations
的输入和输出)。这种情况下,就没必要指定它们之间的依赖关系,因为这个很容易就能推断出来。
这个函数在很多时候都用不到。主要是用在 索引 和 转置 这类的操作中。
2.14.save_for_backward(* tensors)[source]
将传入的 tensor
保存起来,留着backward
的时候用。
这个方法至多只能被调用一次,只能在forward中调用。
之后,被保存的tensors
可以通过 saved_tensors
属性获取。在返回这些tensors
之前,pytorch做了一些检查,保证这些tensor
没有被in-place operations
修改过。
实参可以是None
。
3.Tensor 与Variable的区别和联系
Tensor是Pytorch的一个完美组件,可以生成高维数组,但是要构建神经网络还是远远不够的,我们需要能够计算图的Tensor,那就是Variable。
Variable是对Tensor的一个封装,操作和Tensor是一样的,但是每个Variable都有三个属性,Varibale的Tensor本身的.data
,对应Tensor的梯度.grad
,以及这个Variable是通过什么方式得到的.grad_fn
。Variable计算时,它会逐渐地生成计算图。这个图就是将所有的计算节点都连接起来,最后进行误差反向传递的时候,一次性将所有Variable里面的梯度都计算出来,而tensor就没有这个能力。
4.Parameter
我们知道网络中存在很多参数,这些参数需要在网络训练的过程中实时更新(一个batch更新一次),完成“学习”的过程,譬如最直观的梯度下降法更新参数w:
w.data = w.data - lr * w.grad.data # lr 是学习率
- 网络中若是有100个参数,都要手写更新代码吗?1000个呢?10000个呢…
- Variable默认是不需要求梯度的,那还需要手动设置参数
requires_grad=True
- Variable因为要多次反向传播,那么在bcakward的时候还要手动注明参数
w.backward(retain_graph=True)
Parameters
是 Variable
的子类。Paramenters
和Modules
一起使用的时候会有一些特殊的属性,即:当Paramenters
赋值给Module
的属性的时候,他会自动的被加到Module的
参数列表中(即:会出现在 parameters()
迭代器中)。将Varibale
赋值给Module
属性则不会有这样的影响。 这样做的原因是:我们有时候会需要缓存一些临时的状态(state), 比如:模型中RNN的最后一个隐状态。如果没有Parameter
这个类的话,那么这些临时变量也会注册成为模型变量。
Variable
与 Parameter
的另一个不同之处在于,Parameter
不能被 volatile
(即:无法设置volatile=True
)而且默认requires_grad=True
。Variable
默认requires_grad=False
。
参数说明:
-
data
(Tensor) – parameter tensor. -
requires_grad
(bool, optional) – 默认为True,在BP的过程中会对其求微分。
class Net(Module):
def __init__(self, a, b, ...):
super(net, self).__init__()
self... # parameters
self... # layers
def forward(self):
x = ...
x = ... # 数据流
return x
net = Net(a, b, ...)
net.train()
...
optimizer = torch.optim.SGD(net.parameters(), lr=1e-1)
# 然后在每一个batch中,调用optimizer.step()即可完成参数更新了(loss.backward()之后)