Labwatch is an extension to Sacred for automated hyperparameter optimization
142 | of machine learning algorithm. It implements an interface to state-of-the-art hyperparameter optimization methods
143 | such as RoBO or SMAC.
127 | Please activate JavaScript to enable the search
128 | functionality.
129 |
130 |
131 |
132 | From here you can search these documents. Enter your search
133 | words into the box below and click "search". Note that the search
134 | function will automatically search for all of the words. Pages
135 | containing fewer words won't appear in the result list.
136 |
137 |
138 |
139 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
172 |
173 |
--------------------------------------------------------------------------------
/docs/searchindex.js:
--------------------------------------------------------------------------------
1 | Search.setIndex({docnames:["apis","index","installation","quickstart"],envversion:53,filenames:["apis.rst","index.rst","installation.rst","quickstart.rst"],objects:{"labwatch.assistant":{LabAssistant:[0,0,1,""]},"labwatch.assistant.LabAssistant":{search_space:[0,1,1,""]},"labwatch.converters":{convert_to_configspace:[0,2,0,"-"]},"labwatch.converters.convert_to_configspace":{configspace_config_to_sacred:[0,3,1,""],convert_simple_param:[0,3,1,""],sacred_config_to_configspace:[0,3,1,""],sacred_space_to_configspace:[0,3,1,""]},"labwatch.hyperparameters":{Categorical:[0,0,1,""],Condition:[0,0,1,""],ConditionResult:[0,0,1,""],Constant:[0,0,1,""],Gaussian:[0,0,1,""],Parameter:[0,0,1,""],UniformFloat:[0,0,1,""],UniformInt:[0,0,1,""],UniformNumber:[0,0,1,""],decode_param_or_op:[0,3,1,""]},"labwatch.optimizers.base":{Optimizer:[0,0,1,""]},"labwatch.optimizers.base.Optimizer":{needs_updates:[0,1,1,""],suggest_configuration:[0,1,1,""],update:[0,1,1,""]},"labwatch.optimizers.bayesian_optimization":{BayesianOptimization:[0,0,1,""]},"labwatch.optimizers.bohamiann":{Bohamiann:[0,0,1,""]},"labwatch.optimizers.dngo":{DNGOWrapper:[0,0,1,""]},"labwatch.optimizers.random_search":{RandomSearch:[0,0,1,""]},labwatch:{hyperparameters:[0,2,0,"-"]}},objnames:{"0":["py","class","Python class"],"1":["py","method","Python method"],"2":["py","module","Python module"],"3":["py","function","Python function"]},objtypes:{"0":"py:class","1":"py:method","2":"py:module","3":"py:function"},terms:{"10e":3,"case":[2,3],"class":[0,3],"default":[0,3],"float":0,"function":[0,3],"import":3,"int":3,"long":3,"new":[0,3],"return":[0,3],"true":[0,3],"try":[0,3],For:3,The:[0,3],about:[0,3],accuraci:3,activ:3,actual:3,adapt:3,add:3,addit:0,after:3,algorithm:1,all:[0,3],allow:3,alon:3,alreadi:3,also:3,always_inject_observ:0,api:1,art:[1,3],assist:[1,3],associ:0,assum:3,astyp:3,autom:[1,3],automain:3,automat:1,automl:[2,3],base:[0,3],basic:3,batch:3,batch_siz:3,bayesian:[2,3],bayesian_optim:0,bayesianoptim:0,befor:2,believ:3,better:3,between:3,binari:3,bit:3,bohamiann:0,bool:0,both:3,bound:3,brief:3,bson:0,burnin:0,call:3,can:[0,2,3],captur:3,cast:3,categor:[0,3],categorical_crossentropi:3,cfg:3,chain_length:0,check:0,choic:[0,3],choices_in:0,clone:2,code:3,com:2,command:[2,3],common:3,compil:3,complet:3,condit:0,conditionresult:0,config:[0,3],config_spac:0,configspac:0,configspace_config_to_sacr:0,configur:[0,1],configurationspac:0,connect:3,constant:0,contain:0,continu:3,conveni:3,convert:[1,3],convert_simple_param:0,convert_to_configspac:0,correspond:3,cost:0,creat:0,cspace:0,current:[2,3],dai:3,dark:3,data:3,databas:[0,3],database_nam:0,dataset:3,decod:0,decode_param_or_op:0,decor:0,def:3,defin:[0,3],definit:0,dens:3,depend:3,describ:0,detail:3,dict:[0,3],dictionari:0,differ:[0,3],dimension:3,discret:3,distinguish:3,distribut:[0,3],dngo:0,dngowrapp:0,done:0,drawn:3,dropout:3,dropout_first_lay:3,dropout_second_lay:3,each:[0,3],earli:3,easi:3,either:3,els:0,emerg:3,epoch:3,equival:0,evalu:3,everi:3,exampl:3,execut:2,exist:3,experi:[0,3],exploit:3,explor:3,extens:1,fals:0,feed:3,fill:0,find:3,first:[2,3],fist:3,fit:3,fix:[0,3],float32:3,follow:[2,3],fool:3,forest:3,forward:3,framework:3,from:[0,2,3],furthermor:3,gaussian:[0,3],gener:3,get:3,get_random_config:0,git:2,github:2,give:3,given:0,good:3,guid:3,has:[0,3],have:[2,3],here:[2,3],high:3,histori:3,how:[2,3],http:2,hyperparamet:1,idea:3,implement:[1,3],inconsist:3,independ:3,inform:[0,3],input:3,input_shap:3,instal:1,instanc:[0,3],instanti:3,instead:[0,3],integ:3,interfac:[0,1,3],intern:0,iter:3,its:3,just:[0,3],keep:3,kera:3,knowledg:3,labassist:[0,3],labwatch:[0,2,3],labwatch_demo_kera:3,layer:3,learn:[1,3],learning_r:3,let:3,like:0,line:3,list:[0,3],littl:3,load_data:3,localhost:0,log:3,log_scal:[0,3],look:[2,3],loss:3,lot:3,low:3,lower:[0,3],machin:[1,3],main:1,make:3,manual:2,map:0,matric:3,mean:3,method:[0,1,3],metric:3,might:3,mix:3,mnist:3,model:3,modul:1,mongodb:3,more:3,multipl:1,n_hyper:0,n_iter:0,name:[0,3],need:[0,2],needs_upd:0,network:3,neural:3,nice:3,none:0,note:3,noth:0,now:3,num_class:3,num_units_first_lay:3,num_units_second_lay:3,numer:3,object:3,observ:3,off:3,offer:3,old:3,onli:3,optim:[1,2],optimization_target:3,other:3,otherwis:0,our:3,out:3,outperform:3,overview:3,own:3,paper:3,parallel:3,param:0,paramet:0,pass:3,pip:2,point:3,popul:0,prefix:0,prevent:3,previou:3,print:3,prior:3,probabilist:3,probabl:3,problem:3,procedur:3,process:3,properti:3,pypi:2,python:3,queue:0,quickstart:1,quit:3,random:3,random_search:[0,3],randomsearch:[0,3],rate:3,recent:3,refer:3,relev:3,relu:3,reshap:3,result:[0,3],right:3,rmsprop:3,robo:[1,2],run:[0,3],sacr:[1,3],sacred_config_to_configspac:0,sacred_space_to_configspac:0,sampl:3,scale:3,score:3,seachspac:0,search:1,search_spac:[0,3],searchspac:[0,3],second:3,see:[2,3],select:3,sequenti:3,set:3,sever:3,shape:3,shuffl:3,sigma:0,simpl:[0,3],simplest:3,simpli:3,size:3,smac:[1,2,3],small_search_spac:3,softmax:3,some:3,someth:0,sometim:3,sourc:3,space:[0,1],spend:3,split:3,state:[0,1,3],storag:0,store:[0,3],str:0,subfield:3,subset:3,suggest:[0,3],suggest_configur:0,summari:3,tackl:3,take:3,talk:3,test:3,thei:3,thi:[0,3],thing:3,through:3,time:3,to_categor:3,togeth:3,tool:3,track:3,trade:3,train:3,tri:3,tune:1,tutori:3,type:[0,3],uid:0,understand:3,uniform:3,uniformfloat:[0,3],uniformint:0,uniformnumb:[0,3],unsur:3,updat:0,upper:[0,3],url:0,use:[2,3],used:3,useful:3,user:0,uses:[0,3],util:3,validation_data:3,valu:[0,3],varieti:3,vector:3,verbos:3,version:2,via:3,want:[0,2,3],watcher:0,well:3,were:3,where:3,which:3,whole:3,work:3,would:3,x_test:3,x_train:3,y_test:3,y_train:3,you:[0,2,3],your:3},titles:["API","Welcome to labwatch\u2019s documentation!","Installation","Quickstart"],titleterms:{api:0,assist:0,automat:3,configur:3,content:1,convert:0,document:1,hyperparamet:[0,3],instal:2,labwatch:1,main:0,modul:0,multipl:3,optim:[0,3],quickstart:3,search:3,space:3,tune:3,welcom:1}})
--------------------------------------------------------------------------------
/docs/source/apis.rst:
--------------------------------------------------------------------------------
1 | API
2 | ===
3 |
4 |
5 | ------------
6 | Main Modules
7 | ------------
8 |
9 | Assistant:
10 | ----------
11 | .. autoclass:: labwatch.assistant.LabAssistant
12 | :members:
13 |
14 | Hyperparameters:
15 | ----------------
16 | .. automodule:: labwatch.hyperparameters
17 | :members:
18 |
19 | .. autoclass:: labwatch.hyperparameters.Parameter
20 | :members:
21 |
22 | .. autoclass:: labwatch.hyperparameters.Constant
23 | :members:
24 |
25 | .. autoclass:: labwatch.hyperparameters.Categorical
26 | :members:
27 |
28 | .. autoclass:: labwatch.hyperparameters.UniformNumber
29 | :members:
30 |
31 | .. autoclass:: labwatch.hyperparameters.UniformFloat
32 | :members:
33 |
34 | .. autoclass:: labwatch.hyperparameters.UniformInt
35 | :members:
36 |
37 | .. autoclass:: labwatch.hyperparameters.Gaussian
38 | :members:
39 |
40 | .. autoclass:: labwatch.hyperparameters.ConditionResult
41 | :members:
42 |
43 | .. autoclass:: labwatch.hyperparameters.Condition
44 | :members:
45 |
46 | Converters:
47 | -----------
48 | .. automodule:: labwatch.converters.convert_to_configspace
49 | :members:
50 |
51 | Optimizers:
52 | -----------
53 |
54 |
55 | .. autoclass:: labwatch.optimizers.base.Optimizer
56 | :members:
57 |
58 | .. autoclass:: labwatch.optimizers.bayesian_optimization.BayesianOptimization
59 | :members:
60 |
61 | .. autoclass:: labwatch.optimizers.bohamiann.Bohamiann
62 | :members:
63 |
64 | .. autoclass:: labwatch.optimizers.dngo.DNGOWrapper
65 | :members:
66 |
67 | .. autoclass:: labwatch.optimizers.random_search.RandomSearch
68 | :members:
69 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # labwatch documentation build configuration file, created by
4 | # sphinx-quickstart on Thu May 25 18:25:22 2017.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | # If extensions (or modules to document with autodoc) are in another directory,
16 | # add these directories to sys.path here. If the directory is relative to the
17 | # documentation root, use os.path.abspath to make it absolute, like shown here.
18 | #
19 | # import os
20 | # import sys
21 | import sphinx_bootstrap_theme
22 | # sys.path.insert(0, os.path.abspath('.'))
23 |
24 |
25 | # -- General configuration ------------------------------------------------
26 |
27 | # If your documentation needs a minimal Sphinx version, state it here.
28 | #
29 | # needs_sphinx = '1.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = ['sphinx.ext.autodoc',
35 | 'sphinx.ext.coverage']
36 |
37 | # Add any paths that contain templates here, relative to this directory.
38 | templates_path = ['.templates']
39 |
40 | # The suffix(es) of source filenames.
41 | # You can specify multiple suffix as a list of string:
42 | #
43 | # source_suffix = ['.rst', '.md']
44 | source_suffix = '.rst'
45 |
46 | # The master toctree document.
47 | master_doc = 'index'
48 |
49 | # General information about the project.
50 | project = u'labwatch'
51 | copyright = u'2017, Aaron Klein, Klaus Greff'
52 | author = u'Aaron Klein, Klaus Greff'
53 |
54 | # The version info for the project you're documenting, acts as replacement for
55 | # |version| and |release|, also used in various other places throughout the
56 | # built documents.
57 | #
58 | # The short X.Y version.
59 | version = u''
60 | # The full version, including alpha/beta/rc tags.
61 | release = u''
62 |
63 | # The language for content autogenerated by Sphinx. Refer to documentation
64 | # for a list of supported languages.
65 | #
66 | # This is also used if you do content translation via gettext catalogs.
67 | # Usually you set "language" from the command line for these cases.
68 | language = None
69 |
70 | # List of patterns, relative to source directory, that match files and
71 | # directories to ignore when looking for source files.
72 | # This patterns also effect to html_static_path and html_extra_path
73 | exclude_patterns = ['.build', 'Thumbs.db', '.DS_Store']
74 |
75 | # The name of the Pygments (syntax highlighting) style to use.
76 | pygments_style = 'sphinx'
77 |
78 | # If true, `todo` and `todoList` produce output, else they produce nothing.
79 | todo_include_todos = False
80 |
81 |
82 | # -- Options for HTML output ----------------------------------------------
83 |
84 | # The theme to use for HTML and HTML Help pages. See the documentation for
85 | # a list of builtin themes.
86 | #
87 | #html_theme = 'alabaster'
88 | html_theme = 'bootstrap'
89 | html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
90 |
91 |
92 | # Theme options are theme-specific and customize the look and feel of a theme
93 | # further. For a list of options available for each theme, see the
94 | # documentation.
95 | #
96 | html_theme_options = {
97 | # Navigation bar title. (Default: ``project`` value)
98 | 'navbar_title': "Labwatch",
99 |
100 | # Tab name for entire site. (Default: "Site")
101 | # 'navbar_site_name': "Site",
102 |
103 | # A list of tuples containting pages to link to. The value should
104 | # be in the form [(name, page), ..]
105 | 'navbar_links': [
106 | ('Start', 'index'),
107 | ('Installation', 'installation'),
108 | ('Quickstart', 'quickstart'),
109 | ('APIs', 'apis')
110 | ],
111 |
112 | # Render the next and previous page links in navbar. (Default: true)
113 | 'navbar_sidebarrel': True,
114 |
115 | # Render the current pages TOC in the navbar. (Default: true)
116 | 'navbar_pagenav': True,
117 |
118 | # Tab name for the current pages TOC. (Default: "Page")
119 | 'navbar_pagenav_name': "On this page",
120 |
121 | # Global TOC depth for "site" navbar tab. (Default: 1)
122 | # Switching to -1 shows all levels.
123 | 'globaltoc_depth': 1,
124 |
125 | # Include hidden TOCs in Site navbar?
126 | #
127 | # Note: If this is "false", you cannot have mixed ``:hidden:`` and
128 | # non-hidden ``toctree`` directives in the same page, or else the build
129 | # will break.
130 | #
131 | # Values: "true" (default) or "false"
132 | 'globaltoc_includehidden': "false",
133 |
134 | # HTML navbar class (Default: "navbar") to attach to
element.
135 | # For black navbar, do "navbar navbar-inverse"
136 | 'navbar_class': "navbar",
137 |
138 | # Fix navigation bar to top of page?
139 | # Values: "true" (default) or "false"
140 | 'navbar_fixed_top': "true",
141 |
142 | # Location of link to source.
143 | # Options are "nav" (default), "footer" or anything else to exclude.
144 | 'source_link_position': "footer",
145 |
146 | # Bootswatch (http://bootswatch.com/) theme.
147 | #
148 | # Options are nothing with "" (default) or the name of a valid theme
149 | # such as "amelia" or "cosmo".
150 | 'bootswatch_theme': "cosmo",
151 |
152 | # Choose Bootstrap version.
153 | # Values: "3" (default) or "2" (in quotes)
154 | 'bootstrap_version': "3",
155 | }
156 |
157 | html_sidebars = {'**': ['localtoc.html']}
158 |
159 | # Add any paths that contain custom static files (such as style sheets) here,
160 | # relative to this directory. They are copied after the builtin static files,
161 | # so a file named "default.css" will overwrite the builtin "default.css".
162 | html_static_path = ['.static']
163 |
164 |
165 | # -- Options for HTMLHelp output ------------------------------------------
166 |
167 | # Output file base name for HTML help builder.
168 | htmlhelp_basename = 'labwatchdoc'
169 |
170 |
171 | # -- Options for LaTeX output ---------------------------------------------
172 |
173 | latex_elements = {
174 | # The paper size ('letterpaper' or 'a4paper').
175 | #
176 | # 'papersize': 'letterpaper',
177 |
178 | # The font size ('10pt', '11pt' or '12pt').
179 | #
180 | # 'pointsize': '10pt',
181 |
182 | # Additional stuff for the LaTeX preamble.
183 | #
184 | # 'preamble': '',
185 |
186 | # Latex figure (float) alignment
187 | #
188 | # 'figure_align': 'htbp',
189 | }
190 |
191 | # Grouping the document tree into LaTeX files. List of tuples
192 | # (source start file, target name, title,
193 | # author, documentclass [howto, manual, or own class]).
194 | latex_documents = [
195 | (master_doc, 'labwatch.tex', u'labwatch Documentation',
196 | u'Aaron Klein, Klaus Greff', 'manual'),
197 | ]
198 |
199 |
200 | # -- Options for manual page output ---------------------------------------
201 |
202 | # One entry per manual page. List of tuples
203 | # (source start file, name, description, authors, manual section).
204 | man_pages = [
205 | (master_doc, 'labwatch', u'labwatch Documentation',
206 | [author], 1)
207 | ]
208 |
209 |
210 | # -- Options for Texinfo output -------------------------------------------
211 |
212 | # Grouping the document tree into Texinfo files. List of tuples
213 | # (source start file, target name, title, author,
214 | # dir menu entry, description, category)
215 | texinfo_documents = [
216 | (master_doc, 'labwatch', u'labwatch Documentation',
217 | author, 'labwatch', 'One line description of project.',
218 | 'Miscellaneous'),
219 | ]
220 |
221 |
222 |
223 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. labwatch documentation master file, created by
2 | sphinx-quickstart on Thu May 25 18:25:22 2017.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 | ====================================
6 | Welcome to labwatch's documentation!
7 | ====================================
8 |
9 | Labwatch is an extension to `Sacred `_ for automated hyperparameter optimization
10 | of machine learning algorithm. It implements an interface to state-of-the-art hyperparameter optimization methods
11 | such as `RoBO `_ or `SMAC `_.
12 |
13 |
14 |
15 | Contents
16 | --------
17 |
18 | .. toctree::
19 | :maxdepth: 3
20 |
21 | installation
22 | quickstart
23 | apis
24 |
--------------------------------------------------------------------------------
/docs/source/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 |
5 | To install the current version of labwatch from git execute the following commands:
6 |
7 | .. code:: bash
8 |
9 | git clone https://github.com/automl/labwatch.git
10 | cd labwatch
11 | pip install .
12 |
13 |
14 | Before you can use Bayesian optimization, you need to install `RoBO `_ first.
15 | Have a look `here `_ to see how you can install it.
16 |
17 | If you want to use `SMAC `_, you have to install it first from pypi:
18 |
19 | .. code:: bash
20 |
21 | pip install smac
22 |
23 | In case you want to install manually, look `here `_
24 |
25 |
--------------------------------------------------------------------------------
/docs/source/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quickstart
2 | **********
3 |
4 | The following tutorial assumes that you have a basic understanding of how Sacred works (Experiments, Observers).
5 | In case you are unsure, have a look on the `Sacred Quickstart Guide `_
6 |
7 | In this tutorial we will see how we can optimize the hyperparameters of a feed forward network trained on MNIST
8 | together with Labwatch and Sacred.
9 | We will use the MNIST example of `keras `_ to implement the neural network but note that
10 | both Labwatch and Sacred are completely independent of which framework you use.
11 | You can find the whole source code as well as more examples
12 | `here `_
13 |
14 | Automatically Tuning of Hyperparameters
15 | =======================================
16 |
17 |
18 | Sacred is a useful tool to keep track of all relevant information of your experiments such as hyperparameters,
19 | results, dependencies and so on.
20 |
21 | The following python code adapts the
22 | `keras mnist example `_ to work with Sacred:
23 |
24 |
25 | .. code:: python
26 |
27 | import keras
28 | from keras.datasets import mnist
29 | from keras.models import Sequential
30 | from keras.layers import Dense, Dropout
31 | from keras.optimizers import RMSprop
32 |
33 | from sacred import Experiment
34 |
35 | ex = Experiment()
36 |
37 | @ex.config
38 | def cfg():
39 | batch_size = 128
40 | num_units_first_layer = 512
41 | num_units_second_layer = 512
42 | dropout_first_layer = 0.2
43 | dropout_second_layer = 0.2
44 | learning_rate = 0.001
45 |
46 |
47 | @ex.automain
48 | def run(batch_size,
49 | num_units_first_layer,
50 | num_units_second_layer,
51 | dropout_first_layer,
52 | dropout_second_layer,
53 | learning_rate):
54 |
55 | num_classes = 10
56 | epochs = 20
57 |
58 | # the data, shuffled and split between train and test sets
59 | (x_train, y_train), (x_test, y_test) = mnist.load_data()
60 |
61 | x_train = x_train.reshape(60000, 784)
62 | x_test = x_test.reshape(10000, 784)
63 | x_train = x_train.astype('float32')
64 | x_test = x_test.astype('float32')
65 | x_train /= 255
66 | x_test /= 255
67 | print(x_train.shape[0], 'train samples')
68 | print(x_test.shape[0], 'test samples')
69 |
70 | # convert class vectors to binary class matrices
71 | y_train = keras.utils.to_categorical(y_train, num_classes)
72 | y_test = keras.utils.to_categorical(y_test, num_classes)
73 |
74 | model = Sequential()
75 | model.add(Dense(num_units_first_layer, activation='relu', input_shape=(784,)))
76 | model.add(Dropout(dropout_first_layer))
77 | model.add(Dense(num_units_second_layer, activation='relu'))
78 | model.add(Dropout(dropout_second_layer))
79 | model.add(Dense(10, activation='softmax'))
80 |
81 | model.summary()
82 |
83 | model.compile(loss='categorical_crossentropy',
84 | optimizer=RMSprop(lr=learning_rate),
85 | metrics=['accuracy'])
86 |
87 | history = model.fit(x_train, y_train,
88 | batch_size=batch_size,
89 | epochs=epochs,
90 | verbose=1,
91 | validation_data=(x_test, y_test))
92 | score = model.evaluate(x_test, y_test, verbose=0)
93 | print('Test loss:', score[0])
94 | print('Test accuracy:', score[1])
95 |
96 | results = dict()
97 | results["optimization_target"] = 1 - score[1]
98 |
99 | return results
100 |
101 | In the dark old days you would probably now spend a lot of time to find the right setting for you hyperparameters by
102 | iteratively trying out different setting.
103 |
104 | Well, you're are not alone with this problem and it is actually a common problem in machine learning.
105 | Recently a new subfield in machine learning (`AutoML `_)
106 | has emerged that tries to automated this procedure by casting it as an optimization problem. By now there exist several
107 | optimization methods that tackle the hyperparameter optimization problem.
108 |
109 | To make use of these methods in Labwatch, we fist have to instantiate a LabAssistant which will
110 | connect our Sacred experiment with the hyperparameter optimizer through a MongoDB:
111 |
112 | .. code:: python
113 |
114 | from labwatch.assistant import LabAssistant
115 | from labwatch.optimizers.random_search import RandomSearch
116 |
117 | a = LabAssistant(ex, "labwatch_demo_keras", optimizer=RandomSearch)
118 |
119 | After that we have to define our configuration search space with the hyperparameters that we want to optimize.
120 |
121 | .. code:: python
122 |
123 | @a.searchspace
124 | def search_space():
125 | batch_size = UniformNumber(lower=8,
126 | upper=64,
127 | default=32,
128 | type=int,
129 | log_scale=True)
130 | num_units_first_layer = UniformNumber(lower=16,
131 | upper=1024,
132 | default=32,
133 | type=int,
134 | log_scale=True)
135 | num_units_second_layer = UniformNumber(lower=16,
136 | upper=1024,
137 | default=32,
138 | type=int,
139 | log_scale=True)
140 | dropout_first_layer = UniformFloat(lower=0,
141 | upper=.99,
142 | default=.2)
143 | dropout_second_layer = UniformFloat(lower=0,
144 | upper=.99,
145 | default=.2)
146 | learning_rate = UniformFloat(lower=10e-6,
147 | upper=10e-1,
148 | default=10e-2,
149 | log_scale=True)
150 |
151 |
152 | Here, for each hyperparameter we define a prior distribution, default value, its type and if we want
153 | to adapt it on a log scale or not.
154 |
155 | We can use now Sacred's command line interface to get a new configuration
156 |
157 | .. code:: bash
158 |
159 | python experiment.py with search_space
160 |
161 | Labwatch will now pass all already completed configurations that are stored in the database
162 | to the hyperparameter optimizer, let it suggest a new configuration and then run the experiment with this configuration.
163 |
164 |
165 | Configuration Search Spaces
166 | ===========================
167 |
168 |
169 | At this point is probably a good idea to talk a little bit more about configuration spaces.
170 | In general we distinguish between:
171 |
172 | - *categorical* hyperparameters that can take only discrete choices (e.g. {'a', 'b', 'c'})
173 | - *numerical* hyperparameters that can have either integer or continuous values.
174 |
175 | Furthermore, Labwatch also allows you to define prior distributions (Gaussian, Uniform) for your hyperparameters.
176 | Some hyperparameter optimizers such as for instance random search can exploit this prior knowledge to
177 | suggest better configurations from early on.
178 | In the case that you do not have a prior about you hyperparameter, just use a uniform distribution which is
179 | simply defined by an upper and lower bound.
180 |
181 |
182 | Hyperparameter Optimizers
183 | =========================
184 |
185 |
186 | Labwatch offers a simple interface to a variety of state-of-the-art hyperparameter optimization methods.
187 | Note that every optimizer has its own properties and might not work for all use cases.
188 | The following list will give you a brief overview of the optimizer that can be used with labwatch and in which
189 | setting they would work. For more details we refer to the corresponding papers:
190 |
191 | - **Random search** is probably the simplest hyperparameter optimization method. It just samples hyperparameter
192 | configurations from the prior. The nice thing with random search is that it works in all search
193 | spaces and is easy to parallelize.
194 |
195 | - **Bayesian optimization** fits a probabilistic model to capture the current believe of the objective function.
196 | To select a new configuration, it use an utility function that only depend on the
197 | probabilistic model to trade off exploration and exploitation. Here we use Gaussian process to model our objective
198 | function, which work well in low (<10) dimensional continuous input spaces but do not work with categorical
199 | hyperparameters.
200 |
201 | - **SMAC** is also a Bayesian optimization method but uses random forest instead of Gaussian processes to model
202 | the objective function. It works in high dimensional mixed continuous and discret input space but will be
203 | be probably outperformed by GP-based Bayesian optimization in the low dimensional continuous space.
204 |
205 |
206 | Multiple Search Spaces
207 | ======================
208 |
209 |
210 | Sometimes it is quite convenient to have multiple different search space, for instance if you want to optimize
211 | first only a subset of your hyperparameters and keep the others fixed.
212 |
213 | Labwatch allows to have different search space as long as they have different names. For instance in our running
214 | example if we want to optimize only the learning rate and batch size we can define a second search space:
215 |
216 |
217 | .. code:: python
218 |
219 | @a.searchspace
220 | def small_search_space():
221 | batch_size = UniformNumber(lower=32, upper=64, default=32, type=int, log_scale=True)
222 | learning_rate = UniformFloat(lower=10e-3, upper=10e-2, default=10e-2, log_scale=True)
223 |
224 |
225 | If we now call our experiment via:
226 |
227 |
228 | .. code:: bash
229 |
230 | python experiment.py with small_search_space
231 |
232 | we get a new configuration for the learning rate and the batch where as all other hyperparameter are set to the values
233 | defined in the config cfg().
234 |
235 | Note: To prevent inconsistencies and to not fool the optimizer, Labwatch passes only completed configurations that were
236 | drawn from this search space to the optimizer. This means that our optimizer will not use the information from previous
237 | experiment with the other search space.
238 |
--------------------------------------------------------------------------------
/examples/bo_on_branin.py:
--------------------------------------------------------------------------------
1 | from branin import ex
2 |
3 | print("RUNNING sampled configs")
4 | num_configs = 100
5 | for i in range(num_configs):
6 | ex.run(named_configs=['search_space'])
7 |
--------------------------------------------------------------------------------
/examples/branin.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from labwatch.optimizers.random_search import RandomSearch
6 | from sacred import Experiment
7 | from labwatch.assistant import LabAssistant
8 | from labwatch.hyperparameters import UniformFloat
9 | import numpy as np
10 |
11 |
12 | ex = Experiment()
13 | a = LabAssistant(ex, database_name='branin', optimizer=RandomSearch)
14 |
15 |
16 | @ex.config
17 | def cfg():
18 | x = (0., 5.)
19 |
20 |
21 | @a.search_space
22 | def search_space():
23 | x = (UniformFloat(-5, 10), UniformFloat(0, 15))
24 |
25 |
26 | @ex.automain
27 | def branin(x):
28 | x1, x2 = x
29 | print("{:.2f}, {:.2f}".format(x1, x2))
30 | y = (x2 - (5.1 / (4 * np.pi ** 2)) * x1 ** 2 + 5 * x1 / np.pi - 6) ** 2
31 | y += 10 * (1 - 1 / (8 * np.pi)) * np.cos(x1) + 10
32 |
33 | return y
34 |
--------------------------------------------------------------------------------
/examples/keras_mnist_mlp.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import keras
4 | from keras.datasets import mnist
5 | from keras.models import Sequential
6 | from keras.layers import Dense, Dropout
7 | from keras.optimizers import RMSprop
8 |
9 | from sacred import Experiment
10 | from labwatch.assistant import LabAssistant
11 | from labwatch.hyperparameters import UniformInt, UniformFloat
12 | from labwatch.optimizers.random_search import RandomSearch
13 |
14 | ex = Experiment()
15 | a = LabAssistant(ex, "labwatch_demo_keras", optimizer=RandomSearch)
16 |
17 |
18 | @ex.config
19 | def cfg():
20 | batch_size = 128
21 | num_units_first_layer = 512
22 | num_units_second_layer = 512
23 | dropout_first_layer = 0.2
24 | dropout_second_layer = 0.2
25 | learning_rate = 0.001
26 |
27 |
28 | @a.search_space
29 | def small_search_space():
30 | batch_size = UniformInt(lower=32, upper=64, default=32, log_scale=True)
31 | learning_rate = UniformFloat(lower=10e-3, upper=10e-2, default=10e-2, log_scale=True)
32 |
33 |
34 | @a.search_space
35 | def search_space():
36 | batch_size = UniformInt(lower=8, upper=64, default=32, log_scale=True)
37 | num_units_first_layer = UniformInt(lower=16, upper=1024, default=32, log_scale=True)
38 | num_units_second_layer = UniformInt(lower=16, upper=1024, default=32, log_scale=True)
39 | dropout_first_layer = UniformFloat(lower=0, upper=.99, default=.2)
40 | dropout_second_layer = UniformFloat(lower=0, upper=.99, default=.2)
41 | learning_rate = UniformFloat(lower=10e-6, upper=10e-1, default=10e-2, log_scale=True)
42 |
43 |
44 | @ex.automain
45 | def run(batch_size,
46 | num_units_first_layer,
47 | num_units_second_layer,
48 | dropout_first_layer,
49 | dropout_second_layer,
50 | learning_rate):
51 |
52 | num_classes = 10
53 | epochs = 20
54 |
55 | # the data, shuffled and split between train and test sets
56 | (x_train, y_train), (x_test, y_test) = mnist.load_data()
57 |
58 | x_train = x_train.reshape(60000, 784)
59 | x_test = x_test.reshape(10000, 784)
60 | x_train = x_train.astype('float32')
61 | x_test = x_test.astype('float32')
62 | x_train /= 255
63 | x_test /= 255
64 | print(x_train.shape[0], 'train samples')
65 | print(x_test.shape[0], 'test samples')
66 |
67 | # convert class vectors to binary class matrices
68 | y_train = keras.utils.to_categorical(y_train, num_classes)
69 | y_test = keras.utils.to_categorical(y_test, num_classes)
70 |
71 | model = Sequential()
72 | model.add(Dense(num_units_first_layer, activation='relu', input_shape=(784,)))
73 | model.add(Dropout(dropout_first_layer))
74 | model.add(Dense(num_units_second_layer, activation='relu'))
75 | model.add(Dropout(dropout_second_layer))
76 | model.add(Dense(10, activation='softmax'))
77 |
78 | model.summary()
79 |
80 | model.compile(loss='categorical_crossentropy',
81 | optimizer=RMSprop(lr=learning_rate),
82 | metrics=['accuracy'])
83 |
84 | history = model.fit(x_train, y_train,
85 | batch_size=batch_size,
86 | epochs=epochs,
87 | verbose=1,
88 | validation_data=(x_test, y_test))
89 | score = model.evaluate(x_test, y_test, verbose=0)
90 | print('Test loss:', score[0])
91 | print('Test accuracy:', score[1])
92 |
93 | results = dict()
94 | results["optimization_target"] = 1 - score[1]
95 |
96 | return results
97 |
--------------------------------------------------------------------------------
/labwatch/__about__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | """
4 | This module contains meta-information about the labwatch package.
5 | It is kept simple and separate from the main module, because this information
6 | is also read by the setup.py. And during installation the labwatch module cannot
7 | yet be imported.
8 | """
9 |
10 | from __future__ import division, print_function, unicode_literals
11 |
12 | __all__ = ["__version__", "__authors__", "__url__"]
13 |
14 | __version__ = "0.1.0"
15 |
16 | __authors__ = 'Aaron Klein, Klaus Greff'
17 |
18 | __url__ = "https://github.com/automl/labwatch"
19 |
--------------------------------------------------------------------------------
/labwatch/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 | from labwatch.assistant import LabAssistant
5 |
6 | __version__ = "0.1dev"
7 | __authors__ = ["Jost Tobias Springenberg", "Aaron Klein", "Klaus Greff"]
8 | __url__ = "https://github.com/automl/labwatch"
9 | __all__ = ['LabAssistant', '__version__', '__authors__', '__url__']
10 |
--------------------------------------------------------------------------------
/labwatch/converters/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automl/labwatch/cdd7c55e5741bf4f1216f7b8da8de9fb1480c75b/labwatch/converters/__init__.py
--------------------------------------------------------------------------------
/labwatch/converters/convert_to_configspace.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from labwatch.searchspace import SearchSpace
6 | from labwatch.utils.types import basic_types, str_to_types
7 | from labwatch.utils.types import ParamValueExcept
8 | import numpy as np
9 |
10 | # TODO: guard ConfigSpace import
11 | from ConfigSpace import ConfigurationSpace, Configuration
12 | import ConfigSpace.hyperparameters as csh
13 | from ConfigSpace.conditions import InCondition
14 |
15 |
16 | def convert_simple_param(name, param):
17 | """
18 | Convert a simple labwatch parameter to a ConfigSpace parameter.
19 |
20 | Parameters
21 | ----------
22 | name: str
23 | The name of the parameter.
24 |
25 | param: dict
26 | Dictionary describing the parameter.
27 |
28 | Returns
29 | -------
30 | ConfigSpace.hyperparameters.Hyperparameter:
31 | The converted hyperparameter.
32 | """
33 | if param["_class"] == 'Constant':
34 | return csh.Constant(name, param["value"])
35 | elif param["_class"] == 'Categorical':
36 | # convert the choices to only contain
37 | # basic types (they might contain Constant parameters
38 | basic_choices = []
39 | for choice in param["choices"]:
40 | if isinstance(choice, dict):
41 | basic_choices.append(choice["default"])
42 | elif not isinstance(choice, basic_types):
43 | err = "Choice parameter {} is not " \
44 | "a base type or Constant!"
45 | raise ParamValueExcept(err.format(choice))
46 | else:
47 | basic_choices.append(choice)
48 | return csh.CategoricalHyperparameter(name=name,
49 | choices=basic_choices,
50 | default_value=basic_choices[0])
51 | elif param["_class"] == 'UniformFloat':
52 | return csh.UniformFloatHyperparameter(name=name,
53 | lower=param["lower"],
54 | upper=param["upper"],
55 | default_value=param["default"],
56 | log=param["log_scale"])
57 | elif param["_class"] == 'UniformInt':
58 | return csh.UniformIntegerHyperparameter(name=name,
59 | lower=param["lower"],
60 | upper=param["upper"],
61 | default_value=param["default"],
62 | log=param["log_scale"])
63 | elif param["_class"] == 'UniformNumber':
64 | ptype = str_to_types[param["type"]]
65 | if ptype == float:
66 | return csh.UniformFloatHyperparameter(name=name,
67 | lower=param["lower"],
68 | upper=param["upper"],
69 | default_value=param["default"],
70 | log=param["log_scale"])
71 | elif ptype == int:
72 | return csh.UniformIntegerHyperparameter(name=name,
73 | lower=param["lower"],
74 | upper=param["upper"],
75 | default_value=param["default"],
76 | log=param["log_scale"])
77 | else:
78 | raise ValueError("Don't know how to represent UniformNumber with "
79 | "type: {} in ConfigSpace".format(param["type"]))
80 | elif param["_class"] == 'Gaussian':
81 | return csh.NormalFloatHyperparameter(name=name,
82 | mu=param["mu"],
83 | sigma=param["sigma"],
84 | log=param["log_scale"])
85 |
86 | else:
87 | raise ValueError("Don't know how to represent {} in ConfigSpace "
88 | "notation.".format(param))
89 |
90 |
91 | def sacred_space_to_configspace(space):
92 | """
93 | Convert a Labwatch searchspace to a ConfigSpace.
94 |
95 | Parameters
96 | ----------
97 | space: labwatch.searchspace.SearchSpace
98 | A labwatch searchspace to be converted.
99 |
100 | Returns
101 | -------
102 | ConfigSpace.ConfigurationSpace:
103 | A ConfigurationSpace equivalent to the given SeachSpace.
104 | """
105 | # first convert all non conditionals
106 | non_conditions = {}
107 | conditions = []
108 | for name in space.non_conditions:
109 | param = space.parameters[name]
110 | converted_param = convert_simple_param(name, param)
111 | non_conditions[name] = converted_param
112 | for name in space.conditions:
113 | param = space.parameters[name]
114 | converted_result = convert_simple_param(name, param["result"])
115 | # next build the condition as required by the ConfigSpace
116 | condition = param["condition"]
117 | condition_name = space.uids_to_names[condition["uid"]]
118 | if condition_name not in non_conditions:
119 | raise ValueError("Unknown parameter in Condition")
120 | converted_condition = non_conditions[condition_name]
121 | converted_choices = []
122 | for choice in condition["choices"]:
123 | if isinstance(choice, dict):
124 | if choice["_class"] != "Constant":
125 | raise ValueError("Invalid choice encountered in Condition")
126 | converted_choices.append(choice["value"])
127 | else:
128 | converted_choices.append(choice)
129 | cond = InCondition(converted_result,
130 | converted_condition,
131 | values=converted_choices)
132 | non_conditions[name] = converted_result
133 | conditions.append(cond)
134 | # finally build the ConfigSpace
135 | cs = ConfigurationSpace(seed=np.random.seed())
136 | for _name, param in non_conditions.items():
137 | cs.add_hyperparameter(param)
138 | for cond in conditions:
139 | cs.add_condition(cond)
140 | return cs
141 |
142 |
143 | def sacred_config_to_configspace(cspace, config):
144 | """
145 | Fill a ConfigurationSpace with the given values and return the resulting
146 | Configuration.
147 |
148 | Parameters
149 | ----------
150 | cspace: ConfigSpace.ConfigurationSpace
151 | The configuration space to be populated.
152 |
153 | config: dict
154 | The configuration values as a dictionary mapping names to values.
155 |
156 | Returns
157 | -------
158 | ConfigSpace.Configuration:
159 | The resulting Configuration.
160 | """
161 | if isinstance(cspace, SearchSpace):
162 | raise ValueError("You called sacred_config_to_configspace "
163 | "with an instance of labwatch.SearchSpace "
164 | "but an instance of ConfigSpace.ConfigurationSpace "
165 | "is required. Use sacred_space_to_configspace().")
166 | return Configuration(cspace, values=config)
167 |
168 |
169 | def configspace_config_to_sacred(config):
170 | """
171 | Convert a Configuration into a dict mapping parameter names to values.
172 |
173 | Parameters
174 | ----------
175 | config: ConfigSpace.Configuration
176 |
177 | Returns
178 | -------
179 | dict:
180 | Dictionary mapping parameter names to values.
181 | """
182 | values = config.get_dictionary()
183 | # we remove all None entries as our (sacred searchspace) convention is
184 | # to not include them
185 | return {name: value for name, value in values.items() if value is not None}
186 |
--------------------------------------------------------------------------------
/labwatch/optimizers/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from .base import Optimizer
6 | from .random_search import RandomSearch
7 |
8 | # try:
9 | # from .smac import SMAC
10 | # except ImportError:
11 | # print('WARNING: SMAC not found')
12 |
13 | try:
14 | from .bayesian_optimization import BayesianOptimization
15 | except ImportError:
16 | print('WARNING: BayesianOptimization not found')
17 |
--------------------------------------------------------------------------------
/labwatch/optimizers/base.py:
--------------------------------------------------------------------------------
1 |
2 | import numpy as np
3 |
4 | from labwatch.converters.convert_to_configspace import sacred_config_to_configspace
5 |
6 |
7 | class Optimizer(object):
8 | """Defines the interface for all optimizers."""
9 |
10 | def __init__(self, config_space):
11 | self.config_space = config_space
12 | self.X = None
13 | self.y = None
14 |
15 | def get_random_config(self):
16 | return self.config_space.sample()
17 |
18 | def get_default_config(self):
19 | return self.config_space.default()
20 |
21 | def suggest_configuration(self):
22 | """Suggests a configuration of hyperparameters to be run.
23 |
24 | Returns
25 | -------
26 | dict:
27 | Dictionary mapping parameter names to suggested values.
28 |
29 | The default is to return nothing this is done such that the
30 | user / the watcher can then check the queue or try something else.
31 | If you want get_random_config as default create an instance of
32 | RandomSearch instead.
33 | """
34 | return None
35 |
36 | def update(self, configs, costs, runs):
37 | """
38 | Update the internal state of the optimizer with a list of new results.
39 |
40 | Parameters
41 | ----------
42 | configs: list[dict]
43 | List of configurations mapping parameter names to values.
44 | costs: list[float]
45 | List of costs associated to each config.
46 | runs: list[dict]
47 | List of dictionaries containing additional run information.
48 | """
49 |
50 | converted_configs = [
51 | sacred_config_to_configspace(self.config_space, config)
52 | for config in configs]
53 |
54 | for (config, cost) in zip(converted_configs, costs):
55 | # Maps configuration to [0, 1]^D space
56 | x = config.get_array()
57 |
58 | if self.X is None and self.y is None:
59 | self.X = np.array([x])
60 | self.y = np.array([cost])
61 | elif x not in self.X:
62 | self.X = np.append(self.X, x[np.newaxis, :], axis=0)
63 | self.y = np.append(self.y, np.array([cost]), axis=0)
64 |
65 | def needs_updates(self):
66 | """
67 | Returns
68 | -------
69 | bool:
70 | True if this optimizer needs updates, False otherwise.
71 | """
72 | return False
73 |
--------------------------------------------------------------------------------
/labwatch/optimizers/bayesian_optimization.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | import numpy as np
6 | from ConfigSpace import Configuration
7 | try:
8 | import george
9 | from robo.priors.default_priors import DefaultPrior
10 | from robo.models.gaussian_process_mcmc import GaussianProcessMCMC
11 | from robo.maximizers.direct import Direct
12 | from robo.acquisition_functions.log_ei import LogEI
13 | from robo.acquisition_functions.marginalization import MarginalizationGPMCMC
14 | from robo.initial_design.init_random_uniform import init_random_uniform
15 | except ImportError as e:
16 | print("If you want to use BayesianOptimization you have to install the following dependencies:\n"
17 | "https://github.com/automl/RoBO\n"
18 | "george")
19 | from labwatch.optimizers.base import Optimizer
20 | from labwatch.converters.convert_to_configspace import sacred_space_to_configspace, configspace_config_to_sacred
21 | from labwatch.utils.types import SearchSpaceNotSupported
22 |
23 |
24 | class BayesianOptimization(Optimizer):
25 |
26 | def __init__(self, config_space, burnin=100, chain_length=200,
27 | n_hypers=20):
28 |
29 | if config_space.has_categorical:
30 | raise SearchSpaceNotSupported("GP-based Bayesian optimization only supports numerical hyperparameters.")
31 |
32 | super(BayesianOptimization, self).__init__(config_space)
33 | self.rng = np.random.RandomState(np.random.seed())
34 |
35 | self.burnin = burnin
36 | self.chain_length = chain_length
37 | self.n_hypers = n_hypers
38 |
39 | self.config_space = sacred_space_to_configspace(config_space)
40 |
41 | n_inputs = len(self.config_space.get_hyperparameters())
42 |
43 | self.lower = np.zeros([n_inputs])
44 | self.upper = np.ones([n_inputs])
45 |
46 | self.X = None
47 | self.y = None
48 |
49 | def suggest_configuration(self):
50 | if self.X is None and self.y is None:
51 | new_x = init_random_uniform(self.lower, self.upper,
52 | n_points=1, rng=self.rng)[0, :]
53 |
54 | elif self.X.shape[0] == 1:
55 | # We need at least 2 data points to train a GP
56 | new_x = init_random_uniform(self.lower, self.upper,
57 | n_points=1, rng=self.rng)[0, :]
58 |
59 | else:
60 | cov_amp = 1
61 | n_dims = self.lower.shape[0]
62 |
63 | initial_ls = np.ones([n_dims])
64 | exp_kernel = george.kernels.Matern52Kernel(initial_ls,
65 | ndim=n_dims)
66 | kernel = cov_amp * exp_kernel
67 |
68 | prior = DefaultPrior(len(kernel) + 1)
69 |
70 | model = GaussianProcessMCMC(kernel, prior=prior,
71 | n_hypers=self.n_hypers,
72 | chain_length=self.chain_length,
73 | burnin_steps=self.burnin,
74 | normalize_input=False,
75 | normalize_output=True,
76 | rng=self.rng,
77 | lower=self.lower,
78 | upper=self.upper)
79 |
80 | a = LogEI(model)
81 |
82 | acquisition_func = MarginalizationGPMCMC(a)
83 |
84 | max_func = Direct(acquisition_func, self.lower, self.upper, verbose=False)
85 |
86 | model.train(self.X, self.y)
87 |
88 | acquisition_func.update(model)
89 |
90 | new_x = max_func.maximize()
91 |
92 | next_config = Configuration(self.config_space, vector=new_x)
93 |
94 | # Transform to sacred configuration
95 | result = configspace_config_to_sacred(next_config)
96 |
97 | return result
98 |
--------------------------------------------------------------------------------
/labwatch/optimizers/bohamiann.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | import numpy as np
6 |
7 |
8 | from ConfigSpace import Configuration
9 | try:
10 | from robo.initial_design.init_random_uniform import init_random_uniform
11 | from robo.models.bnn import BayesianNeuralNetwork
12 | from robo.maximizers.direct import Direct
13 | from robo.acquisition_functions.log_ei import LogEI
14 | except:
15 | print("If you want to use Bohamiann you have to install the following dependencies:\n"
16 | "RoBO (https://github.com/automl/RoBO)")
17 | from labwatch.optimizers.base import Optimizer
18 | from labwatch.converters.convert_to_configspace import (
19 | sacred_space_to_configspace, configspace_config_to_sacred)
20 |
21 |
22 | class Bohamiann(Optimizer):
23 |
24 | def __init__(self, config_space, burnin=3000, n_iters=10000):
25 |
26 | super(Bohamiann, self).__init__(sacred_space_to_configspace(config_space))
27 | self.rng = np.random.RandomState(np.random.seed())
28 | self.n_dims = len(self.config_space.get_hyperparameters())
29 |
30 | # All inputs are mapped to be in [0, 1]^D
31 | self.lower = np.zeros([self.n_dims])
32 | self.upper = np.ones([self.n_dims])
33 | self.incumbents = []
34 | self.X = None
35 | self.y = None
36 |
37 | self.model = BayesianNeuralNetwork(sampling_method="sghmc",
38 | l_rate=np.sqrt(1e-4),
39 | mdecay=0.05,
40 | burn_in=burnin,
41 | n_iters=n_iters,
42 | precondition=True,
43 | normalize_input=True,
44 | normalize_output=True)
45 |
46 | self.acquisition_func = LogEI(self.model)
47 |
48 | self.maximizer = Direct(self.acquisition_func, self.lower, self.upper, verbose=False)
49 |
50 | def suggest_configuration(self):
51 |
52 | if self.X is None and self.y is None:
53 | # No data points yet to train a model, just return a random configuration instead
54 | new_x = init_random_uniform(self.lower, self.upper,
55 | n_points=1, rng=self.rng)[0, :]
56 |
57 | else:
58 | # Train the model on all finished runs
59 | self.model.train(self.X, self.y)
60 | self.acquisition_func.update(self.model)
61 |
62 | # Maximize the acquisition function
63 | new_x = self.maximizer.maximize()
64 |
65 | # Maps from [0, 1]^D space back to original space
66 | next_config = Configuration(self.config_space, vector=new_x)
67 |
68 | # Transform to sacred configuration
69 | result = configspace_config_to_sacred(next_config)
70 |
71 | return result
72 |
--------------------------------------------------------------------------------
/labwatch/optimizers/dngo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | import numpy as np
6 |
7 | from ConfigSpace import Configuration
8 | try:
9 | from robo.initial_design.init_random_uniform import init_random_uniform
10 | from robo.acquisition.log_ei import LogEI
11 | from robo.acquisition.integrated_acquisition import IntegratedAcquisition
12 | from robo.maximizers.direct import Direct
13 | from robo.priors.dngo_priors import DNGOPrior
14 | from robo.models.dngo import DNGO
15 | except:
16 | print("If you want to use DNGOWrapper you have to install the following dependencies:\n"
17 | "RoBO (https://github.com/automl/RoBO)")
18 | from labwatch.optimizers.base import Optimizer
19 | from labwatch.converters.convert_to_configspace import (
20 | sacred_space_to_configspace, sacred_config_to_configspace,
21 | configspace_config_to_sacred)
22 |
23 |
24 | class DNGOWrapper(Optimizer):
25 |
26 | def __init__(self, config_space, burnin=1000, chain_length=200,
27 | n_hypers=20):
28 |
29 | super(DNGOWrapper, self).__init__(config_space)
30 | self.rng = np.random.RandomState(np.random.seed())
31 | self.config_space = sacred_space_to_configspace(config_space)
32 | self.n_dims = len(self.config_space.get_hyperparameters())
33 |
34 | # All inputs are mapped to be in [0, 1]^D
35 | self.X_lower = np.zeros([self.n_dims])
36 | self.X_upper = np.ones([self.n_dims])
37 | self.incumbents = []
38 | self.X = None
39 | self.Y = None
40 |
41 |
42 | def suggest_configuration(self):
43 | if self.X is None and self.Y is None:
44 | new_x = init_random_uniform(self.X_lower, self.X_upper,
45 | N=1, rng=self.rng)
46 |
47 | elif self.X.shape[0] == 1:
48 | # We need at least 2 data points to train a GP
49 | Xopt = init_random_uniform(self.X_lower, self.X_upper,
50 | N=1, rng=self.rng)
51 |
52 | else:
53 | prior = DNGOPrior()
54 | model = DNGO(batch_size=100, num_epochs=20000,
55 | learning_rate=0.1, momentum=0.9,
56 | l2=1e-16, adapt_epoch=5000,
57 | n_hypers=20, prior=prior,
58 | do_optimize=True, do_mcmc=True)
59 |
60 |
61 | #acquisition_func = EI(model, task.X_lower, task.X_upper)
62 | lo = np.ones([model.n_units_3]) * -1
63 | up = np.ones([model.n_units_3])
64 | ei = LogEI(model, lo, up)
65 |
66 | acquisition_func = IntegratedAcquisition(
67 | model, ei, self.X_lower, self.X_upper)
68 |
69 | maximizer = Direct(acquisition_func, self.X_lower, self.X_upper)
70 |
71 | model.train(self.X, self.Y)
72 |
73 | acquisition_func.update(model)
74 |
75 | new_x = maximizer.maximize()
76 |
77 |
78 | # Map from [0, 1]^D space back to original space
79 | next_config = Configuration(self.config_space, vector=new_x[0, :])
80 |
81 | # Transform to sacred configuration
82 | result = configspace_config_to_sacred(next_config)
83 |
84 | return result
85 |
86 |
87 | def update(self, configs, costs, runs):
88 | converted_configs = [
89 | sacred_config_to_configspace(self.config_space, config)
90 | for config in configs]
91 |
92 | for (config, cost) in zip(converted_configs, costs):
93 | # Maps configuration to [0, 1]^D space
94 | x = config.get_array()
95 |
96 | if self.X is None and self.Y is None:
97 | self.X = np.array([x])
98 | self.Y = np.array([[cost]])
99 | else:
100 | self.X = np.append(self.X, x[np.newaxis, :], axis=0)
101 | self.Y = np.append(self.Y, np.array([[cost]]), axis=0)
102 |
103 | def needs_updates(self):
104 | return True
105 |
--------------------------------------------------------------------------------
/labwatch/optimizers/random_search.py:
--------------------------------------------------------------------------------
1 |
2 | from labwatch.optimizers import Optimizer
3 |
4 |
5 | class RandomSearch(Optimizer):
6 |
7 | def __init__(self, config_space):
8 | super(RandomSearch, self).__init__(config_space)
9 |
10 | def suggest_configuration(self):
11 | return self.get_random_config()
12 |
13 | def update(self, configs, costs, run_info):
14 | pass
15 |
16 | def needs_updates(self):
17 | return False
18 |
--------------------------------------------------------------------------------
/labwatch/optimizers/smac_wrapper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | import numpy as np
6 | try:
7 | from smac.scenario.scenario import Scenario
8 | from smac.tae.execute_ta_run import StatusType
9 | from smac.facade import smac_facade
10 | except:
11 | print("If you want to use LabwatchScenario you have to install the following dependencies:\n"
12 | "SMAC (https://github.com/automl/SMAC3)")
13 | from labwatch.optimizers.base import Optimizer
14 | from labwatch.converters.convert_to_configspace import (
15 | sacred_space_to_configspace, sacred_config_to_configspace,
16 | configspace_config_to_sacred)
17 |
18 |
19 | class LabwatchScenario(Scenario):
20 | """
21 | Specialize the smac3 scenario here since we want to create
22 | everything within code without reading a smac scenario file.
23 | """
24 |
25 | def __init__(self, config_space, logger):
26 | self.logger = logger
27 | # we don't actually have a target algorithm here
28 | # we will implement algorithm calling and the SMBO loop ourselves
29 | self.ta = None
30 | self.execdir = None
31 | self.pcs_fn = None
32 | self.run_obj = 'quality'
33 | self.overall_obj = self.run_obj
34 |
35 | # Time limits for smac
36 | # these will never be used since we call
37 | # smac.choose_next() manually
38 | self.cutoff = None
39 | self.algo_runs_timelimit = None
40 | self.wallclock_limit = None
41 |
42 | # no instances
43 | self.train_inst_fn = None
44 | self.test_inst_fn = None
45 | self.feature_fn = None
46 | self.train_insts = []
47 | self.test_inst = []
48 | self.feature_dict = {}
49 | self.feature_array = None
50 | self.instance_specific = None
51 | self.n_features = 0
52 |
53 | # save reference to config_space
54 | self.cs = config_space
55 |
56 | # We do not need a TAE Runner as this is done by the Sacred Experiment
57 | self.tae_runner = None
58 | self.deterministic = False
59 |
60 |
61 | class SMAC(Optimizer):
62 | def __init__(self, config_space, seed=None):
63 |
64 | if seed is None:
65 | self.seed = np.random.randint(0, 10000)
66 | else:
67 | self.seed = seed
68 |
69 | self.rng = np.random.RandomState(self.seed)
70 |
71 | super(SMAC, self).__init__(sacred_space_to_configspace(config_space))
72 |
73 | self.scenario = Scenario({"run_obj": "quality",
74 | "cs": self.config_space,
75 | "deterministic": "true"})
76 | self.solver = smac_facade.SMAC(scenario=self.scenario,
77 | rng=self.rng)
78 |
79 | def suggest_configuration(self):
80 | if self.X is None and self.y is None:
81 | next_config = self.config_space.sample_configuration()
82 |
83 | else:
84 | l = list(self.solver.solver.choose_next(self.X, self.y[:, None], incumbent_value=np.min(self.y)))
85 | next_config = l[0]
86 |
87 | result = configspace_config_to_sacred(next_config)
88 |
89 | return result
90 |
91 | def needs_updates(self):
92 | return True
93 |
--------------------------------------------------------------------------------
/labwatch/searchspace.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | import re
6 |
7 | from sacred.config import ConfigScope
8 | from sacred.utils import join_paths
9 | from labwatch.hyperparameters import Parameter, ConditionResult, Categorical
10 | from labwatch.hyperparameters import decode_param_or_op
11 | from labwatch.utils.types import InconsistentSpace, ParamValueExcept
12 |
13 |
14 | class SearchSpace(object):
15 |
16 | def __init__(self, search_space):
17 | super(SearchSpace, self).__init__()
18 | # extract the _id from the searchspace definition
19 | self._id = search_space.get('_id', None)
20 | if '_id' in search_space:
21 | del search_space['_id']
22 |
23 | self.search_space = search_space
24 | parameters = collect_hyperparameters(search_space)
25 | params = sorted(parameters.values(), key=lambda x: x['uid'])
26 | self.uids_to_names = {param["uid"]: param['name'] for param in params}
27 | self.conditions = []
28 | self.non_conditions = []
29 | self.parameters = {}
30 | self.has_categorical = False
31 | # first simply insert all
32 | for param in params:
33 | if isinstance(param, Categorical):
34 | self.has_categorical = True
35 |
36 | assert(isinstance(param, Parameter))
37 | self.parameters[param['name']] = param
38 | if isinstance(param, ConditionResult):
39 | self.conditions.append(param["name"])
40 | else:
41 | self.non_conditions.append(param["name"])
42 |
43 | self.contains_conditions = len(self.conditions) > 0
44 | self.validate_conditions()
45 |
46 | def to_json(self):
47 | son = dict(self.search_space)
48 | son['_class'] = 'SearchSpace'
49 | if self._id is not None:
50 | son['_id'] = self._id
51 | return son
52 |
53 | @classmethod
54 | def from_json(cls, son):
55 | assert son['_class'] == 'SearchSpace'
56 | del son['_class']
57 | return SearchSpace(son)
58 |
59 | def validate_conditions(self):
60 | for pname in self.conditions:
61 | cparam = self.parameters[pname]
62 | conditioned_on = cparam["condition"]["uid"]
63 | if not (conditioned_on in self.uids_to_names):
64 | err = "Conditional parameter: {} depends on: {} " \
65 | "which does not exist in the defined SearchSpace!"
66 | raise InconsistentSpace(err.format(pname, conditioned_on))
67 |
68 | def is_valid_name(self, name):
69 | return name in self.uids_to_names.values()
70 |
71 | def valid(self, config):
72 | # TODO check this again for consistency
73 | valid = True
74 | for pname in self.non_conditions:
75 | valid &= self.parameters[pname].valid(config[pname])
76 | if not valid:
77 | return False
78 | for pname in self.conditions:
79 | cparam = self.parameters[pname]
80 | conditioned_on = cparam["condition"]["uid"]
81 | if cparam in config.keys():
82 | if conditioned_on in self.uids_to_names.keys():
83 | valid &= conditioned_on.sample(config[self.uids_to_names[conditioned_on]])
84 | valid &= cparam.valid(config[pname])
85 | else:
86 | valid = False
87 | return valid
88 |
89 | def sample(self, max_iters_till_cycle=50, strategy="random"):
90 | if strategy not in ["random", "default"]:
91 | raise ParamValueExcept("Unknown sampling strategy {}".format(strategy))
92 | # allocate result dict
93 | res = {}
94 | # first add all fixed parameters
95 | # for pname in self.fixed:
96 | # res[pname] = self[pname]
97 | # second sample all non conditions
98 | considered_params = set()
99 | for pname in self.non_conditions:
100 | if strategy == "random":
101 | res[pname] = self.parameters[pname].sample()
102 | else:
103 | res[pname] = self.parameters[pname].default()
104 | considered_params.add(pname)
105 | # then the conditional parameters
106 | remaining_params = set(self.conditions)
107 | i = 0
108 | while remaining_params:
109 | for pname in self.conditions:
110 | if pname in remaining_params:
111 | cparam = self.parameters[pname]
112 | conditioned_on = self.uids_to_names[cparam["condition"]["uid"]]
113 | if conditioned_on in res.keys():
114 | if strategy == "random":
115 | cres = self.parameters[pname].sample(res[conditioned_on])
116 | else:
117 | cres = self.parameters[pname].default(res[conditioned_on])
118 | if cres:
119 | res[pname] = cres
120 | considered_params.add(pname)
121 | remaining_params.remove(pname)
122 | elif conditioned_on in considered_params:
123 | remaining_params.remove(pname)
124 | else:
125 | continue
126 | i += 1
127 | if i > max_iters_till_cycle:
128 | err = "Cannot satisfy conditionals involving " \
129 | "parameters {} probably a loop! If you are sure " \
130 | "no loop exists increase max_iters_till_cycle"
131 | raise InconsistentSpace(err.format(remaining_params))
132 | return res
133 |
134 | def default(self, max_iters_till_cycle=50):
135 | return self.sample(max_iters_till_cycle, strategy="default")
136 |
137 | def __eq__(self, other):
138 | if not isinstance(other, SearchSpace):
139 | return False
140 | else:
141 | return self.search_space == other.search_space
142 |
143 |
144 | # decorator
145 | def build_search_space(function):
146 | # abuse configscope to parse search space definitions
147 | scope = ConfigScope(function)
148 | space_dict = dict(scope())
149 |
150 | # parse generic dict to a search space
151 | space = SearchSpace(space_dict)
152 | return space
153 |
154 |
155 | def set_name(hparam, name):
156 | if ('name' not in hparam or
157 | len(hparam['name']) > len(name) or
158 | hparam['name'] > name):
159 | hparam['name'] = name
160 |
161 |
162 | def merge_parameters(params, new_params):
163 | for k, v in new_params.items():
164 | if k not in params:
165 | params[k] = v
166 | else:
167 | set_name(params[k], v['name'])
168 | return params
169 |
170 |
171 | def collect_hyperparameters(search_space, path=''):
172 | """
173 | Recursively collect all the hyperparameters from a search space.
174 |
175 | Parameters
176 | ----------
177 | search_space : dict
178 | A JSON-like structure that describes the search space.
179 | path : str
180 | The path to the current entry. Used to determine the name of the
181 | detected hyperparameters. Optional: Only used for the recursion.
182 |
183 | Returns
184 | -------
185 | parameters : dict
186 | A dictionary that to all the collected hyperparameters from their uids.
187 | """
188 | # First try to decode to hyperparameter
189 | if isinstance(search_space, dict):
190 | try:
191 | hparam = decode_param_or_op(search_space)
192 | set_name(hparam, path)
193 | return {hparam['uid']: hparam}
194 | except ValueError:
195 | pass
196 |
197 | parameters = {}
198 | # if the space is a dict (but not a hyperparameter) we parse it recursively
199 | if isinstance(search_space, dict):
200 | for k, v in search_space.items():
201 | # add the current key and a '.' as prefix when recursing
202 | sub_params = collect_hyperparameters(v, join_paths(path, k))
203 | parameters = merge_parameters(parameters, sub_params)
204 | return parameters
205 |
206 | # if the space is a list we iterate it recursively
207 | elif isinstance(search_space, (tuple, list)):
208 | for i, v in enumerate(search_space):
209 | # add '[N]' to the name when recursing
210 | sub_params = collect_hyperparameters(v, path + '[{}]'.format(i))
211 | parameters = merge_parameters(parameters, sub_params)
212 | return parameters
213 | else:
214 | # if the space is anything else do nothing
215 | return parameters
216 |
217 |
218 | def fill_in_values(search_space, values, fill_by='uid'):
219 | """
220 | Recursively insert given values into a search space to receive a config.
221 |
222 | Parameters
223 | ----------
224 | search_space : dict
225 | A JSON-like structure that describes the search space.
226 | values : dict
227 | A dictionary mapping uids to values.
228 |
229 | Returns
230 | -------
231 | dict
232 | A configuration that results from replacing hyperparameters by the
233 | corresponding values.
234 |
235 | """
236 | if isinstance(search_space, dict):
237 | if '_class' in search_space and fill_by in search_space:
238 | return values[search_space[fill_by]]
239 | else:
240 | return {k: fill_in_values(v, values, fill_by)
241 | for k, v in search_space.items()}
242 | elif isinstance(search_space, (list, tuple)):
243 | config = [fill_in_values(v, values, fill_by) for v in search_space]
244 | return type(search_space)(config)
245 | else:
246 | return search_space
247 |
248 |
249 | def get_by_path(config, path):
250 | """
251 | Get a config-entry by its dotted and indexed name.
252 |
253 | Parameters
254 | ----------
255 | config : dict
256 | The configuration dictionary to get the values from.
257 | path : str
258 | The config entry corresponding to the given path.
259 | Returns
260 | -------
261 | object
262 | The configuration entry that corresponds to the given path.
263 |
264 | """
265 | current = config
266 | for p in filter(None, re.split('[.\[\]]', path)):
267 | try:
268 | p = int(p)
269 | except ValueError:
270 | pass
271 | current = current[p]
272 | return current
273 |
274 |
275 | def get_values_from_config(config, hyperparams):
276 | """
277 | Infer the values of hyperparameters from a given configuration.
278 | Parameters
279 | ----------
280 | config : dict
281 | A JSON configuration that has to correspond to the search space.
282 | hyperparams : dict
283 | A dictionary that maps uids to hyperparameters.
284 |
285 | Returns
286 | -------
287 | dict
288 | A dictionary mapping names to values.
289 | """
290 | return {hparam['name']: get_by_path(config, hparam['name'])
291 | for uid, hparam in hyperparams.items()}
292 |
--------------------------------------------------------------------------------
/labwatch/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .fixed_dict import FixedDict
2 | from .hashing import hash_dict
3 |
--------------------------------------------------------------------------------
/labwatch/utils/fixed_dict.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from labwatch.utils.types import fullname
6 |
7 |
8 | def warn_not_allowed(self, key):
9 | warning = "WARNING: you tried to set key {}" \
10 | " for class {} which is among the fixed keys!"
11 | print(warning.format(key, fullname(self)))
12 |
13 |
14 | class FixedDict(dict):
15 | def __init__(self, fixed=None):
16 | if fixed is None:
17 | fixed = {}
18 | super(FixedDict, self).__init__()
19 | for key in fixed.keys():
20 | dict.__setitem__(self, key, fixed[key])
21 | dict.__setitem__(self, "_class", fullname(self))
22 | self.fixed = set(fixed.keys()).union({"_class"})
23 |
24 | def __setitem__(self, key, value):
25 | """ __setitem__ for parameters only works
26 | for non fixed values!
27 | """
28 | if key not in self.fixed:
29 | return dict.__setitem__(self, key, value)
30 | else:
31 | warn_not_allowed(self, key)
32 |
33 | def __delitem__(self, key):
34 | if key not in self.fixed:
35 | dict.__delitem__(self, key)
36 |
--------------------------------------------------------------------------------
/labwatch/utils/hashing.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | def hash_dict(storage):
4 | return hash(json.dumps(storage, sort_keys=True))
5 |
6 |
--------------------------------------------------------------------------------
/labwatch/utils/types.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | from six import integer_types, string_types
3 |
4 |
5 | def str_to_class(cls_str):
6 | # module_name, class_name = cls_str.rsplit('.', 1)
7 | # somemod = importlib.import_module(module_name)
8 | somemod = importlib.import_module('labwatch.hyperparameters')
9 | class_name = cls_str
10 | return getattr(somemod, class_name)
11 |
12 |
13 | def fullname(o):
14 | # return o.__module__ + "." + o.__class__.__name__
15 | return o.__class__.__name__
16 |
17 | types_to_str = {
18 | float: "float",
19 | int: "int",
20 | str: "str"
21 | }
22 |
23 | for t in integer_types:
24 | types_to_str[t] = 'int'
25 | for t in string_types:
26 | types_to_str[t] = 'str'
27 |
28 |
29 | str_to_types = {
30 | "float": float,
31 | "int": int,
32 | "str": str
33 | }
34 |
35 | basic_types = tuple(types_to_str.keys())
36 |
37 |
38 | class ParamValueExcept(Exception):
39 | pass
40 |
41 |
42 | class InconsistentSpace(Exception):
43 | pass
44 |
45 |
46 | class ParamInconsistent(Exception):
47 | pass
48 |
49 |
50 | class SearchSpaceNotSupported(Exception):
51 | pass
52 |
--------------------------------------------------------------------------------
/labwatch/utils/version_checks.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from pkg_resources import parse_version
6 |
7 |
8 | def parse_name_ver(name_version):
9 | name, _, ver = name_version.partition('==')
10 | return name, parse_version(ver)
11 |
12 |
13 | def check_dependencies(ex_dep, run_dep, version_policy):
14 | ex_dep = dict([parse_name_ver(name_version) for name_version in ex_dep])
15 | check_version = {
16 | 'newer': lambda ex, name, b: name in ex and ex[name] >= b,
17 | 'equal': lambda ex, name, b: name in ex and ex[name] == b,
18 | 'exists': lambda ex, name, b: name in ex
19 | }[version_policy]
20 | for name_version in run_dep:
21 | name, ver = parse_name_ver(name_version)
22 | assert check_version(ex_dep, name, parse_version(ver)), \
23 | "{} mismatch: ex={}, run={}".format(name, ex_dep[name], ver)
24 |
25 |
26 | def check_sources(ex_sources, run_sources):
27 | for ex_source, run_source in zip(ex_sources, run_sources):
28 | if not ex_source == tuple(run_source):
29 | raise KeyError('Source files did not match: experiment:'
30 | ' {} [{}] != {} [{}] (run)'.format(
31 | ex_source[0], ex_source[1],
32 | run_source[0], run_source[1]))
33 |
34 |
35 | def check_names(ex_name, run_name):
36 | if not ex_name == run_name:
37 | raise KeyError('experiment names did not match: experiment name '
38 | '{} != {} (run name)'.format(ex_name, run_name))
39 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | classifiers = """
4 | Intended Audience :: Science/Research
5 | Natural Language :: English
6 | Programming Language :: Python
7 | Topic :: Utilities
8 | Topic :: Scientific/Engineering
9 | Topic :: Scientific/Engineering :: Artificial Intelligence
10 | Topic :: Software Development :: Libraries :: Python Modules
11 | License :: OSI Approved :: MIT License
12 | """
13 |
14 | requires = [
15 | 'numpy >= 1.7',
16 | 'sacred',
17 | 'pymongo',
18 | 'ConfigSpace'
19 | ]
20 |
21 | try:
22 | from labwatch import __about__
23 | about = __about__.__dict__
24 | except ImportError:
25 | about = dict()
26 | exec(open("labwatch/__about__.py").read(), about)
27 |
28 |
29 |
30 | setup(name='labwatch',
31 | version=about['__version__'],
32 | description='Hyperparameter optimization extension to Sacred',
33 | long_description=open('README.md').read(),
34 | classifiers=list(filter(None, classifiers.split('\n'))),
35 | author=about['__authors__'],
36 | author_email='kleinaa@cs.infomatik.uni-freiburg.de, springj@cs.uni-freiburg.de',
37 | url=about['__url__'],
38 | packages=['labwatch', 'labwatch.utils', 'labwatch.optimizers', 'labwatch.converters'],
39 | include_package_data=True,
40 | tests_require=['mock', 'mongomock', 'pytest'],
41 | install_requires=requires
42 | )
43 |
--------------------------------------------------------------------------------
/tests/test_conversion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 | import json
5 | import pytest
6 |
7 | from labwatch.hyperparameters import *
8 | from labwatch.searchspace import build_search_space, collect_hyperparameters, \
9 | fill_in_values
10 | from labwatch.converters.convert_to_configspace import (
11 | sacred_space_to_configspace, sacred_config_to_configspace,
12 | configspace_config_to_sacred)
13 |
14 |
15 | def simple_sp():
16 | batch_size = UniformNumber(lower=32, upper=64, default=32, type=int)
17 | num_units_first_conv = UniformNumber(lower=32, upper=64, default=32,
18 | type=int)
19 | num_units_second_conv = UniformNumber(lower=32, upper=64, default=32,
20 | type=int)
21 | dropout_rate = UniformNumber(lower=0.2, upper=0.9, default=0.5, type=float)
22 |
23 |
24 | def space_with_condition():
25 | batch_size = UniformNumber(lower=32, upper=64, default=32, type=int)
26 | n_layers = Categorical([1, 2])
27 | units_first = UniformNumber(lower=32,
28 | upper=64, default=32, type=int)
29 | two = Constant(2)
30 | units_second = UniformNumber(lower=32,
31 | upper=64, default=32, type=int) | Condition(
32 | n_layers, [two])
33 | dropout_second = UniformNumber(lower=0.2, upper=0.8,
34 | default=0.5, type=float) | Condition(
35 | n_layers, [2])
36 |
37 |
38 | def test_convert_small_config_space():
39 | space = build_searchspace(simple_sp)
40 | cspace = sacred_space_to_configspace(space)
41 |
42 | cs_non_conditions = cspace.get_all_unconditional_hyperparameters()
43 | for name in space.non_conditions:
44 | assert name in cs_non_conditions
45 |
46 |
47 | def test_convert_larger_config_space():
48 | space = build_searchspace(space_with_condition)
49 | cspace = sacred_space_to_configspace(space)
50 |
51 | cs_non_conditions = cspace.get_all_unconditional_hyperparameters()
52 | for name in space.non_conditions:
53 | assert name in cs_non_conditions
54 | cs_conditions = cspace.get_all_conditional_hyperparameters()
55 | for name in space.conditions: assert name in cs_conditions
56 |
57 |
58 | def test_convert_config():
59 | space = build_searchspace(space_with_condition)
60 | cspace = sacred_space_to_configspace(space)
61 |
62 | config = space.sample()
63 | cs_config = sacred_config_to_configspace(cspace, config)
64 | assert config == cs_config.get_dictionary()
65 | config_convert_back = configspace_config_to_sacred(cs_config)
66 | assert config == config_convert_back
67 |
68 |
69 | def test_config_config_wrong_space_raises():
70 | space = build_searchspace(space_with_condition)
71 | cspace = sacred_space_to_configspace(space)
72 |
73 | config = space.sample()
74 | # passing the wrong type of space to config_to_configspace raises
75 | with pytest.raises(ValueError):
76 | cs_config = sacred_config_to_configspace(space, config)
77 |
78 |
79 | @pytest.mark.parametrize("hparam, name", [
80 | (Constant(23), 'Constant'),
81 | (UniformFloat(0., 1.), 'UniformFloat'),
82 | (UniformInt(1, 10), 'UniformInt'),
83 | (UniformNumber(0, 1, float), 'UniformNumber'),
84 | (Categorical([2, 4, 6]), 'Categorical'),
85 | (Gaussian(0, 1.0), 'Gaussian')
86 | ])
87 | def test_automatic_hyperparameters_to_dict_conversion(hparam, name):
88 | # use JSON serialization to force dict conversion
89 | d = json.loads(json.dumps(hparam))
90 | assert isinstance(d, dict)
91 | assert "uid" in d
92 | assert "_class" in d
93 | assert d['_class'] == name
94 |
95 |
96 | @pytest.mark.parametrize("hparam", [
97 | Constant(23),
98 | UniformFloat(0., 1.),
99 | UniformInt(1, 10),
100 | UniformNumber(0, 1, float),
101 | Categorical([2, 4, 6]),
102 | Gaussian(0, 1.0),
103 | ])
104 | def test_decoding_hyperparameters_from_dict(hparam):
105 | # use JSON serialization to force dict conversion
106 | d = json.loads(json.dumps(hparam))
107 | # decode
108 | h = decode_param_or_op(d)
109 | assert isinstance(h, Parameter)
110 | assert isinstance(h, type(hparam))
111 | assert h == hparam
112 |
113 |
114 | def test_simple_searchspace_conversion():
115 | a = Constant(7)
116 | b = UniformFloat(0, 1)
117 | space = {
118 | 'a': a,
119 | 'b': b
120 | }
121 | sp_dict = json.loads(json.dumps(space))
122 |
123 | params = collect_hyperparameters(sp_dict, {})
124 | assert params == {
125 | a['uid']: a,
126 | b['uid']: b
127 | }
128 |
129 |
130 | def test_searchspace_conversion_with_repetition():
131 | a = UniformFloat(0, 1)
132 | space = {
133 | 'a': a,
134 | 'b': a
135 | }
136 | sp_dict = json.loads(json.dumps(space))
137 |
138 | params = collect_hyperparameters(sp_dict, {})
139 | assert params == {
140 | a['uid']: a
141 | }
142 |
143 |
144 | def test_searchspace_conversion_with_substructure():
145 | a = UniformFloat(0, 1)
146 | b = UniformInt(2, 12)
147 | c = Gaussian(0, 1)
148 | space = {
149 | 'a': a,
150 | 'foo': {
151 | 'bar': b,
152 | 'nested': {
153 | 'a': a
154 | }
155 | },
156 | 'using_list': [a, b, c]
157 | }
158 | sp_dict = json.loads(json.dumps(space))
159 |
160 | params = collect_hyperparameters(sp_dict, {})
161 | assert params == {
162 | a['uid']: a,
163 | b['uid']: b,
164 | c['uid']: c
165 | }
166 |
167 | assert params[a['uid']]['name'] == 'a'
168 | assert params[b['uid']]['name'] == 'foo.bar'
169 | assert params[c['uid']]['name'] == 'using_list[2]'
170 |
171 |
172 | def test_fill_in_values():
173 | a = UniformFloat(0, 1)
174 | b = UniformInt(2, 12)
175 | c = Gaussian(0, 1)
176 | search_space = json.loads(json.dumps({
177 | 'a': a,
178 | 'foo': {
179 | 'bar': b,
180 | 'nested': {
181 | 'a': a
182 | }
183 | },
184 | 'using_list': [a, b, c]
185 | }))
186 | values = {
187 | a['uid']: 11,
188 | b['uid']: 2.2,
189 | c['uid']: 'c'
190 | }
191 | cfg = fill_in_values(search_space, values)
192 | assert cfg == {
193 | 'a': 11,
194 | 'foo': {
195 | 'bar': 2.2,
196 | 'nested': {
197 | 'a': 11
198 | }
199 | },
200 | 'using_list': [11, 2.2, 'c']
201 | }
202 |
--------------------------------------------------------------------------------
/tests/test_searchspace.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 | from __future__ import division, print_function, unicode_literals
4 |
5 | from labwatch.hyperparameters import *
6 | from labwatch.searchspace import build_search_space
7 |
8 | import pprint
9 |
10 | pp = pprint.PrettyPrinter(indent=4)
11 |
12 |
13 | def test_small_config_space():
14 | def simple_sp():
15 | batch_size = UniformNumber(lower=32, upper=64, default=32, type=int)
16 | num_units_first_conv = UniformNumber(lower=32, upper=64, default=32,
17 | type=int)
18 | num_units_second_conv = UniformNumber(lower=32, upper=64, default=32,
19 | type=int)
20 | dropout_rate = UniformNumber(lower=0.2, upper=0.9, default=0.5,
21 | type=float)
22 |
23 | space = build_searchspace(simple_sp)
24 | cfg = space.sample()
25 | assert space.valid(cfg) == True
26 |
27 |
28 | def test_config_space_with_condition():
29 | def space_with_condition():
30 | batch_size = UniformNumber(lower=32, upper=64, default=32, type=int)
31 | n_layers = Categorical([1, 2])
32 | units_first = UniformNumber(lower=32,
33 | upper=64, default=32, type=int)
34 | two = Constant(2)
35 | units_second = UniformNumber(lower=32,
36 | upper=64, default=32,
37 | type=int) | Condition(n_layers, [two])
38 | dropout_second = UniformNumber(lower=0.2, upper=0.8,
39 | default=0.5, type=float) | Condition(
40 | n_layers, [2])
41 |
42 | space = build_searchspace(space_with_condition)
43 | cfg = space.sample()
44 | pp.pprint(space)
45 | pp.pprint(cfg)
46 | assert space.valid(cfg) == True
47 |
--------------------------------------------------------------------------------