pytorch基础实践2

tensor操作

Reshape 操作

> import torch
> t = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32)

张量 t 的 shape 或者 size 为:

>t.size()
torch.Size([3, 4])
> t.shape
torch.Size([3, 4])

reshape前后元素总量不变,查看一个张量中的元素总量可以用以下两种方法:

  • 计算各个 tensor 各个 axis 上的长度乘积:

> torch.tensor(t.shape).prod()
tensor(12)

  • 或者直接用 numel() 函数(number of elements的缩写):

>t.numel()
12

二阶张量 t 中一共有12个元素,那么对t进行 reshape 可能的结果是将 3x4 改成 1x12、2x6、3x4、4x3、6x2、12x1,当然也可以用 t.reshape(2,2,3) 这样的方式 reshape 为 2x2x3 的三阶张量。

Flatten 操作

把一个n阶的张量变形为只有一行的一阶张量,即变为 array 类型。这个操作通常在CNN中由卷积层传递到全连接层的时候发生。卷积层的输出是很多的 feature map,每一个 feature map 都是一个二阶张量,但是全连接层的输入只能是数组,因此需要把卷积层输出的高阶张量压平变为一维数组,才能输入全连接层。

对二阶张量进行 flatten 的操作,是第一行数据不动,将第二行数据拼接至第一行数据末尾,再将第三行数据拼接到第二行数据的末尾,以此类推。

采用 reshape() 函数对张量 t 进行变换:

>print(t.reshape([1,12]))
> print(t.reshape([1,12]).shape)
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])

输出的张量有两层中括号,而且 shape 为“[1, 12]”,表明其在计算机中存储的形式仍然为二阶张量,只不过有一个 axis 的长度为 1 而已。要进一步将其转为一阶张量,需要用到 squeeze() 函数

> print(t.reshape([1,12]).squeeze())
>print(t.reshape([1,12]).squeeze().shape)
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
torch.Size([12])

squeeze() 函数的作用就是去掉高阶张量中所有长度为 1 的 axes,使高阶张量降低阶数为低阶张量。

类似的还有 unsqueeze() 函数,为张量添加一个长度为1的 axis,相当于 squeeze() 的逆操作:

> print(t.reshape([1,12]).squeeze().unsqueeze(dim=0))
>print(t.reshape([1,12]).squeeze().unsqueeze(dim=0).shape)
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])

定义一个 flatten() 函数包含上述 reshape 和 squeeze 两步:

def flatten(t):
    t = t.reshape(1, -1)
    t = t.squeeze()
    return t

reshape() 函数中的第二个参数填写了“-1”,是让计算机自己根据张量中的元素数量决定一维数组的长度。可以验证:

> flatten(t)
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])

Concatenating 操作(级联)

两个二阶张量:

>t1 = torch.tensor([
[1,2],
[3,4] ])
> t2 = torch.tensor([
[5,6],
[7,8] ])

使用 cat() 函数同时指定拼接的方向(维数),可以得到一个拼接后的新张量。
沿第一个维度(行)拼接为4行2列:

> torch.cat((t1, t2), dim=0)
tensor([
[1, 2],
[3, 4],
[5, 6],
[7, 8]])

沿第二个维度(列)拼接为2行4列:

> torch.cat((t1, t2), dim=1)
tensor([
[1, 2, 5, 6],
[3, 4, 7, 8]])

高阶张量的flatten

一次输入CNN的是不是一张而是一批图像,输入的是一个具有 [B, C, H, W] 形状的四阶张量。如果直接对这个张量进行展平,会把所有图像的所有颜色通道(或者 feature map)全都混在一起。

我们要做的其实只是把 [B, C, H, W] 这个四阶张量的 H 和 W 两个维度展平。

创建3个 4x4 的二阶张量代表3张 4x4 的图片,数字 i 只属于第 i 张图片:

>t1 = torch.tensor([
[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1] ])

>t2 = torch.tensor([
[2,2,2,2],
[2,2,2,2],
[2,2,2,2],
[2,2,2,2]
])

>t3 = torch.tensor([
[3,3,3,3],
[3,3,3,3],
[3,3,3,3],
[3,3,3,3]
])

将这三个二阶张量堆栈起来组成一个 batch,可以用 torch.stack() 函数:

>t = torch.stack((t1, t2, t3))
>t.shape				
 
torch.Size([3, 4, 4])

此时还缺一个中间的颜色通道维度,采用 reshape() 函数来添加:

> t = t.reshape(3,1,4,4)
>t

创建的四阶张量 t 为(注释标明了每一个维度的含义):

tensor(
# Batch
[   # Channel
    [   # Height
        [   # Width
            [1, 1, 1, 1],
            [1, 1, 1, 1],
            [1, 1, 1, 1],
            [1, 1, 1, 1]
        ]
    ],
    [
        [
            [2, 2, 2, 2],
            [2, 2, 2, 2],
            [2, 2, 2, 2],
            [2, 2, 2, 2]
        ]
    ],
    [
        [
            [3, 3, 3, 3],
            [3, 3, 3, 3],
            [3, 3, 3, 3],
            [3, 3, 3, 3]
        ]
    ]
])

因为灰度图片只有一个颜色通道,所以要把 [B, C, H, W] 中从第二个维度起的后三个维度 C、H、W 都展平,采用PyTorch自带的 torch.flatten() 添加一个 start_dim=1 的参数即可:

> t.flatten(start_dim=1).shape
torch.Size([3, 16])

>t.flatten(start_dim=1)
tensor([
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
])

输出结果保留了 batch 的维度,而把后面的维度都展平了,这样同一 batch 中的不同图片数据就不会混在一起了。

对于有三个颜色通道的 RGB 图片,不同的颜色通道(或者 feature map)也不应该混淆在一起,这时候把 torch.flatten() 函数的参数改为 start_dim=2 即可。

element-wise operations

corresponding elements 是指在张量中的位置相同,就是在各个 axis 上的索引编号相同的元素。

> t1 = torch.tensor([
   	[1,2],
   	[3,4]
], dtype=torch.float32)

>t2 = torch.tensor([
	[9,8],
	[7,6]
], dtype=torch.float32)

1 和 9 就是 corresponding elements,因为索引的 indexes 是一样的:

> t1[0][0]
tensor(1.)

> t2[0][0]
tensor(9.) 

所有的 element-wise operations 都只能在具有相同 shape 的张量之间进行

Arithmetic Operations(算术操作)

> t1 + t2
tensor([[10., 10.],
        [10., 10.]])

> t1 - t2
tensor([[-8., -6.],
        [-4., -2.]])

> t1 * t2
tensor([[ 9., 16.],
        [21., 24.]])

> t1 / t2
tensor([[0.1111, 0.2500],
        [0.4286, 0.6667]])

PyTorch中张量间的加减乘除四则运算都是 element-wise 的。

> print(t1 + 2)
  tensor([[3., 4.],
		  [5., 6.]])
			
> print(t1 - 2)
  tensor([[-1.,  0.],
		  [ 1.,  2.]])
			
> print(t1 * 2)
  tensor([[2., 4.],
		  [6., 8.]])
			
> print(t1 / 2)
  tensor([[0.5000, 1.0000],
		  [1.5000, 2.0000]])

用函数命令来运算,结果是一样的:

> print(t1.add(2))

> print(t1.sub(2))

> print(t1.mul(2))

> print(t1.div(2))

但 t1 是一个 2x2 的二阶张量,而数 2 是一个零阶的张量,没有 shape,和之前说的 element-wise operations 需要在 shape 一样的张量之间进行不相符。

Broadcasting Tensors(广播机制)

Broadcasting tensor 就是把两个 shape 不一样的 tensor 匹配成 shape 一样的 sensor 的过程,匹配的方法是将 shape 小的 tensor 进行复制和拼接,使小的 tensor 变成和大的 tensor 具有一样的 shape。这是PyTorch在 shape 不同的张量之间进行操作之前会进行的一个步骤,即:

>np.broadcast_to(2, t1.shape)
array([[2, 2],
       [2, 2]])

对于不同 shape 的 tensor 之间的操作:

> t1 = torch.tensor([
    [1,1],
    [1,1]
], dtype=torch.float32)
> t1.shape
torch.Size([2, 2])

> t2 = torch.tensor([2,4], dtype=torch.float32)
> t2.shape
torch.Size([2])

t1 与 t2 相加时,PyTorch对 t2 进行了 broadcast 以匹配 t1 的 shape:

> np.broadcast_to(t2.numpy(), t1.shape)
array([[2., 4.],
       [2., 4.]], dtype=float32)

> t1 + t2
tensor([[3., 5.],
        [3., 5.]])

Comparison Operations

比较操作是两个张量之间按元素进行比较,返回值的数据类型是 torch.bool(布尔运算值),即 True 和 False。

> t = torch.tensor([
    [0,5,0],
    [6,0,7],
    [0,8,0]
], dtype=torch.float32)

各项比较操作分别为:

> t.eq(0)    # equal to 判断是否相等
tensor([[True, False, True],
        [False, True, False],
        [True, False, True]])

> t.ge(0)    # greater than or equal to 大于等于
tensor([[True, True, True],
        [True, True, True],
        [True, True, True]])

> t.gt(0)    # greater than 大于
tensor([[False, True, False],
        [True, False, True],
        [False, True, False]])

> t.lt(0)    # less than 小于
tensor([[False, False, False],
        [False, False, False],
        [False, False, False]])

> t.le(7)    # less than or equal to 小于等于
tensor([[True, True, True],
        [True, True, True],
        [True, False, True]])

PyTorch也是先对 number 类型的零维张量进行了 broadcasting 之后才进行比较操作。

Element-wise Operations using Functions

> t.abs()    # absolute 取绝对值
tensor([[0., 5., 0.],
        [6., 0., 7.],
        [0., 8., 0.]])

> t.sqrt()    # squrt root 求平方根
tensor([[0.0000, 2.2361, 0.0000],
        [2.4495, 0.0000, 2.6458],
        [0.0000, 2.8284, 0.0000]])

> t.neg()    # negative 取负
tensor([[-0., -5., -0.],
        [-6., -0., -7.],
        [-0., -8., -0.]])

> t.neg().abs()    # 先取负再取绝对值
tensor([[0., 5., 0.],
        [6., 0., 7.],
        [0., 8., 0.]])

Reduction Operations

reduction operations 让我们能够对单个张量内的元素执行运算。

以一个 3x3 二阶张量举例:

> t = torch.tensor([
    [0,1,0],
    [2,0,2],
    [0,3,0]
], dtype=torch.float32)

reduction operations:

> t.sum()    # 求元素之和
tensor(8.)

> t.prod()    # 求元素乘积
tensor(0.)

> t.mean()    # 求元素平均值
tensor(.8889)

> t.std()    # 求元素标准差
tensor(1.1667)

之所以称之为 reduction operations 是因为他们的输出从原来输入的二阶张量变为了零阶张量,张量中总的元素数量减少了:

> t.numel()
9

> t.sum().numel()
1

> t.sum().numel() < t.numel()
True

可以指定 reduction operations 作用的 axis:

> t = torch.tensor([
    [1,1,1,1],
    [2,2,2,2],
    [3,3,3,3]
], dtype=torch.float32)

> t.sum(dim=0)
tensor([6., 6., 6., 6.])

> t.sum(dim=0).shape
torch.Size([4])

> t.sum(dim=1)
tensor([ 4.,  8., 12.])

> t.sum(dim=1).shape
torch.Size([3])

Argmax tensor reduction operation

t = torch.tensor([
    [1,0,0,2],
    [0,3,3,0],
    [4,0,0,5]
], dtype=torch.float32)

> t.max()
tensor(5.)

> t.argmax()
tensor(11)

argmax() 函数输出的只有一个零阶张量,是因为PyTorch先对高阶张量进行了 flatten() 操作,然后才去比较大小:

> t.flatten()
tensor([1., 0., 0., 2., 0., 3., 3., 0., 4., 0., 0., 5.])

> t.flatten().argmax()
tensor(11)

argmax() 函数也可以按指定的 axis 方向进行操作:

> t.max(dim=0)
(tensor([4., 3., 3., 5.]), tensor([2, 1, 1, 2]))

> t.argmax(dim=0)
tensor([2, 1, 1, 2])

> t.max(dim=1)
(tensor([2., 3., 5.]), tensor([3, 1, 3]))

> t.argmax(dim=1)
tensor([3, 1, 3])

max() 函数会返回两个张量,第一个是按指定的 axis 方向找出的最大元素值,第二个是这些最大元素在指定的 axis 方向上的索引号.

argmax() 在神经网络中的用处在于:图像分类神经网络的输出层是一个包含 k 个元素的一阶张量(其中 k 是图片的类别数),张量中的元素是对应各个类别的 prediction values(翻译成置信概率?),所有的 prediction values 之和为 1。我们会取置信概率最大的那一种作为最终预测的类别(比如 a 类预测值 0.05,b 类 0.95,那就认为是 b 类)。这样就需要使用 argmax() 函数来找出最大的 prediction value 对应的是哪一类。

Access Operations

求张量元素的平均值:

> t = torch.tensor([
    [1,2,3],
    [4,5,6],
    [7,8,9]
], dtype=torch.float32)

> t.mean()
tensor(5.)

> type(t.mean())
torch.Tensor

输出的数据类型仍然是一个张量(虽然是零阶张量),可以通过 item() 函数提取出零阶张量中的数据:

> t.mean().item()
5.0

> type(t.mean().item())
float

对于输出为含有多个元素的张量时,可以采用 tolist() 将其转为 Python list:

> t.mean(dim=0).tolist()
[4.0, 5.0, 6.0]	

或者用 numpy() 将其装换为 numpy 数组:

> t.mean(dim=0).numpy()
array([4., 5., 6.], dtype=float32)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值