目录

  • 1 相关信息
  • 2 引言
  • 3 提分技巧及实现
    • 3.1 数据增强
    • 3.2 投票融合
    • 3.2 伪标签
  • 4 加快训练
    • 4.1 混合精度训练
    • 4.2 加速训练的其他技巧
      • 4.2.1 有用到的加速策略
      • 4.2.2 未用到的加速策略

1 相关信息

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–1 赛后总结与分析

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–2 数据分析

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–3 TextCNN Fasttext 方案

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–4 机器学习LGB 方案

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–5 Bert 方案

  • 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–6 提分方案

2 引言

在该次英文文本分类任务中,数据增强在bert中带来了0.1的增益,伪标签在bert中带来了0.2+增益,在TextCNN等传统深度学习模型,带来了0.3+的增益。但是数据增强在传统深度学习模型并没有带来增益。投票融合也带来0.1+的增益。Stacking也带来了增益,但是本次任务中,没有完全正确实现,也带来了0.1+的增益,但是没有投票融合的效果好。

3 提分技巧及实现

3.1 数据增强

Github源码下载

参考队友写的文章NLP 英文文本数据增强

  • 第一种方式:英文文本随机删除、同义词替换、随机插入、随机交换
########################################################################
# 随机删除
# 以概率p删除语句中的词
########################################################################
#这里传入的sentences是一个英文句子
def random_deletion(sentences, p):words = sentences.split()if len(words) == 1:return wordsnew_words = []for word in words:r = random.uniform(0, 1)if r > p:new_words.append(word)if len(new_words) == 0:rand_int = random.randint(0, len(words)-1)return [words[rand_int]]return " ".join(new_words)
########################################################################
# 随机交换
# 随机交换几次
########################################################################
#这里传入的sentences是一个英文句子
def random_swap(sentences, n):words = sentences.split()new_words = words.copy()for _ in range(n):new_words = swap_word(new_words)return " ".join(new_words)def swap_word(new_words):random_idx_1 = random.randint(0, len(new_words)-1)random_idx_2 = random_idx_1counter = 0while random_idx_2 == random_idx_1:random_idx_2 = random.randint(0, len(new_words)-1)counter += 1if counter > 3:return new_wordsnew_words[random_idx_1], new_words[random_idx_2] = new_words[random_idx_2], new_words[random_idx_1] return new_words
########################################################################
# 同义词替换
# 替换一个语句中的n个单词为其同义词
########################################################################from nltk.corpus import stopwords#引入停用词,因为对停用词进行数据增强相当于没有增强
from nltk.corpus import wordnet as wn#引入同义词
import random
stop_words=stopwords.words('english')
for w in ['!',',','.','?','-s','-ly','</s>','s']:stop_words.add(w)words = sentences.split()
def synonym_replacement(sentences, n):words = sentences.split()new_words = words.copy()random_word_list = list(set([word for word in words if word not in stop_words]))     random.shuffle(random_word_list)num_replaced = 0  for random_word in random_word_list:          synonyms = get_synonyms(random_word)if len(synonyms) >= 1:synonym = random.choice(synonyms)   new_words = [synonym if word == random_word else word for word in new_words]   num_replaced += 1if num_replaced >= n: breaksentence = ' '.join(new_words)new_words = sentence.split(' ')return " ".join(new_words)
#获取同义词
def get_synonyms(word):nearbyWordSet=wn.synsets(word)return nearbyWordSet[0].lemma_names()
  • 第二种方式:互译,翻译成其他语言,再翻译回英文
#经过测试,这个翻译的包翻译的时间是最短的
from pygtrans import Translate
#这里传入的sentences是一个英文句子
def backTran(sentences):client = Translate()text1 = client.translate(sentences)text2 = client.translate(text1.translatedText, target='en')return text2.translatedText
  • 使用方法
train_trans = pd.read_csv('./data/train.csv',sep="\t")
test = pd.read_csv('./data/test.csv', sep='\t')aug_train = pd.DataFrame(columns=['title','abstract','categories'])
aug_test = pd.DataFrame(columns=['title','abstract'])# 互译处理,其他处理方式也一样
aug_train["title"] = train["title"].progress_apply(lambda x: backTran(x))
aug_train["abstract"] = train["abstract"].progress_apply(lambda x: backTran(x))
aug_test["title"] = test["title"].progress_apply(lambda x: backTran(x))
aug_test["abstract"] = test["abstract"].progress_apply(lambda x: backTran(x))
  • 第三种方式:对抗训练

该方案只在传统的深度学习模型方案中使用,暂时不知如何在bert使用。有两种方法,分别是FGM和PGD。在本次任务中,PGD效果较为好一些,FGM通过实验没有带来任何增益。且加入对抗训练后,模型收敛变慢,需要加深训练深度,加大epoch.

原理参考队友文章NLP 英文文本数据增强

本次任务使用,参考Github源码

3.2 投票融合

Github源码下载

(1)原理解析

我使用该方式的时候,已经用TextCNN、Fasttext、bert_base、bert_large、roberta_large分别得到了0.79+的结果。通过将提交结果文件放在同一个文件夹后。提交文件样式如下。

对categories列进行唯一类别特征编码,并命名为label。

同理将多个提交结果都编码并存放到同一个csv文件中,如下所示。投票的原理就是每一行进行投票,多数者即为该行的label。如下第一行0是5列中全票,9995行,12的票数多余29的票数,该行的label为12。投票融合的条件是模型之间差异越大,融合效果越好。

(2)实现

import pandas as pd
import numpy as np
import os
from pprint import pprint
DATA_DIR = 'voting_data/'#'./ensemble_submit/8298/'
files = os.listdir(DATA_DIR)
files = [i for i in files if i[0]!='.']
print(len(files))
pprint(files)
# 读取原始文件进行编码
train = pd.read_csv('./data/train.csv', sep='\t')
#将标签进行转换
label_id2cate = dict(enumerate(train.categories.unique()))
label_cate2id = {value: key for key, value in label_id2cate.items()}# 读取提交文件,也可以自定义空的csv文件,该文件将会存储多个结果
sub_exp_df = pd.read_csv('./data/sample_submit.csv')
df_merged = sub_exp_df.drop(['categories'], axis=1)
for file in files:tmp_df = pd.read_csv(DATA_DIR + file)tmp_df['label'] = tmp_df['categories'].map(label_cate2id)tmp_df = tmp_df.drop(['categories'], axis=1)df_merged = df_merged.merge(tmp_df, how='left', on='paperid')
df_merged.head()# 进行计票
def work(pres):count = [0]*39for i in pres:count[i] += 1out = count.index(max(count))return out
tmp_arr = np.array(df_merged.iloc[:,1:])
# 转为list
label_voted = [work(line) for line in tmp_arr]
# 反编码,生成提交文件
sub_exp_df['categories'] = label_voted
sub_exp_df['categories'] = sub_exp_df['categories'].map(label_id2cate)
# 存储提交文件
savepatch =  "./ensemble_submit/8279_voting.csv"
sub_exp_df = sub_exp_df.drop(['label'], axis=1)
sub_exp_df.to_csv(savepatch, index=False)

3.2 伪标签

参考伪标签(Pseudo-Labelling)——锋利的匕首

(1)原理解析

在文本分类任务中,在比赛最初是没法使用的,因为要获得准确的伪标签,一般选择多模型结果投票的方式获得高质量的标签。是在已经从多个方案中得到了较高的预测结果后,通过投票的方式得到高质量标签。具体来说,我使用该方式的时候,已经用TextCNN、Fasttext、bert_base、bert_large、roberta_large分别得到了0.79+的结果。利用以上的投票原理,选择多个模型都投票的数据作为该行数据的label。然后将该行数据加入到训练集中,重新训练模型。

注意:图中的第一个Model和第二个模型不是同一个模型,第二个Model是加入伪标签后重新训练出来的模型

(2)实现
Github源码下载

  • 模型结果合并
import pandas as pd
import numpy as np
import os
from pprint import pprintDATA_DIR = './submit/'
files = os.listdir(DATA_DIR)
files = [i for i in files if i[0]!='.']
print(len(files))
pprint(files)train = pd.read_csv('./data/train.csv', sep='\t')
#将标签进行转换
label_id2cate = dict(enumerate(train.categories.unique()))
label_cate2id = {value: key for key, value in label_id2cate.items()}sub_exp_df = pd.read_csv('./data/sample_submit.csv')
df_merged = sub_exp_df.drop(['categories'], axis=1)
for file in files:tmp_df = pd.read_csv(DATA_DIR + file)tmp_df['label'] = tmp_df['categories'].map(label_cate2id)tmp_df = tmp_df.drop(['categories'], axis=1)df_merged = df_merged.merge(tmp_df, how='left', on='paperid')
df_merged.head()
  • 构造高质量伪造标签
#计票。
def work_high(pres):count = [0]*39for i in pres:count[i] += 1p = 7 # 可根据融合模型的数量选择该数的大小。当共有8个模型融合,>7表示必须有8个模型都投票的才被选择加入伪标签数据if max(count) >p:out = count.index(max(count))else:out = -1return outtmp_arr = np.array(df_merged.iloc[:,1:])
label_voted = [work_high(line) for line in tmp_arr]
# 计算有多少数据不被加入伪标签数据
print(label_voted.count(-1))
# 读取测试集
test_data = train = pd.read_csv('./data/test.csv', sep='\t')
test_data['categories'] = label_voted
test_data = test_data.drop(test_data[test_data['categories']==-1].index)
# 计算有多少伪标签数据
len(test_data)
# 反编码映射
test_data['categories'] = test_data['categories'].map(label_id2cate)
# 合并训练集
train = pd.read_csv('./data/train.csv')
# 存储新的训练集
pseudo_label_train = pd.concat([train,test_data])
model_name = "./data/pseudo_train_data"
pseudo_label_train.to_csv('{}.csv'.format(model_name),sep="\t", index=False)

4 加快训练

4.1 混合精度训练

(1)引言

参考资料pytorch混合精度训练

使用fp16存储网络的权重值、激活值和梯度值进行网络训练,好处是:

减少显存占用:上面的图已经很明显的可以看出,fp16的存储空间为fp32的一半,如果使用fp16进行训练,那么可以减少一半的显存占用,因此也就可以使用更大的batchsize进行大模型的训练;

加快训练和推理速度:fp16可以提高模型的训练和推理的速度。

(2)实现

有两种方法实现,nvidia版本和pytorch版本。这里采用了第一种,其他的方案参考pytorch混合精度训练。实现只需要在pytroch代码上,修改三行代码即可

from apex import amp
model, optimizer = amp.initialize(model, optimizer, opt_level="O1") # 这里是“欧一”,不是“零一”
with amp.scale_loss(loss, optimizer) as scaled_loss:scaled_loss.backward()

opt_level参数:

O0:纯FP32训练,可以作为accuracy的baseline;
O1:混合精度训练(推荐使用),根据黑白名单自动决定使用FP16(GEMM, 卷积)还是FP32(Softmax)进行计算;
O2:“几乎FP16”混合精度训练,不存在黑白名单,除了Batch norm,几乎都是用FP16计算;
O3:纯FP16训练,很不稳定,但是可以作为speed的baseline。

(3)具体实现

#训练模型
import torch.nn as nn
from apex import amp
def train_start(EPOCHS,MAX_LEN,BATCH_SIZE,train, test_data_loader, label_id2cate):#模型定义model = PaperClassifier()model = model.to(device)k_fold = 5predict_all = np.zeros([10000,39])#存储测试集的 预测结果for n in range(k_fold):train_data_loader, val_data_loader = load_data_kfold(train, BATCH_SIZE,MAX_LEN, k_fold, n)#使用差分学习率parameters = get_parameters(model, 2e-5, 0.95, 1e-4)optimizer = AdamW(parameters)# fp16混合精度训练        model, optimizer = amp.initialize(model, optimizer, opt_level="O1")total_steps = len(train_data_loader) * EPOCHSscheduler = get_linear_schedule_with_warmup(optimizer,num_warmup_steps=0,num_training_steps=total_steps)loss_fn = nn.CrossEntropyLoss().to(device)best_accuracy = 0for epoch in range(EPOCHS):print(f'Epoch {epoch + 1}/{EPOCHS}')print('-' * 10)train_acc, train_loss = train_epoch(model,train_data_loader,loss_fn,optimizer,device,scheduler)print(f'Train loss {train_loss} accuracy {train_acc}')val_acc, val_loss= eval_model(model, val_data_loader, loss_fn, device)print(f'Val loss {val_loss} accuracy {val_acc}')if val_acc > best_accuracy:torch.save(model.state_dict(), 'model/best_model_state_large_aug.bin')best_accuracy = val_acc#进行预测y_pred = model_predictions(model, test_data_loader, device)predict_all += np.array(y_pred)test_x.extend(y_pred)
def train_epoch(model, data_loader, loss_fn, optimizer, device, scheduler):print("start training!")model = model.train()losses = []pred_ls = []label_ls = []for d in tqdm(data_loader):input_ids = d["input_ids"].to(device)attention_mask = d["attention_mask"].to(device)targets = d["labels"].to(device)outputs = model(input_ids=input_ids,attention_mask=attention_mask)_, preds = torch.max(outputs, dim=1)loss = loss_fn(outputs, targets)losses.append(loss.item())# fp16混合精度训练with amp.scale_loss(loss, optimizer) as scaled_loss:scaled_loss.backward()
#         loss.backward()nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)optimizer.step()scheduler.step()optimizer.zero_grad()label_ls.extend(d["labels"])pred_ls.extend(preds.tolist())correct_predictions = accuracy_score(label_ls, pred_ls)return correct_predictions, np.mean(losses)

4.2 加速训练的其他技巧

详细内容参考让PyTorch训练速度更快,你需要掌握这17种方法

4.2.1 有用到的加速策略

  • 使用schedule学习率 、AadmW优化器、混合精度训练代码
#训练模型
import torch.nn as nn
from apex import amp
def train_start(EPOCHS,MAX_LEN,BATCH_SIZE,train, test_data_loader, label_id2cate):#模型定义model = PaperClassifier()model = model.to(device)k_fold = 5predict_all = np.zeros([10000,39])#存储测试集的 预测结果for n in range(k_fold):train_data_loader, val_data_loader = load_data_kfold(train, BATCH_SIZE,MAX_LEN, k_fold, n)#使用差分学习率parameters = get_parameters(model, 2e-5, 0.95, 1e-4)# AdamW优化器optimizer = AdamW(parameters)# fp16混合精度训练        model, optimizer = amp.initialize(model, optimizer, opt_level="O1")total_steps = len(train_data_loader) * EPOCHS# schedule学习率 scheduler = get_linear_schedule_with_warmup(optimizer,num_warmup_steps=0,num_training_steps=total_steps)loss_fn = nn.CrossEntropyLoss().to(device)best_accuracy = 0for epoch in range(EPOCHS):print(f'Epoch {epoch + 1}/{EPOCHS}')print('-' * 10)train_acc, train_loss = train_epoch(model,train_data_loader,loss_fn,optimizer,device,scheduler)print(f'Train loss {train_loss} accuracy {train_acc}')val_acc, val_loss= eval_model(model, val_data_loader, loss_fn, device)print(f'Val loss {val_loss} accuracy {val_acc}')if val_acc > best_accuracy:torch.save(model.state_dict(), 'model/best_model_state_large_aug.bin')best_accuracy = val_acc#进行预测y_pred = model_predictions(model, test_data_loader, device)predict_all += np.array(y_pred)test_x.extend(y_pred)
  • 在 DataLoader 中使用多个 worker 和页锁定内存

当使用 torch.utils.data.DataLoader 时,设置 num_workers > 0,而不是默认值 0,同时设置 pin_memory=True,而不是默认值 False。人们选择 worker 数量的经验法则是将其设置为可用 GPU 数量的四倍,大于或小于这个数都会降低训练速度。

    return DataLoader(ds,batch_size=batch_size,sampler = sampler,num_workers=4,  # 多线程pin_memory=True  # 页锁定内存)
  • 把 batch 调到最大

把 batch 调到最大是一个颇有争议的观点。一般来说,如果在 GPU 内存允许的范围内将 batch 调到最大,你的训练速度会更快。但是,你也必须调整其他超参数,比如学习率。一个比较好用的经验是,batch 大小加倍时,学习率也要加倍。

  • 使用自动混合精度(AMP)

与单精度 (FP32) 相比,某些运算在半精度 (FP16) 下运行更快,而不会损失准确率。AMP 会自动决定应该以哪种精度执行哪种运算。这样既可以加快训练速度,又可以减少内存占用。

  • 使用AdamW优化器

Adam,在 PyTorch 中以 torch.optim.AdamW 实现。AdamW 似乎在误差和训练时间上都一直优于 Adam。

  • 小心 CPU 和 GPU 之间频繁的数据传输

当频繁地使用 tensor.cpu() 将张量从 GPU 转到 CPU(或使用 tensor.cuda() 将张量从 CPU 转到 GPU)时,代价是非常昂贵的。item() 和 .numpy() 也是一样可以使用. detach() 代替。

  • 在验证期间关闭梯度计算

在验证期间关闭梯度计算,设置:torch.no_grad() 。

4.2.2 未用到的加速策略

  • 使用梯度积累

增加 batch 大小的另一种方法是在调用 optimizer.step() 之前在多个. backward() 传递中累积梯度。

  • 使用分布式数据并行进行多 GPU 训练

加速分布式训练可能有很多方法,但是简单的方法是使用 torch.nn.DistributedDataParallel 而不是 torch.nn.DataParallel。这样一来,每个 GPU 将由一个专用的 CPU 核心驱动,避免了 DataParallel 的 GIL 问题。

  • 设置梯度为 None 而不是 0

梯度设置为. zero_grad(set_to_none=True) 而不是 .zero_grad()。这样做可以让内存分配器处理梯度,而不是将它们设置为 0。正如文档中所说,将梯度设置为 None 会产生适度的加速,但不要期待奇迹出现。注意,这样做也有缺点,详细信息请查看文档。

  • 使用. as_tensor() 而不是. tensor()

torch.tensor() 总是会复制数据。如果你要转换一个 numpy 数组,使用 torch.as_tensor() 或 torch.from_numpy() 来避免复制数据。

  • 使用梯度裁剪

关于避免 RNN 中的梯度爆炸的问题,已经有一些实验和理论证实,梯度裁剪(gradient = min(gradient, threshold))可以加速收敛。HuggingFace 的 Transformer 实现就是一个非常清晰的例子,说明了如何使用梯度裁剪。本文中提到的其他一些方法,如 AMP 也可以用。在 PyTorch 中可以使用 torch.nn.utils.clip_grad_norm_来实现。

  • 在 BatchNorm 之前关闭 bias

在开始 BatchNormalization 层之前关闭 bias 层。对于一个 2-D 卷积层,可以将 bias 关键字设置为 False:torch.nn.Conv2d(…, bias=False, …)。

  • 使用输入和 batch 归一化

要再三检查一下输入是否归一化?是否使用了 batch 归一化?

【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案--6 提分方案相关推荐

  1. 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案--1 赛后总结与分析

    目录 1 相关信息 2 总结 2.1 TextCNN.Fasttext等DL方案 2.2 机器学习LGB方案 2.3 Bert方案 3 继续提分点 1 相关信息 [NLP]讯飞英文学术论文分类挑战赛T ...

  2. 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–5 Bert 方案

    目录 1 相关信息 2 引言 3 实现 3.1 数据预处理 3.2 Bert 4 提分点技巧讲解 5 未来展望 1 相关信息 [NLP]讯飞英文学术论文分类挑战赛Top10开源多方案–1 赛后总结与分 ...

  3. 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–4 机器学习LGB 方案

    1 相关信息 [NLP]讯飞英文学术论文分类挑战赛Top10开源多方案–1 赛后总结与分析 [NLP]讯飞英文学术论文分类挑战赛Top10开源多方案–2 数据分析 [NLP]讯飞英文学术论文分类挑战赛 ...

  4. 【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案--2 数据分析

    目录 相关信息 1 赛题 2 数据分析 2.1 加载数据 2.2 查看缺失值 2.3 标签分布 2.4 文本长度 2.5 标题摘要合并后字符长度 3 总结 相关信息 [NLP]讯飞英文学术论文分类挑战 ...

  5. 有哪些可助力英文学术论文写作的在线网站、工具或软件?

    说到英文学术论文写作,相信小伙伴们都有同样的困惑:不是难以下手就是好不容易憋出了万字长文,回过头来一看语法漏洞百出,真的很让人头疼!即使是科研大神也会有这样的烦恼.读了万卷文献,依旧Chinglish ...

  6. 英文学术论文写作——模式识别方向(笔记)

    文章目录 文章结构 英文写作tips Latex小技巧 英文学术论文写作经验几乎为0,在老师和师兄们的帮助下,学习到了如何撰写文章.仅限于模式识别方向的. 文章结构 文章除去abstract,ackn ...

  7. 讯飞-糖尿病遗传风险检测挑战赛

    讯飞-糖尿病遗传风险检测挑战赛 前言 相关库 一.比赛报名 1.1 赛事任务 1.2 读取数据 训练数据集 测试数据集 1.3 数据集基本信息 数据集维度 数据表基本信息 二.比赛数据分析 2.1 缺 ...

  8. 给出广义随机petri网在可靠性方面应用的英文学术论文

    在可靠性方面应用广义随机Petri网的英文学术论文有很多.您可以在Google Scholar或者IEEE Xplore等学术搜索引擎上搜索关于"generalized Stochastic ...

  9. 英文学术论文review的回复

    因为之前投稿的文章有了review,所以记录一下回复review的细节. 英文学术论文投稿后,期刊编辑人员会对文章进行审核(论文格式,论文领域等),再将文章送至领域专家进行专业性审核.审核结束后,期刊 ...

最新文章

  1. python学习教程,猜数字游戏开发
  2. 不断电系统的容量如何计算?
  3. Visual Studio Code 1.8版本添加了Hot Exit、Zen Mode及更多调试选项
  4. Install OpenCL on Debian, Ubuntu and Mint orderly
  5. 计算机垃圾回收的过程,计算机体系 – 垃圾收集器
  6. 2014年工作中遇到的20个问题:161-180
  7. python求解next数组实现KMP算法
  8. 国内第三个双机场城市新机场试飞,6月正式投用
  9. Sakai系出名门 未来架构将更激进
  10. 基于java jsp企业人事管理系统mysql
  11. H.264码流分析器,雷霄骅版本人修改版
  12. 交通信息工程 实验四:交通仿真实验(一)
  13. Linux设置 鼠标滚轮方向,如何在Ubuntu中反转鼠标滚动方向(又名自然滚动) | MOS86...
  14. 概率论复习笔记一——伯努利实验及相关的概率分布
  15. html5 3d背景墙,3D背景墙的价格怎样?3D背景墙的介绍?
  16. 什么是APS高级计划排程(高级计划排产)可视化甘特图,有哪些应用效果?
  17. 电路设计_光耦的主要参数
  18. Tushare的使用感受
  19. 台式机计算机图标不见了,win7系统电脑右下角无线网络连接图标不见了怎么办-台式电脑怎么设置无线网络...
  20. 00后女生已博士毕业,拟入职南大

热门文章

  1. Qt for Android(14) —— Android Q 适配之无法接受广播
  2. 青海省果洛藏族自治州谷歌高清卫星地图下载(百度网盘离线包下载)
  3. 基于HTTPS长连接的ESP32+VS1053网络电台收音机Arduino代码
  4. PFC5.0代码快速转换成PFC6.0代码
  5. LED显示行业之低灰过度不均匀
  6. 运用PS将日光照片转为美丽的日落景象
  7. 电科矩阵理论CH2向量与矩阵的范数--证明题
  8. 2022年全球及中国多功能道路检测系统行业十四五应用动态与发展建设规划报告
  9. R语言入门基础操作 啰嗦同桌级教程(一)
  10. 快递查询软件,一键查询物流,根据最后更新物流筛选单号