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

本文涉及的产品
NLP自然语言处理_高级版,每接口累计50万次
NLP自然语言处理_基础版,每接口每天50万次
NLP 自学习平台,3个模型定制额度 1个月
简介: 在讯飞英文学术论文分类挑战赛中使用BERT模型进行文本分类的方法,包括数据预处理、模型微调技巧、长文本处理策略以及通过不同模型和数据增强技术提高准确率的过程。

1 相关信息

2 引言

(1)Baseline的选择

我选择的是Datawhale闫强“致Great Bert方案”进行的迭代优化,其中是使用加载预训练模型,进行微调。用到了差分学习率和AdamW 优化器。这些点这对于Bert的微调有巨大的作用。还有更多Bert微调技巧来自于论文《How to Fine-Tune BERT for Text Classification?》论文中提到的微调策略有

  1. 处理长文本 我们知道BERT 的最大序列长度为 512,BERT 应用于文本分类的第一个问题是如何处理长度大于 512 的文本。本文尝试了以下方式处理长文章。
  • 截断法 文章的关键信息位于开头和结尾。 我们可以使用三种不同的截断文本方法来执行 BERT 微调。
    • 只截断头部
    • 只截断尾部
    • 头尾截断
  • 层级法 输入的文本首先被分成k = L/510个片段,喂入 BERT 以获得 k 个文本片段的表示向量。 每个分数的表示是最后一层的 [CLS] 标记的隐藏状态,然后我们使用均值池化、最大池化和自注意力来组合所有分数的表示。
  1. 不同层的特征 BERT 的每一层都捕获输入文本的不同特征。 改论文研究了来自不同层的特征的有效性,可以看到:最后一层表征效果最好;最后4层进行max-pooling效果最好
  2. 灾难性遗忘 Catastrophic forgetting (灾难性遗忘)通常是迁移学习中的常见诟病,这意味着在学习新知识的过程中预先训练的知识会被遗忘。 因此,本文还研究了 BERT 是否存在灾难性遗忘问题。 我们用不同的学习率对 BERT 进行了微调,发现需要较低的学习率,例如 2e-5,才能使 BERT 克服灾难性遗忘问题。 在 4e-4 的较大学习率下,训练集无法收敛。所以当预训练模型失效不能够收敛的时候多检查下超参数是否设置有问题。
  3. Layer-wise Decreasing Layer Rate 逐层降低学习率 下表 显示了不同基础学习率和衰减因子在 IMDb 数据集上的性能。 我们发现为下层分配较低的学习率对微调 BERT 是有效的,比较合适的设置是 ξ=0.95 和 lr=2.0e-5

(2)方案的改进

1.png

通过以下方案,将单模型Bert从0.79逐步提升到0.8246,最终通过模型融合得到最高得分0.8304

  • 数据预处理

Bert的数据预处理,只做了所有字母转换为小写和词性还原,词性还原比如working还原为work,采用nltk.stem.WordNetLemmatizer.lemmatize(word)工具包。其他的预处理,则是适得其反,过多的预处理,会降低模型精度,因为通过查阅资料,Bert预训练的模型,在训练当初就是使用原生数据训练的,为了达到更好的fine tune微调效果,在使用预训练模型的数据尽量与训练模型时候的数据形式保持统一。

  • K折交叉验证

选择的是5折2epoch训练,3090的显卡,5W的数据,batch—size为10,bert_base都得训练7个小时左右,bert_large 10个小时左右,Roberta_large 14个小时左右。更别说后期通过数据增强和加入伪标签总训练数据近16W的时候,训练时间加倍。一训练就是一整天。所以选择5折2epoch是迫不得已,通过观察发现,训练完模型都没有过拟合,完全可以加大训练深度,设4epoch模型精度会更高。

  • Bert+Bi-LSTM
    • bert_base
    • Bert_large
    • Roberta_large

Bert_ base模型最小,有444M,8G的显存的勉强能跑起来,模型精度在bert中最低,历史最高得分0.8162。bert_large模型规模中等,1G以上的,11G的显存都无法跑起来。模型精度中等,历史最高得分0.8171。Roberta_large规模比Bert_large还要大,需要的显存更大。模型精度最高历史最高得分0.8246,通过其他Baseline的启发,在bert后加一层Bi-LSTM能提高千分点。也有方案加一层Attention的,但是我们没有取得成功。

  • 数据增强:具体实现见英文文本数据增强
    • 第一种:随机删除、随机替换、同义词替换
    • 第二种:互译(回译):翻译成其他语言再翻译会英文

第一种方式,将原始训练集5W的数据全部增强,第二种方式也将原始5W数据进行了增强。在传统 深度学习模型TextCNN、FastText方案中,这两种增强并没有带来增益,但是在Bert中,两种数据增强方式,都带来了增益。

这两种方式我们尝试了三种组合。带来的增益幅度也是不一样的

(1)原始数据5W+第一种数据增强5W:提升0.05+

(2)原始数据5W+第二种数据增强5W:提升0.05+

(3)原始数据5W+第一种数据增强5W+第二种数据增强5W:提升0.1+

将测试集放入各个模型中预测,得到多个模型的预测结果,选择各个模型都投票的数据,给测试集做伪标签,即获得Top K 的高质量标签,再将这些伪标签和对应的文本数据加入训练集中重新从零训练模型。直接将线上提升0.1+个点,反复重复以上操作,还能得到更高准确率,直到不再提升。该方式在传统DL 方案中能提升0.3个点,绝对的大杀器。

2.jpeg

注意图片中的第二个Model和第一个Model不是同一个,第二个Model,是重新训练出来的

  • 调参
    • Batch_size
    • 学习率
    • 文本截断长度MAX_LEN

注意本次任务中通过对比实验,Batch_size选择的是10 ,对应模型初始学习率2e-5,全连接层的初始学习率是1e-4。如果显存够大,可以加倍,但是学习率也需要加倍。实验过程中,batch-size为4时,模型精度更高,但是太费时,我们只能舍弃精度,加快训练。

文本的最大长度,通过实验对比,选择过100、200、300,发现300的时候模型精度最高

混合精度训练是在尽可能减少精度损失的情况下利用半精度浮点数加速训练。它使用FP16即半精度浮点数存储权重和梯度。在减少占用内存的同时起到了加速训练的效果。

Stacking 是将验证集和测试集同时放入多个模型中预测,各自得到预测矩阵,再将每个模型的预测矩阵水平合并得到新的矩阵,则将验证集合并的预测矩阵作为基模型的训练集,去训练基模型,基模型此任务中选择的LogisticRegression 。用测试集合并的预测矩阵放入基模型预测作为最终的预测值。

Voting 在分类任务中是常用的融合技巧,以下一张图,一目了然,每一列表示一个模型的预测结果,每行去计数,每行出现次数最高的数即是该行的最终值。

3.png

但是我们的最终融合方式并不是简单的等权融合,而是变形的加权融合。是把个模型的历史的高分结果,进行了按比例融合,选择了3个bert_base、4个roberta_large、3个bert_large、一个robert_base、1个机器学习LGB、1个textcnn、1个fasttext。如下图所示

4.png

3 实现

Github源码下载

3.1 数据预处理

(1)加载包

import pandas as pd
from nltk.stem import WordNetLemmatizer
from tqdm import tqdm
import re
import nltk
tqdm.pandas()
import re
import os
import pickle

train = pd.read_csv('./data/train_data.csv',sep="\t")
test = pd.read_csv('./data/test.csv', sep='\t')
def preprocess_text(document):
    stemmer = WordNetLemmatizer()
    text = str(document)
    # 替换换行符
    text = text.replace("\n", ' ')
    # 用单个空格替换多个空格
    text = re.sub(r'\s+', ' ', text, flags=re.I)
    # 转换为小写
    text = text.lower()
    # 词形还原
    tokens = text.split()
    tokens = [stemmer.lemmatize(word) for word in tokens]
    preprocessed_text = ' '.join(tokens)
    return preprocessed_text
train["title"] = train["title"].progress_apply(lambda x: preprocess_text(x))
train["abstract"] = train["abstract"].progress_apply(lambda x: preprocess_text(x))
test["title"] = test["title"].progress_apply(lambda x: preprocess_text(x))
test["abstract"] = test["abstract"].progress_apply(lambda x: preprocess_text(x))

(2)训练集预处理

train_text_list = []
for index, row in train.iterrows():
    title = row["title"]
    abstract = row["abstract"]
    text = "[CLS] " + title + " [SEP] " + abstract + " [SEP]"
    train_text_list.append(text)
train['text'] = train_text_list

label_path = "data/pseudo_data/label_id2cate.pkl"
#将标签进行转换
label_id2cate = dict(enumerate(train.categories.unique()))
label_cate2id = {value: key for key, value in label_id2cate.items()}

with open(label_path, 'wb') as f:
    pickle.dump(label_id2cate, f, pickle.HIGHEST_PROTOCOL)
train['label'] = train['categories'].map(label_cate2id)
train_data = pd.DataFrame(columns=['text','label'])
train_data['text'] = train['text']
train_data['label'] = train['label']
train_data.to_csv('data/train_clean_data.csv', sep='\t',index=False)

(3)测试集预处理

test_text_list = []
for index, row in test.iterrows():
    title = row["title"]
    abstract = row["abstract"]
    text = "[CLS] " + title + " [SEP] " + abstract + " [SEP]"
    test_text_list.append(text)
test['text'] = test_text_list
pseudo_label = pd.read_csv("./submit_voting-8-9-2.csv")
test['label'] = pseudo_label['categories'].map(label_cate2id)
# 去除换行符
test_data = test.drop(['paperid', 'title', 'abstract'], axis=1, inplace=False)
test_data.to_csv('data/test_clean_data.csv', sep='\t', index=False)

3.2 Bert

bert_base、bert_large、roberta_large代码都是一样的,唯一的不同在于使用预训练模型和对应分词器的选择不同。在以下代码会注释说明

(1)加载包

# 导入transformers
import transformers
from transformers import AutoConfig,AutoModel,AutoTokenizer,logging
from torch.utils.data import RandomSampler,Dataset, DataLoader
from transformers import BertModel, BertTokenizer, BertConfig, AdamW, get_linear_schedule_with_warmup
from transformers import RobertaTokenizer, RobertaModel
# 导入torch
import torch
import torch.nn as nn
import torch.nn.functional as F

# 常用包
import re
import os
import numpy as np
import pandas as pd
from tqdm import tqdm
import pickle
import os
import torch
import torch.nn as nn
from torch.utils import data
from sklearn.utils import resample
from sklearn.metrics import accuracy_score
# 全局变量
os.environ["TOKENIZERS_PARALLELISM"] = "false"
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
torch.manual_seed(RANDOM_SEED)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

(2)模型结构

cache_dir = '/media/mgege007/winType/cache'
class PaperClassifier(nn.Module):
    def __init__(self):
        n_classes = 39
        super(PaperClassifier, self).__init__()
        # 如果是Bert_large,此处是
        # PRE_TRAINED_MODEL_NAME = "bert-large-uncased"
        # self.robert = BertModel.from_pretrained(PRE_TRAINED_MODEL_NAME)
        # 如果是bert_base,此处是
        PRE_TRAINED_MODEL_NAME = "bert-base-uncased"
        self.robert = BertModel.from_pretrained(PRE_TRAINED_MODEL_NAME)
        # 如果是roberta_large,此处是
          # PRE_TRAINED_MODEL_NAME = 'roberta-large'
        # self.robert = RobertaModel.from_pretrained(PRE_TRAINED_MODEL_NAME)

        self.bilstm = nn.LSTM(input_size=self.robert.config.hidden_size,
                              hidden_size=self.robert.config.hidden_size, batch_first=True, bidirectional=True)
        self.drop = nn.Dropout(p=0.3)
        self.out = nn.Linear(self.robert.config.hidden_size * 2, n_classes)

    def forward(self, input_ids, attention_mask):
        last_hidden_out, pooled_output = self.robert(  # 只要了句子级表示?    _:[10, 300, 768]    [16, 768]
            input_ids=input_ids,
            attention_mask=attention_mask)  # [16, 300]300是句子长度
        last_hidden_out = self.drop(last_hidden_out)
        output_hidden, _ = self.bilstm(last_hidden_out)  # [10, 300, 768]
        output = self.drop(output_hidden)  # dropout
        output = output.mean(dim=1)
        return self.out(output)

(3)读取数据

def data_process():
    train = pd.read_csv('data/train_clean_data.csv', sep='\t')
    test = pd.read_csv('data/test_clean_data.csv', sep='\t')
    label_path = "data/label_id2cate.pkl"# label编码所需要的字典
    if os.path.exists(label_path):
        with open(label_path, 'rb') as f:
            label_id2cate = pickle.load(f)
        return train, test, label_id2cate

(4)封装数据集

class PaperDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_len):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len

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

    def __getitem__(self, item):
        """
        item 为数据索引,迭代取第item条数据
        """
        text = str(self.texts[item])
        label = self.labels[item]

        encoding = self.tokenizer.encode_plus(  # 等价于tokenizer.tokenize() + tokenizer.convert_tokens_to_ids()
            text,
            add_special_tokens=True,
            max_length=self.max_len,
            truncation=True,
            return_token_type_ids=True,
            pad_to_max_length=True,
            return_attention_mask=True,
            return_tensors='pt',
        )
        return {
            'texts': text,
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'labels': torch.tensor(label, dtype=torch.long)
        }
def create_data_loader(df, tokenizer, max_len, batch_size,sampler):
    ds = PaperDataset(  # dataset
        texts=df['text'].values,
        labels=df['label'].values,
        tokenizer=tokenizer,
        max_len=max_len
    )
    return DataLoader(
        ds,
        batch_size=batch_size,
        sampler = sampler,
        num_workers=4,  # 多线程
        pin_memory=True  # 页锁定内存
    )
def create_test_loader(df, tokenizer, max_len, batch_size):
    ds = PaperDataset(  # dataset
        texts=df['text'].values,
        labels=df['label'].values,
        tokenizer=tokenizer,
        max_len=max_len
    )
    return DataLoader(
        ds,
        batch_size=batch_size,
        num_workers=4,#多线程
        pin_memory=True  # 页锁定内存
    )

(5)定义1个epoch训练

def train_epoch(model, data_loader, loss_fn, optimizer, device, scheduler):

    print("start training!")
    model = model.train()
    losses = []
    pred_ls = []
    label_ls = []
    accumulate_step = 10
    i = 0
    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())
        loss = loss/accumulate_step
        loss.backward()
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        if (i+1)%accumulate_step ==0:
            optimizer.step()
            scheduler.step()
            optimizer.zero_grad()
        i+=1
        label_ls.extend(d["labels"])
        pred_ls.extend(preds.tolist())
    correct_predictions = accuracy_score(label_ls, pred_ls)
    return correct_predictions, np.mean(losses)

# 验证
def eval_model(model, data_loader, loss_fn, device):
    model = model.eval()  # 验证预测模式
    losses = []
    pred_ls = []
    label_ls = []
    with torch.no_grad():
        for d in 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)
            y_pred_arr = outputs.data.cpu().numpy()
            losses.append(loss.item())
            pred_ls.extend(preds.tolist())
            label_ls.extend(d["labels"])         
        correct_predictions = accuracy_score(label_ls, pred_ls)
    return correct_predictions, np.mean(losses)

(6)预测结果

def model_predictions(model, data_loader, device):
    model = model.eval()
    result = []
    with torch.no_grad():
        for d in data_loader:
            input_ids = d["input_ids"].to(device)
            attention_mask = d["attention_mask"].to(device)
            outputs = model(
                input_ids=input_ids,
                attention_mask=attention_mask
            )
            y_pred = outputs.data.cpu().numpy()
            result.extend(y_pred)
    return result

(7)K折划分数据

# K折数据划分
def load_data_kfold(dataset,BATCH_SIZE,MAX_LEN, k, n):
    print("Cross validation第{}折正在划分数据集".format(n+1))
    l = len(dataset)
    print(l)
    shuffle_dataset = True
    random_seed = 42  # fixed random seed
    indices = list(range(l))
    if shuffle_dataset:
        np.random.seed(random_seed)
        np.random.shuffle(indices)  # shuffle
    # Collect indexes of samples for validation set.
    val_indices = indices[int(l / k) * n:int(l / k) * (n + 1)]
    train_indices = list(set(indices).difference(set(val_indices)))
    train_sampler = data.SubsetRandomSampler(train_indices)  # build Sampler
    valid_sampler = data.SubsetRandomSampler(val_indices)
    tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
    train_data_loader = create_data_loader(dataset, tokenizer, MAX_LEN, BATCH_SIZE,train_sampler)
    val_data_loader = create_data_loader(dataset, tokenizer, MAX_LEN, BATCH_SIZE,valid_sampler)
    print("划分完成")
    return train_data_loader, val_data_loader

(8)模型层间差分学习率

# 差分学习率的设置
def get_parameters(model, model_init_lr, multiplier, classifier_lr):
    parameters = []
    lr = model_init_lr
    for layer in range(12, -1, -1):  # 遍历模型的每一层,bert_large和robert_large参数选择24,bert_base选择12
        layer_params = {
            'params': [p for n, p in model.named_parameters() if f'encoder.layer.{layer}.' in n],
            'lr': lr
        }
        parameters.append(layer_params)
        lr *= multiplier  # 每一层的学习率*0.95的衰减因子
    classifier_params = {
        'params': [p for n, p in model.named_parameters() if 'layer_norm' in n or 'linear' in n
                   or 'pooling' in n],
        'lr': classifier_lr  # 单独针对全连接层
    }
    parameters.append(classifier_params)
    return parameters

注意:因为bert_large和robert_large是24层网络结构,参数选择24,bert_base是12层网络结构选择12

(9)训练过程

#训练模型
def train_start(EPOCHS,MAX_LEN,BATCH_SIZE,train, test_data_loader, label_id2cate):
    #模型定义
    model = PaperClassifier()
    model = model.to(device)
    #普通学习率
    k_fold = 5
    predict_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)
        total_steps = len(train_data_loader) * EPOCHS
        scheduler = get_linear_schedule_with_warmup(
            optimizer,
            num_warmup_steps=0,
            num_training_steps=total_steps
        )
        loss_fn = nn.CrossEntropyLoss().to(device)
        best_accuracy = 0
        for 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)
            val_x.extend(val_x_epoch)
            val_y.extend(val_y_epoch)
            print(f'Val loss {val_loss} accuracy {val_acc}')

            if val_acc > best_accuracy:
                torch.save(model.state_dict(), 'model/best_model_state_large_ensemble.bin')
                best_accuracy = val_acc

        #进行预测
        y_pred = model_predictions(model, test_data_loader, device)
        predict_all += np.array(y_pred)
    # 预测矩阵取平均
    predictions = predict_all/k_fold
      # 存储预测矩阵
    np.save("submit_arr.npy", predictions)
    pred = np.argmax(predictions, axis=1)
    print("计算完成")
    model_name = "Bert_large_cross"
    sub = pd.read_csv('data/sample_submit.csv')
    # 生成提交文件
    sub['categories'] = list(pred)
    sub['categories'] = sub['categories'].map(label_id2cate)
    sub.to_csv('submit/submit_{}.csv'.format(model_name), index=False)

(9)主函数

# 加载数据集
train, test, label_id2cate = data_process()
EPOCHS = 2
MAX_LEN = 300
BATCH_SIZE = 10
# bert_base和bert_large,对应各自的分词器
# PRE_TRAINED_MODEL_NAME = "bert-large-uncased"
PRE_TRAINED_MODEL_NAME = "bert-base-uncased"
tokenizer = BertTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
# roberta_large 选择以下分词器
# PRE_TRAINED_MODEL_NAME = 'roberta-large/'
# tokenizer = RobertaTokenizer.from_pretrained(PRE_TRAINED_MODEL_NAME)
# 封装测试集。不封装训练集,因为要先K折划分后再实现封装
test_data_loader = create_test_loader(test, tokenizer, MAX_LEN, BATCH_SIZE)
# 开始训练
train_start(EPOCHS,MAX_LEN,BATCH_SIZE,train, test_data_loader, label_id2cate)

4 提分点技巧讲解

  • 数据增强

NLP英文文本数据增强

5 未来展望

  • ITPT

继续预训练自己的数据集,在任务内的训练方式,理论上是可以提升模型的精度,但是我们尝试过训练。设置60epoch,训练5W的数据,训练了20多个小时,只有0.70+的精度,并没有带来提升,没有时间进一步分析就放弃了该方案,费时不讨好。

import warnings
import pandas as pd
from transformers import (AutoModelForMaskedLM,
                          AutoTokenizer, LineByLineTextDataset,
                          DataCollatorForLanguageModeling,
                          Trainer, TrainingArguments)
warnings.filterwarnings('ignore')
train_data = pd.read_csv('data/train/train.csv', sep='\t')
test_data = pd.read_csv('data/test/test.csv', sep='\t')
train_data['text'] = train_data['title'] + '.' + train_data['abstract']
test_data['text'] = test_data['title'] + '.' + test_data['abstract']
data = pd.concat([train_data, test_data])
data['text'] = data['text'].apply(lambda x: x.replace('\n', ''))
text = '\n'.join(data.text.tolist())

with open('text.txt', 'w') as f:
    f.write(text)

model_name = 'roberta-base'
model = AutoModelForMaskedLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.save_pretrained('./paper_roberta_base')

train_dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path="text.txt",  # mention train text file here
    block_size=256)

valid_dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path="text.txt",  # mention valid text file here
    block_size=256)

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

training_args = TrainingArguments(
    output_dir="./paper_roberta_base_chk",  # select model path for checkpoint
    overwrite_output_dir=True,
    num_train_epochs=5,# 轮数不要设置的太大
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    gradient_accumulation_steps=2,
    evaluation_strategy='steps',
    save_total_limit=2,
    eval_steps=200,
    metric_for_best_model='eval_loss',
    greater_is_better=False,
    load_best_model_at_end=True,
    prediction_loss_only=True,
    report_to="none")

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=train_dataset,
    eval_dataset=valid_dataset)

trainer.train()
trainer.save_model(f'./paper_roberta_base')
  • 梯度累计优化

通过梯度缓存和累积的方式,用时间来换取模型精度,最终训练效果等效于更大的batch size。因此,只要你跑得起batch_size=1,只要你愿意花n倍的时间,就可以跑出n倍的batch size。

def train_epoch(model, data_loader, loss_fn, optimizer, device, scheduler, n_examples):

    print("start training!")
    model = model.train()
    losses = []
    correct_predictions = 0
    step = 0
    pred_ls = []
    label_ls = []
    # 累计次数
    accumulation_steps = 4
    i= 0
    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)
        loss = loss / accumulation_steps # 梯度累积
        losses.append(loss.item())
        loss.backward()
        if (i+1) % accumulation_steps == 0: # Wait for several backward steps 
            optimizer.step() # Now we can do an optimizer step 
            nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
            scheduler.step()
            model.zero_grad() # Reset gradients tensors 
        label_ls.extend(d["labels"])
        pred_ls.extend(preds.tolist())
        i+=1
    correct_predictions = accuracy_score(label_ls, pred_ls)
    return correct_predictions, np.mean(losses)
  • 新的方案

BERT推出这一年来,除了XLNet,其他的改进都没带来太多惊喜,无非是越堆越大的模型和数据,以及动辄1024块TPU,让工程师们不知道如何落地。今天要介绍的ELECTRA是我在ICLR盲审中淘到的宝贝(9月25日已截稿),也是BERT推出以来我见过最赞的改进,通过类似GAN的结构和新的预训练任务,在更少的参数量和数据下,不仅吊打BERT,而且仅用1/4的算力就达到了当时SOTA模型RoBERTa的效果。

资料来源:ELECTRA: 超越BERT, 19年最佳NLP预训练模型

目录
相关文章
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
【大语言模型-论文精读】谷歌-BERT:用于语言理解的预训练深度双向Transformers
【大语言模型-论文精读】谷歌-BERT:用于语言理解的预训练深度双向Transformers
131 1
|
4月前
|
机器学习/深度学习 自然语言处理 PyTorch
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案--6 提分方案
在讯飞英文学术论文分类挑战赛中的提分技巧和实现方法,包括数据增强、投票融合、伪标签等策略,以及加快模型训练的技巧,如混合精度训练和使用AdamW优化器等。
47 0
|
4月前
|
机器学习/深度学习 数据采集 自然语言处理
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–4 机器学习LGB 方案
在讯飞英文学术论文分类挑战赛中使用LightGBM模型进行文本分类的方案,包括数据预处理、特征提取、模型训练及多折交叉验证等步骤,并提供了相关的代码实现。
53 0
|
4月前
|
数据采集 自然语言处理 机器学习/深度学习
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–3 TextCNN Fasttext 方案
讯飞英文学术论文分类挑战赛中使用TextCNN和FastText模型进行文本分类的方案,包括数据预处理、模型训练和对抗训练等步骤,并分享了模型调优的经验。
42 0
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
AI技术在自然语言处理中的应用与挑战
【10月更文挑战第3天】本文将探讨AI技术在自然语言处理(NLP)领域的应用及其面临的挑战。我们将分析NLP的基本原理,介绍AI技术如何推动NLP的发展,并讨论当前的挑战和未来的趋势。通过本文,读者将了解AI技术在NLP中的重要性,以及如何利用这些技术解决实际问题。
|
3月前
|
机器学习/深度学习 数据采集 自然语言处理
深度学习在自然语言处理中的应用与挑战
本文探讨了深度学习技术在自然语言处理(NLP)领域的应用,包括机器翻译、情感分析和文本生成等方面。同时,讨论了数据质量、模型复杂性和伦理问题等挑战,并提出了未来的研究方向和解决方案。通过综合分析,本文旨在为NLP领域的研究人员和从业者提供有价值的参考。
|
1月前
|
自然语言处理 API C++
阿里通义推出SmartVscode插件,自然语言控制VS Code,轻松开发应用,核心技术开源!
SmartVscode插件深度解析:自然语言控制VS Code的革命性工具及其开源框架App-Controller
|
2月前
|
自然语言处理 算法 Python
自然语言处理(NLP)在文本分析中的应用:从「被动收集」到「主动分析」
【10月更文挑战第9天】自然语言处理(NLP)在文本分析中的应用:从「被动收集」到「主动分析」
55 4
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
探索AI在自然语言处理中的创新应用
【10月更文挑战第7天】本文将深入探讨人工智能在自然语言处理领域的最新进展,揭示AI技术如何改变我们与机器的互动方式,并展示通过实际代码示例实现的具体应用。
47 1
|
2天前
|
机器学习/深度学习 人工智能 自然语言处理
AI在自然语言处理中的突破:从理论到应用
AI在自然语言处理中的突破:从理论到应用
33 17

热门文章

最新文章

下一篇
DataWorks