呪文一考 ~ stable diffuion と waifu diffuion の認知について ~
【2022年9月25日追記】
画像生成AIで、もっとも大事なのは Prompt つまり呪文。コレがキまらないと話が始まりません。百家争鳴、議論百出、賛否両論、様々な意見があるといえます。それに、その呪文でいい絵が生成できたのも事実でしょう。なので、その中身や個々のワードには踏み込みません。それに、waifuだとワード表みたいなサイトもあるしね。
序論
まずもって、呪文がどのように認知されるかですが、トークンという単位に分割して認知される模様です。このトークンを確認しながら使用するワードを決めるのが重要そうです。
使用ツール
まずもって、呪文をトークンに分割して確認できるようツールを作ります。 stable.diffusion.openvino の環境を持っている人なら、そのまま使えます。
・ prompt.py
from transformers import CLIPTokenizer
import sys
prompt = sys.argv[1]
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
print()
print(tokenizer.tokenize(prompt)[0:tokenizer.model_max_length-2])
print()
print(f"Number of tokens: {len(tokenizer.tokenize(prompt))}")
メモ帳か何かで、上のコードを貼り付けて、prompt.py として保存してください。stable.diffusion.openvino が入っているフォルダだと、その後の利用がラクかも。スタートメニューからAnaconda Prompt (anaconda3) を実行して、保存したフォルダにcdして、
python prompt.py "japanese school girl kawaii"
と、入力してください。
['japanese</w>', 'school</w>', 'girl</w>', 'kawaii</w>']
Number of tokens: 4
と、認知されるトークンと、全体のトークンの数が出てきます。コレを用いて考察していきます。
認知できるワードの数
で、認知できるワードの数ですが、このトークンの数に縛られます。75個より多い分は認知されません。つまり、
python prompt.py "in the Classroom at school, school_uniform, pleated skirt, neat and clean style, long flowing dark black bob Bangs hair, CG, Portrait, A highly detailed illustration, fantasy art, digital painting, perfect face, blush symmetrical beautiful face, shinny aesthetic waifu eyes, small nose perfect pupil,, Kantai_Collection, Idolmaster, Oreimo, hyouka, hiro_suzuhira, JRPG official art, cinematic lighting and intense shadows, rim lighting"
['in</w>', 'the</w>', 'classroom</w>', 'at</w>', 'school</w>', ',</w>', 'school</w>', '_</w>', 'uniform</w>', ',</w>', 'ple', 'ated</w>', 'skirt</w>', ',</w>', 'neat</w>', 'and</w>', 'clean</w>', 'style</w>', ',</w>', 'long</w>', 'flowing</w>', 'dark</w>', 'black</w>', 'bob</w>', 'bangs</w>', 'hair</w>', ',</w>', 'cg</w>', ',</w>', 'portrait</w>', ',</w>', 'a</w>', 'highly</w>', 'detailed</w>', 'illustration</w>', ',</w>', 'fantasy</w>', 'art</w>', ',</w>', 'digital</w>', 'painting</w>', ',</w>', 'perfect</w>', 'face</w>', ',</w>', 'blush</w>', 'sym', 'metrical</w>', 'beautiful</w>', 'face</w>', ',</w>', 'shin', 'ny</w>', 'aesthetic</w>', 'waifu</w>', 'eyes</w>', ',</w>', 'small</w>', 'nose</w>', 'perfect</w>', 'pupil</w>', ',,</w>', 'kan', 'tai</w>', '_</w>', 'collection</w>', ',</w>', 'idol', 'master</w>', ',</w>', 'ore', 'imo</w>', ',</w>', 'hy', 'ou']
Number of tokens: 95
のように、長い呪文を唱えても、最初の少ししかトークンとして認知されてません。コレでは出力が安定しないのも納得です。まずは、このトークンの数を75以下にすることを考えてみましょう。後で例示しますが、トークンの数を意識して出力すると、恐ろしく安定します。
「 」と、「,」と、「_」に違いがあるのか?
結論から言うとありません。正確には「,」「_」は違いがないとおもいます。でも、あえて空白「 」を採用するのは違いがあります。てか、積極的に空白を使いましょう。それは、、、
python prompt.py "japanese_school_girl, kawaii"
['japanese</w>', '_</w>', 'school</w>', '_</w>', 'girl</w>', ',</w>', 'kawaii</w>']
Number of tokens: 7
そう、「,」「_」はトークンとしてカウントされます。しかも入れた数だけ、、コレではたくさんのワードをトークンとして認知させられないのです。それなら空白「 」を採用するのが良しです。でも、コレってホントなの?っていう疑問はもっともです。なので、敢えてある呪文で生成したものを例示します。
例示
python prompt.py "1girl, in_room girl kwaii loli cowboy_shot marble_glowing_skin perfect_symmetrical_face blush_cheeks neat_and_clean_style perfect_pupil starry_anime_eyes small_nose blonde_hair long_hair looking_at_viewer frilled_apron black_maid mob_cap studio_photography strong_rim_cinematic_lighting_intense_shadows pixiv Canon_EOS, SIGMA_Art_Lens_35mm_f1.8 "
['1</w>', 'girl</w>', ',</w>', 'in</w>', '_</w>', 'room</w>', 'girl</w>', 'kwa', 'ii</w>', 'lo', 'li</w>', 'cowboy</w>', '_</w>', 'shot</w>', 'marble</w>', '_</w>', 'glowing</w>', '_</w>', 'skin</w>', 'perfect</w>', '_</w>', 'sym', 'metrical</w>', '_</w>', 'face</w>', 'blush</w>', '_</w>', 'cheeks</w>', 'neat</w>', '_</w>', 'and</w>', '_</w>', 'clean</w>', '_</w>', 'style</w>', 'perfect</w>', '_</w>', 'pupil</w>', 'starry</w>', '_</w>', 'anime</w>', '_</w>', 'eyes</w>', 'small</w>', '_</w>', 'nose</w>', 'blonde</w>', '_</w>', 'hair</w>', 'long</w>', '_</w>', 'hair</w>', 'looking</w>', '_</w>', 'at</w>', '_</w>', 'viewer</w>', 'fr', 'illed</w>', '_</w>', 'apron</w>', 'black</w>', '_</w>', 'maid</w>', 'mob</w>', '_</w>', 'cap</w>', 'studio</w>', '_</w>', 'photography</w>', 'strong</w>', '_</w>', 'rim</w>', '_</w>']
Number of tokens: 101
コレで生成した絵が、下記になります。
ときどき、マジに恋に落としに来てる気満々なのもありますが、まぁまぁ良いのがでます。安定しているじゃん!って思う向きもあると思いますが、多分「strong_rim_cinematic」くらいまでしか認知されていないと思います。次に、
python prompt.py "1girl in room girl kwaii loli cowboy shot marble glowing skin perfect symmetrical face blush cheeks neat and clean style perfect pupil starry anime eyes small nose blonde hair long hair looking at viewer frilled apron black maid mob cap studio photography strong rim cinematic"
['1</w>', 'girl</w>', 'in</w>', 'room</w>', 'girl</w>', 'kwa', 'ii</w>', 'lo', 'li</w>', 'cowboy</w>', 'shot</w>', 'marble</w>', 'glowing</w>', 'skin</w>', 'perfect</w>', 'sym', 'metrical</w>', 'face</w>', 'blush</w>', 'cheeks</w>', 'neat</w>', 'and</w>', 'clean</w>', 'style</w>', 'perfect</w>', 'pupil</w>', 'starry</w>', 'anime</w>', 'eyes</w>', 'small</w>', 'nose</w>', 'blonde</w>', 'hair</w>', 'long</w>', 'hair</w>', 'looking</w>', 'at</w>', 'viewer</w>', 'fr', 'illed</w>', 'apron</w>', 'black</w>', 'maid</w>', 'mob</w>', 'cap</w>', 'studio</w>', 'photography</w>', 'strong</w>', 'rim</w>', 'cinematic</w>']
Number of tokens: 50
コレで生成した絵が、下記になります。
どうっすか?コレぐらい安定してれば、同じと思いませんか?ココらはもう感性になるので、断定はしませんが、同じに思います。しかも、使用トークン数は50。まだまだ属性を盛れるって事です。今やってて、気づいたのですが、「kwaii」になってますね「kawaii」が・・・
トークンにおけるワードの分割問題
ずっと思ってたのが、「fullbody」って効いてるの?ってやつです。で、この辺のワードをトークンにしたのが、下記
python prompt.py "symmetrical fullbody loli lolita"
['sym', 'metrical</w>', 'full', 'body</w>', 'lo', 'li</w>', 'lolita</w>']
Number of tokens: 7
ってカンジで、分割されるワードがあります。fullbody は full と body に分割されます。コレじゃ認知されてないと思います。「loli」→「lolita」のように、分割されないワードを選択するのが吉かもしれません。この辺はもう少し研究していきます。
結論
結論は、トークンの数を75以下にして、分割されないワードを選ぶ。気持ちが悪いかもしれないけど、カンマやアンダーバーは使わない。でも、コレでかなりワードに対して安定しますので、より厳選が可能となります。ドズル・ザビも言ってるじゃないですか、「戦いは数だよ兄貴!」と。なので、当たりを引くためには、狙いの絵をたくさん生成するのが大事なのですよ。
【2022年9月25日追記】
複数のトークンに分割されるワードですが、コレ認知されています。ので、少し上の記事には、取り消し線を引いています。詳しくは下記記事を参照してください。
参考文献
※:ふぎり さんのコードで呪文を解析すると、最後の75トークン目が表示されないので、下のkaraage さんのトークン導出を参考にしました。