张量是基于向量和矩阵的推广,其对应关系如下表所示:
张量维度 | 含义 |
---|---|
0维 | 标量 |
1维 | 向量 |
2维 | 矩阵 |
3维 | 时间序列数据 股价 文本数据 单张彩色图片(RGB) |
其本质是一个数据容器,一些常见数据类型与张量的对应关系如下:
例如,一个图像可以用三个字段表示:
(width, height, channel) = 3D
但是,在机器学习工作中,我们经常要处理不止一张图片或一篇文档——我们要处理一个集合。我们可能有10,000张郁金香的图片,这意味着,我们将用到4D张量:
(batch_size, width, height, channel) = 4D
在PyTorch中, torch.Tensor
是存储和变换数据的主要工具。Tensor 和NumPy的多维数组非常类似,但Tensor 提供GPU计算和自动求梯度等更多功能,使其更加适合深度学习。
方法一:随机初始化矩阵
torch.rand
import torch
x = torch.rand(4, 3)
print(x)
>>tensor([[0.7575, 0.2039, 0.5049],
[0.9079, 0.9716, 0.3000],
[0.6575, 0.4854, 0.1494],
[0.5582, 0.1834, 0.8660]])
方法二:构造全0矩阵
我们可以通过torch.zeros()
构造一个矩阵全为 0,并且通过dtype设置数据类型为 long。除此以外,我们还可以通过torch.zero_()
和torch.zeros_like()
将现有矩阵转换为全0矩阵。
x = torch.zeros(4, 3, dtype=torch.long)
print(x)
>>tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
x = torch.rand(4, 3)
print(torch.zeros_like(x))
>>tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
方法三:直接使用数据构建
x = torch.tensor([5.5, 3])
print(x)
>>tensor([5.5000, 3.0000])
方法四:基于存在的tensor创建tensor
x = torch.ones(4, 3, dtype=torch.double)
# 创建一个全1,数据类型为double的tensor
x = torch.randn_like(x, dtype=torch.float)
# 随机生成tensor并重置数据类型
print(x)
# 结果会有一样的size
# 获取它的维度信息
print(x.size())
print(x.shape)
print("tensor length:"+str(x.shape[0]))
print("tensor width:"+ str(x.shape[1]))
>>tensor([[ 1.4150, -0.4839, -0.2232],
[-1.3060, 0.5646, -0.0978],
[-0.2836, -2.3955, 0.9513],
[-0.0435, -0.0328, -0.2242]])
torch.Size([4, 3])
torch.Size([4, 3])
tensor length:4
tensor width:3
常见的tensor构造方法,如下表所示:
函数 | 功能 |
---|---|
Tensor(sizes) | 基础构造函数 |
tensor(data) | 类似于np.array |
ones(sizes) | 全1 |
zeros(sizes) | 全0 |
eye(sizes) | 对角为1,其余为0 |
arange(s,e,step) | 从s到e,步长为step |
linspace(s,e,steps) | 从s到e,均匀分成step份 |
rand/randn(sizes) | rand是[0,1)均匀分布;randn是服从N(0,1)的正态分布 |
normal(mean,std) | 正态分布(均值为mean,标准差是std) |
randperm(m) | 随机排列 |
# 方式1
y = torch.rand(4, 3)
print(x + y)
# 方式2
print(torch.add(x, y))
# 方式3 in-place,原值修改
y.add_(x)
print(y)
>>tensor([[ 2.2148, -0.0407, 0.7405],
[-0.8507, 1.3077, 0.1737],
[-0.2455, -1.7526, 1.2445],
[ 0.0583, 0.7089, 0.5562]])
tensor([[ 2.2148, -0.0407, 0.7405],
[-0.8507, 1.3077, 0.1737],
[-0.2455, -1.7526, 1.2445],
[ 0.0583, 0.7089, 0.5562]])
tensor([[ 2.2148, -0.0407, 0.7405],
[-0.8507, 1.3077, 0.1737],
[-0.2455, -1.7526, 1.2445],
[ 0.0583, 0.7089, 0.5562]])
x = torch.rand(4,3)
# 取第二列
print(x[:, 1])
>>tensor([0.6757, 0.8069, 0.4068, 0.3438])
需要注意的是:索引出来的结果与原数据共享内存,修改一个,另一个会跟着修改。如果不想修改,可以考虑使用copy()等方法。
y = x[0,:]
print(y)
y += 1
print(y)
print(x[0, :]) # 源tensor也被改了了
>> tensor([0.5997, 0.6757, 0.7756])
tensor([1.5997, 1.6757, 1.7756])
tensor([1.5997, 1.6757, 1.7756])
张量的维度变换常见的方法有torch.view()
和torch.reshape()
,下面我们将介绍第一种方法torch.view()
:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # -1是指这一维的维数由其他维度决定
print(x.size(), y.size(), z.size())
>> torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
注: torch.view() 返回的新tensor与源tensor共享内存(其实是同一个tensor),更改其中的一个,另外一个也会跟着改变。(顾名思义,view()仅仅是改变了对这个张量的观察角度)
x += 1
print(x)
print(y) # 也加了了1
>> tensor([[ 1.7383, 1.4008, 1.4836, -0.5713],
[ 0.9463, 2.5714, -0.0831, 0.4281],
[ 3.1267, -0.2381, 0.9456, 0.1616],
[ 0.4377, -0.0866, 0.6249, -0.7795]])
tensor([ 1.7383, 1.4008, 1.4836, -0.5713, 0.9463, 2.5714, -0.0831, 0.4281,
3.1267, -0.2381, 0.9456, 0.1616, 0.4377, -0.0866, 0.6249, -0.7795])
如果我们希望原张量和变换后的张量互不影响,可以使用第二种方法torch.reshape()
, 同样可以改变张量的形状,但是此函数并不能保证返回的是其拷贝值,所以官方不推荐使用。推荐的方法是我们先用 clone()
创造一个张量副本然后再使用 torch.view()
进行函数维度变换 。
注:使用 clone()
还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源 Tensor 。
如果我们有一个元素 tensor ,我们可以使用 .item() 来获得这个 value,而不获得其他性质:
x = torch.randn(1)
print(x)
print(type(x))
print(x.item())
print(type(x.item()))
>>tensor([-1.3117])
<class 'torch.Tensor'>
-1.3117058277130127
<class 'float'>
PyTorch中的 Tensor 支持超过一百种操作,包括转置、索引、切片、数学运算、线性代数、随机数等等,具体使用方法可参考官方文档。
当对两个形状不同的 Tensor 按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个 Tensor 形状相同后再按元素运算。
x = torch.arange(1, 3).view(1, 2)
print(x)
y = torch.arange(1, 4).view(3, 1)
print(y)
print(x + y)
>>tensor([1, 2])
tensor([[1],
[2],
[3]])
tensor([[2, 3],
[3, 4],
[4, 5]])