|
| 1 | +--- |
| 2 | +title: "nanoVLM: 最简洁、最轻量的纯 PyTorch 视觉-语言模型训练代码库" |
| 3 | +thumbnail: /blog/assets/nanovlm/thumbnail.png |
| 4 | +authors: |
| 5 | +- user: ariG23498 |
| 6 | +- user: lusxvr |
| 7 | +- user: andito |
| 8 | +- user: sergiopaniego |
| 9 | +- user: merve |
| 10 | +- user: pcuenq |
| 11 | +- user: reach-vb |
| 12 | +translators: |
| 13 | +- user: innovation64 |
| 14 | +- user: zhongdongy |
| 15 | + proofread: true |
| 16 | +--- |
| 17 | + |
| 18 | +# nanoVLM: 最简洁、最轻量的纯 PyTorch 视觉-语言模型训练代码库 |
| 19 | + |
| 20 | +[**nanoVLM**](https://github.com/huggingface/nanoVLM) 是使用纯 PyTorch **训练** 你自己的视觉语言模型 (VLM) 的 _最简单_ 方式。它是一个轻量级 _工具包_ ,让你可以在 [免费的 Colab Notebook](https://colab.research.google.com/github/huggingface/nanoVLM/blob/main/nanoVLM.ipynb) 上启动 VLM 训练。 |
| 21 | + |
| 22 | +> 我们受到了 [Andrej Karpathy](https://karpathy.ai/) 的 [nanoGPT](https://github.com/karpathy/nanoGPT) 的启发,为视觉领域提供了一个类似的项目。 |
| 23 | +
|
| 24 | +从本质上讲,nanoVLM 是一个 **工具包**,可以帮助你构建和训练一个能够理解图像和文本,并基于此生成文本的模型。nanoVLM 的魅力在于它的 _简洁性_ 。整个代码库被有意保持 _最小化_ 和 _可读性_ ,使其非常适合初学者或任何想要深入了解 VLM 内部机制而不被复杂性淹没的人。 |
| 25 | + |
| 26 | +在这篇博客中,我们将介绍该项目背后的核心思想,并提供与代码库交互的简单方法。我们不仅会深入项目细节,还会将所有内容封装起来,让你能够快速上手。 |
| 27 | + |
| 28 | +## 目录: |
| 29 | + |
| 30 | +- [什么是视觉语言模型?](#什么是视觉语言模型) |
| 31 | +- [使用代码库](#使用代码库) |
| 32 | +- [架构](#架构) |
| 33 | +- [训练你自己的 VLM](#训练你自己的-vlm) |
| 34 | +- [在预训练模型上运行推理](#在预训练模型上运行推理) |
| 35 | +- [结论](#结论) |
| 36 | +- [参考文献](#参考文献) |
| 37 | + |
| 38 | +## 简要 |
| 39 | + |
| 40 | +你可以按照以下步骤使用我们的 nanoVLM 工具包开始训练视觉语言模型: |
| 41 | + |
| 42 | +```bash |
| 43 | +# 克隆仓库 |
| 44 | +git clone https://github.com/huggingface/nanoVLM.git |
| 45 | + |
| 46 | +# 执行训练脚本 |
| 47 | +python train.py |
| 48 | +``` |
| 49 | + |
| 50 | +这里有一个 [Colab Notebook](https://colab.research.google.com/github/huggingface/nanoVLM/blob/main/nanoVLM.ipynb),可以帮助你在无需本地设置的情况下启动训练运行! |
| 51 | + |
| 52 | +## 什么是视觉语言模型? |
| 53 | + |
| 54 | +顾名思义,视觉语言模型 (VLM) 是一种处理两种模态的多模态模型: 视觉和文本。这些模型通常以图像和/或文本作为输入,生成文本作为输出。 |
| 55 | + |
| 56 | +基于对图像和文本 (输入) 的理解来生成文本 (输出) 是一个强大的范式。它支持广泛的应用,从图像字幕生成和目标检测到回答关于视觉内容的问题 (如下表所示)。需要注意的是,nanoVLM 仅专注于视觉问答作为训练目标。 |
| 57 | + |
| 58 | +<table> |
| 59 | + <tr> |
| 60 | + <td rowspan="4"><img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/nanovlm/cat.jpg" alt=" 一张猫的图片 " width="200"/></td> |
| 61 | + <td>为图像生成标题</td> |
| 62 | + <td>两只猫躺在床上,旁边有遥控器</td> |
| 63 | + <td>图像描述</td> |
| 64 | + </tr> |
| 65 | + <tr> |
| 66 | + <td>检测图像中的物体</td> |
| 67 | + <td><code><locxx><locxx><locxx><locxx></code></td> |
| 68 | + <td>目标检测</td> |
| 69 | + </tr> |
| 70 | + <tr> |
| 71 | + <td>分割图像中的物体</td> |
| 72 | + <td><code><segxx><segxx><segxx></code></td> |
| 73 | + <td>语义分割</td> |
| 74 | + </tr> |
| 75 | + <tr> |
| 76 | + <td>图像中有多少只猫?</td> |
| 77 | + <td>2</td> |
| 78 | + <td>视觉问答</td> |
| 79 | + </tr> |
| 80 | +</table> |
| 81 | + |
| 82 | +> [!TIP] |
| 83 | +> 如果你有兴趣了解更多关于 VLM 的信息,我们强烈建议阅读我们关于该主题的最新博客: [视觉语言模型 (更好、更快、更强)](https://huggingface.co/blog/vlms-2025) |
| 84 | +
|
| 85 | +## 使用代码库 |
| 86 | + |
| 87 | +“废话少说,直接看代码” - 林纳斯·托瓦兹 |
| 88 | + |
| 89 | +在本节中,我们将引导你了解代码库。在跟随学习时,保持一个 [标签页](https://github.com/huggingface/nanoVLM) 开启以供参考会很有帮助。 |
| 90 | + |
| 91 | +以下是我们仓库的文件夹结构。为简洁起见,我们删除了一些辅助文件。 |
| 92 | + |
| 93 | +```bash |
| 94 | +. |
| 95 | +├── data |
| 96 | +│ ├── collators.py |
| 97 | +│ ├── datasets.py |
| 98 | +│ └── processors.py |
| 99 | +├── generate.py |
| 100 | +├── models |
| 101 | +│ ├── config.py |
| 102 | +│ ├── language_model.py |
| 103 | +│ ├── modality_projector.py |
| 104 | +│ ├── utils.py |
| 105 | +│ ├── vision_language_model.py |
| 106 | +│ └── vision_transformer.py |
| 107 | +└── train.py |
| 108 | +``` |
| 109 | + |
| 110 | +## 架构 |
| 111 | + |
| 112 | +```bash |
| 113 | +. |
| 114 | +├── data |
| 115 | +│ └── ... |
| 116 | +├── models # 👈 你在这里 |
| 117 | +│ └── ... |
| 118 | +└── train.py |
| 119 | +``` |
| 120 | + |
| 121 | +我们按照两个知名且广泛使用的架构来建模 nanoVLM。我们的视觉主干网络 ( `models/vision_transformer.py` ) 是标准的视觉 transformer,更具体地说是谷歌的 [SigLIP](https://huggingface.co/docs/transformers/en/model_doc/siglip) 视觉编码器。我们的语言主干网络遵循 [Llama 3](https://huggingface.co/docs/transformers/en/model_doc/llama3) 架构。 |
| 122 | + |
| 123 | +视觉和文本模态通过模态投影模块进行 _对齐_ 。该模块将视觉主干网络产生的图像嵌入作为输入,并将它们转换为与语言模型嵌入层的文本嵌入兼容的嵌入。然后将这些嵌入连接起来并输入到语言解码器中。模态投影模块由像素洗牌操作和线性层组成。 |
| 124 | + |
| 125 | +|  | |
| 126 | +| :--: | |
| 127 | +| 模型架构 (来源: 作者) | |
| 128 | + |
| 129 | +[像素洗牌](https://huggingface.co/papers/1609.05158) 减少了图像标记的数量,这有助于降低计算成本并加快训练速度,特别是对于对输入长度敏感的基于 transformer 的语言解码器。下图演示了这个概念。 |
| 130 | + |
| 131 | +|  | |
| 132 | +| :--: | |
| 133 | +| 像素洗牌可视化 (来源: 作者) | |
| 134 | + |
| 135 | +所有文件都非常轻量且有良好的文档说明。我们强烈建议你逐个查看它们,以更好地理解实现细节 ( `models/xxx.py` ) |
| 136 | + |
| 137 | +在训练时,我们使用以下预训练的主干权重: |
| 138 | + |
| 139 | +1. 视觉主干: [`google/siglip-base-patch16-224`](https://huggingface.co/google/siglip-base-patch16-224) |
| 140 | +2. 语言主干: [`HuggingFaceTB/SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) |
| 141 | + |
| 142 | +> 也可以将主干网络替换为 SigLIP/SigLIP 2 (用于视觉主干) 和 SmolLM2 (用于语言主干) 的其他变体。 |
| 143 | +
|
| 144 | +## 训练你自己的 VLM |
| 145 | + |
| 146 | +现在我们已经熟悉了架构,让我们换个话题,讨论如何使用 `train.py` 训练你自己的视觉语言模型。 |
| 147 | + |
| 148 | +```bash |
| 149 | +. |
| 150 | +├── data |
| 151 | +│ └── ... |
| 152 | +├── models |
| 153 | +│ └── ... |
| 154 | +└── train.py # 👈 你在这里 |
| 155 | +``` |
| 156 | + |
| 157 | +你可以通过以下命令启动训练: |
| 158 | + |
| 159 | +```bash |
| 160 | +python train.py |
| 161 | +``` |
| 162 | + |
| 163 | +这个脚本是整个训练流程的一站式解决方案,包括: |
| 164 | + |
| 165 | +- 数据集加载和预处理 |
| 166 | +- 模型初始化 |
| 167 | +- 优化和日志记录 |
| 168 | + |
| 169 | +**配置** |
| 170 | + |
| 171 | +在任何其他操作之前,脚本从 `models/config.py` 加载两个配置类: |
| 172 | + |
| 173 | +- `TrainConfig` : 对训练有用的配置参数,如学习率、检查点路径等。 |
| 174 | +- `VLMConfig` : 用于初始化 VLM 的配置参数,如隐藏维度、注意力头数等。 |
| 175 | + |
| 176 | +**数据加载** |
| 177 | + |
| 178 | +数据流水线的核心是 `get_dataloaders` 函数。它: |
| 179 | + |
| 180 | +- 通过 Hugging Face 的 `load_dataset` API 加载数据集。 |
| 181 | +- 组合和洗牌多个数据集 (如果提供)。 |
| 182 | +- 通过索引应用训练/验证分割。 |
| 183 | +- 将它们包装在自定义数据集 (`VQADataset` 、`MMStarDataset` ) 和整理器 (`VQACollator` 、`MMStarCollator` ) 中。 |
| 184 | + |
| 185 | +> [!TIP] |
| 186 | +> 这里一个有用的标志是 `data_cutoff_idx` ,对于在小子集上调试很有用。 |
| 187 | +
|
| 188 | +**模型初始化** |
| 189 | + |
| 190 | +模型通过 `VisionLanguageModel` 类构建。如果你从检查点恢复,操作非常简单: |
| 191 | + |
| 192 | +```python |
| 193 | +from models.vision_language_model import VisionLanguageModel |
| 194 | + |
| 195 | +model = VisionLanguageModel.from_pretrained(model_path) |
| 196 | +``` |
| 197 | + |
| 198 | +否则,你将获得一个全新初始化的模型,可选择为视觉和语言预加载主干网络。 |
| 199 | + |
| 200 | +**优化器设置: 两个学习率** |
| 201 | + |
| 202 | +由于模态投影器 ( `MP` ) 是新初始化的,而主干网络是预训练的,优化器被分成两个参数组,每个都有自己的学习率: |
| 203 | + |
| 204 | +- MP 使用较高的学习率 |
| 205 | +- 编码器/解码器堆栈使用较小的学习率 |
| 206 | + |
| 207 | +这种平衡确保 MP 快速学习,同时保留视觉和语言主干网络中的知识。 |
| 208 | + |
| 209 | +**训练循环** |
| 210 | + |
| 211 | +这部分相当标准但结构合理: |
| 212 | + |
| 213 | +- 使用 `torch.autocast` 进行混合精度以提高性能。 |
| 214 | +- 通过 `get_lr` 实现带线性预热的余弦学习率调度。 |
| 215 | +- 每批记录令牌吞吐量 (令牌/秒) 以进行性能监控。 |
| 216 | + |
| 217 | +每 250 步 (可配置),模型在验证集和 `MMStar` 测试数据集上进行评估。如果准确率提高,模型将被保存为检查点。 |
| 218 | + |
| 219 | +**日志记录和监控** |
| 220 | + |
| 221 | +如果启用了 `log_wandb` ,训练统计信息如 `batch_loss` 、`val_loss` 、`accuracy` 和 `tokens_per_second` 将记录到 Weights & Biases 以进行实时跟踪。 |
| 222 | + |
| 223 | +运行使用元数据自动命名,如样本大小、批次大小、epoch 数、学习率和日期,全部由辅助函数 `get_run_name` 处理。 |
| 224 | + |
| 225 | +**推送到 Hub** |
| 226 | + |
| 227 | +使用以下方法将训练好的模型推送到 Hub,供其他人查找和测试: |
| 228 | + |
| 229 | +```python |
| 230 | +model.save_pretrained(save_path) |
| 231 | +``` |
| 232 | + |
| 233 | +你可以轻松地使用以下方式推送它们: |
| 234 | + |
| 235 | +```python |
| 236 | +model.push_to_hub("hub/id") |
| 237 | +``` |
| 238 | + |
| 239 | +## 在预训练模型上运行推理 |
| 240 | + |
| 241 | +使用 nanoVLM 作为工具包,我们训练了一个 [模型并将其发布到 Hub](https://huggingface.co/lusxvr/nanoVLM-222M)。我们使用了 `google/siglip-base-patch16-224` 和 `HuggingFaceTB/SmolLM2-135M` 作为主干网络。该模型在单个 H100 GPU 上对 [cauldron](https://huggingface.co/datasets/HuggingFaceM4/the_cauldron) 的约 170 万个样本训练了约 6 小时。 |
| 242 | + |
| 243 | +这个模型并不旨在与最先进的模型竞争,而是为了揭示 VLM 的组件和训练过程。 |
| 244 | + |
| 245 | +```bash |
| 246 | +. |
| 247 | +├── data |
| 248 | +│ └── ... |
| 249 | +├── generate.py # 👈 你在这里 |
| 250 | +├── models |
| 251 | +│ └── ... |
| 252 | +└── ... |
| 253 | +``` |
| 254 | + |
| 255 | +让我们使用 `generate.py` 脚本在训练好的模型上运行推理。你可以使用以下命令运行生成脚本: |
| 256 | + |
| 257 | +```bash |
| 258 | +python generate.py |
| 259 | +``` |
| 260 | + |
| 261 | +这将使用默认参数并在图像 `assets/image.png` 上运行查询 "What is this?"。 |
| 262 | + |
| 263 | +你可以在自己的图像和提示上使用此脚本,如下所示: |
| 264 | + |
| 265 | +```bash |
| 266 | +python generate.py --image path/to/image.png --prompt "你的提示在这里" |
| 267 | +``` |
| 268 | + |
| 269 | +如果你想可视化脚本的核心,就是这些行: |
| 270 | + |
| 271 | +```python |
| 272 | +model = VisionLanguageModel.from_pretrained(source).to(device) |
| 273 | +model.eval() |
| 274 | + |
| 275 | +tokenizer = get_tokenizer(model.cfg.lm_tokenizer) |
| 276 | +image_processor = get_image_processor(model.cfg.vit_img_size) |
| 277 | + |
| 278 | +template = f"Question: {args.prompt} Answer:" |
| 279 | +encoded = tokenizer.batch_encode_plus([template], return_tensors="pt") |
| 280 | +tokens = encoded["input_ids"].to(device) |
| 281 | + |
| 282 | +img = Image.open(args.image).convert("RGB") |
| 283 | +img_t = image_processor(img).unsqueeze(0).to(device) |
| 284 | + |
| 285 | +print("\nInput:\n ", args.prompt, "\n\nOutputs:") |
| 286 | +for i in range(args.generations): |
| 287 | + gen = model.generate(tokens, img_t, max_new_tokens=args.max_new_tokens) |
| 288 | + out = tokenizer.batch_decode(gen, skip_special_tokens=True)[0] |
| 289 | + print(f" >> Generation {i+1}: {out}") |
| 290 | +``` |
| 291 | + |
| 292 | +我们创建模型并将其设置为 `eval` 。初始化分词器 (用于对文本提示进行分词) 和图像处理器 (用于处理图像)。下一步是处理输入并运行 `model.generate` 以生成输出文本。最后,使用 `batch_decode` 解码输出。 |
| 293 | + |
| 294 | +| 图像 | 提示 | 生成结果 | |
| 295 | +| :--: | :--: | :--: | |
| 296 | +|  | What is this? | In the picture I can see the pink color bed sheet. I can see two cats lying on the bed sheet. | |
| 297 | +|  | What is the woman doing? | Here in the middle she is performing yoga | |
| 298 | + |
| 299 | +> [!TIP] |
| 300 | +> 如果你想在 UI 界面中对训练好的模型运行推理,[这里](https://huggingface.co/spaces/ariG23498/nanovlm) 有一个 Hugging Face Space 供你与模型交互。 |
| 301 | +
|
| 302 | +## 结论 |
| 303 | + |
| 304 | +在这篇博客中,我们介绍了什么是 VLM,探讨了支撑 nanoVLM 的架构选择,并详细解释了训练和推理工作流程。 |
| 305 | + |
| 306 | +通过保持代码库轻量级和可读性,nanoVLM 旨在既作为学习工具,又作为你可以在此基础上构建的基础。无论你是想了解多模态输入如何对齐,还是想在自己的数据集上训练 VLM,这个仓库都能让你快速入门。 |
| 307 | + |
| 308 | +如果你尝试了它,并在它的基础上尝试构建,或者你只是有问题,我们都很乐意听到你的反馈。祝你探索愉快! |
| 309 | + |
| 310 | +## 参考文献 |
| 311 | + |
| 312 | +1. [GitHub - huggingface/nanoVLM: 用于训练/微调小型 VLM 的最简单、最快速的代码库。](https://github.com/huggingface/nanoVLM) |
| 313 | +2. [视觉语言模型 (更好、更快、更强)](https://huggingface.co/blog/vlms-2025) |
| 314 | +3. [视觉语言模型详解](https://huggingface.co/blog/vlms) |
| 315 | +4. [深入视觉语言预训练](https://huggingface.co/blog/vision_language_pretraining) |
| 316 | +5. [SmolVLM: 重新定义小型高效多模态模型](https://huggingface.co/papers/2504.05299) |
0 commit comments