Skip to content

Instantly share code, notes, and snippets.

@rdednl
Last active September 7, 2023 15:01
Show Gist options
  • Save rdednl/64e8fb4b7d4a0e4d047f91188cbfaaed to your computer and use it in GitHub Desktop.
Save rdednl/64e8fb4b7d4a0e4d047f91188cbfaaed to your computer and use it in GitHub Desktop.
batch norm is bad (td3/sac)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "registered-packet",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import gym\n",
"from gym import spaces"
]
},
{
"cell_type": "markdown",
"id": "23febdcb",
"metadata": {},
"source": [
"# This simple environment doesn't do anything. The reward is just the action. Quite simple to learn right?"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "completed-private",
"metadata": {},
"outputs": [],
"source": [
"class RandomEnv(gym.Env):\n",
" def __init__(self, reward_func=lambda a: a):\n",
" super(RandomEnv, self).__init__()\n",
" self.num_steps = 0\n",
" self.max_steps = 30\n",
" self.reward_func = reward_func\n",
" \n",
" self.action_space = spaces.Box(low=-1, high=+1, shape=(1,))\n",
" self.observation_space = spaces.Box(low=-1, high=+1, shape=(3,))\n",
"\n",
" def reset(self):\n",
" self.num_steps = 0\n",
" return self.get_state()\n",
"\n",
" def step(self, action):\n",
" self.num_steps += 1\n",
" action = action[0]\n",
" return self.get_state(), self.reward_func(action), self.get_terminal(), {}\n",
"\n",
" def get_state(self):\n",
" return np.array([0.,0.,0.])\n",
"# return self.observation_space.sample()\n",
"\n",
" def get_terminal(self):\n",
" return True if self.num_steps >= self.max_steps else False"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "8f987136",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"import collections\n",
"\n",
"import torch\n",
"import torch.nn as nn\n",
"from torch.distributions import Normal\n",
"\n",
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')"
]
},
{
"cell_type": "markdown",
"id": "6d7cac66",
"metadata": {},
"source": [
"# Let's define a replay buffer, TD3 and SAC training code. Both of them with two kinds of architecture, one with batch norm, the other one without it..."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b83074b5",
"metadata": {},
"outputs": [],
"source": [
"# REPLAY BUFFER\n",
"Transition = collections.namedtuple('Transition', ('state', 'action', 'next_state', 'reward', 'done'))\n",
"\n",
"class ReplayBuffer():\n",
" def __init__(self, max_size=10000):\n",
" self.data = collections.deque([], maxlen=max_size)\n",
"\n",
" def push(self, state, action, next_state, reward, done):\n",
" self.data.append(Transition(state, action, next_state, reward, done))\n",
"\n",
" def sample(self, batch_size):\n",
" transitions = random.sample(self.data, batch_size)\n",
"\n",
" batch = Transition(*zip(*transitions))\n",
" state = torch.tensor(batch.state, device=device, dtype=torch.float32)\n",
" next_state = torch.tensor(batch.next_state, device=device, dtype=torch.float32)\n",
" action = torch.tensor(batch.action, device=device)\n",
" reward = torch.tensor(batch.reward, device=device)\n",
" done = torch.tensor(batch.done, device=device)\n",
" return state, action, next_state, reward, done\n",
"\n",
" def __len__(self):\n",
" return len(self.data)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "355ef3c4",
"metadata": {},
"outputs": [],
"source": [
"class TrainerBase:\n",
" def __init__(self,\n",
" policy_class,\n",
" batch_size=256,\n",
" random_exploration=10,\n",
" gamma=0.99,\n",
" target_update_factor=0.005,\n",
" target_update_interval=1,\n",
" test_episodes=5,\n",
" test_interval=10,\n",
" print_interval=-1,\n",
" num_episodes=100,\n",
" verbose=0\n",
" ):\n",
" self.batch_size = batch_size\n",
" self.random_exploration = random_exploration\n",
" self.gamma = gamma\n",
" self.target_update_factor=target_update_factor\n",
" self.target_update_interval=target_update_interval\n",
" self.test_episodes=test_episodes\n",
" self.test_interval=test_interval\n",
" self.print_interval=print_interval\n",
" self.num_episodes=num_episodes\n",
" self.verbose = verbose\n",
" \n",
" self.env = RandomEnv()\n",
" self.test_env = RandomEnv()\n",
"\n",
" self.model = policy_class(self.env.observation_space.shape[0], self.env.action_space.shape[0])\n",
" self.target = policy_class(self.env.observation_space.shape[0], self.env.action_space.shape[0])\n",
" self.target.load_state_dict(self.model.state_dict())\n",
" self.target.eval()\n",
"\n",
" self.model.to(device)\n",
" self.target.to(device)\n",
"\n",
" self.memory = ReplayBuffer()\n",
" \n",
" self.training_steps = 0\n",
" self.episode_num = 0\n",
" \n",
" self.actor_optimizer = torch.optim.Adam(self.model.actor.parameters(), lr=3e-4)\n",
" self.critic_optimizer_1 = torch.optim.Adam(self.model.critic_1.parameters(), lr=3e-4)\n",
" self.critic_optimizer_2 = torch.optim.Adam(self.model.critic_2.parameters(), lr=3e-4)\n",
" \n",
" self.run_stats = []\n",
"\n",
" def select_action(self, state, test_mode=False):\n",
" raise NotImplementedError()\n",
"\n",
" def optimize_model(self):\n",
" raise NotImplementedError()\n",
"\n",
" def test(self):\n",
" test_rewards = []\n",
" for i in range(self.test_episodes):\n",
" state = self.test_env.reset()\n",
" episode_total_reward = 0\n",
" done = False\n",
" while not done:\n",
" with torch.no_grad():\n",
" action = self.select_action(state, test_mode=True)\n",
" next_state, reward, done, info = self.test_env.step(action)\n",
" episode_total_reward += reward\n",
" state = next_state\n",
" test_rewards.append(episode_total_reward)\n",
" score = sum(test_rewards)/self.test_episodes\n",
" self.run_stats.append((self.training_steps, score))\n",
" print(f\"[TESTING] [Total Steps: {self.training_steps}] [Average Reward {score:.3f}]\")\n",
" \n",
" def train(self):\n",
" self.test()\n",
" for self.episode_num in range(1, self.num_episodes+1):\n",
" episode_total_reward = 0\n",
" state = self.env.reset()\n",
" done = False\n",
" while not done:\n",
" action = self.select_action(state)\n",
" next_state, reward, done, info = self.env.step(action)\n",
" self.training_steps += 1\n",
" episode_total_reward += reward\n",
" self.memory.push(state, action, next_state, reward, int(done))\n",
" state = next_state\n",
"\n",
" if self.training_steps > self.random_exploration:\n",
" self.optimize_model()\n",
" \n",
" if self.verbose and self.episode_num % self.print_interval == 0:\n",
" print(f'[Episode {self.episode_num}][Reward {episode_total_reward:.3f}]')\n",
" \n",
" if self.episode_num % self.test_interval == 0:\n",
" self.test()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "85acc97a",
"metadata": {},
"outputs": [],
"source": [
"class SAC(TrainerBase):\n",
" def __init__(self,\n",
" policy_class,\n",
" batch_size=256,\n",
" random_exploration=10,\n",
" gamma=0.99,\n",
" learnable_temperature=True,\n",
" init_temperature=0.1,\n",
" target_update_factor=0.005,\n",
" target_update_interval=1,\n",
" test_episodes=5,\n",
" test_interval=10,\n",
" print_interval=1,\n",
" num_episodes=100,\n",
" verbose=0\n",
" ):\n",
" \n",
" super().__init__(\n",
" policy_class,\n",
" batch_size,\n",
" random_exploration,\n",
" gamma,\n",
" target_update_factor,\n",
" target_update_interval,\n",
" test_episodes,\n",
" test_interval,\n",
" print_interval,\n",
" num_episodes,\n",
" verbose\n",
" )\n",
" \n",
" if learnable_temperature:\n",
" self.log_alpha = torch.log(torch.tensor(init_temperature, device=device))\n",
" self.log_alpha.requires_grad = True\n",
" self.log_alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=3e-4)\n",
" else:\n",
" self.log_alpha = torch.tensor(init_temperature, device=device)\n",
" self.log_alpha_optimizer = None\n",
"\n",
" self.target_entropy = -self.env.action_space.shape[0]\n",
"\n",
" def select_action(self, state, test_mode=False):\n",
" if self.training_steps < self.random_exploration:\n",
" return self.env.action_space.sample()\n",
" \n",
" state = torch.tensor([state], device=device, dtype=torch.float32)\n",
" with torch.no_grad():\n",
" action = self.model.select_action(state, test_mode=test_mode)\n",
"\n",
" return torch.clamp(action, -1, 1).cpu().numpy()[0]\n",
"\n",
" def optimize_model(self):\n",
" if self.batch_size > len(self.memory):\n",
" batch_size = len(self.memory)\n",
" else:\n",
" batch_size = self.batch_size\n",
"\n",
" state, action, next_state, reward, done = self.memory.sample(batch_size)\n",
"\n",
" q_1 = self.model.critic_1(state, action).squeeze(-1)\n",
" q_2 = self.model.critic_2(state, action).squeeze(-1)\n",
"\n",
" with torch.no_grad():\n",
" next_action, next_log_pi = self.model.sample_action(next_state)\n",
" value_function_1 = self.target.critic_1(next_state, next_action).squeeze(-1)\n",
" value_function_2 = self.target.critic_2(next_state, next_action).squeeze(-1)\n",
" value_function = torch.min(value_function_1, value_function_2) - self.log_alpha.exp().detach() * next_log_pi\n",
" next_value = (1 - done).float() * value_function\n",
" q_target = reward + self.gamma * next_value\n",
"\n",
" critic_loss_1 = (q_1 - q_target).pow(2).mean()\n",
" critic_loss_2 = (q_2 - q_target).pow(2).mean()\n",
"\n",
" self.critic_optimizer_1.zero_grad()\n",
" critic_loss_1.backward()\n",
" self.critic_optimizer_1.step()\n",
"\n",
" self.critic_optimizer_2.zero_grad()\n",
" critic_loss_2.backward()\n",
" self.critic_optimizer_2.step()\n",
"\n",
" action, log_pi = self.model.sample_action(state)\n",
" q_1 = self.model.critic_1(state, action)\n",
" q_2 = self.model.critic_2(state, action)\n",
"\n",
" actor_loss = ((self.log_alpha.exp().detach() * log_pi) - torch.min(q_1, q_2)).mean()\n",
" self.actor_optimizer.zero_grad()\n",
" actor_loss.backward()\n",
" self.actor_optimizer.step()\n",
"\n",
" if self.log_alpha_optimizer:\n",
" self.log_alpha_optimizer.zero_grad()\n",
" alpha_loss = (self.log_alpha * (-log_pi - self.target_entropy).detach()).mean()\n",
" alpha_loss.backward()\n",
" self.log_alpha_optimizer.step()\n",
" \n",
" if self.training_steps % self.target_update_interval == 0:\n",
" with torch.no_grad():\n",
" for model_param, target_param in zip(self.model.parameters(), self.target.parameters()):\n",
" target_param.mul_(1 - self.target_update_factor)\n",
" target_param.add_(self.target_update_factor * model_param.data)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "c77f15d4",
"metadata": {},
"outputs": [],
"source": [
"class TD3(TrainerBase):\n",
" def __init__(self,\n",
" policy_class,\n",
" batch_size=256,\n",
" random_exploration=10,\n",
" gamma=0.99,\n",
" actor_update_interval=2,\n",
" exploration_noise=0.1,\n",
" target_update_factor=0.005,\n",
" target_update_interval=1,\n",
" test_episodes=5,\n",
" test_interval=10,\n",
" print_interval=1,\n",
" num_episodes=100,\n",
" verbose=0\n",
" ):\n",
" \n",
" super().__init__(\n",
" policy_class,\n",
" batch_size,\n",
" random_exploration,\n",
" gamma,\n",
" target_update_factor,\n",
" target_update_interval,\n",
" test_episodes,\n",
" test_interval,\n",
" print_interval,\n",
" num_episodes,\n",
" verbose\n",
" )\n",
" \n",
" self.actor_update_interval = actor_update_interval\n",
" self.exploration_noise = exploration_noise\n",
"\n",
" def select_action(self, state, test_mode=False):\n",
" if self.training_steps < self.random_exploration:\n",
" return self.env.action_space.sample()\n",
"\n",
" state = torch.tensor([state], device=device, dtype=torch.float32)\n",
" with torch.no_grad():\n",
" action = self.model.select_action(state)\n",
" \n",
" if not test_mode:\n",
" action += torch.randn_like(action) * self.exploration_noise\n",
" action = torch.clamp(action, min=-1, max=+1)\n",
" \n",
" return action.cpu().numpy()[0]\n",
"\n",
" def optimize_model(self):\n",
" if self.batch_size > len(self.memory):\n",
" batch_size = len(self.memory)\n",
" else:\n",
" batch_size = self.batch_size\n",
"\n",
" state, action, next_state, reward, done = self.memory.sample(batch_size)\n",
"\n",
" q_1 = self.model.critic_1(state, action).squeeze(-1)\n",
" q_2 = self.model.critic_2(state, action).squeeze(-1)\n",
" \n",
" with torch.no_grad():\n",
" noise = (torch.randn_like(action) * 0.2).clamp(-0.5, 0.5)\n",
" next_action = (self.target.actor(next_state) + noise).clamp(-1.0, 1.0)\n",
" target_q_1 = self.target.critic_1(next_state, next_action).squeeze(-1)\n",
" target_q_2 = self.target.critic_2(next_state, next_action).squeeze(-1)\n",
" target_q = torch.min(target_q_1, target_q_2)\n",
" target_q = reward + self.gamma * (1 - done).float() * target_q\n",
"\n",
" critic_loss_1 = (q_1 - target_q).pow(2).mean()\n",
" critic_loss_2 = (q_2 - target_q).pow(2).mean()\n",
"\n",
" self.critic_optimizer_1.zero_grad()\n",
" critic_loss_1.backward()\n",
" self.critic_optimizer_1.step()\n",
"\n",
" self.critic_optimizer_2.zero_grad()\n",
" critic_loss_2.backward()\n",
" self.critic_optimizer_2.step()\n",
"\n",
" if self.training_steps % self.actor_update_interval == 0:\n",
" actor_loss = - torch.mean(self.model.critic_1(state, self.model.actor(state)))\n",
" \n",
" self.actor_optimizer.zero_grad()\n",
" actor_loss.backward()\n",
" self.actor_optimizer.step()\n",
" \n",
" with torch.no_grad():\n",
" for model_param, target_param in zip(self.model.parameters(), self.target.parameters()):\n",
" target_param.mul_(1 - self.target_update_factor)\n",
" target_param.add_(self.target_update_factor * model_param.data)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "e098c955",
"metadata": {},
"outputs": [],
"source": [
"class SACActor(nn.Module):\n",
" def __init__(self, state_size, actions, min_log=-20, max_log=2, use_batch_norm=False):\n",
" super(SACActor, self).__init__()\n",
" if use_batch_norm:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU()\n",
" )\n",
" else:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.ReLU()\n",
" )\n",
" \n",
" self.mean = nn.Linear(64, actions)\n",
" self.log_std = nn.Linear(64, actions)\n",
"\n",
" self.min_log = min_log\n",
" self.max_log = max_log\n",
"\n",
" def forward(self, state):\n",
" out = self.layers(state)\n",
" mean = self.mean(out)\n",
" log_std = self.log_std(out)\n",
" log_std = torch.clamp(log_std, min=self.min_log, max=self.max_log)\n",
" return mean, log_std\n",
"\n",
"class SACCritic(nn.Module):\n",
" def __init__(self, state_size, actions, use_batch_norm=False):\n",
" super(SACCritic, self).__init__()\n",
" if use_batch_norm:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size + actions, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 1)\n",
" )\n",
" else:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size + actions, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 1)\n",
" )\n",
" \n",
" def forward(self, state, action):\n",
" return self.layers(torch.cat([state, action], dim=1))\n",
"\n",
"class SACNet(nn.Module):\n",
" def __init__(self, state_size, actions, use_batch_norm=False):\n",
" super(SACNet, self).__init__()\n",
" self.actor = SACActor(state_size, actions, use_batch_norm)\n",
" self.critic_1 = SACCritic(state_size, actions, use_batch_norm)\n",
" self.critic_2 = SACCritic(state_size, actions, use_batch_norm)\n",
"\n",
" def select_action(self, state, test_mode=False):\n",
" self.eval()\n",
" mean, log_std = self.actor(state)\n",
" if test_mode:\n",
" action = torch.tanh(mean)\n",
" else:\n",
" normal = Normal(mean, log_std.exp())\n",
" action = torch.tanh(normal.rsample())\n",
" self.train()\n",
" return action\n",
"\n",
" def sample_action(self, state):\n",
" mean, log_std = self.actor(state)\n",
" std = log_std.exp()\n",
"\n",
" normal = Normal(mean, std)\n",
" xi = normal.rsample()\n",
" action = torch.tanh(xi)\n",
" log_pi = normal.log_prob(xi)\n",
"\n",
" log_pi -= torch.log(1 - action.pow(2) + 1e-6)\n",
" log_pi = log_pi.sum(1, keepdim=True)\n",
"\n",
" return action, log_pi"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b3efb765",
"metadata": {},
"outputs": [],
"source": [
"class TD3Actor(nn.Module):\n",
" def __init__(self, state_size, actions, use_batch_norm=False):\n",
" super(TD3Actor, self).__init__()\n",
" if use_batch_norm:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, actions),\n",
" nn.Tanh(),\n",
" )\n",
" else:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, actions),\n",
" nn.Tanh(),\n",
" )\n",
"\n",
" def forward(self, state):\n",
" return self.layers(state)\n",
"\n",
"class TD3Critic(nn.Module):\n",
" def __init__(self, state_size, actions, use_batch_norm=False):\n",
" super(TD3Critic, self).__init__()\n",
" if use_batch_norm:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size + actions, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.BatchNorm1d(64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 1),\n",
" )\n",
" else:\n",
" self.layers = nn.Sequential(\n",
" nn.Linear(state_size + actions, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 64),\n",
" nn.ReLU(),\n",
" nn.Linear(64, 1),\n",
" )\n",
" \n",
" def forward(self, state, action):\n",
" return self.layers(torch.cat([state, action], dim=1))\n",
"\n",
"class TD3Net(nn.Module):\n",
" def __init__(self, state_size, actions, use_batch_norm=True):\n",
" super(TD3Net, self).__init__()\n",
" self.actor = TD3Actor(state_size, actions, use_batch_norm)\n",
" self.critic_1 = TD3Critic(state_size, actions, use_batch_norm)\n",
" self.critic_2 = TD3Critic(state_size, actions, use_batch_norm)\n",
"\n",
" def select_action(self, state):\n",
" self.eval()\n",
" a = self.actor.forward(state)\n",
" self.train()\n",
" return a"
]
},
{
"cell_type": "markdown",
"id": "caaf501c",
"metadata": {},
"source": [
"# Let's run TD3 with this simple environment with the architecture with batch norm"
]
},
{
"cell_type": "markdown",
"id": "cc9d7c1f",
"metadata": {},
"source": [
"## All these are run for 5 times, just to avoid saying \"oh but it's a random seed...\""
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "fcabc894",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------\n",
"0\n",
"[TESTING] [Total Steps: 0] [Average Reward 0.725]\n",
"[TESTING] [Total Steps: 300] [Average Reward 2.932]\n",
"[TESTING] [Total Steps: 600] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 900] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 2.933]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 2.933]\n",
"----------\n",
"1\n",
"[TESTING] [Total Steps: 0] [Average Reward -1.444]\n",
"[TESTING] [Total Steps: 300] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 600] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 900] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 1200] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 1500] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 1800] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 2100] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 2400] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 2700] [Average Reward -3.604]\n",
"[TESTING] [Total Steps: 3000] [Average Reward -3.604]\n",
"----------\n",
"2\n",
"[TESTING] [Total Steps: 0] [Average Reward 2.215]\n",
"[TESTING] [Total Steps: 300] [Average Reward -0.534]\n",
"[TESTING] [Total Steps: 600] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 900] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 1200] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 1500] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 1800] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 2100] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 2400] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 2700] [Average Reward -0.535]\n",
"[TESTING] [Total Steps: 3000] [Average Reward -0.535]\n",
"----------\n",
"3\n",
"[TESTING] [Total Steps: 0] [Average Reward -1.536]\n",
"[TESTING] [Total Steps: 300] [Average Reward -0.795]\n",
"[TESTING] [Total Steps: 600] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 900] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 1200] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 1500] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 1800] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 2100] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 2400] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 2700] [Average Reward -0.789]\n",
"[TESTING] [Total Steps: 3000] [Average Reward -0.789]\n",
"----------\n",
"4\n",
"[TESTING] [Total Steps: 0] [Average Reward 1.792]\n",
"[TESTING] [Total Steps: 300] [Average Reward -0.675]\n",
"[TESTING] [Total Steps: 600] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 900] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 1200] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 1500] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 1800] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 2100] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 2400] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 2700] [Average Reward -0.672]\n",
"[TESTING] [Total Steps: 3000] [Average Reward -0.672]\n"
]
}
],
"source": [
"td3_stats_batch_norm = []\n",
"for i in range(5):\n",
" print(\"-\" * 10)\n",
" print(i)\n",
" model = TD3(policy_class=lambda s, a: TD3Net(s, a, use_batch_norm=True))\n",
" model.train()\n",
" td3_stats_batch_norm.append(model.run_stats)"
]
},
{
"cell_type": "markdown",
"id": "1b28a4f2",
"metadata": {},
"source": [
"# doesn't work... but if we remove batch norm..."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "47431284",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------\n",
"0\n",
"[TESTING] [Total Steps: 0] [Average Reward -0.037]\n",
"[TESTING] [Total Steps: 300] [Average Reward 19.322]\n",
"[TESTING] [Total Steps: 600] [Average Reward 28.569]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.586]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.817]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 29.900]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 29.938]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 29.959]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 29.971]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 29.979]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 29.993]\n",
"----------\n",
"1\n",
"[TESTING] [Total Steps: 0] [Average Reward 1.559]\n",
"[TESTING] [Total Steps: 300] [Average Reward 26.659]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.388]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.765]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.877]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 29.925]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 29.950]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 29.964]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 29.974]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 29.980]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 29.984]\n",
"----------\n",
"2\n",
"[TESTING] [Total Steps: 0] [Average Reward -2.776]\n",
"[TESTING] [Total Steps: 300] [Average Reward 24.281]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.199]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.748]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.921]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 29.964]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 29.980]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 29.988]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 29.992]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 29.994]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 29.996]\n",
"----------\n",
"3\n",
"[TESTING] [Total Steps: 0] [Average Reward 0.407]\n",
"[TESTING] [Total Steps: 300] [Average Reward 27.670]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.705]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.894]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.948]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 29.970]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 29.981]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 29.987]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 29.990]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 29.992]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 29.994]\n",
"----------\n",
"4\n",
"[TESTING] [Total Steps: 0] [Average Reward 1.229]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.227]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.884]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.957]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.978]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 29.986]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 29.991]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 29.993]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 29.994]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 29.996]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 29.996]\n"
]
}
],
"source": [
"td3_stats_no_batch_norm = []\n",
"for i in range(5):\n",
" print(\"-\" * 10)\n",
" print(i)\n",
" model = TD3(policy_class=lambda s, a: TD3Net(s, a, use_batch_norm=False))\n",
" model.train()\n",
" td3_stats_no_batch_norm.append(model.run_stats)"
]
},
{
"cell_type": "markdown",
"id": "a90745c6",
"metadata": {},
"source": [
"# same things with SAC. Here with Batch Norm"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "59771363",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------\n",
"0\n",
"[TESTING] [Total Steps: 0] [Average Reward -2.863]\n",
"[TESTING] [Total Steps: 300] [Average Reward -2.637]\n",
"[TESTING] [Total Steps: 600] [Average Reward 1.463]\n",
"[TESTING] [Total Steps: 900] [Average Reward 7.295]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 9.499]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 11.115]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 13.719]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 14.046]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 17.136]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 17.300]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 18.709]\n",
"----------\n",
"1\n",
"[TESTING] [Total Steps: 0] [Average Reward -1.422]\n",
"[TESTING] [Total Steps: 300] [Average Reward 2.362]\n",
"[TESTING] [Total Steps: 600] [Average Reward 6.293]\n",
"[TESTING] [Total Steps: 900] [Average Reward 6.952]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 8.212]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 10.403]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 10.594]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 13.462]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 12.795]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 12.896]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 14.561]\n",
"----------\n",
"2\n",
"[TESTING] [Total Steps: 0] [Average Reward 2.287]\n",
"[TESTING] [Total Steps: 300] [Average Reward -3.066]\n",
"[TESTING] [Total Steps: 600] [Average Reward 4.481]\n",
"[TESTING] [Total Steps: 900] [Average Reward 8.786]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 10.877]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 12.028]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 13.635]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 15.625]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 15.664]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 16.005]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 18.392]\n",
"----------\n",
"3\n",
"[TESTING] [Total Steps: 0] [Average Reward 0.901]\n",
"[TESTING] [Total Steps: 300] [Average Reward -13.923]\n",
"[TESTING] [Total Steps: 600] [Average Reward -7.597]\n",
"[TESTING] [Total Steps: 900] [Average Reward -1.609]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 2.220]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 4.721]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 6.056]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 9.206]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 9.931]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 13.146]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 13.121]\n",
"----------\n",
"4\n",
"[TESTING] [Total Steps: 0] [Average Reward 3.851]\n",
"[TESTING] [Total Steps: 300] [Average Reward 0.013]\n",
"[TESTING] [Total Steps: 600] [Average Reward 6.251]\n",
"[TESTING] [Total Steps: 900] [Average Reward 9.068]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 11.889]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 14.886]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 16.303]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 16.039]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 15.649]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 18.159]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 19.134]\n"
]
}
],
"source": [
"sac_stats_batch_norm = []\n",
"for i in range(5):\n",
" print(\"-\" * 10)\n",
" print(i)\n",
" model = SAC(policy_class=lambda s, a: SACNet(s, a, use_batch_norm=True))\n",
" model.train()\n",
" sac_stats_batch_norm.append(model.run_stats)"
]
},
{
"cell_type": "markdown",
"id": "47cd0dd0",
"metadata": {},
"source": [
"# but removing batch norm:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "d38f556f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------\n",
"0\n",
"[TESTING] [Total Steps: 0] [Average Reward 1.242]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.018]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.112]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.085]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 28.996]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 28.908]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 28.725]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 28.561]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 28.505]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 28.409]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 28.648]\n",
"----------\n",
"1\n",
"[TESTING] [Total Steps: 0] [Average Reward 1.220]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.047]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.077]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.077]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 28.946]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 28.853]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 28.770]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 28.747]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 28.660]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 28.728]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 28.611]\n",
"----------\n",
"2\n",
"[TESTING] [Total Steps: 0] [Average Reward -0.713]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.007]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.101]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.087]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 28.976]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 28.872]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 28.827]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 28.865]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 28.670]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 28.517]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 28.591]\n",
"----------\n",
"3\n",
"[TESTING] [Total Steps: 0] [Average Reward 2.078]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.010]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.154]\n",
"[TESTING] [Total Steps: 900] [Average Reward 29.040]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 29.015]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 28.910]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 28.943]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 28.848]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 28.736]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 28.749]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 28.723]\n",
"----------\n",
"4\n",
"[TESTING] [Total Steps: 0] [Average Reward -0.218]\n",
"[TESTING] [Total Steps: 300] [Average Reward 29.017]\n",
"[TESTING] [Total Steps: 600] [Average Reward 29.062]\n",
"[TESTING] [Total Steps: 900] [Average Reward 28.899]\n",
"[TESTING] [Total Steps: 1200] [Average Reward 28.882]\n",
"[TESTING] [Total Steps: 1500] [Average Reward 28.789]\n",
"[TESTING] [Total Steps: 1800] [Average Reward 28.786]\n",
"[TESTING] [Total Steps: 2100] [Average Reward 28.641]\n",
"[TESTING] [Total Steps: 2400] [Average Reward 28.682]\n",
"[TESTING] [Total Steps: 2700] [Average Reward 28.485]\n",
"[TESTING] [Total Steps: 3000] [Average Reward 28.640]\n"
]
}
],
"source": [
"sac_stats_no_batch_norm = []\n",
"for i in range(5):\n",
" print(\"-\" * 10)\n",
" print(i)\n",
" model = SAC(policy_class=lambda s, a: SACNet(s, a, use_batch_norm=False))\n",
" model.train()\n",
" sac_stats_no_batch_norm.append(model.run_stats)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "065d8e1b",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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BiwFXnEvjhrcKRfclEURQn36tD7I48FjKYsZFZ4zt5G5yVfh791Ur/+k+nf/n4GBwM9xAnqdiPxbRK4SkS+5NV66rVBDA+CM0LVPH4g3OvR6rSiXMSYjUgroqjpXVScCg3EuhC4AKnDqpD+HE+CfTnsv84Du3u3koLuFuRJeEA2FLMOlm6mqqqKqqooJEybg9Xojz2fNmsWaNWsi2yorKxk3bhzf+c53WLduXeT1P/vZz6isrIy87oEHHuhwXzZs2MARRxwReX7ttdeya9euyPM5c+bw+9//PqlzjRgxgrFjx1JVVcX48eM588wz2bFjR7uvW7ZsGU899VRSbYgIde5fvu2dU0S44YYbIttWrlzJiBEjkmqnUHToypyqblTVB1T1QmAicAbwH6AEmJHG/uWNcB102bEDaWoimChlMRi0EXo3U11dTXV1NU888QRlZWWR5w8++CBAZNuKFStYvnw5gwcP5rDDDqO+vh6Ayy+/nBUrVlBdXc3jjz/OBRdcwBb3ek2qhgwZwvPPPx95ft1117UI6Kl68MEHqa6u5t1336W+vp758+e3+5pUAnoqBg0axC233NLhr01YIBBIU4+6XsoBXRzTRORyEXkCqAHuB/q6Hy/sSEdE5A8iskZEVESqoravEZFVIlLtPmZ15PyZFs5Bb3fZuR49kAQrGJnMOOUUmDgxM49TTklvX4uKirj++usZOnQo9957LwDl5eWR/du3b0dVCYVCrV575plnct999wHwxz/+kaKiosiI+ZhjjuG5555jzZo1kfNddNFFABxxxBFUVVWxadMmAN5//32OPfZY9ttvP0477TSamtq/TNbU1ERjYyN93e/7FStWcPjhhzN16lQmTJjAL37xC8D55Xb77bezcOFCqqqquP766wF4/PHHOfDAA5kyZQpVVVW8+uqrkXP/8Y9/5KCDDmLkyJHcfffdCfuw9957c84557QYpUd7+umnmTp1KpMnT+aoo47ivffeA5xfMBMnTuT888+nqqqKv/3tb4wYMYKrrrqKww47jGHDhnH77bdz9913c+ihhzJixIhO/ZWUSalWW3wMJ4C/inNz0Q7gJ8BEVR2iqmep6l0d7MtfgcNx7kaNNUtVq9zHgx08f0ZF6qCHUxYTFeWyZedMEg466CDefffdyPM//OEPjB07lqlTp3LnnXcyME7piBkzZrBkyRIA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\n",
"text/plain": [
"<Figure size 400x400 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"def get_min_max_mean(arr):\n",
" tmp_dict = {}\n",
" for i in arr:\n",
" for num, j in enumerate(i):\n",
" val = str((num)*300)\n",
" if val in tmp_dict:\n",
" tmp_dict[val].append(j[1])\n",
" else:\n",
" tmp_dict[val] = [j[1]]\n",
"\n",
" arr_mean = {}\n",
" arr_max = {}\n",
" arr_min = {}\n",
"\n",
" for k, v in tmp_dict.items():\n",
" v = np.array(v)\n",
" arr_mean[k] = v.mean()\n",
" arr_max[k] = v.max()\n",
" arr_min[k] = v.min()\n",
" \n",
" return arr_min, arr_max, arr_mean\n",
" \n",
"ssbn_min, ssbn_max, ssbn_mean = get_min_max_mean(td3_stats_batch_norm)\n",
"ss_min, ss_max, ss_mean = get_min_max_mean(td3_stats_no_batch_norm)\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(5,5), dpi= 80)\n",
"plt.title(\"TD3 Results\")\n",
"plt.ylabel(\"Test Reward\", fontsize=16) \n",
"plt.xlabel(\"Training Steps\", fontsize=16) \n",
"x = list(ssbn_mean.keys())\n",
"plt.plot(x, list(ssbn_mean.values()), color='blue', label=\"TD3 with Batch Norm\") \n",
"plt.plot(x, list(ss_mean.values()), color='red', label=\"TD3 without Batch Norm\")\n",
"plt.axhline(y=30, color='green', linestyle='-', label=\"optimal solution\")\n",
"plt.fill_between(x, list(ssbn_min.values()), list(ssbn_max.values()), color=\"lightblue\")\n",
"plt.fill_between(x, list(ss_min.values()), list(ss_max.values()), color=\"lightcoral\")\n",
"plt.legend()\n",
"plt.savefig(\"td3_batch_norm.png\", format='png')\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "a923ac68",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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sXz4L8bYprBI72AaZmdxikStLUVR4pe4VwEROVZENmC5YH4qIhtE5FR799PAt1gZO7OA+40xC2NkaodwioTlVulKieMd27ZxzWWXMmjQIMaMGcPBBx/MXXeFV0qhJSJV4jhc5s2bxwsvvNDq/mnTppGXl9eYrz9q1Chef/31sM592223hTUy97bbbmss69AekyZNoqioqMnArylTpjQZGKYosSLuxd4Y874xpq8xJtcYk2M/f8Pe5zfGXGWMGWiMGWSM+Uus7Q0XsQc2tSX3XS1xfMoJx9G///6sWLGCuXPn8umnn5KVldVpmyNZ4jgc2hN7gKOPPpp58+Yxf/58Hn/8cS655JKwzn377bd3ugxDW+Tm5vLHP/6xS+fQcgpKdxD3Yh9TIlXjuKXl1FNxOx20FTjoSonj1156kdycHG677bYmJY5/8Ytf7NPO448/zqWXXgp0X4nje++9l+HDhzNy5EjOO++8xt5v857zI488wtSpU9m2bRu///3v+fDDDykrK2u3RDHAnj17GkslA1x//fWMGzeOsrIyjjzyyMa7kuC5Jk6cSFlZGdu2baO8vJxLLrmEESNGMHr06CbFzTZv3swpp5zCsGHDOOaYY9i1a1erNtx444088cQTbNq0aZ99VVVVXHzxxYwYMYIRI0Zw++23N+6bNGkS11xzDRMmTOD4449n2rRpTJ48mXPPPZdhw4Zx+OGHs2TJEn74wx9y0EEHcfzxxzcZ5aso7aFiH0McIqS1ETjtbIljB8LCeXPbrPAYyuTJk7u1xPE777zDP//5Tz777DMWLlxIVlYWN910U5s2lZaWcscddzT23FubtCR4MTjggAP40Y9+xP33N8bgufHGG5k1axbz5s3jyiuvbLzQBc81c+ZM5s2bR2lpKddeey1paWksWLCA+fPnNynP/NVXXzFt2jSWLFlCaWkpjz32WKt29+zZk8suu4xbb711n3133nkn9fX1LFiwgK+++orXXnuNF1/cG2Javnw5n3zyCR988EHj53nPPfewZMkSBg0axCmnnMKjjz7K0qVLSUtL48knn2zzM1SUUGKejRPXRLLGcSt4XA4aGlrOyulsieM/3/unDk0a3t0ljmfMmMHZZ59Nfn4+AFdccUWTMsJd4eijj26cTGTRokVMnjyZOXPm0Lt3b6ZPn87DDz9MZWUlgUCgzR75m2++yVdffYXDvviWlJQ07jvxxBMpKipqfF8LF7YdFrrhhhsYOnToPm6uGTNm8Oc//xmHw0FWVhYXXHAB06dP5+yzzwbg/PPPx+12Nx4/YcKExho8Y8eOxev10qNHDwDGjRvHihUrwvmIFAXQnn3McTkcONsR5o6WOCYQ4JBDDuHLL78M245oljgOvRC5XK7GqpxAl/zoI0aMoF+/fnz22WesW7eOq6++mmeeeYZFixbxwgsvRK1Ucm5uLjfeeCM333xzm8dpqWQlmqjYxwGeVgK1HS1x/O13a9i/Xz/eeustzj33XPbs2cOdd97ZpMTxQw891KIN3VniePLkybz00ktUVFQA8Nhjj3H88ccDMHjwYGbPno3f76empqZJ1cqOllHesGEDK1as4IADDqC8vBy3202vXr0wxvDII03r4+Xk5DQ596mnnsp9993XmOm0ffv2sNttiSuuuIJ58+bxzTffNG6bPHkyTzzxBMYYqqurefrppxs/B0XpblTs4wCrXs2+ct/REsdZbhfnnXceTzzxBJmZmXz88cesWrWKwYMHM3LkSMaPH09NTU2LNnRniePvf//7XHTRRUyYMIGRI0dSUVHB3XffDcAZZ5xB7969Oeiggzj55JMZM2ZME5vq6+sZNWpUqwHaoM++rKyME044gbvuuovRo0czcuRIzjnnHIYPH864ceP2KUn8q1/9iuOOO64xQPvAAw9QX1/PyJEjKSsra5wmsbN4PB7uuOMO1qxZ07jtd7/7HW63u/F/ceqpp3LWWWd1qR1FCZe4L3HcXcSqxHFr1Hh91Ps7P6I2y+3SImdKzL/HSmxpq8SxqkOc4OnCD9Ot1SwVRWkHVYg4wekQXJ0oVCb2pOGKoihtoWIfR3hcHRftbLdWs1QUpX1U7OMIt0OQDlS6T3c6can7RlGUMFCliCNEBE+Y4q3VLBVF6QiqFnFGuIHWLLerQ6NkFUVJbVTs4wynQ9qdaCTT5dRJwxVF6RAq9nFIW66ccNIs16xZs0/hsNBa9JFCRNizZ09EzhWsqNker732WpMyELNnz26sLaMoSuuo2MchrQVqg2mW7blvWhL70Fr0iUxzsR87dmyTypGKorSMVr1sg1OfP5VVu1d1y7kHFQzijXNbrqoZDNTW+f28P/097vz97/D5fBQVFPLoo39j2LBhfPTRR1x99dUcfPDBzJkzB4/HwxNPPNFY+33t2rWUlZXRr18/3njjDfr3789rr71GWVkZkyZN4pBDDmHWrFmsWbOGCy+8kAkTJnDXXXexYcMGrrnmGq67zpqb/frrr+fjjz/G6/WSm5vL3//+93YvGl9++SVXXXUVfr8fn8/HVVddxRVXXMG2bdu4/PLLWbFiBcYYfv7zn3PZZZft8/pJkyZx7bXXNs58NWXKFE4++WRKS0t54403mD59OtOmTePqq69m8ODBXHvttY13BU8//TT33nsvAPvttx+PP/44ffr0Ydq0aTzzzDOUlJSwaNEiPB4PL730UmPFT0VJdlTs4xSPy8H6zZu5dOqF/Ofd6Rw8ejSvvvQCU6ZMYfHixQAsXryYv/zlLzz11FO89NJLnHPOOSxdupRHH320iQC2xNq1a/nwww+pqKigf//+7N69m5kzZ7Jp0yaGDh3KxRdfTH5+PjfeeCP33XcfAC+88AK/+MUv+O9//9um7XfffTfXX3895557LgC7d+8G4Oc//zlDhw7l1VdfZdu2bRxyyCGMHj2aww47LKzP5KSTTuLUU0+lrKysccKTjz76qHH/okWLuOGGG/jmm2/o06cPf/jDH7jkkkt45513ABpr2w8YMICbbrqJe+65p83a9IqSTKjYt0FrPe9o4BBh3uzZDBs+gpEjR5LucnDeeedx1VVXsdGewKR///6NhcrOOussLr30UtavXx/W+adMmYLT6aSgoICBAwdy8sknIyL06dOHkpIS1qxZQ1lZWYdqwgc5+uijufPOO1mxYgXHHHMMRxxxBGDVcw9WgSwtLeWMM85gxowZYYt9e3z44YeceOKJ9OnTB4Arr7ySO+64o7HqZ2v19hUlFVCffRzjtjNuwk2zFJGw0zHDqZXe2Zrw1157LW+99Ra9evXilltu4corr2zV3paIVI375ufXevBKKqNiH8cc8b3DWbp4EUuXWG6bF154gT59+jT2XNesWcOHH34IwL///W969OhB3759O1wHvjXaqwnfGsuWLWPAgAH87Gc/45ZbbmkMqE6ePJm///3vgFUv/tVXX+W4447b5/WDBw/mq6++AuC7777j008/bdzX1ns7+uij+e9//9s4/+ujjz7Kscceq9UfFQV148Q1JSUlPPvss1xwwQX4fD4KCgp4+eWXG3usw4cPZ9q0aVxzzTWkpaXx/PPPIyKMGjWK4cOHM2LECAYOHMgb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\n",
"text/plain": [
"<Figure size 400x400 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"ssbn_min, ssbn_max, ssbn_mean = get_min_max_mean(sac_stats_batch_norm)\n",
"ss_min, ss_max, ss_mean = get_min_max_mean(sac_stats_no_batch_norm)\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(5,5), dpi= 80)\n",
"plt.title(\"SAC Results\")\n",
"plt.ylabel(\"Test Reward\", fontsize=16) \n",
"plt.xlabel(\"Training Steps\", fontsize=16) \n",
"x = list(ssbn_mean.keys())\n",
"plt.plot(x, list(ssbn_mean.values()), color='blue', label=\"SAC with Batch Norm\") \n",
"plt.plot(x, list(ss_mean.values()), color='red', label=\"SAC without Batch Norm\")\n",
"plt.axhline(y=30, color='green', linestyle='-', label=\"optimal solution\")\n",
"plt.fill_between(x, list(ssbn_min.values()), list(ssbn_max.values()), color=\"lightblue\")\n",
"plt.fill_between(x, list(ss_min.values()), list(ss_max.values()), color=\"lightcoral\")\n",
"plt.legend()\n",
"plt.savefig(\"sac_batch_norm.png\", format='png')\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "59748169",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@honglu2875
Copy link

honglu2875 commented Aug 17, 2022

Well, when you updated the target net, you used parameters(). I can see the code is from stable-baseline which is not designed for bn. Batch norm in fact has two more variables that are not included in parameters(). What you had was in fact a partially updated target net which is dramatically different when inferencing.

@honglu2875
Copy link

Also, you abused .eval() and .train(). I suggest you learn about how batch norm works and what are the implications of those methods. I didn't look into the exact logic of each algorithms but I also have a vague feeling that something is not exactly like in the paper (but might still work).

ps: I was just able to fix your TD3 and made the batch norm model run as good as the one without. I don't have time to do SAC for you but I think most likely you should be able to fix it by yourself too if you understand batch norm correctly.

@rdednl
Copy link
Author

rdednl commented Sep 29, 2022

@honglu2875 Hi. My code is not from stable baselines. Also, batch norm learnable parameters that have to be updated on the target are present in the parameters() method:

> for model_param in model.model.actor.layers[1].parameters():
>    print(model_param.shape)

torch.Size([64])
torch.Size([64])

what are the variables that are missing?

Also, what do you mean that I abused .eval() and .train() ?

@honglu2875
Copy link

honglu2875 commented Sep 29, 2022

Check out properties whose names start with "running_" (either in your batch norm layer or state_dict). They are "learnable", meaning they change under training but not by gradients. They are not present in parameters().

All learnable parameters are in state_dict(). parameters() are only those that are updated by gradients.

@honglu2875
Copy link

honglu2875 commented Sep 29, 2022

My code is not from stable baselines.

Ahh.... So this misunderstanding spread wider than I thought... Maybe there is a chain of misuse and people never bother checking.
When stable-baseline came out there was no such thing as batch norm by the way. The code is great and should indeed be our implement baseline. But we, "the later generations", really have more responsibilities when working on earlier codes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment