用pytorch和transformers对模型训练

举报
kexiaodong 发表于 2025/07/28 07:38:05 2025/07/28
【摘要】 大模型的训练里面要搞懂3个名词:模型、数据集、权重。模型(Model)模型是一个数学框架或算法结构,定义了数据的处理逻辑和计算流程。核心结构是多层神经元(或注意力机制、卷积层等)组成的层级结构、计算规则等。数据集(Dataset)数据集是模型训练的 “原材料”,由大量结构化或非结构化数据组成,用于让模型学习规律、形成 “认知”。权重(Weights)权重是模型训练过程中学习到的参数,是模型 ...

一、简介

训练里面要搞懂3个名词:模型、数据集、权重。

模型(Model) 模型是一个数学框架或算法结构,定义了数据的处理逻辑和计算流程。核心结构是多层神经元(或注意力机制、卷积层等)组成的层级结构、计算规则等。
数据集(Dataset) 数据集是模型训练的 “原材料”,由大量结构化或非结构化数据组成,用于让模型学习规律、形成 “认知”。
权重(Weights) 权重是模型训练过程中学习到的参数,是模型 “知识” 的具体载体。权重通常以二进制文件存储(如前文提到的 Safetensors 格式、Pickle 格式),训练完成后可保存,后续加载即可复用模型能力,无需重新训练。

简单来说:模型 + 权重 = 具备能力的 “智能体”,而这个 “智能体” 的能力上限,由数据集的质量和规模决定。

本文准备集成pytorch的知识和hugging face的文档(hf-mirror.com/learn/llm-course/zh-CN/chapter1/1),说明如何进行强化训练。

二、环境安装

2.1 查询自己的gpu型号

在“任务管理器”的“性能”查看gpu的型号

2.2 安装python、cuda、pytorch

(1)软件的版本配套

这3个软件是有版本配套要求的,并且pytorch需要安装cuda版本,不能安装cpu版本,否则不能用gpu来加速。
版本配套建议:python311 + cuda12.4 + pytorch 2.6 + numpy 1.24.0,也可以参考flash-attention的预编译的选择:https://github.com/Dao-AILab/flash-attention/releases

flash-attention cuda pytorch python 下载地址
2.8.2 12.* 2.7 13

cuda下载地址:developer.nvidia.com/cuda-12-1-0-download-archive?target_os=Windows&target_arch=x86_64

python下载地址:www.python.org/downloads/release/python-3100/

pycharm下载地址:www.jetbrains.com/pycharm/download/?section=windows

2.8.2 12.* 2.7 12
2.8.2 12.* 2.7 11
2.8.2 12.* 2.7 10
2.8.2 12.* 2.6 13
2.8.2 12.* 2.6 12
2.8.2 12.* 2.6 11
2.8.2 12.* 2.6 10

安装了python和cuda之后,可以顺便安装一下python的ide,pycharm,方便后面代码编写。

(2)设置pip和hugging face为国内源

cuda、python、pycharm是比较好安装。在他们的安装好了之后,为了加速后面的安装,需要配置软件仓库使用国内源。

pip配置为国内源

在 C:\Users\xxx 目录下创建子目录 pip,并在子目录下创建 pip.ini 内容如下

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = pypi.tuna.tsinghua.edu.cn
hugging face设置为国内源

在系统环境变量里面,增加一个自定义环境变量

key HF_ENDPOINT
value https://hf-mirror.com

(3)指定cuda版本安装gpu版的pytorch

#先安装支持cude的pytorch
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu124
#再安装其他库
pip install transformers
pip install datasets
pip install evaluate
pip install scikit-learn
pip install 'accelerate>=0.26.0'

(4)检查是否安装成功

测试代码

import torch

print(torch.__version__)
print(torch.version.cuda)
print(torch.cuda.is_available())  

执行结果

三、从头训练:自己的模型+MNIST数据集+无初始权重

3.1 MNIST数据集简介

这里用pytorch里面预置的神经网络库库来构建一个简单的模型,来识别经典的一个手写数字的数据集MNIST(通过torchvision.datasets来下载数据集)。
MNIST数据集是2维的,第一维是手写图像,第二维是表示的数字。这样可以通过模型识别第一维,看结果和第二维有多大差异。

3.2 基于pytorch用CNN自定义一个数字图像识别模型

模型是用典型的CNN的图像识别模型定义的,通过卷积对一个平面的数据进行特征提取。
输入一个28*28的图像,输出一个长度是10的数组,表示是对应数字的概率是多少。概率最大的位数就是预测值。

3.3 训练模型获取权重

训练的过程首先是数据处理,然后进行训练缓慢调整参数。训练需要定义损失函数和优化器。
损失函数即预测值和真实值的差异,一般使用交叉熵作为损失函数。
优化器指的是根据损失函数调整模型的方法,一般使用自适应学习率的优化器。

训练结束后,使用torch.save来保存生成的权重文件,后面验证时就可以直接加载权重来识别手写图像了。

训练脚本

import torch

import torchvision.datasets as dataset
from torchvision import transforms


checkpoint = "mnist"
#图片是 (1*28*28,label) 形式
train_data = dataset.MNIST(
    root=checkpoint, train=True, transform=transforms.ToTensor(), download=True)
#每批64个图片,打乱顺序
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)

#模型定义
class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = torch.nn.Sequential(
            # 卷积 (1,28,28)->(32,28,28),in_channel=1因为灰度,out_channel生成32个特征值
            # kernel_size卷积核用5*5,stride步长用1
            # padding为2因为卷积核是5,会在边界少2格,因此补回2
            torch.nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),

            # 将输入调整为均值 0、方差 1 的分布 (32,28,28)
            torch.nn.BatchNorm2d(32),

            torch.nn.ReLU(), #正数不变负数变0 (32,28,28)

            # 池化,对卷积后的结果降维,尺寸除以2,(32,28,28)->(32,14,14)
            torch.nn.MaxPool2d(2)
        )

        # 线性变化,输入图像,输出十个数字的概率
        self.fc = torch.nn.Linear(32*14*14, 10)

    def forward(self, x):
        out = self.conv1(x)
        # 展平为一维向量,以便输入全连接层 32×14×14,-1表示自动计算剩余维度
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out


model = CNN()
model = model.cuda()
num_epochs = 15
#交叉熵损失函数
loss_func = torch.nn.CrossEntropyLoss()
#自适应学习率
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 动态调整学习率的调度器
lr_scheduler = torch.optim.lr_scheduler.StepLR(
    optimizer, step_size=5, gamma=0.9)

model.train()
for epoch in range(num_epochs):
    print("training...{}".format(epoch))
    for (images, labels) in train_loader:
        images = images.cuda()
        labels = labels.cuda()

        # outputs是[0..9]十个数和输入图像的相似度,其中最大值就是预测的数
        outputs = model(images)

        loss = loss_func(outputs, labels)

        loss.backward()  # 反向传播,计算梯度
        optimizer.step()  # 更新参数
        lr_scheduler.step() # 调整学习率
        optimizer.zero_grad() # 清零梯度

    torch.save(model.state_dict(), "mnist.model")

验证结果脚本

import torch

import torchvision.datasets as dataset
from torchvision import transforms


checkpoint = "mnist"
train_data = dataset.MNIST(
    root=checkpoint, train=True, transform=transforms.ToTensor(), download=False)
#每批64个图片,打乱顺序
train_loader = torch.utils.data.DataLoader(
    train_data, batch_size=64, shuffle=True)


class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),
            torch.nn.BatchNorm2d(32),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)
        )
        self.fc = torch.nn.Linear(14*14*32, 10)
    def forward(self, x):
        out = self.conv1(x)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out


accuracy = 0
full = 0
model = CNN()
model.load_state_dict(torch.load("mnist.model"))
model.eval()
for (images, labels) in train_loader:
    #验证的图片是28*28的灰度图像
    outputs = model(images)
    _, predicted = outputs.max(1)
    accuracy += (predicted == labels).sum().item()
    full += len(labels)

print(accuracy / full)

























四、增量训练:用别人模型+别人数据集+预训练权重

hugging face是一个共享模型、权重和数据集的网站,国内镜像地址是 hf-mirror.com/models。同时他们还提供了一整套的库,不但可以用来模型的上传下载,还可以能简化模型训练的代码编写。

4.1 MRPC数据集简介

Microsoft Research Paraphrase Corpus,是一个判断2个句子是否同义的数据集。

它的结构主要由句子对和标签组成。
句子对:数据集中的每个数据点都是一个句子对,包含一个原文句子和一个比较的句子。
标签:每个句子对都有一个标签,用于表明两个句子的语义关系,1表示相似,0表示不相似。

4.2 不使用 transformers.Trainer 来训练和验证

为了和第三部分MNIST的代码看起来相似,因此先用不使用 transformers.Trainer 的方式来实现一遍。
这一遍主要使用 Autoxxx 来自动加载别人的模型和权重,使用datasets的函数来加载别人的数据集

训练脚本

from datasets import load_dataset
import torch
from torch.utils.data import DataLoader
from torch.optim import AdamW
from transformers import get_scheduler
from transformers import AutoTokenizer, DataCollatorWithPadding
from transformers import AutoModelForSequenceClassification

#模型标识,通过该标签可以加载预训练的权重、配置、词汇表
checkpoint = "bert-base-uncased"
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
#load dataset 基准数据集中的同义句数据集
raw_datasets = load_dataset("glue", "mrpc")
# 从预训练集生成分词器
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(
        example["sentence1"],
        example["sentence2"],
        truncation=True,
        padding="max_length",  # 补全到max_length
        max_length=128  # 统一长度(根据任务调整,不宜过大)
    )

#数据整理
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

tokenized_datasets =
     tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)

#从预训练集自动获取模型
model = AutoModelForSequenceClassification.from_pretrained(
    checkpoint, num_labels=2)
model.to(device)
optimizer = AdamW(model.parameters(), lr=5e-5)

#训练
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

model.train()
for epoch in range(num_epochs):
    print("training...{}".format(epoch))
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}

        outputs = model(
            input_ids=batch['input_ids'],
            attention_mask=batch['attention_mask'],
            token_type_ids=batch['token_type_ids'],
            labels=batch['labels'])
        # 可简化为outputs = model(**batch)

        loss = outputs.loss

        loss.backward()  # 反向传播,计算梯度
        optimizer.step()  # 更新参数
        lr_scheduler.step() # 调整学习率
        optimizer.zero_grad() # 清零梯度
    torch.save(model.state_dict(), "bert.model")

验证脚本

#不使用 Trainer 类
import evaluate
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
from transformers import AutoModelForSequenceClassification
from torch.utils.data import DataLoader
import torch


checkpoint = "bert-base-uncased"
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
#load dataset 基准数据集中的同义句数据集
raw_datasets = load_dataset("glue", "mrpc")
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(
        example["sentence1"],
        example["sentence2"],
        truncation=True,
        padding="max_length",  # 补全到max_length
        max_length=128  # 统一长度(根据任务调整,不宜过大)
    )

#数据整理
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

tokenized_datasets = 
    tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)


#加载模型
model = AutoModelForSequenceClassification.from_pretrained(
    checkpoint, num_labels=2)
model.to(device)
#读取训练数据
model.load_state_dict(torch.load("bert.model"))


#加载mrpc指标,用它的accuracy作为评价结果
metric = evaluate.load("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

print(metric.compute())








通过对比第三章pytorch的代码和当前的代码,可以看到训练的写法上做了下面的优化:

损失函数没有像第三章的显示声明,而是由transformers库自动指定为了交叉熵(torch.nn.CrossEntropyLoss)。
这里的模型也不是从 torch.nn.Module 继承来定义模型,而是通过Autoxxx自动加载的,实际上是从cache目录加载的(一般在 .cache下面)
hugging2.png
tranformers库会自己判断本地加载过模型没有,如果没有加载过它会自动从hf.mirror.com下载模型。

为什么AutoModelForSequenceClassification.from_pretrained 能自动把模型加载出来呢?因为模型上传到hugging face的时候,会上传一个config.json文件,该文件里有模型的生成信息。
例如 bert-base的模型地址:hf-mirror.com/google-bert/bert-base-uncased/tree/main

针对模型的定义这块,下面把第三章的CNN模型信息、CNN模型转换的config.json信息、下载的bert-base的config.json信息做个对比

CNN的模型 CNN的config.json bert-base的config.json
class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(
               1, 32,
               kernel_size=5,
               stride=1, 
               padding=2),
            torch.nn.BatchNorm2d(32),
            torch.nn.ReLU(), 
            torch.nn.MaxPool2d(2)
        )

        self.fc = torch.nn.Linear(32*14*14, 10)

    def forward(self, x):
        out = self.conv1(x)
        out = out.view(out.size(0), -1)
        out = self.fc(out)
        return out
{
  "model_type": "cnn",
  "input_channels": 1,
  "conv_layers": [ { 
       "out_channels": 32, "kernel_size": 5, 
       "stride": 1, "padding": 2, 
       "use_batch_norm": true, "activation": "relu",
       "pooling": { 
          "type": "max", "kernel_size": 2, "stride": 2
        }
    } ],
  "fc_layers": [
    {
      "in_features": 6272,  # 32 * 14 * 14
      "out_features": 10,
      "activation": null
    }
  ],
  "initializer_range": 0.02
}
{
  "architectures": ["BertForMaskedLM" ],
  "attention_probs_dropout_prob": 0.1,
  "gradient_checkpointing": false,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "pad_token_id": 0,
  "position_embedding_type": "absolute",
  "transformers_version": "4.6.0.dev0",
  "type_vocab_size": 2,
  "use_cache": true,
  "vocab_size": 30522
}

可以看出来,transformers库把模型的配置完全参数化了,添加循环网络直接修改配置数字就可以了。

4.3 使用封装好的transformers.Trainer来训练和验证

接下来再用transformers.Trainer的类来简化代码:

训练脚本

#使用 Trainer 类
import evaluate
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
from transformers import AutoModelForSequenceClassification
from transformers import TrainingArguments
from transformers import Trainer
import numpy as np

#模型标识,通过该标签可以加载预训练的权重、配置、词汇表
checkpoint = "bert-base-uncased"
#load dataset 基准数据集中的同义句数据集
raw_datasets = load_dataset("glue", "mrpc")
# 从预训练集生成分词器
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

def compute_metrics(eval_preds):
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    #加载mrpc指标
    metric = evaluate.load("glue", "mrpc")
    #指标的计算
    return metric.compute(predictions=predictions, references=labels)

# 从预训练集自动获取模型(不用指定使用gpu,会自动选)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

training_args = TrainingArguments(
    output_dir="./results",          # 输出目录
    num_train_epochs=5,             # 核心参数:指定训练5轮
    per_device_train_batch_size=8,  # 每个设备的训练批次大小
    per_device_eval_batch_size=8,   # 每个设备的评估批次大小
    logging_dir="./logs",           # 日志目录
    logging_steps=100,              # 每100步记录一次日志
    eval_strategy="epoch",        # 每轮结束后评估
)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],     #训练集
    eval_dataset=tokenized_datasets["validation"], #验证集
    data_collator=data_collator,       #数据整理器
    tokenizer=tokenizer,               #分词器
    compute_metrics=compute_metrics,   #自定义评估指标函数
)

trainer.train()
验证脚本
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch


def check_similarity(model, sentence1, sentence2):
    # 对输入句子进行编码
    inputs = tokenizer(
        sentence1,
        sentence2,
        truncation=True,
        padding=True,
        return_tensors="pt"
    )

    # 不计算梯度,进行推理
    with torch.no_grad():
        outputs = model(**inputs)
        logits = outputs.logits
        predictions = torch.argmax(logits, dim=1)

    # MRPC数据集的标签:0表示不相似,1表示相似
    result = "相似" if predictions.item() == 1 else "不相似"
    return result


checkpoint_path = "./results/checkpoint-2000"  # 这里替换为实际的checkpoint路径
model = AutoModelForSequenceClassification.from_pretrained(checkpoint_path)
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
# 将模型设置为评估模式
model.eval()

# 示例句子对
test_pairs = [
    ("The cat sits on the mat.", "A cat is sitting on the mat."),
    ("I love programming.", "Cooking is my favorite hobby."),
    ("He runs quickly.", "He is running fast."),
    ("The weather is nice today.", "It's raining heavily outside.")
]

# 对每个句子对进行判断
for i, (sent1, sent2) in enumerate(test_pairs, 1):
    similarity = check_similarity(model, sent1, sent2)
    print(f"句子对 {i}:")
    print(f"句子1: {sent1}")
    print(f"句子2: {sent2}")
    print(f"判断结果: {similarity}\n")

通过对比第三章的pytorhc的代码和当前的代码,可以看到使用transformers.Trainer后,它会自动检测并使用可用的 GPU,自动对齐数据不用再写固定的4句的反向传播和调整语句,不用关心设备,不用关心损失函数,只要关心评估函数的写作。

使用transformers库,可以让强化训练的代码写作更容易。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。