异常检测基线的上下界判断

最近做异常检测,基于基线进行异常判断,可是用到了好多算法,如,3sigma、箱线图、MAD等,但是显示数据来了,哥哥算法暴露出了缺陷。

下限可以做某习惯的基线,以下为异常,以上为正常

上限可以做某频率的基线,以下为正常,异常为异常

1、3sima

lower = mean + 3*sigma
high = mean - 3*sigma
def get_3sigma(samples_list):
    men = np.mean(samples_list)
    sigma = np.std(samples_list)
    low_limit = men - 3 * sigma 
    high_limit = men + 3 * sigma
    return [low_limit, high_limit]

****************************************3sigma****************************************
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4], range:[0.630686521676914, 7.494313478323086]
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4, 100], range:[-58.09649688081275, 77.5082615866951]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 5, 4, 5, 5, 4, 100], range:[-58.2944744612908, 77.4709450495261]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 6, 4, 6, 6, 4, 100], range:[-58.02021757228309, 77.54962933698897]
data:[4, 4, 4, 46, 4, 4, 4, 34, 4, 0, 4, 6, 4, 6, 6, 24, 100], range:[-58.25226048287068, 88.60520165934126]
data:[4, 4, 5, 100], range:[-96.0306803167733, 152.5306803167733]
data:[1, 0, 8, 100], range:[-99.09550842827772, 153.59550842827772]
data:[1, 3, 3, 6, 8, 10, 10, 1000], range:[-856.2655080519581, 1116.515508051958]
data:[1, 100], range:[-98.0, 199.0]
data:[1, 1, 100], range:[-106.0071426749364, 174.0071426749364]
data:[0, 0, 0, 3, 0, 0], range:[0, 3.8541019662496847]
data:[0, 0, 0, 3, 3, 0], range:[0, 5.242640687119286]

接近正态分布的时候表现优秀。

当分布不是正态分布的时候,或者数据量很少的时候,方差非常大,导致上下界严重不合理。

下限出现了负值,上限也异常的大。

2、箱线图

IQR = Q3-Q1
lower = Q1 - 1.5*IQR
high = Q3 + 1.5*IQR
def tukey_box(samples_list):
    q2 = np.quantile(samples_list, 0.5)
    q1 = np.quantile(samples_list, 0.25)
    q3 = np.quantile(samples_list, 0.75)
    q4 = np.quantile(samples_list, 1.0)
    iqr = q3 - q1
    left = q1 - 1.5 * iqr
    right = q3 + 1.5 * iqr
    return left, right

****************************************tukey_box****************************************
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4], range:(2.5, 6.5)
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4, 100], range:(2.5, 6.5)
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 5, 4, 5, 5, 4, 100], range:(4.0, 4.0)
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 6, 4, 6, 6, 4, 100], range:(4.0, 4.0)
data:[4, 4, 4, 46, 4, 4, 4, 34, 4, 0, 4, 6, 4, 6, 6, 24, 100], range:(1.0, 9.0)
data:[4, 4, 5, 100], range:(-33.125, 65.875)
data:[1, 0, 8, 100], range:(-44.625, 76.375)
data:[1, 3, 3, 6, 8, 10, 10, 1000], range:(-7.5, 20.5)
data:[1, 100], range:(-48.5, 149.5)
data:[1, 1, 100], range:(-73.25, 124.75)
data:[0, 0, 0, 3, 0, 0], range:(0, 0.0)
data:[0, 0, 0, 3, 3, 0], range:(0, 5.625)

数据分布均匀,数据种类众多的时候,表现优秀。

同样存在IQR过大的情况,并且当众数过多的时候,上下限值严重不合理

参考:异常值检测方法-箱线图(boxplot)

3、MAD

def get_mad_range(samples_list):
    med = np.median(samples_list, axis=0)
    abs_dev = np.absolute(samples_list - med)
    med_abs_dev = np.median(abs_dev)
    mad = 1.4826 * med_abs_dev
    low_limit = med - 3 * mad 
    high_limit = med + 3 * mad 
    return [low_limit, high_limit]

****************************************mad_range****************************************
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4], range:[4.0, 4.0]
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4, 100], range:[4.0, 4.0]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 5, 4, 5, 5, 4, 100], range:[4.0, 4.0]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 6, 4, 6, 6, 4, 100], range:[4.0, 4.0]
data:[4, 4, 4, 46, 4, 4, 4, 34, 4, 0, 4, 6, 4, 6, 6, 24, 100], range:[4.0, 4.0]
data:[4, 4, 5, 100], range:[2.2761, 6.7239]
data:[1, 0, 8, 100], range:[-13.2912, 22.2912]
data:[1, 3, 3, 6, 8, 10, 10, 1000], range:[-8.5673, 22.5673]
data:[1, 100], range:[-169.6661, 270.66610000000003]
data:[1, 1, 100], range:[1.0, 1.0]
data:[0, 0, 0, 3, 0, 0], range:[0, 3.8541019662496847]
data:[0, 0, 0, 3, 3, 0], range:[0, 5.242640687119286]

选择算法的时候,本以为MAD方法是个完美的算法,可是还是出了问题。

MAD算法的下限值比较不错,但是也有不如意的,会出现负值,这里我们可以进行下限约束>=0:

low_limit = med - 3 * mad if med - 3 * mad > 0 else 0

上限值遇到众数比较多的时候,MAD=0,因此上限值=median中位数,例如第1-4条数据:

上下限都为4,当出现5的时候就是异常了,按照异常检测场景,这里是不合理的。

参考:异常检测方法-MAD

4、优化

试过的组合:

  • 3*sigma+tukey_box
  • MAD+tukey_box
  • MAD+mean
  • MAD+median
  • MAD+1*sigma
  • MAD+2*sigma
  • MAD+3*sigma

结果MAD+2*sigma表现最佳,原理上分析,当MAD=0时,MAD算法的上限不合理,这时众数偏多,并且接近正态分布,因此当MAD=0的时候,上限值用2*sigma替代。

MAD+2*sigma代码:

def get_mad_range(samples_list):
    med = np.median(samples_list, axis=0)
    abs_dev = np.absolute(samples_list - med)
    med_abs_dev = np.median(abs_dev)
    mad = 1.4826 * med_abs_dev
    low_limit = med - 3 * mad if med - 3 * mad > 0 else 0
    high_limit = med + 3 * mad if mad != 0 
                 else np.mean(samples_list) + 2 * np.std(samples_list)
    return [low_limit, high_limit]

****************************************mad_range****************************************
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4], range:[4.0, 7.494313478323086]
data:[4, 4, 4, 4, 4, 5, 4, 4, 5, 0, 4, 5, 4, 5, 5, 4, 100], range:[4.0, 77.5082615866951]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 5, 4, 5, 5, 4, 100], range:[4.0, 77.4709450495261]
data:[4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 6, 4, 6, 6, 4, 100], range:[4.0, 77.54962933698897]
data:[4, 4, 4, 46, 4, 4, 4, 34, 4, 0, 4, 6, 4, 6, 6, 24, 100], range:[4.0, 88.60520165934126]
data:[4, 4, 5, 100], range:[2.2761, 6.7239]
data:[1, 0, 8, 100], range:[0, 22.2912]
data:[1, 3, 3, 6, 8, 10, 10, 1000], range:[0, 22.5673]
data:[1, 100], range:[0, 270.66610000000003]
data:[1, 1, 100], range:[1.0, 174.0071426749364]
data:[0, 0, 0, 3, 0, 0], range:[0, 2.73606797749979]
data:[0, 0, 0, 3, 3, 0], range:[0, 3.8284271247461903]

优秀!完美!

你可能感兴趣的:(异常检测,算法,网络安全,MAD,箱线图,3sigma)