目录
首先提供一下基本数据,需要自己实现的可以下载下来,自己玩下
链接:https://pan.baidu.com/s/1fWZ1T6f02OYG_C-euiMQJQ?pwd=6666
提取码:6666
1.环境依赖
基本实现涉及以下python库:
import numpy as np
import pandas as pd #读取csv文件的库
import matplotlib.pyplot as plt
import torch
import torch.optim as optim
import datetime
from sklearn import preprocessing
2.读取文件+数据处理
ps:笔者用的是win10,路径前加上r表示‘\’不作特殊意义
features = pd.read_csv(r'C:\Users\yuehen\Desktop\PyTorch框架实战\神经网络实战分类与回归任务\temps.csv')
#print(features,type(features))
years = features['year']
months = features['month']
days = features['day']
dates = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) for year, month, day in zip(years, months, days)]
#转换为datetime格式
dates = [datetime.datetime.strptime(date, '%Y-%m-%d') for date in dates]
代码详解:
pd.read_csv(): 读取csv文件,并以DataFrame类型存储常用的几个参数:
header:指定某行作为列名,默认是infer,自动判断
names :指定列名的列表,若header参数设置为None则用names的作为列名
index_col :用于指定哪一列作为索引列的列号或列名
dtype : 用于指定列的数据类型和字典
效果:ps:temp1是昨天最高气温,temp2是前天最高气温
可以看出,单次读取csv文件后,时间属性是分开存储的,这并不利于我们后对其进行处理。所以我们导入datetime模块,并利用datetime.strptime()函数将数据转换成标准格式。datetime.strptime():根据xxxx-xx-xx的时间字符串格式,转换成时间元祖
ps: 补充python中时间日期格式化符号
%y 表示两位数年份(00-99) %Y 表示四位数年份(0000-9999)
%m 表示月份(01-12) %d 表示月内某一天(00-31)
%H 24小时制 % I 12小时制
%M 分(00-59) %S 秒(00-59)
3.绘制图表
起草,画个图,可以直观的观察数据走向以及数据分布,基本代码解释都在注释中,细节就不多赘述了。
# 布局设置
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, figsize = (10,10))
fig.autofmt_xdate(rotation = 45)
# 标签值,dates对应features列名:actual一列
ax1.plot(dates, features['actual'])
ax1.set_xlabel(''); ax1.set_ylabel('Temperature'); ax1.set_title('Max Temp')
# 昨天
ax2.plot(dates, features['temp_1'])
ax2.set_xlabel(''); ax2.set_ylabel('Temperature'); ax2.set_title('Previous Max Temp')
# 前天
ax3.plot(dates, features['temp_2'])
ax3.set_xlabel('Date'); ax3.set_ylabel('Temperature'); ax3.set_title('Two Days Prior Max Temp')
# 朋友预测值:emmm没啥实际意义可以忽略不计,但基本数据给了,还是画上吧
ax4.plot(dates, features['friend'])
ax4.set_xlabel('Date'); ax4.set_ylabel('Temperature'); ax4.set_title('Friend Estimate')
#自动调整子图参数,使之填充整个图像区域,这是个实验特性,可能在一些情况下不工作,它仅检查坐标轴标签,刻度标签以及标题的部分
plt.tight_layout(pad=2)
plt.show()
#独热编码:能够处理非连续型数值特征,一共7个星期,我们使用7个寄存器,利用True or False(0/1)的形式体现。
features = pd.get_dummies(features)
#DataFrame格式
图标输出效果:
ps:matplotlib支持基本的图表风格定制,我们可以使用pls.available获取支持的绘图风格:
import matplotlib.style as pls
print(pls.available)
这里我们使用‘’538‘风格重新绘制下,只需在布局前添加:plt.style.use('fivethirtyeight')这一串代码即可
稍微解释下:pd.get_dummies(features)
独热编码:能够处理非连续型数值特征,譬如一共7个星期,我们使用7个寄存器,利用True or False(0/1)的形式体现。它会以下列形式给出存储。
还有个小问题:默认的设置无法显示完全列表数据,这需要我们手动设定下pd的参数修改后:
features = pd.get_dummies(features)
#最多行数
pd.set_option('display.max_rows',500)
#最多列数
pd.set_option('display.max_columns',100)
#最大宽度
pd.set_option('display.width',1000)
#DataFrame格式
print(features.head(5))
4.数据处理
4.1 : 设置标签与特征,利用标准化加快收敛速度
我们可以利用sklearn库来进行数据的标准化
#构建标签--以实际温度为标签
labels = np.array(features['actual'])
# 在特征中去掉标签
features= features.drop('actual', axis = 1)
# 名字单独保存一下,以备后面需要使用
feature_list = list(features.columns)
# 转换成ndarray
features = np.array(features)
#标准化
input_features = preprocessing.StandardScaler().fit_transform(features)
部分代码详解:
input_features = preprocessing.StandardScaler().fit_transform(features):
StandardScaler是sklearn库中的一个预处理类,用于实现标准化功能,其数学原理:将每个特征值的数值减去均值,然后除以标准差,有助于消除特征之间的量纲差异(譬如身高1.5-2m之间,年龄0-120(岁)之间,数值范围差异过大,会让模型倾向于较大数值范围的特征,标准化便是用于消除或是大幅度减少这种差异),加快收敛速度且不会因为数值差异而对某些特征产生过大影响。
preprocessing.StandardScaler()主要对训练和测试数据进行标准化:
训练数据:fit_transform()
测试数据:transform()
4.2 构建网络模型:
4.2.1 稍微复杂的版本
ps:这里采用的是比较复杂的构建方式,但是包含了所有基本的构建流程,当然,我们可以利用一些自带的模型来完成快速构建网络模型
基本的逻辑和解释都写在代码里的注释了,整体流程如下:
1.创建输入数据x,y
2.设置网络模型参数(权重矩阵、学习率、损失值对象)
3.设置模型函数(损失函数、激活函数),常见的激活函数可以参考:激活函数(Relu,sigmoid,Tanh,softmax)详解
4.输出损失值,更新参数(注意梯度需要清零,防止梯度叠加)
#将输入格式(narry)转换为torch能接受的tensor格式
x = torch.tensor(input_features, dtype = float)
#y是真实值
y = torch.tensor(labels, dtype = float)
# 权重参数初始化
'''
我们打算搭建一个具有隐藏层的多层感知模型,所以我们设置两个线性模型以及参数(也就是2个权重矩阵),并随机初始化他们的数值(一般都选用标准正态分布)
我们的输入数据是348*14的矩阵,所以我们设置的权重矩阵应该是14*n,这里n我们可以任取,此处我取得值是
128,所以第一个权重矩阵是14*128,第二个权重矩阵后便需要获得输出值,所以第二个权重矩阵应为128*1,后续涉及梯度下降算法,所以requires_grad属性置为True
'''
weights = torch.randn((14, 128), dtype = float, requires_grad = True)
#设置偏置值
biases = torch.randn(128, dtype = float, requires_grad = True)
weights2 = torch.randn((128, 1), dtype = float, requires_grad = True)
biases2 = torch.randn(1, dtype = float, requires_grad = True)
#设置学习率,如果出现梯度爆炸现象,一般是学习率过大导致的,可以适当下调学习率大小
learning_rate = 0.001
#创建损失值对象
losses = []
for i in range(1000):
# 计算隐藏层
hidden = x.mm(weights) + biases
# 加入激活函数:reulu函数:f(x) = 1/(1+e^-x)
hidden = torch.relu(hidden)
# 预测结果
predictions = hidden.mm(weights2) + biases2
# 通计算损失(s手动版均方误差法)
loss = torch.mean((predictions - y) ** 2)
losses.append(loss.data.numpy())
# 打印损失值,每100次打印一份
if i % 100 == 0:
print('loss:', loss)
#返向传播计算
loss.backward()
#更新参数--x = x + (-)grad*rate 沿梯度反方向下降
weights.data.add_(- learning_rate * weights.grad.data)
biases.data.add_(- learning_rate * biases.grad.data)
weights2.data.add_(- learning_rate * weights2.grad.data)
biases2.data.add_(- learning_rate * biases2.grad.data)
# 每次迭代都得记得清空
weights.grad.data.zero_()
biases.grad.data.zero_()
weights2.grad.data.zero_()
biases2.grad.data.zero_()
跑一下:可以明显看出损失值在下降了:
4.2.2简化构建网络模型
上述流程基本是从头开始搭建模型,实际上torch为我们提供了丰富的API来辅助我们快速搭建神经网络模型.这里只是简单的举个例子,回头建个帖子,详细讨论下。
基本参数设置:
input_size = input_features.shape[1]
hidden_size = 128
output_size = 1
batch_size = 16
my_nn = torch.nn.Sequential(
#加入隐藏层
torch.nn.Linear(input_size, hidden_size),
#加入激活函数
torch.nn.Sigmoid(),
#再加一个全连接层
torch.nn.Linear(hidden_size, output_size),
)
#定义损失函数MSE--均方差
cost = torch.nn.MSELoss(reduction='mean')
#利用优化器实现反向传播,参数更新--Adam可以自适应学习率,可以根据不同的梯度信息,动态调整学习率
optimizer = torch.optim.Adam(my_nn.parameters(), lr = 0.001)
训练网络:
这里采用小批量梯度下降法作为训练方式,相关解释可以见:神经网络学习---学习笔记参数优化一栏。
# 存储每100个数值的损失值
losses = []
for i in range(1000):
#用于存储每一个bacth的loss数值
batch_loss = []
# MINI-Batch方法来进行训练
#每次迭代,从0开始,以batch_size为步长,逐渐增加起始索引
for start in range(0, len(input_features), batch_size):
end = start + batch_size if start + batch_size < len(input_features) else len(input_features)
#利用切片获取输出的特征数据
xx = torch.tensor(input_features[start:end], dtype=torch.float, requires_grad=True)
#获取标签
yy = torch.tensor(labels[start:end], dtype=torch.float, requires_grad=True)
#my_nn中进行前向传播
prediction = my_nn(xx)
#计算预测结果与真实标签之间的损失,使用之前定义的均方差损失函数cost
loss = cost(prediction, yy)
#梯度清零
optimizer.zero_grad()
#retain_graph=True表示在计算梯度后保留计算图,以便进行多次梯度更新
loss.backward(retain_graph=True)
#根据计算的梯度自动更新模型的参数
optimizer.step()
#numpy转换,并添加到batch_loss列表
batch_loss.append(loss.data.numpy())
# 打印损失
if i % 100 == 0:
#np.mean()取均值,以均值损失值输出
losses.append(np.mean(batch_loss))
print(i, np.mean(batch_loss))
尝试运行有以下输出结果,但没有显示0-100的输出,产生报错:
UserWarning: Using a target size (torch.Size([16])) that is different to the input size (torch.Size([16, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.
这是由于输入的尺寸是torch.Size([16, 1])
,而目标的尺寸是torch.Size([16])
所以在传入标签数据的时候加上尺寸限定,这样就ok了
yy = torch.unsqueeze(yy, dim = 1)
5.输出成果
主要是利用matplotlib绘制预测图,红色圆点散点图表示预测值,蓝色线条表示真实值,可以看出,整体的趋势是基本符合的,到此,一个简单的预测模型就构建完成了。
#-------预测训练结果---------
x = torch.tensor(input_features, dtype = torch.float)
#便于画图,转换为Numpy
predict = my_nn(x).data.numpy()
#
# dates = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) for year, month, day in zip(years, months, days)]
# dates = [datetime.datetime.strptime(date, '%Y-%m-%d') for date in dates]
# 创建一个表格来存日期和其对应的标签数值
true_data = pd.DataFrame(data = {'date': dates, 'actual': labels})
# 同理,再创建一个来存日期和其对应的模型预测值
months = features[:, feature_list.index('month')]
days = features[:, feature_list.index('day')]
years = features[:, feature_list.index('year')]
test_dates = [str(int(year)) + '-' + str(int(month)) + '-' + str(int(day)) for year, month, day in zip(years, months, days)]
test_dates = [datetime.datetime.strptime(date, '%Y-%m-%d') for date in test_dates]
predictions_data = pd.DataFrame(data = {'date': test_dates, 'prediction': predict.reshape(-1)})
# 真实值,b--蓝色表示
plt.plot(true_data['date'], true_data['actual'], 'b-', label = 'actual')
# (散点图)预测值ro--红色圆点表示
plt.plot(predictions_data['date'], predictions_data['prediction'], 'ro', label = 'prediction')
plt.xticks(rotation = 60);
plt.legend()
# 图名
plt.xlabel('Date'); plt.ylabel('Maximum Temperature (F)'); plt.title('Actual and Predicted Values');
plt.show()