0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DQNの精度向上のアプローチを試す

Posted at

はじめに

今回は、前回の記事の続きになります。

前回の問題点

  • Epsilon(探索率)が常に一定だった(完全ランダムに行動を選択している)
  • 学習が進んでいない

やること

  • 学習が進むにつれて、Epsilon(探索率)を減少させる
  • 報酬が最大化するようにDQNの精度を向上させる

使用コード(前回からの変更箇所のみ)

こちらのコードでは、DQNAgentを定義して、実際に学習させています。

主な変更点はこちらです。

  • Epsilon(探索率)を2乗することで、探索率が少しずつ減少する仕組みを導入
  • 報酬が最大化するようにDQNの精度向上
    • ターゲットネットワークの導入
    • 学習率を変更(0.001→0.0005)
    • エピソード数を変更(1000→10000)
    • ミニバッチサイズの変更(32→64)
    • 経験リプレイのバッファサイズの変更(2000→5000)
import torch.nn as nn
import torch.optim as optim
from collections import deque

class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 128)
        self.fc2 = nn.Linear(128, 128)
        self.fc3 = nn.Linear(128, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

class DQNAgent:
    def __init__(self, state_size, action_size):
        self.state_size = state_size
        self.action_size = action_size
        self.memory = deque(maxlen=5000)
        self.gamma = 0.95  # 割引率
        self.epsilon = 1.0  # 探索率
        self.epsilon_min = 0.01
        self.epsilon_decay = 0.995
        self.learning_rate = 0.0005

        self.model = DQN(state_size, action_size)
        self.target_model = DQN(state_size, action_size)  # ターゲットネットワーク
        self.update_target_model()  # ターゲットネットワークの初期化
        self.optimizer = optim.Adam(self.model.parameters(), lr=self.learning_rate)
        self.criterion = nn.MSELoss()

    def act(self, state):
        if np.random.rand() <= self.epsilon:
            return random.randrange(self.action_size)
        state = torch.FloatTensor(state).unsqueeze(0)
        actions = self.model(state)
        return torch.argmax(actions).item()

    def update_target_model(self):
        self.target_model.load_state_dict(self.model.state_dict())  # ターゲットネットワークを更新

    def train(self, batch_size):
        minibatch = random.sample(self.memory, batch_size)
        for state, action, reward, next_state, done in minibatch:
            target = reward
            if not done:
                target += self.gamma * torch.max(self.model(torch.FloatTensor(next_state).unsqueeze(0))).item()
            target_f = self.model(torch.FloatTensor(state).unsqueeze(0))
            target_f[0][action] = target
            self.optimizer.zero_grad()
            loss = self.criterion(target_f, self.model(torch.FloatTensor(state).unsqueeze(0)))
            loss.backward()
            self.optimizer.step()

    def update_epsilon(self):
        if self.epsilon > self.epsilon_min:
            self.epsilon *= self.epsilon_decay  # Epsilonを減少させる
import matplotlib.pyplot as plt

# 環境とDQNの初期化
env = TaxiDispatchEnv(df)
set_seed(42)
agent = DQNAgent(state_size=2, action_size=5)

# 報酬の記録リスト
total_rewards = []

# 学習ループ
episodes = 10000  # エピソード数
batch_size = 64  # 学習用のミニバッチサイズ

for e in range(episodes):
    state = env.reset()
    total_reward = 0  # そのエピソードの合計報酬
    for time in range(100):
        action = agent.act(state)  # 行動を選択
        next_state, reward, done, _ = env.step(action)  # 環境を進める
        agent.memory.append((state, action, reward, next_state, done))  # 経験を保存
        state = next_state  # 状態を更新
        total_reward += reward  # 報酬を加算
        if done:
            break

    total_rewards.append(total_reward)  # 報酬を記録

    if len(agent.memory) > batch_size:
        agent.train(batch_size)  # DQNの学習を実行

    agent.update_epsilon()

    if e % 10 == 0:  # 10エピソードごとにターゲットネットワークを更新
        agent.update_target_model()

    if e % 100 == 0:
        print(f"Episode {e}, Total Reward: {total_reward}, Epsilon: {agent.epsilon}")

# 学習曲線をプロット
plt.figure(figsize=(10,5))
plt.plot(total_rewards, label="Total Reward per Episode", alpha=0.5)
plt.plot(pd.Series(total_rewards).rolling(100).mean(), label="Moving Average (100 episodes)", linewidth=3, color="red")
plt.xlabel("Episode")
plt.ylabel("Total Reward")
plt.title("Training Progress of DQN for Taxi Dispatch")
plt.legend()
plt.show()

分析結果

Episode 0, Total Reward: 20341.369099999996, Epsilon: 0.995
Episode 100, Total Reward: 9587.634300000003, Epsilon: 0.6027415843082742
Episode 200, Total Reward: 1285.6542000000002, Epsilon: 0.36512303261753626
Episode 300, Total Reward: 22009.842500000002, Epsilon: 0.2211807388415433
Episode 400, Total Reward: 6669.531500000002, Epsilon: 0.13398475271138335
Episode 500, Total Reward: 18566.231999999993, Epsilon: 0.0811640021330769
Episode 600, Total Reward: 11333.869199999997, Epsilon: 0.04916675299948831
Episode 700, Total Reward: 8789.645799999998, Epsilon: 0.029783765425331846
Episode 800, Total Reward: 9440.040000000006, Epsilon: 0.018042124582040707
Episode 900, Total Reward: 16137.161299999992, Epsilon: 0.010929385683282892
Episode 1000, Total Reward: 491.6718, Epsilon: 0.00998645168764533
Episode 1100, Total Reward: 17710.511599999998, Epsilon: 0.00998645168764533
Episode 1200, Total Reward: 20286.200399999994, Epsilon: 0.00998645168764533
Episode 1300, Total Reward: 10290.335699999998, Epsilon: 0.00998645168764533
Episode 1400, Total Reward: 16128.985200000003, Epsilon: 0.00998645168764533
Episode 1500, Total Reward: 5646.302700000001, Epsilon: 0.00998645168764533
Episode 1600, Total Reward: 13463.330900000003, Epsilon: 0.00998645168764533
Episode 1700, Total Reward: 27372.815599999998, Epsilon: 0.00998645168764533
Episode 1800, Total Reward: 16729.798799999997, Epsilon: 0.00998645168764533
Episode 1900, Total Reward: 5966.1325000000015, Epsilon: 0.00998645168764533
Episode 2000, Total Reward: 30143.304699999993, Epsilon: 0.00998645168764533
Episode 2100, Total Reward: 26198.2688, Epsilon: 0.00998645168764533
Episode 2200, Total Reward: 30268.187, Epsilon: 0.00998645168764533
Episode 2300, Total Reward: 3028.933399999999, Epsilon: 0.00998645168764533
Episode 2400, Total Reward: 9231.432700000001, Epsilon: 0.00998645168764533
...
Episode 9600, Total Reward: 39233.26070000001, Epsilon: 0.00998645168764533
Episode 9700, Total Reward: 1329.4756999999997, Epsilon: 0.00998645168764533
Episode 9800, Total Reward: 31979.492200000004, Epsilon: 0.00998645168764533
Episode 9900, Total Reward: 23788.365300000005, Epsilon: 0.00998645168764533

image.png

Epsilon(探索率)は順調に下がっていて、意図通りになったかなと思います。
しかし、9700エピソードの時点でも報酬が1329と低い値とっていて、思うような結果になりませんでした。

もちろん、すべてのアプローチを同時に試したわけではなく、一つずつ試しているのですが、すべて似たような結果になったので、今回はまとめて提示しました。

おわりに

今回の結果を見てみると、なかなか報酬が最大化しないなというのが正直なところです。ただ、10000エピソードの後半(9000エピソードを超えたあたり)からは、少しずつ移動平均が上がっていて、各エピソードの報酬も安定してきているように見えます。もしかしたら、強化学習の精度を上げるためには、もっとエピソード数を増やしたり、データ数を増やしたりした方が良いのかなと思いました

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?