Top Related Projects
Flax is a neural network library for JAX that is designed for flexibility.
An Open Source Machine Learning Framework for Everyone
Tensors and Dynamic neural networks in Python with strong GPU acceleration
DeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective.
🤗 Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX.
Ray is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.
Quick Overview
Gin-Config is a lightweight configuration framework for Python, developed by Google. It allows for easy configuration of hyperparameters in machine learning experiments, providing a simple way to define, modify, and parse configuration objects.
Pros
- Easy to use and integrate with existing Python projects
- Supports hierarchical configurations and inheritance
- Allows for dynamic configuration changes at runtime
- Provides a clean separation between code and configuration
Cons
- Limited documentation and examples compared to some other configuration libraries
- May be overkill for simple projects with minimal configuration needs
- Requires learning a specific syntax for defining configurations
- Not as widely adopted as some other configuration solutions
Code Examples
- Basic configuration definition:
import gin
@gin.configurable
def my_function(param1=1, param2='default'):
print(f"param1: {param1}, param2: {param2}")
gin.parse_config("""
my_function.param1 = 5
my_function.param2 = 'custom'
""")
my_function() # Output: param1: 5, param2: custom
- Using gin with a class:
import gin
@gin.configurable
class MyClass:
def __init__(self, value=10):
self.value = value
def print_value(self):
print(f"Value: {self.value}")
gin.parse_config("MyClass.value = 20")
obj = MyClass()
obj.print_value() # Output: Value: 20
- Loading configuration from a file:
import gin
gin.parse_config_file('config.gin')
@gin.configurable
def process_data(input_size, output_size):
print(f"Processing data with input size {input_size} and output size {output_size}")
process_data() # Values will be loaded from config.gin
Getting Started
To get started with Gin-Config:
-
Install the library:
pip install gin-config
-
Create a Python file (e.g.,
main.py
) with a configurable function:import gin @gin.configurable def my_function(param1=1, param2='default'): print(f"param1: {param1}, param2: {param2}") gin.parse_config("my_function.param1 = 5") my_function()
-
Run the script:
python main.py
This will output: param1: 5, param2: default
Competitor Comparisons
Flax is a neural network library for JAX that is designed for flexibility.
Pros of Flax
- Designed specifically for neural network research and development in JAX
- Offers a more comprehensive ecosystem for deep learning tasks
- Provides better integration with JAX's automatic differentiation and GPU/TPU acceleration
Cons of Flax
- Steeper learning curve due to its more complex architecture
- Less flexible for general-purpose configuration management outside of machine learning
Code Comparison
Flax example:
class CNN(nn.Module):
@nn.compact
def __call__(self, x):
x = nn.Conv(features=32, kernel_size=(3, 3))(x)
x = nn.relu(x)
x = nn.avg_pool(x, window_shape=(2, 2), strides=(2, 2))
return x
Gin-Config example:
import gin
@gin.configurable
def model(num_layers=2, units_per_layer=16):
return tf.keras.Sequential([
tf.keras.layers.Dense(units_per_layer) for _ in range(num_layers)
])
Summary
Flax is a neural network library built on JAX, offering powerful tools for deep learning research. Gin-Config, on the other hand, is a lightweight configuration framework for Python. While Flax excels in machine learning tasks with better JAX integration, Gin-Config provides more general-purpose configuration management with a simpler learning curve.
An Open Source Machine Learning Framework for Everyone
Pros of TensorFlow
- Comprehensive machine learning framework with extensive capabilities
- Large community and ecosystem with numerous resources and extensions
- Supports both high-level and low-level APIs for flexibility
Cons of TensorFlow
- Steeper learning curve due to its complexity and extensive features
- Larger codebase and installation size
- Can be slower for prototyping compared to more lightweight alternatives
Code Comparison
TensorFlow:
import tensorflow as tf
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
Gin-Config:
import gin
@gin.configurable
def create_model(hidden_units=64, num_classes=10):
return tf.keras.Sequential([
tf.keras.layers.Dense(hidden_units, activation='relu'),
tf.keras.layers.Dense(num_classes, activation='softmax')
])
Summary
TensorFlow is a comprehensive machine learning framework, while Gin-Config is a lightweight configuration framework. TensorFlow offers a complete ecosystem for building and deploying ML models, but comes with increased complexity. Gin-Config focuses on simplifying configuration management in Python projects, including TensorFlow-based ones. While TensorFlow provides built-in tools for model creation and training, Gin-Config allows for easy parameterization of functions and classes, which can be particularly useful in ML experiments.
Tensors and Dynamic neural networks in Python with strong GPU acceleration
Pros of PyTorch
- Comprehensive deep learning framework with extensive neural network capabilities
- Large, active community with frequent updates and extensive documentation
- Supports dynamic computational graphs, allowing for more flexible model architectures
Cons of PyTorch
- Steeper learning curve for beginners compared to Gin-Config's simplicity
- Larger codebase and more dependencies, potentially leading to longer setup times
- May be overkill for simple configuration management tasks
Code Comparison
Gin-Config:
import gin
@gin.configurable
def my_function(param1=1, param2='default'):
print(f"param1: {param1}, param2: {param2}")
gin.parse_config_file('config.gin')
my_function()
PyTorch:
import torch
import torch.nn as nn
class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 1)
def forward(self, x):
return self.linear(x)
model = MyModel()
While Gin-Config focuses on configuration management, PyTorch is a full-fledged deep learning framework. Gin-Config offers a simpler approach to parameter configuration, while PyTorch provides a comprehensive set of tools for building and training neural networks.
DeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective.
Pros of DeepSpeed
- Specifically designed for optimizing large-scale deep learning models
- Offers advanced features like ZeRO optimizer and pipeline parallelism
- Provides extensive documentation and examples for various use cases
Cons of DeepSpeed
- Steeper learning curve due to its complexity and advanced features
- May be overkill for smaller projects or simpler machine learning tasks
- Requires more setup and configuration compared to Gin-Config
Code Comparison
DeepSpeed configuration example:
{
"train_batch_size": 32,
"gradient_accumulation_steps": 1,
"optimizer": {
"type": "Adam",
"params": {
"lr": 0.001,
"betas": [0.9, 0.999],
"eps": 1e-8
}
}
}
Gin-Config configuration example:
import gin
@gin.configurable
def train(batch_size=32, learning_rate=0.001):
# Training logic here
pass
gin.parse_config_file('config.gin')
DeepSpeed is more focused on large-scale deep learning optimization, offering advanced features for distributed training and model parallelism. Gin-Config, on the other hand, provides a simpler and more flexible configuration system for general Python applications, including but not limited to machine learning projects. The choice between the two depends on the specific requirements and scale of your project.
🤗 Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX.
Pros of Transformers
- Extensive library of pre-trained models for various NLP tasks
- Active community and frequent updates
- Comprehensive documentation and tutorials
Cons of Transformers
- Larger codebase and potentially steeper learning curve
- Higher computational requirements for some models
Code Comparison
Transformers:
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
Gin-config:
import gin
@gin.configurable
def model_fn(param1, param2):
# Model implementation
pass
gin.parse_config_file('config.gin')
Summary
Transformers is a comprehensive library for state-of-the-art NLP models, offering a wide range of pre-trained models and tools. It's ideal for complex NLP tasks but may require more resources. Gin-config, on the other hand, is a lightweight configuration framework for machine learning experiments, offering flexibility and ease of use for managing hyperparameters and model configurations. While Transformers focuses on providing ready-to-use NLP models, Gin-config is more about streamlining the configuration process for machine learning experiments in general.
Ray is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.
Pros of Ray
- Comprehensive distributed computing framework for scaling AI and ML applications
- Supports various workloads including distributed training, reinforcement learning, and hyperparameter tuning
- Large and active community with extensive documentation and examples
Cons of Ray
- Steeper learning curve due to its broader scope and complexity
- Potentially overkill for simpler projects that don't require distributed computing
- Higher resource requirements for setup and execution
Code Comparison
Ray example:
import ray
@ray.remote
def f(x):
return x * x
futures = [f.remote(i) for i in range(4)]
print(ray.get(futures))
Gin-Config example:
import gin
@gin.configurable
def train(learning_rate=0.01, batch_size=32):
# Training logic here
pass
gin.parse_config_file('config.gin')
train()
Summary
Ray is a powerful distributed computing framework for scaling AI and ML applications, while Gin-Config focuses on dependency injection and configuration management. Ray offers more extensive features for distributed computing but has a steeper learning curve. Gin-Config is simpler and more lightweight, making it suitable for smaller projects or those primarily concerned with configuration management.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Gin Config
Authors: Dan Holtmann-Rice, Sergio Guadarrama, Nathan Silberman Contributors: Oscar Ramirez, Marek Fiser
Gin provides a lightweight configuration framework for Python, based on
dependency injection. Functions or classes can be decorated with
@gin.configurable
, allowing default parameter values to be supplied from a
config file (or passed via the command line) using a simple but powerful syntax.
This removes the need to define and maintain configuration objects (e.g.
protos), or write boilerplate parameter plumbing and factory code, while often
dramatically expanding a project's flexibility and configurability.
Gin is particularly well suited for machine learning experiments (e.g. using TensorFlow), which tend to have many parameters, often nested in complex ways.
This is not an official Google product.
Table of Contents
[TOC]
Basic usage
This section provides a high-level overview of Gin's main features, ordered roughly from "basic" to "advanced". More details on these and other features can be found in the user guide.
1. Setup
Install Gin with pip:
pip install gin-config
Install Gin from source:
git clone https://github.com/google/gin-config
cd gin-config
python -m setup.py install
Import Gin (without TensorFlow functionality):
import gin
Import additional TensorFlow-specific functionality via the gin.tf
module:
import gin.tf
Import additional PyTorch-specific functionality via the gin.torch
module:
import gin.torch
2. Configuring default values with Gin (@gin.configurable
and "bindings")
At its most basic, Gin can be seen as a way of providing or changing default
values for function or constructor parameters. To make a function's parameters
"configurable", Gin provides the gin.configurable
decorator:
@gin.configurable
def dnn(inputs,
num_outputs,
layer_sizes=(512, 512),
activation_fn=tf.nn.relu):
...
This decorator registers the dnn
function with Gin, and automatically makes
all of its parameters configurable. To set ("bind") a value for the
layer_sizes
parameter above within a ".gin" configuration file:
# Inside "config.gin"
dnn.layer_sizes = (1024, 512, 128)
Bindings have syntax function_name.parameter_name = value
. All Python literal
values are supported as value
(numbers, strings, lists, tuples, dicts). Once
the config file has been parsed by Gin, any future calls to dnn
will use the
Gin-specified value for layer_sizes
(unless a value is explicitly provided by
the caller).
Classes can also be marked as configurable, in which case the configuration applies to constructor parameters:
@gin.configurable
class DNN(object):
# Constructor parameters become configurable.
def __init__(self,
num_outputs,
layer_sizes=(512, 512),
activation_fn=tf.nn.relu):
...
def __call__(inputs):
...
Within a config file, the class name is used when binding values to constructor parameters:
# Inside "config.gin"
DNN.layer_sizes = (1024, 512, 128)
Finally, after defining or importing all configurable classes or functions,
parse your config file to bind your configurations (to also permit multiple
config files and command line overrides, see
gin.parse_config_files_and_bindings
):
gin.parse_config_file('config.gin')
Note that no other changes are required to the Python code, beyond adding the
gin.configurable
decorator and a call to one of Gin's parsing functions.
3. Passing functions, classes, and instances ("configurable references")
In addition to accepting Python literal values, Gin also supports passing other
Gin-configurable functions or classes. In the example above, we might want to
change the activation_fn
parameter. If we have registered, say tf.nn.tanh
with Gin (see registering external functions), we can
pass it to activation_fn
by referring to it as @tanh
(or @tf.nn.tanh
):
# Inside "config.gin"
dnn.activation_fn = @tf.nn.tanh
Gin refers to @name
constructs as configurable references. Configurable
references work for classes as well:
def train_fn(..., optimizer_cls, learning_rate):
optimizer = optimizer_cls(learning_rate)
...
Then, within a config file:
# Inside "config.gin"
train_fn.optimizer_cls = @tf.train.GradientDescentOptimizer
...
Sometimes it is necessary to pass the result of calling a specific function or
class constructor. Gin supports "evaluating" configurable references via the
@name()
syntax. For example, say we wanted to use the class form of DNN
from
above (which implements __call__
to "behave" like a function) in the following
Python code:
def build_model(inputs, network_fn, ...):
logits = network_fn(inputs)
...
We could pass an instance of the DNN
class to the network_fn
parameter:
# Inside "config.gin"
build_model.network_fn = @DNN()
To use evaluated references, all of the referenced function or class's
parameters must be provided via Gin. The call to the function or constructor
takes place just before the call to the function to which the result is
passed, In the above example, this would be just before build_model
is called.
The result is not cached, so a new DNN
instance will be constructed for each
call to build_model
.
4. Configuring the same function in different ways ("scopes")
What happens if we want to configure the same function in different ways? For
instance, imagine we're building a GAN, where we might have a "generator"
network and a "discriminator" network. We'd like to use the dnn
function above
to construct both, but with different parameters:
def build_model(inputs, generator_network_fn, discriminator_network_fn, ...):
...
To handle this case, Gin provides "scopes", which provide a name for a specific
set of bindings for a given function or class. In both bindings and references,
the "scope name" precedes the function name, separated by a "/
" (i.e.,
scope_name/function_name
):
# Inside "config.gin"
build_model.generator_network_fn = @generator/dnn
build_model.discriminator_network_fn = @discriminator/dnn
generator/dnn.layer_sizes = (128, 256)
generator/dnn.num_outputs = 784
discriminator/dnn.layer_sizes = (512, 256)
discriminator/dnn.num_outputs = 1
dnn.activation_fn = @tf.nn.tanh
In this example, the generator network has increasing layer widths and 784 outputs, while the discriminator network has decreasing layer widths and 1 output.
Any parameters set on the "root" (unscoped) function name are inherited by
scoped variants (unless explicitly overridden), so in the above example both the
generator and the discriminator use the tf.nn.tanh
activation function. This
works in general for a hierarchy of scopes, e.g., if we are in a scope named
a/b
the config will inherit all the values from scope a
.
5. Full hierarchical configuration {#full-hierarchical}
The greatest degree of flexibility and configurability in a project is achieved
by writing small modular functions and "wiring them up" hierarchically via
(possibly scoped) references. For example, this code sketches a generic training
setup that could be used with the tf.estimator.Estimator
API:
@gin.configurable
def build_model_fn(network_fn, loss_fn, optimize_loss_fn):
def model_fn(features, labels):
logits = network_fn(features)
loss = loss_fn(labels, logits)
train_op = optimize_loss_fn(loss)
...
return model_fn
@gin.configurable
def optimize_loss(loss, optimizer_cls, learning_rate):
optimizer = optimizer_cls(learning_rate)
return optimizer.minimize(loss)
@gin.configurable
def input_fn(file_pattern, batch_size, ...):
...
@gin.configurable
def run_training(train_input_fn, eval_input_fn, estimator, steps=1000):
estimator.train(train_input_fn, steps=steps)
estimator.evaluate(eval_input_fn)
...
In conjunction with suitable external configurables to register TensorFlow
functions/classes (e.g., Estimator
and various optimizers), this could be
configured as follows:
# Inside "config.gin"
run_training.train_input_fn = @train/input_fn
run_training.eval_input_fn = @eval/input_fn
input_fn.batch_size = 64 # Shared by both train and eval...
train/input_fn.file_pattern = ...
eval/input_fn.file_pattern = ...
run_training.estimator = @tf.estimator.Estimator()
tf.estimator.Estimator.model_fn = @build_model_fn()
build_model_fn.network_fn = @dnn
dnn.layer_sizes = (1024, 512, 256)
build_model_fn.loss_fn = @tf.losses.sparse_softmax_cross_entropy
build_model_fn.optimize_loss_fn = @optimize_loss
optimize_loss.optimizer_cls = @tf.train.MomentumOptimizer
MomentumOptimizer.momentum = 0.9
optimize_loss.learning_rate = 0.01
Note that it is straightforward to switch between different network functions, optimizers, datasets, loss functions, etc. via different config files.
6. Additional features
Additional features described in more detail in the user guide include:
- Automatic logging of all configured parameter values (the "operative config"), including TensorBoard integration.
- "Macros", to specify a value used in multiple places within a config, as well as Python-defined constants.
- Module imports and config file inclusion.
- Disambiguation of configurable names via modules.
Best practices
At a high level, we recommend using the minimal feature set required to achieve your project's desired degree of configurability. Many projects may only require the features outlined in sections 2 or 3 above. Extreme configurability comes at some cost to understandability, and the tradeoff should be carefully evaluated for a given project.
Gin is still in alpha development and some corner-case behaviors may be changed in backwards-incompatible ways. We recommend the following best practices:
- Minimize use of evaluated configurable references (
@name()
), especially when combined with macros (where the fact that the value is not cached may be surprising to new users). - Avoid nesting of scopes (i.e.,
scope1/scope2/function_name
). While supported there is some ongoing debate around ordering and behavior. - When passing an unscoped reference (
@name
) as a parameter of a scoped function (some_scope/fn.param
), the unscoped reference gets called in the scope of the function it is passed to... but don't rely on this behavior. - Wherever possible, prefer to use a function or class's name as its configurable name, instead of overriding it. In case of naming collisions, use module names (which are encouraged to be renamed to match common usage) for disambiguation.
- In fact, to aid readability for complex config files, we gently suggest always including module names to help make it easier to find corresponding definitions in Python code.
- When doing "full hierarchical configuration", structure the code to minimize the number of "top-level" functions that are configured without themselves being passed as parameters. In other words, the configuration tree should have only one root.
In short, use Gin responsibly :)
Syntax quick reference
A quick reference for syntax unique to Gin (which otherwise supports
non-control-flow Python syntax, including literal values and line
continuations). Note that where function and class names are used, these may
include a dotted module name prefix (some.module.function_name
).
Syntax | Description |
---|---|
@gin.configurable |
Decorator in Python code that registers a function or class with Gin, wrapping/replacing it with a "configurable" version that respects Gin parameter overrides. A function or class annotated with `@gin.configurable` will have its parameters overridden by any provided configs even when called directly from other Python code. . |
@gin.register |
Decorator in Python code that only registers a function or class with Gin, but does *not* replace it with its "configurable" version. Functions or classes annotated with `@gin.register` will *not* have their parameters overridden by Gin configs when called directly from other Python code. However, any references in config strings or files to these functions (`@some_name` syntax, see below) will apply any provided configuration. |
name.param = value |
Basic syntax of a Gin binding. Once this is parsed, when the
function or class named name is called, it will receive
value as the value for param , unless a
value is explicitly supplied by the caller. Any Python literal may be
supplied as value . |
@some_name |
A reference to another function or class named
some_name . This may be given as the value of a binding, to
supply function- or class-valued parameters. |
@some_name() |
An evaluated reference. Instead of supplying the function
or class directly, the result of calling some_name is
passed instead. Note that the result is not cached; it is recomputed
each time it is required. |
scope/name.param = value |
A scoped binding. The binding is only active when name
is called within scope scope . |
@scope/some_name |
A scoped reference. When this is called, the call will be within
scope scope , applying any relevant scoped bindings. |
MACRO_NAME = value |
A macro. This provides a shorthand name for the expression on the right hand side. |
%MACRO_NAME |
A reference to the macro MACRO_NAME . This has the
effect of textually replacing %MACRO_NAME with whatever
expression it was associated with. Note in particular that the result
of evaluated references are not cached. |
Top Related Projects
Flax is a neural network library for JAX that is designed for flexibility.
An Open Source Machine Learning Framework for Everyone
Tensors and Dynamic neural networks in Python with strong GPU acceleration
DeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective.
🤗 Transformers: State-of-the-art Machine Learning for Pytorch, TensorFlow, and JAX.
Ray is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot