Add presentation slides for OU process and Mehler formula
Sync to site1 / sync (push) Has been cancelled
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""把 OU/Mehler 幻灯片 + 已有播客音频合成为 MP4(复用第一篇流程)。
|
||||
|
||||
流程:读已有音频时长 → 按每句字符占比分配到 12 页 → Chrome 截 12 页 → ffmpeg 合成。
|
||||
用法:python3 build_video2.py
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SCRIPT_JSON = os.path.join(HERE, "ou_mehler-script.json")
|
||||
SLIDE_HTML = os.path.join(HERE, "slides", "OU 过程与 Mehler 公式.html")
|
||||
OUT_DIR = os.path.join(HERE, "output")
|
||||
SHOT_DIR = os.path.join(OUT_DIR, "shots")
|
||||
AUDIO_IN = os.path.join(OUT_DIR, "ou_mehler-podcast.mp3")
|
||||
VIDEO_OUT = os.path.join(OUT_DIR, "ou_mehler-video.mp4")
|
||||
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
|
||||
NUM_PAGES = 12
|
||||
# 句(1-based) -> 页(1-based)。共 44 句、12 页。
|
||||
# 1 封面 / 2 核心问题 / 3 OU直觉 / 4 离散定义 / 5 三性质 / 6 Mehler / 7 核心推论
|
||||
# 8 数值例子 / 9 对齐损失 / 10 衰减图 / 11 小结 / 12 尾页
|
||||
LINE_TO_PAGE = {
|
||||
1: 1, 2: 1, # 封面
|
||||
3: 2, # 核心问题
|
||||
4: 3, 5: 3, 6: 3, # OU 物理直觉
|
||||
7: 4, 8: 4, 9: 4, # 离散定义
|
||||
10: 5, 11: 5, 12: 5, 13: 5, 14: 5, 15: 5, # 三性质
|
||||
16: 6, 17: 6, 18: 6, 19: 6, 20: 6, # Mehler 公式
|
||||
21: 7, 22: 7, 23: 7, # 核心推论
|
||||
24: 8, 25: 8, 26: 8, 27: 8, 28: 8, 29: 8, # 数值例子
|
||||
30: 9, 31: 9, 32: 9, 33: 9, 34: 9, # 对齐损失
|
||||
35: 10, 36: 10, # 衰减图示
|
||||
37: 11, 38: 11, 39: 11, 40: 11, 41: 11, # 小结
|
||||
42: 12, 43: 12, 44: 12, # 尾页
|
||||
}
|
||||
|
||||
|
||||
def run(cmd):
|
||||
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def ffprobe_dur(path):
|
||||
out = subprocess.check_output(
|
||||
["ffprobe", "-v", "error", "-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1", path])
|
||||
return float(out.strip())
|
||||
|
||||
|
||||
def step1_page_durations():
|
||||
total = ffprobe_dur(AUDIO_IN)
|
||||
lines = json.load(open(SCRIPT_JSON, encoding="utf-8"))["lines"]
|
||||
page_chars = {p: 0 for p in range(1, NUM_PAGES + 1)}
|
||||
for i, line in enumerate(lines, 1):
|
||||
page_chars[LINE_TO_PAGE.get(i, NUM_PAGES)] += len(line["paragraph"].strip())
|
||||
grand = sum(page_chars.values())
|
||||
page_dur = {p: total * page_chars[p] / grand for p in page_chars}
|
||||
page_dur[NUM_PAGES] += (total - sum(page_dur.values()))
|
||||
print(f"音频总时长 {total:.2f}s,分配到 {NUM_PAGES} 页:")
|
||||
for p in range(1, NUM_PAGES + 1):
|
||||
print(f" page {p:>2}: {page_dur[p]:6.2f}s ({page_chars[p]} 字)")
|
||||
return page_dur
|
||||
|
||||
|
||||
def step2_screenshots():
|
||||
os.makedirs(SHOT_DIR, exist_ok=True)
|
||||
file_url = "file://" + SLIDE_HTML.replace(" ", "%20")
|
||||
for p in range(1, NUM_PAGES + 1):
|
||||
out = os.path.join(SHOT_DIR, f"page_{p:02d}.png")
|
||||
run([CHROME, "--headless=new", "--disable-gpu", "--hide-scrollbars",
|
||||
"--force-device-scale-factor=1", "--window-size=1920,1080",
|
||||
f"--screenshot={out}", "--virtual-time-budget=2800",
|
||||
f"{file_url}?page={p}&clean=1"])
|
||||
if not os.path.exists(out):
|
||||
raise RuntimeError(f"截图失败 page {p}")
|
||||
print(f"截图 page {p:02d} ✓")
|
||||
|
||||
|
||||
def step3_video(page_dur):
|
||||
tmp_list = os.path.join(SHOT_DIR, "vlist.txt")
|
||||
with open(tmp_list, "w") as lf:
|
||||
for p in range(1, NUM_PAGES + 1):
|
||||
img = os.path.join(SHOT_DIR, f"page_{p:02d}.png")
|
||||
dur = max(page_dur[p], 0.8)
|
||||
vid = os.path.join(SHOT_DIR, f"v_{p:02d}.mp4")
|
||||
run(["ffmpeg", "-y", "-loop", "1", "-i", img, "-t", f"{dur:.3f}",
|
||||
"-r", "30", "-pix_fmt", "yuv420p",
|
||||
"-vf", "scale=1920:1080:force_original_aspect_ratio=decrease,"
|
||||
"pad=1920:1080:(ow-iw)/2:(oh-ih)/2",
|
||||
"-c:v", "libx264", "-preset", "medium", "-crf", "20", vid])
|
||||
lf.write(f"file '{vid}'\n")
|
||||
print(f"页视频 {p:02d} {dur:.2f}s ✓")
|
||||
silent = os.path.join(SHOT_DIR, "silent.mp4")
|
||||
run(["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", tmp_list, "-c", "copy", silent])
|
||||
run(["ffmpeg", "-y", "-i", silent, "-i", AUDIO_IN,
|
||||
"-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-shortest", VIDEO_OUT])
|
||||
print(f"\n✅ 视频已输出:{VIDEO_OUT} ({ffprobe_dur(VIDEO_OUT):.1f}s)")
|
||||
|
||||
|
||||
def main():
|
||||
if not os.path.exists(AUDIO_IN):
|
||||
print(f"ERROR: 找不到音频 {AUDIO_IN}", file=sys.stderr); sys.exit(1)
|
||||
print("=== 步骤 1/3:分配每页时长 ===")
|
||||
page_dur = step1_page_durations()
|
||||
print("\n=== 步骤 2/3:截取 12 页 ===")
|
||||
step2_screenshots()
|
||||
print("\n=== 步骤 3/3:ffmpeg 合成 ===")
|
||||
step3_video(page_dur)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"title": "OU 过程与 Mehler 公式:为什么高阶成分被罚得更狠",
|
||||
"locale": "zh",
|
||||
"lines": [
|
||||
{"speaker": "male", "paragraph": "Hello Deer!欢迎回来。上一期我们聊了 Hermite 多项式,留了个尾巴——相关性为什么会按阶数指数衰减。今天就来把这个坑填上。"},
|
||||
{"speaker": "female", "paragraph": "对,上次最后那个悬念我一直惦记着。今天的主角是两个名字,OU 过程和 Mehler 公式。先说说,它们是用来干嘛的?"},
|
||||
{"speaker": "male", "paragraph": "LeJEPA 训练时需要正样本对,也就是同一个内容的两个视角。问题是:这两个视角是怎么造出来的?为什么这种造法会让高阶的非线性成分被惩罚得更重?"},
|
||||
{"speaker": "female", "paragraph": "造正样本对的办法,就是 OU 过程,全名奥恩斯坦-乌伦贝克过程。这名字有点吓人,但物理图像其实很可爱对吧?"},
|
||||
{"speaker": "male", "paragraph": "非常可爱。你就想象一个小球,被一根弹簧拴在原点。弹簧一直想把它拉回中心,这叫均值回归;同时周围有随机的小扰动在推它,像布朗运动。"},
|
||||
{"speaker": "female", "paragraph": "所以小球既被往回拉、又被随机推。这一拉一推的拉扯,就是 OU 过程。那它在 LeJEPA 里具体长什么样?"},
|
||||
{"speaker": "male", "paragraph": "论文用的是离散版本,一步到位:新的视角 z 撇,等于 ρ 乘以原来的 z,再加上根号下一减 ρ 平方,乘一个全新的高斯噪声。"},
|
||||
{"speaker": "female", "paragraph": "这里的 ρ 是关键,介于零和一之间。它就是上一期我们叫的相关强度,对吧?"},
|
||||
{"speaker": "male", "paragraph": "正是。ρ 越接近一,新视角就越像原来的;ρ 越接近零,两个视角就越不相关。实践里一般取零点八到零点九五之间。"},
|
||||
{"speaker": "female", "paragraph": "明白。那这个造法有几个很妙的性质,我们一个个说。第一个是平稳性?"},
|
||||
{"speaker": "male", "paragraph": "对。如果原来的 z 服从标准高斯,那么造出来的 z 撇也服从同一个标准高斯。算一下就知道:均值还是零,方差还是一,因为 ρ 平方加上一减 ρ 平方正好等于一。"},
|
||||
{"speaker": "female", "paragraph": "漂亮,造完之后分布纹丝不动。这意味着两个视角是平起平坐的,没有谁更特殊。第二个性质呢?"},
|
||||
{"speaker": "male", "paragraph": "第二个是相关性可控。两个视角的协方差正好等于 ρ。所以 ρ 就像一个旋钮,你拧大它两个视角就更像,拧小就更不像,完全在你掌控之中。"},
|
||||
{"speaker": "female", "paragraph": "一个旋钮控制相似度,这设计真干净。第三个性质?"},
|
||||
{"speaker": "male", "paragraph": "第三个叫加性噪声。整个转移可以拆成两块:一块是线性漂移 ρ 乘 z,另一块是独立的噪声。干净的线性加噪声结构,正好满足论文要的假设。"},
|
||||
{"speaker": "female", "paragraph": "好,三个性质都很温和。那真正的硬核来了——Mehler 公式。它到底说了什么?"},
|
||||
{"speaker": "male", "paragraph": "Mehler 公式是 OU 过程的谱定理。一句话说:它把 OU 过程的转移规律,在 Hermite 这套积木的基底下展开了,而且每一阶积木前面的系数,正好是 ρ 的那一阶次方。"},
|
||||
{"speaker": "female", "paragraph": "等等,让我抓住重点:d 阶的 Hermite 成分,前面挂的系数是 ρ 的 d 次方?"},
|
||||
{"speaker": "male", "paragraph": "完全正确。这就是全部魔法的来源。把它用到编码器的某个分量上,就得到一个特别干净的式子:两个视角在这个分量上的相关性,等于各阶谱权重乘以 ρ 的对应次方,再全部加起来。"},
|
||||
{"speaker": "female", "paragraph": "谱权重我们上期讲过,就是那张能量饼图里每一阶占的比例。那现在把这个和数跟 ρ 比一比,会怎样?"},
|
||||
{"speaker": "male", "paragraph": "这就是核心推论。因为每一项里 ρ 的 d 次方都小于等于 ρ 本身——d 至少是一嘛——所以整个加权和一定小于等于 ρ。而所有权重加起来是一,所以上界恰好就是 ρ。"},
|
||||
{"speaker": "female", "paragraph": "所以相关性最多就是 ρ,封顶了。那什么时候能正好取到这个上限?"},
|
||||
{"speaker": "male", "paragraph": "只有一种情况:全部权重都压在一阶上,也就是编码器是纯线性的。只要有任何一个二阶或更高阶的权重大于零,那一项就会严格变小,整个和就够不到 ρ 了。"},
|
||||
{"speaker": "female", "paragraph": "所以等号成立,当且仅当纯线性。这个当且仅当太关键了。我们来点具体数字找找感觉?"},
|
||||
{"speaker": "male", "paragraph": "好。取 ρ 等于零点九。纯线性编码器,相关性就是零点九的一次方,等于零点九,完美顶到上限。"},
|
||||
{"speaker": "female", "paragraph": "纯二次的呢?"},
|
||||
{"speaker": "male", "paragraph": "纯二次,相关性是零点九的平方,等于零点八一,已经掉了零点零九。纯三次是零点九的三次方,约零点七三,掉得更多。"},
|
||||
{"speaker": "female", "paragraph": "那要是一半线性、一半二次的混合呢?"},
|
||||
{"speaker": "male", "paragraph": "混合的就是两者加权平均,零点五乘零点九加零点五乘零点八一,约等于零点八五五。介于两者之间,但仍然够不到零点九。结论很清楚:非线性成分越多,相关性越低。"},
|
||||
{"speaker": "female", "paragraph": "数字一摆,趋势一目了然。那这跟 LeJEPA 真正要优化的目标,是怎么挂上钩的?"},
|
||||
{"speaker": "male", "paragraph": "LeJEPA 的对齐损失,本质是让两个视角的表示尽量接近,写开之后就等于一个常数减去所有分量相关性的总和。所以最小化损失,就等价于最大化相关性总和。"},
|
||||
{"speaker": "female", "paragraph": "而我们刚证明了每个相关性最多是 ρ……"},
|
||||
{"speaker": "male", "paragraph": "对,所以对齐损失有一个下界,正比于一减 ρ。而这个最好的下界,当且仅当每一个编码器分量都是线性的时候才能达到。"},
|
||||
{"speaker": "female", "paragraph": "于是结论就水落石出了:最优编码器必须是线性的。这正是定理一的心脏。"},
|
||||
{"speaker": "male", "paragraph": "一点没错。我们再用一张画面感的图收一下尾:ρ 等于零点九时,一阶零点九、二阶零点八一、三阶零点七三、四阶零点六六、五阶零点五九,像一排越来越短的条。"},
|
||||
{"speaker": "female", "paragraph": "非线性成分的相关性随阶数指数往下掉,越高阶越吃亏。这画面我记住了。我们把今天五个要点串一下?"},
|
||||
{"speaker": "male", "paragraph": "第一,OU 过程用一拉一推的方式造正样本对,相似度由 ρ 这个旋钮控制。"},
|
||||
{"speaker": "female", "paragraph": "第二,平稳性保证两个视角分布相同;相关性恰好是 ρ;结构是线性漂移加独立噪声。"},
|
||||
{"speaker": "male", "paragraph": "第三,Mehler 公式说,d 阶 Hermite 成分的相关性是 ρ 的 d 次方。"},
|
||||
{"speaker": "female", "paragraph": "第四,核心不等式:相关性等于各阶权重乘 ρ 的对应次方之和,最多到 ρ,等号当且仅当纯线性。"},
|
||||
{"speaker": "male", "paragraph": "第五,因此最小化对齐损失就是最大化相关性,逼着编码器学成线性——这就是定理一。"},
|
||||
{"speaker": "female", "paragraph": "完美闭环。下一站去哪?"},
|
||||
{"speaker": "male", "paragraph": "下一讲我们把 Hermite 展开和今天的 OU 衰减正式拼起来,组装成定理一的完整证明,叫谱分解与线性可识别性。"},
|
||||
{"speaker": "female", "paragraph": "好,那我们下期见。谢谢大家收听,记得,弹簧拉小球,ρ 拧大小——我们下次接着拆。拜拜!"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
# OU 过程与 Mehler 公式:为什么高阶成分被罚得更狠(播客文字稿)
|
||||
|
||||
> 来源文档:[`JEPA/math/02_ou_process_mehler.md`](../../../JEPA/math/02_ou_process_mehler.md)
|
||||
> 形式:双主播(男 / 女)对话讲解,约 8 分钟
|
||||
> 配套脚本:[`ou_mehler-script.json`](ou_mehler-script.json)
|
||||
|
||||
---
|
||||
|
||||
**男主播:** Hello Deer!欢迎回来。上一期我们聊了 Hermite 多项式,留了个尾巴——相关性为什么会按阶数指数衰减。今天就来把这个坑填上。
|
||||
|
||||
**女主播:** 对,上次最后那个悬念我一直惦记着。今天的主角是两个名字,OU 过程和 Mehler 公式。先说说,它们是用来干嘛的?
|
||||
|
||||
**男主播:** LeJEPA 训练时需要正样本对,也就是同一个内容的两个视角。问题是:这两个视角是怎么造出来的?为什么这种造法会让高阶的非线性成分被惩罚得更重?
|
||||
|
||||
**女主播:** 造正样本对的办法,就是 OU 过程,全名奥恩斯坦-乌伦贝克过程。这名字有点吓人,但物理图像其实很可爱对吧?
|
||||
|
||||
**男主播:** 非常可爱。你就想象一个小球,被一根弹簧拴在原点。弹簧一直想把它拉回中心,这叫均值回归;同时周围有随机的小扰动在推它,像布朗运动。
|
||||
|
||||
**女主播:** 所以小球既被往回拉、又被随机推。这一拉一推的拉扯,就是 OU 过程。那它在 LeJEPA 里具体长什么样?
|
||||
|
||||
**男主播:** 论文用的是离散版本,一步到位:新的视角 z 撇,等于 ρ 乘以原来的 z,再加上根号下一减 ρ 平方,乘一个全新的高斯噪声。
|
||||
|
||||
**女主播:** 这里的 ρ 是关键,介于零和一之间。它就是上一期我们叫的相关强度,对吧?
|
||||
|
||||
**男主播:** 正是。ρ 越接近一,新视角就越像原来的;ρ 越接近零,两个视角就越不相关。实践里一般取零点八到零点九五之间。
|
||||
|
||||
**女主播:** 明白。那这个造法有几个很妙的性质,我们一个个说。第一个是平稳性?
|
||||
|
||||
**男主播:** 对。如果原来的 z 服从标准高斯,那么造出来的 z 撇也服从同一个标准高斯。算一下就知道:均值还是零,方差还是一,因为 ρ 平方加上一减 ρ 平方正好等于一。
|
||||
|
||||
**女主播:** 漂亮,造完之后分布纹丝不动。这意味着两个视角是平起平坐的,没有谁更特殊。第二个性质呢?
|
||||
|
||||
**男主播:** 第二个是相关性可控。两个视角的协方差正好等于 ρ。所以 ρ 就像一个旋钮,你拧大它两个视角就更像,拧小就更不像,完全在你掌控之中。
|
||||
|
||||
**女主播:** 一个旋钮控制相似度,这设计真干净。第三个性质?
|
||||
|
||||
**男主播:** 第三个叫加性噪声。整个转移可以拆成两块:一块是线性漂移 ρ 乘 z,另一块是独立的噪声。干净的线性加噪声结构,正好满足论文要的假设。
|
||||
|
||||
**女主播:** 好,三个性质都很温和。那真正的硬核来了——Mehler 公式。它到底说了什么?
|
||||
|
||||
**男主播:** Mehler 公式是 OU 过程的谱定理。一句话说:它把 OU 过程的转移规律,在 Hermite 这套积木的基底下展开了,而且每一阶积木前面的系数,正好是 ρ 的那一阶次方。
|
||||
|
||||
**女主播:** 等等,让我抓住重点:d 阶的 Hermite 成分,前面挂的系数是 ρ 的 d 次方?
|
||||
|
||||
**男主播:** 完全正确。这就是全部魔法的来源。把它用到编码器的某个分量上,就得到一个特别干净的式子:两个视角在这个分量上的相关性,等于各阶谱权重乘以 ρ 的对应次方,再全部加起来。
|
||||
|
||||
**女主播:** 谱权重我们上期讲过,就是那张能量饼图里每一阶占的比例。那现在把这个和数跟 ρ 比一比,会怎样?
|
||||
|
||||
**男主播:** 这就是核心推论。因为每一项里 ρ 的 d 次方都小于等于 ρ 本身——d 至少是一嘛——所以整个加权和一定小于等于 ρ。而所有权重加起来是一,所以上界恰好就是 ρ。
|
||||
|
||||
**女主播:** 所以相关性最多就是 ρ,封顶了。那什么时候能正好取到这个上限?
|
||||
|
||||
**男主播:** 只有一种情况:全部权重都压在一阶上,也就是编码器是纯线性的。只要有任何一个二阶或更高阶的权重大于零,那一项就会严格变小,整个和就够不到 ρ 了。
|
||||
|
||||
**女主播:** 所以等号成立,当且仅当纯线性。这个当且仅当太关键了。我们来点具体数字找找感觉?
|
||||
|
||||
**男主播:** 好。取 ρ 等于零点九。纯线性编码器,相关性就是零点九的一次方,等于零点九,完美顶到上限。
|
||||
|
||||
**女主播:** 纯二次的呢?
|
||||
|
||||
**男主播:** 纯二次,相关性是零点九的平方,等于零点八一,已经掉了零点零九。纯三次是零点九的三次方,约零点七三,掉得更多。
|
||||
|
||||
**女主播:** 那要是一半线性、一半二次的混合呢?
|
||||
|
||||
**男主播:** 混合的就是两者加权平均,零点五乘零点九加零点五乘零点八一,约等于零点八五五。介于两者之间,但仍然够不到零点九。结论很清楚:非线性成分越多,相关性越低。
|
||||
|
||||
**女主播:** 数字一摆,趋势一目了然。那这跟 LeJEPA 真正要优化的目标,是怎么挂上钩的?
|
||||
|
||||
**男主播:** LeJEPA 的对齐损失,本质是让两个视角的表示尽量接近,写开之后就等于一个常数减去所有分量相关性的总和。所以最小化损失,就等价于最大化相关性总和。
|
||||
|
||||
**女主播:** 而我们刚证明了每个相关性最多是 ρ……
|
||||
|
||||
**男主播:** 对,所以对齐损失有一个下界,正比于一减 ρ。而这个最好的下界,当且仅当每一个编码器分量都是线性的时候才能达到。
|
||||
|
||||
**女主播:** 于是结论就水落石出了:最优编码器必须是线性的。这正是定理一的心脏。
|
||||
|
||||
**男主播:** 一点没错。我们再用一张画面感的图收一下尾:ρ 等于零点九时,一阶零点九、二阶零点八一、三阶零点七三、四阶零点六六、五阶零点五九,像一排越来越短的条。
|
||||
|
||||
**女主播:** 非线性成分的相关性随阶数指数往下掉,越高阶越吃亏。这画面我记住了。我们把今天五个要点串一下?
|
||||
|
||||
**男主播:** 第一,OU 过程用一拉一推的方式造正样本对,相似度由 ρ 这个旋钮控制。
|
||||
|
||||
**女主播:** 第二,平稳性保证两个视角分布相同;相关性恰好是 ρ;结构是线性漂移加独立噪声。
|
||||
|
||||
**男主播:** 第三,Mehler 公式说,d 阶 Hermite 成分的相关性是 ρ 的 d 次方。
|
||||
|
||||
**女主播:** 第四,核心不等式:相关性等于各阶权重乘 ρ 的对应次方之和,最多到 ρ,等号当且仅当纯线性。
|
||||
|
||||
**男主播:** 第五,因此最小化对齐损失就是最大化相关性,逼着编码器学成线性——这就是定理一。
|
||||
|
||||
**女主播:** 完美闭环。下一站去哪?
|
||||
|
||||
**男主播:** 下一讲我们把 Hermite 展开和今天的 OU 衰减正式拼起来,组装成定理一的完整证明,叫谱分解与线性可识别性。
|
||||
|
||||
**女主播:** 好,那我们下期见。谢谢大家收听,记得,弹簧拉小球,ρ 拧大小——我们下次接着拆。拜拜!
|
||||
|
After Width: | Height: | Size: 537 KiB |
|
After Width: | Height: | Size: 554 KiB |
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 618 KiB |
|
After Width: | Height: | Size: 543 KiB |
|
After Width: | Height: | Size: 550 KiB |
|
After Width: | Height: | Size: 609 KiB |
|
After Width: | Height: | Size: 543 KiB |
|
After Width: | Height: | Size: 534 KiB |
|
After Width: | Height: | Size: 626 KiB |
|
After Width: | Height: | Size: 562 KiB |
|
After Width: | Height: | Size: 530 KiB |
@@ -0,0 +1,357 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OU 过程与 Mehler 公式 · 为什么高阶成分被罚得更狠</title>
|
||||
|
||||
<!-- 设计系统沿用「深空学术 Deep Space」:暗青径向底 + 青绿 #2ec4b6 / 琥珀 #ffb703 -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" crossorigin="anonymous">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" crossorigin="anonymous"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" crossorigin="anonymous"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;500;700;900&family=JetBrains+Mono:wght@400;700&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;}
|
||||
:root{--bg0:#07090d;--teal:#2ec4b6;--amber:#ffb703;--ink:#e8eef2;--ink2:#9fb0ba;--mono:#56727d;--line:rgba(255,255,255,.08);}
|
||||
html,body{width:100%;height:100%;background:var(--bg0);overflow:hidden;font-family:"Noto Sans SC",sans-serif;color:var(--ink);}
|
||||
#wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;}
|
||||
.stage{width:1920px;height:1080px;transform-origin:center center;position:relative;}
|
||||
.slide{position:absolute;inset:0;padding:96px 110px 120px;display:flex;flex-direction:column;
|
||||
background:radial-gradient(120% 120% at 75% 12%, #11202a 0%, #0a0e14 56%, #07090d 100%);}
|
||||
.slide:not(.active){display:none !important;}
|
||||
.slide.posBL{background:radial-gradient(120% 120% at 18% 92%, #11202a 0%, #0a0e14 60%, #07090d 100%);}
|
||||
.slide.posCR{background:radial-gradient(130% 130% at 95% 50%, #12232c 0%, #0a0e14 58%, #07090d 100%);}
|
||||
.glow{position:absolute;width:620px;height:620px;border-radius:50%;pointer-events:none;
|
||||
background:radial-gradient(circle,rgba(46,196,182,.16),transparent 70%);top:-160px;right:-110px;filter:blur(6px);}
|
||||
.glow.amber{background:radial-gradient(circle,rgba(255,183,3,.13),transparent 70%);}
|
||||
.kicker{font-family:"JetBrains Mono",monospace;color:var(--teal);font-size:22px;letter-spacing:.18em;text-transform:uppercase;display:flex;align-items:center;gap:16px;}
|
||||
.kicker .ln{width:54px;height:2px;background:var(--teal);opacity:.7;}
|
||||
.page-footer{position:absolute;left:110px;right:110px;bottom:60px;display:flex;justify-content:space-between;align-items:center;font-family:"JetBrains Mono",monospace;font-size:18px;color:var(--mono);}
|
||||
.page-footer .prog{flex:1;margin:0 30px;height:2px;background:rgba(255,255,255,.08);position:relative;border-radius:2px;}
|
||||
.page-footer .prog i{position:absolute;left:0;top:0;height:100%;background:linear-gradient(90deg,var(--teal),var(--amber));border-radius:2px;transition:width .4s;}
|
||||
h1{font-weight:900;font-size:112px;line-height:1.03;letter-spacing:-.01em;}
|
||||
h1 em{font-style:normal;color:var(--teal);}
|
||||
h2{font-weight:900;font-size:72px;line-height:1.08;margin-top:22px;}
|
||||
h2 em{font-style:normal;color:var(--amber);}
|
||||
.lead{font-size:29px;color:var(--ink2);font-weight:300;margin-top:18px;line-height:1.5;max-width:1320px;}
|
||||
.content{flex:1;display:flex;align-items:center;min-height:0;}
|
||||
.katex{font-size:1em;}
|
||||
.fbox{background:rgba(46,196,182,.06);border:1px solid rgba(46,196,182,.24);border-radius:18px;padding:46px 44px;text-align:center;color:#eafcf9;}
|
||||
.fbox .small{font-size:23px;color:#7fd8cf;margin-top:22px;font-family:"JetBrains Mono",monospace;}
|
||||
.cards{display:flex;gap:36px;width:100%;}
|
||||
.card{flex:1;background:rgba(255,255,255,.03);border:1px solid var(--line);border-radius:18px;padding:40px 36px;}
|
||||
.card .ct{font-size:34px;font-weight:700;margin-bottom:16px;color:var(--teal);}
|
||||
.card .cb{font-size:25px;color:var(--ink2);font-weight:300;line-height:1.55;}
|
||||
.barrow{display:flex;align-items:center;gap:20px;margin:18px 0;font-size:30px;}
|
||||
.barrow .chip{font-family:"JetBrains Mono",monospace;font-size:23px;padding:7px 15px;border-radius:8px;background:#13343a;color:var(--teal);min-width:120px;text-align:center;}
|
||||
.barrow .track{flex:1;height:16px;border-radius:8px;background:rgba(255,255,255,.06);position:relative;overflow:hidden;}
|
||||
.barrow .track i{position:absolute;left:0;top:0;height:100%;border-radius:8px;background:linear-gradient(90deg,var(--teal),var(--amber));}
|
||||
.barrow .val{font-family:"JetBrains Mono",monospace;font-size:23px;color:var(--ink2);min-width:120px;}
|
||||
ul.pts{list-style:none;}
|
||||
ul.pts li{font-size:31px;font-weight:300;color:var(--ink);line-height:1.5;margin:22px 0;padding-left:46px;position:relative;}
|
||||
ul.pts li::before{content:"";position:absolute;left:0;top:16px;width:22px;height:22px;border-radius:6px;background:linear-gradient(135deg,var(--teal),var(--amber));}
|
||||
ul.pts li b{color:var(--teal);font-weight:700;}
|
||||
table.cmp{width:100%;border-collapse:collapse;font-size:28px;}
|
||||
table.cmp th,table.cmp td{padding:22px 24px;text-align:left;border-bottom:1px solid var(--line);}
|
||||
table.cmp th{font-family:"JetBrains Mono",monospace;font-size:21px;color:var(--mono);letter-spacing:.06em;font-weight:400;}
|
||||
table.cmp td.k{color:var(--ink2);font-weight:300;}
|
||||
table.cmp td .hl{color:var(--teal);}
|
||||
table.cmp td .am{color:var(--amber);}
|
||||
.grid4{display:grid;grid-template-columns:1fr 1fr;gap:28px;width:100%;}
|
||||
.grid4 .g{background:rgba(255,255,255,.03);border:1px solid var(--line);border-radius:16px;padding:32px 32px;}
|
||||
.grid4 .g .n{font-family:"Space Grotesk";font-weight:700;font-size:38px;color:var(--teal);}
|
||||
.grid4 .g .t{font-size:27px;margin-top:8px;line-height:1.45;color:var(--ink);font-weight:300;}
|
||||
.grid4 .g .t b{color:var(--amber);font-weight:700;}
|
||||
.cover{justify-content:center;}
|
||||
.cover .meta{position:absolute;bottom:72px;left:110px;font-family:"JetBrains Mono",monospace;font-size:21px;color:var(--mono);}
|
||||
.nav{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:900;display:flex;gap:10px;align-items:center;}
|
||||
.nav button{background:#16242b;border:1px solid #2a3c44;color:#cfe;font-size:15px;padding:9px 18px;border-radius:8px;cursor:pointer;font-family:"JetBrains Mono",monospace;}
|
||||
.nav button:hover{background:#1d3038;}
|
||||
.nav .pg{color:#6f8a93;font-family:"JetBrains Mono",monospace;font-size:14px;padding:0 8px;}
|
||||
.hintkey{position:fixed;top:14px;right:18px;z-index:900;color:#42535b;font-family:"JetBrains Mono",monospace;font-size:13px;}
|
||||
.spring{display:flex;align-items:center;gap:6px;font-size:40px;color:var(--teal);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="hintkey">← → 翻页 · F 全屏</div>
|
||||
<div id="wrap"><div class="stage" id="stage">
|
||||
|
||||
<!-- 1 封面 -->
|
||||
<section class="slide active cover">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>SELF-SUPERVISED · 谱分解 · EP.02</div>
|
||||
<h1 style="margin-top:34px;">OU 过程<br>与 <em>Mehler 公式</em></h1>
|
||||
<p class="lead" style="margin-top:38px;">正样本对怎么造?为什么高阶非线性成分被罚得更狠?</p>
|
||||
<div class="meta">配套播客 · 双主播对话 · 上接 Hermite 多项式,下启线性可识别性</div>
|
||||
</section>
|
||||
|
||||
<!-- 2 核心问题 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>00 · 核心问题</div>
|
||||
<h2>正样本对从<em>哪里来</em>?</h2>
|
||||
<div class="content">
|
||||
<ul class="pts">
|
||||
<li>LeJEPA 训练需要「正样本对」——同一内容的两个视角 $(z, z')$。</li>
|
||||
<li>这对视角是<b>怎么生成</b>的?答案是 <b>OU 过程</b>。</li>
|
||||
<li>为什么这种生成方式,会让<b>高阶 Hermite 成分被更强地惩罚</b>?</li>
|
||||
<li>解开它的钥匙,是 <b>Mehler 公式</b>。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3 OU 物理直觉 -->
|
||||
<section class="slide">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>01 · 物理直觉</div>
|
||||
<h2>弹簧上的<em>小球</em></h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="card" style="flex:1;">
|
||||
<div class="ct">弹簧力 · 均值回归</div>
|
||||
<div class="cb">把小球持续拉回原点,不让它跑远。</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;">
|
||||
<div class="ct">随机扰动 · 布朗噪声</div>
|
||||
<div class="cb">周围的随机推搡,让小球抖动。</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;">
|
||||
<div class="ct">一拉一推 = OU 过程</div>
|
||||
<div class="cb">两股力量的拉扯,正是 Ornstein–Uhlenbeck 过程的图像。</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4 离散 OU 定义 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>02 · 定义</div>
|
||||
<h2>LeJEPA 用的<em>离散一步转移</em></h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="fbox" style="flex:1.1;font-size:42px;">
|
||||
$z' = \rho\,z + \sqrt{1-\rho^{2}}\;\eta$
|
||||
<div class="small">$\eta\sim\mathcal N(0,I),\quad \rho\in(0,1)$</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<p class="lead" style="font-size:30px;">$\rho$ 是<b style="color:var(--teal)">相关系数</b>——两个视角的相似度旋钮。</p>
|
||||
<p class="lead" style="font-size:28px;margin-top:18px;">$\rho\to1$:几乎相同视角;$\rho\to0$:相互独立。实践取 $\rho\in[0.8,0.95]$。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5 三性质 -->
|
||||
<section class="slide">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>03 · 三个性质</div>
|
||||
<h2>OU 过程的<em>三块基石</em></h2>
|
||||
<div class="content">
|
||||
<div class="cards">
|
||||
<div class="card"><div class="ct">① 平稳性</div><div class="cb">$z\sim\mathcal N(0,I)\Rightarrow z'\sim\mathcal N(0,I)$<br>均值 0、方差 $\rho^2+(1-\rho^2)=1$。两视角分布相同。</div></div>
|
||||
<div class="card"><div class="ct">② 相关可控</div><div class="cb">$\mathrm{Cov}(z',z)=\rho\,I$<br>$\rho$ 直接 = 相似度,一个旋钮说了算。</div></div>
|
||||
<div class="card"><div class="ct">③ 加性噪声</div><div class="cb">$z'=\rho z+\eta$:线性漂移 + 独立噪声。<br>满足论文的加性噪声假设。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6 Mehler 公式 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>04 · 谱定理</div>
|
||||
<h2>Mehler 公式:<em>每阶挂 ρᵈ</em></h2>
|
||||
<div class="content" style="gap:56px;">
|
||||
<div class="fbox" style="flex:1.15;font-size:30px;">
|
||||
$p(z'\mid z)=\varphi(z')\displaystyle\sum_{d=0}^{\infty}\rho^{d}\,\frac{He_d(z)He_d(z')}{d!}$
|
||||
<div class="small">OU 转移核在 Hermite 基下展开</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<div class="fbox" style="font-size:30px;">
|
||||
$\mathbb{E}[h_i(z)\,h_i(z')]=\displaystyle\sum_{d=0}^{\infty}\rho^{d}\,w_d$
|
||||
<div class="small">$w_d$=第 $i$ 分量在 $d$ 阶上的谱权重</div>
|
||||
</div>
|
||||
<p class="lead" style="font-size:26px;margin-top:22px;">关键:<b style="color:var(--amber)">d 阶成分的系数 = ρ 的 d 次方</b>。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 7 核心推论 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>05 · 核心推论</div>
|
||||
<h2>相关性<em>封顶在 ρ</em></h2>
|
||||
<div class="content" style="gap:56px;">
|
||||
<div class="fbox" style="flex:1.1;font-size:28px;line-height:2;">
|
||||
$\mathrm{corr}_i=\displaystyle\sum_{d\ge1}w_d\,\rho^{d}\le\sum_{d\ge1}w_d\,\rho=\rho$
|
||||
<div class="small">因为 $\rho^d\le\rho\ (d\ge1)$,且 $\sum w_d=1$</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<p class="lead" style="font-size:30px;">等号成立 <b style="color:var(--teal)">当且仅当 $w_1=1$</b>——编码器纯线性。</p>
|
||||
<p class="lead" style="font-size:27px;margin-top:18px;">只要有任何 $w_{d_0}>0\ (d_0\ge2)$,那一项就严格变小,和够不到 $\rho$。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 8 数值例子 -->
|
||||
<section class="slide">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>06 · 数值例子</div>
|
||||
<h2>取 <em>ρ = 0.9</em> 算给你看</h2>
|
||||
<div class="content">
|
||||
<table class="cmp">
|
||||
<tr><th>编码器</th><th>谱权重</th><th>相关性 corrᵢ</th><th>与 0.9 的差距</th></tr>
|
||||
<tr><td class="k">纯线性 $h=z$</td><td><span class="hl">$w_1=1$</span></td><td><span class="hl">$0.9^1=0.900$</span></td><td>0 | 最优</td></tr>
|
||||
<tr><td class="k">纯二次 $h=z^2-1$</td><td>$w_2=1$</td><td><span class="am">$0.9^2=0.810$</span></td><td>−0.090</td></tr>
|
||||
<tr><td class="k">纯三次 $h=z^3-3z$</td><td>$w_3=1$</td><td><span class="am">$0.9^3=0.729$</span></td><td>−0.171</td></tr>
|
||||
<tr><td class="k">混合 各半</td><td>$w_1=w_2=0.5$</td><td>$0.855$</td><td>−0.045</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 9 与对齐损失 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>07 · 训练联系</div>
|
||||
<h2>对齐损失的<em>下界</em></h2>
|
||||
<div class="content" style="gap:56px;">
|
||||
<div class="fbox" style="flex:1.05;font-size:28px;line-height:1.9;">
|
||||
$\mathcal L_{\text{align}}=\mathbb{E}\|h(z')-h(z)\|^2$<br>
|
||||
$=2n-2\sum_i \mathrm{corr}_i\;\ge\;2(1-\rho)\,n$
|
||||
<div class="small">最小化损失 ⟺ 最大化相关性之和</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<p class="lead" style="font-size:31px;">等号成立 <b style="color:var(--teal)">当且仅当每个 $h_i$ 都是线性的</b>。</p>
|
||||
<p class="lead" style="font-size:28px;margin-top:18px;">这就是定理一的心脏:<b style="color:var(--amber)">最优编码器必须线性</b>。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 10 衰减图示 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>08 · 直觉图示</div>
|
||||
<h2>ρ = 0.9 时<em>逐阶衰减</em></h2>
|
||||
<div class="content">
|
||||
<div style="width:100%;">
|
||||
<div class="barrow"><span class="chip">d=1 线性</span><div class="track"><i style="width:90%"></i></div><span class="val">ρ¹ = 0.900</span></div>
|
||||
<div class="barrow"><span class="chip">d=2 二次</span><div class="track"><i style="width:81%"></i></div><span class="val">ρ² = 0.810</span></div>
|
||||
<div class="barrow"><span class="chip">d=3 三次</span><div class="track"><i style="width:72.9%"></i></div><span class="val">ρ³ = 0.729</span></div>
|
||||
<div class="barrow"><span class="chip">d=4 四次</span><div class="track"><i style="width:65.6%"></i></div><span class="val">ρ⁴ = 0.656</span></div>
|
||||
<div class="barrow"><span class="chip">d=5 五次</span><div class="track"><i style="width:59%"></i></div><span class="val">ρ⁵ = 0.590</span></div>
|
||||
<p class="lead" style="font-size:27px;margin-top:20px;">非线性成分的相关性随阶数<b style="color:var(--amber)">指数衰减</b>——越高阶越吃亏。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 11 小结 -->
|
||||
<section class="slide">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>09 · 小结</div>
|
||||
<h2>五句话<em>带走全场</em></h2>
|
||||
<div class="content">
|
||||
<div class="grid4">
|
||||
<div class="g"><div class="n">01</div><div class="t">OU 过程「一拉一推」造正样本对,相似度由 <b>ρ</b> 控制。</div></div>
|
||||
<div class="g"><div class="n">02</div><div class="t"><b>平稳性</b> + 协方差 = ρ + 线性漂移加噪声,三性质齐备。</div></div>
|
||||
<div class="g"><div class="n">03</div><div class="t"><b>Mehler</b>:d 阶 Hermite 成分相关性 = ρᵈ。</div></div>
|
||||
<div class="g"><div class="n">04</div><div class="t">核心不等式 corrᵢ = Σ wᵈ ρᵈ ≤ ρ,等号 ⟺ <b>纯线性</b>。</div></div>
|
||||
<div class="g" style="grid-column:1 / -1;"><div class="n">05</div><div class="t">最小化对齐损失 → 最大化相关性 → <b>编码器必须线性</b>(定理一)。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 12 尾页 -->
|
||||
<section class="slide cover posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>下一站</div>
|
||||
<h1 style="font-size:88px;margin-top:30px;">谱分解<br><em>与线性可识别性</em></h1>
|
||||
<p class="lead" style="margin-top:34px;font-size:30px;">下一讲把 Hermite 展开与 OU 衰减正式拼起来,组装成定理一的完整证明。</p>
|
||||
<div class="meta">谢谢收听 · 配套音频 ou_mehler-podcast.mp3 · 弹簧拉小球,ρ 拧大小 · 我们下次见</div>
|
||||
</section>
|
||||
|
||||
</div></div>
|
||||
|
||||
<div class="nav">
|
||||
<button id="prev">← 上一页</button>
|
||||
<span class="pg" id="pg">1 / 12</span>
|
||||
<button id="next">下一页 →</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const STAGE = document.getElementById('stage');
|
||||
const slides = Array.from(document.querySelectorAll('.slide'));
|
||||
const total = slides.length;
|
||||
const KEY = 'ou_mehler_deck_page';
|
||||
|
||||
let cur = parseInt(localStorage.getItem(KEY) || '0', 10);
|
||||
if (isNaN(cur) || cur < 0 || cur >= total) cur = 0;
|
||||
|
||||
function fit(){
|
||||
const s = Math.min(window.innerWidth / 1920, window.innerHeight / 1080);
|
||||
STAGE.style.transform = `scale(${s})`;
|
||||
}
|
||||
window.addEventListener('resize', fit); fit();
|
||||
|
||||
function show(i){
|
||||
cur = (i + total) % total;
|
||||
slides.forEach((s, idx) => s.classList.toggle('active', idx === cur));
|
||||
document.getElementById('pg').textContent = `${cur + 1} / ${total}`;
|
||||
const w = ((cur) / (total - 1) * 100).toFixed(1) + '%';
|
||||
document.querySelectorAll('.page-footer .prog i').forEach(b => b.style.width = w);
|
||||
localStorage.setItem(KEY, cur);
|
||||
}
|
||||
|
||||
slides.forEach((s, idx) => {
|
||||
const f = document.createElement('div');
|
||||
f.className = 'page-footer';
|
||||
f.innerHTML = `<span>OU 过程 · Mehler 公式</span><span class="prog"><i></i></span><span>${String(idx+1).padStart(2,'0')} / ${String(total).padStart(2,'0')}</span>`;
|
||||
s.appendChild(f);
|
||||
});
|
||||
|
||||
document.getElementById('prev').addEventListener('click', () => show(cur - 1));
|
||||
document.getElementById('next').addEventListener('click', () => show(cur + 1));
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { e.preventDefault(); show(cur + 1); }
|
||||
else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); show(cur - 1); }
|
||||
else if (e.key === 'Home') show(0);
|
||||
else if (e.key === 'End') show(total - 1);
|
||||
else if (e.key === 'f' || e.key === 'F') {
|
||||
if (!document.fullscreenElement) document.documentElement.requestFullscreen();
|
||||
else document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('wrap').addEventListener('click', (e) => {
|
||||
if (e.target.closest('.nav')) return;
|
||||
if (e.clientX > window.innerWidth * 0.5) show(cur + 1); else show(cur - 1);
|
||||
});
|
||||
|
||||
function renderMath(){
|
||||
if (window.renderMathInElement){
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left:'$$', right:'$$', display:true},
|
||||
{left:'$', right:'$', display:false}
|
||||
],
|
||||
throwOnError:false
|
||||
});
|
||||
} else { setTimeout(renderMath, 120); }
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const pageParam = params.get('page');
|
||||
if (pageParam !== null) {
|
||||
const p = parseInt(pageParam, 10);
|
||||
if (!isNaN(p) && p >= 1 && p <= total) cur = p - 1;
|
||||
}
|
||||
if (params.get('clean') === '1') {
|
||||
document.body.classList.add('clean');
|
||||
const navEl = document.querySelector('.nav'); if (navEl) navEl.style.display = 'none';
|
||||
const hk = document.querySelector('.hintkey'); if (hk) hk.style.display = 'none';
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => { renderMath(); show(cur); });
|
||||
if (document.readyState !== 'loading') { renderMath(); show(cur); }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||