Tensor——张量
tensor可以看作一个多维数组,标量是0维张量,向量是1维张量,矩阵看作二维张量
torch.Tencer是存储和变换数据的主要工具,类似于Numpy,而Tensor提供GPU计算和自动求梯度等更多的功能,因而更适合深度学习任务
创建Tensor
使用 empty 创建一个未初始化的tensor
# 导入torch
import torch
# 创建一个5x3的 未初始化 的Tensor
tensor = torch.empty(5,3)
tensor
tensor([[9.2755e-39, 1.0561e-38, 6.6123e-39],
[8.4490e-39, 9.6428e-39, 1.1112e-38],
[9.5511e-39, 1.0102e-38, 1.0286e-38],
[1.0194e-38, 9.6429e-39, 9.2755e-39],
[9.1837e-39, 9.3674e-39, 1.0745e-38]])
使用 rand 创建一个随机初始化的tensor
# 创建一个5x3的 随机初始化 的Tensor
tensor = torch.rand(5,3)
tensor
tensor([[0.0382, 0.7174, 0.2924],
[0.2784, 0.7799, 0.7862],
[0.2426, 0.1755, 0.1579],
[0.5902, 0.1025, 0.8453],
[0.5292, 0.4720, 0.2294]])
使用 zeros 创建一个全0的tensor
# 创建一个5x3的 全部初始化为0 的Tensor
tensor = torch.zeros(5,3)
tensor
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
使用 ones 创建一个全1的tensor
tensor = torch.ones(5,3)
tensor
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
使用rand()创建一个均匀分布的tensor
tensor = torch.rand(5,3)
tensor
tensor([[0.2607, 0.3371, 0.2242],
[0.3591, 0.1004, 0.8683],
[0.1021, 0.5884, 0.6595],
[0.4889, 0.6538, 0.5106],
[0.0143, 0.8322, 0.0849]])
使用randn()创建一个标准分布的tensor
tensor = torch.randn(5,3)
tensor
tensor([[ 0.8349, 0.4898, 0.6729],
[-0.2988, -0.4673, -1.2728],
[-0.4344, -0.8004, -1.0311],
[-0.7575, -0.1988, -1.2069],
[-1.0390, 0.4296, -0.6993]])
使用 eye 创建一个对角线为1其余为0的tensor
tensor = torch.eye(3,3)
tensor
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
使用tensor直接根据数据创建 tensor
# 直接创建一个元素为5.5 和 3的张量
tensor = torch.tensor([5.5,3])
tensor
tensor([5.5000, 3.0000])
new_ones()
# new_ones 在已经存在的张量(例如x)的基础上快速创建一个新的张量(例如x2),
# 那么x2具有和x1相同的数据类型(torch.dtype)和设备(torch.device)
# 默认重用输入`Tensor`的一些属性,例如数据类型,除非自定义数据类型
print('original tensor:',tensor.dtype,tensor.device)
print('tensor:',tensor)
tensor1 = tensor.new_ones(5,3,dtype=torch.float64)
print('tensor1:',tensor1.dtype,tensor1.device)
print('tensor1:',tensor1)
# 自定义数据类型为float32
tensor2 = tensor.new_ones(5,3,dtype = torch.float32)
print('tensor2:',tensor2.dtype,tensor2.device)
print('tensor2:',tensor2)
original tensor: torch.float32 cpu
tensor: tensor([5.5000, 3.0000])
tensor1: torch.float64 cpu
tensor1: tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor2: torch.float32 cpu
tensor2: tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
randn_like(intput_tensor)
# 返回一个和输入的张量(此处输入tensor2 --> (5,3))大小相同的张量,填充了来自均值为 0 且方差为 1 的正态分布的随机数
tensor = torch.randn_like(tensor2)
tensor
tensor([[-1.0363, 0.4599, -0.1938],
[ 1.4657, 0.4905, 0.0049],
[ 0.6425, -0.5205, -2.7419],
[ 1.6636, -1.4791, -0.7599],
[ 0.4845, 1.3453, 1.0725]])
获取Tensor形状
使用 shape
tensor.shape # 注意不要写成 tensor.shape()
torch.Size([5, 3])
使用size()
tensor.size()
torch.Size([5, 3])
操作——Tensor的各种常用操作
算术操作
加法操作
# 两个测试tensor
t1 = torch.rand(5,3)
print(t1)
t2 = torch.rand(5,3)
print(t2)
tensor([[0.7700, 0.2527, 0.7854],
[0.2529, 0.4505, 0.1423],
[0.8534, 0.0141, 0.6260],
[0.7263, 0.5366, 0.2349],
[0.6167, 0.3870, 0.4339]])
tensor([[0.4644, 0.0766, 0.7044],
[0.3266, 0.8935, 0.5278],
[0.7959, 0.3624, 0.6834],
[0.5171, 0.6444, 0.5704],
[0.4169, 0.0182, 0.4759]])
1.直接相加
t1+t2
tensor([[1.2344, 0.3293, 1.4899],
[0.5795, 1.3440, 0.6700],
[1.6493, 0.3765, 1.3094],
[1.2434, 1.1811, 0.8053],
[1.0337, 0.4052, 0.9098]])
2.使用 add()
torch.add(t1,t2)
tensor([[1.2344, 0.3293, 1.4899],
[0.5795, 1.3440, 0.6700],
[1.6493, 0.3765, 1.3094],
[1.2434, 1.1811, 0.8053],
[1.0337, 0.4052, 0.9098]])
3.使用inplace版本——带下划线后缀的方法
注:PyTorch操作inplace版本都有后缀_
, 例如x.copy_(y), x.t_()
t2.add_(t1)
tensor([[1.2344, 0.3293, 1.4899],
[0.5795, 1.3440, 0.6700],
[1.6493, 0.3765, 1.3094],
[1.2434, 1.1811, 0.8053],
[1.0337, 0.4052, 0.9098]])
索引
可以使用类似NumPy的索引操作来访问Tensor
的一部分,需要注意的是:索引出来的结果与原数据共享内存,也即修改一个,另一个会跟着修改
test_tensor = torch.rand(5,3)
print('test_tensor:\n',test_tensor)
# 获取某一行内容
index_tensor1 = test_tensor[0,:]
index_tensor2 = test_tensor[1,:]
# 获取指定位置的单个元素,例如第2行第2个元素
index_tensor3 = test_tensor[1,1:2] # 索引从0开始,前面的1代表第二行,后面的1:2相当于一个切片操作,获取第2行中的第二个元素
print('index(1,1):',index_tensor3)
print('index_tensor1:',index_tensor1)
print('index_tensor2:',index_tensor2)
index_tensor1 += 1
print('index_tensor1:',index_tensor1)
print('test_tensor[0,:]:',test_tensor[0,:])
test_tensor:
tensor([[0.2057, 0.7814, 0.9296],
[0.3824, 0.0040, 0.3460],
[0.4090, 0.7394, 0.9397],
[0.3415, 0.2694, 0.5211],
[0.7107, 0.7032, 0.1176]])
index(1,1): tensor([0.0040])
index_tensor1: tensor([0.2057, 0.7814, 0.9296])
index_tensor2: tensor([0.3824, 0.0040, 0.3460])
index_tensor1: tensor([1.2057, 1.7814, 1.9296])
test_tensor[0,:]: tensor([1.2057, 1.7814, 1.9296])
改变形状
使用 view() 改变Tensor的形状
注意view()
返回的新Tensor
与源Tensor
虽然可能有不同的size
,但是是共享data
的,也即更改其中的一个,另外一个也会跟着改变。(顾名思义,view仅仅是改变了对这个张量的观察角度,内部数据并未改变)
如果想要返回一个新的副本而不共享数据,可以先使用clone()创造一个副本然后使用view
tensor_copy = test_tensor.clone().view(?)
tensor = torch.rand(5,3) # 原来的tensor 5x3
print('tensor:\n',tensor)
tensor_change = tensor.view(15) # 修改形状 改为一维的15个,1x15
print('tensor_change:\n',tensor_change)
tensor_change2 = tensor.view(-1,5) # -1所指的维度可以通过其他维度推测出来,--> 3x5
print('tensor_change2:\n',tensor_change2)
print(tensor.size(),tensor_change.size(),tensor_change2.size())
tensor:
tensor([[0.2423, 0.9519, 0.9906],
[0.2997, 0.7124, 0.4299],
[0.5868, 0.5261, 0.1628],
[0.0075, 0.1174, 0.5508],
[0.8232, 0.8728, 0.9864]])
tensor_change:
tensor([0.2423, 0.9519, 0.9906, 0.2997, 0.7124, 0.4299, 0.5868, 0.5261, 0.1628,
0.0075, 0.1174, 0.5508, 0.8232, 0.8728, 0.9864])
tensor_change2:
tensor([[0.2423, 0.9519, 0.9906, 0.2997, 0.7124],
[0.4299, 0.5868, 0.5261, 0.1628, 0.0075],
[0.1174, 0.5508, 0.8232, 0.8728, 0.9864]])
torch.Size([5, 3]) torch.Size([15]) torch.Size([3, 5])
使用clone
还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor
item() 可以将一个Tensor标量转换成一个python number
tensor = torch.randn(1)
print(tensor)
tensor.item()
tensor([-1.1877])
-1.1877024173736572
线性函数
trace()——对角线元素之和(矩阵的迹)
tensor = torch.ones(3,3)
print(tensor)
tensor.trace()
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
tensor(3.)
diag()——对角线元素
tensor = torch.rand(3,3)
print(tensor)
tensor.diag()
tensor([[0.0308, 0.7154, 0.0386],
[0.2303, 0.5764, 0.7461],
[0.9273, 0.2662, 0.8488]])
tensor([0.0308, 0.5764, 0.8488])
t()——转置
print(tensor)
tensor.t()
tensor([[0.0308, 0.7154, 0.0386],
[0.2303, 0.5764, 0.7461],
[0.9273, 0.2662, 0.8488]])
tensor([[0.0308, 0.2303, 0.9273],
[0.7154, 0.5764, 0.2662],
[0.0386, 0.7461, 0.8488]])
inverse()——求逆矩阵
print(tensor)
tensor.inverse()
tensor([[0.0308, 0.7154, 0.0386],
[0.2303, 0.5764, 0.7461],
[0.9273, 0.2662, 0.8488]])
tensor([[ 0.8404, -1.7263, 1.4792],
[ 1.4356, -0.0280, -0.0407],
[-1.3684, 1.8948, -0.4252]])
dot() / cross()——内积、外积
注意: dot()只支持一维张量计算
# 注意 dot()只支持一维张量计算
t1 = torch.tensor([3,2])
t2 = torch.tensor([4,1])
print(t1,t2)
print(torch.dot(t1,t2))
# 使用二维张量进行dot()操作会报错如下:
# RuntimeError: 1D tensors expected, but got 2D and 2D tensors
tensor1 = torch.eye(3,3)
tensor2 = torch.ones(3,3)
print(tensor1)
print(tensor2)
# print(torch.dot(tensor1,tensor2)) # 此操作会产生上述错误
tensor([3, 2]) tensor([4, 1])
tensor(14)
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
# cross()
print(torch.cross(tensor1,tensor2))
tensor([[ 0., 1., -1.],
[-1., 0., 1.],
[ 1., -1., 0.]])
广播机制
当对两个形状不同的Tensor
按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个Tensor
形状相同后再按元素运算。
# tensor1 1x2
t1 = torch.arange(1,3).view(1,2)
# tensor2 3x1
t2 = torch.arange(1,4).view(3,1)
print(t1)
print(t2)
t1+t2
tensor([[1, 2]])
tensor([[1],
[2],
[3]])
tensor([[2, 3],
[3, 4],
[4, 5]])
说明: 由于tensor1和tensor2分别是1x2和3x1的,计算tensor1+tensor2,那么tensor1中第一行被广播(复制)到第二三行,变成一个3x2的,tensor2中第一列被广播到第二列变成一个3x2的,那么就变成了两个3x2的按元素相加
广播后的tensor1:[[1,2],[1,2],[1,2]]
广播后的tensor2:[[1,1],[2,2],[3,3]]
Tensor和Numpy转换
用numpy()
和from_numpy()
将Tensor
和NumPy中的数组相互转换。但是需要注意的一点是:
这两个函数所产生的Tensor
和NumPy中的数组共享相同的内存(所以他们之间的转换很快),改变其中一个时另一个也会改变
numpy()——将Tensor转换为Numpy数组
tensor = torch.ones(5)
np = tensor.numpy()
print(tensor,np)
# 对tensor进行+1操作,可以看到numpy内容也发生改变
tensor += 1
print(tensor,np)
# 对numpy进行+1操作,可以看到tensor内容也发生改变
np += 1
print(tensor,np)
tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.]
tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]
tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
from_nump()——将Numpy数组转换为Tensor
import numpy as np
np_test = np.ones(5)
tensor_test = torch.from_numpy(np_test)
print(np_test,tensor_test)
np_test += 1
print(np_test,tensor_test)
tensor_test += 1
print(np_test,tensor_test)
[1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
[3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
torch.tensor()——将NumPy数组转换成Tensor
需要注意的是该方法总是会进行数据拷贝,返回的Tensor
和原来的数据不再共享内存
import numpy as np
np_test = np.ones(5)
tensor_test = torch.tensor(np_test)
print(np_test,tensor_test)
np_test += 1
print(np_test,tensor_test)
tensor_test += 1
print(np_test,tensor_test)
[1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
[2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
Tensor on GPU
使用to() 可以将Tensor在CPU和GPU(需要硬件支持)之间移动
x = torch.tensor([1,2])
if torch.cuda.is_available():
device = torch.device("cuda")
y = torch.ones_like(x,device = device)
x = x.to(device)
z = x+y
print(z)
print(z.to("cpu",torch.double))
tensor([2, 3], device='cuda:0')
tensor([2., 3.], dtype=torch.float64)