DL4J(和相关项目)有很多功能。此篇的目标是总结这个功能,以便用户知道存在什么功能,以及在哪里可以找到更多信息。
前馈层
输出层
卷积层
循环层
无监督层
其它层
图顶点
输入预处理器
迭代/训练监听器
网络保存和加载
网络配置
激活函数
权重初始化
更新器 (优化器)
学习调度
正则化
L1/L2 正则化
Dropout(丢弃)
权重噪声
约束
数据类
迭代器 - 内置 (DL4J-提供数据)
迭代器 -用户提供数据
迭代器 - 适配器和实用迭代器
读取原始数据: DataVec 记录读取器
数据归一化
Spark 网络训练数据类
迁移学习
已训练的模型库 - Model Zoo
技巧 - 模型部署
Keras 导入
分布式训练 (Spark)
超参数优化
前馈层
DenseLayer - (源码) - 简单/标准全连接层
EmbeddingLayer - (源码) - 以正整数索引作为输入,输出向量。只作为模型中的第一层使用。数学上等效于(当启用偏置)DenseLayer,使用OneHot输入,但更高效。
输出层
输出层:通常用作网络中的最后一层。这里会设置损失函数。
OutputLayer - (源码) - 在MLPs/CNNs中的标准分类/回归输出层。有一个内置的全连接的DenseLayer。 2d 输入/输出 (即, 每个示例中的行向量)。
LossLayer - (源码) - 没有参数的输出层 - 只有损失函数和激活函数。2d 输入/输出 (即, 每个示例中的行向量)。与Outputlayer 不同,它有nIn = nOut的限制。
RnnOutputLayer - (源码) - 循环神经网络的输出层。三维(时间序列)的输入和输出。内置有时间分布全连接层。
RnnLossLayer - (源码) - 无参版本的RnnOutputLayer。 三维(时间序列)的输入和输出。
CnnLossLayer - (源码) - 与CNNs一起使用,其中必须在输出的每个空间位置进行预测(例如:分割或去噪)。没有参数,四维输入/输出与形状[小批量,深度,高度,宽度]。当使用softmax时,这是在每个空间位置的深度应用。
Yolo2OutputLayer - (源码) - 用于目标检测的YOLO 2模型实现
CenterLossOutputLayer - (源码) - OutputLayer的一个版本,也试图最小化示例激活的类内距离,即,“如果示例x在Y类中,则确保嵌入(x)接近于所有示例y在Y中的平均值(嵌入(y))。
卷积层
循环层
无监督层
其它层
图顶点
图顶点: 与 ComputationGraph 一起使用。和层类似,顶点通常没有任何参数,并可以支持多个输入。
输入预处理器是一个简单的类/接口,它对一个层的输入进行操作。也就是说,预处理器连接到一个层上,并在输入到输出之前对输入执行一些操作。预处理器还处理反向传播——即,预处理操作一般是可求导的。
请注意,在许多情况下(例如XtoYPreProcessor类),用户不需要(也不应该)手动添加这些,而只能使用.setInputType(InputType.feedForward(10))来代替,这会根据需要推断和添加预处理器。
迭代监听器:可以附加到模型,并在训练期间调用,在每次迭代之后(即,在每次参数更新之后)。训练监听器:迭代监听器的扩展。在训练的不同阶段调用许多附加方法。即在向前传递、梯度计算之后,在每次训练开始或结束。
没有(迭代/训练)在训练之外(即在输出或前馈方法中)被调用。
链接: 主要的评估页
DL4J具有用于评估网络性能的多个类,与测试集相对应。不同的评估类适合于不同类型的网络。
可以使用ModelSerializer类,特别是writeModel、restoreMultiLayerNetwork和restoreComputationGraph方法来保存多层网络和计算图。
对于当前主干(但不是0.9.1) MultiLayerNetwork.save(File)方法 和 MultiLayerNetwork.load(File) 方法已被添加。这些在内部使用ModelSerializer。计算图也增加了类似的保存/加载方法。
示例: 加载与保存网络
网络可以在保存和加载之后进一步训练:但是,请确保加载“更新器”(即,更新器的历史状态,如momentum)。如果不需要进一步的训练,则更新器状态可以被忽略以节省磁盘空间和内存。
大多数归一化器(实现ND4J Normalizer接口)也可以使用addNormalizerToModel方法添加到模型中。
注意,DL4J中用于模型的格式是.zip:可以使用支持zip格式的程序打开/提取这些文件。
本节列出了DL4J支持的各种配置选项。
激活函数可以用两种方式之一定义:(a)通过向配置传递激活枚举值,例如,.activation(Activation.TANH)(b)通过传递IActivation实例,例如,.activation(new ActivationSigmoid())。
注意,DL4J支持自定义激活函数,它可以通过扩展BaseActivationFunction来定义。
支持的激活函数列表:
f(x) = x^3
f(x) = min(1, max(0, 0.2*x + 0.5))
f(x) = x
f(x) = max(0, x) + alpha * min(0, x)
默认的 alpha=0.01
.tanh(y) ~ sgn(y) * { 1 - 1/(1+|y|+y^2+1.41645*y^4)}
近似于 f(x) = 1.7159 * tanh(2x/3)
, 但执行起来更快. (参考文献)f(x) = x
if x>0
或 f(x) = 0
f(x) = 1 / (1 + exp(-x))
f(x) = log(1+e^x)
- 形状类似于RELU 激活函数的平滑版本f(x) = x / (1+|x|)
- 形状类似于标准的 tanh 激活函数 (计算更快).f(x) = max(0, tanh(x))
f(x) = x * sigmoid(x)
(参考文献)权值初始化指的是一个新网络的初始参数应该被设置的方法。
权重初始化通常使用WeightInit枚举来定义。
自定义权重初始化可以使用 .weightInit(WeightInit.DISTRIBUTION).dist(new NormalDistribution(0, 1))
例如. 对于主干 (非 0.9.1 版本) .weightInit(new NormalDistribution(0, 1))
也是可用的, 这相当于以前的方法。
可用的权重初始化。并不是所有的版本都在0.9.1版本中可用:
dist
配置方法来指定)DL4J中的“更新器”是一个需要原始梯度并将其修改为更新的类。然后将这些更新应用于网络参数。这篇CS231n 课程笔记对这些更新器有很好的解释。
DL4J支持的更新器:
支持学习速率的所有更新器也支持学习速率调度(牛顿动量更新器也支持动量调度)。学习速率调度可以根据迭代次数或已逝去的训练数来指定。Dropout(见下文)也可以利用这里列出的调度表。
配置用法,例如: .updater(new Adam(new ExponentialSchedule(ScheduleType.ITERATION, 0.1, 0.99 )))
你可以在你创建的调度对象上通过调用ISchedule.valueAt(int iteration, int epoch)
来制图/监视将在任意点使用的学习率。
可用的调度:
value(i) = initialValue * gamma^i
value(i) = initialValue * (1 + gamma * i)^(-power)
value(i) = initialValue * (1 + i/maxIter)^(-power)
value(i) = initialValue * 1.0 / (1 + exp(-gamma * (iter - stepSize)))
value(i) = initialValue * gamma^( floor(iter/step) )
请注意,自定义调度可以通过实现ISchedule接口来创建。
L1/L2 正则化
L1和L2正则化可以容易地通过配置:.l1(0.1).l2(0.2)添加到网络中。注意, .regularization(true) 必须在0.9.1上启用(这个选项在0.9.1发布后被删除)。
L1和L2正则化仅适用于权重参数。也就是说,.l1 和 .l2 不会影响偏置参数-这些可以使用.l1Bias(0.1).l2Bias(0.2)实现被正则化。
Dropout(丢弃)
所有的丢弃类型公在训练时应用。它们不在测试时应用。
注意(从当前主干开始,但不是0.9.1),丢弃参数也可以根据学习率调度部分中提到的任何调度类来指定。
根据丢弃,丢弃连接/权重噪声只适用于训练时间。
约束是在每次迭代结束时(在参数更新发生之后)放置在模型的参数上的确定性限制。它们可以被认为是正则化的一种类型。
DataSetIterator是DL4J用于对小批量数据进行迭代的抽象,用于训练。DataSetIterator返回DataSet对象,这些对象是小批量,并支持最多1个输入和1个输出数组(INDArray)。
MultiDataSetIterator类似于DataSetIterator,但是返回MultiDataSet对象,该对象可以具有网络所需的多个输入和多个输出数组。
内置迭代器 (DL4J-提供数据)
这些迭代器按需要下载它们的数据。它们返回的实际数据集不是可定制的。
迭代器-用户提供的数据
此子章节的迭代器与用户提供的数据一起使用。
迭代器 - 适配器与实用迭代器
Iterator
或 Iterable
为 一个 DataSetIterator。 不拆分基础数据集对象Iterator
为一个 DataSetIterator. 与ExistingDataSetIterator不同,底层DataSet对象可以是拆分/组合的——即,对于输出,小批量大小可能与输入迭代器不同。Iterator版本
ND4J提供了用于执行数据归一化的多个类。这些实现为数据集预处理器。归一化的基本模式:
DataSetIterator myTrainData = ...
NormalizerMinMaxScaler normalizer = new NormalizerMinMaxScaler();
normalizer.fit(myTrainData)
myTrainData.setPreProcessor(normalizer);
最终结果:来自DataSetIterator的数据现在将被归一化。通常你应该只在训练数据上拟合,并且与仅在训练数据上拟合的相同的/单一的归一化器一起执行 trainData.setPreProcessor(normalizer)
和 testData.setPreProcessor(normalizer)
注意,在适当的情况下(NormalizerStandard.,NormalizerMinMaxScaler),诸如平均值/标准偏差/最小值/最小值的统计数据,跨时间(对于时间序列)和跨图像x/y位置(但是对于图像数据不是深度/通道)共享。
数据归一化示例: 链接
可用的归一化器: DataSet / DataSetIterator
可用的归一化器: MultiDataSet / MultiDataSetIterator
DL4j具有用于执行迁移学习的类/实用程序——即,采用现有网络,并修改一些层(可选地冻结其他层,以便它们的参数不改变)。例如,可以在ImageNet上训练图像分类器,然后应用于新的/不同的数据集。多层网络和计算图都可以与迁移学习一起使用——通常从模型动物园的预训练模型开始(参见下一节),虽然可以单独使用任何多层网络/计算图。
链接: 迁移学习示例
迁移学习的主要类别是TransferLearning。该类具有可用于添加/删除层、冻结层等的构建器模式。FineTuneConfiguration可用于指定非冻结层的学习速率和其他设置。
DL4J提供了一个“model zoo”——一组预训练模型,可以下载和使用(例如,用于图像分类),或者经常用于迁移学习。
链接: Deeplearning4j Model Zoo
DL4J 的 model zoo中可用的模型有:
*注: Keras 已训练好的模型 (不是 DL4J 提供) 或许也可以导入, 使用 DL4J的 Keras 模型导入功能。
Eclipse DL4J库提供了很多功能,我们将这个速查表放在一起,以帮助用户组装神经网络并更快地使用张量。
用于多层网络和计算图的通用参数和层的配置代码。完整的API见MultiLayerNetwork和ComputationGraph。
序列网络
大多数网络配置可以使用多层网络类,如果它们是序列的和简单的。
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(1234)
// 如下的参数会被复制到网络中的每一层
// 对于像 dropOut() 或 activation()这样的参数你应该每一层都设置
// 只指定你需要的参数
.updater(new AdaGrad())
.activation(Activation.RELU)
.dropOut(0.8)
.l1(0.001)
.l2(1e-4)
.weightInit(WeightInit.XAVIER)
.weightInit(Distribution.TruncatedNormalDistribution)
.cudnnAlgoMode(ConvolutionLayer.AlgoMode.PREFER_FASTEST)
.gradientNormalization(GradientNormalization.RenormalizeL2PerLayer)
.gradientNormalizationThreshold(1e-3)
.list()
// 网络中的层,按顺序添加
// 每层设置的参数覆盖上面设置的参数
.layer(new DenseLayer.Builder().nIn(numInputs).nOut(numHiddenNodes)
.weightInit(WeightInit.XAVIER)
.build())
.layer(new ActivationLayer(Activation.RELU))
.layer(new ConvolutionLayer.Builder(1,1)
.nIn(1024)
.nOut(2048)
.stride(1,1)
.convolutionMode(ConvolutionMode.Same)
.weightInit(WeightInit.XAVIER)
.activation(Activation.IDENTITY)
.build())
.layer(new GravesLSTM.Builder()
.activation(Activation.TANH)
.nIn(inputNum)
.nOut(100)
.build())
.layer(new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD)
.weightInit(WeightInit.XAVIER)
.activation(Activation.SOFTMAX)
.nIn(numHiddenNodes).nOut(numOutputs).build())
.pretrain(false).backprop(true)
.build();
MultiLayerNetwork neuralNetwork = new MultiLayerNetwork(conf);
复杂网络
具有复杂图和“分支”的网络需要使用计算图。
ComputationGraphConfiguration.GraphBuilder graph = new NeuralNetConfiguration.Builder()
.seed(seed)
// 如下的参数会被复制到网络中的每一层
// 对于像 dropOut() 或 activation()这样的参数你应该每一层都设置
// 只指定你需要的参数
.activation(Activation.IDENTITY)
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
.updater(updater)
.weightInit(WeightInit.RELU)
.l2(5e-5)
.miniBatch(true)
.cacheMode(cacheMode)
.trainingWorkspaceMode(workspaceMode)
.inferenceWorkspaceMode(workspaceMode)
.cudnnAlgoMode(cudnnAlgoMode)
.convolutionMode(ConvolutionMode.Same)
.graphBuilder()
// 网络中的层,按顺序添加
// 每层设置的参数覆盖上面设置的参数
// 注意你必须为每一层命名并手动指定它的输入
.addInputs("input1")
.addLayer("stem-cnn1", new ConvolutionLayer.Builder(new int[] {7, 7}, new int[] {2, 2}, new int[] {3, 3})
.nIn(inputShape[0])
.nOut(64)
.cudnnAlgoMode(ConvolutionLayer.AlgoMode.NO_WORKSPACE)
.build(),"input1")
.addLayer("stem-batch1", new BatchNormalization.Builder(false)
.nIn(64)
.nOut(64)
.build(), "stem-cnn1")
.addLayer("stem-activation1", new ActivationLayer.Builder()
.activation(Activation.RELU)
.build(), "stem-batch1")
.addLayer("lossLayer", new CenterLossOutputLayer.Builder()
.lossFunction(LossFunctions.LossFunction.SQUARED_LOSS)
.activation(Activation.SOFTMAX).nOut(numClasses).lambda(1e-4).alpha(0.9)
.gradientNormalization(GradientNormalization.RenormalizeL2PerLayer).build(),
"stem-activation1")
.setOutputs("lossLayer")
.setInputTypes(InputType.convolutional(224, 224, 3))
.backprop(true).pretrain(false).build();
ComputationGraph neuralNetwork = new ComputationGraph(graph);
下面的代码片段创建一个基本的管道,从磁盘加载图像,应用随机变换,并将它们拟合到神经网络。它还设置了UI实例,以便你可以可视化进度,并使用早期停止来提前终止训练。你可以为许多不同的用例修改此管道。
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();
File mainPath = new File(System.getProperty("user.dir"), "dl4j-examples/src/main/resources/animals/");
FileSplit fileSplit = new FileSplit(mainPath, NativeImageLoader.ALLOWED_FORMATS, rng);
int numExamples = Math.toIntExact(fileSplit.length());
int numLabels = fileSplit.getRootDir().listFiles(File::isDirectory).length; // 在仅在你的根目录是干净的:只有标签子目录的时候才会起作用。
BalancedPathFilter pathFilter = new BalancedPathFilter(rng, labelMaker, numExamples, numLabels, maxPathsPerLabel);
InputSplit[] inputSplit = fileSplit.sample(pathFilter, splitTrainTest, 1 - splitTrainTest);
InputSplit trainData = inputSplit[0];
InputSplit testData = inputSplit[1];
boolean shuffle = false;
ImageTransform flipTransform1 = new FlipImageTransform(rng);
ImageTransform flipTransform2 = new FlipImageTransform(new Random(123));
ImageTransform warpTransform = new WarpImageTransform(rng, 42);
List> pipeline = Arrays.asList(
new Pair<>(flipTransform1,0.9),
new Pair<>(flipTransform2,0.8),
new Pair<>(warpTransform,0.5));
ImageTransform transform = new PipelineImageTransform(pipeline,shuffle);
DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
// 训练数据集
ImageRecordReader recordReaderTrain = new ImageRecordReader(height, width, channels, labelMaker);
recordReader.initialize(trainData, null);
DataSetIterator trainingIterator = new RecordReaderDataSetIterator(recordReaderTrain, batchSize, 1, numLabels);
//测试数据集
ImageRecordReader recordReaderTest = new ImageRecordReader(height, width, channels, labelMaker);
recordReader.initialize(testData, null);
DataSetIterator testingIterator = new RecordReaderDataSetIterator(recordReaderTest, batchSize, 1, numLabels);
//早停配置,模型保存器,还有训练器
EarlyStoppingModelSaver saver = new LocalFileModelSaver(System.getProperty("user.dir"));
EarlyStoppingConfiguration esConf = new EarlyStoppingConfiguration.Builder()
.epochTerminationConditions(new MaxEpochsTerminationCondition(50)) //Max of 50 epochs
.evaluateEveryNEpochs(1)
.iterationTerminationConditions(new MaxTimeIterationTerminationCondition(20, TimeUnit.MINUTES)) //Max of 20 minutes
.scoreCalculator(new DataSetLossCalculator(testingIterator, true)) //Calculate test set score
.modelSaver(saver)
.build();
EarlyStoppingTrainer trainer = new EarlyStoppingTrainer(esConf, neuralNetwork, trainingIterator);
// 开始训练
trainer.fit();
DataVec附带了一个便利的转换进程类,允许更复杂的数据冲突和数据转换。它与2D和序列数据集都能很好地工作。
Schema schema = new Schema.Builder()
.addColumnsDouble("Sepal length", "Sepal width", "Petal length", "Petal width")
.addColumnCategorical("Species", "Iris-setosa", "Iris-versicolor", "Iris-virginica")
.build();
TransformProcess tp = new TransformProcess.Builder(schema)
.categoricalToInteger("Species")
.build();
// 在spark上进行转换
JavaRDD> processedData = SparkTransformExecutor.execute(parsedInputData, tp);
在创建更复杂的转换之前,我们建议先查看一下 DataVec examples。
MultiLayerNetwork和ComputationGraph都带有内置的eval()方法,允许你传递数据集迭代器并返回评估结果。
// 返回具有准确度、精确度、召回和其他类别的统计信息
Evaluation eval = neuralNetwork.eval(testIterator);
System.out.println(eval.accuracy());
System.out.println(eval.precision());
System.out.println(eval.recall());
// 在多分类数据集上用于曲线下面积的ROC(非二分类)
ROCMultiClass roc = neuralNetwork.doEvaluation(testIterator, new ROCMultiClass());
System.out.println(roc.calculateAverageAuc());
System.out.println(roc.calculateAverageAucPR());
对于高级评估,下面的代码片段可以被适用于训练管道。这是当内置的neuralNetwork.eval()方法输出混乱的结果或你需要检查原始数据时需要使用。
//在测试集上评估模型
Evaluation eval = new Evaluation(numClasses);
INDArray output = neuralNetwork.output(testData.getFeatures());
eval.eval(testData.getLabels(), output, testMetaData); //Note we are passing in the test set metadata here
//从评估对象上获取一个预测错误列表
//这样的预测误差只有在调用之后才可用。
iterator.setCollectMetaData(true)
List predictionErrors = eval.getPredictionErrors();
System.out.println("\n\n+++++ Prediction Errors +++++");
for(Prediction p : predictionErrors){
System.out.println("Predicted class: " + p.getPredictedClass() + ", Actual class: " + p.getActualClass()
+ "\t" + p.getRecordMetaData(RecordMetaData.class).getLocation());
}
//我们也可以加载原始数据:
List predictionErrorRawData = recordReader.loadFromMetaData(predictionErrorMetaData);
for(int i=0; i rawData = predictionErrorRawData.get(i).getRecord();
INDArray networkPrediction = model.output(features);
System.out.println(meta.getLocation() + ": "
+ "\tRaw Data: " + rawData
+ "\tNormalized: " + features
+ "\tLabels: " + labels
+ "\tPredictions: " + networkPrediction);
}
//一此有用的评估方法:
List list1 = eval.getPredictions(1,2); //预测: 实际类 1,预测为类 2
List list2 = eval.getPredictionByPredictedClass(2); //预测类2的所有预测
List list3 = eval.getPredictionsByActualClass(2); //对实际类2的所有预测
更多人工智能译文请查看 https://www.jianshu.com/u/fe7f1456ee73