|
|
před 1 měsícem | |
|---|---|---|
| .. | ||
| README.md | před 4 měsíci | |
| detection.py | před 1 měsícem | |
| isolation_forest_models.pkl | před 5 měsíci | |
| scaler.pkl | před 5 měsíci | |
| three_sigma_model.pkl | před 5 měsíci | |
版本: 1.0.0
最后更新: 2025-11-04
功能: 对双膜系统(UF超滤 + RO反渗透)关键运行指标进行实时异常检测
异常检测模块用于监测双膜水处理系统的关键运行指标,及时发现设备异常、性能退化等问题。
models/anomaly_detection/
├── detection.py # 主程序(训练+预测)
├── README.md # 本文档
├── scaler.pkl # 数据归一化器(训练产物)
├── isolation_forest_models.pkl # 孤立森林模型(训练产物)
├── three_sigma_model.pkl # 3σ统计模型(训练产物)
└── datasets_export_xishan/ # 数据目录(需自行准备)
├── data_export5_1.csv # UF渗透率数据
├── data_export8_1.csv # RO1/RO2压差数据
├── data_export9_1.csv # RO3/RO4压差数据
├── data_export11_1.csv # RO产水流量数据
└── ... (data_export*_2.csv ~ data_export*_26.csv)
data_export*_1.csv ~ data_export*_26.csv)scaler.pkl,用于在线预测n_estimators=100, contamination='auto'n_sigma=3(可调)nu=0.05, kernel='rbf'-1=异常, 1=正常| 指标名称 | 含义 | 单位 | 正常范围 |
|---|---|---|---|
| UF1Per | UF1膜渗透率 | - | 根据历史数据确定 |
| UF2Per | UF2膜渗透率 | - | 根据历史数据确定 |
| UF3Per | UF3膜渗透率 | - | 根据历史数据确定 |
| UF4Per | UF4膜渗透率 | - | 根据历史数据确定 |
| 指标名称 | 含义 | 单位 | 异常情况 |
|---|---|---|---|
| C.M.RO1_DB@DPT_1 | RO1一段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO1_DB@DPT_2 | RO1二段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO2_DB@DPT_1 | RO2一段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO2_DB@DPT_2 | RO2二段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO3_DB@DPT_1 | RO3一段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO3_DB@DPT_2 | RO3二段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO4_DB@DPT_1 | RO4一段压差 | kPa | 压差过高→膜污堵 |
| C.M.RO4_DB@DPT_2 | RO4二段压差 | kPa | 压差过高→膜污堵 |
| RO1_CSFlow | RO1产水流量 | m³/h | 流量异常→泵/阀问题 |
| RO2_CSFlow | RO2产水流量 | m³/h | 流量异常→泵/阀问题 |
| RO3_CSFlow | RO3产水流量 | m³/h | 流量异常→泵/阀问题 |
| RO4_CSFlow | RO4产水流量 | m³/h | 流量异常→泵/阀问题 |
共16个监测指标
graph TB
subgraph 数据加载阶段
A[开始] --> B1[读取data_export5_*.csv]
A --> B2[读取data_export8_*.csv]
A --> B3[读取data_export9_*.csv]
A --> B4[读取data_export11_*.csv]
B1 --> C[提取UF1Per/UF2Per/UF3Per/UF4Per]
B2 --> D[提取RO1/RO2的DPT_1和DPT_2]
B3 --> E[提取RO3/RO4的DPT_1和DPT_2]
B4 --> F[提取RO1~RO4的CSFlow]
C --> G[纵向合并所有批次数据]
D --> G
E --> G
F --> G
end
subgraph 数据预处理阶段
G --> H[DataFrame: 16个监测指标列]
H --> I[MinMaxScaler归一化到0-1区间]
I --> J[保存scaler.pkl]
end
subgraph 模型训练阶段
J --> K{逐列训练}
K --> L1[孤立森林训练]
K --> L2[3σ统计计算]
K --> L3[One-Class SVM训练可选]
L1 --> M1[遍历16个指标列]
M1 --> N1[每列fit一个IsolationForest模型]
N1 --> O1[保存isolation_forest_models.pkl]
L2 --> M2[遍历16个指标列]
M2 --> N2[每列计算mean和std]
N2 --> O2[保存three_sigma_model.pkl]
L3 --> M3[遍历16个指标列]
M3 --> N3[每列fit一个OneClassSVM模型]
N3 --> O3[保存one_class_svm_models.pkl]
end
subgraph 异常检测阶段
O1 --> P[加载新数据]
O2 --> P
O3 --> P
P --> Q[使用scaler归一化新数据]
Q --> R{选择模型预测}
R --> S1[孤立森林预测]
R --> S2[3σ预测]
R --> S3[One-Class SVM预测]
S1 --> T[结果融合可选]
S2 --> T
S3 --> T
T --> U[输出预测结果: -1异常/1正常]
U --> V[结束]
end
sequenceDiagram
participant User as 用户
participant Script as detection.py
participant Data as 数据文件
participant Model as 模型
participant Result as 预测结果
Note over User,Result: 训练阶段
User->>Script: 运行python detection.py
Script->>Data: 批量读取26批次CSV
Data-->>Script: 返回DataFrame(N行×16列)
Script->>Script: MinMaxScaler归一化
Script->>Script: 保存scaler.pkl
loop 对每个指标列
Script->>Model: 训练IsolationForest
Script->>Model: 计算3σ统计量
Script->>Model: 训练OneClassSVM(可选)
end
Script->>Script: 保存所有模型.pkl
Script-->>User: 训练完成
Note over User,Result: 预测阶段
User->>Script: 提供新数据DataFrame
Script->>Script: 加载scaler.pkl和模型
Script->>Script: 归一化新数据
loop 对每个指标列
Script->>Model: 调用predict()
Model-->>Script: 返回-1或1
end
Script->>Result: 生成预测DataFrame
Result-->>User: 返回异常检测结果
| 文件模板 | 包含指标 | 数量 |
|---|---|---|
data_export5_{i}.csv |
UF1Per, UF2Per, UF3Per, UF4Per | 4列 |
data_export8_{i}.csv |
RO1/RO2的DPT_1和DPT_2 | 4列 |
data_export9_{i}.csv |
RO3/RO4的DPT_1和DPT_2 | 4列 |
data_export11_{i}.csv |
RO1~RO4的CSFlow | 4列 |
其中 {i} 为批次号,范围 1~26
UF1Per,UF2Per,UF3Per,UF4Per
0.85,0.87,0.83,0.86
0.84,0.86,0.82,0.85
...
# 安装依赖
pip install numpy pandas scikit-learn joblib matplotlib
# 确认目录结构
cd models/anomaly_detection/
ls datasets_export_xishan/ # 确认数据文件存在
# 运行主程序
python detection.py
输出示例:
开始加载数据...
成功读取: data_export5_1.csv
成功读取: data_export8_1.csv
...
数据合并完成,总样本数: 125680
开始数据归一化...
归一化器已保存为 scaler.pkl
开始训练孤立森林模型...
已训练 UF1Per 的孤立森林模型
已训练 UF2Per 的孤立森林模型
...
孤立森林模型已保存为 isolation_forest_models.pkl
开始训练3σ模型...
已计算 UF1Per 的3σ统计量
...
3σ模型已保存为 three_sigma_model.pkl
所有模型训练和保存完成!
import pandas as pd
import joblib
# 加载模型和归一化器
scaler = joblib.load("scaler.pkl")
if_model = joblib.load("isolation_forest_models.pkl")
ts_model = joblib.load("three_sigma_model.pkl")
# 准备新数据(需包含所有16个指标列)
new_data = pd.DataFrame({
'UF1Per': [0.85, 0.82],
'UF2Per': [0.87, 0.84],
# ... 其他14个指标
})
# 归一化
normalized_data = scaler.transform(new_data)
normalized_df = pd.DataFrame(normalized_data, columns=new_data.columns)
# 孤立森林预测
if_predictions = if_model.predict(normalized_df)
print("孤立森林预测:", if_predictions)
# 3σ预测
ts_predictions = ts_model.predict(normalized_df, n_sigma=3)
print("3σ预测:", ts_predictions)
# 结果融合(投票法)
# -1=异常, 1=正常
final_predictions = (if_predictions + ts_predictions) // 2
原理:
优点:
缺点:
参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
| n_estimators | 100 | 树的数量 |
| contamination | 'auto' | 异常比例(自动估计) |
| random_state | 42 | 随机种子 |
原理:
优点:
缺点:
参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
| n_sigma | 3 | 阈值系数(推荐2~4) |
原理:
优点:
缺点:
参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
| nu | 0.05 | 异常比例上界(5%) |
| kernel | 'rbf' | 径向基核函数 |
| gamma | 'scale' | 核系数(自动) |
启用方法:
detection.py 中取消 OneClassSVMModel 类的注释(第166-203行)main() 函数中相关代码的注释(第227-250行)# detection.py 第14行
data_folder = "datasets_export_xishan"
# detection.py 第22-29行
file_info = {
"data_export5_{}.csv": ["UF1Per", "UF2Per", "UF3Per", "UF4Per"],
"data_export8_{}.csv": ["C.M.RO1_DB@DPT_1", "C.M.RO1_DB@DPT_2",
"C.M.RO2_DB@DPT_1", "C.M.RO2_DB@DPT_2"],
"data_export9_{}.csv": ["C.M.RO3_DB@DPT_1", "C.M.RO3_DB@DPT_2",
"C.M.RO4_DB@DPT_1", "C.M.RO4_DB@DPT_2"],
"data_export11_{}.csv": ["RO1_CSFlow", "RO2_CSFlow", "RO3_CSFlow", "RO4_CSFlow"]
}
孤立森林:
# detection.py 第98行
model = IsolationForest(
n_estimators=100, # 增大→更稳定,但训练慢
contamination='auto', # 或设置具体值如0.05
random_state=42
)
3σ统计:
# 预测时调整
ts_predictions = ts_model.predict(normalized_df, n_sigma=3)
# n_sigma=2 → 更敏感(95%覆盖)
# n_sigma=4 → 更宽松(99.99%覆盖)
| 文件名 | 大小 | 说明 |
|---|---|---|
| scaler.pkl | ~2KB | MinMax归一化器 |
| isolation_forest_models.pkl | ~500KB | 孤立森林模型(16个) |
| three_sigma_model.pkl | ~1KB | 3σ统计量(16个指标的μ和σ) |
DataFrame示例:
| UF1Per | UF2Per | C.M.RO1_DB@DPT_1 | ... |
|---|---|---|---|
| 1 | 1 | -1 | ... |
| 1 | -1 | 1 | ... |
| 1 | 1 | 1 | ... |
1: 正常-1: 异常# 统计每个指标的异常率
anomaly_rate = (predictions == -1).sum() / len(predictions)
print(f"异常率: {anomaly_rate * 100:.2f}%")
# 投票法:两个模型都判定为异常才认为异常
consensus = ((if_pred == -1) & (ts_pred == -1)).astype(int)
consensus = np.where(consensus, -1, 1)
# 找出连续异常的时间段(需要时间戳列)
anomaly_indices = np.where(predictions == -1)[0]
print(f"异常点索引: {anomaly_indices}")
原因: datasets_export_xishan/ 目录不存在或文件命名不符合规则
解决:
# 检查目录
ls datasets_export_xishan/
# 确认文件命名格式
# 正确: data_export5_1.csv, data_export8_2.csv
# 错误: data_export5-1.csv, export5_1.csv
NameError: name 'OneClassSVMModel' is not defined原因: One-Class SVM 默认被注释
解决:
detection.py原因: 数据量过大(26批次 × 数万样本)
解决:
# detection.py 第208行后添加下采样
merged_data = load_and_merge_data()
# 随机采样50%数据
merged_data = merged_data.sample(frac=0.5, random_state=42)
原因:
解决:
# 调整contamination参数
model = IsolationForest(
n_estimators=100,
contamination=0.05, # 假设5%为异常
random_state=42
)
原因: 数据不服从正态分布(偏态、双峰)
解决:
# 查看数据分布
import matplotlib.pyplot as plt
df['UF1Per'].hist(bins=50)
plt.show()
# 考虑数据变换(如log变换)或使用孤立森林
方法:
多模型对比: 比较IF、3σ、OCSVM的一致性
from sklearn.metrics import classification_report
# 需要人工标注的真实标签
y_true = [1, 1, -1, 1, -1, ...]
y_pred = if_model.predict(test_data)
print(classification_report(y_true, y_pred))
方案:
# 1. 封装为函数
def detect_anomaly(new_data):
"""
参数: new_data - DataFrame,包含16个指标列
返回: predictions - DataFrame,-1=异常, 1=正常
"""
scaler = joblib.load("scaler.pkl")
if_model = joblib.load("isolation_forest_models.pkl")
normalized = scaler.transform(new_data)
normalized_df = pd.DataFrame(normalized, columns=new_data.columns)
return if_model.predict(normalized_df)
# 2. 构建API(FastAPI示例)
from fastapi import FastAPI
app = FastAPI()
@app.post("/detect")
def detect(data: dict):
df = pd.DataFrame([data])
result = detect_anomaly(df)
return {"prediction": result.tolist()}