/code-for-nlp-beginner

code for nlp beginner, including Sentiment Analysis, NER, NLI and Language Model.

Primary LanguagePython

实现了nlp-beginner的几个任务,一方面自己练练手,另一方面供刚入门的朋友参考。才学疏浅,难免有不少问题,有任何问题可以发issue或者邮箱联系,万分感谢~

任务一:基于机器学习的文本分类

数据集地址:Classify the sentiment of sentences from the Rotten Tomatoes dataset

  1. sklearn实现,n-gram特征提取直接用sklearn内置的CountVectorizer

  2. numpy实现:

    • 自定义n-gram特征抽取类,用scipycsr_matrix来保存doc-ngram稀疏矩阵;
    • numpy实现用于二分类的Logistic Regression及用于多分类的Softmax Regression
    • 实现三种梯度更新方式:BGDSGD以及MBGD
    方法 参数 准确度
    LR (sklearn) C=0.8; penalty='l1' 0.587
    SoftmaxRegression (numpy) C=0.8; penalty='l1' 0.548

任务二:基于深度学习的文本分类

数据集地址:Classify the sentiment of sentences from the Rotten Tomatoes dataset

  1. 老老实实一步步写分词、构建词典、数据向量化、加载词向量等;

  2. pytorchRNN, GRU, LSTM以及TextCNN,其中自己编写的RNN, GRU, LSTM模型已经测试与pytorch内部nn.xxx一致。

  3. torchtext简化数据的处理操作;

    方法 参数 准确度
    RNN epoch=5; hidden_size = 256; num_layers = 1; bidirectional = True; random embedding 0.629
    RNN epoch=5; hidden_size = 256; num_layers = 1; bidirectional = True; glove_200 embedding 0.633
    CNN epoch=5; num_filters = 200; kernel_sizes = [2, 3, 4]; random embedding 0.654
    CNN epoch=5; num_filters = 200; kernel_sizes = [2, 3, 4]; glove_200 embedding 0.660

说明:该实验glove词向量对结果提升不大,第四个实验效果较为显著;

任务三:基于注意力机制的文本匹配

数据集地址:SNLI

esim

  1. 实现ESIM模型,如上图左边所示,模型主要分三层,由下至上:

    • 第一层用BiLSTM来对句子的每个词向量进行重新编码,使其具备全局性;
    • 第二层先用Attention来提取前提与假设之间的关系,然后重构,以前提为例:
    # x1为前提,x2为假设,new_embed1是假设x1经过BiLSTM后的值,weight2是x2的每个词对x1的归一化相关程度,即attention值。
    # 1. 对假设x2进行加权求和,该值提取出了x2中与x1相关的部分;
    x1_align = torch.matmul(weight2, x2)
    # 2. 将四部分连接起来,用相减以及相乘来实现前提与假设的“交互推断”,文中说可以使得局部信息(如矛盾关系)更加明显;
    x1_combined = torch.cat([new_embed1, x1_align, new_embed1 - x1_align, new_embed1 * x1_align],dim=-1)
    • 第三层是将重构后的前提和假设再放到一个BiLSTM中,文中说为了控制模型复杂度,在传入前先经过一个单层FFN并且ReLU一下。
    • 第四层是输出层,先pooling一下,文中前提和假设都用了max-pooling以及average-pooling两种形式,再对两者连接后MLP输出。
  2. 注意用torchtext读取json文件的方式,及依赖解析数据的读取方式。其实torchtext内部有dataset.nli模块实现了 nli数据读取方式,但是在代码中我还是用原版的FIELD来实现,便于理解其内部的处理流程。

model test accuracy paper test accuracy
0.86 0.88

任务四:基于LSTM+CRF的序列标注

数据集地址:CONLL 2003

采用CNN+BiLSTM+CRF结构,复现论文End-to-end Sequence Labeling via Bi-directional LSTM-CNNs-CRF

  • 数据采用BIOES结构,用bio2bioes.py进行转换。
  • 关于预处理,将数字都变为0;
  • 设置预训练embedding的时候需注意模糊匹配(大小写);
  • 设置vocab的时候使要用到train以及dev+test中出现在embedding中的词;
  • 关于初始化,论文里有详细的设置,注意的是LSTM里面的forget gatebias初始为1,因为开始的时候记忆准确度不高。
  • 关于dropout,LSTM的输入和输出的时候都加dropout;字符embedding进入CNN前也要dropout
  • 关于优化器,论文是采用了SGD,每个epoch都对学习率进行调整,注意这里不能用pytorchSGD中的weight decay,因为它是对权重的衰减(L2正则),而我们要的是对学习率进行衰减。
embedding entity-level F1 paper result
random (uniform) 83.18 80.76
glove 100 90.7 91.21

注意点:使用torchtext进行文本处理,需注意要由于torchtext只能处理tsv/csv/json格式的文本,这里需要自己 从文本读取的句子,也要自定义Datasetmake Example时的两种表现形式:

  • list形式,此时构建FIELD的时候FIELD内部的tokenizer不会起作用;
  • str形式,word空格隔开,此时构建FIELD的时候必须传入tokenizer=lambda x:x.split()(针对该任务)来覆盖内部的tokenizer, 否则会用内部的来分词,得到的word可能不一样了,也就和label不对应了。

任务五:基于神经网络的语言模型

CharRNN来写诗,评价指标为困惑度,是交叉熵损失取exp

  1. 采用单向LSTM,输入的最后一个为[EOS],对应的target为输入右移一位;
  2. 控制诗的长度为MAX_LEN,以一行为单位进行切分,即将超长的诗切分为若干短诗;
  3. 训练200个epoch后,困惑度为400左右。

Input the first word or press Ctrl-C to exit: 鸟

鸟渠霭乌秋,游王居信知。鹏未弟休不,深沙由意。寥五将不,两迹悄臣。生微心日,水复师尘。来称簸更,影乏魍无。