# informer **Repository Path**: ddtu/informer ## Basic Information - **Project Name**: informer - **Description**: learning informer - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2025-02-23 - **Last Updated**: 2025-02-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 时序预测模型概述 ARIMA, Transformer, GRU ,LSTM, Pophet, GBDT Informer, TiDE TiDE模型,一种非基于Transformer的MLP模型 # ARIMA模型 要求:平稳性,均值和方差不发生明显变化 严平稳:分布不随时间改变而改变 弱平稳:期望与相关系数不变(大部分数据都是弱平稳) 差分法:时间序列在t与t-1时刻的差值 二阶差分是在一阶差分基础上再作一次差分 对数据作差分,保证数据平稳性 AR:自回归模型 image-20240816190547803 # Informer模型 学习视频链接:[时间序列预测Informer学习](https://www.bilibili.com/video/BV1JY411f7oG/?spm_id_from=333.337.search-card.all.click&vd_source=1dd8a6b3e378750116f08883bb99eefb) ## 时间序列预测模型背景知识 短序列预测:预测未来一天,三天 趋势预测:上升下降可以预测出来,但是数值差别很大 长序列预测:预测未来10天,30天 Prophet:趋势预测 Arima:短序列精准,趋势预测不准 Informer:长序列和多标签(预测多种类型的变量)预测都可以实现。 LSTM:长序列预测中,序列越长,速度越慢,效果越差(RNN对于长序列的预测效果不是很好) ## Transformer理论基础 ### Attention注意力机制 RNN、注意力机制介绍:[NLP中的RNN、Seq2Seq与attention注意力机制](https://zhuanlan.zhihu.com/p/52119092) 关于QKV注意力机制的理解:[transformer中QKV的通俗理解](https://blog.csdn.net/Weary_PJ/article/details/123531732) 2

Q: 可以理解为想要的要求/提出的条件; K: 理解为目前所有待选项的条件; V: 理解为匹配的结果; 目的是找出最和Q匹配的K。通过点乘计算Q和K的相似度,softmax进行结果的归一化(softmax函数,归一化指数函数;每一个元素范围在(0,1)之间且所有元素的和为1),得到权重矩阵,然后和V进行加权(相乘),最终得到对结果V所需要付出的注意力。上述公式中d~k~的目的是使公式更加稳定。 ### muti-head attention,多头attention 通过h(原文中h=8)个线性变换计算Q,K,V,最后将这h个注意力拼接起来,乘以权重矩阵W(一个全连接层的矩阵变换),得到最终结果。 head类似于CNN里面的卷积核kernel ![img](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2NjE0NjM2,size_16,color_FFFFFF,t_70.png) ### transformer讲解 transformer是完全基于自注意力机制的一个深度学习模型,因为它适用于并行化计算,和它本身模型的复杂程度导致它在精度和性能上都要高于之前流行的RNN循环神经网络。 transformerB站视频讲解:[transformer&Attention注意力机制](https://www.bilibili.com/video/BV1y44y1e7FW/?spm_id_from=333.999.0.0&vd_source=1dd8a6b3e378750116f08883bb99eefb) transformer知乎讲解:[十分钟理解Transformer](https://zhuanlan.zhihu.com/p/82312421) encoder特征重构——decoder基于上一结果推断当前结果 img img image-20230425133736501 自注意力:muti-head attention,是为了提炼出输入的特征。 Add&Norm:add,子层的输入X和子层(注意力层和前馈层)的运算结果相加(借鉴了残差网络,防止退化);norm,即layer normalization,对向量进行标准化加速收敛。 前馈神经网络:进一步提炼特征,例如设置一个两层的神经网络。 masked前缀:超出期望长度的序列,保留期望长度,其余删除;低于期望长度的序列,用0填充(因为填充的位置无意义,所以在位置上用负无穷表示,softmax函数会把该位置计算成0,即不分配任何注意力,整个操作称为padding mask)。 注意力:transformer没有捕捉序列信息的功能,所以在输入文字信息的时候将文字编码为词向量并添加单词位置信息,transformer便可以学习词序信息了。 ## Informer理论基础 套transformer架构; Beyond Efficient Transformer for Long Sequence Time-Series Forecasting, AAAI’21 best paper tranfsformer架构的优势和问题: - 万能模型,直接套用,代码简单,例子多 - 并行计算,速度比LSTM快,全局信息丰富,注意力机制效果好 - 长序列中attention需要每个点与其他点计算(序列很长的话效率低) - decoder输出要基于上个预测结果推断当前预测结果 informer要解决的问题 - attention计算得更快 - decoder一次性输出所有预测 - 堆叠encoder更快 对attention计算的解读: - 长序列中,并不是每一个位置的attention都很重要 - 对于每一个Q来说,只有一小部分的K与他有较强关系 Q均匀分布时(注意力不集中),应予舍弃;对于权重差异较大的Q,应均衡差异 Probattention计算方法: image-20230425152633596 上述:32表示batch;8表示8头注意力机制; image-20230425153301334 image-20230425153527337 上述:25表示选出的96个Q中选出差异最大的25个Q(和25个K没有直接关系);96表示所有的K,即96个K; image-20230425160741051 distilling:蒸馏; > 知识点复习 > pooling:池化;maxpooling:最大池化;下采样,用于特征降维 > > image-20230425161416232 image-20230425162001189 上:相比transformer改进的点在于:在做完attention之后增加了1D的pooling,提升了计算速度以及使特征变得更加明显。 transformer适合图像、时间序列、文本这种有关系的特征数据。 image-20230425163103004 上:transformer里的decoder是类似seq2seq的输出,预测出第一个,把第一个当做已知条件,预测第二个,以此类推。 informer进行了改进,将真实值的一部分也放入了decoder image-20230425163734193 image-20230425164529530 下:位置编码,可以做7d为一个编码、月编码、假期编码 image-20230425164844294 image-20230425165030720 ## Informer源码解读 数据重采样:例如将1h频率数据改成4h频率数据(均值) 出现custom:即可以自定义参数 # 时序数据预测代码实战 ## 数据导入 数据基本格式 ![image-20230506143820467](assets/image-20230506143820467.png) ### 导入 ```python # 读数据 import pandas as pd filename1 = './data/wq_data.xlsx' df = pd.read_excel(filename1, sheet_name = 'data') df['TM'] = pd.to_datetime(df['TM']) # 选取某一点位 df = df.loc[df['STCD'] == 'MS1288118650900006'] # 选取某几列(水质指标) df = df[['TM', 'PH']] ``` ### 数据检查 ```python # 查看数据基本情况 import matplotlib.pyplot as plt df = df.sort_values(by = ['TM']) %config InlineBackend.figure_format = 'svg' fig = plt.figure(figsize=(10,5)) plt.plot(df['TM'], df['PH']) plt.xticks(rotation = 45) plt.title('Time-pH MAP') plt.xlabel('Time') plt.ylabel('pH') plt.show() # plt.savefig('./results/time-ph.png') ``` ## 数据预处理 ### 时间列缺失填补 ```python # 时间列缺失处理 # 将时间列设置为索引 df = df.set_index('TM') # 将频率转换为指定的频率 df_resampled = df.resample('6h').mean # 重新变成DataFrame格式 df_resampled = df_resampled.reset_index() df_resampled = pd.DataFrame(df_resampled) ``` #### 知识补充 ##### 时间频率重采样`df.resample`和`df.asfreq` [pandas.DataFrame.resample官方文档](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html) [pandas.DataFrame.asfreq官方文档](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html) [CSDN: df.resample和df.asfreq区别对比](https://blog.csdn.net/qifeidemumu/article/details/88764211) [Thinbug: asfreq和resample之间的区别](https://www.thinbug.com/q/18060619) [宝藏网站:Data Independant](https://dataindependent.com/pandas/Pandas) [DataIndependent网站用法介绍:Resample – pd.df.resample](https://dataindependent.com/pandas/pandas-resample-pd-df-resample/) 通常来讲,resample比asfreq更加通用 resample常见用法示例() `df.resample(rule, axis=0, closed=None, label=None, convention=start)` > rule: 目标采样频率,例如'M'表示月份,'Y'表示年份 > axis: 默认的0表示第1-N行是时间序列(也即第一列为时间列),也可以设置为1表示第1-N列为时间序列 > closed: 计算规则,两个参数'left'和'right',大部分默认值均为left > label: 时间标签显示,两个参数'left'和'right',大部分默认值均为left > convention: 两个参数'start'和'end' ```python # 生成时间序列列表 tmindex = pd.date_range(start='20230101', end='20230331', freq='D') # 利用period也可以 tmindex = pd.date_range(start='20230101', periods=90, freq='D') ts = pd.DataFrame(range(1, 91), index=tmindex, columns=['count']) # 上采样:低频到高频,例如天到分钟 ts.resample('T').mean() ts.resample('5T').sum() # 下采样,高频到低频,例如天到月 ts.resample('M').mean() ts.resample('3M').sum() ``` down resampling下采样经常用到`label`和`closed`参数 ```python # 左右标签 tmindex = pd.date_range(start='20230501', periods=12, freq='T') ts = pd.DataFrame(range(12), index=tmindex, columns=['count']) ts_m = ts.resample('5T').sum() #前5个值计到了时刻第0分钟 ts_m = ts.resample('5T', label='right').sum() #前5个值计到了时刻第5分钟 # 左右闭合 ts_m = ts.resample('5T', closed='right').sum() #第1个值计到了时刻上个小时的第55分钟,第2-6个值计到了这个时刻的第0分钟 ts_m = ts.resample('5T', label='right', closed='right').sum() #第1个值计到了时刻第0分钟,第2-6个值计到了这个时刻的第5分钟 ``` up resampleing上采样会用到`convention`参数 ```python tmindex = pd.period_range(start='20230101', periods=4, freq='Q') ts = pd.DataFrame(range(4), index=tmindex, columns=['count']) ts_m = ts.resample('M').sum() #原来的值分别出现在了第1、4、7、10月,一共12个月都有数据 ts_m = ts.resample('M', convention='end').sum() #原来的值分别出现在了第3、6、9、12月,第1、2月没有数据 ``` ### 缺失值和零值填充 ```python # 填充预处理 import numpy as np # 将0值和缺失值(None)转换成np.nan df_replaced = df_resampled.replace([0, None], np.nan, inplace=False) # 将0转化成None,但是不能转换成np.nan # df_resampled.loc[df_resampled['PH'] == 0] = None # 对缺失值进行填充,近邻填充,对所有水质指标 df_fillna = df_replaced.fillna(method='ffill') # 对缺失值进行填充,近邻填充,仅改变某一水质指标 df_replaced['PH'].fillna(method='ffill', inplace=True) print(df_replaced.head(10)) plt.plot(df_replaced['TM'], df_replaced['PH']) plt.show() # 对缺失值进行填充,插值填充 df_interpolated = df_resampled.interpolate(method='linear') ``` #### 知识补充 ##### 对于`df.resample`、`df.fillna`等中的`inplace`的说明 `inplace=True`对原有df进行修改 `inplace=False`不对原有df进行修改,例如`df2 = df['PH'].replace([0, None], np.nan, inplace=False)`中`df2`是发生改变了的(且`df2`只有pH这一列的数据),而`df['PH']`以及`df`则还是原来的,保持不变。 ##### 缺失值`None`和`np.nan` [Python基础——None、False、np.nan的区别](https://blog.csdn.net/qq_22937901/article/details/116987239) 实践中,遵循一下三点原则: (1)在用pandas和numpy处理数据阶段将None,NaN统一处理成NaN,以便支持更多的函数。 (2)如果要判断Series,numpy.array整体的等值性,用专门的Series.equals,numpy.array函数去处理,不要用==判断 (3)如果要将数据导入数据库,将NaN替换成None ##### 填充函数`df.fillna` [DataIndependent网站用法介绍:Pandas Fill NA – DataFrame.fillna()](https://dataindependent.com/pandas/pandas-fill-na-dataframe-fillna/) `df.fillna(value=None, method=None, axis=None, inplace=False, limit=None)` > value: 固定值填充;三种: 1.`df.fillna(val)`, 将所有缺失值填充为val; 2.`fillna({'列名1': 填充值1, '列名2': 填充值2})`, 按照指定的值填充特定的列 > method: 近邻填充,可选'ffill':填充为前一个值;和'bfill':填充为后一个值 > axis: 0为按行填充,1为按列填充 > inplace: True改变原有数据; False不在原有数据上进行修改 > limit: 限制向前或者向后填充几个值,需要和'method'参数配合使用 简单示例 ```python # 将0值和缺失值(None)转换成np.nan df_replaced = df_resampled.replace([0, None], np.nan, inplace=False) # 上接 # 对缺失值进行填充,fillna方法近邻填充 df_fillna = df_replaced.(method='ffill') ``` ##### 插值理论基础 首先需要明确,数据的填充工作必须用插值而非拟合,因为拟合可能会舍弃原始采样数据,而这是不能被允许的,即使少量原始数据可能存在一定的偏差,但我们仍假定原始数据是最权威的、无偏差的,我们需要原始数据给我们后续工作提供正确的引导。 以下内容主要从华南理工大学出版社的《应用数据分析》(郑咸义主编)提炼: 我们讨论的主要为插值函数取自代数多项式的代数插值,或称为多项式插值。 根据基函数次数/阶数不同,分为线性(一次)插值,二次插值等。(大多都属于Lagrange插值) 根据插值基函数不同分为Lagrange(拉格朗日)、Hermite(埃尔米特)以及Spline(样条插值),其中: **Lagrange**:最普遍,插值节点函数值相同;**Hermite**:插值节点函数值及导数都相同;**Spline**:由一些具有某些连续性条件的子区间上的分段多项式构成。 > Runge龙格现象:盲目使用多的插值节点构造高次插值多项式,**4次及5次以上插值已很少使用**。所以产生了分段低次插值。 总结:高阶多项式插值虽然光滑,但容易产生Runge现象;分段低次插值虽然具有一致收敛性,但光滑性较差,因为其只保证插值函数的整体连续性,在各小段连接处虽然左右导数存在,但不一定相等,连接处不光滑。所以Spline是个不错的选择。 ##### 插值填充函数`df.interpolate` [官方文档:pandas.DataFrame.interpolate](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.interpolate.html) [SKYTOWNER: Pandas DataFrame | interpolate method](https://www.skytowner.com/explore/pandas_dataframe_interpolate_method) `df.interpolate(method='linear', axis=0, inplace=False, limit=None, limit_direction=None, limit_area=None, downcast=None)` > method: 可选`'linear'`, `'ffill'`, `'index'`, `'time'`, `'polynomial'`, `'spline'`, `'nearest'`等 > axis: 0为按行填充,1为按列填充 > inplace: True改变原有数据; False不在原有数据上进行修改 > limit: 填充的最大个数 > limit_direction: `'forward'`: 默认值,从前向后填,第一个为NaN的话无法填; `'backward'`: 从后向前填; `'both'`: 前后都可以填; > limit_area: `'None'`(默认值)、`'inside'`、`'outside'`三个参数,一般用不到; **详细解读`method`参数** 可选参数很多:['linear', 'time', 'index', 'values', 'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh', 'spline', 'polynomial', 'from_derivatives', 'piecewise_polynomial', 'pchip', 'akima', 'cubicspline'] `'linear'`:线性插值填充,使用该列本身的值进行填充 `'index'`:和'linear'有一些类似,但是借助了index列的值进行填充,这里有解释[SKYTOWNER: Pandas DataFrame | interpolate method](https://www.skytowner.com/explore/pandas_dataframe_interpolate_method) `'ffill'`:利用前一个非空值填充 `'time'`:根据时间增长进行填充,使用时要设置时间列为index(但貌似其他也都是默认要这么设置) `'polynomial'`:多项式拟合,插值点小范围内效果较好,需要指定order;order=1时,同'linear' `'spline'`:样条插值,处理异常值效果较好,但在样条拟合点两侧可能出现振荡,需要指定order。(实际:试了很多次,这个不好用) `'nearest'`:使用给定数据点最近的数据的值插值 `'zero'`:零阶样条插值,类似'nearest',但将间隔值设置为零 `'slinear'`:一阶样条插值,相邻两个非缺失值之间使用一次函数进行插值 `'quadratic'`: 二阶样条插值 `'cubic'`:三阶样条插值 **应该使用哪种方法?** 进行各类方法比较的代码示例(需要承接前面主线的代码): ```python # 将0值和缺失值(None)转换成np.nan df_replaced = df_resampled.replace([0, None], np.nan, inplace=False) # 上接 # 对缺失值进行填充, 插值填充 # 首先设置TM列为index,否则时间列会被当做被插值列,因为pd.interpolate无法对时间序列的值(数据类型不同)插值 df_replaced_tmindex = df_replaced.set_index('TM') #画图准备 %config InlineBackend.figure_format = 'svg' plt.style.use('ggplot') plt.figure(figsize=(12,6)) method = ['linear', 'index', 'ffill', 'polynomial', 'spline'] # method = ['zero', 'nearest', 'slinear', 'quadratic', 'cubic'] color = ['tomato', 'peru', 'gold', 'green', 'deepskyblue', 'mediumslateblue', 'violet', 'royalblue'] plt.scatter(df_replaced['TM'], df_replaced['PH'], color=color[0], s=10, label='oridata') # 循环画图 for i,j in zip(range(5), range(1,6)): if method[i] in ['spline', 'polynomial']: df_fillna = df_replaced_tmindex.interpolate(method=method[i], order=2) else: df_fillna = df_replaced_tmindex.interpolate(method=method[i]) df_fillna.reset_index(inplace=True) plt.plot(df_replaced['TM'], df_fillna['PH'], color=color[j], linewidth=1, label=method[i]) plt.legend() plt.show() ``` 结果比较: ![output-interpolate1](assets/output-interpolate1.svg) ![output-interpolate2](assets/output-interpolate2.svg) 说明:上图中linear和index曲线重合 从上面可以看出: `'ffill'`和`'zero'`结果很类似;而`'nearest'`会在缺失数据的中间有个前非缺失值和后非缺失值的跳动变化; 同是一阶的`'linear'`(包括`'index'`)和`'slinear'`结果很相似;同是二阶的`'polynomial'`和`'quadratic'`结果很相似; `'spline'`的表现很差; 简单粗暴的`'linear'`可以适用于绝大多数情况,特别是在数据大范围缺失的情况下,高阶的函数反而不好用; 对于数据小范围缺失的话,可以采用`'cubic'`,会有较好的平滑效果; 其他:如果没有进行时间列填补的话,一定要用`'time'`; > `'spline'`表现很差可能的原因:是其计算方式可能并不是真正意义上的样条插值,如果根据定义,其应该是平滑的曲线,但事实却不是如此,然后问了gpt,给的示例代码运行后还是跑不通,不想整这么麻烦了,直接不建议用。 > 从这里我认为pands里的`'df.interpolate'`样条插值不是`'spline'`,而是`'zero'`、`'slinear'`、`'quadratic'`、`'cubic'`。 > > image-20230511183632803 ##### k-Nearest Neighbors算法理论基础 ##### 近邻填充:使用KNNImputer工具包 ```python #零值和缺失值填充 from sklearn.impute import KNNImputer # 创建 KNNImputer 对象,设置邻居数量为 2 imputer = KNNImputer(n_neighbors=2) # 使用 KNNImputer 对象进行缺失值填补 df_fillna_tmindex = df_fillna.set_index('TM') val_fillna_tmindex = df_fillna_tmindex.values #相当于把'TM'列删掉了,没有了索引列 imputed = imputer.fit_transform(val_fillna_tmindex) df_imputed = pd.DataFrame(imputed) # 重新找回index和columns df_imputed.index = df_fillna['TM'] df_imputed.columns = df_fillna_tmindex.columns ``` ### 标准化和归一化 ```python ``` ## 构建预测模型 ### 模型构建 ```python ``` ### 模型评估 ```python ``` ### 调参 ```python ``` ## 模型预测 ### 预测 ```python ``` ### 结果输出 ```python ```