Add presentation slides for OU process and Mehler formula
Sync to site1 / sync (push) Has been cancelled
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""把 Hermite 幻灯片 + 已有播客音频合成为 MP4 视频(无需重新调 TTS)。
|
||||
|
||||
流程:
|
||||
1) 读已有完整音频时长;按每句文本字符数占比,把总时长分配到 11 页
|
||||
(字符数是中文 TTS 时长的良好代理;总时长严格等于真实音频)。
|
||||
2) 用 Chrome headless 把 11 页各截一张 1920x1080 PNG(?page=N&clean=1)。
|
||||
3) ffmpeg:每页图片按对应时长生成视频轨,叠加音频,输出 MP4。
|
||||
|
||||
用法:python3 build_video.py
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SCRIPT_JSON = os.path.join(HERE, "hermite_polynomials-script.json")
|
||||
SLIDE_HTML = os.path.join(HERE, "slides", "Hermite 多项式.html")
|
||||
OUT_DIR = os.path.join(HERE, "output")
|
||||
SHOT_DIR = os.path.join(OUT_DIR, "shots")
|
||||
AUDIO_IN = os.path.join(OUT_DIR, "hermite_polynomials-podcast.mp3")
|
||||
VIDEO_OUT = os.path.join(OUT_DIR, "hermite_polynomials-video.mp4")
|
||||
|
||||
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
|
||||
# 句(1-based) -> 页(1-based) 映射。共 43 句、11 页。
|
||||
LINE_TO_PAGE = {
|
||||
1: 1, 2: 1,
|
||||
3: 2, 4: 2, 5: 2, 6: 2,
|
||||
7: 3, 8: 3,
|
||||
9: 4, 10: 4, 11: 4, 12: 4,
|
||||
13: 5, 14: 5, 15: 5, 16: 5, 17: 5,
|
||||
18: 6, 19: 6, 20: 6,
|
||||
21: 7, 22: 7, 23: 7, 24: 7, 25: 7, 26: 7,
|
||||
27: 8, 28: 8, 29: 8,
|
||||
30: 9, 31: 9, 32: 9, 33: 9,
|
||||
34: 10, 35: 10, 36: 10, 37: 10, 38: 10, 39: 10,
|
||||
40: 11, 41: 11, 42: 11, 43: 11,
|
||||
}
|
||||
NUM_PAGES = 11
|
||||
|
||||
|
||||
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)
|
||||
with open(SCRIPT_JSON, encoding="utf-8") as f:
|
||||
lines = json.load(f)["lines"]
|
||||
# 每句字符数(去空白)作为时长权重
|
||||
page_chars = {p: 0 for p in range(1, NUM_PAGES + 1)}
|
||||
for i, line in enumerate(lines, 1):
|
||||
n = len(line["paragraph"].strip())
|
||||
page_chars[LINE_TO_PAGE.get(i, NUM_PAGES)] += n
|
||||
grand = sum(page_chars.values())
|
||||
page_dur = {p: total * page_chars[p] / grand for p in page_chars}
|
||||
# 修正累计误差,使最后一页吸收余量
|
||||
assigned = sum(page_dur.values())
|
||||
page_dur[NUM_PAGES] += (total - assigned)
|
||||
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, total
|
||||
|
||||
|
||||
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")
|
||||
url = f"{file_url}?page={p}&clean=1"
|
||||
run([CHROME, "--headless=new", "--disable-gpu", "--hide-scrollbars",
|
||||
"--force-device-scale-factor=1", "--window-size=1920,1080",
|
||||
f"--screenshot={out}", "--virtual-time-budget=2800", url])
|
||||
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)
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
print("=== 步骤 1/3:按字符占比分配每页时长 ===")
|
||||
page_dur, _ = step1_page_durations()
|
||||
print("\n=== 步骤 2/3:截取 11 页幻灯片 ===")
|
||||
step2_screenshots()
|
||||
print("\n=== 步骤 3/3:ffmpeg 合成视频 ===")
|
||||
step3_video(page_dur)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"title": "Hermite 多项式:高斯世界里的频率分解工具",
|
||||
"locale": "zh",
|
||||
"lines": [
|
||||
{"speaker": "male", "paragraph": "Hello Deer!欢迎回到我们的节目。今天我们要聊一个听起来很硬核、但其实特别优雅的数学工具——Hermite 多项式。"},
|
||||
{"speaker": "female", "paragraph": "对,很多人一听多项式就想躲。但今天我保证,我们用大白话把它讲明白。先说说,我们为什么要管它?"},
|
||||
{"speaker": "male", "paragraph": "好问题。这要从一个叫 LeJEPA 的自监督学习方法说起。它的核心证明里要回答一件事:编码器把数据映射成一个表示之后,这个表示里到底哪一部分,对所谓的正样本对的相关性贡献最大。"},
|
||||
{"speaker": "female", "paragraph": "正样本对,简单说就是同一个东西的两个不同视角,对吧?模型希望它们的表示尽量像。"},
|
||||
{"speaker": "male", "paragraph": "没错。而要回答哪部分贡献最大,你得先有一把刀,能把任意一个函数拆成一块一块。这就像傅里叶分析,把一段声音拆成不同频率的正弦波。"},
|
||||
{"speaker": "female", "paragraph": "啊,这个类比我喜欢。傅里叶是把周期信号拆成高音、低音。那 Hermite 是拆什么?"},
|
||||
{"speaker": "male", "paragraph": "Hermite 是专门为高斯分布、也就是我们常说的正态分布、钟形曲线,量身定做的拆分工具。当你的数据服从钟形曲线时,Hermite 多项式就是那套最自然的基本积木。"},
|
||||
{"speaker": "female", "paragraph": "所以可以理解成:傅里叶是周期世界的积木,Hermite 是高斯世界的积木。那这些积木长什么样?"},
|
||||
{"speaker": "male", "paragraph": "最前面几块特别简单。第零块就是常数一。第一块就是它本身,z。第二块是 z 的平方再减去一。第三块是 z 的三次方减去三倍的 z。越往后次数越高,但都有规律。"},
|
||||
{"speaker": "female", "paragraph": "有规律就好记。是不是有个递推的套路,能从前面两块推出下一块?"},
|
||||
{"speaker": "male", "paragraph": "正是。下一块等于 z 乘以当前这一块,再减去它的阶数乘以前一块。比如想要第二块,就拿 z 乘第一块的 z,得到 z 平方,再减去一倍的常数一,正好是 z 平方减一。"},
|
||||
{"speaker": "female", "paragraph": "漂亮,自己就能一层层搭上去。那这套积木最关键的本事是什么?"},
|
||||
{"speaker": "male", "paragraph": "最关键的叫正交性。意思是说,任意两块不同阶数的积木,在高斯分布下做平均,它们的乘积平均下来等于零。互不干扰。"},
|
||||
{"speaker": "female", "paragraph": "等一下,这个互不干扰,是不是又跟正弦波很像?不同频率的正弦波相乘积分也是零。"},
|
||||
{"speaker": "male", "paragraph": "完全一样的精神。不同频率的波互不打架;不同阶数的 Hermite 积木也互不打架。只有自己跟自己相乘,平均才不为零,而且那个值正好等于它的阶数的阶乘。"},
|
||||
{"speaker": "female", "paragraph": "能举个最小的例子验证一下吗?让我有点踏实感。"},
|
||||
{"speaker": "male", "paragraph": "当然。拿第一块 z 和第二块 z 平方减一相乘,展开就是 z 的三次方减去 z。在钟形曲线下,所有奇数次方的平均值都是零,所以整体就是零减零,等于零。它俩果然互不干扰。"},
|
||||
{"speaker": "female", "paragraph": "服气。那有了这套互不干扰的积木,是不是任何函数都能用它们拼出来?"},
|
||||
{"speaker": "male", "paragraph": "只要这个函数不太离谱,具体说是它的平方的平均值是有限的,那它就能写成这些积木的加权叠加。每一块前面配一个系数,系数大代表这个函数里那个阶数的成分多。"},
|
||||
{"speaker": "female", "paragraph": "就像把一个向量沿着一组坐标轴分解,每个轴上投影多少。这里的轴就是不同阶数的 Hermite 积木。"},
|
||||
{"speaker": "male", "paragraph": "比喻得非常准。好,现在重头戏来了:为什么这套工具对 LeJEPA 这么要命。"},
|
||||
{"speaker": "female", "paragraph": "我正想问。前面铺垫这么多,关键的转折在哪?"},
|
||||
{"speaker": "male", "paragraph": "关键在于那个生成正样本对的过程,数学上叫 OU 过程。你可以把它想象成:拿原始的表示,乘上一个介于零和一之间的系数,再掺进一点点随机噪声,得到第二个视角。"},
|
||||
{"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": "谱权重怎么理解?给我个画面感。"},
|
||||
{"speaker": "male", "paragraph": "你就想象一个饼图。整张饼是这个表示的总能量。线性成分切走一块,二阶切走一块,三阶再一块。每一块的占比就是那个阶数的谱权重,所有块加起来正好是百分之百。"},
|
||||
{"speaker": "female", "paragraph": "那如果一个表示是纯线性的,饼图就是线性那一块占满整张,对吧?"},
|
||||
{"speaker": "male", "paragraph": "完全正确。纯线性,线性那块就是百分之百。纯二次,二阶那块占满。混合的,就是好几块都有。而 LeJEPA 想要的理想状态,就是把整张饼都给线性那一块。"},
|
||||
{"speaker": "female", "paragraph": "我帮大家把今天的几个要点串一下。第一,Hermite 多项式是高斯世界里的频率分解工具,类比傅里叶。"},
|
||||
{"speaker": "male", "paragraph": "第二,它最核心的性质是正交性,不同阶数互不干扰,所以任意合理的函数都能干净地拆成这些积木的叠加。"},
|
||||
{"speaker": "female", "paragraph": "第三,那个生成正样本对的 OU 过程,对越高阶的成分削减得越狠,相关性按阶数指数衰减。"},
|
||||
{"speaker": "male", "paragraph": "第四,因此最大化相关性的最优解,就是只留线性成分,这就推出了所谓的线性可识别性,也是整套理论的基石。"},
|
||||
{"speaker": "female", "paragraph": "讲得太顺了。如果听众还想往下走,下一站是什么?"},
|
||||
{"speaker": "male", "paragraph": "下一站就是去看那个相关性按阶数衰减的公式到底从哪来,背后有个叫 Mehler 公式的东西,会把今天这个直觉彻底坐实。"},
|
||||
{"speaker": "female", "paragraph": "好,那我们下期接着聊。谢谢大家收听,记得,数学不可怕,可怕的是没人陪你拆积木。我们下次见!"},
|
||||
{"speaker": "male", "paragraph": "下次见,拜拜!"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
# Hermite 多项式:高斯世界里的频率分解工具(播客文字稿)
|
||||
|
||||
> 来源文档:[`JEPA/math/01_hermite_polynomials.md`](../../../JEPA/math/01_hermite_polynomials.md)
|
||||
> 形式:双主播(男 / 女)对话讲解,约 10 分钟
|
||||
> 配套脚本:[`hermite_polynomials-script.json`](hermite_polynomials-script.json)
|
||||
|
||||
---
|
||||
|
||||
**男主播:** Hello Deer!欢迎回到我们的节目。今天我们要聊一个听起来很硬核、但其实特别优雅的数学工具——Hermite 多项式。
|
||||
|
||||
**女主播:** 对,很多人一听多项式就想躲。但今天我保证,我们用大白话把它讲明白。先说说,我们为什么要管它?
|
||||
|
||||
**男主播:** 好问题。这要从一个叫 LeJEPA 的自监督学习方法说起。它的核心证明里要回答一件事:编码器把数据映射成一个表示之后,这个表示里到底哪一部分,对所谓的正样本对的相关性贡献最大。
|
||||
|
||||
**女主播:** 正样本对,简单说就是同一个东西的两个不同视角,对吧?模型希望它们的表示尽量像。
|
||||
|
||||
**男主播:** 没错。而要回答哪部分贡献最大,你得先有一把刀,能把任意一个函数拆成一块一块。这就像傅里叶分析,把一段声音拆成不同频率的正弦波。
|
||||
|
||||
**女主播:** 啊,这个类比我喜欢。傅里叶是把周期信号拆成高音、低音。那 Hermite 是拆什么?
|
||||
|
||||
**男主播:** Hermite 是专门为高斯分布、也就是我们常说的正态分布、钟形曲线,量身定做的拆分工具。当你的数据服从钟形曲线时,Hermite 多项式就是那套最自然的基本积木。
|
||||
|
||||
**女主播:** 所以可以理解成:傅里叶是周期世界的积木,Hermite 是高斯世界的积木。那这些积木长什么样?
|
||||
|
||||
**男主播:** 最前面几块特别简单。第零块就是常数一。第一块就是它本身,z。第二块是 z 的平方再减去一。第三块是 z 的三次方减去三倍的 z。越往后次数越高,但都有规律。
|
||||
|
||||
**女主播:** 有规律就好记。是不是有个递推的套路,能从前面两块推出下一块?
|
||||
|
||||
**男主播:** 正是。下一块等于 z 乘以当前这一块,再减去它的阶数乘以前一块。比如想要第二块,就拿 z 乘第一块的 z,得到 z 平方,再减去一倍的常数一,正好是 z 平方减一。
|
||||
|
||||
**女主播:** 漂亮,自己就能一层层搭上去。那这套积木最关键的本事是什么?
|
||||
|
||||
**男主播:** 最关键的叫正交性。意思是说,任意两块不同阶数的积木,在高斯分布下做平均,它们的乘积平均下来等于零。互不干扰。
|
||||
|
||||
**女主播:** 等一下,这个互不干扰,是不是又跟正弦波很像?不同频率的正弦波相乘积分也是零。
|
||||
|
||||
**男主播:** 完全一样的精神。不同频率的波互不打架;不同阶数的 Hermite 积木也互不打架。只有自己跟自己相乘,平均才不为零,而且那个值正好等于它的阶数的阶乘。
|
||||
|
||||
**女主播:** 能举个最小的例子验证一下吗?让我有点踏实感。
|
||||
|
||||
**男主播:** 当然。拿第一块 z 和第二块 z 平方减一相乘,展开就是 z 的三次方减去 z。在钟形曲线下,所有奇数次方的平均值都是零,所以整体就是零减零,等于零。它俩果然互不干扰。
|
||||
|
||||
**女主播:** 服气。那有了这套互不干扰的积木,是不是任何函数都能用它们拼出来?
|
||||
|
||||
**男主播:** 只要这个函数不太离谱,具体说是它的平方的平均值是有限的,那它就能写成这些积木的加权叠加。每一块前面配一个系数,系数大代表这个函数里那个阶数的成分多。
|
||||
|
||||
**女主播:** 就像把一个向量沿着一组坐标轴分解,每个轴上投影多少。这里的轴就是不同阶数的 Hermite 积木。
|
||||
|
||||
**男主播:** 比喻得非常准。好,现在重头戏来了:为什么这套工具对 LeJEPA 这么要命。
|
||||
|
||||
**女主播:** 我正想问。前面铺垫这么多,关键的转折在哪?
|
||||
|
||||
**男主播:** 关键在于那个生成正样本对的过程,数学上叫 OU 过程。你可以把它想象成:拿原始的表示,乘上一个介于零和一之间的系数,再掺进一点点随机噪声,得到第二个视角。
|
||||
|
||||
**女主播:** 那个介于零和一的系数,我们就叫它相关强度吧。它怎么影响不同阶数的积木?
|
||||
|
||||
**男主播:** 这就是最神奇的地方。线性成分,也就是第一阶,经过这个过程之后,两边的相关性正好等于那个系数本身。但二阶成分的相关性,是这个系数的平方。三阶是三次方。
|
||||
|
||||
**女主播:** 等等,因为这个系数小于一,所以平方会更小,三次方更更小。也就是阶数越高,相关性掉得越快?
|
||||
|
||||
**男主播:** 对,是指数级地掉。高阶的非线性成分,经过这个过程之后几乎就被冲淡没了。只有线性成分保留得最结实。
|
||||
|
||||
**女主播:** 我好像猜到结论了。如果模型的目标是让正样本对的相关性尽可能大……
|
||||
|
||||
**男主播:** 聪明。既然线性成分留下的相关性最高,非线性成分都被打了折扣,那最优策略自然就是:只保留线性成分,把所有非线性成分统统丢掉。
|
||||
|
||||
**女主播:** 所以模型被这个机制温柔地逼着,学成了一个本质上线性的表示。这就是他们那个核心定理的直觉来源。
|
||||
|
||||
**男主播:** 正是。研究者还给了一个很贴心的量,叫谱权重,专门衡量一个表示里每个阶数的成分占了多大比例。
|
||||
|
||||
**女主播:** 谱权重怎么理解?给我个画面感。
|
||||
|
||||
**男主播:** 你就想象一个饼图。整张饼是这个表示的总能量。线性成分切走一块,二阶切走一块,三阶再一块。每一块的占比就是那个阶数的谱权重,所有块加起来正好是百分之百。
|
||||
|
||||
**女主播:** 那如果一个表示是纯线性的,饼图就是线性那一块占满整张,对吧?
|
||||
|
||||
**男主播:** 完全正确。纯线性,线性那块就是百分之百。纯二次,二阶那块占满。混合的,就是好几块都有。而 LeJEPA 想要的理想状态,就是把整张饼都给线性那一块。
|
||||
|
||||
**女主播:** 我帮大家把今天的几个要点串一下。第一,Hermite 多项式是高斯世界里的频率分解工具,类比傅里叶。
|
||||
|
||||
**男主播:** 第二,它最核心的性质是正交性,不同阶数互不干扰,所以任意合理的函数都能干净地拆成这些积木的叠加。
|
||||
|
||||
**女主播:** 第三,那个生成正样本对的 OU 过程,对越高阶的成分削减得越狠,相关性按阶数指数衰减。
|
||||
|
||||
**男主播:** 第四,因此最大化相关性的最优解,就是只留线性成分,这就推出了所谓的线性可识别性,也是整套理论的基石。
|
||||
|
||||
**女主播:** 讲得太顺了。如果听众还想往下走,下一站是什么?
|
||||
|
||||
**男主播:** 下一站就是去看那个相关性按阶数衰减的公式到底从哪来,背后有个叫 Mehler 公式的东西,会把今天这个直觉彻底坐实。
|
||||
|
||||
**女主播:** 好,那我们下期接着聊。谢谢大家收听,记得,数学不可怕,可怕的是没人陪你拆积木。我们下次见!
|
||||
|
||||
**男主播:** 下次见,拜拜!
|
||||
@@ -0,0 +1,87 @@
|
||||
# Hermite 多项式:高斯世界里的频率分解工具
|
||||
|
||||
**Host (Male)**: Hello Deer!欢迎回到我们的节目。今天我们要聊一个听起来很硬核、但其实特别优雅的数学工具——Hermite 多项式。
|
||||
|
||||
**Host (Female)**: 对,很多人一听多项式就想躲。但今天我保证,我们用大白话把它讲明白。先说说,我们为什么要管它?
|
||||
|
||||
**Host (Male)**: 好问题。这要从一个叫 LeJEPA 的自监督学习方法说起。它的核心证明里要回答一件事:编码器把数据映射成一个表示之后,这个表示里到底哪一部分,对所谓的正样本对的相关性贡献最大。
|
||||
|
||||
**Host (Female)**: 正样本对,简单说就是同一个东西的两个不同视角,对吧?模型希望它们的表示尽量像。
|
||||
|
||||
**Host (Male)**: 没错。而要回答哪部分贡献最大,你得先有一把刀,能把任意一个函数拆成一块一块。这就像傅里叶分析,把一段声音拆成不同频率的正弦波。
|
||||
|
||||
**Host (Female)**: 啊,这个类比我喜欢。傅里叶是把周期信号拆成高音、低音。那 Hermite 是拆什么?
|
||||
|
||||
**Host (Male)**: Hermite 是专门为高斯分布、也就是我们常说的正态分布、钟形曲线,量身定做的拆分工具。当你的数据服从钟形曲线时,Hermite 多项式就是那套最自然的基本积木。
|
||||
|
||||
**Host (Female)**: 所以可以理解成:傅里叶是周期世界的积木,Hermite 是高斯世界的积木。那这些积木长什么样?
|
||||
|
||||
**Host (Male)**: 最前面几块特别简单。第零块就是常数一。第一块就是它本身,z。第二块是 z 的平方再减去一。第三块是 z 的三次方减去三倍的 z。越往后次数越高,但都有规律。
|
||||
|
||||
**Host (Female)**: 有规律就好记。是不是有个递推的套路,能从前面两块推出下一块?
|
||||
|
||||
**Host (Male)**: 正是。下一块等于 z 乘以当前这一块,再减去它的阶数乘以前一块。比如想要第二块,就拿 z 乘第一块的 z,得到 z 平方,再减去一倍的常数一,正好是 z 平方减一。
|
||||
|
||||
**Host (Female)**: 漂亮,自己就能一层层搭上去。那这套积木最关键的本事是什么?
|
||||
|
||||
**Host (Male)**: 最关键的叫正交性。意思是说,任意两块不同阶数的积木,在高斯分布下做平均,它们的乘积平均下来等于零。互不干扰。
|
||||
|
||||
**Host (Female)**: 等一下,这个互不干扰,是不是又跟正弦波很像?不同频率的正弦波相乘积分也是零。
|
||||
|
||||
**Host (Male)**: 完全一样的精神。不同频率的波互不打架;不同阶数的 Hermite 积木也互不打架。只有自己跟自己相乘,平均才不为零,而且那个值正好等于它的阶数的阶乘。
|
||||
|
||||
**Host (Female)**: 能举个最小的例子验证一下吗?让我有点踏实感。
|
||||
|
||||
**Host (Male)**: 当然。拿第一块 z 和第二块 z 平方减一相乘,展开就是 z 的三次方减去 z。在钟形曲线下,所有奇数次方的平均值都是零,所以整体就是零减零,等于零。它俩果然互不干扰。
|
||||
|
||||
**Host (Female)**: 服气。那有了这套互不干扰的积木,是不是任何函数都能用它们拼出来?
|
||||
|
||||
**Host (Male)**: 只要这个函数不太离谱,具体说是它的平方的平均值是有限的,那它就能写成这些积木的加权叠加。每一块前面配一个系数,系数大代表这个函数里那个阶数的成分多。
|
||||
|
||||
**Host (Female)**: 就像把一个向量沿着一组坐标轴分解,每个轴上投影多少。这里的轴就是不同阶数的 Hermite 积木。
|
||||
|
||||
**Host (Male)**: 比喻得非常准。好,现在重头戏来了:为什么这套工具对 LeJEPA 这么要命。
|
||||
|
||||
**Host (Female)**: 我正想问。前面铺垫这么多,关键的转折在哪?
|
||||
|
||||
**Host (Male)**: 关键在于那个生成正样本对的过程,数学上叫 OU 过程。你可以把它想象成:拿原始的表示,乘上一个介于零和一之间的系数,再掺进一点点随机噪声,得到第二个视角。
|
||||
|
||||
**Host (Female)**: 那个介于零和一的系数,我们就叫它相关强度吧。它怎么影响不同阶数的积木?
|
||||
|
||||
**Host (Male)**: 这就是最神奇的地方。线性成分,也就是第一阶,经过这个过程之后,两边的相关性正好等于那个系数本身。但二阶成分的相关性,是这个系数的平方。三阶是三次方。
|
||||
|
||||
**Host (Female)**: 等等,因为这个系数小于一,所以平方会更小,三次方更更小。也就是阶数越高,相关性掉得越快?
|
||||
|
||||
**Host (Male)**: 对,是指数级地掉。高阶的非线性成分,经过这个过程之后几乎就被冲淡没了。只有线性成分保留得最结实。
|
||||
|
||||
**Host (Female)**: 我好像猜到结论了。如果模型的目标是让正样本对的相关性尽可能大……
|
||||
|
||||
**Host (Male)**: 聪明。既然线性成分留下的相关性最高,非线性成分都被打了折扣,那最优策略自然就是:只保留线性成分,把所有非线性成分统统丢掉。
|
||||
|
||||
**Host (Female)**: 所以模型被这个机制温柔地逼着,学成了一个本质上线性的表示。这就是他们那个核心定理的直觉来源。
|
||||
|
||||
**Host (Male)**: 正是。研究者还给了一个很贴心的量,叫谱权重,专门衡量一个表示里每个阶数的成分占了多大比例。
|
||||
|
||||
**Host (Female)**: 谱权重怎么理解?给我个画面感。
|
||||
|
||||
**Host (Male)**: 你就想象一个饼图。整张饼是这个表示的总能量。线性成分切走一块,二阶切走一块,三阶再一块。每一块的占比就是那个阶数的谱权重,所有块加起来正好是百分之百。
|
||||
|
||||
**Host (Female)**: 那如果一个表示是纯线性的,饼图就是线性那一块占满整张,对吧?
|
||||
|
||||
**Host (Male)**: 完全正确。纯线性,线性那块就是百分之百。纯二次,二阶那块占满。混合的,就是好几块都有。而 LeJEPA 想要的理想状态,就是把整张饼都给线性那一块。
|
||||
|
||||
**Host (Female)**: 我帮大家把今天的几个要点串一下。第一,Hermite 多项式是高斯世界里的频率分解工具,类比傅里叶。
|
||||
|
||||
**Host (Male)**: 第二,它最核心的性质是正交性,不同阶数互不干扰,所以任意合理的函数都能干净地拆成这些积木的叠加。
|
||||
|
||||
**Host (Female)**: 第三,那个生成正样本对的 OU 过程,对越高阶的成分削减得越狠,相关性按阶数指数衰减。
|
||||
|
||||
**Host (Male)**: 第四,因此最大化相关性的最优解,就是只留线性成分,这就推出了所谓的线性可识别性,也是整套理论的基石。
|
||||
|
||||
**Host (Female)**: 讲得太顺了。如果听众还想往下走,下一站是什么?
|
||||
|
||||
**Host (Male)**: 下一站就是去看那个相关性按阶数衰减的公式到底从哪来,背后有个叫 Mehler 公式的东西,会把今天这个直觉彻底坐实。
|
||||
|
||||
**Host (Female)**: 好,那我们下期接着聊。谢谢大家收听,记得,数学不可怕,可怕的是没人陪你拆积木。我们下次见!
|
||||
|
||||
**Host (Male)**: 下次见,拜拜!
|
||||
|
After Width: | Height: | Size: 541 KiB |
|
After Width: | Height: | Size: 590 KiB |
|
After Width: | Height: | Size: 554 KiB |
|
After Width: | Height: | Size: 617 KiB |
|
After Width: | Height: | Size: 548 KiB |
|
After Width: | Height: | Size: 562 KiB |
|
After Width: | Height: | Size: 622 KiB |
|
After Width: | Height: | Size: 584 KiB |
|
After Width: | Height: | Size: 532 KiB |
|
After Width: | Height: | Size: 636 KiB |
|
After Width: | Height: | Size: 532 KiB |
@@ -0,0 +1,259 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hermite 多项式 · 配套幻灯片 · 风格预览</title>
|
||||
|
||||
<!-- KaTeX 公式渲染 -->
|
||||
<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+Serif+SC:wght@400;600;900&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;}
|
||||
html,body{width:100%;height:100%;background:#0a0a0c;overflow:hidden;font-family:"Noto Sans SC",sans-serif;}
|
||||
|
||||
/* ---- tab 切换条(在 stage 外,不被 scale 压缩)---- */
|
||||
#tabbar{position:fixed;top:0;left:0;right:0;height:52px;background:#16161a;display:flex;align-items:center;
|
||||
gap:6px;padding:0 18px;z-index:1000;border-bottom:1px solid #2a2a30;}
|
||||
#tabbar .brand{color:#7a7a85;font-size:13px;font-family:"JetBrains Mono",monospace;margin-right:18px;letter-spacing:.05em;}
|
||||
#tabbar button{background:transparent;border:1px solid #34343c;color:#b8b8c0;font-size:13px;padding:7px 16px;
|
||||
border-radius:6px;cursor:pointer;font-family:"Noto Sans SC",sans-serif;transition:.2s;}
|
||||
#tabbar button:hover{border-color:#56566a;color:#fff;}
|
||||
#tabbar button.on{background:#fff;color:#111;border-color:#fff;font-weight:700;}
|
||||
#tabbar .hint{margin-left:auto;color:#55555f;font-size:12px;font-family:"JetBrains Mono",monospace;}
|
||||
|
||||
/* ---- 舞台缩放 ---- */
|
||||
#wrap{position:fixed;top:52px;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;}
|
||||
.stage{width:1920px;height:1080px;transform-origin:center center;position:relative;}
|
||||
.deck{display:none;width:1920px;height:1080px;position:absolute;top:0;left:0;}
|
||||
.deck.show{display:block;}
|
||||
|
||||
/* 每版内部用左右两屏并排展示「封面 + 内容页」缩略 */
|
||||
.pair{display:flex;width:1920px;height:1080px;}
|
||||
.frame{flex:1;height:100%;position:relative;overflow:hidden;}
|
||||
.frame.left{border-right:2px solid rgba(255,255,255,.06);}
|
||||
.flabel{position:absolute;top:22px;left:26px;z-index:5;font-family:"JetBrains Mono",monospace;font-size:18px;
|
||||
letter-spacing:.1em;opacity:.5;}
|
||||
|
||||
.katex{font-size:1em;}
|
||||
|
||||
/* ============ 版 A:深空学术 Deep Space ============ */
|
||||
.A{background:radial-gradient(120% 120% at 75% 15%, #11202a 0%, #0a0e14 55%, #07090d 100%);color:#e8eef2;}
|
||||
.A .glow{position:absolute;width:680px;height:680px;border-radius:50%;
|
||||
background:radial-gradient(circle,rgba(46,196,182,.18),transparent 70%);top:-180px;right:-120px;filter:blur(8px);}
|
||||
.A .cov{padding:120px 100px;height:100%;display:flex;flex-direction:column;justify-content:center;position:relative;}
|
||||
.A .kic{font-family:"JetBrains Mono",monospace;color:#2ec4b6;font-size:26px;letter-spacing:.25em;margin-bottom:28px;}
|
||||
.A .ttl{font-family:"Noto Sans SC";font-weight:900;font-size:118px;line-height:1.04;letter-spacing:-.01em;}
|
||||
.A .ttl em{font-style:normal;color:#2ec4b6;}
|
||||
.A .sub{margin-top:40px;font-size:30px;color:#9fb0ba;font-weight:300;max-width:760px;line-height:1.5;}
|
||||
.A .meta{position:absolute;bottom:80px;left:100px;font-family:"JetBrains Mono",monospace;font-size:20px;color:#56727d;}
|
||||
|
||||
.A2{background:radial-gradient(120% 120% at 20% 90%, #11202a 0%, #0a0e14 60%, #07090d 100%);color:#e8eef2;padding:96px 100px;display:flex;flex-direction:column;}
|
||||
.A2 h2{font-size:74px;font-weight:900;line-height:1.1;}
|
||||
.A2 h2 em{font-style:normal;color:#ffb703;}
|
||||
.A2 .lead{margin-top:18px;font-size:28px;color:#9fb0ba;font-weight:300;}
|
||||
.A2 .body{flex:1;display:flex;align-items:center;gap:60px;margin-top:30px;}
|
||||
.A2 .fbox{flex:1;background:rgba(46,196,182,.06);border:1px solid rgba(46,196,182,.22);border-radius:18px;
|
||||
padding:54px 50px;font-size:46px;color:#eafcf9;text-align:center;}
|
||||
.A2 .fbox .small{font-size:24px;color:#7fd8cf;margin-top:24px;font-family:"JetBrains Mono",monospace;}
|
||||
.A2 .deca{flex:.9;}
|
||||
.A2 .row{display:flex;align-items:center;gap:18px;margin:20px 0;font-size:30px;}
|
||||
.A2 .chip{font-family:"JetBrains Mono",monospace;font-size:24px;padding:6px 16px;border-radius:8px;background:#13343a;color:#2ec4b6;}
|
||||
.A2 .bar{height:14px;border-radius:7px;background:linear-gradient(90deg,#2ec4b6,#ffb703);}
|
||||
|
||||
/* ============ 版 B:黑曜极简 Swiss Mono ============ */
|
||||
.B{background:#0d0d0d;color:#f2f2ef;}
|
||||
.B .cov{padding:110px 110px;height:100%;display:flex;flex-direction:column;position:relative;}
|
||||
.B .topline{display:flex;align-items:center;gap:24px;font-family:"JetBrains Mono",monospace;font-size:20px;color:#888;letter-spacing:.1em;}
|
||||
.B .topline .rule{flex:1;height:1px;background:#3a3a3a;}
|
||||
.B .big{margin-top:auto;font-family:"Noto Serif SC";font-weight:900;font-size:140px;line-height:.98;letter-spacing:-.02em;}
|
||||
.B .big span{display:block;color:#ff5e3a;}
|
||||
.B .cov .foot{margin-top:46px;font-size:28px;color:#9a9a96;font-weight:300;max-width:900px;line-height:1.45;}
|
||||
.B .idx{position:absolute;top:96px;right:110px;font-family:"Space Grotesk";font-size:200px;font-weight:700;color:#161616;line-height:1;}
|
||||
|
||||
.B2{background:#0d0d0d;color:#f2f2ef;padding:96px 110px;display:flex;flex-direction:column;}
|
||||
.B2 .topline{display:flex;align-items:center;gap:24px;font-family:"JetBrains Mono",monospace;font-size:18px;color:#888;letter-spacing:.1em;}
|
||||
.B2 .topline .rule{flex:1;height:1px;background:#3a3a3a;}
|
||||
.B2 h2{margin-top:34px;font-family:"Noto Serif SC";font-weight:900;font-size:86px;line-height:1.02;}
|
||||
.B2 h2 b{color:#ff5e3a;}
|
||||
.B2 .body{flex:1;display:flex;align-items:center;gap:70px;margin-top:20px;}
|
||||
.B2 .fbox{flex:1;border:2px solid #ff5e3a;border-radius:4px;padding:60px 40px;text-align:center;font-size:50px;}
|
||||
.B2 .fbox .small{font-size:24px;color:#bdbdb8;margin-top:26px;font-family:"JetBrains Mono",monospace;letter-spacing:.05em;}
|
||||
.B2 .deca{flex:.85;}
|
||||
.B2 .row{display:flex;align-items:center;gap:20px;font-size:32px;margin:22px 0;font-family:"Noto Sans SC";}
|
||||
.B2 .num{font-family:"Space Grotesk";font-weight:700;font-size:40px;color:#ff5e3a;width:120px;}
|
||||
.B2 .bar{height:10px;background:#ff5e3a;border-radius:2px;}
|
||||
|
||||
/* ============ 版 C:暖纸学刊 Warm Editorial ============ */
|
||||
.C{background:#f4efe6;color:#1d2733;}
|
||||
.C .cov{padding:120px 110px;height:100%;display:grid;grid-template-columns:1fr;align-content:center;position:relative;}
|
||||
.C .tag{font-family:"JetBrains Mono",monospace;font-size:22px;color:#b25d3a;letter-spacing:.2em;margin-bottom:30px;}
|
||||
.C .ttl{font-family:"Noto Serif SC";font-weight:900;font-size:120px;line-height:1.06;color:#16202b;}
|
||||
.C .ttl em{font-style:italic;color:#b25d3a;font-family:"Noto Serif SC";}
|
||||
.C .sub{margin-top:38px;font-size:30px;color:#52606d;font-weight:400;max-width:820px;line-height:1.55;}
|
||||
.C .seal{position:absolute;bottom:90px;right:110px;width:140px;height:140px;border:2px solid #b25d3a;border-radius:50%;
|
||||
display:flex;align-items:center;justify-content:center;text-align:center;font-family:"Noto Serif SC";color:#b25d3a;font-size:20px;line-height:1.3;}
|
||||
.C .ln{position:absolute;left:110px;right:110px;bottom:70px;height:1px;background:#cbbfa9;}
|
||||
|
||||
.C2{background:#f4efe6;color:#1d2733;padding:96px 110px;display:flex;flex-direction:column;}
|
||||
.C2 .tag{font-family:"JetBrains Mono",monospace;font-size:20px;color:#b25d3a;letter-spacing:.18em;}
|
||||
.C2 h2{font-family:"Noto Serif SC";font-weight:900;font-size:80px;line-height:1.08;margin-top:14px;color:#16202b;}
|
||||
.C2 h2 em{font-style:italic;color:#b25d3a;}
|
||||
.C2 .body{flex:1;display:flex;align-items:center;gap:64px;margin-top:24px;}
|
||||
.C2 .fbox{flex:1;background:#fbf8f1;border:1px solid #d8ccb4;border-radius:10px;padding:56px 46px;text-align:center;
|
||||
font-size:48px;color:#1d2733;box-shadow:0 18px 40px -28px rgba(80,60,30,.5);}
|
||||
.C2 .fbox .small{font-size:23px;color:#8a6b4a;margin-top:24px;font-family:"JetBrains Mono",monospace;}
|
||||
.C2 .deca{flex:.9;}
|
||||
.C2 .row{display:flex;align-items:center;gap:18px;font-size:30px;margin:20px 0;}
|
||||
.C2 .chip{font-family:"Noto Serif SC";font-weight:600;font-size:26px;color:#b25d3a;width:90px;}
|
||||
.C2 .bar{height:12px;border-radius:6px;background:linear-gradient(90deg,#b25d3a,#d9a441);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="tabbar">
|
||||
<span class="brand">HERMITE · 风格预览</span>
|
||||
<button class="on" data-deck="A">A · 深空学术</button>
|
||||
<button data-deck="B">B · 黑曜极简</button>
|
||||
<button data-deck="C">C · 暖纸学刊</button>
|
||||
<span class="hint">每版=封面 + 典型内容页(OU 衰减)|选定后展开完整版</span>
|
||||
</div>
|
||||
|
||||
<div id="wrap">
|
||||
<div class="stage" id="stage">
|
||||
|
||||
<!-- ===================== 版 A ===================== -->
|
||||
<div class="deck show" data-deck="A">
|
||||
<div class="pair">
|
||||
<div class="frame left A">
|
||||
<span class="flabel">封面</span>
|
||||
<div class="glow"></div>
|
||||
<div class="cov">
|
||||
<div class="kic">SELF-SUPERVISED · 谱分解</div>
|
||||
<div class="ttl">Hermite<br><em>多项式</em></div>
|
||||
<div class="sub">高斯世界里的「频率分解」工具——配套播客讲解</div>
|
||||
<div class="meta">EP.01 · 时长 7:43 · LeJEPA 理论基石</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="frame A2">
|
||||
<span class="flabel">内容页</span>
|
||||
<h2>为什么<em>线性</em>成分最值钱</h2>
|
||||
<div class="lead">OU 过程让相关性按阶数指数衰减</div>
|
||||
<div class="body">
|
||||
<div class="fbox">
|
||||
$\mathbb{E}[He_n(z')\,He_n(z)] = \rho^{\,n} \cdot n!$
|
||||
<div class="small">阶数越高 → 相关性掉得越快</div>
|
||||
</div>
|
||||
<div class="deca">
|
||||
<div class="row"><span class="chip">1 阶</span><div class="bar" style="width:88%"></div></div>
|
||||
<div class="row"><span class="chip">2 阶</span><div class="bar" style="width:48%"></div></div>
|
||||
<div class="row"><span class="chip">3 阶</span><div class="bar" style="width:24%"></div></div>
|
||||
<div class="row" style="font-size:24px;color:#9fb0ba;margin-top:30px;">最优解:只保留线性成分 → 线性可识别性</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== 版 B ===================== -->
|
||||
<div class="deck" data-deck="B">
|
||||
<div class="pair">
|
||||
<div class="frame left B">
|
||||
<span class="flabel" style="color:#fff">封面</span>
|
||||
<div class="cov">
|
||||
<div class="idx">He</div>
|
||||
<div class="topline"><span>谱分解 / SPECTRAL</span><span class="rule"></span><span>EP.01</span></div>
|
||||
<div class="big">埃尔米特<span>多项式</span></div>
|
||||
<div class="foot">高斯世界里的频率分解工具 —— 配套播客讲解 · 7 分 43 秒</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="frame B2">
|
||||
<span class="flabel" style="color:#fff">内容页</span>
|
||||
<div class="topline"><span>05 / OU 衰减</span><span class="rule"></span><span>ρⁿ</span></div>
|
||||
<h2>为什么<b>线性</b>成分最值钱</h2>
|
||||
<div class="body">
|
||||
<div class="fbox">
|
||||
$\mathbb{E}[He_n(z')\,He_n(z)] = \rho^{\,n} n!$
|
||||
<div class="small">阶数越高 → 衰减越快</div>
|
||||
</div>
|
||||
<div class="deca">
|
||||
<div class="row"><span class="num">ρ¹</span><div class="bar" style="width:90%"></div></div>
|
||||
<div class="row"><span class="num">ρ²</span><div class="bar" style="width:46%"></div></div>
|
||||
<div class="row"><span class="num">ρ³</span><div class="bar" style="width:22%"></div></div>
|
||||
<div class="row" style="font-size:26px;color:#9a9a96;margin-top:28px;">→ 只留线性 = 线性可识别性</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== 版 C ===================== -->
|
||||
<div class="deck" data-deck="C">
|
||||
<div class="pair">
|
||||
<div class="frame left C">
|
||||
<span class="flabel" style="color:#b25d3a">封面</span>
|
||||
<div class="cov">
|
||||
<div class="tag">SPECTRAL DECOMPOSITION · 第一讲</div>
|
||||
<div class="ttl">Hermite <em>多项式</em></div>
|
||||
<div class="sub">高斯世界里的「频率分解」工具<br>配套播客讲解 · 全长 7 分 43 秒</div>
|
||||
<div class="seal">谱<br>分解</div>
|
||||
<div class="ln"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="frame C2">
|
||||
<span class="flabel" style="color:#b25d3a">内容页</span>
|
||||
<div class="tag">05 · OU 过程与衰减</div>
|
||||
<h2>为什么<em>线性</em>成分最值钱</h2>
|
||||
<div class="body">
|
||||
<div class="fbox">
|
||||
$\mathbb{E}[He_n(z')\,He_n(z)] = \rho^{\,n}\, n!$
|
||||
<div class="small">阶数越高,相关性指数衰减</div>
|
||||
</div>
|
||||
<div class="deca">
|
||||
<div class="row"><span class="chip">ρ¹</span><div class="bar" style="width:90%"></div></div>
|
||||
<div class="row"><span class="chip">ρ²</span><div class="bar" style="width:46%"></div></div>
|
||||
<div class="row"><span class="chip">ρ³</span><div class="bar" style="width:22%"></div></div>
|
||||
<div class="row" style="font-size:24px;color:#6b5640;margin-top:28px;">最优策略:只保留线性 → 线性可识别性</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 舞台自适应缩放
|
||||
function fit(){
|
||||
const s=Math.min((window.innerWidth)/1920,(window.innerHeight-52)/1080);
|
||||
document.getElementById('stage').style.transform=`scale(${s})`;
|
||||
}
|
||||
window.addEventListener('resize',fit); fit();
|
||||
|
||||
// tab 切换
|
||||
document.querySelectorAll('#tabbar button').forEach(b=>{
|
||||
b.addEventListener('click',()=>{
|
||||
const d=b.dataset.deck;
|
||||
document.querySelectorAll('#tabbar button').forEach(x=>x.classList.toggle('on',x===b));
|
||||
document.querySelectorAll('.deck').forEach(x=>x.classList.toggle('show',x.dataset.deck===d));
|
||||
});
|
||||
});
|
||||
|
||||
// KaTeX 渲染
|
||||
window.addEventListener('DOMContentLoaded',()=>{
|
||||
function r(){ if(window.renderMathInElement){
|
||||
renderMathInElement(document.body,{delimiters:[{left:'$',right:'$',display:false}]});
|
||||
} else setTimeout(r,120); }
|
||||
r();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,405 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hermite 多项式 · 高斯世界里的频率分解工具</title>
|
||||
|
||||
<!--
|
||||
设计系统(深空学术 Deep Space)
|
||||
背景:radial #11202a → #0a0e14 → #07090d(暗青底,章节用不同 glow 方位制造节奏)
|
||||
色:primary 青绿 #2ec4b6 / accent 琥珀 #ffb703 / 文本 #e8eef2 / 次级 #9fb0ba / mono #56727d
|
||||
字:标题 Noto Sans SC 900;正文 Noto Sans SC 300/500;公式与标签 JetBrains Mono / KaTeX
|
||||
字号:H1 110-118 / H2 70-78 / 卡标题 34-40 / 正文 26-30 / mono 18-22(震撼态,整体偏大)
|
||||
节奏:封面=大字+留白;类比/正交=卡片;OU/谱权重=图形化条;小结=网格。
|
||||
-->
|
||||
|
||||
<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:116px;line-height:1.03;letter-spacing:-.01em;}
|
||||
h1 em{font-style:normal;color:var(--teal);}
|
||||
h2{font-weight:900;font-size:74px;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:1300px;}
|
||||
|
||||
.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:42px 38px;}
|
||||
.card .ct{font-size:36px;font-weight:700;margin-bottom:18px;}
|
||||
.card .cb{font-size:26px;color:var(--ink2);font-weight:300;line-height:1.55;}
|
||||
.card .cf{margin-top:22px;font-size:30px;color:var(--teal);}
|
||||
|
||||
/* 封面 */
|
||||
.cover{justify-content:center;}
|
||||
.cover .meta{position:absolute;bottom:72px;left:110px;font-family:"JetBrains Mono",monospace;font-size:21px;color:var(--mono);}
|
||||
|
||||
/* 衰减条 */
|
||||
.barrow{display:flex;align-items:center;gap:20px;margin:22px 0;font-size:30px;}
|
||||
.barrow .chip{font-family:"JetBrains Mono",monospace;font-size:24px;padding:7px 16px;border-radius:8px;
|
||||
background:#13343a;color:var(--teal);min-width:96px;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:24px;color:var(--ink2);min-width:70px;}
|
||||
|
||||
/* 表格类比 */
|
||||
table.cmp{width:100%;border-collapse:collapse;font-size:28px;}
|
||||
table.cmp th,table.cmp td{padding:24px 26px;text-align:left;border-bottom:1px solid var(--line);}
|
||||
table.cmp th{font-family:"JetBrains Mono",monospace;font-size:22px;color:var(--mono);letter-spacing:.08em;font-weight:400;}
|
||||
table.cmp td.k{color:var(--ink2);font-weight:300;width:24%;}
|
||||
table.cmp td .fr{color:var(--teal);}
|
||||
table.cmp td .he{color:var(--amber);}
|
||||
|
||||
/* 谱权重饼图 */
|
||||
.pie{width:340px;height:340px;border-radius:50%;flex-shrink:0;}
|
||||
.legend{margin-left:60px;}
|
||||
.legend .li{display:flex;align-items:center;gap:18px;font-size:30px;margin:18px 0;}
|
||||
.legend .dot{width:26px;height:26px;border-radius:6px;}
|
||||
|
||||
/* 列表 */
|
||||
ul.pts{list-style:none;}
|
||||
ul.pts li{font-size:31px;font-weight:300;color:var(--ink);line-height:1.5;margin:24px 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;}
|
||||
|
||||
/* 小结网格 */
|
||||
.grid4{display:grid;grid-template-columns:1fr 1fr;gap:30px;width:100%;}
|
||||
.grid4 .g{background:rgba(255,255,255,.03);border:1px solid var(--line);border-radius:16px;padding:34px 34px;}
|
||||
.grid4 .g .n{font-family:"Space Grotesk";font-weight:700;font-size:40px;color:var(--teal);}
|
||||
.grid4 .g .t{font-size:28px;margin-top:8px;line-height:1.45;color:var(--ink);font-weight:300;}
|
||||
.grid4 .g .t b{color:var(--amber);font-weight:700;}
|
||||
|
||||
/* 导航键(在 stage 外,避免被 scale 压缩)*/
|
||||
.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;}
|
||||
</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.01</div>
|
||||
<h1 style="margin-top:34px;">Hermite<br><em>多项式</em></h1>
|
||||
<p class="lead" style="margin-top:38px;">高斯世界里的「频率分解」工具<br>—— 配套播客讲解,从直觉到 LeJEPA 的理论基石</p>
|
||||
<div class="meta">配套音频 7:43 · 双主播对话 · 数学不可怕,可怕的是没人陪你拆积木</div>
|
||||
</section>
|
||||
|
||||
<!-- 2 动机 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>01 · 动机</div>
|
||||
<h2>我们需要一把<em>拆函数的刀</em></h2>
|
||||
<div class="content">
|
||||
<ul class="pts">
|
||||
<li>LeJEPA 的证明要回答:编码器的表示里,<b>哪一部分</b>对「正样本对」的相关性贡献最大?</li>
|
||||
<li>正样本对 = 同一内容的两个视角,模型希望它们的表示尽量像。</li>
|
||||
<li>要回答这个问题,得先能把<b>任意函数拆成一块块</b>——就像傅里叶把声音拆成不同频率的波。</li>
|
||||
<li>在高斯(钟形)分布下,这套「积木」就是 <b>Hermite 多项式</b>。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 3 傅里叶类比 -->
|
||||
<section class="slide">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>02 · 类比</div>
|
||||
<h2>傅里叶之于周期,<em>Hermite</em> 之于高斯</h2>
|
||||
<div class="content">
|
||||
<table class="cmp">
|
||||
<tr><th>维度</th><th>傅里叶级数</th><th>Hermite 展开</th></tr>
|
||||
<tr><td class="k">适用场景</td><td><span class="fr">周期函数</span></td><td><span class="he">高斯分布下的函数</span></td></tr>
|
||||
<tr><td class="k">基函数</td><td><span class="fr">$\sin nx,\ \cos nx$</span></td><td><span class="he">$He_0,He_1,He_2,\dots$</span></td></tr>
|
||||
<tr><td class="k">正交性</td><td><span class="fr">在区间上积分为 0</span></td><td><span class="he">在高斯期望下为 0</span></td></tr>
|
||||
<tr><td class="k">完备性</td><td><span class="fr">任意周期函数可展开</span></td><td><span class="he">任意 $L^2(\gamma)$ 函数可展开</span></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 4 定义与递推 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>03 · 定义</div>
|
||||
<h2>积木长什么样 & <em>递推</em>搭出来</h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="fbox" style="flex:1;font-size:34px;line-height:2;">
|
||||
$He_0=1,\quad He_1=z$<br>
|
||||
$He_2=z^2-1$<br>
|
||||
$He_3=z^3-3z$<br>
|
||||
$He_4=z^4-6z^2+3$
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<div class="fbox" style="font-size:40px;">
|
||||
$He_{n+1}(z)=z\,He_n(z)-n\,He_{n-1}(z)$
|
||||
<div class="small">下一块 = z × 当前块 − 阶数 × 前一块</div>
|
||||
</div>
|
||||
<p class="lead" style="margin-top:30px;font-size:26px;">验证:$z\cdot z-1\cdot 1=z^2-1=He_2$ ✓</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 5 正交性 -->
|
||||
<section class="slide">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>04 · 核心性质</div>
|
||||
<h2>正交性:不同阶数<em>互不干扰</em></h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="fbox" style="flex:1.1;font-size:34px;">
|
||||
$\mathbb{E}\big[He_m(z)\,He_n(z)\big]=\begin{cases}n! & m=n\\[4pt]0 & m\neq n\end{cases}$
|
||||
<div class="small">$z\sim\mathcal N(0,1)$,期望对钟形曲线取</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<div class="card">
|
||||
<div class="ct">最小例子验证</div>
|
||||
<div class="cb" style="font-size:28px;line-height:1.8;">
|
||||
$\mathbb{E}[He_1\cdot He_2]=\mathbb{E}[z^3-z]$<br>
|
||||
高斯的奇数阶矩为 0 → $=0-0=0$ ✓
|
||||
</div>
|
||||
<div class="cf" style="font-size:26px;">就像不同频率的正弦波互相正交</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 6 完备性 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>05 · 完备性</div>
|
||||
<h2>任意函数都能<em>拆开重组</em></h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="fbox" style="flex:1;font-size:36px;">
|
||||
$h(z)=\sum_{d=0}^{\infty}c_d\,He_d(z)$
|
||||
<div class="small">$c_d=\dfrac{\mathbb{E}[h(z)\,He_d(z)]}{d!}$</div>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<p class="lead" style="font-size:30px;">只要 $\mathbb{E}[h(z)^2]<\infty$(有限能量),就能写成这些积木的<b style="color:var(--teal)">加权叠加</b>。</p>
|
||||
<p class="lead" style="font-size:30px;margin-top:24px;">就像把一个向量沿正交坐标轴分解——每个轴上投影多少,系数 $c_d$ 就有多大。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 7 OU 衰减 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>06 · 关键转折</div>
|
||||
<h2>OU 过程让相关性<em>按阶数指数衰减</em></h2>
|
||||
<div class="content" style="gap:60px;">
|
||||
<div class="fbox" style="flex:.95;font-size:34px;">
|
||||
$\mathbb{E}[He_n(z')He_n(z)]=\rho^{\,n}\cdot n!$
|
||||
<div class="small">$z'=\rho z+\sqrt{1-\rho^2}\,\eta,\quad 0<\rho<1$</div>
|
||||
</div>
|
||||
<div style="flex:1.05;">
|
||||
<div class="barrow"><span class="chip">1 阶</span><div class="track"><i style="width:90%"></i></div><span class="val">ρ¹</span></div>
|
||||
<div class="barrow"><span class="chip">2 阶</span><div class="track"><i style="width:50%"></i></div><span class="val">ρ²</span></div>
|
||||
<div class="barrow"><span class="chip">3 阶</span><div class="track"><i style="width:26%"></i></div><span class="val">ρ³</span></div>
|
||||
<p class="lead" style="font-size:26px;margin-top:18px;">阶数越高 → 相关性掉得越快 → 高阶非线性几乎被冲淡。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 8 线性可识别性 -->
|
||||
<section class="slide">
|
||||
<div class="glow"></div>
|
||||
<div class="kicker"><span class="ln"></span>07 · 结论</div>
|
||||
<h2>最优解:<em>只保留线性成分</em></h2>
|
||||
<div class="content">
|
||||
<ul class="pts">
|
||||
<li>对齐损失要<b>最大化</b>正样本对的相关性。</li>
|
||||
<li>线性成分贡献 $\rho$;非线性成分贡献 $\rho^d<\rho\ (d\ge 2)$,被打了折扣。</li>
|
||||
<li>于是最优策略就是:<b>留下线性,丢掉所有非线性</b>。</li>
|
||||
<li>模型被这个机制温柔地逼成一个本质线性的表示 —— 这就是<b>线性可识别性</b>,整套定理的直觉来源。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 9 谱权重 -->
|
||||
<section class="slide posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>08 · 度量工具</div>
|
||||
<h2>谱权重:一张<em>能量饼图</em></h2>
|
||||
<div class="content">
|
||||
<div class="pie" style="background:conic-gradient(var(--teal) 0 62%, var(--amber) 62% 84%, #4a6b74 84% 100%);"></div>
|
||||
<div class="legend">
|
||||
<div class="li"><span class="dot" style="background:var(--teal)"></span>线性 $w_1$ —— 我们想要的成分</div>
|
||||
<div class="li"><span class="dot" style="background:var(--amber)"></span>二阶 $w_2$ —— 被 OU 衰减更多</div>
|
||||
<div class="li"><span class="dot" style="background:#4a6b74"></span>更高阶 —— 衰减最狠</div>
|
||||
<div class="li" style="font-size:24px;color:var(--ink2);margin-top:14px;font-family:'JetBrains Mono',monospace;">
|
||||
$w_d\ge 0,\quad w_0=0,\quad \textstyle\sum w_d=1$</div>
|
||||
<div class="li" style="font-size:25px;color:var(--ink2);">理想状态:整张饼都给线性那一块。</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 10 小结 -->
|
||||
<section class="slide posCR">
|
||||
<div class="glow"></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">Hermite 是高斯世界的<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">OU 过程使相关性按 <b>ρᵈ</b> 指数衰减,高阶削减更狠。</div></div>
|
||||
<div class="g"><div class="n">04</div><div class="t">最大化相关性 → 只留线性 → <b>线性可识别性</b>。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 11 尾页 -->
|
||||
<section class="slide cover posBL">
|
||||
<div class="glow amber"></div>
|
||||
<div class="kicker"><span class="ln"></span>下一站</div>
|
||||
<h1 style="font-size:92px;margin-top:30px;">Mehler 公式<br><em>把直觉坐实</em></h1>
|
||||
<p class="lead" style="margin-top:34px;font-size:30px;">下一讲深入 $\rho^d$ 衰减的来源 —— 它来自 OU 过程的转移核在 Hermite 基下的展开。</p>
|
||||
<div class="meta">谢谢收听 · 配套音频 hermite_polynomials-podcast.mp3 · 我们下次见</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
<button id="prev">← 上一页</button>
|
||||
<span class="pg" id="pg">1 / 11</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 = 'hermite_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>Hermite 多项式 · 谱分解</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);
|
||||
});
|
||||
|
||||
// KaTeX 渲染
|
||||
function renderMath(){
|
||||
if (window.renderMathInElement){
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left:'$$', right:'$$', display:true},
|
||||
{left:'$', right:'$', display:false}
|
||||
],
|
||||
throwOnError:false
|
||||
});
|
||||
} else { setTimeout(renderMath, 120); }
|
||||
}
|
||||
// URL 参数:?page=N(1-based 定位单页,用于截图/导出)、?clean=1(隐藏 UI)
|
||||
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); });
|
||||
// 兜底:若 DOMContentLoaded 已过
|
||||
if (document.readyState !== 'loading') { renderMath(); show(cur); }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""用火山引擎 v3 单向流式 TTS(/api/v3/tts/unidirectional)把双人对话脚本合成为一个 MP3。
|
||||
|
||||
鉴权:请求头 x-api-key + X-Api-Resource-Id(新版方式,区别于旧版 Bearer;{token})。
|
||||
用法:
|
||||
VOLC_API_KEY=<你的key> python3 synth_volc_v3.py \
|
||||
--script-file hermite_polynomials-script.json \
|
||||
--output-file output/hermite_polynomials-podcast.mp3
|
||||
"""
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
|
||||
ENDPOINT = "https://openspeech.bytedance.com/api/v3/tts/unidirectional"
|
||||
RESOURCE_ID = "volc.service_type.10029"
|
||||
|
||||
# 男女两个大模型情感音色(v2 mars bigtts)
|
||||
VOICE_MALE = "zh_male_beijingxiaoye_emo_v2_mars_bigtts"
|
||||
VOICE_FEMALE = "zh_female_roumeinvyou_emo_v2_mars_bigtts"
|
||||
|
||||
ADDITIONS = json.dumps(
|
||||
{
|
||||
"disable_markdown_filter": True,
|
||||
"enable_language_detector": True,
|
||||
"enable_latex_tn": True,
|
||||
"disable_default_bit_rate": True,
|
||||
"max_length_to_filter_parenthesis": 0,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def synth_line(api_key: str, text: str, speaker: str, retries: int = 3) -> bytes:
|
||||
"""合成一句,返回 mp3 字节。失败抛异常。"""
|
||||
headers = {
|
||||
"x-api-key": api_key,
|
||||
"X-Api-Resource-Id": RESOURCE_ID,
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {
|
||||
"req_params": {
|
||||
"text": text,
|
||||
"speaker": speaker,
|
||||
"additions": ADDITIONS,
|
||||
"audio_params": {"format": "mp3", "sample_rate": 24000},
|
||||
}
|
||||
}
|
||||
last_err = None
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
r = requests.post(ENDPOINT, headers=headers, json=payload, timeout=60)
|
||||
if r.status_code != 200:
|
||||
last_err = f"HTTP {r.status_code}: {r.text[:200]}"
|
||||
time.sleep(1.0 + attempt)
|
||||
continue
|
||||
# 响应体可能是单个 JSON,也可能是多段(流式)。逐段解析 data 累积。
|
||||
audio = b""
|
||||
text_body = r.text.strip()
|
||||
# 尝试按行分割的多 JSON(流式 unidirectional 常见)
|
||||
chunks = []
|
||||
for line in text_body.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
chunks.append(line)
|
||||
if not chunks:
|
||||
chunks = [text_body]
|
||||
ok = False
|
||||
for c in chunks:
|
||||
try:
|
||||
obj = json.loads(c)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if obj.get("code") not in (0, None):
|
||||
last_err = f"code={obj.get('code')} msg={obj.get('message')}"
|
||||
continue
|
||||
d = obj.get("data")
|
||||
if d:
|
||||
audio += base64.b64decode(d)
|
||||
ok = True
|
||||
if ok and audio:
|
||||
return audio
|
||||
last_err = last_err or "no audio data in response"
|
||||
time.sleep(1.0 + attempt)
|
||||
except Exception as e: # noqa
|
||||
last_err = str(e)
|
||||
time.sleep(1.0 + attempt)
|
||||
raise RuntimeError(f"合成失败: {last_err}")
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--script-file", required=True)
|
||||
ap.add_argument("--output-file", required=True)
|
||||
args = ap.parse_args()
|
||||
|
||||
api_key = os.getenv("VOLC_API_KEY")
|
||||
if not api_key:
|
||||
print("ERROR: 需要环境变量 VOLC_API_KEY", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
with open(args.script_file, encoding="utf-8") as f:
|
||||
script = json.load(f)
|
||||
lines = script["lines"]
|
||||
total = len(lines)
|
||||
print(f"加载脚本:{script.get('title','')},共 {total} 句")
|
||||
|
||||
os.makedirs(os.path.dirname(args.output_file) or ".", exist_ok=True)
|
||||
|
||||
all_audio = b""
|
||||
ok_count = 0
|
||||
for i, line in enumerate(lines, 1):
|
||||
speaker = VOICE_MALE if line["speaker"] == "male" else VOICE_FEMALE
|
||||
text = line["paragraph"]
|
||||
try:
|
||||
audio = synth_line(api_key, text, speaker)
|
||||
all_audio += audio
|
||||
ok_count += 1
|
||||
print(f"[{i}/{total}] {line['speaker']:6s} ok ({len(audio)} bytes)")
|
||||
except Exception as e: # noqa
|
||||
print(f"[{i}/{total}] {line['speaker']:6s} FAIL: {e}")
|
||||
time.sleep(0.15) # 轻微限速
|
||||
|
||||
if not all_audio:
|
||||
print("ERROR: 没有任何一句合成成功", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(args.output_file, "wb") as f:
|
||||
f.write(all_audio)
|
||||
print(f"\n完成:{ok_count}/{total} 句成功,已写出 {args.output_file}({len(all_audio)} bytes)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||