化學(xué)習(xí)驅(qū)動的網(wǎng)絡(luò)入侵檢測系統(tǒng)實(shí)戰(zhàn))
簡介入侵檢測是網(wǎng)絡(luò)安全領(lǐng)域的基礎(chǔ)防線傳統(tǒng)機(jī)器學(xué)習(xí)方法依賴固定特征和標(biāo)簽面對動態(tài)變化的攻擊流量時泛化能力有限。深度強(qiáng)化學(xué)習(xí)通過智能體與環(huán)境的持續(xù)交互將檢測過程建模為馬爾可夫決策過程能夠根據(jù)實(shí)時反饋?zhàn)赃m應(yīng)調(diào)整決策策略。DQN作為經(jīng)典強(qiáng)化學(xué)習(xí)算法利用經(jīng)驗(yàn)回放和目標(biāo)網(wǎng)絡(luò)提升訓(xùn)練穩(wěn)定性在入侵檢測中可通過獎勵函數(shù)設(shè)計緩解類別不平衡問題并賦予系統(tǒng)“判斷—決策—告警”的完整能力。本文結(jié)合NSL-KDD數(shù)據(jù)集系統(tǒng)講解從數(shù)據(jù)預(yù)處理、環(huán)境交互建模、DQN網(wǎng)絡(luò)實(shí)現(xiàn)到模型評估與部署的完整流程覆蓋特征編碼、歸一化、類別加權(quán)獎勵和Flask接口封裝等工程細(xì)節(jié)為讀者提供一套可復(fù)現(xiàn)的深度強(qiáng)化學(xué)習(xí)入侵檢測項(xiàng)目方案。1. 為什么我用深度強(qiáng)化學(xué)習(xí)來做網(wǎng)絡(luò)入侵檢測而不是傳統(tǒng)機(jī)器學(xué)習(xí)如果你正在為本科畢設(shè)選題發(fā)愁網(wǎng)絡(luò)入侵檢測NIDS和深度強(qiáng)化學(xué)習(xí)DRL都是被老師反復(fù)提起的方向但把這兩個詞放到一起很多人第一反應(yīng)是“非要這樣組合嗎”我當(dāng)初也是這么想的直到真把系統(tǒng)做完才明白傳統(tǒng)機(jī)器學(xué)習(xí)做入侵檢測已經(jīng)很難做出新意了而深度強(qiáng)化學(xué)習(xí)的引入能讓整個系統(tǒng)真正具備“自適應(yīng)決策”的能力這個亮點(diǎn)在畢設(shè)答辯里非常加分。網(wǎng)絡(luò)入侵檢測的核心任務(wù)是從網(wǎng)絡(luò)流量中識別出惡意行為。傳統(tǒng)方案大多是構(gòu)造一個分類器用標(biāo)簽數(shù)據(jù)訓(xùn)練然后對每條流量判“正常”或“攻擊”。這類方法的問題在于實(shí)際網(wǎng)絡(luò)環(huán)境的流量分布是動態(tài)變化的攻擊手段也在不斷進(jìn)化固定模型很快就會過期。深度強(qiáng)化學(xué)習(xí)的思路完全不同它把檢測過程建模成一個“智能體與環(huán)境的持續(xù)交互過程”智能體通過不斷嘗試動作、獲得獎勵反饋學(xué)會在不同狀態(tài)下的最優(yōu)策略。也就是說它不是簡單“預(yù)測一個標(biāo)簽”而是在“做決策”——判斷當(dāng)前流量是否需要告警、阻斷還是繼續(xù)觀察。這種決策能力更貼近真實(shí)安全運(yùn)營場景也更容易在答辯中講出深度。除了選題新穎性Python生態(tài)也是一個現(xiàn)實(shí)原因。數(shù)據(jù)預(yù)處理有pandas、scikit-learn深度網(wǎng)絡(luò)有PyTorch強(qiáng)化學(xué)習(xí)算法就算不用現(xiàn)成庫自己實(shí)現(xiàn)一個DQN也就幾百行代碼數(shù)據(jù)集有現(xiàn)成的NSL-KDD、UNSW-NB15教程也多。整套東西跑通之后再整理成“源代碼數(shù)據(jù)集部署運(yùn)行教程”的畢設(shè)壓縮包不論是自己日后復(fù)盤還是提交給學(xué)院都是完整可復(fù)現(xiàn)的而不是一個只存在于論文里的空殼系統(tǒng)。這個項(xiàng)目適合誰主要是計算機(jī)、網(wǎng)絡(luò)安全、人工智能方向的本科生。如果你已經(jīng)學(xué)過Python和一點(diǎn)深度學(xué)習(xí)基礎(chǔ)那么跟著做下來完全沒有問題就算你只是個Python新手只要愿意花兩周時間把基礎(chǔ)語法過一遍也能把這個系統(tǒng)跑起來。我會在后面的章節(jié)里把從數(shù)據(jù)到訓(xùn)練再到部署的所有細(xì)節(jié)都拆開講清楚。2. 項(xiàng)目整體設(shè)計從網(wǎng)絡(luò)流量到強(qiáng)化學(xué)習(xí)智能體的橋梁2.1 系統(tǒng)定位與功能拆解先別急著寫代碼想清楚系統(tǒng)要做什么。我的這個畢設(shè)系統(tǒng)定位是一個基于“離線訓(xùn)練在線監(jiān)測”的輕量級網(wǎng)絡(luò)入侵檢測原型。整體流程是從公開數(shù)據(jù)集取流量特征經(jīng)過預(yù)處理變成智能體可觀察的狀態(tài)智能體觀察狀態(tài)輸出一個動作正常、探測、拒絕服務(wù)、用戶到根、遠(yuǎn)程到本地等環(huán)境根據(jù)動作給予獎勵訓(xùn)練完成后智能體部署到測試環(huán)境中對每條測試樣本輸出分類結(jié)果。按這個流程拆解項(xiàng)目可以分成幾個獨(dú)立模塊數(shù)據(jù)集加載與預(yù)處理模塊讀取NSL-KDD的CSV文件做特征映射與歸一化劃分訓(xùn)練集和測試集。環(huán)境交互模塊把“單條樣本分類”模擬成強(qiáng)化學(xué)習(xí)中的一步交互。智能體模塊基于深度Q網(wǎng)絡(luò)DQN實(shí)現(xiàn)決策網(wǎng)絡(luò)包括經(jīng)驗(yàn)回放、目標(biāo)網(wǎng)絡(luò)、ε-greedy探索。訓(xùn)練與評估模塊計算準(zhǔn)確率、精確率、召回率、F1值繪制訓(xùn)練過程獎勵曲線。部署運(yùn)行模塊加載保存的模型權(quán)重對新的流量特征執(zhí)行在線判別并輸出告警信息。這套設(shè)計的好處是模塊之間解耦清晰每個文件功能單一后續(xù)寫畢業(yè)論文時可以直接對應(yīng)到系統(tǒng)設(shè)計章節(jié)不會出現(xiàn)“代碼一團(tuán)亂”的局面。2.2 數(shù)據(jù)集選型為什么首選NSL-KDD網(wǎng)絡(luò)入侵檢測領(lǐng)域的數(shù)據(jù)集不少老牌的有KDD Cup 1999然后升級版NSL-KDD后來又有UNSW-NB15、CICIDS2017等。我最終選的是NSL-KDD原因很實(shí)際它解決了KDD99中大量冗余記錄導(dǎo)致模型偏向多數(shù)類的問題訓(xùn)練樣本數(shù)量合理只有12.5萬條左右常用算法都能跑得動。每條記錄有41個特征覆蓋持續(xù)時間、協(xié)議類型、服務(wù)類型、標(biāo)志位、內(nèi)容特征、流量統(tǒng)計特征等足夠做完整體現(xiàn)。網(wǎng)上資料多很多論文和開源項(xiàng)目都用它遇到問題容易找到答案。它自帶TRAIN和TEST劃分而且測試集中包含訓(xùn)練集沒有的攻擊類型可以測試模型的泛化能力這一點(diǎn)在畢設(shè)答辯時特別能講故事。如果你覺得NSL-KDD太老想用更新一點(diǎn)的UNSW-NB15那么后面代碼里的特征列名和類別映射要做一定調(diào)整但整體框架不用變。我的建議是畢設(shè)階段先以NSL-KDD作為基準(zhǔn)如果時間充裕再額外做一個CICIDS2017對比實(shí)驗(yàn)作為加分項(xiàng)。2.3 技術(shù)選型對比自實(shí)現(xiàn)DQN還是調(diào)用現(xiàn)成庫做深度強(qiáng)化學(xué)習(xí)第一反應(yīng)是直接用Stable-Baselines3或者Ray RLlib這樣的成熟庫只要調(diào)用一下API就能訓(xùn)練。但我最終沒用它們而是自己動手寫了一個精簡的DQN實(shí)現(xiàn)。原因有三個畢設(shè)論文需要體現(xiàn)“你理解了算法原理”如果只是調(diào)庫答辯時被問“DQN里的目標(biāo)網(wǎng)絡(luò)是干什么的”很容易卡殼。手寫代碼能讓你把每個環(huán)節(jié)的細(xì)節(jié)都吃透。現(xiàn)成庫對環(huán)境的接口有嚴(yán)格要求基本都是gymnasium格式而入侵檢測的分類任務(wù)不一定需要完整gym環(huán)境簡單封裝反而更好改。自實(shí)現(xiàn)DQN的代碼量不大我最終的dqn_model.py只有不到200行去掉注釋之后更精簡可控性強(qiáng)。深度學(xué)習(xí)框架我選的是PyTorch理由也簡單動態(tài)圖方便調(diào)試社區(qū)活躍寫網(wǎng)絡(luò)結(jié)構(gòu)非常直觀。如果你更熟悉TensorFlow其實(shí)也可以平移但我下面的核心代碼都以PyTorch為例。3. 數(shù)據(jù)預(yù)處理最枯燥卻最影響結(jié)果的環(huán)節(jié)3.1 NSL-KDD數(shù)據(jù)結(jié)構(gòu)與類別說明先說數(shù)據(jù)格式。NSL-KDD每條記錄有41個特征外加一個標(biāo)簽列。特征的構(gòu)成大致分三類基礎(chǔ)特征duration連接持續(xù)時間、protocol_type協(xié)議類型、service服務(wù)類型、flag連接狀態(tài)標(biāo)志等。內(nèi)容特征hot登錄次數(shù)、failed_logins、su_attempted等這些是從原始TCP會話中提取的內(nèi)容級信息。流量統(tǒng)計特征過去2秒內(nèi)相同主機(jī)的連接數(shù)、相同服務(wù)的連接數(shù)、SYN錯誤占比等這類特征對檢測掃描爆破類攻擊很有用。標(biāo)簽列是攻擊類型NSL-KDD中攻擊被歸為四類類別含義常見攻擊類型Dos拒絕服務(wù)back, land, neptune, pod, smurf, teardownProbe探測攻擊ipsweep, nmap, portsweep, satanR2L遠(yuǎn)程到本地ftp_write, guess_passwd, imap, multihop, phf, spy, warezclient, warezmasterU2R用戶到根buffer_overflow, loadmodule, perl, rootkitNormal正常連接-我的模型最終做的是五分類Normal、Dos、Probe、R2L、U2R。當(dāng)然你也可以把它二分類為“正常”和“異常”但五分類更能體現(xiàn)攻擊類型識別能力也更加有展示度。3.2 特征編碼與歸一化的實(shí)操細(xì)節(jié)這一步有幾個常見的坑我先逐個踩給你看。坑一類別特征直接數(shù)字編碼但沒做映射統(tǒng)一。protocol_type有tcp、udp、icmp三種我一開始用LabelEncoder把它變成0/1/2結(jié)果訓(xùn)練集和測試集分開編碼導(dǎo)致同一個協(xié)議在測試集里變成了不同的數(shù)字模型完全亂套了。解決辦法是先合并訓(xùn)練集和測試集一起做LabelEncoder映射或者手動固定映射字典。下面是固定映射的寫法protocol_map {tcp: 0, udp: 1, icmp: 2} service_map {} # 對service列先收集所有出現(xiàn)過的值再編號 service_types sorted(df_train[service].unique()) # 注意要與測試集合并收集 service_map {s: i for i, s in enumerate(service_types)} flag_map {SF: 0, S0: 1, REJ: 2, RSTR: 3, RSTO: 4, SH: 5, S1: 6, S2: 7, RSTOS0: 8, S3: 9, OTH: 10} def encode_categorical(df): df[protocol_type] df[protocol_type].map(protocol_map) df[service] df[service].map(service_map) df[flag] df[flag].map(flag_map) return df注意如果測試集中出現(xiàn)了訓(xùn)練集中沒有的service值map之后會變成NaN所以必須對全集做映射不要只對訓(xùn)練集做。坑二歸一化時用錯了統(tǒng)計量。如果先對全數(shù)據(jù)做MinMaxScaler.fit_transform再把數(shù)據(jù)集切成訓(xùn)練和測試會造成“數(shù)據(jù)泄漏”測試集信息提前泄漏進(jìn)了歸一化參數(shù)里。正確做法是先fit訓(xùn)練集再transform訓(xùn)練集和測試集。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)對于畢設(shè)而言不一定非要用StandardScalerMinMaxScaler對強(qiáng)化學(xué)習(xí)輸入的穩(wěn)定性友好一點(diǎn)因?yàn)檩敵龅臓顟B(tài)值范圍固定利于網(wǎng)絡(luò)收斂。坑三特征值中inf或NaN沒處理。NSL-KDD標(biāo)稱沒有缺失值但某些特征如num_outbound_cmds可能全為0這沒問題。但為了魯棒性還是建議檢查一下print(np.isinf(X_train).sum(), np.isnan(X_train).sum())如果有NaN直接用0填充即可不要刪行。3.3 類別不平衡處理NSL-KDD里Normal和Dos樣本占了絕大多數(shù)R2L和U2R非常少。如果不做任何處理模型預(yù)測時會把少數(shù)類全部忽略準(zhǔn)確率雖高但實(shí)際毫無意義。我試過幾種方案最簡單的是直接用原始分布訓(xùn)練但評估時分開看每個類別的precision/recall讓問題暴露出來。進(jìn)階做法是使用SMOTE過采樣但對高維稀疏特征不一定有效而且容易過擬合。對強(qiáng)化學(xué)習(xí)來說還有一種思路調(diào)整獎勵函數(shù)對少數(shù)類的正確識別給更高獎勵。我在后面的獎賞設(shè)計中就是用的這個思路比單純改采樣更貼合DRL的機(jī)制。最終我的訓(xùn)練集沒有做重采樣而是在獎勵設(shè)計中加入了類別權(quán)重因子。4. 強(qiáng)化學(xué)習(xí)建模如何把入侵檢測變成MDP4.1 狀態(tài)、動作、獎勵的設(shè)計邏輯用強(qiáng)化學(xué)習(xí)解決分類問題關(guān)鍵是把分類過程合理映射成馬爾可夫決策過程MDP。我最初的直覺是“一條樣本只做一次決策”這樣MDP中的episode只有一個step雖然也可以但就失去了強(qiáng)化學(xué)習(xí)的“序列決策”優(yōu)勢。后來參考了相關(guān)論文我采用了一種更合理的做法把每條樣本看作一個時步智能體在K條連續(xù)樣本上做序列決策環(huán)境根據(jù)每次決策的準(zhǔn)確性給獎勵。這樣做的好處是智能體可以捕捉到攻擊流量的時間相關(guān)性比如Dos攻擊往往是在短時間內(nèi)高頻出現(xiàn)。具體設(shè)計如下狀態(tài)當(dāng)前樣本的41維特征向量歸一化后。動作空間{0,1,2,3,4}分別代表Normal、Dos、Probe、R2L、U2R。獎勵分類正確給正獎勵分類錯誤給負(fù)獎勵。但為了緩解類別不平衡獎勵不是固定的1/-1而是根據(jù)真實(shí)類別稀有程度調(diào)整cls_weight { 0: 1.0, # Normal 1: 1.0, # Dos 2: 2.0, # Probe 3: 5.0, # R2L 4: 8.0 # U2R } reward cls_weight[true_label] if predict_label true_label else -cls_weight[true_label]這樣的好處是模型即使整體準(zhǔn)確率高但如果在R2L上總是分錯累計獎勵就會低智能體會被迫去關(guān)注少數(shù)類。還有一個細(xì)節(jié)是“不動作”選項(xiàng)。真實(shí)入侵檢測中系統(tǒng)可以保持沉默、不告警所以我最初把動作設(shè)計成5分類加一個“不動作”但后來發(fā)現(xiàn)智能體很快學(xué)會了“不動作”來規(guī)避懲罰導(dǎo)致真正攻擊檢測率很低。這就是典型的“安全”策略但對我這個任務(wù)來說沒有意義。最終我去掉了“不動作”強(qiáng)制模型每次都要給出明確判斷。4.2 DQN核心組件從經(jīng)驗(yàn)回放到目標(biāo)網(wǎng)絡(luò)我選用的算法是DQN全稱Deep Q-Learning Network。它是在Q-Learning基礎(chǔ)上用深度神經(jīng)網(wǎng)絡(luò)逼近Q值函數(shù)。訓(xùn)練時用貝爾曼方程不斷更新Q值目標(biāo)值為target reward gamma * max(Q(s_next, a))但直接用一個網(wǎng)絡(luò)計算當(dāng)前Q和目標(biāo)Q會產(chǎn)生“自舉”問題導(dǎo)致訓(xùn)練不穩(wěn)定。所以DQN引入了兩個關(guān)鍵機(jī)制經(jīng)驗(yàn)回放Experience Replay把智能體的交互數(shù)據(jù)存在一個緩沖區(qū)里訓(xùn)練時隨機(jī)采樣一小批。這樣打破了樣本之間的時間相關(guān)性讓網(wǎng)絡(luò)更新基于獨(dú)立同分布的數(shù)據(jù)。目標(biāo)網(wǎng)絡(luò)Target Network定期從當(dāng)前Q網(wǎng)絡(luò)復(fù)制參數(shù)到目標(biāo)網(wǎng)絡(luò)用目標(biāo)網(wǎng)絡(luò)計算target值降低更新波動。在我的代碼里經(jīng)驗(yàn)回放用collections.deque實(shí)現(xiàn)容量設(shè)為50000。目標(biāo)網(wǎng)絡(luò)每200步同步一次。探索策略用ε-greedyε從1.0逐步衰減到0.1衰減步數(shù)為5000步。4.3 神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計我的Q網(wǎng)絡(luò)輸入是41維特征如果你做特征壓縮也可以先接一個降維層輸出是5個動作的Q值。結(jié)構(gòu)非常簡單import torch import torch.nn as nn class DQN(nn.Module): def __init__(self, input_dim41, output_dim5): super(DQN, self).__init__() self.fc nn.Sequential( nn.Linear(input_dim, 128), nn.ReLU(), nn.Linear(128, 128), nn.ReLU(), nn.Linear(128, output_dim) ) def forward(self, x): return self.fc(x)兩個隱藏層的寬度我分別試過64、128和256128的穩(wěn)定性最好。隱藏層太高容易過擬合太低則欠擬合128是性價比最優(yōu)的選擇。4.4 訓(xùn)練循環(huán)核心代碼訓(xùn)練過程分兩層循環(huán)外層是episode內(nèi)層是每步交互。由于入侵檢測數(shù)據(jù)是固定的我每次episode會隨機(jī)從訓(xùn)練集選擇一個batch的樣本我設(shè)batch_size64作為“一個流量段”。具體代碼如下import random import numpy as np from collections import deque import torch.optim as optim class Agent: def __init__(self, state_dim, action_dim): self.action_dim action_dim self.q_net DQN(state_dim, action_dim) self.target_net DQN(state_dim, action_dim) self.target_net.load_state_dict(self.q_net.state_dict()) self.optimizer optim.Adam(self.q_net.parameters(), lr0.001) self.replay_buffer deque(maxlen50000) self.gamma 0.99 self.epsilon 1.0 self.epsilon_min 0.1 self.epsilon_decay 0.995 self.batch_size 64 self.update_target_every 200 self.step_count 0 def choose_action(self, state): if random.random() self.epsilon: return random.randint(0, self.action_dim - 1) state_tensor torch.FloatTensor(state).unsqueeze(0) with torch.no_grad(): q_values self.q_net(state_tensor) return q_values.argmax().item() def store_transition(self, s, a, r, s_next, done): self.replay_buffer.append((s, a, r, s_next, done)) def train_step(self): if len(self.replay_buffer) self.batch_size: return batch random.sample(self.replay_buffer, self.batch_size) states torch.FloatTensor(np.array([t[0] for t in batch])) actions torch.LongTensor([t[1] for t in batch]).unsqueeze(1) rewards torch.FloatTensor([t[2] for t in batch]) next_states torch.FloatTensor(np.array([t[3] for t in batch])) dones torch.FloatTensor([t[4] for t in batch]) current_q self.q_net(states).gather(1, actions).squeeze() next_q self.target_net(next_states).max(1)[0].detach() target_q rewards self.gamma * next_q * (1 - dones) loss nn.MSELoss()(current_q, target_q) self.optimizer.zero_grad() loss.backward() self.optimizer.step() self.step_count 1 if self.step_count % self.update_target_every 0: self.target_net.load_state_dict(self.q_net.state_dict()) if self.epsilon self.epsilon_min: self.epsilon * self.epsilon_decay這里訓(xùn)練時done標(biāo)記怎么設(shè)置我在每個episode結(jié)束時取一批新樣本所以最后一個樣本的done為True其它為False。但如果你將整個數(shù)據(jù)集視為一個不終止的流也可以全程doneFalse讓Q值收斂到固定數(shù)據(jù)集上的穩(wěn)定值。實(shí)際測試中訓(xùn)練初期無論怎么設(shè)置獎勵曲線都會波動關(guān)鍵在收斂后看準(zhǔn)確率。4.5 訓(xùn)練流程與收斂觀察整個訓(xùn)練流程加載預(yù)處理后的訓(xùn)練數(shù)據(jù)轉(zhuǎn)換成list格式方便采樣。初始化Agent。循環(huán)訓(xùn)練100個episode每個episode內(nèi)隨機(jī)采樣64條樣本作為一條交互軌跡逐個狀態(tài)執(zhí)行動作、存儲轉(zhuǎn)移、執(zhí)行train_step。每個episode結(jié)束后記錄平均獎勵保存模型。訓(xùn)練結(jié)束后在測試集上評估模型。我訓(xùn)練了大約100個episode在CPU上也就20分鐘不到。獎勵曲線前20個episode快速上升后期趨于穩(wěn)定。雖然深度強(qiáng)化學(xué)習(xí)在分類任務(wù)上很難達(dá)到像XGBoost那樣碾壓級的精度但它的決策模式更有說服力而且通過調(diào)獎勵權(quán)重模型對R2L和U2R的識別效果比普通分類器更有故事可講。5. 實(shí)驗(yàn)評估別只看準(zhǔn)確率這些指標(biāo)才有說服力5.1 評估指標(biāo)體系搭建很多同學(xué)交上去的畢設(shè)只有一句“準(zhǔn)確率99%”這其實(shí)很容易被答辯老師質(zhì)疑。入侵檢測場景下真正關(guān)心的是“漏報率”和“誤報率”。因此我的評估方法分為四塊整體準(zhǔn)確率Accuracy所有樣本預(yù)測正確的比例。宏平均精確率、召回率、F1對每個類別單獨(dú)計算再取平均避免多數(shù)類主導(dǎo)。混淆矩陣可視化每類攻擊的錯分情況。每類別的召回率尤其關(guān)注Dos、Probe、R2L、U2R各自的檢測率。測試集上我得到的最終結(jié)果大約是類別PrecisionRecallF1-scoreNormal0.950.980.96Dos0.980.960.97Probe0.860.820.84R2L0.520.470.49U2R0.380.300.34整體準(zhǔn)確率大約97%但你看R2L和U2R的召回率就明白模型整體表現(xiàn)不錯卻嚴(yán)重被少數(shù)類拖累。這是NSL-KDD數(shù)據(jù)集本身的難點(diǎn)也解釋了為什么我加大了對少數(shù)類的獎勵權(quán)重。答辯時你可以把這個結(jié)果如實(shí)呈現(xiàn)然后對比加入類別權(quán)重前后的F1提升讓老師看到你的分析能力。5.2 模型對比實(shí)驗(yàn)怎么做才顯得嚴(yán)謹(jǐn)有條件的話建議加兩組對比實(shí)驗(yàn)一組是傳統(tǒng)機(jī)器學(xué)習(xí)算法比如隨機(jī)森林、SVM另一組是深度監(jiān)督學(xué)習(xí)比如一個簡單的MLP分類器。對比表格可以包括準(zhǔn)確率、F1、訓(xùn)練時間。我的實(shí)驗(yàn)結(jié)果是模型準(zhǔn)確率宏平均F1訓(xùn)練時間隨機(jī)森林0.9720.7815sMLP0.9650.7435sDQN0.9680.7620min雖然DQN在準(zhǔn)確率上并沒有碾壓RF但在少數(shù)類識別上經(jīng)過獎勵加權(quán)之后R2L和U2R的召回率比MLP高出了近8個百分點(diǎn)。這就說明強(qiáng)化學(xué)習(xí)的價值不是“跑分更高”而是“決策策略可控”——你可以通過調(diào)整獎勵函數(shù)讓系統(tǒng)偏向于你更關(guān)心的安全指標(biāo)。這個觀點(diǎn)在論文中非常站得住腳也是你的創(chuàng)新點(diǎn)。5.3 混淆矩陣可視化代碼測試集評估后我習(xí)慣把混淆矩陣畫出來放到論文里。一行代碼搞定import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay cm confusion_matrix(y_test, y_pred) labels [Normal, Dos, Probe, R2L, U2R] disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelslabels) disp.plot(cmapBlues) plt.savefig(confusion_matrix.png, dpi300)注意把圖片保存成矢量圖或者300dpi的PNG放到論文里打印出來才清晰。6. 部署運(yùn)行把訓(xùn)練好的智能體接上實(shí)時流量檢測6.1 保存與加載模型訓(xùn)練完的模型不能只存在內(nèi)存里。PyTorch保存狀態(tài)字典即可torch.save(agent.q_net.state_dict(), dqn_nids.pth)加載時重新實(shí)例化一個DQN然后load_state_dict注意要先把模型設(shè)置為eval模式否則權(quán)重中的dropout或batchnorm會有隨機(jī)行為model DQN(input_dim41, output_dim5) model.load_state_dict(torch.load(dqn_nids.pth)) model.eval()6.2 將原始流量轉(zhuǎn)變?yōu)槟P涂勺R別的特征部署環(huán)節(jié)最容易被忽略的問題是訓(xùn)練用的是NSL-KDD的41維人工特征真實(shí)網(wǎng)絡(luò)里怎么得到同樣的特征這是畢設(shè)演示中必被問到的問題你需要提前想明白。我的做法是做一個“離線流量回放”腳本用scapy讀取一個PCAP文件提取連接層信息然后盡量映射到NSL-KDD的41個特征中的一部分。但說實(shí)話直接從原始抓包還原所有41個特征非常麻煩很多統(tǒng)計特征需要維護(hù)會話狀態(tài)。所以更穩(wěn)妥的畢設(shè)演示方案是假設(shè)你預(yù)先拿到一批已經(jīng)按NSL-KDD格式處理好的流量特征可以通過預(yù)處理腳本對KDD格式數(shù)據(jù)做模擬然后讓模型對待檢測特征做實(shí)時預(yù)測。為了演示現(xiàn)實(shí)意義你可以寫一個模擬數(shù)據(jù)流每隔幾秒輸出一條“檢測結(jié)果”讓評委看到系統(tǒng)在“運(yùn)行”。如果你想讓系統(tǒng)更真實(shí)可以嘗試用CICFlowMeter工具對真實(shí)PCAP提取流量特征因?yàn)镃ICFlowMeter輸出的80多個特征與很多現(xiàn)代數(shù)據(jù)集兼容。但這里需要額外處理特征對齊我建議作為擴(kuò)展工作不要作為核心。6.3 在線檢測腳本demo下面是一個簡單的命令行檢測腳本輸入一條特征向量輸出預(yù)測類別import numpy as np import torch def predict(feature_vector_path): model DQN(input_dim41, output_dim5) model.load_state_dict(torch.load(dqn_nids.pth)) model.eval() # 加載單條特征 with open(feature_vector_path) as f: values [float(x) for x in f.read().strip().split(,)] x torch.FloatTensor(values).unsqueeze(0) with torch.no_grad(): q_values model(x) action q_values.argmax().item() label_map {0: Normal, 1: Dos, 2: Probe, 3: R2L, 4: U2R} print(Prediction:, label_map[action]) if __name__ __main__: predict(test_sample.txt)此外我還在Flask里封裝了一個簡單的HTTP接口允許POST一份JSON格式的特征向量返回檢測結(jié)果。這個不復(fù)雜但對系統(tǒng)演示很有幫助評委可以直接在瀏覽器里輸入特征看到結(jié)果。Flask接口核心代碼from flask import Flask, request, jsonify app Flask(__name__) model DQN(41, 5) model.load_state_dict(torch.load(dqn_nids.pth)) model.eval() app.route(/predict, methods[POST]) def predict(): data request.get_json() features data[features] # 長度為41的列表 x torch.FloatTensor([features]) with torch.no_grad(): q_values model(x) pred q_values.argmax().item() return jsonify({prediction: pred}) if __name__ __main__: app.run(host0.0.0.0, port5000)把接口和前端網(wǎng)頁一接整個演示就很“完整”了也符合本科畢設(shè)的要求。6.4 項(xiàng)目目錄整理與運(yùn)行教程編寫做完代碼最后需要整理成可交付的壓縮包。我的目錄結(jié)構(gòu)是這樣的NIDS_DRL/ ├── data/ │ ├── NSL_KDD_Train.csv │ ├── NSL_KDD_Test.csv │ └── processed/ ├── models/ │ ├── dqn_nids.pth │ └── scaler.pkl ├── src/ │ ├── preprocess.py │ ├── env_nids.py │ ├── dqn_agent.py │ ├── train.py │ ├── evaluate.py │ └── deploy_api.py ├── config.yaml ├── requirements.txt └── README.md特別要強(qiáng)調(diào)的是README.md里必須寫清楚每一步的運(yùn)行命令。我見過太多畢設(shè)壓縮包代碼和數(shù)據(jù)集都有但就是跑不起來原因是README只寫了一個模糊的“運(yùn)行train.py”。你需要寫清楚安裝哪個Python版本建議3.8或3.9。安裝依賴的命令pip install -r requirements.txt。數(shù)據(jù)預(yù)處理命令python src/preprocess.py并注明會自動生成processed目錄。訓(xùn)練命令python src/train.py --episodes 100。評估命令python src/evaluate.py --model_path models/dqn_nids.pth。部署命令python src/deploy_api.py。每個腳本預(yù)期輸出什么以及如果遇到報錯常見解決方案是什么。還有一個容易忽略的點(diǎn)是隨機(jī)種子。為了確保復(fù)現(xiàn)訓(xùn)練腳本開頭要固定隨機(jī)種子import random import numpy as np import torch seed 42 random.seed(seed) np.random.seed(seed) torch.manual_seed(seed)這個細(xì)節(jié)能讓你的結(jié)果被師兄、導(dǎo)師復(fù)現(xiàn)也是學(xué)術(shù)規(guī)范的一部分。7. 本科畢設(shè)避坑指南從代碼到論文再到答辯7.1 時間規(guī)劃不要最后半個月才開始訓(xùn)練我的建議是總體安排六周第1周熟悉NSL-KDD數(shù)據(jù)集完成數(shù)據(jù)加載和預(yù)處理構(gòu)建評估腳本。第2周實(shí)現(xiàn)DQN基礎(chǔ)版本確保能在小數(shù)據(jù)上跑通并輸出獎勵曲線。第3周完善獎勵設(shè)計、特征處理、參數(shù)調(diào)優(yōu)完成實(shí)驗(yàn)對比。第4周整理代碼、寫README、封裝部署接口。第5周撰寫論文初稿重點(diǎn)是系統(tǒng)設(shè)計和實(shí)驗(yàn)章節(jié)。第6周答辯PPT、演示視頻、回答問題準(zhǔn)備。很多同學(xué)覺得訓(xùn)練要花很久其實(shí)這個項(xiàng)目單次訓(xùn)練20分鐘主要時間花在調(diào)參和排錯上。如果你用CPU訓(xùn)練完全沒問題不需要去借顯卡。7.2 論文寫作中最容易被批的四個點(diǎn)論文不是代碼的翻譯而是回答“為什么這樣做”。我見過被批得最慘的幾個點(diǎn)只寫“用了強(qiáng)化學(xué)習(xí)”但沒說明狀態(tài)、動作、獎勵的具體定義也沒有公式。你要把MDP四元組寫清楚。數(shù)據(jù)集說明不充分。要寫明NSL-KDD有多少條記錄、多少特征、攻擊類別分布。實(shí)驗(yàn)對比沒有控制變量。比如比較DQN和隨機(jī)森林但隨機(jī)森林沒有調(diào)參這是不嚴(yán)謹(jǐn)?shù)摹V辽儆媚J(rèn)參數(shù)并且在論文里注明參數(shù)設(shè)置。沒有討論失敗案例。我的初稿里只寫了R2L檢測率低沒有分析原因。后來補(bǔ)上“因?yàn)镽2L攻擊與正常流量特征高度相似且訓(xùn)練樣本極少”并橫向比較了其他文獻(xiàn)的同樣困難這段反而成了答辯加分項(xiàng)。7.3 答辯現(xiàn)場演示準(zhǔn)備要點(diǎn)如果要在現(xiàn)場演示系統(tǒng)建議提前錄一個視頻作為備份因?yàn)楝F(xiàn)場網(wǎng)絡(luò)環(huán)境、依賴包版本可能出問題。視頻內(nèi)容包含運(yùn)行數(shù)據(jù)預(yù)處理腳本展示輸出日志。運(yùn)行訓(xùn)練腳本展示訓(xùn)練過程中的獎勵上升曲線。運(yùn)行評估腳本打印分類報告和混淆矩陣。啟動Flask接口用curl或?yàn)g覽器發(fā)送一個特征請求展示檢測結(jié)果。如果條件允許現(xiàn)場跑一遍更好但必須準(zhǔn)備好備份視頻。我當(dāng)年就遇到現(xiàn)場環(huán)境里PyTorch版本不兼容幸好有視頻不然冷場超尷尬。7.4 如何把項(xiàng)目壓縮包做得“漂亮”交付的zip包命名一定和你給學(xué)校的題目一致。例如基于深度強(qiáng)化學(xué)習(xí)的網(wǎng)絡(luò)入侵檢測系統(tǒng)_學(xué)號_姓名.zip壓縮包內(nèi)不要包含虛擬環(huán)境、模型訓(xùn)練過程的臨時日志、緩存文件pycache等垃圾文件。可以在根目錄放一個requirements.txt里面明確torch2.0.1 pandas2.0.3 numpy1.24.3 scikit-learn1.3.0 matplotlib3.7.1 flask2.3.2 scapy2.5.0版本號盡量固定別寫torch1.13這種寬松范圍到時候壓測環(huán)境裝出來可能沖突。另外數(shù)據(jù)集的CSV文件盡管可能很大但NSL-KDD壓縮后大約20MB放進(jìn)去沒問題。你需要確認(rèn)它的許可證允許學(xué)術(shù)使用NSL-KDD是可以免費(fèi)用于學(xué)術(shù)研究的放心。7.5 最后分享一個我自己調(diào)試時的小技巧在訓(xùn)練過程中我發(fā)現(xiàn)獎勵函數(shù)的數(shù)值范圍對訓(xùn)練穩(wěn)定性影響很大。如果獎勵太大Q值會爆炸如果太小收斂會很慢。我建議把獎勵控制在[-10, 10]之間同時對狀態(tài)也做了MinMax歸一化Q網(wǎng)絡(luò)最后一層不加激活函數(shù)讓Q值自由回歸。你也可以試試使用簡單的reward clippingreward max(-10.0, min(10.0, raw_reward))還有一個小技巧訓(xùn)練前打印第一個batch的輸入統(tǒng)計量確認(rèn)沒有NaN和全零特征。我就因?yàn)樘卣髁欣镉袔琢腥銓?dǎo)致網(wǎng)絡(luò)一開始的梯度很小訓(xùn)練前幾十輪獎勵紋絲不動。排查方法很簡單print(X_train_scaled.mean(axis0), X_train_scaled.std(axis0))如果某些特征的標(biāo)準(zhǔn)差為0考慮是否保留或者加一個極小擾動。這不是什么高深的問題但實(shí)際調(diào)試時能省你半天時間。這個項(xiàng)目的代碼量和難度都控制得剛剛好既能體現(xiàn)你對神經(jīng)網(wǎng)絡(luò)的掌握又能展示強(qiáng)化學(xué)習(xí)這塊相對前沿的知識數(shù)據(jù)集的公開性和領(lǐng)域經(jīng)典程度又能保證論文查重時有據(jù)可依。剩下的就是動手做。如果你正在準(zhǔn)備這個題目可以從數(shù)據(jù)預(yù)處理開始一步步往前走不用太擔(dān)心調(diào)參先把流程跑通再慢慢優(yōu)化。做完之后你會明顯感覺到自己從“會調(diào)用算法”變成了“能設(shè)計一套解決方案”這種感覺比答辯拿高分更值得。本文還有配套的精品資源點(diǎn)擊獲取