当前位置:   article > 正文

tensorflow 单词嵌入向量

嵌入向量

用数字表示文本

机器学习模型将向量(数字数组)作为输入。在处理文本时,我们必须先想出一种策略,将字符串转换为数字(或将文本“向量化”),然后再其馈入模型。

实现这一目标的三种策略:

独热编码

作为第一个想法,可以对词汇表中的每个单词进行“独热”编码。考虑这样一句话:“The cat sat on the mat”。这句话中的词汇(或唯一单词)是(cat、mat、on、sat、the)。为了表示每个单词,我们将创建一个长度等于词汇量的零向量,然后在与该单词对应的索引中放置一个 1。

为了创建一个包含句子编码的向量,我们可以将每个单词的独热向量连接起来。

要点:这种方法效率低下。一个独热编码向量十分稀疏(这意味着大多数索引为零)。假设我们的词汇表中有 10,000 个单词。为了对每个单词进行独热编码,我们将创建一个其中 99.99% 的元素都为零的向量。

用一个唯一的数字编码每个单词

第二种方法是使用唯一的数字来编码每个单词。继续上面的示例,可以将 1 分配给“cat”,将 2 分配给“mat”,依此类推。然后,我们可以将句子“The cat sat on the mat”编码为一个密集向量,例如 [5, 1, 4, 3, 5, 2]。这种方法是高效的。现在,我们有了一个密集向量(所有元素均已满),而不是稀疏向量。

但是,这种方法有两个缺点:

  • 整数编码是任意的(它不会捕获单词之间的任何关系)。

  • 对于要解释的模型而言,整数编码颇具挑战。例如,线性分类器针对每个特征学习一个权重。由于任何两个单词的相似性与其编码的相似性之间都没有关系,因此这种特征权重组合没有意义。

单词嵌入向量

单词嵌入向量为我们提供了一种使用高效、密集表示的方法,其中相似的单词具有相似的编码。重要的是不必手动指定此编码。嵌入向量是浮点值的密集向量(向量的长度是可以指定的参数)。它们是可以训练的参数(模型在训练过程中学习的权重,与模型学习密集层权重的方法相同),无需手动为嵌入向量指定值。8 维的单词嵌入向量(对于小型数据集)比较常见,而在处理大型数据集时最多可达 1024 维。维度更高的嵌入向量可以捕获单词之间的细粒度关系,但需要更多的数据来学习。Word Embedding矩阵给每个单词分配一个固定长度的向量表示,这个长度可以自行设定,比如300,实际上会远远小于字典长度(比如10000)。而且两个单词向量之间的夹角值可以作为他们之间关系的一个衡量。如下表示:

Word Embedding表示法

通过简单的余弦函数,我们就可以计算两个单词之间的相关性,简单高效:

  1. import tensorflow as tf
  2. from icecream import ic
  3. from tensorflow import keras
  4. from tensorflow.keras import layers
  5. #1 导入数据
  6. import tensorflow_datasets as tfds
  7. tfds.disable_progress_bar()
  8. #2 使用嵌入向量层
  9. '''
  10. 可以将嵌入向量层理解为一个从整数索引(代表特定单词)映射到密集向量(其嵌入向量)的查找表。嵌入向量的维数(或宽度)是一个参数,
  11. 可以试验它的数值,以了解多少维度适合问题,这与试验密集层中神经元数量的方式非常相似。
  12. '''
  13. embedding_layer = layers.Embedding(1000, 5)
  14. '''
  15. 创建嵌入向量层时,嵌入向量的权重会随机初始化(就像其他任何层一样)。在训练过程中,通过反向传播来逐渐调整这些权重。
  16. 训练后,学习到的单词嵌入向量将粗略地编码单词之间的相似性(因为它们是针对训练模型的特定问题而学习的)。
  17. '''
  18. #如果将整数传递给嵌入向量层,结果会将每个整数替换为嵌入向量表中的向量:
  19. result = embedding_layer(tf.constant([1,2,3]))
  20. ic(result.numpy())

运行结果:

  1. ic| result.numpy(): array([[-0.04286907, 0.01746731, 0.03539313, -0.04714948, 0.01741666],
  2. [-0.0271346 , -0.04651893, 0.03715916, 0.02202106, -0.04117215],
  3. [-0.00178969, 0.01628102, 0.03589281, 0.01006144, 0.03929297]],
  4. dtype=float32)
  1. '''
  2. 对于文本或序列问题,嵌入向量层采用整数组成的 2D 张量,其形状为 (samples, sequence_length),
  3. 其中每个条目都是一个整数序列。它可以嵌入可变长度的序列。您可以在形状为 (32, 10)(32 个长度为 10 的序列组成的批次)或
  4. (64, 15)(64 个长度为 15 的序列组成的批次)的批次上方馈入嵌入向量层。
  5. '''
  6. #返回的张量比输入多一个轴,嵌入向量沿新的最后一个轴对齐。向其传递 (2, 3) 输入批次,输出为 (2, 3, N)
  7. #result = embedding_layer(tf.constant([[0,1,2],[3,4,5]]))
  8. #ic(result.shape)
  9. #ic| result.shape: TensorShape([2, 3, 5]),这里的5,是之前embeddiing(1000,5)层设置的5维参数
  10. '''
  11. 当给定一个序列批次作为输入时,嵌入向量层将返回形状为 (samples, sequence_length, embedding_dimensionality) 的 3D 浮点张量。
  12. 为了从可变长度的序列转换为固定表示,有多种标准方法。可以先使用 RNN、注意力或池化层,然后再将其传递给密集层。下面使用池化,因为它最简单。
  13. 以后使用 RNN 进行文本分类是一个不错的选择。
  14. '''

从头开始学习嵌入向量

  1. #基于 IMDB 电影评论来训练情感分类器
  2. import tensorflow as tf
  3. from icecream import ic
  4. from tensorflow import keras
  5. from tensorflow.keras import layers
  6. #1 导入数据
  7. import tensorflow_datasets as tfds
  8. tfds.disable_progress_bar()#Disabled Tqdm progress bar.
  9. (train_data, test_data), info = tfds.load(
  10. 'imdb_reviews/subwords8k',
  11. split = (tfds.Split.TRAIN, tfds.Split.TEST),
  12. with_info=True, as_supervised=True)
  13. #2 获取编码器 (tfds.features.text.SubwordTextEncoder),并快速浏览词汇表。
  14. #词汇表中的“”代表空格。请注意词汇表如何包含完整单词(以“”结尾)以及可用于构建更大单词的部分单词:
  15. encoder = info.features['text'].encoder
  16. ic(encoder.subwords[:20])
  17. #3 电影评论的长度可以不同。我们将使用 padded_batch 方法来标准化评论的长度。
  18. train_batches = train_data.shuffle(1000).padded_batch(10)
  19. #3.1 打乱数据 shuffle(1000)
  20. # 从train_data数据集中按顺序抽取buffer_size(1000)个样本放在buffer中,然后打乱buffer中的样本
  21. # buffer中样本个数不足buffer_size,继续从data数据集中安顺序填充至buffer_size,
  22. # 此时会再次打乱
  23. test_batches = test_data.shuffle(1000).padded_batch(10)
  24. '''
  25. dataset.shuffle作用是将数据进行打乱操作,传入参数为buffer_size,改参数为设置“打乱缓存区大小”,
  26. 也就是说程序会维持一个buffer_size大小的缓存,每次都会随机在这个缓存区抽取一定数量的数据
  27. dataset.batch作用是将数据打包成batch_size
  28. dataset.repeat作用就是将数据重复使用多少epoch
  29. '''
  30. '''
  31. padded_batch(
  32. batch_size, padded_shapes=None, padding_values=None, drop_remainder=False
  33. )
  34. 注意参数drop_remainder用来约束最后一个batch是不是要丢掉,当这个batch样本数少于batch_size时,
  35. 比如batch_size = 3,最后一个batch只有2个样本。默认是不丢掉
  36. padded_batch是非常见的一个操作,比如对一个变长序列,通过padding操作将每个序列补成一样的长度。
  37. 特点:
  38. 1)padded_shapes使用默认值或者设置为-1,那么每个batch padding后每个维度就是跟这个者个batch的样本各个维度最大值保持一致
  39. 2)当shape固定为特定的size时,那么每个batch的shape就是一样的。
  40. '''
  41. #导入时,评论的文本是整数编码的(每个整数代表词汇表中的特定单词或单词部分)。
  42. #请注意尾随零,因为批次会填充为最长的示例。
  43. #train_batch, train_labels = next(iter(train_batches))
  44. #train_batch.numpy()
  45. '''
  46. array([[5739, 46, 674, ..., 0, 0, 0],
  47. [ 274, 2732, 1289, ..., 0, 0, 0],
  48. [ 19, 118, 874, ..., 0, 0, 0],
  49. ...,
  50. [ 324, 12, 118, ..., 0, 0, 0],
  51. [ 12, 31, 165, ..., 0, 0, 0],
  52. [ 131, 196, 7968, ..., 0, 0, 0]])
  53. '''
  54. #4 创建一个简单模型
  55. '''
  56. 使用 Keras 序列式 API 定义模型。在这种情况下,它是一个“连续词袋”样式的模型。
  57. 小心:此模型不使用遮盖,而是使用零填充作为输入的一部分,因此填充长度可能会影响输出。
  58. '''
  59. embedding_dim=16
  60. model = keras.Sequential([
  61. #嵌入向量层将采用整数编码的词汇表,并查找每个单词索引的嵌入向量。在模型训练时会学习这些向量。
  62. #向量会向输出数组添加维度。得到的维度为:(batch, sequence, embedding)。
  63. layers.Embedding(encoder.vocab_size, embedding_dim),
  64. #接下来,通过对序列维度求平均值,GlobalAveragePooling1D 层会返回每个样本的固定长度输出向量。
  65. #这让模型能够以最简单的方式处理可变长度的输入。
  66. layers.GlobalAveragePooling1D(),
  67. #此固定长度输出向量通过一个包含 16 个隐藏单元的完全连接(密集)层进行流水线传输。
  68. layers.Dense(16, activation='relu'),
  69. #最后一层与单个输出节点密集连接。利用 Sigmoid 激活函数,得出此值是 0 到 1 之间的浮点数,表示评论为正面的概率(或置信度)。
  70. layers.Dense(1)
  71. ])
  72. #model.summary()
  73. '''
  74. Model: "sequential"
  75. _________________________________________________________________
  76. Layer (type) Output Shape Param #
  77. =================================================================
  78. embedding_1 (Embedding) (None, None, 16) 130960
  79. _________________________________________________________________
  80. global_average_pooling1d (Gl (None, 16) 0
  81. _________________________________________________________________
  82. dense (Dense) (None, 16) 272
  83. _________________________________________________________________
  84. dense_1 (Dense) (None, 1) 17
  85. =================================================================
  86. Total params: 131,249
  87. Trainable params: 131,249
  88. Non-trainable params: 0
  89. _________________________________________________________________
  90. '''
  91. #5 编译和训练模型
  92. model.compile(optimizer='adam',
  93. loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
  94. metrics=['accuracy'])
  95. history = model.fit(
  96. train_batches,
  97. epochs=10,
  98. validation_data=test_batches, validation_steps=20)
  99. #6 绘制训练准确率和验证准确率图
  100. import matplotlib.pyplot as plt
  101. history_dict = history.history
  102. acc = history_dict['accuracy']
  103. val_acc = history_dict['val_accuracy']
  104. loss=history_dict['loss']
  105. val_loss=history_dict['val_loss']
  106. epochs = range(1, len(acc) + 1)
  107. plt.figure(figsize=(12,9))
  108. plt.plot(epochs, loss, 'bo', label='Training loss')
  109. plt.plot(epochs, val_loss, 'b', label='Validation loss')
  110. plt.title('Training and validation loss')
  111. plt.xlabel('Epochs')
  112. plt.ylabel('Loss')
  113. plt.legend()
  114. plt.show()
  115. plt.figure(figsize=(12,9))
  116. plt.plot(epochs, acc, 'bo', label='Training acc')
  117. plt.plot(epochs, val_acc, 'b', label='Validation acc')
  118. plt.title('Training and validation accuracy')
  119. plt.xlabel('Epochs')
  120. plt.ylabel('Accuracy')
  121. plt.legend(loc='lower right')
  122. plt.ylim((0.5,1))
  123. plt.show()
  124. #7 检索学习的嵌入向量
  125. #检索在训练期间学习的单词嵌入向量。这将是一个形状为 (vocab_size, embedding-dimension) 的矩阵。
  126. e = model.layers[0]
  127. weights = e.get_weights()[0]
  128. #print(weights.shape) # shape: (vocab_size, embedding_dim)
  129. #(8185, 16)
  130. #将权重写入磁盘。要使用 Embedding Projector,我们将以制表符分隔的格式上传两个文件:一个向量文件(包含嵌入向量)和一个元数据文件(包含单词)。
  131. import io
  132. encoder = info.features['text'].encoder
  133. out_v = io.open('vecs.tsv', 'w', encoding='utf-8')
  134. out_m = io.open('meta.tsv', 'w', encoding='utf-8')
  135. for num, word in enumerate(encoder.subwords):
  136. vec = weights[num+1] # skip 0, it's padding.
  137. out_m.write(word + "\n")
  138. out_v.write('\t'.join([str(x) for x in vec]) + "\n")
  139. out_v.close()
  140. out_m.close()
  141. #8 可视化嵌入向量
  142. '''
  143. 为了可视化嵌入向量,我们将它们上传到 Embedding Projector。
  144. 打开 Embedding Projector:http://projector.tensorflow.org/(也可以在本地 TensorBoard 实例中运行)。
  145. 点击“Load data”。
  146. 上传我们在上面创建的两个文件:vecs.tsv 和 meta.tsv。
  147. 将显示已训练的嵌入向量。可以搜索单词以查找其最邻近。例如,尝试搜索“beautiful”,可能会看到“wonderful”等相邻单词。
  148. 注:结果可能会略有不同,具体取决于训练嵌入向量层之前如何随机初始化权重。
  149. 注:可以试验性地使用更简单的模型来生成更多可解释的嵌入向量。尝试删除 Dense(16) 层,重新训练模型,然后再次可视化嵌入向量。
  150. '''

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/371757
推荐阅读
相关标签
  

闽ICP备14008679号