对基于深度神经网络的Auto Encoder用于异常检测的一些思考

本文探讨了无监督学习在异常检测中的应用,重点介绍了AutoEncoder及其变种——变分AutoEncoder的工作原理与实践案例。

一、前言

    现实中,大部分数据都是无标签的,人和动物多数情况下都是通过无监督学习获取概念,故而无监督学习拥有广阔的业务场景。举几个场景:网络流量是正常流量还是攻击流量、视频中的人的行为是否正常、运维中服务器状态是否异常等等。有监督学习的做法是给样本标出label,那么标label的过程肯定是基于某一些规则(图片除外),既然有了规则,何必要机器学习?基于规则写程序就得了,到底是先有鸡,还是先有蛋?如果计算机可以自己在数据中发现规律,就解决了这个争论。那么基于深度神经网络的auto encoder,可以解决一部分问题。

二、Auto Encoder介绍

    Auto Encoder实际上是一个信息压缩的过程,把高维数据压缩至低维度,先来看一下PCA。(以下图片来自于台大李宏毅教授的ppt)

    34fe52a3a0f32e30a08d0ce2d03e0163c06.jpg

     PCA的解释:原向量x乘以矩阵W得到中间编码c,再乘以W的转置,得到x head,得到的x head希望与原x越接近越好,有一点要注意,从x到c的变换过程是线性的。

    Deep Auto Encoder和PCA类似,只是网络层数更深,变换是非线性的(因为可以加入一些非线性的激活函数),Deep Auto Encoder变成成了如下的样子:

0be9d68fe88df8da0286423d865a466f1de.jpg

    中间有个很窄的hidden layer的输出就是压缩之后的code,当然以bottle layer对称的W不必要互为转置,也不要求一定要用RBM初始化参数,直接train,效果也很好。

    下面来看一下,对于MNIST手写数字数据集用像素点、PCA、Deep Auto Encoder三种方式分别做tSNE的展现图

    2e9f5e704b73975c08b88daf598e9eb2510.jpg

    右上角为deep auto encoder之后做tSNE,所有的数字都分的很开,效果比较好。

    总结一下,PCA和Deep auto encoder所做的事都类似,就是把原数据压缩到一个低维向量,让后再反解回来,反解回来的值希望与原来的值越接近越好。
三、Auto Encoder用于异常检测
    对于自动编码器用于异常检测,可以参考《Variational Autoencoder based Anomaly Detection using Reconstruction Probability》这篇论文 ,论文地址:http://dm.snu.ac.kr/static/docs/TR/SNUDM-TR-2015-03.pdf ,论文标题大概可以这样翻译:基于变分自动编码器重建概率的异常检测。
    文中不是直接从变分自动编码器切入,而是先介绍自动编码器,论文言简意赅,我们先来看看论文中对Auto Encoder的训练过程的描述

   463a9fc46583d82fa8d1cff81e7a22d97d8.jpg
    说明:

        1、编码器fφ,decoder gθ
        2、损失函数说明:这里用二范数来表示了,二范数实际上是欧几里得距离,也就是均方误差,也就是希望解码出来的值和原值月接近越好。

        下面来看,如果将Auto Encoder用于异常检测,还是先看公式

    71940cef64b467d87a6a39599b45cc5d6d7.jpg

    说明:

        1、先用正常的数据集训练一个Auto Encoder

        2、用训练出的Auto Encoder计算异常数据的重建误差,重建误差大于某个阀值α,则为异常,否则则正常。

    文中有这样一段描述:

    Autoencoder based anomaly detection is a deviation based anomaly detection method using  semi-supervised learning. It uses the reconstruction error as the anomaly score. Data points  with high reconstruction are considered to be anomalies.Only data with normal instances are used to train the autoencoder

    这段描述指出了两点:

    1、半监督,用正常的数据训练一个Auto Encoder

    2、重建误差高的数据为异常数据

    普通Deep Auto Encoder有个缺陷,通俗的话来讲就是模型看过的类似的数据它知道,模型没看过的数据,几乎不可能能知道,那变分编码器,就可能解决了一部分问题,通过用分布相似来解释这个问题,请看下面的公式:

    9d56c2d1056874191768ff74981a0b63212.jpg

    整个训练的过程用通俗的话说明一下,首先从标准正态分布中随机L个数据和原x经过hφ函数后得到隐含变量z,注意这里是为每一个样本x都随机L个数据来生成z,loss函数告诉我们两件事情,

第一件,希望z的分布和给定了x的条件下z的分布越接近越好,第二件,希望给定了z的条件下反解回来的x和原分布越接近越好。本质上而言,VAE是给原x加上了随机噪声,同时希望可以反解回原来的值,

中间隐含变量就非常神奇了,可以是高斯分布,也可以是伯努利分布,用概率分布来编码,在自动生成中有妙用。

    那么VAE是如何用于异常检测的呢?请继续往下看。

 d4ea13aaf22af12e62ba7ffc41f2a7e3f15.jpg

    上面的训练过程,可以这样解释,给定x的条件下获取z的分布的平均值和方差,就得到了一个基于x的分布,从该分布中得到L个隐含变量z,通过z反解回x,得到重建概率,

    重建概率小于某个阀值为异常,否则则为正常。

四、一些思考

    论文中只给出了可以这样做以及这样做的效果,但是论文中没有解释为什么这样做。

    其实这个问题比较好类比,训练数据都是正常的,就好比一个人生活的圈子里只有猫,突然来了一条狗,他就肯定不认识了,只知道和猫不同,那么通过数学中的线性回归来类推一下,根据一批正常点拟合了一条直线,有一个游离于这个群体的点,自然离这条直线很远。同样的可以用数据边界来解释,神经网络见过了大量的正常数据,便学习到了数据的边界,游离于边界外的数据,自然也可以摘出来。

    但是同样有一个问题,既然原始样本集里已经有正常数据和异常数据了,通过有监督训练一个分类模型就够了。但是真实的场景里,我们是不知道怎么标注数据的,如果说可以标注,肯定是基于人工指定的规则,既然有了规则,就基于规则写if else(这里是针对数据维度比较小的场景)了,何必要机器来学习,但是规则制定的是否正确还两说,比方说金融领域常见的风险评估,某个用户是否可以放贷给他,已知该用户的各种信息,例如:年龄、性别、职业、收入、信用卡账单等等,这里只是举一个小例子。

    以上的疑问,总结起来就两点

    1、如何标注数据,到底是先有鸡还是先有蛋

    2、基于定死的规则标注的数据是否正确可用

    那么,能不能完全无监督的让机器学习出识别这些异常数据的规则,我觉得通过Auto Encoder是可以做到的,首先,对于异常识别的场景,正常的样本数肯定占大多数,异常的只是少数,把整个样本集完全扔给Deep Auto Encoder,让他去学习,同样可以找出异常数据。

    我写了一个小例子来验证我的观点。

    框架:DL4J

    我们有经典的数据集,根据天气判断是否打球,这里是从weka的data中复制的,

@attribute outlook {sunny, overcast, rainy}
@attribute temperature {hot, mild, cool}
@attribute humidity {high, normal}
@attribute windy {TRUE, FALSE}
@attribute play {yes, no}

@data
sunny,hot,high,FALSE,no
sunny,hot,high,TRUE,no
overcast,hot,high,FALSE,yes
rainy,mild,high,FALSE,yes
rainy,cool,normal,FALSE,yes
rainy,cool,normal,TRUE,no
overcast,cool,normal,TRUE,yes
sunny,mild,high,FALSE,no
sunny,cool,normal,FALSE,yes
rainy,mild,normal,FALSE,yes
sunny,mild,normal,TRUE,yes
overcast,mild,high,TRUE,yes
overcast,hot,normal,FALSE,yes
rainy,mild,high,TRUE,no

    利用DL4J构建一个多层全连接神经网络,代码如下

public class OneHotEncoder {

	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("sunny", 0);
		map.put("overcast", 1);
		map.put("rainy", 2);
		map.put("hot", 3);
		map.put("mild", 4);
		map.put("cool", 5);
		map.put("high", 6);
		map.put("normal", 7);
		map.put("TRUE", 8);
		map.put("FALSE", 9);
		map.put("yes", 0);
		map.put("no", 1);

		String data = new StringBuilder().append("sunny,hot,high,FALSE,no").append("\n")
				.append("sunny,hot,high,TRUE,no").append("\n").append("overcast,hot,high,FALSE,yes").append("\n")
				.append("rainy,mild,high,FALSE,yes").append("\n").append("rainy,cool,normal,FALSE,yes").append("\n")
				.append("rainy,cool,normal,TRUE,no").append("\n").append("overcast,cool,normal,TRUE,yes").append("\n")
				.append("sunny,mild,high,FALSE,no").append("\n").append("sunny,cool,normal,FALSE,yes").append("\n")
				.append("rainy,mild,normal,FALSE,yes").append("\n").append("sunny,mild,normal,TRUE,yes").append("\n")
				.append("overcast,mild,high,TRUE,yes").append("\n").append("overcast,hot,normal,FALSE,yes").append("\n")
				.append("rainy,mild,high,TRUE,no").toString();
		INDArray feature = Nd4j.zeros(new int[] { 9, 10 });
		INDArray featureTest = Nd4j.zeros(new int[] { 5, 10 });
		INDArray label = Nd4j.zeros(14, 2);
		String[] rows = data.split("\n");
		int index=0;
		int indexTest=0;
		for (int i = 0; i < rows.length; i++) {
			String[] cols = rows[i].split(",");
			if(cols[cols.length-1].equals("yes")){
				for (int j = 0; j < cols.length - 1; j++) {
					feature.putScalar(index, map.get(cols[j]), 1.0);
				}
				index++;
			}else{
				for (int j = 0; j < cols.length - 1; j++) {
					featureTest.putScalar(indexTest, map.get(cols[j]), 1.0);
				}
				indexTest++;
			}
			
			label.putScalar(i, map.get(cols[cols.length - 1]), 1.0);
		}
		
		DataSet dataSet = new DataSet( Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature), Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature));
		
		DataSet dataSetTest = new DataSet(featureTest, featureTest);

		MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(123).activation(Activation.TANH)
				.weightInit(WeightInit.XAVIER).updater(new Sgd(0.1)).l2(1e-4).list()
				.layer(0, new DenseLayer.Builder().nIn(10).nOut(20).build())
				.layer(1, new DenseLayer.Builder().nIn(20).nOut(2).build())
				.layer(2, new DenseLayer.Builder().nIn(2).nOut(20).build())
				.layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.MSE).activation(Activation.IDENTITY)
						.nIn(20).nOut(10).build())
				.backprop(true).pretrain(false).build();

		// run the model
		MultiLayerNetwork model = new MultiLayerNetwork(conf);
		model.init();
		model.setListeners(new ScoreIterationListener(1));
		LayerWorkspaceMgr mgr = LayerWorkspaceMgr.noWorkspaces();
		for (int i = 0; i < 10000; i++) {
			model.fit(dataSet);
			System.out.println(model.score(dataSetTest));

		}

	}
}

 上面有一段特殊代码如下,这里是把正常样本的数量加大,造成正负样本的不均衡

DataSet dataSet = new DataSet( Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature), Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature));
		

   训练之后的结果如下:

b213819a3460973ad3021cebcfd7db87179.jpg

    对于异常样本,Loss和整体的Loss有一个数量级的偏差,完全可以作为异常的检测,这里没有刻意的用正样本来训练,而是用正负样本一起训练,完全无监督,就达到了异常检测的效果,当然这适用于正负样本非常不均衡的场景。

    为什么可以这样做,我想了一个不用数学公式推导的解释,将写在下次博客中。

    最后,DL4J是一个非常优秀的Deeplearning框架,对于Java系的小伙伴想了解Deeplearning的,可以看看DL4J的例子。

快乐源于分享。

   此博客乃作者原创, 转载请注明出处

 

 

<think>好的,我现在要帮用户了解或实现基于深度神经网络的心电图异常检测方法。首先,我需要理清用户的需求。用户可能有一定的机器学习或深度学习基础,想要具体实现一个心电图异常检测的模型。用户提到的关键词是深度神经网络、心电图、异常检测、实现方法,所以我需要围绕这些点展开。 根据提供的引用资料,引用[2]和[3]提到了使用深度神经网络处理心电图数据,特别是端到端的DNN结构,直接处理原始ECG数据,无需复杂预处理。引用[4]则提到了使用自编码器进行异常检测。这些信息可以作为实现方法的基础。 首先,我需要确定使用哪种类型的深度神经网络。引用[2]提到的是33层卷积层,引用[3]用了34层网络,这说明卷积神经网络(CNN)在处理ECG时序数据上是有效的。另外,自编码器(Autoencoder)常用于异常检测,因为可以通过重构误差来判断异常,引用[4]的例子就是这种情况。所以可能需要比较这两种方法的优缺点,或者结合使用。 接下来,数据预处理部分。根据引用[3],他们的方法没有对ECG数据进行实质性预处理,直接使用原始数据。这可能意味着用户可以采用类似的策略,但也要考虑数据是否已经标准化,是否需要滤波去噪等。例如,动态心电信号可能有噪声,简单的预处理如标准化或归一化可能还是有必要的。 然后是模型架构。如果采用CNN,可以参考引用[2]的结构,设计多层卷积层,可能加入池化层、BatchNorm和Dropout来防止过拟合。如果是自编码器,编码器部分压缩特征,解码器重构输入,计算重构损失作为异常分数。 数据准备方面,需要分为训练集和测试集,并且确保异常样本在训练集中较少,或者仅使用正常样本训练自编码器。引用[4]提到训练数据是20个“好”心跳,测试数据包含3个“坏”心跳,这说明在异常检测中,可能使用无监督或半监督的方法,因为异常样本较少。 模型训练时,监督学习需要标签,而自编码器属于无监督。用户的问题是关于异常检测,可能数据中的异常样本较少,因此自编码器可能更适合。但如果有足够的标注数据,监督学习也可以考虑,比如分类正常和异常。 评估指标方面,异常检测常用精确率、召回率、F1分数,或者AUC-ROC曲线。对于自编码器,可以通过设定阈值来判断异常,需要调整阈值来平衡误报和漏报。 代码实现部分,需要给出一个示例。例如,使用TensorFlow或PyTorch搭建CNN或自编码器模型。例如,自编码器的代码可能包括编码器和解码器结构,使用MSE损失函数,训练完成后计算重构误差。 还要考虑数据来源,用户可能需要获取ECG数据集,比如MIT-BIH心律失常数据库,或者PhysioNet上的数据。引用[4]提到数据集的获取方式,可能需要指导用户如何获取数据。 另外,引用[3]提到模型的局限性,比如单导联ECG,可能影响性能,这点在实现时需要注意,是否用户的数据是单导联还是多导联,处理方式可能不同。 最后,需要生成相关问题,可能涉及模型优化、数据增强、超参数调优等,或者如何部署模型。 总结步骤:数据准备→模型选择(CNN或自编码器)→模型构建→训练→评估→调优。需要确保用户理解每个环节,并提供代码示例和可能的改进方向。</think>基于深度神经网络的心电图异常检测方法可分为以下步骤实现: ### 一、方法架构设计 $$DNN_{ECG} = f_{\theta}(X_{raw}) \rightarrow \{正常, 异常\}$$ 其中$X_{raw}$为原始心电图时序数据,$\theta$为网络参数[^2][^3] ### 二、关键实现步骤 1. **数据预处理**: - 标准化:$X_{norm} = \frac{X - \mu}{\sigma}$($\mu$为均值,$\sigma$为标准差) - 分割时序窗口:每个样本包含256个采样点(对应1.28秒@200Hz)[^2] 2. **模型构建**(以自编码器为例): ```python import tensorflow as tf class ECG_Autoencoder(tf.keras.Model): def __init__(self): super().__init__() self.encoder = tf.keras.Sequential([ tf.keras.layers.Conv1D(32, 5, activation='relu', input_shape=(256,1)), tf.keras.layers.MaxPooling1D(2), tf.keras.layers.Conv1D(64, 3, activation='relu'), tf.keras.layers.GlobalAveragePooling1D()]) self.decoder = tf.keras.Sequential([ tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Reshape((64, 2)), tf.keras.layers.Conv1DTranspose(64, 3, activation='relu'), tf.keras.layers.UpSampling1D(2), tf.keras.layers.Conv1DTranspose(1, 5, activation='sigmoid')]) def call(self, x): encoded = self.encoder(x) decoded = self.decoder(encoded) return decoded ``` 3. **异常检测机制**: $$异常分数 = \frac{1}{T}\sum_{t=1}^{T}(x_t - \hat{x}_t)^2$$ 当分数超过阈值$\epsilon$时判定为异常[^4] ### 三、实施要点 1. **数据特征**: - 使用单导联ECG信号(Ⅱ导联) - 输入维度:$batch\_size \times 256 \times 1$[^2] 2. **训练策略**: - 仅使用正常样本训练自编码器 - 优化目标:最小化重构误差$L = \frac{1}{N}\sum_{i=1}^{N}||x_i - \hat{x}_i||^2_2$ - 学习率:$1e^{-4}$,批量大小:32 3. **评估指标**: | 指标 | 公式 | 目标值 | |------------|-------------------------|--------| | 精确率 | $\frac{TP}{TP+FP}$ | >0.85 | | 召回率 | $\frac{TP}{TP+FN}$ | >0.90 | | F1-score | $\frac{2PR}{P+R}$ | >0.875 | ### 四、改进方向 1. 采用深度残差网络:$H(x) = F(x) + x$提升特征复用能力[^3] 2. 引入注意力机制: $$Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V$$ 3. 数据增强:添加高斯噪声$N(0,0.1)$、随机时间偏移
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值