間序列預(yù)測(cè)對(duì)比實(shí)戰(zhàn))
簡(jiǎn)介面向畢業(yè)設(shè)計(jì)與期末大作業(yè)的Python時(shí)間序列預(yù)測(cè)源碼完整覆蓋ARIMA、Transformer、LSTM三種模型的搭建、訓(xùn)練與對(duì)比。方案基于MIT-BIH公開心跳數(shù)據(jù)包含310組記錄及預(yù)處理結(jié)果代碼附詳細(xì)注釋部署簡(jiǎn)單適合希望復(fù)用高分項(xiàng)目或系統(tǒng)掌握時(shí)序預(yù)測(cè)流程的高校學(xué)生。資源共1179個(gè)文件壓縮包約19.85MB核心類型包括心跳數(shù)據(jù)文件.hea/.dat/.atr、Python腳本.py、Jupyter Notebook分析筆記.ipynb、可視化圖表.png、模型權(quán)重.pth與說(shuō)明文檔.md/README目錄結(jié)構(gòu)清晰便于按模塊檢索和二次開發(fā)。目前已有225人學(xué)習(xí)下載。項(xiàng)目經(jīng)過(guò)嚴(yán)格調(diào)試可直接用于畢業(yè)設(shè)計(jì)、期末大作業(yè)或課程設(shè)計(jì)除完整源碼與數(shù)據(jù)集外還提供對(duì)比實(shí)驗(yàn)圖表、代碼注釋和文檔說(shuō)明能幫助新手理解三種算法的預(yù)測(cè)差異、復(fù)現(xiàn)關(guān)鍵步驟并降低環(huán)境配置與調(diào)參門檻提升答辯和提交準(zhǔn)備效率。1. 為什么心跳時(shí)間序列預(yù)測(cè)要把 ARIMA、Transformer、LSTM 擺在一起比心跳心電/脈搏時(shí)間序列預(yù)測(cè)是很多可穿戴健康分析和醫(yī)學(xué)數(shù)據(jù)處理任務(wù)里的前置步驟先預(yù)測(cè)未來(lái)一段波形再做異常形態(tài)匹配或節(jié)律判斷。這類“基于ARIMA、Transformer、LSTM對(duì)心跳時(shí)間序列數(shù)據(jù)進(jìn)行預(yù)測(cè)”的項(xiàng)目做的事本質(zhì)上不是選一個(gè)模型跑通而是用統(tǒng)計(jì)模型、循環(huán)網(wǎng)絡(luò)、注意力機(jī)制三種路線對(duì)同一段生理信號(hào)做閉環(huán)對(duì)比評(píng)測(cè)搞清楚數(shù)據(jù)里哪些是周期規(guī)律、哪些是隨機(jī)噪聲、哪些能被模型真正學(xué)到——這個(gè)結(jié)論會(huì)直接影響后續(xù)模型選型和部署方式。對(duì)從業(yè)者來(lái)說(shuō)這類項(xiàng)目的價(jià)值很實(shí)在它覆蓋了從數(shù)據(jù)清洗、滑窗切片、模型訓(xùn)練到統(tǒng)一評(píng)估的完整技術(shù)棧。新手可以用它建立時(shí)序預(yù)測(cè)的選型直覺(jué)熟手則能用它快速驗(yàn)證新數(shù)據(jù)集的“可預(yù)測(cè)性上限”。常見(jiàn)做法是取公開的心電數(shù)據(jù)集或直接使用可穿戴設(shè)備采集的脈搏波數(shù)據(jù)把原始信號(hào)重采樣到固定頻率后分別用三種模型預(yù)測(cè)未來(lái) 1 秒的心跳波形。下文按數(shù)據(jù)工程、ARIMA 基線、LSTM、Transformer、統(tǒng)一評(píng)估與實(shí)戰(zhàn)技巧的順序展開每一步都給出可復(fù)現(xiàn)的參數(shù)和代碼。2. 心跳序列的數(shù)據(jù)工程從原始波形到模型能吃的樣本2.1 數(shù)據(jù)準(zhǔn)備與特征選擇心電數(shù)據(jù)并不是拿來(lái)就能跑常見(jiàn)做法是使用公開心電圖數(shù)據(jù)集或可穿戴設(shè)備采集的脈搏波截取單導(dǎo)聯(lián)的心電信號(hào)通常用 MLII 導(dǎo)聯(lián)或等效單通道并做 128Hz 或 250Hz 的統(tǒng)一重采樣。模型能看懂的輸入是三段式過(guò)去一段觀察窗口的心跳波形以及對(duì)應(yīng)的未來(lái)心跳波形。特征上注意直接使用歸一化后的原始幅值即可不必過(guò)多構(gòu)造手工特征心電信號(hào)本身波形形態(tài)已經(jīng)足夠豐富額外堆砌統(tǒng)計(jì)特征反而會(huì)干擾深度模型的學(xué)習(xí)。我一般會(huì)先用連續(xù) 10 秒的完整信號(hào)做切片。觀察窗口設(shè) 5 秒、預(yù)測(cè)窗口設(shè) 1 秒這種比例對(duì) R 波周期相對(duì)規(guī)律的數(shù)據(jù)很合適。如果數(shù)據(jù)里有較多早搏或運(yùn)動(dòng)偽跡可以把預(yù)測(cè)窗口縮到 0.5 秒降低任務(wù)難度。數(shù)據(jù)集劃分也要按病人或時(shí)段切避免同一病人的不同片段同時(shí)出現(xiàn)在訓(xùn)練集和測(cè)試集里否則模型對(duì)特定病人的“記憶”會(huì)直接影響評(píng)估結(jié)果部署時(shí)表現(xiàn)會(huì)大打折扣。心跳序列是強(qiáng)周期信號(hào)但幅度和間期并不固定。數(shù)據(jù)增強(qiáng)方面我通常會(huì)加入輕微的時(shí)間縮放每段隨機(jī)縮放 0.98~1.02但絕不在心電幅值上做隨機(jī)平移因?yàn)槟菢拥扔诎研碾姷幕€漂移當(dāng)成特征教給模型實(shí)際預(yù)測(cè)時(shí)反而會(huì)放大誤差。最終每個(gè)樣本是[batch, seq_len, 1]的張量喂給 LSTM 和 TransformerARIMA 則吃原始的一維數(shù)值序列。2.2 切片、歸一化與數(shù)據(jù)集劃分的標(biāo)準(zhǔn)流程把原始信號(hào)切成可監(jiān)督學(xué)習(xí)的樣本時(shí)通常會(huì)寫成下面的 Python 函數(shù)。這一步是整個(gè)項(xiàng)目的基石切出來(lái)的數(shù)據(jù)質(zhì)量直接決定三個(gè)模型的天花板。import numpy as np def make_windows(signal, fs128, obs_sec5, pred_sec1, step1): obs_len int(fs * obs_sec) # 觀察窗口640個(gè)點(diǎn) pred_len int(fs * pred_sec) # 預(yù)測(cè)窗口128個(gè)點(diǎn) X, y [], [] for i in range(0, len(signal) - obs_len - pred_len, fs * step): x signal[i:i obs_len] t signal[i obs_len:i obs_len pred_len] # 對(duì)每個(gè)窗口做z-score避免不同樣本之間的幅值差異 mu, sigma x.mean(), x.std() 1e-6 X.append((x - mu) / sigma) y.append((t - mu) / sigma) # 注意用觀察窗口的均值/方差防止未來(lái)信息泄漏 return np.array(X)[..., None], np.array(y)[..., None]代碼里有一個(gè)關(guān)鍵點(diǎn)歸一化的均值和方差取自觀察窗口而不是預(yù)測(cè)窗口。如果對(duì)整段數(shù)據(jù)先全局歸一化再切片測(cè)試時(shí)會(huì)用到未來(lái)的統(tǒng)計(jì)量屬于典型的信息泄漏。這種泄漏在深度模型里不容易馬上暴露但會(huì)嚴(yán)重抬高測(cè)試集指標(biāo)到了真實(shí)部署階段模型立刻被打回原形——這一點(diǎn)務(wù)必留意。參數(shù)上step1表示窗口每次移動(dòng) 1 秒適合數(shù)據(jù)量不大時(shí)做滑窗復(fù)用如果數(shù)據(jù)量充足我會(huì)把 step 改成 2 或 3減少相鄰樣本的相關(guān)性。否則訓(xùn)練集和驗(yàn)證集幾乎重疊驗(yàn)證損失會(huì)異常好看但毫無(wú)參考價(jià)值等于在“開卷考試”。2.3 ARIMA、LSTM、Transformer 對(duì)輸入形態(tài)的不同要求三個(gè)模型的輸入側(cè)差異很大。ARIMA 吃一維數(shù)值序列不需要切片成窗口對(duì)直接用觀察窗口內(nèi)的 640 個(gè)點(diǎn)去滾動(dòng)預(yù)測(cè)后面 128 個(gè)點(diǎn)LSTM 需要三維輸入[batch, time_steps, features]Transformer 的輸入和 LSTM 類似但一般要在 token 維度上先做一次線性映射把單個(gè)幅值點(diǎn)映射成 d_model 維向量然后加上位置編碼。在 LSTM 和 Transformer 的輸入上是否需要特征數(shù)大于 1 的多通道如果做的是單導(dǎo)聯(lián)心跳預(yù)測(cè)一個(gè)幅值特征足夠了。強(qiáng)行加入心率、呼吸等外部變量會(huì)讓模型學(xué)到的組合關(guān)系在測(cè)試時(shí)失效因?yàn)樵O(shè)備更換或佩戴位置變化后這些外部變量的分布會(huì)漂移。這類項(xiàng)目里單通道入門、多通道做擴(kuò)展才是正常路徑。此外我建議把觀察窗口的點(diǎn)數(shù)和下游模型結(jié)構(gòu)一起考慮如果采樣率是 128Hz640 個(gè)點(diǎn)對(duì) LSTM 來(lái)說(shuō)屬于中等長(zhǎng)度可以直接輸入如果采樣率提升到 1000Hz觀察窗口變成 5000 個(gè)點(diǎn)LSTM 訓(xùn)練速度會(huì)明顯下降這時(shí)應(yīng)該先用卷積層降采樣或按心拍邊界切塊而不是硬著頭皮把全部原始點(diǎn)直接喂進(jìn)去。3. 基于 ARIMA 的統(tǒng)計(jì)基線先看傳統(tǒng)方法的上限3.1 為什么 ARIMA 仍是心跳序列預(yù)測(cè)的合格基線雖然 ARIMA 沒(méi)有顯式的周期建模能力但它通過(guò)差分和自回歸項(xiàng)能捕捉短時(shí)間窗口內(nèi)連續(xù)的波形走向。尤其預(yù)測(cè)未來(lái) 0.5 到 1 秒的波形時(shí)ARIMA 的結(jié)果通??勺鳛樯疃葘W(xué)習(xí)模型是否真正有效的參照物。很多深度模型跑出來(lái)的漂亮指標(biāo)對(duì)照 ARIMA 基線后會(huì)發(fā)現(xiàn)只是“噪聲擬合”。判斷心電數(shù)據(jù)是否適合 ARIMA核心是看差分后的序列是否平穩(wěn)做法是 ADF 檢驗(yàn)。多數(shù)心電信號(hào)經(jīng)過(guò)一階差分后能通過(guò)檢驗(yàn)若不行就需要用二階差分。但注意差分階數(shù)過(guò)高會(huì)讓模型把噪聲也當(dāng)成規(guī)律學(xué)進(jìn)去預(yù)測(cè)結(jié)果會(huì)出現(xiàn)嚴(yán)重的鋸齒狀抖動(dòng)。對(duì)心跳數(shù)據(jù)d 設(shè)為 1 或 2 就夠。ARIMA 在這類項(xiàng)目的定位是基線和“可解釋對(duì)照”。深度學(xué)習(xí)模型如果連 ARIMA 都無(wú)法穩(wěn)定超過(guò)說(shuō)明數(shù)據(jù)切分或訓(xùn)練流程可能有 bug而不是模型能力問(wèn)題。這個(gè)判斷聽起來(lái)武斷但在不少實(shí)測(cè)項(xiàng)目中確實(shí)管用ARIMA 差深度模型也好不到哪去ARIMA 好深度模型才有發(fā)力空間。這也解釋了為什么這類項(xiàng)目里 ARIMA 幾乎是必備對(duì)照組。3.2 用 statsmodels 快速構(gòu)建心跳 ARIMA 模型并做滾動(dòng)預(yù)測(cè)使用 statsmodels 庫(kù)能快速搭建 ARIMA 模型常見(jiàn)代碼如下。這是整套項(xiàng)目里最輕量的一段邏輯也是后續(xù)評(píng)估的“最低水位線”。from statsmodels.tsa.arima.model import ARIMA import numpy as np pred_len 128 # 預(yù)測(cè)未來(lái)1秒(128Hz) history list(train_signal[:640]) # 觀察窗口5秒 predictions [] for t in range(len(train_signal[640:640pred_len])): model ARIMA(history, order(2, 1, 2)) fit model.fit(methodinnovations_mle, trendc) pred fit.forecast(steps1)[0] predictions.append(pred) history.append(train_signal[640t])這段代碼是典型的滾動(dòng)預(yù)測(cè)每預(yù)測(cè)一個(gè)點(diǎn)把真實(shí)觀測(cè)值回填到歷史里再進(jìn)行下一輪預(yù)測(cè)。對(duì)心電這種準(zhǔn)周期信號(hào)單步滾動(dòng)預(yù)測(cè)比直接預(yù)測(cè) 128 個(gè)點(diǎn)要穩(wěn)得多原因在于每一步都校正了誤差不會(huì)讓誤差沿著預(yù)測(cè)窗口一路累積。但要注意代碼里每次循環(huán)都重新擬合了 ARIMA 模型這在 128 步預(yù)測(cè)里耗時(shí)尚可如果預(yù)測(cè)窗口更長(zhǎng)或數(shù)據(jù)量大效率會(huì)成為嚴(yán)重問(wèn)題。這時(shí)候常見(jiàn)做法是每 10 步或 20 步重新擬合一次中間步驟直接用已有模型滾動(dòng)預(yù)測(cè)雖然誤差會(huì)稍微累積但速度提升非常明顯。實(shí)際參數(shù)選擇上methodinnovations_mle比默認(rèn)的statespace在小樣本上更穩(wěn)定遇到收斂警告時(shí)可以換回后者對(duì)比。3.3 ARIMA 的階數(shù)選擇與心跳場(chǎng)景下的邊界ARIMA 的參數(shù)p、d、q通常通過(guò) AIC 網(wǎng)格搜索選定。對(duì)心電序列我一般把 p 和 q 的搜索范圍放在 0~3d 固定在 1 或 2。更復(fù)雜的心電特征如早搏、房顫可能讓搜索到的 p、q 偏大而模型會(huì)變得不穩(wěn)定預(yù)測(cè)結(jié)果一旦波動(dòng)過(guò)大就要回到簡(jiǎn)單階數(shù)。在心跳數(shù)據(jù)上“簡(jiǎn)單模型 穩(wěn)定輸出”比“復(fù)雜模型 偶爾爆炸”重要得多。還有一點(diǎn)容易被忽略ARIMA 對(duì)缺失值很敏感。心跳數(shù)據(jù)常因?yàn)樵O(shè)備脫落或運(yùn)動(dòng)偽跡出現(xiàn) NaN如果直接喂給 ARIMA擬合階段會(huì)直接報(bào)錯(cuò)或輸出異常值。通常做法是先用線性插值或前后向填充補(bǔ)齊缺口再進(jìn)行 ARIMA 擬合。如果缺失段太長(zhǎng)超過(guò) 2 秒建議直接丟棄該段信號(hào)不要硬補(bǔ)否則會(huì)把插值產(chǎn)生的虛假波形當(dāng)成真實(shí)規(guī)律學(xué)進(jìn)去。ARIMA 適合做整個(gè)項(xiàng)目評(píng)估的標(biāo)準(zhǔn)基線但它的明顯邊界是不能捕捉心率變異性中的長(zhǎng)程依賴也不能對(duì)多步預(yù)測(cè)給出穩(wěn)定輸出誤差隨時(shí)間累積。所以它不會(huì)是最終方案但它能快速幫你確認(rèn)數(shù)據(jù)是否存在明顯的趨勢(shì)異常并給深度學(xué)習(xí)模型劃定一個(gè)最低標(biāo)準(zhǔn)——后續(xù) LSTM 和 Transformer 的表現(xiàn)都要以它為參照系。4. 基于 LSTM 的循環(huán)預(yù)測(cè)模型把時(shí)序記憶用起來(lái)4.1 LSTM 為什么會(huì)成為心電時(shí)間序列預(yù)測(cè)的默認(rèn)選擇LSTM 在心跳預(yù)測(cè)中成為默認(rèn)選擇是因?yàn)樗瞄T控機(jī)制解決了長(zhǎng)序列訓(xùn)練時(shí)的梯度消失問(wèn)題。心跳序列的每個(gè)波形是有記憶的下一個(gè)波形的形態(tài)往往取決于前面的多個(gè)心動(dòng)周期。LSTM 通過(guò)輸入門、遺忘門、輸出門把“上一個(gè)心跳的形態(tài)”這種信息保留在細(xì)胞狀態(tài)里從而做到基于周期記憶的預(yù)測(cè)。在結(jié)構(gòu)上絕大多數(shù)項(xiàng)目采用雙層 LSTM 加全連接輸出層。第一層 LSTM 學(xué)習(xí)波形段的短時(shí)依賴第二層進(jìn)一步抽象出周期模式。我一般會(huì)把第一層的 return_sequences 設(shè)為 True第二層只返回最后一個(gè)時(shí)間步的隱狀態(tài)再接 Dense 層輸出預(yù)測(cè)窗口長(zhǎng)度。這樣做比單層 LSTM 效果好而且參數(shù)量增加不多訓(xùn)練成本可控。LSTM 輸入是[batch, seq_len, 1]的窗口樣本。如果觀察窗口是 640 個(gè)點(diǎn)直接把這 640 個(gè)點(diǎn)全部喂給 LSTM 并不算長(zhǎng)對(duì) LSTM 來(lái)說(shuō) 640 步屬于中等長(zhǎng)度但如果采樣率提高到 1000Hz觀察窗口點(diǎn)數(shù)是原來(lái)的 4 倍就應(yīng)該考慮先用卷積層降采樣或把序列切分成較小的塊否則訓(xùn)練速度會(huì)明顯下降顯存占用也會(huì)成倍增加。4.2 搭建并訓(xùn)練一個(gè)心跳波形 LSTM 預(yù)測(cè)模型以 PyTorch 為例搭建一個(gè)雙層 LSTM 模型并完成訓(xùn)練閉環(huán)代碼結(jié)構(gòu)如下。這是整個(gè)項(xiàng)目里最核心的可復(fù)用代碼段之一。import torch import torch.nn as nn class HeartLSTM(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, pred_len128): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropout0.2) self.head nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, pred_len) ) def forward(self, x): # x: [batch, seq_len, 1] out, _ self.lstm(x) # 取最后時(shí)間步的輸出 out out[:, -1, :] # [batch, hidden_size] return self.head(out)這里有個(gè)容易踩坑的地方LSTM 的 dropout 參數(shù)dropout0.2只在num_layers 1時(shí)生效且它作用于層與層之間不是時(shí)間步之間。如果為了提效果而把 dropout 加在輸出層之前要自己插入 Dropout 層而不是靠 LSTM 的參數(shù)。這是 PyTorch 文檔里寫明但很多人忽略的細(xì)節(jié)。訓(xùn)練參數(shù)上我一般用 AdamW 優(yōu)化器、學(xué)習(xí)率 1e-3 配合 cosine 衰減batch size 64損失函數(shù)用 HuberLossSmooth L1。它對(duì)心電波形中偶爾出現(xiàn)的異常峰不敏感不會(huì)像 MSE 那樣被個(gè)別離群點(diǎn)帶偏。訓(xùn)練輪數(shù)不建議太多心電數(shù)據(jù)增強(qiáng)手段有限跑 30 到 50 個(gè) epoch 之后基本就能看到驗(yàn)證損失是否觸底再多就容易過(guò)擬合。4.3 LSTM 在心跳預(yù)測(cè)中的預(yù)測(cè)步數(shù)與誤差累積問(wèn)題LSTM 直接輸出未來(lái) 128 個(gè)點(diǎn)訓(xùn)練時(shí)是 teacher forcing 的預(yù)測(cè)時(shí)卻是一次性給出 128 個(gè)點(diǎn)這個(gè)“訓(xùn)練-預(yù)測(cè)不一致”是 LSTM 模型最常見(jiàn)的誤差來(lái)源。雖然它并不像 ARIMA 那樣逐點(diǎn)滾動(dòng)但本質(zhì)上仍要面對(duì)長(zhǎng)期預(yù)測(cè)誤差累積的問(wèn)題只是發(fā)生位置從推理階段挪到了訓(xùn)練階段。緩解辦法有兩種第一種是 Scheduled Sampling訓(xùn)練時(shí)以概率 p 用模型自己的預(yù)測(cè)值代替真實(shí)值作為下一步輸入p 隨訓(xùn)練進(jìn)度衰減。第二種是把模型改成 seq2seq 結(jié)構(gòu)編碼器讀入觀察窗口解碼器逐步輸出預(yù)測(cè)點(diǎn)每一步把上一步的輸出作為輸入。對(duì)心跳預(yù)測(cè)項(xiàng)目第二種更容易落地因?yàn)椴ㄐ晤A(yù)測(cè)本質(zhì)上是逐點(diǎn)生成的不是一次性回歸。我一般會(huì)優(yōu)先嘗試 LSTM 的多步直接輸出如果驗(yàn)證集誤差明顯高于訓(xùn)練集誤差再切到 seq2seq 結(jié)構(gòu)。在實(shí)際數(shù)據(jù)上單步誤差通常能壓得很低但多步誤差會(huì)隨步數(shù)明顯上升所以對(duì)比 ARIMA、Transformer 時(shí)要統(tǒng)一在同樣的預(yù)測(cè)窗口下評(píng)估不能用不同步數(shù)量級(jí)的結(jié)果互相比較。5. 基于 Transformer 的注意力預(yù)測(cè)長(zhǎng)程依賴與并行訓(xùn)練5.1 Transformer 在時(shí)間序列預(yù)測(cè)任務(wù)里的定位Transformer 原本用在自然語(yǔ)言處理上核心是自注意力機(jī)制。它在心跳預(yù)測(cè)上的優(yōu)勢(shì)有兩點(diǎn)一是能直接建模長(zhǎng)距離依賴心跳波形中相距很遠(yuǎn)的心動(dòng)周期也能通過(guò)注意力建立關(guān)聯(lián)二是訓(xùn)練時(shí)可并行處理整個(gè)序列不像 LSTM 必須逐步計(jì)算這在觀察窗口較長(zhǎng)時(shí)能明顯提升訓(xùn)練速度。但 Transformer 也有代價(jià)它沒(méi)有內(nèi)置的時(shí)間順序概念必須加位置編碼才能讓模型感知波形點(diǎn)的先后。常見(jiàn)做法是使用三角函數(shù)位置編碼因?yàn)樗南鄬?duì)位置信息可以通過(guò)線性變換表達(dá)。對(duì)心跳序列來(lái)說(shuō)周期性強(qiáng)三角函數(shù)位置編碼能很好地編碼相對(duì)距離但具體效果還是需要用實(shí)驗(yàn)來(lái)驗(yàn)證不能想當(dāng)然。在輸入形態(tài)上Transformer 通常不直接吃原始采樣點(diǎn)而是先做 token 映射。常見(jiàn)操作是把 5 秒的觀察窗口640 個(gè)點(diǎn)按 16 或 32 個(gè)點(diǎn)切塊每個(gè)塊是一個(gè) token經(jīng)過(guò)線性映射成 d_model 維向量再加上位置編碼才進(jìn)入 Transformer 編碼器。這樣做的原因是單個(gè)心拍大約 0.8 秒切塊后的 token 能更好地表達(dá)一個(gè)個(gè)局部波形片段而不是讓模型在單個(gè)采樣點(diǎn)上浪費(fèi)注意力。5.2 PyTorch 實(shí)現(xiàn)一個(gè)輕量 Transformer 編碼器并用于心電預(yù)測(cè)直接構(gòu)造 Transformer 的編碼器部分作為預(yù)測(cè)模型實(shí)現(xiàn)代碼大致如下。這里的結(jié)構(gòu)是后續(xù)調(diào)參的主戰(zhàn)場(chǎng)。import torch import torch.nn as nn import math class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len1000): super().__init__() pe torch.zeros(max_len, d_model) pos torch.arange(0, max_len).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)) pe[:, 0::2] torch.sin(pos * div_term) pe[:, 1::2] torch.cos(pos * div_term) self.register_buffer(pe, pe.unsqueeze(0)) def forward(self, x): return x self.pe[:, :x.size(1)] class HeartTransformer(nn.Module): def __init__(self, d_model64, nhead4, num_layers2, pred_len128): super().__init__() self.token_embed nn.Linear(16, d_model) self.pos_enc PositionalEncoding(d_model) encoder_layer nn.TransformerEncoderLayer(d_model, nhead, dim_feedforward128, batch_firstTrue, dropout0.1) self.encoder nn.TransformerEncoder(encoder_layer, num_layers) self.head nn.Linear(d_model, pred_len) def forward(self, x): # x: [batch, 640, 1] - 切塊為 [batch, 40, 16] b, seq, _ x.shape x x.reshape(b, seq // 16, 16) # 40個(gè)token x self.token_embed(x) # [batch, 40, d_model] x self.pos_enc(x) x self.encoder(x) x x.mean(dim1) # 全局平均池化 return self.head(x)這段代碼里d_model64、nhead4、num_layers2是我常用的一組輕量參數(shù)。心電數(shù)據(jù)量通常不大參數(shù)過(guò)多會(huì)過(guò)擬合。注意token_embed把每個(gè) 16 點(diǎn)的塊映射到 d_model 維而不是一個(gè)點(diǎn)一個(gè)點(diǎn)地進(jìn)入注意力機(jī)制否則注意力的計(jì)算復(fù)雜度跟序列長(zhǎng)度的平方成正比訓(xùn)練會(huì)非常慢。位置編碼這里用的是經(jīng)典的三角函數(shù)版本register_buffer保證它不會(huì)作為模型參數(shù)被訓(xùn)練。如果換用可學(xué)習(xí)位置編碼在小數(shù)據(jù)集上反而容易過(guò)擬合因?yàn)槲恢眯畔⒈划?dāng)作可訓(xùn)練的偏置項(xiàng)模型會(huì)記住訓(xùn)練集里的波形相位而不是學(xué)到通用的相對(duì)位置關(guān)系。5.3 Transformer 在心跳任務(wù)上的數(shù)據(jù)量與調(diào)參要點(diǎn)Transformer 在心跳項(xiàng)目上最大的問(wèn)題是數(shù)據(jù)量需求比 LSTM 更大。自注意力機(jī)制參數(shù)共享程度低自由度更高同樣的數(shù)據(jù)量下更容易過(guò)擬合。緩解手段除了加 dropout還可以用數(shù)據(jù)增強(qiáng)、更小的 d_model、更淺的結(jié)構(gòu)。數(shù)據(jù)量只有幾千個(gè)樣本時(shí)不建議一上來(lái)就堆參數(shù)量。具體到心跳序列nhead4比nhead8在小數(shù)據(jù)上更穩(wěn)定。num_layers2足夠超過(guò) 4 層在幾千個(gè)樣本規(guī)模下幾乎必過(guò)擬合。學(xué)習(xí)率方面Transformer 對(duì)學(xué)習(xí)率更敏感我一般先用 1e-4 起步配合 warmup 策略前 10 個(gè) step 從 1e-6 線性升到 1e-4再用 cosine 退火。這個(gè)細(xì)節(jié)經(jīng)常決定 Transformer 是否收斂很多復(fù)現(xiàn)失敗都卡在固定學(xué)習(xí)率這里。此外心電信號(hào)是強(qiáng)周期信號(hào)Transformer 需要明確地利用周期信息。如果只用位置編碼而不做任何周期先驗(yàn)?zāi)P鸵材軐W(xué)但需要更多數(shù)據(jù)。我在實(shí)際項(xiàng)目中會(huì)把單個(gè)心拍長(zhǎng)度作為先驗(yàn)知識(shí)把觀察窗口按心拍分成若干段段內(nèi)用 Transformer 編碼器建模細(xì)節(jié)段間用簡(jiǎn)單平均池化這樣預(yù)測(cè)效果往往比純 Transformer 更穩(wěn)——這種帶先驗(yàn)的結(jié)構(gòu)設(shè)計(jì)在小數(shù)據(jù)場(chǎng)景下非常管用。6. 統(tǒng)一評(píng)測(cè)框架與避坑指南6.1 統(tǒng)一評(píng)測(cè)框架三個(gè)模型要在同一把尺子下比較在 ARIMA、LSTM、Transformer 三者的比較上最常見(jiàn)的問(wèn)題是評(píng)估方式不一致。ARIMA 用單步滾動(dòng)預(yù)測(cè)LSTM 用一次性多步輸出Transformer 用多步輸出最后放在同一個(gè)圖表里卻沒(méi)有標(biāo)注各自的任務(wù)難度這種對(duì)比毫無(wú)說(shuō)服力。統(tǒng)一的評(píng)測(cè)方式是三個(gè)模型全都預(yù)測(cè)未來(lái) 128 個(gè)點(diǎn)1 秒波形用同樣長(zhǎng)度的觀察窗口640 個(gè)點(diǎn)評(píng)估指標(biāo)用 RMSE、MAE 以及波形形態(tài)相似度如動(dòng)態(tài)時(shí)間規(guī)整 DTW 距離。RMSE 對(duì)峰值敏感MAE 對(duì)整體偏移敏感DTW 則能衡量波形“形狀”是否對(duì)得上。推薦把三項(xiàng)都算出來(lái)如果 RMSE 低但 DTW 高說(shuō)明波形偏移了半個(gè)相位形態(tài)不對(duì)這樣的預(yù)測(cè)在醫(yī)學(xué)場(chǎng)景里是不能用的。評(píng)估時(shí)還要按病人或時(shí)間段劃分?jǐn)?shù)據(jù)集。常見(jiàn)做法是把前 70% 的數(shù)據(jù)按時(shí)間順序作為訓(xùn)練集中間 15% 做驗(yàn)證集最后 15% 做測(cè)試集。如果數(shù)據(jù)來(lái)自多個(gè)個(gè)體必須保證每個(gè)個(gè)體的數(shù)據(jù)只出現(xiàn)在一個(gè)集合里這是心電數(shù)據(jù)處理中最重要的評(píng)估紀(jì)律。違反這條紀(jì)律的評(píng)估結(jié)果換個(gè)體數(shù)據(jù)后基本都會(huì)崩。6.2 訓(xùn)練與評(píng)估中的主要踩坑記錄我在這類預(yù)測(cè)項(xiàng)目里踩過(guò)不少坑挑幾個(gè)高頻的分享。問(wèn)題一驗(yàn)證損失很低但可視化預(yù)測(cè)波形完全對(duì)不上相位?,F(xiàn)象是訓(xùn)練曲線非常漂亮RMSE 也低但把預(yù)測(cè)波形和真實(shí)波形疊在一起看峰和谷完全錯(cuò)位。原因是驗(yàn)證集與訓(xùn)練集切片重疊太多滑窗步長(zhǎng)太小時(shí)相鄰樣本高度相關(guān)模型等于在“開卷考試”。解決方法是增大滑窗間隔或按時(shí)間段切分驗(yàn)證集確保驗(yàn)證集和訓(xùn)練集沒(méi)有時(shí)間重疊。問(wèn)題二ARIMA 輸出全是上一個(gè)點(diǎn)的延遲復(fù)制?,F(xiàn)象是預(yù)測(cè)曲線比真實(shí)曲線滯后一個(gè)采樣點(diǎn)看起來(lái)幾乎重合但實(shí)際上只是平移。原因是差分階數(shù)不夠序列仍然非平穩(wěn)模型學(xué)到的就是“把最近值平移過(guò)去”。解決方法是先做 ADF 檢驗(yàn)確認(rèn) d 的取值如果仍存在明顯趨勢(shì)考慮對(duì)信號(hào)做高通濾波去除基線漂移再進(jìn)入 ARIMA 擬合。問(wèn)題三LSTM 訓(xùn)練到一半 loss 變成 NaN。常見(jiàn)原因是學(xué)習(xí)率過(guò)大或數(shù)據(jù)里有 NaN。解決方法是先把數(shù)據(jù)中的 NaN 值顯式檢查并補(bǔ)齊再把學(xué)習(xí)率降到 1e-4 試跑 5 個(gè) epoch如果仍然 NaN檢查 LSTM 輸出層是否出現(xiàn)了梯度爆炸必要時(shí)對(duì)梯度做裁剪clip_grad_norm_處理。這個(gè)問(wèn)題的隱藏版本是 loss 不變成 NaN 但開始劇烈震蕩同樣要從學(xué)習(xí)率和數(shù)據(jù)異常兩個(gè)方向排查。問(wèn)題四Transformer 在小數(shù)據(jù)集上嚴(yán)重過(guò)擬合?,F(xiàn)象是訓(xùn)練損失不斷下降驗(yàn)證損失在第 10 個(gè) epoch 后開始上升。解決方法是減小 d_model、加深 dropout、加入早停同時(shí)確認(rèn)注意力頭數(shù)是否過(guò)大4 個(gè)頭一般夠了。如果數(shù)據(jù)量實(shí)在太小應(yīng)該優(yōu)先把 token 切塊變大減少序列長(zhǎng)度降低模型自由度。6.3 模型選型結(jié)論與部署視角的建議在跑完對(duì)比實(shí)驗(yàn)之后我的經(jīng)驗(yàn)是如果預(yù)測(cè)窗口很短0.5 秒以內(nèi)ARIMA 通常夠用深度模型未必有明顯優(yōu)勢(shì)如果預(yù)測(cè)窗口在 1 秒左右LSTM 是性價(jià)比最高的選擇訓(xùn)練穩(wěn)定、參數(shù)少、部署也容易如果數(shù)據(jù)量很大、預(yù)測(cè)窗口更長(zhǎng)且有能力做調(diào)參和訓(xùn)練優(yōu)化Transformer 才有機(jī)會(huì)勝出——但它的優(yōu)勢(shì)并不會(huì)自動(dòng)出現(xiàn)需要做足調(diào)參工作。部署視角上LSTM 的推理速度在 CPU 上已經(jīng)很快1 秒窗口的單次推理大概在幾十毫秒量級(jí)適合實(shí)時(shí)心跳監(jiān)測(cè)Transformer 雖然訓(xùn)練慢但推理時(shí)也可以接受只是模型文件和內(nèi)存占用更大。ARIMA 部署最簡(jiǎn)單不需要深度學(xué)習(xí)框架但滾動(dòng)擬合需要保留狀態(tài)每一步預(yù)測(cè)都要更新歷史數(shù)組時(shí)序性依賴比深度模型更重。7. 實(shí)戰(zhàn)技巧用模型對(duì)比的結(jié)果反推數(shù)據(jù)質(zhì)量與調(diào)參方向這類項(xiàng)目的最終交付物不只是三個(gè)預(yù)測(cè)圖而是一整套可以復(fù)用的評(píng)測(cè)腳本。我會(huì)在項(xiàng)目里額外寫一個(gè)腳本把三個(gè)模型的預(yù)測(cè)結(jié)果和真實(shí)波形疊加可視化逐段打印每個(gè)模型的誤差。這樣做的好處是能直接發(fā)現(xiàn)數(shù)據(jù)切分、預(yù)處理和模型結(jié)構(gòu)上的問(wèn)題而不是對(duì)著總指標(biāo)一頭霧水。具體技巧是把測(cè)試集按心拍邊界切分成獨(dú)立片段逐段對(duì)比三個(gè)模型的誤差。如果 ARIMA 在某些片段上表現(xiàn)得比 LSTM 還好那這些片段很可能是高質(zhì)量、強(qiáng)周期、無(wú)噪聲的而 LSTM 有優(yōu)勢(shì)的片段往往含有輕微的早搏或心率變異。這種“按片段歸因”的做法能幫你快速定位數(shù)據(jù)中哪些模式是模型真正需要的而不是盲目調(diào)參數(shù)。驗(yàn)證階段我習(xí)慣在最終評(píng)估時(shí)再增加一個(gè)“留一對(duì)象”交叉驗(yàn)證每次拿一個(gè)對(duì)象的完整數(shù)據(jù)做測(cè)試集其余對(duì)象的數(shù)據(jù)訓(xùn)練。這樣出來(lái)的指標(biāo)比隨機(jī)切分可靠得多也更接近真實(shí)部署時(shí)的表現(xiàn)。雖然訓(xùn)練次數(shù)會(huì)翻倍但對(duì)心跳預(yù)測(cè)這種小數(shù)據(jù)量任務(wù)來(lái)說(shuō)時(shí)間成本完全可控。另一個(gè)實(shí)用習(xí)慣是保存每個(gè)模型的預(yù)測(cè)殘差序列分析殘差是否存在自相關(guān)。如果 LSTM 或 Transformer 的殘差仍然有明顯的周期性說(shuō)明模型沒(méi)有完全學(xué)到心電的節(jié)律信息通常是因?yàn)橛^察窗口不夠長(zhǎng)或者目標(biāo)窗口里有多個(gè)心拍導(dǎo)致輸出平滑化。此時(shí)把預(yù)測(cè)目標(biāo)從“逐點(diǎn)波形”換成“下一個(gè)心拍間隔或波形模板”效果往往立竿見(jiàn)影。整個(gè)項(xiàng)目做下來(lái)我最大的體會(huì)是不要把三個(gè)模型當(dāng)作競(jìng)爭(zhēng)關(guān)系而是當(dāng)作三條可互相驗(yàn)證的路徑。ARIMA 定義了數(shù)據(jù)的最低可預(yù)測(cè)性LSTM 展示了循環(huán)網(wǎng)絡(luò)的穩(wěn)定上限Transformer 則帶你探索數(shù)據(jù)量足夠時(shí)注意力機(jī)制能走多遠(yuǎn)。對(duì)某個(gè)具體心電預(yù)測(cè)任務(wù)判斷模型是否有效可以先用 ARIMA 定基線再讓 LSTM 打底最后看 Transformer 是否值得投入調(diào)參成本——這樣安排項(xiàng)目邏輯清晰每個(gè)模型都有明確的角色和邊界文檔寫起來(lái)也更有說(shuō)服力。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取