最近又双叒开始学习机器学习了,这次看了些pytorch的教程,
把上次用只用numpy的用pytorch实现一遍。
这里推荐一个gitbook:Dive-into-DL-PyTorch
和一个视频教程《PyTorch深度学习实践》完结合集
题目和基本思路请参考上篇作业一
本篇仅讲代码实现。
首先导入相关的包,没啥好说的。
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
print(torch.__version__)
torch.set_default_tensor_type(torch.FloatTensor)
data = pd.read_csv('../Datasets/weather/train.csv', encoding='big5')
用pandas读取数据,注意编码为big5。
data.rename(columns={'日期':'date', '測站': 'station', '測項': 'feature'}, inplace=True)
先把中文重命名,中文选择很不方便。
data['date'] = pd.to_datetime(data['date'])
把第一列时间转为datetime对象,以调用pandas时间相关的方法。
现在就要开始构造训练数据了。
题目的要求是用前9个小时的数据预测第10个小时的pm2.5值,
那么就要依次取9个小时的数据整理为一行,把第10个小时的pm值取出来作为标签。
但是需要注意的是给我们的数据集中是取的每个月的前20天的数据,
第20天的后9个小时的数据是没有标签值的,所以不能作为训练数据。
那么每一个月有20*24-9=471个样本。
data['month'] = data.date.dt.month
data1 = data.groupby(['month','feature']).apply(reshape_feature)
因为数据在月之间是不连续的,所以只能每个月分开处理。
但是在pandas中,我们可以直接按照月分组并应用apply函数。
直接调用dt.month获取每个datetime对象的月份。
然后又因为一个月的数据是的(20天*18特征)*24小时,我们需要把同样的特征追加到一行,
变为18特征*(24小时*20天)
在上面分组的时候是['month','feature']一起分组的,
所以每一个applay传入的数据就是一个月中同一个特征的数据,那么直接转为numpy后reshape(-1)就拼接完成了。
要注意的是天的前后顺序一定要对。
def reshape_feature(df):
df['day'] = data.date.dt.day
df.sort_values('day', inplace=True)
df.drop(['date', 'station', 'feature', 'day', 'month'], inplace=True, axis=1)
df = df.to_numpy()
return df.reshape(-1)
得到的数据就是图中的样子,一个二维索引,一共12月*18特征行,每行24小时*20天
再然后就可以用二维切片
data1.loc[i][:, j+9]
取出所有特征9个小时的数据,再reshape即可得到训练数据
train_data = []
for i in range(1, 13):
x = np.vstack(data1.loc[i])
train_data.append(np.array([x[:,j:j+9].reshape(-1) for j in range(20*24-9)]))
train_data = np.array(train_data)
train_data = train_data.reshape((-1, 162))
循环结束后train_data是一个包含12个(471*162)array的列表。
把这个列表转为array后再用reshape转为(5652*162)的array。
到这一步也只是构造出我们需要的特征,还需要填充空值和数据标准化。
把这个array转为dataframe。
train_data = pd.DataFrame(train_data)
train_data.replace('NR', 0, inplace=True)
train_data = train_data.astype('float')
train_data.fillna(0, inplace=True)
把NR替换为0,把整个dataframe转为float类型,再填充0值。
然后做标准化处理
train_data = train_data.apply(lambda x: (x - x.mean()) / x.std())
train_data = train_data.values
现在得到了最终的训练数据。
接下来我们取出这些训练数据对于的labels。
labels = data1.loc[:, 'PM2.5'].copy()
labels = np.vstack(labels)
labels = labels[:, 9:]
labels = labels.reshape(-1)
labels就是原数据中的PM2.5值,从data1中取出的是(12个月,24小时*20天)的数据。
然后前9个小时的数据得到第10个pm值,所以用切片选取第10个开始的pm值。
再用reshape把12个月拼接在一起得到最终的标签数据。
经过这么多处理后,终于得到训练样本了。。。
把这些数据转为tensor,接下来正式进入pytorch的部分。
train_data = torch.tensor(train_data.astype('float'), dtype=torch.float)
labels = torch.tensor(labels.astype('float'), dtype=torch.float).view(-1, 1)
train_data, valid_data = train_data[:5500], train_data[5500:]
labels, valid_labels = labels[:5500], labels[5500:]
由于没有给我们测试数据,所以直接从训练集中截取一小部分来作为测试集。
batch_size = 128
dataset = torch.utils.data.TensorDataset(train_data, labels)
train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)
把数据转为dataloader
class LinearNet(nn.Module):
def __init__(self, n_feature):
super(LinearNet, self).__init__()
self.linear1 = nn.Linear(n_feature, 1)
# self.linear2 = nn.Linear(64, 16)
# self.linear3 = nn.Linear(16, 1)
def forward(self, x):
# x = F.relu(self.linear1(x))
# x = F.relu(self.linear2(x))
return self.linear1(x)
定义线性模型
def train(epoch):
total_loss, num = 0, 0
for X,Y in train_iter:
optim.zero_grad()
out = net(X)
l = loss(out, Y)
l.backward()
optim.step()
total_loss += l.item()*X.shape[0]
num += X.shape[0]
if epoch % 30 == 29:
print(f'epoch: {epoch + 1}, loss: {total_loss / num :.4f}')
定义训练函数
net = LinearNet(train_data.shape[1])
loss = nn.MSELoss()
optim = torch.optim.Adam(net.parameters(), lr=0.01)
实例化模型,损失函数,优化器,开始train
for i in range(300):
train(i)
在测试集上看下损失
with torch.no_grad():
print(torch.sum(torch.pow((net(valid_data) - valid_labels), 2))/len(valid_data))
tensor(28.9964)
把测试集上的预测值和实际值做下对比
with torch.no_grad():
for x,y in zip(net(valid_data), valid_labels):
print(round(x.item(),2), y.item())
可以看出有些差的少,但大部分还是相差有点大的。
一个是模型比较简单,二个是样本较少。
本篇也主要是介绍如何生成样本和简单的pytorch的使用,至于如何提升准确度现在还不会。
以后学到了再优化模型。
pytorch部分实在没啥好讲的,已经封装的很成熟了,只需要按照固定的套路去调接口就好了。
其实这里最复杂的是前面的特征提取部分,
如何把原始数据转化为我们的训练样本比后面调用pytorch要难很多,
在上篇中使用了大量的循环,有的甚至是三层for循环,
本篇则使用了大量了pandas和numpy技巧,比如groupby后apply,reshape(-1)拼接数据等。
这样在数据处理部分只用了一个for循环,甚至那里用多重索引的apply后可以一个循环都不用。
这些技巧可以提升代码的可读性,也不用手动实现最基础的部分。
网友评论