NovelAI系（Naifu/NGUI）とstable-diffusion-webui(AUTOMATIC111)のプロンプト強調構文の違い


### プロンプトの強調構文には違いがある

stable-diffusion-webuiを用いても、NovelAI用モデルの環境を構築できるということだが、
これらの2つの環境はプロンプトの前処理が異なっている。（Naifu/NGUIはNovelAIと同環境の模様）

なので「このプロンプトで、この結果が出た」という情報通りに入力セットを与えても、
環境が互いに違うと結果に違いが出てくる。

それを整理しておくと、手元の環境でうまく再現できないというのが減るかもしれない。

【参考】webuiでの環境構築：[Github Discussions Emulate NovelAI](https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/2017)

### NovelAIのプロンプトの強調構文

[公式ドキュメント Strengthening & Weakening Vectors](https://docs.novelai.net/image/strengthening-weakening.html)より

>The weight of AI focus will be multiplied by 1.05 if you enclose the tags or text you want the AI to **focus on more** with **`{` and `}`**.
The weight of AI focus will be divided by 1.05 if you enclose the tags or text you want the AI to **focus on less** with **`[` and `]`**.
Multiple { or [ inside another will multiply the weight each time, so {{ would result in the enclosed part of the prompt being weighted by 1.1025.

`｛｝`で括れば強調、`[]`で括れば弱化。係数が`1.05`で括弧の数だけ乗算する。

### stable-diffusion-webuiの強調構文

[公式ドキュメント Attention/emphasis](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#attentionemphasis)より

>* `a (word)` - increase attention to `word` by a factor of 1.1
>* `a ((word))` - increase attention to `word` by a factor of 1.21 (= 1.1 * 1.1)
>* `a [word]` - decrease attention to `word` by a factor of 1.1
>* `a (word:1.5)` - increase attention to `word` by a factor of 1.5
>* `a (word:0.25)` - decrease attention to `word` by a factor of 4 (= 1 / 0.25)
>* `a \(word\)` - use literal `()` characters in prompt

`{}`には対応していない。代わりに、`()`が強調で（弱化は同じ）、係数が`1.1`という違いがある。



### NovelAI系（Naifu/NGUI）に丸括弧`()`は効くのか

（Naifu系の）コードを確認したところ、丸括弧への処理は入ってないかった。
また、webUI系の入力にある数値で指定する形式(`(word:1.5)`など)も対応していないようだ。
（丸括弧や数値を入れて結果が変わるのはCLIPのモデルによる解釈なのだろうか。）

```python:"naifu/ldm/modules/encoders/modules.py"
class FrozenCLIPEmbedder(AbstractEncoder):
    def __init__(self, version="./models/openai--clip-vit-large-patch14", device="cuda", max_length=77):  # clip-vit-base-patch32
        # ...
        self.emphasis_factor = 1.05 # strength of () and []
        # ...
        self.token_mults = {}
        tokens_with_parens = [(k, v) for k, v in self.tokenizer.get_vocab().items() if '{' in k or '}' in k or '[' in k or ']' in k]
        fac = self.emphasis_factor
        for text, ident in tokens_with_parens:
            mult = 1.0
            for c in text:
                if c == '[':
                    mult /= fac
                if c == ']':
                    mult *= fac
                if c == '{':
                    mult *= fac
                if c == '}':
                    mult /= fac
            if mult != 1.0:
                self.token_mults[ident] = mult
```

### stable-diffusion-webuiは強調をどう処理しているのか

[上述の説明通り](#stable-diffusion-webuiの強調構文)の処理になっている。

[該当コード](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/17a2076f72562b428052ee3fc8c43d19c03ecd1e/modules/prompt_parser.py)から抜粋

```python:stable-diffusion-webui/modules/prompt_parser.py
def parse_prompt_attention(text):
    # ...
    round_bracket_multiplier = 1.1
    square_bracket_multiplier = 1 / 1.1

    def multiply_range(start_position, multiplier):
        for p in range(start_position, len(res)):
            res[p][1] *= multiplier

    for m in re_attention.finditer(text):
        text = m.group(0)
        weight = m.group(1)

        if text.startswith('\\'):
            res.append([text[1:], 1.0])
        elif text == '(':
            round_brackets.append(len(res))
        elif text == '[':
            square_brackets.append(len(res))
        elif weight is not None and len(round_brackets) > 0:
            multiply_range(round_brackets.pop(), float(weight))
        elif text == ')' and len(round_brackets) > 0:
            multiply_range(round_brackets.pop(), round_bracket_multiplier)
        elif text == ']' and len(square_brackets) > 0:
            multiply_range(square_brackets.pop(), square_bracket_multiplier)
        else:
            res.append([text, 1.0])
```