如何用文本分类器击败 Pac-Man:开源分类器 Jeffy 在 Jevman 基准上跑出 8035 分
Show HN: How to beat Pac-Man with a text classifier
开源文本分类器 Jeffy 在 Pac-Man 基准 Jevman 上以 24 局平均 8035 分登顶,约为当前第一名(3210 分)的 2.5 倍,每次决策仅 3ms、每局成本 $0。
第一个路口。三个选项。分类器以89%的置信度选择向左。
Jevman 是一个吃豆人基准测试,AI 智能体通过结构化的文本描述来玩这个游戏。在每个路口,游戏会告诉你每个方向有什么(豆子距离、幽灵位置、陷阱),然后你选择走哪条路。
排名靠前的参赛者使用 LLM。我使用的是 Jeffy,一个在 CPU 上运行的开源文本分类器。
8,035 平均得分(24 局)
2.5× 对比当前第 1 名(3,210)
3ms 每次决策
$0 每局
分类器看到的内容
在每个路口,游戏会为每个可选方向发送一段文本描述:
Go left: nearest pellet 3 steps away; 5 pellets in the next corridor;
nearest dangerous ghost 12 steps away, moving away; you reach the
next junction in 4 steps, 6 steps before any ghost
Go right: nearest pellet 1 step away; 2 pellets in the next corridor;
DANGER: Blinky in this corridor
Go up: nearest pellet 8 steps away; 0 pellets in the next corridor;
TRAP: Pinky can reach the next junction in 3 steps, you need 5这些描述中大约有 37 种独特的模式。它们全都包含数字。它们全都可以用正则表达式解析。
第 1 步:特征工程
游戏描述是结构化文本,所以我用正则表达式将它们解析为数值特征。
def parse_features(description):
f = {"pellet_dist": 99, "ghost_dist": 99,
"ghost_approaching": 0, "has_trap": 0,
"has_danger": 0, ...}
for part in description.split(";"):
m = re.search(r"nearest pellet (\d+) steps? away", part)
if m: f["pellet_dist"] = int(m.group(1))
m = re.search(r"nearest dangerous ghost (\d+) steps? away", part)
if m: f["ghost_dist"] = int(m.group(1))
if "TRAP:" in part: f["has_trap"] = 1
if "DANGER:" in part: f["has_danger"] = 1
return f第 2 步:生成训练数据
要训练分类器,你需要有标签的样本。我写了一个带有手工调优权重的评分函数,并用它玩了几百局。每个路口都成为一个训练样本:解析出的特征是输入,评分器选择的方向是标签。
def score_direction(description):
f = parse_features(description)
score = 0.0
# Survival: avoid ghosts, traps, danger corridors
if f["has_danger"]: score -= 100
if f["has_trap"]: score -= 80
if f["ghost_dist"] < 99:
threat = max(0, 20 - f["ghost_dist"]) * 3
if f["ghost_approaching"]: threat *= 2.5
score -= threat
# Pellet collection
score += max(0, 12 - f["pellet_dist"])
# Hunt frightened ghosts (200-1600 points each)
if f["has_frightened_ghost"]:
score += 40 + max(0, 15 - f["frightened_ghost_dist"]) * 3
return score几百局游戏会产生数千个带标签的路口,足以用来训练。
第 3 步:用 Jeffy 训练
Jeffy 是一个在 CPU 上运行的文本分类器。它支持基于特征的分类器,而这正是我们这里所需要的:特征已经是数字,不需要嵌入。
# Train a classifier on the collected game data
jeffy train --task pacman --features pellet_dist,ghost_dist,ghost_approaching,has_trap,has_danger,... \
--data training_games.jsonl
# Test accuracy on held-out games
jeffy eval --task pacman --data test_games.jsonl训练耗时不到一秒。训练出的模型是在数值特征之上的逻辑回归头。在游戏运行时,Jeffy 的 predict_features() 端点接收解析后的特征向量,并在 3ms 内返回一个方向。
猎杀幽灵
左:通过概率叠加层在路口导航。右:在能量豆激活后猎杀 Pinky。
当能量豆激活时,受惊的幽灵依次价值 200、400、800 和 1600 分。评分函数从追逐豆子转向猎杀幽灵:+40 基础分加上对附近受惊幽灵的距离奖励。平均每局击杀 9.8 个幽灵。
自己运行
# Install Jeffy
pip install jeffy
# Start the Jeffy endpoint (serves the trained pacman classifier)
python3 jevman-endpoint.py 8787
# Clone the benchmark and run 24 games
git clone https://github.com/opper-ai/jevman-benchmark
cd jevman-benchmark && npm install
npm run submissions -- --model jeffy --endpoint http://localhost:8787每局耗时 2-5 秒。总运行时间:全部 24 局约 90 秒。端点将每个路口的文本解析为特征,将它们输入 Jeffy 分类器,并返回一个方向。
为什么这有效
这些描述是结构化文本。所有信息都已经以数字形式存在:豆子距离、幽灵距离、陷阱条件。你不需要推理。你需要的是解析。
一个 LLM 读到“最近的危险幽灵在 5 步之外,正朝你走来”时,做的事情和正则表达式一样:提取数字 5 和标志“正朝你走来”。只不过它用的是 70 亿个参数而不是一个正则表达式,并且耗时 650ms 而不是 3ms。
Jeffy 分类器学到的决策边界与手工调优的评分函数相同,但它是从样本中学习,而不是手动选择权重。一旦训练完成,它就能泛化到以前未见过的路口配置。而且由于它是对少量数值特征进行逻辑回归,它可以在任何 CPU 上以 3ms 运行。
这就是分类器的甜蜜点:当答案是文本中的模式,而不是对文本进行推理时。带有结构化描述的吃豆人是一个分类问题。PR 已开放,CI 将验证结果。
来源:Hacker News · jeffyclassify.com