2016-04-27 08:00:58 -07:00
import logging
import json
import os
import re
import tarfile
import tempfile
from gym import error , monitoring
from gym . scoreboard . client import resource , util
2016-05-02 22:43:17 -07:00
import numpy as np
2016-04-27 08:00:58 -07:00
MAX_VIDEOS = 100
logger = logging . getLogger ( __name__ )
video_name_re = re . compile ( ' ^[ \ w.-]+ \ .(mp4|avi|json)$ ' )
metadata_name_re = re . compile ( ' ^[ \ w.-]+ \ .meta \ .json$ ' )
2016-09-23 01:04:26 -07:00
def upload ( training_dir , algorithm_id = None , writeup = None , benchmark_id = None , api_key = None , ignore_open_monitors = False ) :
2016-04-27 08:00:58 -07:00
""" Upload the results of training (as automatically recorded by your
env ' s monitor) to OpenAI Gym.
Args :
training_dir ( Optional [ str ] ) : A directory containing the results of a training run .
2016-08-13 13:25:19 -04:00
algorithm_id ( Optional [ str ] ) : An algorithm id indicating the particular version of the algorithm ( including choices of parameters ) you are running ( visit https : / / gym . openai . com / algorithms to create an id )
2016-09-23 01:04:26 -07:00
benchmark_id ( Optional [ str ] ) : The benchmark that these evaluations belong to . Will recursively search through training_dir for any Gym manifests . This feature is currently pre - release .
2016-04-27 08:00:58 -07:00
writeup ( Optional [ str ] ) : A Gist URL ( of the form https : / / gist . github . com / < user > / < id > ) containing your writeup for this evaluation .
api_key ( Optional [ str ] ) : Your OpenAI API key . Can also be provided as an environment variable ( OPENAI_GYM_API_KEY ) .
"""
2016-09-23 01:04:26 -07:00
if benchmark_id :
# TODO: validate the number of matching evaluations
benchmark_run = resource . BenchmarkRun . create ( benchmark_id = benchmark_id , algorithm_id = algorithm_id )
benchmark_run_id = benchmark_run . id
recurse = True
# Don't propagate algorithm_id to Evaluation if we're running as part of a benchmark
algorithm_id = None
else :
benchmark_run_id = None
recurse = False
# Discover training directories
directories = [ ]
if recurse :
for name , _ , files in os . walk ( training_dir ) :
if monitoring . detect_training_manifests ( name , files = files ) :
directories . append ( name )
else :
directories . append ( training_dir )
# Actually do the uploads.
for training_dir in directories :
_upload ( training_dir , algorithm_id , writeup , benchmark_run_id , api_key , ignore_open_monitors )
return benchmark_run_id
def _upload ( training_dir , algorithm_id = None , writeup = None , benchmark_run_id = None , api_key = None , ignore_open_monitors = False ) :
2016-05-10 17:25:15 -07:00
if not ignore_open_monitors :
2016-05-18 01:27:58 -07:00
open_monitors = monitoring . _open_monitors ( )
2016-05-10 17:25:15 -07:00
if len ( open_monitors ) > 0 :
envs = [ m . env . spec . id if m . env . spec else ' (unknown) ' for m in open_monitors ]
raise error . Error ( " Still have an open monitor on {} . You must run ' env.monitor.close() ' before uploading. " . format ( ' , ' . join ( envs ) ) )
2016-04-27 08:00:58 -07:00
2016-04-27 12:46:24 -07:00
env_info , training_episode_batch , training_video = upload_training_data ( training_dir , api_key = api_key )
2016-04-28 07:37:32 -07:00
env_id = env_info [ ' env_id ' ]
2016-04-27 08:00:58 -07:00
training_episode_batch_id = training_video_id = None
if training_episode_batch :
training_episode_batch_id = training_episode_batch . id
if training_video :
training_video_id = training_video . id
if logger . level < = logging . INFO :
if training_episode_batch_id is not None and training_video_id is not None :
2016-04-28 07:37:32 -07:00
logger . info ( ' [ %s ] Creating evaluation object from %s with learning curve and training video ' , env_id , training_dir )
2016-04-27 08:00:58 -07:00
elif training_episode_batch_id is not None :
2016-04-28 07:37:32 -07:00
logger . info ( ' [ %s ] Creating evaluation object from %s with learning curve ' , env_id , training_dir )
2016-04-27 08:00:58 -07:00
elif training_video_id is not None :
2016-04-28 07:37:32 -07:00
logger . info ( ' [ %s ] Creating evaluation object from %s with training video ' , env_id , training_dir )
2016-04-27 08:00:58 -07:00
else :
2016-04-28 07:37:32 -07:00
raise error . Error ( " [ %s ] You didn ' t have any recorded training data in {} . Once you ' ve used ' env.monitor.start(training_dir) ' to start recording, you need to actually run some rollouts. Please join the community chat on https://gym.openai.com if you have any issues. " . format ( env_id , training_dir ) )
2016-04-27 08:00:58 -07:00
evaluation = resource . Evaluation . create (
training_episode_batch = training_episode_batch_id ,
training_video = training_video_id ,
env = env_info [ ' env_id ' ] ,
algorithm = {
' id ' : algorithm_id ,
} ,
2016-09-23 01:04:26 -07:00
benchmark_run_id = benchmark_run_id ,
2016-04-27 08:00:58 -07:00
writeup = writeup ,
gym_version = env_info [ ' gym_version ' ] ,
api_key = api_key ,
)
logger . info (
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2016-04-28 07:37:32 -07:00
You successfully uploaded your evaluation on % s to
2016-04-27 08:00:58 -07:00
OpenAI Gym ! You can find it at :
% s
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2016-04-28 07:37:32 -07:00
""" .rstrip(), env_id, evaluation.web_url())
2016-04-27 08:00:58 -07:00
return evaluation
def upload_training_data ( training_dir , api_key = None ) :
# Could have multiple manifests
results = monitoring . load_results ( training_dir )
if not results :
raise error . Error ( ''' Could not find any manifest files in {} .
( HINT : this usually means you did not yet close ( ) your env . monitor and have not yet exited the process . You should call ' env.monitor.start(training_dir) ' at the start of training and ' env.monitor.close() ' at the end , or exit the process . ) ''' .format(training_dir))
manifests = results [ ' manifests ' ]
env_info = results [ ' env_info ' ]
timestamps = results [ ' timestamps ' ]
episode_lengths = results [ ' episode_lengths ' ]
episode_rewards = results [ ' episode_rewards ' ]
2016-09-23 01:04:26 -07:00
episode_types = results [ ' episode_types ' ]
2016-05-29 09:07:09 -07:00
main_seeds = results [ ' main_seeds ' ]
seeds = results [ ' seeds ' ]
2016-04-27 08:00:58 -07:00
videos = results [ ' videos ' ]
2016-04-28 07:37:32 -07:00
env_id = env_info [ ' env_id ' ]
logger . debug ( ' [ %s ] Uploading data from manifest %s ' , env_id , ' , ' . join ( manifests ) )
2016-04-27 08:00:58 -07:00
# Do the relevant uploads
if len ( episode_lengths ) > 0 :
2016-09-23 01:04:26 -07:00
training_episode_batch = upload_training_episode_batch ( episode_lengths , episode_rewards , episode_types , timestamps , main_seeds , seeds , api_key , env_id = env_id )
2016-04-27 08:00:58 -07:00
else :
training_episode_batch = None
if len ( videos ) > MAX_VIDEOS :
2016-04-28 07:37:32 -07:00
logger . warn ( ' [ %s ] You recorded videos for %s episodes, but the scoreboard only supports up to %s . We will automatically subsample for you, but you also might wish to adjust your video recording rate. ' , env_id , len ( videos ) , MAX_VIDEOS )
2016-05-02 22:43:17 -07:00
subsample_inds = np . linspace ( 0 , len ( videos ) - 1 , MAX_VIDEOS ) . astype ( ' int ' )
videos = [ videos [ i ] for i in subsample_inds ]
2016-04-27 08:00:58 -07:00
if len ( videos ) > 0 :
2016-04-28 07:37:32 -07:00
training_video = upload_training_video ( videos , api_key , env_id = env_id )
2016-04-27 08:00:58 -07:00
else :
training_video = None
return env_info , training_episode_batch , training_video
2016-09-23 01:04:26 -07:00
def upload_training_episode_batch ( episode_lengths , episode_rewards , episode_types , timestamps , main_seeds , seeds , api_key = None , env_id = None ) :
2016-04-28 07:37:32 -07:00
logger . info ( ' [ %s ] Uploading %d episodes of training data ' , env_id , len ( episode_lengths ) )
2016-04-27 08:00:58 -07:00
file_upload = resource . FileUpload . create ( purpose = ' episode_batch ' , api_key = api_key )
file_upload . put ( {
' episode_lengths ' : episode_lengths ,
' episode_rewards ' : episode_rewards ,
2016-09-23 01:04:26 -07:00
' episode_types ' : episode_types ,
2016-04-27 08:00:58 -07:00
' timestamps ' : timestamps ,
2016-05-29 09:07:09 -07:00
' main_seeds ' : main_seeds ,
' seeds ' : seeds ,
2016-04-27 08:00:58 -07:00
} )
return file_upload
2016-04-28 07:37:32 -07:00
def upload_training_video ( videos , api_key = None , env_id = None ) :
2016-04-27 08:00:58 -07:00
""" videos: should be list of (video_path, metadata_path) tuples """
with tempfile . TemporaryFile ( ) as archive_file :
2016-04-28 07:37:32 -07:00
write_archive ( videos , archive_file , env_id = env_id )
2016-04-27 08:00:58 -07:00
archive_file . seek ( 0 )
2016-04-28 07:37:32 -07:00
logger . info ( ' [ %s ] Uploading videos of %d training episodes ( %d bytes) ' , env_id , len ( videos ) , util . file_size ( archive_file ) )
2016-04-27 08:00:58 -07:00
file_upload = resource . FileUpload . create ( purpose = ' video ' , content_type = ' application/vnd.openai.video+x-compressed ' , api_key = api_key )
file_upload . put ( archive_file , encode = None )
return file_upload
2016-04-28 07:37:32 -07:00
def write_archive ( videos , archive_file , env_id = None ) :
2016-04-27 08:00:58 -07:00
if len ( videos ) > MAX_VIDEOS :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] Trying to upload {} videos, but there is a limit of {} currently. If you actually want to upload this many videos, please email gym@openai.com with your use-case. ' . format ( env_id , MAX_VIDEOS , len ( videos ) ) )
2016-04-27 08:00:58 -07:00
2016-04-28 07:37:32 -07:00
logger . debug ( ' [ %s ] Preparing an archive of %d videos: %s ' , env_id , len ( videos ) , videos )
2016-04-27 08:00:58 -07:00
# Double check that there are no collisions
basenames = set ( )
manifest = {
' version ' : 0 ,
' videos ' : [ ]
}
with tarfile . open ( fileobj = archive_file , mode = ' w:gz ' ) as tar :
for video_path , metadata_path in videos :
video_name = os . path . basename ( video_path )
metadata_name = os . path . basename ( metadata_path )
if not os . path . exists ( video_path ) :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] No such video file {} . (HINT: Your video recorder may have broken midway through the run. You can check this with `video_recorder.functional`.) ' . format ( env_id , video_path ) )
2016-04-27 08:00:58 -07:00
elif not os . path . exists ( metadata_path ) :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] No such metadata file {} . (HINT: this should be automatically created when using a VideoRecorder instance.) ' . format ( env_id , video_path ) )
2016-04-27 08:00:58 -07:00
# Do some sanity checking
if video_name in basenames :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] Duplicated video name {} in video list: {} ' . format ( env_id , video_name , videos ) )
2016-04-27 08:00:58 -07:00
elif metadata_name in basenames :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] Duplicated metadata file name {} in video list: {} ' . format ( env_id , metadata_name , videos ) )
2016-04-27 08:00:58 -07:00
elif not video_name_re . search ( video_name ) :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] Invalid video name {} (must match {} ) ' . format ( env_id , video_name , video_name_re . pattern ) )
2016-04-27 08:00:58 -07:00
elif not metadata_name_re . search ( metadata_name ) :
2016-04-28 07:37:32 -07:00
raise error . Error ( ' [ {} ] Invalid metadata file name {} (must match {} ) ' . format ( env_id , metadata_name , metadata_name_re . pattern ) )
2016-04-27 08:00:58 -07:00
# Record that we've seen these names; add to manifest
basenames . add ( video_name )
basenames . add ( metadata_name )
manifest [ ' videos ' ] . append ( ( video_name , metadata_name ) )
# Import the files into the archive
tar . add ( video_path , arcname = video_name , recursive = False )
tar . add ( metadata_path , arcname = metadata_name , recursive = False )
2016-05-28 03:52:27 -03:00
f = tempfile . NamedTemporaryFile ( mode = ' w+ ' , delete = False )
try :
2016-04-27 08:00:58 -07:00
json . dump ( manifest , f )
2016-05-28 03:52:27 -03:00
f . close ( )
2016-04-27 08:00:58 -07:00
tar . add ( f . name , arcname = ' manifest.json ' )
2016-05-28 03:52:27 -03:00
finally :
f . close ( )
2016-06-01 02:42:22 -07:00
os . remove ( f . name )