机器学习入门实战---线性回归之PM2.5
线性回归之PM2.5
一、实验说明
相关的数据来源可以查看哔哩哔哩李宏毅老师的视频
给定训练集train.csv,测试集test.csv,要求根据前9个小时的空气检测情况预测第10个小时的PM2.5含量
二、训练集介绍
1.CSV文件,包含*丰原地区240天的气象观测资料,取每个月前20天的数据做训练集,每月后10天数据用于测试;
2.每天的监测时间点为0时,1时…到23时,共24个时间节点;
3.每天的检测指标包括CO、NO、PM2.5、PM10等气体浓度,是否降雨、刮风等气象信息,共计18项;
导入相关包
import sys
import pandas as pd
import numpy as np
三、数据预处理
浏览数据可知,数据中存在一定量的空数据NR,且多存在于RAINFALL一项。RAINFALL表示当天对应时间点是否降雨,有降雨值为1,无降雨值为NR,类似于布尔变量。因此将空数据NR全部补为0即可。
data = pd.read_csv('/home/aistudio/data/data27964/train.csv', encoding = 'big5' ) # 读取结果的结构是DataFrame
data
#表示的0~23小时
#表示的
从相关的文件列表中导入相关数据。
Pandas里主要数据结构包含DataFrame(二维表),如上打印结果,有行有列。但标准说法行(索引),列(标签)。
# panda里利用iloc选取数据,从0开始。iloc(行,列)
# 当前选取从第三列开始的所有数据
#iloc比较简单,它是基于索引位来选取数据集,0:4就是选取 0,1,2,3这四行,需要注意的是这里是前闭后开集合
data = data.iloc[:, 3:]
data[data=='NR'] = 0
data
raw_data = np.array(data) # DataFrame转换成numpy数组
raw_data
array([[‘14’, ‘14’, ‘14’, …, ‘15’, ‘15’, ‘15’],
[‘1.8’, ‘1.8’, ‘1.8’, …, ‘1.8’, ‘1.8’, ‘1.8’],
[‘0.51’, ‘0.41’, ‘0.39’, …, ‘0.35’, ‘0.36’, ‘0.32’],
…,
[‘36’, ‘55’, ‘72’, …, ‘118’, ‘100’, ‘105’],
[‘1.9’, ‘2.4’, ‘1.9’, …, ‘1.5’, ‘2’, ‘2’],
[‘0.7’, ‘0.8’, ‘1.8’, …, ‘1.6’, ‘1.8’, ‘2’]], dtype=object)
print(raw_data.shape) #代表有4320行24列的数据
(4320, 24)
四、特征提取
(1)按月份来处理数据
针对每20天来说,信息维度[18, 480] (18个feature,20*24=480)18个特征
将原始的数据按照每个月来划分,重组成12个 [18,480]
#sample[:, day * 24 :(day+1) * 24]中的“,”前面是":“表明行的内容全部都要,”,"后面的是列的内容,就是按照0-24-48-…这样将24小时的数据提取出来保存
#raw_data的数据是竖着排列的,所以raw_data[18 * (20 * month + day) : 18 * (20 * month + day+1), :]的“,”前面是0-18-36…这样取得
month_data = {} # key: month value: data
for month in range(12):
sample = np.empty([18, 480]) # 创建一个空的【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
#以第一个月为例
print(month_data[0])
print(month_data[0].shape)
[[14. 14. 14. … 14. 13. 13. ]
[ 1.8 1.8 1.8 … 1.8 1.8 1.8 ]
[ 0.51 0.41 0.39 … 0.34 0.41 0.43]
…
[35. 79. 2.4 … 48. 63. 53. ]
[ 1.4 1.8 1. … 1.1 1.9 1.9 ]
[ 0.5 0.9 0.6 … 1.2 1.2 1.3 ]]
(18, 480)
(2)扩充数据集,获取更好的训练效果
根据实验要求,需要用连续9个时间点的数据预测第10个时间点的PM2.5。 而每个月采取的是前20天连续的数据,可以扩展成480小时的连续数据;
具体做法,每个月的第一天的0-8时的数据作为训练数据,9时的数据作标签y;9-17的数据作一个data,18时的数据作标签y…以此推,每个月480小时,有480-9= 471个data(因为走到最后只有最后九个小时减去n ),故此时总数据471 * 12 个;而每个data是18*9
x = np.empty([12 * 471, 18 * 9], dtype = float)#这里的x表示一年的为12*471 data 每个data代表了
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
# reshape将矩阵重整为新的行列数,参数-1代表自动推断,这里去掉了18*9的二维属性,
# 转而以一维序列代替,一维序列的顺序本身可以隐含其时序信息
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] #value
print('x',x)
print(x.shape)
print('y',y)
print(y.shape)
结果:
x [[14. 14. 14. … 2. 2. 0.5]
[14. 14. 13. … 2. 0.5 0.3]
[14. 13. 12. … 0.5 0.3 0.8]
…
[17. 18. 19. … 1.1 1.4 1.3]
[18. 19. 18. … 1.4 1.3 1.6]
[19. 18. 17. … 1.3 1.6 1.8]]
(5652, 162)
y [[30.]
[41.]
[44.]
…
[17.]
[24.]
[29.]]
(5652, 1)
数据的归一化处理
mean_x = np.mean(x, axis = 0) # 求均值, aix=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] # 所有属性归一化,避免使数据的某些特征形成主导作用
x
结果:
array([[-1.35825331, -1.35883937, -1.359222 , …, 0.26650729,
0.2656797 , -1.14082131],
[-1.35825331, -1.35883937, -1.51819928, …, 0.26650729,
-1.13963133, -1.32832904],
[-1.35825331, -1.51789368, -1.67717656, …, -1.13923451,
-1.32700613, -0.85955971],
…,
[-0.88092053, -0.72262212, -0.56433559, …, -0.57693779,
-0.29644471, -0.39079039],
[-0.7218096 , -0.56356781, -0.72331287, …, -0.29578943,
-0.39013211, -0.1095288 ],
[-0.56269867, -0.72262212, -0.88229015, …, -0.38950555,
-0.10906991, 0.07797893]])
损失函数
采用预测值与标签y之间的平均欧时距离来衡量预测的准确程度
num = 471*12, 乘 1/2 是为了在后续求梯度过程中保证梯度系数为1,方便计算
学习率更新
为了在不影响模型效果的前提下提高学习速度,可以对学习率进行实时更新:即让学习率的值在学习初期较大,之后逐渐减小。这里采用比较经典的adagrad算法来更新学习率。
dim = x.shape[1] + 1 #shape[1]表示的为多少列 shape[0]表示的为多少行
w = np.zeros(shape = (dim, 1 )) #empty创建的数组,数组中的数取决于数组在内存中的位置处的值,为0纯属巧合?[0,0,0,.....0,0,0]163
x = np.concatenate((np.ones((x.shape[0], 1 )), x) , axis = 1).astype(float) #axis=1进行,concatenate (np.ones((x.shape[0], 1 ))[5652 ,1]的全一数组)行拼接
#使用的是向量的运算
#初始化学习率(163个参数,163个200)和adagrad
learning_rate = np.array([[200]] * dim)
adagrad_sum = np.zeros(shape = (dim, 1 ))
lamuda=100
#没有隐藏层的网络
#增加精确度的方法
#1.增加迭代次数
#2.正则化的使用
for T in range(100001):
if(T % 5000 == 0 ):
print("T=",T)
print("Loss:",(np.sum((x.dot(w) - y)**2)/ x.shape[0] /2)) #最小二乘损失 #问/除以x.shape[0]是什么意思 ?将向量转化成数值
print('x',(x.dot(w) - y)**2)
gradient = 2 * np.transpose(x).dot(x.dot(w)-y) #损失的导数x*(yh-h)
adagrad_sum += gradient ** 2
w = w - learning_rate * gradient / (np.sqrt(adagrad_sum) + 0.0005)#得到的w
np.save('weight.npy',w)#以二进制的形式将数据进行保存
使用模型预测
# 同处理训练集数据一样
testdata = pd.read_csv('/home/aistudio/data/data27964/test.csv', header = None, encoding = 'big5')
test_data = testdata.iloc[:, 2:]
test_data = test_data.copy() # 为防止pandas出错:A value is trying to be set on a copy of a slice from a DataFrame.
test_data[test_data == 'NR'] = 0
test_data = np.array(test_data)
test_data.shape
w = np.load('weight.npy')
test_x = np.empty(shape = (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(test_x.shape[0]): ##Normalization
for j in range(test_x.shape[1]):
if not std_x[j] == 0 :
test_x[i][j] = (test_x[i][j]- mean_x[j]) / std_x[j]
test_x = np.concatenate((np.ones(shape = (test_x.shape[0],1)),test_x),axis = 1).astype(float)
test_x.shape
(240, 163)
w.shape
(163, 1)
w = np.load('weight.npy')
ans_y = np.dot(test_x, w)
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)