李宏毅课程-机器学习 PM2.5预测

前言

目前有在 百度 的 AI Studio 上的李宏毅课程-机器学习特训营上学习,有练习到回归问题中的练习题,采用逻辑回归的方法去尝试编写程序回归预测PM2.5的值。尝试把一些心得记录下来!

项目描述

  • 本次作业的资料是从行政院环境环保署空气品质监测网所下载的观测资料。
  • 希望大家能在本作业实现 linear regression 预测出 PM2.5 的数值。

数据集介绍

  • 本次作业使用丰原站的观测记录,分成 train set 跟 test set,train set 是丰原站每个月的前 20 天所有资料。test set 则是从丰原站剩下的资料中取样出来。
  • train.csv: 每个月前 20 天的完整资料。
  • test.csv : 从剩下的资料当中取样出连续的 10 小时为一笔,前九小时的所有观测数据当作 feature,第十小时的 PM2.5 当作 answer。一共取出 240 笔不重複的 test data,请根据 feature 预测这 240 笔的 PM2.5。
  • Data 含有 18 项观测数据 AMB_TEMP, CH4, CO, NHMC, NO, NO2, NOx, O3, PM10, PM2.5, RAINFALL, RH, SO2, THC, WD_HR, WIND_DIREC, WIND_SPEED, WS_HR。

载入数据集并转化为Pandas表格

import pandas as pd
import numpy as np
import math

# train.csv 数据 的表头 为
# 日期  测站  测项  0  1  2  3  4  5  .....  21  22  23

# 因为每个月抽取20天的数据,一年有12个月,所以为 12 * 20 * 18(测项) = 4320 行
# 因为从数据的表头观察 有 3(日期、测站、测项) + 24(小时) = 27 列
# 数据表为:4320(行) * 27(列)
data = pd.read_csv('data/train.csv', encoding='big5')

查看数据集资料栏位

# 查看 train.csv 数据 的表头 
data.columns
Index(['日期', '測站', '測項', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
       '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21',
       '22', '23'],
      dtype='object')

# 查看 train.csv 数据 的结构
print("train.csv 的 数据表结构: " + str(data.shape))
train.csv 的 数据表结构: (4320, 24)

查看数据表大致情况

# 查看数据的 前18行
print(data.head(18))
          日期  測站          測項     0     1  ...    19    20    21    22    23
0   2014/1/1  豐原    AMB_TEMP    14    14  ...    16    15    15    15    15
1   2014/1/1  豐原         CH4   1.8   1.8  ...   1.8   1.8   1.8   1.8   1.8
2   2014/1/1  豐原          CO  0.51  0.41  ...  0.45  0.38  0.35  0.36  0.32
3   2014/1/1  豐原        NMHC   0.2  0.15  ...  0.12   0.1  0.09   0.1  0.08
4   2014/1/1  豐原          NO   0.9   0.6  ...   1.9   1.5   1.6   1.8   1.5
5   2014/1/1  豐原         NO2    16   9.2  ...    12   8.1     7   6.9     6
6   2014/1/1  豐原         NOx    17   9.8  ...    13   9.7   8.6   8.7   7.5
7   2014/1/1  豐原          O3    16    30  ...    34    37    38    38    36
8   2014/1/1  豐原        PM10    56    50  ...    63    46    36    42    42
9   2014/1/1  豐原       PM2.5    26    39  ...    44    41    30    24    13
10  2014/1/1  豐原    RAINFALL    NR    NR  ...    NR    NR    NR    NR    NR
11  2014/1/1  豐原          RH    77    68  ...    69    70    70    70    69
12  2014/1/1  豐原         SO2   1.8     2  ...   2.3     2   1.9   1.9   1.9
13  2014/1/1  豐原         THC     2     2  ...   1.9   1.9   1.9   1.9   1.9
14  2014/1/1  豐原       WD_HR    37    80  ...   121   113   112   106   110
15  2014/1/1  豐原  WIND_DIREC    35    79  ...   118   114   108   102   111
16  2014/1/1  豐原  WIND_SPEED   1.4   1.8  ...     3   2.6   2.7   2.1   2.1
17  2014/1/1  豐原       WS_HR   0.5   0.9  ...   2.5   2.8   2.6   2.4   2.3

[18 rows x 27 columns]

Loss (损失函数)介绍

在本次的线性回归中,使用RMSE(均方根误差)定义损失函数,用来度量模型性能的指标。
李宏毅课程-机器学习 PM2.5预测_第1张图片
使用RMSE(均方根误差)来定义损失函数是具有平滑可微的特性。

AdaGrad 算法介绍

对于模型参数的权重值的优化,常常使用梯度下降法来进行优化,而在SGD随机梯度下降法的基础之上,使用AdaGrad算法,可以在参数空间中,对于想对平缓的方向,可以取得更大的进步。
李宏毅课程-机器学习 PM2.5预测_第2张图片
AdaGrad算法与SGD梯度下降法相比,采用了累积平方梯度。
举例讲解不同:

李宏毅课程-机器学习 PM2.5预测_第3张图片

假设参数空间中,有两个参数为 w 和 b,其中从图中看出,b方向的比较陡峭,而w方向比较平缓。
使用小批量梯度下降法(Mini-batch GD),移动的方向如上面的蓝色轨迹所示。

使用AdaGrad算法后,会使用累积平方梯度
李宏毅课程-机器学习 PM2.5预测_第4张图片
李宏毅课程-机器学习 PM2.5预测_第5张图片

在下次计算更新的时候,r是作为分母出现的,越大的反而更新越小,越小的值反而更新越大,那么后面的更新则会像上面绿色线更新一样,明显就会好于蓝色更新曲线。
在参数空间更为平缓的方向,会取得更大的进步(因为平缓,所以历史梯度平方和较小,对应学习下降的幅度较小),并且能够使得陡峭的方向变得平缓,从而加快训练速度。
所以,一般来说AdaGrad算法一开始是激励收敛,到了后面就慢慢变成惩罚收敛,速度越来越慢。

程序码展示

import pandas as pd
import numpy as np
import math

# train.csv 数据 的表头 为
# 日期  测站  测项  0  1  2  3  4  5  .....  21  22  23

# 因为每个月抽取20天的数据,一年有12个月,所以为 12 * 20 * 18(测项) = 4320 行
# 因为从数据的表头观察 有 3(日期、测站、测项) + 24(小时) = 27 列
# 数据表为:4320(行) * 27(列)
data = pd.read_csv('data/train.csv', encoding='big5')

# 发现数据的 前3列 为 日期 观测点 测项
# 选择删除,那么数据表为:4320(行) * 24(列)
data = data.iloc[:, 3:]

# 发现 测项 为 RAINFALL 的数据内容为 NR
# 便于回归预测, 将数据内容 改为0 数值化
data[data == 'NR'] = 0

# 将 pandas 二维表格 转换为 numpy 二维数组
raw_data = data.to_numpy()

# 建立字典(dict)变数,储存每个月份的 数据
month_data = {}

# 一个月有 20天,一天有 24小时,一个月就有 480小时
for month in range(12):
    # 生成 18 * 480 的二维矩阵,因使用 numpy.empty(shape, dtype=float, order=‘C’),矩阵内数据为 float
    sample = np.empty([18, 480])
    for day in range(20):
        sample[:, day * 24:(day + 1) * 24] = raw_data[18 * (20 * month + day):18 * (20 * month + day + 1), :]
    month_data[month] = sample


# 一个月有 480小时,但题目要求依据前 9小时进行预测
# 若对一个月的时间序列进行分类样本数据,可以分为 480-9=471笔
# 因为有18个测项,每个测项有前9个小时的数据,使用 一笔测试数据有 18*9 个特征
x = np.empty([12 * 471, 18 * 9], dtype=float)
# x.shape  = (5652, 162)  
y = np.empty([12 * 471, 1], dtype=float)
# y.shape  = (5652, 1)

for month in range(12):
    for day in range(20):
        for hour in range(24):
            # 当天数(day)==19 代表该月的最后一天
            # 同时 小时(hour)>14 代表后面的数据只剩9笔,没有第10笔数据来预测
            if day == 19 and hour > 14:
                continue
            # month_data[month] = 一个 sample,为 (18, 480) 的二维矩阵
            # x 为 (5652, 162) 的 numpy 二维数组
            # 每次从month_data[month]中取 (18, 9) 的二维矩阵数据,并使用 .reshape(1, -1) 平展化
            # 转换为(1, 162) 的一维矩阵数据 存入 x 中
            x[month * 471 + day * 24 + hour, :] = month_data[month][:, day * 24 + hour:  day * 24 + hour + 9].reshape(1, -1)
            # sample为 (18, 480) 的二维矩阵,18代表18种测项,其中第9种为 PM2.5
            y[month * 471 + day * 24 + hour, 0] = month_data[month][9, day * 24 + hour + 9]


# 标准化 !!!!!!!!!!

# x 为 (5652, 162) 的 numpy 二维数组
# 对每一列 计算 平均数
mean_x = np.mean(x, axis=0)
# mean_x.shape = (1, 162)
# 对每一列 计算 标准差
std_x = np.std(x, axis=0)
# std_x.shape = (1, 162)

# x 为 (5652, 162) 的 numpy 二维数组
# len(x) = 5652
# len(x[0]) = 162
for i in range(len(x)):
    for j in range(len(x[0])):
        # 之前有发现 测项 为 RAINFALL 的数据内容为 NR
        # 便于回归预测, 将数据内容 改为0 数值化
        # 所以 std_x 有的栏位 为 0
        if std_x[j] != 0:
            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]

# 划分 80% 的数据 为训练集
# math.floor(len(x) * 0.8) = 4521
# x_train_set.shape = (4521, 162)
x_train_set = x[:math.floor(len(x) * 0.8), :]
# x_train_set.shape = (4521, 1)
y_train_set = y[:math.floor(len(y) * 0.8), :]

# 划分 20% 的数据 为验证集
# x_validation.shape = (1131, 162)
x_validation = x[math.floor(len(x) * 0.8):, :]
# x_validation.shape = (1131, 1)
y_validation = x[math.floor(len(y) * 0.8):, :]

# dim(dimension) 维数
# 在线性回归方程中,有偏差值
# 为方便矩阵运算,对 维数 +1
dim = 18 * 9 + 1

# w.shape = (163, 1)
w = np.zeros([dim, 1])

# x_train_set.shape = (4520, 163)
x_train_set = np.concatenate(
    (np.ones([len(x_train_set), 1]), x_train_set), axis=1).astype(float)

# 学习率
learning_rate = 100
# 训练次数
iter_time = 30000

# adagrad.shape = (163, 1)
adagrad = np.zeros([dim, 1])
eps = 0.0001  # 平滑系数

for t in range(iter_time):
    # RMSE(均方根误差)
    loss = np.sqrt(np.sum(np.power(np.dot(x_train_set, w) - y_train_set, 2)) / len(x_train_set))

    if (t % 1000 == 0):
        print("迭代次数: %i  损失值: %f" % (t, loss))

    gradient = 2 * np.dot(x_train_set.T, np.dot(x_train_set, w) - y_train_set )
    adagrad += (gradient ** 2)
    w = w - learning_rate * gradient / np.sqrt(adagrad + eps)

# 保存 numpy 二进制文件
np.save('data/weights.npy', w)
# 读取 numpy 二进制文件
# w = np.load('data/weights.npy')

# x_validation 验证集
# x_validation.shape = (1131, 163)
x_validation = np.concatenate(
    (np.ones([len(x_validation), 1]), x_validation), axis=1).astype(float)

loss = np.sqrt(np.sum(np.power(np.dot(x_validation, w) - y_validation, 2)) / len(x_validation))

print("在验证集上的损失值: %f" % (loss))

迭代次数: 0  损失值: 27.239592
迭代次数: 1000  损失值: 5.973549
迭代次数: 2000  损失值: 5.781319
迭代次数: 3000  损失值: 5.742096
迭代次数: 4000  损失值: 5.729108
迭代次数: 5000  损失值: 5.724242
迭代次数: 6000  损失值: 5.722252
迭代次数: 7000  损失值: 5.721327
迭代次数: 8000  损失值: 5.720812
迭代次数: 9000  损失值: 5.720464
迭代次数: 10000  损失值: 5.720190
迭代次数: 11000  损失值: 5.719953
迭代次数: 12000  损失值: 5.719740
迭代次数: 13000  损失值: 5.719544
迭代次数: 14000  损失值: 5.719362
迭代次数: 15000  损失值: 5.719193
迭代次数: 16000  损失值: 5.719035
迭代次数: 17000  损失值: 5.718887
迭代次数: 18000  损失值: 5.718748
迭代次数: 19000  损失值: 5.718619
迭代次数: 20000  损失值: 5.718498
迭代次数: 21000  损失值: 5.718385
迭代次数: 22000  损失值: 5.718279
迭代次数: 23000  损失值: 5.718180
迭代次数: 24000  损失值: 5.718087
迭代次数: 25000  损失值: 5.718001
迭代次数: 26000  损失值: 5.717920
迭代次数: 27000  损失值: 5.717844
在验证集上的损失值: 335.134301

结语

现在从程序码的结果上看,产生了严重的过拟合,导致在验证集上的表现非常非常差,那么问题就在于对模型超参数进行调优(调参),同时上面的程式码也使用了 AdaGrad 算法来对梯度进行更新。在后面要把程序的模型进一步调整,哦,还有 程序码的图示,也加油整理出来。

参考资料

[1] 百度 AI Studio,李宏毅课程-机器学习
[2] 知乎忆臻,Deep Learning 最优化方法之AdaGrad
https://zhuanlan.zhihu.com/p/29920135
[3] CSDN,深度学习优化方法-AdaGrad
https://blog.csdn.net/program_developer/article/details/80756008

你可能感兴趣的:(机器学习)