SKIL/工作流程/SKIL上的分布式训练

SKIL中的分布式训练

SKIL提供了skil spark命令,用于在spark 集群上对DL4J模型进行分布式训练。它几乎类似于使用带有一些附加功能的spark-submit命令,以便能够查看DL4J UI上的训练并通过给定的模型历史服务器详细信息维护模型历史。

 

先决条件
你需要遵循以下步骤:

  1. SKIL
  2. Spark 集群 (或者你可以在本地使用spark,并将master指定为local)

 

使用“skil spark”参与分布式训练的组件

 

1. 实现DataSetProvider接口

为了给你的网络提供数据你需要从org.deeplearning4j.spark.data.DataSetProvider接口实现。接口定义如下:

import org.apache.spark.SparkContext;
import org.apache.spark.rdd.RDD;
import org.datavec.api.transform.TransformProcess;
import org.nd4j.linalg.dataset.DataSet;

public interface DataSetProvider {
    //此函数提供训练模型所需的数据。
    RDD<DataSet> data(SparkContext var1); 
    //此函数定义并返回一个转换过程,该转换过程将保存在模型的ETL JSON中。
    TransformProcess transformProcess(); 
}

SKIL提供此接口(io.skymind.skil.train.spark.MnistProvider)的默认实现,用于提供MNIST数据。在Java中的实现如下:

import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.rdd.RDD;
import org.datavec.api.transform.TransformProcess;
import org.deeplearning4j.datasets.iterator.impl.MnistDataSetIterator;
import org.deeplearning4j.spark.data.DataSetProvider;
import org.nd4j.linalg.dataset.DataSet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class MnistProvider implements DataSetProvider {
    @Override
    public RDD<DataSet> data(SparkContext sparkContext) {
        try {
            MnistDataSetIterator mnist = new MnistDataSetIterator(16, 60000);
            List<DataSet> data = new ArrayList<>();

            while (mnist.hasNext()) {
                data.add(mnist.next());
            }
            return new JavaSparkContext(sparkContext).parallelize(data).rdd();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public TransformProcess transformProcess() {
        return null;
    }
}

 

2. Training master

DL4J中的TrainingMaster是一个抽象(接口),允许将多个不同的训练实现与SparkDl4jMultiLayerSparkComputationGraph一起使用。
目前,DL4J有一个实现,即ParameterAveragingTrainingMaster。基本的TrainingMaster如下:

import org.deeplearning4j.spark.impl.paramavg.ParameterAveragingTrainingMaster

//spark训练配置: see http://deeplearning4j.org/spark for
//解释这些配置选项
//指定每个DataSet对象中有多少个示例
val tm = new ParameterAveragingTrainingMaster.Builder(dataSetObjectSize) 
    //平均和重新分布参数的频率
    .averagingFrequency(5) 
    //如何处理异步预取多个小批量。0禁用预取,较大的值在预取时使用更多内存。
    .workerPrefetchNumBatches(2) 
    //每个工作机线程的最小批处理大小:每个工作机线程中用于每个参数更新的示例数
    .batchSizePerWorker(batchSize) 
    .build();

可以在此处找到有关TrainingMaster Builder配置的更多信息。

 

3. 神经网络配置

最后,要训练的神经网络配置。示例配置(在scala中)如下(对于MultiLayerNetwork

import org.deeplearning4j.nn.api.Model
import org.deeplearning4j.nn.api.OptimizationAlgorithm
import org.deeplearning4j.nn.conf.MultiLayerConfiguration
import org.deeplearning4j.nn.conf.NeuralNetConfiguration
import org.deeplearning4j.nn.conf.inputs.InputType
import org.deeplearning4j.nn.conf.layers.ConvolutionLayer
import org.deeplearning4j.nn.conf.layers.DenseLayer
import org.deeplearning4j.nn.conf.layers.OutputLayer
import org.deeplearning4j.nn.conf.layers.SubsamplingLayer
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork
import org.deeplearning4j.nn.weights.WeightInit
import org.deeplearning4j.spark.impl.paramavg.ParameterAveragingTrainingMaster
import org.deeplearning4j.util.ModelSerializer
import org.nd4j.linalg.activations.Activation
import org.nd4j.linalg.learning.config.Nesterovs
import org.nd4j.linalg.lossfunctions.LossFunctions

import java.io.File;
//如上所述的训练迭代
var builder = new NeuralNetConfiguration.Builder().seed(230) 
        .l2(0.0005)
        .weightInit(WeightInit.XAVIER)
        .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
        .updater(Nesterovs.builder().learningRate(0.01).momentum(0.9).build())
        .list()
        .layer(0, new ConvolutionLayer.Builder(5, 5)
           
                //nIn和nOut指定深度。这里是nChannels,nOut是要应用的过滤器的数量。
                .nIn(1).stride(1, 1).nOut(20).activation(Activation.IDENTITY).build())
        .layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2)
                .stride(2, 2).build())
        .layer(2, new ConvolutionLayer.Builder(5, 5)
                
                //请注意,在后面的层中不需要指定nIn
                .stride(1, 1).nOut(50).activation(Activation.IDENTITY).build())
        .layer(3, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX).kernelSize(2, 2)
                .stride(2, 2).build())
        .layer(4, new DenseLayer.Builder().activation(Activation.RELU).nOut(500).build())
        .layer(5, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD).nOut(10)
                .activation(Activation.SOFTMAX).build())
         //见下文注释
        .setInputType(InputType.convolutionalFlat(28, 28, 1)) 
        .backprop(true).pretrain(false);
var network = new MultiLayerNetwork(builder.build());
network.init();

//将模型写入文件
var neuralNet = new File("/tmp", "neuralnet.bin")
neuralNet.createNewFile()
ModelSerializer.writeModel(network, neuralNet, true);

 

使用“skil spark”命令
使用skil spark命令时,需要使用TrainingMaster#toJson函数将TrainingMaster配置保存在JSON文件中。此外,还需要使用ModelSerializer#writeModel函数将模型配置写入文件。你可以用Zeppelin中这个样本笔记本看看是怎么做到的。

 

样品使用
下面的shell脚本显示了如何使用skil spark命令:

$SKIL_HOME/sbin/skil login --userId admin --password admin # You might have a different userId and password, replace them accordingly.

$SKIL_HOME/sbin/skil spark --master local[*] --trainingMasterPath "/tmp/parameteraveraging.json" --modelPath "/tmp/neuralnet.bin" --dataSetProvider MnistProvider --numEpochs 5 --supervise false --uiUrl localhost:9002

在上面的命令中,--dataSetProvider 的值为MnistProvider,而不是完整的类名(io.skymind.skil.train.spark.MnistProvider)。这种格式也是有效的,应用程序将扫描捆绑在“uberjar”中的类,并使用与给定名称匹配的第一个类。如果要防止其他可能共享相同名称的类之间的不匹配,建议使用该类的全名。

 

日志

运行skil spark命令后,可以在日志文件中查看skil的日志,该文件位于路径下:/var/log/skil/skil.log。

 

DL4J用户界面监控训练
你还可以在--uiUrl参数指定的相同地址上监视DL4J UI上的分布式训练。如果你没有在skil上启动DL4J UI服务器,可以使用$skil_home/sbin/skil ui --uiport 9002命令启动它。下图显示了它的外观:

UI showing the distributed training stats

显示分布式训练统计信息的用户界面
“skil spark”命令的自定义参数
下表列出了skil spark命令的参数、说明和默认值:

变量 描述 默认值

--master

参数服务器要连接到的master URL。spark://host:port, mesos://host:port, yarn, 或  local

"local[*]"
--deploy-mode

是否在本地启动驱动程序(“client”)或
在集群中的一台工作机上(“cluster”)。

"client"
--class 应用程序的主类(对于Java/Scala应用程序) "io.skymind.skil.train.spark.SKILSparkMain"
--jars 驱动程序上要包含的本地jar的逗号分隔列表
和执行器类路径
 
--name 应用名称  
--packages

驱动程序上要包含的本地jar的maven坐标逗号分隔列表和执行器类路径。

将搜索本地maven仓库,然后中央仓库和任何由repositories提供的额外的远程仓库

坐标格式为:groupId:artifactid:version

 
--exclude-packages

groupid:artifactid的逗号分隔列表,在解析--packages中提供的依赖项以避免依赖项冲突时排除

 
--properties-file

要从中加载额外属性的文件的路径。如果未指定,将查找conf/spark-defaults.conf

 
--repositories 以逗号分隔的其他远程存储库列表,用于搜索随包提供的maven坐标  
--files 将放在每个执行器工作目录中的文件的逗号分隔列表  
--driver-memory 驱动器内存(例如1000M、2G)(默认值:1024M)  
--driver-java-options 传递给驱动程序的额外Java选项  
--driver-library-path 要传递给驱动程序的额外库路径条目  
--driver-class-path 要传递给驱动程序的额外类路径条目。注意,随--jar添的jar会自动包含在类路径中。  
--executor-memory 每个执行器的内存(例如1000M、2G)(默认值:1G)  
--proxy-user 提交应用程序时要模拟的用户  
--driver-cores 用于驱动程序核心 1
--yarn-queue 用于提交到的YARN队列 default
--num-executors 要启动的执行器数 2
--principal 在安全hdfs上运行时用于登录kdc的主体  
--key-tab 包含上述主体的keytab的文件的完整路径。此keytab将通过安全的分布式缓存复制到运行应用程序主服务器的节点,用于定期更新登录票证和委派令牌。  
--supervise 如果给定,则在出现故障时重新启动驱动程序  
--kill 如果给定,则杀死指定的驱动程序  
--status 如果给定,则请求指定的驱动程序的状态  
--total-executor-cores 所有执行者的核心总数 1
--trainingMasterPath TrainingMaster的路径  
--modelPath 模型路径  
--uiUrl 界面Url  
--dataSetProvider 数据集提供者  
--jarPath jar文件路径  
--pArgs spark作业程序参数 new ArrayList<String>()
--modelHistoryUrl 模型历史Url null
--modelHistoryId 模型历史Id null
--evalType 评估类型,可能的值有:evaluation, evaluationbinary, roc, rocbinary, rocmulticlass, regressionevaluation null
--numEpochs 训练的轮数 5
--evalDataSetProviderClass 评估数据集提供者 MnistProvider
--multiDataSet 是否为多数据集 false
--modelInstanceId 模型实例ID null
--doInference 用指定的模型URI进行推理 false
--outputPath 进行推理时,保存结果的路径 null
--batchSize 用于推理的批量大小 16
--verbose 打印调试信息 false

skil spark命令从零开始创建一个“uber jar”,它捆绑了所有skil jar,每次更新或添加新插件时,它都会重新生成该uber jar。创建这个jar需要很长时间。创建jar文件后,将启动训练过程,可以使用tail -f/var/log/skil/skil.log 命令查看其输出。

猜你喜欢

转载自blog.csdn.net/bewithme/article/details/89336599