2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像生成のプロンプトエンジニアリング

2
Posted at
[POSITIVE PROMPT]
日本アニメ風 3Dトゥーンキャラクター, セルシェード(Toon), アウトラインあり, ノイズなし, AO軽め,
日本式ライティング(キー+フィル+バック), ソフトなリムライト,
肌: 滑らか, 影弱め, 毛穴なし, ハイライト控えめ,
髪: 2段影のアニメ調, 低色数, つや控えめ,
女子高生, 6.5頭身, 大きい瞳, 滑らかな輪郭, 小鼻, 弱いリップ光,
正面, 全身, リラックスしたニュートラルポーズ, 手指の破綻なし(指5本・関節正常),
制服: ブレザー+リボン+プリーツスカート, ソックス, 上履き,
小物: シンプルな通学カバン(控えめ), 背景: 白または単色パステル,
カメラ: 35mm, 弱いDOF, 画面中央に全身が収まる, 破綻や法線崩れなし

[NEGATIVE PROMPT]
フォトリアル, ピクサー調, 海外CG風, PBR, 金属光沢, 強いスペキュラ, 肌の毛穴, シミ, 汚れ,
指の増減, 指欠け, 余分な手足, 関節破綻, 手の崩れ, 目の歪み, 顔の崩れ, 低解像, ぼけ, ノイズ, 粒状感,
過度なAO, 影が硬すぎ, 服のシワ過多(写実), テキスト, 透かし, ロゴ,
特定作品・特定キャラに似せる, 著作物の固有要素, 露出過多, 性的表現

[RECOMMENDED SETTINGS]
[CONFIG(数値を調整できるテンプレ:SD + LLM)]
※ {} の中だけ差し替えて使う(コピペ前提)

# ===========================
# 0) PRESET(1つ選ぶ)
# ===========================
PRESET = "{STABLE}"   # {STABLE} / {BALANCED} / {VARIATION} / {CHAOS}

# ===========================
# A) SD(画像生成)PARAM
# ===========================
WIDTH  = {768}        # 例: 768 / 896 / 1024
HEIGHT = {1152}       # 例: 1152 / 1344 / 1536

SAMPLER = "{DPM++ 2M Karras}"   # "DPM++ 2M Karras" / "Euler a" / "DPM++ SDE Karras" / "UniPC"
STEPS   = {26}                  # 目安: 20–36
CFG     = {8.0}                 # 目安: 6.5–11.0
SEED    = {123456}              # 固定: 任意整数 / ランダム: -1

# img2img(使う場合のみ)
USE_IMG2IMG = "{OFF}"           # {OFF} / {ON}
DENOISE     = {0.00}            # ON時のみ: 0.45–0.85

# Hires / Upscale(使う場合のみ)
USE_HIRES     = "{OFF}"         # {OFF} / {ON}
HIRES_SCALE   = {0.00}          # ON時のみ: 1.5–2.0
HIRES_STEPS   = {0}             # ON時のみ: 8–15
HIRES_DENOISE = {0.00}          # ON時のみ: 0.25–0.40

# ネガティブ重み(環境対応時のみ)
NEG_WEIGHT = {1.0}              # 目安: 0.8–1.2

# ===========================
# B) LLM(文章生成)PARAM(温度など)
# ===========================
TEMPERATURE        = {0.9}       # 目安: 0.7–1.6(崩れ狙いは 1.6+)
TOP_P              = {0.95}      # 目安: 0.85–0.98
TOP_K              = {50}        # 目安: 30–80(環境が対応する場合)
FREQUENCY_PENALTY  = {0.2}       # 目安: 0.0–0.6
PRESENCE_PENALTY   = {0.1}       # 目安: 0.0–0.6
MAX_TOKENS         = {1200}      # 目安: 600–2000
LLM_SEED           = {123456}    # 固定: 任意整数 / ランダム: -1

# ===========================
# 9) PRESETごとの推奨値(コピペして反映)
# ===========================
# {STABLE}(安定・破綻しにくい)
# SD:  WIDTH=768, HEIGHT=1152, SAMPLER="DPM++ 2M Karras", STEPS=28, CFG=8.5, SEED=123456
#      USE_IMG2IMG="OFF", DENOISE=0.00
#      USE_HIRES="OFF", HIRES_SCALE=0.00, HIRES_STEPS=0, HIRES_DENOISE=0.00
#      NEG_WEIGHT=1.1
# LLM: TEMPERATURE=0.7, TOP_P=0.92, TOP_K=40, FREQUENCY_PENALTY=0.3, PRESENCE_PENALTY=0.1, MAX_TOKENS=900,  LLM_SEED=123456

# {BALANCED}(標準)
# SD:  WIDTH=768, HEIGHT=1152, SAMPLER="DPM++ 2M Karras", STEPS=26, CFG=8.0, SEED=123456
#      USE_IMG2IMG="OFF", DENOISE=0.00
#      USE_HIRES="OFF", HIRES_SCALE=0.00, HIRES_STEPS=0, HIRES_DENOISE=0.00
#      NEG_WEIGHT=1.0
# LLM: TEMPERATURE=0.9, TOP_P=0.95, TOP_K=50, FREQUENCY_PENALTY=0.2, PRESENCE_PENALTY=0.1, MAX_TOKENS=1200, LLM_SEED=123456

# {VARIATION}(揺らぎ・少し尖る)
# SD:  WIDTH=768, HEIGHT=1152, SAMPLER="Euler a", STEPS=24, CFG=7.0, SEED=123456
#      USE_IMG2IMG="OFF", DENOISE=0.00
#      USE_HIRES="OFF", HIRES_SCALE=0.00, HIRES_STEPS=0, HIRES_DENOISE=0.00
#      NEG_WEIGHT=0.9
# LLM: TEMPERATURE=1.2, TOP_P=0.92, TOP_K=40, FREQUENCY_PENALTY=0.2, PRESENCE_PENALTY=0.3, MAX_TOKENS=1400, LLM_SEED=123456

# {CHAOS}(崩れ狙い・破綻リスク高)
# SD:  WIDTH=768, HEIGHT=1152, SAMPLER="DPM++ SDE Karras", STEPS=20, CFG=5.5, SEED=-1
#      USE_IMG2IMG="OFF", DENOISE=0.00
#      USE_HIRES="OFF", HIRES_SCALE=0.00, HIRES_STEPS=0, HIRES_DENOISE=0.00
#      NEG_WEIGHT=0.8
# LLM: TEMPERATURE=1.6, TOP_P=0.88, TOP_K=30, FREQUENCY_PENALTY=0.1, PRESENCE_PENALTY=0.5, MAX_TOKENS=1600, LLM_SEED=-1

Temperature / TEMPERATURE(LLM)
確率分布 P(token | context) を「平坦化/尖鋭化」する係数。
プレーンテキストで書くと:

  • 元の分布: p_i = softmax(z_i) = exp(z_i) / Σ_j exp(z_j)
  • 温度T適用: p_i(T) = softmax(z_i / T) = exp(z_i / T) / Σ_j exp(z_j / T)

意味:

  • T < 1 → 分布が尖る → 上位候補を選びやすい(安定)
  • T > 1 → 分布が平坦 → 低確率候補も選ばれやすい(多様・破綻増)

Top-p / TOP_P(LLM)
温度でできた分布 p_i(T) を「上位から足し上げて p になるまで」に制限してサンプルする手法。
プレーンテキスト:

  1. 確率を降順に並べる: p_(1) ≥ p_(2) ≥ ...
  2. 最小の集合 S を作る: Σ_{i in S} p_(i) ≥ p_target
  3. S の中で再正規化してサンプル: p'i = p_i / Σ{j in S} p_j (i in S)

意味:

  • p_target を下げる → 候補集合が小さくなる → “尖る/安定寄り”
  • temperature 高め + top_p 低め → 候補を絞った上で揺らすので「尖った狂気」になりやすい

Top-k / TOP_K(LLM)
確率上位k個のみに候補を固定する(核サンプリングの“個数版”)。
プレーンテキスト:

  1. 上位k個の集合 K を取る
  2. K 内で再正規化: p'i = p_i / Σ{j in K} p_j (i in K)
  3. p' でサンプル

意味:

  • k 小 → 候補が少ない → 安定
  • k 大 → 候補が多い → 多様

Frequency penalty / FREQUENCY_PENALTY(LLM)
「同じトークンを繰り返したら、そのトークンのスコアを下げる」補正。実装はモデル/提供APIで差があるが、典型はログイット z_i を減点する形。
プレーンテキスト例(代表形):

  • z_i' = z_i - α * count_i

ここで count_i はこれまでに出たトークンiの出現回数、α が frequency_penalty。
意味:

  • α ↑ → 反復が減る
  • ただし α を上げすぎると不自然な言い換えが増える

Presence penalty / PRESENCE_PENALTY(LLM)
「一度でも出たトークンを、再度出しにくくする」補正(回数ではなく出現有無)。
プレーンテキスト例:

  • z_i' = z_i - β * I(count_i > 0)

I( ) は条件が真なら1、偽なら0。β が presence_penalty。
意味:

  • β ↑ → “すでに出た語”が出にくくなり、新しい話題へ逸れやすい

Max tokens / MAX_TOKENS(LLM)
生成長の上限 N_max。単純に「N_maxトークン生成したら停止」。
プレーンテキスト:

  • 出力長 N_out ≤ N_max

Seed / LLM_SEED(LLM)
乱数生成器の初期値。サンプリングは乱数 u ~ Uniform(0,1) を使うので、seed固定で u の列が固定され、同条件で再現しやすい。
プレーンテキスト:

  • RNG(seed) → u_1, u_2, ...

WIDTH / HEIGHT(SD)
画像のピクセルサイズ。計算量は概ねピクセル数に比例する。
プレーンテキスト:

  • pixels = WIDTH * HEIGHT
  • 計算負荷 ~ O(pixels * STEPS)

Sampler / SAMPLER(SD)
拡散モデルの「逆拡散」数値解法(サンプラー)。
概念式(拡散過程)をプレーンテキストで:

  • 前向き拡散: x_t = sqrt(alpha_t) * x_0 + sqrt(1 - alpha_t) * ε, ε ~ N(0, I)

逆過程は「ノイズ ε を予測して x_{t-1} を更新」を繰り返す。
サンプラー差は、この更新式(数値積分法・ノイズ注入方式・スケジューラ)にある。

Steps / STEPS(SD)
逆拡散の反復回数 N_steps。
プレーンテキスト:

  • t = T, T-1, ..., 1 を N_steps 段に離散化して更新

意味:

  • N_steps ↑ → 収束が進み、細部が整いやすい(ただし過剰だと“整いすぎ”になりやすい)

CFG Scale / CFG(SD)
Classifier-Free Guidance。条件付き予測と無条件予測を混ぜて「プロンプト方向」に強く引く。
プレーンテキスト(代表式):

  • ε_guided = ε_uncond + s * (ε_cond - ε_uncond)

ここで s が CFG Scale。
意味:

  • s ↑ → cond(プロンプト)方向の差分を強調 → 指示に忠実
  • s ↓ → 自由度↑(ただし崩れやすい)

Seed / SEED(SD)
初期ノイズ x_T の生成を固定する。
プレーンテキスト:

  • x_T ~ N(0, I) を RNG(seed) で生成
    seed固定で構図・雰囲気が揃いやすい。

USE_IMG2IMG(SD)
初期状態を「純ノイズ」ではなく「元画像にノイズを載せたもの」にするモード。
概念式(Strengthの説明に直結):

  • x_t = sqrt(alpha_t) * x_0 + sqrt(1 - alpha_t) * ε

ここで x_0 が元画像(潜在)。どの t から逆拡散を始めるかで変化量が決まる。

Denoising Strength / DENOISE(SD・img2img)
「どの時刻 t_start から逆拡散を始めるか」を決める係数(実装は線形対応が多い)。
プレーンテキスト(典型):

  • t_start = round(DENOISE * T)

意味:

  • DENOISE 小 → t_start 小 → ノイズ少 → 元画像を保持
  • DENOISE 大 → t_start 大 → ノイズ多 → 別物に寄る

USE_HIRES / HIRES_*(SD)
2段階生成。低解像で一旦作ってから高解像で再生成(img2img的に再ノイズ→再復元)。
プレーンテキストの見方:

  • 第1段: (W,H) で生成
  • 第2段: (kW,kH) に拡大し、DENOISE_hires で再拡散→再復元

NEG_WEIGHT(SD)
ネガティブプロンプトの重み付け(対応UIのみ)。多くは「条件ベクトル」の合成係数として効く。
概念的には:

  • cond = w_pos * cond_pos - w_neg * cond_neg
    のような形で “避けたい方向” を押し返す。UIで実装差が大きく、非対応なら無視される。

PRESET(運用意図)
上の式のどの項を強くするかの組み合わせ最適化。

  • STABLE:CFGやStepsを上げ、seed固定で分布の揺らぎを減らす(安定方向)
  • VARIATION:CFGを下げたりEuler a等で更新を荒くし、揺らぎを増やす
  • CHAOS:CFG↓、Steps↓、seed=-1、SDE系でノイズ注入が強い更新を選ぶ(破綻方向)

注意
SD側の「temperature / top_p / top_k」は通常のStable Diffusion実装には基本的に存在しない(LLMのサンプリング用)。SDで“揺らぎ”を作る主レバーは CFG、Steps、Sampler、Seed、Denoise(img2img)になる。

# Program Name: llm_sampling_params_demo_colab.py
# Creation Date: 20260124
# Purpose: Interactive simulation of LLM sampling params (Temperature/Top-p/Top-k/Penalties) + toy CFG (Colab-ready)

# ============================================================
# 0) Install (Colab) / インストール(Colab)
# ============================================================
try:
    import sys, subprocess
    def _pip(cmd: str):
        subprocess.check_call([sys.executable, "-m", "pip", "-q", "install"] + cmd.split())

    # ipywidgets + numba (version-pinned) / 版固定
    _pip("ipywidgets==8.1.1 numba==0.59.1")
except Exception as e:
    raise RuntimeError(f"pip install failed: {e}")

# Colab widget manager (needed in some runtimes) / Colabで必要な場合あり
try:
    from google.colab import output as colab_output
    colab_output.enable_custom_widget_manager()
except Exception:
    pass

# ============================================================
# 1) Imports / インポート
# ============================================================
import numpy as np
import matplotlib.pyplot as plt
import datetime

from ipywidgets import (
    FloatSlider, IntSlider, Dropdown, Checkbox, Button, HBox, VBox, Output, Label, Layout
)
from numba import njit

# ============================================================
# 2) PARAM_INIT(設定一元管理) / Centralized parameters
# ============================================================
PARAM_INIT = dict(
    # Synthetic logits / 擬似ロジット
    vocab_size=200,            # pseudo vocab size / 擬似語彙数
    sharpness=1.2,             # ranking slope / ランク傾斜
    base_noise=0.15,           # logits noise / ロジット雑音

    # LLM sampling / LLMサンプリング
    temperature=0.9,           # T
    top_p=0.95,                # nucleus p
    top_k=50,                  # top-k
    use_top_p=True,
    use_top_k=False,
    frequency_penalty=0.2,     # alpha
    presence_penalty=0.1,      # beta

    # Sampling simulation / サンプリングシミュレーション
    sample_n=5000,             # Monte Carlo samples
    seed=123456,               # -1 => random

    # Display / 表示
    plot_mode="Probabilities",

    # SD-like toy (CFG) / SD風(玩具)
    cfg_scale=8.0,
)

# ============================================================
# 3) Math core (vectorized + Numba) / 数学コア(ベクトル化+Numba)
# ============================================================
def _stable_softmax(x: np.ndarray) -> np.ndarray:
    # Numerical stability / 数値安定化
    x = x - np.max(x)
    ex = np.exp(x)
    s = np.sum(ex)
    return ex / s

def softmax_with_temperature(logits: np.ndarray, T: float) -> np.ndarray:
    """
    Inputs:
      logits: shape (V,)
      T: temperature (>0)
    Outputs:
      p: probabilities shape (V,)
    Process:
      p_i(T) = softmax(z_i / T)
    """
    T = max(1e-6, float(T))
    return _stable_softmax(logits / T)

def apply_penalties(logits: np.ndarray, counts: np.ndarray, freq_pen: float, pres_pen: float) -> np.ndarray:
    """
    Inputs:
      logits: z_i
      counts: count_i
    Outputs:
      adjusted logits: z'_i
    Process (typical):
      z'_i = z_i - alpha*count_i - beta*I(count_i>0)
    """
    z = logits.copy()
    alpha = float(freq_pen)
    beta = float(pres_pen)

    if alpha != 0.0:
        z = z - alpha * counts
    if beta != 0.0:
        z = z - beta * (counts > 0).astype(np.float64)
    return z

def nucleus_mask(p: np.ndarray, top_p: float) -> np.ndarray:
    # Smallest set S with cumulative prob >= top_p / 累積top_pまでの最小集合
    tp = float(np.clip(top_p, 0.0, 1.0))
    idx = np.argsort(p)[::-1]
    cum = np.cumsum(p[idx])
    cut = int(np.searchsorted(cum, tp, side="left"))
    keep = idx[:max(1, cut + 1)]
    m = np.zeros_like(p, dtype=bool)
    m[keep] = True
    return m

def topk_mask(p: np.ndarray, k: int) -> np.ndarray:
    kk = int(max(1, k))
    idx = np.argsort(p)[::-1][:kk]
    m = np.zeros_like(p, dtype=bool)
    m[idx] = True
    return m

def renorm(p: np.ndarray, mask: np.ndarray) -> np.ndarray:
    q = p.copy()
    q[~mask] = 0.0
    s = float(q.sum())
    if s <= 0.0:
        q[:] = 0.0
        q[int(np.argmax(p))] = 1.0
        return q
    return q / s

@njit(cache=False)
def sample_categorical_numba(p: np.ndarray, n: int, seed: int) -> np.ndarray:
    """
    Inputs:
      p: probability vector (V,)
      n: number of samples
      seed: RNG seed
    Outputs:
      sampled indices (n,)
    Process:
      Inverse-CDF sampling with LCG RNG (reproducible).
    """
    V = p.shape[0]
    cdf = np.empty(V, dtype=np.float64)
    s = 0.0
    for i in range(V):
        s += p[i]
        cdf[i] = s

    out = np.empty(n, dtype=np.int32)

    # LCG
    a = 1664525
    c = 1013904223
    m = 2**32
    state = np.uint32(seed)

    for t in range(n):
        state = np.uint32((a * state + c) % m)
        u = (state.astype(np.float64) + 1.0) / (m + 1.0)  # (0,1)
        lo = 0
        hi = V - 1
        while lo < hi:
            mid = (lo + hi) // 2
            if cdf[mid] >= u:
                hi = mid
            else:
                lo = mid + 1
        out[t] = lo
    return out

def entropy_nats(p: np.ndarray) -> float:
    q = p[p > 0]
    return float(-(q * np.log(q)).sum())

# ============================================================
# 4) Synthetic logits / 擬似ロジット生成
# ============================================================
def make_synthetic_logits(V: int, sharpness: float, noise: float, seed: int) -> np.ndarray:
    rng = np.random.default_rng(int(seed))
    r = np.arange(V, dtype=np.float64)
    base = -np.log1p(r) * float(sharpness)   # decreasing curve
    base = base - base.mean()
    eps = rng.normal(0.0, float(noise), size=V)
    return base + eps

# ============================================================
# 5) Toy CFG (SD-like) / CFG玩具モデル
# ============================================================
def cfg_guidance(eps_uncond: np.ndarray, eps_cond: np.ndarray, s: float) -> np.ndarray:
    # eps_guided = eps_uncond + s*(eps_cond - eps_uncond)
    return eps_uncond + float(s) * (eps_cond - eps_uncond)

# ============================================================
# 6) UI (入力→計算→出力) / UI
# ============================================================
out = Output()
slider_layout = Layout(width="360px")

# --- Synthetic logits controls / 擬似ロジット ---
w_vocab = IntSlider(value=PARAM_INIT["vocab_size"], min=50, max=2000, step=10, description="Vocab", layout=slider_layout)
w_sharp = FloatSlider(value=PARAM_INIT["sharpness"], min=0.2, max=3.0, step=0.05, description="Sharp", layout=slider_layout)
w_noise = FloatSlider(value=PARAM_INIT["base_noise"], min=0.0, max=1.0, step=0.02, description="Noise", layout=slider_layout)

# --- LLM sampling controls / LLMサンプリング ---
w_T = FloatSlider(value=PARAM_INIT["temperature"], min=0.2, max=2.5, step=0.05, description="Temp", layout=slider_layout)
w_top_p = FloatSlider(value=PARAM_INIT["top_p"], min=0.50, max=1.00, step=0.01, description="Top-p", layout=slider_layout)
w_top_k = IntSlider(value=PARAM_INIT["top_k"], min=1, max=500, step=1, description="Top-k", layout=slider_layout)
w_use_p = Checkbox(value=PARAM_INIT["use_top_p"], description="Use top-p")
w_use_k = Checkbox(value=PARAM_INIT["use_top_k"], description="Use top-k")
w_freq = FloatSlider(value=PARAM_INIT["frequency_penalty"], min=0.0, max=1.0, step=0.05, description="FreqPen", layout=slider_layout)
w_pres = FloatSlider(value=PARAM_INIT["presence_penalty"], min=0.0, max=1.0, step=0.05, description="PresPen", layout=slider_layout)

# --- Simulation controls / シミュレーション ---
w_n = IntSlider(value=PARAM_INIT["sample_n"], min=500, max=50000, step=500, description="Samples", layout=slider_layout)
w_seed = IntSlider(value=PARAM_INIT["seed"], min=-1, max=2_000_000_000, step=1, description="Seed", layout=slider_layout)

# --- SD-like toy / SD風玩具 ---
w_cfg = FloatSlider(value=PARAM_INIT["cfg_scale"], min=0.0, max=15.0, step=0.25, description="CFG", layout=slider_layout)

# --- Plot mode / 表示 ---
w_mode = Dropdown(
    options=[
        "Probabilities",
        "Samples Histogram",
        "Candidate Set Size",
        "Toy CFG Effect",
        "Penalty Demo (multi-step)"
    ],
    value=PARAM_INIT["plot_mode"],
    description="Plot",
    layout=Layout(width="460px")
)

# --- Buttons / ボタン ---
btn_run = Button(description="Run / Update", button_style="primary")
btn_reset = Button(description="Reset")
btn_save_png = Button(description="Save PNG")
btn_save_pdf = Button(description="Save PDF")

def _current_seed() -> int:
    # Seed=-1 => random / -1ならランダム
    if int(w_seed.value) == -1:
        return int(datetime.datetime.now().timestamp() * 1e6) % 2_000_000_000
    return int(w_seed.value)

def _save_figure(path: str, as_pdf: bool):
    if as_pdf:
        plt.savefig(path, format="pdf")
    else:
        plt.savefig(path, format="png", dpi=220)

def compute_and_plot(savepath: str = None, save_pdf: bool = False):
    with out:
        out.clear_output(wait=True)
        try:
            seed = _current_seed()
            V = int(w_vocab.value)

            # 1) Synthetic logits / 擬似ロジット
            z0 = make_synthetic_logits(V, w_sharp.value, w_noise.value, seed)

            mode = w_mode.value

            # ====================================================
            # Mode A: single-step distribution + sampling / 単発
            # ====================================================
            if mode in ["Probabilities", "Samples Histogram", "Candidate Set Size"]:
                counts = np.zeros(V, dtype=np.float64)  # no history / 履歴なし

                # penalties -> temperature / ペナルティ→温度
                z = apply_penalties(z0, counts, w_freq.value, w_pres.value)
                pT = softmax_with_temperature(z, w_T.value)

                # top-p/top-k / top-p/top-k
                mask = np.ones(V, dtype=bool)
                if bool(w_use_p.value):
                    mask &= nucleus_mask(pT, w_top_p.value)
                if bool(w_use_k.value):
                    mask &= topk_mask(pT, int(w_top_k.value))
                p = renorm(pT, mask)

                # sampling / サンプリング
                n = int(w_n.value)
                samp = sample_categorical_numba(p.astype(np.float64), n, seed)

                # metrics / 指標
                H = entropy_nats(p)
                eff = int(mask.sum())
                top1 = int(np.argmax(p))
                top1p = float(p[top1])

                # plot / 描画
                plt.figure(figsize=(10, 5))

                if mode == "Probabilities":
                    idx = np.argsort(p)[::-1][:40]
                    plt.bar(np.arange(idx.size), p[idx])
                    plt.xticks(np.arange(idx.size), idx, rotation=90)
                    plt.title("Top Probabilities (after Temperature + Constraints)")
                    plt.xlabel("Token index (sorted)")
                    plt.ylabel("Probability")

                elif mode == "Samples Histogram":
                    c = np.bincount(samp, minlength=V)
                    idx = np.argsort(c)[::-1][:60]
                    plt.bar(np.arange(idx.size), c[idx] / c.sum())
                    plt.xticks(np.arange(idx.size), idx, rotation=90)
                    plt.title("Sample Frequency (Monte Carlo)")
                    plt.xlabel("Token index (sorted)")
                    plt.ylabel("Frequency")

                elif mode == "Candidate Set Size":
                    nuc = nucleus_mask(pT, w_top_p.value)
                    km = topk_mask(pT, int(w_top_k.value))
                    comb = mask
                    sizes = [int(nuc.sum()), int(km.sum()), int(comb.sum())]
                    labels = ["Top-p set", "Top-k set", "Combined"]
                    plt.bar(labels, sizes)
                    plt.title("Candidate Set Size")
                    plt.xlabel("Constraint type")
                    plt.ylabel("Count")

                plt.tight_layout()
                if savepath:
                    _save_figure(savepath, save_pdf)
                plt.show()

                print("------------------------------------------------------------")
                print("Key Stats")
                print("------------------------------------------------------------")
                print(f"Seed: {seed}")
                print(f"Entropy H(p): {H:.3f} nats  (lower => more peaked)")
                print(f"Candidate set size: {eff} / {V}")
                print(f"Top-1 token: {top1}, p={top1p:.4f}")
                print("LLM Params:")
                print(f"  Temperature={w_T.value:.2f}")
                print(f"  Top-p={w_top_p.value:.2f} (use={w_use_p.value})")
                print(f"  Top-k={int(w_top_k.value)} (use={w_use_k.value})")
                print(f"  Frequency penalty={w_freq.value:.2f}, Presence penalty={w_pres.value:.2f}")
                print(f"  Samples={n}")
                print("------------------------------------------------------------")
                if savepath:
                    print(f"Saved: {savepath}")

            # ====================================================
            # Mode B: Toy CFG / CFG玩具
            # ====================================================
            elif mode == "Toy CFG Effect":
                rng = np.random.default_rng(seed)
                eps_un = rng.normal(0, 1, size=256)
                eps_co = eps_un + rng.normal(0, 0.3, size=256)  # correlated
                eps_g = cfg_guidance(eps_un, eps_co, w_cfg.value)

                n_un = float(np.linalg.norm(eps_un))
                n_co = float(np.linalg.norm(eps_co))
                n_g  = float(np.linalg.norm(eps_g))

                plt.figure(figsize=(10, 5))
                plt.plot(eps_un[:80], label=f"eps_uncond (norm={n_un:.1f})")
                plt.plot(eps_co[:80], label=f"eps_cond   (norm={n_co:.1f})")
                plt.plot(eps_g[:80],  label=f"eps_guided (norm={n_g:.1f})")
                plt.title("Toy CFG: eps_guided = eps_uncond + s*(eps_cond - eps_uncond)")
                plt.xlabel("Index")
                plt.ylabel("Value")
                plt.legend()
                plt.tight_layout()
                if savepath:
                    _save_figure(savepath, save_pdf)
                plt.show()

                print("------------------------------------------------------------")
                print("Toy CFG Stats")
                print("------------------------------------------------------------")
                print(f"Seed: {seed}")
                print(f"CFG scale s: {w_cfg.value:.2f}")
                print(f"norm(eps_uncond) = {n_un:.3f}")
                print(f"norm(eps_cond)   = {n_co:.3f}")
                print(f"norm(eps_guided) = {n_g:.3f}")
                print("------------------------------------------------------------")
                if savepath:
                    print(f"Saved: {savepath}")

            # ====================================================
            # Mode C: Penalty demo (multi-step generation) / 反復生成でペナルティを見る
            # ====================================================
            elif mode == "Penalty Demo (multi-step)":
                # This simulates "token-by-token" generation with penalties.
                # ここで count_i が増える → logitsが抑制される様子を見る。
                steps_gen = 80  # generation steps / 生成ステップ(固定)
                counts = np.zeros(V, dtype=np.float64)

                chosen = np.zeros(steps_gen, dtype=np.int32)
                ent = np.zeros(steps_gen, dtype=np.float64)
                top1p = np.zeros(steps_gen, dtype=np.float64)

                seed_local = seed
                for t in range(steps_gen):
                    z = apply_penalties(z0, counts, w_freq.value, w_pres.value)
                    pT = softmax_with_temperature(z, w_T.value)

                    mask = np.ones(V, dtype=bool)
                    if bool(w_use_p.value):
                        mask &= nucleus_mask(pT, w_top_p.value)
                    if bool(w_use_k.value):
                        mask &= topk_mask(pT, int(w_top_k.value))
                    p = renorm(pT, mask)

                    # sample one token / 1トークン生成
                    idx = sample_categorical_numba(p.astype(np.float64), 1, seed_local)[0]
                    seed_local = (seed_local + 1) % 2_000_000_000  # deterministic drift / 再現性のある変化
                    chosen[t] = idx
                    counts[idx] += 1.0

                    ent[t] = entropy_nats(p)
                    top1p[t] = float(np.max(p))

                # Plot: entropy + top1 probability / エントロピーとtop1確率
                plt.figure(figsize=(10, 5))
                plt.plot(ent, label="Entropy H(p)")
                plt.plot(top1p, label="Top-1 probability")
                plt.title("Penalty Demo (multi-step): how distribution changes over time")
                plt.xlabel("Generation step")
                plt.ylabel("Value")
                plt.legend()
                plt.tight_layout()
                if savepath:
                    _save_figure(savepath, save_pdf)
                plt.show()

                # Show most repeated tokens / よく出たトークン
                c = np.bincount(chosen, minlength=V)
                idx = np.argsort(c)[::-1][:15]
                print("------------------------------------------------------------")
                print("Penalty Demo Summary")
                print("------------------------------------------------------------")
                print(f"Seed: {seed}")
                print(f"Gen steps: {steps_gen}")
                print(f"Temperature={w_T.value:.2f}, Top-p(use={w_use_p.value})={w_top_p.value:.2f}, Top-k(use={w_use_k.value})={int(w_top_k.value)}")
                print(f"Frequency penalty={w_freq.value:.2f}, Presence penalty={w_pres.value:.2f}")
                print("Most frequent tokens (token_id: count):")
                for k in idx:
                    if c[k] == 0:
                        break
                    print(f"  {int(k)}: {int(c[k])}")
                print("------------------------------------------------------------")
                if savepath:
                    print(f"Saved: {savepath}")

            else:
                raise ValueError(f"Unknown mode: {mode}")

        except Exception as e:
            print(f"[ERROR] {e}")

def on_run(_):
    compute_and_plot()

def on_reset(_):
    # Reset all widgets / 初期化
    w_vocab.value = PARAM_INIT["vocab_size"]
    w_sharp.value = PARAM_INIT["sharpness"]
    w_noise.value = PARAM_INIT["base_noise"]

    w_T.value = PARAM_INIT["temperature"]
    w_top_p.value = PARAM_INIT["top_p"]
    w_top_k.value = PARAM_INIT["top_k"]
    w_use_p.value = PARAM_INIT["use_top_p"]
    w_use_k.value = PARAM_INIT["use_top_k"]
    w_freq.value = PARAM_INIT["frequency_penalty"]
    w_pres.value = PARAM_INIT["presence_penalty"]

    w_n.value = PARAM_INIT["sample_n"]
    w_seed.value = PARAM_INIT["seed"]

    w_cfg.value = PARAM_INIT["cfg_scale"]
    w_mode.value = PARAM_INIT["plot_mode"]

    compute_and_plot()

def on_save_png(_):
    ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    fname = f"sim_sampling_{w_mode.value.replace(' ','_')}_T{w_T.value:.2f}_p{w_top_p.value:.2f}_k{int(w_top_k.value)}_{ts}.png"
    compute_and_plot(savepath=fname, save_pdf=False)

def on_save_pdf(_):
    ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    fname = f"sim_sampling_{w_mode.value.replace(' ','_')}_T{w_T.value:.2f}_p{w_top_p.value:.2f}_k{int(w_top_k.value)}_{ts}.pdf"
    compute_and_plot(savepath=fname, save_pdf=True)

btn_run.on_click(on_run)
btn_reset.on_click(on_reset)
btn_save_png.on_click(on_save_png)
btn_save_pdf.on_click(on_save_pdf)

ui_left = VBox([
    Label("LLM Sampling Controls"),
    w_T, w_top_p, HBox([w_use_p, w_use_k]),
    w_top_k, w_freq, w_pres,
    Label("Synthetic logits"),
    w_vocab, w_sharp, w_noise,
], layout=Layout(width="520px"))

ui_right = VBox([
    Label("Run & Output"),
    w_mode,
    w_n, w_seed,
    Label("SD-like (toy)"),
    w_cfg,
    HBox([btn_run, btn_reset]),
    HBox([btn_save_png, btn_save_pdf]),
], layout=Layout(width="520px"))

display(HBox([ui_left, ui_right]))
display(out)

# Initial run / 初回実行
compute_and_plot()
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?