MMA 和机器学习
因为去次修了一门模式识别和机器学习, 作业里面需要搞点机器学习的东西, 尝试安利 MMA, 然而失败.
MMA 的话, 实际上确实可能并不是那么的出名, 甚至你可能没准压根就没有听过 MMA 是什么.
凉凉 你说的对, 但是 |\/|ATHE|\/|ATIC/\ 是一款缩写为 MMA 的, 中文翻译为综合格斗的东西.
简单的机器学习的函数
我认为 MMA 的函数对于简单的问题非常的好用, 或者说有点近似于无脑调用了的情况了. 比如说要解决分类问题, 并且使用的是 NaiveBayes 方法. (别问我为什么用这个方法, 只是它目前看来挺靠谱的).
实际上也可以直接不指定方法, 直接让 MMA 为你选择, 并且最后可以使用 Information
的形式来读取训练得到的函数的信息:
众所周知, 模式识别中有两种问题: 分类和回归. 而说得更加玄学一点, 就是假设现实问题中真的存在这样的一个 $F(x)$, 使得输入和结果存在一定的关系. 而最终的目标就是在一个样本集的基础上 ${x \rightarrow F(x) + \mathrm{noise}}$ 希望找到一个 $f(x)$ 使得其尽可能地接近 $F(x)$. 至于分类和回归问题, 那么就是 $f(x)$ 的像空间是连续的, 还是离散的 (尽管
大部分时候这两个问题没法区分).
那么教练, 我想试试看回归模型: 在 MMA 中使用 Predict
命令即可进行回归操作.
尴尬, 这样拟合的函数效果并不是很好💦.
(注: 如果是想要根据函数分布来猜出函数的解析表达式的话, 实际上可以尝试 FindFormula
, 不过最终的效果并不一定很好. 实际上大部分的情况下都是这样的, 虽然大部分情况下, 这些函数很好用, 但是并不是万能的, 经常会打脸.... 如果对解析解非常好奇的话, 有一个项目可以看看: PhySO | Github, PhySO | arxiv)
当然, 还有聚类函数 Cluster
之类的比较通用一些的分类函数. 这里就不一一展开了. 如果想要提高准确率的话, 可以考虑使用 LinearRegression
, LogisticRegression
等等的一些更加细致一些的函数.
这样是不是有点不太够呢?
如果你想要更加细致的, 更加 “实用” 一些的机器学习框架, 不妨来考虑一下使用 MMA 提供的 网络仓库, 里面集成了一些比较常见的神经网络. (并且可以直接在线运行, Basic版本的 Wolfram Cloud 虽然很 low, 但是也不是不能用:
比如以瑟瑟图片检测网络 (Yahoo Open NSFW Model V1) 来作为一个例子 (在 Wolfram Cloud 上跑的):
冷知识: NSFW 的全称是 Not Safe For Work. 所以作为一个正直的青年, 我只能展示一点点, 具体可以自己试试.
能不能再上点强度?
使用 Information[NetModel["Yahoo Open NSFW Model V1"], "SummaryGraphic"]
可以提取并观察一个神经网络的具体构建方式, 里面有各种各样的层, 那么实际上自己通过层开始进行一个网络的构建也不是不可以:
比如说最简单的一个线性的层, 将五个参数整合在一起:
uninitializedNet = NetChain[{LinearLayer[1, "Input" -> 5]},
"Input" -> 5,
"Output" -> 1]
以及构造用于训练的网络:
trainingNet = NetInitialize@NetGraph[<|
"linear" -> uninitializedNet,
"loss" -> MeanSquaredLossLayer[]|>,
{NetPort["Input"] -> "linear" -> NetPort["loss", "Input"],
NetPort["Target"] -> NetPort["loss", "Target"]}]
其中, 损失函数为 MeanSquaredLossLayer
, NetInitialize
给整个网络中的未确定的参数进行随机赋了一个初始值. (注: 如果有那种预训练的网络的话, 你可以理解为预训练相当于给了一个比较好的初始值, 可以有助于训练结果快速收敛)
于是构造一堆数据用来训练:
data = Association[
Flatten[Table[{x1, x2, x3, x4,
x5} -> {2 x1 + 3 x2 - 4 x3 + 5 x4 - 5 x5 + 1 + RandomReal[0.1]},
{x1, -2, 2}, {x2, -2, 2}, {x3, -2, 2}, {x4, -2, 2}, {x5, -2, 2}],
4]];
(* 数据的格式如下 *)
RandomSample[data, 5]
(* <|{0, -1, -1, 1, 1} -> {1.32546},
{1, 1, 0, 1, 1} -> {5.19757},
{0, -1, -2, -1, -2} -> {10.3261},
{1, -2, -2, 0, 2} -> {-5.15193},
{-2, 0, 1, 2, -2} -> {12.8819}|> *)
简单粗暴地划分一下训练集和测试集并喂给训练的网络去进行训练. (注: 一开始 RandomReal
的噪声加大了, 没法消除. )
With[{first = data[[1 ;; -1000]], last = data[[-1001 ;; -1]]},
trainData = <|"Input" -> Keys@first, "Target" -> Values@first|>;
testData = <|"Input" -> Keys@last, "Target" -> Values@last|>;]
results = NetTrain[trainingNet,
trainData, All, ValidationSet -> testData, MaxTrainingRounds -> 320];
从最终的结果中提取训练好的那部分网络并对其进行检测判断:
trainedNet = NetExtract[results["TrainedNet"], "linear"];
testF = Function[{x1, x2, x3, x4, x5}, {2 x1 + 3 x2 - 4 x3 + 5 x4 - 5 x5 + 1}];
(trainedNet[#] - testF @@ #) & /@ {
{1, 0, 0, 0, 0}, {2, 3, 4, 5, 6}, {9, 9, 9, -9, -9}}
(* {{0.0483317}, {0.0473833}, {0.038662}} *)
还行吧.
能不能再进一步?
比如说我想要使用各种各样的 Layer 而不是仅仅只是 LinearLayer. 以及除了简单的单层 Layer, 还想要多层的 Layer, 想要网状结构的 Layer:
没问题, 可以使用 ?*Layer*
来查询所有包含 Layer
名字的函数, 于是就可以用其去构建更加复杂的网络.
图片太长, 懒得截图了.
比如说可以构造一个 LeNet:
uninitializedLenet = NetChain[{
ConvolutionLayer[20, 5],
ElementwiseLayer[Ramp],
PoolingLayer[2, 2],
ConvolutionLayer[50, 5],
ElementwiseLayer[Ramp],
PoolingLayer[2, 2],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],
SoftmaxLayer[]},
"Input" -> enc,
"Output" -> dec]
其中 enc
和 dec
制定为输入和输出的处理器:
dec = NetDecoder[{"Class", Range[0, 9]}];
enc = NetEncoder[{"Image", {28, 28}, "Grayscale"}];
即 dec
为一个可行值为 0~9
的一个分类器, enc
将输入的图像变为 28x28
输入的灰度图像.
能不能再上点强度?
比如说想要自己编写层函数 FunctionLayer
, 想要自己编写损失函数? 虽然好像也不是不行, 但是目前我还没有走到那一步. (简称: 没那个能力)
如果之后能更新的话再更新吧.