Search code examples
pythontensorflowmachine-learningconv-neural-networkautoencoder

Peak Detection of Partial Discharges with CNN


This is one of my first posts here so if I make any mistakes or don't follow some guidelines please be considerate.

For my bachelors thesis im trying to create a CNN with tensorflow which has the ability to do basic peak detection like scipy.find_peaks for example. My input data consists of 2 numpy arrays with timeseries data of different partial discharges combined together with both their current channel and a light channel of the oscilloscope. My y labels for classifying are also 2 arrays with the same shape but are 0 when there is no peak to detect and 1 when there is a peak. So you can already guess the output is very sparse.

Input features Labels

I batch this dataset and feed it into the neural network for the structure and code check further down. As loss function I use BinaryFocalCrossentropy from keras and as metric precision. After that I complie the model and round the prediction to get the indices of where the peaks are. The problem is the predictions are wrong and far away from what they should be but I think peak detection with a CNN should be possible. What am I doing wrong here?

Down below is the code and here is a link to a onedrive folder with my code and the datasets:text

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]))

I also already tried to approach the same problem with an autoencoder but that also doesn't seem to work. You can find the code for the autoencoder also in the onedrive folder.

In my opinion this problem should be solvable and I don't know what I'm missing right now because this task shouldn't be too hard for a CNN.


Solution

  • Since your output is of the same shape as your input, and you want to classify each point, maybe you should try a unet, similar to this one: enter image description here Something like the following (added normalization, and some noise and dropout to prevent overfitting):

    skip_connections = []
    kernel_size = 7
    pool_size = 10
    deepth = 3
    def units(i: int): return 32*2**i
    norm_layer = layers.Normalization()
    norm_layer.adapt(x_train_slices)
    inputs = layers.Input(shape=(input_dim, 2))
    x = norm_layer(inputs)
    x = layers.GaussianNoise(stddev=0.01)(x)
    for i in range(deepth):
        x = layers.Conv1D(units(i), kernel_size, activation='relu', padding='same')(x)
        x = layers.BatchNormalization()(x)
        x = layers.SpatialDropout1D(.3)(x)
        x = layers.Conv1D(units(i), kernel_size, activation='relu', padding='same')(x)
        x = layers.BatchNormalization()(x)
        skip_connections.append(x)
        #x = layers.Conv1D(units(i), kernel_size, strides=pool_size, activation='relu', padding='same')(x)
        x = layers.MaxPool1D(pool_size)(x)
        x = layers.BatchNormalization()(x)
    x = layers.SpatialDropout1D(.3)(x)
    x = layers.Conv1D(units(deepth+1), kernel_size, activation='relu', padding='same')(x)
    x = layers.BatchNormalization()(x)
    x = layers.Conv1D(units(deepth+1), kernel_size, activation='relu', padding='same')(x)
    x = layers.BatchNormalization()(x)
    for i in range(deepth-1, -1, -1):
        x = layers.Conv1DTranspose(units(i), kernel_size, strides=pool_size, activation='relu', padding='same')(x)
        skip_x = skip_connections.pop()
        x = layers.Concatenate(axis=-1)([x, skip_x])
        x = layers.BatchNormalization()(x)
        x = layers.Conv1D(units(i), kernel_size, activation='relu', padding='same')(x)
        x = layers.BatchNormalization()(x)
        x = layers.Conv1D(units(i), kernel_size, activation='relu', padding='same')(x)
        x = layers.BatchNormalization()(x)
    
    
    x = layers.Conv1D(2, kernel_size, activation='sigmoid', padding='same')(x)
    model = tf.keras.Model(inputs=inputs, outputs=x)
    

    Which gives some results:

    Epoch 150/1000
    5/5 [==============================] - 2s 330ms/step - loss: 1.5309e-05 - 
    precision: 0.0903 - val_loss: 1.5629e-05 - val_precision: 1.0000
    Epoch 151/1000
    5/5 [==============================] - 2s 329ms/step - loss: 1.6166e-05 - 
    precision: 0.0681 - val_loss: 1.4105e-05 - val_precision: 0.0938
    Epoch 152/1000
    5/5 [==============================] - 2s 329ms/step - loss: 1.3713e-05 - 
    precision: 0.1277 - val_loss: 1.5543e-05 - val_precision: 1.0000
    Epoch 153/1000
    5/5 [==============================] - 2s 330ms/step - loss: 1.3353e-05 - 
    precision: 0.2615 - val_loss: 1.4101e-05 - val_precision: 0.4091
    Epoch 154/1000
    5/5 [==============================] - 2s 331ms/step - loss: 1.4418e-05 - 
    precision: 0.1412 - val_loss: 1.4621e-05 - val_precision: 0.8333