异常检测算法 & PyOD算法库

  • 一般情况下,可以把异常检测看成是数据不平衡下的分类问题。但在现实情况中,异常检测问题往往是没有标签的,训练数据中并未标出哪些是异常点,因此必须使用无监督学习

  • 引入PyOD库, 提供20多种异常值检测算法的API

    pip install pyod
    

img

用PyOD工具库进行「异常检测」

  • 前人经验:(来自zhihu/csdn)
1. 不存在普遍意义上的最优模型,不过有些模型的表现一直不错,建议优先考虑。对于大数据量和高纬度的数据集,Isolation Forest算法的表现比较好。低维度小数据集上,简单算法KNN和MCD的表现不错。
2. LOF类的算法适用于局部区域空间问题,对于完整区域空间,KNN和Iforest更好。
3. KNN每次运行需要遍历所有数据,所以效率比较低,如果效率要求比较高,用聚类方法更好。
4. 对于不同种类的数据,没有哪一种算法是最好的,HBOS算法在某些数据集上的表现非常好,且运算速度很快。
5. ABOD综合效果最差,尽量不要用。
  • KNN & KNN_Average

    K近邻距离有3种method设置:

    1.距离第k近的点的距离(default)

    2.距离最近的k个点的平均距离

    3.距离最近的k个点中的中间距离(距离中位数)

img

  • CBLOF

    • LOF : Local Outlier Factor

      通过比较每个点p和其邻域点的密度来判断该点是否为异常点,如果点p的密度越低,越可能被认定是异常点。

      密度通过点之间的距离来计算的,点之间距离越远,密度越低,距离越近,密度越高。

      img

    • CBLOF:Cluster-Based Local Outlier Factor (聚类+LOF)

  • Feature Bagging

    • LOF + 集成学习:默认训练10个LOF作为子检测器,综合输出异常点预测结果(子检测器可以配置成其他基础算法,如KNN、HBOS)
  • Isolation Forest

    • 思想:很快划分到叶子节点的大概率是异常

      img

    • 算法步骤:

      1. 从训练数据中随机选择Ψ个点样本点作为subsample,放入树的根节点。
      2. 随机指定一个维度(attribute),在当前节点数据中随机产生一个切割点p——切割点产生于当前节点数据中指定维度的最大值和最小值之间。
      3. 以此切割点生成了一个超平面,然后将当前节点数据空间划分为2个子空间:把指定维度里小于p的数据放在当前节点的左孩子,把大于等于p的数据放在当前节点的右孩子。
      4. 在孩子节点中递归步骤2和3,不断构造新的孩子节点,直到 孩子节点中只有一个数据(无法再继续切割)或 孩子节点已到达限定高度 。
    • 预测:

      对于一个数据样本x,我们将其传入所有的树中,计算x所处的叶子节点的高度平均值,设置一个阈值,高度不够的视为异常。

  • AutoEncoder

    • 基于神经网络的算法(基于autoencoder和GAN网络)
    • img

工作问题简记

问题大致背景(不能详述):某种光感sensor在暗室采集的数据随屏幕亮度增加,采到的异常数据变多,需要送神经网络前过滤异常点,保证模型快速收敛。

异常检测算法没有哪个绝对好坏,需要尝试多种算法验证效果,实际问题往往还需要结合业务特点,充分利用人工经验以及数据特点,本问题的异常检测算法应用思路:

  • 数据随bright增长呈现周期性规律(数据共性,业务相关):异常点分布范围和出现概率随bright增大,因此考虑对原数据dataframe分组拆分成固定bright的各组子dataframe后送pyod算法,分别剔除异常点后拼接输出

    def split_df_by_col(df, col_name):
    	sub_df_list = []
        bright_list = df[col_name].unique()
        for bright in bright_list:
            temp_df = df[df[col_name].isin([bright])]
            sub_df = copy.deepcopy(temp_df)
            sub_df = sub_df.reset_index(drop=True)
            sub_df_list.append(sub_df)
         return sub_df_list
    
    def concat_df_for_out(sub_df_list):
        df_out = sub_df_list[0]
        for i in range(1, len(sub_df_list)):
       	df_out = pd.concat([df_out, sub_df_list[i]], axis=0, ignore_index=True)
        print(df_out)
        return df_out
    
  • 组合各种基础PoyOD算法,实现参数可配,便于后续采集的数据处理后对比效果 (目前集成了本问题效果较好的7种算法)

    • 模型导入和定义

      # Import models
      from pyod.models.abod import ABOD
      from pyod.models.cblof import CBLOF
      from pyod.models.feature_bagging import FeatureBagging
      from pyod.models.hbos import HBOS
      from pyod.models.iforest import IForest
      from pyod.models.knn import KNN
      from pyod.models.lof import LOF
      
      # Define seven outlier detection tools to be compared
      random_state = np.random.RandomState(42)
      classifiers = {
          'ABOD': ABOD(),
          'CBLOF': CBLOF(check_estimator=False, random_state=random_state),
          'FeatureBagging': FeatureBagging(LOF(n_neighbors=35), check_estimator=False, random_state=random_state),
          'HBOS': HBOS(),
          'IsolationForest': IForest(random_state=random_state),
          'KNN': KNN(),
          'KNN_Average': KNN(method='mean')
      }
      
    • OutlierBase基类封装(PyOD的预测相关API所有模型都一样)

      class OutlierDetectorBase:
          """ define interface of Outlier Detection, input (diff_r,diff_g,diff_b)"""
          diff_rgb_clf = ''
          diff_rgb_arr = []
      
          def __init__(self):
              pass
      
          def outlier_create_clf(self, input_df):
              # init clfs and fit data before detect
              pass
      
          @staticmethod
          def outlier_detection_score(clf, diff_arr):
              train_pred = clf.labels_
              train_scores = clf.decision_scores_
              y_test_pred = clf.predict(diff_arr)
              y_test_scores = clf.decision_function(diff_arr)
              outliers = []  # [(i, outlier_score)]
              for i in range(0, len(y_test_pred)):
                  if y_test_pred[i] == 1:
                      # print(i, y_test_scores[i])
                      outliers.append((i, y_test_scores[i]))
              return dict(outliers)
      
          @staticmethod
          def outlier_test_plot(outliers):  # used for plot test
              outliers_diff_index = outliers.keys()
              outliers_diff_scores = outliers.values()
              plt.scatter(outliers_diff_index, outliers_diff_scores)
              plt.show()
      
          def outlier_execute(self, input_df):  # outlier detect enter
              self.outlier_create_clf(input_df)
              diff_rgb_outliers = self.outlier_detection_score(self.diff_rgb_clf, self.diff_rgb_arr)
              return diff_rgb_outliers
      
    • OutlierDetector继承OutlierBase(根据clf_name构造不同算法类型的异常点检测器)

      class OutlierDetector(OutlierDetectorBase):
          """ KNN Outlier Detector3D"""
          clf_name = ''
      
          def __init__(self,  clf_name):
              super().__init__()
              self.clf_name = clf_name
      
          def outlier_create_clf(self, input_df):
              # init knn-clf and fit data before detect
              X1 = input_df['diff_DN_R'].values.reshape(-1, 1)
              X2 = input_df['diff_DN_G'].values.reshape(-1, 1)
              X3 = input_df['diff_DN_B'].values.reshape(-1, 1)
      
              temp_arr = np.concatenate((X1, X2, X3), axis=1)
      
              self.diff_rgb_arr = temp_arr
              self.diff_rgb_clf = classifiers[self.clf_name]  # init clf
              self.diff_rgb_clf.fit(self.diff_rgb_arr)
      
  • range-filter(人工经验,业务相关):diff_r, diff_g, diff_b这三个维度的数据应该在一个合理的范围,明显超过阈值的可以直接剔除。阈值的选取采用topK取中位数的方法确定。

    def range_filter(df, col_name, k):
    	# print('heapq.nlargest: ', heapq.nlargest(10, df[col_name]))
        # print('heapq.nsmallest: ', heapq.nsmallest(10, df[col_name]))
        max_top10 = heapq.nlargest(k, df[col_name])
        min_top10 = heapq.nsmallest(k, df[col_name])
        max_median = np.median(max_top10)
        min_median = np.median(min_top10)
        for index, row in df.iterrows():
            if row[col_name] < min_median or row[col_name] > max_median:
                # print('the row need to drop: ', row)
                df.drop(labels=index, inplace=True)
            else:
                pass
        return df
    
  • 效果示例(7种算法对比)
    异常点剔除前(diff_DN_G通道):
    在这里插入图片描述
    异常点剔除后:

    可以看到原数据波动范围很大,最小的能波动到-120,最大能上到40;

    过滤后分布更加密集,放大后能看到随bright呈现的周期性规律更加明显(原数据受异常点干扰不是很明显)

    CBLOF在这里插入图片描述

    FeatureBagging在这里插入图片描述

    HBOS在这里插入图片描述

    IsolationForest在这里插入图片描述

    KNN在这里插入图片描述

    KNN_Average在这里插入图片描述
    基础结论: 对于该问题,CBLOF 效果最好, KNN 和 FeatureBagging次之, 可能与数据量不大且维度较低有关(数据量5600+, 送异常检测的特征数3)

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐