创建一个大小为 5*3 未初始化的 Tensor:
x = torch.empty(5,3)
输出为:
tensor([[8.4490e-39, 9.6429e-39, 9.2755e-39],
[1.0286e-38, 9.0919e-39, 8.9082e-39],
[9.2755e-39, 8.4490e-39, 9.2755e-39],
[9.6429e-39, 9.6429e-39, 9.3674e-39],
[1.0469e-38, 1.0102e-38, 9.2755e-39]])
创建一个大小为 5*3 随机初始化的 Tensor:
x = torch.rand(5, 3)
输出为:
tensor([[0.9481, 0.9788, 0.6261],
[0.3213, 0.4617, 0.4188],
[0.1769, 0.4106, 0.5583],
[0.5643, 0.5224, 0.8899],
[0.4977, 0.0276, 0.7517]])
创建一个大小为 5x3 且指定数据类型为 long 型值全 0 的 Tensor:
x = torch.zeros(5, 3, dtype=torch.long)
输出为:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
根据数据直接创建 Tensor:
x = torch.tensor([5.5, 3])
输出为:
tensor([5.5000, 3.0000])
根据现有 Tensor 创建。
x = x.new_ones(5, 3, dtype=torch.float64)
# 返回的tensor默认具有相同的torch.dtype和torch.device
# 得到的x为:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
x = torch.randn_like(x, dtype=torch.float)
# 指定新的数据类型
# 得到的x为:
tensor([[ 0.6035, 0.8110, -0.0451],
[ 0.8797, 1.0482, -0.0445],
[-0.7229, 2.8663, -0.5655],
[ 0.1604, -0.0254, 1.0739],
[ 2.2628, -0.9175, -0.2251]])
获取 Tensor 的形状:
print(x.size())
print(x.shape)
# 输出都为:
torch.Size([5, 3]) # 返回的torch.Size是一个tuple, 支持所有tuple的操作。
函数 | 功能 |
---|---|
Tensor(*sizes) | 基础构造函数 |
tensor(data,) | 类似np.array的构造函数 |
ones(*sizes) | 全1Tensor |
zeros(*sizes) | 全0Tensor |
full(*sizes,data) | 值全为data,大小为sizes |
ones_like(tensor) | 值全为1,大小和指定的tensor相同 |
eye(*sizes) | 对角线为1,其他为0 |
arange(s,e,step) | 从s到e,步长为step |
linspace(s,e,steps) | 从s到e,均匀切分成steps份 |
rand/randn(*sizes) | 均匀/标准分布 |
normal(mean,std)/uniform(from,to) | 正态分布/均匀分布 |
randperm(m) | 随机排列 |
这些创建方法都可以在创建的时候指定数据类型dtype和存放设备device(CPU/GPU)。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x = torch.tensor([1,1,1],dtype=torch.float64, device =device)
两个 Tensor 进行加减乘除算术操作时会自动进行Broadcasting。
加法运算。
还可以指定输出:
result = torch.empty(5, 3)
torch.add(x, y, out=result)
减法运算。
对应元素相乘。
除法运算。
矩阵相乘。
import torch
a = torch.ones(2, 1)
b = torch.ones(1, 2)
print(torch.mm(a, b).shape)
print(torch.matmul(a, b).shape)
print((a @ b).shape)
# 输出:
torch.Size([2, 2])
torch.Size([2, 2])
torch.Size([2, 2])
注意:对于高维的Tensor(dim>2),定义其矩阵乘法仅在最后的两个维度上,要求前面的维度必须保持一致,就像矩阵的索引一样并且运算操作语法只有torch.matmul()。(前面的"矩阵索引维度"如果符合Broadcasting机制,也会自动做广播,然后相乘)
c = torch.rand(4, 3, 28, 64)
d = torch.rand(4, 3, 64, 32)
print(torch.matmul(c, d).shape)
# 输出:
torch.Size([4, 3, 28, 32])
c = torch.rand(4, 3, 28, 64)
d = torch.rand(4, 1, 64, 32)
print(torch.matmul(c, d).shape) # 自动广播
# 输出:
torch.Size([4, 3, 28, 32])
幂运算。
a = torch.full([2, 2], 3, dtype=torch.long)
b = a.pow(2) # 也可以a**2
print(b)
tensor([[9., 9.],
[9., 9.]])
开方运算。
c = b.sqrt() # 也可以a**(0.5)
tensor([[3., 3.],
[3., 3.]])
d = b.rsqrt() # 平方根的倒数
tensor([[0.3333, 0.3333],
[0.3333, 0.3333]])
指数和对数运算。
import torch
a = torch.exp(torch.ones(2, 2)) # 得到2*2的全是e的Tensor
print(a)
print(torch.log(a)) # 取自然对数
tensor([[2.7183, 2.7183],
[2.7183, 2.7183]])
tensor([[1., 1.],
[1., 1.]])
注意 log 是以自然对数为底数的,以2为底的用log2,以10为底的用 log10。
函数 | 功能 |
---|---|
.floor() | 取下 |
.ceil() | 取上 |
.trunc() | 取整数 |
.frac() | 取小数 |
.round() | 四舍五入 |
.max() | 最大值 |
.min() | 最小值 |
.abs() | 绝对值 |
.median() | 平均值 |
.clamp(10) | 裁剪运算,小于10的都变成10 |
.clamp(3, 10) | 裁剪运算,小于3的变成3,大于10的变成10 |
可以使用类似NumPy的索引操作来对Tensor进行索引,需要注意的是:索引出来的结果与原数据共享内存,也即修改一个,另一个会跟着修改。
y = x[0, :]
y += 1
print(y)
print(x[0, :]) # 源tensor也被改了
输出为:
tensor([1.6035, 1.8110, 0.9549])
tensor([1.6035, 1.8110, 0.9549])
一些索引高级函数:
函数 | 功能 |
---|---|
index_select(input, dim, index) | 在指定维度dim上选取,比如选取某些行、某些列 |
masked_select(input, mask) | 例子如上,a[a>0],使用ByteTensor进行选取 |
nonzero(input) | 非0元素的下标 |
gather(input, dim, index) | 根据index,在dim维度上选取数据,输出的size与index一样 |
用 view() 函数来改变 Tensor 的形状。
y = x.view(15)
z = x.view(-1, 5) # -1所指的维度可以根据其他维度的值推出来
print(x.size(), y.size(), z.size())
# 输出
torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
注意:view()返回的新Tensor与源Tensor虽然可能有不同的size,但是是共享data的,也即更改其中的一个,另外一个也会跟着改变。(view仅仅是改变了对这个张量的观察角度,内部数据并未改变。)
如果想返回一个真正新的副本(即不共享data内存):
Pytorch还提供了一个reshape()可以改变形状,但是此函数并不能保证返回的是其拷贝,所以不推荐使用。推荐先用clone创造一个副本然后再使用view。
x_cp = x.clone().view(15)
使用clone还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor。
将一个标量 Tensor 转换成一个 Python number。
x = torch.randn(1)
print(x)
print(x.item())
tensor([2.3466])
2.3466382026672363
PyTorch还支持一些线性函数,具体用法参考官方文档。如下表所示:
函数 | 功能 |
---|---|
trace | 对角线元素之和(矩阵的迹) |
diag | 对角线元素 |
triu/tril | 矩阵的上三角/下三角,可指定偏移量 |
mm/bmm | 矩阵乘法,batch的矩阵乘法 |
addmm/addbmm/addmv/addr/baddbmm… | 矩阵运算 |
t | 转置 |
dot/cross | 内积/外积 |
inverse | 求逆矩阵 |
svd | 奇异值分解 |
当对两个形状不同的Tensor按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个Tensor形状相同后再按元素运算。
用numpy()和from_numpy()可以将Tensor和NumPy中的数组相互转换。但是需要注意的一点是: 这两个函数所产生的的Tensor和NumPy中的数组共享相同的内存(所以他们之间的转换很快),改变其中一个时另一个也会改变。
a = torch.ones(5)
b = a.numpy()
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
此外上面提到还有一个常用的方法就是直接用torch.tensor()将NumPy数组转换成Tensor,需要注意的是该方法总是会进行数据拷贝,返回的Tensor和原来的数据不再共享内存。
用方法to()可以将Tensor在CPU和GPU(需要硬件支持)之间相互移动。
# 以下代码只有在PyTorch GPU版本上才会执行
if torch.cuda.is_available():
device = torch.device("cuda") # GPU
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()还可以同时更改数据类型
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x = torch.tensor([1,1,1,1],dtype=torch.float64, device =device)