第5章:生成モデルの応用 (Applications of Generative Models)

Text-to-Image生成からアバター作成システムまでの実践的応用

📖 読了時間: 25-30分 📊 難易度: 中級〜上級 💻 コード例: 7個 📝 演習問題: 5問

学習目標

この章を読むことで、以下を習得できます:


5.1 Text-to-Image生成

Stable Diffusionの概要

Stable Diffusion は、テキストプロンプトから高品質な画像を生成する拡散モデルです。2022年にStability AIによって公開され、オープンソースで利用可能な最も強力なText-to-Image生成モデルの一つです。

```mermaid
graph LR
    A[テキストプロンプト] --> B[CLIP Text Encoder]
    B --> C[Text Embedding77×768]
    C --> D[U-Net Denoiser]
    E[ランダムノイズLatent Space] --> D
    D --> F[Denoising Steps20-50回]
    F --> G[VAE Decoder]
    G --> H[生成画像512×512 or 1024×1024]

    style A fill:#e3f2fd
    style H fill:#c8e6c9
    style D fill:#fff9c4
```

Stable Diffusionのアーキテクチャ構成要素

コンポーネント役割技術詳細
Text Encoderテキストを埋め込みベクトルに変換CLIP ViT-L/14(OpenAI)
VAE Encoder/Decoder画像とLatent空間の変換圧縮率8×、512×512→64×64
U-Net Denoiserノイズ除去と画像生成Cross-attention機構でテキスト条件付け
Schedulerノイズスケジュール管理DDPM, DDIM, Euler, DPM-Solver++

Stable Diffusionの実装

import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
from PIL import Image
import matplotlib.pyplot as plt

class StableDiffusionGenerator:
    """
    Stable Diffusionを使ったText-to-Image生成クラス

    Features:
    - 複数のスケジューラサポート(DDPM, DDIM, Euler, DPM-Solver++)
    - Negative promptサポート
    - CFG(Classifier-Free Guidance)制御
    - シード固定による再現性
    """

    def __init__(self, model_id="stabilityai/stable-diffusion-2-1", device="cuda"):
        """
        Args:
            model_id: HuggingFaceのモデルID
            device: 使用デバイス(cuda or cpu)
        """
        self.device = device if torch.cuda.is_available() else "cpu"

        # パイプラインの初期化
        self.pipe = StableDiffusionPipeline.from_pretrained(
            model_id,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            safety_checker=None  # 実運用では適切なフィルタリングを実装
        )

        # より高速なDPM-Solver++スケジューラを使用
        self.pipe.scheduler = DPMSolverMultistepScheduler.from_config(
            self.pipe.scheduler.config
        )

        self.pipe = self.pipe.to(self.device)

        # メモリ最適化(GPU使用時)
        if self.device == "cuda":
            self.pipe.enable_attention_slicing()
            self.pipe.enable_vae_slicing()

    def generate(
        self,
        prompt,
        negative_prompt="",
        num_inference_steps=25,
        guidance_scale=7.5,
        width=512,
        height=512,
        seed=None,
        num_images=1
    ):
        """
        テキストプロンプトから画像を生成

        Args:
            prompt: 生成したい画像の説明文
            negative_prompt: 避けたい要素の説明文
            num_inference_steps: ノイズ除去のステップ数(20-50推奨)
            guidance_scale: CFGスケール(7-15推奨、高いほどプロンプトに忠実)
            width, height: 生成画像のサイズ(8の倍数)
            seed: 再現性のためのシード値
            num_images: 生成する画像の枚数

        Returns:
            生成された画像のリスト
        """
        # シード設定
        generator = None
        if seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)

        # 画像生成
        with torch.autocast(self.device):
            output = self.pipe(
                prompt=prompt,
                negative_prompt=negative_prompt,
                num_inference_steps=num_inference_steps,
                guidance_scale=guidance_scale,
                width=width,
                height=height,
                generator=generator,
                num_images_per_prompt=num_images
            )

        return output.images

    def generate_grid(self, prompts, **kwargs):
        """
        複数のプロンプトから画像グリッドを生成

        Args:
            prompts: プロンプトのリスト
            **kwargs: generateメソッドへの追加引数

        Returns:
            グリッド画像
        """
        images = []
        for prompt in prompts:
            img = self.generate(prompt, num_images=1, **kwargs)[0]
            images.append(img)

        # グリッド作成
        n = len(images)
        cols = int(n ** 0.5)
        rows = (n + cols - 1) // cols

        w, h = images[0].size
        grid = Image.new('RGB', (w * cols, h * rows))

        for idx, img in enumerate(images):
            grid.paste(img, ((idx % cols) * w, (idx // cols) * h))

        return grid

# 使用例
if __name__ == "__main__":
    # ジェネレーターの初期化
    sd = StableDiffusionGenerator()

    # 基本的な生成
    prompt = "A beautiful sunset over mountains, oil painting style, highly detailed"
    negative_prompt = "blurry, low quality, distorted"

    images = sd.generate(
        prompt=prompt,
        negative_prompt=negative_prompt,
        num_inference_steps=30,
        guidance_scale=7.5,
        seed=42,
        num_images=2
    )

    # 画像表示
    fig, axes = plt.subplots(1, 2, figsize=(12, 6))
    for idx, img in enumerate(images):
        axes[idx].imshow(img)
        axes[idx].axis('off')
        axes[idx].set_title(f'Image {idx + 1}')
    plt.tight_layout()
    plt.show()

    # 複数プロンプトでグリッド生成
    prompts = [
        "A cat astronaut in space, digital art",
        "A futuristic city at night, cyberpunk style",
        "A magical forest with glowing mushrooms",
        "A steampunk robot playing violin"
    ]

    grid = sd.generate_grid(
        prompts,
        negative_prompt="ugly, blurry, low quality",
        num_inference_steps=25,
        guidance_scale=7.5,
        seed=42
    )

    plt.figure(figsize=(10, 10))
    plt.imshow(grid)
    plt.axis('off')
    plt.title('Generated Image Grid')
    plt.show()

プロンプトエンジニアリング技術

プロンプトエンジニアリング は、望む画像を生成するためにテキストプロンプトを最適化する技術です。効果的なプロンプトの構成要素を理解することが重要です。

効果的なプロンプトの構造

$$ \text{Prompt} = \text{Subject} + \text{Style} + \text{Quality} + \text{Details} + \text{Modifiers} $$

要素説明
Subjectメインの被写体”a majestic lion”, “a futuristic building”
Style芸術スタイル”oil painting”, “anime style”, “photorealistic”
Quality品質修飾子”highly detailed”, “8k resolution”, “masterpiece”
Details具体的な詳細”golden hour lighting”, “dramatic shadows”
Modifiers追加の調整”trending on artstation”, “by Greg Rutkowski”
class PromptEngineer:
    """
    効果的なプロンプトを構築するためのヘルパークラス
    """

    # プロンプトテンプレート
    STYLE_KEYWORDS = {
        'photorealistic': 'photorealistic, photo, realistic, high quality photograph',
        'digital_art': 'digital art, digital painting, artstation',
        'oil_painting': 'oil painting, traditional art, canvas',
        'anime': 'anime style, manga, japanese animation',
        'cyberpunk': 'cyberpunk style, neon lights, futuristic',
        '3d_render': '3d render, octane render, unreal engine, blender'
    }

    QUALITY_KEYWORDS = [
        'highly detailed',
        '8k resolution',
        'masterpiece',
        'best quality',
        'sharp focus',
        'professional'
    ]

    NEGATIVE_KEYWORDS = [
        'blurry',
        'low quality',
        'bad anatomy',
        'distorted',
        'ugly',
        'duplicate',
        'watermark'
    ]

    @staticmethod
    def build_prompt(
        subject,
        style='photorealistic',
        quality_level='high',
        additional_details=None,
        artist=None
    ):
        """
        構造化されたプロンプトを構築

        Args:
            subject: メインの被写体
            style: スタイルキーワード
            quality_level: 品質レベル('high', 'medium', 'low')
            additional_details: 追加の詳細(リストまたは文字列)
            artist: アーティスト名(オプション)

        Returns:
            構築されたプロンプト
        """
        components = [subject]

        # スタイル追加
        if style in PromptEngineer.STYLE_KEYWORDS:
            components.append(PromptEngineer.STYLE_KEYWORDS[style])
        else:
            components.append(style)

        # 品質キーワード追加
        if quality_level == 'high':
            components.extend(PromptEngineer.QUALITY_KEYWORDS[:4])
        elif quality_level == 'medium':
            components.extend(PromptEngineer.QUALITY_KEYWORDS[:2])

        # 追加詳細
        if additional_details:
            if isinstance(additional_details, list):
                components.extend(additional_details)
            else:
                components.append(additional_details)

        # アーティスト名
        if artist:
            components.append(f"by {artist}")

        return ", ".join(components)

    @staticmethod
    def build_negative_prompt(custom_negatives=None):
        """
        Negative promptを構築

        Args:
            custom_negatives: カスタムネガティブキーワード

        Returns:
            Negative prompt文字列
        """
        negatives = PromptEngineer.NEGATIVE_KEYWORDS.copy()
        if custom_negatives:
            negatives.extend(custom_negatives)
        return ", ".join(negatives)

    @staticmethod
    def optimize_for_faces(base_prompt):
        """
        顔の生成に特化したプロンプト最適化
        """
        face_keywords = [
            'detailed face',
            'perfect eyes',
            'symmetrical face',
            'professional portrait',
            'sharp facial features'
        ]
        return f"{base_prompt}, {', '.join(face_keywords)}"

    @staticmethod
    def optimize_for_landscapes(base_prompt):
        """
        風景画に特化したプロンプト最適化
        """
        landscape_keywords = [
            'wide angle',
            'epic vista',
            'atmospheric',
            'dramatic lighting',
            'depth of field'
        ]
        return f"{base_prompt}, {', '.join(landscape_keywords)}"

# 使用例
if __name__ == "__main__":
    pe = PromptEngineer()

    # ポートレート生成
    portrait_prompt = pe.build_prompt(
        subject="a young woman with flowing red hair",
        style="digital_art",
        quality_level="high",
        additional_details=["golden hour lighting", "soft shadows"],
        artist="Ilya Kuvshinov"
    )
    portrait_prompt = pe.optimize_for_faces(portrait_prompt)

    # 風景画生成
    landscape_prompt = pe.build_prompt(
        subject="a serene mountain lake surrounded by pine trees",
        style="oil_painting",
        quality_level="high",
        additional_details=["misty morning", "reflections on water"]
    )
    landscape_prompt = pe.optimize_for_landscapes(landscape_prompt)

    # Negative prompt
    negative = pe.build_negative_prompt(["deformed", "disfigured"])

    print("Portrait Prompt:")
    print(portrait_prompt)
    print("\nLandscape Prompt:")
    print(landscape_prompt)
    print("\nNegative Prompt:")
    print(negative)

プロンプトのベストプラクティス :

CFG(Classifier-Free Guidance)の理論

CFGは、条件付き生成において品質を向上させる技術です。条件付きモデルと無条件モデルの予測を組み合わせます。

$$ \epsilon_\theta(z_t, c, t) = \epsilon_\theta(z_t, \emptyset, t) + s \cdot (\epsilon_\theta(z_t, c, t) - \epsilon_\theta(z_t, \emptyset, t)) $$

ここで:


5.2 Image-to-Image変換

Style Transfer(スタイル転送)

Style Transfer は、ある画像のスタイル(色彩、筆触、質感)を別の画像のコンテンツに適用する技術です。Stable Diffusionでは、初期ノイズの代わりに既存画像を使用します。

from diffusers import StableDiffusionImg2ImgPipeline
from PIL import Image
import torch

class StyleTransferGenerator:
    """
    Stable Diffusionを使ったImage-to-Image変換クラス

    Features:
    - Style transfer
    - Image variation生成
    - Strength制御による変換度合い調整
    """

    def __init__(self, model_id="stabilityai/stable-diffusion-2-1", device="cuda"):
        self.device = device if torch.cuda.is_available() else "cpu"

        self.pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
            model_id,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            safety_checker=None
        )

        self.pipe = self.pipe.to(self.device)

        if self.device == "cuda":
            self.pipe.enable_attention_slicing()

    def transfer_style(
        self,
        input_image,
        style_prompt,
        strength=0.75,
        guidance_scale=7.5,
        num_inference_steps=50,
        seed=None
    ):
        """
        画像にスタイルを適用

        Args:
            input_image: 入力画像(PIL ImageまたはパスString)
            style_prompt: 適用したいスタイルの説明
            strength: 変換の強度(0.0-1.0、高いほど大きく変化)
            guidance_scale: CFGスケール
            num_inference_steps: ステップ数
            seed: ランダムシード

        Returns:
            スタイル変換後の画像
        """
        # 画像読み込み
        if isinstance(input_image, str):
            input_image = Image.open(input_image).convert('RGB')

        # リサイズ(8の倍数に)
        w, h = input_image.size
        w = (w // 8) * 8
        h = (h // 8) * 8
        input_image = input_image.resize((w, h))

        # シード設定
        generator = None
        if seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)

        # スタイル転送
        with torch.autocast(self.device):
            output = self.pipe(
                prompt=style_prompt,
                image=input_image,
                strength=strength,
                guidance_scale=guidance_scale,
                num_inference_steps=num_inference_steps,
                generator=generator
            )

        return output.images[0]

    def create_variations(
        self,
        input_image,
        prompt,
        num_variations=4,
        strength=0.5,
        **kwargs
    ):
        """
        入力画像のバリエーションを生成

        Args:
            input_image: 入力画像
            prompt: 変換の方向性を示すプロンプト
            num_variations: 生成するバリエーション数
            strength: 変換強度

        Returns:
            バリエーション画像のリスト
        """
        variations = []
        for i in range(num_variations):
            seed = kwargs.get('seed', None)
            if seed is not None:
                seed = seed + i

            var_img = self.transfer_style(
                input_image,
                prompt,
                strength=strength,
                seed=seed,
                **{k: v for k, v in kwargs.items() if k != 'seed'}
            )
            variations.append(var_img)

        return variations

# 使用例
if __name__ == "__main__":
    st = StyleTransferGenerator()

    # スタイル転送の例
    input_image = "path/to/photo.jpg"

    style_prompts = [
        "oil painting in the style of Van Gogh, swirling brushstrokes",
        "anime style, Studio Ghibli aesthetic, vibrant colors",
        "cyberpunk style, neon lights, futuristic",
        "watercolor painting, soft colors, artistic"
    ]

    fig, axes = plt.subplots(2, 3, figsize=(15, 10))
    axes = axes.flatten()

    # 元画像表示
    original = Image.open(input_image)
    axes[0].imshow(original)
    axes[0].set_title('Original')
    axes[0].axis('off')

    # 各スタイルで転送
    for idx, style_prompt in enumerate(style_prompts):
        styled_img = st.transfer_style(
            input_image,
            style_prompt,
            strength=0.75,
            num_inference_steps=50,
            seed=42
        )

        axes[idx + 1].imshow(styled_img)
        axes[idx + 1].set_title(style_prompt[:30] + '...')
        axes[idx + 1].axis('off')

    # 最後のセルは非表示
    axes[-1].axis('off')

    plt.tight_layout()
    plt.show()

Super-Resolution(超解像)

Super-Resolution は、低解像度画像から高解像度画像を生成する技術です。拡散モデルベースのアプローチが最先端の性能を示しています。

import torch
import torch.nn as nn
from diffusers import StableDiffusionUpscalePipeline
from PIL import Image

class SuperResolutionModel:
    """
    Stable Diffusion Upscalerを使った超解像クラス

    Features:
    - 4倍アップスケーリング
    - ノイズ除去と詳細補完
    - プロンプトによる品質制御
    """

    def __init__(self, model_id="stabilityai/stable-diffusion-x4-upscaler", device="cuda"):
        self.device = device if torch.cuda.is_available() else "cpu"

        self.pipe = StableDiffusionUpscalePipeline.from_pretrained(
            model_id,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
        )

        self.pipe = self.pipe.to(self.device)

        if self.device == "cuda":
            self.pipe.enable_attention_slicing()
            self.pipe.enable_vae_slicing()

    def upscale(
        self,
        input_image,
        prompt="high quality, detailed",
        num_inference_steps=50,
        guidance_scale=7.5,
        noise_level=20,
        seed=None
    ):
        """
        画像を4倍にアップスケール

        Args:
            input_image: 低解像度入力画像
            prompt: 品質向上のプロンプト
            num_inference_steps: ステップ数
            guidance_scale: CFGスケール
            noise_level: ノイズレベル(0-100、高いほどより多くの詳細を生成)
            seed: ランダムシード

        Returns:
            アップスケールされた画像
        """
        # 画像読み込み
        if isinstance(input_image, str):
            input_image = Image.open(input_image).convert('RGB')

        # シード設定
        generator = None
        if seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)

        # アップスケーリング
        with torch.autocast(self.device):
            upscaled = self.pipe(
                prompt=prompt,
                image=input_image,
                num_inference_steps=num_inference_steps,
                guidance_scale=guidance_scale,
                noise_level=noise_level,
                generator=generator
            ).images[0]

        return upscaled

    def progressive_upscale(self, input_image, target_size, **kwargs):
        """
        段階的アップスケーリング(非常に大きなサイズ向け)

        Args:
            input_image: 入力画像
            target_size: 目標サイズ (width, height)

        Returns:
            アップスケールされた画像
        """
        if isinstance(input_image, str):
            input_image = Image.open(input_image).convert('RGB')

        current_img = input_image
        current_size = current_img.size

        while current_size[0] < target_size[0] or current_size[1] < target_size[1]:
            # 4倍アップスケール
            current_img = self.upscale(current_img, **kwargs)
            current_size = current_img.size

            print(f"Upscaled to: {current_size}")

            # 目標サイズを超えたら終了
            if current_size[0] >= target_size[0] and current_size[1] >= target_size[1]:
                break

        # 最終的に目標サイズにリサイズ
        if current_size != target_size:
            current_img = current_img.resize(target_size, Image.LANCZOS)

        return current_img

# 使用例
if __name__ == "__main__":
    sr = SuperResolutionModel()

    # 低解像度画像をアップスケール
    low_res_image = "path/to/low_res.jpg"

    # 異なるノイズレベルで比較
    noise_levels = [10, 20, 40, 60]

    fig, axes = plt.subplots(2, 3, figsize=(15, 10))
    axes = axes.flatten()

    # 元画像
    original = Image.open(low_res_image)
    axes[0].imshow(original)
    axes[0].set_title(f'Original ({original.size[0]}x{original.size[1]})')
    axes[0].axis('off')

    # 各ノイズレベルでアップスケール
    for idx, noise_level in enumerate(noise_levels):
        upscaled = sr.upscale(
            low_res_image,
            prompt="high quality, sharp, detailed, professional photograph",
            noise_level=noise_level,
            seed=42
        )

        axes[idx + 1].imshow(upscaled)
        axes[idx + 1].set_title(f'Noise Level {noise_level}\n({upscaled.size[0]}x{upscaled.size[1]})')
        axes[idx + 1].axis('off')

    axes[-1].axis('off')

    plt.tight_layout()
    plt.show()

5.3 条件付き生成 (Conditional Generation)

Conditional GAN (cGAN)

Conditional GAN は、クラスラベルや属性情報などの条件に基づいて画像を生成するGANの拡張です。GeneratorとDiscriminatorの両方が条件情報を受け取ります。

```mermaid
graph TB
    A[Random Noise z] --> G[Generator G]
    B[Condition cClass Label] --> G
    G --> C[Fake Image x̃]

    D[Real Image x] --> Disc[Discriminator D]
    C --> Disc
    B --> Disc

    Disc --> E[Real/Fake + Class]

    style A fill:#e3f2fd
    style B fill:#fff9c4
    style C fill:#ffccbc
    style D fill:#c8e6c9
    style E fill:#f8bbd0
```

cGANの目的関数

$$ \min_G \max_D V(D, G) = \mathbb{E}{x \sim p{data}(x)}[\log D(x|c)] + \mathbb{E}_{z \sim p_z(z)}[\log(1 - D(G(z|c)|c))] $$

ここで:


5.4 Audio生成

WaveGAN概要

WaveGAN は、生の音声波形を直接生成するGANです。画像生成GANのアーキテクチャを1次元畳み込みに適応させています。

```mermaid
graph LR
    A[Random Noise100-dim] --> B[FC Layer16×256]
    B --> C[Reshape256×16]
    C --> D[Transposed Conv1D×5Upsample]
    D --> E[Output16384 samples1 second @ 16kHz]

    F[Real Audio] --> G[Conv1D×5Downsample]
    E --> G
    G --> H[FC Layer] --> I[Real/Fake]

    style A fill:#e3f2fd
    style E fill:#c8e6c9
    style I fill:#f8bbd0
```

WaveGANの特徴

特徴画像GANWaveGAN
畳み込み2D Conv1D Conv(時間軸)
サンプル長64×64ピクセル16384サンプル(1秒@16kHz)
アップサンプリング2倍ずつ4倍、8倍、16倍など
正規化Batch NormPhase Shuffle(位相シャッフル)

Phase Shuffle : WaveGANの重要な技術で、訓練中にランダムに位相をシフトさせることで、Discriminatorが特定の位相に過学習するのを防ぎます。これによりアーティファクトの少ない自然な音声が生成されます。


5.5 実践プロジェクト: アバター生成システム

アバター生成の要件定義

実用的なアバター生成システムには以下の機能が必要です:

アートワーク作成システム

class ArtworkCreationSystem:
    """
    芸術作品生成システム

    Features:
    - 様々なアートスタイル(油絵、水彩、デジタルアートなど)
    - 構図制御
    - 色彩パレット指定
    - アーティストスタイル模倣
    """

    ART_STYLES = {
        'oil_painting': 'oil painting on canvas, thick brush strokes, impasto technique',
        'watercolor': 'watercolor painting, soft colors, transparent layers, paper texture',
        'digital_art': 'digital art, digital painting, trending on artstation, highly detailed',
        'impressionism': 'impressionist style, loose brushwork, emphasis on light, outdoor scene',
        'surrealism': 'surrealist art, dreamlike, bizarre imagery, subconscious inspiration',
        'abstract': 'abstract art, non-representational, geometric shapes, bold colors',
        'minimalist': 'minimalist art, simple composition, limited color palette, negative space',
        'cyberpunk': 'cyberpunk art, neon colors, futuristic, high tech low life aesthetic'
    }

    COMPOSITIONS = {
        'rule_of_thirds': 'rule of thirds composition, balanced',
        'symmetrical': 'symmetrical composition, centered, mirror-like',
        'diagonal': 'diagonal composition, dynamic, movement',
        'golden_ratio': 'golden ratio composition, harmonious proportions',
        'minimalist': 'minimalist composition, lots of negative space'
    }

    COLOR_PALETTES = {
        'warm': 'warm color palette, reds, oranges, yellows',
        'cool': 'cool color palette, blues, greens, purples',
        'monochromatic': 'monochromatic color scheme, shades of single color',
        'complementary': 'complementary colors, high contrast',
        'pastel': 'pastel colors, soft, muted tones',
        'vibrant': 'vibrant colors, saturated, bold'
    }

    def __init__(self, device="cuda"):
        self.device = device if torch.cuda.is_available() else "cpu"

        self.sd_pipe = StableDiffusionPipeline.from_pretrained(
            "stabilityai/stable-diffusion-2-1",
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
            safety_checker=None
        ).to(self.device)

        if self.device == "cuda":
            self.sd_pipe.enable_attention_slicing()

    def create_artwork(
        self,
        subject,
        art_style='digital_art',
        composition='rule_of_thirds',
        color_palette='vibrant',
        artist_reference=None,
        mood=None,
        additional_details=None,
        num_inference_steps=50,
        guidance_scale=7.5,
        seed=None,
        size=(768, 768)
    ):
        """
        芸術作品を生成

        Args:
            subject: 主題(例: "a mountain landscape", "a cat")
            art_style: アートスタイル
            composition: 構図
            color_palette: 色彩パレット
            artist_reference: 参照アーティスト名
            mood: 雰囲気(例: "melancholic", "joyful")
            additional_details: 追加詳細
            num_inference_steps: ステップ数
            guidance_scale: CFGスケール
            seed: ランダムシード
            size: 画像サイズ

        Returns:
            生成されたアートワーク、使用したプロンプト
        """
        # プロンプト構築
        components = [subject]

        # スタイル
        if art_style in self.ART_STYLES:
            components.append(self.ART_STYLES[art_style])
        else:
            components.append(art_style)

        # 構図
        if composition in self.COMPOSITIONS:
            components.append(self.COMPOSITIONS[composition])

        # 色彩パレット
        if color_palette in self.COLOR_PALETTES:
            components.append(self.COLOR_PALETTES[color_palette])

        # 雰囲気
        if mood:
            components.append(f"{mood} mood")

        # アーティスト参照
        if artist_reference:
            components.append(f"in the style of {artist_reference}")

        # 品質キーワード
        components.extend([
            "masterpiece",
            "highly detailed",
            "professional",
            "award winning"
        ])

        # 追加詳細
        if additional_details:
            if isinstance(additional_details, list):
                components.extend(additional_details)
            else:
                components.append(additional_details)

        prompt = ", ".join(components)

        # Negative prompt
        negative_prompt = "low quality, blurry, distorted, ugly, bad art, amateur"

        # 生成
        generator = None
        if seed is not None:
            generator = torch.Generator(device=self.device).manual_seed(seed)

        with torch.autocast(self.device):
            output = self.sd_pipe(
                prompt=prompt,
                negative_prompt=negative_prompt,
                num_inference_steps=num_inference_steps,
                guidance_scale=guidance_scale,
                generator=generator,
                width=size[0],
                height=size[1]
            )

        return output.images[0], prompt

    def create_series(
        self,
        base_subject,
        num_variations=4,
        vary_parameter='color_palette',
        **base_kwargs
    ):
        """
        テーマに沿った作品シリーズを生成

        Args:
            base_subject: 基本主題
            num_variations: バリエーション数
            vary_parameter: 変化させるパラメータ
            **base_kwargs: 固定パラメータ

        Returns:
            作品リスト、プロンプトリスト
        """
        artworks = []
        prompts = []

        # 変化させる値のリスト
        if vary_parameter == 'color_palette':
            variations = list(self.COLOR_PALETTES.keys())
        elif vary_parameter == 'art_style':
            variations = list(self.ART_STYLES.keys())
        elif vary_parameter == 'composition':
            variations = list(self.COMPOSITIONS.keys())
        else:
            variations = [None] * num_variations

        for i, variation in enumerate(variations[:num_variations]):
            kwargs = base_kwargs.copy()
            if variation:
                kwargs[vary_parameter] = variation

            seed = base_kwargs.get('seed')
            if seed is not None:
                kwargs['seed'] = seed + i

            artwork, prompt = self.create_artwork(base_subject, **kwargs)
            artworks.append(artwork)
            prompts.append(prompt)

        return artworks, prompts

# 使用例
if __name__ == "__main__":
    art_system = ArtworkCreationSystem()

    # 単一作品生成
    artwork, prompt = art_system.create_artwork(
        subject="a serene zen garden with cherry blossoms",
        art_style="watercolor",
        composition="rule_of_thirds",
        color_palette="pastel",
        mood="peaceful",
        seed=42
    )

    print(f"Artwork prompt: {prompt}")
    artwork.show()

    # 作品シリーズ(色彩パレット変化)
    artworks, prompts = art_system.create_series(
        base_subject="a mystical forest",
        num_variations=4,
        vary_parameter='color_palette',
        art_style='digital_art',
        composition='diagonal',
        seed=100
    )

    # グリッド表示
    fig, axes = plt.subplots(2, 2, figsize=(12, 12))
    axes = axes.flatten()

    for idx, (artwork, prompt) in enumerate(zip(artworks, prompts)):
        axes[idx].imshow(artwork)
        axes[idx].axis('off')
        # 色彩パレット名を抽出
        palette = prompt.split('color palette')[0].split(',')[-1].strip()
        axes[idx].set_title(palette.capitalize())

    plt.tight_layout()
    plt.show()

5.6 倫理的考慮事項

生成AIの倫理的課題

生成モデルの応用には重要な倫理的課題が伴います。責任ある開発と利用のために以下の点を考慮する必要があります。

課題説明対策例
ディープフェイク本物と見分けがつかない偽画像・動画電子透かし、出所証明、検出技術
著作権侵害訓練データの権利、生成物の帰属ライセンス確認、適切なクレジット表記
バイアスと公平性訓練データのバイアスが生成物に反映多様なデータセット、バイアス検出
悪用リスク有害コンテンツ、詐欺、ハラスメントセーフティフィルター、利用規約
プライバシー訓練データの個人情報漏洩データ匿名化、差分プライバシー

責任ある利用のためのベストプラクティス

```mermaid
graph TB
    A[生成AIの開発・利用] --> B[透明性]
    A --> C[説明責任]
    A --> D[公平性]
    A --> E[プライバシー保護]
    A --> F[安全性]

    B --> B1[モデルの限界を明示]
    B --> B2[生成物である旨を表示]

    C --> C1[利用規約の整備]
    C --> C2[監査可能性の確保]

    D --> D1[バイアステスト実施]
    D --> D2[多様な表現の保証]

    E --> E1[データ保護措置]
    E --> E2[同意取得プロセス]

    F --> F1[有害コンテンツフィルター]
    F --> F2[誤用防止機能]

    style A fill:#e3f2fd
    style B fill:#c8e6c9
    style C fill:#fff9c4
    style D fill:#ffccbc
    style E fill:#f8bbd0
    style F fill:#b2dfdb
```

実装時の推奨事項 :

  1. Safety Checker実装 : 有害・不適切コンテンツの検出と除外
  2. 透かし埋め込み : AI生成物であることを示す不可視マーカー
  3. 利用ログ記録 : 悪用時の追跡可能性確保
  4. ユーザー教育 : 適切な利用方法とリスクの周知
  5. 継続的監視 : モデルの振る舞いとバイアスのモニタリング

法的・規制的側面


演習問題

演習1: プロンプトエンジニアリング

課題 : 以下のシナリオに対して効果的なプロンプトを設計してください:

  1. 中世ヨーロッパの城の風景画(油絵スタイル)
  2. 未来都市のネオン街(サイバーパンクスタイル)
  3. 静かな日本庭園(水彩画スタイル)

要件 :

ヒント : PromptEngineerクラスを参考に、各要素を明確に分離して構築しましょう。

演習2: Style Transfer実装

課題 : StyleTransferGeneratorクラスを拡張し、以下の機能を追加してください:

  1. 複数スタイル比較 : 1つの画像に複数のスタイルを適用し比較表示
  2. 強度グラデーション : strength値を段階的に変化させた結果を表示
  3. スタイル合成 : 2つのスタイルプロンプトを組み合わせ

期待される出力 : グリッド画像で各バリエーションを可視化

演習3: Conditional GAN拡張

課題 : ConditionalGANクラスを拡張し、複数属性の条件付け(Multi-Label Conditional GAN)を実装してください。

仕様 :

評価基準 : 指定した複数属性を持つ画像が生成されるか

演習4: アバターシステム改善

課題 : AvatarGenerationSystemに以下の機能を追加してください:

  1. アバター編集機能 : 生成後に属性を部分的に変更
  2. 一貫性スコア : 複数生成されたアバターの一貫性を評価
  3. バッチ処理 : 大量のアバターを効率的に生成
  4. カスタムスタイル学習 : ユーザー提供画像からスタイルを学習

実装のポイント : Image-to-Image変換を活用し、既存アバターを基に修正する方法を検討しましょう。

演習5: 倫理的セーフガード実装

課題 : 生成システムに倫理的セーフガードを実装してください:

実装項目 :

  1. コンテンツフィルター : 不適切なプロンプトを検出・拒否
  2. 透かし埋め込み : AI生成を示すマーカーを画像に追加
  3. 生成ログ : プロンプトと生成物を記録
  4. バイアス検出 : 特定属性の過剰/過少表現を検出

テストケース :


まとめ

この章では、生成モデルの実践的応用について学習しました:

生成AIは強力な技術ですが、その利用には責任が伴います。技術的スキルと倫理的配慮の両方を持って、社会に貢献する応用を開発していきましょう。