机器学习之LSTM的Python实现

article/2025/9/22 12:12:06

什么是LSTM?

LSTM(长短期记忆人工神经网络),是一种可以学习长期依赖特殊的RNN(循环神经网络)。

传统循环网络RNN虽然可以通过记忆体,实现短期记忆,进行连续数据的预测。但是当连续数据的序列变长时,会使展开时间步过长,反向传播更新参数时梯度要按时间步连续相乘,会导致梯度消失。故引入LSTM(长短期记忆人工神经网络)。

LSTM的核心理念

循环核

在这里插入图片描述

注:

i t i_t it:输入门(门限)

f t f_t ft:遗忘门(门限)

o t o_t ot:输出门(门限)

C t C_t Ct:细胞态(长期记忆)

h t h_t ht:记忆体(短期记忆)

C t ~ \tilde {C_t} Ct~:候选态(归纳出的新知识)

输入门、遗忘门、输出门

i t = σ ( W i ⋅ [ h t − 1 , x t ] + b i ) f t = σ ( W f ⋅ [ h t − 1 , x t ] + b f ) o t = σ ( W o ⋅ [ h t − 1 , x t ] + b o ) i_t = \sigma(W_i·[h_{t-1},x_t] + b_i) \\ f_t = \sigma(W_f·[h_{t-1},x_t] + b_f) \\ o_t = \sigma(W_o·[h_{t-1},x_t] + b_o) it=σ(Wi[ht1,xt]+bi)ft=σ(Wf[ht1,xt]+bf)ot=σ(Wo[ht1,xt]+bo)

注:

x t x_t xt:当前时刻输入特征

h t − 1 h_{t-1} ht1:上一时刻的短期记忆

W i W_i Wi W f W_f Wf W o W_o Wo:待训练参数矩阵

b i b_i bi b f b_f bf b o b_o bo:待训练偏置项

Sigmoid激活函数:使门限的范围在0-1之间

细胞态

C t = f t ∗ C t − 1 + i t ∗ C t ~ C_t = f_t * C_{t-1} + i_t *\tilde {C_t} Ct=ftCt1+itCt~

注:

C t − 1 C_{t-1} Ct1:上一时刻长期记忆

f t f_t ft:遗忘门(门限)

C t ~ \tilde {C_t} Ct~:候选态(当前时刻归纳出的新知识)

i t i_t it:输入门(门限)

f t ∗ C t − 1 f_t * C_{t-1} ftCt1:留存在脑中的过去的记忆

记忆体

h t = o t ∗ t a n h ( C t ) h_t = o_t * tanh(C_t) ht=ottanh(Ct)

注:

tanh()激活函数

o t o_t ot:输出门(门限)

C t C_t Ct:细胞态(长期记忆)

候选态

C t ~ = t a n h ( W c ⋅ [ h t − 1 , x t ] + b c ) \tilde {C_t} = tanh(W_c ·[h_{t-1},x_t] + b_c) Ct~=tanh(Wc[ht1,xt]+bc)

注:

W c W_c Wc:待训练参数矩阵

b c b_c bc:待训练偏置项

关于深入理解神经网络可以参考:理解神经网络

编程思路

  1. 导入相关模块
  2. 准备训练集和测试集数据
  3. 对数据集进行归一化处理,使送入神经网络的数据分布在0-1之间
  4. 制作训练集和测试集的输入特征和标签,并打乱训练集数据顺序
  5. 将数据装换为RNN需要输入的维度
  6. 搭建神经网络,设置记忆体个数,循环计算层层数
  7. 配置训练方法
  8. 设置断点续训
  9. 执行训练过程
  10. 打印网络结构和参数统计,参数提取和loss可视化

代码实现

import math
import osimport numpy as np
from keras.layers import LSTM, Dropout, Dense
from sklearn.preprocessing import MinMaxScaler
from tensorflow import optimizers
from tensorflow.keras import Sequential, callbacks
import tensorflow as tf
from sklearn.metrics import mean_squared_error, mean_absolute_errorclass CgcpLSTM:def __init__(self, name):"""构造函数,初始化模型:param data_list: 真实数据列表"""# 神经网络名称self.name = name# 训练集占总样本的比例self.train_all_ratio = 0.875# 连续样本点数self.continuous_sample_point_num = 20# 定义归一化:归一化到(0,1)之间self.sc = MinMaxScaler(feature_range=(0, 1))# 每次喂入神经网络的样本数self.batch_size = 64# 数据集的迭代次数self.epochs = 1000000# 每多少次训练集迭代,验证一次测试集self.validation_freq = 1# 配置模型self.model = Sequential([# LSTM层(记忆体个数,是否返回输出(True:每个时间步输出ht,False:仅最后时间步输出ht))# 配置具有80个记忆体的LSTM层,每个时间步输出htLSTM(80, return_sequences=True),Dropout(0.2),# 配置具有100个记忆体的LSTM层,仅在最后一步返回htLSTM(100),Dropout(0.2),Dense(1)])# 配置训练方法# 该应用只观测loss数值,不观测准确率,所以删去metrics选项,一会在每个epoch迭代显示时只显示loss值self.model.compile(optimizer=optimizers.Adam(0.001),loss='mean_squared_error',  # 损失函数用均方误差)# 配置断点续训文件self.checkpoint_save_path = os.path.abspath(os.path.dirname(__file__)) + "\\checkpoint\\" + self.name + "_LSTM_stock.ckpt"if os.path.exists(self.checkpoint_save_path + '.index'):print('-' * 20 + "加载模型" + "-" * 20)self.model.load_weights(self.checkpoint_save_path)# 断点续训,存储最佳模型self.cp_callback = callbacks.ModelCheckpoint(filepath=self.checkpoint_save_path,save_weights_only=True,save_best_only=True,# monitor='val_accuracy',monitor='val_loss',)def make_set(self, data_list):"""使用历史数据制作训练集和测试集:param data_list: 历史数据列表:return: train_set, test_set 归一化处理后的训练集合测试集"""# 将历史数据装换为ndarrayif isinstance(data_list, list):data_array = np.array(data_list)elif isinstance(data_list, np.ndarray):data_array = data_listelse:raise Exception("数据源格式错误")# 对一维矩阵进行升维操作if len(data_array.shape) == 1:data_array = data_array.reshape(data_array.shape[0], 1)if data_array.shape[1] != 1:raise Exception("数据源形状有误")# 按照比例对数据进行分割index = int(data_array.shape[0] * self.train_all_ratio)train_set = data_array[:index, :]test_set = data_array[index:, :]print("train_set_shape:{}".format(train_set.shape))# 对训练集和测试集进行归一化处理train_set, test_set = self.gui_yi(train_set, test_set)print("训练集长度:{}".format(len(train_set)))print("测试集长度:{}".format(len(test_set)))return train_set, test_setdef gui_yi(self, train_set, test_set):"""对训练集合测试集进行归一化处理:param test_set: 未进行归一化的训练集数据:param train_set: 未进行归一化处理的测试集数据:return: train_set, test_set 归一化处理后的训练集合测试集"""# 求得训练集的最大值,最小值这些训练集固有的属性,并在训练集上进行归一化train_set_scaled = self.sc.fit_transform(train_set)# 利用训练集的属性对测试集进行归一化test_set = self.sc.transform(test_set)return train_set_scaled, test_setdef fan_gui_yi(self, data_set):"""逆归一化:param data_set: 需要还原的数据:return:"""# 对数据进行逆归一化还原data_set = self.sc.inverse_transform(data_set)return data_setdef train(self, x_train, y_train, x_test, y_test):"""训练模型:param x_train::param y_train::param x_test::param y_test::return:"""# 训练模型history = self.model.fit(x_train, y_train,# 每次喂入神经网络的样本数batch_size=self.batch_size,# 数据集的迭代次数epochs=self.epochs,validation_data=(x_test, y_test),# 每多少次训练集迭代,验证一次测试集validation_freq=self.validation_freq,callbacks=[self.cp_callback])# 输出模型各层的参数状况self.model.summary()# 参数提取self.save_args_to_file()# 获取模型当前loss值loss = history.history['loss']print("loss:{}".format(loss))try:val_loss = history.history['val_loss']print("val_loss:{}".format(val_loss))except:passdef save_args_to_file(self):"""参数提取,将参数保存至文件:return:"""# 指定参数存取目录file_path = os.path.abspath(os.path.dirname(__file__)) + "\\weights\\"# 目录不存在则创建if not os.path.exists(file_path):os.makedirs(file_path)# 打开文本文件file = open(file_path + self.name + "_weights.txt", 'w')# 将参数写入文件for v in self.model.trainable_variables:file.write(str(v.name) + '\n')file.write(str(v.shape) + '\n')file.write(str(v.numpy()) + '\n')file.close()def test(self, x_test, test_set):"""预测测试:param x_test::param test_set: 测试集:return:"""# 测试集输入模型进行预测predicted_stock_price = self.model.predict(x_test)# 对预测数据还原---从(0,1)反归一化到原始范围predicted_stock_price = self.fan_gui_yi(predicted_stock_price)# 对真实数据还原---从(0,1)反归一化到原始范围real_stock_price = self.fan_gui_yi(test_set[self.continuous_sample_point_num:])# ##########evaluate############### calculate MSE 均方误差 ---> E[(预测值-真实值)^2] (预测值减真实值求平方后求均值)mse = mean_squared_error(predicted_stock_price, real_stock_price)# calculate RMSE 均方根误差--->sqrt[MSE]    (对均方误差开方)rmse = math.sqrt(mean_squared_error(predicted_stock_price, real_stock_price))# calculate MAE 平均绝对误差----->E[|预测值-真实值|](预测值减真实值求绝对值后求均值)mae = mean_absolute_error(predicted_stock_price, real_stock_price)print('均方误差: %.6f' % mse)print('均方根误差: %.6f' % rmse)print('平均绝对误差: %.6f' % mae)def make_x_y_train_and_test(self, data_list):"""制作x_train(训练集输入特征), y_train(训练集标签), x_test(测试集输入特征), y_test(测试集标签):param data_list::return:"""# 获取归一化后的训练集合测试集train_set, test_set = self.make_set(data_list=data_list)# 初始化x_train(训练集输入特征), y_train(训练集标签), x_test(测试集输入特征), y_test(测试集标签)x_train, y_train, x_test, y_test = [], [], [], []# 利用for循环,遍历整个训练集,提取训练集中连续样本为训练集输入特征和标签for i in range(self.continuous_sample_point_num, len(train_set)):x_train.append(train_set[i - self.continuous_sample_point_num:i, 0])y_train.append(train_set[i, 0])# 对训练集进行打乱np.random.seed(7)np.random.shuffle(x_train)np.random.seed(7)np.random.shuffle(y_train)tf.random.set_seed(7)# 将训练集由list格式变为array格式x_train, y_train = np.array(x_train), np.array(y_train)# 使x_train符合RNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]。x_train = self.change_data_to_rnn_input(x_train)# 测试集# 利用for循环,遍历整个测试集,提取训练集中连续样本为训练集输入特征和标签for i in range(self.continuous_sample_point_num, len(test_set)):x_test.append(test_set[i - self.continuous_sample_point_num:i, 0])y_test.append(test_set[i, 0])# 测试集变array并reshape为符合RNN输入要求:[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]x_test, y_test = np.array(x_test), np.array(y_test)print("x_test_shape:{}".format(x_test.shape))x_test = self.change_data_to_rnn_input(x_test)return train_set, test_set, x_train, y_train, x_test, y_testdef change_data_to_rnn_input(self, data_array):"""将数据转变为RNN输入要求的维度:param data_array::return:"""# 对输入类型进行转换if isinstance(data_array, list):data_array = np.array(data_array)elif isinstance(data_array, np.ndarray):passelse:raise Exception("数据格式错误")rnn_input = np.reshape(data_array, (data_array.shape[0], self.continuous_sample_point_num, 1))return rnn_inputdef predict(self, history_data):"""使用模型进行预测:param history_data: 历史数据list:return:预测值"""# 将列表或数组转换为数组并提取最后一组数据if isinstance(history_data, list):history_data_array = history_data[self.continuous_sample_point_num * -1:]history_data_array = np.array(history_data_array)elif isinstance(history_data, np.ndarray):history_data_array = history_data[self.continuous_sample_point_num * -1:]else:raise Exception("数据格式错误")# 对一维数据进行升维处理if len(history_data_array.shape) == 1:history_data_array = history_data_array.reshape(1, self.continuous_sample_point_num)# 对数据形状进行效验if history_data_array.shape[1] != self.continuous_sample_point_num:raise Exception("数据形状有误")# 对数据进行归一化处理history_data_array = history_data_array.Thistory_data_array = self.sc.transform(history_data_array)history_data_array = history_data_array.T# 转换为RNN需要的数据形状history_data_array = self.change_data_to_rnn_input(history_data_array)# 测试集输入模型进行预测predicted_stock_price = self.model.predict(history_data_array)# 对预测数据还原---从(0,1)反归一化到原始范围predicted_stock_price = self.fan_gui_yi(predicted_stock_price)# 预测值value = predicted_stock_price[-1][-1]print("预测值:{}".format(value))return valueif __name__ == '__main__':data_list = [x for x in range(1000)]# print(data_list)# 初始化模型model = CgcpLSTM(name="浓度预测")# 获取训练和测试的相关参数train_set, test_set, x_train, y_train, x_test, y_test = model.make_x_y_train_and_test(data_list=data_list)# 训练模型model.train(x_train, y_train, x_test, y_test)# 对模型进行测试model.test(x_test, test_set)# 利用模型进行预测history = [x for x in range(50)]model.predict(history)

参考文献

人工智能实践:Tensorflow笔记_中国大学MOOC(慕课):https://www.icourse163.org/learn/PKU-1002536002?tid=1462067447

理解 LSTM 网络:https://www.jianshu.com/p/9dc9f41f0b29


http://chatgpt.dhexx.cn/article/J6myYPcX.shtml

相关文章

神经网络:LSTM基础学习

1、LSTM简介 在时间序列数据学习中,传统的循环神经网络(RNN)存在较多的学习瓶颈和技术缺陷,而长短时记忆(LSTM)神经网络克服了循环神经网络的缺陷,使其在长时间序列数据学习训练中能克服梯度爆炸…

Lstm(循环神经网络)

算法模型Lstm(循环神经网络): 简介 LSTM和RNN相似,它们都是在前向传播的过程中处理流经细胞的数据,不同之处在于 LSTM 中细胞的结构和运算有所变化。 LSTM结构: 遗忘门: 遗忘门的功能是决定应丢弃或保留哪些信息。…

基于MATLAB的LSTM神经网络时序预测

参考博客及文献:4 Strategies for Multi-Step Time Series Forecasting Multivariate Time Series Forecasting with LSTMs in Keras (machinelearningmastery.com) LSTM进阶:使用LSTM进行多维多步的时间序列预测_lstm多维多部预测_一只小EZ的博客-CSD…

LSTM神经网络图解

LSTM神经网络图详解 (1)遗忘门,用于计算信息的遗忘(保留)程度,通过sigmoid处理后为0到1的值,1表示全部保留,0表示全部忘记。 f t σ ( W f ⋅ [ h t − 1 , x t ] b f ) f_{t}\si…

【神经网络】LSTM

1.什么是LSTM 长短期记忆(Long short-term memory, LSTM)是一种特殊的RNN,主要是为了解决长序列训练过程中的梯度消失和梯度爆炸问题。简单来说,相比普通的RNN,LSTM能够在更长的序列中有更好的表现。 LSTM区别于RNN地方…

[深入浅出] LSTM神经网络

由来 人类并不是每时每刻都从一片空白的大脑开始他们的思考。在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考。我们的思想拥有持久性。 传统的神经…

简单理解LSTM神经网络

递归神经网络 在传统神经网络中,模型不会关注上一时刻的处理会有什么信息可以用于下一时刻,每一次都只会关注当前时刻的处理。举个例子来说,我们想对一部影片中每一刻出现的事件进行分类,如果我们知道电影前面的事件信息&#xf…

LSTM神经网络

LSTM被广泛用于许多序列任务(包括天然气负荷预测,股票市场预测,语言建模,机器翻译),并且比其他序列模型(例如RNN)表现更好,尤其是在有大量数据的情况下。 LSTM经过精心设…

(神经网络深度学习)--循环神经网络LSTM

一、什么是LSTM: 如果你经过上面的文章看懂了RNN的内部原理,那么LSTM对你来说就很简单了,首先大概介绍一下LSTM,是四个单词的缩写,Long short-term memory,翻译过来就是长短期记忆,是RNN的一种…

机器学习——人工神经网络模型LSTM

LSTM的学习 学习目标: 1理解什么是人工神经网络。2深入理解LSTM(长短期记忆网络)3Code 浅析人工神经网络: 在谈人工神经网络模型之前我们先来了解一下生理上的神经网络。 下面是一张对比图: Neural Science Compute…

LSTM神经网络详解

LSTM 长短时记忆网络(Long Short Term Memory Network, LSTM),是一种改进之后的循环神经网络,可以解决RNN无法处理长距离的依赖的问题,目前比较流行。 长短时记忆网络的思路: 原始 RNN 的隐藏层只有一个状态,即h&am…

LSTM神经网络介绍

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 引言一、介绍1.1什么是LSTM?1.2基础知识1.2.1门控机制1.2.2 相关激活函数1.2.3网络参数介绍 二、LSTM网络架构2.1架构图 三、LSTM的门3.1遗忘门3.2输入门3.3输出门…

大白话理解LSTM神经网络(附实例讲解)

前言 本文章为个人学习笔记整理,所学习的内容来自b站up主老弓的学习日记,附有实例讲解。 归类 长短期记忆神经网络(LSTM)是一种特殊的循环神经网络(RNN)。原始的RNN在训练中,随着训练时间的加长以及网络层数的增多&a…

Idea如何导入一个SpringBoot项目

最近公司要求开发工具要用Idea,作为一个eclipse的老员工,记录一下Idea中遇到的坑 刚开始用Idea从Git上导入一个项目时,遇到了很多坑,网上有很多方法,我不多做介绍。只说明一下我使用的方法。 1.本地新建一个文件夹&a…

idea导入项目框架的方法

学习时,使用IDEA的时候,经常需要导入项目框架,下面操作介绍如何导入项目框架。 打开需要导入的项目 打开方式: 打开 idea ,选择 Import Project 也可以进入idea后,选择 Flie --> New --> Project …

IDEA导入Eclipse项目

背景:用习惯了idea再去用eclipse实在用的不习惯,于是将老的eclipse项目导入到eclipse,网上有很多教程,看了很多博客都不行,一直报错,各种报错,现在终于好了,我们一起来看看怎么将ecl…

关于新版idea如何导入项目

现今有很多同学都发现idea怎么找不到import project这个按钮了,我也遇到了这个问题,经过研究发现,之前使用import project最关键还是在于project form Existing Sources。 而就在打开项目后,File-->New-->Project form Exi…

idea导入项目后没有被识别为maven项目的解决办法

开发中遇到了idea导入项目后没有被识别为maven项目,使用下面方法即可 1、首先点击工具栏最左边的 Help 再点击 Find Action ;或者使用快捷键 CtrlShiftA 2、接着在输入框中输入 maven projects ,会弹出一个 Add Maven Projects 选项&#xf…

IDEA导入web项目并启动

导入项目 依次点击idea左上角的File->Project Structure->project 修改SDK、Language level,选择自己电脑对应的jdk版本,为web的运行提供jdk的环境 第二步,依次点击Facts->Web 点击Artifacts->Web Application:Exploded->…

Java代码实例2,idea导入项目后,没有项目结构

目录 专栏导读一、idea导入项目后,没有项目结构二、解决方案1、点击file -> project structure -> Modules2、点击右上角加号 --> import Modules3、选择import modules from external model4、导入后,就可以显示项目的目录结构了。 专栏导读 &…