NumPy中的一位有效编码

5

我正试图理解一个Python 教程 输出的数值。这些输出并没有按照我能够理解的任何顺序排列。其中特定的Python代码行给我带来了麻烦:

vocab_size = 13   #just to provide all variable values
m = 84 #just to provide all variable values
Y_one_hot = np.zeros((vocab_size, m))
Y_one_hot[Y.flatten(), np.arange(m)] = 1

输入Y.flatten()将被评估为以下numpy数组:
  [ 8  9  7  4  9  7  8  4  8  7  8 12  4  8  9  8 12  7  8  9  7 12  7  2
  9  7  8  7  2  0  7  8 12  2  0  8  8 12  7  0  8  6 12  7  2  8  6  5
  7  2  0  6  5 10  2  0  8  5 10  1  0  8  6 10  1  3  8  6  5  1  3 11
  6  5 10  3 11  5 10  1 11 10  1  3]

np arrange是一个张量,范围从0到83。

np.arange(m)
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
 72 73 74 75 76 77 78 79 80 81 82 83]

好的,现在我遇到困难的是理解新的Y_one_hot输出,我收到了一个尺寸为13的numpy数组(如预期),但是根据Y.flatten()输入,我不明白其中的“1”位于何处。例如,以下是13个数组中的第一个:

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0
  0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0]

有人能够解释一下我是如何从那个输入值得到那个输出数组的吗?看起来1在随机位置,而且在13个数组中,其他一些数组中的1的数量也似乎是随机的。这是否是预期行为?

以下是一个完整可运行的示例:

import numpy as np
import sys
import re



# turn Y into one hot encoding
Y =  np.array([ 8,  9,  7,  4 , 9,  7,  8,  4,  8,  7,  8, 12,  4,  8,  9,  8, 12,  7,  8,  9,  7, 12,  7,  2,
  9,  7,  8,  7,  2,  0,  7,  8, 12,  2,  0,  8,  8, 12,  7,  0,  8,  6, 12,  7,  2,  8,  6,  5,
  7,  2,  0,  6,  5, 10,  2,  0,  8,  5, 10,  1,  0,  8,  6, 10,  1,  3,  8,  6,  5,  1,  3, 11,
  6,  5, 10,  3, 11,  5, 10,  1, 11, 10,  1,  3])
m = 84
vocab_size = 13
Y_one_hot = np.zeros((vocab_size, m))
Y_one_hot[Y.flatten(), np.arange(m)] = 1
np.set_printoptions(threshold=sys.maxsize)
print(Y_one_hot.astype(int))

1
Y.flatten() 选择第一维度的索引。np.arange(m) 选择第二维度的索引。- 使用每个数组的第一个元素 - Y_one_hot[8,0] = 1 - wwii
1
这是预期的行为吗?- 你是在问为什么你的赋值表达式会以那种方式工作,还是在问这是否是进行编码的正确方式? - wwii
在某种程度上,我现在正在阅读答案以尝试理解行为,但因为它恰好适用于我发布的示例值(但至少答案使用最小的示例来解释行为)。列的概念似乎令人困惑,因为在发布的答案中,我可以理解为什么第一个数组的第4列中有1,但是我的13x84的numpy数组的维度似乎使我困惑,因此我正在尝试理解那里的系统... - D3181
np.vstack((Y,np.arange(m))).T 将展示给您如何将索引配对。您可以看到第30个条目 (np.vstack((Y,np.arange(m))).T[29]) 是 [0,29]。因此,您的表达式正在将一个值分配给 Y_one_hot[0,29] - 如果这仍然让您感到困惑,您需要花更多时间阅读Numpy文档并尝试使用示例进行操作 - SO不是教程。jakevdp的答案中链接的文档参考与您的问题相关。 - wwii
2个回答

3

你展示的代码是一种快速将多个标签索引转换为独热编码的方式。

我们可以使用单个索引,并将其转换为一个独热编码向量。为了保持简单,我们将使用编码大小为10(即九个0和一个1):

>>> y = 4
>>> y_ohe = np.zeros(10)
>>> y_ohe[y] = 1
array([0., 0., 0., 0., 1., 0., 0., 0., 0., 0.])

现在,让我们尝试使用多个索引:同时使用5个标签。起始数组将是二维的:(5, 10),即每个标签大小为10的独热编码向量。

>>> y = np.array([4, 2, 1, 7])
>>> y_ohe = np.zeros((4, 10))
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

期望的结果是:
array([[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 7., 0., 0.]])

为此,我们将按行和列进行索引:np.arange(len(y))将给出所有行索引,而y将为我们提供 1 应该在哪些列上。由于 np.arange(len(y))y 长度相同,它们将被压缩迭代,类似于:
>>> for i, j in zip(np.arange(len(y)), y):
>>>     print(i, j)
[0, 4]
[1, 2]
[2, 1]
[3, 7]

下面是二维张量y_ohe中我们希望为其指定值为1的坐标[i, j]:

将索引值分配给值为1的位置:

>>> y_ohe[np.arange(len(y)), y] = 1
array([[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]])

同样地,通过相反的索引方式进行索引:
>>> y = np.array([4, 2, 1, 7])
>>> y_ohe = np.zeros((10, 4))
>>> y_ohe[y, np.arange(len(y))] = 1
array([[0., 0., 0., 0.],
       [0., 0., 1., 0.],
       [0., 1., 0., 0.],
       [0., 0., 0., 0.],
       [1., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 1.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

在您的情况下,变量Y有一个额外的维度,类似于Y = np.array([[4], [2], [1], [7]]),与上面给出的示例相关。这将在被扁平化后得到y

2
代码中的 Y_one_hot[Y.flatten(), np.arange(m)] = 1 将一个由整数索引列表组成的数组值设定为 1(详见整数数组索引)。索引数组会被广播在一起,在一维数组中的结果基本上就是以高效的方式完成以下操作:
for i, j in zip(Y.flatten(), np.arange(m)):
    Y_one_hot[i, j] = 1

每个Y_one_hot的列对应于Y.flatten()的一个条目,并且在给定条目所在的行中具有单个非零值。用一个更小的数组可能更容易看出来:
Y_onehot = np.zeros((2, 3), dtype=int)
Y = np.array([0, 1, 0])

Y_onehot[Y.flatten(), np.arange(3)] = 1

print(Y_onehot)
# [[1 0 1]
#  [0 1 0]]

三个条目对应三列,每列在相应的行中都有一个非零条目。

你可能想要“展示”一下索引是如何配对的-所有来自你的例子的[i,j]。...np.vstack((Y,np.arange(m))).T。原帖的作者仍然没有看到它。 - wwii
或者添加以下代码:for i, j in zip(Y.flatten(), np.arange(m)): print(f'Y_one_hot[{i}, {j}] = 1') - wwii
两个答案都很好地分解了问题,并帮助我理解了值是如何被赋予的。加上wwii的评论,我能够更容易地理解这个例子中发生的逻辑,所以很难为我的问题选择一个“正确的答案”。我建议任何阅读此内容的人都要查看这两个答案,因为它们都是有效的,现在我理解了正在发生的事情,两者都有意义。 - D3181

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接