目录
3、创建配置类,将LSTM的各个超参数声明为变量,便于后续使用
一、数据集
自建数据集--【load.xlsx】。包含2列:
- date列(时间列 ,记录2022年6月2日起始至2023年12月31日为止,日度数据)
- price列(价格列,记录日度数据对应的某品牌衣服的价格,浮点数)
二、任务目标
实现基于时间序列的单特征价格预测
三、代码实现
1、从本地路径中读取数据文件
- read_excel函数读取Excel文件(read_csv用来读取csv文件),读取为DataFrame对象
- index_col='date'将'date'列设置为DataFrame的索引
- .values属性获取price列的值,pandas会将对应数据转换为NumPy数组
python
# 字符串前的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:
2、数据归一化
- 归一化:将原始数据的大小转化为**[0,1]之间**,采用最大-最小值归一化
- 数值过大,造成神经网络计算缓慢
- 在多特征任务中,存在多个特征属性,但神经网络会认为数值越小的,影响越小。所以可能关键属性A的值很小,不重要属性B的值却很大,造成神经网络的混淆
- scikit-learn的转换器通常期望输入是二维的,其中每一行代表一个样本,每一列代表一个特征
- prices**.reshape(-1, 1)用于确保 prices 是一个二维数组**,即使它只有一个特征列
- -1的意思是让 NumPy 自动计算该轴上的元素数量,以保持原始数据的元素总数不变
- fit方法****计算了数据中每个特征的最小值和最大值,这些值将被用于缩放
- transform方法使用这些统计信息来实际缩放数据,将其转换到 [0, 1] 范围内
python
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_prices = scaler.fit_transform(prices.reshape(-1, 1)) # 二维数组
# print(scaled_prices)
打印归一化后的价格数据:
3、创建配置类,将LSTM的各个超参数声明为变量,便于后续使用
- **timestep:**时间步长,滑动窗口大小
- **feature_size:**每个步长对应的特征数量,这里只使用1维,即每天的价格数据
- **batch_size:**批次大小,即一次性送入多少个数据(一时间步长为单位)进行训练
- **output_size:**单输出任务,输出层为1,预测未来1天的价格
- **hidden_size:**隐藏层大小,即神经元个数
- **num_layers:**神经网络的层数
- **learning_rate:**学习率
- **epochs:**迭代轮数,即总共要让神经网络训练多少轮,全部数据训练一遍成为一轮
- **best_loss:**记录损失
- **activation = 'relu':**定义激活函数使用relu
python
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()
4、创建时间序列数据
- 通过滑动窗口移动获取数据,时间步内数据作为特征数据,时间步外1个数据作为标签数据
- 通过序列的切片实现特征和标签的划分
- 通过np.array将数据转化为NumPy数组
python
# 创建时间序列数据
X, y = [], []
for i in range(len(scaled_prices) - config.timestep):
# 从当前索引i开始,取sequence_length个连续的价格数据点,并将其作为特征添加到列表 X 中。
X.append(scaled_prices[i: i + config.timestep])
# 将紧接着这sequence_length个时间点的下一个价格数据点作为目标添加到列表y中。
y.append(scaled_prices[i + config.timestep])
X = np.array(X)
print(X)
y = np.array(y)
print(y)
打印特征数据:
- 三维数组,X 是由多个二维数组(即多个时间步长的数据)组成的,加之本身是一个列表
- 每次迭代都会从 scaled_prices 中取出一个长度为 config.timestep 的连续子序列,并将其添加到 X 列表中
- 由于scaled_prices 本身是一个二维数组,所以每次取出的子序列也是一个二维数组,形状大致为 [config.timestep, features]
- 当多个这样的二维数组被添加到 X 列表中时,X 就变成了一个列表的列表,其中每个内部列表都是一个二维数组
- 它的形状将是 [n_samples - config.timestep, config.timestep, features],这是一个三维数组
打印标签数据:
- 二维数组,y 是由单个数据点(即单个时间步长的数据)组成的,所以它保持为二维数组
- 从 scaled_prices 中取出一个单独的数据点(即一个二维数组中的一行),并将其添加到 y 列表中
- y 列表中的每个元素都是一个一维数组(或可以看作是一个具有多个特征的向量)
- 它的形状将是 [n_samples - config.timestep, features],这仍然是一个二维数组
5、划分数据集
- 按照9:1的比例划分训练集和测试集
- train_test_split:是sklearn.model_selection模块中的一个函数,用于将数据集随机划分为训练集和测试集
- shuffle=False:表示在划分数据之前不进行随机打乱,意味着数据会按照其原始顺序进行划分
- 因为时间序列数据具有时序性,用过去时间数据预测新时间数据,要保证时间有序
- 测试数据为时间序列的末尾数据
python
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, shuffle=False)
6、定义LSTM网络
(1)创建顺序模型实例
python
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是批次中样本的数量 ,它在模型训练时会自动确定(根据你传递给模型的批次数据大小)
python
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:指定了该全连接层的输出单元数量
python
model.add(Dense(config.output_size))
- 由于此例中,全连接层的大小为1,因此LSTM层输出的三维张量在经过全连接层后将被压缩成一个二维张量
- (batch_size, 1)这样的形状
7、编译LSTM模型
- model.compile():这个方法是Keras模型的一个函数,用于配置模型训练前的参数
- optimizer='adam':这里指定了使用Adam优化器来训练模型
- loss='mean_squared_error':这里指定了损失函数为均方误差(Mean Squared Error, MSE)
python
model.compile(optimizer='adam', loss='mean_squared_error')
8、训练模型
- 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 对象是一个记录训练过程中信息的字典,包含了训练过程中的损失值和评估指标(如果有的话)
python
history = model.fit(x=X_train, y=y_train, epochs=config.epochs, batch_size=config.batch_size, verbose=2)
9、模型预测
- model.predict():是 Keras 模型的一个函数,它根据提供的输入数据,给出模型对于这些数据的预测结果
python
predictions = model.predict(X_test)
10、数据反归一化
- 当模型训练完成后并进行预测时,预测出的值会是缩放后的值(即按照训练数据缩放的比例)
- 为了得到原始的比例或范围,需要使用缩放器的 inverse_transform 方法来将这些缩放后的值转换回原始的比例或范围
python
y_test_true_unnormalized = scaler.inverse_transform(y_test)
y_test_preds_unnormalized = scaler.inverse_transform(predictions)
- 确保模型的预测结果和真实的测试集标签都在同一个比例或范围内,从而可以准确地评估模型的性能,并以更直观、更易于理解的方式呈现预测结果
11、绘制图像
python
# 设置图形的大小为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()
12、完整版代码
python
import pandas as pd
import numpy as np
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from matplotlib import pyplot as plt
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
class Config():
timestep = 7
hidden_size = 128
batch_size = 1
output_size = 1
epochs = 500
feature_size = 1
activation = 'relu'
config = Config()
# dataframe对象
qy_data = pd.read_excel(r'E:\load.xlsx', index_col='date')
# print(qy_data)
# numpy数组 一维
prices = qy_data['price'].values
# print(prices)
scaler = MinMaxScaler()
# 归一化后变成二维数组
scaled_prices = scaler.fit_transform(prices.reshape(-1, 1))
# print(scaled_prices)
# Create time series data
X, y = [], []
for i in range(len(scaled_prices) - config.timestep):
X.append(scaled_prices[i: i + config.timestep])
y.append(scaled_prices[i + config.timestep])
X = np.array(X)
# print(X)
y = np.array(y)
# print(y)
# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, shuffle=False)
# Define the LSTM mode
model = Sequential()
model.add(LSTM(activation=config.activation, units=config.hidden_size, input_shape=(config.timestep, config.feature_size)))
model.add(Dense(config.output_size))
# Compile the model
# adam默认学习率是0.01
model.compile(optimizer='adam', loss='mean_squared_error')
model.save('LSTM.h5')
# Train the model
history = model.fit(x=X_train, y=y_train, epochs=config.epochs, batch_size=config.batch_size, verbose=2)
# Predictions
predictions = model.predict(X_test)
# Inverse transform predictions and true values
y_test_true_unnormalized = scaler.inverse_transform(y_test)
y_test_preds_unnormalized = scaler.inverse_transform(predictions)
# Plot true values and predictions
plt.figure(figsize=(10, 5))
plt.plot(y_test_true_unnormalized, label='True Values', marker='o')
plt.plot(y_test_preds_unnormalized, label='Predictions', marker='x')
plt.title('Comparison of True Values and Predictions')
plt.xlabel('Time Steps')
plt.ylabel('Prices')
plt.legend()
plt.show()