一、作业一说明:
- 目的
根据前9个小时的数据,预测出第十个小时 PM2.5 的数值
-
方法
线性回归 -
数据:
使用丰原站的观测记录,分成 train.csv 跟 test.csv,train.csv是丰原站每个月的前 20 天所有数据。test.csv 则是从丰原站剩下的资料中取样出来。
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。
train.csv和test.csv百度云链接:
链接:https://pan.baidu.com/s/1BtoxnoS66wq06POYt5BoAQ
提取码:p33s
二、思路与代码实现
导入需要的库并读取文件
涉及到简单的切片和补缺值,应该都会。
import numpy as np
import pandas as pd
import torch
from torch.utils.data import DataLoader,Dataset
#数据地址,记得改成自己的
path = r'E:/MLdataset/hw1/train.csv'
#读取文件
with open(path,encoding="big5") as t:
train_data = pd.read_csv(t)
#数据清理,保留需要的列,缺值用0补
data = train_data.iloc[:,3:]
data[data == 'NR'] = 0
raw_data = data.to_numpy()
如下图,一个小时有18项观测数据,9个小时就是18×9=162个数据,给每个数据分配一个权重w,再加上偏置b,就是一个线性回归的过程。
为了方便处理,如图所示,我们将18×9的表格拉直为1×162的矩阵,用它与162×1的权重矩阵w相乘,就得到1×1的label(其实还要加上偏置b,详见后面)。这就是我们的一个训练数据。接下来我们从所给的train.csv找出所有的训练数据。
观察train.csv的格式,见下图,发现其按一天为一组数据,第二天的数据竖直接在第一天的后面。
这样的结构不方便我们提取出训练数据和label,稍微处理一下。我们将2014.01.02的数据整体移到与2014.01.01水平的位置,连接起来,2014.01.03及往后的数据依此这样处理。原始数据就由竖条变为横长条形状,如下图。
#将raw_data的数据由竖直变水平连接
month_data = {} #建立了month_data字典,将每个月的数据分别储存
for month in range(12): #12个月
sample = np.empty([18,480]) #有18个数据指标,一天24h,一个月的数据有前20天的,所以20*24=480
for day in range(20): #20天
#切片赋值,举个例子,month=0,day=1时,sample[:,24:48] = raw_data[18:36,:],就是把raw_data的18——36行移到sample的24——24列,及将2014.01.02的数据移到2014.01.01的过程。
sample[:,day*24:(day+1)*24] = raw_data[18*(20*month + day):18*(20*month + day +1),:]
month_data[month] = sample #赋值存储,键为月份,值为18*480的数据表
接下来就从变形好的数据中提取我们想要的训练数据和label。
可以看到,一个月有20天的数据,一天有24个小时,共480个小时,一个小时有18个指标,所以得到的是18×480的矩阵。每连续的10个小时可以作为一组训练数据,前九个小时的data作为输入,第十个小时的PM2.5作为label。在一个月20天的480个小时,就能有471条这样的数据产生。12个月就有12×471条数据。
如前面说的,我们将一个data展开成162*1,再将一个月471个data拼在一起。
12个月就是12×471条data,data为(12×471,18×9),即(5652,162)。每个data的lable只有一个值,所以label为(12×471,1),即(5652,1)
x = np.empty([12*471,18*9],dtype=float)#初始化训练data
y = np.empty([12*471,1],dtype=float)#初始化label
for month in range(12):
for day in range(20):
for hour in range(24):
if hour > 14 and day == 19:
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]
这里要加上一个标准化。比较简单粗暴,直接上代码了。
mean_x = np.mean(x,axis = 0) #求x均值,列
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] #减均值后除标准差
记得前面说的偏置b吧。这里有个技巧,在X的第一列前中添加一个全为1的列,在w的第一行添加一行,当作b。X就变成了(5652,163),W变为(163,1),可以做矩阵乘法。
添加列的过程一行代码就搞定:
x = np.concatenate((np.ones([12 * 471, 1]), x), axis = 1).astype(float)#添加一个全1列
至此,数据准备的过程就结束了。x就是处理好的data,y是处理好的label。
下面使用一下pytorch的DataLoader,Dataset两个类实现一下生成和载入数据集。这两个类的具体使用方法,等后面有时间写一篇专门的博客后,再给链接。
#建立自己的类
class Pm_Dataset(Dataset):
def __init__(self,x,y):
#初始化一些属性
self.x_data = torch.from_numpy(x[:])
self.y_data = torch.from_numpy(y[:])
self.x_data = self.x_data.float()
self.y_data = self.y_data.float()
self.len = x.shape[0]
#使用Dataset必须定义的两个函数__getitem__()和__len__()
def __getitem__(self,index):
return self.x_data[index],self.y_data[index]
def __len__(self):
return self.len
#实例化
dataset = Pm_Dataset(x,y)
#载入数据集
train_loader = DataLoader(dataset=dataset,
batch_size=500,
shuffle=True,
num_workers=0)
下面就是训练优化的过程,使用的pytorch框架后,就比较简单。pytorch的使用可以看我总结的相关博客:
https://blog.csdn.net/zhanglw882/category_10561359.html
#建立训练模型
class Model(torch.nn.Module):#继承自torch.nn.Module
def __init__(self):
super(Model,self).__init__()
#线性层
self.linear = torch.nn.Linear(18*9+1,1)
#前馈
def forward(self,x):
in_size = x.size(0)
x = x.view(in_size, -1)
y_pred = self.linear(x)
return y_pred
#实例化
model = Model()
#损失计算
criterion = torch.nn.MSELoss(reduction='mean')
#优化方法
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)
#训练
for epoch in range(100):
for i,data in enumerate(train_loader,0):
inputs,labels = data
#前馈
y_pred = model(inputs)
#损失
loss = criterion(y_pred,labels)
#梯度归零
optimizer.zero_grad()
print(epoch,i,loss.item())
#反馈
loss.backward()
#优化
optimizer.step()
torch.save(model.state_dict(),"Linear.pth")#保存训练模型权重
至此,训练过程的代码已经写完,得到的模型保存在了Linear.pth中,就能用于进行test。test过程就是将测试数据用使用训练好的模型权重进行运算,得出label,保存即可。
训练过程完整代码如下:
#李宏毅作业1_pytorch实现,系统windows
#导入相关库
import numpy as np
import pandas as pd
import torch
from torch.utils.data import DataLoader,Dataset
#数据地址,记得改成自己的
path = r'E:/MLdataset/hw1/train.csv'
#读取文件
with open(path,encoding="big5") as t:
train_data = pd.read_csv(t)
#数据清理,保留需要的列,缺值用0补
data = train_data.iloc[:,3:]
data[data == 'NR'] = 0
raw_data = data.to_numpy()
#数据处理,变为自己想要的形式
#将raw_data的数据由竖直变水平连接
month_data = {} #建立了month_data字典,将每个月的数据分别储存
for month in range(12): #12个月
sample = np.empty([18,480]) #有18个数据指标,一天24h,一个月的数据有前20天的,所以20*24=480
for day in range(20): #20天
#切片赋值,举个例子,month=0,day=1时,sample[:,24:48] = raw_data[18:36,:],就是把raw_data的18——36行移到sample的24——24列,及将2014.01.02的数据移到2014.01.01的过程。
sample[:,day*24:(day+1)*24] = raw_data[18*(20*month + day):18*(20*month + day +1),:]
month_data[month] = sample #赋值存储,键为月份,值为18*480的数据表
x = np.empty([12*471,18*9],dtype=float)#初始化训练data
y = np.empty([12*471,1],dtype=float)#初始化label
for month in range(12): #12个月
for day in range(20): #20天
for hour in range(24): #24小时
if hour > 14 and day == 19: #第20天,hour=15的时候,不执行后面的代码,进入下次循环
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]
mean_x = np.mean(x,axis = 0) #求x均值,列
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 = np.concatenate((np.ones([12 * 471, 1]), x), axis = 1).astype(float)#添加一个全1列
#建立自己的类
class Pm_Dataset(Dataset):
def __init__(self,x,y):
#初始化一些属性
self.x_data = torch.from_numpy(x[:])
self.y_data = torch.from_numpy(y[:])
self.x_data = self.x_data.float()
self.y_data = self.y_data.float()
self.len = x.shape[0]
#使用Dataset必须定义的两个函数__getitem__()和__len__()
def __getitem__(self,index):
return self.x_data[index],self.y_data[index]
def __len__(self):
return self.len
#实例化
dataset = Pm_Dataset(x,y)
#载入数据集
train_loader = DataLoader(dataset=dataset,
batch_size=500,
shuffle=True,
num_workers=0)
#建立训练模型
class Model(torch.nn.Module):#继承自torch.nn.Module
def __init__(self):
super(Model,self).__init__()
#线性层
self.linear = torch.nn.Linear(18*9+1,1)
#前馈
def forward(self,x):
in_size = x.size(0)
x = x.view(in_size, -1)
y_pred = self.linear(x)
return y_pred
#实例化
model = Model()
#损失计算
criterion = torch.nn.MSELoss(reduction='mean')
#优化方法
optimizer = torch.optim.SGD(model.parameters(),lr=0.01)
#训练
for epoch in range(100):
for i,data in enumerate(train_loader,0):
inputs,labels = data
#前馈
y_pred = model(inputs)
#损失
loss = criterion(y_pred,labels)
#梯度归零
optimizer.zero_grad()
print(epoch,i,loss.item())
#反馈
loss.backward()
#优化
optimizer.step()
torch.save(model.state_dict(),"Linear.pth")#保存训练模型权重