Skills MCP Model 博客 提交 Skills
登录 注册

DeepSeek 多模态教程

DeepSeek VL2 视觉语言模型与 Janus 多模态理解及图像生成。从环境搭建到完整推理,涵盖 OCR、图表分析、视觉问答、文生图等全部核心能力。

开始学习

多模态模型概览

DeepSeek 推出了两款多模态模型:VL2 专注于视觉语言理解,Janus 则同时支持多模态理解与图像生成。两者在架构设计、能力侧重和适用场景上各有不同。

多模态模型概览 — VL2 与 Janus

理解 VL2 和 Janus 的核心差异,是选择合适模型的前提。以下从架构、能力、应用场景等维度进行全面对比。

VL2 与 Janus 定位对比

对比维度 DeepSeek VL2 DeepSeek Janus
核心能力 视觉语言理解(单向) 多模态理解 + 图像生成(双向)
架构设计 视觉编码器 + 语言模型投影 统一自回归 Transformer
图像理解 专业级,动态分辨率 支持,384x384 固定分辨率
图像生成 不支持 支持,文本到图像生成
OCR 能力 极强,动态分辨率加持 基础支持
模型规模 Tiny / Small / Full 三档 Janus-Pro-7B(7B 参数)
典型场景 OCR 文档识别、图表分析、视觉问答 图像描述、多模态对话、文生图创作

架构差异详解

DeepSeek VL2 采用经典的视觉编码器 + 大语言模型架构。SigLIP 作为视觉编码器提取图像特征,通过 MLP 投影层将视觉特征映射到语言模型的嵌入空间,再由 DeepSeekMoE 语言模型生成文本回复。VL2 支持动态分辨率(Dynamic Resolution),能将高分辨率图像切分为多个 tile 分别编码,再与全局缩略图一起送入模型,大幅提升 OCR 和细粒度视觉理解能力。

DeepSeek Janus 采用统一的 Transformer 自回归架构,将图像理解和生成统一在一个框架中。理解路径使用 SigLIP 编码器提取视觉特征并适配到 LLM;生成路径使用 VQ tokenizer 将图像转换为离散 token 序列,再由 LLM 自回归生成。Janus-Pro-7B 在 Janus 基础上进一步优化,提升了多模态理解性能和图像生成质量。

选型建议

  • 需要高精度 OCR 和文档理解:选择 VL2,动态分辨率机制让它在文字识别上表现卓越
  • 需要图像生成能力:选择 Janus,支持文本到图像生成,384x384 分辨率
  • 需要视觉问答和图表分析:VL2 是首选,视觉理解精度更高
  • 需要统一的多模态对话体验:Janus 在理解和生成之间切换更自然
  • 资源有限:VL2 Tiny(3B)或 VL2 Small(16B)可以在消费级 GPU 上运行

DeepSeek VL2 环境搭建

安装依赖、下载模型、配置环境。VL2 提供 Tiny/Small/Full 三种模型规模,可根据硬件配置灵活选择。

VL2 模型规模对比

模型版本 参数规模 视觉编码器 显存需求 适用场景
VL2-Tiny 3B SigLIP-SO400M 约 8GB 快速原型、移动端部署
VL2-Small 16B SigLIP-SO400M 约 40GB 研究、高精度 OCR
VL2-Full 27B(MoE) SigLIP-SO400M 约 80GB 专业级视觉理解、复杂文档分析

安装依赖

# 创建虚拟环境 python -m venv vl2-env vl2-env\Scripts\activate # 安装 PyTorch(根据 CUDA 版本选择) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装核心依赖 pip install transformers accelerate sentencepiece pip install pillow numpy opencv-python # 安装 huggingface_hub 用于下载模型 pip install huggingface_hub # 验证安装 python -c "import torch; import transformers; print('VL2 环境就绪')"

从 Hugging Face 下载模型

from huggingface_hub import snapshot_download # 下载 VL2-Tiny(推荐入门) snapshot_download( repo_id="deepseek-ai/deepseek-vl2-tiny", local_dir="./models/deepseek-vl2-tiny", local_dir_use_symlinks=False, resume_download=True, ) # 下载 VL2-Small(需要更多显存) # snapshot_download( # repo_id="deepseek-ai/deepseek-vl2-small", # local_dir="./models/deepseek-vl2-small", # local_dir_use_symlinks=False, # resume_download=True, # ) print("模型下载完成")

提示

如果下载速度慢,可以设置 HF 镜像:export HF_ENDPOINT=https://hf-mirror.com。更多模型下载技巧请查看 DeepSeek 模型下载指南

VL2 图像理解

VL2 支持图像描述、视觉问答、OCR 文字提取、图表分析和视觉定位等核心能力。以下逐一展示代码实现。

加载模型与图像

from transformers import AutoModelForCausalLM from deepseek_vl2.models import DeepseekVLV2Processor, DeepseekVLV2ForCausalLM import torch from PIL import Image # 加载模型和处理器 model_path = "./models/deepseek-vl2-tiny" vl2_processor = DeepseekVLV2Processor.from_pretrained(model_path) vl2_model = DeepseekVLV2ForCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True, ) # 加载图像 image = Image.open("images/document.jpg").convert("RGB") print(f"图像尺寸: {image.size}")

视觉问答(VQA)

# 单图视觉问答 conversation = [ { "role": "user", "content": [ {"type": "image", "image": "images/photo.jpg"}, {"type": "text", "text": "这张照片里有什么?请详细描述。"}, ], } ] # 准备输入 prepare_inputs = vl2_processor( conversations=conversation, images=[image], force_batchify=True, system_prompt="", ).to(vl2_model.device) # 推理 with torch.no_grad(): inputs_embeds = vl2_model.prepare_inputs_embeds(**prepare_inputs) outputs = vl2_model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, temperature=0.0, ) answer = vl2_processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) print("答案:", answer)

OCR 文字提取

# OCR 文字提取 — VL2 的核心优势 conversation = [ { "role": "user", "content": [ {"type": "image", "image": "images/document.jpg"}, {"type": "text", "text": "请提取这张图片中的所有文字,保持原始格式和排版。"}, ], } ] prepare_inputs = vl2_processor( conversations=conversation, images=[image], force_batchify=True, system_prompt="", ).to(vl2_model.device) with torch.no_grad(): inputs_embeds = vl2_model.prepare_inputs_embeds(**prepare_inputs) outputs = vl2_model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=1024, do_sample=False, ) ocr_text = vl2_processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) print("OCR 提取结果:\n", ocr_text)

图表理解

# 图表分析与数据提取 conversation = [ { "role": "user", "content": [ {"type": "image", "image": "images/chart.png"}, {"type": "text", "text": "请分析这张图表:1) 图表类型是什么?2) 横轴和纵轴分别代表什么?3) 数据趋势如何?4) 有什么关键发现?"}, ], } ] prepare_inputs = vl2_processor( conversations=conversation, images=[chart_image], force_batchify=True, system_prompt="", ).to(vl2_model.device) with torch.no_grad(): inputs_embeds = vl2_model.prepare_inputs_embeds(**prepare_inputs) outputs = vl2_model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, ) chart_analysis = vl2_processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) print("图表分析:", chart_analysis)

视觉定位(Visual Grounding)

# 视觉定位 — 在图像中定位特定对象 conversation = [ { "role": "user", "content": [ {"type": "image", "image": "images/street.jpg"}, {"type": "text", "text": "请定位图像中所有汽车的位置,用边界框坐标描述。"}, ], } ] prepare_inputs = vl2_processor( conversations=conversation, images=[image], force_batchify=True, system_prompt="", ).to(vl2_model.device) with torch.no_grad(): inputs_embeds = vl2_model.prepare_inputs_embeds(**prepare_inputs) outputs = vl2_model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, ) grounding_result = vl2_processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) print("定位结果:", grounding_result)

VL2 动态分辨率

动态分辨率是 VL2 的核心创新之一。它允许模型根据输入图像的实际尺寸,自适应地切分为多个 tile 进行处理,从而在高分辨率图像上保持细节。

动态分辨率机制原理

传统视觉模型通常将图像缩放到固定尺寸(如 384x384),导致高分辨率图像中的文字和细节丢失。VL2 的动态分辨率机制将图像切分为多个局部 tile(每个 tile 默认 384x384),同时保留一个全局缩略图,让模型既能理解整体布局,又能捕捉局部细节。

动态分辨率处理流程:

  • 根据图像宽高比计算最佳切分方案(如 2x2、3x2、1x3 等)
  • 将图像缩放到目标分辨率并切分为多个 tile
  • 生成一个全局缩略图(thumbnail),保留整体布局信息
  • 将所有 tile 和缩略图分别送入视觉编码器
  • 将编码后的视觉特征拼接,送入语言模型

配置动态分辨率

# VL2 处理器内置动态分辨率支持 # 通过 resolution 参数控制切分模式 # 自定义分辨率配置 from deepseek_vl2.models.processing_vlm import VLChatProcessor # 方式一:使用默认动态分辨率 vl2_processor = DeepseekVLV2Processor.from_pretrained( model_path, resolution="dynamic", # 动态分辨率模式 ) # 方式二:手动指定切分方式 # 设置候选分辨率列表 custom_resolutions = [ (1, 1), # 1x1 = 384x384 (1, 2), # 1x2 = 384x768 (2, 1), # 2x1 = 768x384 (2, 2), # 2x2 = 768x768 (2, 3), # 2x3 = 768x1152 (3, 2), # 3x2 = 1152x768 (3, 3), # 3x3 = 1152x1152 ] # 处理器会根据图像宽高比自动选择最匹配的分辨率 # 推理时无需额外配置,处理器自动处理 conversation = [ { "role": "user", "content": [ {"type": "image", "image": "images/high_res_doc.jpg"}, {"type": "text", "text": "请提取这张高分辨率文档中的所有文字。"}, ], } ] prepare_inputs = vl2_processor( conversations=conversation, images=[high_res_image], force_batchify=True, system_prompt="", ).to(vl2_model.device) # 查看实际使用的 tile 数量 num_tiles = prepare_inputs.get("images_seq_mask", torch.tensor([0])).sum().item() print(f"动态分辨率使用了 {num_tiles} 个 tile + 1 个全局缩略图")

动态分辨率 vs 固定分辨率

对比 固定分辨率 动态分辨率
OCR 精度 低,小字模糊 高,小字清晰可辨
推理速度 快,单 tile 较慢,多 tile 并行
显存占用 较高,随 tile 数增加
适用场景 快速预览、低分辨率图像 高精度 OCR、文档分析、图表阅读

最佳实践

对于文档 OCR 和图表分析,始终使用动态分辨率模式。对于简单的场景分类或物体识别,可以使用固定分辨率以节省计算资源。tile 数量建议控制在 9 个以内(约 1152x1152),超过后边际收益递减。

VL2 多轮视觉对话

VL2 支持多轮对话,可以在对话中切换不同图像,模型会记住上下文并持续回答。以下展示多轮对话的实现方式。

多轮对话代码实现

def vl2_multi_turn_chat(model, processor, image_paths, questions): """VL2 多轮视觉对话 Args: model: 加载的 VL2 模型 processor: VL2 处理器 image_paths: 每轮对话对应的图像路径列表 questions: 每轮对话的问题列表 """ conversation_history = [] images_so_far = [] for i, (img_path, question) in enumerate(zip(image_paths, questions)): # 加载当前轮图像 current_image = Image.open(img_path).convert("RGB") images_so_far.append(current_image) # 构建用户消息 user_content = [] for j, img in enumerate(images_so_far): user_content.append({"type": "image", "image": img}) user_content.append({"type": "text", "text": question}) conversation_history.append({ "role": "user", "content": user_content, }) # 准备输入 prepare_inputs = processor( conversations=conversation_history, images=images_so_far, force_batchify=True, system_prompt="", ).to(model.device) # 推理 with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, ) answer = processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) print(f"[第 {i+1} 轮] 问题: {question}") print(f"[第 {i+1} 轮] 回答: {answer}\n") # 将助手回复加入对话历史 conversation_history.append({ "role": "assistant", "content": answer, }) return conversation_history # 使用示例 images = ["images/doc_page1.jpg", "images/doc_page2.jpg"] questions = [ "第一页的主要内容是什么?", "和第二页相比,有什么变化?", ] history = vl2_multi_turn_chat(vl2_model, vl2_processor, images, questions)

对话上下文管理

在多轮对话中,需要注意以下几点:

  • 上下文长度限制:VL2 的上下文窗口有限,长对话需要裁剪历史或使用摘要策略
  • 图像累积策略:每轮可以选择是否携带历史图像,携带全部图像会消耗更多显存
  • 对话历史结构:保持 role (user/assistant) 和 content 的交替结构,确保模型理解对话流
  • 图像引用:在问题中明确引用图像(如"第一张图里的..."),避免模型混淆
  • 及时清理:切换对话主题时,建议重置 conversation_history 避免上下文污染

优化建议

对于长对话场景,建议在每轮只保留最近 3-5 轮对话作为上下文,避免 token 超限。如果需要对多张图像进行对比分析,可以一次性将所有图像放入单轮对话,而不是分轮发送。

Janus 环境搭建

Janus 是 DeepSeek 的统一多模态模型,同时支持图像理解和图像生成。Janus-Pro-7B 是最新版本,在理解和生成两方面都有显著提升。

安装依赖

# 创建虚拟环境 python -m venv janus-env janus-env\Scripts\activate # 安装 PyTorch pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 Janus 核心依赖 pip install transformers accelerate pip install pillow numpy pip install sentencepiece # 安装 huggingface_hub pip install huggingface_hub # 验证安装 python -c "import torch; import transformers; print('Janus 环境就绪')"

下载 Janus-Pro-7B

from huggingface_hub import snapshot_download # 下载 Janus-Pro-7B snapshot_download( repo_id="deepseek-ai/Janus-Pro-7B", local_dir="./models/Janus-Pro-7B", local_dir_use_symlinks=False, resume_download=True, ) # 也可下载 Janus-1.3B(轻量版) # snapshot_download( # repo_id="deepseek-ai/Janus-1.3B", # local_dir="./models/Janus-1.3B", # local_dir_use_symlinks=False, # resume_download=True, # ) print("Janus 模型下载完成")

Janus 模型规模对比

模型版本 参数规模 图像理解 图像生成 显存需求
Janus-1.3B 1.3B 基础支持 384x384 约 6GB
Janus-Pro-7B 7B 增强版 384x384(质量提升) 约 20GB

Janus 多模态理解

Janus 支持图像描述、视觉问答和多模态推理。虽然理解精度不如 VL2,但其统一架构让理解和生成可以无缝切换。

加载 Janus 模型

import torch from transformers import AutoModelForCausalLM from janus.models import MultiModalityCausalLM, VLChatProcessor from PIL import Image # 加载 Janus 模型 model_path = "./models/Janus-Pro-7B" vl_chat_processor = VLChatProcessor.from_pretrained(model_path) tokenizer = vl_chat_processor.tokenizer vl_gpt = MultiModalityCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True, ) vl_gpt.eval() print("Janus 模型加载完成")

图像描述(Image Captioning)

def janus_image_caption(model, processor, image_path): """Janus 图像描述""" image = Image.open(image_path).convert("RGB") conversation = [ { "role": "user", "content": [ {"type": "image", "image": image}, {"type": "text", "text": "请详细描述这张图片的内容。"}, ], }, ] prepare_inputs = processor( conversations=conversation, images=[image], force_batchify=True, ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, pad_token_id=tokenizer.eos_token_id, ) caption = tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) return caption # 使用 caption = janus_image_caption(vl_gpt, vl_chat_processor, "images/photo.jpg") print("图像描述:", caption)

视觉问答

def janus_vqa(model, processor, image_path, question): """Janus 视觉问答""" image = Image.open(image_path).convert("RGB") conversation = [ { "role": "user", "content": [ {"type": "image", "image": image}, {"type": "text", "text": question}, ], }, ] prepare_inputs = processor( conversations=conversation, images=[image], force_batchify=True, ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, pad_token_id=tokenizer.eos_token_id, ) answer = tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) return answer # 使用 answer = janus_vqa(vl_gpt, vl_chat_processor, "images/diagram.png", "这张图中有几个步骤?每个步骤是什么?") print("回答:", answer)

多模态推理

# Janus 支持基于图像内容进行逻辑推理 question = """请分析这张截图中的代码逻辑: 1. 这段代码实现了什么功能? 2. 是否有潜在的 bug? 3. 如何优化这段代码?""" answer = janus_vqa(vl_gpt, vl_chat_processor, "images/code_screenshot.png", question) print("多模态推理结果:\n", answer)

Janus 图像生成

Janus 支持文本到图像生成,输出 384x384 分辨率的图像。通过调整生成参数,可以控制图像的多样性和质量。

文本到图像生成

import numpy as np from janus.utils.io import save_image def janus_text_to_image(model, processor, prompt, output_path, **gen_kwargs): """Janus 文本到图像生成 Args: model: Janus 模型 processor: VLChatProcessor prompt: 图像生成提示词 output_path: 输出图像路径 gen_kwargs: 生成参数 """ # 构建生成对话 conversation = [ { "role": "user", "content": [ {"type": "text", "text": prompt}, ], }, ] prepare_inputs = processor( conversations=conversation, images=[], force_batchify=True, ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=gen_kwargs.get("max_new_tokens", 1024), do_sample=gen_kwargs.get("do_sample", True), temperature=gen_kwargs.get("temperature", 1.0), top_p=gen_kwargs.get("top_p", 0.95), pad_token_id=tokenizer.eos_token_id, ) # 解码生成的图像 token generated_ids = outputs[0].cpu().tolist() image_tokens = model.decode_image_tokens(generated_ids) # 将 token 转换为图像 image = model.gen_vision_model.decode_code( image_tokens.to(model.device), shape=[1, 8, 24, 24], # Janus 图像 shape ) # 保存图像 decoded_image = image[0].cpu().float().numpy().transpose(1, 2, 0) decoded_image = np.clip((decoded_image * 0.5 + 0.5) * 255, 0, 255).astype(np.uint8) Image.fromarray(decoded_image).save(output_path) print(f"图像已保存到: {output_path}") # 生成示例 prompt = "A serene lake at sunset with mountains in the background, oil painting style" janus_text_to_image( vl_gpt, vl_chat_processor, prompt=prompt, output_path="output/sunset_lake.png", temperature=1.0, do_sample=True, )

生成参数说明

参数 说明 推荐值
temperature 控制生成多样性。越高越随机,越低越确定 0.8 - 1.2
top_p 核采样阈值,控制候选 token 范围 0.9 - 0.95
max_new_tokens 生成的最大 token 数,影响图像细节 1024 - 2048
do_sample 是否使用采样。False 则使用贪心解码 True(生成图像)

批量图像生成

def janus_batch_generate(model, processor, prompts, output_dir="output"): """批量生成图像,每个 prompt 生成多张变体""" import os os.makedirs(output_dir, exist_ok=True) for idx, prompt in enumerate(prompts): for variant in range(4): # 每个 prompt 生成 4 张 output_path = f"{output_dir}/gen_{idx+1}_v{variant+1}.png" janus_text_to_image( model, processor, prompt=prompt, output_path=output_path, temperature=0.9 + variant * 0.1, # 每张略有不同 do_sample=True, ) print(f"批量生成完成,共 {len(prompts) * 4} 张图像") # 使用 prompts = [ "A futuristic city with flying cars at night, neon lights", "A traditional Chinese garden with a koi pond and pavilion", "A cute robot reading a book under a tree", ] janus_batch_generate(vl_gpt, vl_chat_processor, prompts)

提示

Janus 的图像生成基于自回归 Transformer,生成速度取决于 max_new_tokens。384x384 分辨率适合大多数场景,如需更高分辨率可以后期使用超分辨率模型放大。提示词建议用英文撰写,描述越具体,生成效果越好。

VL2 与 Janus 全面对比

从架构、能力、模型规模、推理速度和适用场景等维度,全面对比 VL2 和 Janus,帮助你做出正确的模型选择。

核心能力对比

能力维度 DeepSeek VL2 DeepSeek Janus (Pro-7B)
OCR 文字识别 极强(动态分辨率加持) 一般
图表/文档理解 专业级 基础支持
视觉问答 高精度 中等
图像描述 详细精准 自然流畅
图像生成 不支持 支持(384x384)
纯文本对话 支持 支持(更自然)
推理速度 中等(多 tile 时较慢) 较快(固定分辨率)
模型规模 3B / 16B / 27B(MoE) 1.3B / 7B

适用场景推荐

场景 推荐模型 理由
文档 OCR 识别 VL2 动态分辨率,小字清晰
财务报表分析 VL2 图表理解 + 数据提取
多模态聊天机器人 Janus 理解 + 生成统一体验
AI 绘画应用 Janus 原生支持文生图
医学影像分析 VL2 高精度细粒度识别
创意内容生成 Janus 理解 + 生成闭环

组合使用方案

在实际项目中,VL2 和 Janus 可以互补使用:

  • VL2 做理解 + Janus 做生成:用 VL2 高精度理解用户上传的图像,提取关键信息后,用 Janus 生成回复或新图像
  • VL2 做预处理 + Janus 做对话:VL2 负责 OCR 和文档解析,Janus 负责自然语言对话和创意生成
  • 按场景路由:文档类请求路由到 VL2,创意类请求路由到 Janus,实现资源最优利用

更多模型对比和选型指南,请查看 DeepSeek 开源模型列表DeepSeek 模型架构详解

实战:多模态 AI 应用

将 VL2 和 Janus 整合到一个完整的 Web 应用中,支持图像上传、视觉问答和图像生成。使用 Streamlit 构建交互式 UI。

完整应用代码:multimodal_app.py

"""DeepSeek 多模态 AI 应用 — VL2 理解 + Janus 生成""" import streamlit as st import torch from PIL import Image import os # 页面配置 st.set_page_config( page_title="DeepSeek 多模态 AI", page_icon=None, layout="wide", ) st.title("DeepSeek 多模态 AI 应用") st.markdown("支持 VL2 视觉理解与 Janus 图像生成") # 侧边栏:模型选择 with st.sidebar: st.header("模型配置") model_choice = st.radio( "选择模型", ["DeepSeek VL2 (视觉理解)", "DeepSeek Janus (理解+生成)"], ) if "VL2" in model_choice: vl2_size = st.selectbox( "VL2 模型规模", ["Tiny (3B)", "Small (16B)", "Full (27B MoE)"], ) st.caption("推荐 Tiny 用于快速测试,Full 用于生产环境") if "Janus" in model_choice: st.caption("使用 Janus-Pro-7B 模型") gen_mode = st.radio("模式", ["图像理解", "图像生成"]) st.divider() st.markdown("### 关于") st.markdown("DeepSeek 多模态模型实战教程") st.markdown("VL2: OCR / 图表 / 视觉问答") st.markdown("Janus: 理解 + 文生图") # 加载模型(缓存) @st.cache_resource def load_vl2_model(size="tiny"): """加载 VL2 模型""" from deepseek_vl2.models import DeepseekVLV2Processor, DeepseekVLV2ForCausalLM model_map = { "tiny": "deepseek-ai/deepseek-vl2-tiny", "small": "deepseek-ai/deepseek-vl2-small", "full": "deepseek-ai/deepseek-vl2", } model_path = model_map.get(size, model_map["tiny"]) processor = DeepseekVLV2Processor.from_pretrained(model_path) model = DeepseekVLV2ForCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True, ) return model, processor @st.cache_resource def load_janus_model(): """加载 Janus 模型""" from janus.models import MultiModalityCausalLM, VLChatProcessor model_path = "deepseek-ai/Janus-Pro-7B" processor = VLChatProcessor.from_pretrained(model_path) model = MultiModalityCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True, ) model.eval() return model, processor # 主界面 if "VL2" in model_choice: st.header("VL2 视觉理解") uploaded_file = st.file_uploader( "上传图像", type=["png", "jpg", "jpeg", "webp"], help="支持常见图像格式", ) question = st.text_input( "输入你的问题", placeholder="例如:请提取这张图片中的所有文字...", ) task_type = st.selectbox( "任务类型", ["通用问答", "OCR 文字提取", "图表分析", "视觉定位", "图像描述"], ) if uploaded_file and question: image = Image.open(uploaded_file).convert("RGB") col1, col2 = st.columns(2) with col1: st.image(image, caption="上传的图像", use_container_width=True) if st.button("开始分析", type="primary"): with col2: with st.spinner("VL2 正在分析中..."): # 根据任务类型构建提示词 task_prompts = { "通用问答": question, "OCR 文字提取": f"请提取这张图片中的所有文字,保持原始格式:{question}", "图表分析": f"请分析这张图表:{question}", "视觉定位": f"请定位图像中的目标对象:{question}", "图像描述": "请详细描述这张图片的内容。", } # 调用 VL2 推理 model, processor = load_vl2_model(vl2_size.lower().split()[0]) conversation = [{ "role": "user", "content": [ {"type": "image", "image": image}, {"type": "text", "text": task_prompts[task_type]}, ], }] prepare_inputs = processor( conversations=conversation, images=[image], force_batchify=True, system_prompt="", ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=1024, do_sample=False, ) answer = processor.tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) st.markdown("### 分析结果") st.markdown(answer) elif "Janus" in model_choice: if gen_mode == "图像理解": st.header("Janus 图像理解") uploaded_file = st.file_uploader( "上传图像", type=["png", "jpg", "jpeg", "webp"], ) question = st.text_input( "输入你的问题", placeholder="请描述这张图片...", ) if uploaded_file and question: image = Image.open(uploaded_file).convert("RGB") col1, col2 = st.columns(2) with col1: st.image(image, caption="上传的图像", use_container_width=True) if st.button("开始分析", type="primary"): with col2: with st.spinner("Janus 正在分析中..."): model, processor = load_janus_model() tokenizer = processor.tokenizer conversation = [{ "role": "user", "content": [ {"type": "image", "image": image}, {"type": "text", "text": question}, ], }] prepare_inputs = processor( conversations=conversation, images=[image], force_batchify=True, ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=512, do_sample=False, pad_token_id=tokenizer.eos_token_id, ) answer = tokenizer.decode( outputs[0].cpu().tolist(), skip_special_tokens=True, ) st.markdown("### 分析结果") st.markdown(answer) else: st.header("Janus 图像生成") prompt = st.text_area( "输入生成提示词(建议使用英文)", placeholder="A serene lake at sunset with mountains in the background, oil painting style", height=100, ) col1, col2 = st.columns(2) with col1: temperature = st.slider("Temperature", 0.5, 1.5, 1.0, 0.1) with col2: num_images = st.selectbox("生成数量", [1, 2, 4], index=0) if prompt and st.button("生成图像", type="primary"): model, processor = load_janus_model() tokenizer = processor.tokenizer for i in range(num_images): with st.spinner(f"正在生成第 {i+1}/{num_images} 张图像..."): conversation = [{ "role": "user", "content": [{"type": "text", "text": prompt}], }] prepare_inputs = processor( conversations=conversation, images=[], force_batchify=True, ).to(model.device) with torch.no_grad(): inputs_embeds = model.prepare_inputs_embeds(**prepare_inputs) outputs = model.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, max_new_tokens=1024, do_sample=True, temperature=temperature, top_p=0.95, pad_token_id=tokenizer.eos_token_id, ) generated_ids = outputs[0].cpu().tolist() image_tokens = model.decode_image_tokens(generated_ids) gen_image = model.gen_vision_model.decode_code( image_tokens.to(model.device), shape=[1, 8, 24, 24], ) import numpy as np decoded = gen_image[0].cpu().float().numpy().transpose(1, 2, 0) decoded = np.clip((decoded * 0.5 + 0.5) * 255, 0, 255).astype(np.uint8) st.image(decoded, caption=f"生成结果 {i+1}", use_container_width=True) # 页脚 st.divider() st.caption("DeepSeek 多模态 AI 应用 | VL2 + Janus | 本地部署,数据安全")

安装与运行

# 安装 Streamlit pip install streamlit # 运行应用 streamlit run multimodal_app.py # 浏览器打开 # http://localhost:8501

应用功能说明

  • VL2 视觉理解:支持图像上传、多任务类型选择(OCR/图表/定位/描述)、自定义提问
  • Janus 图像理解:上传图像并进行自然语言问答
  • Janus 图像生成:文本到图像生成,支持调整温度和批量生成
  • 模型缓存:使用 Streamlit 缓存机制,模型只加载一次
  • 侧边栏配置:灵活切换模型和模式,调整生成参数

部署建议

生产环境建议使用 VL2-Tiny 或 Janus-1.3B 以降低硬件成本。如需高性能推理,可使用 vLLM 或 TGI 部署模型服务,前端通过 API 调用。详见 DeepSeek 部署教程

DeepSeek 多模态常见问题

DeepSeek VL2 和 Janus 应该选哪个? +
取决于你的需求:如果需要高精度 OCR、文档分析和图表理解,选 VL2(动态分辨率机制让它在文字识别上表现卓越);如果需要图像生成能力或统一的多模态对话体验,选 Janus。两者也可以组合使用,VL2 做理解预处理,Janus 做生成和对话。
VL2 的动态分辨率需要多少显存? +
显存消耗与 tile 数量成正比。VL2-Tiny 在 1x1 tile 时约需 8GB 显存,2x2 tile 时约需 12GB,3x3 tile 时约需 18GB。建议根据实际图像尺寸和 GPU 显存调整 tile 数量。VL2-Small 和 VL2-Full 需要更多显存,推荐使用 A100 或 H100 等高端 GPU。
Janus 生成的图像可以放大吗? +
可以。Janus 原生输出 384x384 分辨率,可以使用超分辨率模型(如 Real-ESRGAN、SwinIR)将图像放大到 1024x1024 或更高。也可以使用 Stable Diffusion 的 img2img 功能进行高清修复。建议在 Janus 生成后,用超分辨率模型做后处理以获得更好的视觉效果。
VL2 和 Janus 可以在 CPU 上运行吗? +
可以,但速度会很慢。VL2-Tiny 和 Janus-1.3B 可以在 CPU 上运行,推理时间可能从 GPU 的几秒增加到几十秒甚至几分钟。推荐至少使用 8GB 显存的 GPU(如 RTX 3060/4060)来运行轻量版本。如果没有 GPU,可以考虑使用云端 GPU 服务或 DeepSeek 官方 API。
多模态模型的推理速度如何优化? +
1) 使用 bfloat16 或 int8 量化减少显存和计算量;2) VL2 中减少 tile 数量(牺牲部分精度换速度);3) 使用 Flash Attention 加速注意力计算;4) 使用 vLLM 或 TensorRT-LLM 部署推理服务;5) 批量处理多张图像,充分利用 GPU 并行能力。对于生产环境,强烈建议使用 vLLM 部署。
VL2 和 Janus 可以微调吗? +
可以。两个模型都支持使用 LoRA 或 QLoRA 进行参数高效微调。VL2 适合在特定领域的文档、图表数据上微调以提升 OCR 和理解精度;Janus 适合在特定风格的图像数据上微调以改善生成效果。微调方法详见 DeepSeek 模型微调教程

DeepSeek 相关教程

深入学习 DeepSeek 模型的使用、部署和生态工具。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

完全免费,取消任意时间。我们不会发送垃圾邮件。