Tensor本身是数据View,与Storage结合形成MVC模式。本主题主要罗列Tensor数据访问有关的函数。
数据访问
item函数
- 直接把标量Tensor转换为Python类型
import torch
# t = torch.Tensor([
# [1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]
# ])
t = torch.Tensor([1])
print(t.item())
1.0
data属性与detach函数
-
data与原Tensor共享Storage,但是属性会改变:requires_grad会变为False。
- 可以通过data修改原数据,修改后,在运行时不会检测,然后可能会产生错误
-
detach()函数返回的是从计算图中剥离出来的Tensor,requires_grad=False。
- 通过detach()函数返回的张量修改数据,这种修改在运行的时候,会先检测是否修改,从而在运行前触发错误。
-
注意:
- 关于Tensor的检测实际与图跟踪有关,这个在Torch中提供了上下文管理来处理,个人喜欢使用上下文管理器,用来管理作用在Tensor上的各种运算操作跟踪。
# data的不安全说明
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
t.requires_grad=True
print(t.data)
print(t.data.requires_grad)
t.data[0,0] =88 # 这种修改在运行时可能导致致命错误
print(t)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
False
tensor([[88., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]], requires_grad=True)
### detach的安全说明(修改的时候直接检测,就是不准修改了,这样安全)
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
t.requires_grad=True
print(t.detach())
print(t.detach().requires_grad)
t.detach()[0,0] =88 # 这种修改在运行时可能导致致命错误
print(t)
t.detach().resize_(3,3)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
False
tensor([[88., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]], requires_grad=True)
tensor([[88., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]])
# data的不安全说明
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
t.requires_grad=True
y = t.sigmoid()
y_ = y.sum()
r = y_.backward(retain_graph=True) # backward本身不会返回值 r = Nones
print(t.grad)
# 修改数据
y.data[0,0]=88 # 不检测错误,但可能已经有错误(不允许在计算中途修改)
y_.backward() # backward本身不会返回值 r = Nones
print(t.grad)
print(t.data[0,0])
tensor([[1.9661e-01, 1.0499e-01, 4.5177e-02],
[1.7663e-02, 6.6480e-03, 2.4665e-03],
[9.1017e-04, 3.3522e-04, 1.2337e-04]])
tensor([[-7.6558e+03, 2.0999e-01, 9.0353e-02],
[ 3.5325e-02, 1.3296e-02, 4.9329e-03],
[ 1.8203e-03, 6.7045e-04, 2.4673e-04]])
tensor(1.)
# data的不安全说明
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
t.requires_grad=True
y = t.sigmoid()
y_ = y.sum()
r = y_.backward(retain_graph=True) # backward本身不会返回值 r = Nones
print(t.grad)
# 修改数据
y.detach()[0,0]=88 # 检测错误,修改数据就会报错,这样最终结果安全。
y_.backward() # backward本身不会返回值 r = Nones
print(t.grad)
print(t.data[0,0])
tensor([[1.9661e-01, 1.0499e-01, 4.5177e-02],
[1.7663e-02, 6.6480e-03, 2.4665e-03],
[9.1017e-04, 3.3522e-04, 1.2337e-04]])
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
in ()
15 # 修改数据
16 y.detach()[0,0]=88 # 检测错误,修改数据就会报错,这样最终结果安全。
---> 17 y_.backward() # backward本身不会返回值 r = Nones
18 print(t.grad)
19 print(t.data[0,0])
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/torch/tensor.py in backward(self, gradient, retain_graph, create_graph)
116 products. Defaults to ``False``.
117 """
--> 118 torch.autograd.backward(self, gradient, retain_graph, create_graph)
119
120 def register_hook(self, hook):
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/torch/autograd/__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables)
91 Variable._execution_engine.run_backward(
92 tensors, grad_tensors, retain_graph, create_graph,
---> 93 allow_unreachable=True) # allow_unreachable flag
94
95
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [3, 3]], which is output 0 of SigmoidBackward, is at version 1; expected version 0 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).
numpy函数
- 把Tensor转换为Numpy的ndarray格式。
- 这种转换大部分没有意义
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.numpy())
[[1. 2. 3.]
[4. 5. 6.]
[7. 8. 9.]]
storage/storage_offset/storage_type函数
- 可以直接返回Tensor的数据存储对象
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.storage())
print(t.storage_offset())
print(t[1,1].storage_offset())
print(t.storage_type())
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
[torch.FloatStorage of size 9]
0
4
stride函数
- 返货Tensor的对Storage的行列的间隔步长,按照维数指定,维度由0,1,2,...指定
- 0行
- 1列
- 注意:
- 使用负数指定逆序的维数,-1表示最后一个维度,-2就是倒数第二个维度,
- 步长的单位是Storage的元素长度。
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.stride()) # 返回所有维度
print(t.stride(1)) # 返回第二维读步长
print(t.stride(0))
print(t.stride(-1))
print(t.stride(-2))
(3, 1)
1
3
1
3
as_strided函数
- 按照指定的步长返回新的Tensor
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
s = t.as_strided((2, 2), (3, 1), storage_offset=2) # 返回的张量与原来的Storage共享存储空间
print(s)
s[0,0]=88
print(t)
tensor([[3., 4.],
[6., 7.]])
tensor([[ 1., 2., 88.],
[ 4., 5., 6.],
[ 7., 8., 9.]])
view与view_as函数
- as_strided函数特例版本,不需要offset,不需要stride
- 需要view前后的size一样。
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
s = t.view((1,9))
print(s)
tensor([[1., 2., 3., 4., 5., 6., 7., 8., 9.]])
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
v = torch.Tensor(1, 9)
s = t.view_as(v)
print(s)
tensor([[1., 2., 3., 4., 5., 6., 7., 8., 9.]])
to与to_**函数
- to系列函数有:
- to
- dtype 与 device转换
- to_dense/ to_sparse
- 稀疏矩阵与稠密矩阵之间转换(转换的是layout格式)
- to_mkldnn
- cpu加速
- to_list
- 转换为python类型
- to
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.to(dtype=torch.int32, device=torch.device("cpu:0"))) # 还可以使用device,根据cpu个数来,只有一个就只能是0
print(t.to_sparse()) # 稠密矩阵调用这个
# print(t.to_dense()) # 稀疏Tensor就调用这个
print(t.to_mkldnn())
print(t.tolist())
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=torch.int32)
tensor(indices=tensor([[0, 0, 0, 1, 1, 1, 2, 2, 2],
[0, 1, 2, 0, 1, 2, 0, 1, 2]]),
values=tensor([1., 2., 3., 4., 5., 6., 7., 8., 9.]),
size=(3, 3), nnz=9, layout=torch.sparse_coo)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]], layout=torch._mkldnn)
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]
type与type_as函数
- type与type_as主要是类型转换。
- type_as是使用参数的Tensor类型
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.type(torch.float32))
t.type_as(t.type(torch.float32))
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
类型转换系列函数
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.int())
print(t.long())
print(t.byte())
print(t.char())
print(t.short())
print(t.half())
print(t.float())
print(t.double())
print(t.bool())
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=torch.int32)
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=torch.uint8)
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=torch.int8)
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=torch.int16)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]], dtype=torch.float16)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]], dtype=torch.float64)
tensor([[True, True, True],
[True, True, True],
[True, True, True]])
reshape与resize函数
- 这个系列函数有:
- reshape:不改变Tensor的元素个数
- reshape_as:使用已知Tensor作为参照
- resize:改变元素个数
- resize_as_:使用已知Tensor作为参照
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.reshape(1, 9))
print(t.resize_(2, 2)) # 按照顺序来
tensor([[1., 2., 3., 4., 5., 6., 7., 8., 9.]])
tensor([[1., 2.],
[3., 4.]])
data_ptr函数
- 返回第一个元素的地址,一般没有什么意义。
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.data_ptr())
140604559708032
dense_dim与sparse_dim函数
- 返回稀疏与稠密矩阵维度
- 这两个函数都是稀疏矩阵使用的。
import torch
t = torch.Tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(t.to_sparse().sparse_dim())
print(t.to_sparse().dense_dim())
2
0
element_size函数
- 元素大小
import torch
t1 = torch.LongTensor(2, 3)
t2 = torch.DoubleTensor(3, 2)
print(t1.element_size(), t2.element_size())
8 8
get_device函数
- 获取设备
- 对CPU来说,应该抛出异常
import torch
t1 = torch.LongTensor(2, 3)
print(t1.cpu().get_device()) # 返回-1,其实应该是抛出异常。
print(t1.get_device()) # 返回-1,其实应该是抛出异常。
-1
nelement函数
- 返回所有元素个数
import torch
t1 = torch.LongTensor(2, 3)
print(t1.nelement())
6
flatten函数
- 与numpy的flat函数一样,把Tensor的layout从多维变成一维。
import torch
t1 = torch.LongTensor(2, 3)
print(t1.flatten())
tensor([0, 0, 0, 0, 0, 0])
dequantize函数
- Tensor去量化
- 量化是一种离散化的技术,可以用于数据压缩等。
import torch
t1 = torch.FloatTensor([0.0, 10.50, 11.50, 11.45]) # 被离散化
print(t1.is_quantized)
False
# 线性量化函数,这个函数在卷积运算中使用。
q_t1= torch.quantize_linear(t1, 0.2, 0, torch.qint8)
print(q_t1.is_quantized)
print(q_t1.data)
print(q_t1.dequantize())
print(q_t1.dequantize().is_quantized)
True
tensor([ 0.0000, 10.4000, 11.6000, 11.4000], size=(4,), dtype=torch.qint8,
scale=0.2, zero_point=0)
tensor([ 0.0000, 10.4000, 11.6000, 11.4000])
False
values函数
- 返回稀疏Tensor的数据;
- 矩阵类型必须是coalesced 稀疏矩阵(聚接后的稀疏矩阵的值)
import torch
i = torch.tensor(
[[0, 1, 1],
[2, 0, 2]])
v = torch.tensor([3, 4, 5], dtype=torch.float32)
sp = torch.sparse_coo_tensor(i, v)
print(sp.coalesce().values())
tensor([3., 4., 5.])
indices函数
- 这个函数只争对torch.sparse_coo布局的稀疏矩阵。
- 返回值得索引,见values函数
import torch
i = torch.tensor(
[[0, 1, 1],
[2, 0, 2]])
v = torch.tensor([3, 4, 5], dtype=torch.float32)
sp = torch.sparse_coo_tensor(i, v)
print(sp.coalesce().indices())
tensor([[0, 1, 1],
[2, 0, 2]])
squeeze_/squeeze与unsqueeze/unsqueeze_函数
- 矩阵降维(减少维数)
- 前提是只有一个元素的向量,可以直接转化为标量,从而实现降维。
- 带后缀下划线的函数,影响被操作的Tensor,同时返回操作后的Tensor。
import torch
t1 = torch.Tensor([
[2],[3],[4]
])
print(t1.squeeze())
print(t1.squeeze().unsqueeze(0)) # 指定扩大哪一维
print(t1.squeeze().unsqueeze(1)) # 不能取大于等于2的维度。
tensor([2., 3., 4.])
tensor([[2., 3., 4.]])
tensor([[2.],
[3.],
[4.]])
import torch
t1 = torch.Tensor([
[2],[3],[4]
])
print(t1.squeeze_())
print(t1)
tensor([2., 3., 4.])
tensor([2., 3., 4.])