@maxxfuu: 推理工程第13/90天 我用C语言构建了一个MNIST分类器。并不为此骄傲,但够坦率说出来。它花了我5……

X AI KOLs Timeline 新闻

摘要

作者分享了用C语言构建MNIST分类器的经历,强调理解基础比编码更重要,并解释了一维向量矩阵乘法的直觉。

推理工程第13/90天 我用C语言构建了一个MNIST分类器。 并不为此骄傲,但够坦率说出来。我花了5天时间才完全理解并掌握一维向量矩阵乘法(matmul)背后的直觉。 它只是三个嵌套的for循环,但不知为何,就是没能理解。它一点也不直观。以下是我希望一开始就知道的一些事情。 实际上,我认为有时候画图、创建不同的可视化,以及和语言模型交流来建立理解,如果方法得当,会大有帮助。 我花了大约6个小时追踪代码、绘制数据流、预测形状以及理解数学原理。这些都不需要任何编码。一旦我完全理解了MNIST的流程,编写代码就变得飞快。在C中实现MNIST花了大约1小时,而理解所有内容花了6小时。 从心态上讲,我认为学习深度学习确实是一个不同的领域。从全栈工程的角度来看,构建健壮的后端和优雅的前端几乎有一套现成的指南。我发现直接投入实践并边做边学比阅读文档能学到更多。 然而,学习推理(Inference)、CUDA以及构建像MNIST这样的简单项目,与全栈工程完全不同。理解事物为何如此运作并建立直觉是一大障碍。未来,我绝对会把精力花在理解基本原理上,而不是编码上。 从技术层面来看,以下是让matmul_forward()的三重for循环实现豁然开朗的直觉!由于矩阵以一维向量的形式存储,选择正确的单元格只需弄清楚三个索引变量中哪个对应步进(step)、步幅(stride)和固定点(pin)。 这不是正式术语,但说得通。 步进(step)是你移动的轴。它可以按行或按列跨越。跨越的方向由固定点(pin)决定。步幅(stride)是每一步在一维数组上跳跃的大小。 看看动画中的B[l*k + j]。固定点是j,它将你固定在一列上,因此唯一的自由变量是步进l。迭代沿着该列从上到下进行。由于B存储在一维数组中,“向下移动一行”不是移动一个单元格,而是跨过整个步幅,即一行宽度k。 虽然哪个变量扮演步幅、固定点和步进的角色完全取决于for循环的结构,但我已经能够使用这个框架泛化各种矩阵乘法的变体。 从明天开始,我将能够用CUDA重写这段C代码!
查看原文
查看缓存全文

缓存时间: 2026/07/20 17:31

推理工程第13/90天

我用C语言写了一个MNIST分类器。

没什么好骄傲的,但我足够坦诚。我花了整整5天才完全理解并掌握1D向量矩阵乘法的直觉。

它只是三个嵌套的for循环,但不知为何,就是没开窍。完全反直觉。以下是我希望一开始就知道的一些事情。

实际上,我认为有时把东西画出来,创建不同的可视化,并与语言模型对话来建立理解,如果方法得当,会有很大帮助。

我花了大约6个小时追踪代码、绘制数据流、预测形状、理解数学原理。这些都不需要写任何代码。一旦我完全理解了MNIST的流程,写代码就快得惊人。用C实现MNIST大约用了1小时,而理解一切用了6小时。

从心态上讲,我认为学习深度学习确实是一个不同的游戏。从全栈工程的角度来看,构建稳健的后端和优雅的前端几乎有一本现成的“手册”。我发现直接投入实践、边做边学比阅读文档更能教会你东西。

然而,学习推理、CUDA以及构建像MNIST这样的简单项目,与全栈工程完全不同。理解某些东西为什么有效、并建立直觉是一个巨大的障碍。往前走,我肯定会把“token”花在理解基本原理上,而不是写代码上。

在技术方面,以下是让我对matmul_forward()的三重for循环实现豁然开朗的直觉!由于矩阵是以1D向量的形式存在,选择正确的单元格就是弄清楚三个索引变量中哪个对应步进(step)、步幅(stride)和固定点(pin)。

这不是正式的术语,但很有道理。

步进是你移动的轴。它可以是行方向或列方向。向哪个方向移动由固定点决定。步幅是你在1D数组上每一步跳过的距离。

看一下动画中的B[l*k + j]。固定点是j,它将你固定在一列上,因此唯一的自由变量是步进l。迭代沿着该列从上到下流动。由于B存放在一个1D数组中,“向下移动一行”并不是移动一个单元格,而是移动一个完整的步幅,即行的宽度k。

虽然哪个变量扮演步幅、固定点和步进完全取决于for循环的结构,但我已经能够使用这个框架来概括矩阵乘法的各种变体。

从明天开始,我就能用CUDA重写这段C代码了!

相似文章