AI 模型:LLM / VLM / VLA 指南
目录 1. 硬件环境与底层框架 The Bedrock 1 硬件环境与底层框架 the bedrock 2. 数据工程:模型的"燃料" Data Pipeline 2 数据工程模型的燃料 data pipeline 3. 模型架构与训练范式 Modeling 3 模型架构与训练范式 modeling 4. 针对性实战案例 Practical Use Cases 4 针对性实战案例 practical use cases 5. 性能优化与…
At a glance
Reading effort and structure before you settle in.
Reader briefing
In this note
63 sections
Reading deck
目录
- 1. 硬件环境与底层框架 (The Bedrock)
- 2. 数据工程:模型的"燃料" (Data Pipeline)
- 3. 模型架构与训练范式 (Modeling)
- 4. 针对性实战案例 (Practical Use Cases)
- 5. 性能优化与本地部署 (Inference)
- 附录
1. 硬件环境与底层框架 (The Bedrock)
1.1 显存需求的第一性原理计算
在动手之前,你必须回答一个问题:我需要多少 GPU 显存?
1.1.1 参数存储
一个参数在不同精度下占用的字节数:
| 精度 | 每参数字节数 | 7B 模型参数占用 | 13B | 70B |
|---|---|---|---|---|
| FP32 | 4 bytes | 28 GB | 52 GB | 280 GB |
| FP16 / BF16 | 2 bytes | 14 GB | 26 GB | 140 GB |
| INT8 | 1 byte | 7 GB | 13 GB | 70 GB |
| INT4 | 0.5 byte | 3.5 GB | 6.5 GB | 35 GB |
公式:
显存_参数 = 参数量 × 每参数字节数
1.1.2 训练时的总显存估算(全精度 Mixed Precision)
训练时,显存远不只参数本身。以 AdamW + Mixed Precision (FP16/BF16 + FP32 master weights) 为例:
总显存 ≈ 模型参数 + 梯度 + 优化器状态 + 激活值
具体拆分(以 FP16 训练 + FP32 master copy 为例):
- FP16 模型参数: 2 bytes × P
- FP32 Master Weights: 4 bytes × P (用于精确更新)
- FP16 梯度: 2 bytes × P
- FP32 Adam m (一阶矩): 4 bytes × P
- FP32 Adam v (二阶矩): 4 bytes × P
─────────────────────────────────────
合计(不含激活值): 16 bytes × P
→ 7B 模型: 16 × 7×10⁹ ≈ 112 GB(仅参数+优化器)
→ 13B 模型: 16 × 13×10⁹ ≈ 208 GB
→ 70B 模型: 16 × 70×10⁹ ≈ 1120 GB ≈ 1.1 TB
激活值(Activation Memory) 取决于 batch_size × seq_len × hidden_dim × num_layers,通常可通过 Gradient Checkpointing(梯度检查点) 将激活值显存降低到 ( 为层数),代价是约 33% 的额外计算时间。
经验法则(全参数训练):所需总显存 ≈ 18-20 × 参数量(以 bytes 计)
1.1.3 实际硬件对照表
| GPU | 显存 | 可全参数训练 | 可 LoRA 微调(BF16) | 可 QLoRA 微调(4bit) |
|---|---|---|---|---|
| RTX 4070 (12GB) | 12 GB | ≤ 0.5B | ≤ 3B | ≤ 7B |
| RTX 4090 (24GB) | 24 GB | ≤ 1B | ≤ 7B | ≤ 13B |
| A100 (80GB) | 80 GB | ≤ 4B | ≤ 30B | ≤ 70B |
| 8×A100 (640GB) | 640 GB | ≤ 30B | ≤ 70B+ | ≤ 180B |
| H100 (80GB) | 80 GB | ≤ 5B | ≤ 34B | ≤ 70B |
1.1.4 存储空间估算
别忘了磁盘:
- 模型 checkpoint:一个 7B FP16 模型约 14 GB,训练中一般保留 3-5 个 checkpoint → 70 GB
- 预训练语料:1T token 的文本语料(tokenized)约 2-4 TB
- 多模态数据:LAION-400M 图像数据集约 80+ TB(原始图像)
- 建议:NVMe SSD ≥ 2TB 用于高频 I/O,HDD 用于冷存储
1.2 本地化训练环境搭建
1.2.1 基础环境(以 Ubuntu 22.04 + CUDA 为例)
# 1. 安装 NVIDIA 驱动(推荐使用 .run 包或 apt 方式)
sudo apt update
sudo apt install -y nvidia-driver-545 # 根据显卡型号选择
# 验证
nvidia-smi
# 2. 安装 CUDA Toolkit(推荐 12.1+,与 PyTorch 版本对齐)
wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run
sudo sh cuda_12.4.0_550.54.14_linux.run --toolkit --silent
# 添加环境变量
echo 'export PATH=/usr/local/cuda-12.4/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
# 验证
nvcc --version
# 3. 安装 cuDNN
# 从 https://developer.nvidia.com/cudnn 下载对应版本
sudo dpkg -i cudnn-local-repo-ubuntu2204-9.x.x_1.0-1_amd64.deb
# 4. 安装 NCCL(多卡通信库,多卡训练必备)
sudo apt install libnccl2 libnccl-dev
1.2.2 Python 训练栈
# 推荐使用 conda 管理环境
conda create -n train python=3.11 -y
conda activate train
# PyTorch(务必对齐 CUDA 版本)
pip install torch==2.3.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# 核心训练框架
pip install transformers>=4.43.0 # Hugging Face 模型库
pip install datasets>=2.20.0 # 数据加载
pip install accelerate>=0.33.0 # 分布式训练抽象层
pip install deepspeed>=0.14.0 # ZeRO 优化器
pip install peft>=0.12.0 # LoRA/QLoRA
pip install trl>=0.9.0 # RLHF/DPO 训练
pip install bitsandbytes>=0.43.0 # 量化支持
pip install flash-attn --no-build-isolation # FlashAttention-2
# 可选但推荐
pip install wandb # 实验追踪
pip install sentencepiece protobuf # Tokenizer 依赖
pip install einops # 张量操作
1.2.3 关键框架的角色定位
┌─────────────────────────────────────────────────────────────────┐
│ 你的训练脚本 │
│ (train.py / train.yaml) │
├─────────────┬───────────────┬───────────────┬──────────────────┤
│ Transformers│ TRL │ PEFT │ Datasets │
│ (模型定义) │ (RLHF/DPO) │ (LoRA/QLoRA) │ (数据加载) │
├─────────────┴───────────────┴───────────────┴──────────────────┤
│ Accelerate │
│ (统一的分布式训练接口 / 设备分配) │
├─────────────────────────────────────────────────────────────────┤
│ DeepSpeed (ZeRO-1/2/3) │
│ (优化器分片 / 梯度分片 / 参数分片 / Offload) │
├─────────────────────────────────────────────────────────────────┤
│ PyTorch (FSDP / DDP) + NCCL (多卡通信) │
├─────────────────────────────────────────────────────────────────┤
│ CUDA + cuDNN + FlashAttention │
└─────────────────────────────────────────────────────────────────┘
各框架解释:
- Accelerate:Hugging Face 出品,一层薄抽象。你写单卡代码,它帮你处理多卡、多机、混合精度。配置驱动,不侵入代码。
- DeepSpeed:微软出品,核心是 ZeRO(Zero Redundancy Optimizer)系列优化。
- FSDP:PyTorch 原生的全分片数据并行(Fully Sharded Data Parallel),与 DeepSpeed ZeRO-3 理念相似。
1.2.4 Accelerate 配置示例
# 交互式配置
accelerate config
# 或直接编写 YAML(推荐)
# accelerate_config.yaml(单机双卡 + DeepSpeed ZeRO-2 示例)
compute_environment: LOCAL_MACHINE
distributed_type: DEEPSPEED
deepspeed_config:
deepspeed_config_file: ds_config.json
zero3_init_flag: false
machine_rank: 0
main_training_function: main
mixed_precision: bf16
num_machines: 1
num_processes: 2 # GPU 数量
gpu_ids: 0,1
1.3 消费级显卡的分布式策略
如果你只有 2-4 张 RTX 4070/4090,以下策略能让你训练更大的模型:
1.3.1 DeepSpeed ZeRO 三阶段对比
显存占用(示例: 7B 模型, 2 卡)
┌──────────────────────────────┐
ZeRO-0 (DDP) │ 每卡存完整副本: ~112 GB/卡 │ ← 放不下
├──────────────────────────────┤
ZeRO-1 │ 优化器状态分片: ~80 GB/卡 │ ← 仍然放不下
(Optimizer Split) ├──────────────────────────────┤
ZeRO-2 │ +梯度分片: ~66 GB/卡 │ ← 还是大
(+Gradient Split) ├──────────────────────────────┤
ZeRO-3 │ +参数分片: ~28 GB/卡 │ ← 勉强可行
(+Parameter Split) ├──────────────────────────────┤
ZeRO-3 + Offload │ 优化器状态→CPU: ~16 GB/卡 │ ← 消费级可行
└──────────────────────────────┘
1.3.2 DeepSpeed ZeRO-3 + CPU Offload 配置
{
"bf16": { "enabled": true },
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"overlap_comm": true,
"contiguous_gradients": true,
"sub_group_size": 1e9,
"reduce_bucket_size": "auto",
"stage3_prefetch_bucket_size": "auto",
"stage3_param_persistence_threshold": "auto",
"stage3_max_live_parameters": 1e9,
"stage3_max_reuse_distance": 1e9,
"stage3_gather_16bit_weights_on_model_save": true
},
"gradient_accumulation_steps": 16,
"gradient_clipping": 1.0,
"steps_per_print": 10,
"train_batch_size": "auto",
"train_micro_batch_size_per_gpu": 1,
"wall_clock_breakdown": false
}
关键参数解读:
offload_optimizer.device: "cpu":将 Adam 的 m/v 状态放到 CPU 内存,极大减少 GPU 显存offload_param.device: "cpu":将不在当前前向/反向计算中的参数也放到 CPU(速度会更慢,但显存极省)gradient_accumulation_steps: 16:模拟更大 batch size,弥补显存不足导致的小 micro-batch
1.3.3 FSDP vs DeepSpeed:如何选择?
| 维度 | FSDP | DeepSpeed ZeRO |
|---|---|---|
| 来源 | PyTorch 官方 | 微软 |
| 集成度 | PyTorch 原生,无额外依赖 | 需要安装 deepspeed 包 |
| CPU Offload | 支持(但生态较新) | 成熟且稳定 |
| NVMe Offload | 不支持 | 支持(ZeRO-Infinity) |
| HF 集成 | Accelerate 原生支持 | Accelerate 原生支持 |
| 推荐场景 | 2-8 卡同构集群 | 消费级显卡 + CPU Offload |
实际建议:
- 4090 × 2 训练 7B QLoRA → Accelerate + FSDP 足够
- 4070 × 4 全参训练 7B → DeepSpeed ZeRO-3 + CPU Offload
- 跨机器训练 → DeepSpeed 配合
pdsh或 Slurm
2. 数据工程:模型的"燃料" (Data Pipeline)
"Garbage in, garbage out" 在大模型时代变为 "Data quality is all you need"。
2.1 通用 LLM 预训练数据
2.1.1 数据来源全景
预训练语料(目标: 数百B ~ 数T tokens)
├── Web 爬取
│ ├── Common Crawl(最大公开爬取数据集,PB 级)
│ ├── FineWeb(Hugging Face 清洗后的 15T token 数据集)
│ └── RefinedWeb(Falcon 团队清洗的 5T tokens)
├── 书籍
│ ├── Project Gutenberg(公版图书)
│ └── Books3(争议数据集,注意版权)
├── 学术论文
│ ├── arXiv(LaTeX 源码)
│ ├── S2ORC(Semantic Scholar)
│ └── PubMed(生物医学)
├── 代码
│ ├── The Stack v2(Hugging Face, 许可证过滤)
│ └── StarCoder Data
├── 百科与结构化
│ ├── Wikipedia(多语言)
│ └── StackExchange
└── 领域特定
├── 法律: CaseText, 裁判文书网
├── 医学: PubMed, MIMIC
└── 金融: SEC Filings, 财经新闻
2.1.2 数据清洗流水线(The Cleaning Pipeline)
这是最被低估但最重要的环节。以下是工业级流水线的完整步骤:
原始数据 (Raw HTML/Text)
│
▼
┌─────────────────────┐
│ 1. 文本提取 │ trafilatura / resiliparse 从 HTML 提取正文
│ (Extraction) │ 去除导航栏、广告、页脚等 boilerplate
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 2. 语言识别 │ fastText lid.176.bin → 过滤非目标语言
│ (Language ID) │ 阈值通常设 0.65+
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 3. URL/域名过滤 │ 黑名单: 成人站点, 垃圾农场, 版权风险域名
│ (URL Filtering) │ UT1 黑名单 + 自定义规则
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 4. 质量过滤 │ 基于启发式规则:
│ (Quality Filter) │ - 文档长度 > 50 words
│ │ - 平均句子长度合理 (5-100 words)
│ │ - 特殊字符/大写比例异常 → 过滤
│ │ - 停用词比例检测(防机器翻译垃圾)
│ │ - perplexity 过滤(KenLM 语言模型打分)
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 5. 文档级去重 │ MinHash + LSH (Locality-Sensitive Hashing)
│ (Deduplication) │ 工具: datatrove / text-dedup
│ │ 阈值: Jaccard > 0.8 判定为重复
│ │ ⚠ 这一步可以去掉 30-50% 的数据
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 6. 段落/句子级去重 │ Exact substring dedup (suffix array)
│ (Fine Dedup) │ 去除在多文档中重复出现的样板段落
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 7. PII 脱敏 │ 正则 + NER 模型去除:
│ (PII Removal) │ 电话号码, 邮箱, 身份证号, 地址
└─────────┬───────────┘
│
▼
清洗后的干净语料 → Tokenization → 序列化
2.1.3 工业级工具:Datatrove
Datatrove 是 Hugging Face 开发的可扩展数据处理库,FineWeb 数据集就是用它构建的。
"""
Datatrove 流水线示例:处理 Common Crawl WARC 文件
"""
from datatrove.pipeline.readers import WarcReader
from datatrove.pipeline.filters import (
URLFilter,
LanguageFilter,
GopherRepetitionFilter,
GopherQualityFilter,
C4QualityFilter,
)
from datatrove.pipeline.dedup import MinhashDedupSignature, MinhashDedupBuckets, MinhashDedupCluster, MinhashDedupFilter
from datatrove.pipeline.extractors import Trafilatura
from datatrove.pipeline.writers import JsonlWriter
from datatrove.executor import LocalPipelineExecutor
# 阶段 1: 提取 + 过滤
pipeline_1 = [
WarcReader("s3://commoncrawl/crawl-data/CC-MAIN-2024-10/"),
Trafilatura(), # HTML → 正文提取
URLFilter(), # URL 黑名单过滤
LanguageFilter(language="zh"), # 只保留中文
GopherQualityFilter(), # Gopher 论文的质量规则
GopherRepetitionFilter(), # 重复内容过滤
C4QualityFilter(), # C4 数据集的过滤规则
JsonlWriter("output/filtered/"),
]
executor = LocalPipelineExecutor(
pipeline=pipeline_1,
tasks=100, # 并行任务数
workers=16, # 进程数
)
executor.run()
# 阶段 2: MinHash 去重(需要多步)
# Step 2.1: 计算签名
# Step 2.2: LSH 分桶
# Step 2.3: 聚类
# Step 2.4: 去重过滤
规模参考:FineWeb 处理了 96 个 Common Crawl 快照,最终产出 15T tokens 的高质量英文数据。中文可参考 CCI (Chinese Corpus Internet) 的实践。
2.1.4 Tokenization 策略
Tokenizer 的选择直接影响模型效率。
| Tokenizer | 代表模型 | 词表大小 | 中文效率 |
|---|---|---|---|
| BPE | GPT-4, LLaMA | 32K-128K | 取决于训练语料比例 |
| SentencePiece (Unigram) | T5, mBART | 32K-250K | 较好 |
| WordPiece | BERT | 30K | 一般 |
关键决策:
- 如果做中文模型,务必确保 tokenizer 训练语料中中文占比足够(否则一个汉字被拆成 3-4 个 token,效率极低)
- LLaMA-3 的 tokenizer 词表扩大到 128K,中文效率显著提升
- 自训练 tokenizer 示例:
from tokenizers import Tokenizer, models, trainers, pre_tokenizers
# 训练 BPE tokenizer
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
trainer = trainers.BpeTrainer(
vocab_size=64000,
special_tokens=["<s>", "</s>", "<pad>", "<unk>", "<mask>"],
min_frequency=2,
show_progress=True,
)
# 在你的语料上训练
files = ["chinese_corpus.txt", "english_corpus.txt", "code_corpus.txt"]
tokenizer.train(files, trainer)
tokenizer.save("my_tokenizer.json")
# 验证中文效率
test = "大规模预训练语言模型的核心在于数据质量"
encoded = tokenizer.encode(test)
print(f"字符数: {len(test)}, Token数: {len(encoded.ids)}")
print(f"压缩率: {len(test)/len(encoded.ids):.2f} 字符/token")
# 理想情况: 中文应达到 1.5-2.5 字符/token
2.2 多模态 VLM 数据
2.2.1 图文对齐数据的类型
VLM 训练数据
├── 预训练阶段(图文对齐 / Feature Alignment)
│ ├── 弱标注图文对 (Weakly-labeled Image-Text Pairs)
│ │ ├── LAION-5B: 50亿图文对, 来自网页 alt-text
│ │ ├── COYO-700M: 7亿图文对
│ │ └── DataComp: 可复现的数据筛选基准
│ │
│ └── 说明: 这些数据质量参差不齐, 但量大
│
├── 指令微调阶段(Visual Instruction Tuning)
│ ├── LLaVA-Instruct-150K: GPT-4 生成的图像问答指令
│ ├── ShareGPT4V: 高质量图像描述
│ ├── ALLaVA: 多样化视觉指令
│ └── 说明: 质量 >> 数量, 通常 100K-1M 条
│
└── 评估数据
├── VQAv2, GQA: 视觉问答
├── TextVQA, OCRBench: OCR 相关
├── MMBench, MME: 综合多模态评测
└── POPE: 幻觉检测
2.2.2 图文对数据的构建与合成
方法 1: 从网页爬取 + CLIP 过滤
"""
使用 CLIP Score 过滤低质量图文对
原理: CLIP 模型可以衡量图像和文本的语义相似度
"""
import torch
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")
def filter_image_text_pair(image_path: str, text: str, threshold: float = 0.25):
"""
CLIP score > threshold → 保留
DataComp 论文建议阈值 0.25-0.30
"""
image = Image.open(image_path)
inputs = processor(text=[text], images=image, return_tensors="pt", padding=True)
with torch.no_grad():
outputs = model(**inputs)
# cosine similarity
score = outputs.logits_per_image.item() / 100.0
return score > threshold, score
方法 2: 用大模型合成高质量描述(LLaVA 方法)
"""
用 GPT-4V 或开源 VLM 为图像生成详细描述/问答对
这是 LLaVA 论文的核心数据构造方法
"""
import openai
import base64
def generate_visual_instruction(image_path: str) -> dict:
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
# 生成三种类型的数据
prompts = {
"conversation": "基于这张图片,生成一段自然的多轮对话(3-5轮)。",
"detail_description": "请详细描述这张图片中的所有内容,包括物体、位置关系、颜色、动作等。",
"complex_reasoning": "基于这张图片,生成一个需要复杂推理才能回答的问题和答案。"
}
results = {}
for task, prompt in prompts.items():
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"}}
]
}]
)
results[task] = response.choices[0].message.content
return results
2.2.3 数据格式标准化
VLM 的训练数据最终需要统一为以下格式:
{
"id": "000001",
"image": "images/000001.jpg",
"conversations": [
{
"from": "human",
"value": "<image>\n请描述这张图片中的场景。"
},
{
"from": "gpt",
"value": "这张图片展示了一个阳光明媚的公园..."
}
]
}
<image>token:这是一个特殊标记,在模型内部会被替换为视觉编码器输出的特征向量序列(通常 256-576 个 visual tokens)。
2.3 具身 VLA 数据
2.3.1 VLA 数据的独特性
VLA(Vision-Language-Action)数据不同于 LLM/VLM,它必须包含:
VLA 训练数据 = 视觉观测 + 语言指令 + 动作标签
每一条轨迹 (Trajectory) 的结构:
{
"task_description": "pick up the red cup and place it on the table",
"steps": [
{
"timestamp": 0.0,
"observation": {
"image": "frame_000.jpg", # RGB 图像 (H×W×3)
"depth": "depth_000.npy", # 深度图 (可选)
"proprio": [0.1, 0.2, 0.3, 0.0, ...] # 本体感受 (关节角度/末端位姿)
},
"action": {
"cartesian": [dx, dy, dz, dRx, dRy, dRz, gripper], # 7-DOF 动作
# 或者
"joint": [dq1, dq2, dq3, dq4, dq5, dq6, gripper] # 关节空间动作
}
},
...
]
}
2.3.2 动作数据的归一化
这是 VLA 数据工程中最容易出错的环节。 不同机械臂的动作空间差异巨大:
"""
VLA 动作数据归一化
关键: 不同机械臂的关节范围、末端执行器坐标系、控制频率都不同
必须统一归一化到 [-1, 1] 或 [0, 1]
"""
import numpy as np
class ActionNormalizer:
"""
针对机械臂动作空间的归一化器
支持两种模式:
1. min-max 归一化 → [-1, 1]
2. z-score 归一化 → μ=0, σ=1 (然后 clip)
"""
def __init__(self, method="minmax"):
self.method = method
self.stats = {}
def fit(self, actions: np.ndarray):
"""
在整个数据集上计算统计量
actions: shape (N, action_dim), 例如 (100000, 7)
"""
if self.method == "minmax":
self.stats["min"] = np.percentile(actions, 1, axis=0) # 用 1% 分位数避免异常值
self.stats["max"] = np.percentile(actions, 99, axis=0)
elif self.method == "zscore":
self.stats["mean"] = actions.mean(axis=0)
self.stats["std"] = actions.std(axis=0) + 1e-8
def normalize(self, action: np.ndarray) -> np.ndarray:
if self.method == "minmax":
normed = 2 * (action - self.stats["min"]) / (self.stats["max"] - self.stats["min"] + 1e-8) - 1
return np.clip(normed, -1, 1)
elif self.method == "zscore":
normed = (action - self.stats["mean"]) / self.stats["std"]
return np.clip(normed, -5, 5) # clip 极端值
def denormalize(self, normed_action: np.ndarray) -> np.ndarray:
"""推理时需要反归一化回真实动作空间"""
if self.method == "minmax":
return (normed_action + 1) / 2 * (self.stats["max"] - self.stats["min"]) + self.stats["min"]
elif self.method == "zscore":
return normed_action * self.stats["std"] + self.stats["mean"]
# 使用示例
normalizer = ActionNormalizer(method="minmax")
# 假设你有一个 Franka Panda 的数据集
# 动作维度: [x, y, z, rx, ry, rz, gripper] = 7-DOF
all_actions = np.load("franka_actions.npy") # (N, 7)
normalizer.fit(all_actions)
# 归一化单条数据
raw_action = np.array([0.5, 0.3, 0.1, 0.0, 0.0, 0.1, 1.0])
normed = normalizer.normalize(raw_action)
print(f"归一化后: {normed}") # 所有值在 [-1, 1]
2.3.3 动作的序列化:离散化 vs 连续化
VLA 模型将动作预测为 token,有两种方式:
方式 1: 离散化(OpenVLA / RT-2 的做法)
"""
将连续动作离散化为 token (bins)
例如: 将 [-1, 1] 均匀分为 256 个 bin → 每个动作维度对应一个 token ID
"""
def discretize_action(action: np.ndarray, num_bins: int = 256) -> list[int]:
"""
action: 归一化后的动作向量, 值域 [-1, 1]
返回: token ID 列表
"""
# [-1, 1] → [0, num_bins-1]
bin_indices = ((action + 1) / 2 * (num_bins - 1)).astype(int)
bin_indices = np.clip(bin_indices, 0, num_bins - 1)
return bin_indices.tolist()
def undiscretize_action(tokens: list[int], num_bins: int = 256) -> np.ndarray:
"""反离散化: token → 连续动作"""
action = np.array(tokens) / (num_bins - 1) * 2 - 1
return action
# 示例
action = np.array([0.5, -0.3, 0.0, 0.1, -0.5, 0.8, 1.0]) # 7-DOF
tokens = discretize_action(action, num_bins=256)
print(f"离散化 tokens: {tokens}") # e.g., [191, 89, 128, 140, 64, 230, 255]
方式 2: 连续回归(直接预测浮点数)
# 在模型最后一层接一个 MLP 回归头
class ActionHead(torch.nn.Module):
def __init__(self, hidden_dim, action_dim=7):
super().__init__()
self.mlp = torch.nn.Sequential(
torch.nn.Linear(hidden_dim, 256),
torch.nn.GELU(),
torch.nn.Linear(256, action_dim),
torch.nn.Tanh() # 输出 [-1, 1]
)
def forward(self, hidden_states):
# hidden_states: 最后一个 token 的隐状态
return self.mlp(hidden_states)
2.3.4 开源机器人数据集
| 数据集 | 规模 | 机器人 | 任务 |
|---|---|---|---|
| Open X-Embodiment | 100万+ 轨迹 | 22 种机器人 | 527 种技能 |
| DROID | 76K 轨迹 | Franka | 多样化操作 |
| BridgeData V2 | 60K 轨迹 | WidowX | 桌面操作 |
| RH20T | 110K 轨迹 | 多种 | 中国团队, 丰富场景 |
使用 Open X-Embodiment 的 RLDS 格式:
import tensorflow_datasets as tfds dataset = tfds.load("fractal20220817_data", split="train") for episode in dataset: for step in episode["steps"]: image = step["observation"]["image"] action = step["action"] # 已归一化
2.4 数据混合策略(Data Mixing)
预训练时不同数据源的混合比例对模型质量影响巨大:
Llama-3 数据混合(推测):
├── Web 文本: 82% (FineWeb 等)
├── 代码: 8% (The Stack)
├── 学术论文: 4% (arXiv, PubMed)
├── 书籍: 4% (公版)
└── 百科+问答: 2% (Wikipedia, StackExchange)
中文模型建议配比:
├── 中文 Web: 40%
├── 英文 Web: 25% (跨语言能力)
├── 代码: 15%
├── 中文百科: 8%
├── 学术论文: 7%
└── 中文书籍: 5%
Hugging Face 教程参考: FineWeb 博客 详细记录了如何从 96 个 CC 快照中清洗出 15T token 的完整过程,包括每个过滤步骤对下游基准测试的影响消融实验。强烈推荐阅读。
Route control
After Reading
Choose the next trail: follow the same topic route, open the research shelf, or continue through nearby notes.