TF2 RNN篇之Embedding层
在神经网络中,单词的表示向量可以直接通过训练的方式得到,我们把单词的表示层
叫作Embedding 层。Embedding 层负责把单词编码为某个词向量𝒗,它接受的是采用数字
编码的单词编号𝑖,如2 表示“I”,3 表示“me”等,系统总单词数量记为𝑁vocab,输出长
度为𝑛的向量𝒗:
v
⃗
=
f
θ
(
i
∣
N
v
o
c
a
b
,
n
)
\vec v = f_{\theta}(i|N_{vocab},n)
v=fθ(i∣Nvocab,n)
Embedding 层实现起来非常简单,构建一个shape 为[𝑁vocab, 𝑛]的查询表对象table,对于任意的单词编号𝑖,只需要查询到对应位置上的向量并返回即可
v
⃗
=
t
a
b
l
e
[
i
]
\vec v = table[i]
v=table[i]
Embedding 层是可训练的,它可放置在神经网络之前,完成单词到向量的转换,得到的表示向量可以继续通过神经网络完成后续任务,并计算误差ℒ,采用梯度下降算法来实现端到端(end-to-end)的训练。
在 TensorFlow 中,可以通过layers.Embedding(𝑁vocab,𝑛)来定义一个Word Embedding层,其中𝑁vocab参数指定词汇数量,𝑛指定单词向量的长度。例如:
x = tf.range(10) # 生成10个单词的数字编码
x = tf.random.shuffle(x)
# 创建共10个单词,每个单词用长度为4的向量表示的层
net = layers.Embedding(10,4)
out = net(x)
上述代码创建了10 个单词的Embedding 层,每个单词用长度为4 的向量表示,可以传入数字编码为0~9 的输入,得到这4 个单词的词向量,这些词向量随机初始化的,尚未经过网络训练,例如:
<tf.Tensor: id=96, shape=(10, 4), dtype=float32, numpy=
array([[-0.00998075, -0.04006485, 0.03493755, 0.03328368],
[-0.04139598, -0.02630153, -0.01353856, 0.02804044],…
我们可以直接查看Embedding 层内部的查询表table:
In [1]: net.embeddings
Out[1]:
<tf.Variable 'embedding_4/embeddings:0' shape=(10, 4) dtype=float32, numpy=
array([[ 0.04112223, 0.01824595, -0.01841902, 0.00482471],
[-0.00428962, -0.03172196, -0.04929272, 0.04603403],…
并查看net.embeddings 张量的可优化属性为True,即可以通过梯度下降算法优化。
In [2]: net.embeddings.trainable
Out[2]:True
预训练的词向量
Embedding 层的查询表是随机初始化的,需要从零开始训练。实际上,我们可以使用预训练的Word Embedding 模型来得到单词的表示方法,基于预训练模型的词向量相当于迁移了整个语义空间的知识,往往能得到更好的性能。
目前应用的比较广泛的预训练模型有Word2Vec 和GloVe 等。它们已经在海量语料库训练得到了较好的词向量表示方法,并可以直接导出学习到的词向量表,方便迁移到其它任务。比如GloVe 模型GloVe.6B.50d,词汇量为40 万,每个单词使用长度为50 的向量表示,用户只需要下载对应的模型文件即可,“glove6b50dtxt.zip”模型文件约69MB
那么如何使用这些预训练的词向量模型来帮助提升NLP 任务的性能?非常简单,对于Embedding 层,不再采用随机初始化的方式,而是利用我们已经预训练好的模型参数去初始化 Embedding 层的查询表。例如:
# 从预训练模型中加载词向量表
embed_glove = load_embed('glove.6B.50d.txt')
# 直接利用预训练的词向量初始化Embedding层
net.set_weights([embed_glove])
经过预训练的词向量模型初始化的Embedding 层可以设置为不参与训练:net.trainable= False,那么预训练的词向量就直接应用到此特定任务上;如果希望能够学到区别于预训练词向量模型不同的表示方法,那么可以把Embedding 层包含进反向传播算法中去,利用梯度下降来微调单词表示方法。
参考书籍: TensorFlow 深度学习 — 龙龙老师