多模态+GUI Agent:从零理解大模型如何看懂图片和操作电脑
多模态+GUI Agent:从零理解大模型如何"看懂"图片和"操作"电脑
Chapter 8/9 完整实战解析。从多模态模型架构详解、CLIP跨模态对齐、LLaVA视觉指令微调,到GUI Agent的工作原理、完整代码框架、实际应用场景,附华为昇腾版适配差异分析。
一、为什么需要多模态模型?
问题: 纯文本大模型(GPT-4 text-only)只能处理文字,无法理解图片、视频、音频。
应用场景:
- 图像描述(Image Captioning): 输入图片 → 输出描述
- 视觉问答(VQA): 输入图片 + 问题 → 输出答案
- 光学字符识别(OCR): 输入图片(含文字)→ 输出文字
- GUI Agent: 输入界面截图 → 输出下一步操作(点击、输入等)
二、多模态模型的核心问题:怎么让语言模型"看懂"图片?
核心思路: 把图片编码成和文本同一个空间的向量,然后像处理文本token一样处理图片向量。
架构图:
图片 → Vision Encoder(如ViT) → 图像嵌入向量 [N, hidden_dim]
↓
和文本嵌入向量拼接 [N+M, hidden_dim]
↓
LLM(如LLaMA)→ 生成回答
关键: Vision Encoder的输出维度,必须和LLM的嵌入维度一致(或通过一个投影层映射)。
三、CLIP:跨模态对齐的开山之作
核心思想: 用对比学习,让配对的图片-文本嵌入向量相似度高,不配对的相似度低。
3.1 CLIP的训练数据
从互联网上收集4亿对(图片,alt-text)数据。
示例:
图片:一张狗在草地上跑的图片
文本:"A dog running on grass"
(这是配对的)
图片:一张狗在草地上跑的图片
文本:"A cat sleeping on sofa"
(这是不配对的)
3.2 CLIP的训练目标
import torch
import torch.nn.functional as F
# 1. 编码图片和文本
image_embed = vision_encoder(images) # [batch, embedding_dim]
text_embed = text_encoder(texts) # [batch, embedding_dim]
# 2. 归一化(让向量都在单位球上)
image_embed = F.normalize(image_embed, dim=-1)
text_embed = F.normalize(text_embed, dim=-1)
# 3. 计算相似度矩阵
logit_scale = self.logit_scale.exp() # 可学习的温度参数
logits = image_embed @ text_embed.t() * logit_scale # [batch, batch]
# 4. 对比学习损失
# 对角线是配对的(正例),非对角线是不配对的(负例)
labels = torch.arange(len(images), device=images.device)
loss = (
F.cross_entropy(logits, labels) +
F.cross_entropy(logits.t(), labels)
) / 2
直观解释:
logits[i, i]:第i张图片和第i段文本的相似度(应该是最高的)logits[i, j](i≠j):第i张图片和第j段文本的相似度(应该很低)- 训练目标:让对角线元素尽可能大,非对角线元素尽可能小
3.3 CLIP的训练结果
训练完成后,CLIP可以实现:
- 图文检索: 给定文本,找最匹配的图片;或给定图片,找最匹配的文本
- 零样本分类: 不用任何训练数据,就能做图像分类
# 零样本分类(不用训练!)
image = preprocess_image("dog.jpg")
image_embed = vision_encoder(image)
# 用文本描述各个类别
categories = ["a photo of a dog", "a photo of a cat", "a photo of a car"]
text_embeds = text_encoder(categories)
# 计算相似度
similarities = image_embed @ text_embeds.t()
predicted_idx = similarites.argmax()
print(f"预测类别: {categories[predicted_idx]}")
# 输出: a photo of a dog
为什么CLIP重要?
- 证明了对比学习可以让视觉和语言在同一个语义空间里表示
- 后续的多模态大模型(LLaVA、Flamingo等) 都用了CLIP的Vision Encoder
四、LLaVA:让LLM能理解图片(视觉指令微调)
CLIP的问题: 只能做"图片-文本匹配",不能"理解图片并对话"。
LLaVA的解决方案: 把CLIP的Vision Encoder和LLM(Vicuna,基于LLaMA微调)连接起来,做视觉指令微调。
4.1 LLaVA架构
输入:图片 + 问题
第1步:图片 → CLIP Vision Encoder → 图像嵌入 [256, 768]
↓
投影层(MLP) → 视觉token [256, 5120] (对齐到LLM的hidden_dim)
第2步:问题 → LLM Tokenizer → 文本token [N, 5120]
第3步:拼接 视觉token + 文本token → [256+N, 5120]
第4步:输入LLM(Vicuna)→ 自回归生成回答
关键创新: 投影层把视觉token"翻译"成语言模型能理解的"视觉单词"。
4.2 视觉指令微调数据
数据格式:
{
"image": "image_001.jpg",
"conversations": [
{"from": "human", "value": "<image>\n请描述这张图片。"},
{"from": "gpt", "value": "这是一张在草地上玩耍的狗的图片。."}
]
}
训练目标: 给定图片和对话历史,预测助手的回复(自回归语言建模)。
# 伪代码
image = load_image("image_001.jpg")
vision_tokens = project(clip_vision_encoder(image)) # [256, 5120]
question = tokenizer("<image>\n请描述这张图片。") # [N]
text_tokens = embedding(question) # [N, 5120]
# 拼接
input_tokens = torch.cat([vision_tokens, text_tokens], dim=0) # [256+N, 5120]
# LLM生成回答
output = llm(input_tokens)
# 计算交叉熵损失:预测助手的回复
loss = cross_entropy(output.logits, target_answer)
4.3 LLaVA的实验效果
数据集: COCO、GQA、OCR-VQA等
评估指标: 准确率(VQA任务)、BLEU/CIDEr(描述生成任务)
结果:
| 模型 | VQA准确率 | 描述生成BLEU |
|---|---|---|
| CLIP(零样本) | 42.3% | - |
| BLIP-2 | 65.8% | 131.2 |
| LLaVA(视觉指令微调) | 70.1% | 138.7 |
结论: 视觉指令微调让模型不仅能"匹配"图片和文本,还能"理解"图片并对话。
五、GUI Agent:让大模型"操作"电脑
核心问题: 怎么让大模型像人一样操作电脑(点击、输入、拖拽)?
应用场景:
- RPA(机器人流程自动化): 自动填写表单、自动爬取数据
- 无障碍辅助: 帮助视障人士操作电脑
- 游戏AI: 让AI玩电脑游戏
5.1 GUI Agent的工作流程
第1步:截图当前界面 → 用多模态模型(如LLaVA)理解界面元素
↓
输出:界面描述("屏幕左上角有一个'搜索'按钮,中间是一个文本框...")
第2步:模型输出下一步动作 → {"action": "click", "element": "搜索框"}
第3步:执行动作(用PyAutoGUI或ADB)→ 点击搜索框
第4步:截图新的界面 → 重复第1-3步
5.2 完整代码框架
import pyautogui
from PIL import Image
import base64
import io
class GUIAgent:
def __init__(self, model):
"""
model: 多模态大模型(如LLaVA、GPT-4V)
"""
self.model = model
def perceive(self):
"""截图并理解界面"""
# 1. 截图
screenshot = pyautogui.screenshot()
screenshot.save("/tmp/screen.png")
# 2. 让多模态模型理解界面
prompt = """
请描述当前界面上的可交互元素及其位置。
格式:
- 元素1:<元素类型>,位于(x1, y1),<功能描述>
- 元素2:.
"""
description = self.model.generate(
image="/tmp/screen.png",
prompt=prompt
)
return description
def plan(self, goal, perception):
"""规划下一步动作"""
prompt = f"""
目标:{goal}
当前界面:{perception}
下一步应该做什么动作?用JSON格式输出。
可选动作:
- {{"action": "click", "x": 100, "y": 200}}
- {{"action": "type", "text": "搜索内容"}}
- {{"action": "scroll", "direction": "down"}}
- {{"action": "done", "result": "任务完成"}}
"""
response = self.model.generate(prompt)
# 解析JSON
action = json.loads(extract_json(response))
return action
def execute(self, action):
"""执行动作"""
if action["action"] == "click":
pyautogui.click(action["x"], action["y"])
elif action["action"] == "type":
pyautogui.typewrite(action["text"])
elif action["action"] == "scroll":
pyautogui.scroll(action["direction"])
elif action["action"] == "done":
return action["result"]
def run(self, goal, max_steps=20):
"""运行Agent"""
for step in range(max_steps):
print(f"Step {step+1}:")
# 1. 感知
perception = self.perceive()
print(f" 感知: {perception[:100]}...")
# 2. 规划
action = self.plan(goal, perception)
print(f" 动作: {action}")
# 3. 执行
result = self.execute(action)
if result:
print(f" 结果: {result}")
break
# 4. 等待界面更新
time.sleep(1)
5.3 实际应用场景
场景1:自动填写表单
# 目标:打开浏览器,搜索"大模型教程",并点击第一个结果
goal = "打开Chrome浏览器,搜索'大模型教程',点击第一个搜索结果。"
agent = GUIAgent(model=llava_model)
agent.run(goal)
场景2:自动爬取数据
# 目标:打开某电商网站,搜索"手机",爬取前10个商品的名称和价格
goal = """
1. 打开浏览器,访问https://www.example.com
2. 在搜索框输入"手机",点击搜索
3. 爬取前10个商品的名称和价格,保存为CSV
4. 完成后通知我
"""
agent = GUIAgent(model=llava_model)
agent.run(goal)
5.4 GUI Agent的挑战
挑战1:界面理解不准确
- 多模态模型可能漏掉界面上的小元素
- 解决:用更大的Vision Encoder(如ViT-L/14),或集成专门的UI元素检测模型(如YOLO)
挑战2:动作执行失败
- 点击坐标不准确(模型输出的x,y可能偏差10-20像素)
- 解决:用模板匹配(Template Matching)精确定位元素
挑战3:任务规划能力不足
- 模型可能"不知道下一步该做什么"(特别是在复杂任务中)
- 解决:用思维链(CoT) 让模型先规划再执行
def plan(self, goal, perception):
prompt = f"""
目标:{goal}
当前界面:{perception}
让我们一步步思考:
1. 当前界面是什么?
2. 为了达到目标,下一步应该做什么?
3. 具体的动作参数是什么(x,y坐标)?
请输出JSON格式的动作。
"""
# ...
六、华为昇腾版教程:国产化适配差异
前面讲的实现都是基于PyTorch + CUDA(NVIDIA GPU)。
华为昇腾版教程适配了昇腾910B NPU,使用MindSpore + CANN框架。
6.1 昇腾版 vs 标准版对比
| 维度 | 标准版(PyTorch) | 昇腾版(MindSpore) |
|---|---|---|
| 深度学习框架 | PyTorch | MindSpore |
| 硬件加速 | CUDA(NVIDIA GPU) | CANN(昇腾NPU) |
| 多模态模型 | CLIP(PyTorch实现) | CLIP(MindSpore实现) |
| LLM | LLaMA(PyTorch) | 盘古(MindSpore) |
| 性能 | A100: 100% | 昇腾910B: ~75% |
6.2 昇腾版代码差异(示例)
标准版(PyTorch):
import torch
import torch.nn as nn
class MultiModalModel(nn.Module):
def __init__(self, vision_encoder, text_encoder):
super().__init__()
self.vision_encoder = vision_encoder
self.text_encoder = text_encoder
self.projector = nn.Linear(768, 5120)
def forward(self, images, texts):
image_embeds = self.vision_encoder(images)
text_embeds = self.text_encoder(texts)
image_embeds = self.projector(image_embeds)
return torch.cat([image_embeds, text_embeds], dim=1)
昇腾版(MindSpore):
import mindspore as ms
import mindspore.nn as nn
class MultiModalModel(nn.Cell):
def __init__(self, vision_encoder, text_encoder):
super().__init__()
self.vision_encoder = vision_encoder
self.text_encoder = text_encoder
self.projector = nn.Dense(768, 5120)
def construct(self, images, texts):
image_embeds = self.vision_encoder(images)
text_embeds = self.text_encoder(texts)
image_embeds = self.projector(image_embeds)
return ms.ops.concat([image_embeds, text_embeds], axis=1)
主要差异:
nn.Module→nn.Cellforward()→construct()torch.cat()→ms.ops.concat()
七、从Chapter 1到Chapter 11的学习路线图
如果你是从零开始学多模态和GUI Agent:
Week 1-2: Chapter 1(综述)+ Chapter 8(多模态基础知识)
- 理解CLIP的对比学习
- 跑通CLIP的图文检索demo
Week 3-4: Chapter 8(LLaVA架构)+ Chapter 9(GUI Agent基础)
- 理解视觉指令微调
- 跑通LLaVA的VQA demo
Week 5-6: Chapter 9(GUI Agent实战)
- 用PyAutoGUI实现简单的界面操作
- 集成LLaVA做界面理解
Week 7-8: 进阶项目
- 做一个"自动填写表单"的GUI Agent
- 优化界面理解的准确率
八、总结与下一步
本文覆盖内容:
- ✅ 多模态模型的核心问题(怎么让LLM"看懂"图片)
- ✅ CLIP的对比学习原理与代码实现
- ✅ LLaVA的视觉指令微调架构与训练方法
- ✅ GUI Agent的完整工作流程与代码框架
- ✅ 实际应用场景(自动填写表单、自动爬取数据)
- ✅ 华为昇腾版教程的差异分析
下一步学习方向:
- 想深入了解多模态模型的训练细节 → 读LLaVA的论文 + 跑通官方代码
- 想做GUI Agent的项目 → 从简单的"自动搜索"任务开始,逐步增加复杂度
- 想研究多模态的安全问题 → 见Chapter 5/6(越狱攻击、对抗样本)
九、面试高频问题
Q1:CLIP的训练目标是什么?
A:对比学习。让配对的图片-文本嵌入向量相似度高,不配对的相似度低。用交叉熵损失训练。
Q2:LLaVA怎么让LLM理解图片?
A:用CLIP Vision Encoder把图片编码成视觉token,通过投影层对齐到LLM的隐藏维度,然后和文本token拼接,输入LLM做自回归生成。
Q3:GUI Agent的工作流程?
A:①截图当前界面 → ②用多模态模型理解界面元素 → ③模型输出下一步动作(JSON格式)→ ④执行动作(PyAutoGUI)→ ⑤重复①-④。
Q4:昇腾版和标准版的主要差异?
A:框架(PyTorch vs MindSpore)、硬件(CUDA vs CANN)、模型(LLaMA vs 盘古)。代码语法有差异,但核心思想一致。
系列总结:
本系列5篇文章,完整覆盖了上海交通大学《动手学大模型 Dive into LLMs》教程的11个章节。从微调与部署、Prompt工程与思维链,到大模型安全(越狱攻击、水印、知识编辑),再到多模态与GUI Agent,每一篇都有完整代码、深度分析和实战建议。
全套资源获取方式:
- GitHub仓库:
https://github.com/Lordog/dive-into-llms - PPT课件:
documents/目录 - 华为昇腾版:仓库README里有单独链接
如果你有任何问题,欢迎在评论区留言!我会一一回复。
系列文章导航:
- [第一篇:交大爆火3万Star开源教程全套资源深度解析]
- [第二篇:从零微调到部署:手把手用Transformers跑通BERT分类]
- [第三篇:Prompt工程+思维链:让大模型从"答非所问"到"举一反三"]
- [第四篇:大模型安全三讲:越狱攻击+水印+知识编辑实战]
- [第五篇(本文):多模态+GUI Agent:从零理解大模型如何"看懂"图片和"操作"电脑]
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)