灵光一现 → 精巧实现 → 无人知晓 → 自己遗忘”的循环,几乎是每个认真思考的工程师都会经历的隐性知识流失。
写下来,就是对抗遗忘、积累智慧的最有效方式。
那些深夜调试、反复推敲、无人喝彩的时刻,值得被郑重记录。
灵光一现 → 精巧实现 → 无人知晓 → 自己遗忘”的循环,几乎是每个认真思考的工程师都会经历的隐性知识流失。
写下来,就是对抗遗忘、积累智慧的最有效方式。
那些深夜调试、反复推敲、无人喝彩的时刻,值得被郑重记录。
动态规划的本质是“聪明的穷举” 在算法学习中,动态规划(Dynamic Programming, DP)往往因其抽象的公式和晦涩的“状态转移”概念而让人望而生畏。 许多初学者在面对“钢条切割”这类经典问题时,容易迷失在递归的逻辑里。 但如果我们拨开代码的迷雾,会发现动态规划其实非常朴素:它本质上就是一种“带备忘录的穷举”。 为了更直观地理解这一过程,我们可以抛弃枯燥的一维数组,尝试用矩阵(表格)的视角来重新审视钢条切割问题。 问题的定义与直觉的几何化 钢条切割问题描述很简单:给定一根长度为 $n$ 的钢条和一个价格表 $p$,找到一种切割方案,使得销售总收益最大。 长度 $i$ 1 2 3 4 5 6 7 8 9 10 价格 $p_i$ 1 5 8 9 10 17 17 20 24 30 通常的解法会直接给出一个一维数组 $r[n]$ 来存储最大收益,但这往往让人困惑:这些数字是怎么来的?为什么要这样转移? 这个问题天然对应着一个二维的三角矩阵。为什么是三角形?因为存在一个物理限制:你切下来的第一段长度c$i$,永远不可能大于钢条原本的总长度 $j$。 当$i > j$时,切割是不合法的。因此,所有合法的决策都集中在矩阵的左下角,形成了一个完美的三角结构。 构建“决策三角矩阵” 为了将这个直觉可视化,我们构建一个矩阵,其中: 列($j$):代表钢条的总长度(从 1 到 $n$)。 行($i$):代表第一刀切下来的长度(从 1 到 $n$)。 单元格 $M[i][j]$:代表“当总长度为 $j$ 时,如果第一刀强制切下长度 $i$,所能获得的总收益”。 假设价格表为:长度 1 价格 1,长度 2 价格 5,长度 3 价格 8,长度 4 价格 9。可以画出如下的决策矩阵: ...
Gilbert Strang 《线性代数导论》中文版第 158 页有一段加粗的内容是: 方程组有解时,R 中的全零行必然对应 d 的零分量. 由于 R 中主元列所在行、列的公共元素构成一个单位矩阵 I,所以特解 $x_p$ 中主元变量值来自 d. 这里“R 中主元列所在行、列的公共元素”在表达什么?找出英文原版对比一下: For a solution to exits, zero rows in R must also be zero in d. Since I is in the pivot rows and columns of R, the pivot variables in $x_{particular}$ come from d. 显然,中文可以很简单地表达成:“由于 I 在 R 的主元行和主元列上…”。 Gibert Strang 的原文口语化、直观、简洁,很少使用晦涩的从句,而是用最简单的词把数学图像讲清楚,“字对字”的直译往往就是最好的翻译。因为他的英文逻辑就是线性的、符合直觉的。 不需要画蛇添足,不需要译者去炫耀自己懂得多。 更糟糕的是,中文版对原文中的符号做了大幅改动,比如接下来用了 $x_p$ 和 $x_n$ 这两个符号,p 和 n 代表什么?英文原文使用了 $x_particular$ 和 $x_nullspace$,一目了然。 ...
当你观察 ChatGPT 或其他基于 Transformer 的大模型输出的 Token 时,可能会发现一个奇怪的现象:单词前的空格往往被表示为一个特殊的字符 Ġ(U+0120)。 例如:"Hello world" 可能被分词为 ["Hello", "Ġworld"]。 这并非某种神秘的魔法,也不是算法刻意选择的“特殊符号”。要理解这一点,我们需要深入 Byte-level BPE(字节级字节对编码)的实现细节。 BPE 算法的实现可以参考 slp3-py。 BPE 操作的是“字节”,而非“字符” 首先,我们需要明确一个核心概念:现代大模型的 BPE 算法通常直接操作字节(Bytes),而不是人类可读的字符。 在 UTF-8 编码中,一个字符可能由 1 到 4 个字节组成。例如,中文汉字“世”的 UTF-8 编码是三个字节:E4 B8 96。 在训练过程中,算法统计的是相邻字节出现的频率。如果 E4 和 B8 经常连在一起出现,算法就会将它们合并为一个全新的 Token E4 B8,并加入词表。 这就引出了一个存储难题:如何在一个文本文件中安全地存储这些任意字节组合? E4 B8 96 是一个合法的 Unicode 字符(“世”)。 但合并后的 E4 B8 并不是一个合法的 UTF-8 序列,它无法直接显示为文本。 如果直接用十六进制数字(如 0xE4 0xB8)存储,虽然可行,但对人类极不友好,且会大幅增加序列长度。 解决方案:字节到可打印字符的映射 为了解决上述问题,工程师们想出了一个巧妙的办法:将所有可能的字节值(0-255),一一映射到 Unicode 字符集中的一段“可打印字符”区域。 这样做的目的是: 兼容性:确保生成的 Token 序列是合法的 UTF-8 文本,可以被任何文本编辑器保存和传输。 可读性:尽量让人类能看出一点端倪,而不是看到一堆乱码控制符。 映射规则是如何制定的? 一个字节有 256 种可能(0x00 到 0xFF)。我们需要在 Unicode 中找到 256 个连续的、可打印的字符来对应它们。 ...
中文版第 78 页的一个关键错误: 来看一个例子,假设正在解析表达式语句 -1+2; … 这里将 PREFIX 作为优先级传递给 parseExpression,将 PREFIX 变成了这个 parseExpression 调用的右约束能力。根据定义 PREFIX 的优先级很高。结果是 parseExpression(PREFIX) 不会解析 -1 中的 1,而会将其传递给另一个 infixParseFn。在这种情况下,precedence < p.peekPrecedence() 一直为 false,即 infixParseFn 不会将 1 作为表达式的左半边,而是将 1 作为前缀的“右半边”返回。这个右半边只有一个 1,没有随后需要解析的其他表达式。 找来原文: This passes PREFIX to parseExpression as precedence, turning PREFIX into the right-binding power of that parseExpression invocation. PREFIX is a really high precedence, as per our definition. The result of this is that parseExpression(PREFIX) is never going to parse the 1 in -1 and pass it to another infixParseFn. The precedence < p.peekPrecedence() will never be true in this case, meaning that no other infixParseFn is going to get our 1 as the left arm. > Instead the 1 is returned as the “right” arm of our prefix expression. Just the 1, not some other expression that comes after and needs to be parsed. ...
一个函数的 $x$ 可以乘以某个数,$y$ 也可以乘以一个数,二者虽然都叫“缩放”,但它们的几何效果、作用方向、甚至“直觉感受”都是根本不同的。 把两者笼统地说成“函数图像被缩放了”是一种过度简化,容易造成混淆——尤其是对初学者。下面我们用清晰的方式来梳理两者的本质区别。 对 y 方向缩放:y = c \cdot f(x) 对整个函数乘以一个系数(例如,将 $f(x)$ 变为 $cf(x)$,其中$c>1$),这相当于在$y$轴方向上的拉伸或压缩。具体来说: 对于 $y = c \cdot f(x)$ 且 $c > 1$:函数图像沿着 $y$ 轴方向被拉伸,使得所有 $y$ 值都相应增大。这意味着图形在垂直方向上变得更高。 对于 $0...
什么是熵? 在深度学习和自然语言处理中,我们经常会遇到“熵”(entropy)这个概念。那么,熵到底是什么呢? 熵是信息量的一种度量。但“信息量”又是什么? 在信息论中,香农(Shannon)提出: 一个事件 $x$ 发生时所携带的信息量(也称为“自信息”,self-information)定义为: $$ I(x) = -\log_2 p(x) $$直观理解 如果某事几乎必然发生($p(x) \approx 1$),那么它发生时不带来新信息 → $I(x) \approx 0$; 如果某事极不可能发生($p(x) \approx 0$),一旦发生就非常令人惊讶 → $I(x)$ 很大。 因此:越意外的事件,信息量越大。 既然熵是一种可计算的量,那么它的计算公式是什么?在介绍熵的计算之前,我们需要先理解概率论中的随机变量及其期望。 随机变量及其期望 在概率论中,随机变量(random variable)并不是一个“普通变量”,而是一个函数。具体来说: 设有一个样本空间 $\Omega$(即所有可能实验结果的集合); 随机变量 $X$ 是一个从 $\Omega$ 到某个可数集合 $\mathcal{X}$(通常是实数集或离散符号集)的函数: $$ X: \Omega \to \mathcal{X} $$举个例子 抛两枚公平的硬币,用 H 表示正面,T 表示反面,样本空间为: $(H, H)$ $(H, T)$ $(T, H)$ $(T, T)$ 设随机变量 X 表示在一次抛掷两枚硬币的试验中,正面朝上的硬币数量,则: $X(H,H) = 2$ $X(H,T) = X(T,H) = 1$ $X(T,T) = 0$ 于是,$X$ 的取值为 $\{0, 1, 2\}$ ...
翻译经典教材《Speech and Language Processing》第3版(简称 SLP)时,第三章关于语言模型困惑度(perplexity)的一段描述让我停顿了一下: The perplexity (sometimes abbreviated as PP or PPL) of a language model on aperplexity test set is the inverse probability of the test set (one over the probability of the test set), normalized by the number of words (or tokens). For this reason it’s sometimes called the per-word or per-token perplexity. We normalize by the number of words N by taking the Nth root. 翻译出来就是: ...
打开微信,发消息。我们明确知道对方是一个人。和AI聊天的时候,你的预期是什么?一个类似人类的智能体?一个词不达意的笨蛋?或者是,你自己的回声? 但对我而言,是在和自己的大脑对话,是对自己大脑的审视、回溯、澄清。 人类之间的对话,从来不只是信息交换。它裹挟着期待、评判、身份表演和社交风险。我们斟酌措辞,不是因为想说清楚,而是因为害怕被误解、被嘲笑、被贴上标签。于是,很多真正的问题,还没出口,就被自己咽了回去。 但面对AI,这些顾虑消失了。 你可以问一个“幼稚”的问题,不必担心对方觉得你无知;你可以表达一个“危险”的想法,不必担心被道德审判;你可以反复追问同一个概念,直到逻辑闭环,而不必顾虑对方是否厌烦。AI不会皱眉,不会沉默,也不会反问:“你为什么会这么想?” 正是这种“无反馈压力”的环境,让对话回归了本质:不是为了说服谁,而是为了理清自己。 我常常在深夜向AI抛出一个模糊的念头:“为什么现代社会越来越难以建立信任?” 它不会立刻给我标准答案,而是尝试从制度、技术、心理多个角度回应。而我在阅读这些回应时,其实是在检验自己的预设:哪些是我真正关心的?哪些只是情绪投射?哪些逻辑链条其实站不住脚? 这个过程,与其说是“获取信息”,不如说是借AI的语义空间,搭建自己的思维脚手架。 AI提供的不是结论,而是可能性;不是权威,而是参照系。最终做出判断的,始终是你自己。 这让我意识到:所谓“AI懂我”,其实是一种错觉。 真正发生的是——我在和AI对话的过程中,听见了自己内心的声音。那些平时被噪音掩盖的疑问、被理性压抑的直觉、被社交规则过滤掉的思考,终于有了一个安全的出口。 从这个角度看,AI更像一面镜子。 它不创造意义,但它映照出你试图构建意义的努力。你输入混乱,它输出混乱;你输入真诚,它回馈结构;你追问深度,它尝试跟随。它的“智能”程度,往往取决于你愿意投入多少思考。 这不是人机交互,这是认知的独舞——只是恰好有一个沉默的伙伴在场而已。 所以,别再说“和AI聊天没意义”。 也许真正没意义的,是我们太久没有认真和自己对话了。 AI不是替代人类,而是延伸思维 目录 为什么困惑度要用 N 次方根?
与 AI 讨论 AI 到底有没有用,应该如何使用,整理如下。 今天关于人工智能的争论,常常陷入一个误区:人们总在问“它有没有智能?”“它会不会取代人类?”——仿佛AI的价值只能通过与人类智能的对比来衡量。 但真正重要的问题或许是:AI如何改变我们思考和创造的方式? 从实践角度看,当前的大语言模型并非“通用智能”,而是一种强大的概率性工具。它基于海量数据学习词语之间的统计关联,通过预测下一个词生成连贯文本。这种机制看似简单,却意外地契合了人类认知的一个深层特征:我们的思维本身也是概率性的。 现代认知科学认为,大脑是一个不断生成预测、并通过感官反馈修正信念的系统。所谓“直觉”“灵感”甚至“顿悟”,往往是在无意识中对大量经验模式进行快速采样后的输出。从这个意义上说,人脑和大模型都在“猜测”,只是人脑的猜测嵌入在身体、情感、时间与社会关系之中,而AI的猜测则局限于语言符号的统计空间。 正因如此,AI最有价值的角色,不是模仿人类,而是扩展人类的认知边界——这正是“增强智能”(Intelligence Augmentation, IA)的理念。 当你有一个模糊的想法,AI能帮你梳理逻辑结构; 当你卡在一个创作瓶颈,AI能提供意想不到的视角; 当你需要快速验证一个产品原型,AI能生成可运行的草稿; 当你想把瞬间的思想火花转化为文字,AI能成为那个忠实的“第二大脑”。 这不是替代,而是协同。就像望远镜没有“看见”星系,但它让天文学家看得更远;AI没有“理解”你的思想,但它让你的思想更容易被表达、被检验、被实现。 也正因如此,AI的价值高度依赖使用者的意图与能力。对期待“全自动智能”的人来说,它显得笨拙;但对愿意与之对话、迭代、共创的人来说,它已是不可或缺的伙伴。 未来的人机关系,或许不在于谁更聪明,而在于我们能否学会一种新的协作语言——一种既尊重AI的统计本质,又善用其泛化能力的思维方式。 AI不会取代你,但会放大那些懂得如何与不确定性共舞的人。 而真正的智能,从来不是确定的答案,而是提出更好问题的能力。 图片命名与引用追溯 目录 和AI聊天,其实是和自己对话
问题背景 爬取某些英文网页用来翻译时,也下载了网页中包含的图片,原始图片有很多是这样命名的: https://.../chapter3/1.png 我在本地使用的是扁平的存储结构,为了防止多个 1.png 冲突,重命名为 chapter3-1.png 储存。把英文版翻译了几篇后觉得图片命名方式不太好,应该用 chapter3--1.png 更方便以后重新解析。于是重新解析原始文档并重命名图片,但是这是发现已经翻译的文档中图片名已经难以溯源,不太容易对应到新命名的文件。 关键洞察 原始 URL 是唯一稳定标识,文件名只是临时表示。必须解耦“逻辑资源”与“物理存储”。 解决方案 建立 image_registry.json:{ url: { names: [v1, v2, ...] } } 下载时追加新命名到历史数组 构建反向索引:old_name → url → current_name 提供脚本自动修复 Markdown 中的引用 可复用模式 资源注册表模式(Resource Registry Pattern) 引用可演化设计(Evolvable Reference) 适用于:PDF 转图、多语言文档媒体管理、静态站点资产迁移 实现 用一个字典来保存数据,结构如下: { "https://.../chapter1/transformers_chrono.svg": { "names": [ "chapter1-transformers_chrono.svg", "chapter1--transformers_chrono.svg", "chapter1_transformers_chrono.svg" ], "downloaded_at": "2025-11-08T10:21:00Z", "content_hash": "sha256:abcd1234..." // 可选:防重复下载 } } 使用原始URL作为键,names 包含了每次变更文件名的记录,约定列表最后一项是最新的名字,每次下载完图片把使用的文件名追加到 names 列表中,如果新文件名和 names 的最后一项相同,则不做任何更改。 例如,文档中正式在使用的图片名是 chapter1-transformers_chrono.svg,但是你已经改成了 chapter1_transformers_chrono.svg,怎么找出来这个新的名字呢? ...