一起学Hugging Face Transformers(13)- 模型微调之自定义训练循环


前言

Hugging Face Transformers 库为 NLP 模型的预训练和微调提供了丰富的工具和简便的方法。虽然 Trainer API 简化了许多常见任务,但有时我们需要更多的控制权和灵活性,这时可以实现自定义训练循环。本文将介绍什么是训练循环以及如何使用 Hugging Face Transformers 库实现自定义训练循环。


一、什么是训练循环

在模型微调过程中,训练循环是指模型训练的核心过程,通过多次迭代数据集来调整模型的参数,使其在特定任务上表现更好。训练循环包含以下几个关键步骤:

1. 训练循环的关键步骤

1) 前向传播(Forward Pass)

  • 模型接收输入数据并通过网络进行计算,生成预测输出。这一步是将输入数据通过模型的各层逐步传递,计算出最终的预测结果。

2) 计算损失(Compute Loss)

  • 将模型的预测输出与真实标签进行比较,计算损失函数的值。损失函数是一个衡量预测结果与真实值之间差距的指标,常用的损失函数有交叉熵损失(用于分类任务)和均方误差(用于回归任务)。

3) 反向传播(Backward Pass)

  • 根据损失函数的值,计算每个参数对损失的贡献,得到梯度。反向传播使用链式法则,将损失对每个参数的梯度计算出来。

4) 参数更新(Parameter Update)

  • 使用优化算法(如梯度下降、Adam 等)根据计算出的梯度调整模型的参数。优化算法会更新每个参数,使损失函数的值逐步减小,模型的预测性能逐步提高。

5) 重复以上步骤

  • 以上过程在整个数据集上进行多次(多个epoch),每次遍历数据集被称为一个epoch。随着训练的进行,模型的性能会不断提升。

2. 示例

假设你在微调一个BERT模型用于情感分析任务,训练循环的步骤如下:

1) 前向传播

  • 输入一条文本评论,模型通过各层网络计算,生成预测的情感标签(如正面或负面)。

2) 计算损失

  • 将模型的预测标签与实际标签进行比较,计算交叉熵损失。

3) 反向传播

  • 计算损失对每个模型参数的梯度,确定每个参数需要调整的方向和幅度。

4) 参数更新

  • 使用Adam优化器,根据计算出的梯度调整模型的参数。

5) 重复以上步骤

  • 在整个训练数据集上进行多次迭代,不断调整参数,使模型的预测精度逐步提高。

3. 训练循环的重要性

训练循环是模型微调的核心,通过多次迭代和参数更新,使模型能够从数据中学习,逐步提高在特定任务上的性能。理解训练循环的各个步骤和原理,有助于更好地调试和优化模型,获得更好的结果。

在实际应用中,训练循环可能会包含一些额外的步骤和技术,例如:

  • 批量训练(Mini-Batch Training):将数据集分成小批量,每次训练一个批量,降低计算资源的需求。
  • 学习率调度(Learning Rate Scheduling):动态调整学习率,以提高训练效率和模型性能。
  • 正则化技术(Regularization Techniques):如Dropout、权重衰减等,防止模型过拟合。

这些技术和方法结合使用,可以进一步提升模型微调的效果和性能。

二、使用 Hugging Face Transformers 库实现自定义训练循环

1. 前期准备

1)安装依赖

首先,确保已经安装了必要的库:

pip install transformers datasets torch

2)导入必要的库

import torch
from torch.utils.data import DataLoader
from transformers import AutoTokenizer, AutoModelForSequenceClassification, get_scheduler
from datasets import load_dataset
from tqdm.auto import tqdm

2. 加载数据和模型

1) 加载数据集

这里我们以 IMDb 电影评论数据集为例:

dataset = load_dataset("imdb")

2) 加载预训练模型和分词器

我们将使用 distilbert-base-uncased 作为基础模型:

model_name = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

3) 预处理数据

定义一个预处理函数,并将其应用到数据集:

def preprocess_function(examples):
    return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)

encoded_dataset = dataset.map(preprocess_function, batched=True)
encoded_dataset = encoded_dataset.rename_column("label", "labels")
encoded_dataset.set_format(type="torch", columns=["input_ids", "attention_mask", "labels"])

4) 创建数据加载器

train_dataloader = DataLoader(encoded_dataset["train"], batch_size=8, shuffle=True)
eval_dataloader = DataLoader(encoded_dataset["test"], batch_size=8)

3. 自定义训练循环

1) 定义优化器和学习率调度器

optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps
)

2) 定义训练和评估函数

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

def train_loop():
    model.train()
    for batch in tqdm(train_dataloader):
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()

def eval_loop():
    model.eval()
    total_loss = 0
    correct_predictions = 0

    with torch.no_grad():
        for batch in tqdm(eval_dataloader):
            batch = {k: v.to(device) for k, v in batch.items()}
            outputs = model(**batch)
            loss = outputs.loss
            logits = outputs.logits
            total_loss += loss.item()

            predictions = torch.argmax(logits, dim=-1)
            correct_predictions += (predictions == batch["labels"]).sum().item()

    avg_loss = total_loss / len(eval_dataloader)
    accuracy = correct_predictions / len(eval_dataloader.dataset)
    return avg_loss, accuracy

3) 运行训练和评估

for epoch in range(num_epochs):
    print(f"Epoch {epoch + 1}/{num_epochs}")
    train_loop()
    avg_loss, accuracy = eval_loop()
    print(f"Validation Loss: {avg_loss:.4f}, Accuracy: {accuracy:.4f}")

总结

通过上述步骤,我们实现了使用 Hugging Face Transformers 库的自定义训练循环。这种方法提供了更大的灵活性,可以根据具体需求调整训练过程。无论是优化器、学习率调度器,还是其他训练策略,都可以根据需要进行定制。希望这篇文章能帮助你更好地理解和实现自定义训练循环,为你的 NLP 项目提供更强大的支持。

  • 32
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值