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
 
 在CODE上查看代碼片派生到我的代碼片
  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
 
 在CODE上查看代碼片派生到我的代碼片
  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
 
 在CODE上查看代碼片派生到我的代碼片
  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
 
 在CODE上查看代碼片派生到我的代碼片
  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM