### 使用Python实现RNN进行产品价格时间序列预测
#### 准备工作
在开始构建RNN模型之前,需要准备环境以及加载必要的库。通常情况下,会使用`pandas`来处理CSV文件中的数据,并利用`matplotlib`来进行可视化操作。
```python
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
import torch
from torch import nn, optim
```
#### 数据预处理
对于时间序列预测而言,首先要做的就是读取并清理数据集。这里假设有一个名为`product_prices.csv`的数据文件,其中包含了产品的月度销售价格记录[^5]。
```python
data = pd.read_csv('product_prices.csv')
# 假设 'Date' 是日期列名,将其转换成 datetime 类型
data['Date'] = pd.to_datetime(data['Date'])
# 设置日期列为索引
data.set_index('Date', inplace=True)
plt.figure(figsize=(10, 6))
plt.plot(data.index, data['Price'], label='Product Price Over Time')
plt.xlabel('Year-Month')
plt.ylabel('Price ($)')
plt.title('Historical Product Prices')
plt.legend()
plt.show()
```
#### 构建特征矩阵与标签向量
为了让RNN更好地学习到时间上的依赖关系,在此阶段应该把原始的时间序列转化为适合喂给神经网络的形式——即将连续几个时刻的价格作为输入X,下一个时刻的价格作为目标y。这种做法也叫做滑动窗口法[^2]。
```python
def create_dataset(dataset, look_back=1):
Xs, ys = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
Xs.append(a)
ys.append(dataset[i + look_back, 0])
return np.array(Xs), np.array(ys)
look_back = 3 # 可调整参数,表示考虑过去多少天的历史价格来做预测
dataset = data.values.astype('float32').reshape(-1, 1) # 转换成numpy数组形式
train_size = int(len(dataset) * 0.8)
test_size = len(dataset) - train_size
train, test = dataset[:train_size], dataset[train_size:]
# 创建训练集和测试集的特征矩阵与标签向量
train_X, train_y = create_dataset(train, look_back)
test_X, test_y = create_dataset(test, look_back)
# 对于PyTorch来说,还需要额外一步将这些数据转置以便适应batch-first的要求
train_X = torch.from_numpy(train_X).view(-1, 1, look_back)
train_y = torch.from_numpy(train_y).unsqueeze(dim=-1)
test_X = torch.from_numpy(test_X).view(-1, 1, look_back)
test_y = torch.from_numpy(test_y).unsqueeze(dim=-1)
```
#### 定义RNN模型架构
定义一个简单的两层RNN结构,每层有十个隐含单元。注意这里的`input_size`应当等于前面设定好的`look_back`值;而`hidden_size`则决定了每一层内部有多少个记忆细胞参与计算[^4]。
```python
class SimpleRNN(nn.Module):
def __init__(self, input_dim, hidden_dim, layer_dim, output_dim):
super(SimpleRNN, self).__init__()
self.hidden_dim = hidden_dim
self.layer_dim = layer_dim
self.rnn = nn.RNN(input_dim, hidden_dim, layer_dim, batch_first=True, nonlinearity='relu')
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
h0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).requires_grad_()
out, hn = self.rnn(x, h0.detach())
out = self.fc(out[:, -1, :])
return out
model = SimpleRNN(input_dim=look_back, hidden_dim=10, layer_dim=2, output_dim=1)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
```
#### 训练过程
接下来就可以正式进入训练环节了。在这个过程中,每次迭代都会随机抽取一批样本送入网络中进行前向传播、损失函数评估以及反向梯度下降更新权重等操作直到满足停止条件为止。
```python
num_epochs = 100
for epoch in range(num_epochs):
outputs = model(train_X.float())
optimizer.zero_grad()
loss = criterion(outputs, train_y.float())
loss.backward()
optimizer.step()
if (epoch+1)%10==0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
```
#### 测试效果
完成上述所有准备工作之后,最后一步自然是要看看所得到的结果究竟如何啦!
```python
with torch.no_grad():
predicted = model(test_X.float()).detach().numpy()
plt.figure(figsize=(10, 6))
plt.plot(range(len(predicted)), predicted, color="blue", marker="o", linestyle="-", linewidth=2, markersize=6, alpha=.7, label="Predicted")
plt.plot(range(len(test_y)), test_y.numpy(), color="red", marker="", linestyle="--", linewidth=2, alpha=.9, label="Actual")
plt.title("Comparison Between Predictions and Actual Values on Test Set")
plt.xlabel("Time Step")
plt.ylabel("Price ($)")
plt.legend(loc="best")
plt.grid(True)
plt.tight_layout()
plt.show()
```