目录
PyTorch的Tensor,它可以是零维(又称为标量或一个数)、一维、二维及多维的数组。Tensor自称为神经网络界的Numpy,它与Numpy相似,二者可以共享内存,且之间的转换非常方便和高效。不过它们也有不同之处,最大的区别就是Numpy会把ndarray放在CPU中进行加速运算,而由Torch产生的Tensor会放在GPU中进行加速运算(假设当前环境有GPU)。
一、Tensor概述
对Tensor的操作很多,从接口的角度来划分,可以分为两类:
- torch.function:如torch.sum、torch.add等。
- tensor.function:如tensor.view、tensor.add等。
这些操作对大部分Tensor都是等价的,如torch.add(x,y)与x.add(y)等价。在实际使用时,可以根据个人爱好选择。
如果从修改方式的角度来划分,可以分为以下两类:
- 不修改自身数据:如x.add(y),x的数据不变,返回一个新的Tensor。
- 修改自身数据:如x.add_(y)(运行符带下划线后缀),运算结果存在x中,x被修改。
-
import torch
-
-
x = torch.tensor([
1,
2])
-
y = torch.tensor([
3,
4])
-
z = x.add(y)
-
print(z)
-
print(x)
-
x.add_(y)
-
print(x)
-
tensor([4, 6])
-
tensor([1, 2])
-
tensor([4, 6])
二、创建Tensor
创建Tensor的方法有很多,可以从列表或ndarray等类型进行构建,也可根据指定的形状构建。下表是常见的创建Tensor的方法。
举例:
-
# 根据list数据生成Tensor
-
t1 = torch.Tensor([[
1,
2,
3], [
4,
5,
6]])
-
-
# 生成和t1形状一样的张量,元素初始值为1
-
print(torch.ones(t1.size()))
-
# torch.ones_like(t1)
-
-
t2 = torch.linspace(
1,
10,
10)
-
t3 = torch.logspace(
1,
10,
10)
-
print(t2)
-
print(t3)
-
-
t4 = torch.rand(t1.size())
-
print(t4)
-
-
t5 = torch.arange(
1,
10,
1)
-
print(t5)
-
-
import numpy
as np
-
t6 = torch.from_numpy(np.array([
1,
2,
3]))
-
print(t6)
-
tensor([[1., 1., 1.],
-
[1., 1., 1.]])
-
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
-
tensor([1.0000e+01, 1.0000e+02, 1.0000e+03, 1.0000e+04, 1.0000e+05, 1.0000e+06,
-
1.0000e+07, 1.0000e+08, 1.0000e+09, 1.0000e+10])
-
tensor([[0.5150, 0.6570, 0.0718],
-
[0.8774, 0.7458, 0.3008]])
-
tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
-
tensor([1, 2, 3], dtype=torch.int32)
注意torch.Tensor与torch.tensor的几点区别:
- torch.Tensor是torch.empty和torch.tensor之间的一种混合,但是,当传入数据时,torch.Tensor使用全局默认dtype(FloatTensor),而torch.tensor是从数据中推断数据类型。
- torch.tensor(1)返回一个固定值1,而torch.Tensor(1)返回一个大小为1的张量,它是随机初始化的值。
-
t1 = torch.Tensor(
1)
-
t2 = torch.tensor(
1)
-
print(
't1的值{}, t1的数据类型{}'.format(t1, t1.type()))
-
print(
't2的值{}, t2的数据类型{}'.format(t2, t2.type()))
-
t1的值tensor([0.]), t1的数据类型torch.FloatTensor
-
t2的值1, t2的数据类型torch.LongTensor
三、修改Tensor形状
在处理数据、构建网络层等过程中,经常需要了解Tensor的形状、修改Tensor的形状。与修改Numpy的形状类似,修改Tenor的形状也有很多类似函数,下表是tensor常用修改形状的函数。
举例:
-
#生成一个形状为2x3的矩阵
-
x = torch.randn(
2,
3)
-
print(x)
-
print(x.dim())
#查看x的维度
-
print(x.numel())
-
-
#把x变为3x2的矩阵
-
w = x.view(
3,
2)
-
print(w)
-
#把x展平为1维向量
-
y = x.view(
-1)
-
print(y)
-
-
#添加一个维度
-
z = torch.unsqueeze(y,
0)
-
print(z)
-
tensor([[ 0.7251, -0.4526, 0.5193],
-
[ 0.6478, -0.7698, -0.0487]])
-
2
-
6
-
tensor([[ 0.7251, -0.4526],
-
[ 0.5193, 0.6478],
-
[-0.7698, -0.0487]])
-
tensor([ 0.7251, -0.4526, 0.5193, 0.6478, -0.7698, -0.0487])
-
tensor([[ 0.7251, -0.4526, 0.5193, 0.6478, -0.7698, -0.0487]])
torch.view与torch.reshpae的异同:
- reshape()可以由torch.reshape(),也可由torch.Tensor.reshape()调用。但view()只可由torch.Tensor.view()来调用。
- 对于一个将要被view的Tensor,新的size必须与原来的size与stride兼容。否则,在view之前必须调用contiguous()方法。
- 同样也是返回与input数据量相同,但形状不同的Tensor。若满足view的条件,则不会copy,若不满足,则会copy。
- 如果你只想重塑张量,请使用torch.reshape。如果你还关注内存使用情况并希望确保两个张量共享相同的数据,请使用torch.view
四、索引操作
Tensor的索引操作与Numpy类似,一般情况下索引结果与源数据共享内存。从Tensor获取元素除了可以通过索引,也可以借助一些函数,下表是常用选择操作函数。
举例:
-
#设置一个随机种子
-
torch.manual_seed(
100)
-
#生成一个形状为2x3的矩阵
-
x = torch.randn(
2,
3)
-
print(x)
-
#根据索引获取第1行,所有数据
-
print(x[
0, :])
-
#获取最后一列数据
-
print(x[:,
-1])
-
-
#生成是否大于0的Byter张量
-
mask = x>
0
-
print(mask)
-
#获取大于0的值
-
print(torch.masked_select(x, mask))
-
-
-
#获取非0下标,即行,列索引
-
torch.nonzero(mask)
-
#获取指定索引对应的值,输出根据以下规则得到
-
#out[i][j] = input[index[i][j]][j] # if dim == 0
-
#out[i][j] = input[i][index[i][j]] # if dim == 1
-
-
index1 = torch.LongTensor([[
0,
1,
1]])
-
print(torch.gather(x,
0, index1))
-
index2 = torch.LongTensor([[
0,
1,
1], [
1,
1,
1]])
-
a = torch.gather(x,
1, index2)
-
print(a)
-
-
#把a的值返回到一个2x3的0矩阵中
-
z = torch.zeros(
2,
3)
-
print(z.scatter_(
1, index2, a))
-
tensor([[ 0.3607, -0.2859, -0.3938],
-
[ 0.2429, -1.3833, -2.3134]])
-
tensor([ 0.3607, -0.2859, -0.3938])
-
tensor([-0.3938, -2.3134])
-
tensor([[1, 0, 0],
-
[1, 0, 0]], dtype=torch.uint8)
-
tensor([0.3607, 0.2429])
-
tensor([[ 0.3607, -1.3833, -2.3134]])
-
tensor([[ 0.3607, -0.2859, -0.2859],
-
[-1.3833, -1.3833, -1.3833]])
-
tensor([[ 0.3607, -0.2859, 0.0000],
-
[ 0.0000, -1.3833, 0.0000]])
五、广播机制
广播机制是向量运算的重要技巧。PyTorch也支持广播机制,以下通过几个示例进行说明。
-
import torch
-
import numpy
as np
-
-
A = np.arange(
0,
40,
10).reshape(
4,
1)
-
B = np.arange(
0,
3)
-
print(A)
-
print(B)
-
#把ndarray转换为Tensor
-
A1 = torch.from_numpy(A)
#形状为4x1
-
B1 = torch.from_numpy(B)
#形状为3
-
#Tensor自动实现广播
-
C1 = A1+B1
-
print(C1)
-
-
#我们可以根据广播机制,手工进行配置
-
#根据规则1,B1需要向A1看齐,把B变为(1,3)
-
B2 = B1.unsqueeze(
0)
#B2的形状为1x3
-
print(B2)
-
#使用expand函数重复数组,分别的4x3的矩阵
-
A2=A1.expand(
4,
3)
-
B3=B2.expand(
4,
3)
-
-
#然后进行相加,C1与C结果一致
-
C2 = A2 + B3
-
print(C2)
-
[[ 0]
-
[10]
-
[20]
-
[30]]
-
[0 1 2]
-
tensor([[ 0, 1, 2],
-
[10, 11, 12],
-
[20, 21, 22],
-
[30, 31, 32]], dtype=torch.int32)
-
tensor([[0, 1, 2]], dtype=torch.int32)
-
tensor([[ 0, 1, 2],
-
[10, 11, 12],
-
[20, 21, 22],
-
[30, 31, 32]], dtype=torch.int32)
六、逐元素操作
与Numpy一样,Tensor也有逐元素操作(Element-Wise),且操作内容相似,但使用函数可能不尽相同。大部分数学运算都属于逐元素操作,其输入与输出的形状相同。下表是常见的逐元素操作。
举例:
-
t = torch.randn(
1,
3)
-
print(t)
-
t1 = torch.randn(
3,
1)
-
t2 = torch.randn(
1,
3)
-
#t+0.1*(t1/t2)
-
print(torch.addcdiv(t,
0.1, t1, t2))
-
-
#计算sigmoid
-
print(torch.sigmoid(t))
-
#将t限制在[0,1]之间
-
print(torch.clamp(t,
0,
1))
-
#t+2进行就地运算
-
t.add_(
2)
-
print(t)
-
tensor([[-0.2586, -0.2510, 0.4770]])
-
tensor([[-0.3861, -0.1857, 0.6969],
-
[-0.3915, -0.1830, 0.7061],
-
[-0.1649, -0.2989, 0.3154]])
-
tensor([[0.4357, 0.4376, 0.6170]])
-
tensor([[0.0000, 0.0000, 0.4770]])
-
tensor([[1.7414, 1.7490, 2.4770]])
这些操作均会创建新的Tensor,如果需要就地操作,可以使用这些方法的下划线版本,例如abs_。
七、归并操作
归并操作顾名思义,就是对输入进行归并或合计等操作,这类操作的输入输出形状一般并不相同,而且往往是输入大于输出形状。归并操作可以对整个Tensor,也可以沿着某个维度进行归并。下表是常见的归并操作。
归并操作一般涉及一个dim参数,指定沿哪个维进行归并。另一个参数是keepdim,说明输出结果中是否保留维度1,缺省情况是False,即不保留。
举例:
-
# 生成一个含6个数的向量
-
a = torch.linspace(
0,
10,
6)
-
#使用view方法,把a变为2x3矩阵
-
a = a.view((
2,
3))
-
print(a)
-
-
#沿y轴方向累加,即dim=0
-
b = a.sum()
-
print(b)
-
#沿y轴方向累加,即dim=0,并保留含1的维度
-
c = a.sum(dim=
0, keepdim=
True)
-
print(c)
-
-
print(torch.cumprod(a,
0))
-
print(torch.cumprod(a,
1))
-
print(torch.cumsum(a,
1))
-
print(torch.mean(a,
1))
-
tensor([[ 0., 2., 4.],
-
[ 6., 8., 10.]])
-
tensor(30.)
-
tensor([[ 6., 10., 14.]])
-
tensor([[ 0., 2., 4.],
-
[ 6., 8., 10.]])
-
tensor([[ 0., 2., 4.],
-
[ 0., 16., 40.]])
-
tensor([[ 0., 0., 0.],
-
[ 6., 48., 480.]])
-
tensor([[ 0., 2., 6.],
-
[ 6., 14., 24.]])
-
tensor([2., 8.])
八、比较操作
比较操作一般是进行逐元素比较,有些是按指定方向比较。下表是常用的比较函数。
举例:
-
x = torch.linspace(
0,
12,
12).view(
4,
3)
-
print(x)
-
#求所有元素的最大值
-
print(torch.max(x))
-
#求y轴方向的最大值
-
print(torch.max(x, dim=
1))
-
#求最大的2个元素
-
print(torch.topk(x,
2, dim=
0))
-
tensor([[ 0.0000, 1.0909, 2.1818],
-
[ 3.2727, 4.3636, 5.4545],
-
[ 6.5455, 7.6364, 8.7273],
-
[ 9.8182, 10.9091, 12.0000]])
-
tensor(12.)
-
torch.return_types.max(
-
values=tensor([ 2.1818, 5.4545, 8.7273, 12.0000]),
-
indices=tensor([2, 2, 2, 2]))
-
torch.return_types.topk(
-
values=tensor([[ 9.8182, 10.9091, 12.0000],
-
[ 6.5455, 7.6364, 8.7273]]),
-
indices=tensor([[3, 3, 3],
-
[2, 2, 2]]))
九、矩阵操作
机器学习和深度学习中存在大量的矩阵运算,常用的算法有两种:一种是逐元素乘法,另外一种是点积乘法。下表是PyTorch中常用的矩阵函数。
【说明】
- Torch的dot与Numpy的dot有点不同,Torch中的dot是对两个为1D张量进行点积运算,Numpy中的dot无此限制。
- mm是对2D的矩阵进行点积,bmm对含batch的3D进行点积运算。
- 转置运算会导致存储空间不连续,需要调用contiguous方法转为连续。
举例:
-
a = torch.tensor([
2,
3])
-
b = torch.tensor([
3,
4])
-
print(torch.dot(a, b))
-
-
x = torch.randint(
10, (
2,
3))
-
y = torch.randint(
6, (
3,
4))
-
print(x)
-
print(x.t())
-
print(y)
-
print(torch.mm(x, y))
-
-
x = torch.randint(
10, (
2,
2,
3))
-
y = torch.randint(
6, (
2,
3,
4))
-
print(torch.bmm(x, y))
-
tensor(18)
-
tensor([[6, 5, 4],
-
[1, 1, 3]])
-
tensor([[6, 1],
-
[5, 1],
-
[4, 3]])
-
tensor([[3, 2, 5, 4],
-
[2, 1, 1, 0],
-
[4, 4, 2, 1]])
-
tensor([[44, 33, 43, 28],
-
[17, 15, 12, 7]])
-
tensor([[[10, 50, 42, 43],
-
[ 2, 60, 21, 46]],
-
-
[[37, 72, 72, 0],
-
[ 4, 14, 49, 0]]])
十、PyTorch与Numpy比较
PyTorch与Numpy有很多类似的地方,并且有很多相同的操作函数名称,或虽然函数名称不同但含义相同;当然也有一些虽然函数名称相同,但含义不尽相同。有些很容易混淆,下面我们把一些主要的区别进行汇总。
转载:https://blog.csdn.net/weixin_40431584/article/details/105456513