/// <summary> /// Pack the data in an ordering expected by the RNN layer used. This method is only called when using Data/Clip inputs. /// </summary> /// <param name="sender">Specifies the sender of the event, which is the MemoryDataLayer.</param> /// <param name="e">Specifies the event parameters.</param> /// <remarks> /// This method is responsible for packing the datum received into the data blob within the event arguments and do so in the /// expected LSTM ordering. /// </remarks> private void memData_OnDataPack(object sender, MemoryDataLayerPackDataArgs<T> e) { List<int> rgDataShape = e.Data.shape(); List<int> rgClipShape = e.Clip.shape(); List<int> rgLabelShape = e.Label.shape(); int nBatch = e.DataItems.Count; int nSeqLen = rgDataShape[0]; e.Data.Log.CHECK_GT(nSeqLen, 0, "The sequence lenth must be greater than zero!"); e.Data.Log.CHECK_EQ(nBatch, e.ClipItems.Count, "The data and clip should have the same number of items."); e.Data.Log.CHECK_EQ(nSeqLen, rgClipShape[0], "The data and clip should have the same sequence count."); rgDataShape[1] = nBatch; // LSTM uses sizing: seq, batch, data1, data2 rgClipShape[1] = nBatch; rgLabelShape[1] = nBatch; e.Data.Reshape(rgDataShape); e.Clip.Reshape(rgClipShape); e.Label.Reshape(rgLabelShape); T[] rgRawData = new T[e.Data.count()]; T[] rgRawClip = new T[e.Clip.count()]; T[] rgRawLabel = new T[e.Label.count()]; int nDataSize = e.Data.count(2); T[] rgDataItem = new T[nDataSize]; T dfClip; int nIdx; for (int i = 0; i < nBatch; i++) { Datum data = e.DataItems[i]; Datum clip = e.ClipItems[i]; T[] rgLabel = unpackLabel(data); for (int j = 0; j < nSeqLen; j++) { dfClip = clip.GetDataAt<T>(j); for (int k = 0; k < nDataSize; k++) { rgDataItem[k] = data.GetDataAt<T>(j * nDataSize + k); } // LSTM: Create input data, the data must be in the order // seq1_val1, seq2_val1, ..., seqBatch_Size_val1, seq1_val2, seq2_val2, ..., seqBatch_Size_valSequence_Length if (e.LstmType == LayerParameter.LayerType.LSTM) nIdx = nBatch * j + i; // LSTM_SIMPLE: Create input data, the data must be in the order // seq1_val1, seq1_val2, ..., seq1_valBatchSize, seq2_val1, seq2_val2, ..., seqSequenceLength_valBatchSize else nIdx = i * nBatch + j; Array.Copy(rgDataItem, 0, rgRawData, nIdx * nDataSize, nDataSize); rgRawClip[nIdx] = dfClip; if (rgLabel != null) { if (rgLabel.Length == nSeqLen) rgRawLabel[nIdx] = rgLabel[j]; else if (rgLabel.Length == 1) { if (j == nSeqLen - 1) rgRawLabel[0] = rgLabel[0]; } else { throw new Exception("The Solver SequenceLength parameter does not match the actual sequence length! The label length '" + rgLabel.Length.ToString() + "' must be either '1' for SINGLE labels, or the sequence length of '" + nSeqLen.ToString() + "' for MULTI labels. Stopping training."); } } } } e.Data.mutable_cpu_data = rgRawData; e.Clip.mutable_cpu_data = rgRawClip; e.Label.mutable_cpu_data = rgRawLabel; m_nRecurrentSequenceLength = nSeqLen; }