哈喽,我是小白~
今儿和大家聊聊LSTM在时间序列预测方面的应用。
如果需要本文PDF版本的同学,文末获取~
另外,文末有总结性的干货~
一起来看下具体细化内容~
LSTM 是什么?
LSTM(Long Short-Term Memory,长短期记忆)是一种时间序列算法,常用于处理和预测基于时间的序列数据,比如天气预测、股价变化、自然语言处理等。它是递归神经网络(RNN)的一种特殊类型,能够记住长期依赖关系,解决了传统 RNN 在处理长序列时容易出现“长期依赖问题”的缺陷。
简单来说,传统 RNN 在处理长时间序列时,由于信息逐渐丢失,无法有效记住较早的数据。而 LSTM 引入了“记忆单元”和“门机制”,可以控制哪些信息保留、哪些信息遗忘,因此能更好地应对长时间间隔的数据关系。
LSTM 工作原理
LSTM 的核心在于“门机制”,主要包括三个重要的门:
1. 遗忘门(Forget Gate): 决定哪些信息需要遗忘。
2. 输入门(Input Gate): 决定哪些新信息需要存储到记忆中。
3. 输出门(Output Gate): 决定哪些信息要输出到下一时刻。
这些门会根据当前输入和之前的隐藏状态(即记忆)来进行计算。
一个简单案例:预测每天的温度
假设你想根据前几天的温度来预测今天的温度。假设过去 7 天的温度如下:
日期 | 温度(°C) |
---|---|
第一天 | 20 |
第二天 | 21 |
第三天 | 19 |
第四天 | 18 |
第五天 | 22 |
第六天 | 23 |
第七天 | 21 |
你想通过这些天的温度,预测第八天的温度。
LSTM 就像是一个有记忆的人,它不仅记得最近几天的温度,还能根据之前的信息做出相应的调整,决定哪些温度数据重要,哪些可以忽略。
遗忘门: 检查过去 7 天的温度,可能决定“忽略”不重要的温度变化(例如波动不大的天数)。
输入门: 决定从新的温度数据中,选择哪些重要信息存入记忆单元。
输出门: 最后根据遗忘和输入门的结果,输出预测结果,即第八天的温度。
LSTM 计算示例
为了简单说明计算过程,我们简化 LSTM 的门结构,假设只有两个输入(温度序列和前一天的预测)和一个隐藏层,LSTM 的门公式如下:
1. 遗忘门:
这里的 是遗忘门的输出,决定了上一个时刻的记忆有多少部分需要保留。 是 sigmoid 激活函数,其输出在 0 到 1 之间。
2. 输入门:
输入门决定当前输入 的哪些部分需要加入到记忆中。
3. 记忆更新:
这个公式更新新的候选记忆 ,使用的是 tanh 激活函数,其输出范围在 -1 到 1 之间。
4. 当前记忆:
当前时刻的记忆是上一时刻记忆的部分保留,再加上本时刻新的记忆。
5. 输出门:
输出门控制将记忆单元中哪些部分作为输出。
6. 最终输出:
最终的输出 是当前记忆经过 tanh 激活函数后,乘上输出门的结果。
具体例子
假设我们现在有 7 天的温度序列(如上表),并希望预测第 8 天的温度。我们手动进行一次计算简化后的 LSTM 操作。
假设初始参数为:
, , , 初始记忆 , 初始隐藏状态 假设偏置
1. 以第 7 天的温度为输入(21°C)
当前输入 ,上一时刻的输出为 (假设初始输出为 0)
遗忘门:
遗忘门几乎为 1,表示我们保留之前的记忆。
输入门:
输入门也接近 1,表示当前输入的重要性很大。
记忆更新:
候选记忆几乎为 1。
当前记忆:
输出门:
最终输出:
根据这个简单的计算过程,LSTM 输出了一个值约为 0.76,这个值可以被用于下一步预测。
公式解析
1. 变量定义
首先,我们定义 LSTM 的一些关键变量:
输入向量: 表示在时间步 的输入数据(如某时刻的温度、股价等)。
隐藏状态: 表示时间步 的隐藏状态,也可以看作是 LSTM 的输出,用来传递到下一时间步。
记忆单元: 表示 LSTM 在时间步 的记忆内容。
遗忘门: ,表示在时间步 遗忘多少之前的记忆。
输入门: ,决定当前的输入有多少需要存储到记忆单元中。
输出门: ,决定当前的隐藏状态和记忆单元有多少内容输出。
此外,LSTM 中有一系列权重矩阵和偏置项:
权重矩阵:
(遗忘门)、(输入门)、(输出门)、(候选记忆) 这些矩阵作用于当前输入 和上一个隐藏状态
偏置项:
, , , 是各个门和记忆单元对应的偏置项。
2. LSTM 公式推导
遗忘门(Forget Gate)
遗忘门决定哪些记忆内容应该被保留,哪些应该被丢弃。它通过输入当前时刻的输入 和上一时刻的隐藏状态 来计算,最终输出值介于 0 到 1 之间。
遗忘门的公式为:
是 sigmoid 激活函数,其输出为 0 到 1 之间的值。接近 0 时表示遗忘,接近 1 时表示保留。 是遗忘门的权重矩阵, 是遗忘门的偏置。 表示将隐藏状态 和输入 拼接在一起形成新的输入。
输入门(Input Gate)
输入门决定哪些当前的输入信息要被存入记忆单元。同样,输入门也依赖当前输入 和上一时刻的隐藏状态 。
输入门的公式为:
是输入门的权重矩阵, 是输入门的偏置。 的值控制当前输入信息存入多少到记忆单元中,0 表示完全忽略,1 表示完全保留。
候选记忆单元(Cell Candidate)
接下来,我们要生成候选记忆单元,也就是当前时刻的潜在记忆更新,它是通过输入门和输入数据的一个候选更新值,决定了可以加入到记忆单元中的信息。
候选记忆的公式为:
是双曲正切函数,其输出范围是 到 ,用于控制候选记忆单元的输出范围。 是候选记忆单元的权重矩阵, 是其偏置。
更新记忆单元(Cell State)
记忆单元 的更新是通过遗忘上一个时刻的一部分记忆,再结合当前时刻新输入的信息。
记忆单元的更新公式为:
表示用遗忘门控制上一时刻的记忆 保留多少。 表示用输入门控制当前时刻的候选记忆 要更新多少。
输出门(Output Gate)
输出门决定了记忆单元中的哪些部分要作为当前时刻的输出,也就是 LSTM 的隐藏状态。
输出门的公式为:
是输出门的权重矩阵, 是其偏置。 的值决定了当前时刻哪些信息需要输出。
最终输出(Hidden State)
最后,当前时刻的隐藏状态 通过输出门和当前的记忆单元计算得到。首先对当前记忆单元 使用 函数,之后与输出门 相乘,得到隐藏状态 。
隐藏状态的公式为:
将记忆单元的值限制在 到 之间。 决定了从记忆中输出的部分。
3. 完整 LSTM 工作流程
综上所述,LSTM 的完整工作流程可以描述为:
1. 遗忘门计算:
计算 ,决定遗忘多少上一个时间步的记忆。
2. 输入门计算:
计算 ,决定输入多少当前时刻的信息。 计算候选记忆 $ \tilde{C}t = \tanh(W_C \cdot [h{t-1}, x_t] + b_C) $,生成当前时刻的潜在记忆。
3. 更新记忆单元:
通过 更新当前记忆单元。
4. 输出门计算:
计算 ,决定输出多少信息。
5. 最终输出:
通过 计算当前时刻的隐藏状态,即 LSTM 的最终输出。
LSTM 的关键在于它的“门机制”,通过遗忘门、输入门和输出门对记忆的管理,能够有效解决长时间序列中信息丢失的问题。
优缺点和适用场景
优点
1. 解决长期依赖问题:LSTM 通过引入遗忘门、输入门、输出门,能够有效保留或遗忘历史信息,避免了传统 RNN(递归神经网络)中梯度消失或梯度爆炸的问题,从而擅长处理长时间跨度的依赖关系。
2. 记忆能力强:LSTM 通过其内在的记忆单元可以保存信息更长时间,适用于需要长时间记忆的时间序列任务。
3. 灵活性高:LSTM 能够灵活地通过门控机制选择哪些信息应该保留,哪些信息应该忽略,因此在非线性、复杂的时间序列数据上表现良好。
4. 较好的泛化能力:由于可以学习到更复杂的时间依赖结构,LSTM 通常在训练数据和测试数据上表现出较好的泛化能力。
缺点
1. 计算复杂度高: LSTM 结构复杂,包含多个门控机制,参数较多,因此计算量大,训练时间较长,尤其在面对大规模数据时效率较低。
2. 难以解释:虽然 LSTM 在捕捉时间序列中的长短期依赖关系上表现良好,但它内部的门控机制较为复杂,难以解释为什么网络会做出特定的决策。
3. 对长时间序列的捕捉仍然有限: 虽然 LSTM 比传统 RNN 更好地处理长时间依赖问题,但在非常长的序列(如成千上万步的序列)上依然可能失效。
4. 需要大量数据:LSTM 需要大量标记数据才能训练出高质量的模型,数据量不足时可能导致模型过拟合或表现不佳。
适用场景
LSTM 主要适用于处理时间依赖性强、数据序列较长的任务。
1. 时间序列预测: LSTM 常用于处理如股票价格、天气、商品需求量等时间序列数据,能够有效预测未来的趋势。
2. 自然语言处理(NLP):LSTM 广泛应用于自然语言处理任务,尤其是处理长文本、翻译、问答等需要捕捉上下文信息的任务。
3. 语音识别:语音信号是典型的时间序列数据,LSTM 在语音识别中可以有效提取信号中的时序特征。
4. 视频分析: 视频可以看作是一系列连续帧的时间序列数据,LSTM 能够在视频分析中捕捉帧间的动态变化,进行行为识别、事件检测等。
完整案例
使用LSTM对股票价格的时间序列数据进行预测,这里假设使用的是一个股票市场的开盘价格数据。
仅仅作为算法学习使用,数据集为虚拟数据集。
代码会涉及数据预处理、LSTM模型构建、训练、以及数据可视化。可视化部分会使用Matplotlib和Seaborn等工具展示。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
# 1. 生成模拟的股票价格数据
np.random.seed(42)
dates = pd.date_range('2020-01-01', periods=1000)
prices = np.sin(np.linspace(0, 20, 1000)) * 100 + np.random.normal(0, 5, 1000) + 100
# 创建 DataFrame
data = pd.DataFrame(data={'Date': dates, 'Open': prices})
data.set_index('Date', inplace=True)
# 可视化股票价格的初始走势
plt.figure(figsize=(12, 6))
sns.lineplot(x=data.index, y=data['Open'], color='blue', lw=2)
plt.title('Stock Open Prices Over Time', fontsize=16)
plt.xlabel('Date')
plt.ylabel('Open Price')
plt.grid(True)
plt.show()
# 2. 数据预处理 - 归一化处理
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data[['Open']])
# 创建时间序列数据集
def create_dataset(dataset, time_step=60):
X, y = [], []
for i in range(len(dataset)-time_step-1):
X.append(dataset[i:(i+time_step), 0])
y.append(dataset[i + time_step, 0])
return np.array(X), np.array(y)
# 生成训练集
time_step = 60
X, y = create_dataset(scaled_data, time_step)
# Reshape输入为LSTM模型的格式 [样本数, 时间步长, 特征数]
X = X.reshape(X.shape[0], X.shape[1], 1)
# 3. 创建LSTM模型
model = Sequential()
model.add(LSTM(units=100, return_sequences=True, input_shape=(time_step, 1)))
model.add(Dropout(0.2))
model.add(LSTM(units=100, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(units=1)) # 输出层
model.compile(optimizer='adam', loss='mean_squared_error')
# 4. 训练模型
model.fit(X, y, epochs=10, batch_size=32, verbose=1)
# 5. 预测
train_predict = model.predict(X)
# 将预测值反归一化
train_predict = scaler.inverse_transform(train_predict)
# 创建一个新的DataFrame用于展示
train_data = data.iloc[time_step+1:]
train_data['Predicted'] = train_predict
# 6. 可视化真实值和预测值
plt.figure(figsize=(14, 7))
plt.plot(train_data.index, train_data['Open'], label='True Open Prices', color='blue', lw=2)
plt.plot(train_data.index, train_data['Predicted'], label='Predicted Open Prices', color='orange', lw=2)
plt.fill_between(train_data.index, train_data['Open'], train_data['Predicted'], color='lightgreen', alpha=0.5)
plt.title('True vs Predicted Stock Prices', fontsize=18)
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend(loc='best')
plt.grid(True)
plt.show()
# 7. 复杂可视化: 热力图分析误差
error = train_data['Open'] - train_data['Predicted']
plt.figure(figsize=(12, 6))
sns.heatmap(np.reshape(error.values, (10, 94)), cmap="YlGnBu", annot=False, cbar=True)
plt.title('Error Heatmap between True and Predicted Prices', fontsize=16)
plt.show()
# 8. 复杂可视化: 预测值与真实值的差异柱状图
plt.figure(figsize=(14, 6))
sns.barplot(x=train_data.index[::10], y=(train_data['Open'] - train_data['Predicted'])[::10], palette='coolwarm')
plt.xticks(rotation=45)
plt.title('Differences between True and Predicted Prices', fontsize=18)
plt.ylabel('Difference')
plt.grid(True)
plt.show()
代码说明:
1. 生成模拟的股票价格数据:这里通过 sin
函数加噪声生成股票的开盘价格数据。
2. 数据预处理:使用MinMaxScaler
对数据进行归一化,将股票价格数据压缩到0-1之间;然后创建时间序列数据集。
3. 构建LSTM模型:使用两层LSTM和Dropout
进行正则化,最后用一个全连接层输出预测值。
4. 模型训练:使用Adam优化器,损失函数为均方误差(MSE)。
5. 预测并反归一化:使用训练好的LSTM模型对训练集进行预测,然后将预测值反归一化回原来的价格范围。
真实值和预测值的折线图,并在图中填充真实值与预测值之间的区域,形成复杂的可视化效果。
其他图形效果,大家自行跑代码,上面代码是完整的。
最后