Python 【大模型】之 Deepseek 多模态 Janus 【图生文】【文生图】本地部署/代码调用 的简单整理

Python 【大模型】之 Deepseek 多模态 Janus 【图生文】本地部署/代码调用 的简单整理

目录

Python 【大模型】之 Deepseek 多模态 Janus 【图生文】本地部署/代码调用 的简单整理

 一、简单介绍

部署的环境要求

Janus 涉及相关链接整理

ms-swift微调

二、Janus 相关代码下载

 三、创建虚拟环境,安装相关依赖

conda 相关命令创建

四、下载 Janus-Pro-7B 模型

迅雷加速下载下面的文件

modelscope下载

五、Python 代码测试 图生文 功能

六、Python 代码测试 文生图 功能

附录

一、运行 文生图 报错 torch.cuda.OutOfMemoryError: CUDA out of memory.

1. 清理 GPU 内存

2. 减少模型和数据的内存占用

3. 优化内存分配

4. 监控 GPU 内存使用

5. 释放其他进程占用的 GPU 内存

6. 使用混合精度训练

7. 检查代码中的内存泄漏

8. 调整代码逻辑

9. 升级硬件

总结


 一、简单介绍

DeepSeek作为一家年轻而富有活力的AI公司,通过开源策略、创新能力与充足的资源支持,在短短时间内成长为全球瞩目的新锐力量。其核心成员兼具深厚的学术功底与实践经验,所攻关的大模型在数学、代码、生物计算以及语言理解等多方面已展示出强大的竞争力。

自成立以来,DeepSeek不断地突破研究瓶颈,先后推出了DeepSeek LLM、DeepSeek V-2和DeepSeek V-3等版本,以极高的性能与成本效益比为业界带来了颠覆性的惊喜。与此同时,DeepSeek在多头延迟注意力(MLA)、混合专家模型(MoE)以及FP8混合精度训练等关键技术上取得的进展,也为后续技术迭代铺平了道路。

Janus-Pro-7B 是由 DeepSeek 开发的一款先进的多模态人工智能模型,专注于高效处理文本和图像任务。该模型通过创新的架构设计和优化的训练策略,在多模态理解与生成领域取得了显著的突破,展现了强大的性能和广泛的应用潜力。

架构设计

  1. 视觉编码分离
    Janus-Pro-7B 采用独特的视觉编码分离技术,将视觉编码过程拆分为独立的路径,解决了传统多模态模型中视觉编码器在理解和生成任务中的角色冲突。这种设计不仅提升了模型的灵活性,还增强了其在多模态任务中的性能。

  2. 统一的 Transformer 架构
    尽管视觉编码被分离,但模型仍然使用单一的 Transformer 架构进行处理,确保了整体的统一性和高效性。这种架构设计使得模型在处理复杂的多模态任务时能够保持高效的计算效率。

  3. 高效视觉处理
    Janus-Pro-7B 采用 SigLIP-L 作为视觉编码器,支持高达 384×384 像素的图像输入,能够处理高质量的视觉信息,为多模态任务提供强大的视觉支持。

Janus-Pro:通过数据和模型扩展实现统一的多模态理解和生成

Janus-Pro 是之前工作成果 Janus 的高级版本。具体来说,Janus-Pro 融合了以下三个方面的改进:(1)优化的训练策略;(2)扩展的训练数据;以及(3)更大模型规模的扩展。凭借这些改进,Janus-Pro 在多模态理解和文本到图像指令遵循能力方面取得了显著的进步,同时也增强了文本到图像生成的稳定性。

Janus:通过分离视觉编码实现统一的多模态理解和生成

Janus 是一种新颖的自回归框架,能够统一多模态理解和生成。它通过将视觉编码分解为独立的路径,解决了以往方法的局限性,同时仍然使用单一的统一 Transformer 架构进行处理。这种分离不仅缓解了视觉编码器在理解和生成任务中的角色冲突,还增强了框架的灵活性。Janus 的性能超越了以往的统一模型,并且在某些任务上达到了或超过了专门任务模型的性能水平。Janus 的简洁性、高灵活性和有效性使其成为下一代统一多模态模型的有力候选者。

JanusFlow:通过整合自回归和修正流实现统一的多模态理解和生成

JanusFlow 引入了一种极简主义架构,将自回归语言模型与修正流相结合,后者是生成建模领域的一种前沿方法。我们的关键发现表明,修正流可以在大型语言模型框架内直接进行训练,无需复杂的架构修改。广泛的实验表明,JanusFlow 在各自领域的专业模型中实现了相当或更优的性能,同时在标准基准测试中显著优于现有的统一方法。这项工作标志着向更高效、更通用的视觉语言模型迈出了重要一步。


部署的环境要求

  • 操作系统: Linux/Windows(推荐使用 Ubuntu 20.04+)
  • Python: 3.8以上
  • CUDA: 11.7以上(记得与PyTorch版本保持一致)
  • GPU: 显存 ≥ 16GB(最佳为 ≥ 24GB)
  • 存储空间: ≥ 30GB可用空间。
  • (必要的时候,可能需要科学上网)
  • Janus-Pro是DeepSeek最新开源的多模态模型,是一种新颖的自回归框架,统一了多模态理解和生成。通过将视觉编码解耦为独立的路径,同时仍然使用单一的、统一的变压器架构进行处理,该框架解决了先前方法的局限性。这种解耦不仅缓解了视觉编码器在理解和生成中的角色冲突,还增强了框架的灵活性。Janus-Pro 超过了以前的统一模型,并且匹配或超过了特定任务模型的性能。


    Janus 涉及相关链接整理

  • 代码链接:https://github.com/deepseek-ai/Janus

  • 模型链接:https://modelscope.cn/collections/Janus-Pro-0f5e48f6b96047

  • 体验页面:https://modelscope.cn/studios/AI-ModelScope/Janus-Pro-7B

  • 技术报告:https://github.com/deepseek-ai/Janus/blob/main/janus_pro_tech_report.pdf


  • ms-swift微调

    ms-swift是魔搭社区提供的大模型与多模态大模型训练部署框架,现已支持450+大模型与150+多模态大模型的训练(预训练、微调、人类对齐)、推理、评测、量化与部署。模型开发者可以在ms-swift框架中一站式完成围绕大模型的各类需求。目前ms-swift的主要能力包含:

    🍎 模型类型:支持450+纯文本大模型、150+多模态大模型,All-to-All全模态模型的训练到部署全流程。

    数据集类型:内置150+预训练、微调、人类对齐、多模态等各种类型的数据集,并支持自定义数据集。

    硬件支持:CPU、RTX系列、T4/V100、A10/A100/H100、Ascend NPU等。

    🍊 轻量训练:支持了LoRA、QLoRA、DoRA、LoRA+、ReFT、RS-LoRA、LLaMAPro、Adapter、GaLore、Q-Galore、LISA、UnSloth、Liger-Kernel等轻量微调方式。

    分布式训练:支持分布式数据并行(DDP)、device_map简易模型并行、DeepSpeed ZeRO2 ZeRO3、FSDP等分布式训练技术。

    量化训练:支持对BNB、AWQ、GPTQ、AQLM、HQQ、EETQ量化模型进行训练。

    RLHF训练:支持纯文本大模型和多模态大模型的DPO、CPO、SimPO、ORPO、KTO、RM、PPO等人类对齐训练方法。

    🍓 多模态训练:支持对图像、视频和语音不同模态模型进行训练,支持VQA、Caption、OCR、Grounding任务的训练。

    界面训练:以界面的方式提供训练、推理、评测、量化的能力,完成大模型的全链路。

    插件化与拓展:支持自定义模型和数据集拓展,支持对loss、metric、trainer、loss-scale、callback、optimizer等组件进行自定义。

    🍉 工具箱能力:除了对大模型和多模态大模型的训练支持外,还支持其推理、评测、量化和部署全流程。

    推理加速:支持PyTorch、vLLM、LmDeploy推理加速引擎,并提供OpenAI接口,为推理、部署和评测模块提供加速。

    模型评测:以EvalScope作为评测后端,支持100+评测数据集对纯文本和多模态模型进行评测。

    模型量化:支持AWQ、GPTQ和BNB的量化导出,导出的模型支持使用vLLM/LmDeploy推理加速,并支持继续训练。

    二、Janus 相关代码下载

    Janus 代码地址:GitHub – deepseek-ai/Janus: Janus-Series: Unified Multimodal Understanding and Generation Models

    1、Git 下载安装(已有可忽略)

    Git 官网:Git

    (Git 下载安装简单这里不在赘述)

    2、选择一个下载的文件夹路径,准备下载 Janus 相关代码

    3、右键调出 Git 下载界面,克隆 Janus 相关代码

    命令:git clone https://github.com/deepseek-ai/Janus.git

    4、克隆好后,进入文件夹 Janus ,文件夹结构大概如下

     三、创建虚拟环境,安装相关依赖

    虚拟环境是一个独立的 Python 环境,它允许你在不同的项目中使用不同版本的 Python 和依赖包,而不会相互干扰。虚拟环境通过隔离项目依赖,解决了全局 Python 环境中可能出现的依赖冲突问题。

    可以使用 conda (Anaconda)创建,或者 python 自带的 virtualenv 创建虚拟空间

    conda 相关命令创建

    1、conda 下载

    GitHub – conda/conda: A system-level, binary package and environment manager running on all major operating systems and platforms.

    2、创建虚拟环境

    conda create -n Janus python=3.10 -y
    

    3、激活环境

    conda activate Janus
    

    4、把激活的路径切换到之前下载好 Janus 代码的路径

    5、安装 Janus 需要的相关依赖

    命令:pip install -e .

    (之后可以使用 pip list 查看环境安装的相关package )

    6、查看是否安装 gradio ,没有就安装 gradio

    命令:pip show gradio ,pip install gradio

    四、下载 Janus-Pro-7B 模型

    下载链接:https://www.modelscope.cn/models/deepseek-ai/Janus-Pro-7B/files

    迅雷加速下载下面的文件

    modelscope下载

    1、可以用 modelscope下载,先安装 modelscope

    命令:pip install modelscope

    2、可以使用 命令下载

    命令:modelscope download –model deepseek-ai/Janus-Pro-7B

    3、使用代码下载,使用 Pycharm 打开工程

    注意添加好 该工程的 interpreter 为之前创建的虚拟环境

    4、这里查看自己的 cuda 版本,对应安装 torch 相关

    命令:pip install torch==2.2.2+cu118 torchvision==0.17.2+cu118 torchaudio==2.2.2+cu118 -f https://download.pytorch.org/whl/torch_stable.html

    (不清楚安装那个版本,可以上官网查询,也可问大模型需求答案)

    pip install torch==2.2.2+cu118 torchvision==0.17.2+cu118 torchaudio==2.2.2+cu118 -f https://download.pytorch.org/whl/torch_stable.html

    5、使用 modelscope 的 snapshot_download 下载 Janus-Pro-7B,运行脚本,开始下载

    # 模型下载
    from modelscope import snapshot_download
    model_dir = snapshot_download('deepseek-ai/Janus-Pro-7B', cache_dir="Data")
    

    五、Python 代码测试 图生文 功能

    1、添加测试图片

    2、编写测试代码

    import torch
    from transformers import AutoModelForCausalLM
    from Janus.janus.models import MultiModalityCausalLM, VLChatProcessor
    from Janus.janus.utils.io import load_pil_images
    
    # 模型路径,指向预训练的多模态因果语言模型
    model_path = "deepseek-ai/Janus-Pro-7B"
    
    # 图片路径,用于后续的多模态输入
    image = './img.png'
    # 用户提出的问题,将与图片一起输入模型
    question = '请说明一下这张图片'
    
    # 加载预训练的多模态对话处理器(VLChatProcessor),用于处理对话和图片输入
    vl_chat_processor: VLChatProcessor = VLChatProcessor.from_pretrained(model_path)
    # 获取 tokenizer,用于文本的编码和解码
    tokenizer = vl_chat_processor.tokenizer
    
    # 加载预训练的多模态因果语言模型(MultiModalityCausalLM)
    vl_gpt: MultiModalityCausalLM = AutoModelForCausalLM.from_pretrained(
        model_path, trust_remote_code=True  # 允许加载远程代码(如果模型中包含自定义代码)
    )
    # 将模型转换为 bfloat16 数据类型,并移动到 GPU 上,设置为评估模式
    vl_gpt = vl_gpt.to(torch.bfloat16).cuda().eval()
    
    # 定义对话内容,包含用户的角色、问题以及对应的图片路径
    conversation = [
        {
            "role": "<|User|>",  # 用户角色标识
            "content": f"<image_placeholder>\n{question}",  # 图片占位符和问题文本
            "images": [image],  # 图片路径列表
        },
        {"role": "<|Assistant|>", "content": ""},  # 助手角色的初始内容为空
    ]
    
    # 加载图片并准备输入数据
    # 使用 load_pil_images 函数加载对话中提到的图片
    pil_images = load_pil_images(conversation)
    # 使用 VLChatProcessor 处理对话和图片,准备模型的输入数据
    # force_batchify=True 强制将输入数据批量化处理
    prepare_inputs = vl_chat_processor(
        conversations=conversation, images=pil_images, force_batchify=True
    ).to(vl_gpt.device)  # 将输入数据移动到模型所在的设备(GPU)
    
    # 运行图像编码器,获取图像嵌入(embeddings)
    # 使用模型的 prepare_inputs_embeds 方法处理输入数据,提取图像嵌入
    inputs_embeds = vl_gpt.prepare_inputs_embeds(**prepare_inputs)
    
    # 运行模型生成回答
    # 使用模型的语言模型(language_model)生成回答
    # 设置生成参数,如最大新生成的 token 数量、是否采样等
    outputs = vl_gpt.language_model.generate(
        inputs_embeds=inputs_embeds,  # 输入嵌入
        attention_mask=prepare_inputs.attention_mask,  # 注意力掩码
        pad_token_id=tokenizer.eos_token_id,  # 填充 token 的 ID
        bos_token_id=tokenizer.bos_token_id,  # 开始 token 的 ID
        eos_token_id=tokenizer.eos_token_id,  # 结束 token 的 ID
        max_new_tokens=512,  # 最大新生成的 token 数量
        do_sample=False,  # 是否采样生成
        use_cache=True,  # 是否使用缓存
    )
    
    # 解码生成的回答
    # 使用 tokenizer 将生成的 token 序列解码为文本
    answer = tokenizer.decode(outputs[0].cpu().tolist(), skip_special_tokens=True)
    # 打印对话格式和模型生成的回答
    print(f"{prepare_inputs['sft_format'][0]}", answer)

    3、右键运行脚本

    4、运行结果如下图,还是能够正确识别图片内容

    六、Python 代码测试 文生图 功能

    1、编写文生图代码

    # 导入必要的库
    import os
    import torch
    import numpy as np
    from PIL import Image
    from transformers import AutoModelForCausalLM
    from Janus.janus.models import MultiModalityCausalLM, VLChatProcessor
    
    # 设置环境变量以避免内存碎片化
    os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
    
    # 指定模型路径
    model_path = "deepseek-ai/Janus-Pro-7B"
    # 定义生成的图片描述内容
    content = "A glass of red wine on a reflective surface. "
    
    # 加载预训练的多模态对话处理器(VLChatProcessor)
    vl_chat_processor: VLChatProcessor = VLChatProcessor.from_pretrained(model_path)
    # 获取 tokenizer,用于文本的编码和解码
    tokenizer = vl_chat_processor.tokenizer
    
    # 加载预训练的多模态因果语言模型(MultiModalityCausalLM)
    vl_gpt: MultiModalityCausalLM = AutoModelForCausalLM.from_pretrained(
        model_path, trust_remote_code=True
    )
    # 将模型转换为 bfloat16 数据类型,并移动到 GPU 上,设置为评估模式
    vl_gpt = vl_gpt.to(torch.bfloat16).cuda().eval()
    
    # 清理未使用的 GPU 内存
    torch.cuda.empty_cache()
    
    # 定义对话内容,包含用户的角色和描述内容
    conversation = [
        {
            "role": "<|User|>",
            "content": content,
        },
        {"role": "<|Assistant|>", "content": ""},
    ]
    
    # 应用 SFT 模板,格式化对话内容
    sft_format = vl_chat_processor.apply_sft_template_for_multi_turn_prompts(
        conversations=conversation,
        sft_format=vl_chat_processor.sft_format,
        system_prompt="",
    )
    # 添加图片开始标记
    prompt = sft_format + vl_chat_processor.image_start_tag
    
    
    # 定义生成图片的函数
    @torch.inference_mode()
    def generate(
            mmgpt: MultiModalityCausalLM,
            vl_chat_processor: VLChatProcessor,
            prompt: str,
            temperature: float = 1,
            parallel_size: int = 4,
            cfg_weight: float = 5,
            image_token_num_per_image: int = 2048,  # 调整为 2048
            img_size: int = 256,  # 调整图片大小
            patch_size: int = 16,
    ):
        input_ids = vl_chat_processor.tokenizer.encode(prompt)
        input_ids = torch.LongTensor(input_ids).cuda()
    
        tokens = torch.zeros((parallel_size * 2, len(input_ids)), dtype=torch.int).cuda()
        for i in range(parallel_size * 2):
            tokens[i, :] = input_ids
            if i % 2 != 0:
                tokens[i, 1:-1] = vl_chat_processor.pad_id
    
        inputs_embeds = mmgpt.language_model.get_input_embeddings()(tokens)
    
        generated_tokens = torch.zeros((parallel_size, image_token_num_per_image), dtype=torch.int).cuda()
    
        for i in range(image_token_num_per_image):
            outputs = mmgpt.language_model.model(inputs_embeds=inputs_embeds, use_cache=True,
                                                 past_key_values=outputs.past_key_values if i != 0 else None)
            hidden_states = outputs.last_hidden_state
    
            logits = mmgpt.gen_head(hidden_states[:, -1, :])
            logit_cond = logits[0::2, :]
            logit_uncond = logits[1::2, :]
            logits = logit_uncond + cfg_weight * (logit_cond - logit_uncond)
    
            probs = torch.softmax(logits / temperature, dim=-1)
    
            next_token = torch.multinomial(probs, num_samples=1)
            generated_tokens[:, i] = next_token.squeeze(dim=-1)
    
            next_token = torch.cat([next_token.unsqueeze(dim=1), next_token.unsqueeze(dim=1)], dim=1).view(-1)
            img_embeds = mmgpt.prepare_gen_img_embeds(next_token)
            inputs_embeds = img_embeds.unsqueeze(dim=1)
    
        # 在调整 generated_tokens 形状之前打印原始形状
        print(f"Generated tokens shape before reshape: {generated_tokens.shape}")
    
        # 尝试调整形状,以确保它适合解码器
        try:
            generated_tokens = generated_tokens.view(parallel_size, 16, 16, 8)
        except Exception as e:
            print(f"Error reshaping generated tokens: {e}")
            # 根据实际需要调整 shape 参数
            # 例如:generated_tokens = generated_tokens.view(parallel_size, -1, 16)
            raise
    
        # 打印最终形状,检查是否匹配
        print(f"Generated tokens shape after reshape: {generated_tokens.shape}")
    
        # 调整 shape 参数以匹配解码器的大小
        try:
            dec = mmgpt.gen_vision_model.decode_code(
                generated_tokens.to(dtype=torch.int),
                shape=[parallel_size, 16, 16, 8]  # 确保形状与卷积层权重匹配
            )
        except Exception as e:
            print(f"Error during decode_code: {e}")
            raise
    
        dec = dec.to(torch.float32).cpu().numpy().transpose(0, 2, 3, 1)
    
        # 确保图像范围正确
        dec = np.clip((dec + 1) / 2 * 255, 0, 255)
    
        visual_img = np.zeros((parallel_size, img_size, img_size, 3), dtype=np.uint8)
        visual_img[:, :, :] = dec
    
        os.makedirs('generated_samples', exist_ok=True)
        for i in range(parallel_size):
            save_path = os.path.join('generated_samples', f"img_{i}.jpg")
            Image.fromarray(visual_img[i]).save(save_path)
    
    
    # 调用生成函数
    generate(
        vl_gpt,
        vl_chat_processor,
        prompt,
    )
    

    2、右键运行相关代码

    3、运行

    (运行时间可能会长些,具体根据设备配置有关)

    4、最后生成的图

    附录

    一、运行 文生图 报错 torch.cuda.OutOfMemoryError: CUDA out of memory.

    torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 144.00 MiB. GPU 0 has a total capacity of 8.00 GiB of which 0 bytes is free. Of the allocated memory 22.17 GiB is allocated by PyTorch, and 106.34 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)

    从错误信息来看, GPU 内存不足,导致 PyTorch 无法为新分配的内存(144.00 MiB)找到足够的空间。这通常是由于 GPU 内存碎片化或分配过多内存给其他进程导致的。以下是一些解决方法和优化建议,仅供参考:

    1. 清理 GPU 内存

    在运行代码之前,可以尝试清理 GPU 内存,释放未使用的内存:

    import torch
    torch.cuda.empty_cache()  # 清理未使用的 GPU 内存

    2. 减少模型和数据的内存占用

  • 使用更小的数据类型:将模型和数据转换为 torch.float16torch.bfloat16,这些数据类型占用的内存更少。

  • model = model.to(torch.float16).cuda()
  • 减少批量大小:降低批量大小可以显著减少 GPU 内存的占用。

  • 减少模型大小:如果可能,使用更小的预训练模型。

  • 3. 优化内存分配

  • 设置内存分配参数:根据错误信息中的建议,可以尝试设置环境变量 PYTORCH_CUDA_ALLOC_CONF 来避免内存碎片化:

  • import os
    os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'

    这将允许 PyTorch 使用可扩展的内存段,从而减少内存碎片化。

    4. 监控 GPU 内存使用

    使用 nvidia-smi 命令监控 GPU 内存的使用情况:

    watch -n 1 nvidia-smi

    这可以帮助你了解 GPU 内存的实时使用情况,以及是否有其他进程占用了大量内存。

    5. 释放其他进程占用的 GPU 内存

    如果有其他进程占用了大量 GPU 内存,可以尝试杀死这些进程:

    nvidia-smi
    # 找到占用 GPU 内存的进程 ID
    kill -9 <PID>

    6. 使用混合精度训练

    如果使用 PyTorch 的 torch.cuda.amp(自动混合精度),可以在训练时自动选择合适的数据类型,减少内存占用:

    from torch.cuda.amp import GradScaler, autocast
    
    scaler = GradScaler()
    
    with autocast():
        outputs = model(inputs)
        loss = criterion(outputs, labels)
    
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

    7. 检查代码中的内存泄漏

    确保没有不必要的变量占用 GPU 内存。例如,使用 del 删除不再需要的变量:

    del some_large_variable
    torch.cuda.empty_cache()

    8. 调整代码逻辑

    在你的代码中,可以尝试以下优化:

  • 减少并行生成的图片数量:将 parallel_size 设置为更小的值(例如 4 或 8)。

  • 减少每张图片的 token 数量:将 image_token_num_per_image 设置为更小的值(例如 256)。

  • 修改后的代码示例:

    generate(
        vl_gpt,
        vl_chat_processor,
        prompt,
        parallel_size=4,  # 减少并行生成的图片数量
        image_token_num_per_image=256,  # 减少每张图片的 token 数量
    )

    9. 升级硬件

    如果上述方法都无法解决问题,可能需要考虑升级 GPU,选择具有更大显存的设备。

    总结

    通过清理 GPU 内存、减少模型和数据的内存占用、优化内存分配、监控 GPU 内存使用、释放其他进程占用的 GPU 内存、使用混合精度训练、检查代码中的内存泄漏以及调整代码逻辑,可以有效解决 torch.cuda.OutOfMemoryError 错误。如果问题仍然存在,可能需要升级硬件。

    作者:仙魁XAN

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python 【大模型】之 Deepseek 多模态 Janus 【图生文】【文生图】本地部署/代码调用 的简单整理

    发表回复