当前位置:   article > 正文

在浏览器中进行深度学习:TensorFlow.js (九)训练词向量 Word Embedding

visualize embedding

词向量,英文名叫Word Embedding,在自然语言处理中,用于抽取语言模型中的特征,简单来说,就是把单词用一个向量来表示。最著名的Word Embedding模型应该是托马斯·米科洛夫(Tomas Mikolov)在Google带领的研究团队创造的Word2vec。

词向量的训练原理就是为了构建一个语言模型,我们假定一个词的出现概率是由它的上下问来决定的,那么我们找来很多的语素来训练这个模型,也就是通过上下文来预测某个词语出现的概率。

âCBOW skip n gramâçå¾çæç´¢ç»æ

如上图所示,词嵌入向量的训练主要有两种模式:

  • 连续词袋 CBOW, 在这个方法中,我们用出现在该单词的上下文的词来预测该单词出现的概率,如上图就是该单词的前两个和后两个。然后我们可以扫描全部的训练语素(所有的句子),对于每一次出现的词都找到对于的上下文的4个词,这样我们就可以构建一个训练集合来训练词向量了。
  • Skip-Gram和CBOW正好相反,它是用该单词来预测前后的4个上下文的单词。注意这里和上面的4个都是例子,你可以选择上下文的长度。

那么训练出来的词向量它的含义是什么呢?

âWord2Vec 中æâçå¾çæç´¢ç»æ

词向量是该单词映射到一个n维空间的表示,首先,所有的单词只有在表示为数学上的向量后在能参与神经网络的运算,其次,单词在空间中的位置反映了词与词之间的关系,距离相近的词可能意味着它们有相近的含义,或者经常一出现。

用神经网络构建语言模型的时候,Embedding常常是作为第一个层出现的,它就是从文本中提取数字化的特征。那么我们今天就看看如何利用TensorflowJS在训练一个词向量嵌入模型吧。

倒入文本

首先倒入我的文本,这里我的文本很简单,你可以替换任何你想要训练的文本

const sentence = "Mary and Samantha arrived at the bus station early but waited until noon for the bus.";

抽取单词和编码

然后,抽取文本中所有的单词序列,在自然语言处理中,Tokenize是意味着把文本变成序列,我这个例子中的单词的抽取用了很简单的regular expression,实际的应用中,你可以使用不同的自然语言处理库提供的Tokenize方法。TensorflowJS中并没有提供Tokenize的方法。(Tensorflow 中由提供 https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/preprocessing/text/Tokenizer

  1. const tokenize = words => {
  2. return words.match(/[^\s\.]+/g);
  3. }
  4. // tokenize
  5. const tokens = tokenize(sentence);

单词序列如下:

["Mary", "and", "Samantha", "arrived", "at", "the", "bus", "station", "early", "but", "waited", "until", "noon", "for", "the", "bus"]

下一步我们要对所有的单词编码,也就是用数字来表示每一个单词

  1. const encode = tokens => {
  2. let encoding_map = {};
  3. let decoding_map = {};
  4. let index = 0;
  5. tokens.map( token => {
  6. if( !encoding_map.hasOwnProperty(token) ) {
  7. const pair = {};
  8. const unpair = {};
  9. pair[token] = index;
  10. unpair[index] = token;
  11. encoding_map = {...encoding_map, ...pair};
  12. decoding_map = {...decoding_map, ...unpair};
  13. index++;
  14. }
  15. })
  16. return {
  17. map: encoding_map,
  18. count: index,
  19. encode: function(word) {
  20. return encoding_map[word];
  21. },
  22. decode: function(index) {
  23. return decoding_map[index];
  24. }
  25. };
  26. }
  27. const encoding = encode(tokens);
  28. const vocab_size = encoding.count;

编码的方式很简单,我们统计每一个出现的单词,然后给每一个单词一个对应的数字。我们使用了两个map,一个存放从单词到数字索引的映射,另一个存放相反的从索引到单词的映射。这个例子中,一种出现了14个单词,那么索引的数字就是从0到13。

准备训练数据

下一步,我们来准备训练数据:

  1. const to_one_hot = (index, size) => {
  2. return tf.oneHot(index, size);
  3. }
  4. // training data
  5. const data = [];
  6. const window_size = 2 + 1;
  7. for ( let i = 0; i < tokens.length; i ++ ) {
  8. const token = tokens[i];
  9. for ( let j = i - window_size; j < i + window_size; j ++) {
  10. if ( j >= 0 && j !=i && j < tokens.length) {
  11. data.push( [ token, tokens[j]] )
  12. }
  13. }
  14. }
  15. const x_train_data = [];
  16. const y_train_data = [];
  17. data.map( pair => {
  18. x = to_one_hot(encoding.encode(pair[0]), vocab_size);
  19. y = to_one_hot(encoding.encode(pair[1]), vocab_size);
  20. x_train_data.push(x);
  21. y_train_data.push(y);
  22. })
  23. const x_train = tf.stack(x_train_data);
  24. const y_train = tf.stack(y_train_data);
  25. console.log(x_train.shape);
  26. console.log(y_train.shape);

one_hot encoding是一种常用的编码方式,例如,对于索引为2的单词,它的one_hot encoding 就是[0,0,1 .... 0], 就是索引位是1其它都是0 的向量,向量的长度和所有单词的数量相等。这里我们定义的上下文滑动窗口的大小为2,对于每一个词,找到它的前后出现的4个单词构成4对,用该词作为训练的输入,上下文的四个词作为目标。(注意在文首尾出的词上下文不足四个)

  1. 0: (2) ["Mary", "and"]
  2. 1: (2) ["Mary", "Samantha"]
  3. 2: (2) ["and", "Mary"]
  4. 3: (2) ["and", "Samantha"]
  5. 4: (2) ["and", "arrived"]
  6. 5: (2) ["Samantha", "Mary"]
  7. 6: (2) ["Samantha", "and"]
  8. 7: (2) ["Samantha", "arrived"]
  9. 8: (2) ["Samantha", "at"]
  10. 9: (2) ["arrived", "Mary"]
  11. 10: (2) ["arrived", "and"]
  12. ... ...

训练集合如上图所示,第一个词是训练的输入,第二次的训练的目标。我们这里采用的方法类似Skip-Gram,因为上下文是预测对象。

模型构建和训练

训练集合准备好,就可以用开始构建模型了。

  1. const build_model = (input_size,output_size) => {
  2. const model = tf.sequential();
  3. model.add(tf.layers.dense({
  4. units: 2, inputShape: output_size, name:'embedding'
  5. }));
  6. model.add(tf.layers.dense(
  7. {units: output_size, kernelInitializer: 'varianceScaling', activation: 'softmax'}));
  8. return model;
  9. }
  10. const model = build_model(vocab_size, vocab_size);
  11. model.compile({
  12. optimizer: tf.train.adam(),
  13. loss: tf.losses.softmaxCrossEntropy,
  14. metrics: ['accuracy'],
  15. });

我们的模型很简单,是一个两层的神经网络,第一层就是我们要训练的嵌入层,第二层是一个激活函数为Softmax的Dense层。因为我们的目标是预测究竟是哪一个单词,其实就是一个分类问题。这里要注意得是我是用的嵌入层的unit是2,也就是说训练的向量的长度是2,实际用户可以选择任何长度的词向量空间,这里我用2是为了便于下面的词向量的可视化,省去了降维的操作。

训练的过程也很简单:

  1. const batchSize = 16;
  2. const epochs = 500;
  3. model.fit(x_train, y_train, {
  4. batchSize,
  5. epochs,
  6. shuffle: true,
  7. });

可视化词向量

训练完成后,我们可以利用该模型的embeding层来生成每一个单词的嵌入向量。然后在二维空间中展示。

  1. // visualize embedding layer
  2. const embedding_layer = model.getLayer('embedding');
  3. const vis_model = tf.sequential();
  4. vis_model.add(embedding_layer);
  5. const vis_result = vis_model.predict(predict_inputs).arraySync();
  6. console.log(vis_result);
  7. const viz_data = [];
  8. for ( let i = 0; i < vocab_size; i ++ ) {
  9. const word = encoding.decode(i);
  10. const pos = vis_result[i];
  11. console.log(word,pos);
  12. viz_data.push( { label:word, x:pos[0], y :pos[1]});
  13. }
  14. const chart = new G2.Chart({
  15. container: 'chart',
  16. width: 600,
  17. height: 600
  18. });
  19. chart.source(viz_data);
  20. chart.point().position('x*y').label('label');
  21. chart.render();

生成的词向量的例子如下:

  1. "Mary" [-0.04273216053843498, -0.18541619181632996]
  2. "and" [0.09561611711978912, -0.29422900080680847]
  3. "Samantha" [0.08887559175491333, 0.019271137192845345]
  4. "arrived" [-0.47705259919166565, -0.024428391829133034]

可视化关系如下图:

9a02e47c4486850938654620101251f23aa.jpg

 

总结

词向量嵌入常常是自然语言处理的第一步操作,用于提取文本特征。我们演示了如何训练一个模型来构建词向量。当然实际操作中,你可以直接使用https://js.tensorflow.org/api/latest/#layers.embedding 来构建你的文本模型,本文是为了演示词向量的基本原理。代码参见https://codepen.io/gangtao/full/jJqbQb

 

参考

转载于:https://my.oschina.net/taogang/blog/3023996

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/358195
推荐阅读
相关标签
  

闽ICP备14008679号