技術約22分で読めます

Qwen3.8 Maxで教師データを増やし、Animaの成功conditioningを短文へ蒸留できるか試した

いけさん目次

前回のAnimaのQwen3-0.6BとLLMAdapterを調べた実験では、短いP0と成功するP3をQwen側・T5側で交差させた。 seed 42では、QwenだけをP3へ変えてT5をP0のままにしてもkanachanの見た目の特徴は戻らず、T5をP3へ変えるとkanachanとkeichanが正しく描き分けられた。

固定辞書で長いプロンプトを組み立てて、入力自体は短く済ませる方法もある。 今回は長文をAnimaへ入力せず、長文成功時のconditioningそのものを短いトリガーから再現できるかを試した。Qwen3-0.6Bを凍結したままDiT直前のconditioningを学習で作り出す小さなresamplerを組み、その学習に使う並び順とシーンの組み合わせをQwen3.8 Maxで増やした。

検証環境

項目内容
マシンMacBook Pro M1 Max、統合メモリ64GB、MPS。抽出・学習・生成ともこの1台で、RunPodは使っていない
生成ローカルComfyUI、1枚あたり約33秒(8 step)
ベースモデルanima-base-v1.0.safetensors
LoRAanima-turbo-lora-v0.1(speed、strength 1.0)、anima-4char-v1_epoch100(char、strength 1.0 / clip 0.8)
text encoderqwen_3_06b_base.safetensors(CLIPTextEncode、type=qwen_image)
教師ペア生成Qwen3.8 Max(アンバサダー枠、ModelScope経由のOpenAI互換API)
resampler学習1500 stepで1分未満、tensor抽出は数十秒

Qwen3.8 Maxは内部tensorを返さないため、並び順とシーンの生成に使う

Qwen3.8 Max previewの早期アクセスでは、専用ワークスペースのOpenAI互換APIから文章とreasoning情報を取得できた。 一方、Alibaba Cloud Model StudioのChat Completions API仕様に記載された応答は、生成文、reasoning、logprobs、usageなどで構成され、各入力tokenの中間hidden stateを返す項目はない。仕様に載っている範囲では、Qwen3.8 Maxの内部tensorをAnimaへ直接入力する実験には使えない。

Model Studioにはembedding専用APIもある。仕様では入力ごとのembeddingベクトルを検索や類似度計算に使い、1024次元も選べる。ただtoken単位のhidden stateを返す仕様やAnimaのLLMAdapterとの互換性は書かれていないため、蒸留には使えない。Qwen3.8 Maxは学習データの生成にのみ使った。

MaxにはP3本文を書かせず、4人の並び順とシーンをJSONで12件返させた。 人物の過不足とJSONの欠落はローカルで検査し、固定rendererが同じ英語テンプレートからP3を組み立てた。Maxが決めるのは並び順とシーンだけで、Animaへ入る文面はrendererの固定テンプレートから出る。

処理ごとに担当を分ける

処理実行場所理由
4人の識別辞書から並び順+シーンのJSONを作るQwen3.8 Max API並び順とシーンの組み合わせを増やせる
JSONからP3を組み立てるローカルの固定renderer属性順と文型を固定し、教師プロンプトを再現可能にする
P3から成功conditioningを抽出する現在のAnima / Qwen3-0.6B / LLMAdapterDiTがすでに生成成功に使っているtensorを教師にする
P0からhidden stateを抽出する現在のAnima / Qwen3-0.6B(凍結)同じ0.6Bを短い入力側にも使い、容量差ではなく変換の有無だけを比較する
resamplerを学習するローカルM1 Max(MPS)50M param程度の小さいネットワークなので、Qwen3-0.6B側さえ凍結すれば1台で足りる
同一seedで画像を比較するローカルの生成環境最終的にDiTが4人を描き分けられるかを確認する

token数43と179の差をresamplerで埋める

前回の4人条件では、LLMAdapterのpadding前出力の長さがP0とP3で大きく違った。

入力T5実token数LLMAdapter出力
P0、トリガー中心43(1, 43, 1024)
P3、識別文・位置文・服装文あり179(1, 179, 1024)

512tokenへpaddingした後ならshapeだけは同じになる。 しかしP0は43番目より後ろがほぼzeroで、P3は179番目まで意味のあるconditioningが入る。両者へそのままMSEを掛けると、同じ人物や属性を表す位置が対応せず、zero paddingの一致が損失の大部分を占める。

P0  : 43 token  ──────────────── zero padding ────────────────
P3  : 179 token ───────────────────────────── zero padding ──

hidden sizeを1024へ射影するだけでは、このtoken数の差は埋まらない。 今回は射影に加えて、短いP0側のhidden stateから教師と同じ179tokenを作るresamplerを学習する。

flowchart TD
    S[P0と人物辞書] --> M[Qwen3.8 Max API]
    M --> J[並び順+シーンのJSON]
    J --> R[固定renderer]
    R --> L[P3教師プロンプト]
    L --> A[現行Anima encoder]
    A --> G[C_good<br/>179〜189×1024]
    S --> O[Qwen3-0.6B凍結<br/>P0 hidden state]
    O --> P[resampler<br/>学習済みquery200個]
    P --> C[C_pred<br/>200×1024]
    G --> D[distillation loss]
    C --> D

初回は4人P3の定型プロンプトだけに限定し、学習済みqueryの数(NQ)を200に固定した。 ペアやトリオまで広げる場合は、教師maskも予測するか、組み合わせごとに有効長を与える設計を後から追加することにして実験開始。

Step 1、教師プロンプトを固定する

入力データはprompt_shortからprompt_teacherまでの4項目で保存する。

フィールド内容
prompt_short実際に入力する短いプロンプト
character_dictトリガーごとの髪型、髪色、瞳、身長、服装
max_jsonQwen3.8 Maxが人物別に整理したJSON
prompt_teacher固定rendererが作ったP3形式の英語プロンプト

Maxに独自トリガーだけを与えて設定を推測させることはせず、辞書にない属性の追加・人物の欠落・順番の変更があったものは教師候補から除外する。API出力はtemperature、seed相当の指定、model ID、reasoning設定も一緒に保存する。

手書きした4ペア(order1〜4)

はじめはQwen3.8 Max APIを一切呼ばず、4人の識別文をanima-4char-qwen-conditioningで確立した固定テンプレート(トリガー+髪型/瞳/装飾の識別文+位置文+身長比較+服装文)のまま、4人の並び順だけを変えて手書きした。

pair IDshort(P0)renderer後のtoken数(T5実token)教師画像の判定
order1kurara, keichan, kanachan, koharu179成功。4人とも見た目の特徴・並び順・身長差が指示通り(既存記事で確認済み)
order2koharu, kanachan, keichan, kurara184成功。4人とも見た目の特徴・並び順・身長差が指示通り(下記Step4画像)
order3keichan, kurara, koharu, kanachan189見た目の特徴・並び順は指示通りで学習に使用。ただし身長差はkanachanがkoharuより低く、指示と合っていない(下記Step4画像の注記)
order4kanachan, koharu, kurara, keichan183成功。4人とも見た目の特徴・並び順・身長差が指示通り(下記Step4画像)

Max API実施記録(Run2)

次にQwen3.8 Maxを実際に呼んだ。usageはprompt_tokens=384completion_tokens=2085total_tokens=2469

Maxへは4人の識別辞書(名前と髪型/瞳/装飾の短い説明)だけを与え、「4人の並び順(permutation)」と「短いシーンフレーズ」のペアをJSONで12件生成させた。文章そのもの(識別文・位置文・身長比較・服装文)はMaxに書かせず、renderer.py(固定renderer、後述)がローカルで組み立てる分担にした。

自己検査(ローカル側、Max呼び出しとは別処理)の対象は「4人の名前が過不足・重複なく含まれているか」「scene_short/scene_fullが空でないか」だけにした。12件中12件がこの検査を通過した(reject 0件)。

自己検査では4人の名前の過不足しか確認できないため、Step2で12件すべての教師P3画像を生成して目視確認した。1件(maxgen06)だけ、指定した並び順と実際に生成された4人の位置が一致しない失敗だった(教師プロンプトの文面は正しいが、生成結果側で見た目の特徴の位置が入れ替わった)。この1件は学習データから除外し、11/12を採用した。

pair ID割り当て教師P3画像の判定
maxgen00〜09の9件(maxgen06を除く)学習成功。4人の見た目の特徴・並び順が指示通り
maxgen06除外失敗。見た目の特徴は揃うが並び順が指示と一致しない(下記画像)
maxgen10・maxgen11検証用成功。4人とも指示通り(下記Step4画像)

固定renderer(renderer.py)は、4人の並び順を受け取って位置文(is on the far left / is second from the left / … / is on the far right)と身長比較文を組み立てる。身長比較文は既存の確立済み識別文の身長ロジックそのままで、同順位タイのkurara・keichanは2人目に出てきたときにthe same height as X、kanachanは同順位者が先に出ていればhalf a head shorter、まだ出ていなければhalf a head shorter than the tallest、koharuは常にthe shortestとする。

このrendererはorder1(既存の4char_P3)とorder2(既存の4char_P3_order_reversed)の実文と全く同じ文を出せることをself-testで確認済み。

学習データから除外したmaxgen06。指定した並び順はkanachan, kurara, koharu, keichanだったが、実際の生成結果は左からkoharu(黒髪赤目)・keichan(金髪リボン)・kanachan(サイドポニー+アホ毛)・kurara(rose-brown髪)の順になっており、4人の見た目の特徴自体は揃っているが指定した並び順とは一致しない

Step 2、成功した教師tensorだけを保存する

prompt_teacherを現行Qwen3-0.6BとLLMAdapterへ入れ、encode経路の段階ごとに、Qwen3-0.6Bの最終hidden state、T5トークナイザーのIDsとweights、LLMAdapterのpadding前出力、512tokenへpaddingした後のconditioningと有効token mask、同じseedで生成した画像を保存する。

画像で4人の本人一致、属性、位置を確認し、失敗したプロンプトのconditioningをC_goodへ入れない。 教師tensorのdtype、各tokenのnorm、全体のmean・stdもmanifestへ残す。

実施記録

step_resampler_extract.py(step2_extract.pyを4ペア分バッチ化)で、order1〜4それぞれについてP0側とP3側のtensorを保存した。CPU固定で、DiT本体はロードしない。

  • 入力側: P0(トリガーのみ)をQwen3-0.6Bへ通したqwen_final。4orderとも同じ4トリガーの並べ替えなので、shapeは全order共通で(1, 38, 1024) float32
  • 教師側: P3(識別文込み)をqwen_final→LLMAdapterへ通したadapter_out_weighted(padding前、real length)。shapeはorderごとに(1, 179, 1024)(1, 189, 1024)(識別文の語数差でtoken数が変わる)

教師tensorの値域を先に確認すると、per-token L2 normの平均が4orderとも4.06〜4.17で揃っている一方、入力側のQwen最終hidden stateは110前後と教師の約27倍のスケールだった。この27倍の差が、Step3で最初のresampler実装の収束を止めた。

実施記録追記(Run2)

Step1のMax API生成12件のうち、教師P3画像で並び順の破綻が確認できたmaxgen06を除く11件分もP0側・P3側のtensorを同じ手順で抽出した(step_resampler_extract.pyのPAIRS辞書を拡張)。Max生成分はP0にscene_shortが加わる分だけP0側のtoken数がorder1〜4(常に38)より長く、43〜45で揃わない。Resampler側はcross-attentionのkey_padding_maskでpadding位置を無視する形に対応した。

これで学習データは合計16ペア(手書き4 + Max生成12)、うちmaxgen06を除いた15ペアが教師画像で確認済みになった。

Step 3、まず0.6Bのままresamplerだけを試す

P0を入れた現行Qwen3-0.6Bのhidden stateから、179tokenのC_goodを予測できるか試す。 この成否で、足りないのがQwen容量なのか、短文から長いconditioningへ展開する学習済み変換なのかを切り分ける。

Qwen3-0.6BとDiTは凍結する。Qwen hidden stateは先に保存し、学習中は射影層とresamplerだけをGPUへ載せる。

初期の損失はmasked MSE、cosine、distributionの3項で記録する。

L = λ_mse * masked_MSE(C_pred, C_good)
  + λ_cos * (1 - cosine(C_pred, C_good))
  + λ_stat * distribution_loss(C_pred, C_good)

distribution_lossではtoken norm、mean、stdを合わせる。 学習側の損失だけで判定せず、学習に混ぜず取り分けておいた検証用ペアのプロンプトから生成した画像で、4人の描き分けを確認する。

実施記録(Run 1)

学習をorder1・order3・order4、検証用をorder2(reversed)にした。P0(resamplerへの入力)は4orderとも4トリガーの並べ替えでtoken数が変わらないため、並び順という情報だけをQwen hidden stateから読み取って教師の並びを再現できるかを確かめる設定にした。

resamplerは学習済みquery 200個(NQ=200、教師最大長189に余裕を持たせた)がP0側のQwen最終hidden stateへcross-attentionする構成(resampler.py)。

項目
アーキテクチャ学習済みquery(200×1024) + [self-attn→cross-attn→FFN]×3層、8 head
dim1024(Qwen最終hidden stateとLLMAdapter出力が両方1024なので射影無し)
trainable parameters50,598,912
optimizerAdamW、lr=2e-4、weight_decay=1e-4
steps1500(学習3件をfull-batch)
損失の重みλ_mse=1.0、λ_cos=1.0、λ_stat=0.1
実行環境ローカルM1 Max、MPS

最初の実装は1500stepでも収束しなかった。context(Qwen最終hidden state)をそのままcross-attentionのkey/valueへ入れていたため、resampler内部の値が入力側のスケール(per-token norm ~110)に引きずられ、教師(per-token norm ~4)との差を埋めるのに出力層のLayerNormだけでは足りず、train cos distanceが0.94前後で頭打ちになった(distribution_lossの内訳を確認すると、初期値の大部分がこの27倍のnormスケール差そのものだった)。contextに事前LayerNormを1枚挟み、出力LayerNormのweight初期値を1.0から0.2へ下げたところ、同じ1500stepで大きく改善した。

train_msetrain_cos_distval_mseval_cos_dist
修正前、step15000.2130.943(≈ほぼ無相関)0.2170.976
修正後、step1(初期値)0.0590.9990.0490.794
修正後、step15000.000460.0047(≈コサイン類似度0.995)0.02340.595(≈コサイン類似度0.405)

学習した3件(order1・order3・order4)はコサイン距離0.005までほぼ完全に再現した。一方検証用のorder2はコサイン距離0.59付近で頭打ちになり、MSEは学習側の約125倍だった。学習3件ではこんなものだと思う。

画像はもう少し込み入った結果になった(画像はStep4参照)。resamplerの検証用(order2)出力では、4人それぞれの見た目の特徴(kanachanのアホ毛+サイドポニー、koharuの黒髪・赤目、kuraraのrose-brown髪+ピアス、keichanの金髪+青リボン)が4人とも正しく描き分けられて出た。ここはP0そのまま(見た目の特徴が別の人物に混ざったり抜けたりする)より、はっきり良くなった。しかし並び順はorder2の指示(koharu, kanachan, keichan, kurara)でなく、学習に使ったorder4(kanachan, koharu, kurara, keichan)とほぼ一致していた。P0の入力token列は4orderとも4トリガーの並べ替えに過ぎず、resamplerは「どのorderで学習したパターンに一番近いか」で出力を選んでいるように見える。見た目の特徴の描き分けだけは初見の並びにも引き継がれたが、並び順がQwen hidden stateから読み取れているかどうかは、この3ペアの学習からでは判断できない。

実施記録(Run2、学習12件・検証用3件)

学習3件では並び順を読み取れているか判断できない、というRun1の結果を受けて、Step1のMax API生成分を使い、学習をorder1・order3・order4 + maxgen00〜09のうちmaxgen06を除いた9件の計12ペア、検証用をorder2 + maxgen10 + maxgen11の計3ペアに増やして、同じ設定(NQ=200、3層resampler、AdamW lr=2e-4、1500 step)で再学習した。

train_msetrain_cos_distval_mse(集約)val_cos_dist(集約)
Run1(学習3/検証用1)、step15000.000460.00470.02340.595
Run2(学習12/検証用3)、step15000.000450.00490.02030.605

学習側の再現度(コサイン距離0.005前後)はRun1とほぼ変わらず、検証用3件をまとめた値も0.595→0.605とほぼ横ばいだった。学習ペア数を4倍にしても、この数値の上では検証用は改善しなかった。

検証用3件の数値は個別でも大差なかった。

検証用msecos_dist
order20.02130.649
maxgen100.01970.571
maxgen110.01990.596

3件をまとめた数値は横ばいだったが、生成画像の崩れ方は3件で別々だった(画像はStep4参照)。学習に使った1パターンの丸暗記という単純な結果ではなくなった。

検証用指示順崩れ方
order2koharu, kanachan, keichan, kurara1番目(koharu)と4番目(kurara)は指示通り。2番目と3番目でkanachanとkeichanが入れ替わる
maxgen10kanachan, keichan, koharu, kurara3番目(koharu)と4番目(kurara)は指示通り。1番目と2番目でkanachanとkeichanが入れ替わる
maxgen11koharu, kanachan, kurara, keichan1番目(koharu)だけ指示通り。2番目と3番目が両方keichan(金髪+リボン)の見た目になり、kuraraが出てこず、kanachanが4番目に押し出される

3件に共通するのは、koharuの立ち位置(左から何番目に出るか)は3件とも指示通りで、kanachanとkeichanが隣接して入れ替わる・あるいは片方が重複する形で崩れるという点だった。並び順の情報の全てを丸暗記に頼っているとまでは言えない(3件とも別々の崩れ方をしており、単一の記憶パターンへ収束していない)。koharuのように見た目がほかと最も離れた特徴(黒髪・赤目、最も低身長)を持つ人物は指示した立ち位置に出やすい一方、kanachanとkeichanのように学習データ中で位置の入れ替わりパターンが多様だった2人の組み合わせは、まだ正しく描き分けられていないように見える。3件をまとめた損失だけでは「変化なし」に見えたが、検証用ごとの内訳と画像を並べて初めて崩れ方の違いが分かった。

Step 4、プロンプト展開とtensor蒸留を画像で比較する

同一プロンプト、同一seedで、P0の直接入力からresampler経由まで4条件を比較する。

  1. P0を現行Animaへそのまま入力
  2. 固定辞書からP3を作って現行Animaへ入力
  3. Qwen3.8 MaxのJSONからP3を作って現行Animaへ入力
  4. P0からQwen3-0.6B+resamplerでC_predを作る
条件4人の本人一致属性の混ざり位置備考
P0崩れる(下記画像)あり指示順と一致しない人数が3人に減ることもあった
固定renderer P3(手書き4件、蒸留なし)4/4成功なし指示順どおりorder1〜4。Step1参照
固定renderer P3(Max生成12件、蒸留なし)11/12成功maxgen06のみ並び順が入れ替わるmaxgen06以外は指示順どおりMax→P3の結果。Step1参照
0.6B resampler(蒸留あり)部分的(下記)見た目の特徴の描き分けはP0より崩れが少ないRun1: 学習に使った別orderをほぼ再現。Run2: koharu/kuraraは合うがkanachan/keichanが入れ替わるコサイン距離 Run1: 0.595 / Run2: 0.605(検証用3件まとめ)

蒸留なしの固定renderer P3は16件中15件が指示通りに描き分けられ、蒸留ありの0.6B resamplerは検証用の並び順が崩れた。短い入力プロンプトの手前でMaxに並び順とシーンを決めさせ、rendererでP3化してAnimaへ入力する方法を、蒸留の対照条件として残す。

複数seedでの確認セクションを除き、大半の比較はseed 42のみの1回で行った。1seedの成功を安定性へ一般化しない。 人物数、本人一致、属性、並び順を別々に判定する。

実施記録(seed 42のみ)

教師として使ったorder3・order4のP3も、resamplerの学習データに入れる前に単独で生成して確認した。まずorder3の教師P3はこうなった。

order3(keichan, kurara, koharu, kanachanの順)の教師P3。左からkeichan(金髪+青リボン)・kurara(rose-brown髪+ピアス)・koharu(黒髪赤目)・kanachan(サイドポニー+アホ毛)。指示順と見た目の特徴は一致しているが、身長はkanachanがkoharuより低く出ている

後で気づいたが、このorder3は身長差が指示どおりに出ていない。教師プロンプトの文面はrendererの身長ロジックどおり(koharuが常に最も低い)なのに、生成画像ではkanachanがkoharuより低い。教師画像の判定を見た目の特徴と並び順中心でやっていたため、この時点では見落としたまま学習データに使っている。ただ学習結果へのダメージはそんなになさそうで、この後の生成では身長差はちゃんと出ている。身長文が正しいプロンプト側の情報もあるからかもしれない。

こちらはorder4の教師P3。身長差もこちらは指示どおりに出ている。

order4(kanachan, koharu, kurara, keichanの順)の教師P3。左からkanachan(サイドポニー+アホ毛)・koharu(黒髪赤目、最も低身長)・kurara(rose-brown髪+ピアス)・keichan(金髪+青リボン、kuraraと同身長)が指示順・見た目の特徴とも一致

order1は学習に使ったペアで、P0そのまま・教師P3・resamplerのC_predを比較する。C_predは学習で見た組み合わせなので、教師とほぼ同じ画像になるかを確認する。1枚目はP0をそのまま入れた結果。

order1、P0(トリガーのみ)をそのまま入力。制服になっており、2番目がkeichanの金髪+リボンを欠き、4番目がkanachanの特徴ではなくkeichan寄りの金髪+リボンになるなど、見た目の特徴が別の人物に混ざっている

2枚目は教師P3を入れた結果。

order1、教師P3(識別文込み)をそのまま入力。kurara・keichan・kanachan(サイドポニー+アホ毛)・koharu(黒髪赤目)が指示順どおりに描き分けられている

3枚目はresamplerのC_predを入れた結果。教師P3とほぼ同じ絵が出た。

order1、resamplerのC_pred(学習ペア)。教師P3とほぼ同一の画像になった。学習サンプルなのでコサイン距離0.005まで再現できており、生成結果もそれを裏付けている

order2は検証用で、resamplerの学習に一度も使っていない並び順。P0そのまま・教師P3・resamplerのC_predに、比較用のorder4教師P3を加えた4枚を並べる。1枚目はP0をそのまま入れた結果。

order2、P0(トリガーのみ)をそのまま入力。1番目がkoharuでなくkurara寄りの髪色になり、2番目はkanachanのサイドポニー+アホ毛を欠き、3番目と4番目も指示した見た目の特徴と食い違っている

2枚目は教師P3を入れた結果。指示した並び順はこの画像のとおりになる。

order2、教師P3(識別文込み)をそのまま入力。koharu(黒髪赤目、最も低身長)・kanachan(サイドポニー+アホ毛)・keichan(金髪+青リボン)・kurara(rose-brown髪+ピアス、keichanと同身長)が指示順どおりに描き分けられている

3枚目はresamplerのC_predを入れた結果。

order2(検証用)、resamplerのC_pred。4人の見た目の特徴(kanachanのアホ毛・koharuの黒髪赤目・kuraraのrose-brown髪+ピアス・keichanの金髪リボン)はP0そのままよりはっきり描き分けられて出た。しかし並び順はorder2の指示(koharu, kanachan, keichan, kurara)ではなく、学習に使ったorder4(kanachan, koharu, kurara, keichan)にほぼ一致した

4枚目は比較用の再掲で、学習に使ったorder4の教師P3。

order4(kanachan, koharu, kurara, keichanの順)の教師P3の再掲。order2のC_predの並びは、指示したorder2ではなくこの画像とほぼ同じ

この4枚を比べると、見た目の特徴の破綻はP0そのまま(1枚目)よりはっきり減っている一方、並び順は指示(教師P3、2枚目)ではなく学習に使ったorder4(4枚目)と一致している。「並び順の情報がQwen hidden stateから読み取られて教師へ再現された」というより、「学習中に見た並び順のうち一番近いものが選ばれた」という暗記に近い結果だった。

実施記録(Run2、seed 42のみ)

Run1のresamplerを学習12件・検証用3件で再学習した後、同じ3条件(P0そのまま・教師P3・C_pred)を検証用3件(order2・maxgen10・maxgen11)で生成し直した。order2のP0そのまま・教師P3画像はRun1と同じ(プロンプト自体を変えていないため)なので上の2枚を再利用し、C_predだけ再学習後のものへ差し替える。

order2(検証用)、Run2(学習12件・検証用3件)で再学習したresamplerのC_pred。1番目(koharu)と4番目(kurara)は指示通りの位置に出た。2番目・3番目はkanachanとkeichanが入れ替わっている。Run1(学習3件)のC_pred(学習に使ったorder4をほぼ再現していた)より、部分的に指示順へ近づいている

maxgen10は検証用で、指示順はkanachan, keichan, koharu, kurara。シーンは駅のホームで待つ。1枚目がP0(トリガー+シーンフレーズ)そのまま、2枚目が教師P3、3枚目がRun2で再学習したresamplerのC_pred。

maxgen10、P0(トリガー+短いシーンフレーズ)をそのまま入力。4人指定したのに3人しか生成されず、制服になっており、見た目の特徴も一部崩れている

maxgen10、教師P3(識別文+シーン込み)をそのまま入力。kanachan・keichan・koharu(最も低身長)・kuraraが指示順どおりに描き分けられている

maxgen10(検証用)、Run2で再学習したresamplerのC_pred。3番目(koharu)と4番目(kurara)は指示通りの位置に出た。1番目・2番目はkanachanとkeichanが入れ替わっている。order2と同じ「kanachan/keichanが隣接して入れ替わる」崩れ方だった

maxgen11は検証用で、指示順はkoharu, kanachan, kurara, keichan。シーンは雨の中庭で遊ぶ。並びはmaxgen10と同じく、P0そのまま・教師P3・Run2のC_pred。

maxgen11、P0(トリガー+短いシーンフレーズ)をそのまま入力。4人指定したのに3人しか生成されず、kuraraが欠落し制服になっている

maxgen11、教師P3(識別文+シーン込み)をそのまま入力。koharu(最も低身長)・kanachan・kurara・keichan(kuraraと同身長)が指示順どおりに描き分けられている

maxgen11(検証用)、Run2で再学習したresamplerのC_pred。1番目(koharu)だけ指示通り。2番目・3番目が両方keichan(金髪+青リボン)のような見た目になり、kuraraが出てこず、kanachanが4番目に押し出されている。3件の検証用の中で最も崩れが大きかった

検証用の3件を並べると、3件をまとめた損失はRun1とほぼ変わらなかった一方、崩れ方はRun1(学習に使った1パターンをほぼ再現する)とは違う形になった。koharuの立ち位置は3件とも合っていたが、kanachanとkeichanは入れ替わる(order2・maxgen10)か、片方が重複してもう片方が消える(maxgen11)かのどちらかで、まだ指示順を正しく再現できていない。P0そのまま(1枚目)は依然として4人目が欠落したり制服になったりと、指示順以前に人数や属性から崩れている点は変わらない。

複数seedでの確認(seed 1234・9999を追加)

ここまでの検証用3件の結果はseed 42の1回だけだったので、C_predを固定したまま(resamplerの重みも入力P0も変えない)、生成のseedだけ1234・9999に変えて同じ検証用3件を再生成した。C_pred自体はseedに関わらず同じtensorなので、ここではDiTのサンプリングが変わったときに4人の描き分けの傾向が変わるかどうかを確かめた。以下、order2・maxgen10・maxgen11の順に、seed1234と9999の結果を2枚ずつ並べる。

order2(検証用)、C_predのseed1234。seed42と同じく1番目(koharu)と4番目(kurara)が正しく、2番目・3番目はkanachanとkeichanが入れ替わっている

order2(検証用)、C_predのseed9999。seed42・1234と全く同じ並び(koharu, keichan, kanachan, kurara)が出た

maxgen10(検証用)、C_predのseed1234。seed42と同じく3番目(koharu)・4番目(kurara)が正しく、1番目・2番目はkanachanとkeichanが入れ替わっている

maxgen10(検証用)、C_predのseed9999。seed42・1234と同じ並びが出た

maxgen11(検証用)、C_predのseed1234。seed42と同じくkoharuだけ正しく、2番目・3番目が両方keichanのような見た目になりkuraraが出てこない

maxgen11(検証用)、C_predのseed9999。koharuの立ち位置は同じだが、seed42・1234とは違い2番目にkurara・3番目にkeichan・4番目にkanachanが出た。4人とも別々の見た目の特徴を持つ点はseed42・1234と違うが、並び順はこれもまた指示通りではない

3seedを並べると、order2とmaxgen10は3seedとも完全に同じ(誤った)並びを再現した。この2条件の入れ替わりはsampling seedだけでは説明しにくく、C_pred側の影響が強いと思われる。一方maxgen11だけはseedごとに違う崩れ方をした(seed42・1234はkeichanの重複+kurara欠落、seed9999は4人とも出るが並びが違う)。この3件の中では、maxgen11のconditioningがDiTのサンプリングによって異なる解釈を受けやすい可能性がある。

蒸留は並び順が崩れ、固定rendererは16件中15件成功した

学習側はRun1(3ペア)・Run2(12ペア)ともコサイン距離0.005前後まで教師conditioningを再現した一方、検証用3件をまとめたコサイン距離は0.595→0.605で、学習ペア数を4倍にしても数値は変わらなかった。生成画像の崩れ方だけが、Run1では学習に使った1パターンをほぼ再現する丸暗記だったのに対し、Run2ではkoharuの立ち位置は合うがkanachanとkeichanが入れ替わる・重複する形へ変わった。この変化は3件をまとめた損失には出ていない。order2とmaxgen10は3seedとも同一の誤った並びを再現した。

一方、蒸留なしでMaxのJSONを固定rendererでP3化してAnimaへ入力する方式は、16件中15件で指示通りに描き分けられた。MSEとコサイン距離が十分下がっても検証用の並び順が直らなかったことから、今回の特徴量レベルの損失(masked MSE + cosine + distribution loss)では、DiTがconditioningのどの差を使って人物を描き分けているかまでは捉えられていないと思われる。C_predC_goodへ近づける代わりに、同一latent・同一timestepでDiTのnoise prediction自体を一致させる損失(L_dit = MSE(DiT(x_t, t, C_pred), DiT(x_t, t, C_good)))へ進める余地は残っているが、DiTを通したbackpropが必要になり計算量が増えるため、この記事の範囲では試していない。