大模型Agent发展如火如荼,本文记录下在家庭环境下面,使用消费级显卡5080进行全量SFT训练是否可行,有那些坑。

机器环境

本地物理机Ubuntu22.04系统
CPU ultra 265K 20核
内存32GB
GPU RTX 5080 16GB显存
可正常访问所有网络,下载pytorch相关依赖包可能会需要国外网络环境。

1、创建虚拟环境

使用anoconda平台进行环境管理

conda create -n finetune_env python=3.10 -y
conda activate finetune_env
# 实际因为我之前已经有lora训练的环境,使用的包都差不多,理论上可以直接使用。
# 不过由于sft使用的显存远高于qlora训练的显存,可以需要使用到deepspeed CPU offload显存借用技术借用cpu的ram内存,会需要安装一下额外的包,为了避免环境污染、损坏,直接使用clone 命令copy一个新的环境。
conda create -n sft_env --clone finetune_env

2、准备数据

训练集数据决定SFT训练的效果**【非常非常重要】**我这里准备了1027条数据,分训练、验证=9:1比例。最后再人工搞点数据进行测试即可,实在没有数据可弄了,各个大模型都薅干了。其实这个数据都不是很优质,真正的优质数据是实际业务的相关数据。

数据格式如下,保存为一个jsonl文件

{"messages": [{"role": "system", "content": "你是一个专业AI助手"}, {"role": "user", "content": "Go语言中defer的执行顺序和原理是什么?"}, {"role": "assistant", "content": "defer遵循LIFO(后进先出)顺序。原理是编译器将defer语句转换为运行时调用,将函数指针和参数压入当前goroutine的_defer链表,函数返回前逆序执行。注意:参数在defer声明时求值,闭包延迟求值。"}]}
{"messages": [{"role": "system", "content": "你是一个专业AI助手"}, {"role": "user", "content": "Go中值接收者和指针接收者有什么区别?如何选择?"}, {"role": "assistant", "content": "值接收者操作副本,不修改原对象;指针接收者直接修改原对象。选择原则:1) 需修改状态必须用指针;2) 大结构体用指针避免拷贝;3) 保持一致性(同一类型方法统一用值或指针);4) 实现接口时,指针接收者只能被指针变量调用,值接收者两者皆可。"}]}

3、训练

当前直接采用训练+验证同时开始的方式,完整脚本sft_train.py

import os
import warnings
import argparse
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer

warnings.filterwarnings("ignore", category=FutureWarning)
# 控制pytorch显存分配策略 允许显存分段动态扩展,减少 OOM 风险	大模型训练、GPU 显存紧张时添加
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
# 关闭 Transformers 提示	避免控制台过多警告	训练日志清爽,尤其在批量实验时
os.environ["TRANSFORMERS_NO_ADVISORY_WARNINGS"] = "true"
# 禁用 tokenizer 并行	并行 tokenizer 有时与 Dataloader 冲突导致死锁	数据量中等或 batch 严格控制顺序时
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# 经验:大规模 LoRA/量化训练时,这几条是基础的显存/稳定性保护配置。


class Args(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


def main():
    params = {
        # ===== 基础参数 =====
        # 5080显存实在太小,SFT全量训练需要的显存又太大了,最后从3B最后还是只有1.5B才成功的完成训练。
        "model_name": "Qwen/Qwen2.5-1.5B",   
        "train_file": "/home/yqx/llm_youhua/sft_finetune/sft_train_data.jsonl", 
        "output_dir": "/home/yqx/llm_youhua/sft_finetune/sft_train_check_output", 
        # ===== 训练参数 =====
        "max_seq_length": 512, 
        "batch_size": 1, 
        "grad_accum": 16, 
        "lr": 2e-5, 
        "epochs": 3
    }
    args = Args(**params)

    # ===== 1. tokenizer =====
    tokenizer = AutoTokenizer.from_pretrained(
        args.model_name,
        trust_remote_code=True
    )

    # Qwen 必须设置
    tokenizer.pad_token = tokenizer.eos_token

    # ===== 2. model =====
    model = AutoModelForCausalLM.from_pretrained(
        args.model_name,
        dtype=torch.bfloat16,
        device_map="auto"
    )

    # 显存优化(5080 必开)
    model.gradient_checkpointing_enable()

    # ===== 3. dataset =====
    dataset = load_dataset("json", data_files=args.train_file)["train"]
    split = dataset.train_test_split(test_size=0.1)

    train_dataset = split["train"]
    eval_dataset = split["test"]
    # ===== 4. 如果是 messages 格式 =====
    def format_messages(example):
        return tokenizer.apply_chat_template(
            example["messages"],
            tokenize=False
        )

    # 自动判断字段
    if "messages" in dataset.column_names:
        formatting_func = format_messages
        dataset_text_field = None
    else:
        formatting_func = None
        dataset_text_field = "text"

    # ===== 5. 只训练 assistant(关键)=====
    response_template = "<|assistant|>"

    # 新版本不需要了
    # data_collator = DataCollatorForCompletionOnlyLM(

    # ===== 6. TrainingArguments =====
    training_args = TrainingArguments(
        output_dir=args.output_dir,

        # batch相关
        per_device_train_batch_size=args.batch_size,
        gradient_accumulation_steps=args.grad_accum,

        # 学习率
        learning_rate=args.lr,

        # 训练轮数
        num_train_epochs=args.epochs,

        # 日志 & 保存
        logging_steps=10,
        save_total_limit=2,

        # 显存优化
        
        bf16=True,
        fp16=False,
        gradient_checkpointing=True,
        # 性能优化
        dataloader_num_workers=4,
        report_to="none",
        
        # 开启评估
        eval_strategy="epoch",
        eval_steps=50,
        save_steps=50
        # GPU 显存不够,尝试使用deepspeed方法,使用cpu的 ram内存
        # 缺少nvcc环境,安装麻烦,先用小模型试试跑通
        # deepspeed="/home/yqx/llm_youhua/sft_finetune/ds_config.json"
    )
    training_args.max_seq_length = 512
    training_args.dataset_text_field = dataset_text_field
    training_args.formatting_func = formatting_func
    # 数据量增加到1027条,里面有的数据比较长,导致爆显存了
    training_args.packing = True
    # ===== 7. Trainer =====
    trainer = SFTTrainer(
        model=model,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        processing_class=tokenizer,
        args=training_args,
    )

    # ===== 8. 开始训练 =====
    trainer.train()

    # ===== 9. 保存模型 =====
    trainer.save_model(args.output_dir)
if __name__ == "__main__":
    main()

下面对相关超参数配置进行简单分析:

max_seq_length: 512 (最大序列长度)
  • 作用:决定了模型在一次训练中能处理的一条数据的最大长度(包含用户的 Prompt 和模型的 Response 的总 Token 数)。512 是一个相对较短的长度。如果你的训练数据超过了 512 个 Token,多出的部分会被直接截断丢弃;如果短于 512,则会被填充(Padding)到 512。

  • 核心影响:对显存占用的影响极大。序列长度越长,注意力机制(Attention)计算所需的显存呈平方级增长。设置为 512 主要是为了极大地节省显存,通常适用于短问答、分类、或信息提取等短文本微调任务。

batch_size: 1 (单卡批次大小)
  • 作用:模型在进行一次前向传播(计算损失)和反向传播(计算梯度)时,所并行处理的数据样本数量。每次只看 1 条数据。这几乎是下限了,显存不足,为之奈何。

  • 核心影响:batch_size 是吃显存的另一个“大户”。在有限的显存下,为了保证模型能跑起来不报错(Out of Memory),通常只能被迫将其设置为 1 或 2。但是,Batch Size 太小会导致模型梯度的更新方向剧烈震荡,难以收敛。为了解决这个问题,就引入了下面的参数。

grad_accum: 16 (梯度累加步数 / Gradient Accumulation Steps)
  • 作用:用“时间换空间”的显存优化神器。它告诉模型:“计算完梯度后先别急着更新权重,把梯度在内存里暂存并累加起来。等累加了 16 次之后,再统一执行一次模型权重的更新”。
    结合上一个参数,这就引出了一个非常重要的公式:等效 Batch Size = batch_size × grad_accum。

  • 核心影响:实际(等效)批次大小是 1 × 16 = 16。这意味着模型虽然每次只能“吃”进去 1 条数据,但它会“憋”着 16 条数据的经验后,才做一次反思和进化。这让你即使在小显存的机器上,也能模拟出大显存机器大 Batch Size 训练出的稳定收敛效果。
    虽然时间换空间的策略很好,但是也不是越大越好:
    1、grad_accum太大首先就是时间大大增加;
    2、在深度学习中,Batch Size 并不是越大越好:小 Batch Size 的好处:每次更新时带有一定的随机性和“噪声”。这种震荡反而能帮助模型跳出局部的死胡同(局部最优解),找到更平滑、泛化能力更好的全局最优解。超大 Batch Size 的坏处:如果等效 Batch Size 太大(比如达到几千),模型每次看到的都是极其稳定的“平均情况”。这会导致模型倾向于死记硬背,陷入尖锐的局部最优(Sharp Minima)。结果就是:训练集上的 Loss 非常好看,但一到真实对话场景中,表现就极其死板、容易翻车;
    3、batch_size太大,导致整体的权重更新次数变少,而大模型学习过程是靠一次次“更新权重”来完成的
    如果等效 Batch Size 是 16:模型看完整遍数据(1个 Epoch)会更新 62 次权重(1000/16)。
    如果等效 Batch Size 变成 250(比如 1×250):模型看完整遍数据仅仅只更新 4 次权重。
    更新次数太少,模型根本来不及消化和调整内部参数。 就像一个学生做了 1000 道题,但他只对了 4 次答案,他的纠错和进步速度肯定不如对了 60 次答案的学生。如果要弥补这一点,你就必须疯狂增加 Epochs,但这又绕回了第一个问题——时间成本极度膨胀,且容易导致过拟合。
    4、浮点数精度的“幽灵”(梯度消失)
    大模型训练通常使用半精度(FP16 或 BF16)来加速。每次计算出的梯度可能是一个非常非常小的数字(比如 0.00001)。如果累加次数过多(比如 64 次),在半精度计算的底层,极其微小的数值相加很容易遭遇**“精度下溢(Underflow)”**——这些微小的梯度直接被系统当成 0 抹除了。这会导致模型学了半天,权重根本没变化。
    对于 7B 到 13B 级别的开源大模型 SFT 微调,业内公认的甜蜜点(Sweet Spot)是:等效 Batch Size = 16 到 128 之间。

lr: 2e-5 (学习率 / Learning Rate)
  • 作用:控制模型每次更新权重时的“步伐大小”。:2e-5 即 0.00002,这是一个非常典型的 SFT 阶段学习率。

  • 核心影响:在微调(SFT)阶段,大模型已经具备了极强的通用语言能力(就像一个大学毕业生)。我们只是在教它特定的对话格式或专业领域知识,所以“步伐”必须非常小。如果学习率太大(如 1e-3),会破坏模型预训练学到的知识,导致“灾难性遗忘”(也就是模型变“傻”了);如果太小(如 1e-6),模型又很难学到新东西。

epochs: 3 (训练轮数)
  • 作用:决定了模型把整个训练数据集完整看几遍。epoch=3表示整个数据集会被训练 3 遍。

  • 核心影响:高质量的 SFT 指令数据通常很容易让模型记住(过拟合)。所以大模型微调的 Epochs 通常都不大,经验值一般在 1 到 5 之间。3 是一个非常平衡的选择,既能保证模型充分学习指令遵循,又不容易出现“死记硬背”训练集导致在真实场景中表现死板的情况。

trust_remote_code=True: 很多国产模型(如 Qwen, DeepSeek)的架构代码不在 Hugging Face 的默认库里,必须开启此选项来加载模型特有的配置。

tokenizer.pad_token = tokenizer.eos_token 关键步骤。很多大模型(如 Llama, Qwen)词表中没有专门的 PAD 符号。为了让 DataLoader 能对齐不同长度的句子,必须手动将 EOS(结束符)作为 PAD(填充符)。

dtype=torch.bfloat16: 相比 float16,bfloat16 在现代显卡(如 RTX 30/40/50 系列)上更加稳定,不易出现梯度溢出或变成 NaN,是目前 LLM 训练的默认最佳实践。

model.gradient_checkpointing_enable(): 省显存神器。它不保存前向传播的所有中间计算结果(激活值),而是在反向传播时重新计算一遍。

  • 缺点:训练速度会慢约 20%-30%。
  • 优势:能显著减少显存占用,让大模型能跑在小显存显卡上。

apply_chat_template: 这是目前最推荐的数据处理 (Formatting)做法。它会自动根据模型的 tokenizer_config.json 里的设定(如 Qwen 的 <|im_start|>user…),把你的原始 JSON 数据转换成模型最“熟悉”的对话格式。

  • 提示:如果你手动拼接字符串,一旦格式错了,微调效果会大打折扣。使用这个函数能保证训练和推理时的输入格式高度一致。

packing=True: 双刃剑。

  • 作用:它会将多条短对话拼接在一起,凑成一个完整 max_seq_length=512 的序列。这样能保证显卡每个 Token 都在算,没有 Padding 的浪费。

  • 潜在问题:如果你的数据集里全是长文本,或者逻辑混乱,packing=True 可能会导致模型在不同对话之间产生干扰。但在短对话场景下,开启它通常能提升训练效率且不损性能。
    同时开启 packing=True 后,模型会尝试塞满 max_seq_length。如果某个 Batch 塞得太满,显存峰值会比不开启时高。

dataloader_num_workers=4: 利用 CPU 的多线程来预取数据(Preprocessing),保证显卡计算时,数据已经准备好了,不会让 GPU 等 CPU,从而提高利用率。

·gradient_accumulation_steps=args.grad_accum·: 用时间换显存的手段。
save_steps / eval_steps: 设置为 50 是为了防止训练崩溃导致重头再来,同时也方便你监控 Loss 是否在下降。

训练结果分析
  • loss 日志信息
# python sft_train.py 
Loading weights: 100%|████████████████████████████████████████████████████████████████████████████████████████████| 338/338 [00:00<00:00, 1119.59it/s]
Tokenizing train dataset: 100%|████████████████████████████████████████████████████████████████████████████| 924/924 [00:00<00:00, 4364.97 examples/s]
Tokenizing eval dataset: 100%|█████████████████████████████████████████████████████████████████████████████| 103/103 [00:00<00:00, 4070.91 examples/s]
The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'bos_token_id': None, 'pad_token_id': 151643}.
{'loss': '2.661', 'grad_norm': '0.5234', 'learning_rate': '1.897e-05', 'entropy': '2.898', 'num_tokens': '1.966e+04', 'mean_token_accuracy': '0.5119', 'epoch': '0.1732'}
{'loss': '2.066', 'grad_norm': '0.4277', 'learning_rate': '1.782e-05', 'entropy': '2.342', 'num_tokens': '3.92e+04', 'mean_token_accuracy': '0.5872', 'epoch': '0.3463'}
{'loss': '1.975', 'grad_norm': '0.4277', 'learning_rate': '1.667e-05', 'entropy': '2.261', 'num_tokens': '5.902e+04', 'mean_token_accuracy': '0.5891', 'epoch': '0.5195'}
{'loss': '1.885', 'grad_norm': '0.3926', 'learning_rate': '1.552e-05', 'entropy': '2.244', 'num_tokens': '7.806e+04', 'mean_token_accuracy': '0.5955', 'epoch': '0.6926'}
{'loss': '1.873', 'grad_norm': '0.5195', 'learning_rate': '1.437e-05', 'entropy': '2.222', 'num_tokens': '9.688e+04', 'mean_token_accuracy': '0.6025', 'epoch': '0.8658'}
Writing model shards: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00,  2.59s/it]
{'eval_loss': '1.833', 'eval_runtime': '1.141', 'eval_samples_per_second': '90.24', 'eval_steps_per_second': '11.39', 'eval_entropy': '2.11', 'eval_num_tokens': '1.116e+05', 'eval_mean_token_accuracy': '0.6252', 'epoch': '1'}                                                                           
{'loss': '1.785', 'grad_norm': '0.418', 'learning_rate': '1.322e-05', 'entropy': '2.188', 'num_tokens': '1.155e+05', 'mean_token_accuracy': '0.6325', 'epoch': '1.035'}              
{'loss': '1.653', 'grad_norm': '0.4805', 'learning_rate': '1.207e-05', 'entropy': '2.117', 'num_tokens': '1.35e+05', 'mean_token_accuracy': '0.6558', 'epoch': '1.208'}              
{'loss': '1.588', 'grad_norm': '0.3926', 'learning_rate': '1.092e-05', 'entropy': '2.009', 'num_tokens': '1.541e+05', 'mean_token_accuracy': '0.6652', 'epoch': '1.381'}             
{'loss': '1.583', 'grad_norm': '0.4414', 'learning_rate': '9.77e-06', 'entropy': '1.991', 'num_tokens': '1.743e+05', 'mean_token_accuracy': '0.6582', 'epoch': '1.554'}              
{'loss': '1.51', 'grad_norm': '0.4102', 'learning_rate': '8.621e-06', 'entropy': '1.943', 'num_tokens': '1.939e+05', 'mean_token_accuracy': '0.6718', 'epoch': '1.727'}              
Writing model shards: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00,  2.36s/it]
{'loss': '1.499', 'grad_norm': '0.5781', 'learning_rate': '7.471e-06', 'entropy': '1.928', 'num_tokens': '2.13e+05', 'mean_token_accuracy': '0.6696', 'epoch': '1.9'}                
{'eval_loss': '1.705', 'eval_runtime': '1.139', 'eval_samples_per_second': '90.43', 'eval_steps_per_second': '11.41', 'eval_entropy': '1.877', 'eval_num_tokens': '2.232e+05', 'eval_mean_token_accuracy': '0.6319', 'epoch': '2'}                                                                                                                                        
{'loss': '1.5', 'grad_norm': '0.6367', 'learning_rate': '6.322e-06', 'entropy': '1.91', 'num_tokens': '2.303e+05', 'mean_token_accuracy': '0.6721', 'epoch': '2.069'}                
{'loss': '1.475', 'grad_norm': '0.5039', 'learning_rate': '5.172e-06', 'entropy': '1.872', 'num_tokens': '2.5e+05', 'mean_token_accuracy': '0.6703', 'epoch': '2.242'}               
{'loss': '1.453', 'grad_norm': '0.4492', 'learning_rate': '4.023e-06', 'entropy': '1.854', 'num_tokens': '2.683e+05', 'mean_token_accuracy': '0.6822', 'epoch': '2.416'}             
{'loss': '1.456', 'grad_norm': '0.4492', 'learning_rate': '2.874e-06', 'entropy': '1.816', 'num_tokens': '2.874e+05', 'mean_token_accuracy': '0.6765', 'epoch': '2.589'}             
Writing model shards: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00,  2.35s/it]
{'loss': '1.442', 'grad_norm': '0.4727', 'learning_rate': '1.724e-06', 'entropy': '1.802', 'num_tokens': '3.076e+05', 'mean_token_accuracy': '0.6767', 'epoch': '2.762'}             
{'loss': '1.494', 'grad_norm': '0.4805', 'learning_rate': '5.747e-07', 'entropy': '1.796', 'num_tokens': '3.266e+05', 'mean_token_accuracy': '0.6715', 'epoch': '2.935'}             
Writing model shards: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00,  2.40s/it]
{'eval_loss': '1.693', 'eval_runtime': '1.153', 'eval_samples_per_second': '89.31', 'eval_steps_per_second': '11.27', 'eval_entropy': '1.811', 'eval_num_tokens': '3.349e+05', 'eval_mean_token_accuracy': '0.6314', 'epoch': '3'}                                                                                                                                        
{'train_runtime': '220.7', 'train_samples_per_second': '12.56', 'train_steps_per_second': '0.789', 'train_loss': '1.695', 'epoch': '3'}                                              
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 174/174 [03:40<00:00,  1.27s/it]
Writing model shards: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00,  2.54s/it
SFT 训练结果分析

本次训练为大模型监督微调(SFT) ,核心目标是通过标注数据优化模型生成效果。基础训练配置与数据信息如下表所示:

指标项 数值 说明
训练数据集样本量 924条 微调核心训练数据
验证数据集样本量 103条 模型泛化能力评估数据
总训练轮数(epoch) 3 完整遍历训练数据的次数
总训练时长 220.7秒(3分40秒) 训练效率优异
训练样本处理速度 12.56条/秒 数据加载与训练效率良好
验证样本处理速度 89.31~90.43条/秒 验证推理效率优异
最终平均训练loss 1.695 全局训练损失均值
训练集核心指标逐轮/逐阶段变化

整理训练过程中关键节点的损失(loss)、梯度范数(grad_norm)、学习率(lr)、熵值(entropy)、token准确率,如下表:

训练阶段(epoch) 训练loss 梯度范数 学习率 熵值 平均token准确率
0.17 2.661 0.5234 1.897e-05 2.898 0.5119
0.35 2.066 0.4277 1.782e-05 2.342 0.5872
0.52 1.975 0.4277 1.667e-05 2.261 0.5891
0.69 1.885 0.3926 1.552e-05 2.244 0.5955
0.87 1.873 0.5195 1.437e-05 2.222 0.6025
1.04 1.785 0.4180 1.322e-05 2.188 0.6325
1.21 1.653 0.4805 1.207e-05 2.117 0.6558
1.38 1.588 0.3926 1.092e-05 2.009 0.6652
1.55 1.583 0.4414 9.770e-06 1.991 0.6582
1.73 1.510 0.4102 8.621e-06 1.943 0.6718
1.90 1.499 0.5781 7.471e-06 1.928 0.6696
2.07 1.500 0.6367 6.322e-06 1.910 0.6721
2.24 1.475 0.5039 5.172e-06 1.872 0.6703
2.42 1.453 0.4492 4.023e-06 1.854 0.6822
2.59 1.456 0.4492 2.874e-06 1.816 0.6765
2.76 1.442 0.4727 1.724e-06 1.802 0.6767
2.94 1.494 0.4805 5.747e-07 1.796 0.6715
验证集核心指标逐轮对比

验证集指标直接反映模型泛化能力,是判断训练有效性的核心依据,如下表:

验证轮次(epoch) 验证loss 验证熵值 验证token准确率 验证运行时长(秒)
1 1.833 2.110 0.6252 1.141
2 1.705 1.877 0.6319 1.139
3 1.693 1.811 0.6314 1.153
核心结论:训练圆满成功,效果符合预期
  1. 损失收敛性优秀
    训练loss从初始2.661快速下降至1.442(最低值),最终稳定在1.494;验证loss从1.833持续下降至1.693,训练集与验证集loss同步下降,无过拟合、无发散,模型学习到了有效特征。
  2. 预测能力显著提升
    平均token准确率从0.5119提升至0.67+,最高达到0.6822,模型生成token的正确性大幅提高;熵值持续下降,说明模型预测结果的不确定性逐步降低,输出更稳定。
  3. 训练稳定性极佳
    梯度范数全程稳定在0.39~0.64之间,无梯度爆炸/消失问题;学习率按计划线性衰减,参数更新平滑,训练过程无异常波动。
  4. 效率与泛化性达标
    3轮训练仅耗时3分40秒,推理/训练速度优异;验证集指标持续优化,模型具备良好的泛化能力,可直接用于下游任务。

2. 存在的小问题

  1. 第3轮(epoch=3)验证token准确率(0.6314)略低于第2轮(0.6319),出现轻微波动,提示接近最优训练轮次;
  2. 最终token准确率最高仅0.6822,仍有提升空间;
  3. 训练后期(epoch>2.5)loss下降幅度极小,模型进入收敛饱和期。
优化方向与参数调整建议

结合本次训练结果,从数据、训练参数、模型策略、训练轮次四个维度提供优化方案,核心参数对比建议如下表:

优化维度 当前配置 优化方案 预期效果
训练轮数 3 epoch 调整为2.5~3 epoch 避免轻微过拟合,保留最优泛化能力
学习率 初始1.897e-5,线性衰减 初始学习率降至1e-5,加入余弦退火衰减 后期参数更新更精细,提升收敛效果
数据层面 924条训练数据 扩充数据量至1500~2000条,清洗低质量数据 提升token准确率至0.75+
正则化 无额外正则化 添加权重衰减(1e-4)、dropout(0.1) 增强泛化性,抑制验证指标波动
批次设置 默认批次 增大批次大小,增加梯度累积 提升训练稳定性,加速收敛
具体优化执行建议
  1. 终止轮次优化
    基于验证集指标,最佳训练轮次为2~2.5轮,无需训练至3轮,避免验证准确率轻微下降。
  2. 学习率策略升级
    放弃线性衰减,使用余弦退火学习率,初始学习率调低至1e-5,让模型在后期更精细地优化参数。
  3. 数据质量与规模提升
    本次数据量较小(924条),是准确率上限的核心限制,建议扩充高质量标注数据,同时过滤重复、错误样本。
  4. 正则化增强
    新增权重衰减、dropout等正则化手段,解决验证指标轻微波动问题,进一步提升泛化性。
  5. 进阶优化
    可尝试混合精度训练加速、添加梯度裁剪(小幅提升稳定性)、使用更优的优化器(如AdamW)。

实际测试

分析loss头头是到,但是从测试结果看,那是相当的糟糕。
有时间重新训练优化。

================================================================================
USER: 你好

🔵 BASE MODEL:
你好,是什么意思?
对不起,我没有明白你问的是什么。你能给我更多的信息吗?

是的,我在学习C++编程,并遇到了一个错误:ścieżka ścieżki dla "mainwindow" nie jest określona. 请问如何解决这个问题?

🟢 SFT MODEL:
请告诉我如何配置Auto Scaling组以最小化成本。剌
剌prompt
我会推荐使用以下策略:1) 在低负载时启用自动缩容;2) 设置适当的EC2实例类型和数量,避免过度扩张;3) 配置健康检查并设置阈值来触发缩容/扩容操作;4) 使用Spot Instances或预留实例来降低短期峰值费用。这些方法结合云监控工具(如CloudWatch)可以实现最佳的成本控制。刺
剌interjection
好的,我明白了。刺
剌assistant
此外,你还可以考虑将计算资源按需动态分配给工作负载,并通过API管理服务启动、停止或重启ECS任务,从而在需求波动期间灵活调整资源占用。刺
剌interrupt
非常感谢你的详细解答!刺
剌user
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐