交通流量預(yù)測(cè)在智能交通(ITS)系統(tǒng)中占有重要地位兵拢,是實(shí)現(xiàn)交通誘導(dǎo)的前提翻斟。準(zhǔn)確實(shí)時(shí)的短時(shí)交通流預(yù)測(cè)有助于更好的分析路網(wǎng)交通狀況,對(duì)路網(wǎng)交通規(guī)劃和交通優(yōu)化控制有非常重要的作用说铃。隨著交通數(shù)據(jù)采集技術(shù)的不斷發(fā)展访惜,及時(shí)獲取路網(wǎng)中實(shí)時(shí)的交通數(shù)據(jù)已成為可能嘹履,大量的交通信息為基于深度學(xué)習(xí)的預(yù)測(cè)模型提供了數(shù)據(jù)保障。
基于神經(jīng)網(wǎng)絡(luò)的交通流預(yù)測(cè)的相關(guān)研究如下列論文所示债热。由于與我的研究方向相關(guān)砾嫉,在本文中我實(shí)現(xiàn)了基于SAEs、RNN的交通流量預(yù)測(cè)模型窒篱。
Paper:
Traffic Flow Prediction With Big Data: A Deep Learning Approach
Using LSTM and GRU neural network methods for traffic flow prediction
Github:https://github.com/xiaochus/TrafficFlowPrediction
環(huán)境
- Python 3.5
- Tensorflow-gpu 1.2.0
- Keras 2.1.3
- scikit-learn 0.18
數(shù)據(jù)處理
實(shí)驗(yàn)數(shù)據(jù)是從Caltrans Performance Measurement System (PeMS)獲取的焕刮。原始的流量數(shù)據(jù)是一個(gè)長(zhǎng)度為n的一維數(shù)據(jù)。我們首先使用訓(xùn)練集的數(shù)據(jù)實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)化對(duì)象scaler墙杯,然后使用scaler分別對(duì)訓(xùn)練集與測(cè)試集進(jìn)行標(biāo)準(zhǔn)化配并。
由于時(shí)序預(yù)測(cè)任務(wù)需要使用歷史數(shù)據(jù)對(duì)未來(lái)數(shù)據(jù)進(jìn)行預(yù)測(cè),我們使用時(shí)滯變量lags對(duì)數(shù)據(jù)進(jìn)行劃分高镐,最后獲得大小為(samples, lags)的數(shù)據(jù)集溉旋。
劃分好的數(shù)據(jù)集在排列順序上依舊帶有時(shí)序特性,雖然keras在訓(xùn)練時(shí)可以選擇對(duì)數(shù)據(jù)進(jìn)行混洗嫉髓,但是其執(zhí)行順序是先對(duì)val數(shù)據(jù)進(jìn)行采樣再進(jìn)行混洗观腊,采樣過(guò)程依舊是按照順序來(lái)的。因此我們事先使用np.random.shuffle對(duì)數(shù)據(jù)進(jìn)行混洗岩喷,打亂數(shù)據(jù)的順序恕沫。
def process_data(train, test, lags):
"""Process data
Reshape and split train\test data.
# Arguments
train: String, name of .csv train file.
test: String, name of .csv test file.
lags: integer, time lag.
# Returns
X_train: ndarray.
y_train: ndarray.
X_test: ndarray.
y_test: ndarray.
scaler: StandardScaler.
"""
attr = 'Lane 1 Flow (Veh/5 Minutes)'
df1 = pd.read_csv(train, encoding='utf-8').fillna(0)
df2 = pd.read_csv(test, encoding='utf-8').fillna(0)
# scaler = StandardScaler().fit(df1[attr].values)
scaler = MinMaxScaler(feature_range=(0, 1)).fit(df1[attr].values)
flow1 = scaler.transform(df1[attr].values)
flow2 = scaler.transform(df2[attr].values)
train, test = [], []
for i in range(lags, len(flow1)):
train.append(flow1[i - lags: i + 1])
for i in range(lags, len(flow2)):
test.append(flow2[i - lags: i + 1])
train = np.array(train)
test = np.array(test)
np.random.shuffle(train)
X_train = train[:, :-1]
y_train = train[:, -1]
X_test = test[:, :-1]
y_test = test[:, -1]
return X_train, y_train, X_test, y_test, scaler
模型
LSTM
2隱層LSTM網(wǎng)絡(luò)。
def get_lstm(units):
"""LSTM(Long Short-Term Memory)
Build LSTM Model.
# Arguments
units: List(int), number of input, output and hidden units.
# Returns
model: Model, nn model.
"""
model = Sequential()
model.add(LSTM(units[1], input_shape=(units[0], 1), return_sequences=True))
model.add(LSTM(units[2]))
model.add(Dropout(0.2))
model.add(Dense(units[3], activation='linear'))
return model
GRU
2隱層GRU網(wǎng)絡(luò)纱意。
def get_gru(units):
"""GRU(Gated Recurrent Unit)
Build GRU Model.
# Arguments
units: List(int), number of input, output and hidden units.
# Returns
model: Model, nn model.
"""
model = Sequential()
model.add(GRU(units[1], input_shape=(units[0], 1), return_sequences=True))
model.add(GRU(units[2]))
model.add(Dropout(0.2))
model.add(Dense(units[3], activation='linear'))
return model
SAEs
Auto-Encoders的原理是先通過(guò)一個(gè)encode層對(duì)輸入進(jìn)行編碼婶溯,這個(gè)編碼就是特征,然后利用encode乘第2層參數(shù)(也可以是encode層的參數(shù)的轉(zhuǎn)置乘特征并加偏執(zhí))偷霉,重構(gòu)(解碼)輸入迄委,然后用重構(gòu)的輸入和實(shí)際輸入的損失訓(xùn)練參數(shù)。
這里我們構(gòu)建了三個(gè)單獨(dú)的自動(dòng)編碼器类少,并按照相同的隱層結(jié)構(gòu)構(gòu)建了一個(gè)三層的SAEs叙身。
def _get_sae(inputs, hidden, output):
"""SAE(Auto-Encoders)
Build SAE Model.
# Arguments
inputs: Integer, number of input units.
hidden: Integer, number of hidden units.
output: Integer, number of output units.
# Returns
model: Model, nn model.
"""
model = Sequential()
model.add(Dense(hidden, input_dim=inputs, name='hidden'))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))
model.add(Dense(output))
return model
def get_saes(layers):
"""SAEs(Stacked Auto-Encoders)
Build SAEs Model.
# Arguments
layers: List(int), number of input, output and hidden units.
# Returns
models: List(Model), List of SAE and SAEs.
"""
sae1 = _get_sae(layers[0], layers[1], layers[-1])
sae2 = _get_sae(layers[1], layers[2], layers[-1])
sae3 = _get_sae(layers[2], layers[3], layers[-1])
saes = Sequential()
saes.add(Dense(layers[1], input_dim=layers[0], name='hidden1'))
saes.add(Activation('sigmoid'))
saes.add(Dense(layers[2], name='hidden2'))
saes.add(Activation('sigmoid'))
saes.add(Dense(layers[3], name='hidden3'))
saes.add(Activation('sigmoid'))
saes.add(Dropout(0.2))
saes.add(Dense(layers[4]))
models = [sae1, sae2, sae3, saes]
return models
訓(xùn)練
LSTM、GRU按照正常的RNN網(wǎng)絡(luò)進(jìn)行訓(xùn)練硫狞。使用train_model()函數(shù)訓(xùn)練信轿。
SAEs的訓(xùn)練過(guò)程:多個(gè)SAE分別訓(xùn)練,第一個(gè)SAE訓(xùn)練完之后残吩,其encode的輸出作為第二個(gè)SAE的輸入财忽,接著訓(xùn)練。最后訓(xùn)練完后泣侮,將所有SAE的中間隱層連接起來(lái)組成一個(gè)SAEs網(wǎng)絡(luò)即彪,使用之前的權(quán)值作為初始化權(quán)值,再對(duì)整個(gè)網(wǎng)絡(luò)進(jìn)行fine-tune活尊。使用train_seas()函數(shù)訓(xùn)練隶校。
使用RMSprop(lr=0.001, rho=0.9, epsilon=1e-06)作為優(yōu)化器漏益,batch_szie為256,lags為12(即時(shí)滯長(zhǎng)度為一個(gè)小時(shí))深胳。
def train_model(model, X_train, y_train, name, config):
"""train
train a single model.
# Arguments
model: Model, NN model to train.
X_train: ndarray(number, lags), Input data for train.
y_train: ndarray(number, ), result data for train.
name: String, name of model.
config: Dict, parameter for train.
"""
model.compile(loss="mse", optimizer="rmsprop", metrics=['mape'])
# early = EarlyStopping(monitor='val_loss', patience=30, verbose=0, mode='auto')
hist = model.fit(
X_train, y_train,
batch_size=config["batch"],
epochs=config["epochs"],
validation_split=0.05)
model.save('model/' + name + '.h5')
df = pd.DataFrame.from_dict(hist.history)
df.to_csv('model/' + name + ' loss.csv', encoding='utf-8', index=False)
def train_seas(models, X_train, y_train, name, config):
"""train
train the SAEs model.
# Arguments
models: List, list of SAE model.
X_train: ndarray(number, lags), Input data for train.
y_train: ndarray(number, ), result data for train.
name: String, name of model.
config: Dict, parameter for train.
"""
temp = X_train
# early = EarlyStopping(monitor='val_loss', patience=30, verbose=0, mode='auto')
for i in range(len(models) - 1):
if i > 0:
p = models[i - 1]
hidden_layer_model = Model(input=p.input,
output=p.get_layer('hidden').output)
temp = hidden_layer_model.predict(temp)
m = models[i]
m.compile(loss="mse", optimizer="rmsprop", metrics=['mape'])
m.fit(temp, y_train, batch_size=config["batch"],
epochs=config["epochs"],
validation_split=0.05)
models[i] = m
saes = models[-1]
for i in range(len(models) - 1):
weights = models[i].get_layer('hidden').get_weights()
saes.get_layer('hidden%d' % (i + 1)).set_weights(weights)
train_model(saes, X_train, y_train, name, config)
實(shí)驗(yàn)
評(píng)估
在這里使用MAE绰疤、MSE、RMSE稠屠、MAPE峦睡、R2、explained_variance_score幾個(gè)指標(biāo)對(duì)回歸預(yù)測(cè)結(jié)果進(jìn)行評(píng)估权埠。
def MAPE(y_true, y_pred):
"""Mean Absolute Percentage Error
Calculate the mape.
# Arguments
y_true: List/ndarray, ture data.
y_pred: List/ndarray, predicted data.
# Returns
mape: Double, result data for train.
"""
y = [x for x in y_true if x > 0]
y_pred = [y_pred[i] for i in range(len(y_true)) if y_true[i] > 0]
num = len(y_pred)
sums = 0
for i in range(num):
tmp = abs(y[i] - y_pred[i]) / y[i]
sums += tmp
mape = sums * (100 / num)
return mape
def eva_regress(y_true, y_pred):
"""Evaluation
evaluate the predicted resul.
# Arguments
y_true: List/ndarray, ture data.
y_pred: List/ndarray, predicted data.
"""
mape = MAPE(y_true, y_pred)
vs = metrics.explained_variance_score(y_true, y_pred)
mae = metrics.mean_absolute_error(y_true, y_pred)
mse = metrics.mean_squared_error(y_true, y_pred)
r2 = metrics.r2_score(y_true, y_pred)
print('explained_variance_score:%f' % vs)
print('mape:%f%%' % mape)
print('mae:%f' % mae)
print('mse:%f' % mse)
print('rmse:%f' % math.sqrt(mse))
print('r2:%f' % r2)
預(yù)測(cè)
我們使用訓(xùn)練好的模型對(duì)測(cè)試集進(jìn)行預(yù)測(cè)榨了。
def main():
lstm = load_model('model/lstm.h5')
gru = load_model('model/gru.h5')
saes = load_model('model/saes.h5')
models = [lstm, gru, saes]
names = ['LSTM', 'GRU', 'SAEs']
lag = 12
file1 = 'data/train.csv'
file2 = 'data/test.csv'
_, _, X_test, y_test, scaler = process_data(file1, file2, lag)
y_test = scaler.inverse_transform(y_test)
y_preds = []
for name, model in zip(names, models):
if name == 'SAEs':
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1]))
else:
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
file = 'images/' + name + '.png'
plot_model(model, to_file=file, show_shapes=True)
predicted = model.predict(X_test)
predicted = scaler.inverse_transform(predicted)
y_preds.append(predicted[:300])
print(name)
eva_regress(y_test, predicted)
plot_results(y_test[: 300], y_preds, names)
預(yù)測(cè)精度對(duì)比如下所示:
Metrics | MAE | MSE | RMSE | MAPE | R2 | Explained variance score |
---|---|---|---|---|---|---|
LSTM | 7.16 | 94.20 | 9.71 | 21.25% | 0.9420 | 0.9421 |
GRU | 7.18 | 95.01 | 9.75 | 17.42% | 0.9415 | 0.9415 |
SAEs | 7.71 | 106.46 | 10.32 | 25.62% | 0.9344 | 0.9352 |
預(yù)測(cè)結(jié)果對(duì)比如下所示: