1+ # Copyright 2017 reinforce.io. All Rights Reserved.
2+ #
3+ # Licensed under the Apache License, Version 2.0 (the "License");
4+ # you may not use this file except in compliance with the License.
5+ # You may obtain a copy of the License at
6+ #
7+ # http://www.apache.org/licenses/LICENSE-2.0
8+ #
9+ # Unless required by applicable law or agreed to in writing, software
10+ # distributed under the License is distributed on an "AS IS" BASIS,
11+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ # See the License for the specific language governing permissions and
13+ # limitations under the License.
14+ # ==============================================================================
15+
16+ import inspect
17+ import os
18+ import numpy as np
19+ import tensorflow as tf
20+ from tensorforce import TensorForceError
21+
22+ class MetaParameterRecorder (object ):
23+ """
24+ Class to record MetaParameters as well as Summary/Description for TensorBoard (TEXT & FILE will come later)
25+
26+ #### General:
27+
28+ * format_type: used to configure data convertion for TensorBoard=0, TEXT & JSON (not Implemented), etc
29+ """
30+
31+ def __init__ (self , current_frame ):
32+ """
33+ Init the MetaPrameterRecord with "Agent" parameters by passing inspect.currentframe() from Agent Class
34+
35+ The Init will search back to find the parent class to capture all passed parameters and store
36+ them in "self.meta_params".
37+
38+ NOTE: Currently only optimized for TensorBoard output
39+
40+ TODO: Add JSON Export, TEXT EXPORT
41+
42+ Args:
43+ current_frame: frame value from class to obtain metaparameters[= inspect.currentframe()]
44+
45+ """
46+ self .ignore_unknown_dtypes = False
47+ self .meta_params = dict ()
48+ self .method_calling = inspect .getframeinfo (current_frame )[2 ]
49+
50+ _ , _ , __ , self .vals_current = inspect .getargvalues (current_frame )
51+ #self is the class name of the frame involved
52+ if 'self' in self .vals_current :
53+ self .recorded_class_type = self .vals_current ['self' ]
54+ # Add explicit AgentName item so class can be deleted
55+ self .meta_params ['AgentName' ] = str (self .vals_current ['self' ])
56+
57+ frame_list = inspect .getouterframes (current_frame )
58+
59+ for frame in frame_list :
60+ #Rather than frame.frame (named tuple), use [0] for python2
61+ args , varargs , keywords , vals = inspect .getargvalues (frame [0 ])
62+ if 'self' in vals :
63+ if self .recorded_class_type == vals ['self' ]:
64+ for i in args :
65+ self .meta_params [i ] = vals [i ]
66+ # Remove the "CLASS" from the dictionary, has no value "AgentName" contains STR of Class
67+ del self .meta_params ['self' ]
68+
69+
70+ def merge_custom (self , custom_dict ):
71+ if type (custom_dict ) is not dict :
72+ raise TensorForceError ("Error: MetaParameterRecorder 'meta_dict' must be passed a dictionary but was passed a type {} which is not supported." .format (str (type (custom_dictdata ))))
73+ for key in custom_dict :
74+ if key in self .meta_params :
75+ raise TensorForceError ("Error: MetaParameterRecorder 'meta_dict' key {} conflicts with internal key, please change passed key." .format (str (key )))
76+ self .meta_params [key ] = custom_dict [key ]
77+ # This line assumes the merge data came from summary_spec['meta_dict'], remove this from summary_spec
78+ del self .meta_params ['summary_spec' ]['meta_dict' ]
79+
80+ def text_output (self , format_type = 1 ):
81+ print ('======================= ' + self .meta_params ['AgentName' ] + ' ====================================' )
82+ for key in self .meta_params :
83+ print (" " , key , type (self .meta_params [key ]),"=" , self .convert_data_to_string (self .meta_params [key ], format_type = format_type ))
84+ print ('======================= ' + self .meta_params ['AgentName' ] + ' ====================================' )
85+
86+ def convert_dictionary_to_string (self , data , indent = 0 , format_type = 0 , seperator = None , eol = None ):
87+ data_string = ""
88+ add_seperator = ""
89+ if eol is None :
90+ eol = os .linesep
91+ if seperator is None :
92+ seperator = ", "
93+
94+ #This should not ever occur but here as a catch
95+ if type (data ) is not dict :
96+ raise TensorForceError ("Error: MetaParameterRecorder Dictionary conversion was passed a type {} not supported." .format (str (type (data ))))
97+
98+ if format_type == 0 : # TensorBoard
99+ label = ""
100+ div = ""
101+ if indent > 0 :
102+ label = " | "
103+ div = "--- | "
104+ data_string += label + "Key | Value" + eol + div + "--- | ----" + eol
105+
106+ for key in data :
107+ key_txt = key
108+ if format_type == 0 : # TensorBoard
109+ key_txt = "**" + key + "**"
110+ key_value_sep = ' | '
111+ if indent > 0 :
112+ key_txt = " | " + key_txt
113+
114+ data_string += add_seperator + key_txt + key_value_sep + self .convert_data_to_string (data [key ], seperator = seperator , indent = indent + 1 ) + eol
115+
116+ return data_string
117+
118+ def convert_list_to_string (self , data , indent = 0 , format_type = 0 , eol = None , count = True ):
119+ data_string = ""
120+ if eol is None :
121+ eol = os .linesep
122+
123+ #This should not ever occur but here as a catch
124+ if type (data ) is not list :
125+ raise TensorForceError ("Error: MetaParameterRecorder List conversion was passed a type {} not supported." .format (str (type (data ))))
126+
127+ for index ,line in enumerate (data ):
128+ data_string_prefix = ""
129+ if count and indent == 0 :
130+ data_string_prefix = str (index + 1 )+ ". "
131+ if format_type == 0 : # TensorBoard
132+ #Only add indent for 2nd item and beyond as this is likely a dictionary entry
133+ if indent > 0 and index > 0 :
134+ data_string_prefix = " | " + data_string_prefix
135+ if index == (len (data )- 1 ):
136+ append_eol = ""
137+ else :
138+ append_eol = eol
139+ data_string += data_string_prefix + self .convert_data_to_string (line , indent = indent + 1 ) + append_eol
140+
141+ return data_string
142+
143+ def convert_ndarray_to_md (self , data , format_type = 0 , eol = None ):
144+ data_string = ""
145+ data_string1 = "|Row|"
146+ data_string2 = "|:---:|"
147+ if eol is None :
148+ eol = os .linesep
149+
150+ #This should not ever occur but here as a catch
151+ if type (data ) is not np .ndarray :
152+ raise TensorForceError ("Error: MetaParameterRecorder ndarray conversion was passed a type {} not supported." .format (str (type (data ))))
153+
154+ shape = data .shape
155+ rank = data .ndim
156+
157+ if rank == 2 :
158+ for col in range (shape [1 ]):
159+ data_string1 += "Col-" + str (col ) + "|"
160+ data_string2 += ":----:|"
161+ data_string += data_string1 + eol + data_string2 + eol
162+
163+ for row in range (shape [0 ]):
164+ data_string += "|" + str (row ) + "|"
165+ for col in range (shape [1 ]):
166+ data_string += str (data [row ,col ]) + "|"
167+
168+ if row != (shape [0 ]- 1 ):
169+ data_string += eol
170+
171+ elif rank == 1 :
172+ data_string += "|Row|Col-0|" + eol + "|:----:|:----:|" + eol
173+
174+ for row in range (shape [0 ]):
175+ data_string += str (row ) + "|" + str (data [row ]) + "|" + eol
176+
177+ return data_string
178+
179+ def convert_data_to_string (self , data , indent = 0 , format_type = 0 , seperator = None , eol = None ):
180+ data_string = ""
181+ if type (data ) is int :
182+ data_string = str (data )
183+ elif type (data ) is float :
184+ data_string = str (data )
185+ elif type (data ) is str :
186+ data_string = data
187+ elif type (data ) is tuple :
188+ data_string = str (data )
189+ elif type (data ) is list :
190+ data_string = self .convert_list_to_string (data , indent = indent , eol = eol )
191+ elif type (data ) is bool :
192+ data_string = str (data )
193+ elif type (data ) is dict :
194+ data_string = self .convert_dictionary_to_string (data , indent = indent , seperator = seperator )
195+ elif type (data ) is np .ndarray :
196+ if format_type == 0 : # TensorBoard
197+ data_string = self .convert_ndarray_to_md (data )
198+ else :
199+ data_string = str (data )
200+ elif data is None :
201+ data_string = "None"
202+ else :
203+ if not self .ignore_unknown_dtypes :
204+ data_string = "Error: MetaParameterRecorder Type conversion from type {} not supported." .format (str (type (data )))
205+ data_string += " (" + str (data )+ ") "
206+ else :
207+ if format_type == 0 : # TensorBoard
208+ data_string = "**?**"
209+
210+ return data_string
211+
212+ def build_metagraph_list (self ):
213+ """
214+ Convert MetaParams into TF Summary Format and create summary_op
215+
216+ Args:
217+ None
218+
219+ Returns:
220+ Merged TF Op for TEXT summary elements, should only be executed once to reduce data duplication
221+
222+ """
223+ ops = []
224+
225+ self .ignore_unknown_dtypes = True
226+ for key in sorted (self .meta_params ):
227+ value = self .convert_data_to_string (self .meta_params [key ])
228+
229+ if len (value ) == 0 :
230+ continue
231+ if isinstance (value ,str ):
232+ ops .append (tf .summary .text (key , tf .convert_to_tensor (str (value ))))
233+ else :
234+ ops .append (tf .summary .text (key , tf .as_string (tf .convert_to_tensor (value ))))
235+
236+ with tf .control_dependencies (tf .tuple (ops )):
237+ self .summary_merged = tf .summary .merge_all ()
238+
239+ return self .summary_merged
240+
241+
0 commit comments