机器学习-回归
前言
此次学习的课程为李宏毅机器学习,之前学过一遍吴恩达的课程,只可惜当时没记笔记,且近些时候没有写代码,逐渐疏忽了,故选择李宏毅再进行新一遍的学习,所谓温故而知新。
回归是我们通常会使用的机器学习中的一类,比如日常中的我们的身高预测,股票预测等等,这些都可以看作为粗略的回归。
举一个例子
小时候我们会玩一个叫赛尔号的游戏,游戏里有各种各样的精力,就好比我们捕捉到了一只雷伊,然后我们可以向雷伊投经验值,让他升级,这只雷伊会有一个攻击力,我们想要预测雷伊的各种各样的属性与其攻击力之间的关系。
于是我们设其血量为\(X_{hp}\),其体重为\(x_{w}\),其身高为\(x_{h}\),其物种为\(x_s\),其战斗力为\(x_{cp}\),然后预测他进化之后的战斗力值。那么便有\(y=b+\sum w_ix_i\),其中\(w_i:weight,b:bias\)。如果单一个\(x_{cp}\)作预测的话便是\(y=b+w\cdot x_{cp}\)。
收集到了数据之后,便是可以进行预测,在此我们使用一个名为Loss函数进行Loss计算 \[ L(f) = \sum^{10}_{n=1}(\hat y^n-f(x^n_{cp}))^2\\ L(f) = \sum^{10}_{n=1}(\hat y^n-(b+w\cdot x^n_{cp}))^2 \] 我们想要的结果是需要 loss 函数尽可能的小,我们需要选择一个最好的函数,在此我们通过梯度下降来进行函数优化,进行如此迭代。 \[ w^1 = w^0-\alpha\frac{dL}{dw}|_{w=w^0}\\ w^2 = w^1-\alpha\frac{dL}{dw}|_{w=w^1} \] 对于我们有两个参数的函数来说,也是一样可以进行迭代 \[ w^1 = w^0-\alpha\frac{\partial L}{\partial w}|_{w=w^0,b=b^0},b^1 = b^0-\alpha\frac{\partial L}{\partial b}|_{w=w^0,b=b^0} \] 沿着梯度方向逐渐减小,直到让 loss 函数最小,偏微分公式如下 \[ \frac{\partial L}{\partial w}=\sum^{10}_{n=1}2(\hat y^n-(b+w\cdot x^n_{cp}))(-x^n_{cp}) \] 那么经过迭代之后便可以求得一个较为合适的\(w和b\),便是得到了方程。
实践
此次的实践为一项 PM.2.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。
import sys
import pandas as pd
import numpy as np
data = pd.read_csv('work/hw1_data/train.csv',encoding='big5')
data[data=='NR']=0
data
日期 | 測站 | 測項 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2014/1/1 | 豐原 | AMB_TEMP | 14 | 14 | 14 | 13 | 12 | 12 | 12 | ... | 22 | 22 | 21 | 19 | 17 | 16 | 15 | 15 | 15 | 15 |
1 | 2014/1/1 | 豐原 | CH4 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | ... | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 |
2 | 2014/1/1 | 豐原 | CO | 0.51 | 0.41 | 0.39 | 0.37 | 0.35 | 0.3 | 0.37 | ... | 0.37 | 0.37 | 0.47 | 0.69 | 0.56 | 0.45 | 0.38 | 0.35 | 0.36 | 0.32 |
3 | 2014/1/1 | 豐原 | NMHC | 0.2 | 0.15 | 0.13 | 0.12 | 0.11 | 0.06 | 0.1 | ... | 0.1 | 0.13 | 0.14 | 0.23 | 0.18 | 0.12 | 0.1 | 0.09 | 0.1 | 0.08 |
4 | 2014/1/1 | 豐原 | NO | 0.9 | 0.6 | 0.5 | 1.7 | 1.8 | 1.5 | 1.9 | ... | 2.5 | 2.2 | 2.5 | 2.3 | 2.1 | 1.9 | 1.5 | 1.6 | 1.8 | 1.5 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4315 | 2014/12/20 | 豐原 | THC | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.7 | 1.7 | ... | 1.8 | 1.8 | 2 | 2.1 | 2 | 1.9 | 1.9 | 1.9 | 2 | 2 |
4316 | 2014/12/20 | 豐原 | WD_HR | 46 | 13 | 61 | 44 | 55 | 68 | 66 | ... | 59 | 308 | 327 | 21 | 100 | 109 | 108 | 114 | 108 | 109 |
4317 | 2014/12/20 | 豐原 | WIND_DIREC | 36 | 55 | 72 | 327 | 74 | 52 | 59 | ... | 18 | 311 | 52 | 54 | 121 | 97 | 107 | 118 | 100 | 105 |
4318 | 2014/12/20 | 豐原 | WIND_SPEED | 1.9 | 2.4 | 1.9 | 2.8 | 2.3 | 1.9 | 2.1 | ... | 2.3 | 2.6 | 1.3 | 1 | 1.5 | 1 | 1.7 | 1.5 | 2 | 2 |
4319 | 2014/12/20 | 豐原 | WS_HR | 0.7 | 0.8 | 1.8 | 1 | 1.9 | 1.7 | 2.1 | ... | 1.3 | 1.7 | 0.7 | 0.4 | 1.1 | 1.4 | 1.3 | 1.6 | 1.8 | 2 |
4320 rows × 27 columns
raw_data = data.iloc[:,3:]
raw_data
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 14 | 14 | 14 | 13 | 12 | 12 | 12 | 12 | 15 | 17 | ... | 22 | 22 | 21 | 19 | 17 | 16 | 15 | 15 | 15 | 15 |
1 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | ... | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 |
2 | 0.51 | 0.41 | 0.39 | 0.37 | 0.35 | 0.3 | 0.37 | 0.47 | 0.78 | 0.74 | ... | 0.37 | 0.37 | 0.47 | 0.69 | 0.56 | 0.45 | 0.38 | 0.35 | 0.36 | 0.32 |
3 | 0.2 | 0.15 | 0.13 | 0.12 | 0.11 | 0.06 | 0.1 | 0.13 | 0.26 | 0.23 | ... | 0.1 | 0.13 | 0.14 | 0.23 | 0.18 | 0.12 | 0.1 | 0.09 | 0.1 | 0.08 |
4 | 0.9 | 0.6 | 0.5 | 1.7 | 1.8 | 1.5 | 1.9 | 2.2 | 6.6 | 7.9 | ... | 2.5 | 2.2 | 2.5 | 2.3 | 2.1 | 1.9 | 1.5 | 1.6 | 1.8 | 1.5 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4315 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.7 | 1.7 | 1.8 | 1.8 | 1.8 | ... | 1.8 | 1.8 | 2 | 2.1 | 2 | 1.9 | 1.9 | 1.9 | 2 | 2 |
4316 | 46 | 13 | 61 | 44 | 55 | 68 | 66 | 70 | 66 | 85 | ... | 59 | 308 | 327 | 21 | 100 | 109 | 108 | 114 | 108 | 109 |
4317 | 36 | 55 | 72 | 327 | 74 | 52 | 59 | 83 | 106 | 105 | ... | 18 | 311 | 52 | 54 | 121 | 97 | 107 | 118 | 100 | 105 |
4318 | 1.9 | 2.4 | 1.9 | 2.8 | 2.3 | 1.9 | 2.1 | 3.7 | 2.8 | 3.8 | ... | 2.3 | 2.6 | 1.3 | 1 | 1.5 | 1 | 1.7 | 1.5 | 2 | 2 |
4319 | 0.7 | 0.8 | 1.8 | 1 | 1.9 | 1.7 | 2.1 | 2 | 2 | 1.7 | ... | 1.3 | 1.7 | 0.7 | 0.4 | 1.1 | 1.4 | 1.3 | 1.6 | 1.8 | 2 |
4320 rows × 24 columns
mouth_data = {}
for mouth in range(12):
sample = np.empty([18,480])
for day in range(20):
sample[:,day*24:(day+1)*24]=raw_data[18*(mouth*20+day):18*(mouth*20+day+1)]
# 数据每个月有 20 天,每天有 24 小时,每个月有 480 小时的数据
# 每天有 18 项数据
# 将每个月 20 天的数据,20*24=480 个数据排列在一行上面
mouth_data[mouth] = sample
根据题目描述,我们要根据前九个小时的数据去预测第十个小时的数据,所以我们需要将数据再次进行切分,每个前九个小时都是x,每个第十个小时都是y,每个月一共有480个小时,所以每个月 y 的数量为 480-9 =471 个,每个 y 对应 18*9 个特征
x = np.empty([12*471,18*9],dtype=float)
y = np.empty([12*471,1],dtype=float)
for mouth in range(12):
for day in range(20):
for hour in range(24):
if day==19 or hour>14:
continue
x[mouth*471+day*24+hour,:] = mouth_data[mouth][:,day*24+hour:day*24+hour+9].reshape(1,-1)
y[mouth*471+day*24+hour,0] = mouth_data[mouth][9,day*24+hour+9]
目前 x 为12 * 471 行, 18*9 列
目前 y 为12 * 471 行, 1 列
将 x 进行归一化
mean_x = np.mean(x,axis=0) # 每一列的均值
std_x = np.std(x,axis=0) # 每一列的标准差
for i in range(12*471):
for j in range(18*9):
if std_x[j]!=0:
x[i][j] = (x[i][j]-mean_x[j])/std_x[j]
# 将数据集进行划分,训练集:测试集 = 4:1
import math
x_train_set = x[:math.floor(len(x)*0.8),:]
y_train_set = y[:math.floor(len(y)*0.8),:]
x_validation = x[math.floor(len(x)*0.8):,:]
y_validation = y[math.floor(len(y)*0.8):,:]
print(len(x_train_set),len(x_train_set),len(x_validation),len(y_validation))
x = np.concatenate((np.zeros([12*471,1]),x),axis=1).astype(float)
4521 4521 1131 1131
使用梯度下降进行训练
dim = 18 * 9 + 1
w = np.zeros([dim, 1])
learning_rate = 0.000001
iter_time = 1000
adagrad = np.zeros([dim, 1])
eps = 0.0000000001
for t in range(iter_time):
loss = np.sqrt(np.sum(np.power(np.dot(x, w) - y, 2))/471/12)#rmse
if(t%100==0):
print(str(t) + ":" + str(loss))
gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y) #dim*1
# adagrad += gradient ** 2
# w = w - learning_rate * gradient / np.sqrt(adagrad + eps)
w = w - learning_rate * gradient
np.save('work/weight.npy', w)
0:23.067503022281024
100:16.01469450959162
200:15.785217268902825
300:15.667044002058859
400:15.59344540214558
500:15.54253683834305
600:15.504902944004627
700:15.475801107300377
800:15.452554803514973
900:15.433523145338306
读取测试数据
testdata = pd.read_csv('work/hw1_data/test.csv',header=None, encoding='big5')
testdata = testdata.iloc[:,2:]
testdata[testdata=='NR']=0
test_data = testdata.to_numpy()
test_x = np.empty([240,18*9],dtype=float)
for i in range(240):
test_x[i,:] = test_data[18*i:18*(i+1),:].reshape(1,-1)
# 归一化
for i in range(len(test_x)):
for j in range(len(test_x[0])):
if std_x[j]!=0:
test_x[i][j] = (test_x[i][j]-mean_x[j])/std_x[j]
test_x = np.concatenate((np.ones([240,1]),test_x),axis=1).astype(float)
进行预测
w = np.load('work/weight.npy')
ans_y = np.dot(test_x,w)
ans_y
保存到 CSV 文件
import csv
with open('work/submit.csv',mode='w',newline='') as submit_file:
csv_writer = csv.writer(submit_file)
header = ['id','value']
csv_writer.writerow(header)
for i in range(240):
row = ['id_'+str(i),ans_y[i][0]]
csv_writer.writerow(row)
print(row)
如此便是完成了预测,并未找到对比的真实数据,等找到真实数据再去测试正确率