Repository: PaddlePaddle/models

Issue:
example configuration for NER.
- In this example, we would like to show how to do sequence tagging task in PaddlePaddle. We take NER as an example.
- Please refer to [this configuration](https://github.com/lcy-seso/paddle_confs_v1/tree/master/sequence_tagging_for_ner) file written in old PaddlePaddle API.
- The example **must** include the training and the decoding process.
- Please pull your codes and docs into the [sequence_tagging_for_ner](https://github.com/lcy-seso/paddle_confs_v1/blob/master/sequence_tagging_for_ner/ner.conf) directory.


Discussion:
Issue on dataset for NER: Is there any way to access NER labeled data? If not, whether or not testing data can be used for training just like what semantic roles labeling in paddle book do.
I recommend to use Conll 2003 NER [dataset](http://www.cnts.ua.ac.be/conll2003/ner/).
- What is the meaning of the input "ws" in [this configuration](https://github.com/lcy-seso/paddle_confs_v1/tree/master/sequence_tagging_for_ner)?

- The raw data in Conll 2003 NER dataset needs application to NIST, I have sent a request Email.
![image](https://cloud.githubusercontent.com/assets/14105589/25690099/890be8e4-30c0-11e7-9214-dc94928c69c3.png)


Resolving patch:
diff --git a/sequence_tagging_for_ner/conll03.py b/sequence_tagging_for_ner/conll03.py
new file mode 100644
--- /dev/null
+++ b/sequence_tagging_for_ner/conll03.py
@@ -0,0 +1,123 @@
+"""
+Conll03 dataset.
+"""
+
+import tarfile
+import gzip
+import itertools
+import collections
+import re
+import numpy as np
+
+__all__ = ['train', 'test', 'get_dict', 'get_embedding']
+
+UNK_IDX = 0
+
+
+def canonicalize_digits(word):
+    if any([c.isalpha() for c in word]): return word
+    word = re.sub("\d", "DG", word)
+    if word.startswith("DG"):
+        word = word.replace(",", "")  # remove thousands separator
+    return word
+
+
+def canonicalize_word(word, wordset=None, digits=True):
+    word = word.lower()
+    if digits:
+        if (wordset != None) and (word in wordset): return word
+        word = canonicalize_digits(word)  # try to canonicalize numbers
+    if (wordset == None) or (word in wordset): return word
+    else: return "UUUNKKK"  # unknown token
+
+
+def load_dict(filename):
+    d = dict()
+    with open(filename, 'r') as f:
+        for i, line in enumerate(f):
+            d[line.strip()] = i
+    return d
+
+
+def get_dict(vocab_file='data/vocab.txt', target_file='data/target.txt'):
+    """
+    Get the word and label dictionary.
+    """
+    word_dict = load_dict(vocab_file)
+    label_dict = load_dict(target_file)
+    return word_dict, label_dict
+
+
+def get_embedding(emb_file='data/wordVectors.txt'):
+    """
+    Get the trained word vector.
+    """
+    return np.loadtxt(emb_file, dtype=float)
+
+
+def corpus_reader(filename='data/train'):
+    def reader():
+        sentence = []
+        labels = []
+        with open(filename) as f:
+            for line in f:
+                if re.match(r"-DOCSTART-.+", line) or (len(line.strip()) == 0):
+                    if len(sentence) > 0:
+                        yield sentence, labels
+                    sentence = []
+                    labels = []
+                else:
+                    segs = line.strip().split()
+                    sentence.append(segs[0])
+                    # transform from I-TYPE to BIO schema
+                    if segs[-1] != 'O' and (len(labels) == 0 or
+                                            labels[-1][1:] != segs[-1][1:]):
+                        labels.append('B' + segs[-1][1:])
+                    else:
+                        labels.append(segs[-1])
+
+        f.close()
+
+    return reader
+
+
+def reader_creator(corpus_reader, word_dict, label_dict):
+    """
+    Conll03 train set creator.
+
+    The dataset can be obtained according to http://www.clips.uantwerpen.be/conll2003/ner/.
+    It returns a reader creator, each sample in the reader includes word id sequence, label id sequence and raw sentence for purpose of print.
+
+    :return: Training reader creator
+    :rtype: callable
+    """
+
+    def reader():
+        for sentence, labels in corpus_reader():
+            word_idx = [
+                word_dict.get(canonicalize_word(w, word_dict), UNK_IDX)
+                for w in sentence
+            ]
+            mark = [1 if w[0].isupper() else 0 for w in sentence]
+            label_idx = [label_dict.get(w) for w in labels]
+            yield word_idx, mark, label_idx, sentence
+
+    return reader
+
+
+def train(data_file='data/train',
+          vocab_file='data/vocab.txt',
+          target_file='data/target.txt'):
+    return reader_creator(
+        corpus_reader(data_file),
+        word_dict=load_dict(vocab_file),
+        label_dict=load_dict(target_file))
+
+
+def test(data_file='data/test',
+         vocab_file='data/vocab.txt',
+         target_file='data/target.txt'):
+    return reader_creator(
+        corpus_reader(data_file),
+        word_dict=load_dict(vocab_file),
+        label_dict=load_dict(target_file))
diff --git a/sequence_tagging_for_ner/ner.py b/sequence_tagging_for_ner/ner.py
new file mode 100644
--- /dev/null
+++ b/sequence_tagging_for_ner/ner.py
@@ -0,0 +1,267 @@
+import math
+import gzip
+import paddle.v2 as paddle
+import paddle.v2.evaluator as evaluator
+import conll03
+import itertools
+
+# init dataset
+train_data_file = 'data/train'
+test_data_file = 'data/test'
+vocab_file = 'data/vocab.txt'
+target_file = 'data/target.txt'
+emb_file = 'data/wordVectors.txt'
+
+train_data_reader = conll03.train(train_data_file, vocab_file, target_file)
+test_data_reader = conll03.test(test_data_file, vocab_file, target_file)
+word_dict, label_dict = conll03.get_dict(vocab_file, target_file)
+word_vector_values = conll03.get_embedding(emb_file)
+
+# init hyper-params
+word_dict_len = len(word_dict)
+label_dict_len = len(label_dict)
+mark_dict_len = 2
+word_dim = 50
+mark_dim = 5
+hidden_dim = 300
+
+mix_hidden_lr = 1e-3
+default_std = 1 / math.sqrt(hidden_dim) / 3.0
+emb_para = paddle.attr.Param(
+    name='emb', initial_std=math.sqrt(1. / word_dim), is_static=True)
+std_0 = paddle.attr.Param(initial_std=0.)
+std_default = paddle.attr.Param(initial_std=default_std)
+
+
+def d_type(size):
+    return paddle.data_type.integer_value_sequence(size)
+
+
+def ner_net(is_train):
+    word = paddle.layer.data(name='word', type=d_type(word_dict_len))
+    mark = paddle.layer.data(name='mark', type=d_type(mark_dict_len))
+
+    word_embedding = paddle.layer.mixed(
+        name='word_embedding',
+        size=word_dim,
+        input=paddle.layer.table_projection(input=word, param_attr=emb_para))
+    mark_embedding = paddle.layer.mixed(
+        name='mark_embedding',
+        size=mark_dim,
+        input=paddle.layer.table_projection(input=mark, param_attr=std_0))
+    emb_layers = [word_embedding, mark_embedding]
+
+    word_caps_vector = paddle.layer.concat(
+        name='word_caps_vector', input=emb_layers)
+    hidden_1 = paddle.layer.mixed(
+        name='hidden1',
+        size=hidden_dim,
+        act=paddle.activation.Tanh(),
+        bias_attr=std_default,
+        input=[
+            paddle.layer.full_matrix_projection(
+                input=word_caps_vector, param_attr=std_default)
+        ])
+
+    rnn_para_attr = paddle.attr.Param(initial_std=0.0, learning_rate=0.1)
+    hidden_para_attr = paddle.attr.Param(
+        initial_std=default_std, learning_rate=mix_hidden_lr)
+
+    rnn_1_1 = paddle.layer.recurrent(
+        name='rnn1-1',
+        input=hidden_1,
+        act=paddle.activation.Relu(),
+        bias_attr=std_0,
+        param_attr=rnn_para_attr)
+    rnn_1_2 = paddle.layer.recurrent(
+        name='rnn1-2',
+        input=hidden_1,
+        act=paddle.activation.Relu(),
+        reverse=1,
+        bias_attr=std_0,
+        param_attr=rnn_para_attr)
+
+    hidden_2_1 = paddle.layer.mixed(
+        name='hidden2-1',
+        size=hidden_dim,
+        bias_attr=std_default,
+        act=paddle.activation.STanh(),
+        input=[
+            paddle.layer.full_matrix_projection(
+                input=hidden_1, param_attr=hidden_para_attr),
+            paddle.layer.full_matrix_projection(
+                input=rnn_1_1, param_attr=rnn_para_attr)
+        ])
+    hidden_2_2 = paddle.layer.mixed(
+        name='hidden2-2',
+        size=hidden_dim,
+        bias_attr=std_default,
+        act=paddle.activation.STanh(),
+        input=[
+            paddle.layer.full_matrix_projection(
+                input=hidden_1, param_attr=hidden_para_attr),
+            paddle.layer.full_matrix_projection(
+                input=rnn_1_2, param_attr=rnn_para_attr)
+        ])
+
+    rnn_2_1 = paddle.layer.recurrent(
+        name='rnn2-1',
+        input=hidden_2_1,
+        act=paddle.activation.Relu(),
+        reverse=1,
+        bias_attr=std_0,
+        param_attr=rnn_para_attr)
+    rnn_2_2 = paddle.layer.recurrent(
+        name='rnn2-2',
+        input=hidden_2_2,
+        act=paddle.activation.Relu(),
+        bias_attr=std_0,
+        param_attr=rnn_para_attr)
+
+    hidden_3 = paddle.layer.mixed(
+        name='hidden3',
+        size=hidden_dim,
+        bias_attr=std_default,
+        act=paddle.activation.STanh(),
+        input=[
+            paddle.layer.full_matrix_projection(
+                input=hidden_2_1, param_attr=hidden_para_attr),
+            paddle.layer.full_matrix_projection(
+                input=rnn_2_1,
+                param_attr=rnn_para_attr), paddle.layer.full_matrix_projection(
+                    input=hidden_2_2, param_attr=hidden_para_attr),
+            paddle.layer.full_matrix_projection(
+                input=rnn_2_2, param_attr=rnn_para_attr)
+        ])
+
+    output = paddle.layer.mixed(
+        name='output',
+        size=label_dict_len,
+        bias_attr=False,
+        input=[
+            paddle.layer.full_matrix_projection(
+                input=hidden_3, param_attr=std_default)
+        ])
+
+    if is_train:
+        target = paddle.layer.data(name='target', type=d_type(label_dict_len))
+
+        crf_cost = paddle.layer.crf(
+            size=label_dict_len,
+            input=output,
+            label=target,
+            param_attr=paddle.attr.Param(
+                name='crfw',
+                initial_std=default_std,
+                learning_rate=mix_hidden_lr))
+
+        crf_dec = paddle.layer.crf_decoding(
+            size=label_dict_len,
+            input=output,
+            label=target,
+            param_attr=paddle.attr.Param(name='crfw'))
+
+        return crf_cost, crf_dec, target
+    else:
+        predict = paddle.layer.crf_decoding(
+            size=label_dict_len,
+            input=output,
+            param_attr=paddle.attr.Param(name='crfw'))
+
+        return predict
+
+
+def ner_net_train(data_reader=train_data_reader, num_passes=1):
+    # define network topology
+    crf_cost, crf_dec, target = ner_net(is_train=True)
+    evaluator.sum(name='error', input=crf_dec)
+    evaluator.chunk(
+        name='ner_chunk',
+        input=crf_dec,
+        label=target,
+        chunk_scheme='IOB',
+        num_chunk_types=(label_dict_len - 1) / 2)
+
+    # create parameters
+    parameters = paddle.parameters.create(crf_cost)
+    parameters.set('emb', word_vector_values)
+
+    # create optimizer
+    optimizer = paddle.optimizer.Momentum(
+        momentum=0,
+        learning_rate=2e-4,
+        regularization=paddle.optimizer.L2Regularization(rate=8e-4),
+        gradient_clipping_threshold=25,
+        model_average=paddle.optimizer.ModelAverage(
+            average_window=0.5, max_average_window=10000), )
+
+    trainer = paddle.trainer.SGD(
+        cost=crf_cost,
+        parameters=parameters,
+        update_equation=optimizer,
+        extra_layers=crf_dec)
+
+    reader = paddle.batch(
+        paddle.reader.shuffle(data_reader, buf_size=8192), batch_size=64)
+
+    feeding = {'word': 0, 'mark': 1, 'target': 2}
+
+    def event_handler(event):
+        if isinstance(event, paddle.event.EndIteration):
+            if event.batch_id % 100 == 0:
+                print "Pass %d, Batch %d, Cost %f, %s" % (
+                    event.pass_id, event.batch_id, event.cost, event.metrics)
+            if event.batch_id % 1000 == 0:
+                result = trainer.test(reader=reader, feeding=feeding)
+                print "\nTest with Pass %d, Batch %d, %s" % (
+                    event.pass_id, event.batch_id, result.metrics)
+
+        if isinstance(event, paddle.event.EndPass):
+            # save parameters
+            with gzip.open('params_pass_%d.tar.gz' % event.pass_id, 'w') as f:
+                parameters.to_tar(f)
+            result = trainer.test(reader=reader, feeding=feeding)
+            print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)
+
+    trainer.train(
+        reader=reader,
+        event_handler=event_handler,
+        num_passes=num_passes,
+        feeding=feeding)
+
+    return parameters
+
+
+def ner_net_infer(data_reader=test_data_reader, model_file='ner_model.tar.gz'):
+    test_data = []
+    test_sentences = []
+    for item in data_reader():
+        test_data.append([item[0], item[1]])
+        test_sentences.append(item[-1])
+        if len(test_data) == 10:
+            break
+
+    predict = ner_net(is_train=False)
+
+    lab_ids = paddle.infer(
+        output_layer=predict,
+        parameters=paddle.parameters.Parameters.from_tar(gzip.open(model_file)),
+        input=test_data,
+        field='id')
+
+    flat_data = [word for word in itertools.chain.from_iterable(test_sentences)]
+
+    labels_reverse = {}
+    for (k, v) in label_dict.items():
+        labels_reverse[v] = k
+    pre_lab = [labels_reverse[lab_id] for lab_id in lab_ids]
+
+    for word, label in zip(flat_data, pre_lab):
+        print word, label
+
+
+if __name__ == '__main__':
+    paddle.init(use_gpu=False, trainer_count=1)
+    ner_net_train(data_reader=train_data_reader, num_passes=1)
+    ner_net_infer(
+        data_reader=test_data_reader, model_file='params_pass_0.tar.gz')


Test patch:
diff --git a/sequence_tagging_for_ner/data/test b/sequence_tagging_for_ner/data/test
new file mode 100644
--- /dev/null
+++ b/sequence_tagging_for_ner/data/test
@@ -0,0 +1,130 @@
+-DOCSTART- -X- O O
+
+CRICKET NNP I-NP O
+- : O O
+LEICESTERSHIRE NNP I-NP I-ORG
+TAKE NNP I-NP O
+OVER IN I-PP O
+AT NNP I-NP O
+TOP NNP I-NP O
+AFTER NNP I-NP O
+INNINGS NNP I-NP O
+VICTORY NN I-NP O
+. . O O
+
+LONDON NNP I-NP I-LOC
+1996-08-30 CD I-NP O
+
+West NNP I-NP I-MISC
+Indian NNP I-NP I-MISC
+all-rounder NN I-NP O
+Phil NNP I-NP I-PER
+Simmons NNP I-NP I-PER
+took VBD I-VP O
+four CD I-NP O
+for IN I-PP O
+38 CD I-NP O
+on IN I-PP O
+Friday NNP I-NP O
+as IN I-PP O
+Leicestershire NNP I-NP I-ORG
+beat VBD I-VP O
+Somerset NNP I-NP I-ORG
+by IN I-PP O
+an DT I-NP O
+innings NN I-NP O
+and CC O O
+39 CD I-NP O
+runs NNS I-NP O
+in IN I-PP O
+two CD I-NP O
+days NNS I-NP O
+to TO I-VP O
+take VB I-VP O
+over IN I-PP O
+at IN B-PP O
+the DT I-NP O
+head NN I-NP O
+of IN I-PP O
+the DT I-NP O
+county NN I-NP O
+championship NN I-NP O
+. . O O
+
+Their PRP$ I-NP O
+stay NN I-NP O
+on IN I-PP O
+top NN I-NP O
+, , O O
+though RB I-ADVP O
+, , O O
+may MD I-VP O
+be VB I-VP O
+short-lived JJ I-ADJP O
+as IN I-PP O
+title NN I-NP O
+rivals NNS I-NP O
+Essex NNP I-NP I-ORG
+, , O O
+Derbyshire NNP I-NP I-ORG
+and CC I-NP O
+Surrey NNP I-NP I-ORG
+all DT O O
+closed VBD I-VP O
+in RP I-PRT O
+on IN I-PP O
+victory NN I-NP O
+while IN I-SBAR O
+Kent NNP I-NP I-ORG
+made VBD I-VP O
+up RP I-PRT O
+for IN I-PP O
+lost VBN I-NP O
+time NN I-NP O
+in IN I-PP O
+their PRP$ I-NP O
+rain-affected JJ I-NP O
+match NN I-NP O
+against IN I-PP O
+Nottinghamshire NNP I-NP I-ORG
+. . O O
+
+After IN I-PP O
+bowling VBG I-NP O
+Somerset NNP I-NP I-ORG
+out RP I-PRT O
+for IN I-PP O
+83 CD I-NP O
+on IN I-PP O
+the DT I-NP O
+opening NN I-NP O
+morning NN I-NP O
+at IN I-PP O
+Grace NNP I-NP I-LOC
+Road NNP I-NP I-LOC
+, , O O
+Leicestershire NNP I-NP I-ORG
+extended VBD I-VP O
+their PRP$ I-NP O
+first JJ I-NP O
+innings NN I-NP O
+by IN I-PP O
+94 CD I-NP O
+runs VBZ I-VP O
+before IN I-PP O
+being VBG I-VP O
+bowled VBD I-VP O
+out RP I-PRT O
+for IN I-PP O
+296 CD I-NP O
+with IN I-PP O
+England NNP I-NP I-LOC
+discard VBP I-VP O
+Andy NNP I-NP I-PER
+Caddick NNP I-NP I-PER
+taking VBG I-VP O
+three CD I-NP O
+for IN I-PP O
+83 CD I-NP O
+. . O O
+