引言

最近实验室老师让我去预测景区内代步车辆的投放量,于是乎,本着“一心一意地输出年富力强的劳动力”这份初心,我就屁颠屁颠地去找资料,然后发现了Holt-Winters模型 , 感觉这个模型可以有,于是就去研究一番,并总结成这篇博客了。

原理分析

移动平均(The simple moving average (SMA))

直观上,最简单的平滑时间序列的方法是实现一个无权重的移动平均,目前已知的方法是用窗口函数,平滑统计量 St就是最近k个观察值的均值。公式如下:

这样的方法存在明显的缺陷,当k比较小时,预测的数据平滑效果不明显,而且突出反映了数据最近的变化;当k较大时,虽然有较好的平滑效果,但是预测的数据存在延迟。而且最少需要k个值(窗口有限)。

加权移动平均

一种稍微复杂的方法是先选择一组权重因子来计算加权移动平均

然后用这些权重来计算这些平滑统计量:

在实践中,通常在选择权重因子时,赋予时间序列中的最新数据更大的权重,并减少对旧数据的权重。这个方法也需要最少k个值,并且计算复杂。

简单指数平滑法

幸运地是有一种方法可以避免上述问题,它叫做指数平滑法。最简单的指数平滑法如下:

其中α是平滑因子,0 < α < 1。换句话说,平滑统计值St是当前统计值Xt与上一时间平滑值St-1的加权平均。这个简单指数平滑是很容易被应用的,因为只要有两个观察值就能计算了。这里α的选取,我们可以采用最小二乘来决定α(最小化

为什么被称为“指数”平滑法

从它的递推公式就能发现:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDY2NTIxNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
简单指数平滑法适用于没有总体趋势的时间序列。如果用来处理有总体趋势的序列,平滑值将往往滞后于原始数据,除非α的值接近1,但这样一来就会造成不够平滑。
###二次指数平滑
为了解决上述问题,于是引出了二次指数平滑,能够保留总体趋势信息。因为将指数平滑应用了两次,所以被称为二次指数平滑。与简单指数平滑相比,二次指数平滑加入了时间趋势统计量bt,公式如下:

若要预测Xt后m天,公式如下:

三次指数平滑

三次指数平滑将时间序列的季节性这一特征也考虑进去了。
季节性被定义为时间序列数据的趋势,它表现出每一个周期重复自身的行为,就像任何周期函数一样。“季节”这个词用来表示行为每隔时间段L就开始自我重复。在自然界中有不同类型的季节性“累加性”(additive)和“累乘性“(multiplicative),就像加法和乘法是数学的基本运算。
如果每个12月都比每个11月多卖出1000套公寓,我们就说这样的季节趋势是“累加性”的。可以用绝对增长来表示。如果我们在夏季比冬季多卖出10%的公寓,那么季节趋势在自然中是“累乘性”的。
累乘性公式如下:

其中 α是数据平滑因子, 0 < α < 1;β是趋势平滑因子,0 < β < 1; γ是季节改变平滑因子0 < γ < 1。
初始化趋势估计b0的公式为:

累加性公式如下:

对三次指数平滑法而言,我们必须初始化一个完整的“季节”Ci的值,不过我们可以简单地设置为全1(针对累乘式)或全0(针对累加式)。只有当序列的长度较短时,我们才需要慎重考虑初始值的选取。
我们这里讲的Holt-Winters模型就是三次指数平滑法。哇,终于切入正题了。
所有的指数平滑法都要更新上一时间步长的计算结果,并使用当前时间步长的数据中包含的新信息。它们通过“混合”新信息和旧信息来实现,而相关的新旧信息的权重由一个可调整的拌和参数来控制。各种方法的不同之处在于它们跟踪的量的个数和对应的拌和参数的个数。三次指数平滑法,功能最强大,既能体现趋势性又能体现季节性,所以三次指数平滑法的参数最多,有三个。

python代码实现

我们知道HoltWinters模型有三个可调参数,我们的目的就是训练出有效的α,β, γ。我们有两种方法,一种就是自己取值来试试,一种就是采用数值优化的思想,比如前面我们提到的最小二乘来最小化误差来求参数(注意不一定能全局收敛!这个问题实在是让人头痛。。。)我们就采用最小二乘法(L-BFGS)。

RMSE的实现

from __future__ import division
from sys import exit
from math import sqrt
from numpy import array
from scipy.optimize import fmin_l_bfgs_bdef RMSE(params, *args):Y = args[0]type = args[1]rmse = 0if type == 'linear':alpha, beta = paramsa = [Y[0]]b = [Y[1] - Y[0]]y = [a[0] + b[0]]for i in range(len(Y)):a.append(alpha * Y[i] + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])y.append(a[i + 1] + b[i + 1])else:alpha, beta, gamma = paramsm = args[2]     a = [sum(Y[0:m]) / float(m)]b = [(sum(Y[m:2 * m]) - sum(Y[0:m])) / m ** 2]if type == 'additive':s = [Y[i] - a[0] for i in range(m)]y = [a[0] + b[0] + s[0]]for i in range(len(Y)):a.append(alpha * (Y[i] - s[i]) + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])s.append(gamma * (Y[i] - a[i] - b[i]) + (1 - gamma) * s[i])y.append(a[i + 1] + b[i + 1] + s[i + 1])elif type == 'multiplicative':s = [Y[i] / a[0] for i in range(m)]y = [(a[0] + b[0]) * s[0]]for i in range(len(Y)):a.append(alpha * (Y[i] / s[i]) + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])s.append(gamma * (Y[i] / (a[i] + b[i])) + (1 - gamma) * s[i])y.append((a[i + 1] + b[i + 1]) * s[i + 1])else:exit('Type must be either linear, additive or multiplicative')rmse = sqrt(sum([(m - n) ** 2 for m, n in zip(Y, y[:-1])]) / len(Y))return rmse

线性实现

def linear(x, fc, alpha = None, beta = None):Y = x[:]if (alpha == None or beta == None):initial_values = array([0.3, 0.1])boundaries = [(0, 1), (0, 1)]type = 'linear'parameters = fmin_l_bfgs_b(RMSE, x0 = initial_values, args = (Y, type), bounds = boundaries, approx_grad = True)alpha, beta = parameters[0]a = [Y[0]]b = [Y[1] - Y[0]]y = [a[0] + b[0]]rmse = 0for i in range(len(Y) + fc):if i == len(Y):Y.append(a[-1] + b[-1])a.append(alpha * Y[i] + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])y.append(a[i + 1] + b[i + 1])rmse = sqrt(sum([(m - n) ** 2 for m, n in zip(Y[:-fc], y[:-fc - 1])]) / len(Y[:-fc]))return Y[-fc:], alpha, beta, rmse

累加性

def additive(x, m, fc, alpha = None, beta = None, gamma = None):Y = x[:]if (alpha == None or beta == None or gamma == None):initial_values = array([0.3, 0.1, 0.1])boundaries = [(0, 1), (0, 1), (0, 1)]type = 'additive'parameters = fmin_l_bfgs_b(RMSE, x0 = initial_values, args = (Y, type, m), bounds = boundaries, approx_grad = True)alpha, beta, gamma = parameters[0]a = [sum(Y[0:m]) / float(m)]b = [(sum(Y[m:2 * m]) - sum(Y[0:m])) / m ** 2]s = [Y[i] - a[0] for i in range(m)]y = [a[0] + b[0] + s[0]]rmse = 0for i in range(len(Y) + fc):if i == len(Y):Y.append(a[-1] + b[-1] + s[-m])a.append(alpha * (Y[i] - s[i]) + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])s.append(gamma * (Y[i] - a[i] - b[i]) + (1 - gamma) * s[i])y.append(a[i + 1] + b[i + 1] + s[i + 1])rmse = sqrt(sum([(m - n) ** 2 for m, n in zip(Y[:-fc], y[:-fc - 1])]) / len(Y[:-fc]))return Y[-fc:], alpha, beta, gamma, rmse

累乘性

def multiplicative(x, m, fc, alpha = None, beta = None, gamma = None):Y = x[:]if (alpha == None or beta == None or gamma == None):initial_values = array([0.0, 1.0, 0.0])boundaries = [(0, 1), (0, 1), (0, 1)]type = 'multiplicative'parameters = fmin_l_bfgs_b(RMSE, x0 = initial_values, args = (Y, type, m), bounds = boundaries, approx_grad = True)alpha, beta, gamma = parameters[0]a = [sum(Y[0:m]) / float(m)]b = [(sum(Y[m:2 * m]) - sum(Y[0:m])) / m ** 2]s = [Y[i] / a[0] for i in range(m)]y = [(a[0] + b[0]) * s[0]]rmse = 0for i in range(len(Y) + fc):if i == len(Y):Y.append((a[-1] + b[-1]) * s[-m])a.append(alpha * (Y[i] / s[i]) + (1 - alpha) * (a[i] + b[i]))b.append(beta * (a[i + 1] - a[i]) + (1 - beta) * b[i])s.append(gamma * (Y[i] / (a[i] + b[i])) + (1 - gamma) * s[i])y.append((a[i + 1] + b[i + 1]) * s[i + 1])rmse = sqrt(sum([(m - n) ** 2 for m, n in zip(Y[:-fc], y[:-fc - 1])]) / len(Y[:-fc]))return Y[-fc:], alpha, beta, gamma, rmse

实验结果

直接构造了个类sin的函数,时间段为[-5,5],预测时间段[5-10](样本数据比较粗糙,多多担待。。。)

Holt-Winters模型原理分析及代码实现(python)相关推荐

  1. Holt-Winters模型原理分析

    Holt-Winters模型原理分析及代码实现(python) from:https://blog.csdn.net/u010665216/article/details/78051192 引言 最近 ...

  2. TFRecord简介,原理分析,代码实现?

    TFRecord简介,原理分析,代码实现? 在利用深度学习算法搭建完成网络之后,我们要对网络进行训练,要训练网络就要有训练数据,通常我们会直接对硬盘上存放数据进行操作,来fetch到网络中.这样直接从 ...

  3. 【SemiDrive源码分析】【MailBox核间通信】51 - DCF_IPCC_Property实现原理分析 及 代码实战

    [SemiDrive源码分析][MailBox核间通信]51 - DCF_IPCC_Property实现原理分析 及 代码实战 一.RTOS 侧 Property Service 初始化流程 1.1 ...

  4. 编译原理语义分析代码_Pix2Pix原理分析与代码解读

    原理分析: 图像.视觉中很多问题都涉及到将一副图像转换为另一幅图像(Image-to-Image Translation Problem),这些问题通常都使用特定的方法来解决,不存在一个通用的方法.但 ...

  5. AFM模型原理及Pytorch代码复现

    一.前言 该模型是和NFM模型结构上非常相似, 算是NFM模型的一个延伸,在NFM中, 不同特征域的特征embedding向量经过特征交叉池化层的交叉,将各个交叉特征向量进行"加和" ...

  6. word2vec原理分析与代码公式详解

    word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,原始的论文和网上的很多分析都写的太过粗略,难以理解.现在有根据源码的具体原理分析和详细的公 ...

  7. SE 注意力模块 原理分析与代码实现

    前言 本文介绍SE注意力模块,它是在SENet中提出的,SENet是ImageNet 2017的冠军模型:SE模块常常被用于CV模型中,能较有效提取模型精度,所以给大家介绍一下它的原理,设计思路,代码 ...

  8. ECA 注意力模块 原理分析与代码实现

    前言 本文介绍ECA注意力模块,它是在ECA-Net中提出的,ECA-Net是2020 CVPR中的论文:ECA模块可以被用于CV模型中,能提取模型精度,所以给大家介绍一下它的原理,设计思路,代码实现 ...

  9. 第四篇:决策树分类算法原理分析与代码实现

    前言 本文详细介绍机器学习分类算法中的决策树算法,并全面详解如何构造,表示,保存决策树,以及如何使用决策树进行分类等等问题. 为了全面的理解学习决策树,本文篇幅较长,请耐心阅读. 算法原理 每次依据不 ...

最新文章

  1. 【Win 10 应用开发】将墨迹保存到图像的两种方法
  2. 对MVC、MVP、MVVM的理解
  3. Siege的线程模型-基于版本2.56
  4. 首发:台大林轩田《机器学习基石》系列课程教材的习题解答和实现
  5. log4j2 mybatis 显示 sql 和 结果集
  6. 【Vue】v-if与v-show的区别
  7. SpringMVC请求后台地址URL没有.*的几种实现方式
  8. 软件测试52讲-测试新技术篇
  9. python骗局-说真的!大家做Python一定不要只会一个方向
  10. spring-retry重试与熔断详解—《亿级流量》内容补充
  11. proDAD Adobe pr cc2020 会声会影视频转场特效制作软件,滤镜特效插件
  12. django html 插入网页背景图片
  13. 实习第一天——网宿报到日
  14. echarts.js 官网
  15. firefox插件推荐
  16. 铟镓砷探测器-主要厂商产品特点、产品规格、价格、销量、销售收入及市场现状
  17. 张大哥笔记-个人站长要具备那些心态来运营自己的网站
  18. 大学英语b级和计算机b,网络统考丨大学英语B级,6大题型及答题技巧!【零基础学员必看】...
  19. 收藏!50个帮你提升自我的网站
  20. CentOS下如何完全卸载MySQL

热门文章

  1. python--Django快速入门之模板层详解
  2. 不用U盘安卓Linux系统,安卓Android-X86 安装教程 不使用U盘安装Androidx86教程
  3. (转载)shell脚本中的 EOF 是什么意思?
  4. SAP那些事-职业篇-23-我有个梦想:做一个不加班的项目
  5. ue4 输出360度全景深度图
  6. 简单概括HAPI(Houdini Engine API)所涵盖的功能
  7. Raffle for Weibo Followers
  8. 饿了么商家开放平台踩坑记录1,php更新商品信息提示业务异常BUSINESS_ERROR by勤勤学长 318692996
  9. 《Maven实战》 读书笔记
  10. vue根据表格字段不同的状态显示不同的颜色。