预训练词向量的使用教程

中文预训练资料下载

按照词向量不同的维度,可大可小,会有多个版本的预训练词向量,可以根据自己需求进行选择:

预训练词向量文件,一般比较大很难打开,大部分文件的格式由3个部分组成,以glove举例:

  1. 第一行有2个数字,左边的表示字典有多少个词,右边的表示词向量的维度
  2. 每行的第一个元素是词本身
  3. 每行的其它元素是词向量

预训练词向量的使用步骤

  1. 将所有语料转化为词索引序列(word_index)。所谓词索引就是为每一个词一次分配一个整数ID。
  2. 生成一个词向量矩阵(embedding_matrix)。第 i 列表示词索引为 i 的词的词向量。
  3. 将词向量矩阵载入Keras Embedding层,设置该层的权重不可再训练(也就是说在之后的网络训练过程中,词向量不再改变)。

数据预处理

我们可以将语料样本转化为神经网络训练所用的tensor。所用到的Keras库是keras.preprocessing.text.Tokenizer和keras.preprocessing.sequence.pad_sequences。代码如下

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
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

# 生成语料词索引序列
tokenizer = Tokenizer(nb_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)

word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))

data = pad_sequences(sequences, maxlen=MAX_SEQUENCE_LENGTH)

# 标签格式转换
labels = to_categorical(np.asarray(labels))
print('Shape of data tensor:', data.shape)
print('Shape of label tensor:', labels.shape)

# 切分成测试集和验证集
indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
labels = labels[indices]
nb_validation_samples = int(VALIDATION_SPLIT * data.shape[0])

x_train = data[:-nb_validation_samples]
y_train = labels[:-nb_validation_samples]
x_val = data[-nb_validation_samples:]
y_val = labels[-nb_validation_samples:]

Embedding layer 设置

接下来,我们从词向量预训练文件中解析出每个词和它所对应的词向量,并用字典(embedding_index)的方式存储。下面主要生成embedding_index:

1
2
3
4
5
6
7
8
9
10
11
12
13
embeddings_index = {}
f = open(os.path.join(pretrained_wv_dir, 'Tencent_AILab_ChineseEmbedding.txt'))
for line in f:
values = line.split()

# 因为每行的第一个元素是词,后面的才是词向量,因此将values[0]与values[1:]分开存放
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')

embeddings_index[word] = coefs
f.close()

print('Found %s word vectors.' % len(embeddings_index))

此时,我们可以根据得到的embedding_index生成上文所定义的词向量矩阵embedding_matrix:

1
2
3
4
5
6
7
8
9
10
11
12
# embedding_matrix的维度 =(语料单词的数量,预训练词向量的维度)
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))

for word, i in word_index.items():
# 根据语料生成的word_index来从embedding_index一条条获取单词对应的向量
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words found in embedding index will be pretrained vectors.
embedding_matrix[i+1] = embedding_vector # i+1 是为了处理OOV,使得预测时未见过的词的位置为0。当然如果不使用这种OOV的处理方式的话,这里的embedding_matrix[i+1]应该变成embedding_matrix[i],下同理。
else:
# words not found in embedding index will be random vectors with certain mean&std.如果单词未能在预训练词表中找到,可以自己生成一串向量。
embedding_matrix[i+1] = np.random.normal(0.053, 0.3146, (1, embed_size))[0] # 0.053, 0.3146 根据统计,可以改变数值

现在我们将这个词向量举证加载到Embedding 层中,注意,我们设置trainable=False使得这个编码层不可再训练。

1
2
3
4
5
6
7
from keras.layers import Embedding

embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
weights=[embedding_matrix],
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)

一个Embedding层的输入应该是一系列的整数序列,比如一个2D的输入,它的shape值为(samples,indices),也就是一个samples行,indices列的矩阵。

下面的是以函数形式写入dictionary模块的demo,跟上面的差不多,仅作参考:

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
from gensim.models import KeyedVectors

def gen_embeddings_index(self, pretrained_wv_path):
"""
create a weight matrix for words in training docs
将预训练词向量文本变成字典形式:{word:vector}
:param pretrained_wv_path: 'Tencent_AILab_ChineseEmbedding.txt' # 预训练的词向量文件
"""
# 使用gensim导入预训练词向量文件,不用管第一行的数值处理
wv_from_text = KeyedVectors.load_word2vec_format(pretrained_wv_path, binary=False)
embeddings_index = {}
for word in wv_from_text.vocab:
embeddings_index[word] = wv_from_text.word_vec(word)
logging.info('Loaded {} word vectors.'.format(len(embeddings_index)))

return embeddings_index

def gen_embedding_matrix(self, word_index, embeddings_index, embed_size):
"""
从庞大的预训练的词向量中,选出训练集中出现的单词的词向量,形成小型的预训练词向量
:param word_index: local dictionary
:param embeddings_index: pretrained word vectors
:param embed_size: 预训练的词向量的维度
:return:
"""
embedding_matrix = np.zeros((len(word_index) + 1, embed_size))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words found in embedding index will be pretrained vectors.
embedding_matrix[i+1] = embedding_vector # i+1 是为了处理OOV,使得预测时未见过的词为0
else:
# words not found in embedding index will be random vectors with certain mean&std.
embedding_matrix[i+1] = np.random.normal(0.053, 0.3146, (1, embed_size))[0] # 0.053, 0.3146 根据统计

# save embedding matrix
embed_df = pd.DataFrame(embedding_matrix)
embed_df.to_csv(self.path_embedding_matrix, header=None, sep=' ')

return embedding_matrix

写在最后

之前在做QA任务的优化时,尝试使用预训练的词向量,那时候还没直接用Bert。先说结果:效果提升不大,甚至说没啥提升。

主要原因:某垂直领域的词向量,太接近了,起不到分开词意的作用。词向量可能在非常泛的语义区分中有作用,比如在聊天的时候,谈的天南地北,能分清钢琴和大米是两回事,但是可能分不清钢琴和吉他,甚至两个向量表达十分地接近。

后面接触Bert之后,就没有深入再做词向量预训练的工作了,只能说Bert使人懒惰😂,接下来会写几篇Bert实战相关文章,敬请期待~

参考:
Using pre-trained word embeddings in a Keras model

打赏2块钱,帮我买杯咖啡,继续创作,谢谢大家!☕~
0%