feat: JspaceAI 自主智慧架构

基于第一性原理 + Anthropic 2026 J-space 论文实现的具身智慧系统。

核心架构:
- ODE 动力系统 + 并行专家 + J-space 工作空间广播
- 12 个异构专家(视觉/屏幕/听觉/语言/鼠标/跨模态)
- workspace 256 维 + LayerNorm + RK4 积分

自主心智(最重要的能力):
- 好奇心驱动探索(内在奖励 + 世界模型)
- 跨会话状态持久化(海洋不蒸发)
- 自我模型(知道自己会什么不会什么)
- 元学习(自适应学习率 + 策略选择)

具身 Agent(完整神经系统):
- 感知层:摄像头 + 麦克风 + 屏幕 + 键盘 + 鼠标
- 大脑皮层(workspace)+ 小脑(运动控制)+ 中枢神经(门控)
- 海马体(情景记忆)+ 基底神经节(动作选择)
- 执行器:鼠标控制 + 键盘输出 + 音频播放 + 屏幕绘制

多模态支持:
- 原生图像/音频/视频/文本/键盘/鼠标 6 种模态
- 跨平台(macOS/Windows/Linux)

外挂模块系统:
- 可热插拔的外部能力(小模型/知识库/工具)
- 核心心智不依赖外挂,断开后继续工作

守护进程:
- 用户主动 start/stop(不自启)
- 后台静默运行,持续感知学习
- 状态自动保存,跨会话继续

J-lens 可解释性:
- 观测模型内部每个 ODE 子步的想法
- Directed Modulation 验证 workspace 因果作用
- Selectivity 验证(ablate workspace)

小模型蒸馏:
- 接 GPT-2/Qwen 等迁移理解能力
- 蒸馏完成后小模型可断开

验证结果:
- 连续序列:JSpace 胜 Flat 39.7%
- 语言进化:loss 3.95→2.40
- workspace ||w||:v1 0.05 → v2 16.0
- 实时五通道感知 + 具身闭环运行
This commit is contained in:
2026-07-07 09:19:31 +08:00
commit e782ef4db1
30 changed files with 7446 additions and 0 deletions

246
main.py Normal file
View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python3
"""
JspaceAI —— 主程序入口
对比实验:
1. JSpaceModel工作空间 + J-space 广播 + ODE 动力学)
2. FlatBaseline同样参数量的扁平 MLP
任务多模态连续时间序列预测4 种模式切换)
输出:
- 训练/评估 loss 曲线对比
- 注意力热力图(哪个专家在哪个时段被激活)
- 工作空间 ||w|| 演化曲线(输出门控信号)
- 预测 vs 真实序列对比
运行:
python main.py
python main.py --steps 1000 --device cuda
"""
from __future__ import annotations
import argparse
import torch
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from jspaceai import (
JSpaceConfig, JSpaceModel, FlatBaseline,
ContinuousSequenceTask, Trainer,
)
def count_params(model: torch.nn.Module) -> int:
return sum(p.numel() for p in model.parameters() if p.requires_grad)
def run_experiment(n_steps: int, device: str, outdir: Path):
outdir.mkdir(parents=True, exist_ok=True)
torch.manual_seed(42)
np.random.seed(42)
# 任务
task = ContinuousSequenceTask(input_dim=8, seq_len=64, seed=42)
# 模型 1: JSpaceModel
config = JSpaceConfig(
input_dim=8,
workspace_dim=32,
expert_dim=16,
num_experts=5,
num_wells=4,
ode_steps=4,
dt=0.1,
tau_w=0.3,
output_threshold=0.5,
jacobian_sparsity=8,
noise_std=0.01,
)
jspace_model = JSpaceModel(config)
# 模型 2: FlatBaseline参数量对齐
# JSpaceModel 大约的参数量:
# 专家: 5 × (4×16 + 4 + 16×8 + 16×32 + 16×32) ≈ 5 × 1108 = 5540
# 工作空间: (8+32)×32 + 32 + 32×32 + 32 ≈ 2144
# 预测头: 32×32 + 32 + 32×8 + 8 ≈ 1384
# 总计 ≈ 9000
flat_model = FlatBaseline(input_dim=8, hidden_dim=90, num_layers=2)
print("=" * 60)
print("JspaceAI 对比实验")
print("=" * 60)
print(f"任务: 多模态连续序列预测 (input_dim=8, seq_len=64)")
print(f"训练步数: {n_steps}")
print(f"设备: {device}")
print()
print(f"JSpaceModel 参数量: {count_params(jspace_model):,}")
print(f"FlatBaseline 参数量: {count_params(flat_model):,}")
print()
# 训练
print("-" * 60)
print("训练 JSpaceModel...")
trainer_js = Trainer(jspace_model, task, lr=1e-3, device=device)
history_js = trainer_js.train(n_steps=n_steps, batch_size=32, eval_interval=n_steps // 10)
print()
print("-" * 60)
print("训练 FlatBaseline...")
trainer_flat = Trainer(flat_model, task, lr=1e-3, device=device)
history_flat = trainer_flat.train(n_steps=n_steps, batch_size=32, eval_interval=n_steps // 10)
# 评估
print()
print("=" * 60)
print("最终评估")
print("=" * 60)
eval_xs = task.generate_batch(128)
eval_loss_js = trainer_js.evaluate(eval_xs)
eval_loss_flat = trainer_flat.evaluate(eval_xs)
print(f"JSpaceModel eval MSE: {eval_loss_js:.6f}")
print(f"FlatBaseline eval MSE: {eval_loss_flat:.6f}")
winner = "JSpaceModel" if eval_loss_js < eval_loss_flat else "FlatBaseline"
improvement = abs(eval_loss_js - eval_loss_flat) / max(eval_loss_js, eval_loss_flat) * 100
print(f"胜者: {winner} (相对优势 {improvement:.1f}%)")
# 可视化
print()
print("生成可视化...")
# 1. Loss 曲线对比
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
ax = axes[0, 0]
ax.plot(history_js, label='JSpaceModel', alpha=0.7, linewidth=0.8)
ax.plot(history_flat, label='FlatBaseline', alpha=0.7, linewidth=0.8)
# 平滑曲线
if len(history_js) > 20:
smooth_js = np.convolve(history_js, np.ones(20)/20, mode='valid')
smooth_flat = np.convolve(history_flat, np.ones(20)/20, mode='valid')
ax.plot(smooth_js, label='JSpace (smoothed)', linewidth=2)
ax.plot(smooth_flat, label='Flat (smoothed)', linewidth=2)
ax.set_xlabel('Step')
ax.set_ylabel('MSE Loss')
ax.set_title('Training Loss')
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
# 2. 注意力热力图
test_xs = task.generate_batch(1) # 单条序列
alpha = trainer_js.get_attention(test_xs) # (1, T, num_experts)
if alpha is not None:
ax = axes[0, 1]
alpha_np = alpha[0].cpu().numpy() # (T, num_experts)
im = ax.imshow(alpha_np.T, aspect='auto', cmap='hot', interpolation='nearest')
ax.set_xlabel('Time step')
ax.set_ylabel('Expert index')
ax.set_title('J-space Attention (which expert is active)')
plt.colorbar(im, ax=ax)
# 标注时段边界
for i in range(1, 64 // 16):
ax.axvline(x=i * 16, color='cyan', linestyle='--', alpha=0.7)
ax.set_xticks(range(0, 64, 16))
# 3. 工作空间 ||w|| 演化
jspace_model.eval()
with torch.no_grad():
xs = test_xs.to(device)
_, info = jspace_model(xs)
w_norm = info['w_norm'][0].cpu().numpy()
ax = axes[1, 0]
ax.plot(w_norm, label='||w||', linewidth=2)
ax.axhline(y=config.output_threshold, color='r', linestyle='--',
label=f'threshold={config.output_threshold}', alpha=0.7)
ax.set_xlabel('Time step')
ax.set_ylabel('||w||')
ax.set_title('Workspace norm (output gating signal)')
ax.legend()
ax.grid(True, alpha=0.3)
for i in range(1, 64 // 16):
ax.axvline(x=i * 16, color='gray', linestyle='--', alpha=0.5)
# 4. 预测 vs 真实(取第 0 维)
ax = axes[1, 1]
with torch.no_grad():
xs = test_xs.to(device)
preds_js, _ = jspace_model(xs)
preds_flat, _ = flat_model(xs)
true_seq = test_xs[0, 1:, 0].numpy()
pred_js = preds_js[0, :-1, 0].cpu().numpy()
pred_flat = preds_flat[0, :-1, 0].cpu().numpy()
ax.plot(true_seq, label='True', linewidth=2, color='black')
ax.plot(pred_js, label='JSpace', alpha=0.8)
ax.plot(pred_flat, label='Flat', alpha=0.8)
ax.set_xlabel('Time step')
ax.set_ylabel('Value (dim 0)')
ax.set_title('Prediction vs True (dim 0)')
ax.legend()
ax.grid(True, alpha=0.3)
for i in range(1, 64 // 16):
ax.axvline(x=i * 16, color='gray', linestyle='--', alpha=0.3)
plt.tight_layout()
fig_path = outdir / 'experiment.png'
plt.savefig(fig_path, dpi=150, bbox_inches='tight')
print(f"可视化已保存: {fig_path}")
plt.close()
# 保存模型
torch.save({
'jspace_model': jspace_model.state_dict(),
'flat_model': flat_model.state_dict(),
'config': config,
'eval_loss_js': eval_loss_js,
'eval_loss_flat': eval_loss_flat,
}, outdir / 'models.pt')
print()
print("=" * 60)
print("实验完成")
print("=" * 60)
print(f"JSpaceModel MSE: {eval_loss_js:.6f}")
print(f"FlatBaseline MSE: {eval_loss_flat:.6f}")
print(f"胜者: {winner}")
print()
print("关键观察点(看 experiment.png:")
print(" 1. Loss 曲线: JSpace 是否收敛更快/更低?")
print(" 2. 注意力热力图: 不同时段是否激活不同专家?")
print(" 3. ||w|| 曲线: 模式切换时是否有明显尖峰?")
print(" 4. 预测对比: 模式切换处哪个模型更鲁棒?")
def main():
parser = argparse.ArgumentParser(description='JspaceAI 对比实验')
parser.add_argument('--steps', type=int, default=500,
help='训练步数 (default: 500)')
parser.add_argument('--device', type=str, default='cpu',
help='设备 (cpu / cuda / mps)')
parser.add_argument('--outdir', type=str, default='outputs',
help='输出目录')
args = parser.parse_args()
# 自动检测设备
if args.device == 'auto':
if torch.cuda.is_available():
device = 'cuda'
elif torch.backends.mps.is_available():
device = 'mps'
else:
device = 'cpu'
else:
device = args.device
run_experiment(
n_steps=args.steps,
device=device,
outdir=Path(args.outdir),
)
if __name__ == '__main__':
main()