1. 四个方向为什么必须放在一起:笔记体系的底层逻辑
真正把这四块内容——机器学习、深度学习、CUDA编程、凸优化——凑成“笔记合集”的人,大概率不是一时兴起,而是被现实逼出来的。
我自己就是这么过来的。最初只想学深度学习,跟着某个公开课跑图像分类,代码能跑,loss能降,但一到自己改模型结构就蒙圈:加了层之后梯度为什么不稳?学习率到底该调多少?换了个激活函数,收敛速度忽快忽慢,我却说不出任何依据。后来才意识到,深度学习只是浮在水面上的冰山尖,下面压着的是机器学习的基础假设、凸优化里的收敛理论、以及CUDA编程才能触碰到的算力瓶颈。不把它们放在同一个知识体系里,永远只能做个调参侠。
这四块内容本质上是一条完整的链路:凸优化提供理论上的迭代框架,机器学习决定问题建模方式,深度学习负责高维非线性表达,CUDA编程则把表达的算力落地。缺了任何一环,其他三环的理解都会打折扣。更直白地说,我见过太多人卡在某个阶段,原因恰恰不在于那个阶段本身,而在于前置知识没打通。
举个例子。你写一个自定义的卷积算子,发现naive实现比PyTorch自带版本慢二十倍。这是什么问题?表面看是CUDA编程问题——线程块大小没调好、共享内存没用起来、访存不连续。但再往下挖:为什么换一种并行归约策略就能快那么多?这涉及并行带宽的利用率和线程同步开销,而这些开销模型分析用得最多的恰恰是凸优化里的约束优化思维——在存储带宽约束下最小化执行时间。四个方向在实践里根本不是独立学科,是同一套系统里互相咬合的齿轮。
所以这个笔记合集的第一原则不是“分科记录”,而是按问题组织、按链路串联。我后期几乎重构了整个笔记结构,不再按“机器学习是第X章、CUDA是第Y章”来排,而是按“一个完整项目从建模到部署会遇到哪些坎、每个坎需要调取哪几个领域的知识”来组织。这样翻笔记的时候,打开的是一个真实问题的解决路径,而不是在四本互不相干的书里来回跳。
如果你也打算做类似的学习沉淀,建议先想清楚一件事:你记录这些笔记的最终目的是什么?如果只是为了“学过”,那随便记记就行;如果是为了形成可复用的解决能力,那就必须从第一天按链路结构来记,而不是攒了一堆孤立的知识碎片之后再强行缝合。缝合的成本,远比你想象中高得多。
2. 学习路径与核心知识地图:我是怎么整理笔记目录的
笔记合集的骨架是目录。目录好不好,直接决定笔记是“能查的资料”还是“吃灰的文件夹”。我这套体系打磨过三轮,最终沉淀成一套适合从入门到进阶的目录结构。下面把每一块的核心内容摊开讲,你可以直接抄走当蓝本。
2.1 机器学习模块:模型能力的上限与下限
机器学习的笔记,我按四层来组织。第一层是监督学习的核心套路:线性回归、逻辑回归、决策树、SVM、K近邻,每类模型都记录三件事——模型在什么假设下成立、损失函数怎么设计、决策边界长什么样。比如线性回归假设噪声是高斯分布,所以用最小二乘;逻辑回归直接对后验概率建模,所以用交叉熵。这个层面的笔记价值在于建立“从数据假设到损失函数”的映射感。
第二层是无监督与降维:K-Means、DBSCAN、PCA、t-SNE、UMAP。我当年最常犯的错是把PCA当特征选择用,后来笔记里专门留了一页讲清楚:PCA是线性变换,保留的是方差最大方向,和“选择有解释性的原始特征”根本不是一回事。
第三层是模型评估与选择:交叉验证、偏差方差分解、ROC曲线、AUC、混淆矩阵。这里我特别记了一件事——为什么分类任务不能只看准确率。类别不平衡时,准确率会骗人,一个永远预测“多数类”的模型也能拿到95%的准确率。笔记里配了一个我实际跑过的例子:某欺诈检测数据集正样本只占2%,一个“全预测为负”的傻瓜模型准确率高达98%,但上线等于报废。这个例子让“评估指标要匹配业务目标”这句话不再是一句口号。
第四层是集成学习与正则化:Bagging、Boosting、随机森林、XGBoost,以及L1、L2、Dropout等正则化手段的底层联系。XGBoost笔记里我记了泰勒展开二阶导是如何进入损失函数的——这是GBDT到XGBoost的关键一步,也是理解LightGBM直方图加速的前提。
2.2 深度学习模块:从反向传播到网络结构设计
深度学习的笔记量最大,我分成五个子块。
第一块是基础组件拆解:全连接层、卷积层、池化层、BN层、各种激活函数与损失函数。每个组件都从“前向传播在做什么、反向传播的梯度长什么样、数值上会不会出问题”三个角度记。比如ReLU的梯度在负数区为0,会导致神经元死亡;Leaky ReLU的负斜率0.01就是在救这个;而GELU的梯度更平滑,所以对Transformer更友好。这些细节最初觉得琐碎,后来发现面试和调参全都在考这些。
第二块是优化器演化:SGD为什么加动量、Adam为什么加二阶矩估计、AdamW为什么把weight decay从梯度里拆出来、以及Lion那种更新规则的符号化思路。笔记里我画过一张对比表,记录每个优化器在不同任务上的实际表现——SGD+momentum在CV任务上往往比Adam更稳,而Transformer训练几乎离不开Adam类优化器。这种“不同结构偏好不同优化器”的经验,是只看论文很难提炼出来的,必须自己跑实验记录。
第三块是经典网络结构:ResNet的残差连接、Inception的多尺度、DenseNet的密集连接、注意力机制从SE到CBAM再到Transformer自注意力的演进。ResNet笔记里我专门推导了残差结构为什么让梯度更容易回传——恒等映射项的梯度为1,不会衰减,这比看十篇论文都管用。
第四块是训练技巧与调参:学习率warmup、余弦退火、梯度裁剪、EMA权重平均、混合精度训练。这块笔记是最容易变成“个人踩坑手册”的地方。比如同样的学习率,跑ResNet50没事,跑BERT可能直接loss变NaN,原因在于深层Transformer的梯度量级差异更大,需要warmup先把参数稳定住。这种经验不写在教科书里,只写在真实项目的挣扎里。
第五块是生成模型与基础理论拓展:GAN、VAE、Diffusion,以及表示学习、对比学习的基本思想。这些我一开始不太重视,后来发现不理解生成模型的loss设计(对抗损失、重建损失、KL散度),很多新论文根本读不进去。
2.3 CUDA编程模块:从线程模型到核函数性能调优
CUDA编程是最劝退但也最“涨功”的一块。我的笔记分四个层次。
第一层次是内存与线程模型:grid、block、thread的三级结构,全局内存、共享内存、寄存器、局部内存的层级关系。大多数人卡住是因为分不清“逻辑上的并行”和“物理上的并行”——一个block里的线程在同一个SM上执行,所以可以用共享内存通信;不同block之间只能用全局内存和原子操作通信。这个区别是所有CUDA优化的出发点。
第二层次是核函数编写基础:如何计算线程索引、如何避免bank conflict、如何使用共享内存做tile复用、如何做并行归约。我记过一个经典例子:两层for循环的SGEMM矩阵乘法,naive版本性能只有机器峰值的不到2%,原因在于每次读A[i][k]都要走一遍全局内存。加上共享内存tile之后,直接跳到峰值的20%以上——不写一次并行程序,永远感受不到访存带宽带来的窒息感。
第三层次是算子优化实战:以softmax、layer_norm、矩阵乘法、卷积为例,记录从baseline到优化版的每一步改动和性能数据。比如softmax的数值稳定性——先减max再做exp,这个看似微小的改动,在fp16下决定了你的输出是正常数还是inf或nan。
第四层次是多流并发与显存管理:使用CUDA Stream实现kernel之间、kernel与数据传输之间的重叠,以及显存池、统一内存等机制。到了这一步,瓶颈往往不再是单个kernel,而是整个pipeline的空闲等待。
2.4 凸优化模块:理论根基的直观化记录
凸优化我学了两遍才真正入门。第一遍按教材顺序啃,被各种定义淹没,第二遍才换成了“从问题出发”的方式,笔记也因此重写。
第一块是凸集与凸函数:定义、常见例子、保凸运算(交集、仿射映射、透视函数)。我记了一个特别直觉的笔记——凸函数图像任意两点连线都在函数图像上方,这意味着局部最优就是全局最优。整个凸优化体系的威力,说到底都来自这个简单的几何直觉。
第二块是无约束优化方法:梯度下降、最速下降、牛顿法、拟牛顿法(BFGS)、共轭梯度。笔记里记录了每种方法的核心迭代公式和收敛性条件,特别对比了“为什么牛顿法收敛快但代价高”——每一步要计算Hessian矩阵的逆,高维情况下根本不现实,所以才有BFGS这种用梯度信息近似Hessian的方案。
第三块是约束优化与对偶理论:拉格朗日乘子法、KKT条件、拉格朗日对偶、Slater条件。这块是我笔记里页数最多的地方。KKT条件理解透了,SVM的推导就通了,正则化项和约束条件的关系也通了——L2正则从“罚项”角度解释,可以等价地看成带范数约束的优化问题。
第四块是进阶算法:次梯度、近端梯度、ADMM、坐标下降。这块跟机器学习的联系尤其紧密:Lasso这种带L1正则的问题在原点不可导,梯度下降用不了,得用次梯度或近端梯度;矩阵分解类问题则适合用坐标下降把参数分组交替更新。
这类知识单独看会很枯燥,但一旦和模型优化问题绑定,立即变得“好用”。我的体会是,凸优化笔记一定要搭配代码来记。理论推导和实际迭代行为之间的差距,只有跑一次实验才能体会——比如理论收敛速度最坏情况和实际收敛表现往往差了十万八千里。
3. 笔记的形态与方法论:记录和整理的具体操作
说完了内容骨架,再说记录本身。同一个知识点,不同记法,事后检索效率和理解深度完全不同。我这套笔记体系在形态上经历过几次迭代,最终确定了几条对自己最有效的方法论。
3.1 迭代式整理:笔记不是一次性写完的
我早期最大的问题是追求“第一次就写好”。拿起一篇论文或者一本书,企图把所有细节一步到位地记录下来,结果是笔记写到一半就放弃了,因为新知识点太多,根本写不全。
后来改成迭代式整理。第一遍记录只需要“骨架+待补标记”:把核心结论、公式出处、和自己的疑问记下来,哪怕只有三四行。过一周回看时,带着新的理解去做第二次增补,把推导过程补上;再过一个月,做第三次增补,把实际项目中对应的案例贴进去。每一步都很轻松,但最终笔记的完整度和理解深度远比一次性写完高。
这个做法背后的逻辑很简单:人对知识的理解是分层的,第一次接触能抓住的只有主干,很多细节要等你在实践中反复碰到之后才能真正消化。硬要在第一遍就写全,只会收获一本“抄写稿”。我现在的每一页核心笔记,几乎都经历过至少三轮增补,第一轮是学完当天,第二轮是学完一周后,第三轮是遇到相关项目之后。
3.2 代码注释式笔记:让理论长在代码上
纯文字笔记有翻车的风险——你觉得自己记明白了,两个月后回头看根本看不懂当时在说什么。后来我做了一个关键改变:要么用可运行的代码承载笔记,要么在代码里写详尽注释配合文字笔记。
比如凸优化的近端梯度算法,光写公式、叙述性质,很容易变成“抄书”。我的笔记是一份完整的Python脚本,里面从最基础的梯度下降开始,逐步加处理不可导项的近端映射,跑在同一个目标函数上,每一步都有注释解释“这一步在做什么、为什么这么做、输出值代表什么”。这种笔记的最大价值是,随时可以重新运行验证,理论是否理解到位一测便知。
深度学习笔记更是离不开代码。每个经典结构,我都会在笔记本里留一个最小可跑的示例代码块——ResNet留一个18层的小实现,Transformer留一个单层自注意力前向过程。不是为了写库,是为了把“这个结构的forward到backward到底怎么走的”这句话锚定在具体实现上。文字会模糊,代码不会。
3.3 推导留痕:公式笔记的正确记录姿势
数学类笔记最容易踩的坑是“抄公式不抄推导”。抄下来的公式是死的,推导过程才藏着理解。
我的公式笔记规格是这样的:每个重要公式,必须回答四个问题:
- 这个公式在什么假设下成立?
- 它的推导第一步是什么?
- 推导过程中的关键技巧或恒等变形是什么?(比如加入再减去一项)
- 如果假设被突破,公式会怎么变?
以SVM的对偶推导为例,笔记里不是直接写“应用拉格朗日乘子得到对偶形式”,而是把对偶目标函数的每一步展开记录下来:原始问题带约束——构造拉格朗日函数——通过对KKT条件消去w和b——得到关于α的对偶问题。推导过程中额外记录了一个细节:为什么对偶问题一定满足约束条件,以及何时对偶间隙不为零。这些信息,论文里通常一句话带过,但恰恰是理解的真正门槛。
3.4 工具链清单:轻量、可检索、可迁移
笔记工具的选择,原则是最少依赖 + 版本可追溯。我试过各种笔记软件,最后沉淀下来的组合非常简单:
- 学术/公式类笔记:用Markdown + LaTeX公式插件,记录到本地仓库。公式用LaTeX书写,方便以后转成任何格式。
- 代码类笔记:直接使用Jupyter Notebook,兼顾代码执行和Markdown记录。
- 概念对比/知识关联:用双向链接的笔记软件做索引,但不在里面放大量正文,只放链接和一段核心结论。
- 原始论文阅读:用PDF注释工具,强调重点,配合“论文笔记模板”写入主笔记体系——模板包含“问题是什么、方法是什么、创新点在哪里、和已有方法比强在哪、局限性在哪”。
工具越轻越好。战力不在软件里,在笔记内容里。内容没有形成体系之前,先花两周纠结选型,纯属消耗。
4. 踩过的坑:从崩溃到能输出的关键转折
这套笔记体系不是一帆风顺搭起来的。中间踩过的坑,有些让我浪费了几个月,有些直接改变了我的学习方式。挑几个对大多数人最有借鉴意义的展开讲。
4.1 只看书不写代码的“虚假学习”
最早学机器学习时,我信奉“先把理论吃透再动手”。于是连着啃了一个多月的书,概念都能背,公式推导也能看懂,但一动手写线性回归发现无从下手——甚至连“特征矩阵要不要加一列全1的偏置项”这种最小的问题都要犹豫半天。
这个坑的根源在于,阅读是被动接收信息,代码是主动信息加工。看懂一个公式和能把它实现出来,中间隔着大量隐性细节。比如归一化的位置、batch size对BN层统计量的影响、以及训练测试阶段dropout行为的不同——这些全是“纸上得来终觉浅”。笔记最大的价值就是强制把“以为懂了”变成“确实会用”。学到一个模型,当天就用小数据跑一个实验,结果存进笔记,再也不自欺欺人。
4.2 一上来就啃CUDA的全面崩溃
我学CUDA的第一次尝试极不愉快:直接拿了一本并行编程的书从头啃,前两周就被各种硬件架构细节淹没,完全不知道哪些是重点。直到后来换了思路——先不学整个CUDA,只学“怎么把我的第一个核函数跑起来”——才真正迈过门槛。
那次挫败留给我的教训是:复杂体系的入门顺序比努力更重要。CUDA的正确顺序应该是“写一个向量加法核函数——跑起来——学会用profiler看瓶颈——再开始学共享内存、流并发这些进阶内容”。而不是先读三百页硬件手册再动手。现在这套笔记目录的排序,就是那次血泪教训的直接结果。
4.3 把凸优化学成“数学分析”,而非“工程工具”
我学凸优化的第一个月是照着数学教材学的,定义、定理、证明,一页页看,越看越觉得这东西跟我做深度学习半毛钱关系没有。直到某次调一个L1正则相关的稀疏模型,发现Lasso在原点不可导导致梯度下降没法直接用,才第一次意识到凸优化是拿来解决问题的,不是拿来考试的。
从那以后,我把学习方式整个扭过来了:每个优化方法都绑定一个机器学习问题。梯度下降绑定逻辑回归,近端梯度绑定Lasso,ADMM绑定带约束的分布式优化,坐标下降绑定矩阵分解。笔记结构完全按问题驱动重新组织。理解“为什么这个算法能在这种结构的目标函数上高效收敛”,比记忆一堆收敛性证明有意义得多。
4.4 笔记只有输入没有输出,逐渐腐烂
”记了笔记就完事“是另一个大坑。笔记这种容器,无论多精美,如果不被反复检索、引用、更新,就会像长期空旷的仓库,落满尘土。
后来我给自己定了一个硬性要求:每隔一段时间,基于笔记产出一篇可以公开分享的东西——一篇技术博客、一个开源实现的代码片段、或者一份内部技术分享的PPT。输出倒逼输入,是最好的笔记活化方式。为了写清楚一个概念,我得把所有相关笔记重新翻一遍,补上不连贯的地方,删掉过时的结论。每一次输出,笔记体系都会被重构一次,变得更结实。
实测下来的感受特别明显:那些只记笔记没输出的知识点,半年后基本原封不动还回去;经过一轮输出整理的知识,两年后还能清楚地记得核心推导和适用条件。
5. 笔记体系从“记”到“用”的最后一公里
如果你已经按照类似结构建立了自己的笔记合集,下一步需要考虑的是:怎么让这套笔记不只躺在硬盘里,而是真正成为你解决新问题的武器库。我最后再分享三个比较个人化的经验。
5.1 建立“问题索引”,而不是“知识索引”
传统的笔记按知识目录检索:想查梯度下降,翻到凸优化章节;想查注意力机制,翻到深度学习章节。但这和真实工作方式不匹配——真实场景里,你面对的是一个具体问题,比如“我的Transformer训练崩了”。
我的做法是在笔记体系的最前面维护了一张问题→知识点映射表。每遇到一个实际问题,就把它记进去,并标注“涉及哪个知识模块、解决思路链路是什么”。比如:
- 训练loss出现NaN → 混合精度下softmax溢出 + 学习率过大梯度爆炸 → 涉及深度学习训练技巧 + CUDA数值稳定性
- 自定义算子比原生慢 → 先查访存连续性,再查共享内存利用率 → 涉及CUDA优化
- 模型过拟合,验证集波动大 → 偏差方差分解 + 加正则化 + 数据增广 → 涉及机器学习评估
这张表让笔记从“字典”变成了“索引+字典”,翻查路径缩短了一个数量级。
5.2 给重要笔记打“可复现”标签
笔记里凡是涉及实验结果的内容,我都会额外在结尾标注“可复现命令”和“期望输出”。比如CUDA性能调优笔记里,记录“共享内存tile版本对比naive版本”的加速比,同时附上复现命令和硬件环境备注。这个“可复现标签”很关键,它的作用是在你未来要复习时,不用重新依赖记忆,跑一遍就能把当时的上下文拉回脑海。
5.3 接受不完美,以“当前够用”为愿景
最后一条,也是我花了最久才想通的:知识体系永远是不完美的,而且不该追求完美。技术演进太快,今天记下的方案明天可能就过时了。真正重要的不是把每篇笔记都写成权威文档,而是让笔记成为你的“外置大脑” ——在问题发生时能快速定位到正确方向,在知识过期时能快速更新。
这套笔记合集对我而言,早已不是学习记录,而是一种思考方式的具象化:把看似分散的知识用问题串起来,把理论和实现焊接在一起,把输入和输出闭环起来。如果你也在这条路上摸索,我的建议是别想太多,先从今天遇到的第一个问题开始,记下第一页笔记。一年之后回头看,你会感谢那个开始。