重要提示

您正在查看 NeMo 2.0 文档。此版本对 API 和新的库 NeMo Run 进行了重大更改。我们目前正在将 NeMo 1.0 中的所有功能移植到 2.0。有关先前版本或 2.0 中尚不可用的功能的文档,请参阅 NeMo 24.07 文档

NeVA (LLaVA)#

NeVA 源自 LLaVA v1.5 (大型语言和视觉助手),是 NeMo 多模态生态系统的关键补充。此模型集成了以大型语言为中心的模型(如 Nemotron、Llama 3、Mistral 等)与视觉编码器,并使用机器生成的多模态语言-图像指令跟随数据进行训练。NeVA 在 LLaVA 奠定的基础上,通过利用 NeMo LLM 框架的功能(如模型并行、序列并行、激活检查点、AMP O2、CuDNN/Flash Attention 等)进一步增强了训练。

从 Hugging Face 导入到 NeMo 2.0#

要导入 Hugging Face (HF) 模型并将其转换为 NeMo 2.0 格式,请运行以下命令。此步骤只需执行一次

from nemo.collections.llm import import_ckpt
from nemo.collections import vlm

if __name__ == '__main__':
    # Specify the Hugging Face model ID
    hf_model_id = "llava-hf/llava-1.5-7b-hf"

    # Import the model and convert to NeMo 2.0 format
    import_ckpt(
        model=vlm.LlavaModel(vlm.Llava15Config7B()),  # Model configuration
        source=f"hf://{hf_model_id}",  # Hugging Face model source
    )

上面的命令将转换后的文件保存在 NeMo 缓存文件夹中,位置:~/.cache/nemo

如果需要,您可以通过在运行脚本之前设置 NEMO_CACHE_DIR 环境变量来更改默认缓存目录。

NeMo 2.0 微调配方#

我们提供了使用 NeMo 2.0 和 NeMo-Run 微调 LLaVA v1.5 的预定义配方。这些配方为 NeMo 2.0 中引入的 nemo.collections.llm api 函数之一配置了 run.Partial。这些配方托管在 llava15_7bllava15_11b 文件中。这些配方使用模拟数据集进行训练。

注意

这些配方使用 MockDataModule 作为 data 参数。您需要将 MockDataModule 替换为您自定义的数据集。

默认情况下,加载非指令版本的模型。要加载不同的模型,请设置 finetune.resume.restore_config.path=nemo://<hf_model_id>finetune.resume.restore_config.path=<local_model_path>

我们在下面提供了一个示例,说明如何调用默认配方并覆盖数据参数

from nemo.collections import vlm

finetune = vlm.llava15_7b.finetune_recipe(
    name="llava15_7b_finetune",
    dir=f"/path/to/checkpoints",
    num_nodes=1,
    num_gpus_per_node=8,
    peft_scheme='lora',  # 'lora', 'none'
)

默认情况下,微调配方将 LoRA 应用于语言模型中的所有线性层,包括交叉注意力层,同时保持视觉模型不冻结。

  • 配置要应用 LoRA 的层:设置 finetune.peft.target_modules。例如,要仅对自注意力 qkv 投影层应用 LoRA,请设置 finetune.peft.target_modules=["*.language_model.*.linear_qkv"]

  • 冻结视觉模型:设置 finetune.peft.freeze_vision_model=True

  • 在没有 LoRA 的情况下微调整个模型:在配方参数中设置 peft_scheme='none'

注意

配方中的配置是使用 NeMo-Run run.Configrun.Partial 配置对象完成的。请查看 NeMo-Run 文档,以了解有关其配置和执行系统的更多信息。

一旦您准备好最终配置,您就可以在任何 NeMo-Run 支持的执行器上执行它。最简单的是本地执行器,它只是在本地的单独进程中运行预训练。您可以按如下方式使用它

import nemo_run as run

run.run(finetune, executor=run.LocalExecutor())

此外,您也可以在同一个 Python 进程中直接运行它,如下所示

run.run(finetune, direct=True)

自带数据#

将默认配方中的 MockDataModule 替换为您自定义的数据集。下面,我们展示了一个使用类似 llava 数据集的示例

from nemo.collections import vlm

# Define the fine-tuning recipe
finetune = vlm.llava15_7b.finetune_recipe(
    name="llava15_7b_finetune",
    dir="/path/to/checkpoints",
    num_nodes=1,
    num_gpus_per_node=8,
    peft_scheme='lora',  # 'lora', 'none'
)

# The following is an example of a custom dataset configuration.
data_config = vlm.ImageDataConfig(
    image_folder="/path/to/images",
    conv_template="v1",  # Customize based on your dataset needs
)

# Data module setup
custom_data = vlm.NevaLazyDataModule(
    paths="/path/to/dataset.json",  # Path to your dataset
    data_config=data_config,
    seq_length=2048,
    global_batch_size=16,  # Global batch size
    micro_batch_size=1,  # Micro batch size
    tokenizer=None,  # Define your tokenizer if needed
    image_processor=None,  # Add an image processor if required
    num_workers=8,  # Number of workers for data loading
)

# Assign custom data to the fine-tuning recipe
finetune.data = custom_data

使用 Energon 数据加载器#

Energon 数据加载器可以与 NeVA 一起使用,以处理用于训练的多模态数据集。本节介绍如何设置和自定义数据加载器,重点介绍关键组件,例如 task_encodermultimodal_sample_config。有关准备数据以与数据模块一起使用的详细信息,请参阅 数据准备 部分。

下面是如何将 Energon 数据加载器与 NeVA 一起用于训练的示例

from nemo.collections.multimodal.data.energon import (
    ImageToken,
    MultiModalSampleConfig,
    EnergonMultiModalDataModule,
)
from transformers import AutoProcessor

# Load processor, tokenizer, and image processor from pre-trained model
processor = AutoProcessor.from_pretrained("llava-hf/llava-1.5-7b-hf")
tokenizer = processor.tokenizer
image_processor = processor.image_processor

# Define dataset path
dataset_path = "<path_to_dataset>"

# Configure multimodal samples
config = MultiModalSampleConfig(
    image_token=ImageToken(token_str="<image>", token_id=-200),
    ignore_place_holder=-100
)

# Initialize the data module
data_module = EnergonMultiModalDataModule(
    path=dataset_path,
    tokenizer=tokenizer,
    image_processor=image_processor,
    seq_length=2048,
    micro_batch_size=1,
    global_batch_size=16,
    num_workers=0,
    multimodal_sample_config=config,
)

# Note: `EnergonMultiModalDataModule` defaults to `MultiModalTaskEncoder` if no custom task encoder is provided.
  • path: 数据集的路径。

  • tokenizer: 用于处理文本数据的分词器。

  • `image_processor`: 图像处理器,用于预处理图像并准备它们以输入到视觉模型。ImageProcessor

  • seq_length: 标记化文本的最大序列长度(默认值:2048)。

  • micro_batch_size`: 每个 GPU 或进程的批大小(默认值:1)。

  • global_batch_size: 所有 GPU 或进程的总批大小(默认值:1)。

  • num_workers: 用于数据加载的工作进程数(默认值:1)。

  • multimodal_sample_config: 多模态样本的配置,允许自定义图像标记、占位符值和模板配置。

  • task_encoder: 如果需要,可以提供自定义任务编码器。默认情况下,EnergonMultiModalDataModule 使用 MultiModalTaskEncoder

  1. 任务编码器: MultiModalTaskEncoder 是一种灵活的编码器,能够处理各种多模态样本类型,例如 VQA、字幕、交错和相似性交错样本。默认情况下,如果没有提供自定义任务编码器,则 task_encoder 默认为 MultiModalTaskEncoder。这使得入门变得容易,同时仍然允许为高级用例进行自定义。

    它足够灵活,可以为新的样本类型注册其他自定义编码器。编码器将样本处理成批次,并准备它们以输入到 NeVA 模型。有关样本类型的更多详细信息,请参阅 Energon 文档 - Megatron-Energon 样本类型

    这种模块化设计确保它可以适应各种多模态训练场景。

  2. MultiModalSampleConfig: MultiModalSampleConfig 定义了多模态样本的配置。它包括以下默认值,可以根据需要进行自定义

    • image_token: 图像的默认标记配置。默认情况下,图像的占位符字符串是字符串 '<image>',标记 ID 为 -200

    • ignore_place_holder: 默认值为 -100,用于表示在损失计算期间要忽略的占位符标记。

    • conversation_template_config: 默认值为 LLaVATemplateConfig。此配置用于多模态对话模板,用于在标记化之前将提示模板应用于输入文本。如果提供了 conversation_template_config,它将用于生成对话提示。否则,如果分词器定义了聊天模板 (tokenizer.chat_template),则将使用分词器的聊天模板。如果分词器和 conversation_template_config 都没有定义聊天模板,则会为 VQA 样本引发 ValueError

    • image_following_text: 一个布尔值,指示图像标记是否应跟随文本标记。它默认为 True

    以下是对话模板配置的示例

    class BaseConversationTemplateConfig:
        """Conversation template config related parameters"""
    
        system: Optional[str] = (
            "A chat between a curious user and artificial assistant agent. The assistant gives helpful, detailed, and polite answers to user's questions.".format()
        )  # fmt: off
        roles: List[str] = field(default_factory=lambda: ['user', 'assistant'])
        stop_string: str = "</s>"
        chat_template = """
        {%- for message in messages %}
            {%- if message['role'] == 'system' %}
                {{- message['content'].strip() + ' ' -}}
            {%- elif message['role'] == 'user' %}
                {{- 'USER: ' -}} {{- message['content'].strip() + ' ' -}}
            {%- elif message['role'] == 'assistant' %}
                {{- 'ASSISTANT: ' -}} {{- message['content'].strip() -}}
                {{- '</s>' -}}
            {%- endif %}
        {%- endfor -%}
        """
    

    此配置包括以下参数

    • system: 一个字符串,用于定义系统的描述或目的,例如“好奇用户和人工智能助理代理之间的聊天。”

    • roles: 对话中的角色列表(默认值:[‘user’, ‘assistant’])。

    • stop_string: 一个字符串,用于指示对话的结束(默认值:</s>)。

    • chat_template: 一个 Jinja2 模板,用于将对话格式化为一系列消息,以进行输入标记化。

    1. 如果提供了 conversation_template_config,它将优先使用并用于格式化对话。

    2. 如果未提供 conversation_template_config,但存在 tokenizer.chat_template,则将使用分词器的模板。

    3. 如果 conversation_template_configtokenizer.chat_template 都不存在,则会为 VQA 样本引发 ValueError

支持的功能#

  1. 序列打包:有关更多信息,请参阅 在 NeVA 中使用打包序列运行 SFT/PEFT

  2. 其他模型并行性:请查看我们的 示例微调脚本,以获取有关设置其他模型并行性的完整示例。

    • 序列并行:在 MegatronStrategy 中添加 sequence_parallel=True

      from nemo import lightning as nl
      strategy = nl.MegatronStrategy(
          tensor_model_parallel_size=tp_size,
          pipeline_model_parallel_size=pp_size,
          ...
          sequence_parallel=True,
      )
      
    • 上下文并行:在 MegatronStrategy 中添加 context_parallel_size=cp_size

      from nemo import lightning as nl
      strategy = nl.MegatronStrategy(
          tensor_model_parallel_size=tp_size,
          pipeline_model_parallel_size=pp_size,
          ...
          context_parallel_size=cp_size,
      )
      

      上下文并行当前仅适用于语言模型。视觉编码器将在这些 rank 上复制。我们正在研究更好的策略。

    • 虚拟流水线并行:在 MegatronStrategy 中添加 virtual_pipeline_model_parallel_size=vpp_size

      from nemo import lightning as nl
      strategy = nl.MegatronStrategy(
          tensor_model_parallel_size=tp_size,
          pipeline_model_parallel_size=pp_size,
          ...
          virtual_pipeline_model_parallel_size=vpp_size,
      )
      
  3. 支持的视觉编码器:NeMo 支持以下视觉编码器

    • CLIPViT 视觉编码器(Hugging Face 和 Megatron Core 后端)

    • SigLIPViT 视觉编码器(Megatron Core 后端)

    • InternViT 视觉编码器(Megatron Core 后端)

    您可以定义相应的视觉 Transformer 配置,并将其插入到 Neva 配置的定义中。请查看我们的 示例微调脚本,以获取完整示例。

    对于 Megatron 后端模型,您必须首先将模块权重转换为 NeMo 格式,然后再使用 vision_model_from_pretrained 加载它们,否则视觉编码器内部将是随机权重。以下是将 InternViT 转换的示例;类似的步骤适用于 CLIP 和 SigLIP。

    from nemo.collections import vlm
    from nemo.collections.llm import import_ckpt
    
    if __name__ == '__main__':
        model_id = "OpenGVLab/InternViT-300M-448px-V2_5"
        model = vlm.InternViTModel(vlm.InternViT_300M_448px_Config())
        import_ckpt(model=model,
                    source=f'hf://{model_id}',
        )
    

    上面的命令将转换后的文件保存在 NeMo 缓存文件夹中,位置:~/.cache/nemo

    from nemo.collections import vlm
    vision_transformer_config = vlm.HFCLIPVisionConfig(
        pretrained_model_name_or_path="openai/clip-vit-large-patch14-336"  # Change model ID here
    )
    neva_config = vlm.NevaConfig(
        vision_transformer_config=vision_transformer_config,
        ...
    )
    
    from nemo.collections import vlm
    vision_transformer_config = vlm.CLIPViTL_14_336_Config()
    neva_config = vlm.NevaConfig(
        vision_transformer_config=vision_transformer_config,
        vision_model_from_pretrained="/path/to/converted/clip_vit_model",
        ...
    )
    
    from nemo.collections import vlm
    vision_transformer_config = vlm.CLIPViTL_14_336_Config()
    neva_config = vlm.NevaConfig(
        vision_transformer_config=vision_transformer_config,
        vision_model_from_pretrained="/path/to/converted/siglip_vit_model",
        ...
    )
    
    from nemo.collections import vlm
    vision_transformer_config = vlm.InternViT_300M_448px_Config()
    neva_config = vlm.NevaConfig(
        vision_transformer_config=vision_transformer_config,
        vision_model_from_pretrained="/path/to/converted/intern_vit_model",
        ...
    )
    
  4. FP8 训练:要启用 FP8 训练,您需要配置 MegatronMixedPrecision 插件并设置适当的 FP8 参数。有关更多详细信息,请查看 Transformer Engine 用户指南

from nemo import lightning as nl
import torch

trainer = nl.Trainer(
    num_nodes=1,
    devices=8,
    ...
    plugins=nl.MegatronMixedPrecision(
        precision="bf16-mixed",
        params_dtype=torch.bfloat16,
        fp8='hybrid',
        fp8_amax_history_len=16,
        fp8_amax_compute_algo="max",
    ),
)

以下是我们目前支持或计划很快支持的预训练配方的完整列表

配方

状态

LLaVA 1.5 7B LoRA

LLaVA 1.5 7B 完全微调

LLaVA 1.5 11B LoRA

LLaVA 1.5 11B 完全微调