时间序列数据挖掘模板: 墨尔本十年气温变化预测

1. 写在前面

时间序列数据广泛存在于量化交易, 回归预测等机器学习应用, 是最常见的数据类型。所以这里通过墨尔本十年气温变化预测的任务来整理一个时间序列数据挖掘的模板,方便以后查阅方便。这个模板可以用在大部分的时间序列预测任务,从股票价格波动,到四季气温变化, 从大桥沉降预测,到城市用电预警等。

要进行下面工作的整理:

  • 探索性数据挖掘和数据可视化: 绘制折线图、热力图、箱型图、小提琴图、滞后图、自相关图, 让枯燥的时间序列数据颜值爆表!
  • 对时间做特征工程:扩展时间数据维度,这个代码模板也可以作为时间序列数据的通用预处理模板
  • 使用多种机器学习模型建立回归拟合模型: 线性回归、多项式回归、岭回归、随机森林、神经网络等,并可视化展示多种模型效果进行对比

通过这次整理,掌握sklearn中常用的工具包以及深度神经网络的搭建Keras,能够学习到处理时间序列的方式,里边还包含了大量的数据可视化的套路。

由于篇幅原因,这里可能不会把所有的代码执行结果展示出来, 只会展示比较重要的结果,毕竟为了以后方便查阅,太多结果图像在反而不太好。

OK, let’s go!

2. 导入包和墨尔本1980-1990十年气温数据集

# Python的数据处理库pandas,类似Excel
import pandas  as pd

# Python绘图工具
import matplotlib.pyplot as plt
import seaborn as sns   # 这个是matplotlib的进一步封装,绘制的图表更加高大上
%matplotlib inline

# 设置绘图大小
plt.style.use({'figure.figsize':(25, 20)})

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

import warnings  # 过滤警告用
warnings.filterwarnings('ignore')

下面导入墨尔本气温数据集

"""读取数据集"""
df = pd.read_csv('dataset/temperature_min.csv')  # csv是通过逗号进行分割,如果不是逗号的数据,需要指定sep

3. 数据探索

3.1 探索性分析初步

第一列Date为日期,也就是时间序列,我们将第一列的数据类型转换为pandas中的datatime日期类型,并将这一列作为数据的索引,便于后续处理。

df['Date'] = pd.to_datetime(df['Date'])
df = df.set_index('Date')

"""df是pandas里面的DataFrame数据类型,具有一系列封装好的属性和方法。  .后面带()是方法,不带()的是属性"""

然后数据简单探索

# 再查看一下数据
df.head()
# 查看各列信息
df.info()
# 统计信息
df.describe()

3.2 数据的EDA分析

下面是数据的EDA分析pandas_profiling直接生成数据报告,这个是比赛探索数据神器,在这里面会看到:

  • 总体的数据信息(首先是数据集信息:变量数(列)、观察数(行)、数据缺失率、内存;数据类型的分布情况),

  • 警告信息

    • 要点:类型,唯一值,缺失值
    • 分位数统计量,如最小值,Q1,中位数,Q3,最大值,范围,四分位数范围
    • 描述性统计数据,如均值,模式,标准差,总和,中位数绝对偏差,变异系数,峰度,偏度
  • 单变量描述(对每一个变量进行描述)

  • 相关性分析(皮尔逊系数和斯皮尔曼系数)

  • 采样查看等

import pandas_profiling as ppf
ppf.ProfileReport(df)

3.3 可视化一些特征

这里会通过各种图进行展示数据的特征,重点是这些图怎么使用。

3.3.1 折线图

我们把Temp,也就是温度属性用折线图可视化一下:

"""绘制折线图"""
df['Temp'].plot(figsize=(30,15))
# 设置座标字体大小
plt.tick_params(labelsize=30)
# 生成刻度线网格
plt.grid()

"""折线图容易产生毛刺,可以换成散点图"""

看一下结果:
在这里插入图片描述

3.3.2 散点图

"""绘制散点图"""
df['Temp'].plot(style='k.', figsize=(30,15))
# 设置座标字体大小
plt.tick_params(labelsize=30)
# 生成刻度线网格
plt.grid()

结果:
在这里插入图片描述

3.3.3 直方图

# 设置绘图大小
plt.style.use({'figure.figsize':(5, 5)})
df['Temp'].plot(kind='hist', bins=20)  # bins参数表示横轴多少等分

# 还有一种
 df['Temp'].hist(bins=50)

结果:

在这里插入图片描述

3.3.4 堆积面积图

# 设置绘图大小
plt.style.use({'figure.figsize':(10, 5)})
df.plot.area(stacked=False)

结果:
在这里插入图片描述

3.3.5 核密度估计图(KDE图)

# 设置绘图大小
plt.style.use({'figure.figsize':(10, 5)})
df['Temp'].plot(kind='kde')

结果:
在这里插入图片描述

3.3.6 热力图

将1982年每一个月的最低气温用热力图展示出来
关于pandas的重采样resample

# 设置绘图大小
plt.style.use({'figure.figsize':(20, 8)})

df['1982'].resample('M').mean().T

sns.heatmap(df['1982'].resample('M').mean().T)

"""为什么6月和7月气温最低?  因为墨尔本在澳大利亚,属于南半球,6月和7月是冬天"""

结果:
在这里插入图片描述

3.4 绘制每年气温变化的直线图,箱型图,热力图,小提琴图

我们刚刚将日期列转换成了pandas中的datetime类型,我们可以直接通过年份和日期索引选择指定时间的数据

df['1984']

下面实用pandas的groupby操作,把每年和每天的温度筛选出来python处理数据的风骚操作pandas 之 groupby&agg

groups = df.groupby(pd.Grouper(freq='1Y'))['Temp']
years = pd.DataFrame()
for name, group in groups:
    years[name.year] = group.values

years

这一个要看一下实现了什么效果:
在这里插入图片描述
这个就是把十年的每一天的数据都给做了出来,每一列代表年,每一行是天。下面绘制图像进行可视化上面这个表:

# 设置绘图大小
plt.style.use({'figure.figsize':(30, 15)})

years.plot()

# 设置图例文字大小和图示大小
plt.legend(fontsize=15, markerscale=15)
# 设置座标文字大小
plt.tick_params(labelsize=15)

结果:
在这里插入图片描述

3.4.1 折线图

"""折线图"""
years.plot(subplots=True, figsize=(20, 45))
plt.show()

这个会绘制每一年的折线图,结果就不全显示了:
在这里插入图片描述

3.4.2 箱型图

这个图可以很容易的看出离群点和数据的分布

"""箱型图"""
years.boxplot(figsize=(20, 10))

结果如下:
在这里插入图片描述

3.4.3 热力图

# 设置绘图大小
plt.style.use({'figure.figsize':(30, 10)})
sns.heatmap(years.T)
"""颜色越黑,表示温度越低, 颜色越亮,表示温度越高"""

结果:
在这里插入图片描述

plt.matshow(years.T, interpolation=None, aspect='auto')

在这里插入图片描述

3.4.4 每一年气温的直方图

"""每一年气温的直方图"""
plt.style.use({'figure.figsize':(30, 22)})
years.hist(bins=15)

这个不做展示。

下面绘制1985年12个月每天的气温数据,这个和上面的异曲同工,只不过那个是每一年的每一天,这个是每一个月的每一天。

# 选取1985年12个月每天的气温数据
groups_month = df['1985'].groupby(pd.Grouper(freq='1M'))['Temp']

months = pd.concat([pd.DataFrame(x[1].values) for x in groups_month], axis=1)
months = pd.DataFrame(months)
months.columns = range(1, 13)
months

看一下months:
在这里插入图片描述
绘制箱型图:

months.boxplot(figsize=(20, 15))
plt.title('墨尔本1982年每个月最低温度分布箱型图')

绘制热力图:

# 设置图像大小
plt.style.use({'figure.figsize':(5, 8)})
sns.heatmap(months)
plt.title('墨尔本1982年每天最低气温分布热力图')
plt.matshow(months, interpolation=None, aspect='auto', cmap='rainbow')

3.4.5 小提琴图

类似箱型图,只不过比箱型图更加高级一些

# 设置图像大小
plt.style.use({'figure.figsize':(15, 10)})

sns.violinplot(data=months)
plt.title('墨尔本1982年每个月最低气温分布小提琴图')

# linewidth参数可以控制线宽
sns.violinplot(data=months, linewidth=3)
plt.title('墨尔本1982年每个月最低气温分布小提琴图')

结果:
在这里插入图片描述

3.5 滞后散点图

时间序列分析假定一个观测值与前面的观测值之间存在一定的关系。

相对于某观察值之前的观测值被称为滞后值,在一个时间步长前的观测值称为滞后一期,在两个时间步长前的观测值称为滞后二期,依次类推。

比如, 对于1982年8月15日的气温数据,8月14日的气温为滞后一期,8月13日的气温为滞后二期

每个观察值之间和其滞后值之间的关系,可以用滞后散点图表示

from pandas.plotting import lag_plot

# 设置图像大小
plt.style.use({'figure.figsize':(10, 10)})

lag_plot(df['Temp'])
plt.title('墨尔本1980-1990年最低气温滞后1期散点图')

结果:
在这里插入图片描述
散点图聚在左下角到右上角,表示与滞后值正相关。 散点图聚在左上角到右下角,表示与滞后值负相关。 离对角线越紧密,表示相关关系越强。 分散的球状散点图表示相关关系微弱。

横轴表示每天的气温,纵轴表示滞后一天的气温

通过lag参数控制滞后值

lag_plot(df['Temp'], lag=3)
plt.title('墨尔本1980-1990年最低气温滞后3期散点图')

结果:
在这里插入图片描述
还可以绘制不同滞后值对应的散点图:

"""绘制不同滞后值对应的滞后散点图"""
lag_list = [1, 2, 3, 5, 10, 20, 50, 100, 150, 180]
plt.style.use({'figure.figsize':(15, 35)})
for i in range(len(lag_list)):
    ax = plt.subplot(5, 2, i+1)
    ax.set_title('t vs t+' + str(lag_list[i]))
    lag_plot(df['Temp'], lag=lag_list[i])
    plt.title('墨尔本1980-1990年最低气温滞后{}期散点图'.format(lag_list[i]))

3.6 自相关图

这个图还是比较重要的,尤其是对于时间序列来说,可以看出周期和相关性。
在这里插入图片描述
代码如下:

from pandas.plotting import autocorrelation_plot   

# 设置图像大小
plt.style.use({'figure.figsize':(15, 10)})

autocorrelation_plot(df['Temp'])
plt.title('墨尔本1980-1990最低气温自相关图')

# 设置座标文字大小
plt.tick_params(labelsize=10)
plt.yticks(np.linspace(-1, 1, 120))

结果如下:
在这里插入图片描述
比如说横轴是1000, 就表示这个数据集所有数据与它之后1000天的数据的自相关程度。

时间间隔的越远,温度之间的自相关程度就摆动着降低。 并且在时间向远推进的过程中,还会出现周期性的波动,这是由季节的周期性更迭造成的

如果我们随机取点绘制滞后图和自相关图,就会发现完全没有规律。

# 设置图像大小
plt.style.use({'figure.figsize':(10, 5)})

a = np.random.randn(100)
a = pd.Series(a)
lag_plot(a)
plt.title("随机数列的1期滞后散点图")

结果:
在这里插入图片描述

autocorrelation_plot(a)
plt.title('随机数列的自相关图')

在这里插入图片描述

4. 时间数据的特征工程

将单纯的日期维度扩展成更多维度,构造更多的输入给模型的特征,增强数据的可解释性,更接近人的业务逻辑。

  • 哪一年
  • 哪一个月
  • 星期几
  • 这个月的第几天
  • 月初还是月末
  • 一年当中的第几天
  • 一年当中的第几个月
# 重新调用pandas的read_csv函数读取数据集文件
df2 = pd.read_csv('dataset/temperature_min.csv')
df2['Date'] = pd.to_datetime(df2['Date'])

下面开始构造数据特征:

  1. 把年月日作为特征构造成列

    # 构造新的一列: 年
    df2.loc[:, 'year'] = df2['Date'].apply(lambda x: x.year)
    
    # 构造新的一列: 月
    df2.loc[:, 'month'] = df2['Date'].apply(lambda x: x.month)
    
    # 构造新的一列: 星期几
    df2.loc[:, 'dow'] = df2['Date'].apply(lambda x:x.dayofweek)
    
    # 构造新的一列: 一个月第几天
    df2.loc[:, 'dom'] = df2['Date'].apply(lambda x: x.day)
    
  2. 是不是周末,是不是周六,是不是周日

    # 构造新的三列: 是不是周末、是不是周六、是不是周日
    df2.loc[:, 'weekend'] = df2['Date'].apply(lambda x:x.dayofweek > 4)
    df2.loc[:, 'weekend_sat'] = df2['Date'].apply(lambda x: x.dayofweek == 5)
    df2.loc[:, 'weekend_sun'] = df2['Date'].apply(lambda x: x.dayofweek == 6)
    
  3. 添加上半月和下半月的信息

# 添加上半月和下半月的信息
def half_month(day):
    if day in range(1, 16):
        return 1
    else:
        return 2

df2.loc[:, 'half_month'] = df2['dom'].apply(lambda x:half_month(x))
  1. 添加每个月上中下旬的信息
# 添加每个月上中下旬的信息
def three_part_month(day):
    if day in range(1, 11):
        return 1
    if day in range(11, 21):
        return 2
    else:
        return 3

df2.loc[:, 'three_part_month'] = df2['dom'].apply(lambda x: three_part_month(x))
  1. 添加每个月四个星期的信息 一个月的第几个星期
# 添加每个月四个星期的信息  一个月的第几个星期
def four_week_month(day):
    if day in range(1, 8):
        return 1
    if day in range(8, 15):
        return 2
    if day in range(15, 22):
        return 3
    else:
        return 4

df2.loc[:, 'four_week_month'] = df2['dom'].apply(lambda x: four_week_month(x))
  1. 添加节假日信息
# 添加节假日信息
df2.loc[:, 'festival'] = 0
df2.loc[(df2.month==1) & (df2.dom<4), 'festival'] = 1

看看结果,已经从原来的一列气温出来了好多个时间特征,这些对于后面模型会有用。
在这里插入图片描述
有了好的特征才有好的模型的输入,并且对于一年的季度还有一个月的上中下旬,这样用1234表示的定类数据,计算机并不能理解什么意思,123表示上中下,并不意味着下旬就比上旬大2,所以需要转成独热编码的形式显示。

5. 独热编码One-Hot Encoding

独热编码用来将定类数据表示成0-1二进制,便于输入模型中。

比如,计算机并不认识颜色这一列的“红”, “黄”, “绿”三个分类,所以我们用“是不是红色”, “是不是黄色”, “是不是绿色”三列来分开“颜色”这一列特征。

下面开始做独热编码,但是在做独热编码之前,先保存一份数据

before_dummy_df = df2.copy()

# 构造数据集的特征
drop_columns = ['Date', 'Temp']
X_before_dummy = before_dummy_df.drop(drop_columns, axis=1)

# 构造数据集的标签
Y = df['Temp']

"独热向量编码"
columns_to_encoding = ['year', 'month', 'dow', 'dom', 'three_part_month', 'four_week_month']

# 使用pandas的get_dummyise函数对df2指定的列进行独热向量编码
dummy_X = pd.get_dummies(X_before_dummy, columns=columns_to_encoding)

独热编码之后,就会发现数据有了72列特征
在这里插入图片描述
我们通过上面,也构造出了X和Y了,到现在我们有两组数据:

  • 没有经过独热向量编码操作的数据X_befor_dummy
  • 经过独热向量编码的数据dummy_X

共有标签Y。

有了数据,我们就可以建立模型进行拟合了。

6. 机器学习模型的回归拟合

准备好了数据集,就可以进行回归拟合了,我们先用几个常见的解决回归问题的机器学习模型:

  • 多元线性回归
  • 多项式回归
  • 岭回归
  • 决策树和随机森林
  • 支持向量机
  • 多层感知机(多层神经网络)

回归模型的评估指标:
在这里插入图片描述

6.1 多元线性回归拟合

在这里插入图片描述
代码如下:

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

# 划分训练集和验证集
x_train, x_test, y_train, y_test = train_test_split(dummy_X, Y, test_size=0.2, random_state=1, shuffle=True)

lr_reg = LinearRegression()
lr_reg.fit(x_train, y_train)
print('截距', lr_reg.intercept_)
print('斜率(线性模型中各特征对应的系数)', lr_reg.coef_)

下面把拟合效果进行可视化:

# 绘制气温散点图
df['Temp'].plot(style='k.', figsize=(20, 10))
df.loc[:, '线性回归'] = lr_reg.predict(dummy_X)
plt.plot(df['线性回归'], 'r.')

# 设置座标文字大小
plt.tick_params(labelsize=20)

# 生成刻度线网格
plt.grid()

结果如下:
在这里插入图片描述

6.2 二项多项式回归

在这里插入图片描述
代码如下:

from sklearn.preprocessing import PolynomialFeatures

# 构建一个特征处理器poly_reg, 它能将输入特征变成二次的,见上面图
poly_reg = PolynomialFeatures(degree=2)

# 使用构建的二项多项式特征处理器poly_reg处理训练数据dummy_X, 得到包含二次特征的训练集X_poly
X_poly = poly_reg.fit_transform(dummy_X)

解释一下上面的代码:
在这里插入图片描述
构建二次多项式回归模型,将上面构造好的X_poly送进去训练

lin_reg_2 = LinearRegression()
lin_reg_2.fit(X_poly, Y)

# 查看回归方程系数
print('每个维度对应的系数(斜率):Cofficients: ', lin_reg_2.coef_)

# 查看回归方程截距
print('截距: intercept', lin_reg_2.intercept_)
df.loc[:, '二次多项式回归'] = lin_reg_2.predict(X_poly)

可视化结果:

# 绘制二次多项式回归模型拟合的气温
df.loc[:, '二次多项式回归'] = lin_reg_2.predict(X_poly)
plt.plot(df['二次多项式回归'], 'g*')

# 绘制气温散点图
df['Temp'].plot(style='k.', figsize=(20, 13))

# 设置图例文字大小和图示大小
plt.legend(fontsize=25, markerscale=5)

# 设置座标文字大小
plt.tick_params(labelsize=25)

# 生成网格
plt.grid()

结果如下:
在这里插入图片描述
三次多项式回归模型:
在这里插入图片描述

# 构建一个特征处理器ploy_reg3, 它能将输入特征变成三次的
ploy_reg3 = PolynomialFeatures(degree=3)

# 使用构建的三次多项式处理训练集dummy_X, 得到包含三次特征的训练集X_poly3
X_poly3 = ploy_reg3.fit_transform(dummy_X)

# 构建三次多项式回归模型并训练
lin_reg_3 = LinearRegression()
lin_reg_3.fit(X_poly3, Y)
df.loc[:, '三次多项式回归'] = lin_reg_3.predict(X_poly3)

这个可视化一下,就会发现这个会过拟合:

# 绘制二次多项式回归模型拟合的气温, 绿色五角星表示
plt.plot(df['二次多项式回归'], 'g*')
# 绘制三次多项式回归模型拟合的气温,红色三角形表示
plt.plot(df['三次多项式回归'], 'r^')

# 绘制气温散点图, 黑色点表示
df['Temp'].plot(style='k.', figsize=(20, 13))

# 设置图例文字大小和图示大小
plt.legend(fontsize=15, markerscale=3)

# 设置座标文字大小
plt.tick_params(labelsize=25)

# 生成网格
plt.grid()

"""这个三项式的这个明显出现了过拟合现象"""

结果:
在这里插入图片描述
三次多项式拟合的点与原始数据完全重合,出现了过拟合。
在机器学习中,我们希望模型能大而化之的学习到数据普遍一般规律,而不是对每一个点死记硬背。
因此,线性回归中,二项多项式回归效果是最好的。

6.3 岭回归

在这里插入图片描述
代码如下:

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(dummy_X, Y, test_size=0.2, random_state=1, shuffle=True)

from sklearn.linear_model import RidgeCV

# alphas 表示正则化强度, 类似之前的代价函数中的λ, cv=5表示5折交叉验证
ridge = RidgeCV(alphas=[0.2, 0.5, 0.8], cv=5)
ridge.fit(x_train, y_train)

ridge.score(x_test, y_test)

可视化一下:

# 绘制气温散点图, 用黑色点表示
df['Temp'].plot(style='k.', figsize=(20, 13))

df.loc[:, '岭回归'] = ridge.predict(dummy_X)
plt.plot(df['岭回归'], 'g.')

# 设置图例文字大小和图示大小
plt.legend(fontsize=15, markerscale=3)

# 设置座标文字大小
plt.tick_params(labelsize=25)

# 生成刻度线网格
plt.grid()

结果:
在这里插入图片描述

6.4 决策树和随机森林

决策树和随机森林的训练数据不需要进行独热向量编码,所以我们可以直接用X_before_dummy。

"""划分训练集和测试集"""
x_train, x_test, y_train, y_test = train_test_split(X_before_dummy, Y, test_size=0.2, random_state=1, shuffle=True)

# 从Python机器学习与数据挖掘工具库sklearn导入随机森林回归器
from sklearn.ensemble import RandomForestRegressor

# 导入网格搜索交叉验证,网格搜索可以让模型参数按我们给定的列表遍历,找到效果最好的模型
# 交叉验证可以充分评估模型的准确性
from sklearn.model_selection import GridSearchCV

# 构造参数字典
param_grid = {
    'n_estimators': [5, 10, 20, 50, 100, 200],    # 决策树的个数
    'max_depth': [3, 5, 7],   # 最大树身, 树太深会造成过拟合
    'max_features': [0.6, 0.7, 0.8, 1]  # 决策树划分时考虑的最大特征数
}

rf = RandomForestRegressor()

# 以随机森林回归器为基础构造网格搜索回归器
grid = GridSearchCV(rf, param_grid=param_grid, cv=3)

# 在训练集上训练
grid.fit(x_train, y_train)

"""选取最优参数对应的模型"""
# 查看效果最好的参数
grid.best_params_
# 指定模型效果最好的参数对应的模型
rf_reg = grid.best_estimator_

# 可视化决策树
from sklearn import tree
import pydotplus
from IPython.display import Image, display

# 从随机森立中选取一棵决策树进行可视化
estimator = rf_reg.estimators_[5]

dot_data = tree.export_graphviz(estimator, out_file=None, filled=True, rounded=True)
graph = pydotplus.graph_from_dot_data(dot_data)
display(Image(graph.create_png()))

6.4.1 特征重要度分析

"""特征重要度分析"""
rf_reg.feature_importances_

print('特征排序')

feature_names = ['year', 'month', 'dow', 'dom', 'weekend', 'weekend_sat', 'weedend_sun', 'half_month',
                'three_part_month', 'four_week_month', 'festival']

feature_importances = rf_reg.feature_importances_
indics = np.argsort(feature_importances)[::-1]

for index in indics:
    print('features %s (%f)' %(feature_names[index], feature_importances[index]))
plt.figure(figsize=(16, 8))

plt.title('随机森立模型不同特征的重要度')
plt.bar(range(len(feature_importances)), feature_importances[indics], color='b')
plt.xticks(range(len(feature_importances)), np.array(feature_names)[indics], color='b')

可以看到特征重要程度:
在这里插入图片描述

6.4.2 可视化回归拟合效果

# 绘制气温散点图
df['Temp'].plot(style='k.', figsize=(20, 15))

# 绘制随机森林模型拟合的气温
df.loc[:, '随机森林'] = rf_reg.predict(X_before_dummy)
plt.plot(df['随机森林'], 'r.')

# 绘制岭回归模型拟合的气温
plt.plot(df['岭回归'], 'g.')

plt.legend(fontsize=15, markerscale=3)

plt.tick_params(labelsize=25)

plt.grid()

结果:
在这里插入图片描述

6.5 多层神经网络

神经网络对输入特征的幅度很敏感,我们首先需要将输入特征归一化

from sklearn.preprocessing import scale

feature = scale(X_before_dummy)
X_train, X_val, Y_train, Y_val = train_test_split(feature, Y, test_size=0.2, random_state=1, shuffle=True)

"""训练集共有2920条数据, 对应2920天的11个时间特征,标签为对应的气温"""
# 使用Keras模型快速搭建全连接神经网络
from keras.models import Sequential
from keras.layers.core import Dense, Dropout
from keras.optimizers import SGD

model = Sequential()

# 第一层,32个神经元,激活函数是relu, 接收11个特征作为输入
model.add(Dense(32, activation='relu', input_shape=(X_train.shape[1], )))

# 第二层,64个神经元,激活函数为relu
model.add(Dense(64, activation='relu'))

# 回归模型的神经网络最后一层不需要有激活函数,直接用一个神经元线性输出结果即可
model.add(Dense(1))

# 将模型封装好,使用均方误差mse作为损失函数,使用学习率0.001的随机梯度下降算法反向传播,同时平均绝对误差mae监控模型训练效果
model.compile(loss='mse', optimizer=SGD(lr=0.001), metrics=['mae'])

# 训练神经网络, 每一批128个数据, 训练50轮(即过50遍完整的训练集), 每一轮结束后用验证集评估模型效果
network_history = model.fit(X_train, Y_train, batch_size=128, epochs=50, verbose=1, validation_data=(X_val, Y_val))

绘制结果误差:

"""绘制训练过程的mse误差和mae误差"""
def plot_history(network_history):
    plt.figure()
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.plot(network_history.history['loss'])
    plt.plot(network_history.history['val_loss'])
    plt.legend(['Training', 'Validation'])
    
    plt.figure()
    plt.xlabel('Epochs')
    plt.ylabel('Mae')
    plt.plot(network_history.history['mean_absolute_error'])
    plt.plot(network_history.history['val_mean_absolute_error'])
    plt.legend(['Training', 'Validation'])
    plt.show()

plot_history(network_history)

模型评估:

model.evaluate(X_val, Y_val, batch_size=10)

结果可视化:

from sklearn.preprocessing import scale
# 绘制气温散点图
df['Temp'].plot(style='k.', figsize=(30, 25))

# 绘制随机森林模型的回归拟合散点图
plt.plot(df['随机森林'], 'r.')

# 绘制岭回归模型的回归拟合散点图
plt.plot(df['岭回归'], 'g.')

# 绘制多层感知神经网络的回归拟合散点图
df.loc[:, "多层神经元"] = model.predict(scale(X_before_dummy))
plt.plot(df['多层神经元'], 'b.')

# 设置图例文字大小和图示大小
plt.legend(fontsize=20, markerscale=5)

# 设置座标文字大小
plt.tick_params(labelsize=25)

# 生成刻度线网格
plt.grid()

结果如下:
在这里插入图片描述

7. 将回归拟合的结果文件保存,并比较不同模型的表现

df.to_csv('final_regression.csv', index=True)

result = pd.read_csv('final_regression.csv')
result_analyse = result.describe().copy()
# 给result_analyse新构造一行MSE
result_analyse.loc['MSE', :] = 0

# 构造计算均方误差的函数
def MSE(yhat, y):
    error = np.array(yhat-y)
    error_power = np.power(error, 2)
    MSE_error = np.sum(error_power) / len(y)
    
    return MSE_error

# 把result_analyse新构造的MSE行填模型的均方误差
for each in result_analyse.columns:
    result_analyse.loc['MSE', each] = MSE(result[each], result['Temp'])
result_analyse

结果如下:
在这里插入图片描述
最后是一个模型比较的可视化结果:

plt.figure(figsize=(20, 20))
plt.subplot(421)
plt.title('平均值')
result_analyse.loc['mean', :].plot(kind='bar', color='k')

plt.subplot(422)
plt.title('方差')
result_analyse.loc['std', :].plot(kind='bar', color='y')

plt.subplot(423)
plt.title('最小值')
result_analyse.loc['min', :].plot(kind='bar', color='m')

plt.subplot(424)
plt.title('下四分位数')
result_analyse.loc['25%', :].plot(kind='bar', color='c')

plt.subplot(425)
plt.title('中位数')
result_analyse.loc['50%', :].plot(kind='bar', color='r')

plt.subplot(426)
plt.title('上四分位数')
result_analyse.loc['75%', :].plot(kind='bar', color='g')

plt.subplot(427)
plt.title('最大值')
result_analyse.loc['max', :].plot(kind='bar', color='b')

plt.subplot(428)
plt.title('均方误差')
result_analyse.loc['MSE', :].plot(kind='bar', color='deepskyblue')

plt.subplots_adjust(wspace=0.07, hspace=0.6)   # 调整子图间距
plt.show()

这里展示一部分结果:
在这里插入图片描述

8. 总结

这里只是想单纯的整理一个时间序列挖掘模板,方便以后查询使用,毕竟感觉写的还是挺全的,从数据导入,探索,各种格式化,到特征工程,建立各种模型,各种模型的评估等,我觉得在后面的任务中,会有借鉴之处,所以先整理下来。

如果想看详细的讲解视频:
时间序列数据挖掘与机器学习:墨尔本十年气温数据集
如果想获取详细的代码和数据集:https://download.csdn.net/download/wuzhongqiang/12245197

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章