【模型实现】基于Python的LSTM网络实现单特征预测回归任务(TensorFlow) 利用Python语言中的TensorFlow库搭建LSTM实现价格预测

单特征:数据集中只包含2列,时间列+价格列,仅利用价格来预测价格

目录

一、数据集

二、任务目标

三、代码实现、

1、创建配置类,将LSTM的各个超参数声明为变量,便于后续使用

2、从本地路径中读取数据文件

3、划分数据集

4、数据归一化

 5、创建时间序列数据

(1)训练集

(2)测试集

6、调整数据形状以适应LSTM的输入要求

7、定义LSTM网络

(1)创建顺序模型实例

(2)添加LSTM层

(3)添加全连接层

8、编译LSTM模型

9、训练模型

10、模型预测

11、数据反归一化

12、绘制图像


一、数据集

自建数据集--【load.xlsx】。包含2列:

  • date列时间列,记录2022年6月2日起始至2023年12月31日为止,日度数据
  • price列价格列,记录日度数据对应的某品牌衣服的价格,浮点数)

二、任务目标

实现基于时间序列的单特征价格预测

三、代码实现、

1、创建配置类,将LSTM的各个超参数声明为变量,便于后续使用

  • timestep:时间步长,滑动窗口大小
  • feature_size:每个步长对应的特征数量,这里只使用1维,即每天的价格数据
  • batch_size:批次大小,即一次性送入多少个数据(一时间步长为单位)进行训练
  • output_size:单输出任务,输出层为1,预测未来1天的价格
  • hidden_size:隐藏层大小,即神经元个数
  • num_layers:神经网络的层数
  • learning_rate:学习率
  • epochs:迭代轮数,即总共要让神经网络训练多少轮,全部数据训练一遍成为一轮
  • best_loss:记录损失
  • activation = 'relu':定义激活函数使用relu
class Config():
 timestep = 7 # 时间步长,滑动窗口大小
 feature_size = 1 # 每个步长对应的特征数量,这里只使用1维,每天的价格数据
 batch_size = 1 # 批次大小
 output_size = 1 # 单输出任务,输出层为1,预测未来1天的价格
 hidden_size = 128 # 隐藏层大小
 num_layers = 1 # lstm的层数
 learning_rate = 0.0001 # 学习率
 epochs = 500 # 迭代轮数
 model_name = 'lstm' # 模型名
 best_loss = 0 # 记录损失
 activation = 'relu' # 定义激活函数
config = Config()

2、从本地路径中读取数据文件

  • read_excel函数读取Excel文件(read_csv用来读取csv文件),读取为DataFrame对象
  • index_col='date''date'列设置为DataFrame的索引
  • .values属性获取price列的值,pandas会将对应数据转换为NumPy类型的一维数组
# 字符串前的r表示一个"原始字符串",raw string
# 文件路径中包含多个反斜杠。如果我们不使用原始字符串(即不使用r前缀),那么Python会尝试解析\U、\N等作为转义序列,这会导致错误
data = pd.read_excel(r'E:\load.xlsx', index_col='date')
# print(data)
prices = data['price'].values
# print(prices)

打印data:

打印prices:

3、划分数据集

  • 按照9:1的比例将数据划分为训练集和测试集
  • train_test_split 是一个常用于数据集分割的函数,通常在机器学习中用于将数据分为训练集和测试集
  • shuffle 参数的作用是控制数据在分割前是否需要随机打乱
    • 当设置为 True 时,数据会被随机打乱,然后按照指定的比例分割成训练集和测试集
    • 当 shuffle=False 时,数据将按照它们原有的顺序直接分割。这意味着数据集中的元素将保持它们在原始数据集中的相对顺序
    • 在时间序列数据的情况下,这通常是必须的,因为时间序列数据具有时间依赖性,如果随机打乱,会破坏数据的时间顺序,从而影响模型学习到的时间依赖关系
train, test = train_test_split(prices, test_size=0.1, shuffle=False)

 打印训练集train的部分数据(一维数组):

4、数据归一化

  • 归一化:将原始数据的大小转化为[0,1]之间,采用最大-最小值归一化
    • 数值过大,造成神经网络计算缓慢
    • 在多特征任务中,存在多个特征属性,但神经网络会认为数值越小的,影响越小。所以可能关键属性A的值很小,不重要属性B的值却很大,造成神经网络的混淆
  • scikit-learn的转换器通常期望输入是二维的,其中每一行代表一个样本,每一列代表一个特征
    • prices.reshape(-1, 1) 用于确保 prices 是一个二维数组,即使它只有一个特征列
    • -1的意思是让 NumPy 自动计算该轴上的元素数量,以保持原始数据的元素总数不变
    • fit方法计算了数据中每个特征的最小值和最大值,这些值将被用于缩放
    • transform方法使用这些统计数据来实际缩放数据,将其转换到 [0, 1] 范围内
  • 训练集上使用fit_transform拟合缩放器,然后在测试集上使用transform以确保使用相同的缩放参数
    • transform 方法使用从训练数据中学习到的最小值和最大值(从训练数据中得到的相同缩放参数)来转换测试数据,确保测试数据使用相同的缩放参数
    • 这样做的目的是保持训练集和测试集的一致性,这样模型在预测时使用的输入数据与训练时看到的输入数据具有相同的尺度
    • 避免数据泄露:如果在测试集上使用 fit 方法,那么测试集的数据将影响模型的训练过程,这可能导致模型过度拟合到测试集,从而影响模型的泛化能力
# 确保训练集和测试集的形状是二维数组
train = train.reshape(-1, 1)
test = test.reshape(-1, 1)
scaler = MinMaxScaler(feature_range=(0, 1))
train_scaled = scaler.fit_transform(train) # 对训练数据进行拟合和转换
test_scaled = scaler.transform(test) # 使用同样的缩放参数转换测试数据
# print(train_prices)

打印归一化后的训练集价格数据(二维数组):

 5、创建时间序列数据

(1)训练集

  • 通过滑动窗口移动获取数据,时间步内数据作为特征数据,时间步外1个数据作为标签数据
  • 通过序列的切片实现特征和标签的划分
  • 通过np.array将数据转化为NumPy数组

# 创建时间序列数据
X_train, y_train = [], []
for i in range(len(train_scaled) - config.timestep):
 # 从当前索引i开始,取sequence_length个连续的价格数据点,并将其作为特征添加到列表X_train中。
 X_train.append(train_scaled[i: i + config.timestep])
 # 将紧接着这sequence_length个时间点的下一个价格数据点作为目标添加到列表y_train中。
 y_train.append(train_scaled[i + config.timestep])
X_train = np.array(X_train)
print(X_train)
y_train = np.array(y_train)
print(y_train)

打印特征数据: 

  • 三维数组X_train是由多个二维数组(即多个时间步长的数据)组成的,加之本身是一个列表
  • 每次迭代都会train_scaled 中取出一个长度为 config.timestep连续子序列,并将其添加到 X 列表中
  • 由于 train_scaled 本身是一个二维数组,所以每次取出的子序列也是一个二维数组,形状大致为 [config.timestep, features]
  • 当多个这样的二维数组被添加到 X 列表中时X_train 就变成了一个列表的列表,其中每个内部列表都是一个二维数组
  • 它的形状将是 [n_samples - config.timestep, config.timestep, features],这是一个三维数组

打印标签数据:

  • 二维数组y_train 是由单个数据点(即单个时间步长的数据)组成的,所以它保持为二维数组
  • train_scaled 中取出一个单独的数据点(即一个二维数组中的一行),并将其添加到 y 列表中
  • y 列表中的每个元素都是一个一维数组(或可以看作是一个具有多个特征的向量)
  • 它的形状将是 [n_samples - config.timestep, features],这仍然是一个二维数组

(2)测试集

  • 测试集同训练集的设置一样
X_test, y_test = [], []
for i in range(len(test_scaled) - config.timestep):
 X_test.append(test_scaled[i: i + config.timestep])
 y_test.append(test_scaled[i + config.timestep])
X_test, y_test = np.array(X_test), np.array(y_test)

6、调整数据形状以适应LSTM的输入要求

  • 将时间序列数据从原始的形状转换成一个三维数组,这是LSTM网络所期望的输入格式,(样本数量, 时间步长, 特征数量)
    • 第一个维度(X_train.shape[0] 或 X_test.shape[0]):表示样本的数量,即有多少个时间序列
    • 第二个维度(X_train.shape[1] 或 X_test.shape[1]):表示每个时间序列中的时间步长数量,即序列的长度
    • 第三个维度(config.feature_size):表示每个时间步长中的特征数量
  • shape 属性是一个 NumPy 数组或 Pandas DataFrame 等数据结构的方法,它返回数据的形状,即各个维度的大小
    • X_train.shape[0] 或 X_test.shape[0]:第一个元素表示数组的第一维大小,通常对应于样本的数量(即有多少个时间序列数据点)
    • X_train.shape[1] 或 X_test.shape[1]:第二个元素表示数组的第二维大小,通常对应于每个时间序列中的时间步长数量(即每个序列包含多少个时间点)
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], config.feature_size))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], config.feature_size))

打印测试集特征X_train数据:

7、定义LSTM网络

(1)创建顺序模型实例

model = Sequential()

(2)添加LSTM层

  • LSTM:这是 Keras 中提供的 LSTM 层的类。通过调用这个类,创建一个 LSTM 层
  • activation=config.activation:这设置了 LSTM 层中使用的激活函数
  • units=config.hidden_size:这设置了LSTM 层中的隐藏单元数量
  • input_shape=(config.timestep, config.feature_size):这定义了输入数据的形状,是一个元组
    • 告诉模型,输入数据应该是一个形状为[batch_size, config.timestep, config.feature_size]的三维
    • 其中batch_size是批次中样本的数量,它在模型训练时会自动确定(根据你传递给模型的批次数据大小)
model.add(LSTM(activation=config.activation, units=config.hidden_size, input_shape=(config.timestep, config.feature_size)))
  •  LSTM层的输出是一个三维张量,其形状通常为(seq_len, batch_size, num_directions * hidden_size)
    • seq_len表示序列长度,即时间序列展开的步数
    • batch_size表示数据批次的大小,即一次性输入到LSTM层的数据样本数量
    • num_directions * hidden_size表示隐藏层的输出特征维度
      • 对于单向LSTM,num_directions为1
      • 对于双向LSTM,num_directions为2。hidden_size则是隐藏层节点数,即LSTM单元中隐藏状态的维度
    • 含义:LSTM层的输出包含了每个时间步的隐藏状态

(3)添加全连接层

  • Dense:是 Keras 中用于创建全连接层的类,也就是每个输入节点与输出节点之间都连接有一个权重
  • config.output_size:指定了该全连接层的输出单元数量
model.add(Dense(config.output_size))
  • 由于此例中,全连接层的大小为1,因此LSTM层输出的三维张量在经过全连接层后将被压缩成一个二维张量
  • (batch_size, 1)这样的形状

8、编译LSTM模型

  • model.compile():这个方法是Keras模型的一个函数,用于配置模型训练前的参数
  • optimizer='adam':这里指定了使用Adam优化器来训练模型
  • loss='mean_squared_error':这里指定了损失函数为均方误差(Mean Squared Error, MSE)
model.compile(optimizer='adam', loss='mean_squared_error')

9、训练模型

  • model.fit():是 Keras 模型的一个函数,用于训练模型。它将根据提供的训练数据 X_train 和对应的标签 y_train通过多次迭代(epochs)来训练模型。
  • x=X_train:指定了训练数据的输入
  • y=y_train:指定了训练数据的标签(或目标值)
  • epochs=config.epochs:指定了训练过程中数据集的完整遍历次数。
  • batch_size=config.batch_size:指定了每次更新模型时使用的样本数
  • verbose=2:控制训练过程中的日志输出。verbose=2 表示每个 epoch 输出一行日志,显示训练过程中的损失值和评估指标(如果在编译时指定了评估指标)
  • history 对象是一个记录训练过程中信息的字典,包含了训练过程中的损失值和评估指标(如果有的话)
history = model.fit(x=X_train, y=y_train, epochs=config.epochs, batch_size=config.batch_size, verbose=2)

10、模型预测

  • model.predict():是 Keras 模型的一个函数,它根据提供的输入数据,给出模型对于这些数据的预测结果
predictions = model.predict(X_test)

11、数据反归一化

  • 当模型训练完成后并进行预测时,预测出的值会是缩放后的值(即按照训练数据缩放的比例)
  • 为了得到原始的比例或范围,需要使用缩放器的 inverse_transform 方法来将这些缩放后的值转换回原始的比例或范围
  • 这里的关键点是:
    • 归一化时需要 fit 方法来确定缩放参数,而反归一化时,由于已经知道了这些参数,只需要使用 inverse_transform 方法来逆转归一化过程。无论是训练集、测试集还是预测值,它们都使用相同的 inverse_transform 方法,因为它们都需要从相同的[0, 1]范围转换回原始的范围
    • 反归一化这里没有使用 fit 方法因为反归一化不需要重新计算任何参数,它只是逆转之前归一化的过程
y_test_true_unnormalized = scaler.inverse_transform(y_test)
y_test_preds_unnormalized = scaler.inverse_transform(predictions)
  • 确保模型的预测结果和真实的测试集标签都在同一个比例或范围内,从而可以准确地评估模型的性能,并以更直观、更易于理解的方式呈现预测结果

12、绘制图像

# 设置图形的大小为10x5单位
plt.figure(figsize=(10, 5))
# 绘制真实的测试集标签,使用圆圈('o')作为标记,并命名为'True Values' 
plt.plot(y_test_true_unnormalized, label='True Values', marker='o')
# 绘制模型的预测值,使用叉号('x')作为标记,并命名为'Predictions' 
plt.plot(y_test_preds_unnormalized, label='Predictions', marker='x')
# 设置图形的标题
plt.title('Comparison of True Values and Predictions')
# 设置x轴的标签
plt.xlabel('Time Steps')
# 设置y轴的标签
plt.ylabel('Prices')
# 显示图例 
plt.legend()
# 显示图形
plt.show()
作者:Star_KeyW原文地址:https://blog.csdn.net/qq_48185833/article/details/138370496

%s 个评论

要回复文章请先登录注册