私は 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 配列は次のように構成されます。 ints {50, 25, 10} の
ネットワークの出力を取得するために、次のコードを使用しています。
double[] output = new double[]{net.rnnTimeStep(Nd4j.create(netInputData)).getDouble(0)};
ここで、netInputData は、1 次元の倍精度配列としてネットワークに入力するデータです。プロットすると次のような
未学習のネットワークの出力が得られるため、このコードが正常に機能していることは比較的確信しています。
ただし、ネットワークをトレーニングしようとすると (短時間だけトレーニングしても、ネットワークの重みが少し変更されるため、出力はトレーニングされていないネットワークと非常に似ているはずです)、次のようになります。定数のように見える出力。
これは、ネットワークのトレーニングに使用しているコードです。
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));
}
繰り返しますが、入力用と目的の出力用のデータを double 配列として取得しています。今回は、2 つの配列は 2 次元です。最初のインデックスは時間 (インデックス 0 は録音されたオーディオの最初のオーディオ データ) を表し、2 番目のインデックスはこのタイム ステップの入力 (またはそれぞれ目的の出力) を表します。
ネットワークのトレーニング後に表示された出力を考えると、データから INDArray を作成するために使用したコードに何か問題があるに違いないと考える傾向があります。これらの配列を初期化するための重要な手順を見逃しているのでしょうか、それともデータをこれらの配列に入れるために必要な順序を間違えたのでしょうか?
事前に助けてくれてありがとう。