目录
1. 相关知识1.1 普通的梯度下降法1.2 Adagrad算法 2. 作业描述3. 数据预处理4. 建立模型5. 损失函数求导6. 完整代码1. 相关知识
1.1 普通的梯度下降法
普通方法可能出现的问题
学习率太小(蓝色的线),下降慢,迭代次数多
学习率太大(绿色的线),下降快,但是太快容易在极优值附近振荡
学习率特别大(黄色的线),直接越过了极优值
改进的想法:随着迭代次数增加,让学习率变小。
初始迭代时,使用较大的学习率加速下降,迭代几次后,减小学习率防止振荡和越过。
1.2 Adagrad算法
每个参数的学习率都把它除上之前微分的均方根,σt是每个参数的所有偏微分的均方根
公式化简
求偏导细节参考第5节
# adagrad更新梯度# 因为常数项的存在,所以dim需要多加一栏# eps项是避免adagrad的分母为0而加的极小数值# 每一个dim都会对应到各自的gradient, weight(w)# 通过一次次的iteration学习def adagrad(x, dim, num):w = np.zeros([dim, 1])x = np.concatenate((np.ones([num, 1]), x), axis = 1).astype(float)learning_rate = 100iter_time = 10000adagrad = np.zeros([dim, 1])eps = 0.0000000001for t in range(iter_time):loss = (np.sum(np.power(np.dot(x, w) - y, 2)) )/ num # mseif(t%100==0):print(str(t) + ":" + str(loss))gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y) #大小为dim*1adagrad += gradient ** 2w = w - learning_rate * gradient / np.sqrt(adagrad + eps)np.save('weight.npy', w)
2. 作业描述
根据某地一年内18项空气质量有关的检测指标,在24小时的变化数据,使用线性回归预测PM2.5(也是指标之一)的变化。
数据如图所示
训练集:每个月前20天的完整数据
测试集:从剩下的资料中(10 * 12天)中抽出240 * 10小时的数据作为测试
每条数据中,前9小时的观测数据作为feature,共9 * 18 = 162个参数,第10小时的PM2.5作为answer
3. 数据预处理
指标RF对应当天是否降雨,有降雨则为1,无降雨值为NR
考虑降雨对PM2.5有显著影响,将NR全部修改为0
去除无用数据,将pandas数据帧转换为numpy的二维数组
然后抽取训练数据
参考答案这里有些小错误
# 数据预处理def dataProcess(data):# 第一步,数据预处理# 去除前3列的无用数据data = data.iloc[1:, 3:]# 将未降雨的值"NR"修改为0data[data == 'NR'] = 0raw_data = data.to_numpy()return raw_data
4. 建立模型
线性模型
取损失函数为MSE
5. 损失函数求导
先将模型改成矩阵形式:
为了便于用numpy写代码,将常数项也看作系数矩阵的一部分。
这里参考了西瓜书的解答:
求偏导公式:
实际上基于均方误差最小化的最小二乘法,可以直接求其闭式最优解。
6. 完整代码
# -*- coding: utf-8 -*-import sysimport pandas as pdimport numpy as npimport csv# 数据预处理def dataProcess(data):# 第一步,数据预处理# 去除前3列的无用数据data = data.iloc[1:, 3:]# 将未降雨的值"NR"修改为0data[data == 'NR'] = 0raw_data = data.to_numpy()return raw_data# 标准化def normalize(x):mean_x = np.mean(x, axis = 0) std_x = np.std(x, axis = 0) for i in range(len(x)): for j in range(len(x[0])): if std_x[j] != 0:x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]return x# adagrad更新梯度# 因为常数项的存在,所以dim需要多加一栏# eps项是避免adagrad的分母为0而加的极小数值# 每一个dim都会对应到各自的gradient, weight(w)# 通过一次次的iteration学习def adagrad(x, dim, num):w = np.zeros([dim, 1])x = np.concatenate((np.ones([num, 1]), x), axis = 1).astype(float)learning_rate = 100iter_time = 10000adagrad = np.zeros([dim, 1])eps = 0.0000000001for t in range(iter_time):loss = (np.sum(np.power(np.dot(x, w) - y, 2)) )/ num # mseif(t%100==0):print(str(t) + ":" + str(loss))gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y) #dim*1adagrad += gradient ** 2w = w - learning_rate * gradient / np.sqrt(adagrad + eps)np.save('weight.npy', w)return w # 只是方便在内存中查看结果# 18个feature中的第10个为要预测的PM2.5# 將原始 4320 * 18 的資料重組成 18 (features) * 4320 (hours) 的資料。# 12个月,每个月20天 12 * 20 * 18 = 4320if __name__ == '__main__':data = pd.read_csv('train.csv', header = None, encoding = 'big5')raw_data = dataProcess(data)# 每九小时形成一条训练data# 考虑PM2.5每日会有周期性的变化规律,比如工厂不会昼夜不停等#(答案思路非常清奇,用0-8预测9,然后1-9预测10,会充分利用一个月数据中的480小时,得到471个训练数据)# 选择用0 - 8点的数据来预测上午9点的PM2.5 # 权重项有18*9 = 162项raw_data = raw_data[:,0:10]x = np.empty([12 * 20, 18 * 9], dtype = float)y = np.empty([12 * 20, 1], dtype = float)for day in range(240):y[day,0] = raw_data[18*day+9,9]for day in range(240):x[day,:] = raw_data[18*day:18*(day+1),0:9].reshape(1, -1)x = normalize(x) # 训练数据标准化dim = 18 * 9 + 1 # 加上截距项num = 240 # 训练数据共12 * 20 = 240条w = adagrad(x, dim, num)# 用训练出来的w,计算ydata_test = pd.read_csv('test.csv', header = None, encoding = 'big5')data_test = data_test.iloc[:, 2:]data_test[data_test == 'NR'] = 0data_test = data_test.to_numpy()test_x = np.empty([240, 18*9], dtype = float)for i in range(240):test_x[i, :] = data_test[18 * i: 18* (i + 1), :].reshape(1, -1)test_x = np.concatenate((np.ones([240, 1]), test_x), axis = 1).astype(float)# 测试数据要和训练数据作相同的标准化test_x = normalize(test_x) ans_y = np.dot(test_x, w)# 把预测结果存入csvwith open('submit.csv', mode='w', newline='') as submit_file:csv_writer = csv.writer(submit_file)header = ['id', 'value']print(header)csv_writer.writerow(header)for i in range(240):row = ['id_' + str(i), ans_y[i][0]]csv_writer.writerow(row)print(row)