图像分类——基于Deeplearning4j
目录
本节将讨论如何使用Deeplearning4j库实现一些神经网络结构。
创建工程
接着前面的项目,首先导入maven依赖
<dependency>
<groupId>org.deeplearning4j</groupId>
<artifactId>deeplearning4j-core</artifactId>
<version>0.4-rc3.8</version>
</dependency>
<dependency>
<groupId>org.deeplearning4j</groupId>
<artifactId>deeplearning4j-nlp</artifactId>
<version>0.4-rc3.8</version>
</dependency>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-x86</artifactId>
<version>0.4-rc3.8</version>
</dependency>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>canova-nd4j-image</artifactId>
<version>0.0.0.14</version>
</dependency>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>canova-nd4j-codec</artifactId>
<version>0.0.0.14</version>
</dependency>

Deeplearning4j
Deeplearning4j库,它是Java与Scala环境下的开源分布式深度学习项目。 Deeplearning4j依赖Spark与Hadoop使用MapReduce框架,并行训练模型,且反复对中心模型中产 生参数进行平均。
MNIST 数据集
MNIST数据集是最著名的数据集之一,由手写数字组成,如图所示。该数据集包含60 000 个训练与10 000个测试图像。

这个数据集通常用在图像识别问题中,以测试算法性能。最差记录的错误率是12%,测试时 使用单层神经网络中的SVM算法,并且没有做预处理。截止到2016年,最低的错误率只有0.21%, 使用的是DropConnect神经网络;紧随其后的是深度卷积网络,错误率为0.23%;然后是深度前 馈网络,错误率是0.35%。
接下来,让我们看看如何加载数据集。
加载数据
Deeplearning4j 提供 了“开箱 即用”的 MNIST 数 据集 加载器。加 载器被初始 化为 DataSetIterator。先导入DataSetIterator类与所有支持的数据集,这些数据集是impl包 的一部分,包含的数据集有Iris、MNIST及其他。
接着定义一些常量,比如28×28个像素组成的图像,有10个目标类与60 000个样本。新初始 化一个MnistDataSetIterator类,用于下载数据集及其标签。参数分别是迭代批大小、总样 本数,以及是否将数据集二值化:
// 定义常量
final int numRows = 28; // 输入图像的高度
final int numColumns = 28; // 输入图像的宽度
int outputNum = 10; // 输出的类别数量(0-9)
int numSamples = 60000; // 训练集的样本数量
int batchSize = 100; // 每批数据的大小
int iterations = 10; // 训练时的迭代次数
int seed = 123; // 随机种子
int listenerFreq = batchSize / 5; // 监听器的频率,每5批次打印一次分数
// 输出加载数据的信息
System.out.println("加载数据···");
// 创建MNIST数据集迭代器,用于读取训练集
DataSetIterator iterator = new MnistDataSetIterator(batchSize, numSamples, true);
但是这里有个问题,这里是自动从国外拉数据,开了VPN也会失败,所以我们可以写个自定义的数据加载器加载本地数据,不过容易OOM,所以我这里没跑出来。
// 定义常量
final int numRows = 28; // 输入图像的高度
final int numColumns = 28; // 输入图像的宽度
int outputNum = 10; // 输出的类别数量(0-9)
int numSamples = 60000; // 训练集的样本数量
int batchSize = 100; // 每批数据的大小
int iterations = 10; // 训练时的迭代次数
int seed = 123; // 随机种子
int listenerFreq = batchSize / 5; // 监听器的频率,每5批次打印一次分数
// 输出加载数据的信息
System.out.println("加载数据···");
// 创建MNIST数据集迭代器,用于读取训练集
DataSetIterator iterator = getMnistDataSetIterator(batchSize, true);
private static DataSetIterator getMnistDataSetIterator(int batchSize, boolean train) throws IOException {
String imagesFile = train ? "train-images-idx3-ubyte.gz" : "t10k-images-idx3-ubyte.gz";
String labelsFile = train ? "train-labels-idx1-ubyte.gz" : "t10k-labels-idx1-ubyte.gz";
INDArray images = loadImages(new File(BASE_PATH, imagesFile));
INDArray labels = loadLabels(new File(BASE_PATH, labelsFile));
DataSet dataSet = new DataSet(images, labels);
// 手动标准化数据
INDArray mean = images.mean(0);
INDArray std = images.std(0);
images.subiRowVector(mean);
images.diviRowVector(std);
List<DataSet> list = new ArrayList<>();
for (int i = 0; i < dataSet.numExamples(); i++) {
list.add(dataSet.get(i));
}
return new ListDataSetIterator(list, batchSize);
}
private static INDArray loadImages(File file) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
int magicNumber = dis.readInt();
//if (magicNumber != 2051) {
// throw new IOException("Invalid magic number in image file!");
//}
int numImages = dis.readInt();
int numRows = dis.readInt();
int numColumns = dis.readInt();
INDArray images = Nd4j.create(numImages, 1, numRows, numColumns);
for (int i = 0; i < numImages; i++) {
for (int r = 0; r < numRows; r++) {
for (int c = 0; c < numColumns; c++) {
int pixel = dis.readUnsignedByte();
images.putScalar(new int[]{i, 0, r, c}, pixel);
}
}
}
return images;
}
}
private static INDArray loadLabels(File file) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
int magicNumber = dis.readInt();
//if (magicNumber != 2049) {
// throw new IOException("Invalid magic number in label file!");
//}
int numLabels = dis.readInt();
INDArray labels = Nd4j.zeros(numLabels, 10);
for (int i = 0; i < numLabels; i++) {
int label = dis.readUnsignedByte();
labels.putScalar(new int[]{i, label}, 1.0);
}
return labels;
}
}
创建模型
本节将讨论如何实际创建一个神经网络模型。先创建一个基本的单层神经网络建立一个基准 标杆,并且学习基本操作。随后,使用DBN与多层卷积网络改进初始结果。
创建单层回归模型
先创建一个单层回归模型,它基于softmax激活函数,如图8-10所示。由于我们只有一个层, 所以神经网络的输入是所有图形像素,即28×28=784个神经元。输出神经元的个数为10,对应于 每一个数字。网络中的层是全连接的,如图

使用一个NeuralNetConfiguration Builder对象定义神经网络。
接着,为梯度搜索定义参数,以便使用共轭梯度最优化算法做迭代。其中,momentum参数 指定优化算法收敛到局部最优的速度,momentum值越高,训练得越快;但速度过快有可能降低 模型准确度
接下来,指定网络有一个层,并且定义错误函数(NEGATIVELOGLIKELIHOOD)、内部 感知器激活函数(softmax),以及输入和输出层的数量,对应于总的图像像素和目标变量数
最后,为网络开启预训练(pretrain),关闭反向传播,实际创建未经训练的网络结构
/**
* 创建一个包含单层输出层的神经网络模型,用于Softmax回归分类任务。
*
* @param seed 随机种子,用于确保模型初始化的可重复性。
* @param iterations 训练时的迭代次数。
* @param numRows 输入数据的行数。
* @param numColumns 输入数据的列数。
* @param outputNum 输出的类别数量。
* @return 一个新的多层神经网络模型。
*/
private static MultiLayerNetwork softMaxRegression(int seed, int iterations, int numRows, int numColumns, int outputNum) {
// 配置神经网络的架构和训练参数
MultiLayerConfiguration configuration = new NeuralNetConfiguration.Builder()
.seed(seed) // 设置随机种子,确保每次运行的初始化一致
.gradientNormalization(GradientNormalization.ClipElementWiseAbsoluteValue) // 梯度裁剪,防止梯度爆炸
.gradientNormalizationThreshold(1.0) // 梯度裁剪的阈值
.iterations(iterations) // 设置训练时的迭代次数
.momentum(0.5) // 设置动量(用于加速训练)
.momentumAfter(Collections.singletonMap(3, 0.9)) // 在第三个迭代后增加动量
.optimizationAlgo(OptimizationAlgorithm.CONJUGATE_GRADIENT) // 设置优化算法为共轭梯度
.list(1) // 定义网络的层数为1层
.layer(0, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) // 定义输出层
.activation("softmax") // 设置激活函数为softmax(用于多分类)
.nIn(numColumns * numRows) // 输入层的大小为 numRows * numColumns
.nOut(outputNum) // 输出层的大小为输出的类别数量
.build())
.pretrain(true) // 启用预训练(无监督学习)
.backprop(false) // 禁用反向传播(监督学习)
.build(); // 构建配置对象
// 根据配置创建多层神经网络模型
MultiLayerNetwork model = new MultiLayerNetwork(configuration);
// 返回创建的模型
return model;
}
一旦网络结构定义完成后,可以用它初始化一个MultiLayerNetwork对象
接下来,调用setListeners方法,绑定模型与训练数据
此外,调用fit(int)方法触发端对端的网络训练
为了评价模型,新建并初始化一个Evaluation对象,用于存储批结果
然后,在数据集上分批做迭代,以便让内存消耗保持在一个合理的范围内,并且结果保存在 一个eval对象中
最后,调用stats()函数获取结果
private static String BASE_PATH = ClassUtils.getDefaultClassLoader().getResource("data/test08").getPath();
public static void main(String[] args) throws Exception {
// 定义常量
final int numRows = 28; // 输入图像的高度
final int numColumns = 28; // 输入图像的宽度
int outputNum = 10; // 输出的类别数量(0-9)
int numSamples = 60000; // 训练集的样本数量
int batchSize = 100; // 每批数据的大小
int iterations = 10; // 训练时的迭代次数
int seed = 123; // 随机种子
int listenerFreq = batchSize / 5; // 监听器的频率,每5批次打印一次分数
// 输出加载数据的信息
System.out.println("加载数据···");
// 创建MNIST数据集迭代器,用于读取训练集
DataSetIterator iterator = new MnistDataSetIterator(batchSize, numSamples, true);
//DataSetIterator iterator = getMnistDataSetIterator(batchSize, true);
// 输出构建模型的信息
System.out.println("构建模型···");
// 创建并初始化神经网络模型
MultiLayerNetwork model = softMaxRegression(seed, iterations, numRows, numColumns, outputNum);
model.init();
// 添加训练监听器,每5批次打印一次分数
model.setListeners(Collections.singletonList((IterationListener) new ScoreIterationListener(listenerFreq)));
// 输出训练信息
System.out.println("训练模型···");
// 训练模型
model.fit(iterator);
// 输出评估信息
System.out.println("评估模型···");
// 创建评估对象
Evaluation evaluation = new Evaluation(outputNum);
// 创建测试集的数据集迭代器
DataSetIterator test = new MnistDataSetIterator(batchSize, 10000);
// 遍历测试集,对模型进行评估
while (test.hasNext()) {
DataSet next = test.next();
INDArray data = model.output(next.getFeatures()); // 获取模型的输出
evaluation.eval(next.getLabels(), data); // 评估模型的预测结果
}
// 打印评估结果
System.out.println(evaluation.stats());
}
基本的单层模型准确度如下
Accuracy: 0.8945
Precision: 0.8985
Recall: 0.8922
F1 Score: 0.8953
从上面结果可以看到,模型准确率是89.22%,错误率为10.88%,这表示模型在MNIST数据 集上表现很差。接下来对模型做进一步改善,将其从简单的单层网络变为带有适度复杂度的深度 信念网络,这种网络使用了受限玻尔兹曼机与多层卷积网络。
创建深度信念网络
本节将创建一个基于受限玻尔兹曼机的深度信念网络,如图8-11所示。深度信念网络由4个 层组成,第一个层将784个输入缩小为500个神经元,然后是250个,再然后是200个,最后是10 个目标值

代码与上一个例子一样,下面看看如何配置这样一个网络
接着定义梯度优化算法
然后,指定网络有4个层
第一个层的输入有784个神经元,输出为500个神经元。使用均方根误差交叉熵——Xavier算 法初始化权重,基于输入与输出神经元的数目自动确定初始化权重的范围
接下来的两个层拥有相同的参数,但是输入与输出神经元的数目有所不同
现在,使用最后一个层将神经元映射到输出。使用softmax激活函数完成这个任务
/**
* 构建一个深度信念网络(Deep Belief Network, DBN)模型。
*
* @param seed 随机种子,用于初始化模型的权重和其他随机变量,确保结果的可重复性。
* @param iterations 训练迭代的次数。
* @param numRows 输入数据的行数。
* @param numColumns 输入数据的列数。
* @param outputNum 输出层的神经元数量,通常等于分类的类别数。
* @return 返回一个配置好的 MultiLayerNetwork 模型。
*/
private static MultiLayerNetwork deepBeliefNetwork(int seed, int iterations, int numRows, int numColumns, int outputNum) {
// 配置神经网络的架构和参数
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
// 设置随机种子,确保模型初始化的权重相同
.seed(seed)
// 梯度归一化,用于防止梯度爆炸或消失问题
.gradientNormalization(GradientNormalization.ClipElementWiseAbsoluteValue)
// 设置梯度归一化的阈值为1.0
.gradientNormalizationThreshold(1.0)
// 设置训练迭代的次数
.iterations(iterations)
// 设置初始动量,用于加速训练过程
.momentum(0.5)
// 设置动量的变化策略,在第3次迭代后动量增加到0.9
.momentumAfter(Collections.singletonMap(3, 0.9))
// 设置优化算法为共轭梯度法(Conjugate Gradient)
.optimizationAlgo(OptimizationAlgorithm.CONJUGATE_GRADIENT)
// 设置网络的层数为4层
.list(4)
// 第一层:RBM,输入层到隐藏层
.layer(0, new RBM.Builder()
// 输入层的神经元数量为 numRows * numColumns
.nIn(numRows * numColumns)
// 隐藏层的神经元数量为500
.nOut(500)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第二层:RBM,隐藏层到隐藏层
.layer(1, new RBM.Builder()
// 输入层的神经元数量为500
.nIn(500)
// 隐藏层的神经元数量为250
.nOut(250)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第三层:RBM,隐藏层到隐藏层
.layer(2, new RBM.Builder()
// 输入层的神经元数量为250
.nIn(250)
// 隐藏层的神经元数量为200
.nOut(200)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第四层:输出层,用于分类任务
.layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
// 激活函数为softmax,用于多分类问题
.activation("softmax")
// 输入层的神经元数量为200
.nIn(200)
// 输出层的神经元数量为outputNum
.nOut(outputNum)
.build())
// 设置预训练为真,即使用RBM进行无监督预训练
.pretrain(true)
// 设置反向传播为假,即不进行反向传播微调
.backprop(false)
.build();
// 创建并返回配置好的多层神经网络模型
MultiLayerNetwork model = new MultiLayerNetwork(conf);
return model;
}
训练与评价的其他部分与单层网络的例子是一样的。请注意,训练深度网络耗费的时间可能 明显要比训练单层网络长得多,但准确度应该能够达到93%左右。
接下来,让我们看一看另外一个深度网络。
创建多层卷积网络
本章最后一个例子中,让我们一起学习如何创建卷积网络,如图8-12所示。这个卷积网络由 7个层组成:首先,用max pooling重复两对卷积与子采样层;然后将最后一个子采样层连接到紧 密相连的前馈神经网络,最后三个层中依次含有120个神经元、84个神经元、10个神经元。这样 一种网络实际上组成了完整的图像识别管道,前4个图层对应于特征提取,后3个图层对应于学习 模型

网络配置的初始化方法与前面一样
接着,指定梯度下降算法及其参数
此外,还要指定7个网络层
第一个卷积层的输入是一幅完整图像,而输出是6个特征图。卷积层应用一个5×5的过滤器, 结果存储在1×1单元格中
第二个层是子采样层,它接收一个2×2区域,并把最大结果存成2×2元素
接下来的两个层重复前面两个层
接着,将子采样层的输出连接到稠密前馈网络,先是120个神经元,然后穿过另一个层,变 成84个神经元
最后一个层将84个神经元与10个输出神经元连接在一起
/**
* 构建一个深度卷积神经网络(Deep Convolutional Neural Network, CNN)模型。
*
* @param seed 随机种子,用于初始化权重和偏差,确保结果可复现。
* @param iterations 训练时的迭代次数。
* @param numRows 输入数据的行数(图像高度)。
* @param numColumns 输入数据的列数(图像宽度)。
* @param outputNum 输出层的神经元数量,通常对应分类任务的类别数。
* @return 返回一个配置好的深度卷积神经网络模型。
*/
private static MultiLayerNetwork deepConvNetwork(int seed, int iterations, int numRows, int numColumns, int outputNum) {
// 构建神经网络配置
MultiLayerConfiguration.Builder conf = new NeuralNetConfiguration.Builder()
.seed(seed) // 设置随机种子
.iterations(iterations) // 设置训练迭代次数
.activation("sigmoid") // 设置激活函数为Sigmoid
.weightInit(WeightInit.DISTRIBUTION) // 设置权重初始化方式为分布初始化
.dist(new NormalDistribution(0.0, 0.01)) // 使用均值为0,标准差为0.01的正态分布初始化权重
.learningRate(1e-3) // 设置学习率
.learningRateScoreBasedDecayRate(1e-1) // 设置基于得分的学习率衰减率
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT) // 使用随机梯度下降优化算法
.list(7) // 设置网络层数为7层
// 第0层:卷积层
.layer(0, new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1})
.name("cnn1") // 层名称为cnn1
.nIn(numRows * numColumns) // 输入数据的维度为图像的行数乘以列数
.nOut(6) // 输出特征图的数量为6
.build())
// 第1层:最大池化层
.layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX, new int[]{2, 2}, new int[]{2, 2})
.name("maxpool1") // 层名称为maxpool1
.build())
// 第2层:卷积层
.layer(2, new ConvolutionLayer.Builder(new int[]{5, 5}, new int[]{1, 1})
.name("cnn2") // 层名称为cnn2
.nOut(16) // 输出特征图的数量为16
.biasInit(1) // 设置偏差初始化为1
.build())
// 第3层:最大池化层
.layer(3, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX, new int[]{2, 2}, new int[]{2, 2})
.name("maxpool2") // 层名称为maxpool2
.build())
// 第4层:全连接层
.layer(4, new DenseLayer.Builder()
.name("ffn1") // 层名称为ffn1
.nOut(120) // 输出神经元数量为120
.build())
// 第5层:全连接层
.layer(5, new DenseLayer.Builder()
.name("ffn2") // 层名称为ffn2
.nOut(84) // 输出神经元数量为84
.build())
// 第6层:输出层
.layer(6, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.name("output") // 层名称为output
.nOut(outputNum) // 输出神经元数量为outputNum
.activation("softmax") // 使用Softmax激活函数
.build())
.backprop(true) // 启用反向传播
.pretrain(false) // 不进行预训练
.cnnInputSize(numRows, numColumns, 1); // 设置CNN输入数据的尺寸(高度、宽度、通道数)
// 构建并返回多层神经网络模型
MultiLayerNetwork model = new MultiLayerNetwork(conf.build());
return model;
}
为了训练这个结构,可以重用前面两个例子中编写的代码。再次提醒,训练可能要花一些时 间,训练结束后,网络的准确度应该能达到98%左右。
完整代码
public class NeuralNetworks {
private static String BASE_PATH = ClassUtils.getDefaultClassLoader().getResource("data/test08").getPath();
public static void main(String[] args) throws Exception {
// 定义常量
final int numRows = 28; // 输入图像的高度
final int numColumns = 28; // 输入图像的宽度
int outputNum = 10; // 输出的类别数量(0-9)
int numSamples = 60000; // 训练集的样本数量
int batchSize = 100; // 每批数据的大小
int iterations = 10; // 训练时的迭代次数
int seed = 123; // 随机种子
int listenerFreq = batchSize / 5; // 监听器的频率,每5批次打印一次分数
// 输出加载数据的信息
System.out.println("加载数据···");
// 创建MNIST数据集迭代器,用于读取训练集
DataSetIterator iterator = new MnistDataSetIterator(batchSize, numSamples, true);
//DataSetIterator iterator = getMnistDataSetIterator(batchSize, true);
// 输出构建模型的信息
System.out.println("构建模型···");
// 创建并初始化神经网络模型
MultiLayerNetwork model = softMaxRegression(seed, iterations, numRows, numColumns, outputNum);
model.init();
// 添加训练监听器,每5批次打印一次分数
model.setListeners(Collections.singletonList((IterationListener) new ScoreIterationListener(listenerFreq)));
// 输出训练信息
System.out.println("训练模型···");
// 训练模型
model.fit(iterator);
// 输出评估信息
System.out.println("评估模型···");
// 创建评估对象
Evaluation evaluation = new Evaluation(outputNum);
// 创建测试集的数据集迭代器
DataSetIterator test = new MnistDataSetIterator(batchSize, 10000);
// 遍历测试集,对模型进行评估
while (test.hasNext()) {
DataSet next = test.next();
INDArray data = model.output(next.getFeatures()); // 获取模型的输出
evaluation.eval(next.getLabels(), data); // 评估模型的预测结果
}
// 打印评估结果
System.out.println(evaluation.stats());
}
private static DataSetIterator getMnistDataSetIterator(int batchSize, boolean train) throws IOException {
String imagesFile = train ? "train-images-idx3-ubyte.gz" : "t10k-images-idx3-ubyte.gz";
String labelsFile = train ? "train-labels-idx1-ubyte.gz" : "t10k-labels-idx1-ubyte.gz";
INDArray images = loadImages(new File(BASE_PATH, imagesFile));
INDArray labels = loadLabels(new File(BASE_PATH, labelsFile));
DataSet dataSet = new DataSet(images, labels);
// 手动标准化数据
INDArray mean = images.mean(0);
INDArray std = images.std(0);
images.subiRowVector(mean);
images.diviRowVector(std);
List<DataSet> list = new ArrayList<>();
for (int i = 0; i < dataSet.numExamples(); i++) {
list.add(dataSet.get(i));
}
return new ListDataSetIterator(list, batchSize);
}
private static INDArray loadImages(File file) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
int magicNumber = dis.readInt();
//if (magicNumber != 2051) {
// throw new IOException("Invalid magic number in image file!");
//}
int numImages = dis.readInt();
int numRows = dis.readInt();
int numColumns = dis.readInt();
INDArray images = Nd4j.create(numImages, 1, numRows, numColumns);
for (int i = 0; i < numImages; i++) {
for (int r = 0; r < numRows; r++) {
for (int c = 0; c < numColumns; c++) {
int pixel = dis.readUnsignedByte();
images.putScalar(new int[]{i, 0, r, c}, pixel);
}
}
}
return images;
}
}
private static INDArray loadLabels(File file) throws IOException {
try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {
int magicNumber = dis.readInt();
//if (magicNumber != 2049) {
// throw new IOException("Invalid magic number in label file!");
//}
int numLabels = dis.readInt();
INDArray labels = Nd4j.zeros(numLabels, 10);
for (int i = 0; i < numLabels; i++) {
int label = dis.readUnsignedByte();
labels.putScalar(new int[]{i, label}, 1.0);
}
return labels;
}
}
/**
* 创建一个包含单层输出层的神经网络模型,用于Softmax回归分类任务。
*
* @param seed 随机种子,用于确保模型初始化的可重复性。
* @param iterations 训练时的迭代次数。
* @param numRows 输入数据的行数。
* @param numColumns 输入数据的列数。
* @param outputNum 输出的类别数量。
* @return 一个新的多层神经网络模型。
*/
private static MultiLayerNetwork softMaxRegression(int seed, int iterations, int numRows, int numColumns, int outputNum) {
// 配置神经网络的架构和训练参数
MultiLayerConfiguration configuration = new NeuralNetConfiguration.Builder()
.seed(seed) // 设置随机种子,确保每次运行的初始化一致
.gradientNormalization(GradientNormalization.ClipElementWiseAbsoluteValue) // 梯度裁剪,防止梯度爆炸
.gradientNormalizationThreshold(1.0) // 梯度裁剪的阈值
.iterations(iterations) // 设置训练时的迭代次数
.momentum(0.5) // 设置动量(用于加速训练)
.momentumAfter(Collections.singletonMap(3, 0.9)) // 在第三个迭代后增加动量
.optimizationAlgo(OptimizationAlgorithm.CONJUGATE_GRADIENT) // 设置优化算法为共轭梯度
.list(1) // 定义网络的层数为1层
.layer(0, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) // 定义输出层
.activation("softmax") // 设置激活函数为softmax(用于多分类)
.nIn(numColumns * numRows) // 输入层的大小为 numRows * numColumns
.nOut(outputNum) // 输出层的大小为输出的类别数量
.build())
.pretrain(true) // 启用预训练(无监督学习)
.backprop(false) // 禁用反向传播(监督学习)
.build(); // 构建配置对象
// 根据配置创建多层神经网络模型
MultiLayerNetwork model = new MultiLayerNetwork(configuration);
// 返回创建的模型
return model;
}
/**
* 构建一个深度信念网络(Deep Belief Network, DBN)模型。
*
* @param seed 随机种子,用于初始化模型的权重和其他随机变量,确保结果的可重复性。
* @param iterations 训练迭代的次数。
* @param numRows 输入数据的行数。
* @param numColumns 输入数据的列数。
* @param outputNum 输出层的神经元数量,通常等于分类的类别数。
* @return 返回一个配置好的 MultiLayerNetwork 模型。
*/
private static MultiLayerNetwork deepBeliefNetwork(int seed, int iterations, int numRows, int numColumns, int outputNum) {
// 配置神经网络的架构和参数
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
// 设置随机种子,确保模型初始化的权重相同
.seed(seed)
// 梯度归一化,用于防止梯度爆炸或消失问题
.gradientNormalization(GradientNormalization.ClipElementWiseAbsoluteValue)
// 设置梯度归一化的阈值为1.0
.gradientNormalizationThreshold(1.0)
// 设置训练迭代的次数
.iterations(iterations)
// 设置初始动量,用于加速训练过程
.momentum(0.5)
// 设置动量的变化策略,在第3次迭代后动量增加到0.9
.momentumAfter(Collections.singletonMap(3, 0.9))
// 设置优化算法为共轭梯度法(Conjugate Gradient)
.optimizationAlgo(OptimizationAlgorithm.CONJUGATE_GRADIENT)
// 设置网络的层数为4层
.list(4)
// 第一层:RBM,输入层到隐藏层
.layer(0, new RBM.Builder()
// 输入层的神经元数量为 numRows * numColumns
.nIn(numRows * numColumns)
// 隐藏层的神经元数量为500
.nOut(500)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第二层:RBM,隐藏层到隐藏层
.layer(1, new RBM.Builder()
// 输入层的神经元数量为500
.nIn(500)
// 隐藏层的神经元数量为250
.nOut(250)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第三层:RBM,隐藏层到隐藏层
.layer(2, new RBM.Builder()
// 输入层的神经元数量为250
.nIn(250)
// 隐藏层的神经元数量为200
.nOut(200)
// 权重初始化方式为Xavier初始化
.weightInit(WeightInit.XAVIER)
// 损失函数为均方根误差交叉熵损失函数
.lossFunction(LossFunctions.LossFunction.RMSE_XENT)
// 可见单元类型为二元单元
.visibleUnit(RBM.VisibleUnit.BINARY)
// 隐藏单元类型为二元单元
.hiddenUnit(RBM.HiddenUnit.BINARY)
.build())
// 第四层:输出层,用于分类任务
.layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
// 激活函数为softmax,用于多分类问题
.activation("softmax")
// 输入层的神经元数量为200
.nIn(200)
// 输出层的神经元数量为outputNum
.nOut(outputNum)
.build())
// 设置预训练为真,即使用RBM进行无监督预训练
.pretrain(true)
// 设置反向传播为假,即不进行反向传播微调
.backprop(false)
.build();
//
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)