Elo 排名系统¶
Elo 排名系统最初为象棋等双人游戏开发,旨在根据玩家(或模型)在对抗赛中的表现,提供动态的定量技能水平度量。通过在每场比赛后持续更新评级,Elo 系统反映了模型的最新能力,从而实现更准确的比较。
在 OLPS 的背景下,比较不同方法在各种数据集上的性能可能会很具挑战性。考虑到某种方法在一个数据集上表现出色,但在另一个数据集上表现不佳,因此建立一种排名方法以比较不同数据集上的方法变得至关重要。
最初,我们创建了一个简单的线性更新版本的 Elo 排名系统。更新两个竞争模型 \(a\) 和 \(b\) 的评级的核心公式如下:
初始化评级:
允许每种方法(不包括事后方法 BEST 和 BCRP)在相同的数据集和指标上以成对的方式相互竞争:
>>> print(battles)
model_a model_b dataset metric winner
0 Market UCRP NYSE(O) CW model_b
1 Market UCRP NYSE(O) APY model_b
2 Market UCRP NYSE(O) SR model_b
3 Market UP NYSE(O) CW model_b
4 Market UP NYSE(O) APY model_b
... ... ... ... ... ...
13657 CW-OGD GRW CRYPTO APY model_b
13658 CW-OGD GRW CRYPTO SR model_b
13659 CW-OGD WAAS CRYPTO CW model_b
13660 CW-OGD WAAS CRYPTO APY model_b
13661 CW-OGD WAAS CRYPTO SR model_b
[13662 rows x 5 columns]
对于模型 \(a\) 和 \(b\) 之间的每场较量,计算预期得分:
其中 \(R_a\) 和 \(R_b\) 分别是模型 \(a\) 和 \(b\) 的当前评级,而 \(S=400\) 是我们设定的缩放因子。
确定实际得分:
如果模型 \(a\) 胜出: \(S_a = 1\)
如果模型 \(b\) 胜出: \(S_a = 0\)
如果是平局: \(S_a = 0.5\)
根据比赛结果更新评级:
其中 \(K\) 是 Elo 更新因子,\(R'_a\) 和 \(R'_b\) 是战斗后的更新评级。
初步实现的详细代码如下:
>>> def compute_elo(battles, K=4, SCALE=400, BASE=10, INIT_RATING=1000):
>>> rating = defaultdict(lambda: INIT_RATING)
>>> for rd, model_a, model_b, winner in battles[['model_a', 'model_b', 'winner']].itertuples():
>>> ra = rating[model_a]
>>> rb = rating[model_b]
>>> ea = 1 / (1 + BASE ** ((rb - ra) / SCALE))
>>> eb = 1 / (1 + BASE ** ((ra - rb) / SCALE))
>>> if winner == "model_a":
>>> sa = 1
>>> elif winner == "model_b":
>>> sa = 0
>>> elif winner == "tie" or winner == "tie (bothbad)":
>>> sa = 0.5
>>> else:
>>> raise Exception(f"unexpected vote {winner}")
>>> rating[model_a] += K * (sa - ea)
>>> rating[model_b] += K * (1 - sa - eb)
>>> return rating
然而,我们观察到即使使用较小的 Elo 更新因子 (\(K\)),排名系统对战斗顺序也很敏感。为了解决这个问题,我们采用了自助法来增强 Elo 排名系统,使我们能够获得评级的置信区间。关键步骤如下:
我们在战斗数据的自助样本上多次运行 compute_elo 函数(例如,1,000 次)。
这样我们就得到了每个模型的 Elo 评级分布,然后可以用来计算中位数和置信区间等稳健统计数据。
最终的 Elo 排名基于自助样本中的中位数 Elo 评级。
该过程在以下代码中实现:
>>> def get_bootstrap_result(battles, func_compute_elo, num_round):
>>> rows = []
>>> for i in tqdm(range(num_round), desc="bootstrap"):
>>> rows.append(func_compute_elo(battles.sample(frac=1.0, replace=True)))
>>> df = pd.DataFrame(rows)
>>> return df[df.median().sort_values(ascending=False).index]
>>> BOOTSTRAP_ROUNDS = 1000
>>> np.random.seed(config["MANUAL_SEED"])
>>> bootstrap_elo_lu = get_bootstrap_result(battles, compute_elo, BOOTSTRAP_ROUNDS)
>>> bootstrap_lu_median = bootstrap_elo_lu.median().reset_index().set_axis(["model", "Elo rating"], axis=1)
>>> bootstrap_lu_median["Elo rating"] = (bootstrap_lu_median["Elo rating"] + 0.5).astype(int)
这种方法提供了一种比较不同 OLPS 方法的原则。
盈利能力的 Elo 排名结果¶
盈利能力的 Elo 评级自助估计¶
模型 |
Elo 评级 |
排名 |
|---|---|---|
SSPO |
1275 |
1 |
ANTI2 |
1251 |
2 |
PPT |
1208 |
3 |
ANTI1 |
1151 |
4 |
GRW |
1043 |
5 |
CWMR-Stdev |
1037 |
6 |
CWMR-Var |
1030 |
7 |
SP |
1028 |
8 |
UCRP |
1018 |
9 |
ONS |
1015 |
10 |
UP |
1007 |
11 |
RMR |
994 |
12 |
OLMAR-S |
993 |
13 |
PAMR |
976 |
14 |
RPRT |
965 |
15 |
CW-OGD |
951 |
16 |
EG |
941 |
17 |
KTPT |
937 |
18 |
WAAS |
930 |
19 |
AICTR |
925 |
20 |
Market |
842 |
21 |
OLMAR-E |
789 |
22 |
SCRP |
693 |
23 |
风险抵御能力的 Elo 排名结果¶
风险抵御能力的 Elo 评级自助估计¶
模型 |
Elo 评级 |
排名 |
|---|---|---|
UP |
1294 |
1 |
SP |
1284 |
2 |
WAAS |
1282 |
3 |
UCRP |
1280 |
4 |
EG |
1278 |
5 |
Market |
1248 |
6 |
GRW |
1229 |
7 |
CW-OGD |
1161 |
8 |
ANTI1 |
1130 |
9 |
ANTI2 |
1049 |
10 |
ONS |
1009 |
11 |
PAMR |
955 |
12 |
CWMR-Var |
941 |
13 |
CWMR-Stdev |
936 |
14 |
RMR |
844 |
15 |
OLMAR-S |
839 |
16 |
PPT |
815 |
17 |
SCRP |
796 |
18 |
KTPT |
787 |
19 |
SSPO |
783 |
20 |
OLMAR-E |
716 |
21 |
AICTR |
673 |
22 |
RPRT |
670 |
23 |
实用性的 Elo 排名结果¶
盈利能力的 Elo 评级自助估计¶
模型 |
Elo 评级 |
排名 |
|---|---|---|
Market |
1502 |
1 |
UCRP |
1388 |
2 |
EG |
1372 |
3 |
CW-OGD |
1139 |
4 |
SP |
1128 |
5 |
PPT |
1087 |
6 |
UP |
1080 |
7 |
OLMAR-S |
1038 |
8 |
WAAS |
997 |
9 |
GRW |
966 |
10 |
ONS |
961 |
11 |
OLMAR-E |
951 |
12 |
RPRT |
939 |
13 |
RMR |
928 |
14 |
PAMR |
912 |
15 |
CWMR-Var |
886 |
16 |
AICTR |
874 |
17 |
ANTI1 |
862 |
18 |
SCRP |
855 |
19 |
CWMR-Stdev |
844 |
20 |
ANTI2 |
817 |
21 |
SSPO |
772 |
22 |
KTPT |
703 |
23 |