Tools
Stable Diffusion のプロンプト 77 トークン制限
CLIP テキストエンコーダの 77 トークン上限と、WebUI 系が長文を扱える仕組み (分割エンコード)、diffusers での対処
要点: Stable Diffusion のプロンプトは CLIP の入力上限 77 トークン (BOS/EOS 込み、実質 75) を超えた分が無言で切り捨てられる。WebUI (A1111/Forge) は 75 トークンごとに分割エンコードして連結するため事実上無制限。素の diffusers は切り捨てるだけ。
数字
- 上限: 77 トークン (先頭 BOS + 末尾 EOS を含む。ユーザが使えるのは 75)
- 1 タグ ≒ 1〜3 トークン。カンマも 1 トークン消費
- 日本語や造語はサブワード分割で膨らみやすい (例:
tsukema→ 3 トークン) - SDXL はエンコーダが 2 本 (CLIP-L + OpenCLIP-G) だが上限はどちらも 77
トークン数の確認
from transformers import CLIPTokenizer
t = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
print(len(t("masterpiece, 1girl, ...").input_ids)) # 77 以下なら安全WebUI が長文を扱える仕組み
75 トークンごとにチャンク分割 → 各チャンクを個別にエンコード → 埋め込みを系列方向に連結して UNet に渡す。チャンク境界をまたぐ概念は弱くなるため、関連タグは近くに置く。
diffusers での対処
- 素の
pipe(prompt=...)は 77 超を警告なしで切り捨て (Token indices sequence length is longer...は tokenizer の警告で、生成は止まらない) - compel で WebUI 同様の分割エンコードができる:
from compel import Compel, ReturnedEmbeddingsType
compel = Compel(tokenizer=[pipe.tokenizer, pipe.tokenizer_2],
text_encoder=[pipe.text_encoder, pipe.text_encoder_2],
returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED,
requires_pooled=[False, True], truncate_long_prompts=False)
emb, pooled = compel(prompt)
nemb, npooled = compel(negative)
# 注意: pad_conditioning_tensors_to_same_length は multi-encoder 構成で
# AttributeError になる (compel 2.x)。空プロンプトのチャンクを cat して自前で長さを揃える
img = pipe(prompt_embeds=emb, pooled_prompt_embeds=pooled,
negative_prompt_embeds=nemb, negative_pooled_prompt_embeds=npooled).images[0]- ネガティブプロンプト側も同じ 77 制限。ポジと長さ (チャンク数) を揃える必要がある
切り捨て時の症状
エラーは出ない。末尾のタグ (スタイル指定を後置していると特にそれ) が効かなくなるだけなので気づきにくい。プロンプトを伸ばしたのに反映されない時はまずトークン数を数える。
最終更新: