[LSTM]lstm模型对输入数据的格式要求

42 阅读8分钟

我们需要使用股票数据来构建适合LSTM输入的序列数据。

LSTM通常用于处理时间序列数据,其输入数据的形状通常为:(样本数, 时间步长, 特征数)。

也就是说,每个样本是一个时间序列片段,包含连续的时间步,每个时间步有多个特征。

假设我们有一组股票数据,包括开盘价(open)、最高价(high)、最低价(low)、收盘价(close)、成交量(volume)等。我们想要用过去N天的数据(时间步长=N)来预测下一天的收盘价。

那么,我们需要构建的输入数据就是一个个连续的N天窗口,每个窗口有N个时间步,每个时间步有多个特征(例如开盘价、最高价、最低价、收盘价、成交量)。

下面我们通过一个例子来展示如何构建这样的数据。

步骤:

  1. 加载股票数据(这里我们使用模拟数据)。
  2. 对数据进行归一化(通常对于LSTM,我们需要对每个特征进行归一化)。
  3. 构建数据集,将时间序列数据转换为适合LSTM的输入形状。

注意:我们通常将数据组织成多个样本,每个样本是连续的时间步,每个时间步有多个特征。

假设我们有一个包含多个特征的时间序列数据,我们想要用过去10天(时间步长=10)的数据来预测下一天的收盘价。

示例数据格式如下(模拟数据):

我来详细解释LSTM所需的序列数据格式,并用股票数据作为例子展示。

1/LSTM输入数据的基本结构

LSTM需要三维数据,形状为:(样本数, 时间步长, 特征数)

  • 样本数:有多少个训练样本
  • 时间步长:每个样本包含多少个时间点的数据
  • 特征数:每个时间点有多少个特征

2/股票数据示例

假设我们有某股票10天的数据:

import pandas as pd
import numpy as np

# 创建示例股票数据
data = {
    'date': pd.date_range('2024-01-01', periods=10, freq='D'),
    'open': [100, 101, 102, 99, 98, 103, 105, 107, 106, 108],
    'high': [102, 103, 104, 101, 100, 105, 107, 109, 108, 110],
    'low': [99, 100, 101, 98, 97, 102, 104, 106, 105, 107],
    'close': [101, 102, 103, 100, 99, 104, 106, 108, 107, 109],
    'volume': [10000, 12000, 11000, 9000, 8000, 15000, 13000, 14000, 12000, 16000]
}

df = pd.DataFrame(data)
print("原始股票数据:")
print(df)

输出:

         date  open  high  low  close  volume
0  2024-01-01   100   102   99    101   10000
1  2024-01-02   101   103  100    102   12000
2  2024-01-03   102   104  101    103   11000
3  2024-01-04    99   101   98    100    9000
4  2024-01-05    98   100   97     99    8000
5  2024-01-06   103   105  102    104   15000
6  2024-01-07   105   107  104    106   13000
7  2024-01-08   107   109  106    108   14000
8  2024-01-09   106   108  105    107   12000
9  2024-01-10   108   110  107    109   16000

3/构建LSTM输入数据的完整过程

<1>选择特征和预处理

from sklearn.preprocessing import MinMaxScaler # 归一化

# 选择特征列
features = ['open', 'high', 'low', 'close', 'volume']  # 去掉date这一列
feature_data = df[features].values

# 数据归一化(LSTM对输入数据的尺度敏感)
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(feature_data)

print("\n归一化后的数据:")
print(scaled_data[:5])  # 显示前5行

<2>定义时间步长并创建序列

假设我们用过去3天的数据预测下一天的收盘价:

def create_sequences(data, time_steps=3):
    # 创建2个空的列表, 一个存放序列, 一个存放标签
    X = []
    y = []
    
    for i in range(len(data) - time_steps):
        # 获取时间步长内的特征序列
        sequence = data[i:(i + time_steps), :]  # 包含所有特征, i从0开始
        # 下一天的收盘价作为目标(假设收盘价在第3列)
        # 这里是预测涨跌的幅度, 而不是预测涨和跌的二分类问题.
        target = data[i + time_steps, 3]  # 第3列是收盘价
        # 把序列数据, 添加到之前定义的空列表中
        X.append(sequence)
        y.append(target)
    # 最后返回, 但是要对x,y转换成数组的形式 , 转换完毕之后, 就是三维的shape了
    return np.array(X), np.array(y)

# 创建序列
time_steps = 3
X, y = create_sequences(scaled_data, time_steps)

print(f"\nLSTM输入数据形状: {X.shape}")
print(f"目标数据形状: {y.shape}")
print(f"样本数: {X.shape[0]}")
print(f"时间步长: {X.shape[1]}")
print(f"特征数: {X.shape[2]}")

<3>查看具体的数据结构

print("\n=== 第一个样本 ===")
print("输入X[0] (3天×5个特征):")
print(f"形状: {X[0].shape}")
print(X[0])

print(f"\n对应的目标y[0]: {y[0]}")
print("(这是第4天的收盘价,用前3天的数据预测)")

<4>完整的数据集展示

print("\n=== 所有样本的数据结构 ===")
for i in range(len(X)):
    print(f"\n样本 {i}:")
    print(f"使用第 {i}{i+2} 天的数据预测第 {i+3} 天的收盘价")
    print(f"目标值 (第{i+3}天收盘价): {y[i]:.4f}")

4/实际应用中的完整代码示例

import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler

# 模拟更多股票数据
np.random.seed(42)
n_days = 100
dates = pd.date_range('2024-01-01', periods=n_days, freq='D')

# 生成随机股票数据
close_prices = 100 + np.cumsum(np.random.randn(n_days) * 2)
open_prices = close_prices * (1 + np.random.randn(n_days) * 0.01)
high_prices = np.maximum(open_prices, close_prices) * (1 + np.random.rand(n_days) * 0.02)
low_prices = np.minimum(open_prices, close_prices) * (1 - np.random.rand(n_days) * 0.02)
volumes = np.random.randint(5000, 20000, n_days)

df = pd.DataFrame({
    'date': dates,
    'open': open_prices,
    'high': high_prices,
    'low': low_prices,
    'close': close_prices,
    'volume': volumes
})

# 添加技术指标
df['ma5'] = df['close'].rolling(5).mean()
df['ma10'] = df['close'].rolling(10).mean()
df['returns'] = df['close'].pct_change()
df = df.dropna()

print("包含技术指标的股票数据:")
print(df.head(10))
print(f"\n数据形状: {df.shape}")

# 准备LSTM输入
features = ['open', 'high', 'low', 'close', 'volume', 'ma5', 'ma10', 'returns']
feature_data = df[features].values

# 归一化
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(feature_data)

# 创建序列
def create_sequences_with_labels(data, time_steps=10):
    X, y = [], []
    
    for i in range(len(data) - time_steps):
        # 输入:过去time_steps天的所有特征
        X.append(data[i:(i + time_steps)])
        # 输出:下一天的收盘价(假设收盘价在第3列)
        y.append(data[i + time_steps, 3])
    
    return np.array(X), np.array(y)

# 创建数据集
time_steps = 10
X, y = create_sequences_with_labels(scaled_data, time_steps)

print(f"\n=== LSTM数据准备完成 ===")
print(f"输入数据 X 形状: {X.shape}")
print(f"  样本数: {X.shape[0]}")
print(f"  时间步长: {X.shape[1]}")
print(f"  特征数: {X.shape[2]}")
print(f"目标数据 y 形状: {y.shape}")

# 分割训练集和测试集
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

print(f"\n训练集大小: {X_train.shape[0]} 个样本")
print(f"测试集大小: {X_test.shape[0]} 个样本")

5/不同时间序列任务的输入格式

<1>单变量时间序列(预测单变量)

# 只使用收盘价
univariate_data = df['close'].values.reshape(-1, 1)
# 此时输入形状为: (样本数, 时间步长, 1)

<2>多变量时间序列(预测单变量)

# 使用多个特征预测收盘价(如上例)
# 输入形状: (样本数, 时间步长, 特征数)

<3>多变量时间序列(预测多变量)

# 预测多个指标(如收盘价和成交量)
def create_multi_output_sequences(data, time_steps=10, target_features=[3, 4]):
    X, y = [], []
    
    for i in range(len(data) - time_steps):
        X.append(data[i:(i + time_steps)])
        # 预测多个目标
        y.append(data[i + time_steps, target_features])
    
    return np.array(X), np.array(y)

# 预测收盘价和成交量
X_multi, y_multi = create_multi_output_sequences(scaled_data, time_steps=10, target_features=[3, 4])
print(f"多输出形状 - X: {X_multi.shape}, y: {y_multi.shape}")

6/关键要点总结

  1. 三维结构:LSTM输入必须是三维的 (samples, timesteps, features)
  2. 序列连续性:每个样本是连续的时间序列片段
  3. 时间步长选择:根据问题复杂度确定,股票通常用5-20天
  4. 特征工程:可以加入技术指标(MA、RSI、MACD等)
  5. 数据归一化:必须归一化,LSTM对数据尺度敏感

7/验证数据是否正确

def validate_sequences(X, y, original_df, feature_names):
    """验证序列数据的正确性"""
    print("\n=== 数据验证 ===")
    print(f"第一个样本的时间跨度:")
    print(f"使用: {original_df.iloc[0:time_steps]['date'].tolist()}")
    print(f"预测: {original_df.iloc[time_steps]['date']}")
    
    print(f"\n第一个样本的特征值 (归一化前):")
    print(original_df.iloc[0:time_steps][feature_names].values)
    
    print(f"\n对应的目标值 (归一化前):")
    print(original_df.iloc[time_steps]['close'])

这种数据格式可以直接输入到LSTM模型中:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense

model = Sequential([
    LSTM(50, return_sequences=True, input_shape=(time_steps, len(features))),
    LSTM(50),
    Dense(1)
])

model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=10, batch_size=32)