SakanaAIのALE−Agentを参考にGoogle Colabで簡易版を実行してみた話

「ALE-Bench」と「ALE-Agent」

最近話題のSakanaAIが発表した「ALE-Bench」と「ALE-Agent」。

これは、AtCoderで実際に出題された最適化コンテストの問題群をベンチマークとして利用し、AIに解かせることでその改善能力を測るという、かなり面白い試みです。

しかもこのALE-Agent。普通のLLM(大規模言語モデル)をベースにしながらも、試行錯誤とフィードバックを通じて少しずつ“賢く”なっていく設計になっています。

そこで、実際にこの仕組みをGoogle Colabで再現できないかと思い、簡易バージョンのALE-Agent風を作ってみました。

ALE-Agentってなに?

ざっくり言うと、ALE-Agentとは
最初は適当な答えを出すけど、スコア(点数)を見て反省しながら、少しずつ解を改善していくAIエージェントです。

まるで将棋のAIが何千回も対局して強くなるように、最適化問題に対して試行錯誤を繰り返し、より良い解を探していくのがこのエージェントの特徴です。

SakanaAIのALE-Benchでは、過去のAtCoder Heuristic Contest(AHC)の問題がベンチマークとして活用されており、AIの改善能力が公平に比較できるようになっています。

Google Colabで簡易版を実行してみる

もちろん本家のALE-Agentのような高度な設計をそのまま再現するのは大変ですが、まずは以下の3ステップで簡易版を作りました。

1 シンプルな最適化問題を定義

今回は、「1〜10の数字を並び替えて、隣り合う数字の差の合計を最大にする」という簡単な問題に設定。

この問題は「最適解が1つに定まらず、試行錯誤でスコアが改善していく構造」があり、ALEの仕組みと似ています。

2 初期解とスコア評価関数

最初はランダムに数字を並べて、スコアを評価。

その後、OpenAIのGPT-4 APIに「今の答えよりも良くなるような並びを提案して」と依頼し、改善案を取得します。

3 スコアが上がったら更新、下がれば無視

提案された解が今より良ければ採用、ダメなら却下という単純なフィードバックループを作成。

これを数多く繰り返すことで、最初の適当な並びから、スコアが高い「より良い解」へと少しずつ進化していきます。

2つのバージョンを用意する

今回はLLMを使わない高速バージョンとLLMを使うハイブリッドバージョンの2つのコードを実行してみました。

1番最後にコードを貼っておきますので、興味のある方は、google colabにて実行してみてください。

(LLMを使うバージョンはopenAIのAPIを使うので課金されます。無料でやりたい方は高速バージョンを使ってください)

実行してみた感想

高速バージョン

最初のスコアは30台後半。そこから少しずつ改善され、最終的には40台後半、場合によっては50台までスコアが伸びることもありました。

実際の出力は以下のようなイメージ:

ハイブリッドバージョン
同じような動きですが、LLMをつかわないでスコアが停滞すると、LLMへGOのスイッチが入り、よりよい解が得られるように、進化していきます。

このように、全体のパフォーマンスが自動で向上していく様がリアルに見えて、なかなか楽しいです。

Darwin Gödel Machineとの違いってなに?

SakanaAIの技術といえば、ALE-Agentと並んで注目されているのが「Darwin Gödel Machine(DGM)」というもう一つの概念。

このDGMは、ALE-Agentとは全く違うレイヤーの自己改善型AIです。

たとえば、今回のALE-Agent風システムは「ちょっとずつ調整して、より良い答えを目指す」タイプ。

一方、DGMは「そもそも自分の考え方が間違ってるかもしれないから、自分の仕組み自体を論理的に修正する」というレベルの話です。

つまり、ALEは「戦術を変える」、DGMは「自分という存在を進化させる」といった違いがあります。

実装難度や動作速度の観点では、現時点ではALE型の方がずっと扱いやすいです。

将来的にはDGM型のAIが真の「汎用人工知能(AGI)」の入口になる可能性もあるかと思います。
(なんと言っても、自分自身のコードを書き換える訳ですから、これはすごい技術です)

まとめ

今回、SakanaAIが提唱するALE-Agentのエッセンスを自分なりにColabで再現してみました。

AIが「自分の出力を反省して少しずつ良くする」というプロセス。
DGMとはまた違ったアプローチを知りました。

もちろん、本家のように高度なツール統合していませんが、
「初期解 → スコア → 改善 → 再評価 → 進化」というループは、通常のシステムにはない面白さがあります。

「とりあえず自分でもやってみたい」という方には、下記にコードを2種類貼っておきますので、google colabにて実行してみてください。

・高速バージョン
LLMを使わないので、API料金はかかりません。
その分、コードもシンプルでLLMを使うからこその面白さには欠けます。
メリットは高速なこと。


import random
import math
import matplotlib.pyplot as plt

# ===== 問題:1〜10の数字を並べて隣接差の合計を最大化 =====
def evaluate(solution):
    return sum(abs(solution[i] - solution[i+1]) for i in range(len(solution)-1))

# ===== 初期解を生成 =====
def generate_initial_solution():
    sol = list(range(1, 11))
    random.shuffle(sol)
    return sol

# ===== 近傍解を生成(2つの数字をランダムに入れ替える) =====
def generate_neighbor_solution(solution):
    neighbor = solution.copy()
    i, j = random.sample(range(len(solution)), 2)
    neighbor[i], neighbor[j] = neighbor[j], neighbor[i]
    return neighbor

# ===== 焼きなまし法の受容判定 =====
def should_accept(current_score, new_score, temperature):
    if new_score > current_score:
        return True
    delta = new_score - current_score
    probability = math.exp(delta / temperature)
    return random.random() < probability

# ===== パラメータ =====
MAX_ITER = 100
INITIAL_TEMP = 10.0
COOLING_RATE = 0.95

# ===== メインループ =====
solution = generate_initial_solution()
score = evaluate(solution)
temperature = INITIAL_TEMP
scores = [score]

print("===== 焼きなまし法による最適化開始 =====")
for i in range(1, MAX_ITER + 1):
    neighbor = generate_neighbor_solution(solution)
    new_score = evaluate(neighbor)

    if should_accept(score, new_score, temperature):
        solution = neighbor
        score = new_score
        print(f"✅ Iter {i:2d} | New Score: {new_score} | Temp: {temperature:.4f} | 採用")
    else:
        print(f"❌ Iter {i:2d} | New Score: {new_score} | Temp: {temperature:.4f} | 却下")

    scores.append(score)
    temperature *= COOLING_RATE

# ===== 結果表示 =====
print("\n===== 最適化終了 =====")
print("最終解:", solution)
print("最終スコア:", score)

# ===== スコア推移のグラフ =====
plt.figure(figsize=(8, 5))
plt.plot(range(len(scores)), scores, marker='o')
plt.title("Score Transition (Simulated Annealing)")
plt.xlabel("Iteration")
plt.ylabel("Score")
plt.grid(True)
plt.show()



・ハイブリットバージョン 上記に加えて、スコアが停滞すると、LLMよGO!のサインが入ります。 結果、スコアが改善される確率が上がります。 注意点はコードを実行するとopenAIのAPI料金がかかります。

 

!pip -q install --upgrade openai matplotlib

import random, math, re, matplotlib.pyplot as plt
from openai import OpenAI

# ---------- OpenAI クライアント ----------
client = OpenAI(api_key="ここにAPIキーを入れる")  # ← APIキーを入力 実際は環境変数に入れたほうが良いが簡易版なので

# ---------- 評価関数(隣接差の合計) ----------
def evaluate(sol):
    return sum(abs(sol[i] - sol[i + 1]) for i in range(len(sol) - 1))

# ---------- 初期解 ----------
def random_solution():
    s = list(range(1, 11))
    random.shuffle(s)
    return s

# ---------- ローカル近傍(2点スワップ) ----------
def local_neighbor(sol):
    nb = sol.copy()
    i, j = random.sample(range(len(sol)), 2)
    nb[i], nb[j] = nb[j], nb[i]
    return nb

# ---------- LLM から改善案を取得 ----------
def llm_propose(sol, score):
    prompt = f"""
目的:1〜10 の数字を並び替え、隣接差の合計を最大化してください。
現在の解: {sol}
現在のスコア: {score}
制約: 1〜10 を重複なく使用し、Python list 形式で 1 行だけ回答してください。
"""
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
    )
    text = resp.choices[0].message.content
    nums = list(map(int, re.findall(r"\d+", text)))
    return nums if len(nums) == 10 and len(set(nums)) == 10 else sol

# ---------- 焼きなまし受容判定 ----------
def accept(cur, new, temp):
    return new > cur or random.random() < math.exp((new - cur) / temp)



# ---------- パラメータ ----------
MAX_ITER       = 30      # 総イテレーション
INITIAL_TEMP   = 10.0
COOLING_RATE   = 0.9
STAGNATE_LIMIT = 4       # 改善なしが連続何回で LLM 発動か

sol   = random_solution()
score = evaluate(sol)
best  = score
temp  = INITIAL_TEMP
no_improve = 0
history = [score]

print("=== 停滞時 LLM 発動ハイブリッド焼きなまし ===")
for it in range(1, MAX_ITER + 1):
    # ---- 改善案生成 ----
    if no_improve >= STAGNATE_LIMIT:
        print(f"\n🧠 Iter {it}: 改善停滞 ({no_improve}) → LLM 発動")
        cand = llm_propose(sol, score)
        no_improve = 0
    else:
        cand = local_neighbor(sol)

    cand_score = evaluate(cand)
    if accept(score, cand_score, temp):
        sol, score = cand, cand_score
        accepted = "✅ 受容"
        no_improve = 0 if score > best else no_improve + 1
        best = max(best, score)
    else:
        accepted = "❌ 却下"
        no_improve += 1

    print(f"{accepted} | Iter {it:2d} | Score {score:2d} | Temp {temp:.3f}")
    history.append(score)
    temp *= COOLING_RATE  # 冷却

# ---------- 結果 ----------
print("\n=== 最終結果 ===")
print("最終解   :", sol)
print("最終スコア:", score)

# ---------- スコア推移グラフ ----------
plt.figure(figsize=(8, 4))
plt.plot(history, marker='o')
plt.title("Score Transition (Stagnation-Triggered LLM Hybrid SA)")
plt.xlabel("Iteration")
plt.ylabel("Score")
plt.grid(True)
plt.show()



いろいろ試してみてください。

sakanaAIさんのブログ記事
https://sakana.ai/ale-bench-jp/

同じく論文
https://arxiv.org/abs/2506.09050

 

****************

最近のデジタルアート作品を掲載!

X 旧ツイッターもやってます。
https://x.com/ison1232

インスタグラムはこちら
https://www.instagram.com/nanahati555/

****************

Follow me!

PAGE TOP