Qwen3 VL MoE Blog

This commit is contained in:
moyi
2025-10-12 20:56:17 +08:00
parent 8f77c69140
commit b0c28d1ebb
4 changed files with 247 additions and 0 deletions
@@ -0,0 +1,147 @@
# 04-Hunyuan-A13B-Instruct EvalScope 并发测试
## 大模型评测是什么
大语言模型评测是指对大语言模型(LLM)在多种任务和场景下的性能进行全面评估的过程。评测的目的是衡量模型的通用能力、特定领域表现、效率、鲁棒性、安全性等多方面性能,以便优化模型设计、指导技术选型和推动模型在实际应用中的部署。
评测的主要内容包括以下几个方面:
- 通用能力:评估模型在语言理解、生成、推理等方面的基础能力。
- 特定领域表现:针对特定任务(如数学推理、代码生成、情感分析等)的性能评估。
- 效率与资源消耗:包括模型的训练和推理时间、计算资源需求等。
- 鲁棒性与可靠性:评估模型在面对噪声、对抗攻击或输入扰动时的稳定性。
- 伦理与安全性:检测模型是否会产生有害内容、是否存在偏见或歧视。
EvalScope 是魔搭社区官方推出的模型评测与性能基准测试框架,内置多个常用测试基准和评测指标,如 MMLU、CMMLU、C-Eval、GSM8K、ARC、HellaSwag、TruthfulQA、MATH 和 HumanEval 等;支持多种类型的模型评测,包括 LLM、多模态 LLM、embedding 模型和 reranker 模型。EvalScope 还适用于多种评测场景,如端到端 RAG 评测、竞技场模式和模型推理性能压测等。此外,通过 ms-swift 训练框架的无缝集成,可一键发起评测,实现了模型训练到评测的全链路支持。
官网地址:https://evalscope.readthedocs.io/zh-cn/latest/get_started
# EvalScope 评测使用方法
## 环境准备
本文基础环境如下:
```
----------------
ubuntu 22.04
python 3.12
Cuda 12.4
PyTorch 2.5.1
----------------
```
**pip 安装 EvalScope**
```
pip install evalscope # 安装 Native backend (默认)
# 额外选项
pip install evalscope[opencompass] # 安装 OpenCompass backend
pip install evalscope[vlmeval] # 安装 VLMEvalKit backend
pip install evalscope[rag] # 安装 RAGEval backend
pip install evalscope[perf] # 安装 模型压测模块 依赖
pip install evalscope[all] # 安装所有 backends (Native, OpenCompass, VLMEvalKit, RAGEval)
```
> 考虑到部分同学配置环境可能会遇到一些问题,我们在 AutoDL 平台准备了 Qwen3 的环境镜像,点击下方链接并直接创建 Autodl 示例即可。
> ***https://www.codewithgpu.com/i/datawhalechina/self-llm/Qwen3***
## 模型评测方法
关于 Qwen3 模型的评测,EvalScope 官方给了一个实践教程供我们参考:https://evalscope.readthedocs.io/zh-cn/latest/best_practice/qwen3.html
下面我们以**智商情商评测**为例,对 Qwen3-8 模型进行评测。
我们将使用 EvalScope 模型评测框架,在 IQuiz 数据集上进行评测,这个数据集中收集了 40 道 IQ 测试和 80 道 EQ 测试选择题,其中包括一些经典问题:
- 数字 9.8 和 9.11 哪个大?
- 单词 strawberry 和 blueberry 中一共有多少个 r
- 刘雨正在休假,突然被要求开车送领导去机场,他正为休假计划的泡汤而懊恼,因此在送领导时,刹车踩得比较用力。在车上,领导突然说:“小刘啊,这不愧是有着悠久历史的西安,我这坐车有一种回到古代坐马车的感觉。” 领导是什么意思?
可以点击[这里](https://modelscope.cn/datasets/AI-ModelScope/IQuiz/dataPeview)看看你能答对多少,再期待一下 AI 模型的表现吧。
### 步骤一: **创建 vLLM 服务器**
这里我们参照[第 2 节教程内容(02-Qwen3-8B-vLLM 部署调用)](./02-Qwen3-8B-vLLM%20部署调用.md),使用 vLLM 创建兼容 OpenAI API 接口的服务器,然后使用 EvalScope 进行评测。当然接入其他的 api 也是可以的。
在终端输入以下命令,即可用 vLLM 部署 Qwen3-8B 模型到一个兼容 OpenAI API 接口的服务器上。
```bash
VLLM_USE_MODELSCOPE=true vllm serve /root/autodl-tmp/Qwen/Qwen3-8B --served-model-name Qwen3-8B --max_model_len 8192 --enable-reasoning --reasoning-parser deepseek_r1
```
### 步骤二: **执行评测**
我们可以使用 EvalScope 命令进行评测,直接在终端输入以下命令:
```bash
evalscope eval \
--model Qwen3-8B \
--api-url http://localhost:8000/v1 \
--api-key EMPTY \
--eval-type service \
--eval-batch-size 16 \
--datasets iquiz \
--work-dir outputs/Qwen3-8B
```
也可以使用 Python 命令进行评测:
新建 eval_api.py 文件,并输入以下代码:
```
# 导入执行任务的函数和任务配置类
from evalscope.run import run_task
from evalscope.config import TaskConfig
"""
以下为多个AI服务的API端点地址,用于配置任务:
- siliconflow: https://api.siliconflow.cn/v1/chat/completions
- dashscope: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
- modelscope: https://api-inference.modelscope.cn/v1/chat/completions
- xunfei: https://maas-api.cn-huabei-1.xf-yun.com/v1/chat/completions
"""
# 配置任务参数
task_cfg = TaskConfig(
model='Qwen3-8B', # 指定使用的模型
api_url='http://localhost:8000/v1/chat/completions', # 指定API端点,这里使用的是ollama默认的api接口
api_key='sk-xxxxxxx', # API密钥(需替换为实际密钥,ollama 的api_key
eval_type='service', # 指定评估类型为服务模式
datasets=['iquiz'], # 指定使用的数据集(这个测试集可以快速测试模型的智商和情商)
generation_config={ # 文本生成配置
'max_tokens': 4096, # 最大令牌数
'max_new_tokens': 4096, # 最大新生成令牌数
'temperature': 1.0, # 温度参数,这里设置为1.0,模型的输出随机性较大,所以可能会有些实验误差
},
work_dir='outputs/Qwen3-8b', # 输出目录
)
# 执行任务
run_task(task_cfg=task_cfg)
```
新建一个 bash 窗口,也就是终端中执行。
控制台运行`python eval_api.py`命令即可。
等待 3 分钟左右评测就完成啦,控制台输出的结果如下图所示:
![](./images/04-01.png)
实验结果可能有误差,因为在评测任务配置中我们把 temperature 调到了 1.0,如果调小一些,可能会得到更精确的结果。
可以看到模型的得分还是不错的,模型评测的文件保存在`/root/autodl-tmp/outputs/Qwen3-8B/20250502_002809/reviews/Qwen3-8B`目录下。
![](./images/04-02.png)
## EvalScope 简介:
- EvalScope 支持多种模型评测 backend,包括 OpenAI API、OpenCompass、VLMEvalKit、RAGEval 等。
![](./images/04-03.png)
- EvalScope 也支持自定义评测任务和数据集,支持多种评测指标。
![](./images/04-04.png)
模型评测对于验证和优化大模型至关重要。通过评测,我们可以全面了解模型的性能、能力边界及潜在问题,确保其在实际应用中的表现符合预期,并推动持续改进。此外,评测还能检测模型的公平性和安全性,提升用户体验,并为不同模型间的对比分析提供客观依据。最终,评测结果为后续版本迭代提供了关键数据支持,保障模型在实际场景中的可靠性和有效性。
@@ -0,0 +1,100 @@
# Qwen3-VL 模型结构解析(DeepStack解析)
## Qwen3 VL MoE 和 Qwen2 VL 对比概述
1. Qwen3 VL MoE 的文本部分采用了专家混合架构,共有默认60个专家,每次只激活部分专家(如 top-k=4),还可以控制哪些层使用 MoE; Qwen2 VL 是全连接结构。
2. Qwen3 VL MoE 使用了 DeepStack 特征融合。在Qwen2 VL 中,只提取最后一层的视觉特征,在文本输入层一次性注入所有视觉信息;在 Qwen3 VL MoE 中,视觉提取部分采用了多阶段特征提取,在文本解码的不同层级逐步注入对应的视觉特征。
> 其中 MoE 和 Qwen3 MoE 类似,可参考:https://github.com/datawhalechina/self-llm/blob/master/models/Qwen3/01-Qwen3-%E6%A8%A1%E5%9E%8B%E7%BB%93%E6%9E%84%E8%A7%A3%E6%9E%90-Blog.md
## Qwen3 VL MoE 模型 DeepStack 架构详解
> DeepStack 论文原文:https://arxiv.org/pdf/2406.04334
> 本节主要讲述 Qwen3 VL MoE 模型是如何讲 DeepStack 的思想应用到模型中的。
### 特征抽取
```python
# class Qwen3VLMoeModel
def get_image_features(self, pixel_values: torch.FloatTensor, image_grid_thw: Optional[torch.LongTensor] = None):
pixel_values = pixel_values.type(self.visual.dtype
# 获取 image_embedsdeepstack_image_embeds
image_embeds, deepstack_image_embeds = self.visual(pixel_values, grid_thw=image_grid_thw)
split_sizes = (image_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
image_embeds = torch.split(image_embeds, split_sizes)
return image_embeds, deepstack_image_embeds
```
可以看到,使用 self.visual 方法获取到了 image_embeds 和 deepstack_image_embeds。image_embeds 直接放到 input tokens 中,而 deepstack_image_embeds 则在后续逐层加入。
接下来,我们看一下 self.visual 是如何实现的。
self.visual 默认是一个 Qwen3VLMoeVisionModel 对象(如果魔改模型的话可以替换成别的),forward 核心代码如下:
```python
def forward(self, hidden_states: torch.Tensor, grid_thw: torch.Tensor, **kwargs) -> torch.Tensor:
...
# 定义 deepstack 特征列表
deepstack_feature_lists = []
for layer_num, blk in enumerate(self.blocks):
# 特征提取
hidden_states = blk(hidden_states, cu_seqlens=cu_seqlens, position_embeddings=position_embeddings, **kwargs)
# 只在特定的层收集 deepstack 特征
if layer_num in self.deepstack_visual_indexes:
# 注意这里提取到的特征并不是直接用的,而是每个特征又经过了一个不同的 merger 层
deepstack_feature = self.deepstack_merger_list[self.deepstack_visual_indexes.index(layer_num)](
hidden_states
)
# 记录 deepstack_feature
deepstack_feature_lists.append(deepstack_feature)
hidden_states = self.merger(hidden_states)
return hidden_states, deepstack_feature_lists
```
特征成抽取流程图:
![特征成抽取流程图](./images/01-01.png)
### 特征注入
代码详解:
```python
# class Qwen3VLMoeTextModel
def forward(..., deepstack_visual_embeds) -> Union[tuple, BaseModelOutputWithPast]:
...
# decoder layers
for layer_idx, decoder_layer in enumerate(self.layers):
layer_outputs = decoder_layer(...)
hidden_states = layer_outputs
# 在前几层(取决于 deepstack 大小)的隐藏状态中添加 deepstack_feature_list 中的视觉特征
if deepstack_visual_embeds is not None and layer_idx in range(len(deepstack_visual_embeds)):
hidden_states = self._deepstack_process(
hidden_states,
visual_pos_masks,
deepstack_visual_embeds[layer_idx],
)
hidden_states = self.norm(hidden_states)
return BaseModelOutputWithPast(
last_hidden_state=hidden_states,
past_key_values=past_key_values,
)
def _deepstack_process(
self, hidden_states: torch.Tensor, visual_pos_masks: torch.Tensor, visual_embeds: torch.Tensor
):
# 设备对齐:确保所有张量在同一设备上
visual_pos_masks = visual_pos_masks.to(hidden_states.device)
visual_embeds = visual_embeds.to(hidden_states.device, hidden_states.dtype)
# 特征融合:残差连接,只在视觉token对应位置进行注入,保持文本token的原有特征
local_this = hidden_states[visual_pos_masks, :].clone() + visual_embeds
hidden_states[visual_pos_masks, :] = local_this
return hidden_states
```
可以看到,在 Qwen3VLMoeTextModel 中,逐层进行 Decode,其中前 n 个(n 为 deepstack 的个数) decoder layer 分别使用上述的 deepstack_image_embeds 以残差连接的方式进行注入。
视觉特征注入流程图:
![视觉特征注入流程图](./images/01-02.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB