Embedding介绍

Embedding是一个嵌入层,将输入的非负整数矩阵中的每个数值,转换为具有固定长度的向量。

在NLP任务中,一般把输入文本映射成向量表示,以便神经网络的处理。在数据处理章节,我们已经将用户和电影的特征用数字表示。嵌入层Embedding可以完成数字到向量的映射。

飞桨已经支持Embedding的API,该接口根据输入从Embedding矩阵中查询对应Embedding信息,并会根据输入参数size (vocab_size, emb_size)自动构造一个二维embedding矩阵。该API重要参数如下所示,详细介绍可参见Embedding API接口文档

函数形式: fluid.dygraph.Embedding(size, param_attr)

  • size (tuple|list):Embedding矩阵的维度。必须包含两个元素,第一个元素是用来表示输入单词的最大数值, 第二个元素是输出embedding的维度。
  • param_attr (ParamAttr):指定Embedding权重参数属性。

  1. import paddle.fluid as fluid
  2. import paddle.fluid.dygraph as dygraph
  3. from paddle.fluid.dygraph import Linear, Embedding, Conv2D
  4. import numpy as np
  5. # 创建飞桨动态图的工作空间
  6. with dygraph.guard():
  7. # 声明用户的最大ID,在此基础上加1(算上数字0)
  8. USR_ID_NUM = 6040 + 1
  9. # 声明Embedding 层,将ID映射为32长度的向量
  10. usr_emb = Embedding(size=[USR_ID_NUM, 32], is_sparse=False)
  11. # 声明输入数据,将其转成variable
  12. arr_1 = np.array([1], dtype="int64").reshape((-1))
  13. print(arr_1)
  14. arr_pd1 = dygraph.to_variable(arr_1)
  15. print(arr_pd1)
  16. # 计算结果
  17. emb_res = usr_emb(arr_pd1)
  18. # 打印结果
  19. print("数字 1 的embedding结果是: ", emb_res.numpy(), "\n形状是:", emb_res.shape)
  1. [1]
  2. name generated_var_0, dtype: VarType.INT64 shape: [1] lod: {}
  3. dim: 1
  4. layout: NCHW
  5. dtype: int64_t
  6. data: [1]
  7.  
  8. 数字 1 embedding结果是: [[-0.01668757 0.01722915 -0.02636782 0.01238998 -0.01431631 0.00452966
  9. 0.00937508 -0.01553859 -0.02951511 -0.03044251 0.02524646 -0.01547804
  10. 0.00172306 0.00431738 0.00906206 0.02476041 -0.02902042 0.02286419
  11. -0.03111525 -0.01072957 0.01195399 -0.0004138 0.01636873 -0.00637084
  12. 0.00514491 -0.02699892 -0.02513088 -0.00041785 0.0096146 -0.02057185
  13. -0.0193451 -0.02130266]]
  14. 形状是: [1, 32]

使用Embedding时,需要注意size这个参数:

  • size。size是包含两个整数元素的列表或者元组。第一个元素为vocab_size(词表大小), 第二个为emb_size(embedding层维度)。使用的ml-1m数据集的用户ID最大为6040,考虑到0的存在,所以这里我们需要将Embedding的输入size的第一个维度设置为6041(=6040+1)。emb_size表示将数据映射为emb_size维度的向量。这里将用户ID数据1转换成了维度为32的向量表示。32是设置的超参数,读者可以自行调整大小。

通过上面的代码,我们简单了解了Embedding的工作方式,但是Embedding层是如何将数字映射为高维度的向量的呢?

实际上,Embedding层和Conv2D, Linear层一样,Embedding层也有可学习的权重,通过矩阵相乘的方法对输入数据进行映射。Embedding中将输入映射成向量的实际步骤是:

  • 将输入数据转换成one-hot格式的向量;

  • one-hot向量和Embedding层的权重进行矩阵相乘得到Embedding的结果。

实现方法如下:

  1. # 创建飞桨动态图的工作空间
  2. with dygraph.guard():
  3. # 声明用户的最大ID,在此基础上加1(算上数字0)
  4. USR_ID_NUM = 10
  5. # 声明Embedding 层,将ID映射为16长度的向量
  6. usr_emb = Embedding(size=[USR_ID_NUM, 16], is_sparse=False)
  7. # 定义输入数据,输入数据为不超过10的整数,将其转成variable
  8. arr = np.random.randint(0, 10, (3)).reshape((-1)).astype('int64')
  9. print("输入数据是:", arr)
  10. arr_pd = dygraph.to_variable(arr)
  11. emb_res = usr_emb(arr_pd)
  12. print("默认权重初始化embedding层的映射结果是:", emb_res.numpy())
  13. # 观察Embedding层的权重
  14. emb_weights = usr_emb.state_dict()
  15. print(emb_weights.keys())
  16. print("\n查看embedding层的权重形状:", emb_weights['weight'].shape)
  17. # 声明Embedding 层,将ID映射为16长度的向量,自定义权重初始化方式
  18. # 定义MSRA初始化方式
  19. init = fluid.initializer.MSRAInitializer(uniform=False)
  20. param_attr = fluid.ParamAttr(initializer=init)
  21. usr_emb2 = Embedding(size=[USR_ID_NUM, 16], param_attr=param_attr)
  22. emb_res = usr_emb2(arr_pd)
  23. print("\nMSRA初始化权重embedding层的映射结果是:", emb_res.numpy())
  1. 输入数据是: [[3]
  2. [3]
  3. [5]]
  4. 默认权重初始化embedding层的映射结果是: [[[ 0.07319915 -0.31181502 -0.217157 -0.3967479 -0.41047537
  5. -0.11882892 -0.20110556 0.04288924 -0.14914796 -0.4794957
  6. 0.14784402 -0.01217031 -0.44350785 0.24657476 0.40264207
  7. -0.42123187]]
  8.  
  9. [[ 0.07319915 -0.31181502 -0.217157 -0.3967479 -0.41047537
  10. -0.11882892 -0.20110556 0.04288924 -0.14914796 -0.4794957
  11. 0.14784402 -0.01217031 -0.44350785 0.24657476 0.40264207
  12. -0.42123187]]
  13.  
  14. [[ 0.12367958 -0.08126739 -0.03898087 -0.46018332 0.43012017
  15. 0.11361349 0.1676197 -0.42571393 0.24998796 -0.08895245
  16. -0.1469143 -0.2648496 0.39830798 -0.18379673 -0.3121131
  17. -0.19391838]]]
  18. odict_keys(['weight'])
  19.  
  20. 查看embedding层的权重形状: [10, 16]
  21.  
  22. MSRA初始化权重embedding层的映射结果是: [[[-0.21339308 0.23698589 0.17333241 0.02846701 -0.07353761
  23. 0.46638224 0.4562641 0.05782131 0.36201525 -0.00333217
  24. -0.11395334 0.43728846 -0.6661781 -0.29415572 -0.44087017
  25. -0.05057243]]
  26.  
  27. [[-0.21339308 0.23698589 0.17333241 0.02846701 -0.07353761
  28. 0.46638224 0.4562641 0.05782131 0.36201525 -0.00333217
  29. -0.11395334 0.43728846 -0.6661781 -0.29415572 -0.44087017
  30. -0.05057243]]
  31.  
  32. [[ 0.34478924 -0.5796076 -0.02870371 -0.032929 -0.3829431
  33. 0.6351127 0.72117573 0.27278185 0.2895902 -0.00816591
  34. -0.187917 0.02802097 0.00138107 -0.5020734 0.04628478
  35. 0.28527454]]]

上面代码中,我们在[0, 10]范围内随机产生了3个整数,因此数据的最大值为整数9,最小为0。因此,输入数据映射为每个one-hot向量的维度是10,定义Embedding权重的第一个维度USR_ID_NUM为10。

这里输入的数据shape是[3, 1],Embedding层的权重形状则是[10, 16],Embedding在计算时,首先将输入数据转换成one-hot向量,one-hot向量的长度和Embedding层的输入参数size的第一个维度有关。比如这里我们设置的是10,所以输入数据将被转换成维度为[3, 10]的one-hot向量,参数size决定了Embedding层的权重形状。最终维度为[3, 10]的one-hot向量与维度为[10, 16]Embedding权重相乘,得到最终维度为[3, 16]的映射向量。

我们也可以对Embeding层的权重进行初始化,如果不设置初始化方式,则采用默认的初始化方式。

神经网络处理文本数据时,需要用数字代替文本,Embedding层则是将输入数字数据映射成了高维向量,然后就可以使用卷积、全连接、LSTM等网络层处理数据了,接下来我们开始设计用户和电影数据的特征提取网络。