优化RoBERTa:在AMD上使用混合精度进行微调

2024-08-20 21:28

本文主要是介绍优化RoBERTa:在AMD上使用混合精度进行微调,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Optimizing RoBERTa: Fine-Tuning with Mixed Precision on AMD — ROCm Blogs

简介

在这篇博客中,我们将探讨如何微调鲁棒优化的BERT预训练方法([RoBERTa](https://arxiv.org/abs/1907.11692))大型语言模型,重点在于PyTorch的混合精度功能。具体来说,我们将利用AMD GPU进行混合精度微调,以在不显著影响精度的情况下加快模型训练过程。

RoBERTa是Facebook AI开发的双向编码器表示转换模型([BERT](https://arxiv.org/abs/1810.04805))的高级变体。它通过修改预训练中的关键超参数(如移除下一个句子预测机制和使用更大的小批量大小进行训练)来增强BERT。在广泛的自然语言处理(NLP)任务中,该模型表现出优越的性能。有关RoBERTa的更多信息,请参见[RoBERTa: A Robustly Optimized BERT Pretraining Approach](https://arxiv.org/abs/1907.11692)。

混合精度训练是一种通过使用16位和32位浮点操作来加速深度学习模型训练阶段的技术。PyTorch通过`torch.cuda.amp`模块支持自动混合精度(AMP)训练。在使用AMP时,包含矩阵乘法的操作将在较低(float 16)精度下进行。较低精度的计算速度更快且使用的内存更少。通过在训练期间保留模型权重的全精度副本来维持模型精度。

有关混合精度训练的更多信息,请参见[自动混合精度包 - torch.amp](Automatic Mixed Precision package - torch.amp — PyTorch 2.4 documentation)和[在使用AMD GPU的PyTorch中自动混合精度](Automatic mixed precision in PyTorch using AMD GPUs — ROCm Blogs)。

您可以在[GitHub文件夹](rocm-blogs/blogs/artificial-intelligence/roberta_amp at release · ROCm/rocm-blogs · GitHub)中找到与本博客文章相关的文件。

系统要求:操作系统和硬件测试

- AMD GPU:请参见[ROCm 文档页面](System requirements (Linux) — ROCm installation (Linux))获取支持的硬件和操作系统。

- ROCm 6.1:请参见[ROCm Linux 安装说明](ROCm installation for Linux — ROCm installation (Linux))获取安装说明。
- Docker:请参见[在 Ubuntu 上安装 Docker 引擎](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository)获取安装说明。

- PyTorch 2.1.2:请使用官方 ROCm Docker 镜像,链接为:[rocm/pytorch:rocm6.1_ubuntu22.04_py3.10_pytorch_2.1.2](https://hub.docker.com/layers/rocm/pytorch/rocm6.1_ubuntu22.04_py3.10_pytorch_2.1.2/images/sha256-f6ea7cee8aae299c7f6368187df7beed29928850c3929c81e6f24b34271d652b?context=explore)。

运行本博客

- 克隆仓库并 cd 进入博客目录:

git clone git@github.com:ROCm/rocm-blogs.git
cd rocm-blogs/blogs/artificial-intelligence/roberta_amp

- 构建并启动容器。有关构建过程的详细信息,请参见 roberta_amp/docker/Dockerfile

cd docker
docker compose build
docker compose up

- 在你的浏览器中打开 http://localhost:8888/lab/tree/src/roberta_amp.ipynb,并打开 roberta_amp.ipynb 笔记本。

微调 RoBERTa 用于情感分类任务

我们使用了在[Hugging Face 网站](https://huggingface.co/)上的[dair-ai/emotion](https://huggingface.co/datasets/dair-ai/emotion)数据集。该数据集是为情感分类任务设计的。它包括一系列标注了六种基本情感(愤怒,恐惧,快乐,爱,悲伤,惊喜)的英文 Twitter 消息。该数据集包括分割和未分割版本的配置。分割配置包含 16,000 个训练示例,以及每个验证和测试分割的 2,000 个示例。我们使用此数据集来微调自定义的 RoBERTa 语言模型,用于情感分类。我们将评估 RoBERTa 在预测文本情感表达方面的性能,比较使用混合精度训练前后的效果。

在使用 PyTorch 微调模型时,有两个选择:*PyTorch Hugging Face Trainer API* 或 *原生 Python*。我们将使用 原生 Python 微调,因为它可以为用户提供更多的训练过程控制。此方法要求用户手动设置训练循环、处理反向传播、以及明确管理数据加载和模型更新。混合精度可以通过手动设置并使用 torch.cuda.amp 模块在原生 PyTorch 中实现。

让我们首先导入以下模块:

import torch
from torch.utils.data import DataLoader
from torch.optim import AdamW
from tqdm.notebook import tqdmfrom transformers import RobertaTokenizer, RobertaForSequenceClassification, set_seed
import datasets
import timeset_seed(42)
from IPython.display import display, clear_output

探索`dair-ai/emotion`数据集并加载数据如下:

# Dataset description can be found at https://huggingface.co/datasets/dair-ai/emotion# Load train validation and test splits
train_data = datasets.load_dataset("dair-ai/emotion", split="train",trust_remote_code=True)
validation_data = datasets.load_dataset("dair-ai/emotion", split="validation",trust_remote_code=True)
test_data = datasets.load_dataset("dair-ai/emotion", split="test",trust_remote_code=True)# Show dataset number of examples and column names
print(train_data)
print(validation_data)
print(test_data,'\n')# Print the first instance and label on the train split
print('Text:',train_data['text'][0], '| Label:', train_data['label'][0])

每个数据分割包含以下内容:

Dataset({features: ['text', 'label'],num_rows: 16000
})
Dataset({features: ['text', 'label'],num_rows: 2000
})
Dataset({features: ['text', 'label'],num_rows: 2000
}) Text: i didnt feel humiliated | Label: 0

在上面的输出中,我们打印了`train`分割数据的第一个训练示例,其对应的标签是`0:sadness`。

使用本地PyTorch进行微调

让我们开始使用本地PyTorch训练方法对我们的自定义RoBERTa模型进行微调。我们将微调两个版本的同一模型:一个版本进行常规微调,另一个版本使用混合精度微调。我们对比两种版本在训练时间和性能指标上的表现。

常规微调和性能指标

首先,我们对数据进行标记化并创建相应的数据加载器:

# Get the device
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# Load train validation and test splits
# Dataset description can be found at https://huggingface.co/datasets/dair-ai/emotiontrain_data = datasets.load_dataset("dair-ai/emotion", split="train",trust_remote_code=True)
validation_data = datasets.load_dataset("dair-ai/emotion", split="validation",trust_remote_code=True)
test_data = datasets.load_dataset("dair-ai/emotion", split="test",trust_remote_code=True)# Load the tokenizer
tokenizer = RobertaTokenizer.from_pretrained('roberta-base')# Tokenize the dataset
def tokenize_function(examples):return tokenizer(examples['text'], padding = 'max_length',return_tensors="pt")# Apply tokenization to each split
tokenized_train_data = train_data.map(tokenize_function, batched = True)
tokenized_validation_data = validation_data.map(tokenize_function, batched = True)
tokenized_test_data = test_data.map(tokenize_function, batched = True)# Set type to PyTorch tensors
tokenized_train_data.set_format(type="torch")
tokenized_validation_data.set_format(type="torch")
tokenized_test_data.set_format(type="torch")# Transform tokenized datasets to PyTorch dataloder
train_loader = DataLoader(tokenized_train_data, batch_size = 32)
validation_loader = DataLoader(tokenized_validation_data, batch_size = 32)
test_loader = DataLoader(tokenized_test_data, batch_size = 32)

现在定义其余组件并执行以下代码开始训练:

# Load Roberta model for sequence classification
num_labels = 6 # The dair-ai/emotion contains 6 labels
epochs = 3
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels = num_labels)
model.to(device)# Instantiate the optimizer with the given learning rate
optimizer = AdamW(model.parameters(), lr = 5e-5)# Training Loop
model.train()# Train the model
torch.cuda.synchronize() # Wait for all kernels to finish
start_time = time.time()    for epoch in range(epochs):for batch in train_loader:inputs = {'input_ids':batch['input_ids'].to(model.device),'attention_mask':batch['attention_mask'].to(model.device),'labels':batch['label'].to(model.device)}outputs = model(**inputs)loss = outputs.lossloss.backward()optimizer.step()optimizer.zero_grad()clear_output(wait=True)            display(f'Epoch: {epoch+1}/{epochs}. Training Loss: {loss.item()}')# Validation Loopmodel.eval()total_eval_loss = 0for batch in validation_loader:with torch.no_grad():inputs = {'input_ids':batch['input_ids'].to(model.device),'attention_mask':batch['attention_mask'].to(model.device),'labels':batch['label'].to(model.device)}outputs = model(**inputs)loss = outputs.losstotal_eval_loss += loss.item()avg_val_loss = total_eval_loss / len(validation_loader)display(f'Validation Loss: {avg_val_loss}')torch.cuda.synchronize() # Wait for all kernels to finish
training_time_regular = time.time() - start_time
print(f'Mixed Precision False. Training time (s):{training_time_regular:.3f}')# Save the model
model.save_pretrained(f'./native_finetuned_roberta_mixed_precision_false')

上面的代码显示每个批次的训练损失和平均验证损失。训练完成后,您将获得类似以下的输出:

'Epoch: 3/3. Training Loss: 0.10250010341405869'
'Validation Loss: 0.18223475706246164'
Mixed Precision False. Training time (s):681.362

上面的输出显示第三个epoch最后一个批次的训练损失,平均验证损失,以及常规训练的总训练时间(在这种情况下约为680秒)。

这个模型的表现如何?让我们计算它的精确度(Precision)、召回率(Recall)和F1性能指标:

from sklearn.metrics import accuracy_score, precision_recall_fscore_supportdef roberta_finetuned_performance_metrics(saved_model_path, tokenizer):is_mixed_precision = saved_model_path.split('_')[-1]model = RobertaForSequenceClassification.from_pretrained(saved_model_path)model.to(device)# return predictionsdef inference(batch):        inputs = {k: v.to(device) for k, v in batch.items() if k in tokenizer.model_input_names}with torch.no_grad():outputs = model(**inputs)predictions = torch.argmax(outputs.logits,dim = -1).cpu().numpy()return {'predictions': predictions}# Perform inference on test setresults = tokenized_test_data.map(inference, batched=True, batch_size = 32)# Extract predictions and true labelspredictions = results['predictions'].tolist()true_labels = tokenized_test_data['label'].tolist()# Compute evaluation metricsaccuracy = accuracy_score(true_labels,predictions)precision, recall, f1, _ = precision_recall_fscore_support(true_labels, predictions, average = 'weighted')print(f'Model mixed precision: {is_mixed_precision}.\nPrecision: {precision:.3f} | Recall: {recall:.3f}  | F1: {f1:.3f}')
saved_model_path = './native_finetuned_roberta_mixed_precision_false'    
roberta_finetuned_performance_metrics(saved_model_path, tokenizer)

输出为:

Model mixed precision: False.
Precision: 0.930 | Recall: 0.925  | F1: 0.919
混合精度微调

现在,让我们在使用本地PyTorch进行训练时利用混合精度。运行以下代码开始训练过程:

# Load Roberta model for sequence classification
num_labels = 6 # The dair-ai/emotion contains 6 labels
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels = num_labels)
model.to(device)# Define the optimizer
optimizer = AdamW(model.parameters(), lr = 5e-5)# Instantiate gradient scaler
scaler = torch.cuda.amp.GradScaler()# Train the model
torch.cuda.synchronize() # Wait for all kernels to finish
model.train()start_time = time.time()  
for epoch in range(epochs):for batch in tqdm(train_loader):optimizer.zero_grad()        inputs = {'input_ids':batch['input_ids'].to(model.device),'attention_mask':batch['attention_mask'].to(model.device),'labels':batch['label'].to(model.device)}# Use Automatic Mixed Precisionwith torch.cuda.amp.autocast():        outputs = model(**inputs)loss = outputs.lossscaler.scale(loss).backward()scaler.step(optimizer)scaler.update()clear_output(wait=True)             display(f'Epoch: {epoch+1}/{epochs}. Training Loss: {loss.item()}')        # Validation loopmodel.eval()total_eval_loss = 0for batch in validation_loader:with torch.no_grad(), torch.cuda.amp.autocast():inputs = {'input_ids':batch['input_ids'].to(model.device),'attention_mask':batch['attention_mask'].to(model.device),'labels':batch['label'].to(model.device)}outputs = model(**inputs)loss = outputs.losstotal_eval_loss +=loss.item()avg_val_loss = total_eval_loss / len(validation_loader)display(f'Validation Loss: {avg_val_loss}')torch.cuda.synchronize() # Wait for all kernels to finish
training_time_amp = time.time() - start_time
print(f'Mixed Precision True. Training time (s):{training_time_amp:.3f}')# Save the model
model.save_pretrained(f'./native_finetuned_roberta_mixed_precision_true')    

在上述代码中,我们显式地使用了`torch.cuda.amp.GradScaler`和`torch.cuda.amp.autocast`来在训练循环中启用自动混合精度。训练结束时,我们将得到以下输出:

'Epoch: 3/3. Training Loss: 0.1367110311985016'
'Validation Loss: 0.1395080569717619'
Mixed Precision True. Training time (s):457.022

使用PyTorch自动混合精度比常规微调实现了更短的训练时间(大约450秒)。

最后,相关的性能指标如下:

saved_model_path = './native_finetuned_roberta_mixed_precision_true'    
roberta_finetuned_performance_metrics(saved_model_path, tokenizer)
Model mixed precision: True.
Precision: 0.927 | Recall: 0.928  | F1: 0.927

我们在训练时间上取得了较好的结果,同时对整体模型性能只有最小的影响。

总结

在这篇博客中,我们探讨了使用混合精度训练对RoBERTa大型语言模型进行微调,重点强调了使用AMD GPU。我们利用PyTorch的自动混合精度(AMP),观察到AMD硬件在加速训练过程方面非常出色,同时确保模型性能的最小损失。AMD硬件与PyTorch AMP的集成展示了一种有效的解决方案,提升了计算效率并缩短了训练时间,使其非常适合深度学习工作流。

这篇关于优化RoBERTa:在AMD上使用混合精度进行微调的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1091215

相关文章

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3

Python Transformer 库安装配置及使用方法

《PythonTransformer库安装配置及使用方法》HuggingFaceTransformers是自然语言处理(NLP)领域最流行的开源库之一,支持基于Transformer架构的预训练模... 目录python 中的 Transformer 库及使用方法一、库的概述二、安装与配置三、基础使用:Pi

关于pandas的read_csv方法使用解读

《关于pandas的read_csv方法使用解读》:本文主要介绍关于pandas的read_csv方法使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录pandas的read_csv方法解读read_csv中的参数基本参数通用解析参数空值处理相关参数时间处理相关

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

Python中使用正则表达式精准匹配IP地址的案例

《Python中使用正则表达式精准匹配IP地址的案例》Python的正则表达式(re模块)是完成这个任务的利器,但你知道怎么写才能准确匹配各种合法的IP地址吗,今天我们就来详细探讨这个问题,感兴趣的朋... 目录为什么需要IP正则表达式?IP地址的基本结构基础正则表达式写法精确匹配0-255的数字验证IP地

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Spring LDAP目录服务的使用示例

《SpringLDAP目录服务的使用示例》本文主要介绍了SpringLDAP目录服务的使用示例... 目录引言一、Spring LDAP基础二、LdapTemplate详解三、LDAP对象映射四、基本LDAP操作4.1 查询操作4.2 添加操作4.3 修改操作4.4 删除操作五、认证与授权六、高级特性与最佳

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要