#!/usr/bin/env python3 """编译单日课程(周六 10:00 批处理)""" import argparse import os import sys from pathlib import Path from jinja2 import Template import yaml # 添加项目根目录到路径 PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent sys.path.insert(0, str(PROJECT_ROOT)) def load_config() -> dict: """加载配置""" config_path = PROJECT_ROOT / "skills" / "mathlab" / "config.yaml" with open(config_path, "r", encoding="utf-8") as f: return yaml.safe_load(f) def generate_symbol_decoder(topic: str) -> str: """生成符号解码字典(示例:感知机)""" return r"""
$w$self.weights (权重向量,shape: [d])
$b$self.bias (偏置标量,shape: [])
$x$input_tensor (输入样本,shape: [d])
$y$label (标签,值域:{-1, +1})
$\sign(\cdot)$np.sign() (符号函数)
$\xi$margin (间隔,用于更新步长)
""" def generate_math_derivation(topic: str) -> str: """生成数学推导(示例:感知机)""" return r""" ### 感知机预测函数 $$f(x) = w \cdot x + b$$ 其中 $\cdot$ 表示向量点积。 ### 损失函数( hinge loss 的简化形式) $$L(w, b) = -\sum_{x_i \in M} y_i (w \cdot x_i + b)$$ $M$ 是误分类点集合。 ### 梯度下降更新规则 $$w \leftarrow w + \eta \cdot y_i \cdot x_i$$ $$b \leftarrow b + \eta \cdot y_i$$ 其中 $\eta$ 是学习率。 """ def generate_html(day: int, topic: str, config: dict) -> str: """生成课程 HTML""" template_path = PROJECT_ROOT / "skills" / "mathlab" / "templates" / "course_template.html" with open(template_path, "r", encoding="utf-8") as f: template_content = f.read() template = Template(template_content) html_content = template.render( day=day, topic=topic, technical_debt="之前的算法无法处理线性不可分数据,导致泛化能力差。感知机通过引入决策边界,将分类问题转化为优化问题。", visual_intuition="想象一条直线(或超平面)将两类点分开。权重向量 $w$ 决定直线的方向,偏置 $b$ 决定直线的位置。", bilibili_keyword="感知机 直观解释", symbol_decoder=generate_symbol_decoder(topic), math_derivation=generate_math_derivation(topic), optimization_bottleneck=r"全量梯度下降需要遍历所有样本,复杂度 $O(N \cdot d)$。现代框架使用 mini-batch SGD 加速。", oj_mission="实现感知机的 forward 和 update 函数,在 XOR 数据集上验证无法收敛(线性不可分)。", katex_css="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css", katex_js="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" ) return html_content def generate_exercise(day: int, topic: str) -> str: """生成练习题""" return f'''""" Day {day} - {topic} 练习 任务:实现 {topic} 的核心算法 """ import numpy as np import matplotlib.pyplot as plt class {topic.replace(" ", "_").lower()}: """{topic} 类""" def __init__(self, learning_rate: float = 0.01): self.learning_rate = learning_rate self.weights = None self.bias = None def forward(self, X: np.ndarray) -> np.ndarray: """前向传播 Args: X: 输入数据,shape: [n_samples, n_features] Returns: 预测结果,shape: [n_samples] """ # TODO: 实现 f(x) = sign(w · x + b) raise NotImplementedError def compute_loss(self, X: np.ndarray, y: np.ndarray) -> float: """计算损失""" # TODO: 实现损失函数 raise NotImplementedError def update(self, X_i: np.ndarray, y_i: int): """更新参数 Args: X_i: 单个样本,shape: [n_features] y_i: 标签,值域 {-1, +1} """ # TODO: 实现梯度下降更新 raise NotImplementedError def fit(self, X: np.ndarray, y: np.ndarray, max_iter: int = 100): """训练模型""" # TODO: 实现训练循环 raise NotImplementedError def plot_concept(): """可视化概念""" # 生成二维数据 np.random.seed(42) X = np.random.randn(100, 2) y = np.sign(X[:, 0] + X[:, 1] - 0.5) * 1 # 绘制散点图 plt.figure(figsize=(8, 6)) scatter = plt.scatter(X[:, 0], X[:, 1], c=y, cmap="bwr", s=100, edgecolors="black") plt.xlabel("x1") plt.ylabel("x2") plt.title("Day {day} - {topic} 可视化") plt.colorbar(scatter) plt.grid(True, alpha=0.3) plt.savefig("./plots/day{day}_concept.png", dpi=150) print(f"✅ 可视化已保存:plots/day{day}_concept.png") if __name__ == "__main__": plot_concept() ''' def generate_test(day: int, topic: str) -> str: """生成测试用例""" return f'''""" Day {day} - {topic} 测试用例 """ import numpy as np import sys sys.path.append("../exercises") # TODO: 导入对应的类 # from day{day}_task import * def test_forward_shape(): """测试前向传播输出形状""" # TODO: 实现测试 assert True def test_loss_computation(): """测试损失计算""" # TODO: 实现测试 assert True def test_update_rule(): """测试参数更新规则""" # TODO: 实现测试 assert True def test_convergence(): """测试收敛性""" # TODO: 实现测试 assert True ''' def main(): parser = argparse.ArgumentParser(description="编译单日课程") parser.add_argument("--day", type=int, required=True, help="天数 (1-7)") parser.add_argument("--topic", type=str, required=True, help="主题名称") args = parser.parse_args() config = load_config() # 创建 staging 目录 staging_dir = PROJECT_ROOT / config["output"]["staging"] staging_dir.mkdir(exist_ok=True) exercises_dir = staging_dir / "exercises" tests_dir = staging_dir / "tests" exercises_dir.mkdir(exist_ok=True) tests_dir.mkdir(exist_ok=True) # 生成 HTML html_content = generate_html(args.day, args.topic, config) html_path = staging_dir / f"course_day{args.day}.html" with open(html_path, "w", encoding="utf-8") as f: f.write(html_content) print(f"✅ 生成课程 HTML: {html_path}") # 生成练习题 exercise_content = generate_exercise(args.day, args.topic) exercise_path = exercises_dir / f"day{args.day}_task.py" with open(exercise_path, "w", encoding="utf-8") as f: f.write(exercise_content) print(f"✅ 生成练习题:{exercise_path}") # 生成测试 test_content = generate_test(args.day, args.topic) test_path = tests_dir / f"test_day{args.day}.py" with open(test_path, "w", encoding="utf-8") as f: f.write(test_content) print(f"✅ 生成测试用例:{test_path}") print(f"\n🎉 Day {args.day} 编译完成!文件已保存到 staging/") if __name__ == "__main__": main()