李宏毅机器学习作业一

该博客详细记录了使用线性回归预测PM2.5浓度的过程,包括数据预处理、特征提取、归一化、训练集划分、模型训练、测试与预测。通过Adagrad优化算法更新学习率,最终实现对测试集的预测并保存结果到CSV文件。
摘要由CSDN通过智能技术生成


前言

         PM2.5预测是李宏毅老师机器学习课程第一个作业,要求实现linear regression预测PM2.5的数值。在第一个线性回归的作业中遇到了很多难点,第一是虽然给出了数据集,但是要对数据集做预处理,整理出需要的数据,并保存到矩阵中和对矩阵的操作,将原始数据依照每个月份重组成 12 18 (特征) * 480 (小时) 的资料。第二是训练模型,需要用adagrad算法来更新学习率,用梯度下降的方法来更新参数并计算loss。第三是对numpy库中函数的了解和运用。因为刚开始接触,主要是去理解分析老师给出的代码学习思路然后仿写,不懂的地方看课程里的代码讲解和一些相关的博客或者问同学朋友。

 

训练集是:12个月–>每月的前20–>每天的24小时–>每小时的18种不同属性的数值

测试集:240–>连续的9小时–>18种不同的属性

要求的输出:根据某天连续的9小时–>18种不同的属性,预测第10小时的PM2.5的值。也就是通过一个18*9的矩阵预测出来一个值

可以分为八个部分:

1.预处理

2.提取特征

3.归一化

4.将训练数据分为训练集和验证集

5.训练

6.测试

7.预测

8.保存预测到CSV文件

一、预处理

        数据的第一行是说明,分别是时间,地点,各空气中物质的名称,以及0-23时他们的浓度。
RAINFALL的值是NR,表示没有下雨,所以把它的值改为0方便处理第一步是数据预处理,因为这里数据是通过矩阵来保存的,第一步就是删减掉不需要的行与列,然后将其保存到矩阵中。

import pandas as pd
import numpy as np

data = pd.read_csv('./data/train.csv', encoding='big5') # 读取数据保存到data中

data = data.iloc[:, 3:]  # 行保留所有,列保留从第三列开始往后,去除了数据中的时间、地点、参数等信息
data[data == 'NR'] = 0  # NR表示没有雨,全部置为0方便处理
raw_data = data.to_numpy()  # 将data的所有数据转换为二维数据并用raw_data来保存

print(raw_data)

 raw_data

 

二、提取特征

        用滑动窗口的思想,将一个月的第一天到第二十天每天二十小时横向排序,取大小为9的窗口,从第一天的第0时一直可以划到第20天的第14时,可以用到更多的数据,这样积累的话使整个数据集得到明显的提升。

month_data = {}
for month in range(12):
    sample = np.empty([18, 480]) # 返回一个18行480列的数组,用来保存一个月的数据(一个月只有20天,一天24个小时)
    for day in range(20):
        sample[:, day * 24 : (day + 1) * 24] = raw_data[18 * (20 * month + day) : 18 * (20 * month + day + 1), :]
        # raw的行每次取18行,列取全部列。送到sample中(sample是18行480列)行给全部行,列给24列,然后列往后增加
    month_data[month] = sample

x = np.empty([12 * 471, 18 * 9], dtype = float)
# 一共480个小时,每9个小时一个数据(480列最后一列不可以计入,因为如果取到最后一行那么最后一个数据便没有了结果{需要9个小时的输入和第10个小时的第10行作为结果}),
# 480-1-9+1=471。471*12个数据集按行排列,每一行一个数据;数据是一个小时有18个特征,而每个数据9个小时,一共18*9列
y = np.empty([12 * 471, 1], dtype = float) # 结果是471*12个数据,每个数据对应一个结果,即第10小时的PM2.5浓度
for month in range(12):
    for day in range(20):
        for hour in range(24):
            if day == 19 and hour > 14: # 取到raw_data中的最后一块行为18,列为9的块之后,就不可以再取了,再取就会超过界限了
                continue
            x[month * 471 + day * 24 + hour, :] = month_data[month][:,day * 24 + hour : day * 24 + hour + 9].reshape(1, -1)
            # 取对应month:行都要取,列取9个,依次进行,最后将整个数据reshape成一行数据(列数无所谓)。然后赋给x,x内的坐标只是为了保证其从0-471*12
            # 列18*9 (9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9)
            y[month * 471 + day * 24 + hour, 0] = month_data[month][9, day * 24 + hour + 9]  # 结果对应的一直是第9(即第10行PM2.5)然后列数随着取得数据依次往后进行
#print(x)
#print(y)

x

 

 


 

 

三、归一化

很多特征的分布的范围很不一样,把他们的范围缩放,使得不同输入的范围是一样的。对数据进行特征缩放。

#特征缩放
mean_x = np.mean(x, axis = 0)  # 18 * 9 求均值,axis = 0表示对各列求均值,返回 1* 列数 的矩阵
std_x = np.std(x, axis = 0) # 18 * 9 求标准差,axis = 0表示对各列求均值,返回 1* 列数 的矩阵
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]

四、将训练数据分为训练集:验证集=8:2来交叉验证

        交叉验证就是将训练集再分为两部分,一部分作为训练集,一部分作为验证集。用训练集训练模型,然后在验证集上比较,得出最好的模型之后,再用全部的训练集训练,然后再用测试集进行测试。这样的好处是因为最终只给我们test data的输入而没有给我们输出,所以我们无法定量我们模型的好坏,而使用验证数据可以简单验证我们模型的好坏,让我们自己心里有数。                      

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(x_train_set)
#print(y_train_set)
#print(x_validation)
#print(y_validation)
#print(len(x_train_set))
#print(len(y_train_set))
#print(len(x_validation))
#print(len(y_validation))

五、训练

决定模型好坏的就是训练的成果,这里使用Loss function和梯度下降算法。

训练中使用Adagrad算法来更新学习率,每个参数的学习率都把它除上之前微分的均方根。

loss function 用的是均方根误差。

dim = 18 * 9 + 1    # 用来做参数vector的维数,加1是为了对bias好处理。最后的h(x)=w1x1+w2x2+'''+WnXn+b
w = np.zeros([dim, 1])    # 生成一个dim行1列的数组用来保存参数值
x = np.concatenate((np.ones([12 * 471, 1]), x), axis = 1).astype(float)
# np.ones来生成12*471行1列的全1数组,np.concatenate,axis=1表示按列将两个数组拼接起来,即在x最前面新加一列内容,
# 之前x是12*471行18*9列的数组,新加一列之后变为12*471行18*9+1列的数组
learning_rate = 100#学习率
iter_time = 1000#迭代次数
adagrad = np.zeros([dim, 1])  #生成dim行即163行1列的数组,adagrad算法更新学习率
eps = 0.0000000001         # 所以加上一个极小数,使分母adagrad不为0
for t in range(iter_time):
    loss = np.sqrt(np.sum(np.power(np.dot(x, w) - y, 2))/471/12)#均方根误差,观测值与真值偏差的平方和与观测次数m比值的平方根。
    if t%100==0:
        print(str(t) + ":" + str(loss))   # 每一百次迭代就输出其损失
    gradient = 2 * np.dot(x.transpose(), np.dot(x, w) - y)
    # dim*1 x.transpose即x的转置,后面是X*W-Y,即2*(x的转置*(X*W-Y))是梯度,具体可由h(x)求偏微分获得.最后生成1行18*9+1列的数组。
    # 转置后的X,其每一行是一个参数,与h(x)-y的值相乘之后是参数W0的修正值,同理可得W0-Wn的修正值保存到1行18*9+1列的数组中,即gradient
    adagrad += gradient ** 2     # adagrad用于保存前面使用到的所有gradient的平方,进而在更新时用于调整学习率
    w = w - learning_rate * gradient / np.sqrt(adagrad + eps)      # 更新w
np.save('weight.npy', w)  # 将参数保存下来
print(w)

每一百次迭代就输出损失 

 

 打印w,共18*9+1行,包含bias

 ...

六、测试

载入验证集验证

w = np.load('weight.npy')
# 使用x_validation和y_validation来计算模型的准确率,因为X已经normalize了,所以不需要再来一遍,只需在x_validation上添加新的一列作为bias的乘数即可
x_validation = np.concatenate((np.ones([1131, 1]), x_validation), axis=1).astype(float)
ans_y = np.dot(x_validation, w)
loss = np.sqrt(np.sum(np.power(ans_y - y_validation, 2)) / 1131)
print(loss)

loss 

 

 

七、预测

载入测试数据,并且和训练集一样对测试集做预处理,使测试数据形成 240 个维度为 18 * 9 + 1 的资料。载入训练得到权重w开始预测。                       

test_data = pd.read_csv('./data/test.csv', header = None, encoding = 'big5')
test_data = test_data.iloc[:, 2:]   # 取csv文件中的全部5行和第3列到结束的列数所包含的数据
test_data[test_data == 'NR'] = 0    # 将其转换为数组
test_data = test_data.to_numpy()
test_x = np.empty([240, 18*9], dtype = float)# 创建一个240行18*9列的空数列用于保存text data的输入
for i in range(240):    # 共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)   # 在test_x前面拼接一列全1数组,构成240行,163列数据

test_x

# 进行预测
w = np.load('weight.npy')
ans_y = np.dot(test_x, w)   # test data的预测值ans_y=test_x与W的积

保存预测结果到CSV文件

# 将预测结果填入文件当中
import csv
with 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)

预测结果

总结

以上就完成了第一个作业

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值