在上一篇文章当中,我们已经详细阅读了论文原文,并对其中的一些较难理解的地方加以解释。本篇文章,将对论文最终得到的结论进行简单的论证。
数据收集
首先,由于重新搭建和论文描述的实验环境太过复杂,这里为了简化(实际是太懒了),可以寻找网上的相关数据集。可以查阅到,有关DoH隧道研究较为出名,并且能够搜索到的公开数据集是来着“北海道大学”公开的数据集,具体链接如下:https://eprints.lib.hokudai.ac.jp/dspace/handle/2115/88092

其中有两个数据集,我们下载第一个就ok。因为第二个单纯的都是DoH隧道流量,适用于研究不同软件发出的DoH隧道流量的探测。第一个数据集其实也是包含整理了第二个数据集,其中的l1-total-add.csv 有很多行数据,包含了多个特征值字段和最后的实际标签(DoH或NonDoH),这恰好是可以给我使用的。
大家可以重新翻看之前的文章,其中讲到了研究者刚开始是使用了如下图的24个特征值进行训练模型:

但是,在最后的论证当中发现,由于可能会出现0填充的影响导致有关TLS记录长度的特征无效,所以研究者只用了有关时间的特征进行训练,发现效果依然不错,也是他们最后得到的结论之一。所以,我们可以借此使用l1-total-add.csv 当中的有关时间的特征进行模型训练。虽然,数据集中并没有记录,并且我们也无法根据现有的数据计算出和论文当中一模一样的12个时间特征,但是,我认为数据集当中的16个时间特征仍旧有效,能够保留论文当中12个时间特征的相关特性。
模型训练
论文使用的是sklearn库中的现有模型进行训练,我们也可以同样进行使用。对于研究者使用的增强决策树模型,下面我将会使用GBDT(Gradient Boosting Decision Tree,梯度提升决策树) 进行替代。
废话不多说,代码也不难,下面直接贴代码。
随机森林
- 训练代码
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.metrics import make_scorer, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.preprocessing import LabelEncoder
from sklearn.utils import shuffle
from joblib import dump # 用于保存模型
from tqdm import tqdm
# 1. 加载数据
print("正在加载数据...")
data = pd.read_csv(r"CIRA-CIC-DoHBrw-2020-and-DoH-Tunnel-Traffic-HKD\l1-total-add.csv")
print(f"数据加载完成,总样本数: {
len(data)}")
# 2. 清理数据
print("\n正在清理数据...")
data = data.dropna()
print(f"清理后剩余样本数: {
len(data)}")
# 3. 分离特征和标签
X = data.iloc[:, 18:-1].values
y = data.iloc[:, -1].values # 标签列是字符串( 'DoH'/'NonDoH')
# 4. 将字符串标签编码为数值(推荐)
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)
print("类别顺序:", label_encoder.classes_)
# 5. 打乱数据
print("\n正在打乱数据顺序...")
X, y = shuffle(X, y, random_state=42)
# 6. 初始化随机森林模型
print("\n初始化随机森林模型...")
rf_model = RandomForestClassifier(
n_estimators=100,
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
random_state=42,
verbose=1,
n_jobs=-1
)
# 7. 定义评估指标
scoring = {
'accuracy': make_scorer(accuracy_score),
'precision': make_scorer(precision_score, pos_label=0), # 根据实际编码调整
'recall': make_scorer(recall_score, pos_label=0),
'f1': make_scorer(f1_score, pos_label=0),
'roc_auc': make_scorer(roc_auc_score, needs_proba=True)
}
# 8. 5折交叉验证
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 9. 执行交叉验证(带进度条)
print("\n开始5折交叉验证...")
results = {
}
for metric_name in tqdm(scoring, desc="交叉验证进度"):
scores = cross_val_score(rf_model, X, y, cv=cv, scoring=scoring[metric_name])
results[metric_name] = scores.mean()
print(f"{
metric_name}: {
scores.mean():.4f} (±{
scores.std():.4f})")
# 10. 输出结果
print("\n平均性能评估:")
for metric, score in results.items():
print(f"{
metric}: {
score:.4f}")
# 11. 训练最终模型(在整个数据集上)
print("\n训练最终模型...")
rf_model.fit(X, y)
print("模型训练完成!")
# 12. 保存模型
dump(rf_model, 'random_forest_model.joblib')
print("模型已保存为 'random_forest_model.joblib'")
- 验证代码
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from joblib import load
from sklearn.preprocessing import LabelEncoder
# 1. 加载数据
print("正在加载数据...")
data = pd.read_csv(r"CIRA-CIC-DoHBrw-2020-and-DoH-Tunnel-Traffic-HKD\l1-total-add.csv")
print(f"数据加载完成,总样本数: {
len(data)}")
# 2. 清理数据
print("\n正在清理数据...")
data = data.dropna()
print(f"清理后剩余样本数: {


1542

被折叠的 条评论
为什么被折叠?



