自然语言处理N天-AllenNLP学习(完整实例,预测论文发表场合-上篇)

自然语言处理N天-AllenNLP学习(完整实例,预测论文发表场合-上篇)_第1张图片
新建 Microsoft PowerPoint 演示文稿 (2).jpg

本文基于AllenNLP中文教程,仅作为个人学习记录,作者也是一位骚话博主,和原文对照发现不少骚话蛤蛤蛤
有一篇帖子总结了一下学习处理NLP问题中间的坑。NLP数据预处理要比CV的麻烦很多。

  • 去除停用词,建立词典,加载各种预训练词向量,Sentence -> Word ID -> Word Embedding的过程(Tobias Lee:文本预处理方法小记),其中不仅需要学习pytorch,可能还要学习spacy,NLTK,numpy,pandas,tensorboardX等常用python包。
  • 用到RNN时,还要经过pad,pack,pad的过程,像这样的很多函数在使用时需要有数学基础加上简单的实践,感觉对一个新人来说,高维数据的流动有点抽象,不容易理解。
  • 数据集的读取,tensorboardX的使用。。。。各种东西要学习。在运行别人的代码后打印出信息,不仅看着上档次,而且可以看到很多实用的信息。。。

AllenNLP是在pytorch基础上的封装,它的目标是处理NLP任务,可以减少很多额外的学习。

  • 分词,帮你用spacy,NLTK,或者简单的按空格分词处理。
  • 数据集的读取,它内置了很多数据集的读取,你可以在通过学习它的读取方式,在它的基础上对自己需要的数据集进行读取。 、
  • 在Sentence -> Word ID -> Word Embedding的过程中,Glove,ELMo,BERT等常用的都可以直接使用,需要word,char粒度的都可以。
  • log打印输出,在内置的输出项之外,你可以很方便地加入想要输出的信息。模型的各个组件中的参数都可以存在一个json/jsonnet文件中,修改参数进行实验很方便。

3. 完整实例,预测论文发表场合

第一部分 数据集和模型

第一步 安装AllenNLP

略过,正常操作就好

第二步 构建module

如2.3所述要想自己利用AllenNLP实现一个模型,需要DatasetReader和Model。
要把我们自己写的代码放在python能够找到的位置(这是示例),这样我们的AllenNLP才能够调用这些模块。
假设我们自己的代码库基础包名叫my_library,符合以下两个条件:

  • 位于python的查找包的路径上
  • 这个库里包含有自己的models以及dataset_readers

第三步 构建DatasetReader

完成代码库建立后就要开始进行代码的编写。首先要有数据。
在这个教程中,我们想要预测某一篇学术论文的发表场合。具体一点说,就是给出一篇论文的标题和摘要,我们想要判断它到底是在”自然语言处理领域“,“机器学习领域”还是”人工智能领域”的论文(其实还是一个监督分类问题,教程中的是判断CS问题,我下的数据集似乎是医学类的……)。
使用的数据集来自Semantic Scholar搜索引擎,地址,选择98K的那个下载即可,一共102篇文章。数据集包含有多个属性,这里注意其中三个:标题(title),摘要(paperAbstract),以及所属领域(venue)。

{"entities":["Lipid Transport","Lipoproteins"],"journalVolume":"1047 3","journalPages":"\n 195-211\n ","pmid":"2252909v1","year":1990,"outCitations":[],"s2Url":"https://semanticscholar.org/paper/4c61478345166be0d917854bd5e5f42a6ade2362","s2PdfUrl":"","id":"4c61478345166be0d917854bd5e5f42a6ade2362","authors":[{"name":"Dick J van der Horst","ids":["6102924"]}],"journalName":"Biochimica et biophysica acta","paperAbstract":"","inCitations":["0e4308f95070a71cc8e5db52b67880256b54e6d1","a7a0e8e0a2cd17846b232e395a74446067952f7d","1d45e3f46d4a5e6736feae46a88732e8a9b71c46","b585844a12e4b947eed0a27b49032082a1c4a8e2","8e6fef247de1aba35a8463d126978fb52cc1331a","0716545e903cc84207c8c9be69184b79efc00308","7b896af119226dfb4a7ff4b0bde891fdcd8c173a","27ee401ebb7708ca3ecb5e958a2d653081fe52cb","0583d5ff19cc1ee16c3c6e51c056e98cba562d2f","038d593921a86feca08346de282b5948a3470719","006d60c77e30a9741470d2182980b80ae45dc0b8","e5d59e510a6d4410cf17857ab7339ed3c93ba784","eb62a7cc4bfbaf06962bfd9d1905e7b93cea509d","319e573c75e0841e1cbeb8372a252b8e98aea1c9","3eda0a149bcbf2139d11c9c4e67eff7b4e1d2cd9","40b420ec2a8a3ba869d956e556f0d006cddeb086","a84d739dac3b0144c0b834d455d95145ed4f0ea5","7fc7bfa40964c492d39c4921905d9214695a30ce","bccbd5d6b27509e3cbbe19537340eb45dc1d8753","1d65e821754d90b077852702039ebb765ebcb399","074f08b98004578274ae90ff8ec2361064c880c3","a44e10b03c9c46b91390a9de80d30295339a30e7","9a8b5acb8dd4c4035212dd1afcefb66420d6c892","0dc9cddf2f862153641639910f20600682c70302"],"pdfUrls":[],"title":"Lipid transport function of lipoproteins in flying insects.","doi":"","sources":["Medline"],"doiUrl":"","venue":"Biochimica et biophysica acta"}

在这一部分除了完成文件读取的代码的编写,还会对这些代码进行测试。教程中抽取了10%的文本作为测试集。测试代码是要采用AllenNLP中提供的测试接口allennlp.common.testing.AllenNlpTestCase。这些接口能够很好的帮我们做好测试的的工作。
我们首先想一想我们需要测试的只是DatasetReader中的read函数。
在测试中如果遇到这样的报错

“UnicodeDecodeError: 'gbk' codec can't decode byte 0xbf in position 2: illegal multibyte sequence”
定位到dataset_readers中,看一下里面读取文件的方法是不是没有加encoding='utf-8'
with open(cached_path(file_path), "r",encoding='utf-8') as data_file:

# -*- encoding=utf-8 -*-
from allennlp.common.util import ensure_list

from allennlp.common.testing import AllenNlpTestCase
from dataset_readers.semantic_scholar_papers import SemanticScholarDatasetReader

def test_read_from_file():
    reader = SemanticScholarDatasetReader()
    dataset = ensure_list(reader.read(r'C:\Users\01\Desktop\机器学习作业\AllenNLP\sample-S2-records'))
    return dataset

if __name__ == '__main__':
    list = test_read_from_file()
    for l in list:
        print(l.fields['title'])
        print(l.fields['abstract'])
        print(l.fields['label'])

可以看到,数据以字典的形式存放在fields中。
注意在这里使用open函数来打开需要读取的文件。open函数是把路径作为网络资源(url)来访问并且读取的,这一点在后面需要下载的数据文件中起到了至关重要的作用。

在打开了文件之后,_read函数的遍历文件中每一行数据,然后把这一行数据转换成JSON格式,从而提取出所需要的字段。
利用text_to_instance将这些字段转换成instance类型的数据。
注意,这个函数的返回值是一个instance而不是整个文本的instances,换句话说,这里只是教给程序如何去处理这些输入,具体的处理过程是透明的。
下面给出text_to_instance的基本实现,这部分在实例中有

    def text_to_instance(self, title: str, abstract: str, venue: str = None) -> Instance:
        tokenized_title = self._tokenizer.tokenize(title)
        tokenized_abstract = self._tokenizer.tokenize(abstract)
        title_field = TextField(tokenized_title, self._token_indexers)
        abstract_field = TextField(tokenized_abstract, self._token_indexers)
        fields = {'title': title_field, 'abstract': abstract_field}
        if venue is not None:
            fields['label'] = LabelField(venue)
        return Instance(fields)
  • 需要注意的第一个重要事项是instance类型实际上就是多个字段的集合。这些字段将会教给AllenNLP进行处理,并且传递给你的model。TextField表示标记化的文本,LabelField表示分类标签。这些输入是纯字符串,但在从训练数据构建词汇表之后,它们将在转换为模型之前转换为整数数组。所有这些都是由AllenNLP以您可以配置的方式为您处理的。
  • 需要注意的第二个重要事项是我们在self上使用一些对象来配置TextFields的构造方式:self._tokenizer和self._token_indexers。 Tokenizer确定标题和摘要如何转换为标记。 Tokenizer可以将字符串拆分为单词,字符,字节对或您想要标记数据的任何其他方式。TokenIndexers确定如何将这些标记表示为模型中的数组。
    例如,如果您的标记是单词,则TokenIndexers可以使用单词id,单词中的字符,单词的词性或许多其他内容来制作数组。请注意,我们没有在代码中指定任何内容,我们只是说我们将获得Tokenizer和一些TokenIndexers作为此DatasetReader的输入,我们将使用它们来处理我们的数据。这使我们以后可以非常灵活地表达我们的输入。我们可以尝试使用字符级CNN或POS标签嵌入,而无需更改我们的DatasetReader或我们的Model代码。
    你有可能意识到了一个问题,就是我们明明没有定义过这些成员变量,这些成员变量是从哪里来的呢!答:这些成员变量都是在init函数中定义的。下面我们就来看看这个函数。
    (上面这两段是机翻,还有照抄原博文)

以下为init函数

@DatasetReader.register("s2_papers")
class SemanticScholarDatasetReader(DatasetReader):
    def __init__(self,
                 tokenizer: Tokenizer = None,
                 token_indexers: Dict[str, TokenIndexer] = None) -> None:
        self._tokenizer = tokenizer or WordTokenizer()
        self._token_indexers = token_indexers or {"tokens": SingleIdTokenIndexer()}

这个构造函数从参数中获取了Tokenizer以及TokenIndexer,这两个参数还都有默认值。
我们只需要在调用这个构造函数的时候传递参数进去就行啦。但是注意!我们是不会自己去调用这个构造函数的,我们调用这个reader和model全都在config中完成,也就意味着我们所有的参数都将会配置文件中完成。
为了能够在配置文件中找到我们定义的这个model以及dataset_reader,我们需要给这两个类注册一个名字@DatasetReader.register("s2_papers"),像这样。有了这个注册,我们就能够在配置文件中使用这个类。AllenNLP为所有的注册的类都实现了一个from_params的方法,这个方法能够非常好根据配置文件中提供的信息,对应的调用构造函数,为我们构造DatasetReader以及model实例。
在几行代码中,我们有一个灵活的DatasetReader,可以为我们的模型获取数据。我们可以运行我们用pytest编写的测试并看到它通过了。

你可能感兴趣的:(自然语言处理N天-AllenNLP学习(完整实例,预测论文发表场合-上篇))