三、算法及解决方案
1. 导入数据
将四天的训练集数据放在一个表中,表的格式为 csv 文件,使用 pandas 包中的 read_csv 方
法将 csv 文件读入,并标记每一列的属性。
import pandas as pd
train=pd.read_csv("F:\\mydocument\\py\\all2.csv",header=None,sep=',',names=["date","time","
direction","type","linkID","length","travelTime","volumn","speed","occupancy","congestionLevel
","null"],encoding='GB2312')
读入后的数据如图 3.1 所示:
图 3.1 训练集原始数据
2. 数据预处理
(1)将传感器的型号进行编码,该问题中一共有 855 个传感器,将传感器的型号用 0 到 854
表示,使用 sklearn 包中的 LabelEncoder
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.pipeline import Pipeline
class MultiColumnLabelEncoder:
def __init__(self,columns = None):
self.columns = columns # array of column names to encode
def fit(self,X,y=None):
return self # not relevant here
def transform(self,X):
output = X.copy()
if self.columns is not None:
for col in self.columns:
output[col] = LabelEncoder().fit_transform(output[col])
else:
for colname,col in output.iteritems():
output[colname] = LabelEncoder().fit_transform(col)
return output
def fit_transform(self,X,y=None):
return self.fit(X,y).transform
train = train.fillna('')
train[['linkID']]=train[['linkID']].apply(LabelEncoder().fit_transform)
传感器编码后的结果如图 3.2 所示:
图 3.2 传感器型号编码后的数据
从图 3.2 中,可以看到 linkID 这一列由原来的 WI-MNT_XML_V001-5006 编码成了相对应
的数值。
( 2 ) 将 拥 堵 程 度 : NON_CONGESTION 、 LIGHT_CONGESTION 、 MEDIUM_CONGESTION 、
HEAVY_CONGESTION、UNKNOWN_CONGESTION_LEVEL 分别映射成 0、1、2、3、4,使用 map
方法,便于将数据放入模型中。
size_mapping = {
'NON_CONGESTION':0,
'LIGHT_CONGESTION':1,
'MEDIUM_CONGESTION':2,
'HEAVY_CONGESTION':3,
'UNKNOWN_CONGESTION_LEVEL':4}
train['congestionLevel'] = train['congestionLevel'].map(size_mapping)
拥堵程度映射后的结果如图 3.3 所示:
图 3.3 拥堵程度映射后的数据
从图 3.3 中可以看出:congestionLevel 这一列已由原来的 NON_CONGESTION 映射成了相
应的数值。
(3)删掉日常生活中不符合常理的数据,删去车辆行程时间为负值的数据、车速为负值的
数据、道路的占有率为负值的数据以及拥堵状况未知的数据,由于题目所提供的数据量较多,
删掉这些数据不会影响训练出的模型的准确度。
print(train.shape)
train=train[~(train['travelTime']<=0)]
train=train[~(train['speed']<0)]
train=train[~(train['occupancy']<0)]
train=train[~(train['occupancy']==4)]
print(train.shape)
Train
数据清洗后,如图 3.4 所示:
从图 3.4 的运行结果中可以看到:数据已由原来的 984958 条删减到了 605935 条数据。
图 3.4 清洗后的数据
(4)特征选择
针对每一个传感器而言,由于传感器的位置是不变的,所以数据中方位(direction)、
道路类型(type)、道路长度(length)都是固定不变的,所以可以将其删去,这些特征不
影响模型的准确度;由于车速(speed)、道路的占有率(occupancy)和道路的拥堵程度
(congestionLevel null)直接相关,拥堵程度高相应的车速会慢、道路占有率会高。所以,
针对该问题,我们只选取时间和拥堵程度作为特征,进行模型的训练。
特征选择后的结果如图 3.5 所示:
图 3.5 特征选择后的数据
(5)时间处理
该问题是时间序列问题,需要把原始数据中的日期和时间这两列转化成模型支持的 datetime
类型,具体处理了方式如下:
train['date'] = train['date'].map(lambda x:'date'+x if len(x)==3 else x)
train['time'] = train['time'].map(lambda x:'date'*(4-len(x))+x if len(x)<4 else x)
train['date_time'] = train['date'] + train['time']
def format_date(s):
month = int(s[:2])
day = int(s[2:4])
h = int(s[4:6])
m = int(s[6:])
return datetime.datetime(2018, month, day, h , m)
train['date_time'] = train['date_time'].map(format_date)
处理后得到的数据如图 3.6 所示:
(6)将数据可视化:以其中的一个传感器为例,画出训练集中交通拥堵程度随时间变化的
图,如图 3.7 和图 3.8 所示,这样能更直观的看出一天中交通拥堵程度变化的规律。
图 3.6 时间处理后的数据
图 3.7 交通拥堵程度变化图
图 3.8 交通拥堵程度变化图
3. 建立 LSTM 模型
(1)考虑到真实的训练环境,把每批次训练样本数(batch_size)、时间步(time_step)、
训练集的数量(train_begin,train_end)设定为参数,使得训练更加高效。为神经网络模型设
定初始值,定义输入层维度为 1,输出层维度为 1,隐藏层的层数为 60,模型的学习率为
0.0006,时间步定义为 20,每次训练 60 个样例。
time_step=20
rnn_unit=10
batch_size=60
input_size=1
output_size=1
lr=0.0006
train_x,train_y=[],[]
for i in range(len(normalize_data)-time_step-1):
#时间步
#隐藏层的个数
#每一批次训练多少个样例
#输入层维度
#输出层维度
#学习率
#训练集
x=normalize_data[i:i+time_step]
y=normalize_data[i+1:i+time_step+1]
train_x.append(x.tolist())
train_y.append(y.tolist())
(2)定义神经网络变量的输入层、输出层的权重和偏倚:
weights={
'in':tf.Variable(tf.random_normal([input_size,rnn_unit])),
'out':tf.Variable(tf.random_normal([rnn_unit,1]))
}
biases={
'in':tf.Variable(tf.constant(0.1,shape=[rnn_unit,])),
'out':tf.Variable(tf.constant(0.1,shape=[1,]))
}
(3)定义神经网络变量:传入的参数为输入网络批次数目,传入模型中的数据需要转成 2
维进行计算,计算后的结果作为隐藏层的输入,隐藏层中的数据需要转成 3 维,作为 lstm cell
的输入,output_rnn 是记录 lstm 每个输出节点的结果,final_states 是最后一个 cell 的结果,
output 作为输出层的输入。
def lstm(batch):
w_in=weights['in']
b_in=biases['in']
input=tf.reshape(X,[-1,input_size])
input_rnn=tf.matmul(input,w_in)+b_in
input_rnn=tf.reshape(input_rnn,[-1,time_step,rnn_unit])
cell=tf.nn.rnn_cell.BasicLSTMCell(rnn_unit)
init_state=cell.zero_state(batch,dtype=tf.float32)
output_rnn,final_states=tf.nn.dynamic_rnn(cell,input_rnn,initial_state=init_state,dtype=tf.fl
oat32)
output=tf.reshape(output_rnn,[-1,rnn_unit])
w_out=weights['out']
b_out=biases['out']
pred=tf.matmul(output,w_out)+b_out
return pred,final_states
(7)训练模型:训练过程中,模型重复训练 10000 次,训练好后将模型保存为 travel.model
def train_lstm():
global batch_size
pred,_=lstm(batch_size)
#损失函数
loss=tf.reduce_mean(tf.square(tf.reshape(pred,[-1])-tf.reshape(Y, [-1])))
train_op=tf.train.AdamOptimizer(lr).minimize(loss)
saver=tf.train.Saver(tf.global_variables())
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
#重复训练 10000 次
for i in range(10000):
step=0
start=0
end=start+batch_size
while(end
if step%10==0:
print(i,step,loss_)
print("保存模型:",saver.save(sess,'travel.model'))
step+=1
(8)预测模型:预测时只需输入[1,time_step,input_size]的测试数据
def prediction():
pred,_=lstm(1)
saver=tf.train.Saver(tf.global_variables())
with tf.Session() as sess:
#参数恢复
module_file = tf.train.latest_checkpoint(base_path+'module2/')
saver.restore(sess, module_file)
#取训练集最后一行为测试样本。shape=[1,time_step,input_size]
prev_seq=train_x[-1]
predict=[]
#得到之后 100 个预测结果
for i in range(100):
试样本
next_seq=sess.run(pred,feed_dict={X:[prev_seq]})
predict.append(next_seq[-1])
#每次得到最后一个时间步的预测结果,与之前的数据加在一起,形成新的测
prev_seq=np.vstack((prev_seq[1:],next_seq[-1]))
#以折线图表示结果
plt.figure()
plt.plot(list(range(len(normalize_data))), normalize_data, color='b')
plt.plot(list(range(len(normalize_data),
len(normalize_data) + len(predict))), predict,
color='r')
4.使用
plt.show()