【NLP笔记】预训练+微调范式之OpenAI Transformer、ELMo、ULM-FiT、Bert..

news2024/12/23 13:48:30

文章目录

  • OpenAI Transformer
  • ELMo
  • ULM-FiT
  • Bert
    • 基础结构
    • Embedding
    • 预训练&微调

【原文链接】: BERT: Pre-training of Deep Bidirectional Transformers for
Language Understanding

【本文参考链接】

  • The Illustrated BERT, ELMo, and co. (How NLP Cracked Transfer Learning)
  • 【NLP从入门到大模型】5.图解Bert

【其他值得关注的预训练+微调范式模型】

  • 【深度学习】GPT-1
  • GPT系列:GPT-2详解
  • RoBERTa 详解(Bert改进模型)
  • XLNet模型(Bert改进模型)
  • ALBert 详解(Bert改进模型)

2018年前后有很多著名的NLP相关的论文产出,形成了一种预训练语义表征模型+下游任务微调的范式,其中长期内影响力较大的之一Bert。

OpenAI Transformer

OpenAI Transformer(OpenAI的Transformer模型)是在2017年底提出的。它是由OpenAI团队提出的一种基于Transformer架构的语言模型,用于处理自然语言处理任务。OpenAI Transformer是一个堆叠的12层Decoder结构(Decoder-Only),在解码器的注意力模块中,由于没有Encoder,所以就不是Cross-Attention,改为self-attention+mask的方式。
在这里插入图片描述
整个训练过程是无监督的,使用大量的未标记数据集训练,将预测下一个词汇作为模型的预测目标。然后预训练好的Transformer结构(Decoder-Only)后面再加上简单的网络结构进行参数调整,并设定目标场景的预测结果,就实现了通过微调去适应下游任务。
在这里插入图片描述

ELMo

ELMo(Embedding for Language Model)是由斯坦福大学的研究团队在2018年提出的,是一种基于双向 LSTM(长短期记忆)模型的语言表示方法,通过在大规模语料库上训练的双向 LSTM 模型来生成深度上下文相关的词向量。
在这里插入图片描述

大语言模型通过海量数据训练出来的Tokenizer可直接用于文本向量化,开源的大模型Tokenizer使得大家能通过下载已经开源的词嵌入模型来完成NLP的任务。Word2vec是将单词固定为指定长度的向量,和以往的经典词嵌入模型不同的是,ELMo则是在为每个单词分配词向量之前先查看整个句子,然后使用bi-LSTM结合上下文语境来训练它对应的词向量。
在这里插入图片描述

ELMo会训练一个模型,这个模型接受一个句子或者单词的输入,输出最有可能出现在后面的一个单词,采用海量语料进行无监督训练。ELMo通过下图的方式将hidden states的状态组合起来提炼出具有语境意义的词嵌入方式(全连接后加权求和)。
在这里插入图片描述

ULM-FiT

ULM-FiT(Universal Language Model Fine-tuning)是一种用于迁移学习的技术,由 Jeremy Howard 和 Sebastian Ruder 在2018年提出。它基于递归神经网络(RNN)构建的语言模型,并使用了一系列创新的技术来提高迁移学习的效果。

ULM-FiT 的核心思想是首先在大规模的通用语料库上训练一个通用的语言模型,然后通过微调(Fine-tuning)的方式,将这个通用模型应用于特定的任务。具体来说,ULM-FiT 包含以下几个关键步骤:

预训练通用语言模型:首先,在大规模的通用语料库上训练一个通用的语言模型,比如基于AWD-LSTM(ASGD Weight-Dropped LSTM)的语言模型。这个通用模型可以捕捉语言的一般规律和语义信息。

微调:接下来,将预训练的通用模型在特定任务的数据集上进行微调,以适应特定任务的语言模式和特征。微调的过程包括在任务数据上进行进一步的训练,并对模型进行调整以提高任务性能。

多阶段微调策略:ULM-FiT 提出了一种多阶段微调的策略,其中包括逐层解冻和差异学习率调整等技术,以提高微调的效果。这种策略可以使模型逐步适应新任务的特征,从而更好地利用预训练的知识。

ULM-FiT 已经在许多自然语言处理任务中取得了显著的成功,包括文本分类、命名实体识别、情感分析等。它的主要优势在于通过预训练通用模型和微调的方式,能够在少量标注数据的情况下快速有效地解决特定任务。

Bert

BERT 是由Google在2018年10月发布的。它是一种基于Transformer模型的语言表示方法,通过在大规模文本数据上进行预训练来学习文本的语义表示。BERT 通过Masked Language Modeling(MLM)和Next Sentence Prediction(NSP)等任务来训练模型,可以捕捉文本序列中的双向语境信息,取得了在多项自然语言处理任务上的领先性能。Bert的训练模式是半监督预训练(Pre-train)+有监督的微调(fine-tuning),预训练出一个强大的文本表征模型,然后根据具体任务进行微调,实现在特定任务上的应用。Goole开源这个模型,并提供预训练好的模型,这使得所有人都可以通过它来构建一个涉及NLP的算法模型,节约了大量训练语言模型所需的时间、精力、知识和资源。

在这里插入图片描述

基础结构

Bert的基础架构是一个堆叠多层的Encoder架构,参数量更大的Bert就是对应的层数更多(如:Bert-base有12层Encoder,Bert-Large共24层Encoder),Bert就是一种Encoder-Only的架构。
在这里插入图片描述
BERT与Transformer 的编码方式一样。将固定长度的字符串作为输入,数据由下而上传递计算,每一层都用到了self attention,并通过前馈神经网络传递其结果,将其交给下一个编码器。
在这里插入图片描述
每个位置返回的输出都是一个隐藏层大小的向量(Base版本BERT为768),该输出向量可以作为下游微调任务的输入向量,在论文中指出使用单层神经网络作为分类器就可以取得很好的效果。
在这里插入图片描述

Embedding

BERT 模型中的词向量(Word Embedding)、位置编码(Positional Encoding)、以及段落编码(Segment Embedding)是通过一系列的处理步骤得到的:

  1. 词向量(Word Embedding)
    BERT 使用了 WordPiece 或 Byte-Pair Encoding(BPE)等分词算法将输入文本序列分割成词语或子词(subwords)。然后,每个词语或子词都被映射为一个固定长度的向量,这个过程就是词向量。BERT 模型使用了多层的词向量表示,其中包括 WordPiece Embeddings 和 Token Type Embeddings。WordPiece Embeddings 将每个词语或子词映射为一个固定长度的向量,而 Token Type Embeddings 用于区分不同句子之间的关系(同Transformer)。
  2. 位置编码(Positional Encoding)
    在 BERT 模型中,位置编码用于向词向量中添加关于词语或子词在序列中位置的信息。BERT 使用了一种特殊的位置编码方法,通常是通过加法或者叠加来将位置编码添加到词向量中。这种位置编码能够为模型提供关于词语在序列中相对位置的信息,从而帮助模型理解文本的顺序关系(同Transformer)。
  3. 段落编码(Segment Embedding)
    在一些需要处理多个句子的任务中,BERT 还会添加段落编码以区分不同句子之间的关系。段落编码通常是一个固定长度的向量,用于区分不同句子所属的段落或者句子之间的关系。在输入序列中,通过在句子的开头添加特殊的标记(比如 [CLS] 标记)来表示句子的开始,然后添加段落编码来区分不同句子之间的关系(如:第一个句子编码段落token全为0,第二个全为1)。

在这里插入图片描述

预训练&微调

前面有说过Bert的模式也是预训练+下游任务微调,如下图所示:
在这里插入图片描述

  1. 预训练
  • Masked Language Modeling(MLM):通过随机掩盖序列中的一些token,让模型去预测被掩盖的token是什么。如在训练过程中随机mask 15%的token,其中80%的序列我们用[MASK]去取代要掩码的token,10%的序列我们将待掩码token随机替换为其他token,10%的序列保持不变,最终的损失函数只计算被mask掉的那个token。
  • Next Sentence Prediction(NSP):让模型理解两个句子之间的联系。训练的输入是句子A和B,B有一半的几率是A的下一句,输入这两个句子,模型预测B是不是A的下一句。预训练的时候可以达到97-98%的准确度。

模型地址:https://huggingface.co/google-bert/bert-base-chinese

# 安装新版本(>=4.0)的transformer,内置bert
from transformers import AutoTokenizer, AutoModelForMaskedLM
# 加载Tokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
# 加载模型
model = AutoModelForMaskedLM.from_pretrained("bert-base-chinese")

# MLM任务测试
# BERT 在预训练中引入了 [CLS] 和 [SEP] 标记句子的开头和结尾,准备输入模型的语句
text = '中国的首都是哪里? 北京是 [MASK] 国的首都。'  
inputs = tokenizer(text, return_tensors='pt')
# 获取MASK的位置
mask_position = inputs["input_ids"].tolist()[0].index(tokenizer.mask_token_id)
print("tokenized txt: {}".format(inputs))
# 使用模型进行预测
with torch.no_grad():
    outputs = bert_model(**inputs).logits

# 获取MASK位置的预测分数,并找到预测得分最高的token
predicted_token_idx = outputs[0, mask_position].argmax().item()
predicted_token = tokenizer.convert_ids_to_tokens([predicted_token_idx])[0]

print(f"The masked token in '{text}' is predicted as '{predicted_token}'")

# NSP任务测试
model_path = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(model_path)
model = BertForNextSentencePrediction.from_pretrained(model_path)

sen_code1 = tokenizer.encode_plus('今天天气怎么样', '今天天气很好')
sen_code2 = tokenizer.encode_plus('小明今年几岁了', '今天天空很蓝')
tokens_tensor = torch.tensor([sen_code1['input_ids'], sen_code2['input_ids']])

model.eval()

outputs = model(tokens_tensor)
seq_relationship_scores = outputs[0]  # seq_relationship_scores.shape= torch.Size([2, 2])
sample = seq_relationship_scores.detach().numpy()  # sample.shape = (2, 2)

pred = np.argmax(sample, axis=1)
print(pred)
# [0 1] 0表示是上下句,1表示不是上下句
  1. 微调
    在这里插入图片描述
    Bert可以实现的几种微调任务:短文本相似 、文本分类、QA机器人、语义标注等微调任务,下面以一个任务为例:
# 下载文本分类数据集
wget http://github.com/skdjfla/toutiao-text-classfication-dataset/raw/master/toutiao_cat_data.txt.zip
import codecs
import json

import torch
from sklearn.model_selection import train_test_split
from torch.utils.data import Dataset, DataLoader
import numpy as np
from transformers import BertTokenizer
from transformers import BertForSequenceClassification, get_linear_schedule_with_warmup


class NewsDataset(Dataset):
    """
    新闻分类数据集
    """

    def __init__(self, encodings, labels):
        self.encodings = encodings
        self.labels = labels

    # 读取单个样本
    def __getitem__(self, idx):
        item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}
        item['labels'] = torch.tensor(int(self.labels[idx]))
        return item

    def __len__(self):
        return len(self.labels)


class NewsClassifier(object):
    """
    新闻分类器
    """

    @classmethod
    def finetuning(cls):
        """
        微调新闻分类器
        """
        model_path = 'bert-base-chinese'
        # 加载预训练的向量化模型
        tokenizer = BertTokenizer.from_pretrained(model_path)
        # 加载预训练的分类模型
        model = BertForSequenceClassification.from_pretrained(model_path, num_labels=17)
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        model.to(device)
        # 加载数据集
        train_dataset, test_dataset = cls.load_dataset(tokenizer)
        # 单个读取到批量读取
        train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
        test_dataloader = DataLoader(test_dataset, batch_size=16, shuffle=True)
        # 优化方法
        optim = torch.optim.AdamW(model.parameters(), lr=2e-5)
        total_steps = len(train_loader) * 1
        scheduler = get_linear_schedule_with_warmup(optim,
                                                    num_warmup_steps=0,  # Default value in run_glue.py
                                                    num_training_steps=total_steps)
        for epoch in range(1):
            print("------------Epoch: %d ----------------" % epoch)
            cls.train(model, train_loader, optim, device, scheduler, epoch)
            cls.validation(model, test_dataloader, device)

        model.save_pretrained("output/bert-news-clf")
        tokenizer.save_vocabulary("output/bert-news-clf")

    @classmethod
    def train(cls, model, train_loader, optim, device, scheduler, epoch):
        """
        训练函数
        """
        model.train()
        total_train_loss = 0
        iter_num = 0
        total_iter = len(train_loader)
        for batch in train_loader:
            # 正向传播
            optim.zero_grad()
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
            outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
            loss = outputs[0]
            total_train_loss += loss.item()

            # 反向梯度信息
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)

            # 参数更新
            optim.step()
            scheduler.step()

            iter_num += 1
            if iter_num % 100 == 0:
                print("epoth: %d, iter_num: %d, loss: %.4f, %.2f%%" % (
                    epoch, iter_num, loss.item(), iter_num / total_iter * 100))

        print("Epoch: %d, Average training loss: %.4f" % (epoch, total_train_loss / len(train_loader)))

    @classmethod
    def flat_accuracy(cls, preds, labels):
        """
        计算准确率
        """
        pred_flat = np.argmax(preds, axis=-1).flatten()
        labels_flat = labels.flatten()
        return np.sum(pred_flat == labels_flat) / len(labels_flat)

    @classmethod
    def validation(cls, model, test_dataloader, device):
        """
        验证函数
        """
        model.eval()
        total_eval_accuracy = 0
        total_eval_loss = 0
        for batch in test_dataloader:
            with torch.no_grad():
                # 正常传播
                input_ids = batch['input_ids'].to(device)
                attention_mask = batch['attention_mask'].to(device)
                labels = batch['labels'].to(device)
                outputs = model(input_ids, attention_mask=attention_mask, labels=labels)

            loss = outputs[0]
            logits = outputs[1]

            total_eval_loss += loss.item()
            logits = logits.detach().cpu().numpy()
            label_ids = labels.to('cpu').numpy()
            total_eval_accuracy += cls.flat_accuracy(logits, label_ids)

        avg_val_accuracy = total_eval_accuracy / len(test_dataloader)
        print("Accuracy: %.4f" % avg_val_accuracy)
        print("Average testing loss: %.4f" % (total_eval_loss / len(test_dataloader)))
        print("-------------------------------")

    @classmethod
    def load_dataset(cls, tokenizer):
        """
        加载新闻分类数据集
        """
        data_path = 'dataset/toutiao_cat_data.txt'
        # 标签
        news_label = [int(x.split('_!_')[1]) - 100 for x in codecs.open(data_path)]
        # 存储标签
        label_text_json = {}
        for x in codecs.open(data_path):
            groups = x.split('_!_')
            label_text_json[int(x.split('_!_')[1]) - 100] = groups[2]
        with open('output/news_label.json', 'w') as file_obj:
            json.dump(label_text_json, file_obj, indent=4)

        # 文本
        news_text = [x.strip().split('_!_')[-1] if x.strip()[-3:] != '_!_' else x.strip().split('_!_')[-2]
                     for x in codecs.open(data_path)]

        # 划分为训练集和验证集
        # stratify 按照标签进行采样,训练集和验证部分同分布
        x_train, x_test, train_label, test_label = train_test_split(news_text[:50000],
                                                                    news_label[:50000],
                                                                    test_size=0.2,
                                                                    stratify=news_label[:50000])

        train_encoding = tokenizer(x_train, truncation=True, padding=True, max_length=64)
        test_encoding = tokenizer(x_test, truncation=True, padding=True, max_length=64)

        train_dataset = NewsDataset(train_encoding, train_label)
        test_dataset = NewsDataset(test_encoding, test_label)
        return train_dataset, test_dataset

    @classmethod
    def predict(cls):
        """
        微调后的模型预测函数
        """
        s = '金价下跌预示着什么?'

        model_path = 'output/bert-news-clf'
        tokenizer = BertTokenizer.from_pretrained(model_path)
        model = BertForSequenceClassification.from_pretrained(model_path, num_labels=17)

        sen_code = tokenizer.encode_plus(s)
        tokens_tensor = torch.tensor([sen_code['input_ids']])
        attention_mask = torch.tensor([sen_code['attention_mask']])

        model.eval()

        outputs = model(tokens_tensor, attention_mask)
        outputs = outputs[0].detach().numpy()
        print(outputs)
        outputs = outputs[0].argmax(0)
        print(outputs)
        # 4

        with open('output/news_label.json') as file_obj:
            news_label_json = json.load(file_obj)
            print(news_label_json[str(outputs)])
            # news_finance


if __name__ == '__main__':
    # 微调
    NewsClassifier.finetuning()
    # 调用微调的模型进行预测
    NewsClassifier.predict()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1533069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASP.NET-Global.asax使用详解

本文介绍了如何使用Global.asax文件来增强ASP.NET Web应用程序的功能。首先,介绍了Global.asax文件的作用和基本功能。接着,详细探讨了在Global.asax中实现定时任务、应用程序级别的错误处理、应用程序启动和结束时执行特定逻辑等功能。随后,…

Macbook m1安装docker详细教程

下载安装包 进入官网 https://www.docker.com/ 下滑找到下载位置 下滑找到Mac对应安装包 等待下载完成即可。 安装 双击打开下载的安装包 将Docker拖到Applications中 安装完成后,找到安装的Docker 双击打开 点击accept同意 进入下面: 点fini…

Chart-based Reasoning: Transferring Capabilities from LLMs to VLMs

Chart-based Reasoning: Transferring Capabilities from LLMs to VLMs 相关链接:arXiv 关键字:Vision-language models、ChartQA、multimodal reasoning、fine-tuning、synthetic data 摘要 本文提出了一种技术,将大型语言模型(…

Python分析人民日报关于台湾的报道

【项目背景】 《人民日报》数据挖掘,时间:1949.10.1-2023.12.31 标题含有“台湾”的报道 需要以下内容 1、标题,即上述时间段的报道标题和相应的报道时间、版面 2、包含标题、时间、版面的所有报道内容 3、报道的年份和数量的趋势图 4、…

CV论文--2024.3.21

1、Chain-of-Spot: Interactive Reasoning Improves Large Vision-Language Models 中文标题:Chain-of-Spot:交互式推理改进大型视觉语言模型 简介:在视觉语言理解领域,模型在解释和推理视觉内容方面的熟练程度已经成为许多应用的…

企业组织机构代码查询入口词令直达关键词口令是什么?

什么是企业组织机构代码? 组织机构代码是国内依法注册、依法登记的机关、企事业单位、社会团体,以及其他组织机构颁发一个在全国范围内唯一的、始终不变的代码标识。组织机构代码在单位的运营和管理中发挥着重要作用。 如何查询获取企业组织机构代码&a…

【机器学习】基于萤火虫算法优化的BP神经网络分类预测(FA-BP)

目录 1.原理与思路2.设计与实现3.结果预测4.代码获取 1.原理与思路 【智能算法应用】智能算法优化BP神经网络思路【智能算法】萤火虫算法(FA)原理及实现 2.设计与实现 数据集: 多输入多输出:样本特征24,标签类别4。…

一些常见漏洞原理

一些漏洞的原理: SQL注入漏洞: SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数…

day65 分层实现商品管理系统

分层实现商品管理系统 事务的特点,隔离级别的分类 在java中如何实现事务的操作 商品信息: 在goods表中作为子表引用class主表的主键作为goods表的外键 有对应主外键关联时也就是n对1的关系时 在使用jdbc技术时goods类对象要包含class类对象作为goods对…

从浏览器地址栏输入 URL 到请求返回发生了什么?

从浏览器地址栏输入 URL 到请求返回发生了什么? 从浏览器地址栏输入 URL 到请求返回发生了什么?简略的答案什么是 URL?DNS域名解析TCP建立连接:三次握手发送HTTP请求消息结构请求报文请求行请求头请求体 响应报文响应行响应头响应…

Vue 计算属性和watch监听

1.1.计算属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- 引入vue.js --><script src"node_modules/vue/dist/vue.js"></script> </h…

LeetCode 刷题 --- 快速幂

前言&#xff1a; 幂运算是一种常见的运算&#xff0c;求取a^n,最容易想到的方法便是通过循环逐个累乘&#xff0c;其复杂度为O(n)&#xff0c;这在很多时候是不够快的&#xff0c;所以我们需要一种算法来优化幂运算的过程。 快速幂&#xff0c;二进制取幂&#xff08;Binary…

【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结

【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结 模糊图像增强技术之去噪声类滤波场景应用小结—【蘇小沐】 文章目录 【视频图像取证篇】模糊图像增强技术之去噪声类滤波场景应用小结&#xff08;一&#xff09;去噪声类滤波器1、去块滤波器&#xff08;Deblo…

基于Vue.js和D3.js的智能停车可视化系统

引言 随着物联网技术的发展&#xff0c;智能停车系统正逐渐普及。前端作为用户交互的主要界面&#xff0c;对于提供直观、实时的停车信息至关重要。 目录 引言 一、系统设计 二、代码实现 1. 环境准备 首先&#xff0c;确保您的开发环境已经安装了Node.js和npm。然后&…

Qt 多元素控件

Qt开发 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例. QTableView 是基于 MVC 设计的控件. QTableView 自身不持有数据. 使用QTableView 的 …

Prometheus 配置Basic auth认证

官方配置说明&#xff1a; Basic auth | Prometheus 一、生成密码加密串 Prometheus于2.24版本&#xff08;包括2.24&#xff09;之后提供Basic Auth功能进行加密访问&#xff0c;在浏览器登录UI的时候需要输入用户密码&#xff0c;访问Prometheus api的时候也需要加上用户密…

Qt 鼠标滚轮示例

1.声明 void wheelEvent(QWheelEvent *event) override;2.实现&#xff08;方便复制、测试起见用静态变量&#xff09; #include <mutex> void MainWindow::wheelEvent(QWheelEvent *event) {static QLabel *label new QLabel("Zoom Level: 100%", this);st…

Cesium for UE-01-虚幻引擎的下载安装及插件配置

学习Cesium for UE的过程笔记 下载UE 下载 下载完成后&#xff0c;点击运行&#xff0c;执行安装 安装完成后需要下载epic game 下载完成&#xff0c;需要登录 没有账号的自行注册&#xff0c;或者选一个有账号的平台登录即可 选择虚幻引擎&#xff0c;再点击库 可以看到下…

Cronos zkEVM 基于 Covalent Network(CQT)数据可用性 API,推动其 Layer2 DeFi 生态更好地发展

在一项旨在显著改善 DeFi 生态的战略举措中&#xff0c;Cronos 与 Covalent Network&#xff08;CQT&#xff09;携手合作&#xff0c;以期待 Cronos zkEVM 的推出。这一整合&#xff0c;预计将进一步降低以太坊生态系统的交易成本、提升交易速度&#xff0c;并带来更好的交易体…

全面:vue.config.js 的完整配置

vue.config.js是Vue项目的配置文件&#xff0c;用于配置项目的构建、打包和开发环境等。 在Vue CLI 3.0之后&#xff0c;项目的配置文件从原来的build和config目录下的多个配置文件&#xff0c;合并成了一个vue.config.js文件。这个文件可以放在项目的根目录下&#xff0c;用于…