OpenAIの最新モデル o4
先日,OpenAIの最新のモデルとしてo3 のほかにo4-mini -highが発表された.今回はo3とo4-miniと比較してその性能を試してみたい.o3とo4-miniの時に送ったプロンプトと同じものを使用して差をみてみる.
出力結果は下記.
Weasel Program を可能な限りリッチな表現を用いたコードで書いて.
その過程をアニメーションとして出力できるようにして.
import random
import string
from dataclasses import dataclass
from typing import List, Callable
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ─── 個体クラス ──────────────────────────────────────────────
@dataclass
class Individual:
genes: str
def fitness(self, target: str) -> int:
"""ターゲット文字列との一致数を返す"""
return sum(1 for g, t in zip(self.genes, target) if g == t)
# ─── 進化アルゴリズム本体 ────────────────────────────────────
class WeaselEvolution:
def __init__(
self,
target: str,
pop_size: int = 200,
mutation_rate: float = 0.05,
gene_pool: str = string.ascii_uppercase + ' ',
) -> None:
self.target = target
self.pop_size = pop_size
self.mutation_rate = mutation_rate
self.gene_pool = gene_pool
self.population = self._init_population()
self.generation = 0
def _init_population(self) -> List[Individual]:
"""ランダムな初期個体群を生成"""
return [
Individual(''.join(random.choices(self.gene_pool, k=len(self.target))))
for _ in range(self.pop_size)
]
def _evaluate(self) -> List[int]:
"""全個体のフィットネスを計算し、ソートして返す"""
scored = [(ind, ind.fitness(self.target)) for ind in self.population]
scored.sort(key=lambda x: x[1], reverse=True)
self.population = [ind for ind, _ in scored]
return [score for _, score in scored]
def _select_parent(self, fitnesses: List[int]) -> Individual:
"""ルーレット選択"""
total = sum(fitnesses)
pick = random.uniform(0, total)
current = 0
for ind, fit in zip(self.population, fitnesses):
current += fit
if current >= pick:
return ind
return self.population[0]
def _mutate(self, parent: Individual) -> Individual:
"""突然変異を施した子個体を返す"""
child_genes = list(parent.genes)
for i in range(len(child_genes)):
if random.random() < self.mutation_rate:
child_genes[i] = random.choice(self.gene_pool)
return Individual(''.join(child_genes))
def next_generation(self) -> str:
"""1世代進化させ、最良個体の文字列を返す"""
fitnesses = self._evaluate()
# エリート(最良個体)はそのままキープ
new_pop = [self.population[0]]
# 残りは選択→突然変異で再生成
while len(new_pop) < self.pop_size:
parent = self._select_parent(fitnesses)
new_pop.append(self._mutate(parent))
self.population = new_pop
self.generation += 1
return self.population[0].genes
# ─── アニメーション設定 ────────────────────────────────────
def animate_evolution(
target: str,
pop_size: int = 200,
mutation_rate: float = 0.05,
max_gens: int = 1000,
interval: int = 200,
) -> None:
evo = WeaselEvolution(target, pop_size, mutation_rate)
fig, ax = plt.subplots(figsize=(10, 4))
text = ax.text(
0.5, 0.5, '',
ha='center', va='center',
fontsize=24, family='monospace'
)
ax.axis('off')
def update(frame: int):
best = evo.next_generation()
text.set_text(f'Gen {evo.generation:4d}: {best}')
# 収束したらアニメーション停止
if best == target:
anim.event_source.stop()
return (text,)
anim = FuncAnimation(
fig, update,
frames=max_gens,
blit=True,
interval=interval,
repeat=False
)
plt.show()
# mp4として保存する場合はこちらを有効化:
anim.save('weasel_evolution.mp4', writer='ffmpeg', dpi=200)
# ─── 実行例 ────────────────────────────────────────────────
if __name__ == '__main__':
TARGET = "METHINKS IT IS LIKE A WEASEL"
animate_evolution(
target=TARGET,
pop_size=300,
mutation_rate=0.05,
max_gens=500,
interval=100
)
o4-mini-highは視覚的に優れた推論を得意とするとうたっているが,確かに出力にクセがある.mp4での保存を提案してきた.