Contributing¶
Introduction¶
We encourage contributions to DeepArchitect. If DeepArchitect has been useful for your work, please cite it and/or contribute to the codebase. We encourage everyone doing research in architecture search to implement their algorithms in DeepArchitect to make them widely available to other researchers and the machine learning community at large. This will significantly improve reproducibility and reusability of architecture search research.
Contributions can be a result of your own research or from implementations of existing algorithms. If you have developed a searcher, search space, evaluator, or any other component or functionality that would be useful to include in DeepArchitect, please make a pull request that follows the guidelines described in this document.
After reading this document, you will understand:
- what are the different types of contributions that we identify;
- what is the folder structure for contributions;
- what is required in terms of tests and documentation for different types of contributions;
- what are the different levels of conformity that we require for different types of contributions.
If you have a feature that you would like to add to DeepArchitect but you are unsure about its suitability, open a GitHub issue for discussion. This guarantees that your efforts are well-aligned with the project direction and needs. Consider including a code snippet or pseudo-code illustrating a useful use case for the feature.
Types of contributions¶
Most contributions will live in the contrib folder. The contrib folder is used for functionality that is likely useful, but that won’t necessarily be maintained over time. While code lies in the contrib folder, the code owners are responsible for its maintenance. If code in the contrib folder breaks and the code owner does not fix it in a timely manner, we reserve the right to move the code to the dev folder. The dev folder contains code sketching interesting functionality that may not be fully functional or it has not been refactored well enough to be integrated in contrib. Unmaintained code will be moved to dev upon breakage. Code in dev should not be used directly, but it can inspire further development.
Code that is part of the contrib folder may eventually be refactored into code that is part of the deep_architect folder. Similarly, code in the dev folder may be refactored in code that goes in the contrib folder. If code becomes part of the deep_architect folder, it becomes the responsibility of the developers of DeepArchitect to maintain it. To create a new contrib folder, it is best to first discuss its scope. We do not impose these restrictions for the dev folder. The dev folder should be used lightly though. We will only accept contributions to the dev folder if it they showcase important functionality and there is sufficient reason to justify their incompleteness. If the functionality is complete, we advise the contributor to refactor it into the contrib folder. Including the contribution in the contrib folder can be done either by adding it to an existing contrib subfolder, or by creating a new well-scoped contrib subfolder.
Cross-pollination between contrib and dev folders is expected and encouraged. For example, a few subcontrib folders already contain useful functionality, but a contributor may want to extend it and encapsulate it in a more coherent contrib subfolder. This scheme allows DeepArchitect to evolve without committing to major refactoring decisions upfront. If the foreseen contribution is better seen as an extension or a fix to an existing contrib folder, please open an issue or a pull request to discuss with the most active contributors on how to best incorporate the contribution in the existing files and folders. We may ask for refactoring changes or additional tests.
Required documentation and tests¶
Your new library in contrib should be placed in
deep_architect/contrib/$YOUR_LIBRARY_NAME
. New folders in contrib
should include a README.md
file providing information about the
functionality that the library seeks to implement, the features that are
implemented in the folder contributed, and an explanation about how the
implementation is split between the different files and folders. Also
include an explanation about when would it be natural to use the code in
this library. This guarantees that a new user will quickly get a
reasonable grasp of how to use the library and what files to look at for
specific desired functionality. Comments for each major class and
function are also recommended but not mandatory. Check the comments in
deep_architect/core.py
to get a sense of the style and format used
for comments. It is also convenient to include in README.md
, a
roadmap for missing functionality that would be nice to include in the
future. This informs future contributors about where the contributed
project is going and compels them to help, e.g., if they believe that
the feature is important.
The following is a typical structure for README.md
: explanation of
the problem that the contributed code tries to solve, some example code,
a brief description of the high-level organization of the contributed
library, and a roadmap for future work items and nice-to-haves and how
other people can contribute to it, additional comments, GitHub handles
of the code owners. If another contributor would like to extend an
existing contributed library, it is best to reach out to the appropriate
owner by writing an issue and mentioning the appropriate owner. The
addition of significant new functionality requires adding more tests to
exercise the newly developed code.
In addition to README.md
, it is convenient to add tests and
examples. The contributor should place tests in
tests/contrib/$YOUR_LIBRARY_NAME
and examples in
examples/contrib/$YOUR_LIBRARY_NAME
. Both tests/contrib
and
examples/contrib
are meant to mostly reproduce the folder structure
in deep_architect/contrib
. This guarantees that removing a
contributed library can be done easily by removing the corresponding
folders in deep_architect/contrib
, tests/contrib
, and
examples/contrib
. While an example is not required, we do require a
few tests to exercise the contributed code and have some guarantee that
specific features remain correct as the contributed code and the
development environment change.
Folder structure for contributions¶
For minimizing coupling between contributions of different people, we
adopt a design similar to the one used in
Tensorflow. Namely, we
have a contrib folder where each new sufficiently different well-scoped
contribution gets assigned a folder in deep_architect/contrib
. The
name of the folder should be chosen to reflect the functionality that
lies within. All the library code contributed by the developer will be
placed in this folder. Main files that are meant to be run should be
placed in examples/contrib
rather than in
deep_architect/contrib
. The same name should be used for both the
folder in deep_architect/contrib
and in examples/contrib
. The
subfolder in examples/contrib
is meant for runnable code related to
or making extensive use of the library code in the
deep_architect/contrib
subfolder. We recommend checking existing
examples in the repo
for determining how to structure and document a new example
appropriately.
Each configuration to run the example should be placed in a JSON
configuration file $CONFIG_NAME.json
in a folder named configs
living in the same folder of the main file of the example. JSON
configuration files guarantee that the options that determine the
behavior of running the code can be kept separated from the code itself.
This is more manageable, programmable, and configurable than having a
command line interface. This guarantees that it is easy to maintain and
store many different configurations, e.g., one configuration where the
code is exercised with few resources and another configuration where the
code is exercised in a longer run, e.g., see
here.
Each JSON file corresponds to a different configuration. We suggest
including a debug.json
to run a quick experiment to validate the
functionality of both the code under contrib/examples
and
deep_architect/contrib
. We recommend the use of configuration files
for all but the most trivial examples. We often use the signature
python path/to/example/main.py -- config_filepath /path/to/config.json
for running examples, where we put all the configuration information in
the JSON file.
Whether contributing examples or libraries, we recommend identifying the
search spaces, searchers, evaluators, and datasets and splitting them
into different files, e.g.,
see.
Having these components into multiple files makes the dependencies more
explicit and improves the reusability of the components. The framework
is developed around these modular components. We recommend creating the
following files when appropriate: evaluators.py
,
search_spaces.py
, searchers.py
, main.py
, and
config.json
.
Development environment¶
The recommended code editor is Visual Studio
Code with recommended plugins
ms-python.python
, donjayamanne.githistory
, eamodio.gitlens
,
donjayamanne.jupyter
, yzhang.markdown-all-in-one
,
ban.spellright
. These can be installed through the extension tab or
in the command line (after Visual Studio Code has been installed) with
code --install-extension $EXTENSION_NAME
where EXTENSION_NAME
should be replaced by the name of each of the extensions.
We include VS Code settings with the repo which makes uses of yapf to automatically format the code on save. This will allow the contributor to effortlessly maintain formatting consistency with the rest of DeepArchitect.
We provide Singularity and Docker containers recipes for the development
environment. These can found in containers
along with additional
information on how to build them.
We have attempted to maintain compatibility with both Python 2 and Python 3. There might exist places in the code base where this is not verified. If you find a place in the codebase that is not simultaneously Python 2 and Python 3 compatible, please issue a pull request fixing the problem.
Code style¶
All contributions should follow the code style used in most of the code
base. When in doubt, mimic the style of deep_architect
. Code in
deep_architect
is the most carefully designed. Getting the general
gist of the design decisions that went in writing this code will help
you write code that fits well with the existing code. We provide an
autoformatter configuration for VS Code.
Readable variable names are preferred for function names, function
arguments, class names, object attributes, object attributes, and
dictionary keys. Names for iterator variables or local variables with a
short lifespan can be shorter and slightly less readable.
deep_architect/core.py
(and the code in deep_architect
in
general) is a good place to get the gist of how these decisions
influenced the naming conventions of the code. Function signatures
should be readable without much documentation. Use four spaces for
indentation. Upon submission of a pull request, some of these aspects
will be reviewed to make sure that the level of conformity is
appropriate for the type of contribution.
Examples of contributions¶
In this section, we identify the most natural contributions for DeepArchitect. These were identified to guarantee that DeepArchitect covers existing architecture search algorithms well. Other contributions are also very much encouraged.
Contributing a searcher¶
Searchers interact with the search space through a very simple interface: the searcher can ask if all the hyperparameters are specified (and therefore, if the specified search space can be compiled to a single model that can be evaluated); if the search space is not specified, the searcher can ask for a single unspecified hyperparameter and assign a value to it. When a value is assigned to an unspecified hyperparameter, the search space transitions, which sometimes gives rise to additional unspecified hyperparameters, e.g., after choosing the number of repetitions for a repetition substitution module.
The most general searchers rely solely on this simple interface. Good examples of general searchers implemented can be found here. In more specific cases, namely in reimplementations of searchers proposed in specific architecture search papers, there is some coupling between the search space and the searcher. In this case, the developed searcher expects the search space to have certain structure or properties. We recommend these types of searchers and search spaces to be kept in a contrib folder dedicated to the specific pair.
Searchers that work with arbitrary search space are preferred. Searchers that require specific properties from the search space are also often easily implemented in the framework. If the searcher requires specific search space properties, document this, e.g., by including example of search spaces that the searcher operates on, by discussing how do these differences compare with the most general case, and by discussing how are these differences supported by the DeepArchitect framework. All searchers should be accompanied by documentation, at the very least a docstring, and ideally both a docstring and an example exercising the searcher.
Contributing a search space¶
A search space encodes the set of architecture that will be under consideration by the searcher. Due to the compositionality properties of search spaces, e.g., through the use of substitution modules, or simply via the use of functions that allow us to create a larger search space from a number of smaller search spaces, new search spaces can be reused for defining yet other search spaces. A search space is as an encoding for the set of architectures that the expert finds reasonable, i.e., encodes the expert’s inductive bias about the problem under consideration.
For certain search spaces, it may make sense to develop them in a framework independent way. For example, all substitution modules are framework independent. Certain search space functionality that takes other smaller search spaces and put them together into a larger search space are also often framework independent.
Due to the flexibility of the domain specific language in DeepArchitect, it is possible to have search spaces over structures different than deep architectures, e.g., it is possible to have search spaces over scikit-learn pipelines or arithmetic circuits. Due to the generic interface that the searchers use to interface with the search space, any existing general searchers can be directly applied to the problem at hand.
The goal of introducing new search spaces may be to explore new interesting structures and to make them available to other people that want to use them.
Contributing an evaluator¶
Evaluators determine the function that we are optimizing over the search space. If the evaluator does a poor job identifying the models that we in fact care about, i.e., the models that achieve high performance when trained to convergence, then the validity of running architecture search is undermined.
It is worth to consider the introduction of new evaluators for specific tasks. For example, if people in the field have found that specific evaluators (i.e., specific ways of training the models) are necessary to achieve high-performance, then it is useful replicate them.
Contributing a surrogate model¶
Sequential model-based optimization (SMBO) searchers use surrogate models extensively. Given a surrogate function predicting a quantity related to performance, a SMBO searcher optimizes this function (often approximately) to pick the architecture to evaluate next. The quantity predicted by the surrogate model does not need to be the performance metric of interest, it can simply be a score that preserves the ordering of the models in the space according to the performance metric of interest. Surrogate models also extend naturally to multi-objective optimization.
The quality of a surrogate function can be evaluated both by the quality of the search it induces, and by how effective it is in determining the relative ordering of models in the search space. A good surrogate functions should be able to embed the architecture to be evaluated and generate accurate predictions. It is unclear which surrogate models predict performance well. We ask contributors to explore different structures and validate their performance. Existing implementations of surrogate functions can be found here.
Conclusion¶
This document outlines guidelines for contributing to DeepArchitect. Having these guidelines in place guarantees that the focus is placed on the functionality developed, rather than on the specific arbitrary decisions taken to implement it. Please make sure that you understand the main points of this document, e.g., in terms of folder organization, documentation, code style, test requirements, and different types of contributions.