你的第一个神经网络——共享单车预测器

引言

本文是学习完集智学园《PyTorch入门课程:火炬上的深度学习》系列课以后的梳理。bash

该系列课程包含大量的实操任务,如文本分类,手写数字识别,翻译器,做曲机,AI游戏等。而在实操中还融入了大量机器学习领域的基础和经典知识,如反向传播原理神经元原理剖析,NLP领域的RNNLSTMword2vec,以及图像领域的迁移学习卷积神经网络等等,很是适合想要入门机器学习的同窗,是当前网络上绝无仅有的好课程。网络

我的学完后受益不浅,在此真诚地向张江老师表示感谢数据结构

同系列文章:用线性回归拟合案例,透彻理解深度学习的反向传播app

正文

本文的任务,是经过训练一个简单的神经网络预测模型,实现了根据天气,季节,日期,星期等自变量,去预测对应日期的单车数量。主要包含的知识点为:数据预处理中的one-hot编码归一化操做,以及构建神经网络的流程机器学习

任务中使用到的数据结构以下。数据已共享在网盘中ide

image

其中,最后一列cnt为目标函数,即单车的数量。表中其余数据,咱们选择season, hr, mnth,weekday, weathersit, temp, hum,holiday, yr等做为因变量,其余数据抛弃不用。每条数据以小时为单位,共约2w条数据,咱们选取部分绘图看一下函数

image

整个任务实现流程能够分为如下三个步骤:学习

  1. 数据预处理
  2. 训练模型
  3. 验证模型

下面咱们开始任务测试

1. 数据预处理

数据预处理是整个建模步骤很是重要的环节,甚至会直接影响到训练效果。在这个环节咱们的核心目标其实仍是围绕一点:为了让训练效果更好。优化

咱们观察数据会发现,原始数据不够好,直接拿来训练的话,极可能会影响到训练过程和最终的训练效果。

那么具体哪里很差,咱们又该怎么处理呢?

1.1. 对类型变量的处理:one-hot编码

在咱们这个案例中,季节天气等几个变量都是咱们为了平常生活方便而约定的规则,属于类型变量,他们的数字大小,并无实际的意义,好比星期二并不表明它比星期一的值大,直接把大小输入模型,会形成神经网络的“误解”:即值越大,会更强烈地影响网络内部的变化。因此咱们须要对这些类型变量作二进制处理。

仍是拿星期举例,咱们须要把星期的标识,所有转化为二进制编码,哪一位为1,就表明对应的类型(其实就至关于,会激活与之相关的神经元)

image

具体到代码实现,pandas有现成的方法,帮助咱们快速生成one-hot编码

dummies = pd.get_dummies(rides['weekday'], prefix='weekday', drop_first=False
复制代码

one-hot编码后的“星期

下面展现真实代码。咱们要操做的对象是'season', 'weathersit', 'mnth', 'hr', 'weekday'这几个属性。处理完成后,将生成的数据再合并进原来的数据中 。最后,去掉原数据集中不相关的特征

# 这是咱们要处理的特征组
dummy_fields = ['season', 'weathersit', 'mnth', 'hr', 'weekday']
for each in dummy_fields:
    dummies = pd.get_dummies(rides[each], prefix=each, drop_first=False)
    # 合并进原来的数据
    rides = pd.concat([rides, dummies], axis=1)
  
# 把原有的类型变量对应的特征去掉,将一些不相关的特征去掉
fields_to_drop = ['instant', 'dteday', 'season', 'weathersit', 'weekday', 'atemp', 'mnth', 'workingday', 'hr']
data = rides.drop(fields_to_drop, axis=1)
data.head() # 打印查看
复制代码

one-hot编码后的数据

完成类型编码的转化以后,先别着急开始,咱们还有一步要作

1.2. 数据值归一化

在不少时候,原是数据中不一样单位的变量,他们的数值差别有可能会很是大。好比在咱们的案例中,温度的范围可能在0-50之间浮动,而湿度则是在0-1之间浮动的小数。而有时的初始数据甚至还会出现几千几万的数量级。若是直接进行训练,固然也不是不能够,只不过神经网络的权重会学习的很是慢,并且最终的效果可能也很差。

因此,咱们须要对不一样单位的数值进行归一化处理,归一化的方式有不少,好比把数值按正态分布转化为[-1,1]区间。在这里使用经常使用的z-score标准化方法。

新数据 = (原数据 - 均值) / 标准差

# 标准化处理,这里将目标数量也作了归一化
quant_features = ['cnt', 'temp', 'hum', 'windspeed']
for each in quant_features:
    # 快速求均值和方差
    mean, std = data[each].mean(), data[each].std()
    # 选取列名为each的特定列, 替换为归一化后的数据
    data.loc[:, each] = (data[each] - mean) / std
data.head() # 打印查看
复制代码

归一化结果

数据预处理环节就结束了,总结来看分为两点,这两点也是绝大部分神经网络训练以前都须要进行的步骤。

  1. 类型变量值的大小可能会影响训练效果,须要进行one-hot编码处理
  2. 不一样数据的尺度不一样,须要进行归一化处理,以提升训练速度和训练效果

下面,咱们就开始进入正式的训练过程

2. 构建模型,开始训练

使用pytorch构建网络真是一件很爽的事情,你不须要关注梯度如何传播,线性映射和非线性映射具体如何运做,你只须要按照pytorch的方法用几行代码构建模型,而后给他输入,获取输出就能够了。

这部分开始,代码量就上升了,我会尽可能讲的详细,注释也会尽量的全面

2.1. 构建输入和目标函数

在这里,咱们的输入为全部会影响当前单车数量的自变量,输出为对应的单车数量,固然,不要忘了分训练集和测试集,训练集用于训练,测试集用于测试最终模型的效果。

target_fields = ['cnt']
# 训练集,去掉最后21天的数据
train_data = data[:-21 * 24]
# 测试集,最后21天的数据
test_data = data[-21 * 24:]

# 定义输入,和预测目标
features, targets = train_data.drop(target_fields, axis=1), train_data[target_fields]
test_features, test_target = test_data.drop(target_fields, axis=1), test_data[target_fields]
复制代码

上面代码定义的输入和预测目标,仍是dataframe格式,咱们须要把数据转化为array或者tensor,这里咱们转成array格式。同时,为了方便计算,须要调整目标函数的维度。从0维(数据量)转化为1维(数据量 x 1)

2.2. 构建模型

咱们的案例不须要使用到多复杂的模型,只须要确保在中间有一层非线性映射,用以拟合曲线便可,构建模型就是这么简单。

同时,pytorch也封装了很多损失函数方法,在这里使用简单MSE模型,直接调用便可。在线性回归拟合案例,透彻理解深度学习的反向传播一文中,有MSE的具体实现说明,很简单,一看就懂了。

input_size = np.array(features).shape[1]
hidden_size = 10
output_size = 1
neu = torch.nn.Sequential(
    torch.nn.Linear(input_size, hidden_size), # 一层线性映射
    torch.nn.Sigmoid(), # 一层sigmoid非线性映射
    torch.nn.Linear(hidden_size, output_size) # 再一层线性映射
)
cost = torch.nn.MSELoss()
optimizer = torch.optim.SGD(neu.parameters(), lr = 0.01)
复制代码

划重点:

  1. torch.nn.Linear: 线性映射
  2. torch.nn.Sigmoid: 非线性映射,能够拟合曲线
  3. torch.nn.MSELoss(): MSE损失函数
  4. torch.optim.SGD(neu.parameters(), lr = 0.01):参数优化器,能够实现反向传播过程,更新全部带梯度的参数等功能,具体使用能够继续往下看。lr表明学习率

到这里,模型已经构建好,下面进入训练过程(抽象出模型后,全部的训练过程都大同小异)

2.3 训练过程

首先定义一个batch_size。至关于定义了,把原始数据以128条为最小训练单位进行训练。

batch_size = 128

image

分batch是为了充分训练模型,并且增长数据的随机性,效果会更好。好比咱们的20000条数据,不分批的话,那么跑完20000条数据,模型才被训练了一次,偏差及计算了一次,反向传播了一次,权重调整了一次,这个效率就很低了;而若是把20000分红一小撮,每一撮是一个完整的训练过程,那么效率就大大提升了。并且,这样能够增长训练数据的随机性。

接着,咱们就直接上代码了。看着代码量比较多,但其实很简单。

Losses = []
for i in range(10000):
    batch_loss = []

    for start in range(0, len(X), batch_size):
        end = start + batch_size if start + batch_size < len(X) else len(X)
        # 获取本批次的输入和预测目标
        xx = torch.tensor(X[start: end, :], dtype=torch.float, requires_grad = True)
        yy =  torch.tensor(Y[start: end, :], dtype=torch.float, requires_grad = True)

        # 输入数据,获得预测值
        predict = neu(xx)

        # 使用损失函数比较预测数据和真实数据,获得loss
        loss = cost(predict, yy)

        # 反向传播,优化权重
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # 记录loss
        batch_loss.append(loss)
    Losses.append(batch_loss[1:])
复制代码

步骤说明:

  1. batch中获取本批次的输入xx和预测目标yy
  2. xx输入前面构造好的神经网络模型neu,获得预测数值predict
  3. 在损失函数cost中对比预测值predict和真实值yy, 获得损失值loss
  4. 在优化器optimizer中完成反向传播,优化权重,清空梯度的操做
  5. 记录loss

这里经过监听loss的变化来判断训练状况,若是loss的变化趋势是。一开始迅速降低,到必定值后趋于稳定再也不降低,一般就说明模型表现良好,训练完成。

loss曲线

上图是我记录的,每一个batch最低的loss,能够看到,大约在600轮训练后,模型就趋于稳定。

这个时候,把最后一步的predict拿出来,和真实值yy作对比,会发现惊人的一致。

image

固然这里的一致并不能说明什么问题,训练数据表现好不能证实模型效果好,这就好像你用A推演出B的结论,再用A去验证B正确同样荒谬。正确的作法是,咱们用和A有同样特征的C,去尝试推演,看是否能得出B。

3. 验证模型

验证的步骤,就是把上面训练过程最后一步的模型拿过来,放到测试集上跑一遍,观察预测值和真实值的差别

对比test_predict和test_yy的差别

image

上图观察可知,不管是工做日仍是周末,也无论当天的天气如何,咱们的预测基本可以贴近真实值。不过中间有几天的数据差别较大,最后分析数据发现,那几天是一个很是规的节假日,致使历史数据不具备参考意义。

到这里,整个训练过程就完成了,咱们获得了一个最简单的模型,只要给定当天的气温,日期,星期等因素,它就能够预测出当天的单车使用数量。

结束

感谢您看到这里。

在从此的一段时间里,我还会尝试图像识别,文本分类和翻译,AI游戏等真实案例。全部学习案例都来自张江老师的PyTorch与深度学习课程。

望与你们共勉。

相关文章
相关标签/搜索