深度学习基础实战:5个Julia案例搞懂SOM、Softmax、卷积、函数拟合与分类
学深度学习时,很多基础概念其实并不难理解,但一旦被框架封装起来,就很难直观看到它们具体是怎么工作的。
比如 Softmax 如何把输出变成类别概率、交叉熵损失为什么会下降、卷积之后图像尺寸为什么会变化,以及无监督聚类和有监督分类到底有什么区别。
前段时间我在 MoHub 上看到了一套**《深度学习综合教学案例库》**,其中“深度学习基础”模块正好用 5 个 Julia 案例分别演示这些问题:
- SOM 无监督聚类
- Softmax 与交叉熵分类
- 深度学习算子可视化
- 神经网络函数拟合
- 鸢尾花模式识别分类
完整案例地址:
https://mohub.net/education/26291/summary
这几个案例都可以在 Syslab 的 Julia 环境中直接运行,主要依赖 TyDeepLearning、TyImageProcessing 和 TyPlot。按照案例目录中的 README 执行对应脚本即可,部分案例会读取配套的 CSV 数据文件。
下面直接看几个实际运行结果。
Softmax与交叉熵:损失从1.29降到0.038
Softmax 案例生成三类二维点云,每类 60 个样本,然后使用线性 Softmax 分类器配合交叉熵损失进行梯度下降训练。
主要参数为:
样本数:60 × 3
训练轮数:220
学习率:0.08
运行结果:
初始损失:1.2918
最终损失:0.0376
训练准确率:100%
从损失曲线可以看到,训练前期下降较快,随后逐渐趋于平缓。
Softmax 会将模型输出的 logits 转换成各类别对应的概率,而交叉熵衡量的是预测概率与真实类别之间的差异。当模型对真实类别给出的概率越来越高时,交叉熵损失也会持续下降。
这个案例生成的三类二维数据本身具有比较明显的可分性,因此最终训练准确率达到 100%。
相比直接调用一个分类接口,这个例子更容易看清:
线性输出 → Softmax 概率 → 交叉熵损失 → 梯度下降 → 分类结果
这一整套多分类训练流程是怎样连接起来的。
函数拟合:隐藏节点数量会怎样影响结果?
函数拟合案例使用径向基特征加线性输出,对一组带噪声的非线性数据进行拟合。
默认使用 10 个隐藏节点,并按照约 7:1.5:1.5 的比例划分训练集、验证集和测试集。
运行结果:
训练 MSE:0.075759
验证 MSE:0.098399
测试 MSE:0.105056
从结果看,训练、验证和测试误差虽然依次略有升高,但整体处于比较接近的量级,没有出现训练误差很低、测试误差却明显增大的情况。
这个案例比较适合用来观察模型复杂度与拟合能力之间的关系。
例如可以直接修改隐藏节点数量:
hiddenLayerSize
再重新运行。
隐藏节点较少时,模型表达能力有限,可能无法捕捉曲线中的复杂变化,容易出现欠拟合。
而随着隐藏节点增加,模型能够表达更复杂的函数,但如果复杂度过高,也可能逐渐开始贴合训练数据中的噪声。
所以“隐藏层节点越多,模型越好”并不成立。
相比单独看一张“欠拟合—正常拟合—过拟合”的示意图,直接修改参数再观察拟合曲线变化会直观很多。
鸢尾花分类:训练91%,测试87%
鸢尾花案例使用经典 Iris 数据完成三分类。
输入数据首先进行特征归一化,然后使用 tanh 随机特征和 Softmax 输出层进行训练,默认训练 300 轮。
运行结果:
训练准确率:91.43%
测试准确率:86.67%
如果只看 86.67% 的测试准确率,其实很难知道模型究竟错在什么地方。
混淆矩阵就能进一步把错误拆开。
从运行结果看,setosa 和 virginica 的测试样本都被正确识别,错误主要集中在 versicolor,其中有 6 个样本被预测成 virginica。
这也说明,评价一个分类模型时,只看 accuracy 往往还不够。
准确率回答的是:
“总共分对了多少?”
而混淆矩阵还能继续回答:
“哪些类别之间最容易混淆?”
这也是这个案例相比单纯输出一个准确率更有价值的地方。
SOM聚类:36个神经元中31个被命中
SOM 案例同样使用鸢尾花特征,但和前面的分类任务有一个很明显的区别:
训练过程中完全不使用样本标签。
标签只在训练结束后用于解释聚类结果,并不会参与网络权重更新。
默认参数为:
SOM 网格:6 × 6
神经元数量:36
训练轮数:200
运行后,36 个神经元中有 31 个被样本命中,也就是说有 5 个神经元为空。
命中图可以看到不同样本最终集中映射到了哪些神经元。
而 U-Matrix 展示的是相邻神经元权重向量之间的距离。距离较大的区域,意味着相邻神经元所代表的特征差异更明显,可以辅助观察潜在的聚类边界。
这里出现少量空神经元并不奇怪。
SOM 网格大小是人为设定的,而样本在特征空间中的分布并不会恰好均匀覆盖整个 6×6 网格,因此部分节点没有样本命中属于正常现象。
这个案例也比较适合自己继续改参数,例如尝试:
4×4
5×5
6×6
8×8
观察不同网格规模下的样本命中情况和 U-Matrix 会有什么变化。
这样也能更直观地理解 SOM 中所谓的竞争学习和拓扑保持到底表现在哪里。
卷积算子可视化:32×32最后变成15×15
相比前面几个案例主要关注“训练”,这个案例更侧重于观察神经网络前向传播过程中发生了什么。
程序首先读取一张混合图形图像,并将其缩放到:
32 × 32
然后依次执行:
3×3卷积
↓
ReLU
↓
2×2最大池化
↓
全连接
↓
Softmax
其中图像尺寸变化非常直观:
输入 32 × 32
卷积后 30 × 30
池化后 15 × 15
这里使用的是一个 3×3 卷积核,在没有 Padding 的情况下作用于 32×32 图像。
输出尺寸为:
32 - 3 + 1 = 30
因此卷积结果变成:
30 × 30
随后经过 2×2 最大池化:
30 / 2 = 15
最后得到:
15 × 15
运行得到的 Softmax 概率约为:
[0.2793, 0.3052, 0.4155]
其中第 3 个类别的概率最高,因此最终输出类别为 3。
不过这个案例真正值得看的其实不是“最后预测成了类别 3”,而是前面的整个变化过程。
输入图像经过卷积后提取局部特征,ReLU 去掉负响应,再经过最大池化进一步压缩尺寸,最后才进入全连接和 Softmax。
这样一来,卷积、激活函数和池化不再只是网络结构图里的几个方框,而是变成了具体的矩阵尺寸和数值变化。
对于后面继续学习 CNN,这种可视化会比较有帮助。
把5个案例放在一起看
这 5 个案例并不是为了搭建一个复杂的深度学习系统,而更像是把几个基础问题分别拆出来验证。
| 案例 | 主要观察内容 |
|---|---|
| SOM 无监督聚类 | 没有标签时样本如何通过竞争学习形成聚类 |
| Softmax 与交叉熵 | 多分类概率、损失函数和梯度下降之间的关系 |
| 算子可视化 | 卷积、ReLU、池化如何改变图像和特征尺寸 |
| 函数拟合 | 模型复杂度如何影响非线性拟合效果 |
| 鸢尾花分类 | 多分类模型如何训练,并通过混淆矩阵分析错误 |
我觉得这类小案例比较适合作为深度学习入门阶段的配套练习。
原因在于,它们没有一开始就把所有过程封装进复杂框架中。
Softmax、SOM、函数拟合和鸢尾花分类中的核心计算过程都比较容易跟踪,卷积、池化等部分则借助 TyDeepLearning 的基础算子完成。
这样可以先把单个概念看清楚,再去学习 CNN、RNN、LSTM 等更复杂的网络结构。
这些案例也有明显的边界
当然,这几个案例的定位主要是原理演示和教学练习,并不是生产级深度学习任务。
首先,数据规模都比较小。
鸢尾花只有 150 条数据,函数拟合也只是百余个采样点,因此这里的训练速度、准确率和误差水平不能直接类比真实的大规模深度学习任务。
其次,随机初始化和数据划分会影响最终指标。
如果修改随机种子,训练准确率、测试准确率或者 MSE 都可能出现一定变化,因此没有必要过度关注小数点后的具体数值。
另外,算子可视化案例中的全连接权重采用固定构造,最终得到的 Softmax 概率主要用于演示完整的前向传播过程,并没有进行真正意义上的网络训练。
所以这套案例更值得关注的是:
算法是怎么一步步运行的,而不是最后的准确率有多高。
小结
如果准备开始学习 CNN、RNN、LSTM 等更复杂的网络,我觉得可以先把几个基础问题弄明白:
-
Softmax 怎样把 logits 转换成类别概率?
-
交叉熵为什么可以作为多分类损失函数?
-
损失值是怎样通过训练逐渐下降的?
-
卷积之后特征图尺寸怎么算?
-
ReLU 和池化分别改变了什么?
-
无监督聚类和有监督分类究竟差在哪里?
-
隐藏节点数量为什么会影响拟合能力?
-
除了准确率,还能怎样分析模型的分类结果?
这 5 个 Julia 案例基本都能找到对应的动手实验。
如果只是想快速调用一个现成模型,它们可能显得有些基础;但如果想在继续学习复杂网络之前,把 Softmax、交叉熵、卷积、聚类、拟合这些概念真正跑一遍,这套案例还是比较合适的。
完整脚本、数据素材和说明文件可以在这里获取:
深度学习综合教学案例库
更多推荐



所有评论(0)