基于CNN的局部放电峰值检测模型预测异常问题排查问询
用TensorFlow构建CNN实现峰值检测的问题
首次发帖,如有不合规范之处请谅解。
为完成本科毕业论文,我尝试用TensorFlow构建CNN,实现类似scipy.find_peaks的基础峰值检测功能:
- 输入:两组numpy数组,对应示波器电流通道与光通道的局部放电时序数据
- 标签:两组同维度数组,无峰值处为0,有峰值处为1,数据极为稀疏
- 输入特征示意图:[输入特征]
- 标签示意图:[标签]
我将数据集分批次输入神经网络,采用Keras的BinaryFocalCrossentropy作为损失函数,precision作为评估指标,编译后对预测结果取整获取峰值位置,但预测结果与预期偏差极大。我认为CNN能完成这个任务,但找不到问题所在,也曾尝试自编码器但同样无效,相关代码和数据集可通过OneDrive获取。
以下是代码:
import numpy as np import pandas as pd import matplotlib.pyplot as plt import math import scipy.signal as ss import tensorflow as tf from functions import slice_up from datetime import datetime from keras import layers, losses, Sequential, metrics from keras.models import Model
df1=pd.read_feather('C:/Users/noahp/OneDrive - ETH Zurich/Schule/Fächer/Semester 7/Bachelor Arbeit/Data/train1.feather') df1=df1.iloc[:,1:] df2=pd.read_feather('C:/Users/noahp/OneDrive - ETH Zurich/Schule/Fächer/Semester 7/Bachelor Arbeit/Data/val.feather') df2=df2.iloc[:,1:] df3=pd.read_feather('C:/Users/noahp/OneDrive - ETH Zurich/Schule/Fächer/Semester 7/Bachelor Arbeit/Data/test.feather') df3=df3.iloc[:,1:]
input_dim=100000 overlap=10000 #overlapping of batches LR = 1e-3 #learning rate EPOCHS = 10 batch_size=5 alpha=8.5 #asymmetric loss constant test_index=3 test_length=100000 #length of signal for testing
def slice_up(data: np.array,input_dim: int, overlap: int) -> np.array: signal_len = len(data) n_slices = int((signal_len - input_dim)/(input_dim - overlap)) + 1 data_sliced = np.zeros([n_slices, input_dim, 2]) for i_example in range(n_slices): index = (input_dim-overlap)*i_example data_sliced[i_example, :, :] = data[index:index+input_dim,:] return data_sliced
x_train=np.array(np.transpose(np.array([df1.iloc[:,2],df1.iloc[:,3]]))) y_train=np.array(np.transpose(np.array([df1.iloc[:,0],df1.iloc[:,1]]))) x_val=np.array(np.transpose(np.array([df2.iloc[:,2],df2.iloc[:,3]]))) y_val=np.array(np.transpose(np.array([df2.iloc[:,0],df2.iloc[:,1]]))) x_test=np.array([np.transpose(np.array([df3.iloc[2,test_index][:test_length],df3.iloc[3,test_index][:test_length]]))]) x_train_slices = slice_up(data=x_train, input_dim=input_dim, overlap=overlap) y_train_slices = slice_up(data=y_train, input_dim=input_dim, overlap=overlap) x_val_slices = slice_up(data=x_val, input_dim=input_dim, overlap=overlap) y_val_slices = slice_up(data=y_val, input_dim=input_dim, overlap=overlap) y_train_binary=np.where(y_train != 0.0, 1, 0) y_val_binary=np.where(y_val != 0.0, 1, 0) y_train_slices_binary = slice_up(data=y_train_binary, input_dim=input_dim, overlap=overlap) y_val_slices_binary = slice_up(data=y_val_binary, input_dim=input_dim, overlap=overlap)
model = Sequential([ layers.Conv1D(4, 50, activation='relu',input_shape=(input_dim,2)), layers.MaxPooling1D(50), layers.Dropout(0.2), layers.BatchNormalization(), layers.Conv1D(2, 10, activation='relu'), layers.MaxPooling1D(5), layers.Dropout(0.2), layers.BatchNormalization(), layers.Conv1D(2, 50, activation='relu'), layers.Dropout(0.2), layers.BatchNormalization(), layers.Conv1D(2, 10, activation='relu'), #layers.Dropout(0.2), #layers.BatchNormalization(), #layers.Conv1D(2, 10, activation='relu'), layers.Flatten(), layers.Dense(500, activation='relu'), layers.Dropout(0.2), layers.Dense(500, activation='relu'), layers.Dense(input_dim*2, activation='sigmoid'), layers.Reshape((input_dim,2)) ]) model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=LR),loss=losses.BinaryFocalCrossentropy(gamma=alpha),metrics=[metrics.Precision()])
history = model.fit(x_train_slices, y_train_slices_binary,epochs=EPOCHS,batch_size=batch_size, validation_data=(x_val_slices, y_val_slices_binary),shuffle=True)
prediction = model.predict(x_test, verbose=1) plt.plot(np.around(prediction[0]))
问题排查与改进方向
先搞定数据预处理
时序数据没做归一化的话,CNN根本学不到有效特征。赶紧给输入的电流、光通道数据做标准化(比如sklearn.preprocessing.StandardScaler),把两个通道的数据拉到同一量级,不然模型会被数值大的通道牵着走。重构模型结构
- 现在的模型池化太狠:第一层50步长的池化直接把10万长度的序列砍到2000,峰值的局部细节全丢了。要么缩小池化步长,要么把大卷积核拆成多个小卷积核(比如把50拆成两个3x的卷积),保留更多局部特征。
- 别用全连接层硬怼回10万维度!全连接层会把时序的空间关联彻底破坏,改用全卷积网络(FCN),用转置卷积或者上采样层恢复序列长度,全程用卷积操作保留时序信息。
调整损失与评估逻辑
gamma=8.5太极端了,Focal Loss的gamma一般设2-5就够,太大的话模型会死磕少数峰值样本,反而忽略整体的特征规律。- 只看precision没用,不平衡数据下precision高可能是模型全输出0导致的。加上recall、F1-score、AUC这些指标,才能真正知道模型有没有学到东西。另外也可以给正样本加
class_weight,进一步平衡类别差异。
优化训练策略
- 10轮训练太少了,模型还没收敛呢。加到30-50轮,同时加Early Stopping,盯着验证集的损失,一旦不下降就停,防止过拟合。
- batch_size=5太小,梯度更新太抖,改成16或者32,训练会更稳定。
检查数据切片的正确性
确认slice_up切出来的输入和标签完全对齐,尤其是重叠部分的标签有没有错位,别辛辛苦苦训半天,数据本身就不匹配。自编码器为啥没用?
自编码器是用来重构输入的,你的任务是判别峰值,目标不匹配。如果非要用自编码器,得把它当异常检测用——把峰值当作“异常”,重点看峰值区域的重构误差,但这比直接用CNN做判别复杂多了,不如先把CNN的路子走通。
内容的提问来源于stack exchange,提问作者Noah Pfenniger
相关产品推荐
相关产品推荐

