深夜十一点,狭窄的书房里,只有书桌上的屏幕还亮着。
林晨盯着pytorch官方教程的最后一个章节,已经看了三遍。屏幕上是一个完整的神经网络代码示例——mNISt手写数字识别,深度学习界的“hello world”。过去两周,他像海绵一样吸收着那些抽象的概念:张量、梯度、反向传播、损失函数。数学补课起了作用,至少现在看到矩阵乘法不再发怵,能理解为什么权重更新要用链式法则。但理解归理解,距离“亲手创造一个能思考的东西”,还隔着一段需要代码去填补的距离。
“跟着做一遍”。他对自己说,声音在安静的房间里显得格外清晰。新建python文件,导入torch和torchvision。下载mNISt数据集——一个包含六万张28x28像素手写数字图片的经典数据集。代码跑起来,终端开始显示下载进度。
林晨看着那些滚动的日志,忽然有些恍惚。三十五岁,再次失业1个月,存款以每月两万多的速度消耗,自己却在这里下载一堆黑白数字图片。这个场景如果拍下来给一年前的自己看,恐怕会觉得是个荒诞的玩笑。但数据下载完成的提示音把他拉回现实。
接下来是数据预处理:将图片像素值从0-255归一化到0-1之间,转换成pytorch能处理的tensor格式,分割成训练集和测试集。这些步骤他已经在Jupyter Notebook里练习过多次,手指在键盘上敲击得流畅。真正的挑战从定义模型开始。
教程提供了一个简单的全连接神经网络:输入层784个节点(28x28),两个隐藏层各128个节点,输出层10个节点(对应数字0-9)。结构图在屏幕上展开,林晨逐行敲入代码。
class Net(nn.module):
def __init__(self):
super(Net, self).__init__
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
不到二十行代码。但林晨知道,这二十行背后是三十年来神经网络研究的结晶。每一层Linear代表一次矩阵乘法和偏置加法,每个ReLU激活函数给模型注入非线性——正是这种非线性让神经网络能够拟合复杂的函数关系。
他想起大学时上的《人工智能导论》,教授说神经网络是“黑箱”,输入输出之间是难以解释的权重迷宫。当时觉得神秘,现在亲手搭建时,却感受到一种奇异的透明感——每一层都在做确定性的数学运算,所谓的“智能”不过是这些运算在大量数据训练后,权重被调整到能提取有效特征的状态。
“代码可以骗人,但逻辑不会”。他低声念着自己的口头禅,点击运行。
模型初始化完成。接下来是训练循环的核心部分:定义损失函数(交叉熵损失)和优化器(随机梯度下降SGd)。学习率设为0.01,一个保守的起始值。第一次训练开始了。
林晨设置了5个epoch(遍历整个训练集的次数)。代码运行,GpU风扇轻微转动——他庆幸自己当年配电脑时选了带独立显卡的型号,虽然是为了打游戏,现在却成了训练模型的利器。
第一个epoch结束时,终端打印出损失值:2.302。准确率:10.3%。“跟随机猜差不多”。林晨苦笑。mNISt有十个类别,随机猜测的准确率应该是10%,模型现在几乎没学到任何东西。
但他没有停。第二个epoch,损失降到1.876,准确率升到23.7%。第三个epoch,1.432,45.2%。数字的变化有种催眠般的魔力,他盯着屏幕,看着那些代表模型正在“学习”的证据。第四个epoch结束时,准确率突破了70%。损失值降到0.876。
林晨身体前倾,手肘撑在桌上。他能感觉到心跳在加速——不是因为紧张,而是因为某种接近真相的兴奋。那些抽象的数学概念正在转化为具体的、可测量的能力提升。模型在“理解”那些手写数字的形状特征。
第五个epoch,最后一个。训练循环在运行,进度条缓慢移动。林晨起身倒了杯水,回到座位时,刚好看到最终结果打印出来:Epoch 5/5, Loss: 0.356, Accuracy: 89.7%。
89.7%。他眨了眨眼,重新读了一遍。然后,几乎是本能地,他修改了代码——将epoch数从5改为20,学习率微调到0.005,重新运行。
这一次他不再干等。他打开了tensorboard(pytorch的可视化工具),看着损失曲线随着训练轮次平滑下降,准确率曲线稳步上升。第10个epoch时,准确率突破92%;第15个epoch,94.1%;第20个epoch结束时,终端显示:Final test Accuracy: 95.3%。
房间里安静得能听到电流声。林晨靠在椅背上,长长地呼出一口气。95.3%。这意味着他刚刚亲手构建的这个简单神经网络,在识别手写数字图片的任务上,已经能达到接近人类的水平——至少对于mNISt这种相对干净的数据集是这样。
但这还不是终点。他打开测试集,随机抽取了几张图片,用训练好的模型进行预测。代码输出预测结果,他对照着真实标签一一核对。
第一张,图片是个歪歪扭扭的“7”,模型预测:7,正确。第二张,像个“3”又有点像“8”,模型预测:3,正确。第三张,一个很标准的“0”,模型预测:0,正确。连续对了十张。
然后他故意挑了一张模糊的、像素似乎有噪声的图片,看起来像“4”但下半部分缺失。模型预测:4,概率置信度87%。真实标签:4。林晨盯着那张图片看了很久。
那一刻,他忽然理解了为什么AI会让人着迷。不是因为它有多神秘,恰恰相反,是因为它的确定性。你给它数据,定义好架构和优化目标,它就会沿着数学上最陡峭的下降方向,一点一点调整自己内部的数百万个参数,直到找到那个能最大限度区分不同类别特征的权重组合。这个过程是可重复、可测量、可改进的。
而更关键的是,这个刚刚能识别手写数字的模型,和那些能识别猫狗图片、翻译语言、下围棋、甚至预测股票走势的模型,在本质上用的是同一套方法论。区别只在于数据规模、模型复杂度和计算资源。
林晨关掉tensorboard,打开一个新的文件。他开始修改代码——不是照搬教程,而是尝试自己的改动:增加一个隐藏层,把神经元数量从128增加到256,尝试不同的激活函数(把ReLU换成LeakyReLU),调整学习率衰减策略。
重新训练。这一次,准确率在30个epoch后达到了97.1%。虽然只是提升了不到两个百分点,但那种“通过调整架构和超参数来控制系统性能”的掌控感,比第一次看到准确率上升时更强烈。
窗外的科技园,几栋写字楼还亮着零星的灯光。林晨看了眼时间,凌晨两点半。他应该去睡觉了,明天还要继续学卷积神经网络(cNN)——那才是处理图像更专业的架构。
但他没动。他打开那个名为“投资系统数学基础构想”的笔记,在空白处写下:
“凌晨2:35。训练出第一个神经网络模型,mNISt准确率97.1%。关键体会:
AI不是魔法,是工程。可量化、可优化。
模型性能取决于三要素:数据质量、架构设计、训练策略。
如果它能从像素中学会识别数字,那它应该也能从K线图中学会识别模式。
下一步:用cNN重做mNISt,理解卷积层如何提取空间特征。这对处理金融时间序列可能有启发”。
写完,他保存笔记,关掉电脑。
躺在床上时,脑子却停不下来。那些损失曲线、准确率数字、权重矩阵在眼前旋转。他想起大学时第一次写出能正确运行的“hello world”程序时的兴奋,想起第一次做出能处理真实业务的电商系统时的成就感。
但今晚的感觉不一样。那不只是“让代码运行起来”的满足,而是更接近……创造。创造一个能“学习”的东西,给它数据,看它成长,调整它的结构让它变得更好。
黑暗中,林晨睁着眼睛。房贷月供一万二,存款还剩不到二十三万,再次失业第二个月。这些数字依然冰冷地悬在头顶。但此刻,他第一次清晰地看到了一条路——不是web3那种充满不确定性的新领域,而是一条基于数学、代码和数据,每一步都踏实可测的路。
AI这条路。
他翻了个身,想跟苏婉说点什么,却听到妻子均匀的呼吸声——她早就睡了。明天吧,明天可以告诉她,自己今天做出了一个能认数字的“小东西”。虽然它现在还只能认数字。但林晨知道,这只是开始。
窗外的鹏城,凌晨三点的街道空旷安静。而在狭窄书房的书桌上,那个保存在硬盘里的模型文件,正安静地等待下一次被加载、被改进、被赋予新的任务。
比如,认识K线图的形态。比如,预测明天的涨跌。比如,从混乱的市场噪声中,找出那些微弱的、可盈利的信号模式。
林晨闭上眼,脑子里不再是焦虑和迷茫,而是一层层堆叠的神经网络架构图,是损失函数需要优化的曲面,是等待被挖掘的金融数据。他睡着了,嘴角带着半年来第一次真正放松的弧度。