zoukankan      html  css  js  c++  java
  • PAC在异常检测中的应用

    注:资料均来源于网络,本文只做知识分享,如侵立删,谢谢。

    PAC算法背景简述:

    在许多领域的研究与应用中,通常需要对含有多个变量的数据进行观测,收集大量数据后进行分析寻找规律。多变量大数据集无疑会为研究和应用提供丰富的信息,但是也在一定程度上增加了数据采集的工作量。更重要的是在很多情形下,许多变量之间可能存在相关性,从而增加了问题分析的复杂性。如果分别对每个指标进行分析,分析往往是孤立的,不能完全利用数据中的信息,因此盲目减少指标会损失很多有用的信息,从而产生错误的结论。

    因此需要找到一种合理的方法,在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量之间存在一定的相关关系,因此可以考虑将关系紧密的变量变成尽可能少的新变量,使这些新变量是两两不相关的,那么就可以用较少的综合指标分别代表存在于各个变量中的各类信息。主成分分析与因子分析就属于这类降维算法。
    来源:https://blog.csdn.net/program_developer/article/details/80632779 

    类似方法包括:奇异值分解(SVD)、主成分分析(PCA)、因子分析(FA)、独立成分分析(ICA)

    Principle Component Analysis是主成分分析,简称PCA。它的应用场景是对数据集进行降维。降维后的数据能够最大程度地保留原始数据的特征(以数据协方差为衡量标准)。
    PCA的原理是通过构造一个新的特征空间,把原数据映射到这个新的低维空间里。PCA可以提高数据的计算性能,并且缓解"高维灾难"。高维灾难详见https://www.leiphone.com/news/201706/Vy3sQDFlI82dAGFF.html
    福利: 这里需要注意的一点是:经常有人把特征选择和特征抽取弄混。特征选择是直接对原始数据特征选择子集,具体的实现方法会在另一篇里写。而特征抽取会对原始数据特征进行变型,我们一般借助PCA方法来做特征抽取。
    关于PCA的数学公式隆重推荐这篇https://blog.csdn.net/aspirinvagrant/article/details/39737669

    用PCA进行异常检测的原理是:PCA在做特征值分解之后得到的特征向量反应了原始数据方差变化程度的不同方向,特征值为数据在对应方向上的方差大小。所以,最大特征值对应的特征向量为数据方差最大的方向,最小特征值对应的特征向量为数据方差最小的方向。原始数据在不同方向上的方差变化反应了其内在特点。如果单个数据样本跟整体数据样本表现出的特点不太一致,比如在某些方向上跟其它数据样本偏离较大,可能就表示该数据样本是一个异常点。

    具体实现步骤:

    1. 对于非异常label的数据取样,进行标准化处理,即scalar
    standard_scalar = StandardScaler()
    centered_training_data = standard_scalar.fit_transform(training_data[all_features])
    
    1. 初始化一个pca对象,用默认参数对所有成分进行保留。对标准化后的数据进行训练,得到基本的PCA模型。注意这里是无监督训练。
    pca = PCA()
    pca.fit(centered_training_data)
    
    1. 用pca()对步骤1中取样的数据(训练集)进行降维,计算数据样本在该方向上的偏离程度。
    transformed_data = pca.transform(training_data)
    y = transformed_data
    lambdas = pca.singular_values_
    M = ((y*y)/lambdas)
    

    这里,y是降维后的数据集。虽说是降维,但因为我们在初始化PCA的时候是对所有参数进行了保留,所以这里的y可以理解为将原始的数据X映射到了一个新的空间:y=X转换矩阵。转换矩阵就是把特征向量按大小顺序从左往右排好组成的过渡矩阵。
    这里,lambdas是训练集(training_data)的特征值集合。
    M是数据样本的偏离程度矩阵。如果原始数据是5000
    34,M还是5000*34。只不过M里的每一行数值代表了每个样本在重构空间里离每个特征向量的距离。
    注意:这里的lambdas主要起归一化的作用,这样可以使得不同方向上的偏离程度具有可比性。在计算了数据样本在所有方向上的偏离程度之后,为了给出一个综合的异常得分,最自然的做法是将样本在所有方向上的偏离程度加起来。

    1. 计算主成分和次成分的阈值。
      'q' 设为可以让前q个成分解释数据集50%的方差
    q = 5
    print "Explained variance by first q terms: ", sum(pca.explained_variance_ratio_[:q])
    

    r设为可以让r以后的成分对应的特征值小于0.2.

    q_values = list(pca.singular_values_ < .2)
    r = q_values.index(True)
    

    根据r和q,对M进行切片,再对每个样本点进行距离的计算。np.sum(major_components, axis=1)就是在算每一行(样本)的距离加总。

    major_components = M[:,range(q)]
    minor_components = M[:,range(r, len(features))]
    major_components = np.sum(major_components, axis=1)
    minor_components = np.sum(minor_components, axis=1)
    

    对切片后的数据集进行阈值计算

    components = pd.DataFrame({'major_components': major_components, 
                                   'minor_components': minor_components})
    c1 = components.quantile(0.99)['major_components']
    c2 = components.quantile(0.99)['minor_components']
    

    这里的c1, c2是人为设定的两个阈值,如果得分大于阈值则判断为异常。
    那么问题来了,我们不仅选了前50%最重要的成分,还选取了后面特征值小于0.2的最不重要的成分。为什么要这样做呢?原因如下:
    一般而言,前几个特征向量往往直接对应原始数据里的某几个特征,在前几个特征向量方向上偏差比较大的数据样本,往往就是在原始数据中那几个特征上的极值点。而后几个特征向量有些不同,它们通常表示某几个原始特征的线性组合,线性组合之后的方差比较小反应了这几个特征之间的某种关系。在后几个特征方向上偏差比较大的数据样本,表示它在原始数据里对应的那几个特征上出现了与预计不太一致的情况。到底是考虑全部特征方向上的偏差,前几个特征向量上的偏差,还是后几个特征向量上的偏差,在具体使用时可以根据具体数据灵活处理。
    当然,根据数据的情况,也可以只考虑数据在前 k 个特征向量方向上的偏差,或者只考虑后 r 个特征向量方向上的偏差。

    1. 计算出训练数据的阈值c1, c2,即上面得到的。

    2. 对于新的数据(测试集),用已经训练好的pca模型和standard_scalar模型进行transform。注意这里是用训练集的标准去tranform新的数据。

    data = standard_scalar.transform(df_full[all_features])
    transformed_data_test = pca.transform(data)
    y_test = transformed_data
    lambdas_test = pca.singular_values_
    M_test = ((y*y)/lambdas)
    

    然后r和q保持不变,还是用在训练集上算出来的r, q来对M_test进行切片

    major_components_test = M_test[:,range(q)]
    minor_components_test = M_test[:,range(r, len(features))]
    major_components_test = np.sum(major_components, axis=1)
    minor_components_test = np.sum(minor_components, axis=1)
    
    1. 制作分类器
    def classifier(major_components, minor_components):  
        major = major_components > c1
        minor = minor_components > c2    
        return np.logical_or(major,minor)
    
    1. 计算结果
    results = classifier(major_components=major_components, minor_components=minor_components)

    来源:CC思SS https://www.jianshu.com/p/e35a47913457
     
  • 相关阅读:
    详解Net Core Web Api项目与在NginX下发布
    一个C#开发者重温Java的心路历程
    C#使用Consul集群进行服务注册与发现
    让我们一起揭开算法的神秘面纱
    C#调用RabbitMQ实现消息队列
    C#调用OpenCV开发简易版美图工具
    我们是如何做go语言系统测试覆盖率收集的?
    高效测试框架推荐之Ginkgo
    性能测试必知必会
    如何保障Go语言基础代码质量?
  • 原文地址:https://www.cnblogs.com/asawang/p/10411934.html
Copyright © 2011-2022 走看看