From d632fd3b7a04f48a9a38e5fd54613c54b5d3c7c8 Mon Sep 17 00:00:00 2001 From: William Shen Date: Mon, 2 May 2022 12:13:20 -0400 Subject: [PATCH 1/9] Fix up things and fix will shen reward --- cell2fire/evaluate_model.py | 14 ++++++++++++++ cell2fire/firehose/helpers.py | 10 ++++++++-- cell2fire/firehose/rewards.py | 25 +++++++++++++++++++------ cell2fire/gym_env.py | 25 +++++++++++++++++++------ 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/cell2fire/evaluate_model.py b/cell2fire/evaluate_model.py index 8590f80..61e9ed8 100644 --- a/cell2fire/evaluate_model.py +++ b/cell2fire/evaluate_model.py @@ -17,6 +17,7 @@ ) from firehose.helpers import IgnitionPoint, IgnitionPoints from firehose.results import FirehoseResults +from firehose.rewards import REWARD_FUNCTIONS from firehose.video_recorder import FirehoseVideoRecorder # Map name to ignition point and steps before simulation and steps per action @@ -246,6 +247,19 @@ def get_action(): help="Specifies whether to use a random or fixed fire ignition point." "Choices: fixed, random, or specify path to a ignition point JSON file", ) + parser.add_argument( + "-r", + "--reward", + default="WillShenReward", + help="Specifies the reward function to use", + choices=set(REWARD_FUNCTIONS.keys()), + ) + parser.add_argument( + "--disable-video", action="store_true", help="Disable video recording" + ) + parser.add_argument( + "-d", "--disable-render", action="store_true", help="Disable cv2 rendering" + ) parser.add_argument( "-o", "--output_dir", diff --git a/cell2fire/firehose/helpers.py b/cell2fire/firehose/helpers.py index b20583b..35732c7 100644 --- a/cell2fire/firehose/helpers.py +++ b/cell2fire/firehose/helpers.py @@ -101,7 +101,7 @@ def forest_data(self) -> np.ndarray: @cached_property def reward_data(self) -> np.ndarray: - if(exists(self.reward_datafile)): + if exists(self.reward_datafile): return np.loadtxt(self.reward_datafile, skiprows=6) else: return None @@ -167,7 +167,7 @@ def manipulate_input_data_folder( print(f"Copied modified input data folder to {tmp_dir}") def overwrite_ignition_points(self, ignition_points: IgnitionPoints): - """ Overwrite ignition points CSV file """ + """Overwrite ignition points CSV file""" tmp_dir = self.tmp_input_folder ignition_points_csv = os.path.join(tmp_dir, IgnitionPoints.CSV_NAME) # Only remove ignitions if it already exists @@ -221,3 +221,9 @@ def generate_random_ignition_points( ) # print("Sampled ignition points:", ignition_points) return ignition_points + + def teardown(self): + """Delete temporary input folder""" + if self.tmp_input_folder: + shutil.rmtree(self.tmp_input_folder) + print(f"Deleted {self.tmp_input_folder}") diff --git a/cell2fire/firehose/rewards.py b/cell2fire/firehose/rewards.py index 3bc104b..b94e58a 100644 --- a/cell2fire/firehose/rewards.py +++ b/cell2fire/firehose/rewards.py @@ -51,14 +51,26 @@ class WillShenReward(Reward): def name(cls) -> str: return "WillShenReward" - def __call__(self, scale: float = 10, action: int = -1, **kwargs) -> float: - assert self.env.state.shape == self.env.forest_image.shape[:2] - + def __call__( + self, + fire_scale: float = 10, + dist_scale: float = 0.05, + action: int = -1, + run_asserts: bool = False, + **kwargs + ) -> float: fire_idxs = np.array(np.where(self.env.state > 0)).T num_cells_on_fire = fire_idxs.shape[0] if fire_idxs.size != 0 else 0 - # Proportion of cells on fire - fire_term = 1 - num_cells_on_fire / self.env.num_cells + # -(num cells on fire) / (total num cells in forest) * scale + fire_term = -num_cells_on_fire / self.env.num_cells * fire_scale + + # Some sanity checks + if run_asserts: + assert self.env.state.shape == self.env.forest_image.shape[:2] + assert fire_idxs.shape[1] == 2 + fire_reward = FireSizeReward(self.env) + assert fire_term == fire_reward() # Hack that will suffice for 2x2 and 3x3 if isinstance(action, list): @@ -81,9 +93,10 @@ def __call__(self, scale: float = 10, action: int = -1, **kwargs) -> float: # Penalize actions far away from fire action_dist_term = min_dist_to_fire / self.env.max_dist + scaled_action_dist_term = action_dist_term * dist_scale # % of cells on fire - min dist to fire / scale - reward = fire_term - action_dist_term / scale + reward = fire_term - scaled_action_dist_term return reward diff --git a/cell2fire/gym_env.py b/cell2fire/gym_env.py index 2b49813..8835e8b 100644 --- a/cell2fire/gym_env.py +++ b/cell2fire/gym_env.py @@ -79,8 +79,8 @@ def __init__( # Image of forest which we overlay self.forest_image = self.helper.forest_image self.uforest_image = (self.forest_image * 255).astype("uint8") - - if(self.helper.reward_data is None): + + if self.helper.reward_data is None: self.reward_mask = None else: self.reward_mask = np.where(self.helper.reward_data > 0) @@ -143,7 +143,9 @@ def __init__( self.yx_to_flatten_idx: Dict[Tuple[int, int], int] = { yx: idx for idx, yx, in self.flatten_idx_to_yx.items() } - min_yx, max_yx = np.array(min(self.yx_to_flatten_idx)), np.array(max(self.yx_to_flatten_idx)) + min_yx, max_yx = np.array(min(self.yx_to_flatten_idx)), np.array( + max(self.yx_to_flatten_idx) + ) self.max_dist = np.linalg.norm(max_yx - min_yx) # Note: Reward function. Call this at end of __init__ just so we're safe @@ -182,7 +184,10 @@ def _set_observation_space(self): # Forest as a RGB image # TODO: should we normalize the RGB image? At least divide by 255 so its [0, 1] self.observation_space = spaces.Box( - low=0, high=255, shape=(self.height, self.width, 3), dtype=np.uint8, + low=0, + high=255, + shape=(self.height, self.width, 3), + dtype=np.uint8, ) elif self.observation_type == "forest": # Forest as -1 (harvested), 0 (nothing), 1 (on fire) @@ -192,7 +197,10 @@ def _set_observation_space(self): elif self.observation_space == "time": # Blind model self.observation_space = spaces.Box( - low=0, high=self.max_steps + 1, shape=(1,), dtype=np.uint8, + low=0, + high=self.max_steps + 1, + shape=(1,), + dtype=np.uint8, ) else: raise ValueError(f"Unsupported observation type {self.observation_type}") @@ -282,7 +290,7 @@ def action_masks(self): return mask def _update_counters(self): - """ Update the counters based on the current state of the forest""" + """Update the counters based on the current state of the forest""" harvested = set(zip(*np.where(self.state == -1))) on_fire = set(zip(*np.where(self.state == 1))) @@ -462,6 +470,11 @@ def reset(self, **kwargs): return self.get_observation() + def close(self): + """Clean up after ourselves""" + self.helper.teardown() + super().close() + def main(debug: bool, delay_time: float = 0.0, **env_kwargs): env = FireEnv(**env_kwargs, verbose=debug) From 204ad5d5757a947f364cf1f999ccc02eceaacab3 Mon Sep 17 00:00:00 2001 From: William Shen Date: Mon, 2 May 2022 12:19:41 -0400 Subject: [PATCH 2/9] Fix thing I should have removed. Smartgit no ty --- cell2fire/evaluate_model.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cell2fire/evaluate_model.py b/cell2fire/evaluate_model.py index 61e9ed8..9d6786c 100644 --- a/cell2fire/evaluate_model.py +++ b/cell2fire/evaluate_model.py @@ -234,12 +234,6 @@ def get_action(): parser.add_argument( "-acd", "--action_diameter", default=1, type=int, help="Action diameter" ) - parser.add_argument( - "--disable-video", action="store_true", help="Disable video recording" - ) - parser.add_argument( - "-d", "--disable-render", action="store_true", help="Disable cv2 rendering" - ) parser.add_argument( "-i", "--ignition_type", From 6f4604b783a655d92150904a696592e7a44d6e85 Mon Sep 17 00:00:00 2001 From: William Shen Date: Mon, 2 May 2022 13:48:22 -0400 Subject: [PATCH 3/9] Sub20x20 Reward Experiments --- sub20x20_reward.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 sub20x20_reward.sh diff --git a/sub20x20_reward.sh b/sub20x20_reward.sh new file mode 100755 index 0000000..fb8fbd5 --- /dev/null +++ b/sub20x20_reward.sh @@ -0,0 +1,41 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 +#SBATCH --gres=gpu:volta:1 + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "ppo-maskable" +do + for map in "Sub20x20" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" #"2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for reward in "WillShenReward" "FireSizeReward" + do + for gamma in "0.95" #"0.5" + do + for seed in "1" + do + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" --reward="$reward" + fi + i=$((i+1)) + done + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} \ No newline at end of file From fe3cfe8c72076ad3e83c37ed9a9102d6dba16657 Mon Sep 17 00:00:00 2001 From: William Shen Date: Mon, 2 May 2022 16:53:21 -0400 Subject: [PATCH 4/9] Add Human Expert Baseline --- cell2fire/evaluate_model.py | 12 ++++++++++++ cell2fire/firehose/baselines.py | 23 +++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/cell2fire/evaluate_model.py b/cell2fire/evaluate_model.py index 9d6786c..6301f88 100644 --- a/cell2fire/evaluate_model.py +++ b/cell2fire/evaluate_model.py @@ -1,6 +1,7 @@ import argparse import json import os +import time from typing import Optional from generate_ignition_points import load_ignition_points @@ -10,6 +11,7 @@ from cell2fire.gym_env import FireEnv from firehose.baselines import ( + HumanExpertAlgorithm, HumanInputAlgorithm, NaiveAlgorithm, NoAlgorithm, @@ -50,6 +52,7 @@ "random": RandomAlgorithm, "naive": NaiveAlgorithm, "human": HumanInputAlgorithm, + "expert": HumanExpertAlgorithm, "none": NoAlgorithm, } @@ -167,6 +170,8 @@ def get_action(): if not args.disable_render: env.render() video_recorder.capture_frame() + if args.delay: + time.sleep(args.delay) if reward is None: raise RuntimeError("Reward is None. This should not happen") @@ -254,6 +259,13 @@ def get_action(): parser.add_argument( "-d", "--disable-render", action="store_true", help="Disable cv2 rendering" ) + parser.add_argument( + "--delay", + default=0.0, + type=float, + help="Delay between steps in simulation. For visualization purposes " + "- note: it doesn't get reflected in the video", + ) parser.add_argument( "-o", "--output_dir", diff --git a/cell2fire/firehose/baselines.py b/cell2fire/firehose/baselines.py index 6fec0ba..1471f13 100644 --- a/cell2fire/firehose/baselines.py +++ b/cell2fire/firehose/baselines.py @@ -62,7 +62,10 @@ def predict(self, obs, **kwargs) -> Tuple[Any, Any]: class NaiveAlgorithm(FlatActionSpaceAlgorithm): """ The Naive algorithm selects the cell that is on fire that is closest to the - ignition point in terms of Euclidean distance. + ignition point in terms of Euclidean distance if use_min is True. + + If use_min is False, then we select the point furthest from the ignition. + This is our Frontier baseline essentially. If cells have already been put out, then it will not consider them. If there are no cells on fire, then it will return -1 (no-op). @@ -77,9 +80,10 @@ def __init__(self, env: FireEnv): self.prev_actions: Set[int] = {-1} self.ignition_point = self.env.ignition_points.points[0] self.ignition_point_yx = self.env.flatten_idx_to_yx[self.ignition_point.idx - 1] + self.use_min = True def _update_ignition_point(self): - """ Update ignition point if it has changed, indicating a reset in the environment """ + """Update ignition point if it has changed, indicating a reset in the environment""" current_ignition_point = self.env.ignition_points.points[0] if current_ignition_point != self.ignition_point: @@ -113,8 +117,13 @@ def predict(self, obs, **kwargs) -> Tuple[Any, Any]: # No cells on fire so no-op return -1, None - # Choose closest cell on fire - closest_idx = np.argmin(dist) + if self.use_min: + # Choose closest cell on fire + closest_idx = np.argmin(dist) + else: + # Choose furthest cell on fire + closest_idx = np.argmax(dist) + chosen_fire_yx = fire_yx[closest_idx] chosen_fire_idx = self.env.yx_to_flatten_idx[chosen_fire_yx] @@ -134,3 +143,9 @@ def predict(self, obs, **kwargs) -> Tuple[Any, Any]: self.prev_actions.add(chosen_fire_idx) return chosen_fire_idx, None + + +class HumanExpertAlgorithm(NaiveAlgorithm): + def __init__(self, env: FireEnv): + super().__init__(env) + self.use_min = False From d65ee0a56f9d8068ab0ca440a8c8a5c56316ce88 Mon Sep 17 00:00:00 2001 From: William Shen Date: Mon, 2 May 2022 17:33:04 -0400 Subject: [PATCH 5/9] Support DQN and add sub20x20 final training script --- cell2fire/rl_experiment_vectorized.py | 7 +---- sub20x20_final.sh | 37 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) create mode 100755 sub20x20_final.sh diff --git a/cell2fire/rl_experiment_vectorized.py b/cell2fire/rl_experiment_vectorized.py index 13af850..af2dde1 100644 --- a/cell2fire/rl_experiment_vectorized.py +++ b/cell2fire/rl_experiment_vectorized.py @@ -154,7 +154,7 @@ def _get_model(self, env) -> Model: print( f"Overrode tensorboard log dir from {old_tf_logdir} to {self.tf_logdir}" ) - elif args.algo in {"ppo", "a2c", "trpo", "ppo-maskable"}: + elif args.algo in {"ppo", "a2c", "trpo", "ppo-maskable", "dqn"}: # If no reload specified then just create a new model model_cls = SB3_ALGO_TO_MODEL_CLASS[args.algo] model = model_cls( @@ -165,11 +165,6 @@ def _get_model(self, env) -> Model: gamma=args.gamma, policy_kwargs=self.model_kwargs, ) - elif args.algo == "dqn": - # DQN doesn't support gamma so handle separately - model = DQN( - args.architecture, env, verbose=1, tensorboard_log=self.tf_logdir - ) else: raise NotImplementedError diff --git a/sub20x20_final.sh b/sub20x20_final.sh new file mode 100755 index 0000000..c95171c --- /dev/null +++ b/sub20x20_final.sh @@ -0,0 +1,37 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "a2c" "ppo" "ppo-maskable" "dqn" +do + for map in "Sub20x20" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" #"2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for gamma in "0.99" "0.95" "0.9" + do + for seed in "1" "2" "3" # 3 seeds + do + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} \ No newline at end of file From a12a47095bf64fb2b1279d63dbbef75d8e9293cd Mon Sep 17 00:00:00 2001 From: William Shen Date: Tue, 3 May 2022 10:18:58 -0400 Subject: [PATCH 6/9] Video Editor ipynb --- cell2fire/evaluate_model.py | 2 +- cell2fire/firehose/video_recorder.py | 4 +- cell2fire/videos/video_editor.ipynb | 202 +++++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 cell2fire/videos/video_editor.ipynb diff --git a/cell2fire/evaluate_model.py b/cell2fire/evaluate_model.py index 6301f88..63ccc0b 100644 --- a/cell2fire/evaluate_model.py +++ b/cell2fire/evaluate_model.py @@ -124,7 +124,7 @@ def main(args): # Get the model for the algorithm and setup video recorder model = _get_model(algo=args.algo, model_path=args.model_path, env=env) video_recorder = FirehoseVideoRecorder( - env, algo=args.algo, disable_video=args.disable_video + env, args=args, disable_video=args.disable_video ) # Override observation type if required - this is for maskable PPO mostly diff --git a/cell2fire/firehose/video_recorder.py b/cell2fire/firehose/video_recorder.py index d5df2e3..9d3c8d8 100644 --- a/cell2fire/firehose/video_recorder.py +++ b/cell2fire/firehose/video_recorder.py @@ -8,7 +8,7 @@ class FirehoseVideoRecorder: """Wrapper around gym VideoRecorder to record firehose environment.""" - def __init__(self, env: FireEnv, algo: str, disable_video: bool = False): + def __init__(self, env: FireEnv, args, disable_video: bool = False): if disable_video: self.video_recorder = None else: @@ -17,7 +17,7 @@ def __init__(self, env: FireEnv, algo: str, disable_video: bool = False): if not os.path.exists("videos"): os.mkdir("videos") - video_fname = f"videos/{algo}-{date_str}.mp4" + video_fname = f"videos/{args.algo}-{args.map}-{date_str}.mp4" self.video_recorder = VideoRecorder(env, video_fname, enabled=True) def capture_frame(self): diff --git a/cell2fire/videos/video_editor.ipynb b/cell2fire/videos/video_editor.ipynb new file mode 100644 index 0000000..74e307f --- /dev/null +++ b/cell2fire/videos/video_editor.ipynb @@ -0,0 +1,202 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "8b8c973e", + "metadata": {}, + "outputs": [], + "source": [ + "# !pip install moviepy" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b7ed1831", + "metadata": {}, + "outputs": [], + "source": [ + "from moviepy.editor import *\n", + "from typing import List, Union\n", + "from datetime import datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "cc3b4e2a", + "metadata": {}, + "outputs": [], + "source": [ + "class Video:\n", + " def __init__(self, fnames: Union[str, List[str]], margin: int=0):\n", + " # Stack videos if required\n", + " if isinstance(fnames, str):\n", + " self.clip = VideoFileClip(fnames)\n", + " self.suffix = fnames.split(\".mp4\")[0]\n", + " else:\n", + " clips = [VideoFileClip(fname).margin(margin) for fname in fnames]\n", + " self.clip = clips_array([clips])\n", + "\n", + " date_str = datetime.now().strftime(\"%Y-%m-%d_%H-%M-%S\")\n", + " self.suffix = date_str + \"-stacked\"\n", + "\n", + " def adjust_speed(self, speed_scale: float):\n", + " assert speed_scale > 0\n", + " self.clip = self.clip.fx(vfx.speedx, speed_scale)\n", + " self.suffix += f\"-speed={speed_scale}x\"\n", + "\n", + " def write(self):\n", + " out_fname = self.suffix + \".mp4\"\n", + " self.clip.write_videofile(out_fname)\n", + "\n", + " def show(self):\n", + " return self.clip.ipython_display()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "86cfb613", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Moviepy - Building video __temp__.mp4.\n", + "Moviepy - Writing video __temp__.mp4\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " " + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Moviepy - Done !\n", + "Moviepy - video ready __temp__.mp4\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "video = Video(\n", + " [\n", + " \"ppo-maskable-Sub20x20-2022-05-03_10-16-32.mp4\",\n", + " \"naive-Sub20x20-2022-05-03_10-16-00.mp4\",\n", + " ]\n", + ")\n", + "video.adjust_speed(0.9)\n", + "video.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "bf65ea6d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Moviepy - Building video __temp__.mp4.\n", + "Moviepy - Writing video __temp__.mp4\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " \r" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Moviepy - Done !\n", + "Moviepy - video ready __temp__.mp4\n", + "Moviepy - Building video ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4.\n", + "Moviepy - Writing video ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " " + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Moviepy - Done !\n", + "Moviepy - video ready ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r" + ] + } + ], + "source": [ + "video.write()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 0bb86ce929a80c868f65b150978a4e5711b038be Mon Sep 17 00:00:00 2001 From: William Shen Date: Tue, 3 May 2022 10:22:09 -0400 Subject: [PATCH 7/9] Update video editor ipynb to support gifs --- cell2fire/videos/video_editor.ipynb | 57 +++++++---------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/cell2fire/videos/video_editor.ipynb b/cell2fire/videos/video_editor.ipynb index 74e307f..0c7009e 100644 --- a/cell2fire/videos/video_editor.ipynb +++ b/cell2fire/videos/video_editor.ipynb @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 41, "id": "cc3b4e2a", "metadata": {}, "outputs": [], @@ -47,9 +47,13 @@ " self.clip = self.clip.fx(vfx.speedx, speed_scale)\n", " self.suffix += f\"-speed={speed_scale}x\"\n", "\n", - " def write(self):\n", - " out_fname = self.suffix + \".mp4\"\n", - " self.clip.write_videofile(out_fname)\n", + " def write(self, video_type: str=\"mp4\"):\n", + " out_fname = self.suffix + \".\" + video_type\n", + "\n", + " if video_type == \"gif\":\n", + " self.clip.write_gif(out_fname)\n", + " else:\n", + " self.clip.write_videofile(out_fname)\n", "\n", " def show(self):\n", " return self.clip.ipython_display()" @@ -57,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 42, "id": "86cfb613", "metadata": {}, "outputs": [ @@ -101,7 +105,7 @@ "" ] }, - "execution_count": 37, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -119,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 43, "id": "bf65ea6d", "metadata": {}, "outputs": [ @@ -127,9 +131,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Moviepy - Building video __temp__.mp4.\n", - "Moviepy - Writing video __temp__.mp4\n", - "\n" + "MoviePy - Building file 2022-05-03_10-20-32-stacked-speed=0.9x.gif with imageio.\n" ] }, { @@ -138,43 +140,10 @@ "text": [ " \r" ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Moviepy - Done !\n", - "Moviepy - video ready __temp__.mp4\n", - "Moviepy - Building video ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4.\n", - "Moviepy - Writing video ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - " " - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Moviepy - Done !\n", - "Moviepy - video ready ppo-maskable-Sub20x20-2022-05-03_09-56-57-speed=0.9x.mp4\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r" - ] } ], "source": [ - "video.write()" + "video.write(\"gif\")" ] } ], From 0e621a79609561f247f9244d475d095f194d61f7 Mon Sep 17 00:00:00 2001 From: William Shen Date: Tue, 3 May 2022 14:17:33 -0400 Subject: [PATCH 8/9] Add experiment scripts --- .gitignore | 3 +++ sub20x20_acd2.sh | 34 +++++++++++++++++++++++++++++++ sub20x20_final.sh | 8 ++++---- sub20x20_maskable-only.sh | 38 ++++++++++++++++++++++++++++++++++ sub40x40_cpu.sh | 42 ++++++++++++++++++++++++++++++++++++++ sub40x40_experiments.sh | 38 ++++++++++++++++++++++++++++++++++ sub40x40_supercloud.sh | 43 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 202 insertions(+), 4 deletions(-) create mode 100755 sub20x20_acd2.sh create mode 100755 sub20x20_maskable-only.sh create mode 100755 sub40x40_cpu.sh create mode 100755 sub40x40_experiments.sh create mode 100755 sub40x40_supercloud.sh diff --git a/.gitignore b/.gitignore index 0ceca4d..51bde49 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ train_args.json *-ignition-points.json stable-baselines3-contrib +*.gif +slurm*.out + diff --git a/sub20x20_acd2.sh b/sub20x20_acd2.sh new file mode 100755 index 0000000..769b377 --- /dev/null +++ b/sub20x20_acd2.sh @@ -0,0 +1,34 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --exclusive +i=1 + +# PPO Maskable with Action Diameter of 2 +for algo in "ppo-maskable" +do + for map in "Sub20x20" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "2" #"2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for gamma in "0.99" "0.9" + do + for seed in "1" # 3 seeds + do + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" --num-processes=48 + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} diff --git a/sub20x20_final.sh b/sub20x20_final.sh index c95171c..242df04 100755 --- a/sub20x20_final.sh +++ b/sub20x20_final.sh @@ -1,9 +1,9 @@ #!/bin/sh #SBATCH --ntasks=1 -#SBATCH --cpus-per-task 16 - +#SBATCH --exclusive i=1 +# Supercloud xeon-p8 has 48 cores per node # We will run random and naive comparisons separately. # They are supported in the evaluate_model.py script @@ -22,7 +22,7 @@ do for seed in "1" "2" "3" # 3 seeds do if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then - python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" --num-processes=48 fi i=$((i+1)) done @@ -34,4 +34,4 @@ do done echo $i -echo ${SLURM_ARRAY_TASK_ID} \ No newline at end of file +echo ${SLURM_ARRAY_TASK_ID} diff --git a/sub20x20_maskable-only.sh b/sub20x20_maskable-only.sh new file mode 100755 index 0000000..64f600c --- /dev/null +++ b/sub20x20_maskable-only.sh @@ -0,0 +1,38 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 +#SBATCH --gres=gpu:volta:1 + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "ppo-maskable" +do + for map in "Sub20x20" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" #"2" #"xy" + do + for architecture in "CnnPolicy" + do + for gamma in "0.9" + do + for seed in "1" "2" "3" # 3 seeds + do + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" --num-processes=16 --train_steps=20000000 --tf_logdir=/home/gridsan/wshen/firehosetmp-sub20x20-maskable + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} diff --git a/sub40x40_cpu.sh b/sub40x40_cpu.sh new file mode 100755 index 0000000..64b3bf9 --- /dev/null +++ b/sub40x40_cpu.sh @@ -0,0 +1,42 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 + +# https://supercloud.mit.edu/submitting-jobs +#source /etc/profile +#module load anaconda/2022a + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "ppo-maskable" +do + for map in "Sub40x40" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" "2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for gamma in "0.95" "0.90" #"0.5" + do + for seed in "1" + do + echo $i $SLURM_ARRAY_TASK_ID + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} diff --git a/sub40x40_experiments.sh b/sub40x40_experiments.sh new file mode 100755 index 0000000..ce55bca --- /dev/null +++ b/sub40x40_experiments.sh @@ -0,0 +1,38 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "ppo-maskable" +do + for map in "Sub40x40" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" "2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for gamma in "0.95" "0.90" #"0.5" + do + for seed in "1" + do + echo $i $SLURM_ARRAY_TASK_ID + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} diff --git a/sub40x40_supercloud.sh b/sub40x40_supercloud.sh new file mode 100755 index 0000000..9f2a8bf --- /dev/null +++ b/sub40x40_supercloud.sh @@ -0,0 +1,43 @@ +#!/bin/sh +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task 16 +#SBATCH --gres=gpu:volta:1 + +# https://supercloud.mit.edu/submitting-jobs +#source /etc/profile +#module load anaconda/2022a + +i=1 + + +# We will run random and naive comparisons separately. +# They are supported in the evaluate_model.py script +for algo in "ppo-maskable" +do + for map in "Sub40x40" #"Harvest40x40" "Sub40x40" + do + for ignition_type in "random" # "fixed" + do + for action_diameter in "1" "2" #"xy" + do + for architecture in "MlpPolicy" "CnnPolicy" + do + for gamma in "0.95" "0.90" #"0.5" + do + for seed in "1" + do + echo $i $SLURM_ARRAY_TASK_ID + if [ $((i)) -eq $((SLURM_ARRAY_TASK_ID + 0)) ]; then + python cell2fire/rl_experiment_vectorized.py --algo="$algo" --map="$map" --ignition_type="$ignition_type" --action_diameter="$action_diameter" --seed=$seed --architecture="$architecture" --gamma="$gamma" --reward WillShenReward + fi + i=$((i+1)) + done + done + done + done + done + done +done + +echo $i +echo ${SLURM_ARRAY_TASK_ID} From 03e79221044c2339dd295978cf3a43101bd44f1e Mon Sep 17 00:00:00 2001 From: William Shen Date: Tue, 3 May 2022 14:18:34 -0400 Subject: [PATCH 9/9] Switch back to fire size reward in eval script as default --- cell2fire/evaluate_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cell2fire/evaluate_model.py b/cell2fire/evaluate_model.py index 63ccc0b..37d60fe 100644 --- a/cell2fire/evaluate_model.py +++ b/cell2fire/evaluate_model.py @@ -249,7 +249,7 @@ def get_action(): parser.add_argument( "-r", "--reward", - default="WillShenReward", + default="FireSizeReward", help="Specifies the reward function to use", choices=set(REWARD_FUNCTIONS.keys()), )