跳转至

8 Transformers库使用

学习目标

  • 了解并掌握管道方式完成基本NLP任务
  • 了解并掌握自动模型方式完成基本NLP任务
  • 了解并掌握具体模型方式完成基本NLP任务

1 了解Transformers库

  • Huggingface总部位于纽约,是一家专注于自然语言处理、人工智能和分布式系统的创业公司。他们所提供的聊天机器人技术一直颇受欢迎,但更出名的是他们在NLP开源社区上的贡献。Huggingface一直致力于自然语言处理NLP技术的平民化(democratize),希望每个人都能用上最先进(SOTA, state-of-the-art)的NLP技术,而非困窘于训练资源的匮乏。同时Hugging Face专注于NLP技术,拥有大型的开源社区。尤其是在github上开源的自然语言处理,预训练模型库 Transformers,已被下载超过一百万次,github上超过24000个star。

  • Huggingface Transformers 是基于一个开源基于 transformer 模型结构提供的预训练语言库。它支持 Pytorch,Tensorflow2.0,并且支持两个框架的相互转换。Transformers 提供了NLP领域大量state-of-art的 预训练语言模型结构的模型和调用框架。

  • 框架支持了最新的各种NLP预训练语言模型,使用者可快速的进行模型调用,并且支持模型further pretraining 和 下游任务fine-tuning。举个例子Transformers 库提供了很多SOTA的预训练模型,比如BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet, CTRL。
  • 社区Transformer的访问地址为:https://huggingface.co/,见下图。 image-20220510111629947
  • 备注
    • 1 点击 Model链接可查看、下载预训练模型。点击Datasets链接可查看、下载数据集。点击Docs链接可以阅读预训练模型的编程文档,十分方便
    • 2 SOTA(state-of-the-art)是指目前对某项任务“最好的”算法或技术

2 Transformers库三层应用结构

  • 管道(Pipline)方式:高度集成的极简使用方式,只需要几行代码即可实现一个NLP任务。
  • 自动模型(AutoMode)方式:可载入并使用BERTology系列模型。
  • 具体模型(SpecificModel)方式:在使用时,需要明确指定具体的模型,并按照每个BERTology系列模型中的特定参数进行调用,该方式相对复杂,但具有较高的灵活度。

image-20220510120516209

3 管道方式完成多种NLP任务

注意:若虚拟机中已经安装transformers,以下安装步骤不需再次执行

# 注意在执行clone之前,要查看当前是在那个目录下,比如$HOME/nlpdev/目录下
# 克隆huggingface的transfomers文件
git clone https://github.com/huggingface/transformers.git

# 进行transformers文件夹
cd transformers

# 切换transformers到指定版本
git checkout v4.19.0

# 安装transformers包
pip install .
# 安装datasets数据库,
# 注意workon xxx虚拟机开发环境,在虚拟机开发环境下安装
pip install datasets

3.1 文本分类任务

  • 文本分类是指模型可以根据文本中的内容来进行分类。例如根据内容对情绪进行分类,根据内容对商品分类等。文本分类模型一般是通过有监督训练得到的。对文本内容的具体分类,依赖于训练时所使用的样本标签。
# 导入工具包
import torch
from transformers import pipeline
import numpy as np

# 情感分类任务
def dm01_test_classification():

    # 1 使用中文预训练模型chinese_sentiment
    # 模型下载地址 git clone https://huggingface.co/techthiyanes/chinese_sentiment

    # 2 实例化pipeline对象
    my_model = pipeline(task='sentiment-analysis', model='./chinese_sentiment')
    # my_model = pipeline(task='sentiment-analysis', model='./bert-base-chinese')

    # 3 文本送给模型 进行文本分类
    output = my_model('我爱北京天安门,天安门上太阳升。')
    print('output--->', output)

# 结果输出
        output---> [{'label': 'star 5', 'score': 0.6314294338226318}]
  • pipeline函数可以自动从官网下载预训练模型,也可以加载本地的预训练模型

transformer库中预训练模型查找和下载

image-20220510134531540

image-20220510134942281

3.2 特征提取任务

  • 特征抽取任务只返回文本处理后的特征,属于预训练模型的范畴。特征抽取任务的输出结果需要和其他模型一起工作。
# 特征抽取任务
def dm02_test_feature_extraction():
    # 1 下载中文预训练模型 git clone https://huggingface.co/bert-base-chinese

    # 2 实例化pipeline对象 返回模型对象
    my_model = pipeline(task='feature-extraction', model='./bert-base-chinese')

    # 3 给模型送数据 提取语句特征
    output = my_model('人生该如何起头')
    print('output--->', type(output), np.array(output).shape)

# 输出结果
# output---> <class 'list'> (1, 9, 768)
# 7个字变成9个字原因: [CLS] 人 生 该 如 何 起 头 [SEP]
  • 不带任务头输出:特征抽取任务属于不带任务头输出,本bert-base-chinese模型的9个字,每个字的特征维度是768
  • 带头任务头输出:其他有指定任务类型的比如文本分类,完型填空属于带头任务输出,会根据具体任务类型不同输出不同的结果

3.3 完型填空任务

  • 完型填空任务又被叫做“遮蔽语言建模任务”,它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。
# 完型填空任务
def dm03_test_fill_mask():

    # 1 下载预训练模型 全词模型git clone https://huggingface.co/hfl/chinese-bert-wwm

    # 2 实例化pipeline对象 返回一个模型
    my_model = pipeline(task='fill-mask', model='chinese-bert-wwm')

    # 3 给模型送数据 做预测
    input = '我想明天去[MASK]家吃饭。'
    output = my_model(input)

    # 4 输出预测结果
    print('output--->', output)

# 输出结果
    # output--->
    # [{'score': 0.34331339597702026, 'token': 1961, 'token_str': '她', 'sequence': '我 想 明 天 去 她 家 吃 饭.'},
    # {'score': 0.2533259987831116, 'token': 872, 'token_str': '你', 'sequence': '我 想 明 天 去 你 家 吃 饭.'},
    # {'score': 0.1874391734600067, 'token': 800, 'token_str': '他', 'sequence': '我 想 明 天 去 他 家 吃 饭.'},
    # {'score': 0.1273055076599121, 'token': 2769, 'token_str': '我', 'sequence': '我 想 明 天 去 我 家 吃 饭.'},
    # {'score': 0.02162978984415531, 'token': 2644, 'token_str': '您', 'sequence': '我 想 明 天 去 您 家 吃 饭.'}]

可以在官网在线查找完型填空结果

image-20220510142812096

3.4 阅读理解任务

  • 阅读理解任务又称为“抽取式问答任务”,即输入一段文本和一个问题,让模型输出结果。
# 阅读理解任务(抽取式问答)
def dm04_test_question_answering():

    # 问答语句
    context = '我叫张三,我是一个程序员,我的喜好是打篮球。'
    questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']

    # 1 下载模型 git clone https://huggingface.co/luhua/chinese_pretrain_mrc_roberta_wwm_ext_large

    # 2 实例化化pipeline 返回模型
    model = pipeline('question-answering', model='chinese_pretrain_mrc_roberta_wwm_ext_large')

    # 3 给模型送数据 的预测结果
    print(model(context=context, question=questions))

    # 输出结果
    '''
    [{'score': 1.2071758523357623e-12, 'start': 2, 'end': 4, 'answer': '张三'},
     {'score': 2.60890374192968e-06, 'start': 9, 'end': 12, 'answer': '程序员'},
     {'score': 4.1686924134864967e-08, 'start': 18, 'end': 21, 'answer': '打篮球'}]
    '''

3.5 文本摘要任务

  • 摘要生成任务的输入一一段文本,输出是一段概况、简单的文字。
# 文本摘要任务
def dm05_test_summarization():

    # 1 下载模型 git clone https://huggingface.co/sshleifer/distilbart-cnn-12-6

    # 2 实例化pipline 返回模型
    my_model = pipeline(task = 'summarization', model="distilbart-cnn-12-6")

    # 3 准备文本 送给模型
    text = "BERT is a transformers model pretrained on a large corpus of English data " \
           "in a self-supervised fashion. This means it was pretrained on the raw texts " \
           "only, with no humans labelling them in any way (which is why it can use lots " \
           "of publicly available data) with an automatic process to generate inputs and " \
           "labels from those texts. More precisely, it was pretrained with two objectives:Masked " \
           "language modeling (MLM): taking a sentence, the model randomly masks 15% of the " \
           "words in the input then run the entire masked sentence through the model and has " \
           "to predict the masked words. This is different from traditional recurrent neural " \
           "networks (RNNs) that usually see the words one after the other, or from autoregressive " \
           "models like GPT which internally mask the future tokens. It allows the model to learn " \
           "a bidirectional representation of the sentence.Next sentence prediction (NSP): the models" \
           " concatenates two masked sentences as inputs during pretraining. Sometimes they correspond to " \
           "sentences that were next to each other in the original text, sometimes not. The model then " \
           "has to predict if the two sentences were following each other or not."
    output = my_model(text)

    # 4 打印摘要结果
    print('output--->', output)

# 输出结果
output---> [{'summary_text': ' BERT is a transformers model pretrained on a large corpus of English data in a self-supervised fashion . It was pretrained with two objectives: Masked language modeling (MLM) and next sentence prediction (NSP) This allows the model to learn a bidirectional representation of the sentence .'}]

3.6 NER任务

  • 实体词识别(NER)任务是NLP中的基础任务。它用于识别文本中的人名(PER)、地名(LOC)、组织(ORG)以及其他实体(MISC)等。例如:(王 B-PER) (小 I-PER) (明 I-PER) (在 O) (办 B-LOC) (公 I-LOC) (室 I-LOC)。其中O表示一个非实体,B表示一个实体的开始,I表示一个实体块的内部。
  • 实体词识别本质上是一个分类任务(又叫序列标注任务),实体词识别是句法分析的基础,而句法分析优势NLP任务的核心。```
# NER任务
def dm06_test_ner():

    # 1 下载模型 git clone https://huggingface.co/uer/roberta-base-finetuned-cluener2020-chinese

    # 2 实例化pipeline 返回模型
    model = pipeline('ner', model='roberta-base-finetuned-cluener2020-chinese')

    # 3 给模型送数据 打印NER结果
    print(model('我爱北京天安门,天安门上太阳升。'))

    '''
    [{'entity': 'B-address', 'score': 0.8838121, 'index': 3, 'word': '北', 'start': 2, 'end': 3},
     {'entity': 'I-address', 'score': 0.83543754, 'index': 4, 'word': '京', 'start': 3, 'end': 4},
     {'entity': 'I-address', 'score': 0.4240591, 'index': 5, 'word': '天', 'start': 4, 'end': 5},
     {'entity': 'I-address', 'score': 0.7524443, 'index': 6, 'word': '安', 'start': 5, 'end': 6},
     {'entity': 'I-address', 'score': 0.6949866, 'index': 7, 'word': '门', 'start': 6, 'end': 7},
     {'entity': 'B-address', 'score': 0.65552264, 'index': 9, 'word': '天', 'start': 8, 'end': 9},
     {'entity': 'I-address', 'score': 0.5376768, 'index': 10, 'word': '安', 'start': 9, 'end': 10},
     {'entity': 'I-address', 'score': 0.510813, 'index': 11, 'word': '门', 'start': 10, 'end': 11}]
    '''

4 自动模型方式完成多种NLP任务

4.1 文本分类任务

  • 文本分类是指模型可以根据文本中的内容来进行分类。例如根据内容对情绪进行分类,根据内容对商品分类等。文本分类模型一般是通过有监督训练得到的。对文本内容的具体分类,依赖于训练时所使用的样本标签。
# 导入工具包
import torch
from transformers import AutoConfig, AutoModel, AutoTokenizer
from transformers import AutoModelForSequenceClassification, AutoModelForMaskedLM, AutoModelForQuestionAnswering
# AutoModelForSeq2SeqLM:文本摘要
# AutoModelForTokenClassification:ner
from transformers import AutoModelForSeq2SeqLM, AutoModelForTokenClassification

# 情感分类任务
def dm01_test_classification():

    # 1 加载tokenizer
    my_tokenizer = AutoTokenizer.from_pretrained('./chinese_sentiment')

    # 2 加载模型
    my_model = AutoModelForSequenceClassification.from_pretrained('./chinese_sentiment')

    # 3 文本转张量
    message = '人生该如何起头'

    # 3-1 return_tensors='pt' 返回是二维tensor
    msg_tensor1 = my_tokenizer.encode(text=message, return_tensors='pt', padding=True, truncation=True, max_length=20)
    print('msg_tensor1--->', msg_tensor1)

    # 3-2 不用return_tensors='pt'是一维列表
    msg_list2 = my_tokenizer.encode(text=message, padding=True, truncation=True, max_length=20)
    print('msg_list2--->', msg_list2)
    msg_tensor2 = torch.tensor([msg_list2])
    print('msg_tensor2--->', msg_tensor2)

    # 4 数据送给模型
    # 4-1
    my_model.eval()
    output1 = my_model(msg_tensor2)
    print('情感分类模型头输出outpout1--->', output1)
    # 4-2
    output2 = my_model(msg_tensor2, return_dict=False)
    print('情感分类模型头输出outpout2--->', output2)
  • AutoTokenizer、AutoModelForSequenceClassification函数可以自动从官网下载预训练模型,也可以加载本地的预训练模型
  • AutoModelForSequenceClassification类管理着分类任务,会根据参数的输入选用不同的模型。
  • AutoTokenizer的encode()函数使用return_tensors=’pt‘参数和不使用pt参数对文本编码的结果不同
  • AutoTokenizer的encode()函数使用padding='max_length'可以按照最大程度进行补齐,俗称打padding
  • 调用模型的forward函数输入return_dict=False参数,返回结果也不同

程序运行结果

msg_tensor1---> tensor([[ 101,  782, 4495, 6421, 1963,  862, 6629, 1928,  102]])
msg_list2---> [101, 782, 4495, 6421, 1963, 862, 6629, 1928, 102]
msg_tensor2---> tensor([[ 101,  782, 4495, 6421, 1963,  862, 6629, 1928,  102]])
情感分类模型头输出outpout1---> SequenceClassifierOutput(loss=None, logits=tensor([[-2.7387, -1.7528,  0.2273,  2.0507,  1.4128]],
       grad_fn=<AddmmBackward>), hidden_states=None, attentions=None)
情感分类模型头输出outpout2---> (tensor([[-2.7387, -1.7528,  0.2273,  2.0507,  1.4128]],
       grad_fn=<AddmmBackward>),)

#注1:101代表[CLS] 102代表[SEP]

4.2 特征提取任务

  • 特征抽取任务只返回文本处理后的特征,属于预训练模型的范畴。特征抽取任务的输出结果需要和其他模型一起工作。
# 特征提取任务-不带任务输出头的任务
def dm02_test_feature_extraction():
    # 1 加载tokenizer
    my_tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path='./bert-base-chinese')

    # 2 加载模型
    my_model = AutoModel.from_pretrained(pretrained_model_name_or_path = './bert-base-chinese')

    # 3 文本转张量
    message = ['你是谁', '人生该如何起头']
    msgs_tensor = my_tokenizer.encode_plus(text=message, return_tensors='pt', truncation=True, pad_to_max_length=True, max_length=30)
    print('msgs_tensor--->', msgs_tensor)

    # 4 给模型送数据提取特征
    my_model.eval()
    output = my_model(**msgs_tensor)
    print('不带模型头输出output--->', output)
    print('outputs.last_hidden_state.shape--->', output.last_hidden_state.shape)  # torch.Size([1, 30, 768])
    print('outputs.pooler_output.shape--->', output.pooler_output.shape)  # torch.Size([1, 768])
  • 不带任务头输出:特征抽取任务属于不带任务头输出,本bert-base-chinese模型的9个字,每个字的特征维度是768
  • 带头任务头输出:其他有指定任务类型的比如文本分类,完型填空属于带头任务输出,会根据具体任务类型不同输出不同的结果

程序运行结果

msgs_tensor---> 
# 1 input_ids对两个句子text2id以后的结果,
# 101表示段落开头,第一个102代表第一个句子结束,第二个102点第二个句子结束
# 后面的0表示 按照编码要求pad_to_max_length=True和max_length=30补充pad零
{'input_ids': tensor([[ 101,  872, 3221, 6443,  102,  782, 4495, 6421, 1963,  862, 6629, 1928,
          102,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0]]), 
# 2 token_type_ids表示段落标志0代表第一个句子,1代表第二个句子
 'token_type_ids': tensor([[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0]]), 
# 3 attention_mask表示注意力机制的掩码数据,1表示有真实数据,0表示是pad数据需要掩码
 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0]])}

# 1 last_hidden_state表示最后一个隐藏层的数据 [1,30,768]
# 2 pooler_output表示池化,也就是对最后一个隐藏层再进行线性变换以后平均池化的结果。分类时候使用。
不带模型头输出output---> BaseModelOutputWithPoolingAndCrossAttentions(
  last_hidden_state=tensor([[[ 0.7001,  0.4651,  0.2427,  ...,  0.5753, -0.4330,  0.1878],
         [ 0.4017,  0.1123,  0.4482,  ..., -0.2614, -0.2649, -0.1497],
         [ 1.2000, -0.4859,  1.1970,  ...,  0.7543, -0.2405, -0.2627],
         ...,
         [ 0.2074,  0.4022, -0.0448,  ..., -0.0849, -0.0766, -0.2134],
         [ 0.0879,  0.2482, -0.2356,  ...,  0.2967, -0.2357, -0.5138],
         [ 0.4944,  0.1340, -0.2387,  ...,  0.2375, -0.1011, -0.3314]]],
       grad_fn=<NativeLayerNormBackward>), 
  pooler_output=tensor([[ 0.9996,  1.0000,  0.9995,  0.9412,  0.8629,  0.9592, -0.8144, -0.9654,
          0.9892, -0.9997,  1.0000,  0.9998, -0.1187, -0.9373,  0.9999, -1.0000,
         ...,
         -0.9967,  1.0000,  0.8626, -0.9993, -0.9704, -0.9993, -0.9971,  0.8522]],
       grad_fn=<TanhBackward>), 
  hidden_states=None, past_key_values=None, attentions=None, cross_attentions=None)

outputs.last_hidden_state.shape---> torch.Size([1, 30, 768])
outputs.pooler_output.shape---> torch.Size([1, 768])

4.3 完型填空任务

  • 完型填空任务又被叫做“遮蔽语言建模任务”,它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。
# 完型填空任务
def dm03_test_fill_mask():

    # 1 加载tokenizer
    modename = "chinese-bert-wwm"
    # modename = "bert-base-chinese"
    my_tokenizer = AutoTokenizer.from_pretrained(modename)

    # 2 加载模型
    my_model = AutoModelForMaskedLM.from_pretrained(modename)

    # 3 文本转张量
    input = my_tokenizer.encode_plus('我想明天去[MASK]家吃饭.', return_tensors='pt')
    print('input--->', input)

    # 4 给模型送数据提取特征
    my_model.eval()
    output = my_model(**input)
    print('output--->', output) 
    print('output.logits--->', output.logits.shape) # [1,12,21128]

    # 5 取概率最高
    mask_pred_idx = torch.argmax(output.logits[0][6]).item()
    print('打印概率最高的字:', my_tokenizer.convert_ids_to_tokens([mask_pred_idx]))

程序运行结果

# 1 input_ids 对句子text2id以后的结果 
# 2 token_type_ids 句子分段信息
# 3 attention_mask 句子掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 2682, 3209, 1921, 1343,  103, 2157, 1391, 7649,  119,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

# 1 logits表示MASK预测的结果,也是一种分类概率
# 2 output.logits的分类形状 [1, 12, 21128]
# 3 通过 my_tokenizer.convert_ids_to_tokens()函数完成id2text的操作
output---> MaskedLMOutput(loss=None, logits=tensor([[[ -9.9017,  -9.6006,  -9.8032,  ...,  -7.9744,  -7.7402,  -8.2912],
         [-14.3878, -15.0353, -14.7893,  ..., -10.0437, -10.5279,  -9.7544],
         [-14.2215, -14.1145, -14.5770,  ...,  -6.3246,  -4.1784,  -4.6072],
         ...,
         [-14.6938, -16.8133, -15.1296,  ...,  -9.2327,  -8.1931, -15.2430],
         [-10.8649, -11.4887, -11.5731,  ...,  -6.5378,  -0.8715,  -5.3870],
         [-11.8495, -11.8358, -12.0314,  ...,  -8.4242,  -6.2741,  -8.2787]]],
       grad_fn=<AddBackward0>), hidden_states=None, attentions=None)
output.logits---> torch.Size([1, 12, 21128])
打印概率最高的字: ['她']

4.4 阅读理解任务

  • 阅读理解任务又称为“抽取式问答任务”,即输入一段文本和一个问题,让模型输出结果。
# 阅读理解任务(抽取式问答)
def dm04_test_question_answering():

    # 1 加载tokenizer
    my_tokenizer = AutoTokenizer.from_pretrained('./chinese_pretrain_mrc_roberta_wwm_ext_large')

    # 2 加载模型
    my_model = AutoModelForQuestionAnswering.from_pretrained('./chinese_pretrain_mrc_roberta_wwm_ext_large')

    # 3 文本转张量
    # 文字中的标点符号如果是中文的话,会影响到预测结果 也可以去掉标点符号
    context = '我叫张三 我是一个程序员 我的喜好是打篮球'
    questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']

    # 4 给模型送数据 模型做抽取式问答
    my_model.eval()
    for question in questions:
        input = my_tokenizer.encode_plus(question, context, return_tensors='pt')
        print('input--->', input)
        output = my_model(**input)
        print('output--->', output)
        start, end = torch.argmax(output.start_logits), torch.argmax(output.end_logits) +1
        answer =  my_tokenizer.convert_ids_to_tokens(input['input_ids'][0][start:end] )
        print('question:', question, 'answer:', answer)

程序运行结果:

# input_ids表示text2id后结果 # token_type_ids表示句子分段信息 # attention_mask表示句子attention掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 3221, 6443, 8043,  102, 2769, 1373, 2476,  676, 2769, 3221,
          671,  702, 4923, 2415, 1447, 2769, 4638, 1599, 1962, 3221, 2802, 5074,
         4413,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1]])}

# start_logits end_logits分布表示从原文中抽取答案的位置概率
# 比如:start_logits的最大值代表句子答案最可能开始的位置
# 比如:end_logits的最大值代表句子答案可能结束的位置
output---> QuestionAnsweringModelOutput(loss=None, start_logits=tensor([[ -1.9978, -11.4788, -12.6324, -11.8324, -12.4148, -11.9371,  -2.7246,
          -6.6402,   3.9131,  -2.9533,  -7.0866,  -9.5696,  -4.2775,  -8.9042,
           0.5753,  -6.9468,  -7.0469,  -8.5334, -11.3796,  -9.3905, -11.0242,
         -11.1047,  -5.7124,  -2.7293,  -7.5896, -12.6013]],
       grad_fn=<CopyBackwards>), end_logits=tensor([[ -1.3483, -12.0141, -11.6312, -11.6629, -11.9607, -12.0039,  -4.6118,
          -7.4034,  -2.3499,   4.7159,  -7.2880,  -9.5317,  -6.6742,  -6.0915,
          -7.0023,  -4.9691,   1.4515,  -7.8329,  -9.0895, -10.3742,  -8.7482,
          -9.8567,  -7.2930,  -5.8163,  -1.7323, -12.2525]],
       grad_fn=<CopyBackwards>), hidden_states=None, attentions=None)

question: 我是谁 answer: ['张', '三']
question: 我是做什么的 answer: ['程', '序', '员']
question: 我的爱好是什么 answer: ['打', '篮', '球']

4.5 文本摘要任务

  • 摘要生成任务的输入一一段文本,输出是一段概况、简单的文字。
# 文本摘要任务
def dm05_test_summarization():
    text = "BERT is a transformers model pretrained on a large corpus of English data " \
           "in a self-supervised fashion. This means it was pretrained on the raw texts " \
           "only, with no humans labelling them in any way (which is why it can use lots " \
           "of publicly available data) with an automatic process to generate inputs and " \
           "labels from those texts. More precisely, it was pretrained with two objectives:Masked " \
           "language modeling (MLM): taking a sentence, the model randomly masks 15% of the " \
           "words in the input then run the entire masked sentence through the model and has " \
           "to predict the masked words. This is different from traditional recurrent neural " \
           "networks (RNNs) that usually see the words one after the other, or from autoregressive " \
           "models like GPT which internally mask the future tokens. It allows the model to learn " \
           "a bidirectional representation of the sentence.Next sentence prediction (NSP): the models" \
           " concatenates two masked sentences as inputs during pretraining. Sometimes they correspond to " \
           "sentences that were next to each other in the original text, sometimes not. The model then " \
           "has to predict if the two sentences were following each other or not."

    # 1 加载tokenizer
    my_tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path="distilbart-cnn-12-6")

    # 2 加载模型
    my_model = AutoModelForSeq2SeqLM.from_pretrained(pretrained_model_name_or_path='distilbart-cnn-12-6')

    # 3 文本转张量
    input = my_tokenizer([text], return_tensors='pt')
    # print('input--->', input)

    # 4 送给模型做摘要
    my_model.eval()
    output = my_model.generate(input.input_ids)
    print('output--->', output)

    # 5 处理摘要结果
    # 5-1 decode 的 skip_special_tokens 参数可以去除 token 前面的特殊字符
    print([my_tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=False) for g in output])

    # 5-2 convert_ids_to_tokens 函数只能将 ids 还原为 token
    # print(my_tokenizer.convert_ids_to_tokens(output[0]))

程序运行结果:

output---> tensor([[    2,     0, 11126,   565,    16,    10,  7891,   268,  1421, 11857,
         26492,    15,    10,   739, 42168,     9,  2370,   414,    11,    10,
          1403,    12, 16101, 25376,  2734,   479,    85,    21, 11857, 26492,
            19,    80, 10366,    35, 31755,   196,  2777, 19039,    36, 10537,
           448,    43,     8,   220,  3645, 16782,    36,   487,  4186,    43,
            20,  3092, 10146, 26511,  1626,    80, 24397, 11305,    25, 16584,
           148, 11857, 32155,   479,  7411,    51, 20719,     7, 11305,    14,
            58,   220,     7,   349,    97,    11,     5,  1461,  2788,     6,
          2128,    45,   479,     2]])

['BERT is a transformers model pretrained on a large corpus of English data in a self-supervised fashion . It was pretrained with two objectives: Masked language modeling (MLM) and next sentence prediction (NSP) The models concatenates two masked sentences as inputs during pretraining . Sometimes they correspond to sentences that were next to each other in the original text, sometimes not .']

4.6 NER任务

  • 实体词识别(NER)任务是NLP中的基础任务。它用于识别文本中的人名(PER)、地名(LOC)、组织(ORG)以及其他实体(MISC)等。例如:(王 B-PER) (小 I-PER) (明 I-PER) (在 O) (办 B-LOC) (公 I-LOC) (室 I-LOC)。其中O表示一个非实体,B表示一个实体的开始,I表示一个实体块的内部。
  • 实体词识别本质上是一个分类任务(又叫序列标注任务),实体词识别是句法分析的基础,而句法分析优势NLP任务的核心。```
# NER任务
def dm06_test_ner():
    # 1 加载tokenizer 加载模型 加载配置文件
    # https://huggingface.co/uer/roberta-base-finetuned-cluener2020-chinese
    my_tokenizer = AutoTokenizer.from_pretrained('roberta-base-finetuned-cluener2020-chinese')
    my_model = AutoModelForTokenClassification.from_pretrained('roberta-base-finetuned-cluener2020-chinese')
    config = AutoConfig.from_pretrained('roberta-base-finetuned-cluener2020-chinese')

    # 2 数据张量化
    inputs = my_tokenizer.encode_plus('我爱北京天安门,天安门上太阳升', return_tensors='pt')
    print('inputs--->', inputs.input_ids.shape, inputs.input_ids) # torch.Size([1, 17])

    # 3 送入模型 预测ner概率 每个字预测的标签概率
    my_model.eval()
    logits = my_model(inputs.input_ids).logits
    print('logits--->', logits.shape)           # torch.Size([1, 17, 32])

    # 4 对预测数据 进行显示
    input_tokens = my_tokenizer.convert_ids_to_tokens(inputs.input_ids[0])
    print('input_tokens--->', input_tokens)
    outputs = []

    for token, value in zip(input_tokens, logits[0]):

        if token in my_tokenizer.all_special_tokens:
            continue

        # 获得每个字预测概率最大的标签索引
        idx = torch.argmax(value).item()

        # 打印索引对应标签
        outputs.append((token, config.id2label[idx]))

    print(outputs)

程序运行结果

inputs---> torch.Size([1, 17]) tensor([[ 101, 2769, 4263, 1266,  776, 1921, 2128, 7305, 8024, 1921, 2128, 7305,
          677, 1922, 7345, 1285,  102]])

logits---> torch.Size([1, 17, 32])

input_tokens---> ['[CLS]', '我', '爱', '北', '京', '天', '安', '门', ',', '天', '安', '门', '上', '太', '阳', '升', '[SEP]']

[('我', 'O'), ('爱', 'O'), ('北', 'B-address'), ('京', 'I-address'), ('天', 'I-address'), ('安', 'I-address'), ('门', 'I-address'), (',', 'O'), ('天', 'B-address'), ('安', 'I-address'), ('门', 'I-address'), ('上', 'O'), ('太', 'O'), ('阳', 'O'), ('升', 'O')]

5 具体模型方式完成NLP任务

5.1 完型填空任务

  • 完型填空任务又被叫做“遮蔽语言建模任务”,它属于BERT模型训练过程中的子任务。下面完成一个中文场景的完型填空。
# 具体模型完型填空任务
def dm01_test_bert_fill_mask():

    # 1 加载tokenizer
    modename = "bert-base-chinese"
    my_tokenizer = BertTokenizer.from_pretrained(modename)

    # 2 加载模型
    my_model = BertForMaskedLM.from_pretrained(modename)

    # 3 文本转张量
    input = my_tokenizer.encode_plus('我想明天去[MASK]家吃饭', return_tensors='pt')
    print('input--->', input)

    # 4 给模型送数据提取特征
    output = my_model(**input)
    print('output--->', output)
    print('output.logits--->', output.logits.shape)  # [1,11,21128]

    # 5 取概率最高
    mask_pred_idx = torch.argmax(output.logits[0][6]).item()
    print('打印概率最高的字:', my_tokenizer.convert_ids_to_tokens([mask_pred_idx]))

程序运行结果

# input_ids表示text2id后结果 # token_type_ids表示句子分段信息 # attention_mask表示句子attention掩码信息
input---> {'input_ids': tensor([[ 101, 2769, 2682, 3209, 1921, 1343,  103, 2157, 1391, 7649,  102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

output---> MaskedLMOutput(loss=None, logits=tensor([[[ -8.1771,  -8.1008,  -8.1191,  ...,  -6.8355,  -6.9482,  -6.9834],
         [ -8.2775,  -8.1251,  -8.1655,  ...,  -6.8471,  -7.4265,  -6.1365],
         [-14.1093, -13.1037, -14.6324,  ...,  -6.0959,  -3.7550,  -5.7456],
         ...,
         [-16.2103, -16.7243, -15.9876,  ...,  -5.9727,  -8.2757,  -7.3852],
         [-13.5615, -13.7670, -13.4497,  ...,  -7.8282,  -4.9095,  -9.1699],
         [-10.3200, -10.1068, -10.4439,  ...,  -6.6468,  -7.0597,  -7.5027]]],
       grad_fn=<AddBackward0>), hidden_states=None, attentions=None)

output.logits---> torch.Size([1, 11, 21128])

6 小结

  • 了解Huggingface Transformers
    • Huggingface Transformers 是基于一个开源基于 transformer 模型结构提供的预训练语言。框架支持了最新的各种NLP预训练语言模型,使用者可快速的进行模型调用,并且支持模型further pretraining 和 下游任务fine-tuning
  • 使用管道方式完成多种NLP任务
    • 文本分类任务sentiment-analysis、特征提取任务feature-extraction、完型填空任务fill-mask、阅读理解任务question-answering、文本摘要任务summarization、NER任务ner
  • 使用自动模型方式完成NLP任务
    • 对Aoto系列模型类进行使用AutoModel、AutoTokenizer、AutoModelForSequenceClassification、AutoModelForMaskedLM、AutoModelForQuestionAnswering、AutoModelForSeq2SeqLM、AutoModelForTokenClassification
  • 使用具体模型方式完成NLP任务
    • 使用具体模型比如BertTokenizer等进行具体NLP任务开发