用深度神经网络处理NER命名实体识别问题

作者: 不会停的蜗牛 2017-05-31 12:59:44

本文结构:
 

  1. 什么是命名实体识别(NER)
  2. 怎么识别?

cs224d Day 7: 项目2-用DNN处理NER问题

课程项目描述地址

什么是NER?

命名实体识别(NER)是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等。命名实体识别是信息提取、问答系统、句法分析、机器翻译等应用领域的重要基础工具,作为结构化信息提取的重要步骤。摘自 BosonNLP

怎么识别?

先把解决问题的逻辑说一下,然后解释主要的代码,有兴趣的话,完整代码请去 这里看 。

代码是在 Tensorflow 下建立只有一个隐藏层的 DNN 来处理 NER 问题。

1.问题识别:

NER 是个分类问题。

给一个单词,我们需要根据上下文判断,它属于下面四类的哪一个,如果都不属于,则类别为0,即不是实体,所以这是一个需要分成 5 类的问题:

  1. • Person (PER) 
  2. • Organization (ORG) 
  3. • Location (LOC) 
  4. • Miscellaneous (MISC) 

我们的训练数据有两列,***列是单词,第二列是标签。

  1. EU ORG 
  2. rejects O 
  3. German MISC 
  4. Peter PER 
  5. BRUSSELS LOC 

2.模型:

接下来我们用深度神经网络对其进行训练。

模型如下:

输入层的 x^(t) 为以 x_t 为中心的窗口大小为3的上下文语境,x_t 是 one-hot 向量,x_t 与 L 作用后就是相应的词向量,词向量的长度为 d = 50 :

我们建立一个只有一个隐藏层的神经网络,隐藏层维度是 100,y^ 就是得到的预测值,维度是 5:

用交叉熵来计算误差:

J 对各个参数进行求导:

得到如下求导公式:

在 TensorFlow 中求导是自动实现的,这里用Adam优化算法更新梯度,不断地迭代,使得loss越来越小直至收敛。

3.具体实现

在 def test_NER() 中,我们进行 max_epochs 次迭代,每次,用 training data 训练模型 得到一对 train_loss, train_acc ,再用这个模型去预测 validation data,得到一对 val_loss, predictions ,我们选择最小的 val_loss ,并把相应的参数 weights 保存起来,***我们是要用这些参数去预测 test data 的类别标签:

  1. def test_NER(): 
  2. config = Config() 
  3. with tf.Graph().as_default(): 
  4. model = NERModel(config) # 最主要的类 
  5.  
  6. init = tf.initialize_all_variables() 
  7. saver = tf.train.Saver() 
  8.  
  9. with tf.Session() as session: 
  10. best_val_loss = float('inf') # ***的值时,它的 loss 它的 迭代次数 epoch 
  11. best_val_epoch = 0 
  12.  
  13. session.run(init) 
  14. for epoch in xrange(config.max_epochs): 
  15. print 'Epoch {}'.format(epoch) 
  16. start = time.time() 
  17. ### 
  18. train_loss, train_acc = model.run_epoch(session, model.X_train, 
  19. model.y_train) # 1.把 train 数据放进迭代里跑,得到 loss 和 accuracy 
  20. val_loss, predictions = model.predict(session, model.X_dev, model.y_dev) # 2.用这个model去预测 dev 数据,得到loss 和 prediction 
  21. print 'Training loss: {}'.format(train_loss) 
  22. print 'Training acc: {}'.format(train_acc) 
  23. print 'Validation loss: {}'.format(val_loss) 
  24. if val_loss < best_val_loss: # 用 val 数据的loss去找最小的loss 
  25. best_val_loss = val_loss 
  26. best_val_epoch = epoch 
  27. if not os.path.exists("./weights"): 
  28. os.makedirs("./weights"
  29.  
  30. saver.save(session, './weights/ner.weights') # 把最小的 loss 对应的 weights 保存起来 
  31. if epoch - best_val_epoch > config.early_stopping: 
  32. break 
  33. ### 
  34. confusion = calculate_confusion(config, predictions, model.y_dev) # 3.把 dev 的lable数据放进去,计算prediction的confusion 
  35. print_confusion(confusion, model.num_to_tag) 
  36. print 'Total time: {}'.format(time.time() - start) 
  37.  
  38. saver.restore(session, './weights/ner.weights') # 再次加载保存过的 weights,用 test 数据做预测,得到预测结果 
  39. print 'Test' 
  40. print '=-=-=' 
  41. print 'Writing predictions to q2_test.predicted' 
  42. _, predictions = model.predict(session, model.X_test, model.y_test) 
  43. save_predictions(predictions, "q2_test.predicted") # 把预测结果保存起来 
  44.  
  45. if __name__ == "__main__"
  46. test_NER() 

4.模型是怎么训练的呢?

首先导入数据 training,validation,test:

  1. # Load the training set 
  2. docs = du.load_dataset('data/ner/train'
  3.  
  4. # Load the dev set (for tuning hyperparameters) 
  5. docs = du.load_dataset('data/ner/dev'
  6.  
  7. # Load the test set (dummy labels only) 
  8. docs = du.load_dataset('data/ner/test.masked'

把单词转化成 one-hot 向量后,再转化成词向量:

  1. def add_embedding(self): 
  2.   # The embedding lookup is currently only implemented for the CPU 
  3.   with tf.device('/cpu:0'): 
  4.  
  5.     embedding = tf.get_variable('Embedding', [len(self.wv), self.config.embed_size])    # assignment 中的 L     
  6.     window = tf.nn.embedding_lookup(embedding, self.input_placeholder)                # 在 L 中直接把window大小的context的word vector搞定 
  7.     window = tf.reshape( 
  8.       window, [-1self.config.window_size * self.config.embed_size]) 
  9.  
  10.     return window 

建立神经层,包括用 xavier 去初始化***层, L2 正则化和用 dropout 来减小过拟合的处理:

  1. def add_model(self, window): 
  2.  
  3.   with tf.variable_scope('Layer1', initializer=xavier_weight_init()) as scope:        # 用initializer=xavier去初始化***层 
  4.     W = tf.get_variable(                                                                # ***层有 W,b1,h 
  5.         'W', [self.config.window_size * self.config.embed_size, 
  6.               self.config.hidden_size]) 
  7.     b1 = tf.get_variable('b1', [self.config.hidden_size]) 
  8.     h = tf.nn.tanh(tf.matmul(window, W) + b1) 
  9.     if self.config.l2:                                                                # L2 regularization for W 
  10.         tf.add_to_collection('total_loss'0.5 * self.config.l2 * tf.nn.l2_loss(W))    # 0.5 * self.config.l2 * tf.nn.l2_loss(W) 
  11.  
  12.   with tf.variable_scope('Layer2', initializer=xavier_weight_init()) as scope: 
  13.     U = tf.get_variable('U', [self.config.hidden_size, self.config.label_size]) 
  14.     b2 = tf.get_variable('b2', [self.config.label_size]) 
  15.     y = tf.matmul(h, U) + b2 
  16.     if self.config.l2: 
  17.         tf.add_to_collection('total_loss'0.5 * self.config.l2 * tf.nn.l2_loss(U)) 
  18.   output = tf.nn.dropout(y, self.dropout_placeholder)                                    # 返回 output,两个variable_scope都带dropout 
  19.  
  20.   return output 

关于 L2正则化 和 dropout 是什么, 如何减小过拟合问题的,可以看 这篇博客,总结的简单明了。

用 cross entropy 来计算 loss:

  1. def add_loss_op(self, y): 
  2.  
  3.   cross_entropy = tf.reduce_mean(                                                        # 1.关键步骤:loss是用cross entropy定义的 
  4.       tf.nn.softmax_cross_entropy_with_logits(y, self.labels_placeholder))                # y是模型预测值,计算cross entropy 
  5.   tf.add_to_collection('total_loss', cross_entropy)            # Stores value in the collection with the given name. 
  6.                                                               # collections are not sets, it is possible to add a value to a collection several times. 
  7.   loss = tf.add_n(tf.get_collection('total_loss'))            # Adds all input tensors element-wise. inputs: A list of Tensor with same shape and type 
  8.  
  9.   return loss 

接着用 Adam Optimizer 把loss最小化:

  1. def add_training_op(self, loss): 
  2.  
  3.   optimizer = tf.train.AdamOptimizer(self.config.lr) 
  4.   global_step = tf.Variable(0, name='global_step', trainable=False
  5.   train_op = optimizer.minimize(loss, global_step=global_step)    # 2.关键步骤:用 AdamOptimizer 使 loss 达到最小,所以更关键的是 loss 
  6.  
  7.   return train_op 

每一次训练后,得到了最小化 loss 相应的 weights。

这样,NER 这个分类问题就搞定了,当然为了提高精度等其他问题,还是需要查阅文献来学习的。下一次先实现个 RNN。

神经网络 深度学习
上一篇:2017年消费者对人工智能态度调查 下一篇:用深度学习快速人脸建模
评论
取消
暂无评论,快去成为第一个评论的人吧

更多资讯推荐

微软麻将 AI 论文发布,首次公开技术细节

2019 年 8 月 29 日,微软发布了一个名为 Suphx(超级凤凰)的「麻将 AI」,在专业的麻将竞技平台上,Suphx 的实力胜过了顶级人类选手的平均水平。

神经小兮 ·  4天前
全球首个翻译引擎进化归来 “细节狂魔”搞定方言

最近,一款在线机器翻译软件在日本大火。这款翻译软件名叫DeepL,大火的原因正是因为它工作太负责了,翻译得太过准确,在日本引起了热议。

刘俊寰 ·  4天前
体验中国自主知识产权天元深度学习引擎与TensorFlow,PyTorch的对比

深度学习驱动之下最早创业的中国AI独角兽旷视,宣布开源自研深度学习框架MegEngine(Brain++核心组件之一),中文名天元——取自围棋棋盘中心点的名称。

尹成 ·  2020-03-31 10:15:42
2020年深度学习优秀GPU一览,看看哪一款最适合你!

如果你准备进入深度学习,什么样的GPU才是最合适的呢?下面列出了一些适合进行深度学习模型训练的GPU,并将它们进行了横向比较,一起来看看吧!

大数据文摘 ·  2020-03-30 09:42:54
从零开始构建简单人工神经网络:1个隐藏层

我们在本文中将构建一个有1个输入层、1个隐藏层和1个输出层的神经网络。我们会看到,我们构建的神经网络能够找到非线性边界。

布加迪 ·  2020-03-26 09:00:00
AI芯片之卷积神经网络原理

卷积神经网络(Convolutional Neural Network,CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。 它包括卷积层(convolutional layer)和池化层(pooling layer)。

人人都是极客 ·  2020-03-25 09:48:10
从零开始构建简单人工神经网络:1个输入层和1个输出层

本上下篇将介绍仅使用numpy Python库从零开始构建人工神经网络(ANN)。上篇将介绍构建一个很简单的ANN,只有1个输入层和1个输出层,没有隐藏层。下篇将介绍构建一个有1个输入层、1个隐藏层和1个输出层的ANN。

布加迪 ·  2020-03-25 09:00:00
清华开源Jittor:首个国内高校自研深度学习框架,一键转换PyTorch

深度学习框架越来越多,主导的团队也从高校研究机构渐渐转向了科技巨头。但是,学界在这一领域的力量不容忽视。今日。清华大学开发了一个名为计图(Jittor)的深度学习框架。

佚名 ·  2020-03-20 14:33:29
Copyright©2005-2020 51CTO.COM 版权所有 未经许可 请勿转载