从零开始-Machine Learning学习笔记(8)-指数平滑及python实现

1. 指数平滑的定义及应用场景

​ 指数平滑由布朗提出、他认为时间序列的态势具有稳定性或规则性,所以时间序列可被合理地顺势推延;他认为最近的过去态势,在某种程度上会持续的未来,所以将较大的权数放在最近的资料。指数平滑法是移动平均法中的一种,其特点在于给过去的观测值不一样的权重,即较近期观测值的权数比较远期观测值的权数要大。根据平滑次数不同,指数平滑法分为一次指数平滑法、二次指数平滑法和三次指数平滑法等。但它们的基本思想都是:预测值是以前观测值的加权和,且对不同的数据给予不同的权数,新数据给予较大的权数,旧数据给予较小的权数。

​ 所以指数平滑主要用在在时间序列分析上。可以做具有时间序列的预测模型。如给出了往年某产品销售的历史数据,让你预测未来某年的销售数据等

2. 一次指数平滑

当时间序列数据没有明显的变化趋势,就可以使用一次指数平滑。实际就是对历史数据的加权平均,它可以用于任何一种没有明显函数规律但确实存在某种前后关联的时间序列的短期预测。

​ 但是一次指数平滑也具有一定的局限性:第一,预测值不能反映趋势变动、季节波动等有规律的变动;第二,这种方法多适用于短期预测,而不适合作中长期的预测;第三,由于预测值是历史数据的均值,因此与实际序列的变化相比有滞后现象。

​ 其预测公式如下:

y  t+1 =αy t +(1α)y  t   y t + 1 ∗ = α y t + ( 1 − α ) y t ∗

其中, y  t+1   y t + 1 ∗ 是t+1时期的预测值, y t   y t 是t时刻的实际值, y  t   y t ∗ 是t时刻的预测值。

所以,在迭代的过程中,我们需要开一个list来存放 y  t   y t ∗ 。最后的预测值就是

y t+1 =αy t [1]+(1α)y  t [1]  y t + 1 = α ∗ y t [ − 1 ] + ( 1 − α ) ∗ y t ∗ [ − 1 ]

取实际值的最后一个与预测值的最后一个就能预测出下一个时刻的值!

其中 y  0   y 0 ∗ 的确定一般有以下的规则:

  • 如果数据小于20,一般取前三项的均值
  • 大于20时,看情况调整第一项的值,一般取第一项就行,因为数据多, y  0   y 0 ∗ 的取值对最后的预测结果影响不大

关于 α  α 的取值方法,请往下看。

3. 二次指数平滑

二次指数平滑是对一次指数平滑的再平滑。它适用于具线性趋势的时间数列。虽然一次指数平均在产生新的数列的时候考虑了所有的历史数据,但是仅仅考虑其静态值,即没有考虑时间序列当前的变化趋势。所以二次指数平滑一方面考虑了所有的历史数据,另一方面也兼顾了时间序列的变化趋势。

二次指数平滑的计算公式为:

Y  t+T =a t +b t T  Y t + T ∗ = a t + b t ∗ T

S (2) t =αS (1) t +(1α)S (2) t1   S t ( 2 ) = α S t ( 1 ) + ( 1 − α ) S t − 1 ( 2 )

a t =2S (1) t S (2) t   a t = 2 S t ( 1 ) − S t ( 2 )

b t =α1α (S (1) t S (2) t )  b t = α 1 − α ( S t ( 1 ) − S t ( 2 ) )

所以,实际上在程序上的操作就是在一次平滑的基础上再做一次指数平滑,其中我们需要两个list来存储一次平滑的值和二次平滑的值,通过对应的两次指数平滑,就可以求出对应的 a t   a t , b t   b t 的值。最后调用公式 Y  t+T =a t +b t T  Y t + T ∗ = a t + b t ∗ T 就可以做出预测,其中T是向后预测的周期,如向后预测一个周期(一天/一年等),那么T=1,往后预测两个就是T=2,以此类推。

4. 三次指数平滑

若时间序列的变动呈现出二次曲线趋势,则需要采用三次指数平滑法进行预测。实际上是在二次指数平滑的基础上再进行一次指数平滑。它与二次指数平滑的区别就是三次平滑还考虑到了季节性效应。

三次指数平滑的计算公式为:

S (3) t =αS (2) t +(1α)S (3) t1   S t ( 3 ) = α S t ( 2 ) + ( 1 − α ) S t − 1 ( 3 )

a t =3S (1) t 3S (2) t +S (3) t   a t = 3 S t ( 1 ) − 3 S t ( 2 ) + S t ( 3 )

b t =α2(1α) 2  [(65α)S (1) t 2(54α)S (2) t +(43α)S (3) t ]  b t = α 2 ( 1 − α ) 2 [ ( 6 − 5 α ) S t ( 1 ) − 2 ( 5 − 4 α ) S t ( 2 ) + ( 4 − 3 α ) S t ( 3 ) ]

c t =(α 2 2(1α) 2  [S 1 t 2S (2) t +S 3 t ])  c t = ( α 2 2 ( 1 − α ) 2 [ S t 1 − 2 S t ( 2 ) + S t 3 ] )

预测未来期的值的计算公式为:

y t+T =a t +b t T+c t T 2   y t + T = a t + b t T + c t T 2

5. 关于平滑系数 α  α 的取值

1、当时间序列呈现较稳定的水平趋势时,应选较小的α,一般可在0.05~0.20之间取值‘

2、当时间序列有波动,但长期趋势变化不大时,可选稍大的α值,常在0.1~0.4之间取值;

3、当时间序列波动很大,长期趋势变化幅度较大,呈现明显且迅速的上升或下降趋势时,宜选择较大的α值,如可在0.6~0.8间选值。以使预测模型灵敏度高些,能迅速跟上数据的变化。

4、当时间序列数据是上升(或下降)的发展趋势类型,α应取较大的值,在0.6~1之间。、

6.指数平滑的python实现

# -*- coding: utf-8 -*-
'''
Exponential smooth(指数平滑)的手工实现(无第三方库)
Author : Kabuto_hui
Date   : 2018.04.19
'''

#指数平滑算法
def exponential_smoothing(alpha, s):
    '''
    一次指数平滑
    :param alpha:  平滑系数
    :param s:      数据序列, list
    :return:       返回一次指数平滑模型参数, list
    '''
    s_temp = [0 for i in range(len(s))]
    s_temp[0] = ( s[0] + s[1] + s[2] ) / 3
    for i in range(1, len(s)):
        s_temp[i] = alpha * s[i] + (1 - alpha) * s_temp[i-1]
    return s_temp

def compute_single(alpha, s):
    '''
    一次指数平滑
    :param alpha:  平滑系数
    :param s:      数据序列, list
    :return:       返回一次指数平滑模型参数, list
    '''
    return exponential_smoothing(alpha, s)

def compute_double(alpha, s):
    '''
    二次指数平滑
    :param alpha:  平滑系数
    :param s:      数据序列, list
    :return:       返回二次指数平滑模型参数a, b, list
    '''
    s_single = compute_single(alpha, s)
    s_double = compute_single(alpha, s_single)

    a_double = [0 for i in range(len(s))]
    b_double = [0 for i in range(len(s))]

    for i in range(len(s)):
        a_double[i] = 2 * s_single[i] - s_double[i]                    #计算二次指数平滑的a
        b_double[i] = (alpha / (1 - alpha)) * (s_single[i] - s_double[i])  #计算二次指数平滑的b

    return a_double, b_double

def compute_triple(alpha, s):
    '''
    三次指数平滑
    :param alpha:  平滑系数
    :param s:      数据序列, list
    :return:       返回三次指数平滑模型参数a, b, c, list
    '''
    s_single = compute_single(alpha, s)
    s_double = compute_single(alpha, s_single)
    s_triple = exponential_smoothing(alpha, s_double)

    a_triple = [0 for i in range(len(s))]
    b_triple = [0 for i in range(len(s))]
    c_triple = [0 for i in range(len(s))]

    for i in range(len(s)):
        a_triple[i] = 3 * s_single[i] - 3 * s_double[i] + s_triple[i]
        b_triple[i] = (alpha / (2 * ((1 - alpha) ** 2))) * ((6 - 5 * alpha) * s_single[i] - 2 * ((5 - 4 * alpha) * s_double[i]) + (4 - 3 * alpha) * s_triple[i])
        c_triple[i] = ((alpha ** 2) / (2 * ((1 - alpha) ** 2))) * (s_single[i] - 2 * s_double[i] + s_triple[i])

    return a_triple, b_triple, c_triple

if __name__ == "__main__":

    alpha = 0.8
    data = [i for i in range(100)]

    sigle = compute_single(alpha, data)

    print(alpha * data[-1] + (1 - alpha) * sigle[-1])

更多的算法实现,可以到我的Github,如果觉得有用请点个Star哟~

相关博客:
https://www.cnblogs.com/zlyblog/p/6589208.html
https://blog.csdn.net/nieson2012/article/details/51980943
https://blog.csdn.net/u013527419/article/details/52822622

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值