2

我是 deeplearning4j 库的新手,但我对一般的神经网络有一些经验。
我正在尝试训练一个循环神经网络(特别是 LSTM),它应该实时检测音乐中的节拍。到目前为止,我发现的所有使用带有 deeplearning4j 的递归神经网络的示例都使用了一个从文件中读取训练数据的阅读器。由于我想通过麦克风实时录制音乐,我无法读取一些预生成的文件,因此输入神经网络的数据是由我的应用程序实时生成的。

这是我用来生成网络的代码:

    NeuralNetConfiguration.ListBuilder builder = new NeuralNetConfiguration.Builder()
            .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT).iterations(1)
            .learningRate(0.1)
            .rmsDecay(0.95)
            .regularization(true)
            .l2(0.001)
            .weightInit(WeightInit.XAVIER)
            .updater(Updater.RMSPROP)
            .list();

    int nextIn = hiddenLayers.length > 0 ? hiddenLayers[0] : numOutputs;
    builder = builder.layer(0, new GravesLSTM.Builder().nIn(numInputs).nOut(nextIn).activation("softsign").build());

    for(int i = 0; i < hiddenLayers.length - 1; i++){
        nextIn = hiddenLayers[i + 1];
        builder = builder.layer(i + 1, new GravesLSTM.Builder().nIn(hiddenLayers[i]).nOut(nextIn).activation("softsign").build());
    }

    builder = builder.layer(hiddenLayers.length, new RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT).nIn(nextIn).nOut(numOutputs).activation("softsign").build());

    MultiLayerConfiguration conf = builder.backpropType(BackpropType.TruncatedBPTT).tBPTTForwardLength(DEFAULT_RECURRENCE_DEPTH).tBPTTBackwardLength(DEFAULT_RECURRENCE_DEPTH)
            .pretrain(false).backprop(true)
            .build();

    net = new MultiLayerNetwork(conf);
    net.init();  

在这种情况下,我使用了大约 700 个输入(主要是录制音频的 FFT 数据)、1 个输出(应该输出 0 [无节拍] 和 1 [节拍] 之间的数字)和我的 hiddenLayers 数组包括整数 {50, 25, 10}。

为了获得网络的输出,我正在使用以下代码:

    double[] output = new double[]{net.rnnTimeStep(Nd4j.create(netInputData)).getDouble(0)};

其中 netInputData 是我想作为一维双精度数组输入网络的数据。
我相对确定这段代码工作正常,因为我得到了一些未经训练的网络的输出,当我绘制它时,它看起来像这样。
但是,一旦我尝试训练一个网络(即使我只训练了很短的时间,这应该会稍微改变网络的权重,因此输出应该与未经训练的网络非常相似),我得到一个看起来像常量的输出。

这是我用来训练网络的代码:

    for(int timestep = 0; timestep < trainingData.length - DEFAULT_RECURRENCE_DEPTH; timestep++){
        INDArray inputDataArray = Nd4j.create(new int[]{1, numInputs, DEFAULT_RECURRENCE_DEPTH},'f');
        for(int inputPos = 0; inputPos < trainingData[timestep].length; inputPos++)
            for(int inputTimeWindowPos = 0; inputTimeWindowPos < DEFAULT_RECURRENCE_DEPTH; inputTimeWindowPos++)
                inputDataArray.putScalar(new int[]{0, inputPos, inputTimeWindowPos}, trainingData[timestep + inputTimeWindowPos][inputPos]);

        INDArray desiredOutputDataArray = Nd4j.create(new int[]{1, numOutputs, DEFAULT_RECURRENCE_DEPTH},'f');
        for(int outputPos = 0; outputPos < desiredOutputData[timestep].length; outputPos++)
            for(int inputTimeWindowPos = 0; inputTimeWindowPos < DEFAULT_RECURRENCE_DEPTH; inputTimeWindowPos++)
                desiredOutputDataArray.putScalar(new int[]{0, outputPos, inputTimeWindowPos}, desiredOutputData[timestep + inputTimeWindowPos][outputPos]);

        net.fit(new DataSet(inputDataArray, desiredOutputDataArray));
    }  

再一次,我得到了输入数据和所需输出的双精度数组。这次这两个数组是二维的。第一个索引表示时间(其中索引 0 是录制音频的第一个音频数据),第二个索引表示该时间步的输入(或相应的所需输出)。
鉴于训练网络后显示的输出,我倾向于认为用于从数据创建 INDArray 的代码一定有问题。我是否错过了初始化这些数组的一些重要步骤,或者我是否弄乱了将数据放入这些数组所需的顺序?

感谢您提前提供任何帮助。

4

1 回答 1

1

我不确定,但可能 99.99% 的训练示例都是 0,只有偶尔出现的 1 正是节拍发生的位置。这可能太不平衡而无法学习。祝你好运。

于 2016-12-05T20:44:54.463 回答