机器学习(李宏毅)作业1-利用regression预测丰原站pm2.5值

最近开始学习机器学习,听同学介绍,选择了台大李宏毅老师的视频,https://www.bilibili.com/video/BV1JE411g7XF
老师布置了很多作业,这里记录下自己写作业的过程和一些参考资料
本篇是作业一,根据丰源站历史的空气数据,预测下一小时的pm2.5的值。
本次作业说明视频参见 https://www.bilibili.com/video/BV1gE411F7td

import numpy as np
import pandas as pd

本次作业要求只用numpy,pandas手写线性回归,所以只导入pandas和numpy两个包,
学习这两个包推荐一个gitbook, 利用python进行数据分析
另外这本书有本实体版的,是另外一个人翻译的,翻译的非常差,非常不建议购买,看这个gitbook版的就好。
本次作业代码参考 https://zhuanlan.zhihu.com/p/115335196
上面知乎专栏的大佬写的比较简单,我添加了更多的细节,便于新人理解。

data = pd.read_csv('data/train.csv', encoding='big5')

首先使用pandas读取训练文件,并做一些预处理,李宏毅老师是湾湾人,
用的是繁体,所以要指定下编码为big5,否则里面的中文为乱码。
看一下文件的结构,有27列,第一列为日期,第三列为检测项,后面的是0-23点每一点的数值。
然后每天有18行,即18个检测项,其中第10行是pm2.5。
一共12月*20天*18项=4320行

data.head(20)


然后前三项是没用的,我们用切片去掉。shape就变为了4320*24
数据中还有很多是NR,我们替换为0。
接下来就是numpy的处理范围了,使用to_numpy转换为矩阵。

data = data.iloc[:,3:]
data.replace('NR',0,inplace=True)
row_data = data.to_numpy()
row_data



参考上图,我们需要把数据shape做一个转换,把240天内相同的测量项放在同一行。
也就是从(12月*20天*18项),24小时变为(18项,(24小时*20天),12月
下面的两个循环就是在做转换,每个月的数据写在以月份为键名的字典里的。

month_data={}
for month in range(12):
    sample = np.empty([18, 480])
    for day in range(20):
        sample[:,day*24:(day+1)*24] = row_data[18*(month*20+day):18*(month*20+(day+1)),:]
    month_data[month] = sample

然后我们以10小时为一个data,把前9个小时的data作训练,第10个小时的值为target。
即0-9点的data来预测10点的pm2.5,第1-10点的和data来预测11点的pm2.5
最后一笔是471-479来预测480的pm2.5,所以一共有471笔data
shape从\color{red}{(18*5760)}变为\color{red}{(12月*471笔),(18项*9小时)}
y就是第10个小时的pm2.5的值,shape为\color{red}{12*471,1}

x = np.empty([12 * 471, 18 * 9], dtype = float)
y = np.empty([12 * 471, 1], dtype = float)
for month in range(12):
    for day in range(20):
        for hour in range(24):
            if day == 19 and hour > 14:
                continue
            x[month * 471 + day * 24 + hour, :] = \
                month_data[month][:,day * 24 + hour : day * 24 + hour + 9].reshape(1, -1)
            y[month * 471 + day * 24 + hour, 0] = month_data[month][9, day * 24 + hour + 9]
x



接下来做Normalize,教学视频里说明了为什么要做Normalize,不知道的请复习视频
按照上面的公式,需要求平均值和标准差,然后x处理对应位置的值

mean_x = np.mean(x, axis = 0)
std_x = np.std(x, axis = 0) #18 * 9 
for i in range(len(x)): #12 * 471
    for j in range(len(x[0])): #18 * 9 
        if std_x[j] != 0:
            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]
x




接下来就是最重要的部分了,我们开始做梯度下降
我们有9小时*18项的变量,所以需要9*18个w值,还有一个bias:b
注意下面代码的第三行,这里是整个矩阵前拼接了一列1值,这一列在点乘后就是b
所以这就是为什么dim有18*9+1项

dim = 18 * 9 + 1
w = np.zeros([dim, 1])
x = np.concatenate((np.ones([12 * 471, 1]), x), axis = 1).astype(float)
learning_rate = 100
iter_time = 1000
adagrad = np.zeros([dim, 1])
eps = 0.0000000001

然后定义loss函数,这里知乎作者用的是均方根误差来做loss函数
贴一段百度百科的解释:
均方根误差是预测值与真实值偏差的平方与观测次数n比值的平方根,
在实际测量中,观测次数n总是有限的,真值只能用最可信赖(最佳)值来代替。
标准误差对一组测量中的特大或特小误差反映非常敏感,
所以,标准误差能够很好地反映出测量的精密度。
这正是标准误差在工程测量中广泛被采用的原因。
因此,标准差是用来衡量一组数自身的离散程度,
而均方根误差是用来衡量观测值同真值之间的偏差,
它们的研究对象和研究目的不同,但是计算过程类似。
因为常数项的存在,所以 dimension (dim) 需要多加一栏;
eps 项是避免 adagrad 的分母为 0 而加的极小数值。
rmse公式:


然后adagrid的公式:


这里对gt的计算我没有看懂,根据公式应该是loss对w的偏导,以后搞懂了再来补说明吧
adagrid这一行就是上面的累计求和
然后对w做更新,这样就可以实现梯度下降了
最后保存计算出的w矩阵

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)
np.save('weight.npy', w)


这样我们的w和b就计算完了,接下来拿测试集做测试
还是和train.csv一样先做预处理和Normalize,然后用w和测试集做点乘就可以得到预测值了
记得在拼接1列1作为b

testdata = pd.read_csv('./data/test.csv', header = None, encoding = 'big5')
test_data = testdata.iloc[:, 2:].copy()
test_data.replace('NR', 0, inplace=True)
test_data = test_data.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('weight.npy')
ans_y = np.dot(test_x, w)
ans_y[:10]


ans_y就是预测值了,然后按照作业要求写成csv文件。
这里转回pandas处理,因为pandas的to_csv方法很好用

res = pd.DataFrame(ans_y)
res['id'] = ['id_' + str(x) for x in res.index]
res.rename(columns={0:'value'}, inplace=True)
res=res[['id', 'value']]  # 调换顺序
res.to_csv('submit.csv', index=False)

最后,可以调整 learning rate、iter_time (iteration 次数)、
取用 features 的多少(取几个小时,取哪些特征)
甚至是不同的 model 来超越 baseline。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容