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 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> datawhale 8月学习——NLP之Transformers:Transformers解决文本分类任务、超参搜索 -> 正文阅读

[人工智能]datawhale 8月学习——NLP之Transformers:Transformers解决文本分类任务、超参搜索

前情回顾

  1. attention和transformers
  2. BERT和GPT
  3. 编写BERT模型
  4. BERT的应用、训练和优化

结论速递

本次任务了解了使用Transformers解决文本分类任务的方法,进一步熟悉了代码流程的使用。在使用BERT解决文本分类任务时,需要先进行tokenization,然后在进行微调的时候,需要先确定训练的超参数和评价指标,才能开始训练。此外,transformers库还允许外部调用optuna或者Ray Tune进行超参数搜索。

1 文本分类

1.1 任务简介

本次的任务来源于GLUE Benchmark
在这里插入图片描述
GLUE榜单包含了9个句子级别的分类任务,分别是:

  • CoLA (Corpus of Linguistic Acceptability) 鉴别一个句子是否语法正确。
    包含了来自于23种语言学出版物的10657个句子,并由其原作者专业地注释了可接受性(语法)。公共版本包含 9594 个属于训练和开发集的句子,不包括属于保留测试集的 1063 个句子(测试集不做公开)。
    可以查看论文
  • SST-2 (Stanford Sentiment Treebank) 判断一个句子的情感正负向。
    这是Stanford进行情感分析设计的数据集。它在 11855 个句子的解析树中包含了 215154 个短语的细粒度情感标签,构建了情绪树库,情绪树中的一棵长这样,可以查看论文
    在这里插入图片描述
  • MNLI (Multi-Genre Natural Language Inference) 给定一个假设,判断另一个句子与该假设的关系:entails, contradicts 或者 unrelated。
    可以查看论文
    在这里插入图片描述
  • MRPC (Microsoft Research Paraphrase Corpus) 判断两个句子是否互为paraphrases。
    包含 5800 对从网络新闻来源中提取的句子的文本文件,以及两个句子是否护卫等价关系的人工注释。
  • STS-B (Semantic Textual Similarity Benchmark) 判断两个句子的相似性(分数为1-5分)。
    包括2012 年至 2017 年在 SemEval 上下文中组织的 STS 任务中使用的英语数据集的选择。数据集的选择包括来自图像标题、新闻标题和用户论坛的文本。
    可以查看论文
  • QQP (Quora Question Pairs2) 判断两个问句是否语义相同。
    【但是链接失效了】
  • QNLI (Question-answering Natural Language Inference) 判断第2句是否包含第1句问题的答案。
    是一个阅读理解数据集,包括通过crowdworkers一组维基百科的文章,在回答每一个问题是文本,或段提出的问题跨度,从相应的读取通道,否则问题可能无法回答。
    有1.1和2.0两个版本,1.1包含 500 多篇文章的 100,000 多个问答对;2.0将SQuAD1.1 中的 100,000 个问题与众包工作者以对抗方式编写的 50,000 多个无法回答的问题相结合。
  • RTE (Recognizing Textual Entailment)判断一个句子是否与假设成entail关系。
    有7个数据集。
  • WNLI (Winograd Natural Language Inference) Determine if a sentence with an anonymous pronoun and a sentence with this pronoun replaced are entailed or not.
    Winograd 模式是一对仅在一个或两个单词上不同的句子,并且包含在两个句子中以相反方式解决的歧义,并且需要使用世界知识和推理来解决。

许多新提出的方法都会在这些benchmark数据集上测试,方便比较性能。
在这里插入图片描述

1.2 加载数据

我们可以通过🤗 Datasets库来加载数据,和对应的评测方式。除了mnli-mm之外,数据加载和评测方式加载只需要简单使用load_datasetload_metric通过任务名字加载,然后自动缓存就可以(mnli-mmmnli)。

这个datasets对象本身是一种DatasetDict数据结构. 对于训练集、验证集和测试集,只需要使用对应的key(train,validation,test)即可得到相应的数据。

cola为例

from datasets import load_dataset, load_metric
task = "cola"
actual_task = "mnli" if task == "mnli-mm" else task
dataset = load_dataset("glue", actual_task)
metric = load_metric('glue', actual_task)

我们可以查看这个DatasetDict

DatasetDict({
    train: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 8551
    })
    validation: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 1043
    })
    test: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 1063
    })
})

可以查看其中一个数据。

[In]: dataset["train"][0]
[Out]: 
{'idx': 0,
 'label': 1,
 'sentence': "Our friends won't buy this analysis, let alone the next one we propose."}

可以查看label的分类。

[In]: lis = [each["label"] for each in dataset["train"]]
[In]: set(lis)
[Out]: {0, 1}

同样,可以查看validationtest中的label情况,validation中同样也是{0, 1}test中是{-1}。那么test中的例子对应的label在哪里呢。

下面这段代码可以帮助我们更好地了解数据长什么样子。

import random
import pandas as pd
from IPython.display import display, HTML

def show_random_elements(dataset, num_examples=10):
    assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
    picks = []
    for _ in range(num_examples):
        pick = random.randint(0, len(dataset)-1)
        while pick in picks:
            pick = random.randint(0, len(dataset)-1)
        picks.append(pick)
    
    df = pd.DataFrame(dataset[picks])
    for column, typ in dataset.features.items():
        if isinstance(typ, datasets.ClassLabel):
            df[column] = df[column].transform(lambda i: typ.names[i])
    display(HTML(df.to_html()))

把训练集输入

show_random_elements(dataset["train"])

在这里插入图片描述
而导入的评估metic是datasets.Metric的一个实例:

[IN]: metric
[OUT]: Metric(name: "glue", features: {'predictions': Value(dtype='int64', id=None), 'references': Value(dtype='int64', id=None)}

直接调用metric的compute方法,随机传入labelspredictions即可得到metric的值:

import numpy as np

fake_preds = np.random.randint(0, 2, size=(64,))
fake_labels = np.random.randint(0, 2, size=(64,))
metric.compute(predictions=fake_preds, references=fake_labels)

输出结果如下

{'matthews_correlation': 0.15694120514358612}

对于对应的数据集,会有对应的metric,在这张图里头可以看到
在这里插入图片描述
教程里头也有小结

每一个文本分类任务所对应的metic有所不同,具体如下:

所以一定要将metric和任务对齐

2 使用BERT解决CoLA任务

2.1 数据预处理

首先进行TokenizerTokenizer首先对输入进行tokenize,然后将tokens转化为预模型中需要对应的token ID,再转化为模型需要的输入格式。

使用AutoTokenizer.from_pretrained方法实例化我们的tokenizer,这样可以确保:

  • 我们得到一个与预训练模型一一对应的tokenizer。
  • 使用指定的模型checkpoint对应的tokenizer的时候,我们也下载了模型需要的词表库vocabulary,准确来说是tokens vocabulary。

这个被下载的tokens vocabulary会被缓存起来,从而再次使用的时候不会重新下载。

from transformers import AutoTokenizer
    
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, use_fast=True)

注意:use_fast=True要求tokenizer必须是transformers.PreTrainedTokenizerFast类型,因为我们在预处理的时候需要用到fast tokenizer的一些特殊特性(比如多线程快速tokenizer)。如果对应的模型没有fast tokenizer,去掉这个选项即可。
几乎所有模型对应的tokenizer都有对应的fast tokenizer。我们可以在模型tokenizer对应表里查看所有预训练模型对应的tokenizer所拥有的特点。

检查数据格式

task_to_keys = {
    "cola": ("sentence", None),
    "mnli": ("premise", "hypothesis"),
    "mnli-mm": ("premise", "hypothesis"),
    "mrpc": ("sentence1", "sentence2"),
    "qnli": ("question", "sentence"),
    "qqp": ("question1", "question2"),
    "rte": ("sentence1", "sentence2"),
    "sst2": ("sentence", None),
    "stsb": ("sentence1", "sentence2"),
    "wnli": ("sentence1", "sentence2"),
}
sentence1_key, sentence2_key = task_to_keys[task]
if sentence2_key is None:
    print(f"Sentence: {dataset['train'][0][sentence1_key]}")
else:
    print(f"Sentence 1: {dataset['train'][0][sentence1_key]}")
    print(f"Sentence 2: {dataset['train'][0][sentence2_key]}")

可以看到CoLA是一个单句子的任务,task_to_keys中存储了这几个任务在数据集中的键名。

定义预处理函数preprocess_function

def preprocess_function(examples):
    if sentence2_key is None:
        return tokenizer(examples[sentence1_key], truncation=True)
    return tokenizer(examples[sentence1_key], examples[sentence2_key], truncation=True)

预处理函数可以处理单个样本,也可以对多个样本进行处理。如果输入是多个样本,那么返回的是一个list

[IN]: preprocess_function(dataset['train'][:5])
[OUT]: {'input_ids': [[101, 2256, 2814, 2180, 1005, 1056, 4965, 2023, 4106, 1010, 2292, 2894, 1996, 2279, 2028, 2057, 16599, 1012, 102], [101, 2028, 2062, 18404, 2236, 3989, 1998, 1045, 1005, 1049, 3228, 2039, 1012, 102], [101, 2028, 2062, 18404, 2236, 3989, 2030, 1045, 1005, 1049, 3228, 2039, 1012, 102], [101, 1996, 2062, 2057, 2817, 16025, 1010, 1996, 13675, 16103, 2121, 2027, 2131, 1012, 102], [101, 2154, 2011, 2154, 1996, 8866, 2024, 2893, 14163, 8024, 3771, 1012, 102]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}

接下来对数据集datasets里面的所有样本进行预处理,处理的方式是使用map函数,将预处理函数prepare_train_featuresmap到所有样本上。

encoded_dataset = dataset.map(preprocess_function, batched=True)

在这里插入图片描述

更好的是,返回的结果会自动被缓存,避免下次处理的时候重新计算(但是也要注意,如果输入有改动,可能会被缓存影响!)。datasets库函数会对输入的参数进行检测,判断是否有变化,如果没有变化就使用缓存数据,如果有变化就重新处理。但如果输入参数不变,想改变输入的时候,最好清理调这个缓存。清理的方式是使用load_from_cache_file=False参数。另外,上面使用到的batched=True这个参数是tokenizer的特点,以为这会使用多线程同时并行对输入进行处理。

2.2 微调预训练模型

使用AutoModelForSequenceClassification 这个类。和tokenizer相似,from_pretrained方法同样可以帮助我们下载并加载模型,同时也会对模型进行缓存,就不会重复下载模型啦。

需要注意的是,STS-B是一个回归问题,MNLI是一个3分类问题。所以

from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer

num_labels = 3 if task.startswith("mnli") else 1 if task=="stsb" else 2
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)

会有如下的提示

Some weights of the model checkpoint at distilbert-base-uncased were not used when initializing DistilBertForSequenceClassification: ['vocab_transform.weight', 'vocab_transform.bias', 'vocab_layer_norm.weight', 'vocab_layer_norm.bias', 'vocab_projector.weight', 'vocab_projector.bias']
- This IS expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert-base-uncased and are newly initialized: ['pre_classifier.weight', 'pre_classifier.bias', 'classifier.weight', 'classifier.bias']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

2.2.1 确定 TrainingArguments

TrainingArguments是训练的设定/参数 。这个训练设定包含了能够定义训练过程的所有属性。

metric_name = "pearson" if task == "stsb" else "matthews_correlation" if task == "cola" else "accuracy"

args = TrainingArguments(
    "test-glue",
    evaluation_strategy = "epoch", #每个epoch做一次验证评估
    save_strategy = "epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    num_train_epochs=5,
    weight_decay=0.01,
    load_best_model_at_end=True,
    metric_for_best_model=metric_name,
)

2.2.2 确定评价指标

由于不同的任务需要不同的评测指标,我们定一个函数来根据任务名字得到评价方法。

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    if task != "stsb":
        predictions = np.argmax(predictions, axis=1)
    else:
        predictions = predictions[:, 0]
    return metric.compute(predictions=predictions, references=labels)

2.2.3 开始训练

全部传给Trainer

validation_key = "validation_mismatched" if task == "mnli-mm" else "validation_matched" if task == "mnli" else "validation"
trainer = Trainer(
    model,
    args,
    train_dataset=encoded_dataset["train"],
    eval_dataset=encoded_dataset[validation_key],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

然后开始训练

trainer.train()

这里用的是Google colab的GPU,用TPU反而计算得很慢(为啥?)
在这里插入图片描述
训练后完成评估

trainer.evaluate()

在这里插入图片描述

2.3 超参数搜索

使用optuna or Ray Tune代码库进行超参数搜索,这两个库的调用是在trainer.hyperparameter_search里头调用的。

在代码的开头,会先坚持HPSearchBackend,如果找不到,会提示安装。
在这里插入图片描述
.intergration.py里面可以找到这两段函数的调用代码。optuna的调用比ray tune要简洁一些。
在这里插入图片描述
在超参数搜索时,Trainer会返回多个训练好的模型,所以需要传入一个定义好的模型从而让Trainer可以不断重新初始化该传入的模型。

def model_init():
    return AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)

然后和之前调用Trainer是类似的,建立一个Trainer

trainer = Trainer(
    model_init=model_init,
    args=args,
    train_dataset=encoded_dataset["train"],
    eval_dataset=encoded_dataset[validation_key],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

调用方法hyperparameter_search。注意,这个过程可能很久,我们可以先用部分数据集进行超参搜索,再进行全量训练。

比如使用1/10的数据进行搜索(但是其实下面的代码并没有)

best_run = trainer.hyperparameter_search(n_trials=10, direction="maximize")

得到的效果最好的模型相关参数如下:

BestRun(run_id='5', objective=0.5448598482839426, hyperparameters={'learning_rate': 1.515507315336777e-05, 'num_train_epochs': 4, 'seed': 8, 'per_device_train_batch_size': 4})

Trainner设置为搜索到的最好参数,进行训练:

for n, v in best_run.hyperparameters.items():
    setattr(trainer.args, n, v)

trainer.train()

在这里插入图片描述
效果似乎并没有变好,可能是因为只跑了10步超参数搜索。

3 使用BERT解决MRPC任务

3.1 数据预处理

和前面代码一致,可以随机查看到一些训练集中的信息。
在这里插入图片描述
也可以查看两个句子检查格式。
在这里插入图片描述

3.2 微调预训练模型

训练结果如下
在这里插入图片描述
在验证集上的准确性如下:
在这里插入图片描述

3.3 超参数搜索

同样的,我们可以进行超参数搜索,得到最优解。

BestRun(run_id='4', objective=1.7440123700486905, hyperparameters={'learning_rate': 6.985828778440409e-05, 'num_train_epochs': 5, 'seed': 33, 'per_device_train_batch_size': 64})

最优解的计算结果如下:
在这里插入图片描述
效果同样没有很好地改善。

跑30个trails呢,best_run出现在了第17个。

BestRun(run_id='17', objective=1.7545374560080442, hyperparameters={'learning_rate': 3.312982201716147e-05, 'num_train_epochs': 4, 'seed': 26, 'per_device_train_batch_size': 32})

再训练看看。
在这里插入图片描述
效果变好了。

试试抽取20%的数据跑60次。

length = len(encoded_dataset["train"]) 
data_train = encoded_dataset["train"].train_test_split(0.8,0.2)
data_train = data_train["train"]
trainer = Trainer(
    model_init=model_init,
    args=args,
    train_dataset=data_train,
    eval_dataset=encoded_dataset[validation_key],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

跑30个trails呢,best_run出现在了第44个。

BestRun(run_id='44', objective=1.6602796290930164, hyperparameters={'learning_rate': 3.443656461851277e-05, 'num_train_epochs': 3, 'seed': 24, 'per_device_train_batch_size': 4})

在这里插入图片描述
出现了比较严重的过拟合现象。(因为跑起来挺花时间的,此处不再尝试别的数据划分方式)

参考阅读

  1. Datawhale教程
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:51:08  更:2021-08-27 11:52:13 
 
开发: 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年5日历 -2024/5/4 7:01:07-

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