NLP与大模型:让机器人理解语言

Transformer是当代AI的统一数学语言。从文本理解到视觉-语言对齐,从指令解析到任务规划——这些能力让机器人不再是聋哑的自动机,而是能听懂"把蓝色杯子放到左边抽屉里"的智能体。

5-7 周
7 个章节
9 个代码示例
5 个验收实验

本阶段目录

  1. Transformer:一切大模型的根基
  2. BERT与指令理解
  3. CLIP:视觉-语言对齐
  4. LLM任务规划
  5. Prompt Engineering:为机器人设计指令
  6. RAG:让机器人随时查阅知识库
  7. 多模态微调:LoRA与量化

1. Transformer:一切大模型的根基

机械类比

想象你在装配一个复杂部件。你手上拿着零件A,眼睛却同时在图纸上扫描零件B的位置(远距离依赖)、确认零件C的安装方向(当前位置),以及不断回顾装配顺序表(外部信息)。Transformer的注意力机制做的就是这件事——让模型在每个时刻自动决定应该"看"序列中的哪些位置。

自注意力:序列建模的统一语言

$\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right)V$

理解这个公式只需要三步:

  1. $QK^T$:计算每对token之间的"相似度"。如果序列有100个token,这就产生一个100×100的注意力矩阵——每个token都"看到"了所有其他token。
  2. $\frac{1}{\sqrt{d_k}}$:缩放因子,防止点积值过大导致softmax饱和(梯度消失)。这是2017年原始论文最重要的工程trick之一。
  3. 乘以$V$:按相似度权重对Value做加权聚合。思路简单但强大——"相似度高的信息获得更高权重"。

多头注意力:让模型同时关注不同方面

单头注意力只能关注一种模式。多头设计让模型同时从多个"子空间"提取信息——就像你同时关注零件的几何形状、配合公差和材料属性。

输入 X Q 投影 K 投影 V 投影 Scaled Dot-Productsoftmax(QKᵀ/√d)·V × h 个头并行 拼接Concat 线性投影Wₒ + 残差连接 & LayerNorm → 前馈网络(FFN) → + 残差连接 & LayerNorm → 堆叠 N 层

为什么Transformer统治了具身智能

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区"))  # → "放置"
🔧 工程连接:你在产线上用PLC梯形图做逻辑控制。BERT微调本质是在做"自然语言→逻辑动作"的映射——但比梯形图灵活得多,因为语言可以表达你从未编程过的指令。

3. CLIP:视觉-语言对齐

核心洞察

训练一个传统图像分类器需要标注"这是猫""这是狗"。CLIP的洞察是:互联网上有数十亿张图片和它们的文字描述——不需要人工标注,用"图片-文字"对直接训练,模型自动学会"猫"这个词和猫的图片之间的对应关系。

对比学习:拉近配对,推远不配对

Image EncoderViT / ResNet Text EncoderTransformer 余弦相似度N×N 矩阵 对角线 → 1非对角线 → 0 零样本能力 "一只猫" → 🐱 "螺丝刀" → 🔧 从未见过也能识别!

CLIP在机器人中的杀手级应用

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的三个阶段

① 离线索引操作手册→向量DB ② 在线检索查询→Top-K文档 ③ 增强生成Prompt+文档→决策 机器人执行

RAG在机器人中的典型场景

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' = W + \Delta W = W + \underbrace{BA}_{\text{低秩矩阵}}$

原始权重 $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%边缘设备/单卡微调
🔧 工程连接:你熟悉的"公差等级"概念在这里完美类比——FP32是IT5级精密加工,INT4是IT10级普通加工。在机器人产线场景,INT4精度的LLM任务规划几乎不损失可用性,但推理速度提升4倍、显存降低7倍。这就是工程权衡。

验收实验

  • 用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 仓库

视频课程

必读论文