股票分析长短期时间序列模型LSTMPython代码

我需要查看stock_forcast.py文件的完整内容,了解其中LSTM模型的实现和使用情况,然后再对其进行解释。

filePath: c:\Users\czliu\Documents\python\stock_forcast.py

现在我已经查看了完整的stock_forcast.py文件,我将对其中的LSTM模型实现进行详细解释,包括模型定义、结构、参数、数据准备和训练过程等方面。# LSTM模型在股票预测程序中的实现分析

1. LSTM模型定义

stock_forcast.py文件中,LSTM模型通过LSTMStockPredictor类实现:

class LSTMStockPredictor(nn.Module):
    def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=1, dropout=0.2):
        super(LSTMStockPredictor, self).__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers

        # LSTM层
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=dropout)

        # 全连接层
        self.fc = nn.Linear(hidden_size, output_size)

        # Sigmoid激活函数,用于分类
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 初始化隐藏状态和细胞状态
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)

        # 前向传播LSTM
        out, _ = self.lstm(x, (h0, c0))

        # 取最后一个时间步的输出
        out = self.fc(out[:, -1, :])

        # 使用sigmoid激活函数得到概率
        out = self.sigmoid(out)

        return out

2. 模型结构详解

2.1 模型参数

  • input_size=1:输入特征维度,这里只使用股票收盘价作为输入
  • hidden_size=64:LSTM隐藏层大小,决定模型的学习能力
  • num_layers=2:LSTM堆叠层数,增加模型深度
  • output_size=1:输出维度,这里输出一个概率值
  • dropout=0.2: dropout比率,防止过拟合

2.2 网络结构

  1. LSTM层:使用PyTorch内置的nn.LSTM实现,采用批量优先(batch_first=True)的输入格式
  2. 全连接层:将LSTM的输出映射到最终的预测值
  3. Sigmoid激活函数:将输出转换为0-1之间的概率值,表示上涨的可能性

2.3 前向传播过程

  1. 初始化隐藏状态(h0)和细胞状态(c0)为全零张量
  2. 将输入数据通过LSTM层,得到输出序列和最终的隐藏状态
  3. 只取LSTM输出序列的最后一个时间步的输出,因为我们只需要预测下一天的趋势
  4. 将LSTM的输出通过全连接层和Sigmoid激活函数,得到最终的预测概率

3. 数据准备与预处理

模型使用prepare_data_for_lstm函数处理数据:

def prepare_data_for_lstm(data, look_back=60, test_size=0.2):
    # 使用收盘价作为特征
    close_prices = data['Close'].values.reshape(-1, 1)

    # 数据归一化
    scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_data = scaler.fit_transform(close_prices)

    # 创建数据集
    X, y = [], []
    for i in range(len(scaled_data) - look_back):
        X.append(scaled_data[i:(i + look_back), 0])
        # 预测下一天是涨(1)还是跌(0)
        y.append(1 if scaled_data[i + look_back, 0] > scaled_data[i + look_back - 1, 0] else 0)

    X, y = np.array(X), np.array(y)

    # 转换为LSTM需要的形状 [样本数, 时间步, 特征数]
    X = np.reshape(X, (X.shape[0], X.shape[1], 1))

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, shuffle=False)

    # 转换为PyTorch张量
    X_train = torch.tensor(X_train, dtype=torch.float32)
    X_test = torch.tensor(X_test, dtype=torch.float32)
    y_train = torch.tensor(y_train, dtype=torch.float32).unsqueeze(1)
    y_test = torch.tensor(y_test, dtype=torch.float32).unsqueeze(1)

    return X_train, X_test, y_train, y_test, scaler, len(X_train)

关键处理步骤:

  1. 使用收盘价作为特征:从股票数据中提取收盘价
  2. 数据归一化:使用MinMaxScaler将数据缩放到0-1之间,提高模型训练稳定性
  3. 构建时间序列数据集
  • 使用look_back=60个连续的收盘价作为输入
  • 预测第61天的涨跌情况(1表示涨,0表示跌)
  1. 调整数据形状:转换为LSTM需要的三维格式 [样本数, 时间步, 特征数]
  2. 划分训练集和测试集:按时间顺序划分,保持时间序列特性
  3. 转换为PyTorch张量:适配PyTorch框架的输入格式

4. 模型训练过程

模型训练通过train_lstm_model函数实现:

def train_lstm_model(model, X_train, y_train, X_test, y_test, epochs=100, batch_size=32, learning_rate=0.001):
    # 定义损失函数和优化器
    criterion = nn.BCELoss()  # 二元交叉熵损失
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

    # 创建DataLoader
    train_dataset = TensorDataset(X_train, y_train)
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

    # 训练模型
    train_losses = []
    test_losses = []

    for epoch in range(epochs):
        model.train()
        train_loss = 0.0

        for batch_X, batch_y in train_loader:
            # 前向传播
            outputs = model(batch_X)
            loss = criterion(outputs, batch_y)

            # 反向传播和优化
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            train_loss += loss.item()

        # 计算平均训练损失
        train_loss /= len(train_loader)
        train_losses.append(train_loss)

        # 在测试集上评估
        model.eval()
        with torch.no_grad():
            test_outputs = model(X_test)
            test_loss = criterion(test_outputs, y_test).item()
            test_losses.append(test_loss)

        # 打印训练进度
        if (epoch + 1) % 10 == 0:
            print(f'Epoch [{epoch+1}/{epochs}], Train Loss: {train_loss:.4f}, Test Loss: {test_loss:.4f}')

    return model, train_losses, test_losses

训练关键设置:

  1. 损失函数:使用二元交叉熵损失(BCELoss),适合二分类问题
  2. 优化器:使用Adam优化器,学习率0.001
  3. 批次训练:使用batch_size=32进行小批量训练
  4. 训练轮次:训练100个epoch
  5. 训练监控:每10个epoch打印一次训练损失和测试损失

5. 模型预测与评估

5.1 预测过程

def predict_stock_trend(model, X_test, y_test):
    model.eval()
    with torch.no_grad():
        predictions = model(X_test)
        # 转换为0/1分类
        predicted_classes = (predictions > 0.5).float()

        # 计算准确率
        accuracy = (predicted_classes == y_test).float().mean()
        print(f'模型准确率: {accuracy:.4f}')

    return predictions, predicted_classes, accuracy

预测步骤:

  1. 将模型设置为评估模式(model.eval())
  2. 关闭梯度计算(with torch.no_grad())以提高效率
  3. 使用模型进行预测,得到每个样本的上涨概率
  4. 将概率转换为0/1分类结果(概率>0.5为涨,否则为跌)
  5. 计算模型在测试集上的准确率

5.2 结果可视化

程序提供了plot_prediction_results函数,用于可视化预测结果,包括:

  • 股票实际收盘价和测试集分割点
  • 模型预测的涨跌趋势

6. LSTM模型的应用特点

  1. 时间序列建模:LSTM特别适合处理时间序列数据,能够捕捉股票价格的时序特征
  2. 长期依赖捕获:通过门控机制,LSTM能够记住长期的价格模式
  3. 二分类任务:这里LSTM被用于预测股票涨跌的二分类任务,而非直接预测价格
  4. 滑动窗口输入:使用60天的历史数据预测下一天的趋势,符合股票分析的常用方法

7. 程序中的LSTM工作流程

  1. 数据获取:从akshare获取股票历史数据
  2. 数据预处理:计算技术指标,准备LSTM输入数据
  3. 模型初始化:创建LSTM模型实例
  4. 模型训练:使用训练集训练LSTM模型
  5. 模型评估:在测试集上评估模型性能
  6. 结果可视化:绘制价格走势图和预测结果图

这个LSTM模型实现了一个基础的股票趋势预测系统,通过深度学习的方法尝试捕捉股票价格的短期波动模式。

开始预测 中国海油 600938

请输入股票代码(如 000001 或 600000):600938
开始训练LSTM模型…
Epoch [10/100], Train Loss: 0.6936, Test Loss: 0.6958
Epoch [20/100], Train Loss: 0.6930, Test Loss: 0.6958
Epoch [30/100], Train Loss: 0.6936, Test Loss: 0.6907
Epoch [40/100], Train Loss: 0.6924, Test Loss: 0.6894
Epoch [50/100], Train Loss: 0.6922, Test Loss: 0.6881
Epoch [60/100], Train Loss: 0.6915, Test Loss: 0.6836
Epoch [70/100], Train Loss: 0.6917, Test Loss: 0.6800
Epoch [80/100], Train Loss: 0.6924, Test Loss: 0.6816
Epoch [90/100], Train Loss: 0.6882, Test Loss: 0.7051
Epoch [100/100], Train Loss: 0.6904, Test Loss: 0.6809
模型准确率: 0.5676

完整代码:

          
# 程序用途及神经网络预测结果解释

## 一、程序主要用途

该Python程序是一个综合的股票分析工具,主要功能包括:

1. **股票数据获取**:使用akshare库从公开数据源获取A股股票的历史交易数据(默认获取近一年的日线数据)。

2. **技术指标计算**:
   - 计算指数移动平均线(EMA,默认20日)
   - 计算价格波动标准差(STD,默认20日)

3. **技术分析可视化**:
   - 绘制股票收盘价与EMA指标对比图
   - 绘制股票价格波动性(STD)走势图

4. **LSTM神经网络预测**:
   - 使用长短期记忆网络(LSTM)预测股票次日涨跌趋势
   - 训练模型并评估预测准确率
   - 可视化预测结果与实际价格走势对比

## 二、神经网络预测结果解释

### 1. 训练过程指标
- **Epoch**:训练轮次,程序默认训练100轮
- **Train Loss**:训练集损失,反映模型在训练数据上的拟合程度
- **Test Loss**:测试集损失,反映模型在未见过数据上的泛化能力

从您提供的训练日志看:
```
Epoch [90/100], Train Loss: 0.6398, Test Loss: 0.8248 
Epoch [100/100], Train Loss: 0.6300, Test Loss: 0.9487
```
训练损失逐渐下降,但测试损失在后期有所上升,说明模型可能出现了一定程度的过拟合(在训练数据上表现好,但对新数据预测能力下降)。

### 2. 模型准确率
```
模型准确率: 0.5135
```
准确率约为51.35%,表示模型预测正确的比例。这个准确率略高于随机猜测(50%),说明模型有一定的预测能力,但效果并不理想。

### 3. 预测结果可视化
- **上半部分图表**:显示股票实际收盘价走势,并标注测试集开始位置
- **下半部分图表**:显示模型对测试集的预测结果(0表示跌,1表示涨)

### 4. 模型局限性与改进方向
- **数据量限制**:仅使用了近一年的日线数据,可考虑增加更长时间周期的数据
- **特征单一**:目前仅使用收盘价作为特征,可加入成交量、换手率等更多指标
- **模型结构**:可尝试调整LSTM层数、隐藏单元数量或添加更多网络层
- **训练参数**:可调整学习率、批量大小、训练轮次等参数
- **过拟合问题**:可添加正则化技术(如L2正则化、Dropout)或使用早停法

## 三、使用建议

1. **数据选择**:建议选择交易活跃、数据质量好的股票进行分析
2. **参数调整**:根据不同股票特性调整技术指标参数(如EMA窗口大小)和LSTM模型参数
3. **结果解读**:股票价格受多种因素影响,模型预测仅供参考,不应作为投资决策的唯一依据
4. **持续优化**:可根据实际预测效果持续调整模型结构和参数

该程序提供了一个完整的股票分析框架,您可以在此基础上根据自己的需求进一步扩展功能或优化模型。

 ```{python}
 import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "Microsoft YaHei", "SimSun", "KaiTi", "FangSong"]
plt.rcParams["axes.unicode_minus"] = False
import akshare as ak
from datetime import datetime, timedelta
import torch
import torch.nn as nn
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, TensorDataset


def fetch_stock_data(symbol, period='1y'):
    """获取股票数据"""
    end_date = datetime.now().strftime('%Y%m%d')
    start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
    
    # 处理股票代码格式,确保符合akshare要求
    if not symbol.startswith(('sh', 'sz')):
        if symbol.startswith(('6', '9')):
            symbol = f"sh{symbol}"
        else:
            symbol = f"sz{symbol}"
    
    # 使用更稳定的stock_zh_a_hist函数获取股票日线数据
    data = ak.stock_zh_a_hist(symbol=symbol[2:],  # 只传股票代码部分,不含sh/sz
                             period="daily",
                             start_date=start_date,
                             end_date=end_date,
                             adjust="qfq")  # 使用前复权价格
    
    # 检查数据获取是否成功
    if data.empty:
        print(f"获取股票{symbol}数据失败,请检查股票代码和网络连接")
        return None
    
    # 先转换日期格式
    if '日期' in data.columns:
        data['日期'] = pd.to_datetime(data['日期'])
        # 设置日期为索引
        data.set_index('日期', inplace=True)
        # 重命名收盘价列
        if '收盘' in data.columns:
            data = data.rename(columns={'收盘': 'Close'})
    else:
        print("数据格式异常,无法处理")
        return None
    
    return data


def calculate_ema(data, window=20):
    """计算指数移动平均线 EMA"""
    ema = data['Close'].ewm(span=window, adjust=False).mean()
    return ema


def calculate_std(data, window=20):
    """计算标准差 STD"""
    std = data['Close'].rolling(window=window).std()
    return std


def plot_analysis(data, ema, std, symbol):
    """绘制图表分析结果"""
    plt.figure(figsize=(14, 8))

    plt.subplot(2, 1, 1)
    plt.plot(data['Close'], label='收盘价', color='blue')
    plt.plot(ema, label='EMA(20)', color='orange')
    plt.title(f'{symbol} 股票价格与EMA分析')
    plt.legend()

    plt.subplot(2, 1, 2)
    plt.plot(std, label='STD(20)', color='red')
    plt.title(f'{symbol} 股票价格波动性(STD)分析')
    plt.legend()

    plt.tight_layout()
    plt.show()


# LSTM模型定义
class LSTMStockPredictor(nn.Module):
    def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=1, dropout=0.2):
        super(LSTMStockPredictor, self).__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        
        # LSTM层
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=dropout)
        
        # 全连接层
        self.fc = nn.Linear(hidden_size, output_size)
        
        # Sigmoid激活函数,用于分类
        self.sigmoid = nn.Sigmoid()
    
    def forward(self, x):
        # 初始化隐藏状态和细胞状态
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        
        # 前向传播LSTM
        out, _ = self.lstm(x, (h0, c0))
        
        # 取最后一个时间步的输出
        out = self.fc(out[:, -1, :])
        
        # 使用sigmoid激活函数得到概率
        out = self.sigmoid(out)
        
        return out


def prepare_data_for_lstm(data, look_back=60, test_size=0.2):
    """准备LSTM模型的数据"""
    # 使用收盘价作为特征
    close_prices = data['Close'].values.reshape(-1, 1)
    
    # 数据归一化
    scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_data = scaler.fit_transform(close_prices)
    
    # 创建数据集
    X, y = [], []
    for i in range(len(scaled_data) - look_back):
        X.append(scaled_data[i:(i + look_back), 0])
        # 预测下一天是涨(1)还是跌(0)
        y.append(1 if scaled_data[i + look_back, 0] > scaled_data[i + look_back - 1, 0] else 0)
    
    X, y = np.array(X), np.array(y)
    
    # 转换为LSTM需要的形状 [样本数, 时间步, 特征数]
    X = np.reshape(X, (X.shape[0], X.shape[1], 1))
    
    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, shuffle=False)
    
    # 转换为PyTorch张量
    X_train = torch.tensor(X_train, dtype=torch.float32)
    X_test = torch.tensor(X_test, dtype=torch.float32)
    y_train = torch.tensor(y_train, dtype=torch.float32).unsqueeze(1)
    y_test = torch.tensor(y_test, dtype=torch.float32).unsqueeze(1)
    
    return X_train, X_test, y_train, y_test, scaler, len(X_train)


def train_lstm_model(model, X_train, y_train, X_test, y_test, epochs=100, batch_size=32, learning_rate=0.001):
    """训练LSTM模型"""
    # 定义损失函数和优化器
    criterion = nn.BCELoss()  # 二元交叉熵损失
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    
    # 创建DataLoader
    train_dataset = TensorDataset(X_train, y_train)
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    
    # 训练模型
    train_losses = []
    test_losses = []
    
    for epoch in range(epochs):
        model.train()
        train_loss = 0.0
        
        for batch_X, batch_y in train_loader:
            # 前向传播
            outputs = model(batch_X)
            loss = criterion(outputs, batch_y)
            
            # 反向传播和优化
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            
            train_loss += loss.item()
        
        # 计算平均训练损失
        train_loss /= len(train_loader)
        train_losses.append(train_loss)
        
        # 在测试集上评估
        model.eval()
        with torch.no_grad():
            test_outputs = model(X_test)
            test_loss = criterion(test_outputs, y_test).item()
            test_losses.append(test_loss)
        
        # 打印训练进度
        if (epoch + 1) % 10 == 0:
            print(f'Epoch [{epoch+1}/{epochs}], Train Loss: {train_loss:.4f}, Test Loss: {test_loss:.4f}')
    
    return model, train_losses, test_losses


def predict_stock_trend(model, X_test, y_test):
    """使用训练好的模型预测股票趋势"""
    model.eval()
    with torch.no_grad():
        predictions = model(X_test)
        # 转换为0/1分类
        predicted_classes = (predictions > 0.5).float()
        
        # 计算准确率
        accuracy = (predicted_classes == y_test).float().mean()
        print(f'模型准确率: {accuracy:.4f}')
    
    return predictions, predicted_classes, accuracy


def plot_prediction_results(data, predicted_classes, look_back, n_train):
    """绘制预测结果"""
    plt.figure(figsize=(14, 8))
    
    # 获取测试集对应的原始数据
    test_start_index = n_train + look_back
    test_data = data.iloc[test_start_index:]
    
    # 创建预测结果的DataFrame
    prediction_df = pd.DataFrame(index=test_data.index)
    prediction_df['Actual'] = data['Close'].iloc[test_start_index:]
    prediction_df['Predicted_Trend'] = predicted_classes.numpy().flatten()
    
    # 绘制收盘价和预测趋势
    plt.subplot(2, 1, 1)
    plt.plot(data['Close'], label='实际收盘价', color='blue')
    if not test_data.empty:
        plt.axvline(x=test_data.index[0], color='red', linestyle='--', label='测试集开始')
    plt.title('股票价格与测试集分割')
    plt.legend()
    
    # 绘制预测趋势
    plt.subplot(2, 1, 2)
    if not test_data.empty:
        plt.plot(test_data.index, prediction_df['Predicted_Trend'], label='预测涨跌', color='green', marker='o', linestyle='')
    plt.title('LSTM模型预测股票涨跌')
    plt.yticks([0, 1], ['跌', '涨'])
    plt.legend()
    
    plt.tight_layout()
    plt.show()


def main():
    symbol = input("请输入股票代码(如 000001 或 600000):")
    data = fetch_stock_data(symbol)
    
    # 检查数据获取是否成功
    if data is not None:
        # 计算技术指标
        ema = calculate_ema(data)
        std = calculate_std(data)
        
        # 绘制技术分析图表
        plot_analysis(data, ema, std, symbol)
        
        # 准备LSTM模型数据
        look_back = 60
        test_size = 0.2
        X_train, X_test, y_train, y_test, scaler, n_train = prepare_data_for_lstm(data, look_back, test_size)
        
        # 初始化模型
        model = LSTMStockPredictor(input_size=1, hidden_size=64, num_layers=2, output_size=1, dropout=0.2)
        
        # 训练模型
        print("开始训练LSTM模型...")
        model, train_losses, test_losses = train_lstm_model(model, X_train, y_train, X_test, y_test, epochs=100, batch_size=32, learning_rate=0.001)
        
        # 预测股票趋势
        predictions, predicted_classes, accuracy = predict_stock_trend(model, X_test, y_test)
        
        # 绘制预测结果
        plot_prediction_results(data, predicted_classes, look_back, n_train)
        
        # 绘制损失曲线
        plt.figure(figsize=(10, 6))
        plt.plot(range(1, len(train_losses) + 1), train_losses, label='训练损失')
        plt.plot(range(1, len(test_losses) + 1), test_losses, label='测试损失')
        plt.xlabel('Epoch')
        plt.ylabel('Loss')
        plt.title('LSTM模型训练损失曲线')
        plt.legend()
        plt.show()


if __name__ == '__main__':
    main()

 ```


# 问豆包:Python股票分析,akshare获取数据,可视化长度傲气记忆网络(LSTM),神经模型采用pytorch。预测股票涨跌​