【Python】PyTorchでUSDJPYの価格予測をしてみた

PyTorchを初めて使い、USDJPYの価格予測をしてみました.
色々、試行錯誤しましたが、損失があまり変わらないです.
移動平均線みたいな予測になってますね.
過学習かどうかの判断が難しいな...

```python
%matplotlib inline

import matplotlib.pyplot as plt
import pandas as pd
```


```python
pd.set_option('display.max_columns', None)
```


```python
df = pd.read_csv('candle_d_dmi_close_5.csv')
```


```python
df.head()
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>time</th>
      <th>volume</th>
      <th>open</th>
      <th>high</th>
      <th>low</th>
      <th>close</th>
      <th>spread</th>
      <th>tr</th>
      <th>plus_dm</th>
      <th>minus_dm</th>
      <th>smoothed_tr</th>
      <th>smoothed_plus_dm</th>
      <th>smoothed_minus_dm</th>
      <th>plus_di</th>
      <th>minus_di</th>
      <th>dx</th>
      <th>adx</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2016-09-01</td>
      <td>0.0</td>
      <td>103.409</td>
      <td>103.996</td>
      <td>103.058</td>
      <td>103.230</td>
      <td>0.017</td>
      <td>0.938</td>
      <td>0.508</td>
      <td>0.000</td>
      <td>0.938</td>
      <td>0.169</td>
      <td>0.000</td>
      <td>18.053</td>
      <td>0.000</td>
      <td>100.000</td>
      <td>100.000</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2016-09-02</td>
      <td>0.0</td>
      <td>103.238</td>
      <td>104.319</td>
      <td>102.799</td>
      <td>103.761</td>
      <td>0.265</td>
      <td>1.520</td>
      <td>0.323</td>
      <td>0.000</td>
      <td>1.132</td>
      <td>0.221</td>
      <td>0.000</td>
      <td>19.484</td>
      <td>0.000</td>
      <td>100.000</td>
      <td>100.000</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2016-09-05</td>
      <td>0.0</td>
      <td>104.047</td>
      <td>104.107</td>
      <td>103.144</td>
      <td>103.405</td>
      <td>0.044</td>
      <td>0.963</td>
      <td>0.000</td>
      <td>0.000</td>
      <td>1.076</td>
      <td>0.147</td>
      <td>0.000</td>
      <td>13.669</td>
      <td>0.000</td>
      <td>100.000</td>
      <td>100.000</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2016-09-06</td>
      <td>0.0</td>
      <td>103.398</td>
      <td>103.805</td>
      <td>101.929</td>
      <td>102.011</td>
      <td>0.014</td>
      <td>1.876</td>
      <td>0.000</td>
      <td>1.215</td>
      <td>1.342</td>
      <td>0.098</td>
      <td>0.405</td>
      <td>7.302</td>
      <td>30.169</td>
      <td>61.026</td>
      <td>87.009</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2016-09-07</td>
      <td>0.0</td>
      <td>101.996</td>
      <td>102.128</td>
      <td>101.205</td>
      <td>101.737</td>
      <td>0.010</td>
      <td>0.923</td>
      <td>0.000</td>
      <td>0.724</td>
      <td>1.203</td>
      <td>0.065</td>
      <td>0.511</td>
      <td>5.434</td>
      <td>42.518</td>
      <td>77.336</td>
      <td>83.784</td>
    </tr>
  </tbody>
</table>
</div>




```python
# import
%matplotlib inline

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from sklearn.model_selection import train_test_split
```


```python
# Dataset
class TimeSeriesDataset(Dataset):
    def __init__(self, X, y):
        self.X = torch.tensor(X, dtype=torch.float32)
        self.y = torch.tensor(y, dtype=torch.float32)
        
    def __len__(self):
        return len(self.X)
    
    def __getitem__(self, idx):
        return self.X[idx], self.y[idx]
```


```python
# Model
class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super(LSTMModel, self).__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)
        
    def forward(self, x):
        # LSTMの初期状態を初期化
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        
        # LSTM出力
        out, _ = self.lstm(x, (h0, c0))
        
        # 最後のタイムステップの出力を使用
        out = self.fc(out[:, -1, :])
        return out
```


```python
# Sequence
def create_sequences(X, y, seq_length):
    X_seq, y_seq = [], []
    for i in range(len(X) - seq_length):
        X_seq.append(X[i:i+seq_length])
        y_seq.append(y[i+seq_length])  # t+1の予測
    return np.array(X_seq), np.array(y_seq)
```


```python
# Main
feature_columns = ['close', 'plus_di', 'minus_di']
target_column = 'close'

sequence_lens = range(200, 201, 10)
split_ratio = 0.8
batch_size = 1000

hidden_sizes = range(200, 201, 10)
num_layers = 3
output_size = 1
num_epochs = 100

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# データ読み込み
# df = pd.read_csv('your_data.csv')

X = df[feature_columns].values
y = df[target_column].values.reshape(-1, 1)

# データの前処理（スケーリング）
X_scaler = StandardScaler()
y_scaler = StandardScaler()

X_scaled = X_scaler.fit_transform(X)
y_scaled = y_scaler.fit_transform(y)

for sequence_len in sequence_lens:
    # シーケンスデータの作成
    X_seq, y_seq = create_sequences(X_scaled, y_scaled, sequence_len)
    
    # トレーニングデータとテストデータに分割
    split_idx = int(len(X_seq) * split_ratio)
    
    X_train, X_test = X_seq[:split_idx], X_seq[split_idx:]
    y_train, y_test = y_seq[:split_idx], y_seq[split_idx:]
    
    # データローダーの作成
    train_dataset = TimeSeriesDataset(X_train, y_train)
    test_dataset = TimeSeriesDataset(X_test, y_test)
    
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False)
    test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
    
    for hidden_size in hidden_sizes:
        print(f"Sequence length: {sequence_len}, Hidden size: {hidden_size}")
        
        # モデルの定義
        model = LSTMModel(X_train.shape[2], hidden_size, num_layers, output_size).to(device)
        
        # 損失関数
        criterion = nn.MSELoss()
        optimizer = optim.Adam(model.parameters(), lr=0.001)
        
        # 訓練・テスト
        train_losses, test_losses = [], []
        
        for epoch in range(num_epochs):
            # 訓練
            model.train()
        
            train_loss = 0
            
            for X_batch, y_batch in train_loader:
                X_batch, y_batch = X_batch.to(device), y_batch.to(device)
                
                # フォワードパス
                outputs = model(X_batch)
        
                # 損失
                loss = criterion(outputs, y_batch)
                
                # バックワードパスと最適化
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                
                train_loss += loss.item()
            
            # テスト
            model.eval()
            
            test_loss = 0
            
            with torch.no_grad():
                for X_batch, y_batch in test_loader:
                    X_batch, y_batch = X_batch.to(device), y_batch.to(device)
                    
                    outputs = model(X_batch)
        
                    # 損失
                    loss = criterion(outputs, y_batch)
                    
                    test_loss += loss.item()
            
            # 損失
            train_losses.append(train_loss / len(train_loader))
            test_losses.append(test_loss / len(test_loader))
        
            if (epoch+1) % 10 == 0:
                print(
                    f'Epoch [{epoch+1:03}/{num_epochs:03}], '
                    f'Train Loss: {train_losses[-1]:.4f}, '
                    f'Test Loss: {test_losses[-1]:.4f}'
                )
    
        # 予測・評価
        model.eval()
        
        predictions, actuals = [], []
        
        # 予測
        with torch.no_grad():
            for X_batch, y_batch in test_loader:
                X_batch = X_batch.to(device)
        
                outputs = model(X_batch)
                
                predictions.extend(outputs.cpu().numpy())
                actuals.extend(y_batch.cpu().numpy())
        
        # スケールを元に戻す
        predictions = y_scaler.inverse_transform(np.array(predictions))
        actuals = y_scaler.inverse_transform(np.array(actuals))
        
        # 評価
        mse = np.mean((predictions - actuals) ** 2)
        rmse = np.sqrt(mse)
        mae = np.mean(np.abs(predictions - actuals))
        
        print(f'Mean Squared Error     : {mse:.4f}')
        print(f'Root Mean Squared Error: {rmse:.4f}')
        print(f'Mean Absolute Error    : {mae:.4f}')
    
        # 可視化
        plt.figure(figsize=(20, 12))
        
        # 訓練・テストの損失
        plt.subplot(2, 1, 1)
        plt.plot(train_losses, label='Training Loss')
        plt.plot(test_losses, label='Testing Loss')
        plt.title('Model Loss')
        plt.xlabel('Epochs')
        plt.ylabel('Loss')
        plt.legend()
        
        # 実績・予測
        plt.subplot(2, 1, 2)
        plt.plot(actuals, label='Actual Price')
        plt.plot(predictions, label='Predicted Price')
        plt.title('Price Prediction')
        plt.xlabel('Time Steps')
        plt.ylabel('Price')
        plt.legend()
        
        plt.tight_layout()
        plt.show()
```

    Sequence length: 200, Hidden size: 200
    Epoch [010/100], Train Loss: 0.0606, Test Loss: 0.1134
    Epoch [020/100], Train Loss: 0.0379, Test Loss: 0.1359
    Epoch [030/100], Train Loss: 0.0118, Test Loss: 0.0716
    Epoch [040/100], Train Loss: 0.0069, Test Loss: 0.0334
    Epoch [050/100], Train Loss: 0.0047, Test Loss: 0.0221
    Epoch [060/100], Train Loss: 0.0036, Test Loss: 0.0162
    Epoch [070/100], Train Loss: 0.0035, Test Loss: 0.0160
    Epoch [080/100], Train Loss: 0.0034, Test Loss: 0.0162
    Epoch [090/100], Train Loss: 0.0033, Test Loss: 0.0156
    Epoch [100/100], Train Loss: 0.0033, Test Loss: 0.0152
    Mean Squared Error     : 2.8465
    Root Mean Squared Error: 1.6871
    Mean Absolute Error    : 1.2243
    

![output_8_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498946/de5be0fc-9383-4144-a14e-6420e491afa2.png)