| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #!/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"""
- <div class="symbol-map">
- <strong>$w$</strong> → <code>self.weights</code> (权重向量,shape: [d])
- </div>
- <div class="symbol-map">
- <strong>$b$</strong> → <code>self.bias</code> (偏置标量,shape: [])
- </div>
- <div class="symbol-map">
- <strong>$x$</strong> → <code>input_tensor</code> (输入样本,shape: [d])
- </div>
- <div class="symbol-map">
- <strong>$y$</strong> → <code>label</code> (标签,值域:{-1, +1})
- </div>
- <div class="symbol-map">
- <strong>$\sign(\cdot)$</strong> → <code>np.sign()</code> (符号函数)
- </div>
- <div class="symbol-map">
- <strong>$\xi$</strong> → <code>margin</code> (间隔,用于更新步长)
- </div>
- """
- 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()
|