LLMのイロレーティング

ChatGPT4などのLLMのイロレーティングを決定してるサイトがある。


https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard

例えば記事を書いている2024/08/20時点のイロレーティングは以下である。

自分はそもそもイロレーティングというものを知らなかったのでこのスコアについて調べて考察してみたことを書きたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/1387c9bb-45f6-add8-a7d9-7df7c6ae97ed.png)

また、Imagen3の評価やらFlux.1の評価でもイロレーティング(Elo Score)を見た。
しかしながらこれらは後述するように相対評価であるため、評価群の異なるImagen3とFlux.1の単純なスコア比較は意味がない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/af7bfe4c-1f6d-de18-c739-1c6b1615767c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/e907c071-512c-05de-7b78-af554bb93480.png)


https://arxiv.org/pdf/2408.07009

https://blackforestlabs.ai/announcing-black-forest-labs/


# イロレーティング
チェスなど対戦型競技(意外なところだとスト6)で強さを測る指標として用いられる。

要するに絶対値を評価する指標がない場合、対戦者同士の勝敗のように相対的指標しかない場合、対戦者に偏りがあると勝率だけでは評価できず、競技者の実力を相対指標から評価するのは中々に難しい。

https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AD%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0

イロレーティングは統計的な理論に基づき任意のプレイヤーが平均的プレイヤーに対して勝利確率、敗北確率をそれぞれ$W,L$とした時、そのプレイヤーのレート(スコア)は

```math
R=400\log_{10}\frac{W}{L}+R_0
```
で表わされる。$W+L=1$より
```math
R=400\log_{10}\frac{W}{1-W}+R_0
```

```math
10^{(R-R_0)/400}=\frac{W}{1-W}
```

```math
W=\frac{10^{(R-R_0)/400}}{10^{(R-R_0)/400}+1}
```

```math
W=\frac{1}{1+10^{-(R-R_0)/400}}
```
プレイヤー$A,B$のレートを$R_A,R_B$とすると$A$が$B$に勝つ勝率は

```math
W_{AB}=\frac{1}{1+10^{-(R_A-R_B)/400}}
```
となる。

この分布はシグモイド関数$\frac{1}{1+e^{-ax}},(a=\log_e10)$の一種であり、レート差を横軸($x=(R_A-R_B)/400$)に取ればロジスティック分布になる。
$R_0$は慣習的に1500が使われるとあるがLLMの比較では$R_0=1000$が使われている。

この任意分布と任意分布の累積分布関数の関係性から、LLMの実力が実行毎にばらつくとしてその性能分布を考えてみたい。

# 分布と分布の累積分布関数
以下表では分布と分布の累積分布関数の例をいくつか示す。

| 分布 | 分布の累積分布関数 |
| ---- | ---- |
| ディラックのデルタ関数 | ヘヴィサイドの階段関数<br>$\frac{1}{2}(1+sign(x))$ |
| ロジスティック分布 | シグモイド関数<br> $\frac{1}{2}(1+tanh(x/2))=\frac{1}{1+exp(-x)}$|
| 正規分布(ガウス分布) | 正規分布の累積分布関数<br>$\frac{1}{2}(1+erf(\frac{\sqrt{2}}{2}x))$ |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/70c4c51a-5525-364b-b791-77467d635197.png)


```python
import numpy as np
import scipy
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 1000)
y1 = (1+np.sign(x))/2
y2 = (1+np.tanh(x))/2
y3 = (1+scipy.special.erf(np.sqrt(2)/2*x))/2

plt.plot(x, y1, label='Heaviside step function')
plt.plot(x, y2, label='sigmoid function')
plt.plot(x, y3, label='Cumulative distribution function\n of the Gaussian distribution' )
plt.legend()
plt.show()
```

# プレイヤーA：ガウス分布、プレイヤーB：ディラックのデルタ関数、判定確率：ヘヴィサイドの階段関数
プレイヤーBのスコアを固定として、プレイヤーAのスコアを中心(R0+i)として大きさ400の正規分布とし、勝敗確率をヘヴィサイドの階段関数とする。
この場合、プレイヤーAの出力にはブレがあり、プレイヤーBの出力は固定で、勝敗判定は二個のスコア差で1でも差があればすぐに判断が付くとする。
この場合の確率分布は$+\sigma$以下になる確率で示され、少し操作すれば正規分布のよく知る「68.27%、95.45%」が現れるのが確認できる。


従ってこの場合の勝利確率は正規分布の累積分布関数になる事が示唆される。


```python:
import numpy as np

point = 250000
R0 = 1000

for i in [0,50,100,150,200,250,300,350,400,800]:
    x = R0 + i + 400*np.random.randn(point)
    x2 = R0
    x3 = np.mean((1+np.sign(x-x2))/2)
    print(i, x3, 1-(1-x3)*2)
-------------------------------------------------------
 0 0.500516 0.0010319999999999219
50 0.549052 0.09810399999999997
100 0.59888 0.19775999999999994
150 0.644776 0.28955200000000003
200 0.690264 0.380528
250 0.73482 0.46964000000000006
300 0.773608 0.5472159999999999
350 0.809372 0.618744
400 0.841576 0.683152
800 0.977312 0.9546239999999999
```


# プレイヤーA：ガウス分布、プレイヤーB：ガウス分布、判定確率：ヘヴィサイドの階段関数
プレイヤーA、プレイヤーBの出力にはブレがあり、勝敗判定は二個のスコア差で1でも差があればすぐに判断が付くとする。
仮に下記のように独立な二個のブレを与えた場合、前項の68.27%、95.45%が現れる。
この時、プレイヤーA、Bの合成ばらつきが$\sigma=\sqrt{\sigma_A^2+\sigma_B^2}$で示される。


```python:
import numpy as np

point = 250000
R0 = 1000

for i in [0,50,100,150,200,250,300,350,400,800]:
    x = R0 + i + np.sqrt(3)/2 * 400*np.random.randn(point)
    x2 = R0 + np.sqrt(1)/2 * 400*np.random.randn(point)
    x3 = np.mean((1+np.sign(x-x2))/2)
    print(i, x3, 1-(1-x3)*2)
-------------------------------------------------------
0 0.500908 0.0018160000000000398
50 0.550256 0.10051199999999993
100 0.598216 0.19643199999999994
150 0.644228 0.28845600000000005
200 0.691408 0.38281600000000005
250 0.73294 0.46588000000000007
300 0.772732 0.545464
350 0.809424 0.6188480000000001
400 0.841256 0.682512
800 0.976784 0.953568
```

# プレイヤーA：ディラックのデルタ関数、プレイヤーB：ディラックのデルタ関数、判定確率：正規分布の累積分布関数
プレイヤーA、プレイヤーBの出力にはばらつきが無く、判定する試合の判定が二者のランク差に依存する関数とする。この場合、以下に定義すると前述の68.27%、95.45%が現れる。

これは試合に運要素が絡む麻雀とかポーカーにおいてはプレイヤーの実力差によってのみで結果が決まるわけではない。また、ゲームによっては風の強弱でジャイアントキリングが起きやすかったり起きにくかったりする。累積分布関数の元分布のばらつきが大きいほどジャイアントキリングが起きやすく、元分布のばらつきが小さい(累積分布関数がヘヴィサイドの階段関数に近い)ほど勝負は実力通りの結果になる。
erf関数内に係数を掛けた時、係数が大きいほどゲーム結果はヘヴィサイドの階段関数に近づく。つまり元分布のばらつきが小さくなる。

これをLLMのイロレーティングにて考えると評価者が誠実、マジョリティ、有能であるほど評価結果はヘヴィサイドの階段関数に近くなり、評価者がひねくれもの、マイノリティ、無能であるほどヘヴィサイドの階段関数から遠くなる。(とはいえそもそもLLMを評価しようという人物は一般人よりは公正さを兼ね備えているのだろうが)


```python
import numpy as np
import scipy

point = 250000
R0 = 1000

for i in [0,50,100,150,200,250,300,350,400,800]:
    x = R0 + i
    x2 = R0
    x3 = np.mean((1+scipy.special.erf(np.sqrt(2)/2*(x-x2)/400))/2)
    print(i, x3, 1-(1-x3)*2)
-------------------------------------------------------
0 0.5 0.0
50 0.5497382248301129 0.09947644966022584
100 0.5987063256829237 0.1974126513658474
150 0.6461697666727237 0.2923395333454475
200 0.6914624612740131 0.38292492254802624
250 0.7340144709512995 0.4680289419025989
300 0.7733726476231317 0.5467452952462635
350 0.8092130471474893 0.6184260942949786
400 0.8413447460685429 0.6826894921370859
800 0.9772498680518208 0.9544997361036416
```

# プレイヤーA：ガウス分布、プレイヤーB：ガウス分布、判定確率：正規分布の累積分布関数
プレイヤーA、プレイヤーBの出力にはブレがあり、判定する試合の判定が二者のランク差に依存する関数とする。この場合、以下に定義すると前述の68.27%、95.45%が現れる。

この時、プレイヤーA、B、ゲームの合成ばらつきが$\sigma=\sqrt{\sigma_A^2+\sigma_B^2+\sigma_{Game}^2}$で示される。

```python
import numpy as np
import scipy

point = 250000
R0 = 1000

for i in [0,50,100,150,200,250,300,350,400,800]:
    x = R0 + i + 1/np.sqrt(3) * 400*np.random.randn(point)
    x2 = R0 + 1/np.sqrt(3) * 400*np.random.randn(point)
    x3 = np.mean((1+scipy.special.erf(np.sqrt(3) * np.sqrt(2)/2*(x-x2)/400))/2)
    print(i, x3, 1-(1-x3)*2)
-------------------------------------------------------
0 0.5006449390426556 0.0012898780853112246
50 0.550331232269876 0.10066246453975203
100 0.5992975337406553 0.19859506748131062
150 0.6461786989043891 0.2923573978087781
200 0.6917405763033204 0.38348115260664084
250 0.7343345911066413 0.4686691822132827
300 0.7729113435337737 0.5458226870675473
350 0.8088380965320013 0.6176761930640027
400 0.8411733299392107 0.6823466598784214
800 0.9773774817649422 0.9547549635298844
```

# ガウス分布かロジスティック分布か
ここまでプレイヤーAの出力、プレイヤーBの出力をガウス分布。
ゲームの判定分布を正規分布の累積分布関数と考えてきたが、wikiのイロレーティングの数式はどうみてもロジスティック分布とシグモイド関数のそれである。
そこで、前述のガウス分布と正規分布の累積分布関数をロジスティック分布とシグモイド関数と置き換えて再度考えてみる。



なお、後で気付いたのだがイロレーティングは理論的には別にガウス分布でも良かった。
むしろ何故ロジスティック分布を採用したのかはよく分からない。
下記より引用
>「レート差を入力として勝率を出力するプログラム」としてみたときに、正規分布を前提とするモデルを採用するのか、ロジスティック分布を前提とするモデルを採用するのかの議論があるようです。**Eloは正規分布を想定したそうですが、チェス協会はロジスティック分布の方がよい、と。**
...
どちらが良いフィットを与えるかは**対戦ゲームの特性**に依存する


https://qiita.com/41semicolon/items/9d9d0cbd940dbd5adcd7




# プレイヤーA：ディラックのデルタ関数、プレイヤーB：ディラックのデルタ関数、判定確率：シグモイド関数
プレイヤーA、プレイヤーBの出力には全くばらつきが無く、判定者の判断にばらつきがあるとする。

以下の様に計算した時、係数$a=\log10$のシグモイド関数を判定確率とした結果がwikiのイロレーティングの勝利確率と合う。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/7e1a3fc3-bbe6-f8e0-b580-0254a9f80380.png)

https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%AD%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0#%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%8B%E3%82%89%E3%81%AE%E5%AE%9F%E5%8A%9B%E3%81%AE%E6%8A%8A%E6%8F%A1

```python
for i in [0,50,100,150,200,250,300,350,400,800]:
    a = np.log(10)
    x = R0+i
    x2 = R0
    x3 = 1/(1+np.exp(-a*(x-x2)/400))
    x4 = 1/(1+10**(-i/400))
    print(i, x3, x4)
-------------------------------------------------------
0 0.5 0.5
50 0.5714631174083815 0.5714631174083814
100 0.6400649998028851 0.6400649998028851
150 0.7033850034718286 0.7033850034718286
200 0.7597469266479578 0.7597469266479578
250 0.8083176725494586 0.8083176725494586
300 0.8490204427886767 0.8490204427886767
350 0.8823382970469413 0.8823382970469413
400 0.9090909090909091 0.9090909090909091
800 0.9900990099009901 0.9900990099009901
```
# プレイヤーA：ロジスティック分布、プレイヤーB：ディラックのデルタ関数、判定確率：ヘヴィサイドの階段関数
プレイヤーAにロジスティック分布のばらつきがあり、プレイヤーBの出力には全くばらつきが無く、判定者の判断も正確であるとする。

以下の様に計算した時、前項の勝利確率と合う。


```python
for i in [0,50,100,150,200,250,300,350,400,800]:
    a = np.log(10)
    x = np.random.logistic(loc=R0+i, scale=400/a, size=point)
    x2 = R0
    x3 = np.mean((1+np.sign(x-x2))/2)
    x4 = 1/(1+10**(-i/400))
    print(i, x3, x4)
-------------------------------------------------------
0 0.49996 0.5
50 0.572964 0.5714631174083814
100 0.640708 0.6400649998028851
150 0.701268 0.7033850034718286
200 0.75928 0.7597469266479578
250 0.808788 0.8083176725494586
300 0.849044 0.8490204427886767
350 0.882408 0.8823382970469413
400 0.909188 0.9090909090909091
800 0.99044 0.9900990099009901
```

# プレイヤーA：ロジスティック分布、プレイヤーB：ロジスティック分布、判定確率：ヘヴィサイドの階段関数
プレイヤーA、プレイヤーBの出力にロジスティック分布のばらつきがあり、判定者の判断は正確であるとする。

以下の様に計算した時、近い値にはなったものの微妙に一致しない。

```python
for i in [0,50,100,150,200,250,300,350,400,800]:
    a = np.log(10)
    b = np.log(2)
    x = np.random.logistic(loc=R0+i, scale=400/a * b, size=point)
    x2 = np.random.logistic(loc=R0, scale=400/a * b, size=point)
    x3 = np.mean((1+np.sign(x-x2))/2)
    x4 = 1/(1+10**(-i/400))
    print(i, x3, x4)
-------------------------------------------------------
0 0.499976 0.5
50 0.568592 0.5714631174083814
100 0.634444 0.6400649998028851
150 0.696836 0.7033850034718286
200 0.753844 0.7597469266479578
250 0.80374 0.8083176725494586
300 0.846528 0.8490204427886767
350 0.880716 0.8823382970469413
400 0.908904 0.9090909090909091
800 0.992524 0.9900990099009901
```

二個のロジスティック分布が同じばらつきなら足し合わせはそれほど難しくはないが、二個の異なるばらつき同士のロジスティック分布を足してそれをロジスティック分布とみなすのは一般に困難である。

```math
\frac{e^{-ax}}{(1+e^{-ax})^2}+\frac{e^{-ax}}{(1+e^{-ax})^2}=\frac{2e^{-ax}}{(1+e^{-ax})^2}
```

複数の分布の足し合わせならロジスティック分布ではなくガンベル分布を定義すれば何とかなるのかもしれないがよく分からない。


https://zenn.dev/nagayu71/articles/11ac4735b03188


# プレイヤーA：ロジスティック分布、プレイヤーB：ロジスティック分布、判定確率：シグモイド関数
プレイヤーA、プレイヤーBの出力にロジスティック分布のばらつきがあり、判定者の判断にもばらつきがあるとする。

少し考えたのだが、3つのロジスティック分布の足し合わせがよく分からなかった。
機械学習のsoftmax関数的な足し合わせになるのではと少し思ったものの思い付きの域を出ない。


# LLMのイロレーティングの考察
二個のLLMの出力を評価者が確認し、LLMモデルの優劣を決め、イロレーティングを確認するには二個のLLMの性能以外に三つのばらつき変数がある。
それは二個のLLMの出力のそれぞれのばらつきと評価者の判定確率である。
(入力promptのばらつきはLLM出力のばらつきに内包される)

理想は評価者の判定確率がヘヴィサイドの階段関数に従っているとよいが、現実には完璧に評価するのは困難でシグモイド関数に寄っていると思われる。イロレーティングを用いているためシグモイド関数に寄っていること自体はまあいいのだが、気になる点として二個のLLMが共に評価者より賢い場合、評価者には評価が困難であると思われる。また、LLMの出力がどちらも等しくゴミの場合もどちらが優れているかという評価は困難だろう。

一方、二個のLLMの出力それぞれにもばらつきがある筈であるが、イロレーティングはそれぞれのLLM個別の出力のばらつきは考慮していないように思う。
また、LLM分布的に端(超高性能か超低性能)にあるもので、ばらつきの大きさが平均LLMの出力のばらつきより小さい堅実なモデルはそれより大きい平均ばらつきを仮定してfitされるためElo Scoreが過剰に高め(低め)になるのではと思った。
たとえ話をするなら目をつぶって互いに相手の出す手を押し合いして、プレイヤーの立っている位置を推定するのだが、2番目が目を開けてみると1番目は腕を曲げてすぐ手前に立っており、目をつぶっていた2番目の想像した立ち位置よりずっと2番目の立ち位置に近かったということだ。

# LLMのイロレーティングの継続性
LLMは相対評価なので、評価モデルに著しく性能の低いモデルを大量に追加されると、既存のモデルのElo Scoreは上昇する。逆に現在のtopはgpt4oだがこれ以上のモデルを沢山追加されるとgpt4oのElo Scoreはどんどん下がっていく。

例えばGPT-3.5-Turbo-1106のElo Scoreは2024/08/21現在でこそ1068だが、おそらくこれが出た当時はElo Scoreは1300～1200あったのだろうと推察される。

# まとめ：
LLMのイロレーティングを調べてみて思ったことを考察した。