現(xiàn)網(wǎng)絡(luò)入侵檢測(cè):從NSL-KDD預(yù)處理到模型調(diào)優(yōu)實(shí)戰(zhàn))
簡(jiǎn)介這套Python實(shí)現(xiàn)的基于CNN網(wǎng)絡(luò)入侵檢測(cè)項(xiàng)目源碼與文檔說(shuō)明適合深度學(xué)習(xí)入門(mén)、網(wǎng)絡(luò)安全方向課設(shè)或期末大作業(yè)的本科生及自學(xué)者。項(xiàng)目難度適中代碼經(jīng)過(guò)本地編譯驗(yàn)證完整覆蓋NSL-KDD數(shù)據(jù)集預(yù)處理、CNN模型定義、訓(xùn)練與預(yù)測(cè)流程并附有README說(shuō)明與實(shí)操文檔可幫助理解入侵檢測(cè)任務(wù)的建模思路與代碼組織方式。壓縮包共33個(gè)文件約21.58MB主要包含4個(gè)Python源碼文件、12個(gè)cvs數(shù)據(jù)文件、7個(gè)xml工程配置文件、1個(gè)pth模型權(quán)重文件以及準(zhǔn)確率曲線圖、混淆矩陣圖表等輔助材料源碼按數(shù)據(jù)預(yù)處理、模型構(gòu)建、訓(xùn)練、預(yù)測(cè)模塊拆分注釋清晰、易讀易改。數(shù)據(jù)預(yù)處理腳本和模型訓(xùn)練代碼均可直接運(yùn)行支持自行調(diào)節(jié)學(xué)習(xí)率、卷積核等參數(shù)復(fù)用于其他二分類或多分類任務(wù)也可作為期末答辯時(shí)的完整演示項(xiàng)目。目前已有355人學(xué)習(xí)下載適合希望快速參考完整實(shí)現(xiàn)或作為高分期末作業(yè)模板的讀者。1. 用 Python 和 CNN 做網(wǎng)絡(luò)入侵檢測(cè)這門(mén)課設(shè)為什么值得認(rèn)真做網(wǎng)絡(luò)入侵檢測(cè)系統(tǒng)NIDS一直是網(wǎng)絡(luò)安全領(lǐng)域的熱門(mén)方向而用 Python 實(shí)現(xiàn)基于 CNN卷積神經(jīng)網(wǎng)絡(luò)的入侵檢測(cè)模型正是許多高校網(wǎng)絡(luò)工程、信息安全專業(yè)期末大作業(yè)的高頻選題。這類項(xiàng)目源碼加上文檔說(shuō)明被打包成 zip 發(fā)放本質(zhì)上是在考察學(xué)生能否把深度學(xué)習(xí)理論落地到真實(shí)流量數(shù)據(jù)上而不是停留在調(diào)庫(kù)層面。打開(kāi)這個(gè)壓縮包你大概率會(huì)看到 KDD Cup 1999 或 NSL-KDD 數(shù)據(jù)集的預(yù)處理腳本、CNN 模型定義文件、訓(xùn)練與評(píng)估代碼以及一份說(shuō)明文檔。這個(gè)方向能解決的問(wèn)題很明確傳統(tǒng)入侵檢測(cè)系統(tǒng)依賴專家規(guī)則和特征匹配對(duì)未知攻擊的泛化能力差而 CNN 能自動(dòng)從流量特征中提取空間相關(guān)性把分類任務(wù)交給網(wǎng)絡(luò)自己學(xué)習(xí)。適合的人群包括正在做課程設(shè)計(jì)的學(xué)生、想入門(mén)深度學(xué)習(xí)在安全領(lǐng)域應(yīng)用的開(kāi)發(fā)者以及需要快速搭建一個(gè)可演示 IDS 原型的工程師。接下來(lái)的內(nèi)容我會(huì)從數(shù)據(jù)集處理講到模型調(diào)參再講踩過(guò)的坑盡量讓你拿到源碼后能直接跑通、能講清楚、能應(yīng)付答辯。2. 入侵檢測(cè)與 CNN 的契合點(diǎn)為什么流量數(shù)據(jù)適合用卷積網(wǎng)絡(luò)2.1 從特征工程到自動(dòng)特征提取的轉(zhuǎn)變傳統(tǒng)入侵檢測(cè)的做法是先定義大量規(guī)則比如某個(gè)端口短時(shí)間內(nèi)連接次數(shù)超過(guò)閾值就判定為掃描攻擊。這種方式依賴人工經(jīng)驗(yàn)規(guī)則覆蓋不全時(shí)漏報(bào)率很高。后來(lái)發(fā)展到機(jī)器學(xué)習(xí)方法用 SVM、隨機(jī)森林等算法對(duì)特征向量分類但特征仍需要人工挑選。CNN 的思路完全不同它把原始特征當(dāng)作一種空間結(jié)構(gòu)來(lái)處理通過(guò)卷積核在特征維度上滑動(dòng)自動(dòng)學(xué)習(xí)局部組合特征。網(wǎng)絡(luò)流量數(shù)據(jù)經(jīng)過(guò)預(yù)處理后每條記錄通常是幾十個(gè)數(shù)值特征協(xié)議類型、源字節(jié)數(shù)、目的字節(jié)數(shù)、連接狀態(tài)等。這些特征看似是平鋪的一維向量但相鄰特征之間存在某種關(guān)聯(lián)——比如同一個(gè) TCP 連接中的包長(zhǎng)、標(biāo)志位、持續(xù)時(shí)長(zhǎng)在空間上相鄰這種局部模式恰好是 CNN 擅長(zhǎng)捕捉的。把一維特征向量 reshape 成合適的形狀喂給卷積層卷積核就能提取出特征之間的短程依賴關(guān)系這比全連接網(wǎng)絡(luò)直接學(xué)習(xí)所有特征組合要高效得多。2.2 CNN 處理一維序列輸入的常見(jiàn)結(jié)構(gòu)對(duì)于 NIDS 這種輸入是一維特征向量的任務(wù)常見(jiàn)做法是使用一維卷積Conv1D而不是圖像領(lǐng)域常用的 Conv2D。一個(gè)典型的網(wǎng)絡(luò)結(jié)構(gòu)包括輸入層接受 shape 為 (seq_len, feature_dim) 或 (seq_len,) 的數(shù)據(jù)經(jīng)過(guò)幾個(gè) Conv1D 層提取特征每層后接 BatchNormalization 和 MaxPooling1D 做歸一化和降采樣最后接全局池化或展平層再通過(guò)全連接層輸出分類概率。代碼骨架大概是這樣的from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout, BatchNormalization def build_cnn_model(input_shape, num_classes): model Sequential([ Conv1D(filters64, kernel_size3, activationrelu, input_shapeinput_shape), BatchNormalization(), MaxPooling1D(pool_size2), Conv1D(filters128, kernel_size3, activationrelu), BatchNormalization(), MaxPooling1D(pool_size2), Flatten(), Dense(128, activationrelu), Dropout(0.5), Dense(num_classes, activationsoftmax) ]) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) return model這段代碼里input_shape 需要根據(jù)特征維度確定。如果每條樣本有 41 個(gè)特征NSL-KDD 標(biāo)準(zhǔn)可以 reshape 成 (41, 1)即把每個(gè)特征當(dāng)作一個(gè)時(shí)間步Conv1D 在特征序列上滑動(dòng)。filters 表示卷積核數(shù)量決定提取多少種局部模式kernel_size 表示卷積核覆蓋的相鄰特征個(gè)數(shù)一般取 3 或 5太小感受野不足太大容易過(guò)擬合。BatchNormalization 加速收斂并穩(wěn)定訓(xùn)練Dropout 防止過(guò)擬合這些做課設(shè)答辯時(shí)都是加分點(diǎn)。2.3 二分類還是多分類取決于數(shù)據(jù)集標(biāo)簽定義KDD Cup 1999 和 NSL-KDD 的標(biāo)簽分為正常Normal和四類攻擊DoS、Probe、R2L、U2R。做課設(shè)時(shí)先確認(rèn)文檔里要求的是二分類正常 vs 異常還是五分類正常 四類攻擊。二分類簡(jiǎn)單準(zhǔn)確率容易做高但答辯時(shí)老師常問(wèn)各類攻擊的檢出率分別是多少這時(shí)只看總體準(zhǔn)確率是不夠的五分類任務(wù)中 R2L 和 U2R 樣本量極少是典型的類別不平衡問(wèn)題需要額外處理。選擇哪種分類粒度直接決定模型輸出層神經(jīng)元數(shù)量和損失函數(shù)。多分類用 softmax 和 categorical_crossentropy二分類用 sigmoid 和 binary_crossentropy。建議文檔說(shuō)明里明確寫(xiě)清楚實(shí)驗(yàn)設(shè)定把混淆矩陣和每類別的精確率、召回率都打出來(lái)這比只給一個(gè)準(zhǔn)確率數(shù)字有說(shuō)服力得多。3. 數(shù)據(jù)預(yù)處理與特征編碼決定模型上限的關(guān)鍵一步3.1 NSL-KDD 數(shù)據(jù)集的加載與清洗NSL-KDD 是 KDD Cup 1999 的改進(jìn)版本去掉了冗余記錄訓(xùn)練集和測(cè)試集的分布更合理。拿到源碼包后第一步通常是加載 CSV 格式的 KDDTrain.csv 和 KDDTest.csv。每條記錄有 41 個(gè)特征和 1 個(gè)標(biāo)簽特征分為三類連續(xù)數(shù)值型如 duration、src_bytes、離散符號(hào)型如 protocol_type、service、flag、以及二進(jìn)制型。加載代碼通常長(zhǎng)這樣import pandas as pd column_names [ duration, protocol_type, service, flag, src_bytes, dst_bytes, land, wrong_fragment, urgent, hot, # ... 省略部分特征名 dst_host_srv_count, dst_host_same_srv_rate, dst_host_diff_srv_rate, dst_host_same_src_port_rate, dst_host_srv_diff_host_rate, dst_host_serror_rate, dst_host_srv_serror_rate, dst_host_rerror_rate, dst_host_srv_rerror_rate, label ] train_df pd.read_csv(KDDTrain.csv, namescolumn_names) test_df pd.read_csv(KDDTest.csv, namescolumn_names) print(f訓(xùn)練集形狀: {train_df.shape}, 測(cè)試集形狀: {test_df.shape}) print(train_df[label].value_counts())這里需要注意KDD 數(shù)據(jù)集的原始 CSV 沒(méi)有表頭必須手動(dòng)指定列名。如果列名數(shù)量和 CSV 實(shí)際列數(shù)不匹配pandas 會(huì)報(bào)錯(cuò)常見(jiàn)的翻車(chē)點(diǎn)在最后有一列difficulty_levelNSL-KDD 特有讀數(shù)據(jù)時(shí)要么把這一列也加進(jìn)列名要么用usecols忽略它。建議先打印train_df.shape確認(rèn)列數(shù)不要想當(dāng)然。3.2 符號(hào)特征編碼LabelEncoder 還是 OneHotEncoderprotocol_type、service、flag 這三個(gè)特征是離散字符串不能直接喂給 CNN。常見(jiàn)的處理方式有兩種如果每個(gè)符號(hào)特征的取值種類少如 protocol_type 只有 3 種取值用 OneHot 編碼展開(kāi)成多列如果取值很多service 有 70 種左右用 LabelEncoder 轉(zhuǎn)成整數(shù)再歸一化。但這里有個(gè)坑LabelEncoder 給類別賦予的整數(shù)大小是隨機(jī)的比如 tcp0、udp1、icmp2這個(gè)大小關(guān)系沒(méi)有實(shí)際意義CNN 的卷積核會(huì)把它當(dāng)作真實(shí)數(shù)值關(guān)系來(lái)學(xué)習(xí)效果往往不如 OneHot。我一般會(huì)這樣做from sklearn.preprocessing import LabelEncoder # 方法一對(duì)取值少的特征做 OneHot train_df pd.get_dummies(train_df, columns[protocol_type, flag]) # 方法二對(duì)取值多的特征做 LabelEncoder 并歸一化 le LabelEncoder() for col in [service]: train_df[col] le.fit_transform(train_df[col]) test_df[col] le.transform(test_df[col])但這里有個(gè)嚴(yán)重的問(wèn)題訓(xùn)練集和測(cè)試集的類別取值可能不一致。比如訓(xùn)練集里 service 有 70 種測(cè)試集里可能出現(xiàn)訓(xùn)練集沒(méi)見(jiàn)過(guò)的 service 值。如果直接用訓(xùn)練集擬合的 LabelEncoder 去 transform 測(cè)試集sklearn 會(huì)直接拋異常。解決方法是先合并兩個(gè)數(shù)據(jù)集做編碼或者對(duì)測(cè)試集中出現(xiàn)的新類別統(tǒng)一映射到一個(gè)未知類再在文檔里說(shuō)明這種處理的理由。這個(gè)細(xì)節(jié)在答辯時(shí)很加分因?yàn)樗w現(xiàn)了對(duì)真實(shí)場(chǎng)景中分布漂移問(wèn)題的理解。3.3 數(shù)值特征歸一化與訓(xùn)練集測(cè)試集泄漏41 個(gè)特征里數(shù)值型特征的量綱差異巨大src_bytes 可能是 0 到幾百萬(wàn)而 land 只有 0 或 1。如果不歸一化卷積層的梯度更新會(huì)被大數(shù)值特征主導(dǎo)模型很難收斂。標(biāo)準(zhǔn)做法是用 StandardScaler 或 MinMaxScaler但必須只在訓(xùn)練集上擬合 scaler然后同時(shí) transform 訓(xùn)練集和測(cè)試集。如果先對(duì)全量數(shù)據(jù)做歸一化再劃分訓(xùn)練集測(cè)試集會(huì)造成數(shù)據(jù)泄漏測(cè)試集的信息被模型間接看到了評(píng)估結(jié)果虛高。from sklearn.preprocessing import StandardScaler scaler StandardScaler() train_numeric_cols train_df.select_dtypes(include[float64, int64]).columns train_df[train_numeric_cols] scaler.fit_transform(train_df[train_numeric_cols]) test_df[train_numeric_cols] scaler.transform(test_df[train_numeric_cols])這段代碼里scaler 只在 train_df 上 fittest_df 只做 transform。注意select_dtypes會(huì)把標(biāo)簽列也選進(jìn)去如果標(biāo)簽是字符串沒(méi)問(wèn)題如果是整數(shù)編碼就需要先剔除 label 列。處理完特征后把標(biāo)簽單獨(dú)拆出來(lái)做 one-hot 編碼用于多分類模型訓(xùn)練。最后要把數(shù)據(jù) reshape 成 CNN 輸入格式通常是(樣本數(shù), 特征維度, 1)。4. 模型訓(xùn)練與評(píng)估把準(zhǔn)確率做上去的完整流程4.1 數(shù)據(jù)集劃分不要只留一個(gè)測(cè)試集拿到源碼后常見(jiàn)的做法是直接把 KDDTrain 當(dāng)訓(xùn)練集、KDDTest 當(dāng)測(cè)試集然后開(kāi)始訓(xùn)練。但這樣做有一個(gè)問(wèn)題你無(wú)法在訓(xùn)練過(guò)程中驗(yàn)證模型效果超參數(shù)調(diào)優(yōu)沒(méi)有依據(jù)。更好的流程是從訓(xùn)練集里再切出 20% 作為驗(yàn)證集訓(xùn)練時(shí)監(jiān)控驗(yàn)證集準(zhǔn)確率和損失防止過(guò)擬合。from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val train_test_split( X_train_raw, y_train_raw, test_size0.2, random_state42, stratifyy_train_raw ) X_train X_train.reshape(X_train.shape[0], X_train.shape[1], 1) X_val X_val.reshape(X_val.shape[0], X_val.shape[1], 1)stratifyy_train_raw是按類別比例分層抽樣保證驗(yàn)證集里各個(gè)攻擊類型的占比和訓(xùn)練集一致。這對(duì) NSL-KDD 尤其重要因?yàn)?R2L 和 U2R 樣本極少如果不分層抽樣驗(yàn)證集里可能一個(gè) U2R 樣本都分不到驗(yàn)證指標(biāo)完全失真。4.2 訓(xùn)練循環(huán)與 EarlyStoppingCNN 在 NSL-KDD 上訓(xùn)練比較容易過(guò)擬合因?yàn)橛?xùn)練集規(guī)模只有幾萬(wàn)條且類別不平衡嚴(yán)重。常見(jiàn)做法是設(shè)置早停當(dāng)驗(yàn)證集損失連續(xù)多個(gè) epoch 不再下降時(shí)停止訓(xùn)練并用回調(diào)保存最佳模型。from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ModelCheckpoint(best_model.h5, monitorval_accuracy, save_best_onlyTrue) ] history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size256, callbackscallbacks, verbose1 )patience10表示連續(xù) 10 個(gè) epoch 驗(yàn)證損失沒(méi)改善就停。restore_best_weightsTrue會(huì)把模型權(quán)重回滾到驗(yàn)證集表現(xiàn)最好的那一輪而不是停在最后一個(gè) epoch。batch_size 取 256 在大多數(shù) GPU 或 CPU 上都能接受太大容易收斂慢太小則梯度噪聲大、訓(xùn)練振蕩。如果訓(xùn)練時(shí)發(fā)現(xiàn)驗(yàn)證損失震蕩劇烈優(yōu)先減小學(xué)習(xí)率或增大 batch_size。4.3 評(píng)估指標(biāo)準(zhǔn)確率之外必須看混淆矩陣課設(shè)報(bào)告里如果只寫(xiě)準(zhǔn)確率 99.2%答辯老師大概率會(huì)追問(wèn)對(duì) U2R 攻擊的檢出率是多少NSL-KDD 測(cè)試集中 U2R 樣本極少很多模型對(duì)其檢出率為 0但總體準(zhǔn)確率依然很高這就是準(zhǔn)確率指標(biāo)的欺騙性。必須輸出混淆矩陣、各類別的 precision、recall、F1-score。from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_pred model.predict(X_test) y_pred_class np.argmax(y_pred, axis1) y_true_class np.argmax(y_test, axis1) print(classification_report(y_true_class, y_pred_class, target_nameslabel_names)) print(confusion_matrix(y_true_class, y_pred_class))看報(bào)告時(shí)重點(diǎn)關(guān)注 R2L 和 U2R 兩行的 recall。如果這兩行數(shù)字很低說(shuō)明模型基本沒(méi)學(xué)會(huì)少數(shù)類攻擊模式這時(shí)候需要回頭處理類別不平衡。一個(gè)簡(jiǎn)單的改進(jìn)是class_weight讓少數(shù)類在 loss 計(jì)算中擁有更高權(quán)重。另一個(gè)是過(guò)采樣少數(shù)類樣本但要注意別在測(cè)試集上做任何采樣。4.4 類別不平衡的兩種處理方案對(duì)比處理 NSL-KDD 類別不平衡課設(shè)中最常用兩種做法class_weight 和少數(shù)類過(guò)采樣。class_weight 不改數(shù)據(jù)只在訓(xùn)練時(shí)加大少數(shù)類的 loss 占比實(shí)現(xiàn)簡(jiǎn)單。from sklearn.utils.class_weight import compute_class_weight class_weights compute_class_weight( balanced, classesnp.unique(y_train_raw), yy_train_raw ) class_weight_dict dict(enumerate(class_weights))過(guò)采樣則用 imbalanced-learn 庫(kù)的 SMOTE 生成少數(shù)類合成樣本但 SMOTE 是基于特征空間插值的對(duì)表格型數(shù)據(jù)效果尚可對(duì)類別型特征多的數(shù)據(jù)可能生成矛盾的樣本。做課設(shè)時(shí)用 class_weight 更安全答辯時(shí)講清楚原理就夠如果追求更好的少數(shù)類檢出率再嘗試 SMOTE 并對(duì)比兩組實(shí)驗(yàn)結(jié)果。5. 常見(jiàn)坑與避坑指南課設(shè)翻車(chē)現(xiàn)場(chǎng)復(fù)盤(pán)5.1 特征維度不匹配導(dǎo)致模型無(wú)法編譯現(xiàn)象輸入模型時(shí)報(bào)錯(cuò)Input 0 of layer conv1d is incompatible提示期望三維輸入但實(shí)際給了兩維。原因Conv1D 要求輸入是(batch_size, steps, channels)而很多人在預(yù)處理后直接拿二維數(shù)組(樣本數(shù), 特征數(shù))去喂模型忘記 reshape。解決在 fit 之前執(zhí)行X_train X_train.reshape(-1, n_features, 1)第三維設(shè)為 1 表示單通道。這個(gè)錯(cuò)誤在第一次跑代碼時(shí)幾乎必現(xiàn)源碼包里的腳本如果沒(méi)處理手動(dòng)補(bǔ)上即可。5.2 訓(xùn)練集和測(cè)試集類別不一致導(dǎo)致 transform 崩潰現(xiàn)象執(zhí)行測(cè)試集 LabelEncoder transform 時(shí)報(bào)unknown class錯(cuò)誤。原因測(cè)試集中某個(gè) service 協(xié)議在訓(xùn)練集中從未出現(xiàn)編碼器不認(rèn)識(shí)。解決合并訓(xùn)練集測(cè)試集后統(tǒng)一 fit或?qū)y(cè)試集中的新類別做未知類映射。課設(shè)報(bào)告里如果提到這個(gè)處理說(shuō)明考慮了真實(shí)流量的開(kāi)放性問(wèn)題是加分項(xiàng)。另一個(gè)相關(guān)坑是 OneHot 編碼后訓(xùn)練集和測(cè)試集列數(shù)不同原因同樣是新類別產(chǎn)生了新列用pd.concat后統(tǒng)一編碼能根治。5.3 訓(xùn)練準(zhǔn)確率不升反降或直接 NaN現(xiàn)象loss 在幾個(gè) epoch 后變成 NaN或準(zhǔn)確率一直徘徊在基線水平。原因?qū)W習(xí)率過(guò)大導(dǎo)致梯度爆炸或數(shù)值特征沒(méi)有歸一化或激活函數(shù)選擇不當(dāng)。解決先把學(xué)習(xí)率降到 0.0001 試試確認(rèn)歸一化已執(zhí)行檢查數(shù)據(jù)里是否有無(wú)窮大或空值。加 BatchNormalization 層能緩解梯度問(wèn)題。另外要注意NSL-KDD 中有少量特征的取值極端分布如 src_bytes 的右偏只用 StandardScaler 有時(shí)不如取 log 變換穩(wěn)定這屬于進(jìn)階調(diào)參技巧。5.4 準(zhǔn)確率高但攻擊檢出率極低現(xiàn)象總準(zhǔn)確率 99%但 U2R 和 R2L 的 recall 接近于 0。原因類別極度不平衡模型把所有樣本都預(yù)測(cè)為多數(shù)類就是最優(yōu)策略。解決使用 class_weight 加重少數(shù)類權(quán)重或在訓(xùn)練時(shí)對(duì)少數(shù)類做重復(fù)采樣。評(píng)估時(shí)不要只看準(zhǔn)確率輸出 macro-F1 或加權(quán) F1這幾個(gè)指標(biāo)對(duì)不平衡更敏感。如果答辯時(shí)老師問(wèn)為什么 U2R 檢不出回答思路是U2R 樣本只有幾十條特征空間被正常流量淹沒(méi)模型沒(méi)有足夠的判別信息這是數(shù)據(jù)層面的問(wèn)題不是模型結(jié)構(gòu)的問(wèn)題。5.5 結(jié)果無(wú)法復(fù)現(xiàn)現(xiàn)象同一個(gè)腳本多次運(yùn)行準(zhǔn)確率波動(dòng)明顯。原因沒(méi)有固定隨機(jī)種子模型初始化隨機(jī)數(shù)據(jù)劃分隨機(jī)。解決在代碼開(kāi)頭固定 numpy、tensorflow 和 Python 內(nèi)置 random 的種子。import random import numpy as np import tensorflow as tf random.seed(42) np.random.seed(42) tf.random.set_seed(42)課設(shè)報(bào)告里截圖的結(jié)果必須可復(fù)現(xiàn)不然答辯時(shí)老師讓你現(xiàn)場(chǎng)跑一遍結(jié)果和報(bào)告不一樣就很尷尬。固定種子后模型訓(xùn)練速度和結(jié)果基本一致這是所有深度學(xué)習(xí)項(xiàng)目中必須養(yǎng)成的習(xí)慣。6. 進(jìn)階玩法與驗(yàn)證技巧讓課設(shè)從能跑變成能講6.1 試試用訓(xùn)練好的模型做離線流量檢測(cè)演示模型訓(xùn)練完可以寫(xiě)一個(gè)小函數(shù)接收一條 CSV 格式的流量記錄輸出預(yù)測(cè)類型做成命令行工具。def predict_single_record(model, record_df, scaler, encoder): record record_df.copy() for col in [protocol_type, flag]: record pd.get_dummies(record, columns[col]) record record.reindex(columnsfeature_columns, fill_value0) record[numeric_cols] scaler.transform(record[numeric_cols]) record_array record.values.reshape(1, -1, 1) pred model.predict(record_array) return label_names[np.argmax(pred)]這段代碼里reindex是核心單挑記錄做 OneHot 后列數(shù)肯定和訓(xùn)練時(shí)不一樣用訓(xùn)練時(shí)的完整特征列列表對(duì)齊缺失列填 0。這樣模型才能接受輸入。演示時(shí)拿測(cè)試集里幾條已知類型的數(shù)據(jù)跑一遍輸出預(yù)測(cè)結(jié)果比光講一堆訓(xùn)練曲線更有說(shuō)服力。6.2 畫(huà)訓(xùn)練曲線時(shí)注意原始記錄而不是平滑曲線課設(shè)報(bào)告里通常會(huì)放訓(xùn)練準(zhǔn)確率和損失曲線圖。畫(huà)圖時(shí)用 matplotlib 記錄 history 中的原始數(shù)據(jù)就好關(guān)鍵是把訓(xùn)練集和驗(yàn)證集兩條線放一起對(duì)比觀察過(guò)擬合發(fā)生的 epoch 位置。如果只畫(huà)訓(xùn)練集曲線沒(méi)法展示早停策略的效果。import matplotlib.pyplot as plt plt.plot(history.history[accuracy], labeltrain_acc) plt.plot(history.history[val_accuracy], labelval_acc) plt.xlabel(Epoch) plt.ylabel(Accuracy) plt.legend() plt.savefig(training_history.png, dpi150)圖片保存分辨率要夠不要截窗口模糊圖。文檔里左圖右圖對(duì)應(yīng)說(shuō)明比如第 20 輪后驗(yàn)證集準(zhǔn)確率開(kāi)始下降說(shuō)明過(guò)擬合出現(xiàn)觸發(fā)早停回滾到第 18 輪權(quán)重。6.3 把你的模型和傳統(tǒng)機(jī)器學(xué)習(xí)做橫向?qū)Ρ却疝q時(shí)老師最常問(wèn)的問(wèn)題之一為什么用 CNNCNN 比隨機(jī)森林好在哪建議在文檔里補(bǔ)一個(gè)對(duì)比實(shí)驗(yàn)跑一個(gè)隨機(jī)森林或邏輯回歸作為基線比較二者在 NSL-KDD 測(cè)試集上的準(zhǔn)確率和 F1。CNN 在這種表格數(shù)據(jù)上優(yōu)勢(shì)并不明顯但特征相關(guān)性提取能力比手工特征略強(qiáng)特別是在原始特征不做太多人工構(gòu)造的情況下。如果不做對(duì)比實(shí)驗(yàn)老師容易懷疑為什么不用隨機(jī)森林。實(shí)現(xiàn)方式很簡(jiǎn)單用 sklearn 的 RandomForestClassifier 跑同樣的數(shù)據(jù)然后對(duì)比兩到三個(gè)指標(biāo)。對(duì)比結(jié)果不一定要 CNN 全面碾壓哪怕 CNN 在 U2R 上多召回幾個(gè)樣本或者總體 F1 高一點(diǎn)都有交代。如果隨機(jī)森林反而更好那也可以說(shuō)明傳統(tǒng)方法在小樣本攻擊上仍有優(yōu)勢(shì)未來(lái)可以考慮 CNN 樹(shù)模型的集成方案這比硬吹 CNN 合理得多。6.4 文檔說(shuō)明怎么寫(xiě)才打高分我見(jiàn)過(guò)很多課設(shè)源碼里帶的文檔說(shuō)明長(zhǎng)篇大論貼代碼卻唯獨(dú)不寫(xiě)實(shí)驗(yàn)結(jié)果分析。高分文檔至少要有這幾塊問(wèn)題定義和數(shù)據(jù)集描述、數(shù)據(jù)預(yù)處理流程圖、模型結(jié)構(gòu)圖和每層參數(shù)表、超參數(shù)設(shè)置表、訓(xùn)練曲線圖、混淆矩陣與各類別指標(biāo)表、對(duì)比實(shí)驗(yàn)表以及遇到的三個(gè)問(wèn)題和解決辦法章節(jié)。每個(gè)表格配一句解釋比如從表 4 可以看出U2R 的召回率僅為 0.12主要原因是訓(xùn)練集中該類別只有 52 條樣本。這種文檔老師沒(méi)理由扣分。整個(gè)項(xiàng)目做下來(lái)我最深的感受是CNN 在網(wǎng)絡(luò)入侵檢測(cè)上的表現(xiàn)很大程度上取決于數(shù)據(jù)預(yù)處理而不是網(wǎng)絡(luò)深度。我自己的課設(shè)就是從亂吞 Shape 錯(cuò)誤開(kāi)始一步步把代碼跑通又因?yàn)轭悇e不平衡反復(fù)調(diào)整最后才把改進(jìn)過(guò)程寫(xiě)成文檔。這條路你大概率也要走一遍希望這些踩坑經(jīng)驗(yàn)?zāi)軒湍闵僮邘撞綇澛?。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取