技術約45分で読めます

AnimaのDiTの残差ストリームに色の差を足して、髪色だけ変わる方向を探した

いけさん目次

GPT-OSSの隠れ状態を記号構造の式に差し替えても答えがほぼ同じだった論文の話をしていた。
LLMの全層の隠れ状態をテンソル積表現という閉じた式で作り直しても本体の答えがほぼ変わらず、役割を書き換える介入も通る。
ならば画像生成のDiTでも同じ見方ができるんじゃないか、という流れになった。
LLMでいう文法や論理の内部アルゴリズムにあたるものは、DiTなら空間配置、部品構造、属性の束縛、遠近と遮蔽、denoise過程の役割分担あたりに対応するはずだと考えた。

Animaの4人LoRAの崩れをconditioningの中まで覗いた記録では、DiT直前のconditioningまでは介入して調べたが、そのconditioningをDiTがどう画像空間へ実装しているかは調べていない。
入口だけ調べて「プロンプトは理解してそうだ」で止まっていた。

今回はDiT本体を開けてみる。
まず赤一色と青一色の絵で色がどこで決まりどこに書き込まれるかを調べ、次に1人の髪、最後に2人並びの髪色を試した。

検証環境

項目内容
マシンApple M1 Max 64GB、macOS 26.5
実行方法ComfyUI v0.30.1(commit 0764232)の comfy パッケージを直接importする自作ハーネス(.image-work/dit-probe/
DiTAnima-Base v1.0(bf16)
テキストエンコーダQwen3-0.6B-Base+T5トークナイザー、Anima本体のLLMAdapter
LoRAなし(Turboも外す)
共通条件30 step、CFG 4.0、euler、scheduler simple、1216×832(トークン格子52×76)
ネガティブworst quality, low quality, blurry, lowres, text, watermark, bad hands
計測対象CFGのcond側パスだけ

DiTの構造はComfyUIの comfy/ldm/cosmos/predict2.py にある。
各Blockは self-attention(画像トークン同士)→ cross-attention(画像トークンがconditioningを参照)→ MLP の順で、adaLNでtimestep埋め込みが掛かる。
テキストはcross-attentionだけを通って画像側へ入るので、E1では各画像トークンがどのテキスト位置をどれだけ強く参照したかをcross-attentionの重みで測る。重みだけで実際の寄与が決まるわけではないので、あとの節ではconditioningの差し替えでも確かめる。
Qwen-ImageのようなMMDiTだと画像とテキストが同じattentionに混ざるが、Animaではテキスト側の重みだけを別に扱える。
モデルの構成はAnima-Baseのmodel cardのとおりで、以下は手元で動かして測った値。

項目実測値
Block数28
hidden次元2048
ヘッド数16(head_dim 128)
トークン格子(縦×横)52×76=3952画像トークン(patch 2、1216×832のまま偶数)
conditioning shape(1, 512, 1024)。実際のプロンプトは38トークン前後で、残りはゼロパディング
1枚の生成時間(30 step)254秒(4 stepで38秒)

cond側とuncond側は transformer_options["cond_or_uncond"] で見分ける。
この解像度ではcondとuncondが別呼び出しで交互に来るので、0の行だけを計測・改変した。
単語とcross-attentionのキー位置の対応は、T5トークナイザーで実際にトークン化して調べた。

ハーネスの動作確認でまず引っかかったのが、self-attentionの左右遮断。(b,1,S,S) の真偽値マスクでSDPAに指定すると、MPSでは数Block進んだところでNaNになって真っ黒な画像が出る。
同期を強制すると再現しない。原因は分からないままだが、左右のトークンを切り出してモデル自身のattention演算を別々に走らせる方式(ブロック対角マスクと等価)にしたら通ったので、そのまま進めた。
もう1つは残差ストリームで、ComfyUI側がfp32で保持していて値が大きく(最大で2.7e5程度)、fp16で保存すると中盤Blockでごく一部がinfになる。E2の保存はfp32にした。

計測を入れても画像が変わらないかは、attnとresidのフック有無で出力PNGを比べた。bit単位で一致。conditioning差し替えも、全Block・全stepで差し替えるとプロンプトBを素で生成した画像とbit単位で同じになった。

実験の順序

実際に行った順は2人並び(E0〜E7)が先で、単色(S0〜S5)はそのあとに追加した。

番号名前やること測るもの
単色S0単色が出るか赤・青・緑・黄の単色プロンプトをseed 42で生成する画像の平均RGBと標準偏差。単色と呼べるか
単色S1色が決まるstep赤と青の生成で、各stepのdenoise予測(x0)をVAEで絵に戻し、平均RGBを取る何step目で赤と青に分かれるか
単色S2残差の差同じseedで赤と青を回し、全Block・全stepの出力を保存して差を取る差のL2がどのBlockで大きいか。差の大きい次元が巨大値の次元と同じか
単色S3方向の移植青 − 赤 の平均方向を赤の生成に足す。Block群を変えて7回、全Blockでstepを限定して2回画像の平均RGBが青に近づくか。どのBlockで変わるか
単色S3x単独Blockと係数S3で色が変わったBlock群を1つずつに分け、step 0〜1だけに足す。係数0.7も1回Block 1つで足りるか
単色S4空間で切るS3で色が変わったBlock群に、左半分のトークンにだけ方向を足す左半分だけ青くなるか
1人S5髪への移植同じ方向を、1人の金髪の絵の髪トークンに足す髪が青くなるか。顔と服が残るか
2人E0ベースライン左右の髪色を入れ替えた2本をseed 5個ずつ素で生成する左右の髪色が指示どおりか
2人E1cross-attention map全Block・全stepの blondeblack へのattention重みを空間マップに戻す偏りが立ち上がるBlockとstep
2人E2残差ストリーム差分同じseedで2本を回し、全Block出力の差を取る差の左右非対称度
2人E3conditioning差し替え指定Block群または指定step範囲だけ、もう一方のconditioningに差し替えるどこを差し替えると髪色が入れ替わるか
2人E4self-attention左右遮断画像トークンのself-attentionで左右の相互参照を切る遮断で何が起きるか
2人E5・E6トークン単位の書き換え髪色のトークン位置だけを差し替える。Block・step・空間・混合比で絞る元の絵を残して髪色だけ変わるか
2人E7残差ストリームを直接書き換える髪色の差ベクトルを髪トークンにだけ足す髪だけ変わるか

S0 単色が出るか

プロンプトは solid red background, plain background, no humans, flat color, empty の色名だけを変えた4本。ネガティブは共通条件のまま。
1回目で4色とも単色になったので、文言の試行はしていない。

平均RGB標準偏差(R / G / B)単色度
red213 / 18 / 300.61 / 0.55 / 0.46100%
blue1 / 87 / 1870.50 / 0.41 / 0.57100%
green62 / 132 / 790.51 / 0.43 / 0.51100%
yellow255 / 241 / 730.01 / 0.52 / 0.76100%

単色度は、RGBの3チャンネルすべてがその画像の平均から±20以内に入る画素の割合。どの画像も各チャンネルの値の幅が5〜10階調しかない。

S0 redの生成結果

S0 blueの生成結果

S0 greenの生成結果

S0 yellowの生成結果

T5トークン化では4本とも15トークンで、色の単語は solid の次の位置1に1トークンで来た。conditioningの1行が色の単語1つに対応していた。

S1 色が決まるstep

各sampling stepでサンプラーが出すdenoise予測(x0)をVAEで絵に戻し、平均RGBを取った。
取り方はComfyUIのサンプラーのcallbackで、プレビュー用に渡される denoised をそのまま保存し、サンプリング後にまとめてデコードした。
DiTは変更していないので最終画像は通常生成の gen とbit単位で一致した(SHA-256で確認)。30回のデコードは110秒で、1回3.7秒。

red・blueに加えて、S5で使う1人の絵 G(1girl, upper body, looking at viewer, simple background, blonde hair, long hair)でも取った。

stepredblueG
0255 / 66 / 595 / 133 / 255255 / 254 / 250
1220 / 17 / 283 / 87 / 179239 / 212 / 185
2210 / 24 / 354 / 89 / 180244 / 225 / 199
3213 / 24 / 332 / 89 / 183248 / 228 / 206
5213 / 20 / 311 / 88 / 185248 / 225 / 207
10213 / 17 / 301 / 87 / 186247 / 223 / 204
15213 / 17 / 301 / 87 / 186247 / 222 / 203
29213 / 18 / 301 / 87 / 187246 / 222 / 203

S1 redの各stepのx0を1/8に縮小して並べたもの

S1 blueの各stepのx0を1/8に縮小して並べたもの

S1 Gの各stepのx0を1/8に縮小して並べたもの

3本とも、step 1で全チャンネルが最終色の±30以内に入った。
step 0のx0はどれか1チャンネルが255に張り付いていて外れるが、2回目の予測でもう色は決まっていて、あとは値が詰まっていくだけ。redの単色度はstep 0で51%、step 1で96%、step 2で100%。
Gも色の平均はstep 1で決まっていた。形のほうはstep 1ではぼやけた塊で、step 4で人物と分かる形になり、目や襟の細部はstep 10あたりから。

色は最初の2 stepで決まるらしい。なのでS3以降で方向を足すときは、全stepに足す条件のほかにstep 0〜1だけに足す条件も入れた。

S2 残差の差

同じseed 42でredとblueを回し、全28 Block・全30 stepの出力をfp32で保存した(1本25GB)。
同じstep・同じBlockの出力の差を取り、全トークンで平均したベクトル d = blue − red を作った。
確かめるのは、差が少数の次元に偏るか、偏るならそれがE2で見つかる巨大値の次元と同じか。なので d のノルム、redの残差ノルムに対する比、|d| の上位20次元がノルムに占める割合、その20次元がredの残差自体で値の大きい上位20次元と重なる数を出した。
表はstep 1と15、Blockは4つおき。

stepBlockdのノルム残差ノルム上位20次元の割合巨大値の次元との重なり
105300.180.930
14103883090.130.880
18124287030.140.872
1121239106300.120.880
1161250119600.110.880
1201377137000.100.860
1243054142200.220.892
12712030618200.190.581
15027570.480.901
154327186140.380.941
158347787930.400.931
15123511106000.330.922
15163527119300.300.922
15203583136100.260.921
15244925150200.330.914
152711460635200.180.654

S2 blue−redの差ベクトルのBlock別の統計。上からノルム、残差ノルムとの比、上位20次元の割合、巨大値の次元との重なり数

S2 差のトークン平均L2。縦が保存したstep、横がBlock

S2 差の空間マップ。行がstep、列がBlock

赤と青の差は2048次元中の20次元に85〜94%が集中していた。Block 26〜27だけ58〜65%に下がる。あとのE7で髪色の差も20次元に7〜8割集中するのと同じ形。
その20次元は、残差自体で値の大きい巨大値の次元とはほぼ重ならない(中間Blockで0〜2、Block 24と27で最大4)。2人の実験のE7の段階では同じ次元じゃないかと考えていたが、違った。色の差はおおむね普通の大きさの次元にある。

差は空間的には均一で、Block 0〜11と22〜27ではトークン間のばらつき(変動係数)が0.02〜0.17。
Block 12〜21だけ3952トークン中62個(1.6%)で差が中央値の10倍以上あり、位置はBlock 12〜21で同じで、Block 22で消えた。
Block 0の差は5〜27で、Block 1以降の1000以上に比べるとほぼゼロ。赤青の2本では、Block 0の出力はプロンプトの違いをほとんど反映していないらしい。

S3 方向の移植

S2の d をredの生成に足した。領域は全トークン、平均方向、係数1.0。Block群を7つに分けて全stepに足す7本と、全Blockでstepを限定する2本。
射影は平均RGBを red→blue の直線に射影した値で、0がred、1がblue。

番号Blockstep平均RGB単色度射影見た目
S3-10〜3全部255 / 40 / 143%−0.16飽和した橙赤に黄色の粒
S3-24〜7全部254 / 21 / 134%−0.18飽和した赤に細かい粒
S3-38〜11全部0 / 0 / 0全面黒(残差がNaN)
S3-412〜15全部0 / 0 / 0全面黒(残差がNaN)
S3-516〜19全部0 / 0 / 0全面黒(残差がNaN)
S3-620〜23全部0 / 0 / 0全面黒(残差がNaN)
S3-724〜27全部0 / 162 / 25597%1.22単色のシアン寄りの青。薄いトークン格子の織り目
S3-80〜270〜10 / 0 / 0全面黒(残差がNaN)
S3-90〜272〜290 / 0 / 0全面黒(残差がNaN)

飽和した2本には、上位20次元を0にした版も回した。

番号Blockstep平均RGB単色度射影見た目
S3-1c0〜3全部236 / 234 / 24361%0.59白に近い薄紫、粒状
S3-2c4〜7全部167 / 223 / 25530%0.80水色、斜めの織り目が強い

S3 S3-1 Block 0〜3

S3 S3-2 Block 4〜7

S3 S3-3 Block 8〜11

S3 S3-4 Block 12〜15

S3 S3-5 Block 16〜19

S3 S3-6 Block 20〜23

S3 S3-7 Block 24〜27

S3 S3-8 全Block step 0〜1

S3 S3-9 全Block step 2〜29

S3 S3-1c Block 0〜3、上位20次元を0

S3 S3-2c Block 4〜7、上位20次元を0

Block 24〜27に足したときだけ青に変わった。しかも青を通り越してシアン寄り(射影1.22)。
Block 8〜23は4Blockまとめて足すと残差がNaNで全面黒。全Blockで足してstep範囲を絞っても同じだった。Block 0〜7は赤のまま飽和した。
差ベクトルは各Blockでそれぞれ次のBlockの入力もずらすので、4つまとめて足すと影響が重なる。NaNはそのせいかもしれないが、この測定では判別できない。
上位20次元を0にした版は、Block 0〜7でも青の側に変わったが、単色度が30〜60%まで下がって織り目が出た。

S3の追加 単独Blockと係数

S1で色がstep 1までに決まっていたので、Block 24〜27の4つを1つずつに分け、step 0〜1だけに足してみた。係数を0.7に下げた4本まとめも1本試した。

番号Blockstep係数平均RGB単色度射影
S3x-1240〜11.00 / 142 / 255100%1.20
S3x-2250〜11.00 / 142 / 255100%1.20
S3x-3260〜11.00 / 136 / 255100%1.19
S3x-4270〜11.00 / 137 / 255100%1.19
S3x-524〜270〜10.70 / 175 / 255100%1.23

S3追加 S3x-1 Block 24だけ、step 0〜1

S3追加 S3x-2 Block 25だけ、step 0〜1

S3追加 S3x-3 Block 26だけ、step 0〜1

S3追加 S3x-4 Block 27だけ、step 0〜1

S3追加 S3x-5 Block 24〜27、step 0〜1、係数0.7

Block 1つ、step 0〜1だけで、赤が完全に青になった。24〜27のどれでも同じで、単色度も100%のまま。
射影が1を超えてシアン寄りになるのは4本まとめでも単独でも変わらず、係数0.7に下げても1.23。2点しか試していないが、方向の大きさに対して色が線形に変わる感じではない。

S4 空間で切る

Block 24〜27に、左半分(列0〜37)のトークンにだけ d を足した。全stepの版と、step 0〜1だけの版。

番号step左半分の平均RGB右半分の平均RGB境界
S4-1全部0 / 161 / 255(射影1.22)188 / 170 / 131(射影0.43)x=631で色差141。トークン境界に沿ってギザギザ
S4-20〜10 / 162 / 255(射影1.22)171 / 6 / 9(射影0.07)x=629で色差327。幅1画素

S4 S4-1 Block 24〜27の左半分、全step

S4 S4-2 Block 24〜27の左半分、step 0〜1

step 0〜1だけの版は、左が青、右が赤のままで、境界は1画素幅の直線。
全stepの版は左が青になる一方、右がベージュに崩れて、境界がトークン境界のギザギザになった。
境界の位置は列38のトークン境界にあたる608画素より1.3〜1.4トークン分右の629〜631画素。VAEの受容野(1画素を復元するときに参照する周辺の範囲)の分だと思う。

この条件では、単色の差方向を最後の4 Blockへstep 0〜1で足すと、左右を分けて色を変えられた。
無介入のときに色がそのBlockで書き込まれているかまでは確かめていない。中間Block(8〜23)に足すと残差が発散するので、そこには同じ形では足せない。

S5 髪への移植

単色で取れた方向を、1人の金髪の絵Gの髪トークンに足した。
髪はもともと黄色系なので方向は blue − yellow にして、yellowの残差はBlock 24〜27・step 0〜1だけ保存した。比較で blue − red も1本足した。

Gの髪マスクは素Gから金髪の画素(R>150、G>110、B<160、R−B>40)を取り、25%以上で髪トークンとした。3952トークン中1054個。頭頂のハイライトを拾うためにBの上限を140ではなく160にしていて、首の影が2〜3トークン入っている。

S5 Gの髪マスクを素Gに重ねた図

素Gの髪内の平均RGBは 235 / 190 / 118。

番号方向Blockstep領域素Gとの画素差(全体 / 髪内 / 髪外)髪内の平均RGB
S5-1blue − yellow24〜270〜194 / 185 / 610 / 3 / 252
S5-2blue − red24〜270〜168 / 137 / 430 / 177 / 256
S5-3blue − yellow24〜270〜1全部165 / 177 / 1610 / 25 / 248
S5-4blue − yellow24〜27全部223 / 181 / 2390 / 0 / 0

S5 S5-1 blue−yellowをBlock 24〜27・step 0〜1で髪トークンに

S5 S5-2 blue−redをBlock 24〜27・step 0〜1で髪トークンに

S5 S5-3 blue−yellowをBlock 24〜27・step 0〜1で全トークンに

S5 S5-4 blue−yellowをBlock 24〜27・全stepで髪トークンに

髪トークンに足したS5-1は、髪マスクの形がそのまま青いベタ塗りの塊になった。
塊の輪郭は滑らかな曲線で黒い縁取りが付き、人物はその下の空いた場所に小さく描き直されて、服は赤のまま、塗りは平坦な線画調。
blue − red のS5-2も同じ崩れ方で、色が水色になった。
全トークンに足したS5-3は全面が青になって人物が消えた。全stepに足したS5-4は残差が発散して真っ黒。

単色で取れた方向は、足した領域を青一色の面にする方向だったらしい。
髪の領域に足すとそこを青い物体として描いて、人物は空いた場所に描き直された。
step 0〜1は色だけでなく構図も決まる段階なので(S1でGの形はstep 4まで固まらない)、そこに面の指示を足したから構図ごと変わったのだと思う。

2人並びの髪色へ

時系列では単色より前にやった実験。2人並んだ絵で、左の子が金髪、右の子が黒髪という属性と位置の束縛を、conditioningの差し替えから入って最後に残差ストリームの操作まで調べた。

LoRAを使わない理由

キャラLoRAを当てると、見つかった構造がAnimaの性質なのかLoRAの性質なのか見分けがつかない。上の4人LoRAの記録で、キャプションの書き方しだいで外見がトリガーへ吸収されるのも確かめているので、LoRAの影響が混ざってしまう。
素のAnimaで blonde hairblack hair のような普通の属性なら、テキスト側で属性が1〜2トークンに対応して、cross-attentionでその単語からどのパッチに重みが乗ったかを調べやすい。同じ理由でTurbo LoRAも外して、通常stepとCFGありで回した。

プロンプト

2人が横に並ぶ絵で、髪色だけを左右で入れ替えた2本を基本にする。

記号プロンプト
A2girls, standing side by side, upper body, looking at viewer, simple background. The girl on the left has blonde hair. The girl on the right has black hair.
B2girls, standing side by side, upper body, looking at viewer, simple background. The girl on the left has black hair. The girl on the right has blonde hair.

AとBは単語の集合が同じで、束縛だけが違う。
エンコーダ側の表現にも差は出るが、cross-attentionの重みを単語ごとに空間へ戻せば、DiTがその差をどこで位置へ変換したかが分かるはず。

E0 ベースライン

A・Bをseed 42〜46で素のまま生成。10枚とも2人が描かれて、左右の髪色は指示どおりだった。
判定は画像を1枚ずつ開いて、人数と左右の髪色だけを見たままで書いた。

プロンプトseed左の髪色右の髪色
A42blondeblack
B42blackblonde
A43blonde(彩度の高い黄色)black
B43blackblonde(彩度の高い黄色)
A44blondeblack
B44blackblonde
A45blondeblack
B45blackblonde
A46blondeblack
B46black(青みを含む濃紺寄り)blonde

E0 プロンプトA seed 42(指示は左blonde・右black)

E0 プロンプトB seed 42(指示は左black・右blonde)

E0 プロンプトA seed 43(指示は左blonde・右black)

E0 プロンプトB seed 43(指示は左black・右blonde)

E0 プロンプトA seed 44(指示は左blonde・右black)

E0 プロンプトB seed 44(指示は左black・右blonde)

E0 プロンプトA seed 45(指示は左blonde・右black)

E0 プロンプトB seed 45(指示は左black・右blonde)

E0 プロンプトA seed 46(指示は左blonde・右black)

E0 プロンプトB seed 46(指示は左black・右blonde)

服の色は指定していないが、seed 42のAでは左の子の服が白系、右の子の服が黒、seed 46のBでは左が紺の上着、右が赤のセーターになっている。

髪色と位置が入れ替わったseedはE4で使うつもりだったが、10枚とも指示どおりで出なかった。素のAnimaはこのプロンプトだと崩れないらしい。
E4は髪色に服の色を足した条件にして、まず指示から外れるかどうかから確かめた。

E1 cross-attention map

E0で指示どおりだったA・seed 42を、全Block・全stepのcross-attention重みを記録しながら同条件で生成した。
記録はq・kのコピーから別計算しているので、出力画像は記録なしのE0と同一になる(bit単位で一致することはハーネスの動作確認で確かめた)。
各Blockの各stepで、画像トークン3952個それぞれが blonde(T5トークン位置25)と black(位置34)へ向けたattention重みをヘッド平均で取り、52×76の格子に戻した。
そのうえで「左半分の格子に落ちた重みの合計 ÷ 全体」を左半分比率とした。0.5なら左右に偏りがない。

E1 単語blondeへのcross-attentionの左半分比率。縦がsampling step、横がDiT Block

E1 単語blackへのcross-attentionの左半分比率。縦がsampling step、横がDiT Block

数値はこんな感じ。

Blockblonde step 0blonde step 10blonde step 29black step 0black step 10black step 29
00.500.500.500.500.500.48
20.540.630.580.460.320.44
50.670.810.570.310.250.46
90.910.900.640.090.120.38
130.900.840.660.100.120.31
160.810.750.600.190.200.39
190.610.630.520.410.390.48
200.490.490.490.490.490.50
270.500.500.500.500.500.50

Block 0〜1とBlock 20〜27は、どのstepでも両方の単語が0.50前後で、左右の偏りがなかった。
偏りはBlock 2あたりから出始め、Block 8〜16で最も強く、step 0の時点でBlock 9は blonde が0.91、black が0.09でほぼ鏡像になっていた。
偏りはstepが進むほど弱まり、Block 9の blonde は step 0 の0.91から step 29 では0.64まで下がった。

左半分比率は方向だけを表しているので、格子全体の重みの平均(1トークンあたり)も出した。conditioningは512トークンなので、均等に参照していれば1トークンあたり 1/512 ≒ 0.00195 になる。表は1000倍した値。

Blockblonde step 0blonde step 10blonde step 29black step 0black step 10black step 29
01.00.91.00.90.90.9
52.31.60.31.70.90.3
96.22.30.47.72.20.3
133.61.10.22.91.20.2
195.62.10.85.92.00.7
200.90.91.00.90.91.0
270.90.91.01.01.01.1

step 0のBlock 9は均等の3〜4倍の重みが髪色の単語に集まっていて、step 29ではその同じBlockが均等の1/5以下まで下がった。
一方でBlock 0〜1と20〜27は最初から最後まで均等の半分程度で、値がほとんど変わらなかった。
後半Blockの0.50は、この2単語をほとんど参照していない状態の値らしい。代わりに何を参照しているかは、この計測では取っていない。

空間マップは、Block 4・8・12・16・20をstep 0・5・10・20・29で切り出し、パネルごとにそのパネルの最大値で正規化した(パネル間で明るさは比較できない。形だけを確かめる図)。

E1 単語blondeへのcross-attentionの空間マップ。行がstep、列がBlock、各パネルは自身の最大値で正規化

E1 単語blackへのcross-attentionの空間マップ。行がstep、列がBlock、各パネルは自身の最大値で正規化

E1 Block 12でのblondeとblackの空間マップをstep 0〜29で並べたもの

blonde の図では、Block 4がstep 5以降で左の人物の髪の形に沿って明るくなり、Block 8もstep 5以降は左の人物の輪郭が出た。Block 12と16は少数の点に重みが集中していて面としての形が出ず、Block 20は格子全体がほぼ平らだった。
Block 4の左半分比率は0.6前後で表の上では偏りが小さいが、空間マップでは左の人物の髪の形が出ている。右の人物側にも薄く重みが広がっていて、その分だけ比率が下がっている。
Block 12・16の点状の集中が何のトークン位置なのかは、この図からは分からない。

E2 残差ストリームの差分

同じseed 42でAとBを生成し、全Blockの出力(残差ストリーム、形は 1×52×76×2048)をstep 0・1・2・5・10・15・20・29でfp32のまま保存した。1回あたり6.8GB。
AとBの同じstep・同じBlockの出力を引き、トークンごとのL2ノルムを取って52×76の格子に戻した。
step 0は初期の潜在表現が同じなので、差はconditioningの違いだけから生じる。step 1以降は潜在表現の軌道も分かれ、差にはそれも混ざっていた。

E2 AとBの残差ストリーム差分のトークン平均L2。縦が保存したstep、横がDiT Block

E2 AとBの残差ストリーム差分の空間マップ。行がstep、列がBlock。パネルごとにカラーバーが違う

E2 AとBの残差ストリーム差分の左右非対称度。縦が保存したstep、横がDiT Block。正なら左半分に差が大きく、負なら右半分に大きい

step 0のトークン平均L2はBlockが深くなるほど増えた。

Blockstep 0のL2平均
00.08
118
427
856
9116
12330
16542
20546
24613
271416

Block 0の出力はAとBでほぼ同じで、Block 1で一気に18まで上がった。Block 11から12で170から330へ、Block 26から27で946から1416へ急に上がった。
ただし残差ストリーム自体の大きさもBlockごとに違うはずで、ここでは差の絶対値だけを出している。

空間マップでは、step 0のBlock 0〜8の差は格子全体に散ったノイズ状で形が出ておらず、Block 24と27は同じstep 0でもすでに2人の頭の位置に2つの塊が出ていた。
step 1〜2になるとBlock 0〜8でも2人の頭の位置に塊が出て、step 5以降は2人の髪と輪郭の形がそのまま差になった。
どのBlock・どのstepでも差は左右両方の人物に出ていて、片側にだけ差が集まるパネルはなかった。AとBは左右両方の髪色を入れ替えているので、差は入れ替えの内容どおりに両側へ出た。
Block 12・16・20では、格子の上端付近の数トークンだけが10万〜20万の値を持ち、残りは真っ暗になった。ハーネスの確認でfp16の範囲を超えた要素があったのはこのトークンで、ComfyUIが残差ストリームをfp32で保持している理由もここにあると思われる。

左右非対称度は「左半分の平均 − 右半分の平均」を全体平均で割った値で、どのBlock・stepでも±0.4の範囲になった。
step 0のBlock 9が−0.38で右寄り、step 5〜20のBlock 12〜20が−0.16〜−0.37で右寄り、step 2の同じBlock範囲は+0.11〜+0.22で左寄り、step 29はどこもほぼ0だった。
Block 12〜20の値には上で書いた上端の外れ値トークンも含まれている。

どのBlockで束縛が決まるかは差分だけでは分からないので、E3で差し替えて確かめた。

E3 conditioningの差し替え

Aで生成しながら、指定した範囲のcross-attentionだけBのconditioningに差し替えた。seed 42、cond側だけ。
差し替えが全Block・全stepなら素のBとbit単位で一致することはハーネスの動作確認で確かめた。
判定は画像を1枚ずつ開いて髪色の配置を見たままで書き、補助として素のA(E0のseed 42)と素のBに対する平均絶対画素差(RGB、0〜255)を付けた。素Aと素Bの間の画素差は65.49。

E3a Block軸

step 0〜29の全部で、Block群だけを差し替えた。

差し替え範囲髪色の配置髪色以外の変化素Aとの画素差素Bとの画素差
Block 0〜3Aのまま両者の目が黒くなった(Aの左は青緑、右は茶)5.9865.14
Block 4〜7Aのまま襟元が縦リブのボタン付き立ち襟に変わり、顔が面長になった11.7062.52
Block 8〜11Aのまま顔の寄りが強くなり目が大きな黒目、左のトップスが薄紫のボタンシャツ17.0462.49
Block 12〜15Bに反転二人とも黒のノースリーブタートルネックで肩出し、右の髪が短いボブ、上半身の寄り構図59.0438.06
Block 16〜19Aのまま襟がフリルからバックル付きの詰襟に変わった6.4864.28
Block 20〜23Aのまま襟元に金具のディテールが加わった4.2365.87
Block 24〜27Aのままほぼ同じ3.1165.63
Block 0〜13Aのまま二人ともノースリーブのリブタートルネックで肩出し、右の髪が肩までのボブ、左のトップスは灰紫、寄り構図30.7659.40
Block 14〜27Bに反転二人とも黒のノースリーブタートルネックで肩出し、右の髪が短いボブ、寄り構図59.7738.35

E3 BlockをBlock 0〜3だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 4〜7だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 8〜11だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 12〜15だけBのconditioningに差し替えた結果。髪色の配置はBに反転

E3 BlockをBlock 16〜19だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 20〜23だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 24〜27だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 0〜13だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 BlockをBlock 14〜27だけBのconditioningに差し替えた結果。髪色の配置はBに反転

Block 12〜15とBlock 14〜27の2つで髪色の配置が反転した。どちらも画素差が素Aから59、素Bへ38で、AでもBでもない別の絵になっている。反転した2枚は服・髪型・構図の変わり方も同じ。
Block 0〜13の差し替えは反転しなかったが、服と構図は反転した2枚と同じように変わった(画素差30.76)。
Block 12〜13を含んで反転しなかったのはこの1枚だけなので、Block 12〜15の4つのうち14〜15を含むかどうかが分かれ目っぽい。ただBlock群の切り方が2通りしかなく、Block 14〜15だけの差し替えは試していない。

Block 0〜3、16〜19、20〜23、24〜27の差し替えは髪色の配置を変えず、画素差も3〜6程度。目の色や襟の飾りといった細部だけが変わった。
Block 4〜7と8〜11は画素差が12〜17で、顔の寄り方やトップスが変わったが髪色の配置はAのまま。

E1で左右の偏りが最も強かったのはBlock 8〜16で、E3aではそのうちBlock 12〜15を差し替えたときに反転した。
E1の偏りが強いBlock 8〜11を差し替えても反転しなかったので、cross-attentionの偏りの強さと、そのBlockを差し替えたときに髪色が入れ替わるかどうかは、この実験では一致していない。

E3b step軸

全Blockで、step範囲だけを差し替えた。

差し替え範囲髪色の配置髪色以外の変化素Aとの画素差素Bとの画素差
step 0〜4Bに反転Bと同じ平面的な線画、茶のカーディガン+薄紫のフリル襟、右の目は灰色。Bより肩の写る範囲がやや広い65.3410.11
step 5〜9Aのまま襟がバックル付きの詰襟に変わった5.7766.15
step 10〜19Aのままほぼ同じ2.2365.56
step 20〜29Aのままほぼ同じ0.7565.46
step 0〜9Bに反転素Bとほぼ同一65.601.90
step 10〜29Aのままほぼ同じ2.2965.55

E3 stepをstep 0〜4だけBのconditioningに差し替えた結果。髪色の配置はBに反転

E3 stepをstep 5〜9だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 stepをstep 10〜19だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 stepをstep 20〜29だけBのconditioningに差し替えた結果。髪色の配置はAのまま

E3 stepをstep 0〜9だけBのconditioningに差し替えた結果。髪色の配置はBに反転

E3 stepをstep 10〜29だけBのconditioningに差し替えた結果。髪色の配置はAのまま

step 0〜4だけBにすると、髪色の配置も服も線画の質感も素Bに寄った(素Bとの画素差10.11)。step 0〜9なら素Bとほぼ同一(1.90)。
逆にstep 5〜9、10〜19、20〜29、10〜29のどれを差し替えても素Aとほぼ同じで、step 5〜9で襟が変わった以外は画素差3以下。
step 5〜9はstep 0〜4と同じ長さなのに、結果は素Aのままだった。

E1で測った、cross-attentionの偏りがstepとともに弱まって後半stepでは髪色の単語をほとんど参照しなくなる傾向とは合っている。ただE1の偏りは最終stepでも0.5まで下がりきっていなかったので、後半stepの参照が完全にゼロというわけでもない。

E4 self-attentionの左右遮断

E0で髪色の束縛が崩れたseedがなかったので、まず髪色に服の色を足したプロンプトCで、素のまま3seed生成した。

記号プロンプト
C2girls, standing side by side, upper body, looking at viewer, simple background. The girl on the left has blonde hair and wears a red dress. The girl on the right has black hair and wears a blue dress.
seed人数左の髪色左の服色右の髪色右の服色一致数
4224/4
4324/4
4424/4

E4 プロンプトC seed 42の素の生成(指示は左が金髪+赤い服、右が黒髪+青い服)

E4 プロンプトC seed 43の素の生成(指示は左が金髪+赤い服、右が黒髪+青い服)

E4 プロンプトC seed 44の素の生成(指示は左が金髪+赤い服、右が黒髪+青い服)

3枚とも4項目すべて指示どおり。seed 42の右の服は青というより紺だが、青系として一致に入れた。
この条件でも崩れず、遮断で束縛が改善するかは比較しようがない。
それでも遮断を入れると絵がどう変わるか自体は分かるので、同じ3seedでself-attentionのマスクを入れてみた。
マスクは画像トークンのself-attentionで左右のトークンが同じ側だけを参照する形にし、cross-attentionとMLPは変更していない。実装は検証環境に書いた左右分割のattentionのまま。

6枚とも最左の人物は金髪に赤い服、最右の人物は黒髪で、最右の服色だけ紺と青に分かれた。

マスク範囲seed人数最右の服色素との違い素との画素差
全Block424画面中央に縦の継ぎ目。左半分は白背景の平塗り線画、右半分は陰影付きの塗り込みで画風が別。左半分の2人は腕・胴が白く塗り残され、腕が途中で消える86.75
全Block434中央に縦の継ぎ目。左半分は陰影付き、右半分は平塗り。右半分の2人は腕がなく胴のシルエットのみで、首が細く体と繋がっていない99.88
全Block444中央に縦の継ぎ目。左半分は白背景、右半分はグレー背景・太線の別画風。右半分の右の人物は髪が黒に青緑が混ざる88.94
Block 8〜16424継ぎ目なしで画風は連続。人物が2人から4人に増え、金髪+赤と黒髪+青のペアが2回繰り返される78.25
Block 8〜16434継ぎ目なし。4人で同じペアが2回。左から2人目の胸元が肌色のまま服に繋がっていない79.19
Block 8〜16443継ぎ目なし。3人で、中央の人物は前髪が金・後頭部が黒に分かれ、服は青71.60

E4 self-attentionの左右遮断を全Blockに入れたseed 42の結果

E4 self-attentionの左右遮断を全Blockに入れたseed 43の結果

E4 self-attentionの左右遮断を全Blockに入れたseed 44の結果

E4 self-attentionの左右遮断をBlock 8〜16に入れたseed 42の結果

E4 self-attentionの左右遮断をBlock 8〜16に入れたseed 43の結果

E4 self-attentionの左右遮断をBlock 8〜16に入れたseed 44の結果

全Blockで遮断すると、3枚とも画面中央に縦の継ぎ目が入って、左半分と右半分がそれぞれ独立した1枚の絵になった。
各半分の中に金髪+赤と黒髪+青の2人組が両方描かれて、合計4人。左右で画風も背景の明るさも違う。
左半分と右半分が互いを参照できないと、それぞれの半分がプロンプト全体を単独で描くらしい。
Block 8〜16だけの遮断では継ぎ目は消えて画風も1枚として連続したが、人数は4人・4人・3人に増えた。seed 44では髪が金と黒に分かれた人物が1人出ている。

E5 トークン単位の書き換え

E3の差し替えはconditioning全体を替えたので、髪色以外も変わっていた。
そこでconditioningのうち blonde(位置25)と black(位置34)のトークンベクトルだけを別プロンプトのものに差し替えた。入れ替えのBに加えて、左を赤髪にしたRも用意した。

記号プロンプト差し替え元の位置
BAの左右の髪色を入れ替えたもの位置25=black、位置34=blonde
RAの blondered にしたもの(The girl on the left has red hair.位置25=red
番号差し替え元差し替える位置範囲
E5-1B25と34全Block・全step
E5-2B25と34Block 12〜15・全step
E5-3B25と34全Block・step 0〜4
E5-4R25全Block・全step
E5-5R25Block 12〜15・全step
E5-6R25全Block・step 0〜4

seed 42、その他はE0と同条件。比較用にRを素のまま生成した1枚も加えた。

A・B・RはT5トークン化すると38トークンで完全に整列していて、位置25はそれぞれ blondeblackred、位置34はそれぞれ blackblondeblack だった。
差し替えはE3と同じ段階(DiT Blockのcross-attentionに届く 1×512×1024 のconditioning)で、指定した行だけを置き換える。
全512位置を指定するとE3の全体差し替えとbit単位で一致した。

番号位置範囲左の髪色右の髪色一番近い基準髪色以外の変化(素A比)
E5-1B25と34全Block・全stepblackblonde素B襟が薄紫のフリル高襟、茶のボタン付き上着、目の色も線の濃さも素B
E5-2B25と34Block 12〜15blackblonde(ボブ)素A(画風・構図・肌)。服は別黒のノースリーブのリブタートルネックで肩と腕が露出、フリル襟なし、右がボブ
E5-3B25と34step 0〜4blackblonde素B素Bとほぼ同じ。右の髪が素Bより少し長い
E5-4R25全Block・全stepredblack(ボブ)素R肌がグレー、赤・黒・白だけの高コントラストな画風、赤と黒のハイネック。素Rとの画素差4.3
E5-5R25Block 12〜15オレンジ寄りの明るい茶black素A(画風・構図・肌)。服は別くすんだピンクと黒のボタン付きハイネック、フリル襟なし、目は茶〜黒。素Rとの画素差39.7
E5-6R25step 0〜4redblack(ボブ)素R素Rとほぼ同じ(画素差5.4)。左の首元に紫と茶の色斑
素Rなしなしなしredblack(ボブ)(基準)肌がグレー、赤・黒・白の高コントラスト、赤と黒のハイネック
番号素A全体左上右上左下右下素B全体
E5-164.364.765.669.058.09.5
E5-257.748.955.577.648.839.5
E5-364.264.365.768.857.912.3
E5-448.348.925.176.542.565.6
E5-518.717.312.332.712.457.7
E5-647.648.725.073.743.266.0
素R47.749.125.776.039.965.9

画素差は素Aとの平均絶対差(RGB、0〜255)で、4象限は画面を縦横に2分割したもの。上段に髪、下段に服が入る。

E5 E5-1。Bの位置25と34を全Block・全stepで差し替えた結果

E5 E5-2。Bの位置25と34をBlock 12〜15で差し替えた結果

E5 E5-3。Bの位置25と34をstep 0〜4で差し替えた結果

E5 E5-4。Rの位置25を全Block・全stepで差し替えた結果

E5 E5-5。Rの位置25をBlock 12〜15で差し替えた結果

E5 E5-6。Rの位置25をstep 0〜4で差し替えた結果

E5 素R。プロンプトRをseed 42で素のまま生成した基準

2トークンだけをBに替えて全Block・全stepで差し替えると、絵全体が素Bになった(素Bとの画素差9.5)。step 0〜4だけでも同じ(12.3)。seed 42では、AとBの絵の違いは512トークンのうちこの2つのベクトルでほぼ決まっていた。
位置を絞っても範囲を全部にすると元の絵Aは残らず、髪色以外の服も襟も線の質感も一緒にBのものに変わった。
Block 12〜15だけに絞ると、髪色は元に従って変わり(Bなら反転、Rならオレンジ寄り)、顔・構図・画風は素Aのまま残った。素Aとの画素差はRで18.7、4象限では髪のある上段が17と12、服のある左下が33。
ただしその服は素AでもBでもRでもない。3本のBlock 12〜15差し替え(E3aのconditioning全体、E5-2、E5-5)はどれもフリル襟が消えてタートルネックかハイネックになった。
Rの位置25を全Blockで差し替えると素Rとほぼ同じ(4.3)で、こちらは髪だけでなく肌がグレーになって画風が赤黒白の高コントラストに変わった。red の1トークンが画面全体の色設計を変えているようだ。
Block 12〜15限定のRでは、髪は鮮やかな赤にはならず、オレンジ寄りの明るい茶どまり。

E6 単独Block・混合比・空間制限

E5でBlockとstepのどちらで区切っても髪色だけを切り出せなかったので、残りの条件を試した。

番号内容本数
E6-a単独BlockRの位置25、Bの位置25と34を、Block 12・13・14・15の1つずつに入れる8
E6-b混合比Rの位置25を全Block・全stepに、元ベクトルとの混合比0.25と0.5で入れる2
E6-c空間制限差し替えたトークンを、指定した画像トークン領域のcross-attentionにだけ効かせる。領域外は元のconditioningで計算した出力を使う4

E6-cの領域は52×76のトークン格子で決める。「左半分」は列0〜37、「左上」は列0〜37かつ行0〜29(E1の空間マップで左の人物の髪が入る範囲)。

番号位置と領域範囲
E6-c1R25を左半分全Block・全step
E6-c2R25を左上全Block・全step
E6-c3R25を左半分Block 12〜15
E6-c4B25を左半分、34を右半分全Block・全step

seed 42、その他はE0と同条件。判定と画素差の出し方はE5と同じで、素R(E5の基準)との全体差も加えた。

空間制限の実装は、領域付きトークンがあるBlock・stepでcross-attentionを2回計算し、差し替えなしのconditioningで出した出力と差し替えありの出力を、画像トークンが領域に入るかどうかで選ぶ形。
領域外のトークンは差し替えなしの出力をそのまま使う。self-attentionとMLPは変更していない。
混合比1.0と0.0はそれぞれ従来の差し替えと素Aにbit単位で一致した。

番号差し替え範囲左の髪色右の髪色一番近い基準髪色以外の変化(素A比)
E6-aR 25Block 12blonde(やや黄土寄り)black素A目が茶褐色に、襟が縦リブのハイネック+ボタン列(両名同型)
E6-aR 25Block 13blondeblack素A目はそのまま。襟がボタン留めのスタンドカラー(両名同型)
E6-aR 25Block 14blonde(オレンジ寄りの黄色)black素A目が茶褐色、襟が縦リブのハイネック+ボタン列、右の人物が左に寄って肩が接触
E6-aR 25Block 15blondeblack素A目はそのまま。襟がボタン留めのスタンドカラー
E6-aB 25,34Block 12blondeblack素A目が黒に、左の服が白〜灰の縦ストライプ+グレーのフリル襟
E6-aB 25,34Block 13blondeblack素Aほぼ同じ
E6-aB 25,34Block 14blonde(くすんだ薄黄)black素A目が黒褐色、左の服が灰紫の縦リブ+ボタン列、右の人物が左に寄り肩が接触
E6-aB 25,34Block 15blondeblack素A目が黒に。服はほぼ同じ
E6-bR 25、混合比0.25全Block・全stepblonde(オレンジ寄りの黄色)black素A目が茶褐色、襟がハイネック+ボタン列、右の人物が左に寄る
E6-bR 25、混合比0.5全Block・全stepオレンジ(blondeとredの間)black構図は素A、服と塗りは素R寄り左が赤・右が黒のハイネック、肌がくすんだ灰褐色、線が太くフラット塗り
E6-c1R 25を左半分全Block・全stepredblack素R全面が別画風。肌がグレー、赤黒白の高コントラスト、右がボブ
E6-c2R 25を左上全Block・全stepredblack素R全面が別画風。左は赤のVネック、右は太いリブのタートルネック、右がボブ、線が荒い
E6-c3R 25を左半分Block 12〜15オレンジ(明るい橙)black素A目が茶褐色、左が薄赤茶・右が黒のハイネック+ボタン列、右の人物が左に寄る
E6-c4B 25を左半分、34を右半分全Block・全stepblackblonde素B全面が別画風。ベタ塗りと太い黒線、茶カーディガン+薄紫フリル襟、右がボブ
番号素A全体左上右上左下右下素B全体素R全体
E6-a R 25 Block 1212.38.711.018.311.263.144.4
E6-a R 25 Block 135.14.52.99.53.765.747.3
E6-a R 25 Block 1414.913.811.023.311.759.841.3
E6-a R 25 Block 154.02.92.07.93.466.047.4
E6-a B 25,34 Block 129.46.19.111.810.565.244.9
E6-a B 25,34 Block 133.21.73.35.62.465.747.2
E6-a B 25,34 Block 1417.012.413.629.412.658.945.2
E6-a B 25,34 Block 156.04.25.58.56.065.345.9
E6-b 混合比0.2514.610.912.123.312.161.641.7
E6-b 混合比0.526.926.515.249.616.157.833.2
E6-c147.248.424.875.340.565.75.6
E6-c239.746.623.060.329.059.725.2
E6-c318.617.412.332.312.457.839.7
E6-c470.167.871.771.669.318.169.7

E6 E6-a。R 25をBlock 12で差し替えた結果

E6 E6-a。R 25をBlock 13で差し替えた結果

E6 E6-a。R 25をBlock 14で差し替えた結果

E6 E6-a。R 25をBlock 15で差し替えた結果

E6 E6-a。B 25,34をBlock 12で差し替えた結果

E6 E6-a。B 25,34をBlock 13で差し替えた結果

E6 E6-a。B 25,34をBlock 14で差し替えた結果

E6 E6-a。B 25,34をBlock 15で差し替えた結果

E6 E6-b。R 25、混合比0.25を全Block・全stepで差し替えた結果

E6 E6-b。R 25、混合比0.5を全Block・全stepで差し替えた結果

E6 E6-c1。R 25を左半分を全Block・全stepで差し替えた結果

E6 E6-c2。R 25を左上を全Block・全stepで差し替えた結果

E6 E6-c3。R 25を左半分をBlock 12〜15で差し替えた結果

E6 E6-c4。B 25を左半分、34を右半分を全Block・全stepで差し替えた結果

単独Block(E6-a)では、4つのどれを差し替えても髪色の配置は反転せず、赤にもならなかった。Block 13と15は画素差3〜6でほぼ素Aのまま、Block 12と14は目の色と襟が変わった。E3aとE5で反転したBlock 12〜15は、4つ全部を差し替えないと反転しない範囲だった。
混合比(E6-b)では色が連続的に変わり、0.25でオレンジ寄りの黄色、0.5で赤みの強いオレンジになって、顔と構図は素Aのままだった。ただし0.5では服の色も肌の塗りも素R寄りに変わっていて、髪だけが変わる段階はなかった。
空間制限(E6-c)では髪色だけを切り出せなかった。位置25を左半分の画像トークンにだけ参照させても、絵全体が素Rになった(素Rとの画素差5.6)。左上だけに絞っても素R側(25.2)。Block 12〜15に限定したものは、領域なしのE5-5と画素差18.6対18.7でほぼ同じ絵になった。
cross-attentionの出力を領域外でそのままにしても、そのBlockのself-attentionとMLP、次のBlock以降を通って全体へ広がっていた。E4で確かめた左右のトークンが互いを参照する動きが、ここでは差し替えの影響を全体へ広げている。
髪色のトークンを差し替えた画像では、素Aで左右非対称だった襟(左が淡いピンクのフリル、右が黒のフリル)が、左右で同じハイネック+ボタン列に毎回置き換わった。E5でも同じで、Block 12〜15を経由する差し替えに共通している。

Block、step、空間、混合比の4つを試して、元の絵を残したまま髪色だけを書き換える条件はこのハーネスの範囲では見つからなかった。
反転が起きるBlock 12〜15を通す差し替えでは服が必ず一緒に変わり、空間で切ってもself-attentionが全体へ広げていた。髪色のトークンがそこで服の情報と一緒に扱われているのだと思う。
切るならcross-attentionだけでなくself-attentionも同時に領域で切ることになるが、E4ではそれをすると絵が左右2枚に分かれた。

E7 残差ストリームを直接書き換える

conditioningを替えると服まで一緒に変わったので、conditioningは一切変更せず、DiTの中の残差を直接書き換える側に移った。
論文は隠れ状態を役割と内容に分けて、内容だけを書き換えていた。画像側ではBlock出力の隠れ状態から左の人物の髪色の方向を取り出して、髪のトークンにだけ足した。

方向の作り方は、同じseedでAとRを回して、各Block・各stepの出力を保存し、髪マスク内のトークンで R − A を取る。
髪マスクは素A(seed 42)の画像から金髪の画素を色でしきい値処理して52×76の格子単位にまとめたもの。
足し方は2通りで、領域内の平均ベクトルを1本足す「平均方向」と、トークンごとの差をそのまま足す「トークン別」。

番号方向足すBlock領域係数足し方
E7-1R − A12〜15左の髪1.0平均方向
E7-2R − A12〜15左上(列0〜37、行0〜29)1.0平均方向
E7-3R − A8〜19左の髪1.0平均方向
E7-4R − A12〜15左の髪2.0平均方向
E7-5R − A12〜15左の髪1.0トークン別
E7-6R − A12〜15右の髪(左の髪領域を左右反転)1.0平均方向
E7-7B − A12〜15左の髪1.0平均方向

E7-6は対照で、左の人物用の方向を右の人物の髪に足したときに右が赤くなるかを確かめる。
seed 42、全step、cond側だけ、その他はE0と同条件。判定と画素差の出し方はE5と同じで、髪マスクの内側と外側でも分けた。

最初の7本で加算が飽和したので、係数0.1と0.3、差ベクトルの絶対値が大きい上位20次元を0にしたもの、その両方を組み合わせたものを追加した。

番号方向Block領域係数次元の除外
E7-8R − A12〜15左の髪0.1なし
E7-9R − A12〜15左の髪0.3なし
E7-10R − A12〜15左の髪1.0上位20次元を0
E7-11R − A12〜15左の髪0.1上位20次元を0
E7-12R − A12〜15左の髪0.3上位20次元を0
E7-13R − A12〜15左の髪0.05上位20次元を0
E7-14R − A12〜15左の髪0.1上位102次元(5%)を0

E7の髪マスクと差ベクトルの大きさ

素Aの左半分から金髪の画素(R>150、G>110、B<140、R−B>40)を取り、16×16画素ごとに25%以上なら髪トークンとした。3952トークン中562個で、行4〜51、列10〜32の範囲にある。襟のフリル上部にも数トークン入っている。

E7 髪マスクを素Aに重ねた図。トークン格子の単位でそのまま拡大している

残差はA・R・BをBlock 8〜19・全30 stepでfp32で保存し直した(1本11.7GB)。
表は髪マスク内の平均ベクトルのノルムと、同じ領域の残差ノルムの平均。

Blockstep差ベクトルのノルム残差ノルム上位20次元がノルムに占める割合
1208097060.0080.80
1215149487900.1700.80
1307697810.0080.70
1315149691250.1640.79
140103106460.0100.71
1415161999350.1630.83
150156108920.0140.84
15151527108000.1410.79

step 15の差ベクトルはstep 0の約19倍あり、残差の14〜17%の大きさだった。
そのノルムの7〜8割が2048次元中の20次元に集中していて、差ベクトルは方向というより少数の次元の大きさで決まっている。この20次元がE2で見つけた巨大値のトークンの次元と同じかどうかは確かめていない。前半の単色実験S2では、色の差が集中する20次元は巨大値の次元とほぼ重ならなかった。
上位20次元を0にするとノルムは0.55〜0.71倍になった。

E7の結果

番号方向Block領域係数次元の除外左の人物右の人物マスクの形が出るか
E7-1R−A 平均12〜15左の髪1.0なし髪のあった場所が白の塊で埋まり、縁と内部に赤のブロック。顔は灰色でぼやけ目鼻が判別できないボブ丈、肌は灰色、黒のハイネック出る
E7-2R−A 平均12〜15左上1.0なし頭部が単色オレンジの矩形で覆われ髪が見えない。服はピンクボブ丈、肌は灰色、黒のハイネック出る
E7-3R−A 平均8〜19左の髪1.0なし髪のあった場所が水色のブロック塊。顔は隠れる。画面全体がピンクのノイズ髪がこげ茶、うねりのある中程度の長さ、顔に赤の斑点出る
E7-4R−A 平均12〜15左の髪2.0なし髪のあった場所が水色のブロック塊。全体がピンクのノイズ髪が灰茶、顔に赤とピンクの斑点出る
E7-5R−A トークン別12〜15左の髪1.0なし髪のあった場所が水色で髪の筋の線だけ残る。顔は水色と赤の縞ボブ丈、肌は灰色、黒のハイネック出る
E7-6R−A 平均12〜15右の髪1.0なし髪は金のまま彩度が上がる。目が茶に、服がサーモン色のハイネック髪のあった場所が白と灰の粒状ノイズ、顔は灰色で目鼻がない出る
E7-7B−A 平均12〜15左の髪1.0なし全面が黒(残差にNaN)同左
E7-8R−A 平均12〜15左の髪0.1なし髪が赤〜橙の単色で面塗り、前髪の筋が消える。顔の輪郭と表情は残り、目が暗赤に。服が暗赤のリブハイネック長い直毛と前髪は保持、襟元が暗赤、髪の右端に緑の帯わずかに出る
E7-9R−A 平均12〜15左の髪0.3なし髪が橙〜赤でブロック状の濃淡。顔は灰白の面で目鼻が消えるボブ丈、肌は灰白出る
E7-10R−A 平均12〜15左の髪1.0上位20次元を0髪がベージュの網点模様に赤の斑。顔は灰色の面ボブ丈、肌は灰白、フリル襟なし出る
E7-11R−A 平均12〜15左の髪0.1上位20次元を0髪がオレンジ〜赤で毛の筋が残る。顔は素Aのまま、目が青緑から茶に。淡いピンクのフリル襟が残り赤みが少し乗る長い黒髪、黒のフリル襟つき上着のまま上端がわずかに角張る
E7-12R−A 平均12〜15左の髪0.3上位20次元を0髪が赤の面塗りで縦縞、顔は目鼻の線だけ残り肌が灰白。服も赤の面ボブ丈、肌が灰白、黒のハイネック出る
E7-13R−A 平均12〜15左の髪0.05上位20次元を0髪がオレンジで毛の筋が残る。顔は素Aのまま、目が茶に。淡いピンクのフリル襟が残る長い黒髪、黒のフリル襟つき上着のまま出ない
E7-14R−A 平均12〜15左の髪0.1上位102次元を0髪は金のまま彩度がわずかに上がる程度。目が黒に。他は素Aとほぼ同じ素Aとほぼ同じ出ない
番号素A全体左上右上左下右下髪内髪外素R全体
E7-142.647.125.355.442.791.534.551.2
E7-252.552.545.464.847.443.354.055.3
E7-347.348.033.156.052.391.040.177.0
E7-449.343.639.951.861.882.543.778.7
E7-542.544.829.551.344.483.835.648.4
E7-647.224.763.246.155.049.546.952.3
E7-7163.8214.0156.1186.299.1156.3165.1129.4
E7-838.348.117.170.617.4104.927.326.7
E7-945.040.623.575.240.976.439.826.0
E7-1035.221.823.050.145.833.935.434.1
E7-1131.240.615.353.215.693.020.931.2
E7-1241.246.018.975.324.693.132.625.1
E7-1322.327.113.434.913.760.116.036.8
E7-148.99.96.413.26.023.06.543.6

画素差は素Aとの平均絶対差(RGB、0〜255)。髪内・髪外は髪マスクを画素へ戻した領域の内外。

E7 E7-1。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-2。R−A 平均の方向をBlock 12〜15の左上の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-3。R−A 平均の方向をBlock 8〜19の左の髪の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-4。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数2.0で足した結果(次元の除外はなし)

E7 E7-5。R−A トークン別の方向をBlock 12〜15の左の髪の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-6。R−A 平均の方向をBlock 12〜15の右の髪の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-7。B−A 平均の方向をBlock 12〜15の左の髪の領域に係数1.0で足した結果(次元の除外はなし)

E7 E7-8。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.1で足した結果(次元の除外はなし)

E7 E7-9。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.3で足した結果(次元の除外はなし)

E7 E7-10。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数1.0で足した結果(次元の除外は上位20次元を0)

E7 E7-11。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.1で足した結果(次元の除外は上位20次元を0)

E7 E7-12。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.3で足した結果(次元の除外は上位20次元を0)

E7 E7-13。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.05で足した結果(上位20次元を0)

E7 E7-14。R−A 平均の方向をBlock 12〜15の左の髪の領域に係数0.1で足した結果(上位102次元を0)

係数1.0の平均方向(E7-1)は飽和した。髪マスクの領域が白い塊になり、輪郭にトークン格子のギザギザがそのまま出た。差ベクトルの大部分を占める20次元を髪マスク内の全トークンに足すと残差が発散した。
領域を矩形にする(E7-2)、Blockを広げる(E7-3)、係数を上げる(E7-4)、トークン別にする(E7-5)は、いずれも壊れ方が変わり、髪として描かれたものは1枚もなかった。
右の髪に足す対照(E7-6)は右の髪領域が粒状ノイズになり、左の髪は金のまま彩度が上がった。左用の方向でも右に足せば右が壊れた。
B − A の方向(E7-7)は残差がNaNになり全面黒になった。左右入れ替えの差ベクトルは、色替えの差ベクトルと同じやり方では使えなかった。

係数を0.1に下げる(E7-8)と髪が赤くなり顔の輪郭が残ったが、髪は面塗りで前髪の筋が消え、服は暗赤のハイネックに変わった。
上位20次元を0にしただけ(E7-10)では飽和が別の形の壊れ方(網点模様)に変わった。
上位20次元を0にしたうえで係数0.1(E7-11)で、左の髪だけ赤系になり、顔・襟・右の人物が残った。髪は毛の筋を保ったオレンジ〜赤で、淡いピンクのフリル襟も右の人物の黒髪と黒い上着も残っている。変わった点は左の目の色が茶になったことと、襟に赤みが少し乗ったこと。画素差は素Aから31.2、髪外で20.9、右上と右下は15前後だった。
同じ条件で係数0.3(E7-12)に上げるとまた飽和した。
係数を0.05(E7-13)に下げると、髪はオレンジで毛の筋が残り、顔・襟・右の人物は素Aのままだった。髪外の画素差は16.0、右上と右下は13〜14で、E7-11より素Aからの変化が少なく、髪の色は赤より手前のオレンジで止まった。マスクの形は出ていない。
除外を上位5%(102次元)に広げて係数0.1(E7-14)にすると、髪は金のままで素Aとの画素差が8.9まで下がった。色を変えていた成分は、上位1%より外で5%より内の次元にもあった。

E7-11とE7-13で、残差ストリームを直接書き換えるとconditioning経由では出なかった、左の髪だけ色が変わって顔・襟・右の人物が残る画像が出た。
E5-5(conditioning側でBlock 12〜15に限定)は素Aとの画素差18.7で服が変わっていたが、E7-13は22.3で服が残っている。差が出た場所が違う。
ただ条件はかなり狭い。係数は0.05〜0.1で、0.3だと壊れ、上位20次元の除外がいり、除外を102次元まで広げると色が変わらなくなる。
方向そのものは少数の巨大値の次元が大部分を占めていて、そこを外した残りの成分がようやく色として働くのだと思うが、その残りが髪色だけを表しているかは確かめていない。目の色が茶に変わるのは4条件とも同じで、髪と目が同じ成分で変わっている可能性がある。

単色の色方向と髪色の属性

単色の実験で分かったことを2人並びに戻す。
色はdenoiseのstep 1までに決まる。赤・青・人物の絵で共通だった。赤と青の残差の差は2048次元中の20次元に85〜94%が集中していて、その20次元は残差の巨大値の次元とは別。
Block 24〜27に差を足すと色が変わり、そのうち1つだけ、step 0〜1だけで足りた。中間Block(8〜23)に足すと残差が発散する。最後のBlock群にstep 0〜1で左半分だけ足すと、1画素幅の境界で左が青、右が赤に分かれた。
同じ方向を人物の髪に足すと、髪の形そのままの青い面になって、人物は描き直された。今回単色差から作った方向は、領域を面として塗り直すように働くらしい。

2人並びで髪色が切り出せなかった理由の一部はたぶんここで、残差の中にある色の方向が「この髪を何色にするか」ではなく「この領域を何色の面にするか」だったから。
今回の抽出法では、物体の形を保って髪色だけ変える安定した方向は見つからなかった。E7-11とE7-13は、係数0.05〜0.1と上位20次元の除外という狭い条件で残った2枚。