ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零开始训练大模型教程:用TaoToken统一Key跑通数据准备到LoRA微调全流程

从零开始训练大模型教程:用TaoToken统一Key跑通数据准备到LoRA微调全流程 1. 个人开发者从零训练大模型到底难在哪环境、数据与调用链路很多人第一次听到“从零开始训练大模型”脑子里浮现的是几千张 A100 和几百万美元预算。其实对个人开发者来说真正要跑通的不是 GPT-4 级别的预训练而是一个可对话的小参数模型参数量在 0.5B 到 1.5B 之间用 LoRA 在单张消费级显卡上做指令微调最后能正常回答中文问题。这条路是真实可行的我自己在一张 24G 显存的卡上跑通过完整链路。难点不在“训练”这个动作本身而在链路太长。你要依次解决Python 与 CUDA 环境、数据清洗与格式统一、Tokenizer 是否要扩词表、基座模型选哪个、LoRA 的 target_modules 怎么配、训练完怎么合并权重、推理时怎么验证效果。任何一环出错报错信息都可能把你卡半天。另一个被低估的坑是模型调用管理。训练阶段你可能要调用大模型做数据蒸馏让强模型生成指令数据推理阶段又要调用模型做效果对比。如果每个环节都单独申请 Key、单独记 Base URL配置会散落在十几个脚本里换一次环境就要重新对一遍。我试过用统一 Key 的方式把训练和推理阶段的调用收敛到一处配置量明显下降。这篇文章面向的是有 Python 基础、想亲手跑通第一个可对话模型的开发者。我会按“环境搭建 → 数据准备 → Tokenizer 处理 → LoRA 微调 → 推理验证”的顺序给出可复制的配置和命令每一步都说明预期结果。你不需要多卡集群一张 16G 以上显存的卡就能跟着做。需要先明确一个概念本文说的“从零训练”指的是从开源基座出发做指令微调不是从随机初始化开始预训练。后者对个人开发者不现实也没必要。我们要的是让一个已经懂语言的基础模型学会按你的数据风格回答问题。2. TaoToken 统一 Key 在训练链路里的定位与前置准备在整条链路里TaoToken 扮演的是统一模型调用通道的角色。它不参与梯度计算也不替代你的训练框架而是把“调用模型”这件事标准化一个 API Key、一个 Base URL就能在数据蒸馏、效果对比、推理验证这些环节调用不同模型。官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。为什么训练流程里需要它举两个真实场景。第一做数据蒸馏时你需要一个强模型根据种子指令生成问答对这一步是纯 API 调用。第二LoRA 训练完之后你要对比微调前后模型的回答质量如果每次都手动切换不同的 Key 和地址脚本会变得很难维护。用统一 Key 之后这些调用都走同一套配置。前置准备分三块。第一块是本地环境Python 3.10、PyTorch 2.1、CUDA 12.1以及 transformers、peft、datasets、accelerate 这几个库。第二块是模型调用凭证去控制台创建一个 API Key地址在 https://taotoken.net/console Key 管理页面是 https://taotoken.net/api-keys 。第三块是基座模型权重建议从 Qwen2.5-0.5B-Instruct 或 Qwen2.5-1.5B-Instruct 起步中文支持好显存占用低。安装依赖的命令如下建议放在独立虚拟环境里python -m venv llm_train source llm_train/bin/activate pip install torch2.1.2 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.44.0 peft0.12.0 datasets2.20.0 accelerate0.33.0 pip install openai1.40.0这里装 openai 库是为了调用统一 API 做数据蒸馏和效果对比不是用来训练的。版本号建议锁死transformers 和 peft 的版本兼容性比较敏感跨版本容易出现target_modules找不到的问题。配置凭证时不要把 Key 硬编码进脚本。用环境变量的方式export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在 Python 里这样读取import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], )这样配置的好处是训练脚本、数据生成脚本、评测脚本共用同一份凭证换机器时只需要重新导出环境变量。如果你打算长期做编码类 Agent 或反复微调可以了解下 Coding Plan 的用法地址是 https://taotoken.net/coding-plan 适合调用频率较高的场景。3. 可复制的数据准备与 LoRA 微调配置这一节是全文的核心给出可以直接复制运行的配置。先讲数据格式再讲 Tokenizer 处理最后是 LoRA 训练配置。数据格式统一用 Alpaca 风格的三字段结构这是目前兼容性最好的格式{ instruction: 把下面这句话翻译成英文, input: 今天天气很好, output: The weather is nice today. }如果你的任务不需要额外输入input留空字符串即可。数据清洗要做三件事去掉 HTML 标签和乱码字符、过滤掉 output 长度小于 5 个字符的样本、去重。清洗脚本示例import json import re def clean_text(text): text re.sub(r[^], , text) text re.sub(r\s, , text).strip() return text def load_and_clean(path): samples [] seen set() with open(path, r, encodingutf-8) as f: for line in f: item json.loads(line) inst clean_text(item.get(instruction, )) inp clean_text(item.get(input, )) out clean_text(item.get(output, )) if len(out) 5: continue key inst inp if key in seen: continue seen.add(key) samples.append({instruction: inst, input: inp, output: out}) return samplesTokenizer 环节个人开发者不建议扩词表。扩词表意味着新增 embedding 需要重新训练反而增加难度。Qwen2.5 系列的中文 tokenizer 已经足够好直接用原词表即可。如果你用的是中文支持较弱的基座再考虑扩词表但那属于进阶操作。数据转成训练格式时用 chat template 拼接from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(Qwen/Qwen2.5-0.5B-Instruct) def format_sample(sample): messages [ {role: user, content: sample[instruction] \n sample[input]}, {role: assistant, content: sample[output]}, ] return tokenizer.apply_chat_template(messages, tokenizeFalse) texts [format_sample(s) for s in samples]LoRA 训练配置用 peft 的 LoraConfig关键参数如下from peft import LoraConfig, get_peft_model, TaskType lora_config LoraConfig( task_typeTaskType.CAUSAL_LM, r8, lora_alpha16, lora_dropout0.05, target_modules[q_proj, k_proj, v_proj, o_proj], biasnone, )r8是秩个人实验够用target_modules要和你基座模型的注意力层命名一致Qwen2.5 用上面这四个。如果报Target modules not found先用print(model)看一下层名再改。训练超参用 TrainingArguments 配置from transformers import TrainingArguments training_args TrainingArguments( output_dir./lora_out, per_device_train_batch_size2, gradient_accumulation_steps8, num_train_epochs3, learning_rate2e-4, logging_steps10, save_steps200, fp16True, warmup_ratio0.03, lr_scheduler_typecosine, )per_device_train_batch_size2配合gradient_accumulation_steps8等效 batch size 是 1616G 显存能跑 0.5B 模型。如果显存不够把 batch size 降到 1梯度累积加到 16。4. 启动训练与推理验证的完整动作配置写好后启动训练。完整训练脚本骨架from transformers import AutoModelForCausalLM, Trainer from datasets import Dataset model AutoModelForCausalLM.from_pretrained( Qwen/Qwen2.5-0.5B-Instruct, torch_dtypeauto, device_mapauto, ) model get_peft_model(model, lora_config) dataset Dataset.from_list([{text: t} for t in texts]) dataset dataset.map(lambda x: tokenizer(x[text], truncationTrue, max_length512), batchedTrue) trainer Trainer( modelmodel, argstraining_args, train_datasetdataset, ) trainer.train() trainer.save_model(./lora_out/final)启动命令python train_lora.py 21 | tee train.log预期结果日志里 loss 从 2.0 左右逐步下降到 1.0 以下200 步左右能看到明显下降。如果 loss 一直不降检查数据格式是否被正确 tokenize以及 learning_rate 是否过大。训练完成后做推理验证。先加载基座加 LoRA 权重from peft import PeftModel base AutoModelForCausalLM.from_pretrained(Qwen/Qwen2.5-0.5B-Instruct, device_mapauto) model PeftModel.from_pretrained(base, ./lora_out/final) model model.merge_and_unload()然后写一个对话测试函数def chat(prompt): messages [{role: user, content: prompt}] text tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) inputs tokenizer(text, return_tensorspt).to(model.device) outputs model.generate(**inputs, max_new_tokens128, do_sampleTrue, temperature0.7) return tokenizer.decode(outputs[0][inputs[input_ids].shape[1]:], skip_special_tokensTrue) print(chat(你好请介绍一下你自己))成功结果应该是模型用你训练数据的风格回答而不是基座原本的通用回答。如果输出重复或乱码把 temperature 降到 0.3 再试。验证阶段还可以用统一 API 做对比。把同一个问题分别发给微调后的本地模型和通过 API 调用的强模型人工对比回答质量resp client.chat.completions.create( modelclaude-3-5-sonnet, messages[{role: user, content: 你好请介绍一下你自己}], ) print(resp.choices[0].message.content)模型对话入口在 https://taotoken.net/models 接入文档在 https://taotoken.net/doc 里面有各模型的 Model ID 对照表。Claude Code 相关配置可参考 https://taotoken.net/claude-code 。5. 训练与调用阶段的常见报错逐条排查这一节按真实报错整理每条给出原因和修复动作。报错一401 Unauthorized或invalid api key。出现在调用 API 做数据蒸馏时。原因是环境变量没导出或者 Key 复制时带了空格。检查方式echo $TAOTOKEN_API_KEY看是否有值。修复重新导出注意不要有多余换行。如果用的是 settings 配置文件确认字段名是api_key而不是apikey。报错二local proxy failed或连接超时。原因是 Base URL 写错或者网络环境有额外配置。确认base_url是https://taotoken.net/api不要多加/v1后缀部分库会自动补。如果仍然失败检查是否有全局代理环境变量干扰unset http_proxy https_proxy后再试。报错三reading choices或KeyError: choices。出现在解析 API 返回时。原因是返回体结构和你预期的不一致通常是请求本身失败了但没抛异常。修复先打印完整resp看结构确认resp.choices存在再取值。加一层判断if not resp.choices: print(空返回:, resp) return报错四Target modules not found。出现在 LoRA 配置阶段。原因是target_modules里的层名和基座模型不匹配。修复加载模型后print(model)找到注意力层的实际命名Qwen 系列是q_proj等Llama 系列也是但有些模型用query、key。改对即可。报错五CUDA out of memory。原因是 batch size 或 max_length 太大。修复顺序先把per_device_train_batch_size降到 1再把max_length从 512 降到 256最后开gradient_checkpointingTrue。三招下去 16G 显存基本够用。报错六OAuth相关错误。出现在某些需要 OAuth 流程的调用场景。确认你用的是 API Key 方式而不是 OAuth 方式两者凭证不通用。API Key 在 https://taotoken.net/api-keys 创建创建后立即复制保存页面刷新后不再显示完整 Key。报错七训练 loss 为nan。原因是学习率过大或数据里有空样本。修复learning_rate 从 2e-4 降到 1e-4并在数据清洗阶段过滤掉 output 为空的样本。fp16 训练时偶尔也会出现 nan换成 bf16 更稳。排查时养成看完整 traceback 的习惯报错最后一行往往只是表象往上翻几行才能看到真正原因。6. 把训练链路固化下来配置管理与后续迭代跑通一次之后最重要的是把配置固化否则下次换数据集又要重新调一遍。建议把关键参数抽到一个 YAML 文件里base_model: Qwen/Qwen2.5-0.5B-Instruct lora: r: 8 alpha: 16 dropout: 0.05 target_modules: [q_proj, k_proj, v_proj, o_proj] train: batch_size: 2 grad_accum: 8 epochs: 3 lr: 0.0002 max_length: 512 api: base_url: https://taotoken.net/api model: claude-3-5-sonnet训练脚本读这个 YAML改参数不用动代码。数据蒸馏脚本也读同一份配置里的 api 段保证调用通道一致。后续迭代方向有三个。第一是扩数据从几百条扩到几千条效果提升最明显。第二是调 LoRA 秩r从 8 提到 16 或 32适合任务更复杂的场景但显存占用上升。第三是换基座0.5B 跑通后换 1.5B 或 7B流程完全一样只是显存要求更高。数据蒸馏环节可以做得更系统。用种子指令让强模型批量生成问答对脚本里加上重试和去重import time def gen_with_retry(prompt, retries3): for i in range(retries): try: resp client.chat.completions.create( modelclaude-3-5-sonnet, messages[{role: user, content: prompt}], temperature0.8, ) return resp.choices[0].message.content except Exception as e: print(f第{i1}次失败: {e}) time.sleep(2) return None生成的数据要人工抽检强模型偶尔会输出格式不对的内容直接进训练集会污染效果。最后说一个实用技巧训练前先用 50 条数据跑 10 步确认整个链路能通、loss 能降再上全量数据。这样能把“配置错误”和“数据问题”分开定位省下大量等待时间。整个流程跑顺之后你会发现从零训练一个可对话的小模型核心工作量其实在数据准备和配置调试上训练本身反而是最省心的环节。
返回列表