GLM-4V-9B Streamlit实战教程:自定义系统提示词与对话上下文管理

你是不是也遇到过这种情况:好不容易部署了一个多模态大模型,上传了图片,问了问题,结果它要么给你一堆乱码,要么就只会复读你的问题,完全答非所问?

我之前用GLM-4V-9B的官方示例时就经常被这个问题困扰。模型明明很强大,但用起来就是不对劲。后来我发现,问题出在两个方面:一是官方代码在某些环境下有兼容性问题,二是对话的上下文管理不够灵活。

今天,我就带你一步步搭建一个基于Streamlit的GLM-4V-9B本地部署方案。这个方案不仅解决了官方的兼容性问题,还增加了两个超实用的功能:自定义系统提示词完整的对话上下文管理。这意味着你可以像调教ChatGPT一样,告诉模型“你是一个专业的图像分析师”,然后它就会用更专业的口吻来回答你的问题。

更重要的是,我们实现了4-bit量化加载,这意味着你不需要昂贵的专业显卡,用消费级的RTX 4060 Ti(16GB)甚至更低的配置就能流畅运行这个百亿参数的多模态大模型。

1. 环境准备与一键部署

在开始之前,我们先看看需要准备什么。整个过程比你想的要简单得多。

1.1 硬件与软件要求

硬件要求(最低配置):

  • GPU:NVIDIA显卡,显存 ≥ 12GB(推荐16GB以上)
  • 内存:16GB RAM
  • 存储:至少20GB可用空间(用于下载模型)

软件要求:

  • 操作系统:Linux(Ubuntu 20.04+)或 Windows(WSL2)
  • Python:3.8 - 3.10版本
  • CUDA:11.7或11.8(与PyTorch版本匹配)

如果你不确定自己的环境,可以运行以下命令检查:

# 检查Python版本
python --version

# 检查CUDA是否可用(如果已安装PyTorch)
python -c "import torch; print(f'PyTorch版本: {torch.__version__}')"
python -c "import torch; print(f'CUDA可用: {torch.cuda.is_available()}')"

1.2 快速安装步骤

我们的项目已经打包好了所有依赖,你只需要几个命令就能完成部署:

# 1. 克隆项目代码
git clone https://github.com/your-repo/glm-4v-9b-streamlit.git
cd glm-4v-9b-streamlit

# 2. 创建并激活虚拟环境(推荐)
python -m venv venv
# Linux/Mac
source venv/bin/activate
# Windows
venv\Scripts\activate

# 3. 安装依赖包
pip install -r requirements.txt

# 4. 下载模型(如果首次运行会自动下载,也可以手动下载加速)
# 模型会自动下载到 ~/.cache/huggingface/hub 目录

这里有个小技巧:如果你在国内,下载Hugging Face模型可能会比较慢。可以设置镜像源来加速:

# 设置Hugging Face镜像(在运行程序前设置环境变量)
export HF_ENDPOINT=https://hf-mirror.com

# Windows系统用这个命令
# set HF_ENDPOINT=https://hf-mirror.com

1.3 启动应用

安装完成后,启动应用只需要一行命令:

# 启动Streamlit应用
streamlit run app.py --server.port 8080

启动后,打开浏览器访问 http://localhost:8080,你就能看到清爽的聊天界面了。

第一次运行时会自动下载GLM-4V-9B模型(约20GB),根据你的网速可能需要一些时间。下载完成后,模型会缓存在本地,下次启动就很快了。

2. 核心功能上手:从看图说话到专业分析

现在应用已经跑起来了,让我们看看它能做什么。界面很简单,主要分三个区域:左侧是设置面板,中间是聊天区域,右侧是历史记录。

2.1 基础使用:让模型看懂图片

我们先从最简单的开始。在左侧面板上传一张图片,然后在底部的输入框问问题。

试试这几个问题:

  • "详细描述这张图片的内容"
  • "图片里有多少个人?"
  • "提取图片中的所有文字"
  • "这张图是在哪里拍的?"

你会发现,模型不仅能识别物体,还能理解场景、情感甚至一些隐含信息。比如你上传一张餐桌照片,它不仅能说出有什么菜,还能推断出这可能是一顿家庭晚餐。

2.2 自定义系统提示词:让模型扮演特定角色

这是我们的核心功能之一。在左侧面板的"系统提示词"输入框中,你可以定义模型的角色和行为。

几个实用的提示词示例:

# 专业图像分析师
你是一个专业的图像分析师,擅长从图片中提取结构化信息。请用专业的术语描述图片内容,并给出详细的分析报告。

# 创意故事讲述者
你是一个富有想象力的故事讲述者。请根据图片内容创作一个有趣的故事,包含人物、情节和结局。

# 技术文档助手
你是一个技术文档助手,擅长从图表和截图中提取技术信息。请用清晰、准确的语言描述图片中的技术细节。

# 语言学习伙伴
你是一个语言学习伙伴,请用简单的中文描述图片内容,适合中文学习者理解。可以适当加入词汇解释。

设置好系统提示词后,你再问同样的问题,模型的回答风格会完全不同。比如用"专业图像分析师"提示词时,它会用更结构化的方式回答,可能还会加上"分析结论"、"建议"这样的部分。

2.3 对话上下文管理:实现真正的多轮对话

多模态对话的难点在于如何管理历史记录。我们的方案完美解决了这个问题。

上下文管理的特点:

  1. 完整的对话历史:每次对话(图片+问题+回答)都会被记录下来
  2. 智能上下文截断:当对话轮数太多时,会自动保留最重要的部分
  3. 可视化历史记录:右侧面板可以查看所有历史对话
  4. 一键清除:可以随时清空对话历史,开始新的会话

实际使用场景: 假设你上传了一张设计稿,可以这样对话:

  • 第一轮:"描述这个UI设计的主要元素"
  • 第二轮:"登录按钮的颜色是什么?"(模型能记住这是同一张图)
  • 第三轮:"这个配色方案有什么优缺点?"
  • 第四轮:"如果要把主色调改为蓝色,需要调整哪些部分?"

模型能记住整个对话过程,回答会越来越精准。

3. 代码深度解析:我们解决了哪些问题

你可能好奇,为什么我们的版本比官方示例更稳定、功能更强大?下面我拆解几个关键的技术点。

3.1 解决兼容性问题:动态类型适配

官方代码在某些PyTorch/CUDA环境下会报错,主要是因为数据类型不匹配。我们是这样解决的:

def prepare_image_tensor(image_path, model, device):
    """
    准备图片张量,自动适配模型视觉层的参数类型
    解决 RuntimeError: Input type and bias type should be the same 错误
    """
    from PIL import Image
    import torch
    from transformers import AutoProcessor
    
    # 加载图片
    image = Image.open(image_path).convert('RGB')
    
    # 获取处理器
    processor = AutoProcessor.from_pretrained("THUDM/glm-4v-9b")
    
    # 处理图片
    processed = processor(images=image, return_tensors="pt")
    raw_tensor = processed['pixel_values']
    
    # 关键:动态获取视觉层的数据类型
    try:
        # 从模型视觉层获取实际使用的数据类型
        visual_dtype = next(model.transformer.vision.parameters()).dtype
    except AttributeError:
        # 备用方案
        visual_dtype = torch.float16
    
    # 将图片张量转换为与模型视觉层相同的数据类型和设备
    image_tensor = raw_tensor.to(device=device, dtype=visual_dtype)
    
    return image_tensor

这个方案的好处是自适应。无论你的环境用的是float16还是bfloat16,代码都能自动匹配,避免了手动指定类型可能导致的冲突。

3.2 正确的Prompt构造顺序

官方示例有个bug:Prompt顺序不对,导致模型把图片当成了系统背景图,而不是用户输入的一部分。这会导致模型输出乱码(如</credit>)或者直接复读问题。

我们修正后的逻辑:

def build_multimodal_prompt(model, processor, image_tensor, question_text, system_prompt=""):
    """
    构建多模态Prompt,确保正确的顺序:系统提示 -> 用户 -> 图片 -> 文本
    """
    # 1. 系统提示词(如果有)
    system_part = []
    if system_prompt:
        system_ids = processor.encode(system_prompt, add_special_tokens=False)
        system_part = [model.bos_token_id] + system_ids
    
    # 2. 用户标识
    user_ids = processor.encode("[gMASK]sop", add_special_tokens=False)
    
    # 3. 图片部分
    # 获取图片token(模型内部处理)
    image_token_ids = model.process_images(image_tensor)
    
    # 4. 问题文本
    text_ids = processor.encode(question_text, add_special_tokens=False)
    
    # 5. 按正确顺序拼接:系统 -> 用户 -> 图片 -> 文本
    if system_part:
        input_ids = torch.cat([
            torch.tensor(system_part).unsqueeze(0),
            torch.tensor(user_ids).unsqueeze(0),
            image_token_ids,
            torch.tensor(text_ids).unsqueeze(0)
        ], dim=1)
    else:
        input_ids = torch.cat([
            torch.tensor(user_ids).unsqueeze(0),
            image_token_ids, 
            torch.tensor(text_ids).unsqueeze(0)
        ], dim=1)
    
    return input_ids

这个顺序很重要:[用户标识] + [图片token] + [问题文本]。这样模型才知道"先看这张图,然后回答这个问题"。

3.3 4-bit量化实现:让大模型在消费级显卡上运行

GLM-4V-9B有90亿参数,全精度加载需要约18GB显存。我们通过4-bit量化(QLoRA)将其降低到约7GB,这样RTX 4060 Ti(16GB)就能流畅运行了。

from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch

def load_quantized_model(model_name="THUDM/glm-4v-9b"):
    """
    使用4-bit量化加载模型
    """
    # 配置4-bit量化
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,  # 启用4-bit加载
        bnb_4bit_compute_dtype=torch.float16,  # 计算时使用float16
        bnb_4bit_use_double_quant=True,  # 双重量化,进一步压缩
        bnb_4bit_quant_type="nf4",  # 使用NF4量化类型
    )
    
    # 加载模型
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        quantization_config=bnb_config,
        device_map="auto",  # 自动分配设备
        trust_remote_code=True,  # 信任远程代码
        torch_dtype=torch.float16,
    )
    
    # 设置模型为评估模式
    model.eval()
    
    return model

量化后的模型精度损失很小(通常<1%),但显存占用减少了60%以上。这对于个人开发者和小团队来说,意味着不需要投入大量硬件成本就能用上最先进的多模态模型。

4. 高级功能与实用技巧

掌握了基础用法后,我们来看看一些高级功能和实用技巧,让你的使用体验更上一层楼。

4.1 批量图片处理

虽然界面是单张图片上传,但你可以通过修改代码实现批量处理。比如,你想分析一个文件夹里的所有产品图片:

import os
from glob import glob

def batch_process_images(image_folder, questions, system_prompt=""):
    """
    批量处理图片
    """
    results = []
    
    # 获取所有图片文件
    image_files = glob(os.path.join(image_folder, "*.jpg")) + \
                  glob(os.path.join(image_folder, "*.png")) + \
                  glob(os.path.join(image_folder, "*.jpeg"))
    
    for img_path in image_files:
        print(f"处理: {os.path.basename(img_path)}")
        
        # 对每张图片问所有问题
        for question in questions:
            # 这里调用我们的对话函数
            answer = chat_with_image(
                image_path=img_path,
                question=question,
                system_prompt=system_prompt
            )
            
            results.append({
                "image": os.path.basename(img_path),
                "question": question,
                "answer": answer
            })
    
    return results

# 使用示例
if __name__ == "__main__":
    # 定义要问的问题
    questions = [
        "描述图片中的主要产品",
        "产品的颜色是什么?",
        "估计产品的尺寸",
        "这个产品可能用在什么场景?"
    ]
    
    # 批量处理
    results = batch_process_images(
        image_folder="./product_images",
        questions=questions,
        system_prompt="你是一个电商产品分析师,请详细描述产品特征"
    )
    
    # 保存结果
    import json
    with open("analysis_results.json", "w", encoding="utf-8") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)

4.2 结合其他工具使用

GLM-4V-9B可以和其他工具结合,实现更强大的功能:

1. 与OCR结合,提取结构化信息

def extract_structured_info(image_path):
    """
    从图片中提取结构化信息
    """
    # 先用GLM-4V理解图片内容
    description = chat_with_image(
        image_path=image_path,
        question="详细描述图片中的所有文字和数字信息",
        system_prompt="你是一个数据提取专家,请准确提取所有文字和数字信息"
    )
    
    # 然后用规则或NLP模型解析成结构化数据
    # 这里可以根据你的具体需求定制解析逻辑
    
    return structured_data

2. 自动生成图片描述文件

def generate_image_alt_text(image_folder):
    """
    为文件夹中的所有图片生成alt文本(用于网页无障碍访问)
    """
    alt_texts = {}
    
    for img_file in os.listdir(image_folder):
        if img_file.lower().endswith(('.png', '.jpg', '.jpeg')):
            img_path = os.path.join(image_folder, img_file)
            
            # 生成简洁的alt文本
            alt_text = chat_with_image(
                image_path=img_path,
                question="用一句话描述这张图片的主要内容,适合作为网页图片的alt文本",
                system_prompt="你是一个网页内容助手,请生成简洁准确的图片描述"
            )
            
            alt_texts[img_file] = alt_text
    
    return alt_texts

4.3 性能优化建议

如果你发现生成速度比较慢,可以尝试这些优化:

1. 调整生成参数

# 在生成回答时调整这些参数
generation_config = {
    "max_new_tokens": 512,  # 减少生成的最大长度
    "temperature": 0.7,     # 降低随机性,加快速度
    "do_sample": False,     # 使用贪婪解码,速度更快
    "repetition_penalty": 1.1,  # 防止重复
}

2. 使用缓存 对于相同的图片和问题,可以使用缓存避免重复计算:

from functools import lru_cache
import hashlib

@lru_cache(maxsize=100)
def cached_chat_with_image(image_hash, question, system_prompt):
    """
    带缓存的图片对话函数
    """
    # 这里实现你的对话逻辑
    pass

def get_image_hash(image_path):
    """计算图片的哈希值作为缓存键"""
    with open(image_path, 'rb') as f:
        return hashlib.md5(f.read()).hexdigest()

5. 常见问题与解决方案

在实际使用中,你可能会遇到一些问题。这里我总结了一些常见情况和解决方法。

5.1 模型输出乱码或奇怪字符

问题: 模型输出像</credit><|im_end|>这样的标记,或者中英文混杂很奇怪。

原因: 通常是Prompt构造不正确,或者模型没有正确识别图片和文本的关系。

解决:

  1. 确保使用我们修正后的Prompt构造逻辑
  2. 检查系统提示词是否合适,有时候过于复杂的提示词会干扰模型
  3. 尝试简化问题,先问简单的问题测试

5.2 显存不足错误

问题: 出现CUDA out of memory错误。

原因: 图片太大,或者对话历史太长。

解决:

  1. 压缩图片尺寸(Streamlit界面上传时会自动压缩)
  2. 清空对话历史,重新开始
  3. 如果使用自己的代码,可以限制图片分辨率:
from PIL import Image

def resize_image(image_path, max_size=1024):
    """调整图片尺寸"""
    img = Image.open(image_path)
    
    # 计算缩放比例
    ratio = min(max_size / img.width, max_size / img.height)
    new_size = (int(img.width * ratio), int(img.height * ratio))
    
    # 调整尺寸
    img = img.resize(new_size, Image.Resampling.LANCZOS)
    
    return img

5.3 生成速度慢

问题: 模型回答需要很长时间。

原因: 生成长度太长,或者硬件性能有限。

解决:

  1. 设置max_new_tokens参数,限制生成长度
  2. 使用性能更好的GPU
  3. 关闭不必要的后台程序,释放显存

5.4 模型回答不准确

问题: 模型识别错误,或者回答与图片内容不符。

原因: 多模态模型对某些类型的图片识别能力有限。

解决:

  1. 尝试不同的提问方式
  2. 使用更具体的系统提示词引导模型
  3. 如果涉及专业领域,在问题中提供一些背景信息

6. 总结

通过这个教程,我们完成了一个完整的GLM-4V-9B多模态大模型本地部署方案。相比官方示例,我们的版本有三个显著优势:

第一是稳定性。我们解决了官方代码在特定环境下的兼容性问题,通过动态类型适配和正确的Prompt构造,确保了模型在各种环境下都能稳定运行。

第二是实用性。新增的自定义系统提示词功能,让你可以根据不同场景调整模型的角色和行为。完整的对话上下文管理,让多轮对话成为可能。

第三是易用性。4-bit量化技术让这个百亿参数的大模型能在消费级显卡上运行,大大降低了使用门槛。基于Streamlit的界面简洁直观,无需编程基础也能轻松使用。

这个方案可以应用在很多实际场景中:电商产品分析、社交媒体内容理解、教育辅助、无障碍服务等等。你可以根据自己的需求,调整系统提示词,让模型成为你的专属助手。

最重要的是,所有代码都是开源的,你可以根据自己的需求进行修改和扩展。比如添加语音输入输出、集成到现有系统、或者训练自己的LoRA适配器。

多模态AI正在改变我们与计算机交互的方式。以前我们需要用文字描述图片,现在可以直接让AI"看"图片并理解它。这个技术还在快速发展,未来会有更多令人兴奋的应用出现。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐