DeepSeek Janus-Pro の動作確認と結果レポート（Mac M3 16GBも実行可能！）

## **はじめに**

本記事では、Macのローカル環境およびNvidia GPU環境で DeepSeek Janus-Proを動作させる手順を解説します。

今回は **Mac M3 (16GB)**, **Nvidia L4** を使用し、**Janus-Pro 1B**および**7Bモデル** の性能を比較します。

また、画像生成を含むマルチモーダルタスクの実行結果についても詳細なレポートを記載します。

## Janus-Proは何のモデル

**Janus-Pro** は、視覚的理解とテキスト生成を統一的に処理できる多モーダルモデルです。1Bや7Bといった異なるパラメータ規模のモデルを提供し、多モーダル理解やテキストから画像生成のタスクで高い性能を発揮します。

### 特徴：

1. **多モーダル統一性**：視覚とテキストタスクを統合し、高い効率性を実現。
2. **拡張性**：1Bから7Bまでのスケールで、さまざまなハードウェア環境に対応可能。
3. **卓越した性能**：GenEvalやDPG-Benchなどのベンチマークで、競合モデルを上回る成果。
    
    ![](https://storage.googleapis.com/zenn-user-upload/b32a386698ac-20250201.png)
    

## **動作確認環境**

| デバイス | モデル | 結果 |
| --- | --- | --- |
| **MacBook Pro M3 (16GB)** | **Janus-Pro 1B** | ✅ 動作可能 (推論時間約115秒) |
| **MacBook Pro M3 (16GB)** | **Janus-Pro 7B** | ❌ 動作不可 (メモリ不足で起動できず) |
| **Nvidia L4 GPU** | **Janus-Pro 7B** | ✅ 動作可能 (推論時間約35秒) |

**⚠ 注意**
- Mac M3 **16GB** では **Janus-Pro 7B を起動できません**。
- メモリ不足によりロード時にクラッシュするため、Macで7Bモデルを利用する場合は **32GB以上のRAMを推奨** します。
- 一方、**Janus-Pro 1B は 16GB でも問題なく動作** しました。
- また、**Nvidia L4 (GPU) では Janus-Pro 7B をスムーズに動作可能** でした。

## **環境構築**

### **1. モデルのダウンロード**

Hugging Face から **DeepSeek Janus-Pro** のモデルをダウンロードします。

```bash
git clone https://github.com/deepseek-ai/Janus.git
```

### **2. 必要なライブラリのインストール**

`transformers` や `torch` などのライブラリをインストールします。

```bash
cd Janus
pip install -e .
```

## **DeepSeek Janus-Pro の実行**

### **1. Python スクリプトの作成**

公式のサンプル例をもとに作成し、Mac環境でテストできます。

<details>
<summary>
Sample code
</summary>

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

device = torch.device("cpu")

# specify the path to the model
model_path = "deepseek-ai/Janus-Pro-1B"
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
)

conversation = [
    {
        "role": "
",
        "content": "A child's handwritten letter on an aged parchment, with an ink quill resting nearby."
    },
    {"role": "
", "content": ""},
]

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 = 1,
    cfg_weight: float = 5,
    image_token_num_per_image: int = 576,
    img_size: int = 384,
    patch_size: int = 16,
):
    input_ids = vl_chat_processor.tokenizer.encode(prompt)
    input_ids = torch.LongTensor(input_ids)

    tokens = torch.zeros((parallel_size*2, len(input_ids)), dtype=torch.int).to(device)
    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()
    tokens = torch.zeros((parallel_size*2, len(input_ids)), dtype=torch.int).to(device)
    generated_tokens = torch.zeros((parallel_size, image_token_num_per_image), dtype=torch.int).to(device)

    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)

    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.float16).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

    from datetime import datetime

    current_time = datetime.now()
    hour = current_time.strftime("%H")
    minute = current_time.strftime("%M")

    os.makedirs('generated_samples_test_1b', exist_ok=True)
    for i in range(parallel_size):
        save_path = os.path.join('generated_samples_test_1b', f"img_{hour}_{minute}_{i}.jpg")
        PIL.Image.fromarray(visual_img[i]).save(save_path)

import time

start_time = time.time()

generate(
    vl_gpt,
    vl_chat_processor,
    prompt,
)

end_time = time.time()
elapsed_time = end_time - start_time
print(f"Time cost: {elapsed_time:.2f}s")

```
</details>

## **結果レポート**

### **1. 生成した画像**
**Janus-Pro 1B**
| ![](https://storage.googleapis.com/zenn-user-upload/9fe541babb6e-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/73e37c1681a1-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/87457fd6f2c7-20250201.jpg) |
|--------------------------|--------------------------|--------------------------|

| ![](https://storage.googleapis.com/zenn-user-upload/b6a5afbdc7cb-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/8da8ec1ebbb0-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/b2837e694a96-20250201.jpg) |
|--------------------------|--------------------------|--------------------------|

**Janus-Pro 7B**
| ![](https://storage.googleapis.com/zenn-user-upload/548a1e7401fe-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/eb431b18e307-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/da4ee31a08b6-20250201.jpg) |
|--------------------------|--------------------------|--------------------------|


| ![](https://storage.googleapis.com/zenn-user-upload/51897919ed5f-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/550584429d4a-20250201.jpg) | ![](https://storage.googleapis.com/zenn-user-upload/a01b36a92d10-20250201.jpg) |
|--------------------------|--------------------------|--------------------------|

<details>
<summary>
Prompts
</summary>

```plaintext
1. A glass teapot on a wooden table, with sunlight filtering through the leaves, casting intricate shadows.
2. A dimly lit alley at sunset, with neon lights reflecting off the wet cobblestone street, and a cat sitting under a streetlamp.
3. A group of diverse people sitting in a cozy cafe, engaged in deep conversation, with steaming cups of coffee in front of them.
4. A crystal ball placed on a velvet cloth, reflecting and distorting the surroundings.
5. A neon sign in Tokyo with kanji glowing against a rainy night, reflecting on the wet pavement.
6. A child's handwritten letter on an aged parchment, with an ink quill resting nearby.
```
</details>



### **2. 画像の品質**

| 項目 | 評価 |
| --- | --- |
| **色合いの正確性** | ⭐⭐⭐⭐☆ |
| **ディテールの再現** | ⭐⭐⭐☆☆ |
| **プロンプト適合度** | ⭐⭐⭐☆☆ |
| **推論速度** | ⭐⭐☆☆☆ |


## **まとめ**

### **メリット**

✅ **ローカル環境で LLM+画像生成が可能**

✅ **Nvidia L4 では 7B モデルも高速推論が可能**

✅ **テキスト・画像のマルチモーダル出力が可能**

### **デメリット**

❌ **Mac M3 (16GB) では Janus-Pro 7B の動作不可**

❌ **推論速度がやや遅い（特にM1/M3 Macでの実行）**

❌ **複雑なシーン生成能力が限定的**

DeepSeek Janus-Pro は、**ローカル環境でマルチモーダル AI を扱いたい人** にとって非常に興味深いモデルですが、生成された画像の**色彩感覚や全体的な雰囲気は優れている**ものの、細かいディテールや複雑なシーンの再現では改善の余地があります。
## **おまけ**
私は、**yhooai ソリューション**として、AIを活用しお客様の課題解決を支援することに取り組んでいます。これまでのプロジェクトや取り組みについては、[公式ウェブサイト](https://yhooai.com/) に詳しく記載しております。お仕事のご依頼や相談がございましたら、以下のリンクよりお気軽にご連絡ください。今後ともどうぞよろしくお願いいたします！

https://yhooai.com/#contact