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

2023-05-16

一、前言

    现实中,大部分数据都是无标签的,人和动物多数情况下都是通过无监督学习获取概念,故而无监督学习拥有广阔的业务场景。举几个场景:网络流量是正常流量还是攻击流量、视频中的人的行为是否正常、运维中服务器状态是否异常等等。有监督学习的做法是给样本标出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的例子。

快乐源于分享。

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

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 给CheckBox加上动画

    容我慢写
  • SEC物权链奖金制度分析

    启程SEC物权链是什么 xff1f 靠谱吗 xff1f SEC公链是什么 xff1f 物权链怎么样 xff1f 分析于下 xff1a 一 定位 xff1a 依托原中小企业上市包装辅导策划以及不良资产运作等业务 xff0c 130家实体资产分
  • Linux安装pqsql

    PostgreSQL安装部署 1 解压 tar xvf postgresql 10 5 1 linux x64 binaries tar gz 将解压文件重命名 mv pgsql postgresql PostgreSQL无法在root用户
  • 高考失败的程序员的逆袭

    高考作文不得出现真实的校名和人名 xff0c 所以本文不包含任何真实的校名 人名乃至赞助学校的企业名 xff0c 小说情节纯属虚构 如果情节与现实生活的事实雷同 xff0c 请谨记 xff1a 英国有个人叫牛顿 xff0c 德国有个人叫莱布
  • Android程序员的十大转型之路

    IT行业是一个瞬息万变的行业 xff0c 程序员是一个不进则退的职业 我作为一个Android程序员 xff0c 多年来一直保持随时可以转型其他技术领域的状态 xff0c 保持对新技术敏感的嗅觉 我先说说Android程序员不可能转型的几个
  • 【玖哥乱弹】编程语言间的斗争

    在初级程序员阶段 xff0c 每个人都不可避免遇到选择编程语言和职业方向的难题 我挑选了几个常见的编程语言 xff0c 分析了优缺点和职业方向 xff0c 以供想当程序员的人参考 C C 43 43 一句话概括 xff1a 大多数中国程序员
  • 【玖哥乱弹】成功的IT人士这样转型AI

    AlphaGo在与围棋世界冠军的对弈大获全胜 xff0c 不但让我们领略到了AI的巨大潜力 xff0c 还把AI推上了新的浪潮之巅 作为一个从即将过去的移动互联网时代走来的Android工程师 xff0c 我深深感受到自己成了传统行业 xf
  • 【玖哥乱弹】程序员如何成为别人的男朋友

    这个世界上程序员数量很多 xff0c 有女朋友的程序员在其中的比例却很少 究其原因 xff0c 不外乎大多数程序员根本不知道怎么才能成为别人的男朋友 成为别人的男朋友对于富二代和拆迁户很容易 xff0c 而对于程序员却很难 xff0c 潘驴
  • Linux库函数之opendir/closedir/readdir

    在Linux环境下 xff0c 有时候需要编写一些实用的工具 xff0c 比如检索功能 xff0c 最近在做病毒查杀应用开发 xff0c 涉及到批量扫描指定目录下文件 xff0c 因为要测试大量病毒文件 xff0c 该部分功能又是要通过AP
  • 编译ROS包出现错误 提示:invoking &quot;make cmake_check_build_system&quot; failed

    问题原因 xff1a 不同包里不允许有重名的节点 解决方案 xff1a 编辑重复的package文件夹里的CMakeLists txt文件 xff0c 对涉及到节点名称的代码进行修改 调试过程 xff1a 逐行分析问题代码 xff0c 问题
  • Centos7.X添加本地至Docker用户组

    根据上篇文章 Centos7 X通过rpm包安装Docker 安装好Docker之后 xff0c 发现必须使用sudo提权的方式或者直接使用root方式才能操作docker xff0c 实际上我们平常登录都是自己的账户 xff0c 这样操作
  • pytorch填坑:RuntimeError: cudnn RNN backward can only be called in training mode

    运行pytorch时 xff0c 训练很正常 xff0c 但是如果切换到eval 模式之后再继续训练 xff0c 发现报错 xff1a RuntimeError cudnn RNN backward can only be called i
  • Linux Mint 18.2安装VMware Workstation Pro 12

    VMware Workstation xff08 中文名 威睿工作站 xff09 是一款功能强大的桌面虚拟计算机软件 xff0c 提供用户可在单一的桌面上同时运行不同的操作系统 xff0c 和进行开发 测试 部署新的应用程序的最佳解决方案
  • ubuntu 使用nginx和gunicorn 部署服务

    1 安装gunicorn pip install gunicorn 2 创建下列名为web py的文件 xff1a from flask import Flask app 61 Flask name 64 app route 39 39 d
  • unable to execute ':/usr/local/cuda/bin/nvcc': No such file or directory

    仔细看下面的错误 xff1a unable to execute 39 usr local cuda bin nvcc 39 No such file or directory 是多了一个冒号 解决方式 xff1a vi bashrc 将
  • ubuntu系统设置ssh连接端口

    1 sudo vim etc ssh sshd config 将其中的 Port 22行取消注释 xff0c 并且将22换成自己想要的端口 xff0c 例如5677等 2 重启ssh服务 systemctl restart sshd net
  • anaconda 环境查找虚拟环境

    1 查找虚拟环境 conda info envs 会出现如下结果 2 选择虚拟环境 source activate tensorflow p36 3 退出虚拟环境 source deactivate 4 安装librosa conda in
  • python logging.info在终端没输出

    问题描述 在pyhton脚本中logging info 34 hello world 34 希望输出 39 hello world 39 但是在终端没有输出 解决方法 在文件开始的地方添加以下内容 span class pln style
  • ImportError: libSM.so.6: cannot open shared object file: No such file or directory解决

    运行如下命令即可解决 span class pln style margin 0px padding 0px border 0px font style inherit font weight inherit font size 6 lin
  • linux 查看版本信息

    1 uname xff0d a xff08 Linux查看版本当前操作系统内核信息 xff09 Linux ml 4 4 0 109 generic 132 Ubuntu SMP Tue Jan 9 19 52 39 UTC 2018 x8

随机推荐

  • 你真的了解串口吗(示波器串口波形分析)

    串口是最常用的外设了 xff0c 串口基本都是单片机的标配 串口通信只需要3条线组成 xff0c 分别为RX TX GND 下面将重点分析串口数据帧组成 一 串口通信帧 串口通信帧数据如此 xff0c 每帧由空闲位 起始位 数据位 校验位
  • C++——类和对象(4)

    作者 xff1a 几冬雪来 时间 xff1a 2023年5月6日 内容 xff1a C 43 43 类和对象内容讲解 目录 前言 xff1a 1 运算符重载 xff08 续 xff09 xff1a 2 赋值重载 xff1a 结尾 xff1a
  • Shell脚本-NF、FS(OFS)、RS(ORS)、NR(FNR)

    1 NF xff1a number of fileds xff08 字段 域的个数 xff09 整数 NF xff1a 取最后一列的字符串 xff0c 等同于 1 2 xff0c NF 1 NF等等 来看个例子吧 kdvmt 64 kdvm
  • 七月历程

    六月底老师通知让我提前返校 xff0c 去长春自我隔离一段时间 xff0c 这几天一直在收拾东西 空余时间没用来学习 xff0c 不过倒是上 steam 上买了个游戏 QAQ 2020 是个多灾多难的年份 澳洲火灾 xff0c 东非蝗虫肆虐
  • Shell脚本 - cut、sort、paste

    1 cut xff1a 用来提取文件的片段 d 后面指定分隔的符号 f 指定显示第几列 c 后面跟显示的字符1 n xff0c character b 后面根据显示的字节 xff0c byte kdvmt 64 kdvmt temp cat
  • 儿童诗词学习

    鹿寨 xff08 王维 xff09 空山不见人 xff0c 但闻人语响 返景入深林 xff0c 复照青苔上 独坐敬亭山 xff08 李白 xff09 众鸟高飞尽 xff0c 孤云独去闲 相看两不厌 xff0c 只有敬亭山 杂诗 xff08
  • 分页存储管理中的页表项长度是什么?

    看到很多人有疑问 xff1f 读到这里的时候我也有疑问的 在操作系统的分页存储管理方式中 xff0c 写道 xff1a 将页表始址与页号和页表项长度的乘积 相加 xff0c 便得到该表项在页表中的位置 于是可从中得到该页的物理块号 xff0
  • apt-get和aptitude

    1 apt get apt get是一条linux命令 xff0c 适用于deb包管理式的操作系统 xff0c 主要用于自动从互联网的软件仓库中搜索 安装 升级 卸载软件或操作系统 Advanced Package Tool xff0c 又
  • SPOOLing技术

    虚拟性是OS的四大特征之一 如果说可以通过多道程序技术 将一台物理CPU虚拟为多台 逻辑CPU xff0c 从而允许多个用户共享一台主机 xff0c 那么通过SPOOLing技术 便可将一台物理I O设备虚拟为多台 逻辑I O设备 xff0
  • C语言 gets()和scanf()函数的区别

    scanf 函数和gets 函数都可用于输入字符串 xff0c 但在功能上有区别 若想从键盘上输入字符串 34 hi hello 34 xff0c 则应该使用gets 函数 gets可以接收空格 xff1b 而scanf遇到空格 回车和Ta
  • vector删除元素之pop_back(),erase(),remove()

    向量容器vector的成员函数pop back 可以删除最后一个元素 而函数erase 可以删除由一个iterator指出的元素 xff0c 也可以删除一个指定范围的元素 还可以采用通用算法 remove 来删除vector容器中的元素 x
  • setw()使用方法

    1 setw xff08 int n xff09 只是对直接跟在 lt lt 后的输出数据起作用 xff0c 而在之后的 lt lt 需要在之前再一次使用setw xff1b xff08 Sets the number of charact
  • 主存到Cache直接映射、全相联映射和组相联映射

    Cache的容量很小 xff0c 它保存的内容只是主存 xff08 内存 xff09 内容的一个子集 xff0c 且Cache与主存的数据交换是以块 xff08 cache line xff09 为单位的 为了把信息放到Cache中 xff
  • 图的存储结构-十字链表和邻接多重表

    1 十字链表 对于有向图来说 xff0c 邻接表是有缺陷的 关心了出度问题 xff0c 想了解入度就必须要遍历整个图才能知道 反之 xff0c 逆邻接表 解决了入度 却不了解出度的情况 有没有可能把邻接表和逆邻接表结合起来呢 xff1f 答
  • U3D开发的逆天级大型游戏有哪些

    1 World of Diving 潜水世界 一款潜水游戏 潜水世界 xff1a http dx60 downyouxi com qianshuishijie zip 氛围不错 xff0c 不过细看建模好像不是特别精细的样子 2 The F
  • 中断屏蔽技术

    1 中断屏蔽技术 xff1a 主要用于多重中断 多重中断 xff1a xff08 中断嵌套 xff09 当CPU 正在执行某个中断服务程序时 xff0c 另一个中断源又提出了新的中断请求 xff0c 而CPU 又响应了这个新的请求 xff0
  • 互斥锁Mutex和信号量

    Mutex是一把钥匙 xff0c 一个人拿了就可进入一个房间 xff0c 出来的时候把钥匙交给队列的第一个 一般的用法是用于串行化对critical section代码的访问 xff0c 保证这段代码不会被并行的运行 Is a key to
  • TCP/UDP的Socket编程接口详解

    socket概述 xff1a socket是在应用层和传输层之间的一个抽象层 xff0c 它把TCP IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信 socket起源于UNIX xff0c 在Unix一切皆文件哲学的
  • release版本程序调用libvlc接口出现异常

    release版本程序调用libvlc接口出现异常 super raymond lu at gmail dot com 转载请注明出处http blog csdn net raymond lu rl article details 7515
  • 对基于深度神经网络的Auto Encoder用于异常检测的一些思考

    一 前言 现实中 xff0c 大部分数据都是无标签的 xff0c 人和动物多数情况下都是通过无监督学习获取概念 xff0c 故而无监督学习拥有广阔的业务场景 举几个场景 xff1a 网络流量是正常流量还是攻击流量 视频中的人的行为是否正常