DeepLearning in Pytorch|共享单车预测NN详解(思路+代码剖析)

目录

概要

一、代码概览

二、详解

基本逻辑

1.数据准备

2.设计神经网络

初版

改进版

测试

总结


概要

原文链接:DeepLearning in Pytorch|我的第一个NN-共享单车预测

我的第一个深度学习神经网络模型---利用Pytorch设计人工神经网络对某地区租赁单车的使用情况进行预测

输入节点为1个,隐含层为10个,输出节点数为1的小型人工神经网络,用数据的下标预测单车数量。

本文将以该神经网络模型为例,剖析深度学习部分概念,同时对原代码进行拆解分析

PS:

1.该神经网络无法达到解决实际问题的要求,但它结构简单,包含了神经网络的基本元素,可以达到初步入门深度学习以及熟悉Pytorch使用的效果,同时在实践过程中引出了过拟合现象。

2.Pytorch 2.2.1 (CPU) Python 3.6.13|Anaconda 环境

3.《深度学习原理与Pytorch实践》学习笔记


一、代码概览

bike1.py

二、详解

基本逻辑

在这里我要首先以一种朴素的方式描述一下学习和神经网络的基本逻辑。

假设自变量 x 和因变量 y,且它们之间存在映射关系 y = f(x)。法则 f(x) 未知,而现在我们有一些已知的 y 及其对应的 x,我们希望通过观测这些数据当中 y 与 x 的特征和关系,推测出可能满足要求的 f(x),从而对于一个新的自变量 x0,我们可以得到 f(x0) 作为预测值。

这些已知x和y的样本称为数据集,我们通常取一部分作为训练样本(训练集)供模型学习,另一部分(测试集)用于测试。

在实际中, y 与 x 的关系往往是复杂的,需要通过多层函数实现( y = f(g(h(...(x)))) ),我们把函数的层数称为深度,每个层都有权重参数和其他参数对上一层的值处理(加权求和+激活函数)后传递给更深层的神经元。在训练过程中,当一个输入 x0 经过多个层得到预测输出 y0’(1)时,模型就会与实际值 y0 比较,然后对各个层的参数进行调整,重新输入模型得到 y0’(2),再与 y0 比对······经过多次迭代,最终当 y0(n) 与 y0 的误差在可接受范围内(模型预测值与实际值拟合)时,我们就得到了一个对训练集学习产生的预测神经网络。

其中,输入层到输出层的过程称为神经网络的前馈运算,迭代进行参数更新的过程称为反向传播算法

一般而言,前馈运算可以表示为以下的伪代码:

# 输入数据
input_data = ...

# 输入层
input_layer_output = input_data

# 隐藏层(可以有多个隐藏层)
for layer in hidden_layers:
    layer_activation = activation_function(layer_weights * input_layer_output)
    input_layer_output = layer_activation

# 输出层
output_activation = activation_function(output_layer_weights * input_layer_output)

# 输出结果
output_result = output_activation

其中,activation_function() 表示激活函数,layer_weightsoutput_layer_weights 表示每个神经元的权重。

这就是神经网络的前馈运算过程。它负责将输入数据从输入层传递到输出层,并产生相应的输出结果。前馈运算和反向传播算法只是神经网络的一部分,其他重要的步骤包括损失函数计算等。

神经网络的数学逻辑较为复杂,建议先做了解后再来上手实操。下面我们讨论我们的BikeNN。

1.数据准备

#导入需要使用的库
import numpy as np
import pandas as pd #读取csv文件的库
import matplotlib.pyplot as plt #绘图
import torch
import torch.optim as optim
#读取数据到内存中,rides为一个dataframe对象
data_path = 'Documents/hour.csv' #文件路径
rides = pd.read_csv(data_path)
rides.head()

在这个项目中,我们引入了numpy pandas matplotlib.pyplot torch 和 torch.optim 

NumPy提供多维数组对象和数学函数;而Pandas 是建立在 NumPy 之上的数据处理库,适用于数据清洗、分析和处理;Matplotlib 是一个绘图库,matplotlib.pyplot 是 Matplotlib 的子模块,用于将数据可视化(绘图);PyTorch 是我们的主要工具,与其他深度学习框架相比它提供了张量计算和动态计算图(可以将张量理解为多维数组);torch.optim 是 PyTorch 提供的优化算法模块,包含了常见的优化器,用于更新深度学习模型中的参数以最小化损失函数。

这些库是我们在基于Pytorch的深度学习任务中最基本的函数库,我们用 NumPy 加载和处理数据,利用 Pandas 进行数据清洗和转换,使用 Matplotlib.pyplot 可视化数据,然后使用 PyTorch 构建和训练深度学习模型,并利用 torch.optim 模块选择和应用优化算法。

#我们取出最后一列的前50条记录来进行预测
counts = rides['cnt'][:50]

#获得变量x,它是1,2,……,50
x = np.arange(len(counts))

# 将counts转成预测变量(标签):y
y = np.array(counts)

# 绘制一个图形,展示曲线长的样子
plt.figure(figsize = (10, 7)) #设定绘图窗口大小
plt.plot(x, y, 'o-') # 绘制原始数据
plt.xlabel('X') #更改坐标轴标注
plt.ylabel('Y') #更改坐标轴标注
plt.show()

在读取文件后,我们定义了NumPy数组 x 和 y,在本例中,x 是样本的序号(创建了一个1-50的有序数组),y 是共享单车数目counts(从hour.csv读取得到),然后利用绘图函数将X和Y关系用图像表示了出来:

2.设计神经网络

初版

#版本1
#取出数据库中的最后一列的前50条记录来进行预测
counts = rides['cnt'][:50]

#创建变量x,它是1,2,……,50
x = torch.tensor(np.arange(len(counts), dtype = float), requires_grad = True)

# 将counts转成预测变量(标签):y
y = torch.tensor(np.array(counts, dtype = float), requires_grad = True)

首先取出前50组样本作为训练集,定义张量 x 和 y:

(PS:我们在这里主要用到两种数据结构,一种是Python提供的NumPy数组,另一种是Pytorch提供的tensor张量,二者之间可以通过库函数转换)

使用 np.arange(len(counts), dtype=float) 创建了一个 NumPy 数组,其中包含了 0 到 49 的数字序列,然后使用 torch.tensor() 将该数组转换为 PyTorch 张量 x,设置 requires_grad=True 允许对 x 进行梯度计算;

np.array(counts, dtype = float)counts 转换为 NumPy 数组,然后使用 torch.tensor() 函数将其转换为 PyTorch 的张量对象,设置 requires_grad 参数以启用梯度计算。这样就可以在训练神经网络时,根据预测值与真实标签的差异来计算损失,并通过反向传播更新网络参数。

# 设置隐含层神经元的数量
sz = 10

# 初始化所有神经网络的权重(weights)和阈值(biases)
weights = torch.randn((1, sz), dtype = torch.double, requires_grad = True) #1*10的输入到隐含层的权重矩阵
biases = torch.randn(sz, dtype = torch.double, requires_grad = True) #尺度为10的隐含层节点偏置向量
weights2 = torch.randn((sz, 1), dtype = torch.double, requires_grad = True) #10*1的隐含到输出层权重矩阵

learning_rate = 0.001 #设置学习率
losses = []

# 将 x 转换为(50,1)的维度,以便与维度为(1,10)的weights矩阵相乘
x = x.view(50, -1)
# 将 y 转换为(50,1)的维度
y = y.view(50, -1)

这段代码实现了一个简单的神经网络模型,包括设置隐含层神经元数量、初始化权重和阈值、设定学习率、以及对输入数据进行维度转换的操作,具体如下:

  1. 设置了隐含层神经元的数量为 10。
  2. 初始化所有神经网络的参数(权重和阈值)
    • weights 是一个大小为 (1, 10) 的权重矩阵,将输入到隐含层的权重初始化为随机值。
    • biases 是一个大小为 (10,) 的偏置向量,用于隐含层节点的初始化。
    • weights2 是一个大小为 (10, 1) 的权重矩阵,表示隐含层到输出层的权重初始化。
  3. learning_rate = 0.001:设置学习率为 0.001。
  4. losses = []:初始化一个空列表用于存储损失值。
  5. 将输入 x 和标签 y 进行维度转换(根据矩阵乘法的运算规则需要):
    • x.view(50, -1) 将 x 转换为大小为 (50, 1) 的张量,以便与大小为 (1, 10) 的 weights 矩阵相乘。
    • y.view(50, -1) 将 y 转换为大小为 (50, 1) 的张量。

这段代码准备了神经网络所需的初始参数(也是模型后面迭代更新的对象),并对输入数据进行了适当的处理,以便后续在神经网络模型中使用。这是建立神经网络模型前的一些基本设置和准备工作。

重头戏来了

for i in range(100000):
    # 从输入层到隐含层的计算
    hidden = x * weights + biases
    # 将sigmoid函数作用在隐含层的每一个神经元上
    hidden = torch.sigmoid(hidden)
    #print(hidden.size())
    # 隐含层输出到输出层,计算得到最终预测
    predictions = hidden.mm(weights2)#
    #print(predictions.size())
    # 通过与标签数据y比较,计算均方误差
    loss = torch.mean((predictions - y) ** 2) 
    #print(loss.size())
    losses.append(loss.data.numpy())
    # 每隔10000个周期打印一下损失函数数值
    if i % 10000 == 0:
        print('loss:', loss)   
    #对损失函数进行梯度反传
    loss.backward()
    #利用上一步计算中得到的weights,biases等梯度信息更新weights或biases中的data数值
    weights.data.add_(- learning_rate * weights.grad.data)  
    biases.data.add_(- learning_rate * biases.grad.data)
    weights2.data.add_(- learning_rate * weights2.grad.data)
    # 清空所有变量的梯度值。
    # 因为pytorch中backward一次梯度信息会自动累加到各个变量上,因此需要清空,否则下一次迭代会累加,造成很大的偏差
    weights.grad.data.zero_()
    biases.grad.data.zero_()
    weights2.grad.data.zero_()

这段代码是神经网络结构中最重要的训练部分,也是深度学习任务所需运行时间最长的部分,包括前向传播、计算损失、反向传播更新参数等步骤。对于循环次数为 100,000 次的训练过程:

  1. 从输入层到隐含层的计算:将输入 x 与权重 weights 相乘并加上偏置 biases 得到隐含层的输出 hidden
  2. 对隐含层的输出应用 Sigmoid 函数,将其值压缩到 (0, 1) 区间内。
  3. 隐含层的输出再通过权重 weights2 得到最终的预测结果 predictions
  4. 计算预测结果与真实标签 y 之间的均方误差作为损失值 loss
  5. 将每一步的损失值存储在列表 losses 中。
  6. 每 10,000 次迭代打印一次当前的损失值。
  7. 对损失值进行反向传播,计算各个参数的梯度信息。
  8. 根据梯度信息使用梯度下降算法更新权重 weightsbiases 和 weights2
  9. 清空所有参数的梯度信息,以便下一轮迭代时重新计算新的梯度。

我们不难发现,每一层(包括中间的隐藏层和最后的输出层)的输出 hidden/predictions 都是通过加权参数作用下加权求和,然后由激活函数Sigmoid函数决定神经元是否激活。

每次迭代得到一个预测结果predictions,将它与真实标签 y 比对得到损失值 loss并将其可视化(每迭代10000次输出一次当前的loss,这样我们可以看到loss随着迭代次数增多的变化)。

通过我们前面提到的反向传播算法(Pytorch提供 .backward()函数计算所有变量的梯度信息,并将叶节点的导数值存储在.grad中)计算各个参数的梯度信息,从而更新参数配置(weightsbiases 和 weights2)以获得更准确的预测predictions。

# 打印误差曲线
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

误差loss随迭代次数变化曲线: 

显然误差loss随迭代次数增多而下降,在20000次以后基本不再下降 

x_data = x.data.numpy() # 获得x包裹的数据
plt.figure(figsize = (10, 7)) #设定绘图窗口大小
xplot, = plt.plot(x_data, y.data.numpy(), 'o') # 绘制原始数据

yplot, = plt.plot(x_data, predictions.data.numpy())  #绘制拟合数据
plt.xlabel('X') #更改坐标轴标注
plt.ylabel('Y') #更改坐标轴标注
plt.legend([xplot, yplot],['Data', 'Prediction under 1000000 epochs']) #绘制图例
plt.show()

最后我们获取 x 和对应 predictions 的数据绘制出来: 

 这个模型有两个问题:

1.显然预测曲线在第一个波峰比较好地拟合了数据,但是在此后,它却与真实数据相差甚远

2.它的运行时间较长

实际上这两个问题是基于同一个原因:我们输入层的 x 范围是1-50,经过权重和偏置(范围在(-1,1))处理后结果就变成了(-50,50)可能会使得 sigmoid 函数的多个峰值无法很好地发挥作用,需要更多的计算时间来让神经网络学习适应这种范围的输入;sigmoid 函数的输出范围在(0, 1),如果输入的值范围较大,可能会导致 sigmoid 函数在不同位置出现多个峰值,这可能会增加模型训练的复杂度,需要更多的迭代次数才能调节神经元的权重和偏置,使得模型收敛到期望的位置。如果用于适应Sigmoid函数输入的时间少些,模型的拟合程度也会更高。

改进版

要改进上面的问题可以考虑两种手段:

  • 调整权重和偏置的初始化范围,使其适应输入数据的范围,可以尝试根据实际情况选择更合适的初始化方案。
  • 使用其他激活函数代替 sigmoid 函数,如 ReLU 函数等,能够更好地处理梯度消失和爆炸等问题,有助于加速模型的收敛速度。(AlexNet)

在这里我们采用第一种:将输入数据的范围做归一化处理,也就是让 x 的输入数值范围为0~1。

因为数据中 x 的范围是1~50,所以,我们只需要将每一个数值都除以50(len(counts))就可以了:

x = torch.FloatTensor(np.arange(len(counts), dtype = float) / len(counts))

改进后的代码 :

#版本2
#取出最后一列的前50条记录来进行预测
counts = rides['cnt'][:50]

#创建归一化的变量x,它的取值是0.02,0.04,...,1
x = torch.tensor(np.arange(len(counts), dtype = float) / len(counts), requires_grad = True)

# 创建归一化的预测变量y,它的取值范围是0~1
y = torch.tensor(np.array(counts, dtype = float), requires_grad = True)

# 设置隐含层神经元的数量
sz = 10

# 初始化所有神经网络的权重(weights)和阈值(biases)
weights = torch.randn((1, sz), dtype = torch.double, requires_grad = True) #1*10的输入到隐含层的权重矩阵
biases = torch.randn(sz, dtype = torch.double, requires_grad = True) #尺度为10的隐含层节点偏置向量
weights2 = torch.randn((sz, 1), dtype = torch.double, requires_grad = True) #10*1的隐含到输出层权重矩阵

learning_rate = 0.001 #设置学习率
losses = []

# 将 x 转换为(50,1)的维度,以便与维度为(1,10)的weights矩阵相乘
x = x.view(50, -1)
# 将 y 转换为(50,1)的维度
y = y.view(50, -1)

for i in range(100000):
    # 从输入层到隐含层的计算
    hidden = x * weights + biases
    # 将sigmoid函数作用在隐含层的每一个神经元上
    hidden = torch.sigmoid(hidden)
    # 隐含层输出到输出层,计算得到最终预测
    predictions = hidden.mm(weights2)# + biases2.expand_as(y)
    # 通过与标签数据y比较,计算均方误差
    loss = torch.mean((predictions - y) ** 2) 
    losses.append(loss.data.numpy())
    # 每隔10000个周期打印一下损失函数数值
    if i % 10000 == 0:
        print('loss:', loss)
    #对损失函数进行梯度反传
    loss.backward()
    #利用上一步计算中得到的weights,biases等梯度信息更新weights或biases中的data数值
    weights.data.add_(- learning_rate * weights.grad.data)  
    biases.data.add_(- learning_rate * biases.grad.data)
    weights2.data.add_(- learning_rate * weights2.grad.data)
    # 清空所有变量的梯度值。
    # 因为pytorch中backward一次梯度信息会自动累加到各个变量上,因此需要清空,否则下一次迭代会累加,造成很大的偏差
    weights.grad.data.zero_()
    biases.grad.data.zero_()
    weights2.grad.data.zero_()
plt.semilogy(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

是的,其他部分完全一样!我们再来看看它的误差曲线: 

预测结果曲线: 

我们可以发现:

和先前相比,loss 直到迭代80000次才逐渐稳定,因为有更多的迭代用于更新模型的参数,使其更加准确,而非浪费在处理Sigmoid函数峰值上;

改进后的模型运行速度更快,数据图出现了两个波峰,也非常好地拟合了这些数据点,形成一条优美的曲线。

测试

用后面50组样本(测试集)进行测试 

#测试50组数据
counts_predict = rides['cnt'][50:100] #读取待预测的接下来的50个数据点

#首先对接下来的50个数据点进行选取,注意x应该取51,52,……,100,然后再归一化
x = torch.tensor((np.arange(50, 100, dtype = float) / len(counts))
                 , requires_grad = True)
#读取下50个点的y数值,不需要做归一化
y = torch.tensor(np.array(counts_predict, dtype = float), requires_grad = True)

x = x.view(50, -1)
y = y.view(50, -1)

# 从输入层到隐含层的计算
hidden = x * weights + biases

# 将sigmoid函数作用在隐含层的每一个神经元上
hidden = torch.sigmoid(hidden)

# 隐含层输出到输出层,计算得到最终预测
predictions = hidden.mm(weights2)

# 计算预测数据上的损失函数
loss = torch.mean((predictions - y) ** 2) 
print(loss)


x_data = x.data.numpy() # 获得x包裹的数据
plt.figure(figsize = (10, 7)) #设定绘图窗口大小
xplot, = plt.plot(x_data, y.data.numpy(), 'o') # 绘制原始数据
yplot, = plt.plot(x_data, predictions.data.numpy())  #绘制拟合数据
plt.xlabel('X') #更改坐标轴标注
plt.ylabel('Y') #更改坐标轴标注
plt.legend([xplot, yplot],['Data', 'Prediction']) #绘制图例
plt.show()

测试的计算过程和训练当中每一次迭代的计算过程基本类似

作出拟合数据与原始数据图:

模型预测与实际数据竟然完全对不上!为什么我们的神经网络可以非常好地拟合已知的50个数据点,却在测试集上出错了呢?因为y(单车数量)与x(数据序号)根本没有关系!这就是在机器学习中最常见的困难---过拟合

过拟合(Overfitting)是指机器学习模型在训练数据上表现很好,但在测试数据上表现较差的情况。过拟合通常发生在模型过度复杂(参数太多)或者训练数据量太少的情况下。这两种情况都可能会把训练集的特征误以为是全部样本的特征。

对于我们的单车预测模型,问题显然在于我们要求模型学习 单车数量y 与 数据序号x 之间的关系,模型通过学习我们给出的前五十组数据(训练集)学会了它所认为的样本特征,但当我们引入后面50组样本(测试集)时,我们发现模型学到的特征是没有意义的,它只能反映训练集中的某些特点。

如果要解决这个问题,我们就应该让模型学习关于样本的更多特征,如:星期几、是否节假日、温度、湿度等(显然这些才是真正会影响x的因素)。当然从理论上讲,这样得到的神经网络更复杂,但显然他的预测更能达到我们想要的效果。 


总结

我们通过简单的神经网络模型--单车预测了解了深度学习的基本逻辑和Pytorch的使用(虽然这是一个错误的模型hhh),我们将在下一篇中用更多新的手段构建预测更精准、更好的神经网络模型

主要参考资料:《深度学习原理与Pytorch实践》

参考代码:bike1.py · che.melsm/DeepLearning Project - Gitee.com

阅读原文:DeepLearning in Pytorch|我的第一个NN-共享单车预测

  • 30
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 深度学习是一项非常热门的技术,在人工智能领域得到广泛应用。PyTorch是一种使用Python编程语言的开源深度学习框架,它非常适合研究和开发深度学习模型。为了帮助初学者更好地学习PyTorch深度学习技术,CSDN(全球最大中文IT社区)开设了“DeepLearning with PyTorch”系列课程。 这个系列课程以实践为主要教学方式,让学生在实际操作中掌握PyTorch深度学习的技能。在学习过程中,学生可以学到基础的模型结构设计,各种优化算法,如学习率调整、梯度下降等,并且可以在实战操作中学到如何使用PyTorch完成各种实际应用,例如图像分类和识别,自然语言处理等等。 这门课程的受众群体不仅仅是那些想要从事人工智能开发的工程师,它对于对深度学习感兴趣的学生和科研人员也是非常有用的。这是因为在这个课程中,教师基于实际使用场景和数据集介绍了PyTorch深度学习技术,从实践中总结出的方法和经验不仅可以快速提升工程开发效率,也可以加深对深度学习理论的理解。 总之,“DeepLearning with PyTorch”系列课程非常实用和有趣,可以为初学者提供全面而深入的深度学习知识,帮助他们掌握用PyTorch来开发深度学习模型的基础技能。 ### 回答2: Deep Learning是一种用于训练多层神经网络的机器学习方法,已被广泛应用于视觉、语音、自然语言处理等领域。而PyTorch是一种开源的深度学习框架,具有快速、灵活、易用等优点,因此受到了越来越多的关注和使用。 CSDN是一个致力于IT技术在线学习和分享的平台,在其中学习deeplearning with pytorch将能够获取丰富的知识和实践经验。首先,我们需要了解PyTorch的基本概念和操作方法,如如何构建网络模型、定义损失函数和优化器、进行前向传播和反向传播等。然后,我们可以学习如何使用PyTorch进行数据预处理,如数据清洗、标准化、归一化等。此外,还可了解如何使用PyTorch进行分布式训练、混合精度训练等高级技术,以及如何在GPU上进行训练和推理等实践技巧。 总之,在CSDN上学习deeplearning with pytorch,能够让我们更好地掌握PyTorch的使用技巧,帮助我们更快、更好地完成深度学习的应用开发和研究工作。同时也可以通过活跃在CSDN平台上与其他开发者的交流来共同进步。 ### 回答3: PyTorch是一种针对深度学习任务的开源机器学习库,它支持快速的原型设计和大量的实验,是当前科学界和工业界中最受欢迎的深度学习框架之一。CSDN推出的Deeplearning with Pytorch系列课程就是致力于教授学生如何使用PyTorch进行深度学习,以及在此基础上更深层次的研究探索。 此系列课程包含了从入门到进阶多个方面的内容,在基础课程中,学员将学会如何使用PyTorch进行深度学习的各个方面,包括但不限于神经网络、优化器、损失函数等,使其基本掌握PyTorch的使用方法。而在进阶课程中,以一些大型深度学习任务为基础,详细介绍了超参数优化、神经网络模型架构选择、分布式训练、自己写网络模型等更高级的知识,通过深度剖析一些开源库的源码,为学员提供了很多实现深度学习任务的技巧和方法。 课程的开设不仅帮助了很多想更深入了解深度学习的爱好者,也有助于那些打算将深度学习应用在自己的科研工作中的研究者们更加快捷、有效地完成自己的研究任务。相信随着人工智能的不断发展,PyTorch这样的框架将会发挥越来越重要的作用,而帮助大家掌握这些工具的Deeplearning with Pytorch系列课程也必将得到更多的关注和支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值