美文网首页Python学习资料整理
Pytorch实现共享单车数量预测

Pytorch实现共享单车数量预测

作者: carmanzzz | 来源:发表于2019-03-12 20:11 被阅读30次

    之前分享过Pytorch实现简单线性回归算法的内容:Pytorch实现简单的线性回归算法,这次分享一下用pytorch预测贡献单车数量的项目,具体的理论可能讲的不太明白,大家参考一下代码就可以。

    共享单车的调配一直是困扰共享单车公司的大难题,通过数据分析能够有效的调配资源,方便用户也有利于公司的营收,记得当时在北京实习的时候,每晚下班出来都希望能有辆单车扫,结果一出公司门发现都被人骑走了,哈哈。

    ok,现在进入正题,这里用的数据是国外的一家共享单车的数据:数据来源

    po上代码:

    #导入需要使用的库
    import numpy as np
    import pandas as pd 
    import matplotlib.pyplot as plt
    import torch
    from torch.autograd import Variable
    import torch.optim as optim
    

    读取数据:

    data_path = 'Bike-Sharing-Dataset/hour.csv'
    rides = pd.read_csv(data_path)
    rides.head()
    
    image.png
    #我们取出最后一列的前50条记录来进行预测
    counts = rides['cnt'][:50]
    x = np.arange(len(counts))    #获得变量x,它是1,2,……,50
    y = np.array(counts)    # 将counts转成预测变量(标签):y
    # 绘制一个图形,展示曲线长的样子
    plt.figure(figsize = (10, 7)) 
    fig, ax = plt.subplots()
    ax.plot(x, y, 'o-') 
    plt.xlabel('X') 
    plt.ylabel('Y') 
    
    image.png

    可以复习一下之前学的内容,用线性回归做一下,可以想见效果一定很差~~

    counts = rides['cnt'][:50]
    x = Variable(torch.FloatTensor(np.arange(len(counts), dtype = float)))
    y = Variable(torch.FloatTensor(np.array(counts, dtype = float)))
    
    a = Variable(torch.rand(1), requires_grad = True) #创建a变量,并随机赋值初始化
    b = Variable(torch.rand(1), requires_grad = True) #创建b变量,并随机赋值初始化
    print('Initial parameters:', [a, b])
    learning_rate = 0.00001 #设置学习率
    for i in range(1000):
        predictions = a.expand_as(x) * x+ b.expand_as(x)  #计算在当前a、b条件下的模型预测数值
        loss = torch.mean((predictions - y) ** 2) 
        print('loss:', loss)
        loss.backward() #对损失函数进行梯度反传
    
        a.data.add_(- learning_rate * a.grad.data)  #利用上一步计算中得到的a的梯度信息更新a中的data数值
        b.data.add_(- learning_rate * b.grad.data)  #利用上一步计算中得到的b的梯度信息更新b中的data数值
        a.grad.data.zero_() #清空a的梯度数值
        b.grad.data.zero_() #清空b的梯度数值
    
    # 绘制图形,展现线性回归的效果,结果惨不忍睹
    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') 
    str1 = str(a.data.numpy()[0]) + 'x +' + str(b.data.numpy()[0]) 
    plt.legend([xplot, yplot],['Data', str1]) 
    plt.show()
    
    image.png

    线性回归失败了,因为根本不是线性关系!

    接下来用人工神经网络Neu来进行预测:
    首先要进行数据预处理,因为在进行预测的时候很多变量都是类型变量,如季节(1,2,3,4),星期(1,2……6),类型数据的数值只是代表类型,没有大小的意义,数值越高并不表示相应的信号强度越大。解决方案是将类型变量进行one-hot 编码,做逻辑回归时也经常采用这样的方法。
    示例:
    season=1→(1,0,0,0)
    season=2→(0,1,0,0)
    season=3→(0,0,1,0)
    season=4→(0,0,0,1)

    开始:

    #对于类型变量的特殊处理
    # season=1,2,3,4, weathersi=1,2,3, mnth= 1,2,...,12, hr=0,1, ...,23, weekday=0,1,...,6
    # 经过下面的处理后,将会多出若干特征,例如,对于season变量就会有 season_1, season_2, season_3, season_4
    # 这四种不同的特征。
    
    dummy_fields = ['season', 'weathersit', 'mnth', 'hr', 'weekday']
    for each in dummy_fields:
        #利用pandas对象,我们可以很方便地将一个类型变量属性进行one-hot编码,变成多个属性
        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()
    

    接下来对数值类型的变量进行标准化处理,因为每个数值型变量都是相互独立的,所以它们的数值绝对大小与问题本身没有关系,为了消除数值大小的差异,可以对每一个数值型变量进行标准化处理,也就是让其数值都围绕着0左右波动。

    # 调整所有的特征,标准化处理
    quant_features = [ 'temp', 'hum', 'windspeed']
    # 我们将每一个变量的均值和方差都存储到scaled_features变量中。
    scaled_features = {}
    for each in quant_features:
        mean, std = data[each].mean(), data[each].std()
        scaled_features[each] = [mean, std]
        data.loc[:, each] = (data[each] - mean)/std
    

    对数据集进行分割:

    # 将所有的数据集分为测试集和训练集,我们以后21天数据一共21*24个数据点作为测试集,其它是训练集
    test_data = data[-21*24:]
    train_data = data[:-21*24]
    # 将我们的数据列分为特征列和目标列
    #目标列
    target_fields = ['cnt','casual', 'registered']
    features, targets = train_data.drop(target_fields, axis=1), train_data[target_fields]
    test_features, test_targets = test_data.drop(target_fields, axis=1), test_data[target_fields]
    # 将数据从pandas dataframe转换为numpy
    X = features.values
    Y = targets['cnt'].values
    Y = Y.astype(float)
    Y = np.reshape(Y, [len(Y),1])
    losses = []
    

    接下来可以调用强大的PyTorch现成函数,构建序列化的神经网络:
    定义神经网络架构,features.shape[1]个输入层单元,10个隐含层,1个输出层

    input_size = features.shape[1]
    hidden_size = 10
    output_size = 1
    batch_size = 128
    
    neu = torch.nn.Sequential(
        torch.nn.Linear(input_size, hidden_size),
        torch.nn.Sigmoid(),
        torch.nn.Linear(hidden_size, output_size),
    )
    cost = torch.nn.MSELoss()
    optimizer = torch.optim.SGD(neu.parameters(), lr = 0.01)
    
    # 神经网络训练循环
    
    losses = []
    for i in range(1000):
        # 每128个样本点被划分为一个撮,在循环的时候一批一批地读取
        batch_loss = []
        # start和end分别是提取一个batch数据的起始和终止下标
        for start in range(0, len(X), batch_size):
            end = start + batch_size if start + batch_size < len(X) else len(X)
            xx = Variable(torch.FloatTensor(X[start:end]))
            yy = Variable(torch.FloatTensor(Y[start:end]))
            predict = neu(xx)
            loss = cost(predict, yy)
            zero_grad()
            loss.backward()
            optimizer_step(0.01)
            batch_loss.append(loss.data.numpy())
        
        # 每隔100步输出一下损失值(loss)
        if i % 100==0:
            losses.append(np.mean(batch_loss))
            print(i, np.mean(batch_loss))
    
    # 打印输出损失值
    plt.plot(np.arange(len(losses))*100,losses)
    plt.xlabel('epoch')
    plt.ylabel('MSE')
    
    image.png

    最后可以测试一下神经网路:

    # 用训练好的神经网络在测试集上进行预测
    
    targets = test_targets['cnt'] #读取测试集的cnt数值
    targets = targets.values.reshape([len(targets),1]) #将数据转换成合适的tensor形式
    targets = targets.astype(float) #保证数据为实数
    
    # 将属性和预测变量包裹在Variable型变量中
    x = Variable(torch.FloatTensor(test_features.values))
    
    y = Variable(torch.FloatTensor(targets))
    
    # 用神经网络进行预测
    predict = neu(x)
    predict = predict.data.numpy()
    
    # 将后21天的预测数据与真实数据画在一起并比较
    # 横坐标轴是不同的日期,纵坐标轴是预测或者真实数据的值
    
    fig, ax = plt.subplots(figsize = (10, 7))
    mean, std = scaled_features['cnt']
    ax.plot(predict * std + mean, label='Prediction')
    ax.plot(targets * std + mean, label='Data')
    ax.legend()
    ax.set_xlabel('Date-time')
    ax.set_ylabel('Counts')
    
    # 对横坐标轴进行标注
    dates = pd.to_datetime(rides.loc[test_data.index]['dteday'])
    dates = dates.apply(lambda d: d.strftime('%b %d'))
    ax.set_xticks(np.arange(len(dates))[12::24])
    _ = ax.set_xticklabels(dates[12::24], rotation=45)
    
    
    image.png

    预测结果基本还是靠谱的,当时观察显示在Dec24—Dec27之间出现了比较大的偏差,这是怎么回事呢?原来因为这是美国共享单车的数据,这几天是圣诞节,大家基本都呆在家里happy,没什么人骑共享单车,而神经元却把它当作普通天来对待,自然预测失败了。
    针对错误的预测,还可以去分析解剖神经元,不过那个东西太过复杂,大家可以自行搜索。

    接下来再说一说关于2元分类预测的问题,预测这一天的共享单车是超过均值还是小于均值。对此只需要对Neuc进行小小的更改,将其输出单元数量设置为2,并加上Sigmoid函数就可以了。

    # 重新构造用于分类的人工神经网络Neuc
    input_size = features.shape[1]
    hidden_size = 10
    output_size = 2  #  输出0类或1类
    batch_size = 128
    
    neuc = torch.nn.Sequential(
        torch.nn.Linear(input_size, hidden_size),
        torch.nn.Sigmoid(),
        torch.nn.Linear(hidden_size, output_size),
        torch.nn.Sigmoid(),
    )
    
    # 将损失函数定义为交叉熵
    cost = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(neuc.parameters(), lr = 0.1)
    
    Y_labels = Y > np.mean(Y)
    Y_labels = Y_labels.astype(int)
    Y_labels = Y_labels.reshape(-1)
    Y_labels
    
    # 定义一个专门计算分类错误率的函数,它的基本思想是,对于预测向predictions的每一行,
    # 取最大的那个元素的下标,与标签labels中的元素做比较
    
    def error_rate(predictions, labels):
        """计算预测错误率的函数,其中predictions是模型给出的一组预测结果,labels是数据之中的正确答案"""
        predictions = np.argmax(predictions, 1)
        return 100.0 - (
          100.0 *
          np.sum( predictions == labels) /
          predictions.shape[0])
    
    # 神经网络训练循环
    losses = []
    errors = []
    for i in range(4000):
        # 每128个样本点被划分为一个撮
        batch_loss = []
        batch_errors = []
        for start, end in zip(range(0, len(X), batch_size), range(batch_size, len(X)+1, batch_size)):
            xx = Variable(torch.FloatTensor(X[start:end]))
            yy = Variable(torch.LongTensor(Y_labels[start:end]))
            predict = neuc(xx)
            loss = cost(predict, yy)
            err = error_rate(predict.data.numpy(), yy.data.numpy())
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            batch_loss.append(loss.data.numpy())
            batch_errors.append(err)
        
        # 每隔100步输出一下损失值(loss)
        if i % 100==0:
            losses.append(np.mean(batch_loss))
            errors.append(np.mean(batch_errors))
            print(i, np.mean(batch_loss), np.mean(batch_errors))
    
    # 打印输出损失值
    plt.plot(np.arange(len(losses))*100,losses, label = 'Cross Entropy')
    plt.plot(np.arange(len(losses))*100, np.array(errors) / float(100), label = 'Error Rate')
    plt.xlabel('epoch')
    plt.ylabel('Cross Entropy/Error rates')
    plt.legend()
    
    # 读取测试数据
    targets = test_targets['cnt']
    targets = targets.values.reshape([len(targets), 1])
    Y_labels = targets > np.mean(Y)
    Y_labels = Y_labels.astype(int)
    Y_labels = Y_labels.reshape(-1)
    x = Variable(torch.FloatTensor(test_features.values))
    
    # 打印神经网络预测的错误率
    predict = neuc(x)
    print(error_rate(predict.data.numpy(), Y_labels))
    
    # 接下来,我们把预测正确的数据和错误的数据分别画出来,纵坐标分别是预测正确的概率和预测错误的概率
    prob = predict.data.numpy()
    rights = np.argmax(prob, 1) == Y_labels
    wrongs = np.argmax(prob, 1) != Y_labels
    right_labels = Y_labels[rights]
    wrong_labels = Y_labels[wrongs]
    probs = prob[rights, :]
    probs1 = prob[wrongs, :]
    rightness = [probs[i, right_labels[i]] for i in range(len(right_labels))]
    right_index = np.arange(len(targets))[rights]
    wrongness = [probs1[i, wrong_labels[i]] for i in range(len(wrong_labels))]
    wrong_index = np.arange(len(targets))[wrongs]
    fig, ax = plt.subplots(figsize = (8, 6))
    ax.plot(right_index, rightness, '.', label='Right')
    ax.plot(wrong_index, wrongness,'o',label='Wrong')
    ax.legend()
    plt.ylabel('Probabilities')
    dates = pd.to_datetime(rides.loc[test_features.index]['dteday'])
    dates = dates.apply(lambda d: d.strftime('%b %d'))
    ax.set_xticks(np.arange(len(dates))[12::24])
    _ = ax.set_xticklabels(dates[12::24], rotation=45)
    
    image.png

    蓝色部分是预测正确的部分,黄色部分是预测错误的部分,y轴对应的是预测正确或错误的概率。掌握了这部分,对其他的预测问题只要针对情况修改代码就可以啦。

    相关文章

      网友评论

        本文标题:Pytorch实现共享单车数量预测

        本文链接:https://www.haomeiwen.com/subject/bornpqtx.html