来自 numpy 的一种热编码

One hot encoding from numpy

我正在尝试理解示例 python tutorial 输出的值。输出似乎是我能理解的任何顺序。特定的 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() 输入的位置,例如这里是 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]

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

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

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))

Y_one_hot[Y.flatten(), np.arange(m)] = 1 行是用整数索引列表设置数组的值(记录在 Integer Array Indexing

索引数组 broadcast 在一起,一维数组的结果本质上是一种有效的方法:

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]]

三个条目映射到三列,每列在对应于值的行中有一个非零条目。

您显示的代码是将多个标签索引转换为单热编码的快速方法。

让我们用单个索引来做,并将其转换为单热编码向量。为了简单起见,我们将坚持 10 的编码大小(即九个 0 和一个 0):

>>> 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] 坐标。

将索引值分配给 1s:

>>> 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