[GH-ISSUE #209] 可以像OpenAI一样对图像做吉卜力风格的转换吗? #169

Closed
opened 2026-10-26 21:13:22 +00:00 by bsakaguchi · 4 comments
Owner

Originally created by @princepride on GitHub (Apr 2, 2025).
Original GitHub issue: https://github.com/deepseek-ai/Janus/issues/209

我注意到Janus-pro和gpt-4o一样也是端到端的多模态模型,但是我自己尝试把两段示例代码合并进行图片的吉卜力风格转换,貌似没有成功,请问这样做是可行的吗,可以提供示例代码吗

Originally created by @princepride on GitHub (Apr 2, 2025). Original GitHub issue: https://github.com/deepseek-ai/Janus/issues/209 我注意到Janus-pro和gpt-4o一样也是端到端的多模态模型,但是我自己尝试把两段示例代码合并进行图片的吉卜力风格转换,貌似没有成功,请问这样做是可行的吗,可以提供示例代码吗
Author
Owner

@princepride commented on GitHub (Apr 4, 2025):

import os
import torch
import numpy as np
from transformers import AutoModelForCausalLM
from janus.models import MultiModalityCausalLM, VLChatProcessor
import PIL

# specify the path to the model
model_path = "deepseek-ai/Janus-Pro-7B"
vl_chat_processor: VLChatProcessor = VLChatProcessor.from_pretrained(model_path)
tokenizer = vl_chat_processor.tokenizer

vl_gpt: MultiModalityCausalLM = AutoModelForCausalLM.from_pretrained(
    model_path, trust_remote_code=True
)
vl_gpt = vl_gpt.to(torch.bfloat16).cuda().eval()

from janus.utils.io import load_pil_images

question = "Help me convert this image to Ghibli-style"
image = r"image.jpg"
conversation = [
    {
        "role": "<|User|>",
        "content": f"<image_placeholder>\n{question}",
        "images": [image],
    },
    {"role": "<|Assistant|>", "content": ""},
]

# ---------------------------
# 第一部分:获取图像+提示词对应的 embedding
# ---------------------------
# 加载图片并准备输入
pil_images = load_pil_images(conversation)
prepare_inputs = vl_chat_processor(
    conversations=conversation, images=pil_images, force_batchify=True
).to(vl_gpt.device)

# 生成图像和文本输入对应的 embedding(条件分支初始 embedding)
inputs_embeds = vl_gpt.prepare_inputs_embeds(**prepare_inputs)  # shape: (1, seq_length, embed_dim)

# 将 vl_chat_processor.image_start_tag 转换为 token,再获取对应的 embedding
image_start_token_ids = tokenizer.encode(vl_chat_processor.image_start_tag, add_special_tokens=False)
# 假设转换后的 tag 为一段 token(如多个 token,也可适当处理),这里直接获取所有 token的 embedding
image_start_token_tensor = torch.tensor(image_start_token_ids, device=vl_gpt.device).unsqueeze(0)  # shape: (1, tag_len)
image_start_embeds = vl_gpt.language_model.get_input_embeddings()(image_start_token_tensor)  # shape: (1, tag_len, embed_dim)

# 拼接:在原有 inputs_embeds 后追加 image_start_embeds,形成新的条件分支 embedding
cond_embeds = torch.cat([inputs_embeds, image_start_embeds], dim=1)  # shape: (1, seq_length+tag_len, embed_dim)
seq_len = cond_embeds.shape[1]

# 生成无条件分支 embedding:复制 cond_embeds,然后将除首尾外的所有 token 替换为 pad token 的 embedding
uncond_embeds = cond_embeds.clone()
pad_id = vl_chat_processor.pad_id
# 获取 pad token 的 embedding(shape: (1, embed_dim))
pad_embedding = vl_gpt.language_model.get_input_embeddings()(torch.tensor([pad_id], device=vl_gpt.device))
if seq_len > 2:
    # 替换除首尾外位置
    uncond_embeds[:, 1:-1, :] = pad_embedding.expand(seq_len - 2, -1).unsqueeze(0)

# 合并条件和无条件分支,得到最终用于生成的输入 embedding(shape: (2, seq_len, embed_dim))
final_inputs_embeds = torch.cat([cond_embeds, uncond_embeds], dim=0)

# ---------------------------
# 第二部分:基于处理后的 embedding 生成图像
# ---------------------------
@torch.inference_mode()
def generate(
    mmgpt: MultiModalityCausalLM,
    vl_chat_processor: VLChatProcessor,
    final_inputs_embeds: torch.Tensor,
    temperature: float = 1,
    parallel_size: int = 1, 
    cfg_weight: float = 5,
    image_token_num_per_image: int = 576,
    img_size: int = 384,
    patch_size: int = 16,
):
    # 使用处理好的 final_inputs_embeds 作为起始输入(2行:条件与无条件)
    inputs_embeds = final_inputs_embeds
    generated_tokens = torch.zeros((parallel_size, image_token_num_per_image), dtype=torch.int).cuda()
    past_key_values = None

    for i in range(image_token_num_per_image):
        outputs = mmgpt.language_model.model(
            inputs_embeds=inputs_embeds,
            use_cache=True,
            past_key_values=past_key_values if i != 0 else None
        )
        past_key_values = outputs.past_key_values
        hidden_states = outputs.last_hidden_state  # shape: (2, seq_len, hidden_dim)

        logits = mmgpt.gen_head(hidden_states[:, -1, :])
        # 分别提取条件与无条件的 logits
        logit_cond = logits[0::2, :]
        logit_uncond = logits[1::2, :]
        logits_combined = logit_uncond + cfg_weight * (logit_cond - logit_uncond)
        probs = torch.softmax(logits_combined / temperature, dim=-1)

        next_token = torch.multinomial(probs, num_samples=1)
        generated_tokens[:, i] = next_token.squeeze(dim=-1)

        # 构造下步输入 embedding:复制生成的 token 为条件和无条件分支(各一份)
        next_token_cat = 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_cat)
        inputs_embeds = img_embeds.unsqueeze(dim=1)

    # 解码生成的图像 token
    dec = mmgpt.gen_vision_model.decode_code(
        generated_tokens.to(dtype=torch.int),
        shape=[parallel_size, 8, img_size // patch_size, img_size // patch_size]
    )
    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")
        PIL.Image.fromarray(visual_img[i]).save(save_path)

# 调用生成函数
generate(
    vl_gpt,
    vl_chat_processor,
    final_inputs_embeds,
)

我参考示例代码写了个图生图的推理程序,基本上是在图生文的代码基础上,添加了生图标识,然后在input_embed部分构造了一个padding的input_embed,但是生成的图片没有任何意义,是本身不可实现图生图还是我的代码有问题

<!-- gh-comment-id:2778446306 --> @princepride commented on GitHub (Apr 4, 2025): ``` import os import torch import numpy as np from transformers import AutoModelForCausalLM from janus.models import MultiModalityCausalLM, VLChatProcessor import PIL # specify the path to the model model_path = "deepseek-ai/Janus-Pro-7B" vl_chat_processor: VLChatProcessor = VLChatProcessor.from_pretrained(model_path) tokenizer = vl_chat_processor.tokenizer vl_gpt: MultiModalityCausalLM = AutoModelForCausalLM.from_pretrained( model_path, trust_remote_code=True ) vl_gpt = vl_gpt.to(torch.bfloat16).cuda().eval() from janus.utils.io import load_pil_images question = "Help me convert this image to Ghibli-style" image = r"image.jpg" conversation = [ { "role": "<|User|>", "content": f"<image_placeholder>\n{question}", "images": [image], }, {"role": "<|Assistant|>", "content": ""}, ] # --------------------------- # 第一部分:获取图像+提示词对应的 embedding # --------------------------- # 加载图片并准备输入 pil_images = load_pil_images(conversation) prepare_inputs = vl_chat_processor( conversations=conversation, images=pil_images, force_batchify=True ).to(vl_gpt.device) # 生成图像和文本输入对应的 embedding(条件分支初始 embedding) inputs_embeds = vl_gpt.prepare_inputs_embeds(**prepare_inputs) # shape: (1, seq_length, embed_dim) # 将 vl_chat_processor.image_start_tag 转换为 token,再获取对应的 embedding image_start_token_ids = tokenizer.encode(vl_chat_processor.image_start_tag, add_special_tokens=False) # 假设转换后的 tag 为一段 token(如多个 token,也可适当处理),这里直接获取所有 token的 embedding image_start_token_tensor = torch.tensor(image_start_token_ids, device=vl_gpt.device).unsqueeze(0) # shape: (1, tag_len) image_start_embeds = vl_gpt.language_model.get_input_embeddings()(image_start_token_tensor) # shape: (1, tag_len, embed_dim) # 拼接:在原有 inputs_embeds 后追加 image_start_embeds,形成新的条件分支 embedding cond_embeds = torch.cat([inputs_embeds, image_start_embeds], dim=1) # shape: (1, seq_length+tag_len, embed_dim) seq_len = cond_embeds.shape[1] # 生成无条件分支 embedding:复制 cond_embeds,然后将除首尾外的所有 token 替换为 pad token 的 embedding uncond_embeds = cond_embeds.clone() pad_id = vl_chat_processor.pad_id # 获取 pad token 的 embedding(shape: (1, embed_dim)) pad_embedding = vl_gpt.language_model.get_input_embeddings()(torch.tensor([pad_id], device=vl_gpt.device)) if seq_len > 2: # 替换除首尾外位置 uncond_embeds[:, 1:-1, :] = pad_embedding.expand(seq_len - 2, -1).unsqueeze(0) # 合并条件和无条件分支,得到最终用于生成的输入 embedding(shape: (2, seq_len, embed_dim)) final_inputs_embeds = torch.cat([cond_embeds, uncond_embeds], dim=0) # --------------------------- # 第二部分:基于处理后的 embedding 生成图像 # --------------------------- @torch.inference_mode() def generate( mmgpt: MultiModalityCausalLM, vl_chat_processor: VLChatProcessor, final_inputs_embeds: torch.Tensor, temperature: float = 1, parallel_size: int = 1, cfg_weight: float = 5, image_token_num_per_image: int = 576, img_size: int = 384, patch_size: int = 16, ): # 使用处理好的 final_inputs_embeds 作为起始输入(2行:条件与无条件) inputs_embeds = final_inputs_embeds generated_tokens = torch.zeros((parallel_size, image_token_num_per_image), dtype=torch.int).cuda() past_key_values = None for i in range(image_token_num_per_image): outputs = mmgpt.language_model.model( inputs_embeds=inputs_embeds, use_cache=True, past_key_values=past_key_values if i != 0 else None ) past_key_values = outputs.past_key_values hidden_states = outputs.last_hidden_state # shape: (2, seq_len, hidden_dim) logits = mmgpt.gen_head(hidden_states[:, -1, :]) # 分别提取条件与无条件的 logits logit_cond = logits[0::2, :] logit_uncond = logits[1::2, :] logits_combined = logit_uncond + cfg_weight * (logit_cond - logit_uncond) probs = torch.softmax(logits_combined / temperature, dim=-1) next_token = torch.multinomial(probs, num_samples=1) generated_tokens[:, i] = next_token.squeeze(dim=-1) # 构造下步输入 embedding:复制生成的 token 为条件和无条件分支(各一份) next_token_cat = 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_cat) inputs_embeds = img_embeds.unsqueeze(dim=1) # 解码生成的图像 token dec = mmgpt.gen_vision_model.decode_code( generated_tokens.to(dtype=torch.int), shape=[parallel_size, 8, img_size // patch_size, img_size // patch_size] ) 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") PIL.Image.fromarray(visual_img[i]).save(save_path) # 调用生成函数 generate( vl_gpt, vl_chat_processor, final_inputs_embeds, ) ``` 我参考示例代码写了个图生图的推理程序,基本上是在图生文的代码基础上,添加了生图标识,然后在input_embed部分构造了一个padding的input_embed,但是生成的图片没有任何意义,是本身不可实现图生图还是我的代码有问题
Author
Owner

@akikohaku commented on GitHub (Apr 11, 2025):

你好,根据论文里所描述的模型结构以及实测,janus-pro模型的图生图似乎是无法实现的,模型并没有训练过从und. encoder或者tokenizer输入图像来生成图像,其中使用的gen. encoder仅用于训练过程。

在另一个模型JanusFlow中,我认为图生图是可以实现的,其生成原理类似于sd3.5,使用latent图像的输入使其具备图生图的可能性,但我尚未测试这一方案。

<!-- gh-comment-id:2796098454 --> @akikohaku commented on GitHub (Apr 11, 2025): 你好,根据论文里所描述的模型结构以及实测,janus-pro模型的图生图似乎是无法实现的,模型并没有训练过从und. encoder或者tokenizer输入图像来生成图像,其中使用的gen. encoder仅用于训练过程。 在另一个模型JanusFlow中,我认为图生图是可以实现的,其生成原理类似于sd3.5,使用latent图像的输入使其具备图生图的可能性,但我尚未测试这一方案。
Author
Owner

@princepride commented on GitHub (Apr 11, 2025):

原来如此,我还想可能会在Zero Shot的情况下进行图生图,我在JanusFlow模型试试

<!-- gh-comment-id:2796113942 --> @princepride commented on GitHub (Apr 11, 2025): 原来如此,我还想可能会在Zero Shot的情况下进行图生图,我在JanusFlow模型试试
Author
Owner

@LHRYANG commented on GitHub (May 22, 2025):

原来如此,我还想可能会在Zero Shot的情况下进行图生图,我在JanusFlow模型试试

请问你试了吗?JanusFlow可以吗

<!-- gh-comment-id:2899685025 --> @LHRYANG commented on GitHub (May 22, 2025): > 原来如此,我还想可能会在Zero Shot的情况下进行图生图,我在JanusFlow模型试试 请问你试了吗?JanusFlow可以吗
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
deepseek-ai/Janus#169
No description provided.