嵌入层

畅游人工智能之海 带你学习Keras的嵌入层

写在开篇

mbedding 实际上是一种映射,将单词从原先的表示映射到新的多维空间中,在这个多维空间中,词向量之间的距离表征了单词之间的语义相关性。例如,cat 和 kitten语义相近,而cow和cat语义相差就比较远,因此一个好的嵌入模型应该能正确的将cat 和kitten放的比较近,同时cat和cow的距离相对较远。620

类型说明

Embedding

嵌入层 其作用是将正整数(索引值)转换为固定尺寸的稠密向量。例如:[[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]],只能用作模型中的第一层。

1
keras.layers.Embedding(input_dim, output_dim, embeddings_initializer='uniform', embeddings_regularizer=None, activity_regularizer=None, embeddings_constraint=None, mask_zero=False, input_length=None)

例子

1
2
3
4
5
6
7
8
9
10
11
model = Sequential()
model.add(Embedding(1000, 64, input_length=10))
# 模型将输入一个大小为 (batch, input_length) 的整数矩阵。
# 输入中最大的整数(即词索引)不应该大于 999 (词汇表大小)
# 现在 model.output_shape == (None, 10, 64),其中 None 是 batch 的维度。

input_array = np.random.randint(1000, size=(32, 10))

model.compile('rmsprop', 'mse')
output_array = model.predict(input_array)
assert output_array.shape == (32, 10, 64)

参数

  • input_dim: int > 0。词汇表大小, 即,最大整数 index + 1。
  • output_dim: int >= 0。词向量的维度。
  • embeddings_initializer: embeddings 矩阵的初始化方法
  • embeddings_regularizer: embeddings matrix 的正则化方法
  • embeddings_constraint: embeddings matrix 的约束函数
  • mask_zero: 是否把 0 看作为一个应该被遮蔽的特殊的 "padding" 值。这对于可变长的 循环神经网络层 十分有用。如果设定为 True,那么接下来的所有层都必须支持 masking,否则就会抛出异常。如果 mask_zero 为 True,作为结果,索引 0 就不能被用于词汇表中 (input_dim 应该与 vocabulary + 1 大小相同)。

input_length: 输入序列的长度,当它是固定的时。如果你需要连接 FlattenDense 层,则这个参数是必须的 (没有它,dense 层的输出尺寸就无法计算)。

输入尺寸

尺寸为 (batch_size, sequence_length) 的 2D 张量。

输出尺寸

尺寸为 (batch_size, sequence_length, output_dim) 的 3D 张量。

keras中的Embedding层有两种词嵌入的方式,如果需要语义特征,可以使用预训练的方式,通过参数指定预训练词向量矩阵,该词向量矩阵可以是基于任何语言模型,如Word2vec、Glove等。如果不需要语义特征,还有另一种方式是随机初始化,Embedding在随机初始化方式下是一个全连接层,而后面得到的词表示是全连接层的权重参数。

实验例子

利用GloVe预训练词向量对新闻文本进行分类

篇幅有限,只保留主要部分,所有代码请参考 在Keras模型中使用预训练的词向量

  • 数据预处理:

    先遍历下语料文件下的所有文件夹,获得不同类别的新闻以及对应的类别标签

  • Embedding layer设置:

    接下来,从GloVe文件中解析出每个词和它所对应的词向量,并用字典的方式存储

1
2
3
4
5
6
7
8
embeddings_index = {}
f = open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'))
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()

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

1
2
3
4
5
6
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words not found in embedding index will be all-zeros.
embedding_matrix[i] = embedding_vector

现在将这个词向量矩阵加载到Embedding层中,注意,trainable=False使得这个编码层不可再训练。

1
2
3
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行,indeces列的矩阵。每一次的batch训练的输入应该被padded成相同大小。所有的序列中的整数都将被对应的词向量矩阵中对应的列(也就是它的词向量)代替,比如序列[1,2]将被序列[词向量[1],词向量[2]]代替。这样,输入一个2D张量后,我们可以得到一个3D张量。

训练1D卷积

最后,可以使用一个小型的1D卷积解决这个新闻分类问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedded_sequences = embedding_layer(sequence_input)
x = Conv1D(128, 5, activation='relu')(embedded_sequences)
x = MaxPooling1D(5)(x)
x = Conv1D(128, 5, activation='relu')(x)
x = MaxPooling1D(5)(x)
x = Conv1D(128, 5, activation='relu')(x)
x = MaxPooling1D(35)(x) # global max pooling
x = Flatten()(x)
x = Dense(128, activation='relu')(x)
preds = Dense(len(labels_index), activation='softmax')(x)

model = Model(sequence_input, preds)
model.compile(loss='categorical_crossentropy',
optimizer='rmsprop',
metrics=['acc'])

# happy learning!
model.fit(x_train, y_train, validation_data=(x_val, y_val),
nb_epoch=2, batch_size=128)

在两次迭代之后,这个模型最后可以达到0.95的分类准确率(4:1分割训练和测试集合)。也可以利用正则方法(例如dropout)或在Embedding层上进行fine-tuning获得更高的准确率。

作为对比,也可以直接使用keras自带的Embedding层训练词向量而不用GloVe向量。

1
2
3
embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
input_length=MAX_SEQUENCE_LENGTH)

写在文末

相信大家经过今天的学习,能够获得对embedding层有一个初步的认识,感兴趣的小伙伴可以自己动手做做实验,我们下次见!