IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 【Pytorch】RoBERTa+LSTM+多头自注意力(文本分类) -> 正文阅读

[人工智能]【Pytorch】RoBERTa+LSTM+多头自注意力(文本分类)

【Pytorch】BERT+LSTM+多头自注意力(文本分类)

2018年Google提出了BERT[1](Bidirectional Encoder Representations from Transformers)预训练模型,刷新了11项NLP任务的精度,在NLP领域掀起一波预训练(pre-training)模型热潮。通过对BERT、RoBERTa、GPT等预训练模型微调(fine-tuning)或者作为文本的特征提取器进行迁移学习成为当时直到现在最流行的文本分类方法。

1. BERT基本原理

主流的预训练模型包括:BERT、XLNET、Roberta、MacNet、GPT等,这些预训练模型大多是BERT based。BERT由Transformer的encoder堆叠而成,可以简单的分为3层:输入层、中间层、输出层;输出层有两个输出,一个是句嵌入(pooler output),即文本的开始标志[CLS]的最后一层输出,包含了整个文本的特征;另一个是字嵌入(sequence output),即文本所有字token的最后一层输出,其基本结构如下图 :
图 1 BERT基本结构
图 1 BERT基本结构

可以看到BERT基本结构由12个Transformer的encoder组成,提供了pooler output和sequence output两个输出。
为了使得BERT模型能够适应各类任务,统一将输入格式转化为:

其中[CLS]表示分类任务的特殊token,输出为pooler output,[SEP]为分隔符。
此外,BERT和Transformer一样也加入了位置编码 position encoding,使用的方法是类似词嵌入的方式(Parametric),直接获得位置嵌入。
由于对输入进行了改造,使得模型可能有多个句子segment,为了识别字token属于哪个句子,需要加入segment的嵌入编码,所以BERT的输入融合了word embedding、position encoding和segment embedding,如下图:
在这里插入图片描述

2. BERT的一般过程

BERT等预训练模型分为两个阶段:

  1. 预训练阶段,通过在海量语料库上以无监督学习的方式为文本学习语言特征,即从一个文本到一组特征的过程,这组特征可以是一个新的文本也可以是一组标签的概率等。
  2. 微调阶段,待模型预训练好后,可以直接使用BERT将文本转换为可以动态学习的特征,即把BERT视为神经网络的特征提取器,并在前向传播时对文本进行(字、句)嵌入,反向传播时动态学习并修改这个嵌入,然后在这个特征提取器的基础上再添加一个网络层便可以完成对特定任务的微调;比如情感分析任务,只需在pooler output层的后面加一个全连接层,神经元个数为情感类别数,再经过softmax即可得到情感分类概率,或者将Sequence output视为字嵌入结合LSTM、CNN等模型。所以说BERT是一个可以在不同NLP领域进行迁移学习的模型。
    由于预训练阶段需要海量的语料和大量的计算机资源,所以google开源了各语言预训练模型[2],本文主要采用了哈工大的“chinese-roberta-wwm-ext”[3]。

3. RoBERTa的使用

RoBERTa由Yinhan Liu等人[4]在2019年提出,他们在BERT的基础上进一步精化和优化,主要在三方面对BERT做出改进:

  1. 参数量:更大的batch_size,更多的训练样本,还使用BPE(Byte-Pair Encoding)来处理文本数据。
  2. 优化器:原BERT优化函数采用的是Adam默认的参数,其中β_1=0.9,β_2=0.999,在RoBERTa中考虑采用了更大的batches,所以将β_2改为了0.98。
  3. 训练策略:改用了动态掩码的方式训练模型,证明了NSP(Next Sentence Prediction)训练策略的不足;
    RoBerta有两个输入:
  4. 输入索引(input_ids),输入文本各字在vacab中的索引,需要设置一个文本最大长度sequence_length,长截断,短用0填充。size: [batch_size, sequence_length].
  5. 注意力遮掩(attention_mask),由于文本是变长的,且有填充操作,为了识别文本真实长度/需要mask的长度,设置attention_mask,字用1表示,填充用0表示。size: [batch_size, sequence_length].
    RoBerta有两个输出:
    1.[CLS]的输出(pooler output),对应文本标识符[CLS]的最后一层输出,包含文本整体特征,可作为文本的句嵌入。size: [batch_size, WordVec_size]。
  6. 序列输出(sequence output),对应的是序列中的所有字的最后一层输出,可视为文本的字嵌入。size: [batch_size, sequence_length, WordVec _size]。

4. RoBERTa- LSTM -多头自注意力模型

LSTM结合多头自注意力模型可见本人的上上篇博客,本文将结合RoBERTa - LSTM - 多头自注意力(Muti-Attention)三者建立分类模型(在跑实验的时候可以设置为BiLSTM或者BiGRU)。
上文说到,RoBerta有两个输出,一个是[CLS]的输出,可作为文本的句嵌入,另一个是序列输出(sequence output),可视为文本的字嵌入,那么我们能不能同时结合两个输出做文章呢?简单地说就是,将字嵌入通过LSTM -多头自注意力得到一个新的句嵌入,然后将该句嵌入和RoBERTa的句嵌入concat,这样不就得到了一个同时结合了RoBERTa - LSTM - 多头自注意力的句嵌入了吗,再将其输入到全连接层(分类器)即可进行文本分类任务了!(其他分类任务同理,不同点只在于数据预处理)
具体流程见下图:
在这里插入图片描述

5. 模型部分源码

模型基于pytorch平台,只展示部分关键代码。
网络参数设置:

class Parameters:
    def __init__(self, parameters):
        # 设置超参数
        self.Doc_Size = parameters[0]      # 文本词数
        self.WordVec_Size = parameters[1]  # 词向量长度
        self.WeightDecay = parameters[2]   # 权重衰减系数
        self.epoches = parameters[3]       # 迭代次数
        self.batch_size = parameters[4]    # 小批量样本数,即每次训练batch_size个
        self.features = parameters[5]      # 分类特征数,共有6个情绪{'angry', 'fear', 'happy', 'neutral', 'sad', 'surprise'}
        self.learning_rate = parameters[6]  # 学习速率/梯度下降的步长
        self.Epsilon = parameters[7]       # -
        self.dropout_rate = parameters[8]  # dropout的概率一般0.0~0.5
        self.seed = parameters[9]          # 随机种子
        self.hidden_dim = parameters[10]   # 隐藏状态的维数/隐藏层节点个数,视词向量维度而定
        self.n_layers = parameters[11]     # lstm隐藏层层数
        self.bidirect = parameters[12]     # 是否双向
        self.cuda = parameters[13]         # 是否使用GPU
        self.num_heads = parameters[14]    # 注意力机制的头数
        self.dim_k = parameters[15]        # query and key的维数,必须是num_heads的整数倍,大小取决于词向量维度,大小适中即可
        self.dim_v = parameters[16]        # value的维数
        self.bert_path = './hfl-RoBERTa-wwm-ext, Chinese/'

# 通过一个向量直接设置所有参数,方便调参
ARGS = Parameters([150, 768, 1e-5, 6, 48, 6, 1e-5, 1e-8, 0.1, 12, 768, 1, True, True, 4, 768, 768])

模型定义,优化器设置,不同层学习率等:

	# 2. 模型定义
    # 先定义robert,BertModel用来获取词向量
    robert = BertModel.from_pretrained(ARGS.bert_path).to(device)
    # 再定义attention
    # 以robert作为Roberta_LSTM的参数,因为robert也要在__init__中参与反向传播
    robert_lstm_att = Roberta_LSTM_Att(robert).to(device)

    lstm_params = list(map(id, robert_lstm_att.lstm.parameters()))
    dense_params = list(map(id, robert_lstm_att.dense.parameters()))
    robert_params = list(map(id, robert_lstm_att.robert.parameters()))
    # 除lstm_params + dense_params + robert_params之外的基础参数,即自注意力的权重系数
    base_params = filter(lambda p: id(p) not in lstm_params + dense_params + robert_params,
           robert_lstm_att.parameters())
    # 3. 定义和梯度更新算法AdamW,优化器optimizer
    t_total = len(train_loader) * ARGS.epoches
     # 下列参数 不进行正则化(权重衰减)
    no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
    # 设置不同分层的学习率
    optimizer_grouped_parameters = [
        {
            "params": base_params,
            "weight_decay": ARGS.WeightDecay,
            "lr": ARGS.learning_rate*10  # 自注意力层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.robert.named_parameters() if not any(nd in n for nd in no_decay)],
            "weight_decay": ARGS.WeightDecay,
            "lr": ARGS.learning_rate  # RoBERTa层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.robert.named_parameters() if any(nd in n for nd in no_decay)],
            "weight_decay": 0.0,
            "lr": ARGS.learning_rate  # RoBERTa层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.lstm.named_parameters() if not any(nd in n for nd in no_decay)],
            "weight_decay": ARGS.WeightDecay,
            "lr": ARGS.learning_rate*10  # LSTM层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.lstm.named_parameters() if any(nd in n for nd in no_decay)],
            "weight_decay": 0.0,
            "lr": ARGS.learning_rate*10  # LSTM层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.dense.named_parameters() if not any(nd in n for nd in no_decay)],
            "weight_decay": ARGS.WeightDecay,
            "lr": ARGS.learning_rate*10  # 全连接层的学习率
        },
        {
            "params": [p for n, p in robert_lstm_att.dense.named_parameters() if any(nd in n for nd in no_decay)],
            "weight_decay": 0.0,
            "lr": ARGS.learning_rate*10
        },
    ]

    optimizer = AdamW(optimizer_grouped_parameters, lr=ARGS.learning_rate, correct_bias=False)
    # loss_func
    criterion = nn.CrossEntropyLoss()
    # scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lambda epoch: 0.99**epoch, last_epoch=-1)
    scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=t_total)
    # print(scheduler.get_lr())
    # for param_group in optimizer.param_groups:
    #     print(param_group['weight_decay'])

forward源码:

def forward(self, batch):
        # input_ids, attention_mask, token_type_ids
        # output[0]: [batch, Doc_Size, WordVec_size]  文本矩阵 接CNN,LSTM等
        # output[1]: [batch, WordVec_size]  文本向量 接MLP,SVM等
        input_ids, attention_mask, token_type_ids, labels = batch
        outputs = self.robert(input_ids=input_ids,
                              attention_mask=attention_mask,)
        # output[0]: [batch, 128, 768]
        out1, h_n = self.lstm(outputs[0])  # lstm层
        out1, weights = self.MultiAttention1(out1, h_n)
        input = torch.cat([out1, outputs[1]], dim=-1)
        outputs = self.dense(input)  # 全连接层
        return outputs

实际上,Roberta层和LSTM、MA、Dense层需要设置不同的学习率,Roberta层稍小,一般为1e-5左右,LSTM、MA、Dense层稍大一般为1e-4左右。此外,代码细节过多,不作过多展示,需要的可私信

6. 消融实验

消融实验(Ablation experiment)[5] 是为了证明组合模型的整体性,即证明模型的各个部分是否都在发挥作用。方法就是逐一把模型的各个组成部分从模型中去除,然后判断对模型的影响程度,如果有下降,则说明该组成部分是有用的不可分割的,反之就是可有可无的,如果都有下降则证明模型是统一的,每个部分都是不可或缺的,对于RBs-BG-MA模型,我们进行如下消融实验以证明模型的整体性(数据集是文本六分类):
在这里插入图片描述

我们将模型分解为:句嵌入、字嵌入+LSTM、MultiAttention三个模块,为此逐个移除其中一个模块来判断对模型的影响程度,可以看到:
移除句嵌入模块后(移除后为RB-BG -MA模型)模型准确率下降了1.24%左右, F_Macro下降了1.40%左右,移除多头自注意力模块后(移除后为RBs-BG模型)准确率下降了1.66%左右, F_Macro下降了2.11%左右,移除字嵌入模块后(移除后为RB-MLP模型)准确率下降了1.73%左右, F_Macro下降了1.63%左右,可见这三个模块对于模型都有重要作用;结果也显示,纯RoBERTa的性能已经很好了,单独使用某一两个模块反而会降低RoBERTa的性能,可见RBs-BG-MA模型的性能是各个模块共同作用的结果,缺一不可。

需要源码的同学可私信我哦^ ^


[1] Devlin J, Chang M-W, Lee K, et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv preprint, arXiv:1810.04805 [cs.CL] 2018.

[2] jacobdevlin-google. bert[EB/OL].https://github.com/google-research/bert. 11 Mar 2020.
[3] Cui Y, Che W, et al. Pre-Training with Whole Word Masking for Chinese BERT[J]. 2019.
[4] Liu Y, Ott M, Goyal N, et al. RoBERTa: A Robustly Optimized BERT Pretraining Approach. arXiv preprint, arXiv:1907.11692 [cs.CL] 2019.
[5] Ren S, He K, Girshick R, et al. Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks[J]. IEEE Transactions on Pattern Analysis & Machine Intelligence, 2017, 39(6):1137-1149.

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:31:25  更:2022-02-26 11:33:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 19:27:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码