1. 程式人生 > >DL4J中文文件/模型/RNN(迴圈神經網路)

DL4J中文文件/模型/RNN(迴圈神經網路)

DL4J中的迴圈神經網路

本文概述了在DL4J中如何使用迴圈神經網路的具體訓練特徵和實用性。本文假定對迴圈神經網路及其使用有一定了解,而不是對遞迴神經網路的介紹,並且假定你對它們的使用和術語有一些熟悉。

內容

  • 基礎:資料和網路配置
  • RNN訓練特徵
    • 通過時間截斷的反向傳播
    • 掩碼:一對多、多對一和序列分類
      • 訓練後的掩碼與序列分類
    • RNN層與其它層結合
  • 測試時間:一步一步的預測
  • 匯入時間序列資料
  • 示例

基礎:資料和網路配置

DL4J 目前支援以下型別的迴圈神經網路

  • GravesLSTM (長短期記憶)
  • BidirectionalGravesLSTM(雙向格拉夫長短期記憶)
  • BaseRecurrent

每種網路的Java文件都是可用的, GravesLSTM,BidirectionalGravesLSTMBaseRecurrent

用於RNN的資料

暫時考慮一個標準的前饋網路(DL4J中的多層感知機或“密連層”)。這些網路期望輸入和輸出資料是二維的:即,具有“形狀”的資料[numExamples,inputSize]。這意味著進入前饋網路的資料具有“numExamples”行/示例,其中每行由“inputSize”列組成。單個示例將具有形狀[1,inputSize],但是在實踐中,為了計算和優化效率,我們通常使用多個示例。類似地,標準前饋網路的輸出資料也是二維的,具有形狀[numExamples,outputSize]。

相反,RNN的資料是時間序列。因此,他們有3個維度:一個額外的時間維度。輸入資料因此具有形狀[numExamples,inputSize,timeSeriesLength],輸出資料具有形狀[numExamples,outputSize,timeSeriesLength]。這意味著,我們的INDArray中的資料被佈置成如此 使得位置(i,j,k)的值是 小批量中第i個示例的第k個時間步驟的第j個值。該資料佈局如下所示。

當使用類CSVSequenceRecordReader匯入時間序列資料時,資料檔案中的每一行表示一個時間步驟,用第一行(或頭行後第一行(如果存在)中的最早觀察到的時間序列 及csv的最後一行中的最新觀察來表示。每個特徵時間序列是CSV檔案的單獨列。例如,如果你在時間序列中有五個特徵,每個特徵具有120個觀察值,以及大小為53的訓練和測試集,那麼將有106個輸入csv檔案(53個輸入,53個標籤)。53個輸入CSV檔案將分別有五列和120行。標籤CSV檔案將有一列(標籤)和一行。

Data: Feed Forward vs. RNN

RnnOutputLayer  (迴圈神經網路輸出層)

迴圈神經網路輸出層是用作具有許多迴圈神經網路系統(用於迴歸和分類任務)的最後層的一種層。迴圈神經網路輸出層處理諸如評分計算、給定損失函式時的錯誤計算(預測與實際)等問題。在功能上,它與“標準”OutputLayer類(與前饋網路一起使用)非常相似;但是它同時輸出(並且期望作為標籤/目標)三維時間序列資料集。

迴圈神經網路輸出層的配置遵循與其他層相同的設計:例如,將多層網路中的第三層設定為迴圈神經網路輸出層以進行分類:

.layer(2, new RnnOutputLayer.Builder(LossFunction.MCXENT).activation(Activation.SOFTMAX)
.weightInit(WeightInit.XAVIER).nIn(prevLayerSize).nOut(nOut).build())

在實踐中使用迴圈神經網路輸出層可以在示例中看到,連結到本文末尾。

RNN 訓練特徵

通過時間截斷的反向傳播

訓練神經網路(包括RNNs)會在計算上非常苛刻。對於迴圈神經網路,當我們處理長序列時尤其如此,即具有許多時間步長的訓練資料。

為了降低迴圈神經網路中每個引數更新的計算複雜度,提出了截斷反向傳播時間演算法(BPTT)。總而言之,對於給定的計算能力,它允許我們更快地訓練網路(通過執行更頻繁的引數更新)。建議在輸入序列長的時候使用截斷的BPTT(通常超過幾百個時間步長)。

考慮當訓練具有長度為12個時間步長的時間序列的迴圈神經網路時會發生什麼。這裡,我們需要進行12步的正向傳遞,計算誤差(基於預測的與實際的),並且進行12步的後向傳遞:

Standard Backprop Training

對於12個時間步長,在上面的影象中,這不是問題。然而,考慮到輸入時間序列是10000個或更多的時間步長。在這種情況下,對於每個引數更新的每個正向和向後傳遞,通過時間的標準反向傳播將需要10000個時間步驟。這計算要求當然是非常大的。

在實踐中,截斷的BPTT將前向和後向傳播分成一組較小的前向/後向傳播操作。這些向前/向後傳播片斷的長度是由使用者設定的引數。例如,如果我們使用長度為4個時間步長的截斷BPTT,學習看起來如下:

Truncated BPTT

請注意,截斷BPTT和標準BPTT的總體複雜度大致相同——在前向/反向傳播中,它們時間步數量都相同。然而,使用這種方法,我們得到3個引數更新,而不是一個近似相同的工作量。然而,成本並不完全相同,每個引數更新都有少量的開銷。

截斷BPTT的缺點是在截斷的BPTT中學習的依賴的長度可以短於完整的BPTT。這是很容易看到的:考慮上面的影象,TBPTT長度為4。假設在時間步驟10,網路需要儲存來自時間步驟0的一些資訊,以便做出準確的預測。在標準的BPTT中,這是可以的:梯度可以從時間10到時間0沿著展開的網路一路向後流動。在截斷的BPTT中,這是有問題的:時間步10的梯度沒有返回到足夠遠的地方,導致儲存所需資訊的所需引數更新。這種折衷通常是值得的,並且(只要適當地設定截斷BPTT長度),截斷BPTT在實踐中工作得很好。

在DL4J中使用截斷的BPTT非常簡單:只需將下列程式碼新增到網路配置中(最後,在網路配置的最後.build()之前)

.backpropType(BackpropType.TruncatedBPTT)
.tBPTTLength(100)

上面的程式碼片段將導致任意網路訓練(即,對MultiLayerNetwork.fit() 方法的呼叫)使用長度為100步的片段的截斷BPTT。

一些值得注意的事情:

  • 預設情況下(如果不手動指定反向傳播型別),DL4J將使用BackpropType.Standard(即,全BPTT)。
  • tBPTTLength配置引數設定截斷的BPTT傳遞的長度。通常,這是在50到200個時間步長的某個地方,不過取決於應用程式和資料。
  • 截斷的BPTT長度通常是總時間序列長度(即,200對序列長度1000)的一部分,但是當使用TBPTT(例如,具有兩個序列的小批—一個長度為100和另一個長度為1000——以及TBPTT長度為200 -將正確工作)時,在同一個小批次中時可變長度時間序列是可以的。 

掩碼:一對多、多對一和序列分類

基於填充和掩碼的思想DL4J支援RNN的一些相關的訓練特徵。填充和掩碼允許我們支援訓練情況,包括一對多、多對一,還支援可變長度時間序列(在同一小批量中)。

假設我們想訓練一個具有不會在每一個時間步長髮生輸入或輸出的迴圈神經網路。這個例子(對於一個例子)在下面的影象中顯示。DL4J支援所有這些情況的網路訓練:

RNN Training Types

沒有掩碼和填充,我們僅限於多對多的情況(上面,左邊):即,(a)所有示例都具有相同的長度,(b)示例在所有時間步驟都有輸入和輸出。

填充背後的思想很簡單。考慮在相同的小批量中兩個長度分別為50和100時間步的時間序列。訓練資料是矩形陣列;因此,我們填充(即,向其新增零)較短的時間序列(對於輸入和輸出),使得輸入和輸出都具有相同的長度(在本示例中:100個時間步驟)。

當然,如果這是我們全部所做的,它會在訓練過程中產生問題。因此,除了填充,我們使用掩碼機制。掩碼背後的思想很簡單:我們有兩個額外的陣列,記錄輸入或輸出是否實際出現在給定時間步長和示例中,或者輸入/輸出是否只是填充。

回想一下,對於RNN,我們的小批量處理資料具有3維,分別具有輸入和輸出的形狀[miniBatchSize、inputSize、timeSeriesLength]和[miniBatchSize、outputSize、timeSeriesLength]。然後填充陣列是二維的,輸入和輸出都具有形狀[miniBatchSize,timeSeriesLength],每個時間序列和示例的值是0 (‘absent’) or 1 (‘present’)。用於輸入和輸出的掩碼陣列被儲存在單獨的陣列中。

對於單個示例,輸入和輸出掩碼陣列如下所示:

RNN Training Types

對於“不需要掩碼”的情況,我們可以等效地使用所有1的掩碼陣列,這將給出與完全沒有掩碼陣列相同的結果。還要注意,在學習RNN時可以使用零、一或兩個掩碼陣列——例如,多對一的情況可以只針對輸出使用掩碼陣列。

在實踐中:這些填充陣列通常在資料匯入階段建立(例如,由SequenceRecordReaderDatasetIterator(稍後討論)建立),並且包含在DataSet物件中。如果DataSet包含掩碼陣列,多層網路擬合將在訓練期間自動使用它們。如果它們不存在,則不使用掩碼功能。

帶有掩碼的評估與評分

當進行評分和評估時(也就是說,當評估RNN分類器的精度)時,掩碼陣列也是重要的。例如,考慮多對一的情況:每個示例只有一個輸出,並且任何評估都應該考慮這一點。

使用(輸出)掩碼陣列的評估可以在評估時使用,通過把它傳遞到以下方法:

Evaluation.evalTimeSeries(INDArray labels, INDArray predicted, INDArray outputMask)

其中,標籤是實際輸出(3d時間序列),預測的是網路預測(3d時間序列,與標籤的形狀相同),並且outputMask是用於輸出的2d掩碼陣列。注意,評估不需要輸入掩碼陣列。

得分計算也將利用掩碼陣列,通過MultiLayerNetwork.score(DataSet) 方法。同樣,如果DataSet包含輸出掩碼陣列,那麼在計算網路的得分(損失函式-均方誤差、負對數似然等)時將自動使用它。

訓練後的掩碼與序列分類

序列分類是掩碼的一種常用方法。其思想是,儘管我們有一個序列(時間序列)作為輸入,但我們只希望為整個序列提供一個單一的標籤(而不是在序列中的每個時間步提供一個標籤)。

然而,RNN通過設計輸出序列,輸入序列的長度相同。對於序列分類,掩碼允許我們在最後的時間步用這個單一標籤訓練網路,我們本質上告訴網路除了最後的時間步之外實際上沒有任何標籤資料。

現在,假設我們已經訓練了我們的網路,並且希望從時間序列輸出陣列獲得最後的預測時間步。我們該怎麼做呢?

為了得到最後一個時間步,有兩個案例需要注意。首先,當只有一個示例時,實際上不需要使用掩碼陣列:我們只需要獲得輸出陣列中的最後一個時間步:

    INDArray timeSeriesFeatures = ...;
    INDArray timeSeriesOutput = myNetwork.output(timeSeriesFeatures);
    int timeSeriesLength = timeSeriesOutput.size(2);		//Size of time dimension
    INDArray lastTimeStepProbabilities = timeSeriesOutput.get(NDArrayIndex.point(0), NDArrayIndex.all(), NDArrayIndex.point(timeSeriesLength-1));

假設分類(不過,迴歸的過程相同),上面的最後一行給出了最後一個時間步的概率,即序列分類的類概率。

稍微複雜一點的情況是,在一個小批(特徵陣列)中有多個示例,其中每個示例的長度不同。(如果所有長度相同:我們可以使用與上面相同的過程)。

在這個“可變長度”的情況下,我們需要分別為每個示例獲取最後一個時間步。如果資料流管道中的每個示例都有時間序列長度,那麼就變得簡單了:我們只是迭代示例,用該示例的長度替換上面程式碼中的timeSeriesLength

如果我們沒有直接的時間序列的長度,我們需要從掩碼陣列中提取它們。

如果我們有一個標籤掩碼陣列(它是一個one-hot向量,像每個時間序列[0,0,01,1,0]):

    INDArray labelsMaskArray = ...;
    INDArray lastTimeStepIndices = Nd4j.argMax(labelMaskArray,1);

或者,如果我們只有特徵掩碼:一個快速和粗爆的方法就是使用這個:

    INDArray featuresMaskArray = ...;
    int longestTimeSeries = featuresMaskArray.size(1);
    INDArray linspace = Nd4j.linspace(1,longestTimeSeries,longestTimeSeries);
    INDArray temp = featuresMaskArray.mulColumnVector(linspace);
    INDArray lastTimeStepIndices = Nd4j.argMax(temp,1);

要理解這裡正在發生的事情,請注意,最初我們有一個特徵掩碼,如[1,1,1,1,0],我們希望從中獲得最後一個非零元素。我們對映[1,1,1,1,0] 到 [1,2,3,4,0],然後得到最大的元素(這是最後一個時間步長)。

在這兩種情況下,我們都可以做到以下幾點:

    int numExamples = timeSeriesFeatures.size(0);
    for( int i=0; i<numExamples; i++ ){
        int thisTimeSeriesLastIndex = lastTimeStepIndices.getInt(i);
        INDArray thisExampleProbabilities = timeSeriesOutput.get(NDArrayIndex.point(i), NDArrayIndex.all(), NDArrayIndex.point(thisTimeSeriesLastIndex));
    }

RNN層與其它層結合

DL4J中的RNN層可以與其他層型別相結合。例如,可以在同一網路中組合DenseLayer(密連層)和LSTM(長短記錄單元)層,或者組合用於視訊的卷積(CNN)層和LSTM層。

當然,DenseLayer(密連層)和卷積層不處理時間序列資料——他們期望不同型別的輸入。為了解決這個問題,我們需要使用層前處理器功能:例如,CnnToRnnPreProcessor和FeedForwardToRnnPreprocessor類。請參見這裡所有前處理器。幸運的是,在大多數情況下,DL4J配置系統將根據需要自動新增這些前處理器。然而,可以手動新增前處理器(覆蓋每個層自動新增的前處理器)。

例如,為了手動添在1層和2層之間新增前處理器,請將下列內容新增到網路配置中:.inputPreProcessor(2, new RnnToFeedForwardPreProcessor())

測試時間:一步一步的預測

與其他型別的神經網路一樣,可以使用MultiLayerNetwork.output()和MultiLayerNetwork.feedForward() 方法生成對RNNs的預測。這些方法在許多情況下是有用的;然而,這些方法的侷限性在於,我們只能對時間序列從零開始每次生成預測。

例如,考慮我們希望在實時系統中生成預測的情況,其中這些預測基於大量的歷史。在這種情況下,使用輸出/前饋方法是不切實際的,因為它們在每次呼叫整個資料歷史時進行完全的正向傳遞。如果我們希望在每個時間步長對單個時間步長進行預測,那麼這些方法可能既(a)非常耗效能,又(b)浪費,因為它們一遍又一遍地進行相同的計算。

對於這些情況,多層網路提供了四種方法:

  • rnnTimeStep(INDArray)
  • rnnClearPreviousState()
  • rnnGetPreviousState(int layer)
  • rnnSetPreviousState(int layer, Map<String,INDArray> state)

rnnTimeStep()方法被設計成允許有效地執行前向傳遞(預測),一次執行一個或多個步驟。與輸出/前饋方法不同,rnnTimeStep方法在被呼叫時跟蹤RNN層的內部狀態。重要的是要注意,rnnTimeStep和輸出/feedForward方法的輸出應該是相同的(對於每個時間步),無論我們是一次全部做出這些預測(輸出/feedForward),還是每次生成一個或多個步驟(rnnTimeStep)。因此,唯一的區別應該是計算成本。

總之,MultiLayerNetwork.rnnTimeStep()方法做了兩件事:

  1. 使用先前儲存狀態(如果有的話)生成輸出/預測(前向傳遞)
  2. 更新儲存的狀態,儲存最後一個時間步的啟用(準備下次rnnTimeStep被呼叫時使用)

例如,假設我們想使用RNN來預測天氣,提前一小時(基於前面100小時的天氣作為輸入)。如果我們要使用輸出方法,在每一小時,我們需要輸入整整100個小時的資料,以預測101個小時的天氣。然後,為了預測102小時的天氣,我們需要在100小時(或101小時)的資料中進食,這樣的時間為103小時。

或者,我們可以使用rnnTimeStep方法。當然,如果我們要在進行第一次預測之前利用全部100個小時的歷史,我們仍然需要進行全面的向前傳遞:

RNN Time Step

我們第一次呼叫rnnTimeStep時,兩種方法之間唯一的實際區別是儲存了上一個時間步的啟用/狀態——這用橙色表示。但是,下次我們使用rnnTimeStep方法時,這個儲存狀態將被用於做出下一個預測:

RNN Time Step

這裡有許多重要的區別:

  1. 在第二個圖片中(rnnTimeStep的第二次呼叫),輸入資料由單個時間步組成,而不是由資料的完整歷史組成
  2. 前向傳播是一個單一的時間步(與數百個或更多)相比。
  3. rnnTimeStep方法返回後,內部狀態將自動更新。因此,可以以與時間102相同的方式進行時間103的預測。等等。

但是,如果希望開始對新的(完全獨立的)時間序列進行預測,則必須(而且很重要)使用MultiLayerNetwork.rnnClearPreviousState()方法手動清除儲存的狀態。這將重置網路中所有迴圈層的內部狀態。

如果需要儲存或設定用於預測的RNN的內部狀態,則可以針對每一層分別使用rnnGetPreviousState和rnnSetPreviousState方法。例如,在序列化(網路儲存/載入)期間,這可能是有用的,因為rnnTimeStep方法中的內部網路狀態在預設情況下沒有被儲存,並且必須單獨儲存和載入。注意,這些GET/SET狀態方法返回並接受一個由啟用型別作為鍵的對映。例如,在LSTM模型中,有必要儲存輸出啟用和儲存單元狀態。

其他一些注意事項:

  • 我們可以同時為多個獨立的示例/預測使用rnnTimeStep方法。在上面的天氣示例中,例如,我們希望使用相同的神經網路對多個地點進行預測。這與訓練和前向傳遞/輸出方法相同:多行(輸入資料中的維度0)用於多個示例。
  • 如果沒有設定歷史/儲存狀態(即,最初或呼叫rnnClearPreviousState之後),則使用預設初始化(零)。這是與訓練過程相同的方法。
  • rnnTimeStep可以同時用於任意數量的時間步-不只是一個時間步。然而,重要的是要注意:
    • 對於單個時間步預測:資料是二維的,形狀是 [numExamples,nIn];在這種情況下,輸出也是二維的,形狀是[numExamples,nOut]。
    • 對於多個時間步預測:資料是三維的,具有形狀[numExamples,nIn,numTimeSteps];輸出將具有形狀[numExamples,nOut,numTimeSteps]。同樣,最後的時間步啟用和以前一樣被儲存。
  • 在rnnTimeStep的呼叫之間不可能改變示例的數量(換句話說,如果rnnTimeStep的第一次使用是針對例如3個示例,則所有後續的呼叫必須具有3個示例)。在重置內部狀態之後(使用rnnClearPreviousState()),任何數量的示例都可以用於rnnTimeStep的下一次呼叫。
  • rnnTimeStep方法不改變引數,只在訓練完成後才使用網路。
  • rnnTimeStep方法與包含單個和堆疊/多個RNN層的網路以及結合其他層型別(例如卷積層或密連層)的網路一起工作。
  • RnnOutputLayer 層型別不具有任何內部狀態,因為它沒有任何迴圈連線。

匯入時間序列資料

RNN的資料匯入是複雜的,因為我們有多種不同型別的資料可用於RNN:一對多、多對一、可變長度時間序列等。本節將描述當前實現的DL4J的資料匯入機制。

這裡描述的方法利用SequenceRecordReaderDataSetIterator類,以及來自DataVec的CSVSequenceRecordReader類。此方法目前允許你從檔案中載入被(製表符、逗號等)分隔的資料,其中每個時間序列位於單獨的檔案中。該方法還支援:

  • 可變長度時間序列輸入
  • 一對多和多對一資料載入(輸入和標籤在不同檔案中)
  • 用於分類的從索引到one-hot表示(即“2”到[0,0,1,0])的標籤轉換
  • 跳過資料檔案開始時的固定/指定行數(即註釋或頭行)

注意在所有情況下,資料檔案中的每一行代表一個時間步。

(除了下面的例子,你可能會發現這些單元測試是有用處的。)

示例1:在單獨檔案中同一長度、輸入和標籤的時間序列

假設在我們的訓練資料中有10個時間序列,由20個檔案表示:每個時間序列有10個檔案用於輸入,而輸出/標籤有10個檔案。現在,假設這20個檔案都包含相同數量的時間步(即,相同的行數)。

為了使用SequenceRecordReaderDataSetIteratorCSVSequenceRecordReader方法,我們首先建立兩個CSVSequenceRecordReader物件,一個用於輸入,一個用於標籤:

SequenceRecordReader featureReader = new CSVSequenceRecordReader(1, ",");
SequenceRecordReader labelReader = new CSVSequenceRecordReader(1, ",");

這個特定的建構函式需要跳過的行數(這裡跳過的1行)和分隔符(這裡使用逗號字元)。

第二,我們需要初始化這兩個讀取器,告訴他們從哪裡獲取資料。我們使用一個InputSplit物件來實現這一點。假設我們的時間序列被編號,檔名為“myInput_0.csv”、“myInput_1.csv”、“myLabels_0.csv”等等。一種方法是使用NumberedFileInputSplit

featureReader.initialize(new NumberedFileInputSplit("/path/to/data/myInput_%d.csv", 0, 9));
labelReader.initialize(new NumberedFileInputSplit(/path/to/data/myLabels_%d.csv", 0, 9));

在這個特定的方法中,“%d”被替換為相應的數字,並且使用數字0到9(包括 )。

最後,我們可以建立我們的SequenceRecordReaderdataSetIterator:

DataSetIterator iter = new SequenceRecordReaderDataSetIterator(featureReader, labelReader, miniBatchSize, numPossibleLabels, regression);

這個DataSetIterator可以被傳送到MultiLayerNetwork.fit() 方法來訓練網路。

miniBatchSize引數指定每個小批量中的例項數量(時間序列)。例如,對於總共10個檔案,miniBatchSize為5將給我們兩個資料集,每個資料集包含2個小批(DataSet物件),每個小批中包含5個時間序列。

注意:

  • 對於分類問題: numPossibleLabels是你的資料集中類別的數量 。 使用  regression = false。
    • 標籤資料:每行一個值,作為一個分類索引
    • 標籤資料將自動轉換為one-hot表示。
  • 對於迴歸問題: numPossibleLabels不被使用(設為任何值) 並使用 regression = true。
    • 輸入和標籤中的值可以是任意的(不像分類:可以有任意數量的輸出)。
    • 當regression = true 不進行標籤的處理。

示例2:在同一檔案中同一長度、輸入和標籤的時間序列。

根據上一個示例,假設我們的輸入資料和標籤不是一個單獨的檔案,而是在同一個檔案中。但是,每個時間序列仍然在一個單獨的檔案中。

從DL4J 0.4-rc3.8開始,這種方法對輸出具有單列的限制(分類索引或單個實值迴歸輸出)

在這種情況下,我們建立並初始化單個讀取器。同樣,我們跳過一個標題行,並將格式指定為逗號分隔,並假設我們的資料檔名為“myData_0.csv”, …, “myData_9.csv”:

SequenceRecordReader reader = new CSVSequenceRecordReader(1, ",");
reader.initialize(new NumberedFileInputSplit("/path/to/data/myData_%d.csv", 0, 9));
DataSetIterator iterClassification = new SequenceRecordReaderDataSetIterator(reader, miniBatchSize, numPossibleLabels, labelIndex, false);

miniBatchSizenumPossibleLabels與前面的示例相同。這裡,labelIndex指定標籤在哪個列。例如,如果標籤在第五列中,則使用labelIndex= 4(即,列被索引為0到numColumns-1)。

對於單一輸出值的迴歸,我們使用:

DataSetIterator iterRegression = new SequenceRecordReaderDataSetIterator(reader, miniBatchSize, -1, labelIndex, true);

同樣,numPossibleLabels引數不用於迴歸。

示例3:不同長度的時間序列(多對多)

根據前兩個示例,假設對於每個示例,輸入和標籤具有相同的長度,但是這些長度在時間序列之間不同。

我們可以使用相同的方法(CSVSequenceRecordReader和SequenceRecordReaderDataSetIterator),不過使用不同的建構函式:

DataSetIterator variableLengthIter = new SequenceRecordReaderDataSetIterator(featureReader, labelReader, miniBatchSize, numPossibleLabels, regression, SequenceRecordReaderDataSetIterator.AlignmentMode.ALIGN_END);

此處的引數與前面的示例相同,除了AlignmentMode.ALIGN_END。這種對齊模式輸入告訴SequenceRecordReaderDataSetIterator需要兩件事:

  1. 時間序列可以具有不同的長度。
  2. 為每個示例把輸入和標籤對齊,以使它們的最後值出現在同一時間步。

注意,如果特徵和標籤總是具有相同的長度(如示例3中的假設),那麼兩個對齊模式(AlignmentMode.ALIGN_END 和 AlignmentMode.ALIGN_START)將給出相同的輸出。對齊模式選項將在下一節中解釋。

還要注意:在資料陣列中,可變長度時間序列總是在時間0處開始:如果要求,將在時間序列結束之後新增填充。

與上面的示例1和2不同,上面的variableLengthIter例項生成的DataSet物件還將包括輸入和掩碼陣列,如本文前面所述。

例4:多對一和一對多資料

我們還可以使用示例3中的AlignmentMode功能來實現多對一RNN序列分類器。在這裡,讓我們假設:

  • 輸入和標籤在單獨的被分隔的檔案中。
  • 標籤檔案包含一個單行(時間步)(用於分類的類索引,或用於迴歸的一個或多個數字)。
  • 示例之間的輸入長度可以(可選地)不同。

實際上,與示例3相同的方法可以做到這一點:

DataSetIterator variableLengthIter = new SequenceRecordReaderDataSetIterator(featureReader, labelReader, miniBatchSize, numPossibleLabels, regression, SequenceRecordReaderDataSetIterator.AlignmentMode.ALIGN_END);

對準模式相對簡單。它們指定是否填充較短時間序列的開始或結束。下面的圖表展示了它如何與掩碼陣列(如本文前面所討論的)一起工作:

Sequence Alignment

一對多個案例(類似於上面的最後一個案例,但只有一個輸入)是通過使用AlignmentMode.ALIGN_START實現。

注意,在包含不同長度的時間序列的訓練資料的情況下,標籤和輸入將針對每個示例單獨對齊,然後按要求填充更短的時間序列:

Sequence Alignment

可用的層


GravesBidirectionalLSTM

[原始碼]

雙向長短記憶單元迴圈神經網路,基於Graves:迴圈神經網路有監督序列標籤  http://www.cs.toronto.edu/~graves/phd.pdf

雙向層包裝器可以使任何迴圈層雙向,特別是GravesLSTM。注意,這個層增加了兩個方向的輸出,這兩個方向轉換成雙向的“ADD”模式。

gateActivationFunction

public Builder gateActivationFunction(String gateActivationFn) 

LSTM門的啟用函式。注意:這應該被限制在範圍0-1:sigmoid或hard sigmoid,例如

  • 引數 gateActivationFn 是 LSTM 門的啟用函式

gateActivationFunction

public Builder gateActivationFunction(Activation gateActivationFn) 

LSTM門的啟用函式。注意:這應該被限制在範圍0-1:sigmoid或hard sigmoid,例如

  • 引數 gateActivationFn 是 LSTM 門的啟用函式

gateActivationFunction

public Builder gateActivationFunction(IActivation gateActivationFn) 

LSTM門的啟用函式。注意:這應該被限制在範圍0-1:sigmoid或hard sigmoid,例如

  • 引數 gateActivationFn 是 LSTM 門的啟用函式

GravesLSTM

[原始碼]

長短記憶單元迴圈神經網路,基於Graves:迴圈神經網路有監督序列標籤 http://www.cs.toronto.edu/~graves/phd.pdf

用於在CUDA (Nvidia) GPUs上進行更快的網路訓練


LSTM

[原始碼]

無窺視孔連線的LSTM迴圈神經網路層。支援CUDNN加速。詳見https://deeplearning4j.org/cudnn


RnnLossLayer

[原始碼]

迴圈神經網路損失層。

處理各種目標(損失)函式的梯度等計算。

這裡是分佈密連元件。因此,輸出啟用大小等於輸入大小。

輸入和輸出啟用與其他RNN層相同:分別具有三維形狀 [miniBatchSize,nIn,timeSeriesLength] 和 [miniBatchSize,nOut,timeSeriesLength]。

注意,RnnLossLayer還具有配置啟用功能的選項。

nIn

public Builder nIn(int nIn) 
  • 引數 lossFunction 是損失層的損失函式

RnnOutputLayer

[原始碼]

和標籤的形狀[minibatch,nOut,sequenceLength]。它還支援掩碼陣列。

注意,RnnOutputLayer也可用於一維CNN層,其中也有[minibatch,nOut,sequenceLength]啟用/標籤形狀 。

build

public RnnOutputLayer build() 
  • 引數 lossFunction 是輸出層的損失函式

Bidirectional

[原始碼]

雙向是一個“包裝器”層:它封裝任何單向RNN層,使其成為雙向的。

注意,支援多種不同的模式——這些模式指定了應該如何從引數中組合啟用,這裡不共享——封裝的RNN層有2個單獨的副本,每個都有單獨的引數。

getNOut

public long getNOut() 

該模式列舉定義瞭如何將前向和後向網路的啟用組合起來。

新增:輸出 =前向+反向(元素新增)

乘: 輸出=前向x反向(元素的乘法)

平均值:輸出=0.5(前向+反向)

連線:連線啟用。

其中“前向”是前向RNN的啟用,“反向”是反向RNN的啟用。在所有情況下,除了連線,輸出啟用大小與被這個層包裹的標準RNN大小相同。在連線情況下,輸出啟用大小(一維度)比標準RNN的啟用陣列大2倍。

getUpdaterByParam

public IUpdater getUpdaterByParam(String paramName) 

獲取給定引數的更新器。通常,所有使用的更新器都是一樣的,但這不一定是這樣的。

  • 引數 paramName 是引數名稱
  • 返回 IUpdater 

LastTimeStep

[原始碼]

LastTimeStep是一個“包裝器”層:它包裝任何RNN(或CNN1D)層,並在前向傳播期間提取出最後一個時間步,並將其作為行向量(每個示例)返回。也就是說,對於3D(時間序列)輸入(形狀[minibatch,layerSize,timeSeriesLength]),我們採用最後一個時間步並將其返回為形狀[minibatch,layerSize]的2D陣列。

注意,最後的時間步操作考慮了任何掩碼陣列(如果存在):因此,可變長度時間序列(在同一小批中)在這裡如預期那樣被處理。


SimpleRnn

[原始碼]

簡單的RNN-AKA“vanilla”RNN是最簡單的迴圈神經網路層。

注意,其他體系結構(LSTM等)通常更有效,特別是對於較長的時間序列;然而,SimpleRnn的計算速度非常快,因此在資料集中的時間依賴的長度只有幾步長的情況下可以考慮使用。