【问题标题】:Munging PyTorch's tensor shape from (C, B, H) to (B, C*H)将 PyTorch 的张量形状从 (C, B, H) 更改为 (B, C*H)
【发布时间】:2019-04-27 23:48:34
【问题描述】:

给定一些神经网络层的形状为 (C, B, H) torch.Size([2, 5, 32]) 的输入张量,其中

  • channels = 2
  • batch_size = 5
  • hidden_size = 32

目标是展平通道并将输入张量操纵为形状 (B, C*H) torch.Size([5, 2 * 32]),其中:

  • batch_size = 5
  • hidden_size = 32 * 2

我已尝试执行以下操作:

import torch

t = torch.rand([2, 5, 32])

# Changed from (channels, batch_size, hidden_size) 
# -> (batch_size, channels, hidden_size)
t = t.permute(1, 0, 2)

# Reshape using view(), where batch_size is t.size(0) 
# and -1 is to flatten the left over values to the other dimension.
z = t.contiguous().view(t.size(0), -1)

print(z.shape)
print(z)

[出]:

torch.Size([5, 64])
tensor([[0.3911, 0.9586, 0.2104, 0.3937, 0.9976, 0.3378, 0.0630, 0.6676, 0.0806,
         0.9311, 0.5219, 0.1697, 0.7442, 0.5162, 0.2555, 0.0826, 0.5502, 0.9700,
         0.3375, 0.5012, 0.9025, 0.8176, 0.1465, 0.1848, 0.3460, 0.9999, 0.7892,
         0.7577, 0.6615, 0.2620, 0.6868, 0.2003, 0.4840, 0.8354, 0.9253, 0.3172,
         0.9516, 0.8962, 0.1272, 0.2268, 0.6510, 0.5166, 0.6772, 0.9616, 0.9826,
         0.5254, 0.9191, 0.4378, 0.7048, 0.8808, 0.0299, 0.1102, 0.9710, 0.8714,
         0.7256, 0.9684, 0.6117, 0.1957, 0.8663, 0.4742, 0.2843, 0.6548, 0.9592,
         0.1559],
        [0.2333, 0.0858, 0.5284, 0.2965, 0.3863, 0.3370, 0.6940, 0.3387, 0.3513,
         0.1022, 0.3731, 0.3575, 0.7095, 0.0053, 0.7024, 0.4091, 0.3289, 0.5808,
         0.5640, 0.8847, 0.7584, 0.8878, 0.9873, 0.0525, 0.7731, 0.2501, 0.9926,
         0.5226, 0.0925, 0.0300, 0.4176, 0.0456, 0.4643, 0.4497, 0.5920, 0.9519,
         0.6647, 0.2379, 0.4927, 0.9666, 0.1675, 0.9887, 0.7741, 0.5668, 0.7376,
         0.4452, 0.7449, 0.1298, 0.9065, 0.3561, 0.5813, 0.1439, 0.2115, 0.5874,
         0.2038, 0.1066, 0.3843, 0.6179, 0.8321, 0.9428, 0.1067, 0.5045, 0.9324,
         0.3326],
        [0.6556, 0.1479, 0.9288, 0.9238, 0.1324, 0.0718, 0.6620, 0.2659, 0.7162,
         0.7559, 0.7564, 0.2120, 0.3943, 0.9497, 0.7520, 0.8455, 0.4444, 0.4708,
         0.8371, 0.6365, 0.3616, 0.0326, 0.1581, 0.4973, 0.6701, 0.9245, 0.8274,
         0.3464, 0.7044, 0.5376, 0.0441, 0.5210, 0.8603, 0.7396, 0.2544, 0.3514,
         0.5686, 0.3283, 0.7248, 0.4303, 0.9531, 0.5587, 0.8703, 0.1585, 0.9161,
         0.9043, 0.9778, 0.4489, 0.9463, 0.8655, 0.5576, 0.1135, 0.1268, 0.3424,
         0.1504, 0.2265, 0.1734, 0.1872, 0.3995, 0.1191, 0.0532, 0.6109, 0.1662,
         0.6937],
        [0.6342, 0.1922, 0.1758, 0.4625, 0.7654, 0.6509, 0.2908, 0.1546, 0.4768,
         0.3779, 0.2490, 0.0086, 0.6170, 0.5425, 0.6953, 0.4730, 0.5834, 0.8326,
         0.0165, 0.8236, 0.0023, 0.7479, 0.5621, 0.9894, 0.5957, 0.0857, 0.6087,
         0.5667, 0.5478, 0.8197, 0.9228, 0.7329, 0.4434, 0.5894, 0.9860, 0.6133,
         0.2395, 0.4718, 0.8830, 0.6361, 0.6104, 0.6630, 0.5084, 0.7604, 0.7591,
         0.3601, 0.6888, 0.6767, 0.9178, 0.5291, 0.0591, 0.4320, 0.7875, 0.5038,
         0.4419, 0.0319, 0.3719, 0.5843, 0.0334, 0.3525, 0.0023, 0.1205, 0.4040,
         0.7908],
        [0.0989, 0.8436, 0.0425, 0.6247, 0.6091, 0.4778, 0.2692, 0.4785, 0.9217,
         0.9604, 0.6355, 0.4686, 0.9414, 0.7722, 0.8013, 0.1660, 0.6578, 0.6414,
         0.6814, 0.6212, 0.4124, 0.7102, 0.7416, 0.7404, 0.9842, 0.6542, 0.0106,
         0.3826, 0.5529, 0.8079, 0.9855, 0.3012, 0.2341, 0.9353, 0.6597, 0.7177,
         0.8214, 0.1438, 0.4729, 0.6747, 0.9310, 0.4167, 0.3689, 0.8464, 0.9395,
         0.9407, 0.8419, 0.5486, 0.1786, 0.1423, 0.9900, 0.9365, 0.3996, 0.1862,
         0.6232, 0.7547, 0.7779, 0.4767, 0.6218, 0.9079, 0.6153, 0.1488, 0.5960,
         0.4015]])

虽然permute() + view() 实现了所需的输出,但还有其他方法可以执行相同的操作吗?有没有更好的方法可以直接rehape而无需先置换形状的顺序?

【问题讨论】:

  • 如果您了解为什么需要t.contiguous(),那么您应该了解为什么需要permuteview。不,这里没有捷径,你需要它们,只有重塑 t 不会做你所期望的,除非先置换。

标签: python numpy reshape pytorch tensor


【解决方案1】:

让我们看看“幕后”,看看为什么一个人必须同时拥有permute/transposeview 才能从C-B-@ 987654331@转B-C*H:

张量的元素作为长连续向量存储在内存中。例如,如果你看一个 2-3-4 张量,它有 24 个元素存储在内存中的 24 个连续位置。这个张量还有一个“标题”,告​​诉 pytorch 将这 24 个值视为 2×3×4 张量。这是通过存储张量的size 以及“步幅”来完成的:为了沿着每个维度到达下一个元素,需要跳跃的“步幅”是多少。在我们的示例中,size=(2,3,4)strides=(12, 4, 1)(您可以自己查看,也可以查看更多相关信息here)。

现在,如果您只想将size 更改为2-(3*4),则无需移动内存中张量的任何项,只需更新张量的“标题”即可。通过设置size=(2, 12)strides=(12, 1) 你就完成了!

或者,如果你想将张量“转置”到3-2-4,那就有点棘手了,但你仍然可以通过操纵步幅来做到这一点。设置 size=(3, 2, 4)strides=(4, 12, 1) 可以准确地提供您想要的,而无需移动内存中的任何真实张量元素。

然而,一旦你操纵了步幅,你就不能轻易改变张量的大小——因为现在你需要为一个(或多个)维度设置两个不同的“步幅”值。这就是为什么此时您必须致电contiguous()

总结
如果你想从形状(C, B, H) 移动到(B, C*H),你必须有permutecontiguousview 操作,否则你只是打乱你的张量的条目。

2-3-4张量的小例子:

a = 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

如果你只是改变你得到的张量的view

a.view(3,8) 
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23]])

不是你想要的!
你需要有

a.permute(1,0,2).contiguous().view(3, 8)
array([[ 0,  1,  2,  3, 12, 13, 14, 15],
       [ 4,  5,  6,  7, 16, 17, 18, 19],
       [ 8,  9, 10, 11, 20, 21, 22, 23]])

【讨论】:

    【解决方案2】:

    Einops 允许在一行(可读)中进行此类元素重新排列

    from einops import rearrange
    import torch
    
    t = torch.rand([2, 5, 32])
    y = rearrange(t, 'c b h -> b (c h)')
    y.shape  # prints torch.Size([5, 64])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 2019-12-19
      • 2020-11-18
      • 1970-01-01
      相关资源
      最近更新 更多