之前分享过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()
#我们取出最后一列的前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')
可以复习一下之前学的内容,用线性回归做一下,可以想见效果一定很差~~
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()
线性回归失败了,因为根本不是线性关系!
接下来用人工神经网络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')
最后可以测试一下神经网路:
# 用训练好的神经网络在测试集上进行预测
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)
预测结果基本还是靠谱的,当时观察显示在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)
蓝色部分是预测正确的部分,黄色部分是预测错误的部分,y轴对应的是预测正确或错误的概率。掌握了这部分,对其他的预测问题只要针对情况修改代码就可以啦。