Giter Club home page Giter Club logo

yacs's Introduction

YACS

Introduction

YACS was created as a lightweight library to define and manage system configurations, such as those commonly found in software designed for scientific experimentation. These "configurations" typically cover concepts like hyperparameters used in training a machine learning model or configurable model hyperparameters, such as the depth of a convolutional neural network. Since you're doing science, reproducibility is paramount and thus you need a reliable way to serialize experimental configurations. YACS uses YAML as a simple, human readable serialization format. The paradigm is: your code + a YACS config for experiment E (+ external dependencies + hardware + other nuisance terms ...) = reproducible experiment E. While you might not be able to control everything, at least you can control your code and your experimental configuration. YACS is here to help you with that.

YACS grew out of the experimental configuration systems used in: py-faster-rcnn and Detectron.

Usage

YACS can be used in a variety of flexible ways. There are two main paradigms:

  • Configuration as local variable
  • Configuration as a global singleton

It's up to you which you prefer to use, though the local variable route is recommended.

To use YACS in your project, you first create a project config file, typically called config.py or defaults.py. This file is the one-stop reference point for all configurable options. It should be very well documented and provide sensible defaults for all options.

# my_project/config.py
from yacs.config import CfgNode as CN


_C = CN()

_C.SYSTEM = CN()
# Number of GPUS to use in the experiment
_C.SYSTEM.NUM_GPUS = 8
# Number of workers for doing things
_C.SYSTEM.NUM_WORKERS = 4

_C.TRAIN = CN()
# A very important hyperparameter
_C.TRAIN.HYPERPARAMETER_1 = 0.1
# The all important scales for the stuff
_C.TRAIN.SCALES = (2, 4, 8, 16)


def get_cfg_defaults():
  """Get a yacs CfgNode object with default values for my_project."""
  # Return a clone so that the defaults will not be altered
  # This is for the "local variable" use pattern
  return _C.clone()

# Alternatively, provide a way to import the defaults as
# a global singleton:
# cfg = _C  # users can `from config import cfg`

Next, you'll create YAML configuration files; typically you'll make one for each experiment. Each configuration file only overrides the options that are changing in that experiment.

# my_project/experiment.yaml
SYSTEM:
  NUM_GPUS: 2
TRAIN:
  SCALES: (1, 2)

Finally, you'll have your actual project code that uses the config system. After any initial setup it's a good idea to freeze it to prevent further modification by calling the freeze() method. As illustrated below, the config options can either be used a global set of options by importing cfg and accessing it directly, or the cfg can be copied and passed as an argument.

# my_project/main.py

import my_project
from config import get_cfg_defaults  # local variable usage pattern, or:
# from config import cfg  # global singleton usage pattern


if __name__ == "__main__":
  cfg = get_cfg_defaults()
  cfg.merge_from_file("experiment.yaml")
  cfg.freeze()
  print(cfg)

  # Example of using the cfg as global access to options
  if cfg.SYSTEM.NUM_GPUS > 0:
    my_project.setup_multi_gpu_support()

  model = my_project.create_model(cfg)

Command line overrides

You can update a CfgNode using a list of fully-qualified key, value pairs. This makes it easy to consume override options from the command line. For example:

cfg.merge_from_file("experiment.yaml")
# Now override from a list (opts could come from the command line)
opts = ["SYSTEM.NUM_GPUS", 8, "TRAIN.SCALES", "(1, 2, 3, 4)"]
cfg.merge_from_list(opts)

The following principle is recommended: "There is only one way to configure the same thing." This principle means that if an option is defined in a YACS config object, then your program should set that configuration option using cfg.merge_from_list(opts) and not by defining, for example, --train-scales as a command line argument that is then used to set cfg.TRAIN.SCALES.

Python config files (instead of YAML)

yacs>= 0.1.4 supports loading CfgNode objects from Python source files. The convention is that the Python source must export a module variable named cfg of type dict or CfgNode. See examples using a CfgNode and a dict as well as usage in the unit tests.

yacs's People

Contributors

atranitell avatar patricklabatut avatar pocketpixels avatar ppwwyyxx avatar rbgirshick avatar rizhiy avatar rodrigoberriel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yacs's Issues

Accept all `pyyam`l types as valid types.

I need to use dates in my config.
When I put in my date in the yaml file:

DATE: 2020-01-01

it is automatically converted to datetime.date by yaml.safe_load.
This then breaks the loading since date is not a valid type.

Currently, I have to explicitly state that the value is a string using !!str, this is cumbersome.

I suggest that yacs should support all types supported by pyyaml.

[Feature request] better error messages when types mismatch

If the yaml file containing the configuration options has a type mismatch (e.g., an int instead of a float), the error message is currently not very helpful and it is hard to spot where the error is.

For example

  File "test_net.py", line 57, in main
    cfg.merge_from_file(args.config_file)
  File "/private/home/fmassa/.conda/envs/detectron/lib/python3.6/site-packages/yacs/config.py", line 113, in merge_from_file
    cfg = CfgNode(load_cfg(f))
  File "/private/home/fmassa/.conda/envs/detectron/lib/python3.6/site-packages/yacs/config.py", line 226, in load_cfg
    return _to_cfg_node(cfg_as_dict)
  File "/private/home/fmassa/.conda/envs/detectron/lib/python3.6/site-packages/yacs/config.py", line 258, in _to_cfg_node
    return convert_to_cfg_node(cfg_dict)
  File "/private/home/fmassa/.conda/envs/detectron/lib/python3.6/site-packages/yacs/config.py", line 255, in convert_to_cfg_node
    cfg_node[k] = convert_to_cfg_node(v)
  File "/private/home/fmassa/.conda/envs/detectron/lib/python3.6/site-packages/yacs/config.py", line 250, in convert_to_cfg_node
    assert _valid_type(cfg_dict)
AssertionError

Ideally, it would be good to be explicit on which argument caused the problem, what was its type, and what was the expected type.


EDIT: the error was actually due to a misformated yaml, but the error message was not really very helpful in this case.

Update version in pip

Currently version 0.1.5 is not available through PIP, it would be nice to have the latest version there.

[Feature request] Allow CfgNode to have keys added from yaml files

In my experiments I sometimes need to use a function with many keyword arguments.
Listing all possible kwargs in main config is impractical, since I can't easily determine default values and sometimes there are too many of them.

It would be nice to be able to create CfgNode that allows addition of new attributes from yaml file.

[Feature request] Load YAML with BASE config

Allow constructing a new config file based on an existing one

This feature has been implemented in the config class in repo facebookresearch/fvcore, which uses a special key _BASE_ to specify a base config file.

Such a feature improves the maintainability of large number of similar config files, and helps highlight the difference between similar configs.

Would like to hear your opinion on this. Many thanks.

[Feature request] Add a required parameter utility to throw an error if parameter not provided

This suggestion is very broad but the idea would be to be able to assign an YACS leaf object to deal with required parameters. This concept could be extended to other functionalities like help strings, range checkers, type checkers, etc...

Example:

from yacs.params import required

_C = CfgNode()
_C.LEARNING_RATE = required(type=float)

Let me know if you think it is a good idea and I can start working on a PR.

UnicodeEncodeError: 'ascii' codec can't encode characters in position 21-25: ordinal not in range(128)

File "/Users/lance/anaconda2/lib/python2.7/site-packages/yacs/config.py", line 513, in _check_and_coerce_cfg_value_type
    original_type, replacement_type, original, replacement, full_key

The error occurs in merge_from_file('xxx.yml') when I defined a path='' in the default config and in the xxx.yml setting with path: '你好' in chinese.

I have debug this, it seems that in python 2.x, the yaml package safe_load method will auto convert the str with chinese to unicode, so in this case, str mismatch with unicode.

Although I can redefined path=u'' to solve the problem for python2, but it really not pythonic, so I think it's a small bug. Can you fix the bug for python2.x?

Thanks.

[Feature request] Allow usage of additional types in config

Currently only a few primitive types are allowed as values in config, this is a bit restrictive. For example, I would like to store pixel mean values in the config, currently I would need to use list or tuple. In most cases I would have to convert those values to np.array each time I want to use them, which is inconvenient.

It would be nice if I could use np.array inside config. I think any type which can be represented as a string or in terms of simple types should be available to be used in the config. In case of np.array it can be represented in terms of nested lists in yaml file and convert to array on load.

Why pycocotools has no author, licence info neither a github repo or official website?

Hi and sry for posting it right here.

I came to this gitrepo from the website https://pypi.org/project/pycocotools/ through the maintainer: https://pypi.org/user/rbg/ which seems also to be the maintainer of this project.

Sadly pycoocotools is missing some relevant information as: licence, website, gitrepo, full name author.

Currently the legal use of pycocotools is unclear. Please @rgb provide the necessary information.
Thanks in advance

Freeze not working properly (or almost at all)

I was just trying to test out the freeze function and did something like this:

cfg = get_cfg_defaults()
cfg.freeze()
cfg.merge_from_file('input.yaml')

and it turns out merging from a file is let happen although the config node is allegedly frozen. I looked into the code and this makes sense since setattr (which is the only place that actually checks whether the object is immutable) is never called explicitly, but rather lines like this are used instead. Basically in the current, form frozen is only checked for one-time assignments (like cfg.lr=0.1).

There is another behavior which I doubt is intended: judging by the code, both merge_from_list and _merge_a_into_b descent into the tree structure without passing down the immutability information, so one could freeze the global (root) cfg but then be able to modify a grand child of it since the parent hasn't been frozen. Am I missing something? These two points deem freeze() in its current form almost useless.

Key "ON" interpreted as True?

Hi, I have a yaml file that use ON as key name, but it will be interpreted as True

For example:

SOME_FEATURE:
  ON: False

will be loaded as {SOME_FEATURE: {True: False}} and cause type error.

This might be a bug of ConfigNode.load_cfg method?

Evaluation of strings

value = literal_eval(value)

This part makes yacs inconsistent with yaml.
In yaml if you want a number to be interpreted as a string you can put it in quotes (e.g. 10 vs "10"), but in yacs they will be both converted to numbers.

In which cases is eval used and would it be possible to add an option to disable it?

Allow type mismatch when one type is subclass of the other

This would allow doing the following thing:

class SpecialConfig(CfgNode):
    field1: int = 0
    field2: str = 'foo'


_C = CfgNode()
_C.special = SpecialConfig()


def update_spec_config(spec_config: SpecialConfig):
    config = _C.clone()
    new_config = CfgNode()
    new_config.special = spec_config

    print("Default config:\n\r", config)
    config.merge_from_other_cfg(new_config)
    return config

if __name__ == '__main__':
    spec = dict(field1=1, field2='bar')
    config = update_spec_config(SpecialConfig(spec))
    print("New config:")
    print(config)

At present, this won't work, yielding the error:

Traceback (most recent call last):
  File "yacs_config.py", line 23, in <module>
    config = update_spec_config(SpecialConfig(spec))
  File "yacs_config.py", line 18, in update_spec_config
    config.merge_from_other_cfg(new_config)
  File "/home/manifold/miniconda3/lib/python3.8/site-packages/yacs/config.py", line 217, in merge_from_other_cfg
    _merge_a_into_b(cfg_other, self, self, [])
  File "/home/manifold/miniconda3/lib/python3.8/site-packages/yacs/config.py", line 456, in _merge_a_into_b
    v = _check_and_coerce_cfg_value_type(v, b[k], k, full_key)
  File "/home/manifold/miniconda3/lib/python3.8/site-packages/yacs/config.py", line 510, in _check_and_coerce_cfg_value_type
    raise ValueError(
ValueError: Type mismatch (<class '__main__.SpecialConfig'> vs. <class 'yacs.config.CfgNode'>) with values ( vs. field1: 1
field2: bar) for config key: special

Wouldn't it make sense to allow this case, because SpecialConfig is a subclass of CfgNode?
It's not obvious that this is useful, because I could do without the subclass. However having a class allows for typed access to values and a constructor with default values

Update "child" params when "parent" params are modified

Thank you for your great software! I often find myself needing a pattern where I define some "child" parameter that depends on other "parent" parameters; is it possible/is there a way to ensure that a "child" parameter (e.g., B) that depends on other parameters (e.g., A) gets updated when the "parent" parameter (A) is updated?

For example:

# in config.py
from yacs.config import CfgNode as CN
_C  = CN()

_C.A = 1.0
_C.B = 2.0*_C.A
cfg = _C
# in main.py
from config import cfg

if __name__ == "__main__":
  cfg.merge_from_list(["A", 3.0])
  cfg.B # <-------- Expected 6.0, it is 2.0

Thanks,
Andrea

Can i load yaml file directly ?

I want load yaml file directly, code like
cfg = load_cfg('***.yaml')
but i got an error, i.e. no corresponding CfgNode exist.

Writing and reading to disk

Sorry for opening this question as an issue. I am pretty new to YAML.

I am able to get yacs working on my environment. However, I am not sure what's the best way to dump a CfgNode object onto disk in YAML and then read back the YAML file to construct the CfgNode object again. Any guideline?

how to overide command-line args?

i usually use yacs with config file as below:
python3 run.py --cfg /home/plask/seoha/kubeflow/metrabs/configs/aist_only_hrnet_new_aug_all.yaml

eg.
TRAIN:
AUGMENT : true
OPTIM: "ranger"
LR: 0.0001
BACKBONE_TRAIN: true
BATCH_SIZE : 16
OCCLUDE_AUGMENT : false
NUM_STEPS_START_ABS : 10000000

but i wand to override some args like

python3 run.py --cfg /home/plask/seoha/kubeflow/metrabs/configs/aist_only_hrnet_new_aug_all.yaml --train-optim "adam" --train-lr 1e-4

i have to override with command-line(not modifying yaml file)
how can i do this?

Allow upcast when merging

I am trying to make a template config for my projects.
Unfortunately, currently I can't find a way to do it properly.
When I try to merge yaml file into config it complains of type mismatch.

Example:

from yacs.config import CfgNode

class Template(CfgNode):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.TRAIN = CfgNode()
        self.TRAIN.LR = 1

class RepoCfg(Template):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.DATA = CfgNode()
        self.DATA.SOURCE = ""

If I try to use RepoCfg.load_cfg(cfg_path), it creates all nodes as RepoCfg when they are declared as CfgNode.

What is the advantage of using yacs instead of traditional yaml files and yaml parsing?

I know. A stupid and naive question. But I am a beginner and I am struggling to find a major use case for my workflow so maybe if the advantages can be spelled out, that would be even better!

Also, does YACS support the notion of assigning python objects and variables to the configuration parameters (unlike YAML)? For example, if one wanted to specify a callback function as a parameter, is it possible to do this with YACS (again of course, you can't specify this in YAML and the way around it is to make a hacky getattr call).

AssertionError: Invalid type <enum 'TrianingMode'> for key MODE; valid types = {<class 'tuple'>, <class 'list'>, <class 'int'>, <class 'float'>, <class 'bool'>, <class 'str'>, <class 'NoneType'>}

I am going to use ENUM class in my config file and I got this error. my code is

from utils import MetricsType, TrianingMode
from yacs.config import CfgNode as CN
# -----------------------------------------------------------------------------
# Config definition
# -----------------------------------------------------------------------------

_C = CN()

_C.MODEL = CN()
_C.MODEL.NUM_CLASSES = 2

# -----------------------------------------------------------------------------
# INPUT
# -----------------------------------------------------------------------------
_C.INPUT = CN()

# -----------------------------------------------------------------------------
# Dataset
# -----------------------------------------------------------------------------
_C.DATASET = CN()
_C.DATASET.DATASET_ADDRESS = '../data/bank/bank.csv'
_C.DATASET.DATASET_BRIEF_DESCRIPTION = '../data/bank/description.txt'
_C.DATASET.TARGET = 'y'
# ---------------------------------------------------------------------------- #
# Models
# ---------------------------------------------------------------------------- #
_C.SVM = CN()
_C.SVM.NAME = 'SVM'
_C.SVM.MODE = TrianingMode.CLASSIFICATION
_C.SVM.KERNEL = 'rbf'
_C.SVM.GAMMA = 'scale'
_C.SVM.VERBOSE = True
# ---------------------------------------------------------------------------- #
# metric
# ---------------------------------------------------------------------------- #
_C.METRIC = CN()

_C.METRIC = MetricsType.F1_SCORE_MACRO
_C.CONFUSION_MATRIX = True


# ---------------------------------------------------------------------------- #
# Misc options
# ---------------------------------------------------------------------------- #
# _C.OUTPUT_DIR = "../outputs"

how to merge two config?

config1.py:

from yacs.config import CfgNode as CN
_C = CN()
_C.SYSTEM = CN()
# Number of GPUS to use in the experiment
_C.SYSTEM.NUM_GPUS = 8
# Number of workers for doing things
_C.SYSTEM.NUM_WORKERS = 4

config2.py:

from yacs.config import CfgNode as CN
_C = CN()
_C.TRAIN = CN()
# A very important hyperparameter
_C.TRAIN.HYPERPARAMETER_1 = 0.1
# The all important scales for the stuff
_C.TRAIN.SCALES = (2, 4, 8, 16)

how meger two config?

Git tags for releases

Would it be possible to add git tags for releases?

I'm looking to package this for Nix, and would like to be able to run the tests on the fetched source during the build. The PyPI source tarball does not include the required examples directory, so a git tag would be nice.

[Feature request] Add pretty-print to __repr__

I think it would be nice to have __repr__ of CfgNode have a prettier print, which has one field per line with proper indentation. It currently prints everything in a single line (as is the default for dict), and makes searching for specific arguments a bit difficult.
While it's possible to use external libraries like pprint to achieve this, I think that having it built-in would be valuable.

A possible draft implementation:

def _addindent(s_, numSpaces):
    s = s_.split("\n")
    # don't do anything for single-line stuff
    if len(s) == 1:
        return s_
    first = s.pop(0)
    s = [(numSpaces * " ") + line for line in s]
    s = "\n".join(s)
    s = first + "\n" + s
    return s

class CfgNode(dict):
   ...
    def __repr__(self):
        r = object.__repr__(self)
        attrs = {k: v for k, v in self.items()}
        if attrs:
            r += ": \n"
            s = []
            for k, v in attrs.items():
                attr_str = "{}: {}".format(repr(k), repr(v))
                attr_str = _addindent(attr_str, 2)
                s.append(attr_str)
            r += "  " + "\n  ".join(s)
        return r

Alias `CfgNode` as `CN`

In the example CfgNode is imported as CN.
In my code, I also do the same, since it saves a bit of space.

Unfortunately, my IDE (PyCharm) is not able to automatically recognise this alias and I have to manually import CfgNode every time, which becomes tedious after a while if you use it a lot.

I suggest adding CN as a proper alias, so that IDE can detect it.

Name clash

So far I haven't seen any major variables in other packages named CN, the only conflict is https://pypi.org/project/cn/#description, but it appears to be empty and dead.

Bug report: `cfg.merge_from_file("experiment.yaml")`

If the YAML file has repeated "parent" tag, YACS only merges the final set of parameters. For example, only the second list of HYPER are merged.

HYPER: 
    BATCH_SIZE: 10
SYS: 
    SOME_CONSTANT: 3
HYPER: 
   LEARNING_RATE: 1e-3

As a result, only LEARNING_RATE: 1e-3 is applied.

Can we convert CfgNode to yaml file or anyother format?

I have a config node which has certain mode config nodes,
is there anyway I can get a yaml file out of it?
CfgNode({'USE_CUDA': True, 'NO_GPU': False, 'RANDOMIZATION': CfgNode({'SEED': 5}), 'BACKBONE': CfgNode({'MODEL_NAME': 'resnet50', 'RESNET_STOP_LAYER': 4}), 'INPUT': CfgNode({'MEAN': (0.485, 0.456, 0.406), 'STD': (0.229, 0.224, 0.225)}), 'DTYPE': CfgNode({'FLOAT': 'torch.cuda.FloatTensor', 'LONG': 'torch.cuda.LongTensor'}), 'ANCHORS': CfgNode({'ASPECT_RATIOS': (1, 1.5, 2), 'ANCHOR_SCALES': (192, 128, 256), 'N_ANCHORS_PER_LOCATION': 9}), 'RPN': CfgNode({'OUT_CHANNELS': 512, 'LAYER_CHANNELS': (512, 256, 128), 'N_ANCHORS_PER_LOCATION': 9, 'SOFTPLUS_BETA': 1, 'SOFTPLUS_THRESH': 20, 'CONV_MEAN': 0, 'CONV_VAR': 0.01, 'BIAS': 0, 'UNCERTAIN_MEAN': 0, 'UNCERTAIN_VAR': 0.01, 'UNCERTAIN_BIAS': 1.0, 'ACTIVATION_ALPHA': 1}), 'TRAIN': CfgNode({'OPTIM': 'adam', 'LR': 1e-05, 'MOMENTUM': 0.09, 'EPOCHS': 40, 'MILESTONES': (10, 18, 25), 'DSET_SHUFFLE': True, 'BATCH_SIZE': 1, 'FREEZE_BACKBONE': False, 'LR_DECAY': 0.1, 'LR_DECAY_EPOCHS': 50, 'SAVE_MODEL_EPOCHS': 5, 'TRAIN_TYPE': 'probabilistic', 'DATASET_DIVIDE': 0.9, 'NUSCENES_IMAGE_RESIZE_FACTOR': 1.5, 'CLASS_LOSS_SCALE': 5.0, 'FAKE_BATCHSIZE': 27})})

`dump()` will fail if some key is an integer

Running:

from yacs.config import CfgNode as CN
cfg = CN()
cfg["MYKEY"] = CN()
cfg.MYKEY[1] = "a_sub_value"
print(cfg.dump())

Gives:

Traceback (most recent call last):
  File "/Users/zeltserj/yacs/example/main.py", line 5, in <module>
    print(cfg.dump())
  File "/Users/zeltserj/yacs/yacs/config.py", line 206, in dump
    self_as_dict = convert_to_dict(self, [])
  File "/Users/zeltserj/yacs/yacs/config.py", line 203, in convert_to_dict
    cfg_dict[k] = convert_to_dict(v, key_list + [k])
  File "/Users/zeltserj/yacs/yacs/config.py", line 203, in convert_to_dict
    cfg_dict[k] = convert_to_dict(v, key_list + [k])
  File "/Users/zeltserj/yacs/yacs/config.py", line 196, in convert_to_dict
    ".".join(key_list), type(cfg_node), _VALID_TYPES
TypeError: sequence item 1: expected str instance, int found

running with "1" instead, works fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.