您当前的位置: 首页 > 

Better Bench

暂无认证

  • 1浏览

    0关注

    695博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

2021 第五届“达观杯” 基于大规模预训练模型的风险事件标签识别】3 Bert和Nezha方案

Better Bench 发布时间:2021-09-24 16:20:28 ,浏览量:1

目录
  • 相关链接
  • 1 引言
  • 2 NEZHA方案
    • 2.1 预训练
    • 2.2 微调
  • 3 Bert 方案
    • 3.1 预训练
    • 3.2 微调
  • 3 模型融合和TTA测试集数据增强
  • 4 总结和反思
  • 5 参考资料

相关链接

【2021 第五届“达观杯” 基于大规模预训练模型的风险事件标签识别】1 初赛Rank12的总结与分析 【2021 第五届“达观杯” 基于大规模预训练模型的风险事件标签识别】2 DPCNN、HAN、RCNN等传统深度学习方案 【2021 第五届“达观杯” 基于大规模预训练模型的风险事件标签识别】3 Bert和Nezha方案

1 引言

在这里插入图片描述

2 NEZHA方案

(1)代码结构

完整源码下载Github

├── Bert_pytorch # Bert 方案
│   ├── bert-base-chinese # 初始权重,下载地址https://huggingface.co/bert-base-chinese#
│   ├── bert_finetuning # Bert微调
│   │   ├── Config.py # Bert配置文件
│   │   ├── ensemble_10fold.py # 10折checkpoint融合
│   │   ├── ensemble_single.py #每种模型不划分验证集只生成的一个模型,用这些模型进行checkpoint融合
│   │   ├── generate_pseudo_label.py # 利用做高分模型 给无标注数据做伪标签
│   │   ├── main_bert_10fold.py # 划分10折的Bert,这种会存储10个模型,每一个fold一个模型
│   │   ├── main_bert_all.py # 不划分验证集的Bert,这种只会存储一个模型
│   │   ├── model.py # 17种魔改Bert,和其他网络的具体实现部分
│   │   ├── models 
│   │   ├── NEZHA # 网络结构实现文件,来源于官网
│   │   │   ├── configuration_nezha.py
│   │   │   └── modeling_nezha.py
│   │   ├── predict.py # 用模型模型进行预测测试集
│   │   ├── predict_tta.py # 用模型进行预测测试集,并使用TTA 测试集增强
│   │   ├── stacking.py # Stacking集成方法
│   │   └── utils.py # 工具函数
│   ├── bert_model_1000 # 存储预训练模型,下载地址https://drive.google.com/file/d/1rpWe5ec_buORvu8-ezvvAk9jrUZkOsIr/view?usp=sharing
│   ├── Data_analysis.ipynb # 数据分析
│   ├── Generate_TTA.ipynb # 生成TTA测试集增强的文件
│   └── pretrain # Bert预训练
│       ├── bert_model 
│       │   ├── vocab_100w.txt # 100W未标注数据语料的词典,有18544个词
│       │   ├── vocab_3462.txt # 整个训练集和测试集的词典,不包括未标注数据
│       │   └── vocab.txt
│       ├── NLP_Utils.py
│       ├── train_bert.py # Bert预训练主函数
│       └── transformers1.zip # transformes较高的版本
├── data
│   ├── datagrand_2021_test.csv # 测试集
│   └── datagrand_2021_train.csv # 训练集
├── Nezha_pytorch #NEZHA预训练方案
│   ├── finetuning #  Nezha微调
│   │   ├── Config.py 
│   │   ├── model.py #模型实现文件
│   │   ├── models
│   │   ├── NEZHA
│   │   │   ├── configuration_nezha.py
│   │   │   └── modeling_nezha.py
│   │   ├── NEZHA_main.py #微调主函数
│   │   ├── predict.py # 10折模型预测
│   │   ├── submit
│   │   │   └── submit_bert_5epoch-10fold-first.csv
│   │   └── utils.py
│   ├── nezha-cn-base #nezha-base初始权重,下载地址https://github.com/lonePatient/NeZha_Chinese_PyTorch
│   ├── nezha_model #存放预训练生成的模型
│   ├── NEZHA_models
│   ├── nezha_output #预训练的checkpoint
│   ├── pretrain #nezha预训练
│   │   ├── __init__.py
│   │   ├── NEZHA
│   │   │   ├── configuration_nezha.py
│   │   │   ├── modeling_nezha.py
│   │   ├── nezha_model
│   │   │   └── vocab.txt # 预训练时,所需要的训练集的词典
│   │   ├── NLP_Utils.py
│   │   ├── train_nezha.py #预训练NEZHA的主函数
│   │   └── transformers1.zip # 更高版本的transformers
│   └── submit

2.1 预训练

nezha-base-chinese 初始权重下载

nezha-large效果并不如nezha-base,区别只在于初始加载的权重不同以及预训练的网络层数不同。其他NEZHA-base和NEZHA-large一样。以下只针对NEZHA-base详解。

(1)重要方法

  • Mask策略 动态mask:可以每次迭代都随机生成新的mask文本,增强模型泛化能力 N-gram Mask:以掩码概率mask_p的概率选中token,为增加训练难度,选中部分以70%、20%、10%的概率进行1-gram、2-gram、3-gram片段的mask(选中token使用[MASK]、随机词、自身替换的概率和原版Bert一致) 长度自适应:考虑到对短文本进行过较长gram的mask对语义有较大破坏,长度小于7的文本不进行3-gram mask,小于4的文本不进行2-gram mask(这一点在是参考原作者代码的,并没有进行修改,虽然已经在代码中已经实现,但是在该赛题中,并没有长度低于7的句子。所以并没有起任何作用,也没有任何影响) 防止小概率的连续Mask:已经mask了的文本片段,强制跳过下一个token的mask,防止一长串连续的mask
  • 掩码概率: mask_p,原本是0.15,我们通过增加了掩码概率为0.5增大预训练的难度,能够一定程度防止微调过拟合。
  • 截断长度: 根据数据分析,发现句子的平均词数是54左右,随机选择了100的截断长度,这一点并没有进行调参
  • 截断方式: 首尾截断,还有首部截断和尾部截断并没有进行对比,一直使用的首尾截断。实现过程就是计算大于截断长度的数,首部截断一半,尾部截断一半。
  • Epoch: 设置为480时,NEZHA单模效果最佳。
  • 只训练word_embedding和position_emebedding 加快训练。在打印查看model的position_embedding的时候,并没有找到,实现时就只训练了word_embedding。能缩短两倍的训练时间
model = NeZhaForMaskedLM.from_pretrained("./nezha-cn-base/")
model.resize_token_embeddings(len(tokenizer))
# 只训练word_embedding。能缩短两倍的训练时间
for name, p in model.named_parameters():
    if name != 'bert.embeddings.word_embeddings.weight':
        p.requires_grad = False
  • Warmup学习率和权重衰退: 采用transformers的有预训练函数,参数设置如下
from transformers import Trainer, TrainingArguments,BertTokenizer
training_args = TrainingArguments(
    output_dir='Nezha_pytorch/pretrain/nezha_output',# 此处必须是绝对路径
    overwrite_output_dir=True,
    num_train_epochs=1000,
    per_device_train_batch_size=32,
    save_steps=10000,#每10000step就 save一次
    save_total_limit=3,
    logging_steps=len(dl),#每个epoch log一次
    seed=2021,
    learning_rate=5e-5,
    weight_decay=0.01,#权重衰退
    warmup_steps=int(450000*150/batch_size*0.03)# warmup
)
  • 分块shuffle: 原源代码作者实现,我们并未修改这块 分块shuffle将长度差不多的样本组成batch快,块间shuffle,减少padding部分运算量,预训练耗时减少了约40%
#sortBsNum:原序列按多少个bs块为单位排序,可用来增强随机性
#比如如果每次打乱后都全体一起排序,那每次都是一样的
def blockShuffle(data:list,bs:int,sortBsNum,key):
    random.shuffle(data)#先打乱
    tail=len(data)%bs#计算碎片长度
    tail=[] if tail==0 else data[-tail:]
    data=data[:len(data)-len(tail)]
    assert len(data)%bs==0#剩下的一定能被bs整除
    sortBsNum=len(data)//bs if sortBsNum is None else sortBsNum#为None就是整体排序
    data=splitList(data,sortBsNum*bs)
    data=[sorted(i,key=key,reverse=True) for i in data]#每个大块进行降排序
    data=unionList(data)
    data=splitList(data,bs)#最后,按bs分块
    random.shuffle(data)#块间打乱
    data=unionList(data)+tail
    return data
from torch.utils.data.dataloader import _SingleProcessDataLoaderIter,_MultiProcessingDataLoaderIter
#每轮迭代重新分块shuffle数据的DataLoader
class blockShuffleDataLoader(DataLoader):
    def __init__(self, dataset: Dataset,sortBsNum,key,**kwargs):
        assert isinstance(dataset.data,list)#需要有list类型的data属性
        super().__init__(dataset,**kwargs)#父类的参数传过去
        self.sortBsNum=sortBsNum
        self.key=key

    def __iter__(self):
        #分块shuffle
        self.dataset.data=blockShuffle(self.dataset.data,self.batch_size,self.sortBsNum,self.key)
        if self.num_workers == 0:
            return _SingleProcessDataLoaderIter(self)
        else:
            return _MultiProcessingDataLoaderIter(self)

(2)掩码策略实现

class MLM_Data(Dataset):
    def __init__(self,textLs:list,maxLen:int,tk:BertTokenizer):
        super().__init__()
        self.data=textLs
        self.maxLen=maxLen
        self.tk=tk
        self.spNum=len(tk.all_special_tokens)
        self.tkNum=tk.vocab_size

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

    def random_mask(self,text_ids):
        input_ids, output_ids = [], []
        rands = np.random.random(len(text_ids))
        idx=0
        mask_p = 0.5 # 原始是0.15,加大mask_p就会加大预训练难度
        while idx            
关注
打赏
1665674626
查看更多评论
1.5194s