异常检测主要目标是将异常事件与正常事件区分开来,因此才有了“异常”一词。本文将介绍基于声音信号的工业机械异常检测,使用的数据集是MIMII声音数据集,该数据集很容易在网上获得。
异常检测的任务可以通过多种方式实现。其中最简单的一种方法是将问题作为监督学习任务,并对正常和异常声音训练分类器。这种方法的问题是异常情况很少,相应地异常类的数据量有限,这样会对分类性能有很大的影响。还有一种无监督学习技术,在不使用标签的情况下就可以进行判断,这种方法使用一种常见的神经网络架构就是自编码器。
自编码器在深度学习中非常流行,并且已经成功地应用于许多任务中,例如噪声去除,数据压缩等。
自编码器
自编码器架构由三个块构成,即编码器块、隐空间和解码器块,如下图所示。模型学习数据的隐藏内部表示,该数据使用比原始数据更低的维度来描述数据集的信息。
异常检测
现在引入了自编码器后,可以利用该模型执行异常检测。首先使用机器在正常状态下运行的声音信号来训练构建的自编码器模型。然后将使用训练好的模型在错误阈值的帮助下执行异常检测。
因为我们这里使用声音数据集,所以需要从原始声音信号中提取特征作。声音的特征提起一般会使用梅尔频谱图。这里使用librosa包完成了如下所示的操作。
def extract_signal_features(signal, sr, n_mels=64, frames=5, n_fft=1024):
# Compute a mel-scaled spectrogram:
mel_spectrogram = librosa.feature.melspectrogram(
y=signal,
sr=sr,
n_fft=n_fft,
n_mels=n_mels
)
# Convert to decibel (log scale for amplitude):
log_mel_spectrogram = librosa.power_to_db(mel_spectrogram, ref=np.max)
# Generate an array of vectors as features for the current signal:
features_vector_size = log_mel_spectrogram.shape[1] - frames + 1
# Skips short signals:
dims = frames * n_mels
if features_vector_size < 1:
return np.empty((0, dims), np.float32)
# Build N sliding windows (=frames) and concatenate them to build a feature vector:
features = np.zeros((features_vector_size, dims), np.float32)
for t in range(frames):
features[:, n_mels * t: n_mels * (t + 1)] = log_mel_spectrogram[:, t:t + features_vector_size].T
return features
一旦提取了特征并创建了由正常声音组成的数据集,下一步就是设计自动编码器模型架构。
def autoencoder_baseline(input_dims):
#input layer
inputLayer = Input(shape=(input_dims,))
#Encoder block
x = Dense(128, activation="relu")(inputLayer)
x = Dense(64, activation="relu")(x)
#Latent space
x = Dense(32, activation="relu")(x)
#Decoder block
x = Dense(64, activation="relu")(x)
x = Dense(128, activation="relu")(x)
#Output layer
x = Dense(input_dims, activation=None)(x)
#Create and return the model
return Model(inputs=inputLayer, outputs=x)
然后使用准备好的数据集训练创建的模型,如下所示。
#Set model parameters
#Shape of the input data
input_shape = n_mels*frames
#Loss function
model_loss = 'mean_squared_error'
#Optimizer learning rate
lr = 1e-3
#Batch size and number of epochs to train the model
batch_size = 512
epochs = 30
#Create the baseline model and compile it with the hyperparameters
baseline_model = autoencoder_baseline(input_shape)
baseline_model.compile(loss=model_loss,
optimizer=Adam(learning_rate=lr))
#Model training
baseline_hist = baseline_model.fit(
train_data,
train_data,
batch_size=batch_size,
epochs=epochs,
verbose=2
)
模型的学习曲线如下图所示。从图中可以明显看出,模型损失开始时相当高,但随着训练的进行迅速下降。
模型训练好后就可以用来进行信号重建。这次要重建的信号既包含来自机器的正常声音,也包含异常声音。使用训练后的模型重建正常和异常声音对应的rmse损失使用下图中的直方图来描述。从图中可以明显看出,正常声音的rmse低于异常声音。
结果非常直观,因为模型在训练阶段没有异常声音的数据,这就是相应的rmse高于正常操作声音的原因。
所以我们可以将重构的rmse值与阈值进行比较,进行异常检测。阈值设置为正常声音的平均rmse,因为异常声音的rmse将高于此值。该模型使用标准分类指标进行评估,如精度,召回率,f1分数和准确性,如下所示。
precision recall f1-score support
Normal 0.73 0.78 0.76 120
Anomaly 0.77 0.72 0.74 120
accuracy 0.75 240
macro avg 0.75 0.75 0.75 240
weighted avg 0.75 0.75 0.75 240
可以看到效果还是和不错的,F1值已经达到了0.75.
总结
本文的完整代码可以在下面给出的github链接中访问。
https://avoid.overfit.cn/post/97d547d5df8e4d6daf7368ef73a9bc6e
作者:Naveed