Code Monkey home page Code Monkey logo

nn-meter's Introduction

nn-Meter is a novel and efficient system to accurately predict the inference latency of DNN models on diverse edge devices. The key idea is dividing a whole model inference into kernels, i.e., the execution units of fused operators on a device, and conduct kernel-level prediction. We currently evaluate four popular platforms on a large dataset of 26k models. It achieves 99.0% (mobile CPU), 99.1% (mobile Adreno 640 GPU), 99.0% (mobile Adreno 630 GPU), and 83.4% (Intel VPU) prediction accuracy.

The current supported hardware and inference frameworks:

Device Framework Processor +-10% Accuracy Hardware name
Pixel4 TFLite v2.1 CortexA76 CPU 99.0% cortexA76cpu_tflite21
Mi9 TFLite v2.1 Adreno 640 GPU 99.1% adreno640gpu_tflite21
Pixel3XL TFLite v2.1 Adreno 630 GPU 99.0% adreno630gpu_tflite21
Intel Movidius NCS2 OpenVINO2019R2 Myriad VPU 83.4% myriadvpu_openvino2019r2

nn-Meter has achieved the Mobisys 21 Best Paper Award! For more details, please check out paper:

nn-Meter: towards accurate latency prediction of deep-learning model inference on diverse edge devices

Who should consider using nn-Meter

  • Those who want to get the DNN inference latency on mobile and edge devices with no deployment efforts on real devices.
  • Those who want to run hardware-aware NAS with NNI.
  • Those who want to build latency predictors for their own devices (Documents of nn-Meter builder).
  • Those who want to use the 26k latency benchmark dataset.

Installation

Currently nn-Meter has been tested on Linux and Windows system. Windows 10, Ubuntu 16.04 and 20.04 with python 3.6.10 are tested and supported. Please first install python3 before nn-Meter installation. Then nn-Meter could be installed by running:

pip install nn-meter

nn-meter==2.0 has been released now.

If you want to try latest code, please install nn-Meter from source code. First git clone nn-Meter package to local:

git clone [email protected]:microsoft/nn-Meter.git
cd nn-Meter

Then simply run the following pip install in an environment that has python >= 3.6. The command will complete the automatic installation of all necessary dependencies and nn-Meter.

pip install .

nn-Meter is a latency predictor of models with type of Tensorflow, PyTorch, Onnx, nn-meter IR graph and NNI IR graph. To use nn-Meter for specific model type, you also need to install corresponding required packages. The well tested versions are listed below:

Testing Model Type Requirements
Tensorflow tensorflow==2.6.0
Torch torch==1.9.0, torchvision==0.10.0, (alternative)[onnx>=1.9.0, onnx-simplifier==0.3.6] or [nni>=2.4][1]
Onnx onnx==1.9.0
nn-Meter IR graph ---
NNI IR graph nni>=2.4

[1] Please refer to nn-Meter Usage for more information.

Please also check the versions of numpy and scikit_learn. The different versions may change the prediction accuracy of kernel predictors.

The stable version of wheel binary package will be released soon.

Usage

To apply for hardware latency prediction, nn-Meter provides two types of interfaces:

  • command line nn-meter after nn-meter installation.
  • Python binding provided by the module nn_meter

Here is a summary of supported inputs of the two methods.

Testing Model Type Command Support Python Binding
Tensorflow Checkpoint file dumped by tf.saved_model() and end with .pb Checkpoint file dumped by tf.saved_model and end with .pb
Torch Models in torchvision.models Object of torch.nn.Module
Onnx Checkpoint file dumped by torch.onnx.export() or onnx.save() and end with .onnx Checkpoint file dumped by onnx.save() or model loaded by onnx.load()
nn-Meter IR graph Json file in the format of nn-Meter IR Graph dict object following the format of nn-Meter IR Graph
NNI IR graph - NNI IR graph object

In both methods, users could appoint predictor name and version to target a specific hardware platform (device). Currently, nn-Meter supports prediction on the following four configs:

Predictor (device_inferenceframework) Processor Category Version
cortexA76cpu_tflite21 CPU 1.0
adreno640gpu_tflite21 GPU 1.0
adreno630gpu_tflite21 GPU 1.0
myriadvpu_openvino2019r2 VPU 1.0

Users can get all predefined predictors and versions by running

# to list all predefined predictors
nn-meter --list-predictors 

Predict latency of saved CNN model

After installation, a command named nn-meter is enabled. To predict the latency for a CNN model with a predefined predictor in command line, users can run the following commands (sample models can be downloaded here)

# for Tensorflow (*.pb) file
nn-meter predict --predictor <hardware> [--predictor-version <version>] --tensorflow <pb-file_or_folder> 
# Example Usage
nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --tensorflow mobilenetv3small_0.pb 

# for ONNX (*.onnx) file
nn-meter predict --predictor <hardware> [--predictor-version <version>] --onnx <onnx-file_or_folder>
#Example Usage
nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --onnx mobilenetv3small_0.onnx 

# for torch model from torchvision model zoo (str)
nn-meter predict --predictor <hardware> [--predictor-version <version>] --torchvision <model-name> <model-name>... 
#Example Usage
nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --torchvision mobilenet_v2

# for nn-Meter IR (*.json) file
nn-meter predict --predictor <hardware> [--predictor-version <version>] --nn-meter-ir <json-file_or_folder> 
#Example Usage
nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --nn-meter-ir mobilenetv3small_0.json 

--predictor-version <version> arguments is optional. When the predictor version is not specified by users, nn-meter will use the latest version of the predictor.

nn-Meter can support batch mode prediction. To predict latency for multiple models in the same model type once, user should collect all models in one folder and state the folder after --[model-type] liked argument.

It should also be noted that for PyTorch model, nn-meter can only support existing models in torchvision model zoo. The string followed by --torchvision should be exactly one or more string indicating name(s) of some existing torchvision models. To apply latency prediction for torchvision model in command line, onnx and onnx-simplifier packages are required.

Convert to nn-Meter IR Graph

Furthermore, users may be interested to convert tensorflow pb-file or onnx file to nn-Meter IR graph. Users could convert nn-Meter IR graph and save to .json file be running

# for Tensorflow (*.pb) file
nn-meter get_ir --tensorflow <pb-file> [--output <output-name>]

# for ONNX (*.onnx) file
nn-meter get_ir --onnx <onnx-file> [--output <output-name>]

Output name is default to be /path/to/input/file/<input_file_name>_<model-type>_ir.json if not specified by users.

Use nn-Meter in your python code

After installation, users can import nn-Meter in python code

from nn_meter import load_latency_predictor

predictor = load_latency_predictor(hardware_name, hardware_predictor_version) # case insensitive in backend

# build your model (e.g., model instance of torch.nn.Module)
model = ... 

lat = predictor.predict(model, model_type) # the resulting latency is in unit of ms

By calling load_latency_predictor, user selects the target hardware and loads the corresponding predictor. nn-Meter will try to find the right predictor file in ~/.nn_meter/data. If the predictor file doesn't exist, it will download from the Github release.

In predictor.predict(), the allowed items of the parameter model_type include ["pb", "torch", "onnx", "nnmeter-ir", "nni-ir"], representing model types of tensorflow, torch, onnx, nn-meter IR graph and NNI IR graph, respectively.

<span id="torch-model-converters"> For Torch models, the shape of feature maps is unknown merely based on the given network structure, which is, however, significant parameters in latency prediction. Therefore, torch model requires a shape of input tensor for inference as a input of predictor.predict(). Based on the given input shape, a random tensor according to the shape will be generated and used. Another thing for Torch model prediction is that users can install the onnx and onnx-simplifier packages for latency prediction (referred to as Onnx-based latency prediction for torch model), or alternatively install the nni package (referred to as NNI-based latency prediction for torch model). Note that the nni option does not support command line calls. In addition, if users use nni for latency prediction, the PyTorch modules should be defined by the nn interface from NNI import nni.retiarii.nn.pytorch as nn (view NNI doc for more information), and the parameter apply_nni should be set as True in the function predictor.predict(). Here is an example of NNI-based latency prediction for Torch model:

import nni.retiarii.nn.pytorch as nn
from nn_meter import load_latency_predictor

predictor = load_latency_predictor(...)

# build your model using nni.retiarii.nn.pytorch as nn
model = nn.Module ...

input_shape = (1, 3, 224, 224)
lat = predictor.predict(model, model_type='torch', input_shape=input_shape, apply_nni=True) 

The Onnx-based latency prediction for torch model is stable but slower, while the NNI-based latency prediction for torch model is unstable as it could fail in some case but much faster compared to the Onnx-based model. The Onnx-based model is set as the default one for Torch model latency prediction in nn-Meter. Users could choose which one they preferred to use according to their needs.

Users could view the information all built-in predictors by list_latency_predictors or view the config file in nn_meter/configs/predictors.yaml.

Users could get a nn-Meter IR graph by applying model_file_to_graph and model_to_graph by calling the model name or model object and specify the model type. The supporting model types of model_file_to_graph include "onnx", "pb", "torch", "nnmeter-ir" and "nni-ir", while the supporting model types of model_to_graph include "onnx", "torch" and "nni-ir".

nn-Meter Builder

nn-Meter builder is an open source tool for users to build latency predictor on their own devices. There are three main parts in nn-Meter builder:

backend: the module of connecting backends;

backend_meta: the meta tools related to backend. Here we provide the fusion rule tester to detect fusion rules for users' backend;

kernel_predictor_builder: the tool to build different kernel latency predictors.

Users could get access to nn-Meter builder by calling nn_meter.builder. For more details to use nn-Meter builder, please check the document of nn-Meter builder.

Hardware-aware NAS by nn-Meter and NNI

To empower affordable DNN on the edge and mobile devices, hardware-aware NAS searches both high accuracy and low latency models. In particular, the search algorithm only considers the models within the target latency constraints during the search process.

Currently we provide two examples of hardware-aware NAS, including end-to-end multi-trial NAS which is a random search algorithm on SPOS NAS search space, and the popular ProxylessNAS, which is a one-shot NAS algorithm with hardware-efficient loss function. More examples of other widely-used hardware-aware NAS and model compression algorithms are coming soon.

Multi-trial SPOS Demo

To run multi-trail SPOS demo, NNI should be installed through source code by following NNI Doc

python setup.py develop

Then run multi-trail SPOS demo:

python ${NNI_ROOT}/examples/nas/oneshot/spos/multi_trial.py

How the demo works

Refer to NNI Doc for how to perform NAS by NNI.

To support hardware-aware NAS, you first need a Strategy that supports filtering the models by latency. We provide such a filter named LatencyFilter in NNI and initialize a Random strategy with the filter:

simple_strategy = strategy.Random(model_filter=LatencyFilter(threshold=100, predictor=base_predictor))

LatencyFilter will predict the models' latency by using nn-Meter and filter out the models whose latency with the given predictor are larger than the threshold (i.e., 100 in this example). You can also build your own strategies and filters to support more flexible NAS such as sorting the models according to latency.

Then, pass this strategy to RetiariiExperiment:

exp = RetiariiExperiment(base_model, trainer, strategy=simple_strategy)

exp_config = RetiariiExeConfig('local')
...
exp_config.dummy_input = [1, 3, 32, 32]

exp.run(exp_config, port)

In exp_config, dummy_input is required for tracing shape info.

ProxylessNAS Demo

To run the one-shot ProxylessNAS demo, users can run the NNI ProxylessNAS training demo:

python ${NNI_ROOT}/examples/nas/oneshot/proxylessnas/main.py --applied_hardware <hardware> --reference_latency <reference latency (ms)>

How the demo works

Refer to NNI Doc for how to perform NAS by NNI.

ProxylessNAS currently builds a lookup table, that stores the measured latency of each candidate building block in the search space. The latency sum of all building blocks in a candidate model will be treated as the model inference latency. With leveraging nn-Meter in NNI, users can apply ProxylessNAS to search efficient DNN models on more types of edge devices. In NNI implementation, a HardwareLatencyEstimator predicts expected latency for the mixed operation based on the path weight of ProxylessLayerChoice. To call nn-Meter in NNI ProxylessNAS, users can add the arguments of "--applied_hardware <hardware> --reference_latency <reference latency (ms)>" in the example.

Benchmark Dataset

To evaluate the effectiveness of a prediction model on an arbitrary DNN model, we need a representative dataset that covers a large prediction scope. nn-Meter collects and generates 26k CNN models. (Please refer the paper for the dataset generation method.)

We release the dataset, and provide an interface of nn_meter.dataset for users to get access to the dataset. Users can also download the data from the Download Link on their own.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

License

The entire codebase is under MIT license

The dataset is under Open Use of Data Agreement

Citation

If you find that nn-Meter helps your research, please consider citing it:

@inproceedings{nnmeter,
    author = {Zhang, Li Lyna and Han, Shihao and Wei, Jianyu and Zheng, Ningxin and Cao, Ting and Yang, Yuqing and Liu, Yunxin},
    title = {nn-Meter: Towards Accurate Latency Prediction of Deep-Learning Model Inference on Diverse Edge Devices},
    year = {2021},
    publisher = {ACM},
    address = {New York, NY, USA},
    url = {https://doi.org/10.1145/3458864.3467882},
    doi = {10.1145/3458864.3467882},
    booktitle = {Proceedings of the 19th Annual International Conference on Mobile Systems, Applications, and Services},
    pages = {81–93},
}

@misc{nnmetercode,
    author = {Microsoft Research nn-Meter Team},
    title = {nn-Meter: Towards Accurate Latency Prediction of Deep-Learning Model Inference on Diverse Edge Devices},
    year = {2021},
    url = {https://github.com/microsoft/nn-Meter},
}

nn-meter's People

Contributors

administrator2992 avatar dependabot[bot] avatar forchapeatl avatar jiahangxu avatar kaleid-liner avatar lynazhang avatar marstechhan avatar microsoftopensource avatar mydmdm avatar ultmaster avatar xuan301 avatar zhiqwang 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

nn-meter's Issues

Refine setup process 2

  • support batch mode in cmd line auch as nn-meter --onnx <file-or-folder> --predictor <predictor-name>
    • in code: predictor-name; in doc: hardware; in readme: device + inference framework
    • refine integration-test by batch mode
  • complete IR model test
    • install Ubuntu to support nni development and install the latest nni package
      • reset python env in Ubuntu
      • nni install
      • test nni multi-trail.py in nni
    • fix nni-ir model in nn-meter
    • refine readme.md and give an instruction of test
    • report an issue in nni found when testing nn-Meter
    • add the integration test of nni-ir graph (found that the current nni does not support nn-Meter module, maybe we should waiting for the next release of nni)
  • refine the API and model type list in nn-meter.py
    • change model type of --torch to --torchvision
    • change model type to --nni-ir and --nnmeter-ir
    • predict torch model by calling its name
    • support batch mode for torch model
    • add --torchvision test in integration_test_torch.py (intend to run parallel to save time)
    • refine readme.md
    • change torchvision type to torch considering the nn.Module in python binding
  • add a new usage --getir to get nn-meter ir model for tensorflow and onnx model
    • --getir usage testing
    • add --getir usage test in integration_test.py
    • edit README.md
  • edit the API in NNI (remove default config)
    • add nn-meter in related projects of nni
    • change 'nni' to 'nni-ir'
  • arrange docs
  • public
    • open a PR to refresh the data release link in config
  • add hardware device attribution in config/predictors.yaml, and refine the hard code in predictor loading (category: cpu)
  • add cache in integration test

Why used [kernel_extent_w, kernel_extent_h] as k_size?

https://github.com/YuqiData-UW/nn-Meter/blob/4a00b043e7ad5ed37eb1946dc67b55107abdca75/nn_meter/ir_converter/frozenpb_converter/shape_inference.py#L469

{'inbounds': ['model/LAYER_0_pad/Pad', 'model/LAYER_0/Conv2D/ReadVariableOp'], 'attr': {'name': 'model/LAYER_0/Conv2D', 'type': 'Conv2D', 'output_shape': [[-1, 226, 226, 64]], 'attr': {'dilations': [1, 1], 'strides': [1, 1], 'padding': b'VALID', 'data_format': b'NCHW', 'kernel_shape': [3, 3], 'weight_shape': [3, 3, 3, 64], 'pads': [0, 0, 0, 0]}, 'input_shape': [[-1, 226, 226, 3]]}, 'outbounds': ['model/LAYER_0/BiasAdd']}

I got this result from Con2D, shouldn't the output_shape be [[-1, 224, 224, 64]] since the padding is VALID? If I replace [kernel_extent_w, kernel_extent_h] with k_size, the output_shape can be [[-1, 224, 224, 64]].

Formula (1) in nn_meter/builder/backend_meta/fusion_rule_tester/generate_testcase.py

Hi, @JiahangXu , in nn_meter/builder/backend_meta/fusion_rule_tester/generate_testcase.py, I guess the 'or' should be 'and', because 'or' means $min(T_{op_1}\ ,\ T_{op_2}\ ,\ T_{op_1}+T_{op_2}\ ,\ T_{op_1op_2})$, 'and' means $min(T_{op_1}\ ,\ T_{op_2})$.

secondary_op_lat = min(lat for op, lat in self.latency.items() if op != 'block' or op != self.false_case)

Thanks & Regards,
X. Zhang

NotImplementedError

Hello,

I am building fusion rule tester for a Pytorch model, when I am running tests/unit_test/test_fusion_rule_detector.py, it shows the NotImplementedError:
Screenshot from 2024-02-06 00-15-43

And when I go to the nn_meter/builder/backend_meta/fusion_rule_tester/build_torch_models.py, it shows like follows:
def _model_block(self):
raise NotImplementedError

def _model_relu_relu(self):
    raise NotImplementedError

def _model_dwconv_relu_relu(self):
    raise NotImplementedError

def _model_relu_dwconv(self):
    raise NotImplementedError

def _model_dwconv_relu(self):
    raise NotImplementedError

def _model_dwconv(self):
    raise NotImplementedError

So could you please tell me how to solve this error?

Question on Padding Calculation for Building PyTorch Networks

Hi. I am confused about this tiny piece of utility function:

According to PyTorch:
$$h_o = \lfloor \frac{h_i + 2p - k}{s} + 1 \rfloor,$$
where $h_i, h_o$ specify the input and output height, $p$ specifies the padding, $k$ specifies the kernel size, and $s$ specifies the stride. The equation can be transformed as:
$$p \rightarrow \frac{s(h_o - 1) + k - h_i}{2} = \frac{s h_o - h_i + k - s}{2} = \frac{k - (h_i \% s) - s}{2}.$$

From my understanding, the comment in the function implies that $s h_o$ and $h_i$ should be close to each other ($h_o = h_i // s$) in order to avoid large calculated padding results. I also understand that due to the upper bound, it will get complicated to get the exact $p$ to meet the requirement. My question here is about the code implementation of [this line]:(https://github.com/microsoft/nn-Meter/blob/d0c9cef65c3dd1f703ab9ca177f44ad988f52e5e/nn_meter/builder/nn_modules/torch_networks/utils.py#L26):

Shouldn't the calculation subtract an extra $s$ (i.e., pad = max(ks - (hw % s) - s, 0))?

For confirmation, here is some test code with example ($k=11, s=2, h=3$):

>>> k = 11
>>> s = 2
>>> h = 3
# test the function 
>>> p0 = get_padding(ks=k, s=s, hw=h)
>>> p0
5
# calculate directly using the inferred equation
>>> p1 = (k - (h % s) - s) / 2
>>> p1
4.0
# for confirmation, calculate h_o with p0 and p1 (expected to be h // s)
>>> (h + 2 * p0 - k) / s + 1
2.0
>>> (h + 2 * p1 - k) / s + 1
1.0
>>> h // s
1

in the next two rounds, the accuracy rate drops

Hi, I am using nn-meter to train a dwconv-bn-renlu predictor on a custom backend with the configuration in the image below. In the first round of training, the accuracy rate can reach about 90%, but in the next two rounds, the accuracy rate drops. Want to ask for leave Have you ever encountered this situation? And how to solve it?
image

Why set output_shape to 0?

I got an input_shape [-1, 333, 333, 2048] and `reduction_indices': [2, 3], it would produce [-1, 0]. Is this expected?

Example:

# graph['model/LAYER_172/Mean']
{'inbounds': ['model/LAYER_171/Relu', 'model/LAYER_172/Mean/reduction_indices'], 'attr': {'name': 'model/LAYER_172/Mean', 'type': 'Mean', 'output_shape': [[-1, 0]], 'attr': {'reduction_indices': [2, 3]}, 'input_shape': [[-1, 0]]}, 'outbounds': ['model/LAYER_172_EXPAND1/ExpandDims']}
(Pdb) graph['model/LAYER_171/Relu']

# graph['model/LAYER_171/Relu']
{'inbounds': ['model/LAYER_170/add'], 'attr': {'name': 'model/LAYER_171/Relu', 'type': 'Relu', 'output_shape': [[-1, 0]], 'attr': {}, 'input_shape': [[-1, 333, 333, 2048]]}, 'outbounds': ['model/LAYER_172/Mean']}

Issues Test Cases on Customized Backend

Hello, i have issues when trying test cases on customized backend. actually i copy tflitecpu backend for my customized backend but cause i want to implement in my laptop (using Linux Environtment). so i edited the shell commands and profiler commands as needed.

MyProfiler :

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import os
from nn_meter.builder.backends import BaseProfiler

class TFLiteProfiler(BaseProfiler):
    use_gpu = None

    def __init__(self, dst_kernel_path, benchmark_model_path, graph_path='', dst_graph_path='', num_threads=1, num_runs=50, warm_ups=10):
        """
        @params:
        graph_path: graph file. path on host server
        dst_graph_path: graph file. path on device
        kernel_path: dest kernel output file. path on device
        benchmark_model_path: path to benchmark_model
        """
        self._graph_path = graph_path
        self._dst_graph_path = dst_graph_path
        self._dst_kernel_path = dst_kernel_path
        self._benchmark_model_path = benchmark_model_path
        self._num_threads = num_threads
        self._num_runs = num_runs
        self._warm_ups = warm_ups

    def profile(self, graph_path, preserve = False, clean = True, close_xnnpack = False, **kwargs):
        """
        @params:
        preserve: tflite file exists in remote dir. No need to push it again.
        clean: remove tflite file after running.
        """
        model_name = os.path.basename(graph_path)
        remote_graph_path = os.path.join(self._dst_graph_path, model_name)
        kernel_cmd = f'--kernel_path={self._dst_kernel_path}' if self._dst_kernel_path else ''
        close_xnnpack_cmd = f'--use_xnnpack=false' if close_xnnpack else ''

        try:
            kernel_cmd = f'--kernel_path={self._dst_kernel_path}' if self._dst_kernel_path else ''
            close_xnnpack_cmd = f'--use_xnnpack=false' if close_xnnpack else ''
            res = os.system(f' {self._benchmark_model_path} {kernel_cmd} {close_xnnpack_cmd}' \
                               f' --num_threads={self._num_threads}' \
                               f' --num_runs={self._num_runs}' \
                               f' --warmup_runs={self._warm_ups}' \
                               f' --graph={remote_graph_path}' \
                               f' --enable_op_profiling=true' \
                               f' --use_gpu={"true" if self.use_gpu else "false"}')
        except:
            raise
        finally:
            if clean:
                os.system(f"rm {remote_graph_path}")

        return res

MyBackend :

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import os
import shutil
import logging
from nn_meter.builder.backends import BaseBackend
from nn_meter.utils.path import get_filename_without_ext
logging = logging.getLogger("nn-Meter")

class TFLiteBackend(BaseBackend):
    parser_class = None
    profiler_class = None

    def update_configs(self):
        """update the config parameters for TFLite platform
        """
        super().update_configs()
        self.profiler_kwargs.update({
            'dst_graph_path': self.configs['REMOTE_MODEL_DIR'],
            'benchmark_model_path': self.configs['BENCHMARK_MODEL_PATH'],
            'dst_kernel_path': self.configs['KERNEL_PATH']
        })

    def convert_model(self, model_path, save_path, input_shape=None):
        """convert the Keras model instance to ``.tflite`` and return model path
        """
        import tensorflow as tf
        model_name = get_filename_without_ext(model_path)
        model = tf.keras.models.load_model(model_path)
        converter = tf.lite.TFLiteConverter.from_keras_model(model)
        tflite_model = converter.convert()
        converted_model = os.path.join(save_path, model_name + '.tflite')
        open(converted_model, 'wb').write(tflite_model)
        shutil.rmtree(model_path)
        return converted_model
    
    def test_connection(self):
        """check the status of backend interface connection
        """
        ...
        logging.keyinfo("hello TFLitex64 backend !")

my backend register config :

builtin_name: TFLitex64
package_location: /home/nn-meter/backends/tflitex64
class_module: cpu
class_name: TFLiteCPUBackend
defaultConfigFile: /home/nn-meter/backends/tflitex64/backend_tflitex64_config.yaml

my backend default config :

REMOTE_MODEL_DIR: /home/nn-meter/models
BENCHMARK_MODEL_PATH: /tensorflow_src/bazel-bin/tensorflow/lite/tools/benchmark/benchmark_model
KERNEL_PATH:

and i used cpu.py in this repo

my custom backend is able to be registered in my laptop but i have issue when i try test fusion rules. i got profile_error.log that error 'int' object has no attribute 'splitlines'. so i try to debug content above line 27 cpu.py because in line 27 call splitlines and the result is 0 (it's integer) so i think that is the issue of error 'int' object has no attribute 'splitlines' but i don't know what is wrong in my configuration or code.

And also i got the issue WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually. when i try test fusion rules. which configuration and where should I save the configuration file ?

and i have a question again, does REMOTE_MODEL_DIR must to have any model when running profile_model ?

Thanks

How to convert nn-Meter IR graph back to pb/onnx model?

Hi, I am very interested in the nn-Meter IR graph, but it seemed that we can only convert pb/onnx model to nn-Meter IR. Not opposite. Any plan on implementing a two-way IR? So that we can fully utilize the IR graphs in the benchmark datasets.

"list index out of range" when using torch Conv1d

For debugging purposes i built a simple model based on PyTorch Lightning:

class TCNModel(nni.retiarii.evaluator.pytorch.lightning.LightningModule):
def init(self):
super().init()
self.output = nn.Conv1d(1, 1, kernel_size=1)

def forward(self, x):
x = self.output(x)
return x

and:

def compute_model_latency_in_ms(model, batch_size, latency_platform):
predictor = load_latency_predictor(latency_platform)
latency = predictor.predict(model=model, model_type='torch', input_shape=[batch_size,1,88201])
return latency

When trying to predict the latency of this with nn-meter, i get the following error:

PS C:\Users\alexa\Desktop\Code\NAS_New_Trial> python -u "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Baseline_Train.py"
Global seed set to 42
Using 16bit native Automatic Mixed Precision (AMP)
GPU available: True, used: True
TPU available: False, using: 0 TPU cores
IPU available: False, using: 0 IPUs
HPU available: False, using: 0 HPUs
[2022-05-12 12:03:43] INFO (root/MainThread) checking local kernel predictors at C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21
[2022-05-12 12:03:43] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\add.pkl
C:\Users\alexa\Python308\lib\site-packages\sklearn\base.py:310: UserWarning: Trying to unpickle estimator DecisionTreeRegressor from version 0.23.1 when using version 0.24.2. This might lead to breaking code or invalid results. Use at your own risk.
  warnings.warn(
C:\Users\alexa\Python308\lib\site-packages\sklearn\base.py:310: UserWarning: Trying to unpickle estimator RandomForestRegressor from version 0.23.1 when using version 0.24.2. This might lead to breaking code or invalid results. Use at your own risk.
  warnings.warn(
[2022-05-12 12:03:43] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\addrelu.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\avgpool.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\bn.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\bnrelu.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\channelshuffle.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\concat.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\conv-bn-relu.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\dwconv-bn-relu.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\fc.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\global-avgpool.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\hswish.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\maxpool.pkl
[2022-05-12 12:03:44] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\relu.pkl
[2022-05-12 12:03:45] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\se.pkl
[2022-05-12 12:03:45] INFO (root/MainThread) load predictor C:\Users\alexa/.nn_meter/data\predictor\cortexA76cpu_tflite21\split.pkl
[2022-05-12 12:03:45] INFO (root/MainThread) Start latency prediction ...
[2022-05-12 12:03:45] INFO (root/MainThread) Onnx-based Torch Converter is applied for model conversion
Traceback (most recent call last):
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Baseline_Train.py", line 53, in <module>
    lat = compute_model_latency_in_ms(model, args.batch_size, latency_platform)
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Utils.py", line 37, in compute_model_latency_in_ms
    latency = predictor.predict(model=model, model_type='torch', input_shape=[batch_size,1,88201])
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\nn_meter_predictor.py", line 107, in predict
    self.kd.load_graph(graph)
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\kernel_detector\kernel_detector.py", line 19, in load_graph
    new_graph = convert_nodes(graph)
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\kernel_detector\utils\ir_tools.py", line 42, in convert_nodes
    cin = node["attr"]["input_shape"][0][3]
IndexError: list index out of range

After a lot of fiddling around, I noticed that this happens with a Conv1D but not with Conv2D.
Alternatively, I could change
cin = node["attr"]["input_shape"][0][3]
to
cin = node["attr"]["input_shape"][0][2]
in nn_meter\kernel_detector\utils\ir_tools.py, but obviously I don't know how this influences the prediction itself and if that leads to weird behaviour with models that utilize Conv2D.

At this point I'd like to ask you for
a) a quick fix I can apply safely (I need nn-meter for my bachelors thesis ;) )
b) an update for future users.

Also I find it weird that both fixes (usind Conv3D and modifying ir_tools.py) lead to a prediction of 0ms latency with this simple model. Is that plausible?

Thank you very much!

EDIT:

Using a more complex model leads to even more errors regarding the Conv1D:

class TCNModel(pl.LightningModule):   
    def __init__(self, 
                 ninputs=1,
                 noutputs=1,
                 kernel_size=13, 
                 dilation_growth=10, 
                 channel_growth=1, 
                 channel_width=32, 
                 stack_size=10,
                 grouped=False,
                 causal=True,
                 lr = 5e-3, 
                 train_loss = "l1+stft", # 'stft' or 'l1+stft' or 'l1'
                 save_dir = "UnknownEffect",
                 num_examples = 5):
        super().__init__()
        self.save_hyperparameters()

        out1_ch = ninputs * channel_width * channel_growth
        out2_ch = out1_ch * channel_growth
        out3_ch = out2_ch * channel_growth
        out4_ch = out3_ch * channel_growth

        dilation1 = 1
        dilation2 = dilation_growth ** (1 % stack_size)
        dilation3 = dilation_growth ** (2 % stack_size)
        dilation4 = dilation_growth ** (3 % stack_size)

        self.block1 = TCNBlock(ninputs, out1_ch, kernel_size, dilation1, causal, grouped)
        self.block2 = TCNBlock(out1_ch, out2_ch, kernel_size, dilation2, causal, grouped)
        self.block3 = TCNBlock(out2_ch, out3_ch, kernel_size, dilation3, causal, grouped)
        self.block4 = TCNBlock(out3_ch, out4_ch, kernel_size, dilation4, causal, grouped)
        self.output = nn.Conv1d(out4_ch, noutputs, kernel_size=1)        

    def forward(self, x):  
        
        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.output(x)
        return x

class TCNBlock(nn.Module):
    def __init__(self, 
                in_ch, 
                out_ch, 
                kernel_size=3, 
                dilation=1, 
                grouped=False, 
                causal=True):
        super().__init__()

        self.in_ch = in_ch
        self.out_ch = out_ch
        self.kernel_size = kernel_size
        self.dilation = dilation
        self.grouped = grouped
        self.causal = causal

        self.conv1 = nn.Conv1d(in_ch, out_ch, kernel_size=kernel_size, dilation=dilation)
        # self.bn = nn.BatchNorm1d(out_ch)
        # self.relu = nn.PReLU(out_ch)
        # self.res = nn.Conv1d(in_ch, out_ch, kernel_size=1, groups=in_ch)

    def forward(self, x):
        x = self.conv1(x)

        return x

leads to:

[2022-05-12 13:14:00] INFO (root/MainThread) Start latency prediction ...
[2022-05-12 13:14:00] INFO (root/MainThread) Onnx-based Torch Converter is applied for model conversion
[2022-05-12 13:14:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#0', 'input_tensors': [[4, 1, 88201]], 'ks': [13], 'strides': [1], 'cin': 88201, 'cout': 88189, 'inbounds': [], 'outbounds': ['conv#1']}
[2022-05-12 13:14:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#1', 'input_tensors': [[4, 32, 88189]], 'ks': [13], 'strides': [1], 'cin': 88189, 'cout': 88069, 'inbounds': ['conv#0'], 'outbounds': ['conv#2']}
[2022-05-12 13:14:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#2', 'input_tensors': [[4, 32, 88069]], 'ks': [13], 'strides': [1], 'cin': 88069, 'cout': 86869, 'inbounds': ['conv#1'], 'outbounds': ['conv#3']}
[2022-05-12 13:14:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#3', 'input_tensors': [[4, 32, 86869]], 'ks': [13], 'strides': [1], 'cin': 86869, 'cout': 74869, 'inbounds': ['conv#2'], 'outbounds': ['conv#4']}
[2022-05-12 13:14:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#4', 'input_tensors': [[4, 32, 74869]], 'ks': [1], 'strides': [1], 'cin': 74869, 'cout': 74869, 'inbounds': ['conv#3'], 'outbounds': []}   
Traceback (most recent call last):
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Baseline_Train.py", line 53, in <module>
    lat = compute_model_latency_in_ms(model, args.batch_size, latency_platform)
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Utils.py", line 37, in compute_model_latency_in_ms
    latency = predictor.predict(model=model, model_type='torch', input_shape=[batch_size,1,88201])
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\nn_meter_predictor.py", line 109, in predict
    py = nn_predict(self.kernel_predictors, self.kd.kernels) # in unit of ms
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\predict_by_kernel.py", line 53, in nn_predict
    features = get_predict_features(kernel_units)
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\extract_feature.py", line 49, in get_predict_features
    ks = item["ks"][1]
IndexError: list index out of range

and after modifying extract_feature.py line 49
to ks = item["ks"][-1]
I get

[2022-05-12 13:17:00] INFO (root/MainThread) Start latency prediction ...
[2022-05-12 13:17:00] INFO (root/MainThread) Onnx-based Torch Converter is applied for model conversion
[2022-05-12 13:17:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#0', 'input_tensors': [[4, 1, 88201]], 'ks': [13], 'strides': [1], 'cin': 88201, 'cout': 88189, 'inbounds': [], 'outbounds': ['conv#1']}
[2022-05-12 13:17:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#1', 'input_tensors': [[4, 32, 88189]], 'ks': [13], 'strides': [1], 'cin': 88189, 'cout': 88069, 'inbounds': ['conv#0'], 'outbounds': ['conv#2']}
[2022-05-12 13:17:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#2', 'input_tensors': [[4, 32, 88069]], 'ks': [13], 'strides': [1], 'cin': 88069, 'cout': 86869, 'inbounds': ['conv#1'], 'outbounds': ['conv#3']}
[2022-05-12 13:17:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#3', 'input_tensors': [[4, 32, 86869]], 'ks': [13], 'strides': [1], 'cin': 86869, 'cout': 74869, 'inbounds': ['conv#2'], 'outbounds': ['conv#4']}
[2022-05-12 13:17:01] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#4', 'input_tensors': [[4, 32, 74869]], 'ks': [1], 'strides': [1], 'cin': 74869, 'cout': 74869, 'inbounds': ['conv#3'], 'outbounds': []}   
Traceback (most recent call last):
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Baseline_Train.py", line 53, in <module>
    lat = compute_model_latency_in_ms(model, args.batch_size, latency_platform)
  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Utils.py", line 37, in compute_model_latency_in_ms
    latency = predictor.predict(model=model, model_type='torch', input_shape=[batch_size,1,88201])
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\nn_meter_predictor.py", line 109, in predict
    py = nn_predict(self.kernel_predictors, self.kd.kernels) # in unit of ms
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\predict_by_kernel.py", line 53, in nn_predict
    features = get_predict_features(kernel_units)
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\extract_feature.py", line 50, in get_predict_features
    s = item["strides"][1] if "strides" in item else 1
IndexError: list index out of range

and after modifying extract_feature.py line 50 to
strides = item["strides"][-1]
I get

[2022-05-12 13:17:14] INFO (root/MainThread) Start latency prediction ...
[2022-05-12 13:17:14] INFO (root/MainThread) Onnx-based Torch Converter is applied for model conversion
[2022-05-12 13:17:15] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#0', 'input_tensors': [[4, 1, 88201]], 'ks': [13], 'strides': [1], 'cin': 88201, 'cout': 88189, 'inbounds': [], 'outbounds': ['conv#1']}
[2022-05-12 13:17:15] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#1', 'input_tensors': [[4, 32, 88189]], 'ks': [13], 'strides': [1], 'cin': 88189, 'cout': 88069, 'inbounds': ['conv#0'], 'outbounds': ['conv#2']}
[2022-05-12 13:17:15] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#2', 'input_tensors': [[4, 32, 88069]], 'ks': [13], 'strides': [1], 'cin': 88069, 'cout': 86869, 'inbounds': ['conv#1'], 'outbounds': ['conv#3']}
[2022-05-12 13:17:15] INFO (root/MainThread) {'op': 'conv', 'name': 'conv#3', 'input_tensors': [[4, 32, 86869]], 'ks': [13], 'strides': [1], 'cin': 86869, 'cout': 74869, 'inbounds': ['conv#2'], 'outbounds': ['con  File "c:\Users\alexa\Desktop\Code\NAS_New_Trial\Utils.py", line 37, in compute_model_latency_in_ms
    latency = predictor.predict(model=model, model_type='torch', input_shape=[batch_size,1,88201])
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\nn_meter_predictor.py", line 109, in predict
    py = nn_predict(self.kernel_predictors, self.kd.kernels) # in unit of ms
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\predict_by_kernel.py", line 53, in nn_predict
    features = get_predict_features(kernel_units)
  File "C:\Users\alexa\Python308\lib\site-packages\nn_meter\predictor\prediction\extract_feature.py", line 51, in get_predict_features
    inputh = item["inputh"]
KeyError: 'inputh'

and so on.
Is it safe to assume that 1D Convolutions are not supported at this point?

how to predict model generated from keras model.save()?

Hi,

I have generated a keras model and saved it using keras model.save() function which results in saved_model folder consisting of assests,variable,keras_metadata.pb and saved_model.pb file.

When running the nn-meter predictor command:

nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --tensorflow saved_model.pb

resulted in the following error:

(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/dwconv-bn-relu.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/add.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/bnrelu.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/relu.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/global-avgpool.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/bn.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/maxpool.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/hswish.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/fc.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/conv-bn-relu.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/split.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/se.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/avgpool.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/concat.pkl
(nn-Meter) load predictor /home/.nn_meter/data/predictor/cortexA76cpu_tflite21/channelshuffle.pkl
(nn-Meter) Start latency prediction ...
Traceback (most recent call last):
File "/home/Desktop/Projects/nn-Meter/py3.9_env/bin/nn-meter", line 33, in
sys.exit(load_entry_point('nn-meter', 'console_scripts', 'nn-meter')())
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/utils/nn_meter_cli/interface.py", line 266, in nn_meter_cli
args.func(args)
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/utils/nn_meter_cli/predictor.py", line 56, in apply_latency_predictor_cli
latency = predictor.predict(model, model_type) # in unit of ms
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/predictor/nn_meter_predictor.py", line 106, in predict
graph = model_file_to_graph(model, model_type, input_shape, apply_nni=apply_nni)
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/ir_converter/utils.py", line 42, in model_file_to_graph
converter = FrozenPbConverter(filename)
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/ir_converter/frozenpb_converter/frozenpb_converter.py", line 15, in init
parser = FrozenPbParser(file_name)
File "/home/Desktop/Projects/nn-Meter/py3.9_env/nn-Meter/nn_meter/ir_converter/frozenpb_converter/frozenpb_parser.py", line 16, in init
graph.ParseFromString(f.read())
google.protobuf.message.DecodeError: Error parsing message with type 'tensorflow.GraphDef'

How can i predict the model generated from saved_model.pb?

I saw in the examples provided there is single .pb file which can be predicted. So do i need to convert the saved_model folder into single .pb file? If so, how to convert saved_model to single .pb file?

I have attached the generated saved_model below for your reference.

Please respond as soon as possible.

Thanks.

mobilenetv1_0.zip

fusion_rules not match

In your open-sourced cortexA76cpu_tflite21 predictor fusion_rules.json file, I found:

"RBC": {
"latency": {},
"obey": null
},
"CBC": {
"latency": {},
"obey": null
},
"BF_bn_relu": {
"obey": true
},
"BF_conv_bn": {
"obey": true
},
"BF_dwconv_bn": {
"obey": true
},
"BF_conv_bn_relu": {
"obey": true
},
"BF_dwconv_bn_relu": {
"obey": true
}

do you mean these kernels are naturally fused and hence doesn't need to detect? Why it that? Do I need to add all those to my own detected_fusion_rule.json?

What's more, in the fusion_rules.json, the fusion rules you seem to have detected are:

se_relu, pooling_reshape, dense_concat, dense_add, dense_relu, concat_dense, conv_pooling, conv_relu, add_relu, relu_dense, relu_relu, dwconv_relu, reshape_convtrans, reshape_conv, reshape_relu, reshape_reshape.

But in this page, you claim that the CPU kernel are:

conv-relu,fc,maxpool,global-avgpool,fc-relu,concat,avgpool,conv-bn-relu,bn-relu,conv,SE-relu,conv-bn,dwconv-bn,dwconv-bn-relu,add,hswish,SE,conv-bn-bn-relu,relu,add-relu,channelshuffle,split.

Those two are very different, why?
What confuses me even more is, in the default predictorbuild_config.yaml, the kernels are:

conv-bn-relu, dwconv-bn-relu, maxpool, avgpool, fc, concat, split, channelshuffle, se, global-avgpool, bnrelu, bn, hswish, relu, addrelu, add.

Although you mentioned, in the same page, that we can use one conv-bn-relu kernel to represent all conv-related kernels, the rest are still a bit different with your claimed cpu kernels. Does this mean we can merge other kernels to a general kernel as well? And what's the rule for that?

I have this issue, because I want to reproduce your results on a a78 cpu, but I get a very different fusion_rules results with yours, and I don't know what to do with it. Should I implement all the new kernels, or should I merge some of these kernels? My results of detected_fusion_rule.json on a78 cpu are:

add_avgpool, add_concat, add_conv, add_relu, avgpool_add, avgpool_concat, avgpool_relu, avgpool_reshape, concat_convtrans, concat_fc, conv_relu, convtrans_relu, fc_relu, dwconv_add,
dwconv_relu, relu_avgpool,relu_concat,relu_conv,relu_convtrans,relu_relu,reshape_concat,reshape_conv,
reshape_convtrans,reshape_dwconv,reshape_reshape

I wonder if nn-Meter can be applied to other network structures?

I modify the original neural models in the code, but it returns
"ValueError: Unsupported Model Name: modresnet50_0 in torchvision.
Supporting list: resnet18, alexnet, vgg16, squeezenet, densenet161, inception_v3, googlenet, shufflenet_v2, mobilenet_v2, Resnext50_32x4d wide_resnet50_2, mnasnet"

Refine setup process

  • manage default devices configuration
  • clarify / check dependencies
    • my suggestion is to exclude frameworks (e.g. tensorflow / pytorch / nni) from installation dependencies (because user may only use one of them)
    • check the framework installation and version on demand when user imports models
  • add entry_point to support command line usage
  • downloaded predictors (data / test data) to ~/.nn_meter/data
  • add --verbose to print debug-level information (e.g., the divided kernel information)
    • refine logging level
    • use two buffer to hold logging
  • add --tersorflow etc to specify model type
  • try model compression with gzip
  • Specify parameter type in the function declaration
  • change nn_meter/utils/graphe_tool.py to graph_tool.py and Graph class
    • change all graphe to graph and check its using.
  • add requirements in setup.py
  • code testing
    • .pb model
    • .onnx model
    • link an action on GitHub with .yml
    • setting cache policy to avoid redundancy downloading in test (Leaving to the next PR)

User interface pipeline for building tools

The building tools consists of two key component: the fusion rule detection and adaptive data sampler:

builder
	/configs  # this config folders contain: backends config file, kernel init sampling file, and a base config file.
	/rule_tester
	/adaptive_sampler

Now we consider that user has a new hardware platform (inference framework+device), and he wants to use nn-Meter to build latency predictor, user can follow the steps:

  1. create a project folder (named by user's platform)
    • copy default backend config from nn_meter/builder
    • user edit the config files, test the device connection
  2. run rule_tester to get all the fused operators (get_testcases)
  3. prepare test models, run get_fusionrule to get kernels
  4. run adaptive_sample to sample data and build latency predictor
    a. check with current kernel configs, if the target kernels are new, please also add the tf kernel code
    b. edit the initial sampling count for the kernels, it will sample from a prior distribution
    c. sample from the prior distribution, save the sampled kernel models
    d. measure the latency on the device
    e. split data into train/test, choose the regression model, build the latency prediction (current implementation: kernel_predictor)
    f. evaluate the regression model, return the rmse, rmspe, label the data points with largest prediction error
    g. random fine-grained data sampling for large-error-data-points
    h. repeat from step d, until the rmse, rmspe in f meet user's requirements

How to generate material/testmodels/mobilenetv3small_0.pb

Hi,
I would like to generate a TF2 frozen pb model (such as material/testmodels/mobilenetv3small_0.pb), but my models(generated by TF 2.6.0 and TF 2.7.0) can't be converted to nnm-ir. I find dataset/generator/generate_model.py is used to generate the keras h5 models. Is it possible to release the reference code to generate TF2 frozen pb model? And which TF version was used to generate material/testmodels/mobilenetv3small_0.pb?

Thanks & Regards,
X. Zhang

Signficant differences between measurements and predictions on Pixel 4

Hi,

I am trying to reproduce the results of nn-Meter by comparing the measurements on Pixel 4 with the results from the pre-trained predictors (i.e., cortexA76cpu_tflite21 and adreno640gpu_tflite21). However, I observed significant differences between the measurements and predictions.

I converted the provided TensorFlow pb models (i.e., pb_models) into .tflite format, and built the binary benchmark_model from TFLite v2.1 source. I benchmarked all the models with the following commands on Pixel 4 (Snapdragon 855 and Adreno 640):

# For CPUs:
/data/local/tmp/benchmark_model --warmup_runs=10 --num_runs=10 --num_threads=1 --graph=${path}

# For GPUs:
/data/local/tmp/benchmark_model --warmup_runs=10 --num_runs=10 --use_gpu=true --graph=${path}

For example, the measurement of resnet18_0 on CPU shows:

$ /data/local/tmp/benchmark_model --warmup_runs=10 --num_runs=10 --num_threads=1 --graph=${path}
STARTING
...
Loaded model /data/local/tmp/output/tflite-pb-tf21/resnet18_0.tflite
resolved reporter
INFO: Initialized TensorFlow Lite runtime.
Initialized session in 0.732ms
[Init Phase] - Memory usage: max resident set size = 3.07422 MB, total malloc-ed size = 14.5485 MB
[Init Phase] - Memory usage: max resident set size = 3.07422 MB, total malloc-ed size = 14.5485 MB
Running benchmark for at least 10 iterations and at least 0.5 seconds but terminate if exceeding 150 seconds.
count=10 first=150351 curr=119397 min=119349 max=150351 avg=122502 std=9282

Running benchmark for at least 10 iterations and at least 1 seconds but terminate if exceeding 150 seconds.
count=10 first=119529 curr=119341 min=119341 max=119529 avg=119410 std=53

[Overall] - Memory usage: max resident set size = 71.4961 MB, total malloc-ed size = 31.3739 MB
Average inference timings in us: Warmup: 122502, Init: 732, no stats: 119410

but the prediction on cortexA76cpu_tflite21 is:

...
(nn-Meter) Get weight shape of fc13.fc/MatMul from ['fc13.fc/weight'], input shape:[512, 1000].
(nn-Meter) Get input shape of fc13.fc/MatMul from Reshape, input shape:[-1, 512].
(nn-Meter) Input shape of fc13.fc/MatMul op is [[-1, 512]].
(nn-Meter) Output shape of fc13.fc/MatMul op is [[-1, 1000]].
(nn-Meter) Predict latency: 216.19714599005837 ms
resnet18_0,216.19714599005837

with error 81% (i.e., 216.19 v.s. 119.41).

Similarly, for resnet50_0 on GPU, the measurement is:

$ /data/local/tmp/benchmark_model --warmup_runs=10 --num_runs=10 --use_gpu=true --graph=${path}
STARTING!
...
Loaded model /data/local/tmp/output/tflite-pb-tf21/resnet50_0.tflite
resolved reporter
INFO: Initialized TensorFlow Lite runtime.
INFO: Created TensorFlow Lite delegate for GPU.
ERROR: Next operations are not supported by GPU delegate:
MEAN: Operation is not supported.
First 70 operations will run on the GPU, and the remaining 2 on the CPU.
INFO: Initialized OpenCL-based API.
Applied GPU delegate.
Initialized session in 665.544ms
[Init Phase] - Memory usage: max resident set size = 274.34 MB, total malloc-ed size = 1.32245 MB
Running benchmark for at least 10 iterations and at least 0.5 seconds but terminate if exceeding 150 seconds.
count=10 first=51879 curr=58338 min=43539 max=58484 avg=55702.5 std=4507

Running benchmark for at least 10 iterations and at least 1 seconds but terminate if exceeding 150 seconds.
count=18 first=58433 curr=58263 min=56980 max=59873 avg=58350.9 std=674

[Overall] - Memory usage: max resident set size = 274.34 MB, total malloc-ed size = 1.90115 MB
Average inference timings in us: Warmup: 55702.5, Init: 665544, no stats: 58350.9

and the predictor produces the following:

...
(nn-Meter) Find node fc21.fc/MatMul with its weight op fc21.fc/weight.
(nn-Meter) Get weight shape of fc21.fc/MatMul from ['fc21.fc/weight'], input shape:[2048, 1000].
(nn-Meter) Get input shape of fc21.fc/MatMul from Reshape, input shape:[-1, 2048].
(nn-Meter) Input shape of fc21.fc/MatMul op is [[-1, 2048]].
(nn-Meter) Output shape of fc21.fc/MatMul op is [[-1, 1000]].
(nn-Meter) Predict latency: 91.73126828870865 ms
resnet50_0,91.73126828870865

with error 57% (i.e., 91.73 v.s. 58.35).

I am wondering whether I set up the same experimental environment as the one for training the predictors. I can provide more information (e.g., the tflite models) if needed and look into the issue further.

Thank you!

Milestone I

  • Refine documentation
  • Check all copyright header
  • Add cache in the integration test
  • Check the bug that TorchConverter can not based on NNI
  • Check the bug that OnnxBasedTorchConverter differs in different torchvision version
  • benchmark dataset open-source
  • Check the incorrect kernel detection in NNIBasedTorchConverter (Jianyu)
  • Pack a pip package for pip install
  • Support nn-meter in nni
    • Hardware-aware NAS
    • SPOS
    • Proxyless NAS

About lower-level ir optimizatin detection

I appreciate your efforts, nice work! Well, seems that nn-meter does not consider the tensor/code level optimization, while optimizations in this stage can make quite huge effects on run-time performance, especially for AI accelerators. Anyone interested in this topic? Will nn-meter extend the work to fully considerations of compiler level acceleration?

无法Create Workspace for customized platform

为什么nn-meter create --customized-workspace "C:\Users\HP\Desktop\nn meter builder1" --backend "1660s"显示
Traceback (most recent call last):
File "c:\programdata\anaconda3\lib\runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "c:\programdata\anaconda3\lib\runpy.py", line 87, in run_code
exec(code, run_globals)
File "C:\ProgramData\Anaconda3\Scripts\nn-meter.exe_main
.py", line 7, in
File "c:\programdata\anaconda3\lib\site-packages\nn_meter\utils\nn_meter_cli\interface.py", line 266, in nn_meter_cli
args.func(args)
File "c:\programdata\anaconda3\lib\site-packages\nn_meter\utils\nn_meter_cli\builder.py", line 76, in create_workspace_cli
raise ValueError(f"Create workspace failed. Please check the backend registration information.")
ValueError: Create workspace failed. Please check the backend registration information.
请问哪里出问题了呢?

for customized platform

nn-meter create --customized-workspace <path/to/place/workspace/> --backend

How to use PyTorch or TensorFlow to check the predicted value

Thank you very much for opening up such a dataset. However, I want to ask a question.
I can use meter.predictor to predict the inference latency about "id": "resnet34_350"(a example)
And then, I want to check whether the prediction value is corrected. So I want to run resnet34_350 using PyTorch. But How can I make the structure of resnet34_350 "{"input_im_0": {"inbounds": [], "attr": {"name": "input_im_0", "type": "Placeholded······" used in Pytorch code.
I will be very appreciate that you can answer my question.

Error in Prediction of certain Model Families

Hi ,

I have generated an google net onnx model for prediction and the model is compatible to predict using in built predictor but i couldn't predict using my customized predictor. From the families listed in https://github.com/microsoft/nn-Meter/tree/dev/dataset-generator/nn_meter/dataset/generator/configs , I am facing this issue with google net , dense net , squeeze net , shufflenetV2 families.

I have attached the required materials for reference : Material

When running the nn-meter predictor command:
nn-meter predict --predictor tflitemicropredictor --predictor-version 1.0 --onnx googlenet_0_deq.onnx
resulted in the following error:

2023-05-05 17:21:23.559308: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2023-05-05 17:21:23.559335: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
(nn-Meter) checking local kernel predictors at /../nn-Meter/py3.9_env/tflitemicropredictor
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/addrelu.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/dwconv-bn-relu.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/add.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/bnrelu.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/relu.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/global-avgpool.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/bn.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/maxpool.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/hswish.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/fc.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/conv-bn-relu.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/split.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/se.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/avgpool.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/concat.pkl
(nn-Meter) load predictor /../nn-Meter/py3.9_env/tflitemicropredictor/channelshuffle.pkl
(nn-Meter) Start latency prediction ...
Traceback (most recent call last):
File "/../nn-Meter/py3.9_env/bin/nn-meter", line 33, in
sys.exit(load_entry_point('nn-meter', 'console_scripts', 'nn-meter')())
File "/../nn-Meter/py3.9_env/nn-Meter/nn_meter/utils/nn_meter_cli/interface.py", line 266, in nn_meter_cli
args.func(args)
File "/../nn-Meter/py3.9_env/nn-Meter/nn_meter/utils/nn_meter_cli/predictor.py", line 56, in apply_latency_predictor_cli
latency = predictor.predict(model, model_type) # in unit of ms
File "/../nn-Meter/py3.9_env/nn-Meter/nn_meter/predictor/nn_meter_predictor.py", line 113, in predict
py = nn_predict(self.kernel_predictors, self.kd.get_kernels()) # in unit of ms
File "/../nn-Meter/py3.9_env/nn-Meter/nn_meter/predictor/prediction/predict_by_kernel.py", line 54, in nn_predict
py = predict_model(features, predictors)
File "/../nn-Meter/py3.9_env/nn-Meter/nn_meter/predictor/prediction/predict_by_kernel.py", line 39, in predict_model
pys = pred.predict(dicts[kernel]) # in unit of ms
File "/../nn-Meter/py3.9_env/lib/python3.9/site-packages/sklearn/ensemble/_forest.py", line 981, in predict
X = self._validate_X_predict(X)
File "/../nn-Meter/py3.9_env/lib/python3.9/site-packages/sklearn/ensemble/_forest.py", line 602, in _validate_X_predict
X = self._validate_data(X, dtype=DTYPE, accept_sparse="csr", reset=False)
File "/../nn-Meter/py3.9_env/lib/python3.9/site-packages/sklearn/base.py", line 588, in _validate_data
self._check_n_features(X, reset=reset)
File "/../nn-Meter/py3.9_env/lib/python3.9/site-packages/sklearn/base.py", line 389, in _check_n_features
raise ValueError(
ValueError: X has 6 features, but RandomForestRegressor is expecting 5 features as input.

Hope you reply back soon.
Thank you.

google.protobuf.message.DecodeError: Error parsing message with type 'tensorflow.GraphDef'

when I use 'nn-meter predict --predictor cortexA76cpu_tflite21 --predictor-version 1.0 --tensorflow mobilenetv3small_0.onnx ' or 'nn-meter predict --predictor cortexA76cpu_tflite21 --tensorflow mobilenetv3small_0.json' in my command line, this error occured. Any suggestions?

Traceback (most recent call last):
File "/opt/conda/bin/nn-meter", line 8, in
sys.exit(nn_meter_cli())
File "/opt/conda/lib/python3.7/site-packages/nn_meter/nn_meter_cli.py", line 182, in nn_meter_cli
args.func(args)
File "/opt/conda/lib/python3.7/site-packages/nn_meter/nn_meter_cli.py", line 54, in apply_latency_predictor_cli
latency = predictor.predict(model, model_type) # in unit of ms
File "/opt/conda/lib/python3.7/site-packages/nn_meter/predictor/nn_meter_predictor.py", line 102, in predict
graph = model_file_to_graph(model, model_type, input_shape, apply_nni=apply_nni)
File "/opt/conda/lib/python3.7/site-packages/nn_meter/ir_converter/utils.py", line 41, in model_file_to_graph
converter = FrozenPbConverter(filename)
File "/opt/conda/lib/python3.7/site-packages/nn_meter/ir_converter/frozenpb_converter/frozenpb_converter.py", line 15, in init
parser = FrozenPbParser(file_name)
File "/opt/conda/lib/python3.7/site-packages/nn_meter/ir_converter/frozenpb_converter/frozenpb_parser.py", line 19, in init
graph.ParseFromString(f.read())
google.protobuf.message.DecodeError: Error parsing message with type 'tensorflow.GraphDef'

how to get all model to generate datasets on my device?

I want to generate my training dataset (including model and corresponding latency) on my own device, I have read the article here (https://github.com/microsoft/nn-Meter/blob/ffd51e32c31026896fe2bda198b49fe5d756f184/docs/dataset.md ), it mentioned there is no model files but only the structure(in .jsonl files) of these model, so I havn't find any way to get the delay data of these model files running on my device.

So how can I get these models?

For example, you mentioned in the article that "it requires hundreds of GB storage to store the full dataset", is it possible to store these data in the network disk and open for download, or is there any other way for me to get my own dataset, so that I can get valuable datasets for me, thanks very much!

Roadmap

nn-Meter is not only a latency predictor but also a critical component in the hardware-aware model design. It empowers existing NAS (neural architecture search) and other efficient model design tasks to be specialized for the target hardware platform.

There are multiple aspects will be covered in this and related repo, including:

  • latency prediction and pre-trained predictors
    • the IR converter, kernel detection tools
    • builtin kernel predictors and pre-trained weights
  • algorithm integration (mainly in NNI), the integration of latency prediction in existing NAS and compression algorithms.
  • model latency dataset, the collected latencies of thousands of model architectures. Also includes data loaders and an improved GNN predictor.

Release Plan

version 1.0-alpha

  • Date: 2021 August
  • Latency prediction
    • basic framework and utilities for latency prediction (e.g., config management, artifacts downloading, builtin predictors)
    • basic CI workflow with integrated test
    • documentation and examples
  • Algorithm integration
    • initial multi-trial NAS example

version 1.0-beta

  • Date: 2021 November
  • Algorithm integration
    • SPOS / Proxyless NAS in NNI
    • SPOS: first integrate nn-meter in the evolution search (move to 2.0)
    • Proxyless NAS: predict the block latency in the search space, provide the lookup table
  • Dataset
    • make model-latency dataset public
    • reference design of an improved GNN latency predictor

version 2.0

  • Date: 2021 November December
  • Algorithm integration
    • SPOS: first integrate nn-meter in the evolution search
  • latency predictor building tools
    • fusion rule detecton
    • adaptive data sampler

Is it possible to use nn-meter for TFLite micro?

Hi,
I would like to measure the inference time for my edge device which supports TensorFlow Lite Micro framework. Is it possible to add support for my edge device? If so it is possible to do it from my side? or does it requires updates in the library itself?

Thanks & Regards,
Ramson Jehu K

How can I get the models in datasets.zip?

I found the dataset generation method in paper:

For each model (e.g., AlexNet), we generate 2,000 variants
(e.g., AlexNets) by re-sampling the output channel number and kernel size for each layer. Specifically, the new output channel number is randomly selected from [0.2 × 𝐶𝑜𝑢𝑡, 1.8 × 𝐶𝑜𝑢𝑡], and the kernel size is sampled from {1, 3, 5, 7, 9}

and the generated models in the datasets.zip, but how can I get the models to execute them by myself?

I noticed that these models are ir graphs, how can I transfer them back to pytorch/tensorflow models to do the inference by myself

regarding converting onnx model to graph

Hi,I encounter two questions when reading the code:

  1. It seems that we may not need NetworkX to first convert onnx model to a graph of NetworkX (in function "to_networkx"), the overall information may be extracted directly from onnx model into the result graph in "OnnxConverter.convert" method.

  2. In "to_networkx" function, you added tensor as a node into the G graph ( e.g., "G.add_edge(input_name, node.name)"), it seems no use but has to skip the tensor node when calculating the inbounds/outbounds in "OnnxConverter.convert" method as follows:

for succ in self.G.successors(node):
    for  succ_succ in self.G.successors(succ):
          ...

just want to know is there something I do not understand about the above code, many thanks!

datasets 中模型构建onnx无法通过infershape

您好
我尝试根据datasets当中Alexnet模型的参数生成为onnx,但是�无法通过infershape的检测,�我想了解下这些模型是如何实现测速的,是根据单个node的预测结果求和得到整个模型的速度吗?
image

KeyError in graph_tool

Within the get_node_type method in graph_tool.py there is an if-clause to check, if a certain key exists in graph.keys(), but if it fails, accessing the key is still tried and will fail with a KeyError:

def get_node_type(self, name):
    if name in self.graph.keys() and "attr" in self.graph[name].keys():  # <-- Check if name is in keys
        return self.graph[name]["attr"]["type"]
    else:
        logging.info(name, self.graph[name])  # <-- Causes key error
    return None

Instead, something like that could be done:

logging.info(name, self.graph.get(name, "Name not in graph"))

I'm not sure if it is expected behavior that the name does not exist within the keys. If it's not, a different, more precise error message should be printed and an exception should be raised accordingly.

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.