Claude が出力にウォーターマークを入れるという話をみて、それにしてもLLMの出力にウォーターマークを入れるのってどうやるんだろうなあと思っていたところだったので、解説記事からリンクされていた論文 Scalable watermarking for identifying large language model outputs を読んでみた。2024年、著者は DeepMind の研究者っぽい。
たぶんみんな手法に興味があると思うのでアルゴリズムの部分だけ書いていく。
LLM ではコンテキストであるそれまでの文章から次の単語を予測していく。抽象的には条件付き確率で次の単語の確率分布が与えられる。普通の出力では、この単語の確率分布に従って次の単語をランダムに選ぶ。ありえそうな単語を選んで、選ばれた単語がコンテキストに追加されてさらに次の単語の確率分布を求める…と続けていく。この「どの単語を選ぶか」という部分の操作を特殊な操作にすることで、普通の確率分布通りにはならないような単語が選ばれやすくなるようにする。
具体的には、単語ごとに m 個の評価値(スコア)を与える g という関数を設定する。提供側は乱数シード値を持っていて、それと直前の数単語、そして単語候補から予測不能な評価値を計算するというような関数を作る(具体的な構成方法は後述)。
単語を選択するときには「2のm乗個の単語候補」を作る。 g は m 個のスコアを与えるので、2のm乗個の単語候補で m 段のトーナメントを組み、各対戦ではgのスコアが高いものが勝ち上がるようにする。で、そうやって最後まで勝ち上がった単語が実際の出力結果になる。これだけだとどの単語を優先するかは完全に g で決まるが、2のm乗個の単語候補は LLM の出した確率分布に従って出力する(しかも重複ありにしている)。重複もありなので基本的にはありうる単語ばかりが出てくるが、それはそれとしてその中から g の評価値が(トーナメント的な意味で)強いものが選ばれる。あり得ないものは選ばれづらいが、確率が高いからといって選ばれやすいわけではない。
入力が SynthID を経て出力されたものかを判定するウォーターマーク検出のアルゴリズムは、入力文のすべての単語の g のスコアを計算して、そのスコアがある閾値を超えているものを SynthID の出力とみなす。ウォーターマークと言っているけれど、出力も確率的なので検出も確率的で、 false positive も false negative も当然存在しうる。
実際の g の計算手法についても見てみるが、基本的には決定的な計算ながらどれがどう優先されるか予測できないように、単にハッシュ関数を使っている。ハッシュ関数の入力として、まず生成側は一種の乱数シードのような固定のデータ(鍵データ)を持つ。これに加えて、直前の数単語も入力に加える。これも一種のコンテキストだが、LLMの長大なコンテキストと違ってものすごく短い値を使っている(論文だと4トークンとか)。これに加えて今予測したい単語候補のデータと、m段のトーナメントのための段数の情報を入力とする。これをハッシュ関数に入れた計算結果を使う。もっとも、論文ではハッシュ値そのものではなくて、それを入力としてベルヌーイ分布 B(0.5) を計算していると言っている。まあつまり上位ビットだけを使ってスコアを 0 か 1 かの1ビットの値にしている。またハッシュ関数の実装は、論文から参照されているコードを見ていると線形合同法の非常に単純な計算で済ませているみたい。
SynthID のアルゴリズムは基本的にはこれだけだ。LLMの作る単語の生起確率に基づきつつ、その中のどれを選ぶかが生起確率によらない一種のランダムで決まる。このランダムには独特の複雑な偏りがあるので、これによって生成された文章には独特のパターンが生まれる(ただし単純な偏りではないので、人間が気づくのは不可能だったりする)。このパターン通りの偏りかどうかを計算してウォーターマーク判定を行う。ということのようだ。
ただし、あくまでも Claude はこの論文を参照しつつ a version of SynthID を採用したと述べているのみなので、細かなパラメータ(直前の何単語を選ぶかとか、ハッシュ関数の実装方式、トーナメントの段数 m など)はこの論文のままではないだろうと思うし、それどころかスコア関数の詳細などが全然違うことも考えられる。
ただ、論文を読んで考えるに、このウォーターマークについて言えることはあるだろうと思う。
まず、先述のとおりウォーターマークといっても false positive も false negative もありうる。短い文章などではそもそも判定不能だったりもするだろう。また、文章の種類や生成パラメータによってはウォーターマークの効果が現れないことがありうる。論文ではエントロピーが低い場合と呼んでいるが、コード、JSONなどの構造的データでは次のトークンが確定的に決まって他にあり得ないことがある。また temperature が非常に低く設定されているシチュエーションでは生起確率の高いものばかりが選ばれてしまい、ウォーターマークのスコア関数の影響が限定的になって検出が難しくなるといったことも考えられる。
一方、それなりの長さの普通の文章みたいなものであれば確かに精度良く検出できるものは実現できそうである。その場合、人手でちょっとした修正を加えるぐらいではなかなかウォーターマークを外したりというのは難しいのではないか、という気はする。
例えば単語を一つだけ変えてみるとする。その単語のスコア値が変化するし、直後の数語のスコア値も変化するが、影響は限定的なものになりそうだ(ウォーターマークのコンテキスト長は非常に短いので)。文章や段落を入れ替えたりといった編集をしても文中のスコア値は変わらないのでウォーターマークへの影響は小さそうだ。でも例えばすべての文末の語調を変えたり(ですます調からだである調に変えるなど)、手間はかかるが意味的には大したことのない細かい単語の修正をやっていくとそれなりにスコアを下げられるかもしれない(が文章によるかもしれない)。ちなみにそういう改変を LLM にやらせると、結局ウォーターマークの影響を受けてしまい、うまくいかなくなるだろうことが想像されるので、あくまでも人手での改変をするしかないだろうとは思う。
でもツールなどによる機械的な改変でウォーターマークを外すことは可能かもという気はする。例えばそこらじゅうに zero-width space を挿入するといった手法により、人間の目には変わらないが機械から見たトークン列が大きく変化するような改変はいろいろありうるので、そういう手法によりウォーターマークを回避できるかもしれない。 zero-width space ぐらいなら、それを省いてから判定するといった手法によりウォーターマーク外しを回避できるかもしれないが、似たような手法はたくさんあり(かつてフィッシング詐欺などでは、見た目がそっくりな字を使ってドメイン名を誤認させるためのテクニックなどがあった)、その全てに対応するのはかなり大変かもしれない。今のところウォーターマーク検出APIはまだ提供されていないので具体的にどうなるかは断言しづらいけど、まあそういうツールとのイタチごっこが繰り広げられるというのはありそう。大変ですね、知らんけど。
ところで、ここまではLLMの出力した文章を他人がチェックしたいみたいな話だったけど、ウォーターマークって蒸留にも影響があるんだろうか。ウォーターマーク付きClaudeの出力から学習したモデルはウォーターマークのパターンまでも学習する、ことはありえてもおかしくないような気もする。そうして蒸留の証拠となったらそれはそれで面白い現象だなとは思う。
まあどうなんでしょうね。やっぱり論文の手法と現実の製品とはまた少し距離があるものなので、実際にどうなるかというのは興味深いところ。でもまあそれはそれとして論文読んで手法とか考え方がわかったのでよかった。