目前有在 百度 的 AI Studio 上的李宏毅课程-机器学习特训营上学习,有练习到回归问题中的练习题,采用逻辑回归的方法去尝试编写程序回归预测PM2.5的值。尝试把一些心得记录下来!
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]
在本次的线性回归中,使用RMSE(均方根误差)定义损失函数,用来度量模型性能的指标。
使用RMSE(均方根误差)来定义损失函数是具有平滑可微的特性。
对于模型参数的权重值的优化,常常使用梯度下降法来进行优化,而在SGD随机梯度下降法的基础之上,使用AdaGrad算法,可以在参数空间中,对于想对平缓的方向,可以取得更大的进步。
AdaGrad算法与SGD梯度下降法相比,采用了累积平方梯度。
举例讲解不同:
假设参数空间中,有两个参数为 w 和 b,其中从图中看出,b方向的比较陡峭,而w方向比较平缓。
使用小批量梯度下降法(Mini-batch GD),移动的方向如上面的蓝色轨迹所示。
在下次计算更新的时候,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