GLM-4.1V-Thinking

对标题的评论会显示在这里

01-GLM-4.1V-Thinking vLLM部署调用

对这一段的评论会显示在这里

GLM-4.1V-Thinking 是一由智谱 AI 基于 GLM-4-9B-0414 基座模型推出的新版VLM开源模型,引入思考范式,通过课程采样强化学习 RLCS(Reinforcement Learning with Curriculum Sampling)全面提升模型能力, 达到 10B 参数级别的视觉语言模型的最强性能。

对这一段的评论会显示在这里

环境准备

对这一段的评论会显示在这里

本文的试验基础环境如下:

对这一段的评论会显示在这里
----------------
PyTorch  2.5.1
Python  3.12(ubuntu22.04)
CUDA  12.4
GPU  A800-80GB(80GB) * 1
----------------
对这一段的评论会显示在这里

克隆GLM4.1V项目仓库

对这一段的评论会显示在这里
git clone https://github.com/THUDM/GLM-4.1V-Thinking.git
cd GLM-4.1V-Thinking
对这一段的评论会显示在这里

pip换源加速,下载并安装依赖包

对这一段的评论会显示在这里
python -m pip install --upgrade pip
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

pip install modelscope
pip install -r GLM-4.1V-Thinking/requirements.txt
对这一段的评论会显示在这里

关键依赖项包括:

对这一段的评论会显示在这里

torch>=2.7.1
gradio>=5.35.0
PyMuPDF>=1.26.1
av>=14.4.0(用于视频处理)
accelerate>=1.6.0
从GitHub获取的最新transformers和vLLM

对这一段的评论会显示在这里

模型下载

对这一段的评论会显示在这里

模型可以从Hugging Face和ModelScope仓库获取。可以显式下载,也可以在首次使用时执行仓库代码自动下载。

对这一段的评论会显示在这里

ModelScope

对这一段的评论会显示在这里

新建 .py/.ipynb 文件,复制下述代码并执行:使用 modelscope 中的 snapshot_download 函数下载模型,第一个参数为模型名称,参数 cache_dir 为模型自定义下载路径。

对这一段的评论会显示在这里
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen3-8B', cache_dir='/root/autodl-tmp', revision='master')
对这一段的评论会显示在这里

Hugging Face CLI

对这一段的评论会显示在这里
huggingface-cli download THUDM/GLM-4.1V-9B-Thinking --local-dir /root/autodl-tmp/GLM-4.1V-9B-Thinking
对这一段的评论会显示在这里

ps:记得修改对应的 cache_dir / local_dir为你的模型下载路径哦~

对这一段的评论会显示在这里

transformers调用代码

对这一段的评论会显示在这里

使用transformers库作为推理的改写交互脚本进行调用测试

对这一段的评论会显示在这里
import argparse
import re
import torch
from transformers import AutoProcessor, Glm4vForConditionalGeneration

def build_content(image_paths, video_path, text):
    content = []
    if image_paths:
        for img_path in image_paths:
            content.append({"type": "image", "url": img_path})
    if video_path:
        content.append({"type": "video", "url": video_path})
    content.append({"type": "text", "text": text})
    return content

processor = AutoProcessor.from_pretrained("/root/autodl-tmp/ZhipuAI/GLM-4.1V-9B-Thinking", use_fast=True)
model = Glm4vForConditionalGeneration.from_pretrained(
    "/root/autodl-tmp/ZhipuAI/GLM-4.1V-9B-Thinking", torch_dtype=torch.bfloat16, device_map="cuda:0"
)
messages = []
messages.append(
    {
        "role": "user",
        "content": [
            {"type": "video", "url": "/root/autodl-tmp/media/demo3.mp4"},
            {"type": "text", "text": "这个视频的内容是什么?"},
        ],
    }
)
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt",
    padding=True,
).to(model.device)

output = model.generate(
    **inputs,
    max_new_tokens=25000,
    repetition_penalty=1.0,
    do_sample=1.0 > 0,
    top_k=2,
    temperature=1.0,
)
raw = processor.decode(
    output[0][inputs["input_ids"].shape[1] : -1], skip_special_tokens=False
)
match = re.search(r"<answer>(.*?)</answer>", raw, re.DOTALL)
answer = match.group(1).strip() if match else ""
messages.append(
    {"role": "assistant", "content": [{"type": "text", "text": answer}]}
)
print(f"Assistant: {raw}")
对这一段的评论会显示在这里

使用的视频文件示例:

对这一段的评论会显示在这里
对这一段的评论会显示在这里

执行代码结果如下:

对这一段的评论会显示在这里
image-2.png
image-2.png
对这一段的评论会显示在这里
Assistant: <think>用户现在需要描述视频内容。首先看画面:主要是Logi品牌的键盘,有手在操作按键。要分解每个细节:键盘是黑色带银色上沿,有数字键、功能键(如ins、delete、home等),手部动作是点击不同按键(比如数字键、功能键),背景有桌面、耳机(白色耳机和黑色耳机线),还有其他物品。需要按时间顺序或场景描述,说明是手在键盘上操作,按键的交互,以及环境元素。

首先,视频展示了一个人在操作Logi品牌的键盘。画面中可以看到黑色的键盘,带有银色的上边缘,键盘上有数字按键(0-9等)、功能按键(如ins、delete、home、end、page up/down等)。有一只手在键盘上点击不同的按键,比如先点击数字键“2”,然后“8”等,展示按键操作。背景中可以看到桌面,上面有白色的耳机、黑色的耳机线等物品,营造出办公或桌面场景的氛围。整体是近距离展示键盘操作,重点在按键交互和键盘外观。

现在组织语言:视频展示了一只手在Logi品牌的黑色键盘上操作,键盘带有银色上边缘,包含数字键、功能键(如ins、delete、home、end、page up/down等)。手部依次点击不同的按键,背景可见桌面、白色耳机和黑色耳机线等物品,呈现出桌面操作键盘的场景,重点展现键盘按键的交互与键盘外观细节。</think><answer>视频展示了在桌面环境中,一只手在Logi品牌的黑色键盘上操作。该键盘带有银色上边缘,包含数字键(0 - 9)、功能键(如“ins”“delete”“home”“end”“page up/down”等)。手部依次点击不同的按键(如数字键、功能键),背景中可见桌面、白色耳机和黑色耳机线等物品,呈现出近距离操作键盘的场景,重点展现键盘按键的交互与键盘外观细节。
对这一段的评论会显示在这里

vLLM调用代码准备

对这一段的评论会显示在这里

首先从 vLLM 库中导入 LLM 和 SamplingParams 类。LLM 类是使用 vLLM 引擎运行离线推理的主要类。SamplingParams 类指定采样过程的参数,用于控制和调整生成文本的随机性和多样性。

对这一段的评论会显示在这里

vLLM 提供了非常方便的封装,我们直接传入模型名称或模型路径即可,不必手动初始化模型和分词器。

对这一段的评论会显示在这里

在 /root/autodl-tmp 路径下新建 vllm_model.py 文件并在其中输入以下内容

对这一段的评论会显示在这里
# vllm_model.py
from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
import os
import json

def prepare_model(model_path, max_tokens=512, temperature=0.8, top_p=0.95, max_model_len=2048):
    # 初始化 vLLM 推理引擎
    llm = LLM(model=model_path, tokenizer=model_path, max_model_len=max_model_len,trust_remote_code=True)
    return llm

def get_completion(prompts, llm, max_tokens=512, temperature=0.8, top_p=0.95, max_model_len=2048):
    stop_token_ids = [151329, 151336, 151338]
    # 创建采样参数。temperature 控制生成文本的多样性,top_p 控制核心采样的概率
    sampling_params = SamplingParams(temperature=temperature, top_p=top_p, max_tokens=max_tokens, stop_token_ids=stop_token_ids)
    # 初始化 vLLM 推理引擎
    outputs = llm.generate(prompts, sampling_params)
    return outputs

# 初始化 vLLM 推理引擎
model_path = '/root/autodl-tmp/ZhipuAI/GLM-4.1V-9B-Thinking'
tokenizer = AutoTokenizer.from_pretrained(model_path)
llm = prepare_model(model_path)

prompt = "5的阶乘是多少?"
messages = [
    {"role": "user", "content": prompt}
]

# 应用template中的chat模板
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)

outputs = get_completion(text, llm, max_tokens=1024, temperature=1, top_p=1, max_model_len=2048)
print(outputs[0].outputs[0].text)
对这一段的评论会显示在这里

执行代码结果如下:

对这一段的评论会显示在这里
image-3.png
image-3.png
对这一段的评论会显示在这里

GLM-4.1V-9B-Thinking支持多种类型的多模态输入,但有特定限制:

对这一段的评论会显示在这里

| 输入类型 | 最大允许数量 | 格式支持 |
| 图片 | 10张图片(Gradio),300(API) | JPG, JPEG, PNG, GIF, BMP, TIFF, WEBP |
| 视频 | 1个视频 | MP4, AVI, MKV, MOV, WMV, FLV, WEBM, MPEG, M4V |
| 文档 | 1个PDF或1个PPT | PDF, PPT, PPTX(内部转换为图片) |

对这一段的评论会显示在这里

创建兼容 OpenAI API 接口的服务器

对这一段的评论会显示在这里

GLM-4.1V-Thinking 兼容 OpenAI API 协议,所以我们可以直接使用 vLLM 创建 OpenAI API 服务器。vLLM 部署实现 OpenAI API 协议的服务器非常方便。默认会在 http://localhost:8000 启动服务器。服务器当前一次托管一个模型,并实现列表模型、completions 和 chat completions 端口。

对这一段的评论会显示在这里

completions:是基本的文本生成任务,模型会在给定的提示后生成一段文本。这种类型的任务通常用于生成文章、故事、邮件等。
chat completions:是面向对话的任务,模型需要理解和生成对话。这种类型的任务通常用于构建聊天机器人或者对话系统。

对这一段的评论会显示在这里

在创建服务器时,我们可以指定模型名称、模型路径、聊天模板等参数。

对这一段的评论会显示在这里

-host 和 -port 参数指定地址。
-model 参数指定模型名称。
-chat-template 参数指定聊天模板。
-served-model-name 指定服务模型的名称。
-max-model-len 指定模型的最大长度。

对这一段的评论会显示在这里
vllm serve /root/autodl-tmp/ZhipuAI/GLM-4.1V-9B-Thinking
    --served-model-name GLM-4.1V-9B-Thinking
    --max_model_len 25000
    --limit-mm-per-prompt '{"image": 32, "video": 1}'
    --allowed-local-media-path /
对这一段的评论会显示在这里
image-4.png
image-4.png
对这一段的评论会显示在这里

通过 curl 命令查看当前的模型列表

对这一段的评论会显示在这里
curl http://localhost:8000/v1/models
对这一段的评论会显示在这里

得到的返回值如下所示

对这一段的评论会显示在这里
{
	"object":"list",
	"data":[
		{
			"id":"GLM-4.1V-9B-Thinking",
			"object":"model",
			"created":1753411607,
			"owned_by":"vllm",
			"root":"/root/autodl-tmp/ZhipuAI/GLM-4.1V-9B-Thinking",
			"parent":null,
			"max_model_len":8192,
			"permission":[
				{
						"id":"modelperm-7a0d15b7334b4d19b024c2829c96b44a",
						"object":"model_permission",
						"created":1753411607,
						"allow_create_engine":false,
						"allow_sampling":true,
						"allow_logprobs":true,
						"allow_search_indices":false,
						"allow_view":true,
						"allow_fine_tuning":false,
						"organization":"*",
						"group":null,
						"is_blocking":false
						}
					]
			}
		]
}
对这一段的评论会显示在这里

使用 curl 命令测试 OpenAI Completions API

对这一段的评论会显示在这里
curl http://localhost:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "GLM-4.1V-9B-Thinking",
        "prompt": "456*123等于多少?<think>\n",
        "max_tokens": 1024,
        "temperature": 0
    }'
对这一段的评论会显示在这里

得到的返回值如下所示

对这一段的评论会显示在这里
image-5.png
image-5.png
对这一段的评论会显示在这里

• 用 Python 脚本请求 OpenAI Chat Completions API 的单图像示例

对这一段的评论会显示在这里
# vllm_openai_completions.py
import argparse
import os
from openai import OpenAI

def get_media_type(file_path):
    video_extensions = {".mp4", ".avi", ".mov"}
    image_extensions = {".jpg", ".jpeg", ".png"}
    _, ext = os.path.splitext(file_path.lower())
    return (
        "video_url"
        if ext in video_extensions
        else "image_url"
        if ext in image_extensions
        else None
    )

def create_content_item(media_path, media_type):
    if media_path.startswith(("http://", "https://")):
        url = media_path
    else:
        url = "file://" + media_path

    if media_type == "video_url":
        return {"type": "video_url", "video_url": {"url": url}}
    else:
        return {"type": "image_url", "image_url": {"url": url}}

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="sk-xxx", # 随便填写,只是为了通过接口参数校验
)

media_path = "/root/autodl-tmp/media/demo1.png"
media_type = get_media_type(media_path)
messages = [
    {
        "role": "user",
        "content": [
            create_content_item(media_path, media_type),
            {"type": "text", "text": "这张图是什么"},
        ],
    }
]
print("=========Messages=========")
print(messages)
response = client.chat.completions.create(
    model="GLM-4.1V-9B-Thinking",
    messages = messages,
    temperature = 1.0,
    top_p = None,
    extra_body = {
        "skip_special_tokens": False,
        "repetition_penalty": 1.0,
    },
)
print("=========Answer=========")
print(response.choices[0].message.content.strip())
对这一段的评论会显示在这里

示例使用的图像如下所示:

对这一段的评论会显示在这里
image-6.png
image-6.png
对这一段的评论会显示在这里

得到的结果如下所示:

对这一段的评论会显示在这里
image-7.png
image-7.png
对这一段的评论会显示在这里

• 用 Python 脚本请求 OpenAI Chat Completions API 的多图像分析

对这一段的评论会显示在这里
# vllm_openai_completions.py
# 上述代码不变
media_path_1 = "/root/autodl-tmp/media/demo2-1.JPG"
media_path_2 = "/root/autodl-tmp/media/demo2-2.JPG"

messages = [
    {
        "role": "user",
        "content": [
            create_content_item(media_path_1, media_type),
            create_content_item(media_path_2, media_type),
            {"type": "text", "text": "这两张图像之间可能有哪些关联?"},
        ],
    }
]
对这一段的评论会显示在这里

分别传入多张图像的路径进行解析,GLM-4.1V-Thinking 可同时分析多达 10 张图像

对这一段的评论会显示在这里

示例使用的图像如下所示:

对这一段的评论会显示在这里
对这一段的评论会显示在这里

执行代码结果如下:

对这一段的评论会显示在这里

Assistant: 根据提供的参考信息,这两张图片之间可能存在以下几种关联:

对这一段的评论会显示在这里

健康生活方式关联

对这一段的评论会显示在这里

第1张图片展示的是无糖可口可乐,强调"0糖 0脂肪 0卡路里",表明产品定位为健康饮品选择 第2张图片显示体重秤和体重记录(42.7公斤),这直接与体重管理相关 两者共同反映了现代人对健康饮食和体重管理的关注

对这一段的评论会显示在这里

时间与行为关联

对这一段的评论会显示在这里

第2张图片中的日期"10/23"可能表示特定日期,与第1张图片的可口可乐消费相关 可能是在10月23日,这个人选择了无糖可乐作为健康饮品,并记录了体重

对这一段的评论会显示在这里

心理动机关联

对这一段的评论会显示在这里

第1张图片中的无糖可乐可能代表一种健康选择的替代品,减少糖分和卡路里摄入 第2张图片中的"YOU'RE GONNA LOVE IT!"可能是在鼓励自己坚持健康的生活方式,包括选择无糖饮料

对这一段的评论会显示在这里

产品与消费者关联

对这一段的评论会显示在这里

第1张图片是可口可乐的产品信息 第2张图片可能是消费者使用无糖可乐后的记录或反馈,展示了产品的健康益处 这些关联表明,这两张图片可能共同讲述了一个关于健康意识增强、注重体重管理的生活方式故事,其中无糖可乐作为健康饮食选择的一部分。

对这一段的评论会显示在这里

02-GLM-4.1V-Thinking Gradio部署

对这一段的评论会显示在这里

THUDM也提供一个gradio界面脚本,搭建一个可以直接使用的 Web 界面,支持图片,视频,PDF,PPT等多模态输入。当然,如果glm4.1v在本地调用的及修改对应的模型路径即可。

对这一段的评论会显示在这里
image-10.png
image-10.png
对这一段的评论会显示在这里
python /root/autodl-tmp/GLM-4.1V-Thinking/inference/trans_infer_gradio.py
对这一段的评论会显示在这里

使用AutoDL的云端机器的小伙伴根据自己系统的指引连接即可

对这一段的评论会显示在这里
ssh -F /dev/null -CNg -L 7860:127.0.0.1:7860 [root@connect.nma1.seetacloud.com](mailto:root@connect.nma1.seetacloud.com) -p 36185
对这一段的评论会显示在这里

启动示例

对这一段的评论会显示在这里
image-11.png
image-11.png
对这一段的评论会显示在这里
image-12.png
image-12.png
对这一段的评论会显示在这里
image-13.png
image-13.png
对这一段的评论会显示在这里
image-14.png
image-14.png
对这一段的评论会显示在这里

03-GLM-4.1V-Thinking LoRA 及 SwanLab 可视化记录

对这一段的评论会显示在这里

数据集构建

对这一段的评论会显示在这里

对模型进行 supervised-finetuningsft,有监督微调)的数据格式如下:

对这一段的评论会显示在这里
{
  "instruction": "回答以下用户问题,仅输出答案。",
  "input": "1+1等于几?",
  "output": "2"
}
对这一段的评论会显示在这里

其中,instruction 是用户指令,告知模型其需要完成的任务;input 是用户输入,是完成用户指令所必须的输入内容;output 是模型应该给出的输出。

对这一段的评论会显示在这里

有监督微调的目标是让模型具备理解并遵循用户指令的能力,那我们能做哪些有意思的事情呢?或许我们可以通过大量风格化人物设定的对话数据来微调得到一个能够有对应特色对话风格的模型。

对这一段的评论会显示在这里

这里,我找了一个魔搭上开源的赛博猫娘数据集来进行这次教程,试想哪一个佬不想拥有一个赛博猫娘呢?传送门:沐雪猫娘化数据集

对这一段的评论会显示在这里
{
  "instruction": "沐雪的功能是什么?",
  "input": "",
  "output": "喵~本雪的主要功能是让你开心喵!用可爱的猫娘之力治愈你的心灵,喵呜~"
  "history":[]
}
对这一段的评论会显示在这里

那么,让我们开始准备领养沐雪吧

对这一段的评论会显示在这里

数据准备

对这一段的评论会显示在这里

导入对应的库并对我们的数据文件进行转换:

对这一段的评论会显示在这里
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer

# 将JSON文件转换为CSV文件
df = pd.read_json('/root/autodl-tmp/LLaMA-Factory/data/muice-dataset-train.catgirl.json') # 注意修改
ds = Dataset.from_pandas(df)
对这一段的评论会显示在这里

LoRALow-Rank Adaptation)训练的数据是需要经过格式化、编码之后再输入给模型进行训练的,我们需要先将输入文本编码为 input_ids,将输出文本编码为 labels,编码之后的结果是向量。我们首先定义一个预处理函数,这个函数用于对每一个样本,同时编码其输入、输出文本并返回一个编码后的字典:

对这一段的评论会显示在这里
def process_func(example):
    MAX_LENGTH = 1024 # 设置最大序列长度为1024个token
    input_ids, attention_mask, labels = [], [], [] # 初始化返回值
    # 适配chat_template
    instruction = tokenizer(
        f"[gMASK]<sop><|system|>\n现在你要扮演皇帝身边的女人--甄嬛" 
        f"<|user|>\n{example['instruction'] + example['input']}"  
        f"<|assistant|>\n<think></think>\n",  
        add_special_tokens=False   
    )
    response = tokenizer(f"{example['output']}", add_special_tokens=False)
    # 将instructio部分和response部分的input_ids拼接,并在末尾添加eos token作为标记结束的token
    input_ids = instruction["input_ids"] + response["input_ids"]
    # 注意力掩码,表示模型需要关注的位置
    attention_mask = instruction["attention_mask"] + response["attention_mask"]
    # 对于instruction,使用-100表示这些位置不计算loss(即模型不需要预测这部分)
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"]
    if len(input_ids) > MAX_LENGTH:  # 超出最大序列长度截断
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }
对这一段的评论会显示在这里

加载模型及tokenizer

对这一段的评论会显示在这里
from transformers import Glm4vForConditionalGeneration
import torch

# 记得将模型路径替换为自己本地的模型路径
tokenizer = AutoTokenizer.from_pretrained('ZhipuAI/GLM-4.1V-9B-Thinking')
model = Glm4vForConditionalGeneration.from_pretrained(
    'ZhipuAI/GLM-4.1V-9B-Thinking',
    torch_dtype=torch.bfloat16,
    device_map="auto",
    attn_implementation="sdpa"
)
对这一段的评论会显示在这里

Lora Config

对这一段的评论会显示在这里

LoraConfig这个类中可以设置很多参数,比较重要的如下

对这一段的评论会显示在这里

task_type:模型类型,现在绝大部分 decoder_only 的模型都是因果语言模型 CAUSAL_LM
target_modules:需要训练的模型层的名字,主要就是 attention部分的层,不同的模型对应的层的名字不同
rLoRA 的秩,决定了低秩矩阵的维度,较小的 r 意味着更少的参数
lora_alpha:缩放参数,与 r 一起决定了 LoRA 更新的强度。实际缩放比例为lora_alpha/r,在当前示例中是 32 / 8 = 4 倍
lora_dropout:应用于 LoRA 层的 dropout rate,用于防止过拟合

对这一段的评论会显示在这里
from peft import LoraConfig, TaskType, get_peft_model

config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    inference_mode=False, # 训练模式
    r=8, # Lora 秩
    lora_alpha=32, # Lora alpha
    lora_dropout=0.1 # Dropout 比例
)
对这一段的评论会显示在这里

Training Arguments

对这一段的评论会显示在这里

output_dir:模型的输出路径
per_device_train_batch_size: batch_size
gradient_accumulation_steps: 梯度累计
num_train_epochs:顾名思义 epoch

对这一段的评论会显示在这里
from transformers import TrainingArguments
args = TrainingArguments(
    output_dir="./output/glm4_1V-Thinking_lora", # 输出路径
    per_device_train_batch_size=4, # batch_size
    gradient_accumulation_steps=4, # 梯度累计
    logging_steps=2,
    num_train_epochs=3, # epoch
    save_steps=10, 
    learning_rate=1e-4, # lr
    save_on_each_node=True,
    gradient_checkpointing=True,
    report_to="none",
)
对这一段的评论会显示在这里

实例化SwanLabCallback

对这一段的评论会显示在这里
import swanlab
from swanlab.integration.transformers import SwanLabCallback

swanlab.login(api_key='your apikey', save=True) # 记得替换为自己账号的apikey
# 实例化SwanLabCallback
swanlab_callback = SwanLabCallback(
    project="self-llm", 
    experiment_name="glm4.1v-lora-catgirl"
)
对这一段的评论会显示在这里

训练完成后可以看到自己训练过程中训练的相关参数曲线

对这一段的评论会显示在这里
image-15.png
image-15.png
对这一段的评论会显示在这里

加载LoRA模型推理

对这一段的评论会显示在这里

训练完成后挑选效果最佳的LoRA模型权重(在前面定义的输出路径下),加载权重进行推理,跟沐雪打个招呼吧~

对这一段的评论会显示在这里
from transformers import AutoTokenizer, AutoProcessor, Glm4vForConditionalGeneration
import torch
from peft import PeftModel

mode_path = 'ZhipuAI/GLM-4.1V-9B-Thinking' # 本地glm4.1-Thinking的模型路径
lora_path = 'output/glm4_1V-Thinking_lora/checkpoint-180' # 这里改称你的 lora 输出对应 checkpoint 地址

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path)
processor = AutoProcessor.from_pretrained(mode_path, use_fast=True)
# 加载模型
model = Glm4vForConditionalGeneration.from_pretrained(mode_path, 
                                                      device_map="auto",
                                                      torch_dtype=torch.bfloat16, 
                                                      trust_remote_code=True)

# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)
对这一段的评论会显示在这里

say hi!

对这一段的评论会显示在这里
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": "你是谁?"
            }
        ],
    }
]
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt"
).to(model.device)

generated_ids = model.generate(**inputs, max_new_tokens=8192)
output_text = processor.decode(generated_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=False)
print(output_text)
对这一段的评论会显示在这里

喵呜~本雪是AI猫猫喵!专门用喵星人的智慧帮助大家解决各种问题喵~记得给本雪小鱼干当礼物喵!喵~喵呜~本雪是AI猫猫喵!专门用喵星人的智慧帮助大家解决各种问题喵~记得给本雪小鱼干当礼物喵!喵~

对这一段的评论会显示在这里

接下来我们测试一下glm4.1V-Thinking的视觉理解效果带入沐雪会是什么样的

对这一段的评论会显示在这里

需要注意的是,glm4.1V的图像、视频的数量及大小有所限制,且pytorch版本需要在2.2以上

对这一段的评论会显示在这里
messages = [
    {
        "role": "user",
        "content": [{
                "type": "text",
                "text":"假设你是一只猫娘。"}],
    },
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "url": "https://cdn.colorhub.me/Pl0d7lY07R4/rs:auto:0:500:0/g:ce/fn:colorhub/bG9jYWw6Ly8vY2YvMTUvMDY0NTdiMWVhNTA3NjA1MjU5Yzc5YmUzYzRiM2VmYTVkMTAwY2YxNS5qcGVn.webp"
            },
            {
                "type": "text",
                "text": "本雪觉得它怎么样呀?"
            }
        ],
    }
]
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt"
).to(model.device)

generated_ids = model.generate(**inputs, max_new_tokens=8192)
output_text = processor.decode(generated_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=False)
print(output_text)
对这一段的评论会显示在这里

喵呜~这个皮革看起来很舒服喵!就像本雪的毛一样柔软喵~你觉得呢喵?本雪想摸一摸喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)喵~(蹭蹭)喵~(舔毛)喵~(打滚)喵~(摇尾巴)喵~(喵喵叫)喵~(跳起来)喵~(扑过去)

对这一段的评论会显示在这里
image-16.png
image-16.png
对这一段的评论会显示在这里

hhhhh

对这一段的评论会显示在这里