首页 > 代码库 > TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载

TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载

http://blog.csdn.net/scotfield_msn/article/details/60339415

在TensorFlow (RNN)深度学习下 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 

双向LSTM+CRF跑序列标注问题

源码下载

 

去年底样子一直在做NLP相关task,是个关于序列标注问题。这 sequence labeling属于NLP的经典问题了,开始尝试用HMM,哦不,用CRF做baseline,by the way, 用的CRF++。

关于CRF的理论就不再啰嗦了,街货。顺便提下,CRF比HMM在理论上以及实际效果上都要好不少。但我要说的是CRF跑我这task还是不太乐观。P值0.6样子,R低的离谱,所以F1很不乐观。mentor告诉我说是特征不足,师兄说是这个task本身就比较难做,F1低算是正常了。

 

CRF做完baseline后,一直在着手用BiLSTM+CRF跑 sequence labeling,奈何项目繁多,没有多余的精力去按照正常的计划做出来。后来还是一点一点的,按照大牛们的步骤以及参考现有的代码,把 BiLSTM+CRF的实现拿下了。后来发现,跑出来的效果也不太理想……可能是这个task确实变态……抑或模型还要加强吧~

 

这里对比下CRF与LSTM的cell,先说RNN吧,RNN其实是比CNN更适合做序列问题的模型,RNN隐层当前时刻的输入有一部分是前一时刻的隐层输出,这使得他能通过循环反馈连接看到前面的信息,将一段序列的前面的context capture 过来参与此刻的计算,并且还具备非线性的拟合能力,这都是CRF无法超越的地方。而LSTM的cell很好的将RNN的梯度弥散问题优化解决了,他对门卫gate说:老兄,有的不太重要的信息,你该忘掉就忘掉吧,免得占用现在的资源。而双向LSTM就更厉害了,不仅看得到过去,还能将未来的序列考虑进来,使得上下文信息充分被利用。而CRF,他不像LSTM能够考虑长远的上下文信息,它更多地考虑整个句子的局部特征的线性加权组合(通过特征模板扫描整个句子),特别的一点,他计算的是联合概率,优化了整个序列,而不是拼接每个时刻的最优值。那么,将BILSTM与CRF一起就构成了还比较不错的组合,这目前也是学术界的流行做法~

 

另外针对目前的跑通结果提几个改进点:

1.+CNN,通过CNN的卷积操作去提取英文单词的字母细节。

2.+char representation,作用与上相似,提取更细粒度的细节。

3.考虑将特定的人工提取的规则融入到NN模型中去。

 

好了,叨了不少。codes time:

 

完整代码以及相关预处理的数据请移步github: scofiled‘s github/bilstm+crf

注明:codes参考的是chilynn

 

requirements:

ubuntu14

python2.7

tensorflow 0.8

numpy

pandas0.15

 

BILSTM_CRF.py

 

[python] view plain copy
 
 技术分享技术分享
  1. import math  
  2. import helper  
  3. import numpy as np  
  4. import tensorflow as tf  
  5. from tensorflow.models.rnn import rnn, rnn_cell  
  6.   
  7. class BILSTM_CRF(object):  
  8.       
  9.     def __init__(self, num_chars, num_classes, num_steps=200, num_epochs=100, embedding_matrix=None, is_training=True, is_crf=True, weight=False):  
  10.         # Parameter  
  11.         self.max_f1 = 0  
  12.         self.learning_rate = 0.002  
  13.         self.dropout_rate = 0.5  
  14.         self.batch_size = 128  
  15.         self.num_layers = 1     
  16.         self.emb_dim = 100  
  17.         self.hidden_dim = 100  
  18.         self.num_epochs = num_epochs  
  19.         self.num_steps = num_steps  
  20.         self.num_chars = num_chars  
  21.         self.num_classes = num_classes  
  22.           
  23.         # placeholder of x, y and weight  
  24.         self.inputs = tf.placeholder(tf.int32, [None, self.num_steps])  
  25.         self.targets = tf.placeholder(tf.int32, [None, self.num_steps])  
  26.         self.targets_weight = tf.placeholder(tf.float32, [None, self.num_steps])  
  27.         self.targets_transition = tf.placeholder(tf.int32, [None])  
  28.           
  29.         # char embedding  
  30.         if embedding_matrix != None:  
  31.             self.embedding = tf.Variable(embedding_matrix, trainable=False, name="emb", dtype=tf.float32)  
  32.         else:  
  33.             self.embedding = tf.get_variable("emb", [self.num_chars, self.emb_dim])  
  34.         self.inputs_emb = tf.nn.embedding_lookup(self.embedding, self.inputs)  
  35.         self.inputs_emb = tf.transpose(self.inputs_emb, [1, 0, 2])  
  36.         self.inputs_emb = tf.reshape(self.inputs_emb, [-1, self.emb_dim])  
  37.         self.inputs_emb = tf.split(0, self.num_steps, self.inputs_emb)  
  38.   
  39.         # lstm cell  
  40.         lstm_cell_fw = tf.nn.rnn_cell.BasicLSTMCell(self.hidden_dim)  
  41.         lstm_cell_bw = tf.nn.rnn_cell.BasicLSTMCell(self.hidden_dim)  
  42.   
  43.         # dropout  
  44.         if is_training:  
  45.             lstm_cell_fw = tf.nn.rnn_cell.DropoutWrapper(lstm_cell_fw, output_keep_prob=(1 - self.dropout_rate))  
  46.             lstm_cell_bw = tf.nn.rnn_cell.DropoutWrapper(lstm_cell_bw, output_keep_prob=(1 - self.dropout_rate))  
  47.   
  48.         lstm_cell_fw = tf.nn.rnn_cell.MultiRNNCell([lstm_cell_fw] * self.num_layers)  
  49.         lstm_cell_bw = tf.nn.rnn_cell.MultiRNNCell([lstm_cell_bw] * self.num_layers)  
  50.   
  51.         # get the length of each sample  
  52.         self.length = tf.reduce_sum(tf.sign(self.inputs), reduction_indices=1)  
  53.         self.length = tf.cast(self.length, tf.int32)    
  54.           
  55.         # forward and backward  
  56.         self.outputs, _, _ = rnn.bidirectional_rnn(  
  57.             lstm_cell_fw,   
  58.             lstm_cell_bw,  
  59.             self.inputs_emb,   
  60.             dtype=tf.float32,  
  61.             sequence_length=self.length  
  62.         )  
  63.           
  64.         # softmax  
  65.         self.outputs = tf.reshape(tf.concat(1, self.outputs), [-1, self.hidden_dim * 2])  
  66.         self.softmax_w = tf.get_variable("softmax_w", [self.hidden_dim * 2, self.num_classes])  
  67.         self.softmax_b = tf.get_variable("softmax_b", [self.num_classes])  
  68.         self.logits = tf.matmul(self.outputs, self.softmax_w) + self.softmax_b  
  69.   
  70.         if not is_crf:  
  71.             pass  
  72.         else:  
  73.             self.tags_scores = tf.reshape(self.logits, [self.batch_size, self.num_steps, self.num_classes])  
  74.             self.transitions = tf.get_variable("transitions", [self.num_classes + 1, self.num_classes + 1])  
  75.               
  76.             dummy_val = -1000  
  77.             class_pad = tf.Variable(dummy_val * np.ones((self.batch_size, self.num_steps, 1)), dtype=tf.float32)  
  78.             self.observations = tf.concat(2, [self.tags_scores, class_pad])  
  79.   
  80.             begin_vec = tf.Variable(np.array([[dummy_val] * self.num_classes + [0] for _ in range(self.batch_size)]), trainable=False, dtype=tf.float32)  
  81.             end_vec = tf.Variable(np.array([[0] + [dummy_val] * self.num_classes for _ in range(self.batch_size)]), trainable=False, dtype=tf.float32)   
  82.             begin_vec = tf.reshape(begin_vec, [self.batch_size, 1, self.num_classes + 1])  
  83.             end_vec = tf.reshape(end_vec, [self.batch_size, 1, self.num_classes + 1])  
  84.   
  85.             self.observations = tf.concat(1, [begin_vec, self.observations, end_vec])  
  86.   
  87.             self.mask = tf.cast(tf.reshape(tf.sign(self.targets),[self.batch_size * self.num_steps]), tf.float32)  
  88.               
  89.             # point score  
  90.             self.point_score = tf.gather(tf.reshape(self.tags_scores, [-1]), tf.range(0, self.batch_size * self.num_steps) * self.num_classes + tf.reshape(self.targets,[self.batch_size * self.num_steps]))  
  91.             self.point_score *= self.mask  
  92.               
  93.             # transition score  
  94.             self.trans_score = tf.gather(tf.reshape(self.transitions, [-1]), self.targets_transition)  
  95.               
  96.             # real score  
  97.             self.target_path_score = tf.reduce_sum(self.point_score) + tf.reduce_sum(self.trans_score)  
  98.               
  99.             # all path score  
  100.             self.total_path_score, self.max_scores, self.max_scores_pre  = self.forward(self.observations, self.transitions, self.length)  
  101.               
  102.             # loss  
  103.             self.loss = - (self.target_path_score - self.total_path_score)  
  104.           
  105.         # summary  
  106.         self.train_summary = tf.scalar_summary("loss", self.loss)  
  107.         self.val_summary = tf.scalar_summary("loss", self.loss)          
  108.           
  109.         self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate).minimize(self.loss)   
  110.   
  111.     def logsumexp(self, x, axis=None):  
  112.         x_max = tf.reduce_max(x, reduction_indices=axis, keep_dims=True)  
  113.         x_max_ = tf.reduce_max(x, reduction_indices=axis)  
  114.         return x_max_ + tf.log(tf.reduce_sum(tf.exp(x - x_max), reduction_indices=axis))  
  115.   
  116.     def forward(self, observations, transitions, length, is_viterbi=True, return_best_seq=True):  
  117.         length = tf.reshape(length, [self.batch_size])  
  118.         transitions = tf.reshape(tf.concat(0, [transitions] * self.batch_size), [self.batch_size, 6, 6])  
  119.         observations = tf.reshape(observations, [self.batch_size, self.num_steps + 2, 6, 1])  
  120.         observations = tf.transpose(observations, [1, 0, 2, 3])  
  121.         previous = observations[0, :, :, :]  
  122.         max_scores = []  
  123.         max_scores_pre = []  
  124.         alphas = [previous]  
  125.         for t in range(1, self.num_steps + 2):  
  126.             previous = tf.reshape(previous, [self.batch_size, 6, 1])  
  127.             current = tf.reshape(observations[t, :, :, :], [self.batch_size, 1, 6])  
  128.             alpha_t = previous + current + transitions  
  129.             if is_viterbi:  
  130.                 max_scores.append(tf.reduce_max(alpha_t, reduction_indices=1))  
  131.                 max_scores_pre.append(tf.argmax(alpha_t, dimension=1))  
  132.             alpha_t = tf.reshape(self.logsumexp(alpha_t, axis=1), [self.batch_size, 6, 1])  
  133.             alphas.append(alpha_t)  
  134.             previous = alpha_t             
  135.               
  136.         alphas = tf.reshape(tf.concat(0, alphas), [self.num_steps + 2, self.batch_size, 6, 1])  
  137.         alphas = tf.transpose(alphas, [1, 0, 2, 3])  
  138.         alphas = tf.reshape(alphas, [self.batch_size * (self.num_steps + 2), 6, 1])  
  139.   
  140.         last_alphas = tf.gather(alphas, tf.range(0, self.batch_size) * (self.num_steps + 2) + length)  
  141.         last_alphas = tf.reshape(last_alphas, [self.batch_size, 6, 1])  
  142.   
  143.         max_scores = tf.reshape(tf.concat(0, max_scores), (self.num_steps + 1, self.batch_size, 6))  
  144.         max_scores_pre = tf.reshape(tf.concat(0, max_scores_pre), (self.num_steps + 1, self.batch_size, 6))  
  145.         max_scores = tf.transpose(max_scores, [1, 0, 2])  
  146.         max_scores_pre = tf.transpose(max_scores_pre, [1, 0, 2])  
  147.   
  148.         return tf.reduce_sum(self.logsumexp(last_alphas, axis=1)), max_scores, max_scores_pre          
  149.   
  150.     def train(self, sess, save_file, X_train, y_train, X_val, y_val):  
  151.         saver = tf.train.Saver()  
  152.   
  153.         char2id, id2char = helper.loadMap("char2id")  
  154.         label2id, id2label = helper.loadMap("label2id")  
  155.   
  156.         merged = tf.merge_all_summaries()  
  157.         summary_writer_train = tf.train.SummaryWriter(‘loss_log/train_loss‘, sess.graph)    
  158.         summary_writer_val = tf.train.SummaryWriter(‘loss_log/val_loss‘, sess.graph)       
  159.           
  160.         num_iterations = int(math.ceil(1.0 * len(X_train) / self.batch_size))  
  161.   
  162.         cnt = 0  
  163.         for epoch in range(self.num_epochs):  
  164.             # shuffle train in each epoch  
  165.             sh_index = np.arange(len(X_train))  
  166.             np.random.shuffle(sh_index)  
  167.             X_train = X_train[sh_index]  
  168.             y_train = y_train[sh_index]  
  169.             print "current epoch: %d" % (epoch)  
  170.             for iteration in range(num_iterations):  
  171.                 # train  
  172.                 X_train_batch, y_train_batch = helper.nextBatch(X_train, y_train, start_index=iteration * self.batch_size, batch_size=self.batch_size)  
  173.                 y_train_weight_batch = 1 + np.array((y_train_batch == label2id[‘B‘]) | (y_train_batch == label2id[‘E‘]), float)  
  174.                 transition_batch = helper.getTransition(y_train_batch)  
  175.                   
  176.                 _, loss_train, max_scores, max_scores_pre, length, train_summary =\  
  177.                     sess.run([  
  178.                         self.optimizer,   
  179.                         self.loss,   
  180.                         self.max_scores,   
  181.                         self.max_scores_pre,   
  182.                         self.length,  
  183.                         self.train_summary  
  184.                     ],   
  185.                     feed_dict={  
  186.                         self.targets_transition:transition_batch,   
  187.                         self.inputs:X_train_batch,   
  188.                         self.targets:y_train_batch,   
  189.                         self.targets_weight:y_train_weight_batch  
  190.                     })  
  191.   
  192.                 predicts_train = self.viterbi(max_scores, max_scores_pre, length, predict_size=self.batch_size)  
  193.                 if iteration % 10 == 0:  
  194.                     cnt += 1  
  195.                     precision_train, recall_train, f1_train = self.evaluate(X_train_batch, y_train_batch, predicts_train, id2char, id2label)  
  196.                     summary_writer_train.add_summary(train_summary, cnt)  
  197.                     print "iteration: %5d, train loss: %5d, train precision: %.5f, train recall: %.5f, train f1: %.5f" % (iteration, loss_train, precision_train, recall_train, f1_train)    
  198.                       
  199.                 # validation  
  200.                 if iteration % 100 == 0:  
  201.                     X_val_batch, y_val_batch = helper.nextRandomBatch(X_val, y_val, batch_size=self.batch_size)  
  202.                     y_val_weight_batch = 1 + np.array((y_val_batch == label2id[‘B‘]) | (y_val_batch == label2id[‘E‘]), float)  
  203.                     transition_batch = helper.getTransition(y_val_batch)  
  204.                       
  205.                     loss_val, max_scores, max_scores_pre, length, val_summary =\  
  206.                         sess.run([  
  207.                             self.loss,   
  208.                             self.max_scores,   
  209.                             self.max_scores_pre,   
  210.                             self.length,  
  211.                             self.val_summary  
  212.                         ],   
  213.                         feed_dict={  
  214.                             self.targets_transition:transition_batch,   
  215.                             self.inputs:X_val_batch,   
  216.                             self.targets:y_val_batch,   
  217.                             self.targets_weight:y_val_weight_batch  
  218.                         })  
  219.                       
  220.                     predicts_val = self.viterbi(max_scores, max_scores_pre, length, predict_size=self.batch_size)  
  221.                     precision_val, recall_val, f1_val = self.evaluate(X_val_batch, y_val_batch, predicts_val, id2char, id2label)  
  222.                     summary_writer_val.add_summary(val_summary, cnt)  
  223.                     print "iteration: %5d, valid loss: %5d, valid precision: %.5f, valid recall: %.5f, valid f1: %.5f" % (iteration, loss_val, precision_val, recall_val, f1_val)  
  224.   
  225.                     if f1_val > self.max_f1:  
  226.                         self.max_f1 = f1_val  
  227.                         save_path = saver.save(sess, save_file)  
  228.                         print "saved the best model with f1: %.5f" % (self.max_f1)  
  229.   
  230.     def test(self, sess, X_test, X_test_str, output_path):  
  231.         char2id, id2char = helper.loadMap("char2id")  
  232.         label2id, id2label = helper.loadMap("label2id")  
  233.         num_iterations = int(math.ceil(1.0 * len(X_test) / self.batch_size))  
  234.         print "number of iteration: " + str(num_iterations)  
  235.         with open(output_path, "wb") as outfile:  
  236.             for i in range(num_iterations):  
  237.                 print "iteration: " + str(i + 1)  
  238.                 results = []  
  239.                 X_test_batch = X_test[i * self.batch_size : (i + 1) * self.batch_size]  
  240.                 X_test_str_batch = X_test_str[i * self.batch_size : (i + 1) * self.batch_size]  
  241.                 if i == num_iterations - and len(X_test_batch) < self.batch_size:  
  242.                     X_test_batch = list(X_test_batch)  
  243.                     X_test_str_batch = list(X_test_str_batch)  
  244.                     last_size = len(X_test_batch)  
  245.                     X_test_batch += [[for j in range(self.num_steps)] for i in range(self.batch_size - last_size)]  
  246.                     X_test_str_batch += [[‘x‘ for j in range(self.num_steps)] for i in range(self.batch_size - last_size)]  
  247.                     X_test_batch = np.array(X_test_batch)  
  248.                     X_test_str_batch = np.array(X_test_str_batch)  
  249.                     results = self.predictBatch(sess, X_test_batch, X_test_str_batch, id2label)  
  250.                     results = results[:last_size]  
  251.                 else:  
  252.                     X_test_batch = np.array(X_test_batch)  
  253.                     results = self.predictBatch(sess, X_test_batch, X_test_str_batch, id2label)  
  254.                   
  255.                 for i in range(len(results)):  
  256.                     doc = ‘‘.join(X_test_str_batch[i])  
  257.                     outfile.write(doc + "<@>" +" ".join(results[i]).encode("utf-8") + "\n")  
  258.   
  259.     def viterbi(self, max_scores, max_scores_pre, length, predict_size=128):  
  260.         best_paths = []  
  261.         for m in range(predict_size):  
  262.             path = []  
  263.             last_max_node = np.argmax(max_scores[m][length[m]])  
  264.             # last_max_node = 0  
  265.             for t in range(1, length[m] + 1)[::-1]:  
  266.                 last_max_node = max_scores_pre[m][t][last_max_node]  
  267.                 path.append(last_max_node)  
  268.             path = path[::-1]  
  269.             best_paths.append(path)  
  270.         return best_paths  
  271.   
  272.     def predictBatch(self, sess, X, X_str, id2label):  
  273.         results = []  
  274.         length, max_scores, max_scores_pre = sess.run([self.length, self.max_scores, self.max_scores_pre], feed_dict={self.inputs:X})  
  275.         predicts = self.viterbi(max_scores, max_scores_pre, length, self.batch_size)  
  276.         for i in range(len(predicts)):  
  277.             x = ‘‘.join(X_str[i]).decode("utf-8")  
  278.             y_pred = ‘‘.join([id2label[val] for val in predicts[i] if val != and val != 0])  
  279.             entitys = helper.extractEntity(x, y_pred)  
  280.             results.append(entitys)  
  281.         return results  
  282.   
  283.     def evaluate(self, X, y_true, y_pred, id2char, id2label):  
  284.         precision = -1.0  
  285.         recall = -1.0  
  286.         f1 = -1.0  
  287.         hit_num = 0  
  288.         pred_num = 0  
  289.         true_num = 0  
  290.         for i in range(len(y_true)):  
  291.             x = ‘‘.join([str(id2char[val].encode("utf-8")) for val in X[i]])  
  292.             y = ‘‘.join([str(id2label[val].encode("utf-8")) for val in y_true[i]])  
  293.             y_hat = ‘‘.join([id2label[val] for val in y_pred[i]  if val != 5])  
  294.             true_labels = helper.extractEntity(x, y)  
  295.             pred_labels = helper.extractEntity(x, y_hat)  
  296.             hit_num += len(set(true_labels) & set(pred_labels))  
  297.             pred_num += len(set(pred_labels))  
  298.             true_num += len(set(true_labels))  
  299.         if pred_num != 0:  
  300.             precision = 1.0 * hit_num / pred_num  
  301.         if true_num != 0:  
  302.             recall = 1.0 * hit_num / true_num  
  303.         if precision > and recall > 0:  
  304.             f1 = 2.0 * (precision * recall) / (precision + recall)  
  305.         return precision, recall, f1    



 

util.py

 

[python] view plain copy
 
 技术分享技术分享
  1. #encoding:utf-8  
  2. import re  
  3. import os  
  4. import csv  
  5. import time  
  6. import pickle  
  7. import numpy as np  
  8. import pandas as pd  
  9.   
  10. def getEmbedding(infile_path="embedding"):  
  11.     char2id, id_char = loadMap("char2id")  
  12.     row_index = 0  
  13.     with open(infile_path, "rb") as infile:  
  14.         for row in infile:  
  15.             row = row.strip()  
  16.             row_index += 1  
  17.             if row_index == 1:  
  18.                 num_chars = int(row.split()[0])  
  19.                 emb_dim = int(row.split()[1])  
  20.                 emb_matrix = np.zeros((len(char2id.keys()), emb_dim))  
  21.                 continue  
  22.             items = row.split()  
  23.             char = items[0]  
  24.             emb_vec = [float(val) for val in items[1:]]  
  25.             if char in char2id:  
  26.                 emb_matrix[char2id[char]] = emb_vec  
  27.     return emb_matrix  
  28.   
  29. def nextBatch(X, y, start_index, batch_size=128):  
  30.     last_index = start_index + batch_size  
  31.     X_batch = list(X[start_index:min(last_index, len(X))])  
  32.     y_batch = list(y[start_index:min(last_index, len(X))])  
  33.     if last_index > len(X):  
  34.         left_size = last_index - (len(X))  
  35.         for i in range(left_size):  
  36.             index = np.random.randint(len(X))  
  37.             X_batch.append(X[index])  
  38.             y_batch.append(y[index])  
  39.     X_batch = np.array(X_batch)  
  40.     y_batch = np.array(y_batch)  
  41.     return X_batch, y_batch  
  42.   
  43. def nextRandomBatch(X, y, batch_size=128):  
  44.     X_batch = []  
  45.     y_batch = []  
  46.     for i in range(batch_size):  
  47.         index = np.random.randint(len(X))  
  48.         X_batch.append(X[index])  
  49.         y_batch.append(y[index])  
  50.     X_batch = np.array(X_batch)  
  51.     y_batch = np.array(y_batch)  
  52.     return X_batch, y_batch  
  53.   
  54. # use "0" to padding the sentence  
  55. def padding(sample, seq_max_len):  
  56.     for i in range(len(sample)):  
  57.         if len(sample[i]) < seq_max_len:  
  58.             sample[i] += [for _ in range(seq_max_len - len(sample[i]))]  
  59.     return sample  
  60.   
  61. def prepare(chars, labels, seq_max_len, is_padding=True):  
  62.     X = []  
  63.     y = []  
  64.     tmp_x = []  
  65.     tmp_y = []  
  66.   
  67.     for record in zip(chars, labels):  
  68.         c = record[0]  
  69.         l = record[1]  
  70.         # empty line  
  71.         if c == -1:  
  72.             if len(tmp_x) <= seq_max_len:  
  73.                 X.append(tmp_x)  
  74.                 y.append(tmp_y)  
  75.             tmp_x = []  
  76.             tmp_y = []  
  77.         else:  
  78.             tmp_x.append(c)  
  79.             tmp_y.append(l)   
  80.     if is_padding:  
  81.         X = np.array(padding(X, seq_max_len))  
  82.     else:  
  83.         X = np.array(X)  
  84.     y = np.array(padding(y, seq_max_len))  
  85.   
  86.     return X, y  
  87.   
  88. def extractEntity(sentence, labels):  
  89.     entitys = []  
  90.     re_entity = re.compile(r‘BM*E‘)  
  91.     m = re_entity.search(labels)  
  92.     while m:  
  93.         entity_labels = m.group()  
  94.         start_index = labels.find(entity_labels)  
  95.         entity = sentence[start_index:start_index + len(entity_labels)]  
  96.         labels = list(labels)  
  97.         # replace the "BM*E" with "OO*O"  
  98.         labels[start_index: start_index + len(entity_labels)] = [‘O‘ for i in range(len(entity_labels))]   
  99.         entitys.append(entity)  
  100.         labels = ‘‘.join(labels)  
  101.         m = re_entity.search(labels)  
  102.     return entitys  
  103.   
  104. def loadMap(token2id_filepath):  
  105.     if not os.path.isfile(token2id_filepath):  
  106.         print "file not exist, building map"  
  107.         buildMap()  
  108.   
  109.     token2id = {}  
  110.     id2token = {}  
  111.     with open(token2id_filepath) as infile:  
  112.         for row in infile:  
  113.             row = row.rstrip().decode("utf-8")  
  114.             token = row.split(‘\t‘)[0]  
  115.             token_id = int(row.split(‘\t‘)[1])  
  116.             token2id[token] = token_id  
  117.             id2token[token_id] = token  
  118.     return token2id, id2token  
  119.   
  120. def saveMap(id2char, id2label):  
  121.     with open("char2id", "wb") as outfile:  
  122.         for idx in id2char:  
  123.             outfile.write(id2char[idx] + "\t" + str(idx)  + "\r\n")  
  124.     with open("label2id", "wb") as outfile:  
  125.         for idx in id2label:  
  126.             outfile.write(id2label[idx] + "\t" + str(idx) + "\r\n")  
  127.     print "saved map between token and id"  
  128.   
  129. def buildMap(train_path="train.in"):  
  130.     df_train = pd.read_csv(train_path, delimiter=‘\t‘, quoting=csv.QUOTE_NONE, skip_blank_lines=False, header=None, names=["char", "label"])  
  131.     chars = list(set(df_train["char"][df_train["char"].notnull()]))  
  132.     labels = list(set(df_train["label"][df_train["label"].notnull()]))  
  133.     char2id = dict(zip(chars, range(1, len(chars) + 1)))  
  134.     label2id = dict(zip(labels, range(1, len(labels) + 1)))  
  135.     id2char = dict(zip(range(1, len(chars) + 1), chars))  
  136.     id2label =  dict(zip(range(1, len(labels) + 1), labels))  
  137.     id2char[0] = "<PAD>"  
  138.     id2label[0] = "<PAD>"  
  139.     char2id["<PAD>"] = 0  
  140.     label2id["<PAD>"] = 0  
  141.     id2char[len(chars) + 1] = "<NEW>"  
  142.     char2id["<NEW>"] = len(chars) + 1  
  143.   
  144.     saveMap(id2char, id2label)  
  145.       
  146.     return char2id, id2char, label2id, id2label  
  147.   
  148. def getTrain(train_path, val_path, train_val_ratio=0.99, use_custom_val=False, seq_max_len=200):  
  149.     char2id, id2char, label2id, id2label = buildMap(train_path)  
  150.     df_train = pd.read_csv(train_path, delimiter=‘\t‘, quoting=csv.QUOTE_NONE, skip_blank_lines=False, header=None, names=["char", "label"])  
  151.   
  152.     # map the char and label into id  
  153.     df_train["char_id"] = df_train.char.map(lambda x : -if str(x) == str(np.nan) else char2id[x])  
  154.     df_train["label_id"] = df_train.label.map(lambda x : -if str(x) == str(np.nan) else label2id[x])  
  155.       
  156.     # convert the data in maxtrix  
  157.     X, y = prepare(df_train["char_id"], df_train["label_id"], seq_max_len)  
  158.   
  159.     # shuffle the samples  
  160.     num_samples = len(X)  
  161.     indexs = np.arange(num_samples)  
  162.     np.random.shuffle(indexs)  
  163.     X = X[indexs]  
  164.     y = y[indexs]  
  165.       
  166.     if val_path != None:  
  167.         X_train = X  
  168.         y_train = y   
  169.         X_val, y_val = getTest(val_path, is_validation=True, seq_max_len=seq_max_len)  
  170.     else:  
  171.         # split the data into train and validation set  
  172.         X_train = X[:int(num_samples * train_val_ratio)]  
  173.         y_train = y[:int(num_samples * train_val_ratio)]  
  174.         X_val = X[int(num_samples * train_val_ratio):]  
  175.         y_val = y[int(num_samples * train_val_ratio):]  
  176.   
  177.     print "train size: %d, validation size: %d" %(len(X_train), len(y_val))  
  178.   
  179.     return X_train, y_train, X_val, y_val  
  180.   
  181. def getTest(test_path="test.in", is_validation=False, seq_max_len=200):  
  182.     char2id, id2char = loadMap("char2id")  
  183.     label2id, id2label = loadMap("label2id")  
  184.   
  185.     df_test = pd.read_csv(test_path, delimiter=‘\t‘, quoting=csv.QUOTE_NONE, skip_blank_lines=False, header=None, names=["char", "label"])  
  186.       
  187.     def mapFunc(x, char2id):  
  188.         if str(x) == str(np.nan):  
  189.             return -1  
  190.         elif x.decode("utf-8") not in char2id:  
  191.             return char2id["<NEW>"]  
  192.         else:  
  193.             return char2id[x.decode("utf-8")]  
  194.   
  195.     df_test["char_id"] = df_test.char.map(lambda x:mapFunc(x, char2id))  
  196.     df_test["label_id"] = df_test.label.map(lambda x : -if str(x) == str(np.nan) else label2id[x])  
  197.       
  198.     if is_validation:  
  199.         X_test, y_test = prepare(df_test["char_id"], df_test["label_id"], seq_max_len)  
  200.         return X_test, y_test  
  201.     else:  
  202.         df_test["char"] = df_test.char.map(lambda x : -if str(x) == str(np.nan) else x)  
  203.         X_test, _ = prepare(df_test["char_id"], df_test["char_id"], seq_max_len)  
  204.         X_test_str, _ = prepare(df_test["char"], df_test["char_id"], seq_max_len, is_padding=False)  
  205.         print "test size: %d" %(len(X_test))  
  206.         return X_test, X_test_str  
  207.   
  208. def getTransition(y_train_batch):  
  209.     transition_batch = []  
  210.     for m in range(len(y_train_batch)):  
  211.         y = [5] + list(y_train_batch[m]) + [0]  
  212.         for t in range(len(y)):  
  213.             if t + 1 == len(y):  
  214.                 continue  
  215.             i = y[t]  
  216.             j = y[t + 1]  
  217.             if i == 0:  
  218.                 break  
  219.             transition_batch.append(i * 6 + j)  
  220.     transition_batch = np.array(transition_batch)  
  221.     return transition_batch  


train.py

 

 

[python] view plain copy
 
 技术分享技术分享
  1. import time  
  2. import helper  
  3. import argparse  
  4. import numpy as np  
  5. import pandas as pd  
  6. import tensorflow as tf  
  7. from BILSTM_CRF import BILSTM_CRF  
  8.   
  9. # python train.py train.in model -v validation.in -c char_emb -e 10 -g 2  
  10.   
  11. parser = argparse.ArgumentParser()  
  12. parser.add_argument("train_path", help="the path of the train file")  
  13. parser.add_argument("save_path", help="the path of the saved model")  
  14. parser.add_argument("-v","--val_path", help="the path of the validation file", default=None)  
  15. parser.add_argument("-e","--epoch", help="the number of epoch", default=100, type=int)  
  16. parser.add_argument("-c","--char_emb", help="the char embedding file", default=None)  
  17. parser.add_argument("-g","--gpu", help="the id of gpu, the default is 0", default=0, type=int)  
  18.   
  19. args = parser.parse_args()  
  20.   
  21. train_path = args.train_path  
  22. save_path = args.save_path  
  23. val_path = args.val_path  
  24. num_epochs = args.epoch  
  25. emb_path = args.char_emb  
  26. gpu_config = "/cpu:0"  
  27. #gpu_config = "/gpu:"+str(args.gpu)  
  28. num_steps = 200 # it must consist with the test  
  29.   
  30. start_time = time.time()  
  31. print "preparing train and validation data"  
  32. X_train, y_train, X_val, y_val = helper.getTrain(train_path=train_path, val_path=val_path, seq_max_len=num_steps)  
  33. char2id, id2char = helper.loadMap("char2id")  
  34. label2id, id2label = helper.loadMap("label2id")  
  35. num_chars = len(id2char.keys())  
  36. num_classes = len(id2label.keys())  
  37. if emb_path != None:  
  38.     embedding_matrix = helper.getEmbedding(emb_path)  
  39. else:  
  40.     embedding_matrix = None  
  41.   
  42. print "building model"  
  43. config = tf.ConfigProto(allow_soft_placement=True)  
  44. with tf.Session(config=config) as sess:  
  45.     with tf.device(gpu_config):  
  46.         initializer = tf.random_uniform_initializer(-0.1, 0.1)  
  47.         with tf.variable_scope("model", reuse=None, initializer=initializer):  
  48.             model = BILSTM_CRF(num_chars=num_chars, num_classes=num_classes, num_steps=num_steps, num_epochs=num_epochs, embedding_matrix=embedding_matrix, is_training=True)  
  49.   
  50.         print "training model"  
  51.         tf.initialize_all_variables().run()  
  52.         model.train(sess, save_path, X_train, y_train, X_val, y_val)  
  53.   
  54.         print "final best f1 is: %f" % (model.max_f1)  
  55.   
  56.         end_time = time.time()  
  57.         print "time used %f(hour)" % ((end_time - start_time) / 3600)  


test.py

 

 

[python] view plain copy
 
 技术分享技术分享
  1. import time  
  2. import helper  
  3. import argparse  
  4. import numpy as np  
  5. import pandas as pd  
  6. import tensorflow as tf  
  7. from BILSTM_CRF import BILSTM_CRF  
  8.   
  9. # python test.py model test.in test.out -c char_emb -g 2  
  10.   
  11. parser = argparse.ArgumentParser()  
  12. parser.add_argument("model_path", help="the path of model file")  
  13. parser.add_argument("test_path", help="the path of test file")  
  14. parser.add_argument("output_path", help="the path of output file")  
  15. parser.add_argument("-c","--char_emb", help="the char embedding file", default=None)  
  16. parser.add_argument("-g","--gpu", help="the id of gpu, the default is 0", default=0, type=int)  
  17. args = parser.parse_args()  
  18.   
  19. model_path = args.model_path  
  20. test_path = args.test_path  
  21. output_path = args.output_path  
  22. gpu_config = "/cpu:0"  
  23. emb_path = args.char_emb  
  24. num_steps = 200 # it must consist with the train  
  25.   
  26. start_time = time.time()  
  27.   
  28. print "preparing test data"  
  29. X_test, X_test_str = helper.getTest(test_path=test_path, seq_max_len=num_steps)  
  30. char2id, id2char = helper.loadMap("char2id")  
  31. label2id, id2label = helper.loadMap("label2id")  
  32. num_chars = len(id2char.keys())  
  33. num_classes = len(id2label.keys())  
  34. if emb_path != None:  
  35.     embedding_matrix = helper.getEmbedding(emb_path)  
  36. else:  
  37.     embedding_matrix = None  
  38.   
  39. print "building model"  
  40. config = tf.ConfigProto(allow_soft_placement=True)  
  41. with tf.Session(config=config) as sess:  
  42.     with tf.device(gpu_config):  
  43.         initializer = tf.random_uniform_initializer(-0.1, 0.1)  
  44.         with tf.variable_scope("model", reuse=None, initializer=initializer):  
  45.             model = BILSTM_CRF(num_chars=num_chars, num_classes=num_classes, num_steps=num_steps, embedding_matrix=embedding_matrix, is_training=False)  
  46.   
  47.         print "loading model parameter"  
  48.         saver = tf.train.Saver()  
  49.         saver.restore(sess, model_path)  
  50.   
  51.         print "testing"  
  52.         model.test(sess, X_test, X_test_str, output_path)  
  53.   
  54.         end_time = time.time()  
  55.         print "time used %f(hour)" % ((end_time - start_time) / 3600)  
技术分享



 

相关预处理的数据请参考github: scofiled‘s github/bilstm+crf

TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载