林晨坐在书桌前,窗外是科技园不变的灯火通明,而他的电脑屏幕上,正同时打开着两个截然不同的编程环境。
那个 97.1% 准确率的 mNISt 模型带来的兴奋感尚未完全消退,但理性很快占据了上风。林晨清楚,偶然的成功不能复制,要想真正把 AI 技术变成自己手中的利器,尤其是未来应用到变幻莫测的金融市场分析上,他必须系统性地掌握工具,而不仅仅是跟着教程跑通一个例子。
深度学习框架,就是这最重要的工具。
当前主流的两大巨头——tensorFlow 和 pytorch,如同武林中的两大门派,各有拥趸,各有优劣。林晨决定,花上一周时间,把这两个框架都深入体验一遍,再决定自己的主攻方向。
他首先打开了 tensorFlow 的官方文档。作为谷歌出品的老牌框架,tensorFlow 以其工业级的稳定性、强大的分布式训练能力和成熟的部署生态着称。
林晨按照指南安装配置,开始了第一个 tensorFlow 程序——依旧是手写数字识别。
“静态计算图……”林晨一边敲代码,一边琢磨着这个核心概念。
在 tensorFlow 1.x 时代,需要先定义计算图(Graph),然后在会话(Session)中运行,这种“先构图,后执行”的模式,对于习惯了 python 即时执行思维的程序员来说,确实有些绕。
好在现在主流已是 tensorFlow 2.x,默认开启了 Eager Execution(急切执行),像普通 python 代码一样即时运行,大大降低了入门门槛。通过高级 ApI tf.keras,构建模型变得相当直观。
import tensorflow as tf
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.dense(128, activation=relu),
tf.keras.layers.dense(10, activation=softmax)
])
model.pile(optimizer=adam, loss=sparse_categorical_crossentropy, metrics=[accuracy])
代码简洁明了。
训练过程也顺利,最终准确率跑到了 97.5%,比上次自己用“原生” pytorch 略高一点点。但林晨在尝试自定义一个损失函数,并想深入查看中间某层梯度时,遇到了一些麻烦。
tensorFlow 的调试体验,尤其是想要深入其计算过程时,总觉得隔着一层,不如直接写 python 代码那样透明和易于介入。它的优势似乎更在于“生产部署”——一旦模型确定,转换成 Savedmodel 或 tFLite 格式,在各种终端和服务器上的部署非常成熟。
“这就像一套精密的自动化生产线”,林晨在笔记上写道,“设计好了流程,它就能稳定高效地运转,但如果你想中途调整某个零件的加工参数,可能需要专门的工具和接口”。
接下来,他切换到了 pytorch 的环境。
其实上一章的 mNISt 就是用 pytorch 的教程完成的,但那次更多是“跟随”。这次,他打算以“研究者”和“探索者”的心态重新审视它。
pytorch 由 Facebook(现 meta)AI 研究院推出,核心设计哲学是“python 优先”和动态计算图。林晨从最基本的张量(tensor)操作开始,感受其与 Numpy 的相似性带来的亲切感。
构建模型时,他需要自己定义一个继承自 torch.nn.module 的类,并在 forward 方法中明确写出数据流动的过程。
import torch
import torch.nn as nn
import torch.optim as optim
class SimpleNN(nn.module):
def __init__(self):
super.__init__
self.flatten = nn.Flatten
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 128),
nn.ReLU,
nn.Linear(128, 10)
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
这种面向对象的方式,让模型的每一层、每一个操作都清晰可见。
最让林晨感到舒心的是 pytorch 的自动微分(autograd)系统和调试的便捷性。他可以在训练循环的任何地方,插入打印语句查看张量的值、形状,甚至计算梯度。
动态图意味着计算图是在代码运行时动态构建的,这带来了无与伦比的灵活性——你可以用 python 原生的控制流(if、for、while)来构建模型结构,这在研究新模型结构时简直是神器。
“这才是编程的感觉”。林晨忍不住感叹。
他尝试修改网络结构,增加一个跳跃连接(Skip connection),在 tensorFlow 中可能需要调整 tf.keras 的 ApI 用法,而在 pytorch 里,他几乎像写普通 python 类一样自然地在 forward 方法里实现了。
为了更直观地对比,林晨决定用两个框架分别实现同一个稍复杂的模型——一个简单的卷积神经网络(cNN),再次在 mNISt 上测试。
结果,在模型性能上两者相差无几(pytorch 版本 98.2%,tensorFlow 版本 98.1%),但在开发体验上,林晨明显感觉 pytorch 更符合他的思维习惯。
那种对模型细节的掌控感,对训练过程每一步的透明可见,让他觉得安心。尤其是想到未来他要开发的智能投资系统,必然需要频繁地尝试各种网络结构、损失函数,甚至自定义训练逻辑,pytorch 的动态和灵活显得至关重要。
“tensorFlow 像是为大规模部署而生的重型战舰,稳定、强大,但转向稍慢;pytorch 则像是灵巧的突击舰,适合快速迭代和前沿探索”。林晨在对比总结中写下这句话,“对于目前的我,处于技术探索和原型快速开发阶段,pytorch 显然是更合适的武器”。
做出了选择,林晨感觉目标清晰了许多。
他不再满足于仅仅实现教程里的基础模型。接下来的几天,他给自己定下了新的挑战:不依赖高级 ApI 的过度封装,尽量从相对底层的角度,用 pytorch 重新实现几个深度学习的经典模型,深入理解其机理。
他选择了三个有代表性的模型:LeNet-5(cNN 开山鼻祖之一)、简单循环神经网络(RNN)用于体验时序处理,以及 transformer 中最核心的多头自注意力(multi-head Attention)机制。
这不再是跟着教程敲代码,而是需要他仔细阅读原始论文(或权威解读),理解每一层的数学含义,然后用 pytorch 的张量操作和自动微分将其“翻译”成代码。
实现 LeNet-5 相对顺利,虽然比用现成的 nn.conv2d 和 nn.maxpool2d 组合要繁琐,但亲手写出卷积和池化的操作过程,让他对图像特征提取的理解深刻了许多。
RNN 的实现则让他对处理序列数据、隐藏状态传递有了直观感受。
而 transformer 的自注意力机制,则是最大的挑战。矩阵运算、缩放点积、多头拆分与合并……林晨对着公式,一遍遍推导,一步步用代码实现。
当最后那个小小的自注意力模块能正确计算出一组示例序列的注意力权重时,窗外已是凌晨三点,但他毫无倦意,只有一种攻克难关后纯粹的智力愉悦。
这些重新造轮子的过程,速度很慢,也没有直接带来任何实用价值,但林晨知道,这些深入底层的理解,正在构筑他技术大厦最坚实的地基。
他不再只是一个调用 ApI 的“调参侠”,而是开始真正理解这些强大工具背后的原理。
夜深人静,林晨保存好所有代码和笔记。他活动了一下僵硬的脖颈,目光落在屏幕上那些自己亲手实现的模型结构图上。
从静态图到动态图的选择,从使用 ApI 到理解原理的深入,他感觉自己正一步步褪去过去十年跨境电商开发中形成的某种“应用层思维”,向着技术更本质的层面靠近。
“这些 cNN、RNN、Attention,本质上都是在学习数据中的模式和关联”。他靠在椅背上,若有所思,“数字图片中的边缘、形状是模式,语言序列中的上下文依赖是关联……那么,金融时间序列数据中的趋势、波动、周期,以及不同资产之间的联动,是不是也是一种更深层次、更复杂的模式和关联”?
这个念头让他心跳微微加速。
技术工具的选择和深入掌握,最终都是为了通向那个目标——理解并量化金融市场的不确定性。
他感觉,手中的“武器”正在变得趁手,而那个模糊的目标,也似乎因为技术路径的清晰,而透出了更具体的光亮。