带你从入门到精通——自然语言处理(九. 迁移学习和transformers库)
建议先阅读我之前的博客,掌握一定的自然语言处理前置知识后再阅读本文,链接如下:
带你从入门到精通——自然语言处理(一. 文本的基本预处理方法和张量表示)-CSDN博客
带你从入门到精通——自然语言处理(二. 文本数据分析、特征处理和数据增强)-CSDN博客
带你从入门到精通——自然语言处理(三. RNN的分类和LSTM)-CSDN博客
带你从入门到精通——自然语言处理(四. GRU和seq2seq模型)-CSDN博客
带你从入门到精通——自然语言处理(五. 自注意力机制和transformer的输入部分)-CSDN博客
带你从入门到精通——自然语言处理(六. Transformer的编码器部分)-CSDN博客
带你从入门到精通——自然语言处理(七. Transformer的解码器部分、输出部分和整体搭建)-CSDN博客
带你从入门到精通——自然语言处理(八. FastText)-CSDN博客
目录
九. 迁移学习和transformers库
9.1 迁移学习
9.1.1 迁移学习概述
迁移学习是机器学习中的一种方法,旨在将在一个任务(源任务,即有充足数据且已经训练好模型的任务)中学到的模型参数、特征表示或其他知识迁移另一个相关的任务(目标任务,即待解决的任务)中,在目标任务数据不足或标注数据困难的情况下,迁移学习可以显著加快模型在目标任务上的训练,提高模型的性能。
9.1.2 预训练模型
预训练模型是指已经在大规模、多样化的数据集上训练过的模型,因此预训练模型具有通用性,使得预训练模型能够直接应用到不同的下游任务中,并且有着不错的表现。
词向量迁移即是直接使用预训练模型的一种实际应用,词向量迁移是指将一个预先在大规模、多样化的数据集中训练好的词向量模型直接应用到新的、特定任务的数据集上的过程。
9.1.3 微调
微调(fine-tuning)是指将预训练模型应用到新的、特定任务的数据集上,并使用新任务的数据集继续训练预训练模型,调整预训练模型的参数,使其更好地适应新任务。
注意:根据新任务的不同需求,可以替换或添加不同的输出层(顶层)。
常见的微调策略如下:
全量微调策略:更新预训练模型的所有层以及顶层中的所有参数。
部分微调策略:冻结预训练模型的部分层的参数,只更新顶层的参数。
逐步解冻策略:先冻结预训练模型的所有层的参数,只更新顶层的参数,然后逐步解冻预训练模型的层的参数。
差异学习率策略:即使用不同的学习率训练不同的层,通常预训练模型中的参数使用较小的学习率。
9.2 Transformers库
Transformers库是由Huggingface提供的一个基于transformer模型结构的预训练模型开源库,transformers库提供了NLP领域中大量的SOTA(state-of-the-art)预训练模型,它同时支持PyTorch以及Tensorflow2.0,并且支持两个深度学习框架的相互转换。
9.2.1 管道
管道(pipeline)是transformers库中的一种极度简化的API,封装了模型加载、数据预处理、模型推理和后处理过程(后处理过程是指对输出结果进行进一步处理或优化的过程),用户只需几行代码即可完成复杂的NLP任务。
使用管道完成情感分析任务的示例如下:
from transformers import pipeline
import torch
def dm01():
model = pipeline(task='sentiment-analysis', model='model_demo/chinese_sentiment', device=torch.divice('cuda'))
print(model('中国GDP即将世界第一'))
if __name__ == '__main__':
dm01() # [{'label': 'star 4', 'score': 0.5729215741157532}]
# label为预测的标签
# score为预测结果的置信度
如果需要修改模型结构或后处理过程,则不能使用管道。
9.2.2 自动模型
自动模型(AutoModel)是transformers库中的一种用于加载和操作模型的类,AutoModel能够根据预训练模型的文件名称自动识别并加载对应的预训练模型并支持手动控制后处理过程,需要搭配自动分词器(AutoTokenizer)使用。
使用自动模型完成问答系统任务的示例如下:
from transformers import AutoTokenizer, AutoModelForQuestionAnswering
def dm02():
tokenizer = AutoTokenizer.from_pretrained('model_demo/chinese_pretrain_mrc_roberta_wwm_ext_large')
# 加载预训练分词器
model = AutoModelForQuestionAnswering.from_pretrained('model_demo/chinese_pretrain_mrc_roberta_wwm_ext_large')
# 加载问答系统任务的预训练模型
context = '我叫张三,我是一个程序员,我的喜好是打篮球'
print(len(context)) # 21
# 定义上下文
questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']
# 定义问题
model.eval()
# 切换模型到评估模式
for q in questions:
input = tokenizer.encode_plus(text=q, text_pair=context, return_tensors='pt',
max_length=32, padding='max_length', truncation='only_second')
# 参数padding能够指定填充策略,用于统一不同长度的序列,如果是文本对,则将文本对的总长度pad到指定值,可选取参数值如下:
# True表示填充到当前批次中最长序列的长度
# 'max_length'表示填充到参数max_length指定的长度
# False(默认)表示不填充
# 参数truncation能够指定截断策略(截断到参数max_length指定的长度),用于处理超长文本,可选取参数值如下:
# True:使用模型默认的截断策略
# "only_first" / "only_second"用于指定截断第一个或第二个文本。
# False(默认)表示不截断。
# 参数text为q即问题,参数text_pair为context即原文
# return_tensors='pt'表示返回一个PyTorch张量, 如果为tf表示返回一个TensorFlow张量, np表示返回一个NumPy数组
print(input)
'''
{'input_ids': tensor([[ 101, 2769, 3221, 6443, 8043, 102, 2769, 1373, 2476, 676, 8024, 2769,
3221, 671, 702, 4923, 2415, 1447, 8024, 2769, 4638, 1599, 1962, 3221,
2802, 5074, 4413, 102, 0, 0, 0, 0]]), '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, 1, 1, 0, 0, 0, 0]]), '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, 1, 1, 0, 0, 0, 0]])}
'''
print(input['input_ids'].shape)
# torch.Size([1, 32])
# 32为len(q) 4 + len(context) 21 + 4(4个'[PAD]'标记) + 3(开头添加一个'[CLS]'标记, 两个句子的结尾分别添加一个'[SEP]'标记)
print(input['token_type_ids'].shape)
# torch.Size([1, 32]), 将开头的'[CLS]'标记、q以及第一个句子结尾的'[SEP]'标记标记为0,视为第一个句子
# 将context以及其结尾的'[SEP]'标记为1,视为第二个句子,将4个'[PAD]'标记标记为0,视为第三个句子
print(input['attention_mask'].shape)
# torch.Size([1, 32]), 如果有padding策略则将padding部分标记为0,其他部分标记为1
output = model(**input) # **表示将字典的键值对解析为k=v的形式进行关键字传参
print(output)
'''
QuestionAnsweringModelOutput(loss=None, start_logits=tensor([[ -1.2187, -12.0521, -12.9116, -12.6337, -12.9228, -12.0023, -6.6158,
-9.5680, 1.3080, -4.1796, -11.0244, -9.2609, -11.5569, -5.4156,
-11.1753, -2.1366, -9.1935, -9.2614, -11.8510, -10.8918, -12.7235,
-10.9114, -12.3196, -12.8263, -9.6451, -5.4781, -9.3533, -13.1813,
-12.8528, -12.8283, -12.8381, -12.8390]], grad_fn=<CloneBackward0>), end_logits=tensor([[ -0.7600, -12.5497, -12.1096, -12.0863, -12.1607, -12.4662, -8.9323,
-9.7707, -4.4377, 2.0638, -9.0598, -10.5982, -10.7649, -8.0404,
-7.1301, -8.8496, -7.3208, -1.2483, -9.5052, -10.8482, -10.6369,
-11.3168, -10.0696, -10.9715, -9.8428, -7.4151, -3.6476, -11.9602,
-12.4490, -12.4578, -12.4501, -12.4561]], grad_fn=<CloneBackward0>), hidden_states=None, attentions=None)
'''
start, end = torch.argmax(output.start_logits), torch.argmax(output.end_logits)
# start和end表示模型预测答案在input_ids中的起始和结束位置
ans = tokenizer.convert_ids_to_tokens(input.input_ids[0][start: end + 1])
# 使用tokenizer.convert_ids_to_tokens方法将input_ids中从start到end位置的id转为token
print(ans) # ['张', '三']
break
if __name__ == '__main__':
dm02()
如果需要修改模型结构,则不能使用自动模型。
9.2.3 具体模型
具体模型是指使用transformers库中的具体模型类(例如BertForMaskedML)来加载和操作模型,使用该方式加载的模型支持修改模型结构以及后处理过程。
使用具体模型完成MLM(Masked Language Model)任务的示例如下:
import torch
from transformers import BertTokenizer, BertForMaskedLM
def dm03():
tokenizer = BertTokenizer.from_pretrained('model_demo/bert-base-chinese')
# 加载指定预训练模型的分词器
model = BertForMaskedLM.from_pretrained('model_demo/bert-base-chinese')
# 加载指定预训练模型
msg = tokenizer.encode_plus('我明天想去[MASK]家吃饭', return_tensors='pt')
print(msg)
'''
{'input_ids': tensor([[ 101, 2769, 3209, 1921, 2682, 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]])}
'''
model.eval()
output = model(**msg)
print(output)
'''
MaskedLMOutput(loss=None, logits=tensor([[[ -8.1490, -8.0702, -8.0586, ..., -6.7672, -6.8472, -6.8189],
[ -8.2212, -8.0452, -8.0652, ..., -6.5686, -7.0383, -5.5924],
[-15.5649, -15.5280, -15.1843, ..., -11.3474, -11.4219, -9.1208],
...,
[-16.1509, -16.7822, -15.7513, ..., -5.7605, -8.7244, -7.5829],
[-13.3876, -13.6202, -13.2302, ..., -7.9500, -5.5694, -8.9297],
[-10.5808, -10.4429, -10.7326, ..., -6.9652, -6.9201, -7.6223]]],
grad_fn=<ViewBackward0>), hidden_states=None, attentions=None)
'''
print(output.logits.shape)
# torch.Size([1, 11, 21128]),11表示输入的句子长度(添加了'[CLS]'标记以及'[SEP]'标记),21128表示词表长度
idx = torch.argmax(output.logits[0, 6]).item()
# 选取'[MASK]'标记的位置
print(tokenizer.convert_ids_to_tokens(idx)) # 你
if __name__ == '__main__':
dm03()更多推荐


所有评论(0)