summaryrefslogtreecommitdiff
path: root/slides/SDIL_project_intro.py
diff options
context:
space:
mode:
Diffstat (limited to 'slides/SDIL_project_intro.py')
-rw-r--r--slides/SDIL_project_intro.py764
1 files changed, 249 insertions, 515 deletions
diff --git a/slides/SDIL_project_intro.py b/slides/SDIL_project_intro.py
index 19e49c5..357f3db 100644
--- a/slides/SDIL_project_intro.py
+++ b/slides/SDIL_project_intro.py
@@ -1,55 +1,34 @@
#!/usr/bin/env python3
-"""Build the context-free SDIL project introduction deck.
-
-All quantitative claims come from tracked project result files. The deck uses
-only completed full-method experiments: the controlled traffic panel, the
-standard ResNet-20/32/56 panel, and the calibrated synthetic BCI confirmation.
-"""
+"""Build a plain, evidence-first English introduction to the SDIL project."""
from pathlib import Path
-from typing import Iterable, Sequence
from PIL import Image
from pptx import Presentation
from pptx.dml.color import RGBColor
-from pptx.enum.shapes import MSO_CONNECTOR, MSO_SHAPE
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.util import Inches, Pt
ROOT = Path(__file__).resolve().parents[1]
-OUT_DIR = ROOT / "slides"
-ASSET_DIR = OUT_DIR / "assets"
-OUT_PATH = OUT_DIR / "SDIL_project_intro.pptx"
+SLIDES = ROOT / "slides"
+ASSETS = SLIDES / "assets"
+OUTPUT = SLIDES / "SDIL_project_intro.pptx"
FIG3 = ROOT / "results/figs/figure3_innovation.png"
+FIG4 = ROOT / "results/figs/figure4_resnet_confirmation.png"
FIG5 = ROOT / "results/figs/figure5_bci_v2.png"
+FIG6 = ROOT / "results/figs/figure6_standard_depth_scaling.png"
-SLIDE_W = 13.333
-SLIDE_H = 7.5
-
-FONT = "Noto Sans CJK SC"
-FONT_LATIN = "DejaVu Sans"
+FONT = "DejaVu Sans"
+BLACK = "111111"
+GRAY = "111111"
+LIGHT_GRAY = "B5B5B5"
+WHITE = "FFFFFF"
-BG = "F7F9FB"
-PAPER = "FFFFFF"
-INK = "17212B"
-MUTED = "5D6975"
-LIGHT = "DDE4EA"
-GRID = "D9E0E6"
-BLUE = "0072B2"
-BLUE_LIGHT = "DCEFF9"
-GREEN = "009E73"
-GREEN_LIGHT = "DDF3EC"
-ORANGE = "E69F00"
-ORANGE_LIGHT = "FBEBC9"
-RED = "D55E00"
-GRAY = "777777"
-DARK_GRAY = "3F4850"
-
-def rgb(hex_color: str) -> RGBColor:
- return RGBColor.from_string(hex_color)
+def rgb(value: str) -> RGBColor:
+ return RGBColor.from_string(value)
def add_text(
@@ -61,27 +40,25 @@ def add_text(
h: float,
*,
size: float = 18,
- color: str = INK,
bold: bool = False,
+ color: str = BLACK,
align=PP_ALIGN.LEFT,
valign=MSO_ANCHOR.TOP,
- margin: float = 0.03,
+ margin: float = 0.02,
font: str = FONT,
- line_spacing: float = 1.0,
):
box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
- tf = box.text_frame
- tf.clear()
- tf.word_wrap = True
- tf.margin_left = Inches(margin)
- tf.margin_right = Inches(margin)
- tf.margin_top = Inches(margin)
- tf.margin_bottom = Inches(margin)
- tf.vertical_anchor = valign
- p = tf.paragraphs[0]
- p.alignment = align
- p.line_spacing = line_spacing
- run = p.add_run()
+ frame = box.text_frame
+ frame.clear()
+ frame.word_wrap = True
+ frame.margin_left = Inches(margin)
+ frame.margin_right = Inches(margin)
+ frame.margin_top = Inches(margin)
+ frame.margin_bottom = Inches(margin)
+ frame.vertical_anchor = valign
+ paragraph = frame.paragraphs[0]
+ paragraph.alignment = align
+ run = paragraph.add_run()
run.text = text
run.font.name = font
run.font.size = Pt(size)
@@ -90,72 +67,26 @@ def add_text(
return box
-def add_rich_text(
- slide,
- runs: Sequence[tuple[str, float, str, bool]],
- x: float,
- y: float,
- w: float,
- h: float,
- *,
- align=PP_ALIGN.LEFT,
- valign=MSO_ANCHOR.TOP,
- margin: float = 0.03,
-):
+def add_bullets(slide, items, x, y, w, h, *, size=18, gap=7, color=BLACK):
box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
- tf = box.text_frame
- tf.clear()
- tf.word_wrap = True
- tf.margin_left = Inches(margin)
- tf.margin_right = Inches(margin)
- tf.margin_top = Inches(margin)
- tf.margin_bottom = Inches(margin)
- tf.vertical_anchor = valign
- p = tf.paragraphs[0]
- p.alignment = align
- for text, size, color, bold in runs:
- run = p.add_run()
- run.text = text
- run.font.name = FONT
- run.font.size = Pt(size)
- run.font.bold = bold
- run.font.color.rgb = rgb(color)
- return box
-
-
-def add_bullets(
- slide,
- items: Iterable[str],
- x: float,
- y: float,
- w: float,
- h: float,
- *,
- size: float = 17,
- color: str = INK,
- bullet_color: str = BLUE,
- gap: float = 7,
-):
- box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
- tf = box.text_frame
- tf.clear()
- tf.word_wrap = True
- tf.margin_left = Inches(0.03)
- tf.margin_right = Inches(0.03)
- tf.margin_top = Inches(0.02)
- tf.margin_bottom = Inches(0.02)
- for idx, item in enumerate(items):
- p = tf.paragraphs[0] if idx == 0 else tf.add_paragraph()
- p.level = 0
- p.space_after = Pt(gap)
- p.line_spacing = 1.05
- p.text = ""
- marker = p.add_run()
- marker.text = "● "
- marker.font.name = FONT
- marker.font.size = Pt(max(size - 4, 10))
- marker.font.color.rgb = rgb(bullet_color)
- body = p.add_run()
+ frame = box.text_frame
+ frame.clear()
+ frame.word_wrap = True
+ frame.margin_left = Inches(0.02)
+ frame.margin_right = Inches(0.02)
+ frame.margin_top = Inches(0.01)
+ frame.margin_bottom = Inches(0.01)
+ for index, item in enumerate(items):
+ paragraph = frame.paragraphs[0] if index == 0 else frame.add_paragraph()
+ paragraph.space_after = Pt(gap)
+ paragraph.line_spacing = 1.05
+ paragraph.text = ""
+ bullet = paragraph.add_run()
+ bullet.text = "• "
+ bullet.font.name = FONT
+ bullet.font.size = Pt(size)
+ bullet.font.color.rgb = rgb(color)
+ body = paragraph.add_run()
body.text = item
body.font.name = FONT
body.font.size = Pt(size)
@@ -163,470 +94,273 @@ def add_bullets(
return box
-def add_rect(
- slide,
- x: float,
- y: float,
- w: float,
- h: float,
- *,
- fill: str = PAPER,
- line: str = LIGHT,
- radius: bool = True,
- line_width: float = 1.0,
-):
- kind = MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE
- shape = slide.shapes.add_shape(kind, Inches(x), Inches(y), Inches(w), Inches(h))
- shape.fill.solid()
- shape.fill.fore_color.rgb = rgb(fill)
- shape.line.color.rgb = rgb(line)
- shape.line.width = Pt(line_width)
- return shape
-
-
-def add_line(
- slide,
- x1: float,
- y1: float,
- x2: float,
- y2: float,
- *,
- color: str = MUTED,
- width: float = 1.5,
- dash=None,
-):
- line = slide.shapes.add_connector(
- MSO_CONNECTOR.STRAIGHT,
- Inches(x1),
- Inches(y1),
- Inches(x2),
- Inches(y2),
- )
- line.line.color.rgb = rgb(color)
- line.line.width = Pt(width)
- if dash is not None:
- line.line.dash_style = dash
- return line
-
-
-def add_arrow(slide, x1, y1, x2, y2, *, color=MUTED, width=2.0):
- line = add_line(slide, x1, y1, x2, y2, color=color, width=width)
- line.line.end_arrowhead = True
- return line
-
-
-def add_circle(slide, cx, cy, r, *, fill=BLUE, line=PAPER, line_width=1.0):
- shape = slide.shapes.add_shape(
- MSO_SHAPE.OVAL,
- Inches(cx - r),
- Inches(cy - r),
- Inches(2 * r),
- Inches(2 * r),
- )
- shape.fill.solid()
- shape.fill.fore_color.rgb = rgb(fill)
- shape.line.color.rgb = rgb(line)
- shape.line.width = Pt(line_width)
- return shape
-
-
def add_title(slide, title: str, number: int, subtitle: str | None = None):
- add_text(slide, title, 0.55, 0.28, 11.9, 0.54, size=26, bold=True)
- add_text(slide, f"{number:02d}", 12.35, 0.34, 0.45, 0.3, size=11, color=MUTED, align=PP_ALIGN.RIGHT)
- add_line(slide, 0.55, 0.91, 12.78, 0.91, color=LIGHT, width=1.0)
+ add_text(slide, title, 0.52, 0.28, 11.95, 0.55, size=27, bold=True)
+ add_text(slide, str(number), 12.35, 0.35, 0.42, 0.25, size=10, color=GRAY, align=PP_ALIGN.RIGHT)
if subtitle:
- add_text(slide, subtitle, 0.58, 1.00, 12.0, 0.35, size=13.5, color=MUTED)
+ add_text(slide, subtitle, 0.55, 0.92, 12.10, 0.36, size=13.5, color=GRAY)
def add_footer(slide, text: str):
- add_text(slide, text, 0.58, 7.18, 12.15, 0.2, size=8.5, color=MUTED, valign=MSO_ANCHOR.MIDDLE)
+ add_text(slide, text, 0.55, 7.17, 12.20, 0.18, size=8.5, color=GRAY)
-def add_badge(slide, text, x, y, w, *, fill=BLUE_LIGHT, color=BLUE, size=13):
- add_rect(slide, x, y, w, 0.43, fill=fill, line=fill, radius=True)
- add_text(slide, text, x + 0.05, y + 0.02, w - 0.1, 0.35, size=size, color=color, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
+def add_takeaway(slide, text: str, y: float = 6.55):
+ add_text(slide, text, 0.70, y, 11.93, 0.40, size=16, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
def prepare_crops():
- ASSET_DIR.mkdir(parents=True, exist_ok=True)
- img = Image.open(FIG5)
- w, h = img.size
- crops = {
- "figure5a_actor_critic.png": (0, 0, w // 2, h // 2),
- "figure5d_outcome.png": (w // 2, h // 2, w, h),
- }
- for name, box in crops.items():
- img.crop(box).save(ASSET_DIR / name)
-
-
-def add_custom_line_chart(
- slide,
- x: float,
- y: float,
- w: float,
- h: float,
- categories: Sequence[str],
- series: Sequence[tuple[str, Sequence[float], str]],
- *,
- ymin: float,
- ymax: float,
- yticks: Sequence[float],
- title: str,
- show_legend: bool = True,
-):
- add_rect(slide, x, y, w, h, fill=PAPER, line=LIGHT, radius=True)
- add_text(slide, title, x + 0.25, y + 0.12, w - 0.5, 0.35, size=15, bold=True)
- left = x + 0.60
- right = x + w - 0.25
- top = y + 0.72
- bottom = y + h - 0.65
-
- def px(i):
- if len(categories) == 1:
- return (left + right) / 2
- return left + i * (right - left) / (len(categories) - 1)
-
- def py(v):
- return bottom - (v - ymin) * (bottom - top) / (ymax - ymin)
-
- for tick in yticks:
- yy = py(tick)
- add_line(slide, left, yy, right, yy, color=GRID, width=0.8)
- add_text(slide, f"{tick:g}", x + 0.08, yy - 0.12, 0.42, 0.24, size=9.5, color=MUTED, align=PP_ALIGN.RIGHT, valign=MSO_ANCHOR.MIDDLE)
-
- add_line(slide, left, top, left, bottom, color=DARK_GRAY, width=1.1)
- add_line(slide, left, bottom, right, bottom, color=DARK_GRAY, width=1.1)
- for i, cat in enumerate(categories):
- xx = px(i)
- add_line(slide, xx, bottom, xx, bottom + 0.07, color=DARK_GRAY, width=1.0)
- add_text(slide, cat, xx - 0.38, bottom + 0.10, 0.76, 0.28, size=9.5, align=PP_ALIGN.CENTER)
-
- for name, values, color in series:
- points = [(px(i), py(v)) for i, v in enumerate(values)]
- for (x1, y1), (x2, y2) in zip(points, points[1:]):
- add_line(slide, x1, y1, x2, y2, color=color, width=2.3)
- for xx, yy in points:
- add_circle(slide, xx, yy, 0.065, fill=color, line=PAPER, line_width=0.8)
-
- if show_legend:
- legend_y = y + h - 0.27
- total = len(series)
- slot = (w - 0.55) / total
- for i, (name, _, color) in enumerate(series):
- lx = x + 0.30 + i * slot
- add_line(slide, lx, legend_y + 0.08, lx + 0.26, legend_y + 0.08, color=color, width=2.3)
- add_circle(slide, lx + 0.13, legend_y + 0.08, 0.04, fill=color, line=color)
- add_text(slide, name, lx + 0.32, legend_y - 0.04, slot - 0.34, 0.25, size=9.2, color=INK, valign=MSO_ANCHOR.MIDDLE)
+ ASSETS.mkdir(parents=True, exist_ok=True)
+
+ image5 = Image.open(FIG5)
+ w5, h5 = image5.size
+ image5.crop((0, 0, w5 // 2, h5 // 2)).save(ASSETS / "figure5a_actor_critic.png")
+ image5.crop((w5 // 2, h5 // 2, w5, h5)).save(ASSETS / "figure5d_outcome.png")
+
+ image6 = Image.open(FIG6)
+ w6, h6 = image6.size
+ image6.crop((0, 0, w6, h6 // 2)).save(ASSETS / "figure6_top_row.png")
def build_deck():
prepare_crops()
- prs = Presentation()
- prs.slide_width = Inches(SLIDE_W)
- prs.slide_height = Inches(SLIDE_H)
- blank = prs.slide_layouts[6]
+
+ presentation = Presentation()
+ presentation.slide_width = Inches(13.333)
+ presentation.slide_height = Inches(7.5)
+ blank = presentation.slide_layouts[6]
def new_slide():
- slide = prs.slides.add_slide(blank)
- background = slide.background
- background.fill.solid()
- background.fill.fore_color.rgb = rgb(BG)
+ slide = presentation.slides.add_slide(blank)
+ slide.background.fill.solid()
+ slide.background.fill.fore_color.rgb = rgb(WHITE)
return slide
- # Slide 1: title and one-minute summary.
+ # 1. Title.
slide = new_slide()
- add_text(slide, "SDIL", 0.62, 0.60, 3.0, 0.72, size=38, color=BLUE, bold=True)
- add_text(slide, "让局部学习使用意外的反馈", 0.62, 1.28, 7.1, 0.70, size=29, bold=True)
+ add_text(slide, "Somato-Dendritic Innovation Learning", 0.70, 1.02, 11.95, 0.75, size=34, bold=True)
+ add_text(slide, "Local credit assignment from soma-unpredicted dendritic signals", 0.72, 1.96, 11.75, 0.52, size=22)
add_text(
slide,
- "Somato-Dendritic Innovation Learning\n受 Francioni et al.(Harnett lab)Nature 2026 启发",
- 0.65,
- 2.10,
- 6.5,
- 0.88,
+ "Inspired by the neuron-specific somato-dendritic residuals reported by Francioni et al., Nature 2026",
+ 0.72,
+ 2.70,
+ 11.50,
+ 0.55,
size=16,
- color=MUTED,
- line_spacing=1.08,
+ color=GRAY,
+ )
+ add_text(
+ slide,
+ "Question: should a local learner use apical activity itself, or the component that is unexpected from the same neuron's somatic state?",
+ 0.72,
+ 4.35,
+ 11.60,
+ 1.00,
+ size=23,
+ bold=True,
)
+ add_text(slide, "Project overview · August 2026", 0.72, 6.80, 4.0, 0.30, size=11, color=GRAY)
- add_rect(slide, 7.55, 0.72, 5.05, 2.42, fill=PAPER, line=LIGHT, radius=True, line_width=1.2)
- add_text(slide, "核心操作", 7.90, 1.02, 1.4, 0.35, size=14, color=MUTED, bold=True)
- add_text(slide, "teaching signal", 7.92, 1.48, 1.75, 0.34, size=16, color=DARK_GRAY, align=PP_ALIGN.RIGHT)
- add_text(slide, "=", 9.76, 1.45, 0.35, 0.40, size=22, color=MUTED, bold=True, align=PP_ALIGN.CENTER)
- add_text(slide, "apical feedback", 10.12, 1.31, 2.05, 0.33, size=15.5, color=INK, bold=True, align=PP_ALIGN.CENTER)
- add_text(slide, "− expected from soma", 10.12, 1.75, 2.05, 0.33, size=15.5, color=BLUE, bold=True, align=PP_ALIGN.CENTER)
- add_text(slide, "每个神经元各自减去正常 soma–dendrite 耦合", 7.90, 2.43, 4.25, 0.38, size=13.5, color=MUTED, align=PP_ALIGN.CENTER)
-
- cards = [
- ("BP-free", "学习器内无反向计算图", BLUE_LIGHT, BLUE),
- ("92.76%", "ResNet-56 + 4× predictable traffic", GREEN_LIGHT, GREEN),
- ("1.33×", "BP MAC estimate", ORANGE_LIGHT, RED),
- ]
- for i, (big, small, fill, color) in enumerate(cards):
- x = 0.65 + i * 4.12
- add_rect(slide, x, 4.17, 3.75, 1.47, fill=fill, line=fill, radius=True)
- add_text(slide, big, x + 0.18, 4.36, 3.39, 0.48, size=25, color=color, bold=True, align=PP_ALIGN.CENTER)
- add_text(slide, small, x + 0.18, 4.91, 3.39, 0.38, size=12.3, color=INK, align=PP_ALIGN.CENTER)
+ # 2. Biological observation and learning rule.
+ slide = new_slide()
+ add_title(slide, "From the biological observation to a local learning rule", 2)
add_text(
slide,
- "一句话:可扩展的局部信用路径负责把方向送到各层;SDIL 负责从混合反馈中提取可用于学习的部分。",
- 0.82,
- 6.30,
- 11.7,
- 0.50,
+ "The Harnett-lab study fits each neuron's normal soma–dendrite relationship. The residual is decorrelated from soma and carries outcome and signed task information.",
+ 0.72,
+ 1.18,
+ 11.75,
+ 0.76,
size=18,
- color=INK,
+ )
+ add_text(slide, "aₗ = sₗ + nₗ", 1.08, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
+ add_text(slide, "mixed apical activity", 1.08, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
+ add_text(slide, "âₗ = Pₗ(hₗ)", 5.10, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
+ add_text(slide, "neutral-period prediction", 5.10, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
+ add_text(slide, "rₗ = aₗ − âₗ", 9.13, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
+ add_text(slide, "somato-dendritic innovation", 9.13, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
+ add_text(
+ slide,
+ "ΔWₗ = η (rₗ ⊙ φ′(uₗ)) hₗ₋₁ᵀ",
+ 2.63,
+ 3.66,
+ 8.10,
+ 0.68,
+ size=29,
bold=True,
align=PP_ALIGN.CENTER,
)
- add_footer(slide, "Project introduction · ICLR 2027 work in progress · headline values use completed full experiments")
-
- # Slide 2: scientific origin and problem.
- slide = new_slide()
- add_title(slide, "问题:反馈通道不只传教学信号", 2, "把全部 apical activity 当作 error,会把正常状态与上下文一起写入权重。")
-
- add_rect(slide, 0.62, 1.52, 5.70, 4.88, fill=PAPER, line=LIGHT)
- add_text(slide, "局部学习常见假设", 0.95, 1.78, 2.6, 0.38, size=17, bold=True)
- add_rect(slide, 0.98, 2.42, 1.55, 0.76, fill=BLUE_LIGHT, line=BLUE)
- add_text(slide, "teaching\nsignal", 1.10, 2.52, 1.31, 0.50, size=14, color=BLUE, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
- add_rect(slide, 0.98, 3.62, 1.55, 0.76, fill="EFF1F3", line=GRAY)
- add_text(slide, "ordinary\ntraffic", 1.10, 3.72, 1.31, 0.50, size=14, color=DARK_GRAY, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
- add_arrow(slide, 2.55, 2.80, 3.55, 3.25, color=BLUE, width=2.1)
- add_arrow(slide, 2.55, 4.00, 3.55, 3.55, color=GRAY, width=2.1)
- add_rect(slide, 3.58, 2.72, 2.10, 1.36, fill="F3F5F6", line=DARK_GRAY)
- add_text(slide, "raw apical\nfeedback", 3.76, 2.93, 1.74, 0.72, size=18, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
- add_arrow(slide, 4.63, 4.10, 4.63, 4.85, color=RED, width=2.4)
- add_rect(slide, 3.48, 4.90, 2.30, 0.84, fill="F8E6DE", line=RED)
- add_text(slide, "update direction\nmay rotate", 3.65, 5.04, 1.96, 0.52, size=14.2, color=RED, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
-
- add_rect(slide, 6.58, 1.52, 6.12, 4.88, fill=PAPER, line=LIGHT)
- add_text(slide, "Nature 2026 提供了更具体的对象", 6.92, 1.78, 4.8, 0.38, size=17, bold=True)
- # Simple soma-dendrite regression sketch.
- plot_x, plot_y, plot_w, plot_h = 7.08, 2.60, 2.35, 2.30
- add_line(slide, plot_x, plot_y + plot_h, plot_x + plot_w, plot_y + plot_h, color=DARK_GRAY, width=1.2)
- add_line(slide, plot_x, plot_y + plot_h, plot_x, plot_y, color=DARK_GRAY, width=1.2)
- add_line(slide, plot_x + 0.20, plot_y + 1.97, plot_x + 2.12, plot_y + 0.35, color=GRAY, width=2.0)
- points = [(0.35, 1.74), (0.72, 1.41), (1.03, 1.29), (1.42, 0.88), (1.82, 0.61)]
- for dx, dy in points:
- add_circle(slide, plot_x + dx, plot_y + dy, 0.055, fill=GRAY, line=PAPER)
- expected_x = plot_x + 1.45
- expected_y = plot_y + 0.90
- observed_y = plot_y + 0.35
- add_circle(slide, expected_x, observed_y, 0.075, fill=BLUE, line=PAPER)
- add_line(slide, expected_x, expected_y, expected_x, observed_y, color=BLUE, width=2.8)
- add_text(slide, "residual", expected_x + 0.12, observed_y + 0.10, 0.80, 0.25, size=11.5, color=BLUE, bold=True)
- add_text(slide, "soma", plot_x + 0.88, plot_y + plot_h + 0.18, 0.80, 0.25, size=10.5, color=MUTED, align=PP_ALIGN.CENTER)
- add_text(slide, "dendrite", plot_x - 0.68, plot_y + 0.93, 0.70, 0.25, size=10.5, color=MUTED, align=PP_ALIGN.CENTER)
-
+ add_text(slide, "presynaptic activity × local postsynaptic gain × dendritic innovation", 2.62, 4.38, 8.12, 0.36, size=15, color=GRAY, align=PP_ALIGN.CENTER)
add_bullets(
slide,
[
- "先拟合每个神经元正常的 soma–dendrite 关系",
- "残差与 soma 明显去相关",
- "残差携带 outcome 与神经元特异的有符号任务信息",
+ "Training uses locally available quantities, forward observations, and manual synaptic updates.",
+ "Node-perturbation feedback supports the small-network experiments; reciprocal Kolen–Pollack plasticity supports the standard ResNets.",
+ "The inherited credit path transports layer-specific directions. SDIL removes soma-predictable traffic from those directions.",
],
- 9.75,
- 2.56,
- 2.62,
- 2.60,
- size=14.3,
- gap=8,
+ 1.02,
+ 5.05,
+ 11.15,
+ 1.45,
+ size=15.5,
+ gap=4,
)
- add_rect(slide, 6.93, 5.34, 5.42, 0.68, fill=BLUE_LIGHT, line=BLUE_LIGHT)
- add_text(slide, "算法问题:学习是否也应使用 residual,而不是 raw activity?", 7.10, 5.50, 5.08, 0.33, size=15.2, color=BLUE, bold=True, align=PP_ALIGN.CENTER)
- add_footer(slide, "Source: Francioni et al., “Vectorized instructive signals in cortical dendrites,” Nature (2026).")
+ add_footer(slide, "Sources: Francioni et al. (Nature 2026); Lansdell et al. (ICLR 2020); Akrout et al. (NeurIPS 2019).")
- # Slide 3: method.
+ # 3. Controlled ablation.
slide = new_slide()
- add_title(slide, "方法:预测正常耦合,再用残差完成局部更新", 3, "SDIL 改变教学变量;它不要求重新发明整条反馈传播路径。")
-
- steps = [
- ("1", "Neutral observation", "âₗ = Pₗ(hₗ)", "估计同一神经元的正常耦合", "EFF1F3", DARK_GRAY),
- ("2", "Innovation", "rₗ = aₗ − âₗ", "只保留 soma 无法预测的部分", BLUE_LIGHT, BLUE),
- ("3", "Local plasticity", "ΔWₗ = η(rₗ ⊙ φ′(uₗ))hₗ₋₁ᵀ", "pre × post gain × dendritic residual", GREEN_LIGHT, GREEN),
- ]
- for i, (num, label, eq, desc, fill, color) in enumerate(steps):
- x = 0.67 + i * 4.18
- add_rect(slide, x, 1.70, 3.76, 2.38, fill=fill, line=color, radius=True, line_width=1.4)
- add_circle(slide, x + 0.35, 2.04, 0.20, fill=color, line=color)
- add_text(slide, num, x + 0.23, 1.90, 0.24, 0.28, size=13, color=PAPER, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
- add_text(slide, label, x + 0.68, 1.83, 2.72, 0.36, size=16, color=color, bold=True)
- add_text(slide, eq, x + 0.22, 2.48, 3.32, 0.53, size=19, color=INK, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE, font=FONT_LATIN)
- add_text(slide, desc, x + 0.30, 3.34, 3.16, 0.43, size=13.5, color=MUTED, align=PP_ALIGN.CENTER)
- if i < 2:
- add_arrow(slide, x + 3.82, 2.86, x + 4.10, 2.86, color=MUTED, width=2.0)
-
- add_rect(slide, 0.67, 4.42, 5.96, 2.02, fill=PAPER, line=LIGHT)
- add_text(slide, "学习器内部为什么是 BP-free", 0.98, 4.68, 3.2, 0.35, size=17, bold=True)
- add_bullets(
+ add_title(
slide,
- [
- "更新只读取 pre-synaptic activity、local gain 和本细胞 residual",
- "无 reverse-mode autograd;不复制 forward-weight transpose",
- ],
- 0.98,
- 5.12,
- 5.25,
- 1.12,
- size=13.5,
- gap=5,
+ "Residualization is load-bearing under predictable traffic",
+ 3,
+ "Controlled MNIST intervention · depth 3 · width 256 · five paired seeds",
)
+ slide.shapes.add_picture(str(FIG3), Inches(0.42), Inches(1.37), width=Inches(12.48), height=Inches(4.87))
+ add_takeaway(slide, "At traffic strength ρ = 0.5: raw 10.38%, norm-matched raw 10.31%, innovation 97.35%.")
+ add_footer(slide, "Norm matching equalizes per-example update magnitude. The remaining difference comes from teaching direction.")
- add_rect(slide, 6.88, 4.42, 5.78, 2.02, fill=PAPER, line=LIGHT)
- add_text(slide, "信用路径是可替换的已有组件", 7.19, 4.68, 3.8, 0.35, size=17, bold=True)
+ # 4. Standard ResNet-20 confirmation.
+ slide = new_slide()
+ add_title(
+ slide,
+ "Dynamic innovation survives a standard ResNet-20 test",
+ 4,
+ "CIFAR-10 · 200 epochs · five untouched paired seeds · four-times-RMS soma-predictable traffic",
+ )
+ slide.shapes.add_picture(str(FIG4), Inches(0.43), Inches(1.47), width=Inches(12.46), height=Inches(3.82))
add_bullets(
slide,
[
- "Small nets: node-perturbation feedback vectorizer",
- "ResNet: reciprocal KP plasticity",
- "两者均为已有组件;SDIL 的贡献是 residualization",
+ "Dynamic SDIL under traffic: 91.584% mean test accuracy; clean reciprocal KP: 91.388%.",
+ "Mean early-layer teaching-signal cosine: 0.999687.",
+ "Cost: 1.326× the matched BP MAC estimate, zero task-loss queries, and one neutral observation per example.",
],
- 7.19,
- 5.10,
- 5.06,
- 1.18,
- size=13.2,
- bullet_color=GREEN,
+ 1.20,
+ 5.56,
+ 10.95,
+ 1.08,
+ size=15.5,
gap=3,
)
- add_footer(slide, "Credit substrates: Lansdell et al. (ICLR 2020); reciprocal plasticity: Akrout et al. (NeurIPS 2019).")
+ add_footer(slide, "Exact gradients serve diagnostic measurement; local signals drive every learning update.")
- # Slide 4: controlled causal evidence.
+ # 5. Standard-depth scaling.
slide = new_slide()
- add_title(slide, "关键消融:减法改变了方向,不只是信号大小", 4, "强 soma-predictable traffic 下,raw 与 norm-matched raw 都失效;innovation 保留学习方向。")
- slide.shapes.add_picture(str(FIG3), Inches(0.40), Inches(1.38), width=Inches(12.52), height=Inches(4.89))
- add_rect(slide, 1.02, 6.36, 11.25, 0.57, fill=BLUE_LIGHT, line=BLUE_LIGHT)
- add_rich_text(
+ add_title(
slide,
- [
- ("ρ = 0.5:", 15, INK, True),
- ("raw 10.38%", 15, DARK_GRAY, True),
- (" · norm-matched raw 10.31%", 15, ORANGE, True),
- (" · innovation 97.35%", 15, BLUE, True),
- (" (5 seeds)", 13, MUTED, False),
- ],
- 1.18,
- 6.47,
- 10.92,
- 0.33,
+ "Scaling from ResNet-20 to ResNet-56",
+ 5,
+ "Complete 60-endpoint CIFAR-10 panel · five seeds per method and depth · shared hyperparameters",
+ )
+ figure6_top = ASSETS / "figure6_top_row.png"
+ slide.shapes.add_picture(str(figure6_top), Inches(0.56), Inches(1.42), width=Inches(12.22), height=Inches(4.38))
+ add_text(
+ slide,
+ "SDIL under 4× traffic: 91.584% → 92.254% → 92.760%; all five R20/R56 pairs improve.",
+ 0.73,
+ 6.01,
+ 11.90,
+ 0.40,
+ size=14.8,
+ bold=True,
+ align=PP_ALIGN.CENTER,
+ )
+ add_text(
+ slide,
+ "Reciprocal KP provides the scalable credit path. SDIL preserves this path under mixed apical traffic.",
+ 0.73,
+ 6.48,
+ 11.90,
+ 0.34,
+ size=14.5,
align=PP_ALIGN.CENTER,
- valign=MSO_ANCHOR.MIDDLE,
)
- add_footer(slide, "Controlled MNIST traffic intervention. Norm matching uses the same per-example update magnitude as innovation.")
+ add_footer(slide, "BP and clean reciprocal KP follow the same depth trend; fixed DFA remains near 31%.")
- # Slide 5: standard-scale evidence.
+ # 6. Dynamical task.
slide = new_slide()
- add_title(slide, "标准 ResNet:扩展性来自 KP,抗混合流量来自 SDIL", 5, "60 个 CIFAR-10 endpoints;每个深度 5 seeds,200 epochs,无 depth-specific tuning。")
-
- categories = ["R20", "R32", "R56"]
- bp = [91.624, 92.302, 92.632]
- dfa = [31.878, 32.684, 30.850]
- kp = [91.388, 92.332, 92.670]
- sdil = [91.584, 92.254, 92.760]
- add_custom_line_chart(
+ add_title(
slide,
- 0.62,
- 1.48,
- 5.92,
- 4.82,
- categories,
- [("BP", bp, DARK_GRAY), ("DFA", dfa, ORANGE), ("clean KP", kp, GREEN), ("SDIL + traffic", sdil, BLUE)],
- ymin=25,
- ymax=95,
- yticks=[30, 50, 70, 90],
- title="完整尺度:固定 DFA 随深度仍处于约 31%",
+ "A local actor–critic produces temporal outcome signals",
+ 6,
+ "Synthetic BCI task · six task clusters × five model seeds · manual local updates",
)
- add_custom_line_chart(
+ panel_a = ASSETS / "figure5a_actor_critic.png"
+ panel_d = ASSETS / "figure5d_outcome.png"
+ slide.shapes.add_picture(str(panel_a), Inches(0.58), Inches(1.42), width=Inches(5.96), height=Inches(4.43))
+ slide.shapes.add_picture(str(panel_d), Inches(6.80), Inches(1.42), width=Inches(5.96), height=Inches(4.43))
+ add_text(
+ slide,
+ "Final success: 100% · terminal outcome decoding: 99.83% · outcome-lesion effect: 0.400.",
+ 0.68,
+ 6.06,
+ 11.98,
+ 0.36,
+ size=14.8,
+ bold=True,
+ align=PP_ALIGN.CENTER,
+ )
+ add_text(
slide,
- 6.80,
- 1.48,
- 5.92,
- 4.82,
- categories,
- [("BP", bp, DARK_GRAY), ("clean KP", kp, GREEN), ("SDIL + 4× traffic", sdil, BLUE)],
- ymin=91.0,
- ymax=93.0,
- yticks=[91.0, 91.5, 92.0, 92.5, 93.0],
- title="近 BP 放大:SDIL 在 4× traffic 下跟随 clean KP",
+ "The synthetic task supplies terminal reward and tests neuron-specific outcome surprise with causal lesions.",
+ 0.68,
+ 6.52,
+ 11.98,
+ 0.32,
+ size=14.0,
+ align=PP_ALIGN.CENTER,
)
- add_badge(slide, "SDIL: 91.58 → 92.76%", 0.88, 6.45, 3.42, fill=BLUE_LIGHT, color=BLUE)
- add_badge(slide, "1.31–1.33× BP MAC", 4.48, 6.45, 3.42, fill=ORANGE_LIGHT, color=RED)
- add_badge(slide, "0 loss queries · 1 neutral obs/example", 8.08, 6.45, 4.25, fill=GREEN_LIGHT, color=GREEN, size=12.2)
- add_footer(slide, "Source: complete 60-record ResNet panel. SDIL-specific claim is robustness under traffic, not clean superiority over KP.")
+ add_footer(slide, "Outcome labels and exact causal roles serve evaluation.")
- # Slide 6: dynamical task evidence.
- slide = new_slide()
- add_title(slide, "动态任务:residual 携带 outcome surprise,而不只是分类误差", 6, "独立的 synthetic BCI actor–critic:6 task clusters × 5 models,所有学习更新均为手写局部规则。")
- p_a = ASSET_DIR / "figure5a_actor_critic.png"
- p_d = ASSET_DIR / "figure5d_outcome.png"
- slide.shapes.add_picture(str(p_a), Inches(0.57), Inches(1.46), width=Inches(5.95), height=Inches(4.42))
- slide.shapes.add_picture(str(p_d), Inches(6.80), Inches(1.46), width=Inches(5.95), height=Inches(4.42))
- add_badge(slide, "100% final task success", 0.93, 6.07, 3.44, fill=BLUE_LIGHT, color=BLUE)
- add_badge(slide, "99.83% terminal outcome decoding", 4.73, 6.07, 3.82, fill=GREEN_LIGHT, color=GREEN, size=12.4)
- add_badge(slide, "outcome lesion: −0.400 separation", 8.92, 6.07, 3.50, fill=ORANGE_LIGHT, color=RED, size=12.0)
- add_text(slide, "范围:这是机制验证用的合成任务;terminal reward 被直接提供,不是皮层数据。", 0.82, 6.66, 11.70, 0.32, size=13.5, color=MUTED, align=PP_ALIGN.CENTER)
- add_footer(slide, "Source: complete untouched calibrated BCI confirmation; outcome labels and exact roles are diagnostic-only.")
-
- # Slide 7: positioning and next decisive evidence.
+ # 7. Current scientific position.
slide = new_slide()
- add_title(slide, "当前最准确的定位:可扩展 local credit 的混合信号分离模块", 7)
-
- # Pipeline strip.
- pipeline = [
- ("existing credit path", "KP / learned feedback", "EFF1F3", DARK_GRAY, 0.66, 2.28),
- ("mixed apical channel", "instruction + ordinary traffic", ORANGE_LIGHT, RED, 3.33, 2.76),
- ("SDIL", "subtract soma-predictable part", BLUE_LIGHT, BLUE, 6.52, 2.52),
- ("local update", "eligibility × innovation", GREEN_LIGHT, GREEN, 9.48, 2.80),
- ]
- for i, (head, sub, fill, color, x, w) in enumerate(pipeline):
- add_rect(slide, x, 1.35, w, 0.98, fill=fill, line=color, radius=True, line_width=1.2)
- add_text(slide, head, x + 0.12, 1.51, w - 0.24, 0.28, size=14.2, color=color, bold=True, align=PP_ALIGN.CENTER)
- add_text(slide, sub, x + 0.12, 1.87, w - 0.24, 0.24, size=10.8, color=INK, align=PP_ALIGN.CENTER)
- if i < len(pipeline) - 1:
- next_x = pipeline[i + 1][4]
- add_arrow(slide, x + w + 0.06, 1.84, next_x - 0.07, 1.84, color=MUTED, width=1.8)
-
- columns = [
- (0.66, 3.05, 3.80, "已经建立", BLUE, BLUE_LIGHT, [
- "可预测 traffic 会旋转 raw local update",
- "per-neuron residualization 在受控干扰下是 load-bearing 的",
- "组合方法在 ResNet-20/32/56 上保持近 BP accuracy",
- ]),
- (4.76, 3.05, 3.80, "真正的新东西", GREEN, GREEN_LIGHT, [
- "把 Harnett residual 直接定义成 teaching variable",
- "neutral-period predictor + dynamic local projection",
- "机制、方向、成本和动态 outcome 的联合证据",
- ]),
- (8.86, 3.05, 3.80, "仍然缺少", RED, ORANGE_LIGHT, [
- "自然任务中不可预先测量的 mixed feedback",
- "真实硬件或真实神经数据上的必要性",
- "证明 SDIL 的价值不能由 clean KP 单独解释",
- ]),
- ]
- for x, y, w, head, color, fill, items in columns:
- add_rect(slide, x, y, w, 2.68, fill=PAPER, line=LIGHT)
- add_rect(slide, x, y, w, 0.56, fill=fill, line=fill)
- add_text(slide, head, x + 0.18, y + 0.10, w - 0.36, 0.32, size=16, color=color, bold=True, align=PP_ALIGN.CENTER)
- add_bullets(slide, items, x + 0.25, y + 0.78, w - 0.50, 1.70, size=13.2, bullet_color=color, gap=7)
-
- add_rect(slide, 1.06, 6.10, 11.20, 0.67, fill=BLUE_LIGHT, line=BLUE_LIGHT)
+ add_title(slide, "Current scientific position", 7)
+ add_text(slide, "Established", 0.78, 1.30, 3.30, 0.38, size=20, bold=True)
+ add_bullets(
+ slide,
+ [
+ "Soma-predictable apical traffic can rotate a raw local teaching signal.",
+ "Per-neuron innovation preserves learning direction under controlled traffic.",
+ "Dynamic innovation works with a scalable BP-free reciprocal credit path on ResNet-20/32/56.",
+ "A separate local actor–critic reproduces neuron-specific outcome signaling in a controlled dynamical task.",
+ ],
+ 0.82,
+ 1.82,
+ 11.50,
+ 2.15,
+ size=17,
+ gap=7,
+ )
+ add_text(slide, "Attribution", 0.78, 4.15, 3.30, 0.38, size=20, bold=True)
+ add_bullets(
+ slide,
+ [
+ "Reciprocal KP provides clean-setting scale and feedback tracking.",
+ "SDIL provides soma-conditioned traffic removal and the associated mechanism tests.",
+ ],
+ 0.82,
+ 4.66,
+ 11.50,
+ 0.92,
+ size=17,
+ gap=7,
+ )
+ add_text(slide, "Next decisive experiment", 0.78, 5.82, 4.20, 0.38, size=20, bold=True)
add_text(
slide,
- "下一项决定性证据:在自然或硬件产生的 state-dependent mixed traffic 中,raw credit 失败,而 SDIL 无需 oracle calibration 即可恢复。",
- 1.27,
- 6.25,
- 10.78,
- 0.34,
- size=14.6,
- color=BLUE,
- bold=True,
- align=PP_ALIGN.CENTER,
+ "Evaluate raw reciprocal credit and SDIL under naturally generated, state-dependent mixed traffic with component-level bias hidden from the learner.",
+ 0.82,
+ 6.30,
+ 11.45,
+ 0.58,
+ size=18,
)
- add_footer(slide, "Bounded conclusion: strong controlled mechanism + standard-scale compatibility; natural mixed-traffic necessity remains open.")
+ add_footer(slide, "Target claim: scalable local credit assignment with adaptive removal of predictable feedback contamination.")
- OUT_DIR.mkdir(parents=True, exist_ok=True)
- prs.save(OUT_PATH)
- print(OUT_PATH)
+ SLIDES.mkdir(parents=True, exist_ok=True)
+ presentation.save(OUTPUT)
+ print(OUTPUT)
if __name__ == "__main__":