【Tensorflow 2.0 正式版教程】词嵌入(word embedding)与GLoVe

这篇教程来讲解自然语言处理中的词嵌入,也就是word embedding,并介绍GLoVe预训练参数的加载。

简单来说,word embedding是将单词转换为向量,从而进一步参与神经网络的计算。在tensorflow 2.0中,tensorflow.keras.layers.Embedding实现了这一功能。其中embedding层计算了一个行向量乘矩阵的矩阵乘法,其中行向量是one hot形式的单词,矩阵为权重,所以本质上权重的每一行代表一个单词在语义空间的特征向量,该矩阵的行数为单词数。

下面以一个简单的文本分类实例具体讲解。数据库选用IMDB影评情感分析,该数据tensorflow提供官方下载与解析函数。

(train_sequences, train_labels), (test_sequences, test_labels) = tf.keras.datasets.imdb.load_data(num_words=word_num)
word_index = tf.keras.datasets.imdb.get_word_index()

其中sequences是序列化的结果,即以数字代替单词,num_words参数是最大单词数,特别的,0代表空白符,用于后续补位,1代表起始符,2代表未知单词,3代表未使用的单词。后续4为最高词频单词’the’,5及之后按词频排序。
word_index即单词到数字的映射关系,有个坑是这个映射表里’the’对应的是1,之后需要特殊处理。

考虑到tensorflow的数据放在了googleapis.com上,对于网络状况不好的环境,也可以先从官方下载数据:imdb.npz,imdb_word_index.json,然后在上面的加载函数中指定path参数。

将sequences输入到网络之前,还需要统一长度,小于指定长度的在前面补0,大于指定长度的截取后段。

train_sequences = pad_sequences(train_sequences, maxlen=max_len)
test_sequences = pad_sequences(test_sequences, maxlen=max_len)

此时,输入到网络的文本数据尺寸应为(batch_size, max_len),经过Embedding层,得到(batch_size, max_len, embedding_dim)大小的词嵌入向量,其中embedding_dim为语义空间维数。由此可以搭建一个简单的全连接网络对文本进行分类。

def Model():
    model = tf.keras.Sequential()
    model.add(Embedding(word_num, embedding_dim))
    model.add(GlobalAveragePooling1D())
    model.add(Dense(128, activation=tf.nn.relu))
    model.add(Dense(2, activation='softmax'))
    return model

然后便可以开始训练

word_num = 10000
max_len = 256
embedding_dim = 100

# get model
model = Model()
model.summary()

# train
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_sequences,
          train_labels,
          batch_size=512,
          epochs=10)

# test
test_loss, test_acc = model.evaluate(test_sequences, test_labels)
print(test_acc)

此时可以达到约87.1%的准确率。

更进一步,Embedding层的权重是随机初始化的,为了取得更好的结果,需要加载预训练的权重,如word2vec、GLoVe等,这里以GLoVe为例。

GLoVe权重可以从官方下载。考虑到权重的每一行代表一个单词在语义空间的特征向量,因此可以借助word_index中的映射关系进行赋值。

def get_embedding_weight(weight_path, word_index):
    # embedding_weight = np.zeros([word_num, embedding_dim])
    embedding_weight = np.random.uniform(-0.05, 0.05, size=[word_num, embedding_dim])
    cnt = 0
    with open(weight_path, 'r') as f:
        for line in f:
            values = line.split()
            word = values[0]
            if word in word_index.keys() and word_index[word] + 3 < word_num:
                """
                In tf.keras.dataset.imdb.load_data(), there are 4 special mark.
                : 0
                : 1
                : 2
                : 3
                So word_index loaded from offical file, "mdb_word_index.json", need to +3.
                """
                weight = np.asarray(values[1:], dtype='float32')
                embedding_weight[word_index[word] + 3] = weight
                cnt += 1
    print('matched word num: {}'.format(cnt))
    return embedding_weight

然后在Embedding层实例化时指定权重
Embedding(word_num, embedding_dim, weights=[embedding_weight])
我在实验中选用GLoVe_path = '/home1/dataset/GLoVe/glove.6B.100d.txt',在测试集上可以达到88.2%的准确率。相比随机初始化的方式,加载预训练权重提升了分类准确率。

考虑到本文仅作embedding层与GLoVe加载的简单教学,最终达到的准确率较低。代码中的超参数与模型本身可进一步调整以达到更好的结果。完整的代码可以在我的github上找到。
https://github.com/Apm5/tensorflow_2.0_tutorial/blob/master/RNN/simple_example.py

你可能感兴趣的:(tensorflow)