基于 LSTM 的船舶轨迹预测,单步预测

1、工具包

import numpy as np
import tensorflow as tf
import pandas as pd
import matplotlib.pyplot as plt
from keras.layers.core import Dense, Activation, Dropout
from keras.layers import LSTM
from keras.models import Sequential, load_model
from keras.callbacks import CSVLogger, ReduceLROnPlateau
from keras.optimizers import adam_v2
import transbigdata as tbd # 轨迹处理包,此处用于显示底图
import warnings

warnings.filterwarnings("ignore")
#设置随机种子,让每次结果都一样,方便对照
np.random.seed(120)
tf.random.set_seed(120)
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

2、读取数据集

下载地址:
(测试数据)https://download.csdn.net/download/weixin_43261465/87251665
(训练数据)https://download.csdn.net/download/weixin_43261465/87251645

# 读入轨迹序列数据
data = pd.read_csv('./train.csv',index_col=0).iloc[:, 0:3]
data.head()

基于 LSTM 的船舶轨迹预测,单步预测_第1张图片
绘制轨迹

# 经纬度绘制范围
bound = [min(data['lon'])-0.005, min(data['lat'])-0.005,
         max(data['lon'])+0.005, max(data['lat'])+0.005]
fig = plt.figure(figsize=(20, 15))
# 绘制武汉市底图
tbd.plot_map(plt,bound, zoom=12, style=0)
for tri in set(data['tri_id']):
    temp = data.loc[data.tri_id == tri]
    # 绘制单个轨迹
    plt.plot(temp['lon'],temp['lat'],'-b')
plt.show()

3、提取特征和标签数据

每连续的 20 个位置为一组,预测下一个位置

def create_dataset(data, train_num):
	"""
    :param data:  		轨迹数据集合
    :param train_num: 	多少个数据一组
    :return: 	  		数据、标签、用来反归一化的最大最小值
    """
    train_seq = []
    train_label = []

    nor = np.array(data.iloc[:,1:3])
    m = nor.max(axis=0)
    n = nor.min(axis=0)
    
	# 对轨迹一条一条的进行处理
    for tri_id in set(data['tri_id']):
        data_temp = data.loc[data.tri_id == tri_id]
        # 得到经度、纬度
        data_temp = np.array(data_temp.iloc[:,1:3])
        # 标准化
        data_temp = (data_temp - n) / (m - n)

        for i in range(data_temp.shape[0] - train_num):
            x = []
            for j in range(i, i + train_num):
                x.append(list(data_temp[j,:]))
            train_seq.append(x)
            train_label.append(data_temp[i + train_num,:])

    train_seq = np.array(train_seq, dtype='float64')
    train_label = np.array(train_label, dtype='float64')

    return train_seq,train_label,[m,n]

4、构建模型

def trainModel(train_X, train_Y):
    model = Sequential()
    model.add(LSTM(120,input_shape=(train_X.shape[1], train_X.shape[2]),return_sequences=True))
    model.add(Dropout(0.3))
    model.add(LSTM(120,return_sequences=False))
    model.add(Dropout(0.3))
    model.add(Dense(train_Y.shape[1]))
    model.add(Activation("relu"))
    adam = adam_v2.Adam(learning_rate=0.001)

    model.compile(loss='mse', optimizer=adam, metrics=['acc'])
    # 训练日志
    log = CSVLogger("./csvlog_lon_lat_one_step.csv",separator=",",append=True)
    reduce = ReduceLROnPlateau(monitor='val_acc', factor=0.1, patience=1, verbose=1,
                               mode='auto', epsilon=0.0001, cooldown=0, min_lr=0.0000001)
    model.fit(train_X, train_Y, epochs=20, batch_size=64, verbose=1,validation_split=0.1,callbacks=[log,reduce])
    # 打印神经网络结构,统计参数数目
    model.summary()

    return model

5、训练

# 20 个为一组
train_num = 20
#生成训练数据
train_seq,train_label,max_min = create_dataset(data, train_num)
print("data ", data.shape)
print("train_seq ", train_seq.shape)
print("train_label ", train_label.shape)

# 训练模型
model = trainModel(train_seq, train_label)
loss, acc = model.evaluate(train_seq, train_label, verbose=1)
print('Loss : {}, Accuracy: {}'.format(loss, acc * 100))

# 保存模型
model.save("./traj_mode_wuhan_lon_lat_one_step_20.h5")

6、损失函数和准确率的变化

logs = pd.read_csv("./csvlog_lon_lat_one_step.csv")

fig, ax = plt.subplots(2,2,figsize=(15,12))
ax[0][0].plot(logs['epoch'],logs['acc'], label='acc')
ax[0][0].set_title('acc')

ax[0][1].plot(logs['epoch'],logs['loss'], label='loss')
ax[0][1].set_title('loss')

ax[1][0].plot(logs['epoch'],logs['val_acc'], label='val_acc')
ax[1][0].set_title('val_acc')

ax[1][1].plot(logs['epoch'],logs['val_loss'], label='val_loss')
ax[1][1].set_title('val_loss')

plt.show()

基于 LSTM 的船舶轨迹预测,单步预测_第2张图片

7、预测

读取测试数据

data_all = pd.read_csv('./test.csv',index_col=0).iloc[:, 0:3]
li = list(set(data_all['tri_id']))
test_data = data_all.loc[data_all.tri_id == li[0]]
test_data.head()

基于 LSTM 的船舶轨迹预测,单步预测_第3张图片
得到预测数据和真实数据

test_seq,test_label,_ = create_dataset(test_data,train_num)
model = load_model("./traj_mode_wuhan_lon_lat_one_step_20.h5")
y_pre = []
y_true = test_label
for i in range(len(test_seq)):
    y_hat = model.predict(test_seq[i].reshape(1, train_num, 2))
    y_pre.append(y_hat[0])
y_pre = np.array(y_pre, dtype='float64')

数据反归一化

#反归一化
def FNormalizeMult(y_pre,y_true,max_min):
    [m1,n1],[m2,n2] = max_min
    y_pre[:,0] = y_pre[:,0] * (m1 - m2) + m2
    y_pre[:,1] = y_pre[:,1] * (n1 - n2) + n2
    y_true[:,0] = y_true[:,0] * (m1 - m2) + m2
    y_true[:,1] = y_true[:,1] * (n1 - n2) + n2
    # 计算距离
    y_pre = np.insert(y_pre, y_pre.shape[1],
                      get_distance_hav(y_true[:,1],y_true[:,0],y_pre[:,1],y_pre[:,0]), axis=1)

    return y_pre,y_true


# 计算两个经纬度之间的直线距离
def hav(theta):
    s = np.sin(theta / 2)
    return s * s
def get_distance_hav(lat0, lng0, lat1, lng1):
    EARTH_RADIUS = 6371
    # "用haversine公式计算球面两点间的距离。"
    # 经纬度转换成弧度
    lat0 = np.radians(lat0)
    lat1 = np.radians(lat1)
    lng0 = np.radians(lng0)
    lng1 = np.radians(lng1)

    dlng = np.fabs(lng0 - lng1)
    dlat = np.fabs(lat0 - lat1)
    h = hav(dlat) + np.cos(lat0) * np.cos(lat1) * hav(dlng)
    distance = 2 * EARTH_RADIUS * np.arcsin(np.sqrt(h))
    return distance


#反归一化
f_y_pre,f_y_true = FNormalizeMult(y_pre,y_true,max_min)

第三列为预测值和真实值的距离差

f_y_pre[0:3]

基于 LSTM 的船舶轨迹预测,单步预测_第4张图片

8、绘制显示真实轨迹和预测轨迹

# 预测轨迹与真实轨迹
plt.subplots(figsize=(20, 12))
plt.plot(f_y_pre[:, 0], f_y_pre[:, 1], "b-", label='预测值')
plt.plot(f_y_true[:, 0], f_y_true[:, 1], "r-", label='真实值')
plt.legend(loc='upper left',fontsize=15)
plt.grid()
plt.show()

基于 LSTM 的船舶轨迹预测,单步预测_第5张图片

print(f"最大值: {max(f_y_pre[:,2])}\n最小值: {min(f_y_pre[:,2])}\n均值: {np.mean(f_y_pre[:,2])}\n"
      f"方差: {np.var(f_y_pre[:,2])}\n标准差: {np.std(f_y_pre[:,2])}\n中位数: {np.median(f_y_pre[:,2])}")

基于 LSTM 的船舶轨迹预测,单步预测_第6张图片
显示在地图上

bound = [min(f_y_pre[:, 0])-0.005, min(f_y_pre[:, 1])-0.005,
         max(f_y_pre[:, 0])+0.005, max(f_y_pre[:, 1])+0.005]
fig = plt.figure(figsize=(20, 15))
ax1 =plt.subplot(111)
tbd.plot_map(plt,bound, zoom=12, style=0)
ax1.plot(f_y_pre[:, 0], f_y_pre[:, 1], "b-", label='预测值')
ax1.plot(f_y_true[:, 0], f_y_true[:, 1], "r-", label='真实值')
ax1.set_xlabel('lon', fontsize=20)
ax1.set_ylabel('lat', fontsize=20)
ax1.legend(loc='upper left',fontsize=20)
ax1.grid(True)

ax2 = ax1.twinx()
ax2.plot(f_y_true[:, 0], f_y_pre[:,2],"p-", label='差值')
ax2.set_ylabel('经纬度相差距离 km', fontsize=20)

plt.title(f"预测值与真实值", fontsize=20)
plt.show()

基于 LSTM 的船舶轨迹预测,单步预测_第7张图片

你可能感兴趣的:(lstm,python)