NLP与大模型:让机器人理解语言
Transformer是当代AI的统一数学语言。从文本理解到视觉-语言对齐,从指令解析到任务规划——这些能力让机器人不再是聋哑的自动机,而是能听懂"把蓝色杯子放到左边抽屉里"的智能体。
本阶段目录
1. Transformer:一切大模型的根基
机械类比
想象你在装配一个复杂部件。你手上拿着零件A,眼睛却同时在图纸上扫描零件B的位置(远距离依赖)、确认零件C的安装方向(当前位置),以及不断回顾装配顺序表(外部信息)。Transformer的注意力机制做的就是这件事——让模型在每个时刻自动决定应该"看"序列中的哪些位置。
自注意力:序列建模的统一语言
理解这个公式只需要三步:
- $QK^T$:计算每对token之间的"相似度"。如果序列有100个token,这就产生一个100×100的注意力矩阵——每个token都"看到"了所有其他token。
- $\frac{1}{\sqrt{d_k}}$:缩放因子,防止点积值过大导致softmax饱和(梯度消失)。这是2017年原始论文最重要的工程trick之一。
- 乘以$V$:按相似度权重对Value做加权聚合。思路简单但强大——"相似度高的信息获得更高权重"。
多头注意力:让模型同时关注不同方面
单头注意力只能关注一种模式。多头设计让模型同时从多个"子空间"提取信息——就像你同时关注零件的几何形状、配合公差和材料属性。
为什么Transformer统治了具身智能
- 长距离依赖:传统RNN处理序列要一步步传递信息,100个token以外的信息早就衰减了。Transformer一次关注全局。
- 并行计算:训练时所有token同时计算,GPU利用率远高于RNN。
- 多模态统一:文本、图像patches、机器人动作序列——都可以编码为token,用同一个Transformer处理。这就是RT-2、Octo等VLA模型的架构基础。
import torch, torch.nn as nn, torch.nn.functional as F
class MultiHeadAttention(nn.Module):
"""工业级多头注意力——这是你会在每个VLA模型中看到的模块"""
def __init__(self, d_model=512, n_heads=8):
super().__init__()
assert d_model % n_heads == 0
self.d_k = d_model // n_heads
self.n_heads = n_heads
self.Wq = nn.Linear(d_model, d_model)
self.Wk = nn.Linear(d_model, d_model)
self.Wv = nn.Linear(d_model, d_model)
self.Wo = nn.Linear(d_model, d_model)
def forward(self, q, k, v, mask=None):
B = q.size(0)
# 线性投影 + 拆成多头: (B, seq, d) → (B, n_heads, seq, d_k)
Q = self.Wq(q).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
K = self.Wk(k).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
V = self.Wv(v).view(B, -1, self.n_heads, self.d_k).transpose(1, 2)
# 缩放点积注意力
scores = Q @ K.transpose(-2, -1) / (self.d_k ** 0.5)
if mask is not None: scores = scores.masked_fill(mask == 0, -1e9)
attn = F.softmax(scores, dim=-1)
out = (attn @ V).transpose(1, 2).contiguous().view(B, -1, self.d_k * self.n_heads)
return self.Wo(out)
常见误区
不要把"多头"理解为"多个独立的模型"。所有头共享输入,只是投影矩阵不同。如果去掉多头(即n_heads=1),模型容量暴跌,无法同时捕捉语法、语义、位置等多种关系。
2. BERT与指令理解
你已经知道的
机械图纸上的每一个GD&T标注都有精确含义——位置度Φ0.1和Φ0.05是完全不同的要求。自然语言指令同理:"把杯子放到桌子上"和"把杯子放到桌子旁边"——一个介词之差,动作完全不同。BERT的工作就是从海量文本中学会这些语言模式的精确含义。
BERT的核心创新:双向上下文
之前的语言模型(如GPT-1)只能从左到右看文本。BERT通过Masked Language Model (MLM)实现真正的双向理解——就像你读图纸时不仅看左边的标注,还参考右边的剖视图来理解完整设计意图。
| 任务 | 输入 | 输出 | 机器人应用 |
|---|---|---|---|
| 文本分类 | "把红色零件装到B区" | 意图: pick_and_place | 指令路由:语音→技能选择 |
| 命名实体识别 | "抓取左边的蓝色杯子" | 目标: 杯子, 颜色: 蓝, 位置: 左 | 指令参数提取 |
| 句子相似度 | "拧紧螺丝" vs "紧固螺栓" | 0.92 (高度相似) | 多语言指令归一化 |
| 问答 | "M6螺栓的扭矩是多少?" + 工艺手册 | "12 N·m" | 操作知识检索 |
在机器人指令上微调BERT
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import Trainer, TrainingArguments
import torch
# 假设我们有100条标注的机器人指令
# 格式: {"text": "把红色零件放到B区", "label": 2}
# 标签: 0=导航, 1=抓取, 2=放置, 3=拧紧, 4=检测
model_name = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(
model_name, num_labels=5
)
def tokenize(examples):
return tokenizer(examples["text"], padding="max_length",
truncation=True, max_length=64)
# 训练参数
training_args = TrainingArguments(
output_dir="./results", num_train_epochs=5,
per_device_train_batch_size=16, learning_rate=2e-5,
evaluation_strategy="epoch", save_strategy="epoch",
load_best_model_at_end=True,
)
# 推理:实时解析机器人指令
def predict_command(text):
inputs = tokenizer(text, return_tensors="pt", truncation=True)
with torch.no_grad():
logits = model(**inputs).logits
label = torch.argmax(logits, dim=-1).item()
labels = ["导航", "抓取", "放置", "拧紧", "检测"]
return labels[label]
print(predict_command("把红色零件放到B区")) # → "放置"
3. CLIP:视觉-语言对齐
核心洞察
训练一个传统图像分类器需要标注"这是猫""这是狗"。CLIP的洞察是:互联网上有数十亿张图片和它们的文字描述——不需要人工标注,用"图片-文字"对直接训练,模型自动学会"猫"这个词和猫的图片之间的对应关系。
对比学习:拉近配对,推远不配对
CLIP在机器人中的杀手级应用
- 开放词汇物体识别:不需要训练"杯子"类。直接给文本"蓝色马克杯",CLIP在图像中找到匹配区域。
- 抓取候选排序:生成100个候选抓取姿态,用CLIP判断哪个姿态最像"抓住杯子把手"。
- 任务进度判断:当前画面 vs "杯子已放入托盘"——CLIP打分,判断任务是否完成。
- 异常检测:"螺丝正确拧入" vs "螺丝歪斜"——即使你从未标注过异常样本。
import torch
from transformers import CLIPProcessor, CLIPModel
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
def zero_shot_classify(image, candidates):
"""零样本物体识别:不给训练样本,直接用文字描述分类"""
inputs = processor(
text=candidates, images=image,
return_tensors="pt", padding=True
)
with torch.no_grad():
outputs = model(**inputs)
# logits_per_image: [1, N]——每个候选文本的匹配分数
probs = outputs.logits_per_image.softmax(dim=-1)
for cand, p in zip(candidates, probs[0]):
print(f"{cand:20s}: {p.item():.4f}")
return candidates[probs[0].argmax()]
# 机器人看到桌面上的物体,用CLIP判断是什么
candidates = [
"a red screwdriver on a table",
"a blue coffee mug",
"a metal wrench",
"a black USB cable",
"nothing, empty table",
]
# result = zero_shot_classify(camera_image, candidates)
工程技巧
CLIP对提示词格式敏感。与其用"mug",不如用"a photo of a ceramic coffee mug on a wooden table"。这个技巧叫Prompt Engineering for CLIP——在机器人场景中,加上"on a workbench""in a factory"等上下文描述会显著提升准确率。
4. LLM任务规划:从语言到动作序列
核心问题
人类接到指令"给我倒杯水"时,大脑自动做任务分解:走到厨房→拿起杯子→打开水龙头→接水→关水→端回来。每一步还能继续分解(走到厨房 = 定位厨房门→规划路径→移动)。LLM的任务规划就是用语言模型的能力来做这种层级化任务分解。
Chain-of-Thought 规划模式
让LLM输出不仅是动作序列,还包括每一步的推理过程:
# LLM规划的典型输入
指令: "把桌上红色杯子里的水倒进水槽,然后把杯子放回原处"
# LLM的Chain-of-Thought输出
思考:
1. 我需要先定位"红色杯子"和"水槽"
2. 杯子里面有水,需要先抓取然后移动到水槽上方
3. 倒水操作需要一个倾斜动作
4. 倒完水后需要回到桌面原位置
规划动作:
["find('red mug')",
"move_to('red mug')",
"grasp('red mug')",
"find('sink')",
"move_to('sink')",
"pour(angle=120, duration=3s)",
"move_to('table original position')",
"release()"]
构建机器人可执行的JSON规划
直接让LLM输出结构化JSON,机器人控制器直接解析执行:
from openai import OpenAI
system_prompt = """你是一个机器人任务规划器。输出严格的JSON格式。
可用技能: pick(obj), place(obj,loc), move_to(loc), pour(src,tgt),
open(drawer), close(drawer), push(obj,dir), wipe(surface)
可用物体: mug, plate, spoon, bottle, drawer, cabinet, table, sink
可用位置: left, right, center, top_shelf, bottom_shelf"""
user_instruction = "把蓝色杯子从桌上拿到水槽边,倒掉里面的水,放回桌上原处"
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_instruction}
],
response_format={"type": "json_object"},
temperature=0.1, # 低温度 = 确定性输出
)
# 输出: {"plan": [{"action":"find","target":"blue mug","location":"table"},...]}
plan = json.loads(response.choices[0].message.content)
for step in plan["plan"]:
execute_skill(step) # 调用机器人技能库
LLM规划的工程陷阱
- 幻觉物体:LLM可能规划出"pick(hammer)",但场景中没有锤子。必须加物理环境校验。
- 不可行动作:LLM不关心物理可行性。你的技能库是硬约束——LLM只能从已有技能中选择。
- 重规划需求:执行失败时(抓取滑落、物体被移动),需要LLM根据新观测重新规划。这叫做closed-loop planning。
5. Prompt Engineering:为机器人设计指令
为什么Prompt Engineering对机器人至关重要
在机器人场景中,Prompt不是简单的"问答"——它承载了任务约束、物理常识、安全规则和错误恢复策略。一个好的Prompt设计能让LLM从"胡说八道的聊天机器人"变成"可靠的任务规划引擎"。
结构化Prompt的五个要素
| 要素 | 作用 | 示例 |
|---|---|---|
| 1. 角色设定 | 限定LLM的行为边界 | "你是一个精密装配机器人控制器" |
| 2. 场景描述 | 提供环境和物体信息 | "工作台上有螺丝刀(M3)、螺栓盒、扭矩扳手" |
| 3. 技能库 | 定义可用动作空间 | "可用函数: pick, place, screw, measure_torque" |
| 4. 约束与安全 | 硬性规则,不可违反 | "扭矩不得超过2.5Nm,否则会损坏螺纹" |
| 5. 输出格式 | 确保可被机器解析 | "输出JSON数组,每个元素含action和args字段" |
Few-Shot Prompting:用示例教LLM
给LLM看几个"输入→正确输出"的例子,比写复杂规则更有效:
示例 1:
输入: "拧紧M6螺栓"
输出: {"tool": "torque_wrench", "target": "M6_bolt", "torque": 12.0, "unit": "Nm"}
示例 2:
输入: "检查法兰面平整度"
输出: {"tool": "dial_indicator", "target": "flange_A", "tolerance": 0.02, "unit": "mm"}
示例 3:
输入: "组装轴承到轴上"
输出: {"tool": "bearing_press", "target": "bearing_6205", "location": "shaft_end",
"warning": "确保轴清洁无毛刺,轴承加热到80°C"}
现在处理新指令: "把端盖装到壳体上并用M4螺钉固定"
→ LLM根据示例的模式自动生成正确答案
工程最佳实践
- 用代码校验输出:永远不要直接信任LLM的JSON。解析后用schema验证:动作是否在技能库中?物体是否存在?参数是否在合理范围?
- 加CoT但不依赖CoT:让LLM"先思考再输出"能提升规划质量,但最终只提取动作部分,忽略推理文本。
- 温度设置:规划任务用 temperature=0.0~0.2(确定性),创意任务用 0.7~1.0。
6. RAG:让机器人随时查阅知识库
机械类比
你在产线上遇到一个不熟悉的设备故障,不会凭空猜测——你会去查维修手册。RAG (Retrieval-Augmented Generation) 就是给LLM配了一本可实时检索的"维修手册"。机器人遇到训练数据中没有的场景时,RAG让它能够查阅相关文档再决策。
RAG的三个阶段
RAG在机器人中的典型场景
- 维护场景:机器人检测到异常振动 → 检索维修手册中的"轴承故障诊断"章节 → LLM根据手册内容生成排故步骤
- 新物体操作:遇到从未见过的工具 → 检索物品数据库中的操作说明 → 生成抓取和操作策略
- 安全合规:在危险区域作业前 → 检索安全规程文档 → LLM判断当前操作是否合规
- 多语言支持:中文指令"拧紧" → 检索英文手册 "tighten to 12 Nm" → 跨语言知识融合
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 1. 构建索引:将维修手册分块向量化
with open("robot_maintenance_manual.txt") as f:
manual = f.read()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=100,
separators=["\n\n", "\n", "。", ";"]
)
chunks = splitter.split_text(manual)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_texts(chunks, embeddings)
# 2. 运行时检索:机器人遇到未知情况
query = "关节3电机过热报警,温度超过85°C如何处理"
docs = vectorstore.similarity_search(query, k=3)
# 3. 增强LLM:将检索结果注入Prompt
context = "\n\n".join([d.page_content for d in docs])
augmented_prompt = f"""参考以下维修手册内容回答机器人的操作问题:
{context}
当前问题: {query}
请给出具体操作步骤,每步标注预计耗时和安全注意事项。"""
# response = llm.invoke(augmented_prompt)
RAG的工程挑战
- 检索质量:手册中"轴承温度过高"和"电机过热"在语义上是高度相关的,但字面上不匹配。需要好的embedding模型来捕捉这种语义相似性。
- 文档质量:如果你的操作手册本身就含混不清,RAG只会放大混乱。Garbage in, garbage out。
- 延迟:检索+LLM推理可能耗时2-5秒——对于实时机器人控制来说太慢。RAG更适合"任务规划"(秒级),不适合"伺服控制"(毫秒级)。
7. 多模态微调:LoRA与量化
你需要知道什么
全量微调一个70B的LLaMA模型需要8块A100 GPU。作为个人开发者或小型实验室,你需要的是参数高效微调(PEFT)——只训练极少量参数(<1%),在单块消费级GPU上就能让大模型适配你的机器人任务。
LoRA:低秩适应的核心思想
原始权重 $W$ 冻结不动。可训练的 $B$ 和 $A$ 是两个小矩阵,乘积 $BA$ 的秩远小于 $W$。对于 $d \times d$ 的矩阵,全量微调训练 $d^2$ 个参数,LoRA只需 $2 \cdot d \cdot r$($r \ll d$)。当 $d=4096, r=16$,参数量从 16M 降到 131K——减少 99.2%。
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
import torch
# 1. 加载基座模型(4-bit量化为节省显存)
model_name = "Qwen/Qwen2.5-7B-Instruct" # 中文能力强,适合工业指令
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="auto",
load_in_4bit=True, # 4-bit量化 → 7B模型仅需 ~5GB 显存
)
# 2. 配置LoRA:只训练注意力层的投影矩阵
lora_config = LoraConfig(
r=16, # 低秩维度(越大容量越大,但训练越慢)
lora_alpha=32, # 缩放因子(通常 α = 2×r)
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
task_type=TaskType.CAUSAL_LM,
)
lora_model = get_peft_model(model, lora_config)
lora_model.print_trainable_parameters()
# 输出: trainable params: 41,943,040 || all params: 7,615,571,968 || trainable%: 0.55%
# 3. 准备机器人指令数据集(100-500条即可)
robot_instructions = [
{"input": "检测到工件偏移0.3mm,需要修正",
"output": '{"action":"offset_correct","axis":"XY","value":0.3,"tool":"vacuum_gripper"}'},
{"input": "力矩超限报警,关节4",
"output": '{"action":"emergency_stop","joint":4,"check":"collision_detect"}'},
]
# 4. 训练(单块RTX 3090即可)
training_args = TrainingArguments(
output_dir="./robot-llm-lora", num_train_epochs=3,
per_device_train_batch_size=4, gradient_accumulation_steps=4,
learning_rate=2e-4, fp16=True, logging_steps=10,
save_strategy="epoch",
)
trainer = Trainer(model=lora_model, args=training_args, train_dataset=dataset)
trainer.train()
# 5. 保存:LoRA权重只有 ~10MB,而不是完整模型的 14GB
lora_model.save_pretrained("./robot-llm-lora-final")
量化:用精度换可行性
| 量化方式 | 模型大小 (7B) | 推理速度 | 质量损失 | 适用场景 |
|---|---|---|---|---|
| FP32 | ~28 GB | 基线 | 0% | 训练 |
| FP16/BF16 | ~14 GB | ~2× | 可忽略 | 推理/微调 |
| INT8 | ~7 GB | ~3× | < 0.5% | 生产部署 |
| INT4 (GPTQ/AWQ) | ~4 GB | ~4× | < 2% | 边缘设备/单卡微调 |
验收实验
- 用BERT微调100条机器人指令分类器,5类准确率 > 90%
- 用CLIP对10类桌面物体做零样本识别,Top-1 > 80%,并尝试Prompt Engineering优化
- 用LLM将20条中文指令拆解为JSON技能序列,正确率 > 85%,包含异常处理分支
- 构建一个机器人维修知识库(至少50个文档块),实现RAG查询并验证Top-3检索准确率 > 80%
- 用LoRA在单GPU上微调一个7B模型,适配5种机器人指令格式,对比微调前后的指令执行成功率
拓展资源:NLP与大模型
GitHub 仓库
- huggingface/transformers — BERT/GPT/CLIP/Llama 等模型一站式调用,NLP的"标准库"
- mlfoundations/open_clip — CLIP开源实现,支持多种视觉编码器
- karpathy/nanoGPT — GPT最小实现(~300行),学习Transformer内部机制的最佳代码
- huggingface/peft — LoRA/QLoRA等参数高效微调框架
- langchain-ai/langchain — RAG应用开发框架
- ggerganov/llama.cpp — C++推理引擎,在边缘设备上跑LLM
视频课程
- Stanford CS224N: NLP with Deep Learning — 经典NLP课程(2024版含大模型内容)
- Andrej Karpathy: Let's build GPT from scratch — 2小时手写GPT,理解Transformer的最佳捷径
- Andrej Karpathy: Let's reproduce GPT-2 — 4小时深入讲解GPT-2复现
- State of GPT (Karpathy @ Microsoft Build) — LLM训练全流程:预训练→SFT→RLHF→部署
必读论文
- Attention Is All You Need (2017) — Transformer原论文,全部现代LLM的起点
- BERT (2018) — 预训练语言模型的里程碑
- CLIP (2021) — 视觉-语言对齐的开创性工作
- QLoRA (2023) — 4-bit量化的高效微调方法
- RAG (2020) — Retrieval-Augmented Generation原始论文