「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。 本文是神经网络深度学习-一个demo编程和最少必要理论入门。
神经网络
深度学习(DL, Deep Learning)是机器学习(ML, Machine Learning)领域中一个研究方向,它被引入机器学习使其更接近于最初的目标——人工智能。深度神经网络(DNN)是深度学习的一种框架,它是一种具备至少一个隐层的神经网络。与浅层神经网络类似,深度神经网络也能够为复杂非线性系统提供建模,但多出的层次为模型提供了更高的抽象层次,因而提高了模型的能力。
神经网络理论
神经网络的深度学习是从已有的数据集里面学习出表达一个实体的最佳预测。比如对于这些手写的数字,就是预测出实际的数字是什么。
在神经网络里面,我们有这样的模型:
可以得到每个节点的值
转为矩阵相乘,则是
这样就得到了一个比较简单的式子。 这是一个层层递推的公式,神经网络的层级越多,则这个公式将会更复杂。
假设C为代价函数:
是神经网络的输出值。
我们的目标就是使得代价最小。可以使用反向误差传播法进行训练。 权重更新可以使用下式进行随机梯度下降法求解
C为代价函数, 为学习率。
我们看到在这个模型里面引入了很多的参数,那么它们究竟发挥了什么样的作用呢。
权重w
在平面上,w的改变可以用来改变直线的方向。
偏置b
在平面上,偏置b可以改变左右的位置。
这个在分类的过程中能发挥很大的作用。
激活函数的作用
使用激活函数,可以使得非线性化。
相比于线性的简单分类,就是更加灵活了。
模型训练
MINIST数据集
本文引入的demo是MINIST数据集的学习。如下图:
是一个手写数字图像的数据集,每个图像都由一个整数标记。可用MINIST来训练一个神经网络,使之能读取每幅图像并识别其中的数字。用到的MINIST数据集的特点是:
- 包含6万个样例的训练集和1万个样例的测试集。
- 每幅图像是28*28的像素点组成
- 一共有0~9这10个数字
DataSetIterator mnistTrain = new MnistDataSetIterator(128, true, rngSeed);
DataSetIterator mnistTest = new MnistDataSetIterator(128, false, rngSeed);
每次将128张图片输入神经网络,进行一次迭代,更新权重矩阵等参数后,继续学习后面的128张图片,直到所有的图片都学习完。我们称这样的过程为一个epoch,比如我们使用15次epoch来训练这个神经网络模型。
model.fit(mnistTrain, 15);
模型训练
使用deeplearning4j的MNIST示例,代码如下:
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(rngSeed) //include a random seed for reproducibility
// use stochastic gradient descent as an optimization algorithm
.updater(new Nesterovs(0.006, 0.9))
.l2(1e-4)
.list()
.layer(new DenseLayer.Builder() //create the first, input layer with xavier initialization
.nIn(numRows * numColumns)
.nOut(1000)
.activation(Activation.RELU)
.weightInit(WeightInit.XAVIER)
.build())
.layer(new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD) //create hidden layer
.nIn(1000)
.nOut(outputNum)
.activation(Activation.SOFTMAX)
.weightInit(WeightInit.XAVIER)
.build())
.build();
MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
//print the score with every 1 iteration
model.setListeners(new ScoreIterationListener(1));
log.info("Train model....");
model.fit(mnistTrain, numEpochs);
- 随机数是用来初始化一些参数的,比如权重矩阵
- Nesterovs是使用动量的随机梯度下降法(来不断更新的值),学习率为0.006,动量为0.9
- l2(1e-4),我们用L2正则化来防止个别权重对总体结果产生过大的影响
对于神经网络的每一层,都可以自定义输入和输出节点的个数、激活函数等配置。
神经网络的每一层
第一层
第一层,是DenseLayer。
-
输入是numRows*numColumns个数据,刚好是一张图片的像素个数
-
输出到下层的1000个节点
-
激活函数使用ReLU,ReLU是一个类似爬坡的函数,如下示意图。
- 权重初始化是使用WeightInit.XAVIER。 WeightInit.XAVIER比较复杂,函数实现是:
public class WeightInitXavier implements IWeightInit {
@Override
public INDArray init(double fanIn, double fanOut, long[] shape, char order, INDArray paramView) {
Nd4j.randn(paramView).muli(FastMath.sqrt(2.0 / (fanIn + fanOut)));
return paramView.reshape(order, shape);
}
}
在很多论文里面提到,“Xavier”初始化方法是一种很有效的神经网络权重向量初始化方法。
输出层(Output)
这个demo, 第二层,是OutputLayer。
.layer(new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD) //create hidden layer
.nIn(1000)
.nOut(outputNum)
.activation(Activation.SOFTMAX)
.weightInit(WeightInit.XAVIER)
.build())
- 输出层需要提供一个损失函数(使得损失函数最小),这里提供的是LossFunction.NEGATIVELOGLIKELIHOOD,负log极大似然函数。
- nIn:1000,也就是上层的输出值的个数。
- nOut:10,在这个上下文里面就是要分为0-9这10个数字🔢出来
- 激活函数选用SOFTMAX,这是输出层常用的一个激活函数
- 权重矩阵和前面的一样初始化
更多层
为了使得学习效果更好,我们可以在输出层之前加入更多的DenseLayer。
.layer(new DenseLayer.Builder() //create the first input layer.
.nIn(numRows * numColumns)
.nOut(500)
.build())
.layer(new DenseLayer.Builder() //create the second input layer
.nIn(500)
.nOut(100)
.build())
.layer(new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD) //create hidden layer
.activation(Activation.SOFTMAX)
.nOut(outputNum)
.build())
损失和更新
上面配置的神经网络形象的描述了这个学习模型。我们的目标是使得损失函数的值最小。 对于每一个batch中的每一个图片,最开始使用初始的权重矩阵,得到了训练值。训练值和实际是有差别的
因为对误差函数求导过于复杂,可以使用反向误差传播法进行训练。 权重更新可以使用下式进行随机梯度下降法求解
C为代价函数, 为学习率。
循环迭代,直到网络对输入的响应达到满意的预定的目标范围为止。
模型评估结果
模型跑完之后,输出性能:
o.d.o.l.ScoreIterationListener - Score at iteration 7032 is 0.13418120768640643
o.d.o.l.ScoreIterationListener - Score at iteration 7033 is 0.1026667706126966
o.d.o.l.ScoreIterationListener - Score at iteration 7034 is 0.09364372163420558
o.d.e.q.m.f.c.MNISTSingleLayer - Evaluate model....
o.d.e.q.m.f.c.MNISTSingleLayer -
========================Evaluation Metrics========================
# of classes: 10
Accuracy: 0.9757
Precision: 0.9758
Recall: 0.9754
F1 Score: 0.9755
Precision, recall & F1: macro-averaged (equally weighted avg. of 10 classes)
=========================Confusion Matrix=========================
0 1 2 3 4 5 6 7 8 9
---------------------------------------------------
951 1 13 2 2 5 0 1 2 3 | 0 = 0
0 1129 3 0 0 0 1 1 1 0 | 1 = 1
2 2 1020 0 1 0 2 4 1 0 | 2 = 2
0 1 11 983 0 3 0 3 3 6 | 3 = 3
0 0 11 0 958 1 4 2 1 5 | 4 = 4
2 0 0 8 0 871 3 1 3 4 | 5 = 5
2 2 4 1 5 10 933 0 1 0 | 6 = 6
0 4 9 2 0 0 1 1006 2 4 | 7 = 7
1 2 7 4 2 14 0 2 937 5 | 8 = 8
1 4 2 7 12 6 0 4 4 969 | 9 = 9
Confusion matrix format: Actual (rowClass) predicted as (columnClass) N times
==================================================================
o.d.e.q.m.f.c.MNISTDoubleLayer - ****************Example finished********************
- 单层的经过了7034多次迭代后终于结束迭代,得分是 0.09364372163420558(这个其实是损失),可以看出值一步步迭代的过程中损失已经变得越来越小了。
- 7034次迭代也说明了训练的图片个数是,大约6万张。
- 整体的预测达到的效果是:
- 转确性: 97.57%
- 精确率 97.58%
- 召回率 97.54%
- F1值(也就是精确率和召回率的平均)达到了97.55%
- 比如980多张手写的0,其中有951张被识别为0,其他分别有1、13、2、3、5、0、1、2、3张被识别为1、2、3、4、5、6、7、8、9.准确率是97%