見出し画像

Stable Diffusionモデルの破損チェックと修復について

1. 概要

 以前より、一部のStable Diffusionモデルにおいて「一部のトークンが無視されるバグ」、つまり内部データの破損があるという情報を耳にしていました。その後、同様のモデルが他にも複数存在するとことが判明したので、本記事にてチェックと修復の手順を説明します。ファイル形式は、「.ckpt」と「.safetensors」のどちらにも対応しています。

参考情報

 本記事では手順しか説明しませんので、なるべく下記の記事を参照して内容を確認してください。


2. 修復の手順(1)

 先に、本記事とは別の方法を紹介します。

 AUTOMATIC1111氏のStable Diffusion web UI上の拡張機能で、破損モデルを修復することが可能です。修復のほか、剪定(圧縮)やVAEの同梱もできるstable-diffusion-model-toolkitの利用手順は、下記の記事を参照してください。


3. 破損チェックの手順

 PC上で、AUTOMATIC1111氏のStable Diffusion web UI(以下、web UI)が利用できることを前提とします(最近リリースされたWindows専用版は除外)。利用開始までの手順は、下記の記事にて紹介しています。

実行ファイルの準備

 まず、先に紹介した記事の#3から「fix_postion_ids.py」をダウンロードして、適当なディレクトリに置いてください。私の記事を見てそのまま構築された方は「\aiwork」で構いません。

 次に、同じディレクトリに、下記の内容でバッチファイルを作ってください。ファイル名は短くて打ちやすいものが良いでしょう。私は「chk.bat」にしました。

@echo off
python fix_position_ids.py --model %1 --verbose

web UIの仮想環境に切り替える

 コマンド プロンプトを開いてください。下記の例を参考に、web UIの仮想環境に入ってください。ディレクトリが異なる場合は適宜変更してください。バッチファイルを作っておくのも良いでしょう。

cd \aiwork
stable-diffusion-webui\venv\Scripts\activate

 上記のコマンドを実行後、先頭に「(venv)」の文字が付いていれば大丈夫です。バッチファイルを置いた場所が異なる場合は、ここで移動してください。

チェックの実行

★ディレクトリ名やファイル名に、半角スペースや特殊な文字が含まれていると正しく実行できません。お手数をかけますが一時的に修正してください。

 先ほどのバッチファイルの名前(「chk」等)を入れて、半角スペースを1つ入れて、チェックしたいモデルのファイルをコマンド プロンプトのウインドウにドラッグ&ドロップしてください。下記のような形でファイルのフルパスが入るので、Enterキーを押してください。

(venv) C:\aiwork>chk C:\aiwork\stable-diffusion-webui\models\Stable-diffusion\sd-v1-4-full-ema.ckpt

 Enterを押した時に「このファイルを開く方法を~」の表示が出てしまった場合はそれを無視して、コマンド プロンプトのウインドウでダブルクリックしてから、改めてEnterを押してください。

チェック結果の表示(正常)

 下記は、実際の実行結果です。破損が無い場合はこのような表示になり、見た目もすっきりしています。実際のデータは、一番上の「current data」の部分のみです。値の型がint64であることも、ある意味重要です。

loading ... sd-v1-4-full-ema.ckpt
# current data is:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.Size([1, 77])
torch.int64
# == if changed to torch.int64 ==
<class 'torch.Tensor'>
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
torch.int64
# change to:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.int64
#
tensor([[True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True]])

チェック結果の表示(破損)

 次に、破損の報告が上がっている「Elysium_Anime_V3.safetensors」の場合です。それぞれの配列の意味は上から順に、モデルが持つ値(本例では型がfloat32)、実利用時に使用される値(int64に変換した整数)、本来あるべき値(固定)、使用される値と本来の値の正誤、となっています。また、値がずれている(誤の判定の)位置と、失われた値が末尾に示されています。失われた値の位置にあるトークンは無視され、重複した値の位置にあるトークンは強調されるようです。

loading ... Elysium_Anime_V3.safetensors
# current data is:
tensor([[ 0.0000,  1.0000,  2.0000,  2.9995,  3.9999,  4.9985,  5.9990,  6.9994,
          7.9999,  9.0004,  9.9971, 11.0013, 11.9979, 13.0021, 13.9988, 15.0031,
         15.9998, 16.9965, 18.0007, 19.0049, 19.9942, 20.9984, 22.0025, 23.0068,
         23.9958, 25.0000, 26.0042, 26.9935, 27.9976, 29.0019, 30.0061, 30.9954,
         31.9996, 33.0037, 33.9930, 35.0121, 36.0015, 36.9907, 38.0098, 38.9991,
         39.9884, 41.0074, 41.9967, 42.9861, 44.0051, 44.9944, 46.0137, 47.0028,
         47.9917, 49.0107, 50.0000, 50.9897, 52.0084, 52.9983, 53.9869, 55.0066,
         55.9953, 56.9852, 58.0039, 58.9935, 60.0122, 61.0019, 61.9908, 63.0102,
         63.9991, 64.9880, 66.0075, 67.0262, 67.9860, 69.0047, 70.0242, 70.9832,
         72.0030, 73.0214, 73.9815, 75.0000, 76.0197]])
<class 'torch.Tensor'>
torch.Size([1, 77])
torch.float32
# == if changed to torch.int64 ==
<class 'torch.Tensor'>
tensor([[ 0,  0,  1,  2,  3,  4,  5,  6,  7,  9,  9, 11, 11, 13, 13, 15, 15, 16,
         18, 19, 19, 20, 22, 23, 23, 25, 26, 26, 27, 29, 30, 30, 31, 33, 33, 35,
         36, 36, 38, 38, 39, 41, 41, 42, 44, 44, 46, 47, 47, 49, 50, 50, 52, 52,
         53, 55, 55, 56, 58, 58, 60, 61, 61, 63, 63, 64, 66, 67, 67, 69, 70, 70,
         72, 73, 73, 74, 76]])
torch.int64
# change to:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.int64
#
tensor([[ True, False, False, False, False, False, False, False, False,  True,
         False,  True, False,  True, False,  True, False, False,  True,  True,
         False, False,  True,  True, False,  True,  True, False, False,  True,
          True, False, False,  True, False,  True,  True, False,  True, False,
         False,  True, False, False,  True, False,  True,  True, False,  True,
          True, False,  True, False, False,  True, False, False,  True, False,
          True,  True, False,  True, False, False,  True,  True, False,  True,
          True, False,  True,  True, False, False,  True]])
corrupt token indexes : [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 17, 20, 21, 24, 27, 28, 31, 32, 34, 37, 39, 40, 42, 43, 45, 48, 51, 53, 54, 56, 57, 59, 62, 64, 65, 68, 71, 74, 75]
missing token numbers : [8, 10, 12, 14, 17, 21, 24, 28, 32, 34, 37, 40, 43, 45, 48, 51, 54, 57, 59, 62, 65, 68, 71, 75]

 2番目の「1.0000」を整数に変換すると「0」になるのは不思議な気がしますが、小数を4桁に丸めた際に繰り上がってしまったのかもしれません。


4. 修復の手順

 同じスクリプトを使用して修復する場合は、下記の内容でバッチファイルを作成して(ファイル名は「fix.bat」など)、チェックの時と同じように実行してください。上書きの確認は無いので注意してください。

@echo off
python fix_position_ids.py --model %1 --out %~dp1%~n1_fix%~x1

修復を行う

★ディレクトリ名やファイル名に、半角スペースや特殊な文字が含まれていると正しく実行できません。お手数をかけますが一時的に修正してください。

 実行例です。「Elysium_Anime_V3.safetensors」を入力して、同じディレクトリに「Elysium_Anime_V3_fix.safetensors」を出力しています。

(venv) C:\aiwork>fix C:\aiwork\stable-diffusion-webui\models\Stable-diffusion\Elysium_Anime_V3.safetensors
loading ... Elysium_Anime_V3.safetensors
# change to:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.int64
#
tensor([[ True, False, False, False, False, False, False, False, False,  True,
         False,  True, False,  True, False,  True, False, False,  True,  True,
         False, False,  True,  True, False,  True,  True, False, False,  True,
          True, False, False,  True, False,  True,  True, False,  True, False,
         False,  True, False, False,  True, False,  True,  True, False,  True,
          True, False,  True, False, False,  True, False, False,  True, False,
          True,  True, False,  True, False, False,  True,  True, False,  True,
          True, False,  True,  True, False, False,  True]])
corrupt token indexes : [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 17, 20, 21, 24, 27, 28, 31, 32, 34, 37, 39, 40, 42, 43, 45, 48, 51, 53, 54, 56, 57, 59, 62, 64, 65, 68, 71, 74, 75]
missing token numbers : [8, 10, 12, 14, 17, 21, 24, 28, 32, 34, 37, 40, 43, 45, 48, 51, 54, 57, 59, 62, 65, 68, 71, 75]
Saving...

 出力されたファイルをチェックしてみると、正常な値になったことが分かります。

(venv) C:\aiwork>chk C:\aiwork\stable-diffusion-webui\models\Stable-diffusion\Elysium_Anime_V3_fix.safetensors
loading ... Elysium_Anime_V3_fix.safetensors
# current data is:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.Size([1, 77])
torch.int64
# == if changed to torch.int64 ==
<class 'torch.Tensor'>
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
torch.int64
# change to:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
         36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
         54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
         72, 73, 74, 75, 76]])
<class 'torch.Tensor'>
torch.int64
#
tensor([[True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True, True, True, True, True, True, True, True,
         True, True, True, True, True]])

修復モデルの注意点

 モデルを修復したことでプロンプトの扱いが変化し、破損モデルとは異なる出力が得られることがあります(元の破損具合と与えるプロンプトに依存します)。留意の上でご利用ください。また、破損モデルを破棄する場合は、修復モデルが正しく利用できることを確認してから行ってください。

終了の方法

 ウインドウをおもむろに閉じても問題ないと思いますが、仮想環境から抜ける方法も覚えておいてください。

deactivate

 上記のコマンドを実行するだけです。


5. 各モデルのチェック結果

 下記の記事にて、実際にチェックした結果を記載しています。内容は更新される場合があります。


6. おまけ

 実際に使っているスクリプトです。チェックに使用するバッチファイルを下記の内容に変更すると、チェックしたファイルと同じディレクトリに結果を出力します。certutilの出力が想定と異なる場合はハッシュ値以外のメッセージも含まれてしまうので、findstrの処理(指定した文字が含まれる行を破棄)を修正してください。

@echo off
echo %~n1%~x1 > %~dp1%~n1%~x1.txt
certutil -hashfile %1 SHA256 | findstr /v コマンドは | findstr /v ハッシュ >> %~dp1%~n1%~x1.txt
echo. >> %~dp1%~n1%~x1.txt
python fix_position_ids.py --model %1 --verbose | findstr /v loading >> %~dp1%~n1%~x1.txt
(おまけの追加:チェック結果の概要を表示します。お好みで上記のコードに足してください。)
echo.
echo result of %~n1%~x1
type %~dp1%~n1%~x1.txt  | findstr torch.float | findstr /v dtype
type %~dp1%~n1%~x1.txt  | findstr token

 出力ファイルは、チェックしたファイル名に「.txt」を付加したファイル名で、内容は下記例のように「ファイル名、SHA256、チェック結果」です。

testdata.ckpt 
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
 
# current data is:
tensor([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
(略)
(おまけの追加を足した場合、破損モデルでは下記のような表示が出ます。)
result of testdata.ckpt
torch.float32
corrupt token indexes : [7, 14, 25, 28, 29, 31, 50, 53, 56, 58, 59, 61, 62]
missing token numbers : [7, 14, 25, 29, 31, 50, 53, 56, 59, 62]

★しつこく書きますが、ディレクトリ名やファイル名に、半角スペースや特殊な文字が含まれていると正しく実行できません。お手数をかけますが一時的に修正してください。


7. その他

 私が書いた他の記事は、メニューよりたどってください。

 noteのアカウントはメインの@Mayu_Hiraizumiに紐付けていますが、記事に関することはサブアカウントの@riddi0908までお願いします。

この記事が気に入ったらサポートをしてみませんか?