如何使用嵌入层获得短语的向量表示并用它进行 PCA

数据挖掘 机器学习 神经网络 nlp 词嵌入 主成分分析
2022-03-12 13:54:55

我试图从概念和 Python 代码的角度来理解,如何将语料库中存在的短语(用于训练神经网络短语进行分类)表示为向量以及如何使用它们进行 PCA。

考虑一下我不想使用 Word2Vec 嵌入,我只想从我的神经网络的嵌入层中提取向量。

我选择了解如何执行此操作的示例如下:

import numpy as np
from keras.preprocessing.text import one_hot, Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.embeddings import Embedding
# define documents
docs = np.array(['Well done!',
        'Good work',
        'Great effort',
        'nice work',
        'Excellent!',
        'Weak',
        'Poor effort!',
        'not good',
        'poor work',
        'Could have done better.'])
# define class labels
labels = np.array([1,1,1,1,1,0,0,0,0,0])
# train the tokenizer
vocab_size = 15
tokenizer = Tokenizer(num_words=vocab_size)
tokenizer.fit_on_texts(docs)

# encode the sentences
encoded_docs = tokenizer.texts_to_sequences(docs)
# pad documents to a max length of 4 words
max_length = 4
padded_docs = pad_sequences(encoded_docs, maxlen=max_length, padding='post')
print(padded_docs)
# define the model
model = Sequential()
model.add(Embedding(vocab_size, 2, input_length=max_length, name='embeddings'))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))

# compile the model
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# fit the model
model.fit(padded_docs, labels, epochs=50, verbose=2)

它是一个分类器 MLP,所以我定义了与文档的每个句子相对应的类标签,我根据 tokenizer 模块为每个单词分配一个整数,我准备所有单词序列具有相同的长度,因为 keras 喜欢工作这样,然后我最终定义、编译和拟合模型。拟合模型后,我可以使用以下代码行提取嵌入层的权重:

# save embeddings
embeddings = model.get_layer('embeddings').get_weights()[0]

这是一个具有二维嵌入空间的二维数组(由我选择):

array([[-0.02900218, -0.02272025],
       [-0.03750041,  0.08604637],
       [ 0.00261297,  0.06689994],
       [ 0.06822112, -0.07083904],
       [ 0.042956  ,  0.00642773],
       [-0.01934443, -0.03651911],
       [ 0.02451712,  0.02507548],
       [ 0.01995835,  0.03889224],
       [ 0.01348991,  0.01143651],
       [ 0.02176871,  0.01283678],
       [-0.04610137, -0.04942843],
       [-0.02342983, -0.07704163],
       [-0.08990634, -0.06908827],
       [ 0.07353339, -0.06115208],
       [-0.06146053,  0.09602208]], dtype=float32)

在这一点上,我有两个巨大的困难:

  • 如何用嵌入权重和向量来表示语料库的每个短语:基于这个问题的好答案,我想首先我必须检查哪些是分配给每个单词的整数,我可以做到和:
print(encoded_docs)

这给了我以下表示:

[[6, 2], [3, 1], [7, 4], [8, 1], [9], [10], [5, 4], [11, 3], [5, 1], [12, 13, 2, 14]]

然后我将之前打印的训练网络的嵌入权重分配给每个整数,因此我得到:

X=np.array([[[[ 0.02451712,  0.02507548], [ 0.00261297,  0.06689994]], [[ 0.06822112, -0.07083904], [-0.03750041,  0.08604637]], [[ 0.01995835,  0.03889224], [ 0.042956  ,  0.00642773]], [[ 0.01348991,  0.01143651], [-0.03750041,  0.08604637]],  [ 0.02176871,  0.01283678], [-0.04610137, -0.04942843], [[-0.01934443, -0.03651911], [ 0.042956  ,  0.00642773]], [[-0.02342983, -0.07704163],  [ 0.06822112, -0.07083904]], [[-0.01934443, -0.03651911], [-0.03750041,  0.08604637]], [[-0.08990634, -0.06908827], [ 0.07353339, -0.06115208], [ 0.00261297,  0.06689994], [-0.06146053,  0.09602208]]]])

说 X 包含我的文档中所有单词的向量表示是否正确?此外,如果是或者在任何情况下,Python 中是否有一个函数可以让您获取它?还是应该从头开始实施它?然后,我忽略了序列用零填充的事实。我应该添加零并将我的所有向量(二维数组)作为 4 维向量以便正确表示每个单词吗?

  • 一旦我获得了文档中每个单词的向量表示,我该如何做我获得的二维数组的 PCA 表示?什么是样本,什么是变量?理论上,我应该得到一个图,其中标记为 1 的数据聚集在一起,标记为 0 的数据聚集在一起,这要归功于它们现在由通过训练分类器神经网络获得的权重给出。

我希望我对所有事情都不会太过分。

先感谢您。

PS:请,如果您对问题投反对票,请告诉我您投反对票的原因。这不是我提出的问题,但有一些研究工作。

2个回答

我们可以使用下面的代码来获取每个单词的嵌入

 words_embeddings = {w:embeddings[idx] for w, idx in tokenizer.word_index.items()}

res_vectors = np.empty((0, 2), float)
words = []
for k,v in words_embeddings.items():
  print(k,"-->", v)
  words.append(k)
  res_vectors = np.append(res_vectors, [v], axis=0)

由于每个单词都表示为 2D 向量,因此我没有降低向量的维数。使用下面的代码,我们可以获得单词表示。

import matplotlib.pyplot as plt
plt.figure(figsize=(13,7))
plt.scatter(res_vectors[:,0],res_vectors[:,1],linewidths=10,color='blue')
plt.xlabel("PC1",size=15)
plt.ylabel("PC2",size=15)
plt.title("Word Embedding Space",size=20)
for i, word in enumerate(words):
  plt.annotate(word,xy=(res_vectors[i,0],res_vectors[i,1]))

在此处输入图像描述

为了获得更好的结果,尝试增加每个单词的向量维度。

如果我们对一个词使用一个 100 维的向量。我们可以如下使用 PCA。

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
res_vectors = pca.fit_transform(res_vectors)
print(res_vectors)

在这种情况下的单词表示。 在此处输入图像描述

我们可以得到如下的词组嵌入:

doc_vecs = np.empty((0,2), float)
for i, doc in enumerate(padded_docs):
  vec = np.empty((0,2), float)
  for token in doc:  
    vec = np.append(vec, [embeddings[token]], axis=0)
  vec = vec.mean(axis=0)  
  print(docs[i], "-->", vec)
  doc_vecs = np.append(doc_vecs, [vec], axis=0)

短语表示:

import matplotlib.pyplot as plt
plt.figure(figsize=(13,7))
plt.scatter(doc_vecs [:,0],doc_vecs [:,1],linewidths=10,color='blue')
plt.xlabel("PC1",size=15)
plt.ylabel("PC2",size=15)
plt.title("Phrase Embedding Space",size=20)
for i, doc in enumerate(docs):
  plt.annotate(doc,xy=(doc_vecs [i,0],doc_vecs [i,1]))

在此处输入图像描述

  1. 您的 X 包含文档中所有单词的表示。每个文档(短语)都表示为 4x2 二阶张量(因为您使用最大长度 4 进行填充,并且您的词嵌入有 2 个维度)。

您可以使用嵌入(encoded_docs)获得这样的矩阵。填充的使用取决于您尝试使用这些嵌入执行的下游任务。

  1. PCA 通常用于映射向量RdRc. 上面的文档表示是R42,要在这些上执行 PCA,您必须扁平化您的文档表示或使用一些最近的张量 PCA 技术。

您可以像上面所做的那样将表示展平以获得 R8每个文档的向量,或者您可以将短语中的每个单词相加组合以获得R2向量。我会建议后一种选择。第一个选项要求您填充短语,而第二个选项不需要填充。

发布此内容后,您可以使用常规 PCA 来减少维度并可视化数据并识别集群。但是对于您在此示例中使用的数据,可能不会产生有意义的表示或可视化,因为它太小了。