news 2026/10/6 2:54:46

从规则库到ML模型:入侵检测中的贝叶斯、KNN与神经网络实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从规则库到ML模型:入侵检测中的贝叶斯、KNN与神经网络实战

简介:面向机器学习与网络安全的入门及进阶学习者,这套基于KDD CUP99数据集的入侵检测实战资源,整合了KNN、高斯贝叶斯、BP神经网络与决策树四种分类方法。项目从原始数据集中抽取约8万条样本,统一完成训练与测试,并分别输出二分类和五分类结果,采用5折交叉验证评估,可直接运行 classify.py 复现实验,适合毕业设计、课程设计或工程实训参考。压缩包共51个文件、3.41MB,主要包含Python源码、PNG结果图、HTML可视化报告、模型权重文件(pkl/pth)、CSV数据文件与Markdown说明文档,既有完整可运行代码,也有训练后的模型存档和混淆矩阵、ROC曲线等图表,便于对照学习与二次开发。已有93人学习下载,适合希望快速掌握KDD CUP99预处理、分类器对比及性能评估流程的读者。

1. 从规则库漏报到三类ML模型:入侵检测为什么绕不开贝叶斯、KNN和神经网络

真实环境里安全运营最烦的不是攻击太隐蔽,而是规则库越滚越大、误报越来越多,新变种一到又得连夜加规则。做入侵检测之所以绕不开机器学习路线,就是要让模型从流量或审计数据里自己总结“攻击长什么样”。KDD-CUP99是这个领域最经典的评测数据集,贝叶斯、KNN、神经网络恰好代表了概率推断、近邻学习和端到端非线性拟合三条不同路径。这篇文章不堆算法推导,按我实际跑通这条实验链路的路子来:数据清洗、模型实现、参数调优、避坑记录,最后给横向对比和集成验证。适合正在做安全方向课设、毕设或产品原型的工程师参考。

2. KDD-CUP99 数据工程:41维特征、标签编码与训练测试集划分

2.1 字段结构与攻击类别映射

KDD-CUP99 来自1999年前后的入侵检测评估项目,数据是模拟局域网上抓取的一批网络连接记录,每条连接被打上“正常”或具体攻击名。做实验时一般用它的 10% 训练子集,完整版本数据量太大,跑 KNN 这种惰性学习的模型会非常痛苦。

每条记录是一行逗号分隔的字段,前 41 列是特征,最后一列是标签。特征分成四组:第一组是连接基本属性,比如 duration、protocol_type、service、flag、src_bytes、dst_bytes;第二组是连接内容属性,比如登录失败次数、root_shell、su_attempted 等;第三组和第四组分别是基于时间和基于主机的统计特征,比如 count、serror_rate、dst_host_srv_count。离散特征主要是 protocol_type、service、flag,其余大多是数值型,还有少量取值只有 0/1 的二元特征。这类混合结构决定了后面不能用同一套预处理方式:离散特征要做编码,长尾数值特征要变换,连续特征要标准化。

标签层先把攻击名归并成五类:正常 normal、拒绝服务 DoS、探测 Probe、远程到本地 R2L、本地提权 U2R。这是 KDD-CUP99 评测的常规世界观,所有模型最终都在这个五分类上比。读数据时留意一点:原始标签列带点号后缀,比如 “normal.” “neptune.”,需要先清理。

2.2 读取数据、映射标签与训练测试切分的代码

常见做法是直接用 pandas 读文本,自己补列名。把压缩包解压后的 kddcup.data_10_percent.txt 放到当前目录,然后执行:

import pandas as pd import numpy as np from sklearn.model_selection import train_test_split col_names = [ 'duration', 'protocol_type', 'service', 'flag', 'src_bytes', 'dst_bytes', 'land', 'wrong_fragment', 'urgent', 'hot', 'num_failed_logins', 'logged_in', 'num_compromised', 'root_shell', 'su_attempted', 'num_root', 'num_file_creations', 'num_shells', 'num_access_files', 'num_outbound_cmds', 'is_host_login', 'is_guest_login', 'count', 'srv_count', 'serror_rate', 'srv_serror_rate', 'rerror_rate', 'srv_rerror_rate', 'same_srv_rate', 'diff_srv_rate', 'srv_diff_host_rate', 'dst_host_count', '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' ] df = pd.read_csv('kddcup.data_10_percent.txt', header=None, names=col_names + ['label']) df['label'] = df['label'].str.replace('.', '', regex=False) attack_map = { 'normal': 'normal', 'neptune': 'dos', 'smurf': 'dos', 'back': 'dos', 'teardrop': 'dos', 'pod': 'dos', 'land': 'dos', 'satan': 'probe', 'portsweep': 'probe', 'ipsweep': 'probe', 'nmap': 'probe', 'guess_passwd': 'r2l', 'ftp_write': 'r2l', 'imap': 'r2l', 'phf': 'r2l', 'multihop': 'r2l', 'warezmaster': 'r2l', 'warezclient': 'r2l', 'spy': 'r2l', 'buffer_overflow': 'u2r', 'loadmodule': 'u2r', 'perl': 'u2r', 'rootkit': 'u2r' } df['attack_type'] = df['label'].map(attack_map) df = df[df['attack_type'].notna()] print(df['attack_type'].value_counts())

这里的 attack_map 把具体攻击名归并成五类。map 之后如果出现 NaN,说明数据里存在本次映射没有覆盖的攻击名,直接 dropna 过滤掉,避免把未知类带进训练。value_counts 打印出来后你会发现类别分布极度不均衡:normal、neptune、smurf 占了大头,u2r 可能只有几十条。

接下来做特征编码和切分。一个关键原则是:先切分,再做任何编码和标准化,否则测试集信息会提前进入训练流程,后面模型指标全是虚的。

train_df, test_df = train_test_split( df, test_size=0.2, random_state=42, stratify=df['attack_type'] ) cat_cols = ['protocol_type', 'service', 'flag'] num_cols = [c for c in col_names if c not in cat_cols] X_train = pd.get_dummies( train_df.drop(columns=['label', 'attack_type']), columns=cat_cols ) X_test = pd.get_dummies( test_df.drop(columns=['label', 'attack_type']), columns=cat_cols ) X_test = X_test.reindex(columns=X_train.columns, fill_value=0) # 长尾特征做 log1p,减小极端值对距离和梯度的影响 for col in ['src_bytes', 'dst_bytes']: X_train[col] = np.log1p(X_train[col]) X_test[col] = np.log1p(X_test[col]) y_train = train_df['attack_type'] y_test = test_df['attack_type'] from sklearn.preprocessing import StandardScaler scaler = StandardScaler().fit(X_train) X_train_s = scaler.transform(X_train) X_test_s = scaler.transform(X_test)

get_dummies 先把三个离散列展开成多列,service 这个字段取值非常多,展开后特征维度会到三四百,这是正常现象。测试集 reindex 对齐训练集列,遇到没见过的 service 值补 0,这是处理新类别最常见的做法。src_bytes、dst_bytes 偏态严重,最大值能到几千万,不变换的话 KNN 的距离计算会被这两列直接主导。StandardScaler 只 fit 训练集,测试集用同一个 scaler 的 transform,这是基线实验里必须守住的红线。

到这里数据工程结束,得到的 X_train_s 和 X_test_s 进入任何机器学习模型都不需要再做额外处理。接下来的 KNN、朴素贝叶斯、神经网络都吃这份数据。

3. KNN 与朴素贝叶斯的最小可运行实现:距离度量、k值和概率平滑

把 KNN 和朴素贝叶斯放在一起讲,是因为它们代表了同一种工程思维的两种极端:KNN 不假设特征分布,纯靠样本间距离说话;朴素贝叶斯则做了很强的条件独立假设,用先验和似然相乘算概率。两者都在几分钟内能跑出基线结果,非常适合当作入侵检测实验的起点。

3.1 KNN 的两个关键参数:k 值与距离度量

KNN 在训练阶段几乎不花时间,它只是把样本存下来,真正的计算全部发生在预测阶段。新来一条连接记录时,模型在特征空间里找最近的 k 个邻居,让邻居投票决定它是 normal、dos、probe、r2l 还是 u2r。这套逻辑的问题在于:KDD-CUP99 的样本量在十万级,特征维度几百维,预测一条样本要跟所有训练样本算一遍距离,代价不低。

我一般会先调三个参数:k 值、weights 模式、距离度量。k 太小容易把单个噪声点当成决定项,k 太大又会让相距很远的样本也参与投票。weights 有两个常用选项,uniform 是周围 k 个邻居一视同仁,distance 是按距离倒数为近邻加权。类别不均衡时,uniform 很容易被数量占优的 dos 邻居带偏,distance 会让更近的少数类样本有更大话语权。metric 默认用欧氏距离,特征经过标准化后基本够用;曼哈顿距离在某些高维场景下更稳,但实测差距不大。

from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import classification_report knn = KNeighborsClassifier( n_neighbors=7, weights='distance', metric='euclidean', algorithm='brute_force', n_jobs=-1 ) knn.fit(X_train_s, y_train) # 测试集太大时 KNN predict 会很慢,先抽 10000 条评估 X_test_eval = X_test_s[:10000] y_test_eval = y_test.iloc[:10000].values y_pred_knn = knn.predict(X_test_eval) print(classification_report(y_test_eval, y_pred_knn, digits=4))

algorithm 参数值得单独说。sklearn 默认 auto,内部会自动判断用 KD 树还是暴力计算。特征维度在几百维时 KD 树的加速效果会很差,甚至自动退化成 brute_force,所以我直接显式指定 brute_force,配合 n_jobs=-1 让多核并行算距离,代码简单,速度也可控。k 值我会按 1、3、5、7、9、15 扫一遍,观察测试集上的宏平均 F1,而不是只看 overall accuracy。k=1 在训练集上几乎完美,测试集上震荡明显;k 在 5 到 15 之间的结果是相对稳的。调参时可以只抽三五千条样本快速试,确定再全量预测。

KNN 还有一个容易被忽略的点是特征维度的解释性。模型在几百维空间里找到的“近邻”,很难直接回答“为什么这条是攻击”,这对安全场景的告警解释是个短板。KNN 的优势是实现在所有基线模型里最简单,且不需要任何分布假设,适合先拿来做通道验证。

3.2 朴素贝叶斯的条件独立假设与零概率平滑

朴素贝叶斯对入侵检测的意义在于它给每条告警一个概率值,而不只是分类结果。它的公式底子是贝叶斯定理:P(攻击类型 | 特征) 等于先验 P(攻击类型) 乘上各个特征的条件概率 P(特征 | 攻击类型),再除以归一化常数。朴素两个字来自条件独立假设——它假定特征之间互不影响。真实网络流量里这个假设几乎不成立,比如 serror_rate 和 count 明显相关,所以理论上它不如神经网络能刻画复杂关系。但在入侵检测场景里,它仍然值得跑,因为训练快、预测快、概率输出天然适合做阈值告警。

from sklearn.naive_bayes import GaussianNB gnb = GaussianNB(var_smoothing=1e-9) gnb.fit(X_train_s, y_train) # 预测时取出各类别概率,方便后面调报警阈值 y_prob_gnb = gnb.predict_proba(X_test_s) y_pred_gnb = gnb.predict(X_test_s) print(gnb.classes_)

GaussianNB 是 Sklearn 里处理连续特征的朴素贝叶斯实现,它假设每个特征在每个类别下服从高斯分布,用训练数据估计均值和方差。前面的 log1p 变换加标准化,正好让 src_bytes、dst_bytes 这类长尾特征更接近高斯,这是影响贝叶斯效果的关键一步。

var_smoothing 是最值得调的参数,它会在每个特征的方差估计上叠加一个极小值,避免某些类别里某个特征完全不变导致方差为 0、概率直接爆炸。默认值通常够用,但训练数据里某一类样本特别少时,比如 u2r 只有几十条,把 var_smoothing 调到 1e-6 能减少数值抖动。如果遇到概率输出出现极端 0 和 1 的情况,优先检查这个参数。

predict_proba 输出的概率可以当成置信度来用。实际做自适应入侵检测时,我不会只用 argmax 取类别,而是对每个类别设一个置信度门槛,比如检测到 r2l 的概率超过 0.6 才告警,低于门槛的样本继续观察。这个思路就是自适应入侵检测的朴素实现。想更进一步,可以去看动态贝叶斯网络或稀疏贝叶斯学习,前者把时间片之间的状态转移建模进去,后者在高维特征里做自动选择,但在这份数据集上作为基线,朴素贝叶斯已经足够。

4. 神经网络入侵检测:前馈网络的结构设计、训练与过拟合排查

4.1 输入输出设计和隐藏层规模怎么定

标题里的神经网络,最直接的落地实现是前馈神经网络,也就是 BP 神经网络结构图里最常见的那种全连接结构。它不要求特征之间满足特定分布,也不依赖样本间的距离假设,而是通过多层非线性变换拟合决策边界。对 KDD-CUP99 这种单条连接记录的表格型数据,前馈网络是比 CNN、LSTM、图神经网络更自然的起点。LSTM 和 RNN 需要输入具备时间序列结构,一维卷积神经网络需要把特征组织成连续窗口,而这些数据每行就是一条独立连接,硬套序列模型反而会增加不必要的预处理复杂度。

输入维度就是编码后的列数,取决于 one-hot 后 service 展开了多少列,通常在三百到四百之间。输出固定为 5 个类别,最后一层用 softmax 得到概率分布。隐藏层我先给一个可复现的两层结构:第一层 128 个神经元,ReLU 激活,接 BatchNormalization 和 Dropout;第二层 64 个神经元,同样接归一化和 Dropout;最后接 5 节点的 softmax 输出层。神经元数量不需要一上来就给很大,数据十万量级、输入几百维,两层全连接足以学出有效的决策面。先跑小网络,再根据训练曲线决定是否加宽加深,这是我最常用的方式。

如果之后想把方向往序列模型延伸,可以考虑把每个源 IP 的连接记录按时间窗口重组,形成会话序列再喂给 LSTM 或一维卷积神经网络。那就完全是另一套特征工程了,KDD-CUP99 原始每条记录独立排列,不补充会话聚合信息,模型看不到时序上下文。

4.2 用 Keras 搭建前馈网络:模型代码与训练参数

先把标签转成整数编码,神经网络里 sparse_categorical_crossentropy 接收的是整数类编号,不需要手动做 one-hot 编码。然后定义模型结构并训练:

from sklearn.preprocessing import LabelEncoder from tensorflow import keras from tensorflow.keras.layers import Dense, Dropout, BatchNormalization from tensorflow.keras.callbacks import EarlyStopping le = LabelEncoder() y_train_enc = le.fit_transform(y_train) y_test_enc = le.transform(y_test) model = keras.Sequential([ keras.Input(shape=(X_train_s.shape[1],)), Dense(128, activation='relu'), BatchNormalization(), Dropout(0.3), Dense(64, activation='relu'), BatchNormalization(), Dropout(0.3), Dense(5, activation='softmax') ]) model.compile( optimizer=keras.optimizers.Adam(learning_rate=1e-3), loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) early_stop = EarlyStopping( monitor='val_loss', patience=5, restore_best_weights=True ) history = model.fit( X_train_s, y_train_enc, validation_split=0.15, batch_size=512, epochs=50, callbacks=[early_stop], verbose=1 )

这个代码块里的几个参数需要解释。BatchNormalization 放在 Dropout 之前,作用是把每层输出拉回到均值为 0、方差为 1 的分布里,网络训练会更稳,学习率可以给得稍微大一点。Dropout 0.3 表示每轮训练随机丢弃 30% 的神经元输出,这是防止全连接网络过拟合最有效的单点操作。validation_split=0.15 是从训练集尾部再切 15% 当验证集,注意它是从切分完的 X_train_s 里继续切,不碰测试集。EarlyStopping 盯的是 val_loss,patience 设为 5,意思是连续 5 轮验证损失不下降就停,并且恢复成历史最优权重,这是防止神经网络越练越偏的后悔药机制。

训练结束后用测试集评估时,要注意 label 的编码顺序。LabelEncoder 是按字母排序的,sklearn 里 KNN 和贝叶斯的 classes_ 也是排序后的类别名,三者顺序一致,后面做模型集成时概率才能直接相加。

4.3 训练曲线判读:什么迹象说明网络在过拟合

模型训练不是 fit 完就结束,我每次都会把 history 里的训练损失和验证损失拉出来看。正常状态是两条曲线一起下降然后走平;如果训练损失还在降,验证损失却开始掉头向上,这就是过拟合的经典信号。全连接网络参数量大,KDD-CUP99 里 u2r、r2l 类别样本又少,网络很容易把少数类样本直接记住而不是学出泛化模式。

遇到这种情况,我的调整顺序是:先加大 Dropout 到 0.4 甚至 0.5,再看要不要减小网络的宽度或层数,最后才考虑降低学习率。不要一上来就加数据,也不要盲目加层。另一个常用的观察指标是验证集上的宏平均 F1,而不是 accuracy。因为整体准确率在类别不平衡的数据上非常容易虚高,模型全预测成 normal 都能拿到九成以上,只有分类别看 F1 才能找到模型真正弱在哪一类。

神经网络在这种任务上往往能拿到比其他两个模型更高的整体 F1,但它的缺点是训练过程有随机性,不同随机种子结果波动明显,调参也更依赖经验。遇到同样代码两次结果不同,不用奇怪,先固定随机种子,再用多次训练取平均来评估。

5. 避坑记录:KDD-CUP99 实验里最容易翻车的五件事

5.1 数据泄漏:标准化拟合了全量数据

比较常见的翻车是把缩放器和编码器放在切分之前对整个数据集 fit 了一遍。现象是训练集和测试集准确率都很高,但把模型拿到新流量上一测,效果马上掉下来。原因是 scaler 在 fit 全量数据时已经偷偷看到了测试集的统计信息,测试集不再具有“未知数据”的意义。处理方式很简单:先 train_test_split,再用训练集 fit scaler,后续所有测试集操作都只调 transform。特征编码同理,get_dummies 不涉及统计量泄漏,但列对齐必须发生在测试集上。

5.2 样本不均衡:U2R 样本太少导致准确率虚高

KDD-CUP99 的类别分布天然是一边倒的,neptune 和 smurf 这类 DoS 攻击占了极大比例,u2r 样本少到可以忽略。现象是模型整体 accuracy 能到 99%,但看 u2r 的召回率可能只有 0.1。原因是分类器学会了把绝大多数样本判成 normal 或 dos 来压低损失,少数类根本得不到有效的学习信号。处理方式是先 stratify 切分,保证训练和测试里类别比例一致;然后评估时一定看按类别的召回率和宏平均 F1;必要时候对 dos 大类做下采样,或者复制少数类样本做上采样。神经网络训练时还可以给每个类别配 class_weight,让少数类的损失权重更高。

5.3 同源会话被随机切分,验证集指标虚高

现象是随机切分后模型在测试集上表现不错,部署到真实网络里却大跌眼镜。原因是 KDD-CUP99 里同一个 IP 的连续连接记录在特征上高度相似,随机切分会让这些相似样本一部分进训练集、一部分进测试集,模型等于在“背答案”。KNN 和神经网络对这种泄漏尤其敏感。处理方式是按连接会话或源 IP 分组建模:先按 src 字段聚合,再把整个组划到训练或测试的同一侧。没有现成的 session id 时,我一般会按源 IP 加时间窗口构造分组维度,再基于组做切分。牺牲一点训练样本量,换来的是接近真实分布的评估。

5.4 离散特征直接喂给 KNN:one-hot 距离被稀疏列主导

现象是 KNN 在 one-hot 编码后的数据上预测很慢,近邻解释起来也很怪。原因是 service 字段展开后产生了几十个稀疏列,欧氏距离计算被这些 0/1 列主导,真正的连续特征反而失声了。处理方式是混合特征时不要一刀切:连续特征做标准化,离散特征如果一定要 one-hot,可以考虑对距离度量做折中,或者把连续特征和离散特征拆开算加权距离。更省事的做法是只对 KNN 使用数值主导的特征子集,把离散特征作为附加判断条件。这个坑在贝叶斯模型里影响相对小,因为概率模型本身就是在条件概率上工作,稀疏列不会通过距离传染。

5.5 朴素贝叶斯遇上零概率:训练集没见过的组合直接预测为 0

现象是某条测试样本的特征组合在训练数据里没有出现过,predict_proba 输出里某一类的概率直接变成 0。原因是朴素贝叶斯把所有特征的条件概率相乘,任何一个条件概率为 0 都会让整个乘积归零。处理方式是给概率估计加平滑,高斯朴素贝叶斯用 var_smoothing,离散特征的变体则用拉普拉斯平滑参数 alpha。另一个更长远的处理是在特征工程阶段多做一步,先观察每个特征在各类别下的分布形态,对稀疏区块的类别做合并。

6. 让模型投入验证:混淆矩阵、交叉验证与三类模型的集成投票

6.1 按攻击类别看混淆矩阵,不只看准确率

模型都跑完后,我最先看的是测试集上的混淆矩阵,不是 accuracy。用 sklearn 的 classification_report 打印每个类别的 precision、recall、f1-score,重点盯三类:probe 能不能从 normal 里分出来,r2l 和 u2r 是不是几乎全军覆没。一般经验是 doS 和 probe 好分,r2l、u2r 因为训练样本太少,三个模型在这里都会明显退化。发现这一点不要太早下结论说模型不行,先做少数类增强,再重新对比,这属于算法的正常规律。

6.2 集成策略:概率平均在 KDD-CUP99 上最稳

三个模型决策边界完全不同,集成通常会提升稳定性。我一般不用硬投票,而是做概率平均:把 KNN、朴素贝叶斯、神经网络三者的 predict_proba 结果按类别取平均,再取 argmax。KNN 和贝叶斯互补性最强,一个重局部距离,一个重全局概率分布,集成后往往比单个模型更稳。代码上很简单:

ensemble_prob = ( knn.predict_proba(X_test_eval) + gnb.predict_proba(X_test_s[:10000]) + nn_prob[:10000] ) / 3 y_pred_ensemble = np.argmax(ensemble_prob, axis=1)

这里有个前提:三个模型输出的类别顺序必须一致。sklearn 的 classes_ 和 LabelEncoder 都是按字母排序,所以 normal、dos、probe、r2l、u2r 的顺序是固定的。如果不放心,在集成前打印一遍各模型的 classes_ 做核对。概率平均的另一个好处是能直接把这个平均概率当成告警分数,接入到自适应入侵检测的阈值体系里。

6.3 迁移到真实流量前,先解决特征对齐

从 KDD-CUP99 拿到的模型不能直接上生产,核心问题是特征对齐。真实网络流量没有现成的 conn 记录表,需要自己把 netflow 或全量抓包转成同样的 41 维统计特征,尤其是 count、serror_rate 这类依赖时间窗口的统计量,必须按连接会话重新聚合。我踩过一次坑:直接拿历史训练好的模型跑真实流量,特征对不上,召回率低到没法看,最后只能回炉重做特征工程。从那以后我每次搭入侵检测原型,都先花时间写特征对齐脚本,再谈模型调参。希望这个顺序能帮到你少走一段弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 2:51:52

大模型中的 Q4_K_M 含义

FP32/FP16/BF16浮点数介绍 normal value 公式:sign位宽度/exp位宽度/fraction位宽度 FP32 1/8/23FP16 1/5/10BF16 1/8/7 normal value 公式不包含 subnormal/NaN/Inf value (-1)^s ✖️ (1.fraction) ✖️ 2^(exp - bias)0.15625 0.125 0.03125 2^(-3) 2^(…

作者头像 李华
网站建设 2026/10/6 2:50:57

AI技术高速发展,翻译行业真的会被取代吗?

如果五年前问“机器翻译会不会取代翻译员”,很多人的答案可能还是:机器能翻大意,真正专业的内容还是得靠人。到了 2026 年,这个回答已经不够准确了。现在的大模型可以处理长文本、识别上下文,也能完成实时语音翻译。普…

作者头像 李华
网站建设 2026/10/6 2:50:39

HTML+CSS学习笔记.5

1关于CSS表格的属性: 之前说过使用border可以为CSS表格添加边框,使用上图的属性还可以进行对边框的一些拓展。border-width可以修改边框宽度,border-color可以修改边框颜色,border-style可以修改边框的风格(主要用于修改边框的样式…

作者头像 李华
网站建设 2026/10/6 2:50:05

J组模拟赛4补题报告

总分:第一题:30分钟,100分,第二题:30分钟,100分第三题:1.5小时,50分,第四题:30分钟,10分第一题和第二题成功AC,于是又用bfs做第三题&a…

作者头像 李华
网站建设 2026/10/6 2:49:20

HWASan 只有十六进制地址看不懂:HarmonyOS Native 崩溃符号化排查链

HWASan 只有十六进制地址看不懂:HarmonyOS Native 崩溃符号化排查链 这不是一个 API 的问题 测试包出现 use-after-free 报告,日志里有地址却没有可读源码位置;开发者换了一个新编译的 so 去解析,得到完全错误的函数。地址只有和…

作者头像 李华
网站建设 2026/10/6 2:49:17

通信数据与视觉结果的映射,核心是**在 PLC 内存模型和视觉结果之间建立一张精确的“地址表”**,让双方对每一个字节的含义达成一致

通信数据与视觉结果的映射,核心是在 PLC 内存模型和视觉结果之间建立一张精确的“地址表”,让双方对每一个字节的含义达成一致。映射做错了,PLC 收到的是错位的数字,产线会做出错误动作。 映射的三个层次 第一层:数据类…

作者头像 李华