迁移学习

1 迁移学习的概念

预训练模型

定义: 简单来说别人训练好的模型。一般预训练模型具备复杂的网络模型结构;一般是在大量的语料下训练完成的
  • 预训练语言模型的类别
现在我们接触到的预训练语言模型,基本上都是基于transformer这个模型迭代而来的
因此划分模型类别的时候,以transformer架构来划分:
Encoder-Only: 只有编码器部分的模型,代表:BERT
Decoder-Only: 只要解码器部分的模型,代表:GPT
Encoder-Decoder: 本质就transformer架构,代表:T5

微调

定义:一般是对预训练语言模型,进行垂直领域数据的微调,可以将预训练模型的参数全部微调或者部分微调或者不微调,但是一般我们在做任务的时候,会在预训练模型后加入自定义网络,自定义网络模型的参数需要训练

迁移学习的两种方式

开箱即用: 当预训练模型的任务和我们要做的任务相似时,可以直接使用预训练模型来解决对应的任务
微调: 进行垂直领域数据的微调,一般在预训练网络模型后,加入自定义网络,自定义网络模型的参数需要训练,但是预训练模型的参数可以全部微调或者部分微调或者不微调。

2 NLP常用预训练模型

2.1 当下NLP流行的预训练模型

  • BERT

  • GPT

  • GPT-2

  • Transformer-XL

  • XLNet

  • XLM

  • RoBERTa

  • DistilBERT

  • ALBERT

  • T5

  • XLM-RoBERTa

2.2 BERT及其变体

  • bert-base-uncased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在小写的英文文本上进行训练而得到.

  • bert-large-uncased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在小写的英文文本上进行训练而得到.

  • bert-base-cased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在不区分大小写的英文文本上进行训练而得到.

  • bert-large-cased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在不区分大小写的英文文本上进行训练而得到.

  • bert-base-multilingual-uncased: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在小写的102种语言文本上进行训练而得到.

  • bert-large-multilingual-uncased: 编码器具有24个隐层, 输出1024维张量, 16个自注意力头, 共340M参数量, 在小写的102种语言文本上进行训练而得到.

  • bert-base-chinese: 编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在简体和繁体中文文本上进行训练而得到.

3 Transformers库使用

3.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/

3.2 三层应用结构

  • 管道(Pipline)方式:高度集成的极简使用方式,只需要几行代码即可实现一个NLP任务。

  • 自动模型(AutoMode)方式:可载入并使用BERTology系列模型。

  • 具体模型(SpecificModel)方式:在使用时,需要明确指定具体的模型,并按照每个BERTology系列模型中的特定参数进行调用,该方式相对复杂,但具有较高的灵活度。

3.3 Pipeline方式应用预训练模型

文本分类
定义:对一个文本进行分类:eg:对一个评论文本,判断其实好评还是差评

代码实现

# 1.实现文本分类任务
def dm01_test_classcify():
    # 基于pipeline函数返回需要的模型
    # model = pipeline(task="sentiment-analysis", model='./model/chinese_sentiment')
    model = pipeline(task="text-classification", model='./model/chinese_sentiment')
    # 直接使用model来预测
    result = model('我爱北京天安门,天安门上太阳升。')
    print(f'result--》{result}')
​
特征抽取
定义:将文本输入模型,得到特征向量的表示

代码实现

# 2.实现特征提取任务
def dm02_text_feature():
    # 基于pipeline函数返回需要的模型
    # model = pipeline(task="feature-extraction", model='./model/chinese_sentiment')
    model = pipeline(task="feature-extraction", model='./model/bert-base-chinese')
    # 直接使用model来预测
    result = model('人生该如何起头')
    print(f'result--》{torch.tensor(result).size()}')
完形填空
掩码任务,将一段文本中的某个token进行MASK,然后通过模型来预测被MASK掉的词

代码实现

# 3.实现完形填空任务
def dm03_fill_mask():
    # 基于pipeline函数返回需要的模型
    model = pipeline(task="fill-mask", model='./model/chinese-bert-wwm')
    # 直接使用model来预测
    result = model('我想明天去[MASK]家吃饭。')
    print(f'result--》{result}')
阅读理解
question-answer,根据文本以及问题,从文本里面解答问题的答案

代码实现

def dm04_qa():
    context = "我叫张三,我是一个程序员,我的喜好是打篮球。"
    questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']
    # 基于pipeline函数返回需要的模型
    model = pipeline(task="question-answering", model='./model/chinese_pretrain_mrc_roberta_wwm_ext_large')
    # 直接使用model来预测
    result = model(context=context, question=questions)
    print(f'result--》{result}')
文本摘要
对文档的概括总结

代码实现

# 5.实现文本摘要任务
def dm05_summary():
    # 基于pipeline函数返回需要的模型
    model = pipeline(task="summarization", model='./model/distilbart-cnn-12-6')
    # 直接使用model来预测
    # 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."
    result = model(text)
    print(f'result--》{result}')
命名实体识别
对一段文本进行序列标注,对每一个词汇都要进行分类,习惯用BIO或者BIOES的标识符

代码实现

# 6.实现NER任务
def dm06_ner():
​
    # 基于pipeline函数返回需要的模型
    model = pipeline(task="ner", model='./model/roberta-base-finetuned-cluener2020-chinese')
    # 直接使用model来预测
    result = model("张三丰会读《三味书屋》")
    # print(f'result--》{result}')
    pprint(result)

3.4 AutoModel方式应用预训练模型

文本分类
定义:对一个文本进行分类:eg:对一个评论文本,判断其实好评还是差评

代码实现

# 1.实现文本分类
def dm01_test_classcify():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/chinese_sentiment")
    # 2.加载模型
    my_model = AutoModelForSequenceClassification.from_pretrained("./model/chinese_sentiment")
    # 3.准备样本
    message = "人生该如何起头"
    # message = "人生该如何起头人生该如何起头人生该如何起头人生该如何起头人生该如何起头人生该如何起头人生该如何起头"
    # 4.对样本进行编码:
    # 4.1 return_tensors="pt",默认返回张量数据,而且是二维的
    # padding="max_length"不足的补齐,默认用0补齐,
    # truncation按照最大句子长度截断
    output1 = my_tokenizer.encode(message, return_tensors="pt", padding="max_length",
                                  max_length=20, truncation=True)
    print(f'output1---》{output1}')
    # # 4.2 默认返回一维列表,所以要进行后处理
    # output2 = my_tokenizer.encode(message, padding="max_length",
    #                               max_length=20, truncation=True)
    # output2 = torch.tensor([output2])
    # print(f'output2---》{output2}')
​
    # 5.将数据送入模型
    my_model.eval()
​
    result = my_model(output1)
    print(f'result--》{result}')
​
特征抽取
定义:将文本输入模型,得到特征向量的表示

代码实现

# 2.实现特征抽取
def dm02_test_feature():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/bert-base-chinese")
    # 2.加载模型
    my_model = AutoModel.from_pretrained("./model/bert-base-chinese")
    # 3.准备样本
    message = ['你是谁', '人生该如何起头']
    # 4.对样本进行编码:
    # 4.1 return_tensors="pt",默认返回张量数据,而且是二维的
    # padding="max_length"不足的补齐,默认用0补齐,
    # truncation按照最大句子长度截断
    output1 = my_tokenizer.encode_plus(message, return_tensors="pt", padding="max_length",
                                       max_length=20, truncation=True)
    print(f'output1---》{output1}')
​
​
    # # 5.将数据送入模型
    my_model.eval()
    #
    # result = my_model(input_ids=output1["input_ids"],
    #                   token_type_ids=output1["token_type_ids"],
    #                   attention_mask=output1["attention_mask"])
    result = my_model(**output1)
    # print(f'result--》{result}')
    print(f'result--last_hidden_state--》{result.last_hidden_state.shape}')
    print(f'result--pooler_output--》{result.pooler_output.shape}')
    # # 自己对结果分析,取出最大概率索引值
    # # topv, topi = torch.topk(result.logits,k=1,dim=-1)
    # # print(f'topv--》{topv}')
    # # print(f'topi--》{topi}')
​
完形填空
掩码任务,将一段文本中的某个token进行MASK,然后通过模型来预测被MASK掉的词

代码实现

# 3.实现完形填空fill_mask
def dm03_test_fill_mask():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/chinese-bert-wwm")
    # 2.加载模型
    my_model = AutoModelForMaskedLM.from_pretrained("./model/chinese-bert-wwm")
    # 3.准备样本
    message = "我想明天去[MASK]家吃饭."
    # 4.对样本进行编码:
    # 4.1 return_tensors="pt",默认返回张量数据,而且是二维的
    output1 = my_tokenizer.encode_plus(message, return_tensors="pt")
    print(f'output1---》{output1}')
    # # # 5.将数据送入模型
    my_model.eval()
    # #
    # # result = my_model(input_ids=output1["input_ids"],
    # #                   token_type_ids=output1["token_type_ids"],
    # #                   attention_mask=output1["attention_mask"])
    result = my_model(**output1).logits
    # result.logits-->[1,12,21128]
    # print(f'result--》{result}')
    print(f'result--logits--》{result.shape}')
    # print(f'result--pooler_output--》{result.pooler_output.shape}')
    # # # 自己对结果分析,取出最大概率索引值
    # 找出MASK位置对应的预测最大值的索引。MASK位置索引6
    # result[0]-->[12,21128]
    print(f'result[0]--》{result[0].shape}')
    index = torch.argmax(result[0][6]).item()
    print(index)
    # 将索引值映射为真实的token:convert_ids_to_tokens__》将索引映射为token
    token = my_tokenizer.convert_ids_to_tokens(index)
    print(f'token--》{token}')
阅读理解
question-answer,根据文本以及问题,从文本里面解答问题的答案

代码实现

def dm04_test_qa():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/chinese_pretrain_mrc_roberta_wwm_ext_large")
    # 2.加载模型
    my_model = AutoModelForQuestionAnswering.from_pretrained("./model/chinese_pretrain_mrc_roberta_wwm_ext_large")
    # 3.准备样本
    context = '我叫张三 我是一个程序员 我的喜好是打篮球'
    questions = ['我是谁?', '我是做什么的?', '我的爱好是什么?']
    # 4.将数据送入模型
    my_model.eval()
​
    for question in questions:
        print(f'question--》{question}')
        inputs = my_tokenizer.encode_plus(question, context, return_tensors='pt')
        # inputs = my_tokenizer(question, context, return_tensors='pt')
        print(f'inputs--》{inputs}')
        # print(f'inputs[input_ids]--》{inputs["input_ids"].shape}')
        outputs = my_model(**inputs)
        # print(f'outputs--》{outputs}')
        # print(f'outputs.start_logits--》{outputs.start_logits.shape}')
        # print(f'outputs.end_logits--》{outputs.end_logits.shape}')
        # 寻找最大索引位置
        # 答案的开始索引
        start_index = torch.argmax(outputs.start_logits, dim=-1).item()
        end_index = torch.argmax(outputs.end_logits, dim=-1).item()
        # print(f'start_index--》{start_index}')
        # print(f'end_index--》{end_index}')
        #根据input_ids进行答案的切片并且直接转换为token
        a = inputs["input_ids"][0][start_index: end_index+1]
        results = my_tokenizer.convert_ids_to_tokens(a)
        print(f'results--》{results}')
​
文本摘要
对文档的概括总结

代码实现

def dm05_test_summary():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/distilbart-cnn-12-6")
    # 2.加载模型
    my_model = AutoModelForSeq2SeqLM.from_pretrained("./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."
    # 4.将text文本进行张量化表示
    inputs = my_tokenizer.encode_plus(text, return_tensors='pt')
    # inputs = my_tokenizer([text], return_tensors='pt')
    print(f'inputs--》{inputs}')
    # 4.将数据送入模型
    my_model.eval()
​
    # 5.将数据送入模型
    # outputs = my_model(**inputs)
    # outputs = my_model.generate(**inputs)
    outputs = my_model.generate(inputs["input_ids"])
    print(f'outputs--》{outputs}')
    # 6.对模型预测结果进行真实token的映射,skip_special_tokens:跳过特殊字符;clean_up_tokenization_spaces=False,将标点符号和token分开
    results = my_tokenizer.decode(outputs[0], skip_special_tokens=True, clean_up_tokenization_spaces=False)
    print(f'results--》{results}')
命名实体识别
对一段文本进行序列标注,对每一个词汇都要进行分类,习惯用BIO或者BIOES的标识符

代码实现

def dm06_test_ner():
    # 1.加载分词器
    my_tokenizer = AutoTokenizer.from_pretrained("./model/roberta-base-finetuned-cluener2020-chinese")
    # 2.加载模型
    my_model = AutoModelForTokenClassification.from_pretrained("./model/roberta-base-finetuned-cluener2020-chinese")
    # print(f'my_model-->{my_model}')
    # 3.加载配置文件
    my_config = AutoConfig.from_pretrained("./model/roberta-base-finetuned-cluener2020-chinese")
    # print(f'my_config-->{my_config}')
    # 4.准备样本
    text = "我爱北京天安门,天安门上太阳升"
    # # 4.将text文本进行张量化表示
    inputs = my_tokenizer.encode_plus(text, return_tensors='pt')
    # print(f'inputs--》{inputs}')
    # 5.将数据送入模型
    my_model.eval()
    #
    # # 5.将数据送入模型
    outputs = my_model(**inputs).logits
    # outputs = my_model(inputs["input_ids"])
    # print(f'outputs--》{outputs.shape}')
    # 找到原始的id对应的token
    tokens = my_tokenizer.convert_ids_to_tokens(inputs["input_ids"][0])
    # print(f'tokens--》{tokens}')
​
    # 6.预测出结果
    output_list = [] # 存储结果
    # my_tokenizer.all_special_tokens特殊字符--['[UNK]', '[SEP]', '[PAD]', '[CLS]', '[MASK]']
    print(my_tokenizer.all_special_tokens)
    for token, logit in zip(tokens, outputs[0]):
        # print(f"token-->{token}")
        # print(f"logit-->{logit}")
        if token in my_tokenizer.all_special_tokens:
            continue
        index = torch.argmax(logit, dim=-1).item()
        # print(f'index-->{index}')
        # 将index映射为真实的label
        label = my_config.id2label[index]
        output_list.append((token, label))
    print(output_list)

4 迁移学习实践(中文文本分类)

4.0 实现流程

1.获取数据集
2.数据预处理: 实例化dataset,dataloader,注意这里面在dataloder里用了自定义函数进行文本张量化处理
3.搭建模型: 注意,我们这里用bert预训练模型来得到文本的特征表示,然后在经过自定义网络实现分类
3.模型训练: 注意,不训练bert模型的参数,只更新自己定义的网络参数
4.模型测试: 注意,如果在GPU上训练的模型,想在CPU上使用, model.load_state_dict(torch.load(path, map_location="cpu")) #将模型放到cpu上

4.1 数据预处理

定义Dataset
我们这里是直接应用的datasets第三方库里面的load_dataset的方法,可以直接返回DataSet数据源对象

代码实现

def  dm_file2dataset():
    # 1.加载训练数据集
    train_dataset = load_dataset('csv', data_files="./data/train.csv", split="train")
    # 1.1第二种加载方式
    # train_dataset = load_dataset(path='./data', data_files="train.csv", split="train")
    print(f'训练数--》{train_dataset[0]}')
    # print(f'训练数据取出前三行数据---》{train_dataset[:3]}')
    # 2.加载测试数据集
    test_dataset = load_dataset('csv', data_files="./data/test.csv", split="train")
    # print(f'test_dataset--》{test_dataset}')
    # print(f'测试数据取出前三行数据---》{test_dataset[:3]}')
    # 3.加载验证数据集
    valid_dataset = load_dataset('csv', data_files="./data/validation.csv", split="train")
    # print(f'valid_dataset--》{valid_dataset}')
    # print(f'验证数据取出前三行数据--》{valid_dataset[:3]}')
    return train_dataset, test_dataset, valid_dataset
定义批处理函数
在Dataloader里面自动调用,目的是处理dataset里面的数据,进行张量化处理

代码实现

def collate_fn1(data):
    '''
    data:是从dataset里面获取的数据.类型为list,[第一个样本,第二样本,。。。]
# data传过来的数据是list eg: 批次数8,8个字典
    # [{'text':'xxxx','label':0} , {'text':'xxxx','label':1}, ...]
    '''
    # print(f'data-->{len(data)}')
    # print(f'data[0]-->{data[0]}')
    sents = [item["text"] for item in data]
    labels = [item["label"] for item in data]
    inputs = my_pre_tokenizer.batch_encode_plus(sents, truncation=True,
                                            max_length=500, padding="max_length",
                                            return_tensors='pt',
                                            return_length=True)
    # print(f'inputs-——》{inputs}')
    # print(f'inputs["input_ids"]-——》{inputs["input_ids"].shape}')
    inputs_ids = inputs["input_ids"]
    token_type_ids = inputs["token_type_ids"]
    attention_mask = inputs["attention_mask"]
    labels = torch.LongTensor(labels)
    return inputs_ids, token_type_ids, attention_mask, labels
实例化Dataloader

代码实现

def get_dataloader():
    # 1.获得dataset的数据源对象
    train_dataset = load_dataset('csv', data_files="./data/train.csv", split="train")
    # 2.实例化dataloader
    my_dataloader = DataLoader(dataset=train_dataset, batch_size=8, shuffle=True,
                                collate_fn=collate_fn1, drop_last=True)
    # # print(f'my_dataloader-->{len(my_dataloader)}')
    # for inputs_ids, token_type_ids, attention_mask, labels in my_dataloader:
    #     print(f'inputs_ids--》{inputs_ids.shape} ')
    #     print(f'token_type_ids--》{token_type_ids.shape} ')
    #     print(f'attention_mask--》{attention_mask.shape} ')
    #     print(f'labels--》{labels.shape} ')
    #     break
    return my_dataloader

4.2 搭建模型

应用迁移学习的思路:bert预训练模型特征处理+自定义模型(分类)

代码实现

# 自定义下游任务模型
class AiModel(nn.Module):
    def __init__(self):
        super().__init__()
        # 定义输出层
        # 768代表bert模型的特征表示,2代表二分类
        self.linear = nn.Linear(768, 2)

    def forward(self, input_ids, token_type_ids, attention_mask):
        # 不对预训练模型的参数更新
        # my_pre_model代表bert预训练模型的对象
        with torch.no_grad():
            bert_output = my_pre_model(input_ids=input_ids, attention_mask=attention_mask,token_type_ids=token_type_ids)
        # print(f'bert模型的输出结果-->{bert_output}')
        # print(f'bert模型的last_hidden_state-->{bert_output.last_hidden_state.shape}')
        # print(f'bert模型的pooler_output-->{bert_output.pooler_output.shape}')
        # bert模型的输出结果包括:last_hidden_state,pooler_output
        # last_hidden_state(最后一层)--》[8,500,768]-->8个样本,每个样本500个单词,每个单词用768维度的向量表示
        # pooler_output--》[8,768]-->这个代表8个样本,每个样本都用768维度的向量表示。
        # bert模型输出有一个特殊的字符CLS,pooler_output的结果是每一个样本得到CLS这个token对应的向量表示,
        # 原始论文中说明:当利用bert模型去做分类任务的时候,一般直接取CLS这个token对应的向量表示当前这个样本的特征,进行实现分类
        output = self.linear(bert_output.pooler_output) # output-->[8, 2]
        return output

4.3 模型训练

注意: 1,因为使用的预训练模型,所以在训练的时候,对自定义的模型加上,model.train();2.不更新bert预训练模型的参数《requires_grad=False》3.如果要想在GPU上训练:3.1将预训练模型的对象放到GPU上,3.2自定义的模型对象放到GPU上,3.3模型的输入放到GPU上,eg:input_ids = input_ids.to('cuda');model = model.to('cuda')

代码实现

# 定义训练方法
def train_model():
    # 1.加载训练数据集
    train_dataset = load_dataset('csv', data_files="./data/train.csv", split="train")
    # 2.实例化模型
    my_model = AiModel().to(device)
    # 3. my_pre_model对预训练模型的参数requires_grad设置为False,不计算梯度
    for param in my_pre_model.parameters():
        # print(f'param--》{param.requires_grad}')
        param.requires_grad_(False)
        # print(f'param--》{param.requires_grad}')
    # p.numel()计算每个参数的元素个数
    # total_params = sum(p.numel() for p in my_pre_model.parameters())
    # 4.实例化损失函数对象
    # mean计算一个批次样本的平均损失,sum是损失之和,
    my_crossentropy = nn.CrossEntropyLoss(reduction='mean')
    # 5.实例化优化器
    my_adamw = AdamW(my_model.parameters(), lr=5e-4)
    # 6.设置模型为训练模型
    my_model.train()
    # 7.开始训练
    for epoch_idx in range(3):
        start_time = time.time()
        # 实例化dataloader
        my_dataloader = DataLoader(dataset=train_dataset, batch_size=8,
                                   shuffle=True, collate_fn=collate_fn1,
                                   drop_last=True)
        # 内部数据迭代
        for i, (inputs_ids, token_type_ids, attention_mask, labels) in enumerate(tqdm(my_dataloader), start=1):
            # print(f'labels---》{labels}')
            inputs_ids = inputs_ids.to(device)
            token_type_ids = token_type_ids.to(device)
            attention_mask = attention_mask.to(device)
            labels = labels.to(device)
            output = my_model(inputs_ids, token_type_ids, attention_mask,)
            # print(f'output--》{output.shape}')
            # print(f'output--》{output}')
            # 计算损失
            loss = my_crossentropy(output, labels)
            # print(f'loss--》{loss}')
            # 梯度清零
            my_adamw.zero_grad()
            # 反向传播
            loss.backward()
            # 梯度更新
            my_adamw.step()

            # 打印日志:每隔5步计算平均准确率
            if i % 5 == 0:
                tem = torch.argmax(output, dim=-1)
                # print(f'tem--》{tem}')
                acc = (tem == labels).sum().item() / len(labels)
                use_time = time.time() - start_time
                print("当前训练的轮次%d,迭代的步数%d,当前的损失%.2f, 当前的准确率%.2f, 时间%d"%(epoch_idx+1, i,
                                                                                       loss, acc, use_time))

        # 保存模型
        torch.save(my_model.state_dict(), "./AI19_model/classify_%d.bin"%(epoch_idx+1))

4.4 模型预测

注意: model.eavl()和with torch.no_grad()
# 如果在GPU上训练的模型,想在CPU上使用, model.load_state_dict(torch.load(path, map_location="cpu")) #将模型放到cpu上

代码实现

# 定义模型评估方法
def test_model():
    # 1.加载训练数据集
    test_dataset = load_dataset('csv', data_files="./data/test.csv", split="train")
    # 2.实例化dataloader
    test_dataloader = DataLoader(dataset=test_dataset, batch_size=8, shuffle=True,
                                 collate_fn=collate_fn1, drop_last=True)
    # 3.加载训练好的模型
    path = './AI19_model/classify_3.bin'
    my_model = AiModel()
    my_model.load_state_dict(torch.load(path, map_location="cpu")) #将模型放到cpu上
    # my_model = my_model.to("cpu")
    # 4.定义测试的参数
    correct = 0
    total = 0
    # 5.设定模型为eval
    my_model.eval()
    # 6.迭代数据送入模型
    for i, (inputs_ids, token_type_ids, attention_mask, labels )in enumerate(tqdm(test_dataloader), start=1):
        with torch.no_grad():
            output = my_model(inputs_ids, token_type_ids, attention_mask,)

        # 得到预测的最大概率值的索引
        temp = torch.argmax(output, dim=-1)
        # 得到预测正确的个数
        correct += (temp == labels).sum().item()
        # 当前训练的样本个数
        total += len(labels)
        # 每隔5步打印测试的结果
        if i % 5 == 0:
            print(f'平均准确率-->{correct/total}')
            # 取出一个批次的第一个样本来查验是否预测正确
            text_list = my_pre_tokenizer.decode(inputs_ids[0],skip_special_tokens=True)
            print(f'原始的文本是--》{text_list}', end='    ')
            print(f'模型预测的结果是:{temp[0]}, 真实的结果是:{labels[0]}')

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐