机器学习实战——成品油价格预测

2024成品油价格预测

在成品油价格预测的研究中,数据的时间范围对模型的准确性与可靠性具有重要影响。考虑到2015年全球油价出现了大幅下跌2020年到2023年COVID-19疫情的爆发对全球经济、能源需求以及油价波动产生了深远的影响。基于上述考虑,预测2024年成品油价时选择2016年至2019年四年期间的数据作为模型的训练集。此段时间的数据较为稳定,未受极端油价波动或疫情等异常因素的影响,能够较为真实地反映常规经济环境下的成品油价格波动情况。

1.数据收集与处理

笔者选取了如下特征值:

  1. 国际原油价格    # 数据来源:https://www.eia.gov/dnav/pet/hist/RWTCD.htm                                                       https://www.eia.gov/dnav/pet/hist/RBRTED.htm
  2. 国内原油进口量  # 数据来源:国家统计局 国家海关总署 国家数据
    (2024年1-2月进口量8831,对官方合并后的数据采取估值[未采取机器学习中对缺失值使用的方法] 17年数据因多平台数据不一致可能存在误差)
  3. 汇率       # 数据来源:央行 (由于休息日没有汇率,故采用上一个工作日的数据)
  4. 历史油价    # 数据来源:发改委 题中数据与发改委数据一致

2.模型选择

LSTM(长短期记忆网络)具备学习长时间序列依赖的能力,独特设计使其能够在语音识别、机器翻译以及时间序列预测等任务中表现优异。而LSTM能够捕捉到时间序列中的非线性关系,这种非线性建模对受到多种因素影响(尤其是时间)的成品油价格进行预测有着很高的准确性。故选择长短期记忆网络作为机器学习模型。

3.代码编写

同时预测汽油柴油的代码如下:

#!/usr/bin/python3
# -*- coding:utf-8 -*-
# @author  : xiao6ming6
# @time    : 2024/12/22 18:27
# @function: the script is used to do predict oil prices
# @version : V1
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential # 部分版本要加src from keras.src.models import Sequential
from keras.layers import LSTM, Dense, Input # 部分版本 from keras.src.layers import LSTM, Dense, Input
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号

# 加载数据
train_df = pd.read_excel('data.xlsx', sheet_name='训练集')
test_df = pd.read_excel('data.xlsx', sheet_name='测试集')

# 数据预处理
scaler = MinMaxScaler(feature_range=(0, 1)) # 特征缩放
scaled_train = scaler.fit_transform(train_df.drop(columns=['调整日期']))
scaled_test = scaler.transform(test_df.drop(columns=['调整日期']))

# 函数用于创建时间序列数据集
def create_dataset(dataset, look_back=1): # 设置滑动窗口为1
    dataX, dataY = [], []
    for i in range(len(dataset)-look_back-1):
        a = dataset[i:(i+look_back), :]
        dataX.append(a)
        dataY.append(dataset[i + look_back, :2])  # 预测汽油和柴油价格
    return np.array(dataX), np.array(dataY)

look_back = 1
trainX, trainY = create_dataset(scaled_train, )
testX, testY = create_dataset(scaled_test, )

# 转换为LSTM输入格式 [样本数, 时间步长, 特征数]
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[2]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[2]))

# 构建LSTM模型
model = Sequential()
model.add(Input(shape=(1, trainX.shape[2])))
model.add(LSTM(50))
model.add(Dense(2))  # 输出两个预测值:汽油和柴油价格
model.compile(loss='mean_squared_error', optimizer='adam')

# 训练模型 verbose=0:不显示训练过程。verbose=1:显示进度条。verbose=2:每个 epoch 输出一行日志,显示当前 epoch 的训练进度和损失值
history = model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2) # 100轮 1个样本每次

# 预测
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)

# 反向转换预测值 将数据从标准化空间转换回原始数值范围
trainPredict = scaler.inverse_transform(np.concatenate((trainPredict, np.zeros((trainPredict.shape[0], scaled_train.shape[1]-2))), axis=1))[:, :2]
testPredict = scaler.inverse_transform(np.concatenate((testPredict, np.zeros((testPredict.shape[0], scaled_train.shape[1]-2))), axis=1))[:, :2]

# 绘制汽油价格的训练集和测试集的结果
plt.figure(figsize=(15, 10))

# 汽油训练集结果
train_dates = train_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(3, 1, 1)
plt.plot(train_dates, trainPredict[:, 0], label='训练集预测汽油价格')
plt.plot(train_dates, train_df['成品油价格(汽油)'][look_back:-1], label='训练集实际汽油价格')
plt.title('汽油价格预测(训练集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 汽油测试集结果
test_dates = test_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(3, 1, 2)
plt.plot(test_dates, testPredict[:, 0], label='测试集预测汽油价格')
plt.plot(test_dates, test_df['成品油价格(汽油)'][look_back:-1], label='测试集实际汽油价格')
plt.title('汽油价格预测(测试集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 训练损失曲线
plt.subplot(3, 1, 3)
plt.plot(history.history['loss'], label='训练损失')
plt.title('训练损失曲线')
plt.xlabel('轮次')
plt.ylabel('损失')
plt.legend()

plt.tight_layout()
plt.show()

# 绘制柴油价格的训练集和测试集的结果
plt.figure(figsize=(15, 10))

# 柴油训练集结果
plt.subplot(3, 1, 1)
plt.plot(train_dates, trainPredict[:, 1], label='训练集预测柴油价格')
plt.plot(train_dates, train_df['成品油价格(柴油)'][look_back:-1], label='训练集实际柴油价格')
plt.title('柴油价格预测(训练集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 柴油测试集结果
plt.subplot(3, 1, 2)
plt.plot(test_dates, testPredict[:, 1], label='测试集预测柴油价格')
plt.plot(test_dates, test_df['成品油价格(柴油)'][look_back:-1], label='测试集实际柴油价格')
plt.title('柴油价格预测(测试集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 训练损失曲线
plt.subplot(3, 1, 3)
plt.plot(history.history['loss'], label='训练损失')
plt.title('训练损失曲线')
plt.xlabel('轮次')
plt.ylabel('损失')
plt.legend()

plt.tight_layout()
plt.show()

应粉丝要求,只预测汽油的代码如下:

#!/usr/bin/python3
# -*- coding:utf-8 -*-
# @author  : xiao6ming6
# @time    : 2024/12/24 17:40
# @function: the script is used to predict gasoline prices
# @version : V2
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.src.models import Sequential
from keras.src.layers import LSTM, Dense, Input
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号

# 加载数据
train_df = pd.read_excel('data.xlsx', sheet_name='训练集')
test_df = pd.read_excel('data.xlsx', sheet_name='测试集')

# 数据预处理
scaler = MinMaxScaler(feature_range=(0, 1))  # 特征缩放
scaled_train = scaler.fit_transform(train_df.drop(columns=['调整日期']))
scaled_test = scaler.transform(test_df.drop(columns=['调整日期']))

# 函数用于创建时间序列数据集
def create_dataset(dataset, look_back=1):  # 设置滑动窗口为1
    dataX, dataY = [], []
    for i in range(len(dataset) - look_back - 1):
        a = dataset[i:(i + look_back), :]
        dataX.append(a)
        dataY.append(dataset[i + look_back, 0])  # 只预测汽油价格
    return np.array(dataX), np.array(dataY)

look_back = 1
trainX, trainY = create_dataset(scaled_train)
testX, testY = create_dataset(scaled_test)

# 转换为LSTM输入格式 [样本数, 时间步长, 特征数]
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[2]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[2]))

# 构建LSTM模型
model = Sequential()
model.add(Input(shape=(1, trainX.shape[2])))
model.add(LSTM(50))
model.add(Dense(1))  # 输出一个预测值:汽油价格
model.compile(loss='mean_squared_error', optimizer='adam')

# 训练模型
history = model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)

# 预测
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)

# 反向转换预测值
trainPredict = scaler.inverse_transform(
    np.concatenate((trainPredict, np.zeros((trainPredict.shape[0], scaled_train.shape[1] - 1))), axis=1)
)[:, 0]
testPredict = scaler.inverse_transform(
    np.concatenate((testPredict, np.zeros((testPredict.shape[0], scaled_train.shape[1] - 1))), axis=1)
)[:, 0]

# 绘图
plt.figure(figsize=(15, 10))

# 汽油训练集结果
train_dates = train_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(2, 1, 1)
plt.plot(train_dates, trainPredict, label='训练集预测汽油价格')
plt.plot(train_dates, train_df['成品油价格(汽油)'][look_back:-1], label='训练集实际汽油价格')
plt.title('汽油价格预测(训练集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 汽油测试集结果
test_dates = test_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(2, 1, 2)
plt.plot(test_dates, testPredict, label='测试集预测汽油价格')
plt.plot(test_dates, test_df['成品油价格(汽油)'][look_back:-1], label='测试集实际汽油价格')
plt.title('汽油价格预测(测试集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

plt.tight_layout()
plt.show()

只预测柴油价格的代码如下:

#!/usr/bin/python3
# -*- coding:utf-8 -*-
# @author  : xiao6ming6
# @time    : 2024/12/24 18:25
# @function: the script is used to predict diesel prices
# @version : V2
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.src.models import Sequential
from keras.src.layers import LSTM, Dense, Input
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号

# 加载数据
train_df = pd.read_excel('data.xlsx', sheet_name='训练集')
test_df = pd.read_excel('data.xlsx', sheet_name='测试集')

# 数据预处理
scaler = MinMaxScaler(feature_range=(0, 1))  # 特征缩放
scaled_train = scaler.fit_transform(train_df.drop(columns=['调整日期']))
scaled_test = scaler.transform(test_df.drop(columns=['调整日期']))

# 函数用于创建时间序列数据集
def create_dataset(dataset, look_back=1):  # 设置滑动窗口为1
    dataX, dataY = [], []
    for i in range(len(dataset) - look_back - 1):
        a = dataset[i:(i + look_back), :]
        dataX.append(a)
        dataY.append(dataset[i + look_back, 1])  # 只预测柴油价格
    return np.array(dataX), np.array(dataY)

look_back = 1
trainX, trainY = create_dataset(scaled_train)
testX, testY = create_dataset(scaled_test)

# 转换为LSTM输入格式 [样本数, 时间步长, 特征数]
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[2]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[2]))

# 构建LSTM模型
model = Sequential()
model.add(Input(shape=(1, trainX.shape[2])))
model.add(LSTM(50))
model.add(Dense(1))  # 输出一个预测值:柴油价格
model.compile(loss='mean_squared_error', optimizer='adam')

# 训练模型
history = model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)

# 预测
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)

# 反向转换预测值
trainPredict = scaler.inverse_transform(
    np.concatenate((np.zeros((trainPredict.shape[0], 1)), trainPredict, np.zeros((trainPredict.shape[0], scaled_train.shape[1] - 2))), axis=1)
)[:, 1]
testPredict = scaler.inverse_transform(
    np.concatenate((np.zeros((testPredict.shape[0], 1)), testPredict, np.zeros((testPredict.shape[0], scaled_train.shape[1] - 2))), axis=1)
)[:, 1]

# 绘图
plt.figure(figsize=(15, 10))

# 柴油训练集结果
train_dates = train_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(2, 1, 1)
plt.plot(train_dates, trainPredict, label='训练集预测柴油价格')
plt.plot(train_dates, train_df['成品油价格(柴油)'][look_back:-1], label='训练集实际柴油价格')
plt.title('柴油价格预测(训练集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

# 柴油测试集结果
test_dates = test_df['调整日期'][look_back:-1]  # 调整日期索引,使其与预测值长度一致
plt.subplot(2, 1, 2)
plt.plot(test_dates, testPredict, label='测试集预测柴油价格')
plt.plot(test_dates, test_df['成品油价格(柴油)'][look_back:-1], label='测试集实际柴油价格')
plt.title('柴油价格预测(测试集)')
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend()

plt.tight_layout()
plt.show()

4.预测及泛化效果

4.1同时预测效果:

汽油价格预测
柴油价格预测

4.2单独预测效果

汽油
柴油

5.改进方向

5.1模型改进方向

扩展数据来源: 加强对节假日、季节性变化、政策调控等因素的搜集和量化分析,以增强模型对复杂环境的适应能力。
改进模型: 结合其他时间序列预测模型(如GRU、Transformer等),探索模型集成或混合建模的策略,充分利用不同模型的优势以提高预测性能。
异常波动处理: 通过引入异常检测机制或采用数据增强技术,丰富模型对高波动时期的学习能力,减少预测误差。建立实时数据更新与模型反馈机制,以便快速适应市场环境的变化,进一步提高预测的时效性和可靠性。

5.2代码改进方向

加入相关指标: 在使用模型预测后加入准确率,MSE,R2 等指标进一步解释模型性能
避免过拟合: 加入正则化、早停、数据增强等方法处理可能因过于复杂的架构或不足的训练数据而出现过拟合问题,使用交叉验证检查模型的泛化能力。


附录

文中所用数据:https://xiao6ming6.lanzoub.com/ihRAN2izaech
参考文档(简略版):https://xiao6ming6.lanzoub.com/iZkXw2izaebg


版权声明

本文为原创内容,版权归作者所有。未经作者明确许可,禁止转载、引用或用于商业用途。

如需转载或使用,请联系作者获取授权。

版权所有 xiao6ming6 © 2024

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值