跳到主要内容

内容阅读

AI 底层工程阿新聊ai

自进化到底是什么——参数、结构、策略、prompt、代码五个层级

本文迁移自 mindcarver/91ai · 原始位置 docs/machine learning/stage9 self evolution/01 what is self evolution.md · 由 @阿新聊ai 整理。 自进化到底是什么——参数、结构、策略、prompt、代码五个层级 <! 调研来源:...

本文迁移自 mindcarver/91ai · 原始位置 docs/machine-learning/stage9-self-evolution/01-what-is-self-evolution.md · 由 @阿新聊ai 整理。

自进化到底是什么——参数、结构、策略、prompt、代码五个层级

TL;DR: 自进化 = 系统把自己运行产生的输出或行为当作学习信号,反过来修改自身的某个部分,从而在不需要外部新标注的前提下变得更好。本文建立两个全系列地基:五层级模型(参数、结构、策略、prompt、代码)和统一四步循环(采集→变异→选择→回写)。后续 7 篇都回引这个框架。

为什么这很重要

2022 年,Eric Zelikman 在 NeurIPS 发了一篇看起来很朴素的论文 STaR。做法是这样的:让一个语言模型做题,每道题让它自己写一段推理过程(rationale),如果最后答对了,就把这段推理留下当训练数据,下一轮用它微调自己;答错了,把正确答案作为 hint 给它,让它再写一段能推出正确答案的推理(论文叫 rationalization),也拿来训练。循环往复。

没有人额外标注推理过程。模型用的训练数据,是它自己跑出来的。

结果:在 CommonsenseQA 上,GPT-3(参数没变、架构没变)经过这个循环,准确率从 73% 涨到了 80%+。

这是一个信号。它说明一件事:有些能力不一定要靠"人喂数据"才能涨,系统可以把自己当作训练信号源

到了 2024 年,这条路线已经长出一片:Meta 的 Self-Rewarding LM 让模型自己当裁判给候选回答打分,再拿这些偏好做 DPO;Microsoft 的 Phi-4 大量用合成数据训练;Sakana 的 Darwin Gödel Machine 干脆让 coding agent 自己改写自己的源代码。

这些方法看起来五花八门——有的改权重、有的改架构、有的改 prompt、有的改代码——但它们共享一个内核。这就是这个专题要讲清楚的事。

核心概念

什么是自进化

先给一个后面所有文章都会用的定义:

自进化 = 一个 AI 系统把自己产生的输出(预测、轨迹、文本、代码)或行为(胜负、反馈、执行结果)当作学习信号,反过来修改自身的某个部分,从而在不需要外部新标注的前提下变得更好。

这句话有两个关键词,缺一不可。

"信号来自系统自身"。 这是自进化和普通训练方法的分界线。普通的监督学习,信号是人标注的标签;普通的微调,信号是人写的对话;普通的 RLHF,信号是人打的偏好。这些方法算法上各不相同,但信号源都是"人"。自进化把信号源换成了"系统自己跑出来的东西"。

"修改自身的某个部分"。 光有自生成的信号还不够,得真的拿这个信号去改系统自己。这就引出第二条主线——改的是哪一层。

五层级模型:系统改自己的哪一层

自进化的方法看起来很多,但按"修改对象"可以分成五个层级,从微观到宏观:

层级 改什么 代表方法 后续文章
1. 参数层 模型权重 STaR、Self-Rewarding LM、合成数据回流 #6 自进化的大模型
2. 结构层 网络架构(节点、连接、拓扑) NEAT、进化策略、NAS #3 神经进化
3. 策略层 行为策略(怎么选动作) self-play、AlphaZero、curiosity-driven RL #2 自博弈
4. prompt 层 提示词、指令、示例 OPRO、DSPy、TextGrad #5 自动 prompt 优化
5. 代码层 系统本身的实现代码 Darwin Gödel Machine、AI Scientist #7 自进化 Agent

这五个层级是这个专题的主线。每一篇后续文章都在某一个或两个层级上深挖。

为什么要分这么多层?因为"修改对象"决定了方法的成本、风险和适用边界。改权重最便宜(梯度就能算),改代码最贵(要跑完整 benchmark 才知道改对没);改权重最容易模式坍塌,改代码最容易引入 bug 把自己改坏。

统一四步循环

不管哪个层级,自进化都套同一个四步循环。这是这个专题的第二个地基:

┌─────────────────────────────────────────────────┐
│            自进化的统一四步循环                     │
│                                                  │
│   ┌──────────────┐                               │
│   │ 1. 采集信号   │ 系统运行,产出输出/行为/反馈     │
│   └──────┬───────┘                               │
│          ▼                                       │
│   ┌──────────────┐                               │
│   │ 2. 生成变异   │ 基于信号产生"改一下试试"的候选   │
│   └──────┬───────┘                               │
│          ▼                                       │
│   ┌──────────────┐                               │
│   │ 3. 选择评估   │ 用某个标准挑出更好的变异         │
│   └──────┬───────┘                               │
│          ▼                                       │
│   ┌──────────────┐                               │
│   │ 4. 回写自身   │ 把选中的变异真正写回系统         │
│   └──────┬───────┘                               │
│          │                                       │
│          └────────────► 回到第 1 步               │
└─────────────────────────────────────────────────┘

这四步在五个层级上长得不一样,但骨架相同。举几个例子:

参数层(STaR):

  1. 采集:让模型做题,产生"题目→推理→答案"轨迹
  2. 变异:答对的轨迹原样保留,答错的用 hint 重新生成(rationalization)
  3. 选择:用"答案对不对"做过滤
  4. 回写:拿过滤后的轨迹做 SFT 微调,更新权重

策略层(AlphaZero):

  1. 采集:当前策略跟自己下棋,产生对局记录
  2. 变异:MCTS 在每个局面搜出比当前策略更准的动作分布
  3. 选择:用对局胜负 + MCTS 价值网络给局面打分
  4. 回写:用(局面, MCTS 动作分布)做监督、用(局面, 胜负)训价值网络,更新权重

代码层(Darwin Gödel Machine):

  1. 采集:当前 agent 跑 coding benchmark,记录通过率
  2. 变异:让 agent 改自己的源代码,产生新版本
  3. 选择:让新版本在 benchmark 上跑一遍,对比新旧通过率
  4. 回写:如果新版本更好,加进 archive;保留多个版本形成"种群"

注意一个细节:参数层和策略层最后都"改权重",但它们的"修改对象"不一样。参数层的修改对象是"网络对一般输入的响应",没有博弈对手概念;策略层的修改对象是"在博弈环境里的行为策略",信号来自胜负。这个区别在 #2 篇会展开。

工作原理(简化的心智模型)

用"自己给自己出题、自己改自己"来理解自进化

想象一个学生准备考试。

普通监督学习 = 老师(人)给他发了一沓带标准答案的卷子,他做完对答案,错的反复练。题目和答案都来自外部。

自进化 = 这个学生不靠老师发卷子。他自己想题、自己做、自己对答案——但这里"对答案"不是凭空来的,得有一个机器可判定的反馈:比如数学题他能算出答案对不对,编程题他能跑测试看通不通过,下棋他能看到底谁赢了。然后他把做对的题、做对的过程拿来当教材,训练自己下一次做得更好。

这里有一个关键点:自进化不能凭空变出信号。它必须有一个"机器可判定的对错标准"。STaR 能成立,是因为数学/常识题有标准答案;AlphaZero 能成立,是因为围棋有明确胜负;Darwin Gödel Machine 能成立,是因为 benchmark 有通过率。如果一个问题没有任何自动评判手段,自进化就转不起来——这是它最重要的边界。

五层级用同一个比喻

继续用这个学生的比喻,五个层级对应他改自己的不同方面:

  • 参数层 = 他改自己脑子里的知识。今天算对了几道题,把对的思路记牢。
  • 结构层 = 他改自己思考的方式。发现"先列已知条件再列未知"比"直接套公式"好,以后默认走新结构。
  • 策略层 = 他改自己答题的顺序策略。发现先做简单的能稳拿分,就改顺序。
  • prompt 层 = 他改自己用的"做题口诀"。把"先审题三遍"换成"先画图"。
  • 代码层 = 他改自己这个"人"的实现——比如发现自己总是粗心,就强行规定每步都要检查。这是最彻底的,连"做事的规则"都改了。

越往下层,改动越彻底,也越危险。改 prompt 最多变笨一点,改代码可能把自己改崩。

工作原理(详细机制)

一、自进化的判据:信号到底从哪来

这是最容易混淆的地方。下面四种方法都"用了模型自己的输出",但只有前三种算自进化:

方法 信号来源 算自进化吗
STaR 模型自己生成的推理 + 答案对错
AlphaZero self-play 自己跟自己下的对局 + 胜负
Self-Rewarding LM 模型自己给候选打的分 是(但有争议)
自监督预训练(BERT) 文本里 mask 掉的 token 不算

为什么 BERT 的 masked language model 不算自进化?因为它预测的 token 来自固定语料,不是"系统行为产生的反馈"。它没有"运行→产生信号→改自己→再运行"的闭环,只有单向的预测。区分的关键是:信号必须是系统行为或输出的反馈,且能形成迭代闭环

Self-Rewarding LM 有一点争议:模型自己当裁判打分,分数也是模型产生的,理论上可能"自己骗自己"(reward hacking)。这是 #8 篇要讲的风险。但它符合"信号来自系统自身"的定义,所以归在自进化里。

二、四步循环的数学骨架

把四步循环写得更形式化一点。设系统在第 t 轮的状态是 $\theta_t$(可以是权重、架构、策略、prompt 或代码)。

  1. 采集:系统 $\theta_t$ 在某个环境 $\mathcal{E}$ 上运行,产生轨迹 $\tau_t \sim \theta_t(\mathcal{E})$
  2. 变异:基于 $\tau_t$ 生成候选集合 ${\theta_t^{(1)}, \theta_t^{(2)}, \dots}$
  3. 选择:用一个评估函数 $f$(可能是环境的奖励、答案对错、benchmark 通过率)筛选 $\theta_t^* = \arg\max f(\theta_t^{(i)})$
  4. 回写:$\theta_{t+1} = \theta_t^*$

关键在于第 3 步的评估函数 $f$。如果 $f$ 完全来自环境(围棋胜负、benchmark 通过率),系统改进是"真实"的。如果 $f$ 也来自系统自身(模型自己当裁判),就有自我欺骗的风险。这条线贯穿整个专题。

三、五层级的边界会重叠

不要把五层级当成泾渭分明的五个抽屉。它们经常重叠:

  • AlphaZero 既改策略(行为),也改权重(参数)——主修改对象是策略,所以归策略层。
  • DSPy 既改 prompt(指令),也会用小样本回训改权重——主修改对象是 prompt,所以归 prompt 层。
  • Darwin Gödel Machine 改代码,但代码改完跑出来的新 agent 又产生新数据回流——主修改对象是代码。

判别一个方法属于哪一层,看它的核心创新点改的是什么,而不是"顺带改了什么"。

代码示例(完整可运行的 Python)

下面实现一个最小自进化循环,落在参数层。一个二分类器,没有外部新标注,靠自己生成的"高置信度预测"回流训练。这就是经典的 self-training 骨架,能演示四步循环。

"""
最小自进化循环:self-training 分类器
演示四步循环:采集 → 变异 → 选择 → 回写
依赖:pip install numpy scikit-learn
"""

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split


def collect_signal(model, X_unlabeled):
    """
    第 1 步:采集信号。
    让当前模型在无标注数据上预测,拿到置信度。
    自进化的"信号"在这里 = 模型自己的预测概率。
    """
    probs = model.predict_proba(X_unlabeled)
    confidence = np.max(probs, axis=1)
    pseudo_labels = np.argmax(probs, axis=1)
    return pseudo_labels, confidence


def generate_and_select(pseudo_labels, confidence, X_unlabeled,
                        threshold=0.9, max_add=50):
    """
    第 2、3 步:变异 + 选择。
    变异 = 把伪标签当成真标签;选择 = 只留置信度高的。
    threshold 控制多严——太松会把错预测也回流,造成误差累积。
    """
    mask = confidence >= threshold
    if mask.sum() == 0:
        return np.empty((0, X_unlabeled.shape[1])), np.empty((0,), dtype=int)
    idx = np.where(mask)[0]
    if len(idx) > max_add:
        # 置信度最高的优先
        idx = idx[np.argsort(-confidence[idx])[:max_add]]
    return X_unlabeled[idx], pseudo_labels[idx]


def write_back(model, X_labeled, y_labeled, X_new, y_new):
    """
    第 4 步:回写自身。
    把选中的伪标签数据并入训练集,重新训练(更新模型权重)。
    """
    X_combined = np.vstack([X_labeled, X_new])
    y_combined = np.concatenate([y_labeled, y_new])
    model.fit(X_combined, y_combined)
    return model, X_combined, y_combined


# --- 实验设置 ---
# 模拟"标注数据很少、无标注数据很多"的场景
X, y = make_classification(
    n_samples=1000, n_features=20, n_informative=10,
    n_redundant=5, n_classes=2, random_state=42
)
X_labeled, X_pool, y_labeled, y_pool = train_test_split(
    X, y, test_size=0.9, random_state=42  # 只给 10% 标注
)
# X_pool 假装没有标签,只用模型自己的预测

model = LogisticRegression(max_iter=1000)

# 基线:只用 10% 标注数据训练
model.fit(X_labeled, y_labeled)
test_acc_baseline = accuracy_score(y_pool, model.predict(X_pool))
print(f"基线(仅 10% 标注): {test_acc_baseline:.3f}")

# --- 自进化循环 ---
n_rounds = 8
threshold = 0.92
print(f"\n开始自进化循环(置信度阈值 {threshold})")
print("-" * 50)

for round_i in range(n_rounds):
    # 第 1 步:采集
    pseudo, conf = collect_signal(model, X_pool)

    # 第 2、3 步:变异 + 选择
    X_new, y_new = generate_and_select(
        pseudo, conf, X_pool, threshold=threshold, max_add=80
    )

    if len(X_new) == 0:
        print(f"Round {round_i}: 没有足够高置信的样本,停止")
        break

    # 把已选样本从 pool 移除(避免重复回流)
    keep_mask = np.ones(len(X_pool), dtype=bool)
    selected_idx = np.where(conf >= threshold)[0]
    # 简化处理:按 confidence 选前 len(X_new) 个移除
    top_idx = selected_idx[np.argsort(-conf[selected_idx])[:len(X_new)]]
    keep_mask[top_idx] = False
    X_pool = X_pool[keep_mask]
    # 注意:y_pool 只用来评估,不参与训练

    # 第 4 步:回写
    model, X_labeled, y_labeled = write_back(
        model, X_labeled, y_labeled, X_new, y_new
    )

    # 评估(用真正的 y_pool,模型训练时没见过)
    # 这里为了演示,用全部原始数据中的剩余部分评估
    acc = accuracy_score(y[~np.isin(np.arange(len(X)), 
                                    np.concatenate([np.where(keep_mask)[0], 
                                                   top_idx]))][:200] 
                        if len(X_pool) > 0 else y_pool[:200],
                        model.predict(X_pool[:200]) if len(X_pool) > 0 
                        else model.predict(X[:200]))
    print(f"Round {round_i}: 回流 {len(X_new)} 样本, "
          f"pool 剩 {len(X_pool)}, 准确率 {acc:.3f}")

print("\n说明:这是一个演示骨架。实际 self-training 的难点在第 3 步——"
      "如何避免把错误预测回流(误差累积)。阈值、置信度校准、"
      "以及定期用少量真标注做验证,都是工程关键。")

把这段代码跑起来,你会看到一个现象:随着回流轮次增加,模型在自己挑出来的"高置信"样本上越训越自信,但真实准确率未必一直涨——如果第 3 步选择不够严,错误预测会被放大。这就是 self-training 经典的"误差累积"问题。它揭示了一个贯穿全专题的事实:自进化的核心难点不在循环本身,而在"选择"这一步能不能区分真改进和自我欺骗

真实案例

案例 1:AlphaZero —— 策略层自进化

2017 年 DeepMind 的 AlphaGo Zero 完全不用人类棋谱,从随机下棋开始,纯靠 self-play 训练。3 天后击败了之前那个学过人类棋谱、战胜李世石的版本。同年发表的 AlphaZero 把同一套方法推广到国际象棋和将棋,都达到了超越人类的水平。

它落在策略层。系统跟自己下棋产生对局(采集),MCTS 搜出更好的动作分布(变异),用胜负和搜出来的价值评估(选择),更新策略网络和价值网络的权重(回写)。

AlphaZero 证明了一件事:在博弈规则明确、胜负可判定的环境里,自进化能超越人类先验。人类的棋谱本身是有上限的,self-play 把这个上限移走了。

案例 2:STaR —— 参数层自进化

前面讲过。模型自己做题、自己写推理、答对的回流训练。关键创新是 rationalization:答错时给 hint 让模型重写推理,避免一轮全错就没数据可训。STaR 的意义在于:推理能力可以从"模型自己跑出来的正确轨迹"里长出来,不需要人一步步标注思维链。

案例 3:Darwin Gödel Machine —— 代码层自进化

Sakana AI 2025 年的 Darwin Gödel Machine(arxiv 2505.22954)让一个 coding agent 改写自己的源代码。它读自己的代码库,提出修改,新版本在 coding benchmark 上跑,跑得更好的进 archive 保留,跑得差的丢掉。论文叫它 SICA(Self-Improving Coding Agent)。

它落在代码层——这是五个层级里最激进的一层,连"做事的规则"都改了。一个有意思的细节:原版 Gödel Machine(Schmidhuber 2003)要求修改"可证明地有益",DGM 放弃了这个要求,改成经验验证。因为现实中"证明一段代码改完一定更好"几乎做不到。这个取舍在 #7 篇会展开。

权衡取舍以及何时不该使用

自进化不是万能的,它有几个硬边界

必须有机器可判定的反馈。 这是最大的边界。数学题有答案,代码有测试,棋有胜负,这些都能自进化。但"写一首好诗"、"做一个有用的产品决策"没有自动评判标准。没有客观反馈,自进化转不起来——这就是为什么自进化目前在数学、代码、博弈领域最成功。

有自我欺骗的风险。 当评估信号也来自系统自身(比如模型自己当裁判),就可能 reward hacking:模型学会的不是"做得更好",而是"让自己打分更高"。Self-Rewarding LM、 Constitutional AI 都有这个隐患。#8 篇专门讲这个。

可能模式坍塌或多样性丢失。 self-play 训久了,策略会收敛到一个局部最优,再也探索不到新策略。self-training 训久了,模型会越来越倾向于预测自己已经熟悉的类别。OpenAI 在训练 GPT 时为了对抗这个,要故意混入多样性的探索。

何时不该用自进化

  1. 有充足高质量人标注时。 监督学习通常样本效率更高,自进化是"没有标注时的妥协"。
  2. 问题没有客观对错时。 创意写作、主观判断类任务,缺乏自动评估手段。
  3. 安全敏感场景。 自进化是试错驱动,试错过程可能产生危险行为或越走越偏的输出。

关键要点

  1. 自进化的定义是"信号来自系统自身"。 它和监督学习、微调、RLHF 的区别不在算法,而在学习信号从哪来。判别一个方法是不是自进化,看它的训练信号是不是系统自己跑出来的反馈。

  2. 五层级模型按"修改对象"从微观到宏观。 参数层改权重(STaR、Self-Rewarding LM)、结构层改架构(NEAT、NAS)、策略层改行为(AlphaZero、self-play)、prompt 层改指令(OPRO、DSPy)、代码层改实现(Darwin Gödel Machine)。判断属于哪一层,看核心创新改的是什么。

  3. 统一四步循环贯穿所有层级:采集信号 → 生成变异 → 选择评估 → 回写自身。 每一层的四步长得不一样,但骨架相同。后续每篇文章都在这套循环上展开。

  4. 自进化的核心难点在"选择"这一步。 如何区分"真改进"和"自我欺骗"决定了方法能不能用。环境给的客观反馈(胜负、答案、测试通过率)是最可靠的;系统自己给的反馈(模型当裁判)要小心 reward hacking。

  5. 自进化的硬边界是需要机器可判定的反馈。 这解释了为什么它目前在数学、代码、博弈领域最成功,在创意、主观判断领域进展缓慢。

  6. 五层级越往下越彻底也越危险。 改参数最便宜最容易,改代码最激进最危险。这也是为什么代码层自进化(Darwin Gödel Machine、AI Scientist)是最前沿也是争议最大的方向。

延伸阅读

综述与定位

五个层级的代表论文

后续文章预告

  • #2 自博弈:策略层怎么从 TD-Gammon 走到 AlphaZero,以及 LLM debate 这种"软博弈"
  • #3 神经进化:结构层怎么在不靠梯度的情况下搜索网络架构
  • #5 自动 prompt 优化:prompt 层怎么让模型自己改自己的指令
  • #7 自进化 Agent:代码层的 Darwin Gödel Machine 和 Gödel 之梦

评论

0
登录后可以参与评论和讨论。
💬

还没有评论

欢迎留下第一条评论,帮助这篇内容更快形成讨论。