0.前言
遞歸!循環神經網絡Recurrent Neural Network
循環神經網絡(又稱遞歸神經網絡,Recurrent Neural Network,RNN)。是一種用于處理序列數據的神經網絡結構,具有記憶功能,能夠捕捉序列中的時間依賴關系。RNN通過循環連接的方式,將當前時間步的輸入和上一時間步的隱藏狀態作為輸入,計算當前時間步的隱藏狀態和輸出,從而實現對序列數據的建模和預測。
說到遞歸描述,各位肯定會想到馬爾科夫鏈,循環神經網絡和馬爾科夫鏈具有一定的相似性,兩者都是用來捕捉和描述序列數據的變化規律。
但兩者在其原理上有根本區別:
馬爾可夫鏈需要滿足馬爾可夫狀態,即知道t時刻的狀態時,t+1時刻的狀態的概率分布明確。我們可以通過轉移矩陣基于t時刻的信息計算t+1、t+2等未來時刻中不同狀態的預期概率。因此未來狀態僅受當前狀態影響,過去發生過什么并不重要。它常常被用來描述理想狀態下物理場中概率變化目標的屬性。而循環神經網絡則不需要滿足任何前提條件,它可以基于過去任何長短時間的數據,基于統計經驗給出未來的最可能狀態。
但是相較于循環神經網絡而言,馬爾可夫鏈在實際應用中具有天生缺陷:
- 一方面是馬爾可夫鏈的轉移矩陣的概率映射是線性的,并不能很好的描述非線性的長迭代狀態(比如,當轉移矩陣會隨時間步長發生變化時)。所以馬爾可夫鏈在自然語言中的處理和應用非常拉跨。對于RNN而言,由于其本身結構的復合性,RNN的狀態轉移可以滿足非線性的變化需求。
- 同時馬爾可夫狀態的設定過于理想,現實世界的應用工程中,狀態空間本身,轉移矩陣的精確求解困難。而對于RNN而言,無論滿足滿足馬爾可夫狀態,都可以從遞歸過程中挖掘統計規律。因此在一些具有統計學特性外的外部附加規則的預測任務中(比如游戲抽卡),RNN能夠反應與時間步長相關的特征。
然而,對于傳統的RNN,隨著序列長度的增加,計算得到的梯度在反向傳播過程中會逐漸消失或爆炸,導致模型難以訓練。這種現象被稱為“梯度消失”或“梯度爆炸”,它限制了RNN捕捉和利用長距離依賴關系的能力。這對于許多需要理解長期依賴關系的任務,如自然語言處理、語音識別等,是一個嚴重的挑戰。
LSTM的誕生
1997年,由德國慕尼黑工業大學的計算機科學家Sepp Hochreiter與Jürgen Schmidhuber共同提出了LSTM(Long Short-Term Memory)模型。LSTM是一種特別的RNN,旨在解決傳統RNN在處理長序列數據時遇到的梯度消失和梯度爆炸問題。
原文鏈接:Long Short-Term Memory | MIT Press Journals & Magazine | IEEE Xplore
原文PDF:nc.dvi
Sepp Hochreiter與Jürgen Schmidhuber在實驗中對LSTM和之前其他RNN模型做了進一步對比:利用無噪聲和有噪聲的序列對不同的RNN進行訓練,這些序列長度較長,對于有噪聲序列而言,其中只有少數數據是重要的,而其余數據則起到干擾作用。
表:LSTM與RTRL(RealTime Recurrent Learning)、ELM(Elman nets,又稱單循環網絡)和RCC(Recurrent Cascade-Correlation,級聯相關學習架構)在長序列無噪聲數據中的表現對比
可以發現,相較于RTRL、ELM、RCC等傳統模型,LSTM在長序列無噪聲模型中的訓練成功率更高,同時訓練速度更快。
注:表中LSTM的Block為一個LSTM單元,Block為4代表4層LSTM單元進行堆疊,Size代表一個Block或LSTM單元中memory cell(記憶單元,也就是長期記憶)并列的數量,為了方便計算,這些記憶單元共享輸入門和輸出門和遺忘門。其實,文中所說的Size其實就是我們現在說的隱藏單元數量,由于1997年計算機技術不是很發達,GPU并行計算的運用不多,所以原文的結構寫得很復雜。(具體示例結構如原文圖2所示,由于是編碼訓練的形式,原文展示得很復雜,看不懂沒關系,不影響后面理解)。
其中,原文對Size的表述是:Memory cell blocks of size S form a structure where S memory cells share the same input gate and the same output gate. These blocks facilitate information storage. However, as with conventional neural nets, it is not so easy to code a distributed input within a single cell. Since each memory cell block has as many gate units as a single memory cell, namely two, the block architecture can be even slightly more efficient (see paragraph on computational complexity in Section).
表:LSTM與RTRL、BPTT(Back-Propagation Through Time)和RCC在長序列無局部規律性數據中的表現對比
由于長期記憶(細胞記憶)的特性,在無局部規律數據中,LSTM的表現相較于RTRL、BPTT和CH模型在訓練速度及跟蹤能力上有質的差異,不僅訓練迭代需求縮小了十倍以上,而且表現出極好的追蹤能力。這也是為什么在那么多RNN模型中,LSTM可以獲得成功的原因。
簡單來說而言:LSTM(Long Short-Term Memory)是一種特殊的遞歸神經網絡(RNN),旨在解決傳統RNN在處理長序列數據時遇到的梯度消失和梯度爆炸問題。LSTM通過引入“記憶單元”和“門控機制”,能夠高效建模長期和短期依賴關系。
1.長短期記憶網絡?LSTM(Long Short-Term Memory)的基本結構
常規LSTM單元的結構如圖1所示,在一些論文中常被為“記憶細胞”(Memory Cell),但在中文語境我更傾向于稱之為LSTM單元,這樣不容易產生混淆。
這是由于GPU的推廣,目前的LSTM設計類似于多個“傳統的記憶細胞”并聯在一起,組成一個集合體,對應LSTM開山論文中的“Cell block”,來傳輸多維的記憶信息,而在開山論文中,Memory cell代表只能處理數值而非多維向量的單元,因此在當前時代,Memory cell和Cell block在很多語境下基本上是一個東西,所以這個cell究竟是那個cell?這個描述不是很嚴謹。
一般而言,這就是,當其在處理數據時,數據內容在內部循環流動,序列數據或任何長度的單維度數據(dim=1)在輸入LSTM時,按照不同的時間步長t,依次輸入模型,在這一過程中當前的細胞狀態(cell sate)和隱藏狀態(hidden state)在不斷變化。
它包含三個關鍵的門控機制:輸入門(Input Gate)、遺忘門(Forget Gate)和輸出門(Output Gate)。其詳細信息如下圖所示:
圖1:LSTM單元經典結構細節(一圖流),以下介紹皆與該圖對應
圖中:
- 為了簡化表達,不同位置weight、Bias代表不同權重乘法和偏置加法操作,盡管名字相同,但不同位置的weight、Bias的參數相互獨立
- “()”括號內的數值代表在該環節傳輸或該參數的“張量”大小,如Weight(N*N)代表權重矩陣的張量規模是N*N。
- 短期記憶(隱藏狀態
)和長期記憶(細胞狀態
)的張量尺度必須一致,均為 N 維向量,N是可以自己需求設計數值,稱為隱藏單元的數量
1.1.遺忘門(Forget Gate):
遺忘門決定了前一時刻的記憶細胞狀態中有多少信息需要保留或遺忘。
它接收當前的輸入和上一時間步的隱藏狀態
?,通過Sigmoid函數輸出一個0到1之間的值,表示遺忘的比例。
計算公式為:
? ? ? ? ? ? ? ? ? ? (1)
其中和
?是遺忘門的權重和偏置的參數矩陣,為了區分對應隱藏狀態和當前輸入的權重矩陣相互獨立,格外加了數字下表如:
,后同。
1.2.輸入門(Input Gate):
輸入門決定了當前時間步的輸入信息中有多少需要被加入到細胞狀態中。
如一圖流所示,它包含兩個部分:首先,使用Sigmoid函數決定哪些信息需要更新(Potential Memory to remember),;其次,使用Tanh函數生成一個候選長期(Potential Long-Trem menmory),也就是
。
計算公式為:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(2)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (3)
1.3.細胞狀態-長期記憶(Cell-State):
????????Cell-State負責在序列的各個時間步長之間存儲和傳遞信息,簡單的來說,它就是長期記憶。它像是一個傳輸帶,在整個序列處理過程中,信息在上面流動,只有少量的線性操作被應用于信息本身,信息流動相對簡單。
每次迭代中,細胞狀態更新公式為:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (4)
根據式(1)、(2)、(3),(4)可完整展開為:
? ? ? ? (5)
? ? ? ? 其中,為該時間步輸出的細胞狀態,
為上一時間步輸出的的細胞狀態,其中
由遺忘門計算而出,決定了上一刻信息保留的程度。
是選擇門從隱藏狀態與當前時間步或取信息產生的。總結而言:遺忘門決定了前一時刻的記憶細胞狀態中有多少信息需要保留,輸入門決定了當前時間步的輸入信息中有多少需要被加入到細胞狀態中。
1.4.隱藏狀態-短期記憶(Hidden State)與輸出門(Output Gate):
????????輸入門決定了基于當前時間步的細胞狀態、輸入數據和上一時間步的隱藏狀態,輸出當前時間步隱藏狀態,對于序列預測,ht就是LSTM對于下一時間步輸入
的預測。如果你是以序列輸出且以回歸損失函數反向傳播的。
它接收當前的輸入xt?和上一時間步的隱藏狀態ht?1?,通過Sigmoid函數輸出一個0到1之間的值,表示輸出的比例。
計算公式為:
? ? ? ? ? ? ? ? ? ? ? (6)
? 同樣可以完整展開為:?
???? ?(7)
2.長短期記憶網絡?LSTM(Long Short-Term Memory)的具體設計細節及應用注意事項
2.1.LSTM的參數
LSTM的參數量計算
如基本結構介紹及一圖流中所示,LSTM的模型參數主要包括權重和偏置,其中權重根據計算對象分為輸入參數權重(Input Weight)和針對隱藏狀態的權重(Recurrent Weight),偏置則對應遺忘門、輸入門和輸出門中的計算。具體信息可以參照一圖流,里面已經標的很清楚了
我們來簡單地舉個例子,下圖是我設計的一個簡單的LSTM模型,其中輸入為長度807的序列數據,LSTM隱藏單元的大小被設置成4000,因此HiddenState和Cell State的大小都為4000
圖二
根據圖一的展示,對應圖二我們可以簡單的計算得到
輸入參數的權重參數數量為:
?4(輸入接口)*[4000*1](單個權重矩陣大小)*[807*3](輸入數據維度=所有空間長度累乘*通道長度)=16000*2421=38736000;
隱藏狀態的權重參數數量為:
???4(輸入接口)*[4000*4000](單個權重矩陣大小)=16000*4000=64000000;
偏置參數的數量為:
4(四個處理點位)*[4000*1](單個偏置矩陣大小)=16000*1=16000;
總參數為:16000+64000000+38736000=?102752000
注意:在matlab中,LSTM的設計總是對象單一時間步的,比如這里,當我的輸入具有空間屬性時,LSTM會先將其flatten至序列再進行處理,所以輸入參數的數量就是:所有空間長度累乘*通道長度。并不是你輸入一個序列長度為100(S)通道為3的數據,程序就建立輸入維度為3的模型,在計算中迭代100次。而是輸入維度為300,不限迭代次數模型。
在設計LSTM的輸入時,一般不包含空間屬性,只有通道屬性、批次、和時間步,也就是CBT。在計算參數的例子中,故意加入了空間屬性,也是為了說明這一特性。讀者可以參考后面的實戰示例,加深理解。
圖:當LSTM輸入圖像時
2.2.狀態激活方法 StateActivation Fuction 與 門激活方法 GateActivation Function
在LSTM(長短期記憶網絡)中,狀態激活方法(State Activation Function)和門激活方法(Gate Activation Function)起著至關重要的作用。它們分別用于生成候選記憶狀態和控制信息的流動。雖然默認情況下,狀態激活方法使用tanh函數,門激活方法使用sigmoid函數,但在實際應用中,這些激活函數可以根據具體需求進行調整和替換。
狀態激活方法(State Activation Function)
默認激活函數:tanh
tanh函數(雙曲正切函數)將輸入映射到-1到1之間,其輸出均值為0,這有助于保持梯度在傳播過程中相對穩定,從而在一定程度上緩解梯度消失問題。
softsign
softsign函數是另一種非線性激活函數,其輸出范圍也是-1到1,但與tanh函數相比,softsign函數在輸入接近0時具有更大的梯度,這有助于模型在訓練初期更快地收斂。此外,softsign函數在輸入絕對值較大時,其輸出趨近于±1的速度更慢,這有助于避免梯度爆炸問題,以改善模型的收斂速度和穩定性。
ReLU(Rectified Linear Unit)
ReLU函數是深度學習中常用的激活函數,其輸出在輸入為正數時為輸入本身,在輸入為負數時為0。ReLU函數具有計算簡單、非飽和性的特點,能夠有效緩解梯度消失問題。盡管ReLU函數在LSTM中不常用作狀態激活函數,但在某些特定的應用場景下,如處理稀疏特征或加速訓練過程時,可以嘗試使用ReLU函數。然而,需要注意的是,由于ReLU函數的輸出范圍不是-1到1,直接使用ReLU函數可能會破壞LSTM中記憶細胞的0中心化特性,因此在實際應用中需要進行適當的調整。
門激活方法(Gate Activation Function)
默認激活函數:sigmoid
特點:sigmoid函數將輸入映射到0到1之間,其輸出值可以解釋為信息通過門的概率。sigmoid函數在輸入較小或較大時,其輸出趨近于0或1,這有助于實現門控機制,即控制信息的流動。
hard-sigmoid
hard sigmoid函數作為sigmoid函數的近似,計算速度更快。
2.3.Bi-LSTM?
Bi-LSTM特別簡單,其結構就是兩個LSTM模塊鏡像并列,然后方向相反,因此其有兩條流向相反的細胞狀態和兩條流向相反的隱藏狀態,因此其參數量的計算就是簡單的LSTM*2即可(隱藏單元數量相同時),讀者可以對應圖二和圖三進行領悟,這里就沒必要展開來講了。
?圖三
2.4.LSTM的輸入和輸出(重點)
LSTM及Bi-LSTM的輸入一般為序列數據,格式通常為(CBT),即一個通道度、批次度和時間度。在2.1的示例中,我們可以發現LSTM的單次輸入總會鋪平成向量。
對于輸出長度為K的向量X時,其對應輸入的權重轉化矩陣W為N*K,N為隱藏單元數量,在計算權重時,W在前,輸入向量X在后,進行矩陣乘法,W×X,輸出的參數大小正好是N*1,對應隱藏狀態的大小,使得數據對齊。
LSTM的輸出比較重要,分為序列狀態輸出(sequence output)和最后狀態輸出(last output),注意!無論那種輸出,它們輸出的皆是隱藏狀態信息,而不是細胞狀態信息。
輸出為隱藏狀態主要是LSTM的設計考量,一是隱藏狀態就是LSTM對下一個時間步信息的預測信息,二是隱藏狀態在不同時間步的變化更大,含有更多信息,在序列輸出時也能夠保留更多的特征細節,讀者可以對照式5和式7進行理解。
從應用層面而言:
序列狀態輸出常用于序列到序列的任務,如機器翻譯、信號轉化等任務,其中每個時間步的輸出都對應著輸出序列中的一個元素。但是記住,序列狀態輸出的時間序列數據,雖然它可以跟神經元連接,但輸出是有時間步長的,也就是你的每個訓練樣本的對應學習標簽要相同長度的數據。
最后狀態輸出用于序列到標簽的任務,如文本分類、情感分析等,其中LSTM的輸出用于預測整個序列的類別或標簽。回歸預測同樣如此。需要注意的是最后狀態輸出沒有時間步長,或者時間步長為1。
其實無論是序列狀態輸出還是最后狀態輸出,在分類和回歸問題都可以使用,這主要看最后一層的LSTM是序列輸出還是步長輸出,比如我堆疊三層LSTM,前兩層用序列輸出的LSTM,最后一層用最后狀態輸出的LSTM。LSTM的設計是很自由的,各位只需要知道序列輸出的信息更多即可。
圖:三層嵌套LSTM,第三層LSTM用了最后狀態輸出,時間步長消去了。
圖:三層嵌套LSTM,但第三層還是用sequence輸出,輸出始終保持是時間序列狀態。
關于MinLength:
SequenceinputLayer常常作物LSTM的前置輸入層,可以設置時間步長的最短長度,實際上這個操作就是檢驗,如果輸入的數據時間步長小于MinLength的值,那么程序會將自動進行報錯(不會自動填充),而且這并不會影響輸出數據時間維度的大小,seqence輸出的話,輸入是長度K,輸出就是K,無關MinLength。
2.5.LSTM特別的偏置初始化:unit-forget-gate
unit-forget-gate
作為偏置初始化策略時,這通常意味著對遺忘門的偏置進行特定的初始化,以鼓勵或抑制遺忘門在訓練初期的行為。例如,在某些情況下,可能會將遺忘門的偏置初始化為一個較大的正值,以使得模型在訓練開始時傾向于保留更多的信息(因為遺忘門的輸出接近于1),這有助于模型更穩定地學習長期依賴關系。
3.LSTM實戰
3.1 LSTM-渦散發動機組壽命預測(序列到序列):
在該實戰示例中,對于LSTM模型而言:單次輸入是一系列多維向量(多個步長的不同屬性信息)(K*P,K為時間步幅,長度任意,P為屬性數量即向量維度=17),輸出為多個(K個)1*1的數值,訓練最小批次量為20。
也就是序列到序列,序列的長度即時間步長K,即:
輸入:17(C)*20(B)*K(T)
輸出:1(C)*20(B)*K(T)
因此損失函數也是基于輸出(1(C)*20(B)*K(T))與標準預期(1(C)*20(B)*K(T))的MSE均值進行計算。
原代碼下載:
25-LSTM-SequenceToSequence_Example.zip
鏈接: https://pan.baidu.com/s/1c1_GBWSi4d946T1NizQoeA?pwd=as2g提取碼: as2g?
代碼展示及分析:
%使用深度學習進行序列到序列回歸
%本示例展示了如何使用深度學習來預測發動機的剩余使用壽命(RUL)。
%為了訓練一個深度神經網絡,使其能夠從時間序列或序列數據中預測數值,您可以使用長短期記憶(LSTM)網絡。
%本示例使用了[1]中描述的渦扇發動機退化模擬數據集。示例中訓練了一個LSTM網絡,以根據表示發動機中各種傳感器的時間序列數據,預測發動機的剩余使用壽命(預測性維護),以循環次數為單位。訓練數據包含100臺發動機的模擬時間序列數據。每個序列的長度各不相同,并且對應于一個從開始到故障(RTF)的完整實例。測試數據包含100個部分序列,以及每個序列結束時對應的剩余使用壽命值。
%數據集包含100個訓練觀測值和100個測試觀測值。%渦扇發動機退化模擬數據集中的每個時間序列代表一臺不同的發動機。每臺發動機在開始時具有不同程度的初始磨損和制造差異。每臺發動機在時間序列開始時均正常運行,并在序列中的某個時刻出現故障。在訓練集中,故障幅度逐漸增大,直至系統失效。
%在訓練數據,一個cell是一個樣本,cell中的矩陣行對應時間步幅,列對應不同的屬性(通道)
%文件中有26列數字,用空格分隔。每一行是在單個運行周期內采集的數據快照,每一列代表一個不同的變量。各列對應屬性如下:
%第1列 – 機組編號
%第2列 – 循環時間
%第3–5列 – 運行設置
%第6–26列 –21個不同的傳感器測量值%因此在這個示例中,我們只知道發動機在不同時間步的屬性,以及它不同時間步對應的剩余壽命,模型所要做的,就是利用不同時間步的數據,預測不同時間步中對應的剩余壽命。
clear all
load("data.mat");
%移除具有恒定值的特征
%對于所有時間步長都保持恒定的特征可能會對訓練產生負面影響。找出具有相同最小值和最大值的行(屬性)數據,并將這些屬性移除。
XTrainConcatenatedTimesteps = cat(1,XTrain{:});
m = min(XTrainConcatenatedTimesteps,[],1);
M = max(XTrainConcatenatedTimesteps,[],1);
idxConstant = M == m;for i = 1:numel(XTrain)XTrain{i}(:,idxConstant) = [];
end
numFeatures = size(XTrain{1},2)
Normalize Training Predictors
%將訓練預測變量標準化為均值為零、方差為一。按樣本處理。
XTrainConcatenatedTimesteps = cat(1,XTrain{:});
mu = mean(XTrainConcatenatedTimesteps,1);
sig = std(XTrainConcatenatedTimesteps,0,1);for i = 1:numel(XTrain)XTrain{i} = (XTrain{i} - mu) ./ sig;
end
%Clip Responses
%為了在發動機接近故障時從序列數據中學習更多信息,我們將數據進行裁剪,減去前面保持健康的部分,將剩余壽命(RUC)在閾值150處進行截斷。也就是說,預測參數RUC的范圍僅為1~150,超過了150的部分,我們令其為150,這樣,數據就能在低壽命樣本群體中有較好的表現。
thr = 150;
for i = 1:numel(TTrain)TTrain{i}(TTrain{i} > thr) = thr;
end
%此圖顯示了第一個觀測樣本及其對應的截斷響應。%Prepare Data for Padding
%準備數據進行填充
%為了最小化添加到小批量數據中的填充量,按序列長度對訓練數據進行排序。然后,選擇一個能夠均勻劃分訓練數據的小批量大小,以減少小批量數據中的填充量。
按序列長度對訓練數據進行排序。
for i=1:numel(XTrain)sequence = XTrain{i};sequenceLengths(i) = size(sequence,1);
end[sequenceLengths,idx] = sort(sequenceLengths,"descend");
XTrain = XTrain(idx);
TTrain = TTrain(idx);
%View the sorted sequence lengths in a bar chart.
figure
bar(sequenceLengths)
xlabel("Sequence")
ylabel("Length")
title("Sorted Data")
%選擇一個能均勻分割訓練數據的小批量大小,以減少小批量中的填充量。該圖展示了在批量大小為20時,對未排序和已排序序列所添加的填充。%定義網絡架構。創建一個LSTM網絡,該網絡由一個包含200個隱藏單元的LSTM層、一個大小為50的全連接層以及一個丟棄概率為0.5的丟棄層組成。
numResponses = size(TTrain{1},2);%為1
numHiddenUnits = 200;layers = [ ...sequenceInputLayer(numFeatures)lstmLayer(numHiddenUnits,OutputMode="sequence")fullyConnectedLayer(50)dropoutLayer(0.5)fullyConnectedLayer(numResponses)];
%指定訓練選項。使用“adam”優化器,以小批量大小為20進行60個周期的訓練。設置學習率為0.01。為防止梯度爆炸,將梯度閾值設置為1。為保持序列按長度排序,將“Shuffle(洗牌)”選項設置為“never(從不)”。在圖表中顯示訓練進度,并監控均方根誤差(RMSE)指標。
maxEpochs = 60;
miniBatchSize = 20;options = trainingOptions("adam", ...MaxEpochs=maxEpochs, ...MiniBatchSize=miniBatchSize, ...InitialLearnRate=0.01, ...GradientThreshold=1, ...Shuffle="never", ...Metrics="rmse", ...Plots="training-progress", ...Verbose=0);Train the Network
net = trainnet(XTrain,TTrain,layers,"mse",options);%Test the Network
%使用本示例附帶的 processTurboFanDataTest 函數準備測試數據。processTurboFanDataTest 函數從 filenamePredictors 和 filenameResponses 中提取數據,并返回元胞數組 XTest 和 TTest,它們分別包含測試預測序列和響應序列。
filenamePredictors = fullfile("test_FD001.txt");
filenameResponses = fullfile("RUL_FD001.txt");
[XTest,TTest] = processTurboFanDataTest(filenamePredictors,filenameResponses);
%利用從訓練數據中計算得出的 idxConstant 來移除具有常數值的特征(不隨其他變量或時間變化的固定值)。接著,采用與訓練數據相同的參數對測試預測數據進行標準化處理。最后,根據訓練數據所使用的閾值對測試響應進行裁剪。
for i = 1:numel(XTest)XTest{i}(:,idxConstant) = [];XTest{i} = (XTest{i} - mu) ./ sig;TTest{i}(TTest{i} > thr) = thr;
end
%使用神經網絡進行預測。若要對多個觀測值進行預測,請使用 minibatchpredict 函數。minibatchpredict 函數在可用的情況下會自動使用 GPU。
%為了防止函數向數據添加填充,請將小批量大小指定為 1。若要以元胞數組的形式返回預測結果,請將 UniformOutput 設置為 false。
YTest = minibatchpredict(net,XTest,MiniBatchSize=1,UniformOutput=false);
%LSTM 網絡對部分序列進行預測時,每次處理一個時間步。在每個時間步,網絡僅使用當前時間步的值以及根據之前時間步計算得到的網絡狀態進行預測。網絡在每個預測之間更新其狀態。minibatchpredict 函數返回這些預測的序列。預測序列的最后一個元素對應于部分序列的預測剩余使用壽命(RUL)。
%或者,也可以使用 predict 函數并更新網絡的 State 屬性,來逐個時間步進行預測。這在時間步的值以流的形式到達時非常有用。
%在圖表中可視化部分預測結果。
idx = randperm(numel(YTest),4);
figure
for i = 1:numel(idx)subplot(2,2,i)plot(TTest{idx(i)},"--")hold onplot(YTest{idx(i)},".-")hold offylim([0 thr + 25])title("Test Observation " + idx(i))xlabel("Time Step")ylabel("RUL")
end
legend(["Test Data" "Predicted"],Location="southeast")
%對于給定的部分序列,預測的當前剩余使用壽命(RUL)是預測序列的最后一個元素。計算預測值的均方根誤差(RMSE),并在直方圖中可視化預測誤差。
for i = 1:numel(TTest)TTestLast(i) = TTest{i}(end);YTestLast(i) = YTest{i}(end);
end
figure
rmse = sqrt(mean((YTestLast - TTestLast).^2))
histogram(YTestLast - TTestLast)
title("RMSE = " + rmse)
ylabel("Frequency")
xlabel("Error")%References
%Saxena, Abhinav, Kai Goebel, Don Simon, and Neil Eklund. "Damage propagation modeling for aircraft engine run-to-failure simulation." In Prognostics and Health Management, 2008. PHM 2008. International Conference on, pp. 1-9. IEEE, 2008.
在示例中,為提高模型對低壽命的樣本的敏感程度,對剩余壽命的大于150的預測標簽修改為150。
RNN數據的預處理-填充與截斷
注意!在訓練模型前,我們需要將不同時間步幅的數據進行排序,以提高訓練效率,這是因為在每次最小批次訓練前,程序總是自動地將該批次的數據按最長或最短時間步長的數據進行對齊,即將該批次的數據步長統一提高或裁剪,使樣本的K對齊。(后面的例子同理)
在訓練長短期記憶網絡(LSTM)或其他循環神經網絡(RNN)時,處理不同長度序列的常見方法是對序列進行填充(padding)或截斷(truncation),以確保每個批次中的所有序列具有相同的長度。在Matlab中,對于函數trainnet(),LSTM默認的處理方法是后填充。
填充:
確定批次中所有序列的最大長度。
將所有序列填充到這個最大長度。通常使用零填充(即在序列的末尾添加零),因為零對大多數神經網絡來說是一個中性元素。
后填充(post-padding):在序列的末尾添加零,這是最常見的方法。
前填充(pre-padding):在序列的開頭添加零,某些情況下可能更有用,但較少使用。
截斷:
如果某些序列的長度超過了預設的最大長度限制,可以選擇截斷這些序列。
截斷通常是從序列的開頭或末尾去掉一些元素,具體取決于任務需求和序列的特性。
?訓練結果如圖所示:
子函數:
function [predictors,responses] = processTurboFanDataTest(filenamePredictors,filenameResponses)
% processTurboFanDataTest 函數從 filenamePredictors 和 filenameResponses
% 中提取數據,并返回包含測試預測序列和響應序列的元胞數組 predictors 和 responses。
% 在 filenamePredictors 中,時間序列在系統故障前的某段時間結束。
% filenameResponses 中的數據為測試數據提供了真實剩余使用壽命(RUL)值的向量。predictors = processTurboFanDataTrain(filenamePredictors);RULTest = dlmread(filenameResponses);numObservations = numel(RULTest);responses = cell(numObservations,1);
for i = 1:numObservationsX = predictors{i};sequenceLength = size(X,1);rul = RULTest(i);responses{i} = rul+sequenceLength-1:-1:rul;
endendfunction [predictors,responses] = processTurboFanDataTrain(filenamePredictors)
% processTurboFanDataTrain 函數從 filenamePredictors 中提取數據,
% 并返回分別包含預測序列和響應序列的元胞數組 predictors 和 responses。
% 數據包含以 zip 格式壓縮的文本文件,文件中有 26 列數字,各列之間用空格分隔。
% 每一行代表在一個運行周期內采集的數據快照,每一列代表一個不同的變量。
% 各列對應的內容如下:
% 1: 機組編號
% 2: 運行周期時間
% 3–5: 運行設置
% 6–26: 傳感器測量值 1–17dataTrain = dlmread(filenamePredictors);numObservations = max(dataTrain(:,1));predictors = cell(numObservations,1);
responses = cell(numObservations,1);
for i = 1:numObservationsidx = dataTrain(:,1) == i;predictors{i} = dataTrain(idx,3:end);timeSteps = dataTrain(idx,2);responses{i} = flipud(timeSteps);
endend
3.2 Bi-LSTM的信號分類預測(序列到標簽):?
源碼:
鏈接: https://pan.baidu.com/s/12bm6sMVHEvlD_5OOf73BCg?pwd=uk5m 提取碼: uk5m?
數據示例:?
單批次模型訓練的輸入和輸出:
模型輸入:3(C)*64(B)*K(T)
模型輸出:1(C)*64(B)*1(T)或? 1(C)*64(B)
注意:由于是last輸出,輸出的時間步長被消掉了
%Sequence Classification Using Deep Learning
%本示例展示了如何使用長短期記憶(LSTM)網絡對序列數據進行分類。
%要訓練一個用于分類序列數據的深度神經網絡,您可以使用 LSTM 神經網絡。LSTM 神經網絡允許您將序列數據輸入到網絡中,并基于序列數據的各個時間步進行預測。%這個示例使用了波形數據集。本示例通過訓練一個長短期記憶(LSTM)神經網絡來識別給定時間序列數據的波形類型。訓練數據包含四種波形的時間序列數據。每個序列具有三個通道,且長度各不相同。%Load Sequence Data
%從 WaveformData 加載示例數據。序列數據是一個 numObservations×1 的單元數組,其中 numObservations 是序列的數量。每個序列都是一個 numTimeSteps×numChannels 的數值數組,其中 numTimeSteps 是序列的時間步數,numChannels 是序列的通道數。標簽數據是一個 numObservations×1 的分類向量。
clear variables
load WaveformData
%展示數據
numChannels = size(data{1},2);idx = [3 4 5 12];
figure
tiledlayout(2,2)
for i = 1:4nexttilestackedplot(data{idx(i)},DisplayLabels="Channel "+string(1:numChannels))xlabel("Time Step")title("Class: " + string(labels(idx(i))))
end
classNames = categories(labels)
%留出部分數據進行測試。將數據劃分為訓練集和測試集,其中訓練集包含 90% 的數據,測試集包含剩余的 10% 數據。要使用 trainingPartitions 函數來劃分數據,該函數作為本示例的輔助文件附加提供。要訪問此文件,請將示例作為實時腳本打開。
numObservations = numel(data);
[idxTrain,idxTest] = trainingPartitions(numObservations,[0.9 0.1]);
XTrain = data(idxTrain);
TTrain = labels(idxTrain);XTest = data(idxTest);
TTest = labels(idxTest);%Prepare Data for Padding
%數據填充工作
%在訓練過程中,軟件默認會將訓練數據拆分為小批量,并對序列進行填充,使它們具有相同的長度。過多的填充可能會對網絡性能產生負面影響。
%為了防止訓練過程添加過多的填充,您可以按序列長度對訓練數據進行排序,并選擇一個適當的小批量大小,以便一個小批量中的序列具有相似的長度。下圖顯示了數據排序前后對序列進行填充的效果。%Get the sequence lengths for each observation.
numObservations = numel(XTrain);
for i=1:numObservationssequence = XTrain{i};sequenceLengths(i) = size(sequence,1);
end
Sort the data by sequence length.
[sequenceLengths,idx] = sort(sequenceLengths);
XTrain = XTrain(idx);
TTrain = TTrain(idx);
%View the sorted sequence lengths in a bar chart.
figure
bar(sequenceLengths)
xlabel("Sequence")
ylabel("Length")
title("Sorted Data")%Define LSTM Neural Network Architecture
%定義LSTM神經網絡架構如下:
%指定輸入大小:
%輸入大小設定為輸入數據的通道數。
%雙向LSTM層:
%使用一個包含120個隱藏單元的雙向LSTM層。雙向LSTM層能夠在每個時間步長上從序列的前向和后向兩個方向進行學習。
%設置輸出為序列的最后一個元素。
%全連接層:
%添加一個全連接層,其輸出大小與類別數相匹配。
%softmax層:
%在全連接層之后,添加一個softmax層,以將輸出轉換為概率分布。
%如果你在預測時能夠訪問完整序列(例如,在處理靜態數據集或進行批處理預測時),則可以在網絡中使用雙向LSTM層。雙向LSTM層能夠在每個時間步長上利用整個序列的信息。如果你無法在預測時訪問完整序列(例如,在進行時間序列預測或逐步預測時),則應使用單向LSTM層代替。
%簡而言之,這段描述定義了一個LSTM神經網絡,該網絡包括一個與輸入數據通道數相匹配的輸入層、一個雙向LSTM層(或根據需要替換為單向LSTM層)、一個全連接層和一個softmax層。
numHiddenUnits = 120;
numClasses = 4;layers = [sequenceInputLayer(numChannels)bilstmLayer(numHiddenUnits,OutputMode="last")fullyConnectedLayer(numClasses)softmaxLayer]%Specify Training Options
%指定訓練選項。選擇哪個選項需要進行實證分析。為了通過實驗探索不同的訓練選項配置,可以使用實驗管理器應用程序。
%使用Adam求解器進行訓練。
%訓練200個周期。
%指定學習率為0.002。
%將梯度裁剪閾值設置為1。
%為了保持序列按長度排序,禁用洗牌功能。
%在圖中顯示訓練進度,并監控準確率。
%禁用詳細輸出。
options = trainingOptions("adam", ...MaxEpochs=200, ...InitialLearnRate=0.002,...GradientThreshold=1, ...Shuffle="never", ...%注意這里不要亂序訓練Plots="training-progress", ...minibatchsize=64,...Metrics="accuracy", ...Verbose=false,...ValidationData={XTest,TTest},...ValidationFrequency=50);%Train LSTM Neural Networknet = trainnet(XTrain,TTrain,layers,"crossentropy",options);%Test LSTM Neural Network
%對測試數據進行分類,并計算預測的分類準確率。
%LSTM神經網絡net是使用相似長度的序列組成的小批量數據進行訓練的。請確保測試數據以相同的方式組織。按序列長度對測試數據進行排序。
numObservationsTest = numel(XTest);
for i=1:numObservationsTestsequence = XTest{i};sequenceLengthsTest(i) = size(sequence,1);
end[sequenceLengthsTest,idx] = sort(sequenceLengthsTest);
XTest = XTest(idx);
TTest = TTest(idx);
%對測試數據進行分類,并計算預測的分類準確率。
%使用minibatchpredict函數進行預測,并使用scores2label函數將得分轉換為標簽。
scores = minibatchpredict(net,XTest);
YTest = scores2label(scores,classNames);
Calculate the classification accuracy. The accuracy is the percentage of correctly correctly predicted labels.
acc = mean(YTest == TTest)
%Display the classification results in a confusion chart.
figure
confusionchart(TTest,YTest)
子函數
function varargout = trainingPartitions(numObservations,splits)
%TRAININGPARTITIONS 用于拆分訓練數據的隨機索引
% [idx1,...,idxN] = trainingPartitions(numObservations,splits) 返回
% 隨機索引向量,以幫助拆分具有指定觀察數量的數據集,其中 SPLITS 是一個
% 長度為 N 的分區大小向量,且其元素之和為 1。
%
% % 示例:獲取500個觀察值的50%-50%訓練-測試拆分的索引。
% [idxTrain,idxTest] = trainingPartitions(500,[0.5 0.5])
%
% % 示例:獲取500個觀察值的80%-10%-10%訓練、驗證、測試拆分的索引。
% [idxTrain,idxValidation,idxTest] = trainingPartitions(500,[0.8 0.1 0.1])argumentsnumObservations (1,1) {mustBePositive}splits {mustBeVector,mustBeInRange(splits,0,1,"exclusive"),mustSumToOne}
endnumPartitions = numel(splits);
varargout = cell(1,numPartitions);idx = randperm(numObservations);idxEnd = 0;for i = 1:numPartitions-1idxStart = idxEnd + 1;idxEnd = idxStart + floor(splits(i)*numObservations) - 1;varargout{i} = idx(idxStart:idxEnd);
end% Last partition.
varargout{end} = idx(idxEnd+1:end);endfunction mustSumToOne(v)
% Validate that value sums to one.if sum(v,"all") ~= 1error("Value must sum to one.")
endend
?訓練結果:
最后,如果您覺得這篇文章寫得好,對您有幫助,麻煩給個寶貴的贊支持我繼續創作(^v^)