├── .gitignore
├── LICENSE
├── README.md
├── pull_request_template.md
└── tfjs-converter
├── .babelrc
├── .npmignore
├── DEVELOPMENT.md
├── README.md
├── cloudbuild.yml
├── demo
├── control_flow
│ ├── README.md
│ ├── index.html
│ └── loop_model.js
├── mobilenet
│ ├── README.md
│ ├── cat.jpg
│ ├── imagenet_classes.js
│ ├── index.html
│ ├── index.js
│ └── mobilenet.js
├── package.json
├── ssd
│ ├── classes.js
│ ├── image1.jpg
│ ├── image2.jpg
│ ├── index.html
│ └── index.js
└── yarn.lock
├── docs
└── supported_ops.md
├── karma.conf.js
├── package.json
├── python
├── .pylintrc
├── BUILD
├── MANIFEST.in
├── README.md
├── WORKSPACE
├── __init__.py
├── build-pip-package.sh
├── requirements-dev.txt
├── requirements.txt
├── run-python-tests.sh
├── setup.cfg
├── setup.py
├── tensorflowjs
│ ├── BUILD
│ ├── __init__.py
│ ├── converters
│ │ ├── BUILD
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── converter.py
│ │ ├── converter_binary_test.sh
│ │ ├── converter_test.py
│ │ ├── generate_test_model.py
│ │ ├── keras_h5_conversion.py
│ │ ├── keras_h5_conversion_test.py
│ │ ├── keras_tfjs_loader.py
│ │ ├── keras_tfjs_loader_test.py
│ │ ├── tf_saved_model_conversion_v2.py
│ │ └── tf_saved_model_conversion_v2_test.py
│ ├── op_list
│ │ ├── arithmetic.json
│ │ ├── basic_math.json
│ │ ├── control.json
│ │ ├── convolution.json
│ │ ├── creation.json
│ │ ├── dynamic.json
│ │ ├── evaluation.json
│ │ ├── graph.json
│ │ ├── image.json
│ │ ├── logical.json
│ │ ├── matrices.json
│ │ ├── normalization.json
│ │ ├── reduction.json
│ │ ├── slice_join.json
│ │ ├── spectral.json
│ │ └── transformation.json
│ ├── quantization.py
│ ├── quantization_test.py
│ ├── read_weights.py
│ ├── read_weights_test.py
│ ├── version.py
│ ├── write_weights.py
│ └── write_weights_test.py
└── test_pip_package.py
├── rollup.config.js
├── scripts
├── build-npm.sh
├── gen_doc.ts
├── gen_json.ts
├── make-version
├── publish-npm.sh
├── tag-version
└── test_snippets.ts
├── src
├── data
│ ├── api.proto
│ ├── compiled_api.ts
│ └── types.ts
├── executor
│ ├── execution_context.ts
│ ├── execution_context_test.ts
│ ├── graph_executor.ts
│ ├── graph_executor_test.ts
│ ├── graph_model.ts
│ ├── graph_model_test.ts
│ ├── model_analysis.ts
│ ├── model_analysis_test.ts
│ ├── model_rewrite.ts
│ ├── model_rewrite_test.ts
│ ├── tensor_array.ts
│ └── tensor_array_test.ts
├── index.ts
├── operations
│ ├── custom_op
│ │ ├── node_value_impl.ts
│ │ ├── node_value_impl_test.ts
│ │ ├── register.ts
│ │ └── register_test.ts
│ ├── executors
│ │ ├── arithmetic_executor.ts
│ │ ├── arithmetic_executor_test.ts
│ │ ├── basic_math_executor.ts
│ │ ├── basic_math_executor_test.ts
│ │ ├── control_executor.ts
│ │ ├── control_executor_test.ts
│ │ ├── convolution_executor.ts
│ │ ├── convolution_executor_test.ts
│ │ ├── creation_executor.ts
│ │ ├── creation_executor_test.ts
│ │ ├── dynamic_executor.ts
│ │ ├── dynamic_executor_test.ts
│ │ ├── evaluation_executor.ts
│ │ ├── evaluation_executor_test.ts
│ │ ├── graph_executor.ts
│ │ ├── graph_executor_test.ts
│ │ ├── image_executor.ts
│ │ ├── image_executor_test.ts
│ │ ├── logical_executor.ts
│ │ ├── logical_executor_test.ts
│ │ ├── matrices_executor.ts
│ │ ├── matrices_executor_test.ts
│ │ ├── normalization_executor.ts
│ │ ├── normalization_executor_test.ts
│ │ ├── reduction_executor.ts
│ │ ├── reduction_executor_test.ts
│ │ ├── slice_join_executor.ts
│ │ ├── slice_join_executor_test.ts
│ │ ├── spectral_executor.ts
│ │ ├── spectral_executor_test.ts
│ │ ├── test_helper.ts
│ │ ├── transformation_executor.ts
│ │ ├── transformation_executor_test.ts
│ │ └── utils.ts
│ ├── op_list
│ │ ├── arithmetic.ts
│ │ ├── basic_math.ts
│ │ ├── control.ts
│ │ ├── convolution.ts
│ │ ├── creation.ts
│ │ ├── dynamic.ts
│ │ ├── evaluation.ts
│ │ ├── graph.ts
│ │ ├── image.ts
│ │ ├── logical.ts
│ │ ├── matrices.ts
│ │ ├── normalization.ts
│ │ ├── op_list_test.ts
│ │ ├── reduction.ts
│ │ ├── slice_join.ts
│ │ ├── spectral.ts
│ │ └── transformation.ts
│ ├── op_mapper_schema.ts
│ ├── operation_executor.ts
│ ├── operation_executor_test.ts
│ ├── operation_mapper.ts
│ ├── operation_mapper_test.ts
│ └── types.ts
├── version.ts
└── version_test.ts
├── tools
├── compiled_api.d.ts
├── compiled_api.js
└── pb2json_converter.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage/
3 | package-lock.json
4 | npm-debug.log
5 | yarn-error.log
6 | .DS_Store
7 | dist/
8 | .idea/
9 | *.tgz
10 | .cache
11 | package/
12 |
13 | bazel-*
14 |
15 | *.pyc
16 | .rpt2_cache/
17 | /compiled_api.js
18 |
19 | # Contains auto-generated json files produced by `yarn gen-json`
20 | **.yalc
21 | **yalc.lock
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This repository has been archived in favor of tensorflow/tfjs.
2 |
3 | This repo will remain around for some time to keep history but all future PRs should be sent to [tensorflow/tfjs](https://github.com/tensorflow/tfjs) inside the tfjs-core folder.
4 |
5 | All history and contributions have been preserved in the monorepo.
--------------------------------------------------------------------------------
/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## This repository has been archived in favor of the monorepo at https://github.com/tensorflow/tfjs. Please send pull requests there.
--------------------------------------------------------------------------------
/tfjs-converter/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "modules": false
7 | }
8 | ]
9 | ],
10 | "plugins": [
11 | "external-helpers"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/tfjs-converter/.npmignore:
--------------------------------------------------------------------------------
1 | .babelrc
2 | .DS_Store
3 | .idea/
4 | .pylintrc
5 | .rpt2_cache
6 | .travis.yml
7 | .vscode
8 | *.tgz
9 | *.txt
10 | **.yalc
11 | **yalc.lock
12 | cloudbuild.yml
13 | coverage/
14 | demo/
15 | DEVELOPMENT.md
16 | dist/**/*_test.d.ts
17 | dist/**/*_test.js
18 | docs/
19 | ISSUE_TEMPLATE.md
20 | karma.conf.js
21 | node_modules/
22 | npm-debug.log
23 | package-lock.json
24 | package/
25 | python/
26 | rollup.config.google.js
27 | rollup.config.js
28 | scripts/
29 | src/**/*_test.ts
30 | test_results
31 | tsconfig.json
32 | tslint.json
33 | yarn-error.log
34 | yarn.lock
35 | .git
36 |
--------------------------------------------------------------------------------
/tfjs-converter/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Notes for TensorFlow.js-Converter Developers
2 |
3 | ## Python Development
4 |
5 | There are some Python libraries, binary and tests in the `python/` directory.
6 |
7 | It is recommended to do your Python development and testing in a
8 | [virtualenv](https://virtualenv.pypa.io/en/stable/) or
9 | [pipenv](https://docs.pipenv.org/).
10 |
11 | As a prerequisite, install the following dependencies for python testing
12 | ```sh
13 | cd python
14 | pip install -r requirements.txt
15 | ```
16 |
17 | For Python linter, install `pylint`, e.g.,
18 | * `apt-get install -y pylint`
19 |
20 | To run the Python linter:
21 | ```sh
22 | cd python
23 | pylint tensorflowjs
24 | ```
25 |
26 | To run the python unit tests, there are two options. You can choose the one that
27 | you prefer.
28 |
29 | 1. Run the tests using the `run-python-tests.sh` script:
30 |
31 | ```sh
32 | cd python
33 | ./run-python-tests.sh
34 | ```
35 |
36 | 2. Run the tests using Bazel. See bazel installation guide
37 | [here](https://docs.bazel.build/versions/master/install.html). Once bazel
38 | is installed, do:
39 |
40 | ```sh
41 | cd python
42 | bazel test tensorflowjs/...
43 | ```
44 |
45 | Be sure to run the tests under **both** Python 2 and Python 3.
46 |
47 | ### Building and testing the tensorflowjs pip package
48 |
49 | ```sh
50 | cd python
51 |
52 | # You need to specify a folder where the pip wheel file will be stored, e.g.,
53 | ./build-pip-package.sh /tmp/my_tensorflowjs_pip
54 |
55 | # If the script succeeds, you can use `pip install` to install the pip package:
56 |
57 | pip install --force-reinstall \
58 | /tmp/my_tensorflowjs_pip/tensorflowjs-0.0.1-py2-none-any.whl
59 | ```
60 |
61 | `build-pip-package.sh` provides a flag (`--test`) with which you can run a
62 | test-on-install after building the pip package. Make sure you are using a
63 | `virutalenv` or `pipenv` to avoid changing your base environmnet.
64 |
65 | ```sh
66 | ./build-pip-package.sh --test /tmp/my_tensorflowjs_pip
67 | ```
68 |
--------------------------------------------------------------------------------
/tfjs-converter/cloudbuild.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - name: 'node:10'
3 | entrypoint: 'yarn'
4 | id: 'yarn'
5 | args: ['install']
6 | - name: 'node:10'
7 | entrypoint: 'yarn'
8 | id: 'test-browser'
9 | args: ['test-ci']
10 | waitFor: ['yarn']
11 | env: ['BROWSERSTACK_USERNAME=deeplearnjs1', 'NIGHTLY=$_NIGHTLY']
12 | secretEnv: ['BROWSERSTACK_KEY']
13 | - name: 'gcr.io/google-appengine/python'
14 | dir: 'python'
15 | entrypoint: 'bash'
16 | args: ['./build-pip-package.sh', '--test', '/tmp/tfjs-pips']
17 | waitFor: ['yarn']
18 | - name: 'python:2'
19 | dir: 'python'
20 | entrypoint: 'bash'
21 | args: ['./run-python-tests.sh']
22 | waitFor: ['yarn']
23 | - name: 'python:3.6'
24 | dir: 'python'
25 | entrypoint: 'bash'
26 | args: ['./run-python-tests.sh']
27 | waitFor: ['yarn']
28 | - name: 'node:10'
29 | entrypoint: 'yarn'
30 | id: 'test-snippets'
31 | args: ['test-snippets']
32 | waitFor: ['yarn']
33 | secrets:
34 | - kmsKeyName: projects/learnjs-174218/locations/global/keyRings/tfjs/cryptoKeys/enc
35 | secretEnv:
36 | BROWSERSTACK_KEY: CiQAkwyoIW0LcnxymzotLwaH4udVTQFBEN4AEA5CA+a3+yflL2ASPQAD8BdZnGARf78MhH5T9rQqyz9HNODwVjVIj64CTkFlUCGrP1B2HX9LXHWHLmtKutEGTeFFX9XhuBzNExA=
37 | timeout: 1800s
38 | logsBucket: 'gs://tfjs-build-logs'
39 | substitutions:
40 | _NIGHTLY: ''
41 | options:
42 | logStreamingOption: 'STREAM_ON'
43 | substitution_option: 'ALLOW_LOOSE'
44 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/control_flow/README.md:
--------------------------------------------------------------------------------
1 | # TensorFlow SavedModel Import Demo
2 |
3 | This demo imports a TensorFlow model that is generated by tf.while_loop
4 | for inference in the browser. The model was pre-converted to TensorFlow.js
5 | format and hosted on Google Cloud, using the steps in
6 | the repo's [README.md](../../README.md).
7 |
8 | The following commands will start a web server on `localhost:1234` and open
9 | a browser page with the demo.
10 |
11 | The demo allows you to change the conditions of the loop interactively and observe the execution result right in the browser.
12 |
13 | ```bash
14 | cd demo # If not already in the demo directory.
15 | yarn # Installs dependencies.
16 | yarn control_flow # Starts a web server and opens a page. Also watches for changes.
17 | ```
18 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/control_flow/index.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 | TensorFlow.js Converter: ControlFlow
33 | This example uses a Tensorflow model that is generated by tf.while_loop method.
34 |
35 |
36 | You can change the conditions of the loop and press the run button to
37 | observe the execution result.
38 |
39 |
40 |
41 |
42 | var output = 0;
43 | ; ; i++) {
44 | ; j++) {
45 | ;
46 | }
47 | console.log(output+1);
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/control_flow/loop_model.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tf from '@tensorflow/tfjs';
19 | const GOOGLE_CLOUD_STORAGE_DIR =
20 | 'https://storage.googleapis.com/tfjs-models/savedmodel/';
21 | const MODEL_FILE_URL = 'nested_loop/model.json';
22 | const OUTPUT_NODE_NAME = 'Add';
23 |
24 | export class LoopModel {
25 | constructor() {}
26 |
27 | async load() {
28 | this.model = await tf.loadGraphModel(
29 | GOOGLE_CLOUD_STORAGE_DIR + MODEL_FILE_URL);
30 | }
31 |
32 | dispose() {
33 | if (this.model) {
34 | this.model.dispose();
35 | }
36 | }
37 |
38 | async predict(init, loop, loop2, inc) {
39 | const dict = {
40 | 'init': tf.scalar(init, 'int32'),
41 | 'times': tf.scalar(loop, 'int32'),
42 | 'times2': tf.scalar(loop2, 'int32'),
43 | 'inc': tf.scalar(inc, 'int32')
44 | };
45 | return this.model.executeAsync(dict, OUTPUT_NODE_NAME);
46 | }
47 | }
48 |
49 | window.onload = async () => {
50 | const resultElement = document.getElementById('result');
51 |
52 | resultElement.innerText = 'Loading Control Flow model...';
53 |
54 | const loopModel = new LoopModel();
55 | console.time('Loading of model');
56 | await loopModel.load();
57 | console.timeEnd('Loading of model');
58 | resultElement.innerText = 'Model loaded.';
59 |
60 | const runBtn = document.getElementById('run');
61 | runBtn.onclick = async () => {
62 | const init = document.getElementById('init').value;
63 | const loop = document.getElementById('loop').value;
64 | const loop2 = document.getElementById('loop2').value;
65 | const inc = document.getElementById('inc').value;
66 | console.time('prediction');
67 | const result = await loopModel.predict(init, loop, loop2, inc);
68 | console.timeEnd('prediction');
69 |
70 | resultElement.innerText = "output = " + result.dataSync()[0];
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/mobilenet/README.md:
--------------------------------------------------------------------------------
1 | # TensorFlow SavedModel Import Demo
2 |
3 | This demo imports the
4 | [MobileNet v2.0](https://www.tensorflow.org/hub/modules/google/imagenet/mobilenet_v2_100_224/classification/1)
5 | model for inference in the browser. The model was pre-converted to TensorFlow.js
6 | format and hosted on Google Cloud, using the steps in
7 | the repo's [README.md](../../README.md).
8 |
9 | The following commands will start a web server on `localhost:1234` and open
10 | a browser page with the demo.
11 |
12 | ```bash
13 | cd demo # If not already in the demo directory.
14 | yarn # Installs dependencies.
15 | yarn mobilenet # Starts a web server and opens a page. Also watches for changes.
16 | ```
17 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/mobilenet/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tensorflow/tfjs-converter/06b9238b6262d2b359e441998d7a7277a5196886/tfjs-converter/demo/mobilenet/cat.jpg
--------------------------------------------------------------------------------
/tfjs-converter/demo/mobilenet/index.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 | TensorFlow.js Converter: MobileNet
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/mobilenet/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import 'babel-polyfill';
18 | import * as tf from '@tensorflow/tfjs';
19 | import {MobileNet} from './mobilenet';
20 | import imageURL from './cat.jpg';
21 |
22 | const cat = document.getElementById('cat');
23 | cat.onload = async () => {
24 | const resultElement = document.getElementById('result');
25 |
26 | resultElement.innerText = 'Loading MobileNet...';
27 |
28 | const mobileNet = new MobileNet();
29 | console.time('Loading of model');
30 | await mobileNet.load();
31 | console.timeEnd('Loading of model');
32 |
33 | const pixels = tf.browser.fromPixels(cat);
34 |
35 | console.time('First prediction');
36 | let result = mobileNet.predict(pixels);
37 | const topK = mobileNet.getTopKClasses(result, 5);
38 | console.timeEnd('First prediction');
39 |
40 | resultElement.innerText = '';
41 | topK.forEach(x => {
42 | resultElement.innerText += `${x.value.toFixed(3)}: ${x.label}\n`;
43 | });
44 |
45 | console.time('Subsequent predictions');
46 | result = mobileNet.predict(pixels);
47 | mobileNet.getTopKClasses(result, 5);
48 | console.timeEnd('Subsequent predictions');
49 |
50 | mobileNet.dispose();
51 | };
52 | cat.src = imageURL;
53 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/mobilenet/mobilenet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tf from '@tensorflow/tfjs';
19 |
20 | import {IMAGENET_CLASSES} from './imagenet_classes';
21 |
22 | const GOOGLE_CLOUD_STORAGE_DIR =
23 | 'https://storage.googleapis.com/tfjs-models/savedmodel/';
24 | const MODEL_FILE_URL = 'mobilenet_v2_1.0_224/model.json';
25 | const INPUT_NODE_NAME = 'images';
26 | const OUTPUT_NODE_NAME = 'module_apply_default/MobilenetV2/Logits/output';
27 | const PREPROCESS_DIVISOR = tf.scalar(255 / 2);
28 |
29 | export class MobileNet {
30 | constructor() {}
31 |
32 | async load() {
33 | this.model = await tf.loadGraphModel(
34 | GOOGLE_CLOUD_STORAGE_DIR + MODEL_FILE_URL);
35 | }
36 |
37 | dispose() {
38 | if (this.model) {
39 | this.model.dispose();
40 | }
41 | }
42 | /**
43 | * Infer through MobileNet. This does standard ImageNet pre-processing before
44 | * inferring through the model. This method returns named activations as well
45 | * as softmax logits.
46 | *
47 | * @param input un-preprocessed input Array.
48 | * @return The softmax logits.
49 | */
50 | predict(input) {
51 | const preprocessedInput = tf.div(
52 | tf.sub(input.asType('float32'), PREPROCESS_DIVISOR),
53 | PREPROCESS_DIVISOR);
54 | const reshapedInput =
55 | preprocessedInput.reshape([1, ...preprocessedInput.shape]);
56 | return this.model.execute(
57 | {[INPUT_NODE_NAME]: reshapedInput}, OUTPUT_NODE_NAME);
58 | }
59 |
60 | getTopKClasses(logits, topK) {
61 | const predictions = tf.tidy(() => {
62 | return tf.softmax(logits);
63 | });
64 |
65 | const values = predictions.dataSync();
66 | predictions.dispose();
67 |
68 | let predictionList = [];
69 | for (let i = 0; i < values.length; i++) {
70 | predictionList.push({value: values[i], index: i});
71 | }
72 | predictionList = predictionList
73 | .sort((a, b) => {
74 | return b.value - a.value;
75 | })
76 | .slice(0, topK);
77 |
78 | return predictionList.map(x => {
79 | return {label: IMAGENET_CLASSES[x.index], value: x.value};
80 | });
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tfjs-converter-demo",
3 | "version": "0.0.1",
4 | "description": "Imports mobilenet model using the tfc.js converter",
5 | "main": "index.js",
6 | "license": "Apache-2.0",
7 | "private": true,
8 | "dependencies": {
9 | "@tensorflow/tfjs": "0.15.3",
10 | "babel-polyfill": "^6.26.0"
11 | },
12 | "scripts": {
13 | "mobilenet": "NODE_ENV=development parcel mobilenet/index.html --no-hmr --open",
14 | "ssd": "NODE_ENV=development parcel ssd/index.html --no-hmr --open",
15 | "control_flow": "NODE_ENV=development parcel control_flow/index.html --no-hmr --open"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.0.0-0",
19 | "@babel/plugin-transform-runtime": "^7.3.4",
20 | "@babel/polyfill": "~7.2.5",
21 | "@babel/preset-env": "~7.3.4",
22 | "@babel/runtime": "~7.3.4",
23 | "clang-format": "~1.2.2",
24 | "parcel-bundler": "~1.11.0"
25 | },
26 | "babel": {
27 | "presets": [
28 | [
29 | "env",
30 | {
31 | "modules": false,
32 | "targets": {
33 | "browsers": [
34 | "> 1%",
35 | "last 3 versions",
36 | "ie >= 9",
37 | "ios >= 8",
38 | "android >= 4.2"
39 | ]
40 | },
41 | "useBuiltIns": false
42 | }
43 | ]
44 | ],
45 | "plugins": [
46 | [
47 | "@babel/plugin-transform-runtime"
48 | ]
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/ssd/image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tensorflow/tfjs-converter/06b9238b6262d2b359e441998d7a7277a5196886/tfjs-converter/demo/ssd/image1.jpg
--------------------------------------------------------------------------------
/tfjs-converter/demo/ssd/image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tensorflow/tfjs-converter/06b9238b6262d2b359e441998d7a7277a5196886/tfjs-converter/demo/ssd/image2.jpg
--------------------------------------------------------------------------------
/tfjs-converter/demo/ssd/index.html:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 | TensorFlow.js converter: ssd_mobilenet_v1_coco_2018_01_28
20 |
21 |
22 |
23 |
![]()
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tfjs-converter/demo/ssd/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import 'babel-polyfill';
18 | import * as tf from '@tensorflow/tfjs';
19 | import {CLASSES} from './classes';
20 | import imageURL from './image1.jpg';
21 | import image2URL from './image2.jpg';
22 |
23 | const GOOGLE_CLOUD_STORAGE_DIR =
24 | 'https://storage.googleapis.com/tfjs-models/savedmodel/';
25 | const MODEL_URL =
26 | GOOGLE_CLOUD_STORAGE_DIR + 'coco-ssd-mobilenet_v1/model.json';
27 |
28 | let modelPromise;
29 |
30 | window.onload = () => modelPromise = tf.loadGraphModel(MODEL_URL);
31 |
32 | const button = document.getElementById('toggle');
33 | button.onclick = () => {
34 | image.src = image.src.endsWith(imageURL) ? image2URL : imageURL;
35 | };
36 |
37 | const image = document.getElementById('image');
38 | image.src = imageURL;
39 |
40 | const runButton = document.getElementById('run');
41 | runButton.onclick = async () => {
42 | const model = await modelPromise;
43 | const pixels = tf.browser.fromPixels(image);
44 | console.log('model loaded');
45 | console.time('predict1');
46 | const res1 = await model.executeAsync(pixels.reshape([1, ...pixels.shape]));
47 | res1.map(t => t.dataSync());
48 | console.timeEnd('predict1');
49 | console.time('predict2');
50 | const res2 = await model.executeAsync(pixels.reshape([1, ...pixels.shape]));
51 | const count = res2[3].dataSync()[0];
52 | const boxes = res2[0].dataSync();
53 | const scores = res2[1].dataSync();
54 | const classes = res2[2].dataSync();
55 | console.timeEnd('predict2');
56 |
57 |
58 | const c = document.getElementById('canvas');
59 | const context = c.getContext('2d');
60 | context.drawImage(image, 0, 0);
61 | context.font = '10px Arial';
62 |
63 | console.log('number of detections: ', count);
64 | for (let i = 0; i < count; i++) {
65 | const min_y = boxes[i * 4] * 399;
66 | const min_x = boxes[i * 4 + 1] * 600;
67 | const max_y = boxes[i * 4 + 2] * 399;
68 | const max_x = boxes[i * 4 + 3] * 600;
69 |
70 | context.beginPath();
71 | context.rect(min_x, min_y, max_x - min_x, max_y - min_y);
72 | context.lineWidth = 1;
73 | context.strokeStyle = 'black';
74 | context.stroke();
75 | context.fillText(
76 | scores[i].toFixed(3) + ' ' + CLASSES.find(label => label.id === classes[i]).display_name,
77 | min_x, min_y - 5);
78 | }
79 | };
80 |
81 |
--------------------------------------------------------------------------------
/tfjs-converter/karma.conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | module.exports = function(config) {
19 | config.set({
20 | frameworks: ['jasmine', 'karma-typescript'],
21 | files: [{pattern: 'src/**/*.ts'}],
22 | exclude: [
23 | 'src/docs/**/*.ts'
24 | ],
25 | preprocessors: {
26 | 'src/**/*.ts': ['karma-typescript'], // *.tsx for React Jsx
27 | 'src/**/*.js': ['karma-typescript'], // *.tsx for React Jsx
28 | },
29 | karmaTypescriptConfig: {
30 | tsconfig: 'tsconfig.json',
31 | compilerOptions: {
32 | allowJs: true,
33 | declaration: false,
34 | module: 'commonjs'
35 | },
36 | coverageOptions: {instrumentation: false},
37 | reports: {} // Do not produce coverage html.
38 | },
39 | reporters: ['progress', 'karma-typescript'],
40 | browsers: ['Chrome'],
41 | browserStack: {
42 | username: process.env.BROWSERSTACK_USERNAME,
43 | accessKey: process.env.BROWSERSTACK_KEY
44 | },
45 | reportSlowerThan: 500,
46 | browserNoActivityTimeout: 30000,
47 | customLaunchers: {
48 | bs_chrome_mac: {
49 | base: 'BrowserStack',
50 | browser: 'chrome',
51 | browser_version: 'latest',
52 | os: 'OS X',
53 | os_version: 'Sierra'
54 | },
55 | bs_firefox_mac: {
56 | base: 'BrowserStack',
57 | browser: 'firefox',
58 | // TODO(cais): Change it back to 'latest' once the ongoing instability
59 | // with regard to OS X and FireFox is resolved on BrowserStack:
60 | // https://github.com/tensorflow/tfjs/issues/1620
61 | browser_version: '66.0',
62 | os: 'OS X',
63 | os_version: 'Sierra'
64 | }
65 | },
66 | client: {
67 | args: ['--grep', config.grep || '']
68 | }
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/tfjs-converter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tensorflow/tfjs-converter",
3 | "version": "1.2.7",
4 | "description": "Tensorflow model converter for javascript",
5 | "main": "dist/src/index.js",
6 | "jsnext:main": "dist/tf-converter.esm.js",
7 | "module": "dist/tf-converter.esm.js",
8 | "types": "dist/src/index.d.ts",
9 | "unpkg": "dist/tf-converter.min.js",
10 | "jsdelivr": "dist/tf-converter.min.js",
11 | "miniprogram": "dist/miniprogram",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/tensorflow/tfjs-converter.git"
15 | },
16 | "license": "Apache-2.0",
17 | "peerDependencies": {
18 | "@tensorflow/tfjs-core": "1.2.8"
19 | },
20 | "devDependencies": {
21 | "@tensorflow/tfjs-core": "1.2.8",
22 | "@types/deep-equal": "^1.0.1",
23 | "@types/jasmine": "~2.8.6",
24 | "@types/long": "~3.0.32",
25 | "@types/node-fetch": "1.6.9",
26 | "ajv": "~6.3.0",
27 | "babel-core": "~6.26.3",
28 | "babel-plugin-external-helpers": "~6.22.0",
29 | "babel-preset-env": "~1.7.0",
30 | "clang-format": "~1.2.2",
31 | "copyfiles": "~1.2.0",
32 | "deep-equal": "^1.0.1",
33 | "jasmine-core": "~3.1.0",
34 | "karma": "~4.0.1",
35 | "karma-browserstack-launcher": "~1.4.0",
36 | "karma-chrome-launcher": "~2.2.0",
37 | "karma-firefox-launcher": "~1.1.0",
38 | "karma-jasmine": "~2.0.1",
39 | "karma-typescript": "~4.0.0",
40 | "node-fetch": "~2.1.2",
41 | "opn": "~5.1.0",
42 | "protobufjs": "~6.8.6",
43 | "rimraf": "~2.6.2",
44 | "rollup": "~0.58.2",
45 | "rollup-plugin-cleanup": "~3.0.0",
46 | "rollup-plugin-commonjs": "~9.1.3",
47 | "rollup-plugin-node-resolve": "~3.3.0",
48 | "rollup-plugin-typescript2": "~0.13.0",
49 | "rollup-plugin-uglify": "~3.0.0",
50 | "ts-node": "~4.1.0",
51 | "tslint": "~5.8.0",
52 | "tslint-no-circular-imports": "~0.5.0",
53 | "typescript": "3.3.3333",
54 | "yalc": "~1.0.0-pre.21"
55 | },
56 | "scripts": {
57 | "build": "yarn gen-json --test && tsc && copyfiles -f src/data/compiled_api.* dist/src/data/",
58 | "build-npm": "./scripts/build-npm.sh",
59 | "link-local": "yalc link",
60 | "publish-local": "yarn build-npm && yalc push",
61 | "test": "yarn gen-json --test && karma start",
62 | "test-ci": "yarn gen-json --test && yarn build && yarn lint && yarn run-browserstack",
63 | "test-snippets": "ts-node ./scripts/test_snippets.ts",
64 | "run-browserstack": "karma start --singleRun --browsers='bs_firefox_mac,bs_chrome_mac' --reporters='dots,karma-typescript,BrowserStack'",
65 | "lint": "tslint -p . -t verbose",
66 | "make-version": "sh -c ./scripts/make-version",
67 | "gen-doc": "ts-node ./scripts/gen_doc.ts",
68 | "gen-json": "ts-node ./scripts/gen_json.ts",
69 | "pb2json": "ts-node ./tools/pb2json_converter.ts",
70 | "build-pip-package": "yarn gen-json --test && cd python && ./build-pip-package.sh --test /tmp/tfjs-pips",
71 | "run-python-tests": "yarn gen-json --test && cd python && ./run-python-tests.sh"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tfjs-converter/python/BUILD:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tensorflow/tfjs-converter/06b9238b6262d2b359e441998d7a7277a5196886/tfjs-converter/python/BUILD
--------------------------------------------------------------------------------
/tfjs-converter/python/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include requirements.txt
2 |
--------------------------------------------------------------------------------
/tfjs-converter/python/README.md:
--------------------------------------------------------------------------------
1 | # tensorflowjs: The Python Package for TensorFlow.js
2 |
3 | The **tensorflowjs** pip package contains libraries and tools for
4 | [TensorFlow.js](https://js.tensorflow.org).
5 |
--------------------------------------------------------------------------------
/tfjs-converter/python/WORKSPACE:
--------------------------------------------------------------------------------
1 | workspace(name = "org_tensorflow_js")
2 |
--------------------------------------------------------------------------------
/tfjs-converter/python/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tensorflow/tfjs-converter/06b9238b6262d2b359e441998d7a7277a5196886/tfjs-converter/python/__init__.py
--------------------------------------------------------------------------------
/tfjs-converter/python/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | -r requirements.txt
2 | pylint==1.8.3
3 |
--------------------------------------------------------------------------------
/tfjs-converter/python/requirements.txt:
--------------------------------------------------------------------------------
1 | h5py==2.8.0
2 | keras==2.2.4
3 | numpy==1.16.4
4 | six==1.11.0
5 | tensorflow==1.14.0
6 | tensorflow-hub==0.5.0
7 |
--------------------------------------------------------------------------------
/tfjs-converter/python/run-python-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Copyright 2018 Google LLC
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # ==============================================================================
16 |
17 | # A script that runs all Python unit tests in tfjs-layers.
18 |
19 | set -e
20 |
21 | SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22 |
23 | TEST_FILES="$(find "${SCRIPTS_DIR}" -name '*_test.py')"
24 |
25 | pip install virtualenv
26 |
27 | TMP_VENV_DIR="$(mktemp -d --suffix=_venv)"
28 | virtualenv -p "python" "${TMP_VENV_DIR}"
29 | source "${TMP_VENV_DIR}/bin/activate"
30 |
31 | pip install -r "${SCRIPTS_DIR}/requirements-dev.txt"
32 |
33 | cd "${SCRIPTS_DIR}"
34 | pylint --rcfile=.pylintrc tensorflowjs
35 |
36 | export PYTHONPATH=".:${PYTHONPATH}"
37 | for TEST_FILE in ${TEST_FILES}; do
38 | echo
39 | echo "====== Running test: ${TEST_FILE} ======"
40 | echo
41 | python "${TEST_FILE}"
42 | done
43 |
44 | echo
45 | echo "All tests passed."
46 | echo
47 |
48 | deactivate
49 | rm -rf "${TMP_VENV_DIR}"
50 |
--------------------------------------------------------------------------------
/tfjs-converter/python/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
--------------------------------------------------------------------------------
/tfjs-converter/python/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Google LLC. All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 | """Build pip wheel for model_converter."""
16 |
17 | import os
18 | import setuptools
19 | from tensorflowjs import version
20 |
21 | DIR_NAME = os.path.dirname(__file__)
22 |
23 | def _get_requirements(file):
24 | "Reads the requirements file and returns the packages"
25 | with open(os.path.join(DIR_NAME, file), 'r') as requirements:
26 | return requirements.readlines()
27 |
28 | CONSOLE_SCRIPTS = [
29 | 'tensorflowjs_converter = tensorflowjs.converters.converter:pip_main',
30 | ]
31 |
32 | setuptools.setup(
33 | name='tensorflowjs',
34 | version=version.version,
35 | description='Python Libraries and Tools for TensorFlow.js',
36 | url='https://js.tensorflow.org/',
37 | author='Google LLC',
38 | author_email='opensource@google.com',
39 | classifiers=[
40 | 'Development Status :: 3 - Alpha',
41 | 'Intended Audience :: Developers',
42 | 'Intended Audience :: Education',
43 | 'Intended Audience :: Science/Research',
44 | 'License :: OSI Approved :: Apache Software License',
45 | 'Programming Language :: JavaScript',
46 | 'Programming Language :: Python :: 2',
47 | 'Programming Language :: Python :: 3',
48 | 'Topic :: Scientific/Engineering',
49 | 'Topic :: Scientific/Engineering :: Mathematics',
50 | 'Topic :: Scientific/Engineering :: Artificial Intelligence',
51 | 'Topic :: Software Development',
52 | 'Topic :: Software Development :: Libraries',
53 | 'Topic :: Software Development :: Libraries :: Python Modules',
54 | ],
55 | py_modules=[
56 | 'tensorflowjs',
57 | 'tensorflowjs.version',
58 | 'tensorflowjs.quantization',
59 | 'tensorflowjs.read_weights',
60 | 'tensorflowjs.write_weights',
61 | 'tensorflowjs.converters',
62 | 'tensorflowjs.converters.common',
63 | 'tensorflowjs.converters.converter',
64 | 'tensorflowjs.converters.keras_h5_conversion',
65 | 'tensorflowjs.converters.keras_tfjs_loader',
66 | 'tensorflowjs.converters.tf_saved_model_conversion_v2',
67 | ],
68 | include_package_data=True,
69 | packages=['tensorflowjs/op_list'],
70 | package_data={
71 | 'tensorflowjs/op_list': ['*.json']
72 | },
73 | install_requires=_get_requirements('requirements.txt'),
74 | entry_points={
75 | 'console_scripts': CONSOLE_SCRIPTS,
76 | },
77 | license='Apache 2.0',
78 | keywords='tensorflow javascript machine deep learning converter',
79 | )
80 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/BUILD:
--------------------------------------------------------------------------------
1 | package(
2 | default_visibility = [
3 | "//tensorflowjs:__subpackages__",
4 | ],
5 | )
6 |
7 | licenses(["notice"]) # Apache 2.0
8 |
9 | exports_files(["LICENSE"])
10 |
11 | py_library(
12 | name = "tensorflowjs",
13 | srcs = ["__init__.py"],
14 | srcs_version = "PY2AND3",
15 | deps = [
16 | ":quantization",
17 | ":version",
18 | "//tensorflowjs/converters:converter"
19 | ],
20 | visibility = ["//visibility:public"],
21 | )
22 |
23 | py_library(
24 | name = "expect_h5py_installed",
25 | # This is a dummy rule used as a h5py dependency in open-source.
26 | # We expect h5py to already be installed on the system, e.g. via
27 | # `pip install h5py`.
28 | )
29 |
30 | py_library(
31 | name = "expect_numpy_installed",
32 | # This is a dummy rule used as a numpy dependency in open-source.
33 | # We expect numpy to already be installed on the system, e.g. via
34 | # `pip install numpy`.
35 | )
36 |
37 | py_library(
38 | name = "expect_tensorflow_installed",
39 | # This is a dummy rule used as a tensorflow dependency in open-source.
40 | # We expect tensorflow to already be installed on the system, e.g. via
41 | # `pip install tensorflow` or `pip install tensorflow-gpu`.
42 | )
43 |
44 | py_library(
45 | name = "expect_tensorflow_hub_installed",
46 | # This is a dummy rule used as a tensorflow_hub dependency in open-source.
47 | # We expect tensorflow to already be installed on the system, e.g. via
48 | # `pip install tensorflow` or `pip install tensorflow-gpu`.
49 | )
50 |
51 | py_library(
52 | name = "quantization",
53 | srcs = ["quantization.py"],
54 | srcs_version = "PY2AND3",
55 | deps = [
56 | "//tensorflowjs:expect_numpy_installed",
57 | ],
58 | )
59 |
60 | py_library(
61 | name = "write_weights",
62 | srcs = ["write_weights.py"],
63 | srcs_version = "PY2AND3",
64 | deps = [
65 | ":quantization",
66 | ":read_weights",
67 | "//tensorflowjs:expect_numpy_installed",
68 | ],
69 | )
70 |
71 | py_library(
72 | name = "read_weights",
73 | srcs = ["read_weights.py"],
74 | srcs_version = "PY2AND3",
75 | deps = [
76 | ":quantization",
77 | "//tensorflowjs:expect_numpy_installed",
78 | ],
79 | )
80 |
81 | py_library(
82 | name = "version",
83 | srcs = ["version.py"],
84 | srcs_version = "PY2AND3",
85 | deps = [],
86 | )
87 |
88 | py_test(
89 | name = "quantization_test",
90 | srcs = ["quantization_test.py"],
91 | srcs_version = "PY2AND3",
92 | deps = [
93 | ":quantization",
94 | "//tensorflowjs:expect_numpy_installed",
95 | ],
96 | )
97 |
98 | py_test(
99 | name = "write_weights_test",
100 | srcs = ["write_weights_test.py"],
101 | srcs_version = "PY2AND3",
102 | deps = [
103 | ":write_weights",
104 | "//tensorflowjs:expect_numpy_installed",
105 | ],
106 | )
107 |
108 | py_test(
109 | name = "read_weights_test",
110 | srcs = ["read_weights_test.py"],
111 | srcs_version = "PY2AND3",
112 | deps = [
113 | ":read_weights",
114 | ":write_weights",
115 | "//tensorflowjs:expect_numpy_installed",
116 | ],
117 | )
118 |
119 | # A filegroup BUILD target that includes all the op list json files in the
120 | # the op_list/ folder. The op_list folder itself is a symbolic link to the
121 | # actual op_list folder under src/.
122 | filegroup(
123 | name = "op_list_jsons",
124 | srcs = glob(["op_list/*.json"]),
125 | )
126 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 |
16 | from __future__ import absolute_import
17 | from __future__ import division
18 | from __future__ import print_function
19 |
20 | # pylint: disable=unused-imports
21 | from tensorflowjs import converters
22 | from tensorflowjs import quantization
23 | from tensorflowjs import version
24 |
25 | __version__ = version.version
26 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/converters/BUILD:
--------------------------------------------------------------------------------
1 | package(
2 | default_visibility = [
3 | "//tensorflowjs:__subpackages__",
4 | ],
5 | licenses = ["notice"], # Apache 2.0
6 | )
7 |
8 | py_library(
9 | name = "common",
10 | srcs = ["common.py"],
11 | srcs_version = "PY2AND3",
12 | deps = ["//tensorflowjs:version"],
13 | )
14 |
15 | py_library(
16 | name = "keras_h5_conversion",
17 | srcs = ["keras_h5_conversion.py"],
18 | srcs_version = "PY2AND3",
19 | deps = [
20 | ":common",
21 | "//tensorflowjs:expect_h5py_installed",
22 | "//tensorflowjs:expect_numpy_installed",
23 | "//tensorflowjs:write_weights",
24 | ],
25 | )
26 |
27 | py_test(
28 | name = "keras_h5_conversion_test",
29 | srcs = ["keras_h5_conversion_test.py"],
30 | srcs_version = "PY2AND3",
31 | deps = [
32 | ":keras_h5_conversion",
33 | "//tensorflowjs:expect_h5py_installed",
34 | "//tensorflowjs:expect_numpy_installed",
35 | "//tensorflowjs:expect_tensorflow_installed",
36 | "//tensorflowjs:version",
37 | ],
38 | )
39 |
40 | py_library(
41 | name = "keras_tfjs_loader",
42 | srcs = ["keras_tfjs_loader.py"],
43 | srcs_version = "PY2AND3",
44 | deps = [
45 | ":keras_h5_conversion",
46 | "//tensorflowjs:expect_numpy_installed",
47 | "//tensorflowjs:read_weights",
48 | ],
49 | )
50 |
51 | py_test(
52 | name = "keras_tfjs_loader_test",
53 | srcs = ["keras_tfjs_loader_test.py"],
54 | srcs_version = "PY2AND3",
55 | deps = [
56 | ":keras_h5_conversion",
57 | ":keras_tfjs_loader",
58 | "//tensorflowjs:expect_numpy_installed",
59 | "//tensorflowjs:expect_tensorflow_installed",
60 | ],
61 | )
62 |
63 | py_test(
64 | name = "tf_saved_model_conversion_v2_test",
65 | srcs = ["tf_saved_model_conversion_v2_test.py"],
66 | srcs_version = "PY2AND3",
67 | deps = [
68 | ":tf_saved_model_conversion_v2",
69 | "//tensorflowjs:expect_tensorflow_installed",
70 | "//tensorflowjs:expect_tensorflow_hub_installed",
71 | ],
72 | )
73 |
74 | py_library(
75 | name = "tf_saved_model_conversion_v2",
76 | srcs = ["tf_saved_model_conversion_v2.py"],
77 | data = ["//tensorflowjs:op_list_jsons"],
78 | srcs_version = "PY2AND3",
79 | deps = [
80 | "//tensorflowjs:expect_numpy_installed",
81 | "//tensorflowjs:expect_tensorflow_installed",
82 | "//tensorflowjs:expect_tensorflow_hub_installed",
83 | "//tensorflowjs:version",
84 | "//tensorflowjs:write_weights",
85 | "//tensorflowjs/converters:common",
86 | ],
87 | )
88 |
89 | py_binary(
90 | name = "converter",
91 | srcs = ["converter.py"],
92 | srcs_version = "PY2AND3",
93 | deps = [
94 | ":keras_h5_conversion",
95 | ":keras_tfjs_loader",
96 | ":tf_saved_model_conversion_v2",
97 | "//tensorflowjs:expect_h5py_installed",
98 | "//tensorflowjs:expect_tensorflow_installed",
99 | "//tensorflowjs:version",
100 | ],
101 | visibility = ["//visibility:public"],
102 | )
103 |
104 | py_binary(
105 | name = "generate_test_model",
106 | srcs = ["generate_test_model.py"],
107 | testonly = True,
108 | srcs_version = "PY2AND3",
109 | deps = [
110 | "//tensorflowjs:expect_tensorflow_installed",
111 | ]
112 | )
113 |
114 | py_test(
115 | name = "converter_test",
116 | srcs = ["converter_test.py"],
117 | srcs_version = "PY2AND3",
118 | deps = [
119 | ":converter",
120 | ":keras_tfjs_loader",
121 | "//tensorflowjs:expect_numpy_installed",
122 | "//tensorflowjs:expect_tensorflow_installed",
123 | "//tensorflowjs:version",
124 | ],
125 | )
126 |
127 | sh_test(
128 | name = "converter_binary_test",
129 | srcs = ["converter_binary_test.sh"],
130 | data = [
131 | ":converter",
132 | ":generate_test_model",
133 | ],
134 | )
135 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/converters/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 |
16 | from __future__ import absolute_import
17 | from __future__ import division
18 | from __future__ import print_function
19 |
20 | # pylint: disable=unused-imports,line-too-long
21 | from tensorflowjs.converters.keras_h5_conversion import save_keras_model
22 | from tensorflowjs.converters.keras_tfjs_loader import deserialize_keras_model
23 | from tensorflowjs.converters.keras_tfjs_loader import load_keras_model
24 | from tensorflowjs.converters.tf_saved_model_conversion_v2 import convert_tf_saved_model
25 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/converters/common.py:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 |
16 | from tensorflowjs import version
17 |
18 |
19 | # File name for the indexing JSON file in an artifact directory.
20 | ARTIFACT_MODEL_JSON_FILE_NAME = 'model.json'
21 |
22 | # JSON string keys for fields of the indexing JSON.
23 | ARTIFACT_MODEL_TOPOLOGY_KEY = 'modelTopology'
24 | ARTIFACT_WEIGHTS_MANIFEST_KEY = 'weightsManifest'
25 |
26 | FORMAT_KEY = 'format'
27 | TFJS_GRAPH_MODEL_FORMAT = 'graph-model'
28 | TFJS_LAYERS_MODEL_FORMAT = 'layers-model'
29 |
30 | GENERATED_BY_KEY = 'generatedBy'
31 | CONVERTED_BY_KEY = 'convertedBy'
32 |
33 |
34 | def get_converted_by():
35 | """Get the convertedBy string for storage in model artifacts."""
36 | return 'TensorFlow.js Converter v%s' % version.version
37 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/converters/converter_binary_test.sh:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 |
16 | set -e
17 |
18 | GENERATE_BIN="${TEST_SRCDIR}/org_tensorflow_js/tensorflowjs/converters/generate_test_model"
19 | CONVERTER_BIN="${TEST_SRCDIR}/org_tensorflow_js/tensorflowjs/converters/converter"
20 |
21 | # 1. Test tf_saved_model --> tfjs_graph_model conversion.
22 | SAVED_MODEL_DIR="$(mktemp -d)"
23 | echo "Genearting TF SavedModel for testing..."
24 | "${GENERATE_BIN}" "${SAVED_MODEL_DIR}" --model_type tf_saved_model
25 | echo "Done genearting TF SavedModel for testing at ${SAVED_MODEL_DIR}"
26 |
27 | OUTPUT_DIR="${SAVED_MODEL_DIR}_converted"
28 | "${CONVERTER_BIN}" \
29 | --input_format tf_saved_model \
30 | --output_format tfjs_graph_model \
31 | "${SAVED_MODEL_DIR}" \
32 | "${OUTPUT_DIR}"
33 |
34 | if [[ ! -d "${OUTPUT_DIR}" ]]; then
35 | echo "ERROR: Failed to find conversion output directory: ${OUTPUT_DIR}" 1>&2
36 | exit 1
37 | fi
38 |
39 | # Clean up files.
40 | rm -rf "${SAVED_MODEL_DIR}" "${OUTPUT_DIR}"
41 |
42 | # 2. Test keras HDF5 --> tfjs_layers_model conversion.
43 | KERAS_H5_PATH="$(mktemp).h5"
44 | echo "Genearting Keras HDF5 model for testing..."
45 | "${GENERATE_BIN}" "${KERAS_H5_PATH}" --model_type tf_keras_h5
46 | echo "Done genearting Keras HDF5 model for testing at ${KERAS_H5_PATH}"
47 |
48 | OUTPUT_H5_PATH="${KERAS_H5_PATH}_converted.h5"
49 | "${CONVERTER_BIN}" \
50 | --input_format keras \
51 | --output_format tfjs_layers_model \
52 | "${KERAS_H5_PATH}" \
53 | "${OUTPUT_H5_PATH}"
54 |
55 | if [[ ! -d "${OUTPUT_H5_PATH}" ]]; then
56 | echo "ERROR: Failed to find conversion output directory: ${OUTPUT_H5_PATH}" 1>&2
57 | exit 1
58 | fi
59 |
60 | # Clean up files.
61 | rm -rf "${KERAS_H5_PATH}" "${OUTPUT_H5_PATH}"
62 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/converters/generate_test_model.py:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 | """A binary that generates saved model artifacts for testing."""
16 |
17 | from __future__ import absolute_import
18 | from __future__ import division
19 | from __future__ import print_function
20 |
21 | import argparse
22 | import os
23 | import sys
24 |
25 | import tensorflow as tf
26 |
27 | tf.enable_eager_execution()
28 |
29 |
30 | def parse_args():
31 | parser = argparse.ArgumentParser(
32 | 'Generates saved model artifacts for testing.')
33 | parser.add_argument(
34 | 'output_path',
35 | type=str,
36 | help='Model output path.')
37 | parser.add_argument(
38 | '--model_type',
39 | type=str,
40 | required=True,
41 | choices=set(['tf_keras_h5', 'tf_saved_model']),
42 | help='Model format to generate.')
43 | return parser.parse_known_args()
44 |
45 |
46 | def main(_):
47 |
48 | if args.model_type == 'tf_keras_h5':
49 | model = tf.keras.Sequential()
50 | model.add(tf.keras.layers.Dense(5, activation='relu', input_shape=(8,)))
51 | model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
52 | model.save(os.path.join(args.output_path))
53 | elif args.model_type == 'tf_saved_model':
54 | class TimesThreePlusOne(tf.train.Checkpoint):
55 |
56 | @tf.function(input_signature=[
57 | tf.TensorSpec(shape=None, dtype=tf.float32)])
58 | def compute(self, x):
59 | return x * 3.0 + 1.0
60 |
61 | tf.saved_model.save(TimesThreePlusOne(), args.output_path)
62 | else:
63 | raise ValueError('Unrecognized model type: %s' % args.model_type)
64 |
65 |
66 | if __name__ == '__main__':
67 | args, unparsed = parse_args()
68 | tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
69 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/dynamic.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "NonMaxSuppressionV2",
4 | "category": "dynamic",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "boxes",
9 | "type": "tensor"
10 | },
11 | {
12 | "start": 1,
13 | "name": "scores",
14 | "type": "tensor"
15 | },
16 | {
17 | "start": 2,
18 | "name": "maxOutputSize",
19 | "type": "number"
20 | },
21 | {
22 | "start": 3,
23 | "name": "iouThreshold",
24 | "type": "number"
25 | }
26 | ]
27 | },
28 | {
29 | "tfOpName": "NonMaxSuppressionV3",
30 | "category": "dynamic",
31 | "inputs": [
32 | {
33 | "start": 0,
34 | "name": "boxes",
35 | "type": "tensor"
36 | },
37 | {
38 | "start": 1,
39 | "name": "scores",
40 | "type": "tensor"
41 | },
42 | {
43 | "start": 2,
44 | "name": "maxOutputSize",
45 | "type": "number"
46 | },
47 | {
48 | "start": 3,
49 | "name": "iouThreshold",
50 | "type": "number"
51 | },
52 | {
53 | "start": 4,
54 | "name": "scoreThreshold",
55 | "type": "number"
56 | }
57 | ]
58 | },
59 | {
60 | "tfOpName": "Where",
61 | "category": "dynamic",
62 | "inputs": [
63 | {
64 | "start": 0,
65 | "name": "condition",
66 | "type": "tensor"
67 | }
68 | ],
69 | "attrs": [
70 | {
71 | "tfName": "T",
72 | "name": "dtype",
73 | "type": "dtype",
74 | "notSupported": true
75 | }
76 | ]
77 | },
78 | {
79 | "tfOpName": "ListDiff",
80 | "category": "dynamic",
81 | "inputs": [
82 | {
83 | "start": 0,
84 | "name": "x",
85 | "type": "tensor"
86 | },
87 | {
88 | "start": 1,
89 | "name": "y",
90 | "type": "tensor"
91 | }
92 | ],
93 | "attrs": [
94 | {
95 | "tfName": "T",
96 | "name": "dtype",
97 | "type": "dtype",
98 | "notSupported": true
99 | }
100 | ]
101 | }
102 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/evaluation.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "TopKV2",
4 | "category": "evaluation",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "x",
9 | "type": "tensor"
10 | },
11 | {
12 | "start": 1,
13 | "name": "k",
14 | "type": "number"
15 | }
16 | ],
17 | "attrs": [
18 | {
19 | "tfName": "sorted",
20 | "name": "sorted",
21 | "type": "bool"
22 | }
23 | ]
24 | }
25 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/graph.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "PlaceholderWithDefault",
4 | "category": "graph",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "default",
9 | "type": "tensor"
10 | }
11 | ],
12 | "attrs": [
13 | {
14 | "tfName": "shape",
15 | "name": "shape",
16 | "type": "shape"
17 | },
18 | {
19 | "tfName": "dtype",
20 | "name": "dtype",
21 | "type": "dtype"
22 | }
23 | ]
24 | },
25 | {
26 | "tfOpName": "Placeholder",
27 | "category": "graph",
28 | "attrs": [
29 | {
30 | "tfName": "shape",
31 | "name": "shape",
32 | "type": "shape"
33 | },
34 | {
35 | "tfName": "dtype",
36 | "name": "dtype",
37 | "type": "dtype"
38 | }
39 | ]
40 | },
41 | {
42 | "tfOpName": "Const",
43 | "category": "graph"
44 | },
45 | {
46 | "tfOpName": "Identity",
47 | "category": "graph",
48 | "inputs": [
49 | {
50 | "start": 0,
51 | "name": "x",
52 | "type": "tensor"
53 | }
54 | ]
55 | },
56 | {
57 | "tfOpName": "IdentityN",
58 | "category": "graph",
59 | "inputs": [
60 | {
61 | "start": 0,
62 | "end": 0,
63 | "name": "x",
64 | "type": "tensors"
65 | }
66 | ]
67 | },
68 | {
69 | "tfOpName": "Snapshot",
70 | "category": "graph",
71 | "inputs": [
72 | {
73 | "start": 0,
74 | "name": "x",
75 | "type": "tensor"
76 | }
77 | ]
78 | },
79 | {
80 | "tfOpName": "Rank",
81 | "category": "graph",
82 | "inputs": [
83 | {
84 | "start": 0,
85 | "name": "x",
86 | "type": "tensor"
87 | }
88 | ]
89 | },
90 | {
91 | "tfOpName": "Size",
92 | "category": "graph",
93 | "inputs": [
94 | {
95 | "start": 0,
96 | "name": "x",
97 | "type": "tensor"
98 | }
99 | ]
100 | },
101 | {
102 | "tfOpName": "Shape",
103 | "category": "graph",
104 | "inputs": [
105 | {
106 | "start": 0,
107 | "name": "x",
108 | "type": "tensor"
109 | }
110 | ]
111 | },
112 | {
113 | "tfOpName": "ShapeN",
114 | "category": "graph",
115 | "inputs": [
116 | {
117 | "start": 0,
118 | "end": 0,
119 | "name": "x",
120 | "type": "tensors"
121 | }
122 | ]
123 | },
124 | {
125 | "tfOpName": "Print",
126 | "category": "graph",
127 | "inputs": [
128 | {
129 | "start": 0,
130 | "name": "x",
131 | "type": "tensor"
132 | },
133 | {
134 | "start": 1,
135 | "name": "data",
136 | "type": "tensors"
137 | }
138 | ],
139 | "attrs": [
140 | {
141 | "tfName": "message",
142 | "name": "message",
143 | "type": "string"
144 | },
145 | {
146 | "tfName": "first_n",
147 | "name": "firstN",
148 | "type": "number",
149 | "notSupported": true
150 | },
151 | {
152 | "tfName": "summarize",
153 | "name": "summarize",
154 | "type": "number",
155 | "defaultValue": 3
156 | }
157 | ]
158 | },
159 | {
160 | "tfOpName": "NoOp",
161 | "category": "graph",
162 | "inputs": []
163 | },
164 | {
165 | "tfOpName": "StopGradient",
166 | "category": "graph",
167 | "inputs": [
168 | {
169 | "start": 0,
170 | "name": "x",
171 | "type": "tensor"
172 | }
173 | ]
174 | },
175 | {
176 | "tfOpName": "FakeQuantWithMinMaxVars",
177 | "category": "graph",
178 | "inputs": [
179 | {
180 | "start": 0,
181 | "name": "x",
182 | "type": "tensor"
183 | }
184 | ],
185 | "attrs": [
186 | {
187 | "tfName": "min",
188 | "name": "min",
189 | "type": "number"
190 | },
191 | {
192 | "tfName": "max",
193 | "name": "max",
194 | "type": "number"
195 | }
196 | ]
197 | }
198 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/image.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "ResizeBilinear",
4 | "category": "image",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "images",
9 | "type": "tensor"
10 | },
11 | {
12 | "start": 1,
13 | "name": "size",
14 | "type": "number[]"
15 | }
16 | ],
17 | "attrs": [
18 | {
19 | "tfName": "align_corners",
20 | "name": "alignCorners",
21 | "type": "bool"
22 | },
23 | {
24 | "tfName": "T",
25 | "name": "dtype",
26 | "type": "dtype",
27 | "notSupported": true
28 | }
29 | ]
30 | },
31 | {
32 | "tfOpName": "ResizeNearestNeighbor",
33 | "category": "image",
34 | "inputs": [
35 | {
36 | "start": 0,
37 | "name": "images",
38 | "type": "tensor"
39 | },
40 | {
41 | "start": 1,
42 | "name": "size",
43 | "type": "number[]"
44 | }
45 | ],
46 | "attrs": [
47 | {
48 | "tfName": "align_corners",
49 | "name": "alignCorners",
50 | "type": "bool"
51 | },
52 | {
53 | "tfName": "T",
54 | "name": "dtype",
55 | "type": "dtype",
56 | "notSupported": true
57 | }
58 | ]
59 | },
60 | {
61 | "tfOpName": "CropAndResize",
62 | "category": "image",
63 | "inputs": [
64 | {
65 | "start": 0,
66 | "name": "image",
67 | "type": "tensor"
68 | },
69 | {
70 | "start": 1,
71 | "name": "boxes",
72 | "type": "tensor"
73 | },
74 | {
75 | "start": 2,
76 | "name": "boxInd",
77 | "type": "tensor"
78 | },
79 | {
80 | "start": 3,
81 | "name": "cropSize",
82 | "type": "number[]"
83 | }
84 | ],
85 | "attrs": [
86 | {
87 | "tfName": "method",
88 | "name": "method",
89 | "type": "string"
90 | },
91 | {
92 | "tfName": "extrapolation_value",
93 | "name": "extrapolationValue",
94 | "type": "number"
95 | }
96 | ]
97 | }
98 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/matrices.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "MatMul",
4 | "category": "matrices",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "a",
9 | "type": "tensor"
10 | },
11 | {
12 | "start": 1,
13 | "name": "b",
14 | "type": "tensor"
15 | }
16 | ],
17 | "attrs": [
18 | {
19 | "tfName": "transpose_a",
20 | "name": "transposeA",
21 | "type": "bool",
22 | "defaultValue": false
23 | },
24 | {
25 | "tfName": "transpose_b",
26 | "name": "transposeB",
27 | "type": "bool",
28 | "defaultValue": false
29 | },
30 | {
31 | "tfName": "T",
32 | "name": "dtype",
33 | "type": "dtype",
34 | "notSupported": true
35 | }
36 | ]
37 | },
38 | {
39 | "tfOpName": "BatchMatMul",
40 | "category": "matrices",
41 | "inputs": [
42 | {
43 | "start": 0,
44 | "name": "a",
45 | "type": "tensor"
46 | },
47 | {
48 | "start": 1,
49 | "name": "b",
50 | "type": "tensor"
51 | }
52 | ],
53 | "attrs": [
54 | {
55 | "tfName": "adj_x",
56 | "name": "transposeA",
57 | "type": "bool",
58 | "defaultValue": false
59 | },
60 | {
61 | "tfName": "adj_y",
62 | "name": "transposeB",
63 | "type": "bool",
64 | "defaultValue": false
65 | },
66 | {
67 | "tfName": "T",
68 | "name": "dtype",
69 | "type": "dtype",
70 | "notSupported": true
71 | }
72 | ]
73 | },
74 | {
75 | "tfOpName": "BatchMatMulV2",
76 | "category": "matrices",
77 | "inputs": [
78 | {
79 | "start": 0,
80 | "name": "a",
81 | "type": "tensor"
82 | },
83 | {
84 | "start": 1,
85 | "name": "b",
86 | "type": "tensor"
87 | }
88 | ],
89 | "attrs": [
90 | {
91 | "tfName": "adj_x",
92 | "name": "transposeA",
93 | "type": "bool",
94 | "defaultValue": false
95 | },
96 | {
97 | "tfName": "adj_y",
98 | "name": "transposeB",
99 | "type": "bool",
100 | "defaultValue": false
101 | },
102 | {
103 | "tfName": "T",
104 | "name": "dtype",
105 | "type": "dtype",
106 | "notSupported": true
107 | }
108 | ]
109 | },
110 | {
111 | "tfOpName": "Transpose",
112 | "category": "matrices",
113 | "inputs": [
114 | {
115 | "start": 0,
116 | "name": "x",
117 | "type": "tensor"
118 | },
119 | {
120 | "start": 1,
121 | "name": "perm",
122 | "type": "number[]"
123 | }
124 | ],
125 | "attrs": [
126 | {
127 | "tfName": "T",
128 | "name": "dtype",
129 | "type": "dtype",
130 | "notSupported": true
131 | }
132 | ]
133 | }
134 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/reduction.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "Max",
4 | "category": "reduction",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "x",
9 | "type": "tensor"
10 | },
11 | {
12 | "start": 1,
13 | "name": "axis",
14 | "type": "number[]"
15 | }
16 | ],
17 | "attrs": [
18 | {
19 | "tfName": "keep_dims",
20 | "name": "keepDims",
21 | "type": "bool"
22 | }
23 | ]
24 | },
25 | {
26 | "tfOpName": "Mean",
27 | "category": "reduction",
28 | "inputs": [
29 | {
30 | "start": 0,
31 | "name": "x",
32 | "type": "tensor"
33 | },
34 | {
35 | "start": 1,
36 | "name": "axis",
37 | "type": "number[]"
38 | }
39 | ],
40 | "attrs": [
41 | {
42 | "tfName": "keep_dims",
43 | "name": "keepDims",
44 | "type": "bool"
45 | }
46 | ]
47 | },
48 | {
49 | "tfOpName": "Min",
50 | "category": "reduction",
51 | "inputs": [
52 | {
53 | "start": 0,
54 | "name": "x",
55 | "type": "tensor"
56 | },
57 | {
58 | "start": 1,
59 | "name": "axis",
60 | "type": "number[]"
61 | }
62 | ],
63 | "attrs": [
64 | {
65 | "tfName": "keep_dims",
66 | "name": "keepDims",
67 | "type": "bool"
68 | }
69 | ]
70 | },
71 | {
72 | "tfOpName": "Sum",
73 | "category": "reduction",
74 | "inputs": [
75 | {
76 | "start": 0,
77 | "name": "x",
78 | "type": "tensor"
79 | },
80 | {
81 | "start": 1,
82 | "name": "axis",
83 | "type": "number[]"
84 | }
85 | ],
86 | "attrs": [
87 | {
88 | "tfName": "keep_dims",
89 | "name": "keepDims",
90 | "type": "bool"
91 | }
92 | ]
93 | },
94 | {
95 | "tfOpName": "All",
96 | "category": "reduction",
97 | "inputs": [
98 | {
99 | "start": 0,
100 | "name": "x",
101 | "type": "tensor"
102 | },
103 | {
104 | "start": 1,
105 | "name": "axis",
106 | "type": "number[]"
107 | }
108 | ],
109 | "attrs": [
110 | {
111 | "tfName": "keep_dims",
112 | "name": "keepDims",
113 | "type": "bool"
114 | }
115 | ]
116 | },
117 | {
118 | "tfOpName": "Any",
119 | "category": "reduction",
120 | "inputs": [
121 | {
122 | "start": 0,
123 | "name": "x",
124 | "type": "tensor"
125 | },
126 | {
127 | "start": 1,
128 | "name": "axis",
129 | "type": "number[]"
130 | }
131 | ],
132 | "attrs": [
133 | {
134 | "tfName": "keep_dims",
135 | "name": "keepDims",
136 | "type": "bool"
137 | }
138 | ]
139 | },
140 | {
141 | "tfOpName": "ArgMax",
142 | "category": "reduction",
143 | "inputs": [
144 | {
145 | "start": 0,
146 | "name": "x",
147 | "type": "tensor"
148 | },
149 | {
150 | "start": 1,
151 | "name": "axis",
152 | "type": "number"
153 | }
154 | ]
155 | },
156 | {
157 | "tfOpName": "ArgMin",
158 | "category": "reduction",
159 | "inputs": [
160 | {
161 | "start": 0,
162 | "name": "x",
163 | "type": "tensor"
164 | },
165 | {
166 | "start": 1,
167 | "name": "axis",
168 | "type": "number"
169 | }
170 | ]
171 | },
172 | {
173 | "tfOpName": "Prod",
174 | "category": "reduction",
175 | "inputs": [
176 | {
177 | "start": 0,
178 | "name": "x",
179 | "type": "tensor"
180 | },
181 | {
182 | "start": 1,
183 | "name": "axis",
184 | "type": "number[]"
185 | }
186 | ],
187 | "attrs": [
188 | {
189 | "tfName": "keep_dims",
190 | "name": "keepDims",
191 | "type": "bool"
192 | }
193 | ]
194 | }
195 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/spectral.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "FFT",
4 | "category": "spectral",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "x",
9 | "type": "tensor"
10 | }
11 | ]
12 | },
13 | {
14 | "tfOpName": "IFFT",
15 | "category": "spectral",
16 | "inputs": [
17 | {
18 | "start": 0,
19 | "name": "x",
20 | "type": "tensor"
21 | }
22 | ]
23 | },
24 | {
25 | "tfOpName": "RFFT",
26 | "category": "spectral",
27 | "inputs": [
28 | {
29 | "start": 0,
30 | "name": "x",
31 | "type": "tensor"
32 | },
33 | {
34 | "start": 1,
35 | "name": "fft_length",
36 | "type": "number",
37 | "notSupported": true
38 | }
39 | ]
40 | },
41 | {
42 | "tfOpName": "IRFFT",
43 | "category": "spectral",
44 | "inputs": [
45 | {
46 | "start": 0,
47 | "name": "x",
48 | "type": "tensor"
49 | },
50 | {
51 | "start": 1,
52 | "name": "fft_length",
53 | "type": "number",
54 | "notSupported": true
55 | }
56 | ]
57 | }
58 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/op_list/transformation.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tfOpName": "Cast",
4 | "category": "transformation",
5 | "inputs": [
6 | {
7 | "start": 0,
8 | "name": "x",
9 | "type": "tensor"
10 | }
11 | ],
12 | "attrs": [
13 | {
14 | "tfName": "SrcT",
15 | "name": "sdtype",
16 | "type": "dtype",
17 | "notSupported": true
18 | },
19 | {
20 | "tfName": "DstT",
21 | "name": "dtype",
22 | "type": "dtype"
23 | }
24 | ]
25 | },
26 | {
27 | "tfOpName": "ExpandDims",
28 | "category": "transformation",
29 | "inputs": [
30 | {
31 | "start": 0,
32 | "name": "x",
33 | "type": "tensor"
34 | },
35 | {
36 | "start": 1,
37 | "name": "axis",
38 | "type": "number"
39 | }
40 | ]
41 | },
42 | {
43 | "tfOpName": "Pad",
44 | "category": "transformation",
45 | "inputs": [
46 | {
47 | "start": 0,
48 | "name": "x",
49 | "type": "tensor"
50 | },
51 | {
52 | "start": 1,
53 | "name": "padding",
54 | "type": "number[]"
55 | }
56 | ],
57 | "attrs": [
58 | {
59 | "tfName": "constant_value",
60 | "name": "constantValue",
61 | "type": "number",
62 | "defaultValue": 0
63 | }
64 | ]
65 | },
66 | {
67 | "tfOpName": "PadV2",
68 | "category": "transformation",
69 | "inputs": [
70 | {
71 | "start": 0,
72 | "name": "x",
73 | "type": "tensor"
74 | },
75 | {
76 | "start": 1,
77 | "name": "padding",
78 | "type": "number[]"
79 | },
80 | {
81 | "start": 2,
82 | "name": "constantValue",
83 | "type": "number",
84 | "defaultValue": 0
85 | }
86 | ]
87 | },
88 | {
89 | "tfOpName": "Reshape",
90 | "category": "transformation",
91 | "inputs": [
92 | {
93 | "start": 0,
94 | "name": "x",
95 | "type": "tensor"
96 | },
97 | {
98 | "start": 1,
99 | "name": "shape",
100 | "type": "number[]"
101 | }
102 | ]
103 | },
104 | {
105 | "tfOpName": "Squeeze",
106 | "category": "transformation",
107 | "inputs": [
108 | {
109 | "start": 0,
110 | "name": "x",
111 | "type": "tensor"
112 | }
113 | ],
114 | "attrs": [
115 | {
116 | "tfName": "axis",
117 | "tfDeprecatedName": "squeeze_dims",
118 | "name": "axis",
119 | "type": "number[]"
120 | }
121 | ]
122 | },
123 | {
124 | "tfOpName": "SpaceToBatchND",
125 | "category": "transformation",
126 | "inputs": [
127 | {
128 | "start": 0,
129 | "name": "x",
130 | "type": "tensor"
131 | },
132 | {
133 | "start": 1,
134 | "name": "blockShape",
135 | "type": "number[]"
136 | },
137 | {
138 | "start": 2,
139 | "name": "paddings",
140 | "type": "number[]"
141 | }
142 | ]
143 | },
144 | {
145 | "tfOpName": "BatchToSpaceND",
146 | "category": "transformation",
147 | "inputs": [
148 | {
149 | "start": 0,
150 | "name": "x",
151 | "type": "tensor"
152 | },
153 | {
154 | "start": 1,
155 | "name": "blockShape",
156 | "type": "number[]"
157 | },
158 | {
159 | "start": 2,
160 | "name": "crops",
161 | "type": "number[]"
162 | }
163 | ]
164 | },
165 | {
166 | "tfOpName": "DepthToSpace",
167 | "category": "transformation",
168 | "inputs": [
169 | {
170 | "start": 0,
171 | "name": "x",
172 | "type": "tensor"
173 | }
174 | ],
175 | "attrs": [
176 | {
177 | "tfName": "block_size",
178 | "name": "blockSize",
179 | "type": "number"
180 | },
181 | {
182 | "tfName": "data_format",
183 | "name": "dataFormat",
184 | "type": "string"
185 | }
186 | ]
187 | }
188 | ]
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/quantization_test.py:
--------------------------------------------------------------------------------
1 | # Copyright 2018 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 | from __future__ import absolute_import
16 | from __future__ import division
17 | from __future__ import print_function
18 |
19 | import unittest
20 |
21 | import numpy as np
22 |
23 | from tensorflowjs import quantization
24 |
25 | class TestQuantizationUtil(unittest.TestCase):
26 |
27 | def _runQuantizeTest(
28 | self, range_min, range_max, data_dtype, quantization_dtype,
29 | expected_scale):
30 | d = np.arange(range_min, range_max + 1, dtype=data_dtype)
31 | q, s, m = quantization.quantize_weights(d, quantization_dtype)
32 | self.assertAlmostEqual(s, expected_scale)
33 | self.assertEqual(q.dtype, quantization_dtype)
34 |
35 | de_q = quantization.dequantize_weights(q, s, m, data_dtype)
36 | np.testing.assert_allclose(de_q, d)
37 |
38 | if range_min <= 0 <= range_max:
39 | d_0 = np.zeros(1, data_dtype)
40 | q_0 = np.round((d_0 - m) / s).astype(quantization_dtype)
41 | self.assertEqual(
42 | quantization.dequantize_weights(q_0, s, m, data_dtype), d_0)
43 |
44 | def testAllEqual(self):
45 | d = np.ones(5, dtype=np.float32)
46 | q, s, m = quantization.quantize_weights(d, np.uint8)
47 | self.assertEqual(s, 1.0)
48 | self.assertEqual(q.dtype, np.uint8)
49 |
50 | de_q = quantization.dequantize_weights(q, s, m, np.float32)
51 | np.testing.assert_array_equal(de_q, d)
52 |
53 | def testQuantizeNegativeFloats(self):
54 | self._runQuantizeTest(-3, -1, np.float32, np.uint8, expected_scale=2/255)
55 | self._runQuantizeTest(-3, -1, np.float32, np.uint16, expected_scale=2/65536)
56 |
57 | def testQuantizeNegativeAndZeroFloats(self):
58 | self._runQuantizeTest(-3, 0, np.float32, np.uint8, expected_scale=3/255)
59 | self._runQuantizeTest(-3, 0, np.float32, np.uint16, expected_scale=3/65536)
60 |
61 | def testQuantizeNegativeAndPositiveFloats(self):
62 | self._runQuantizeTest(-3, 3, np.float32, np.uint8, expected_scale=6/255)
63 | self._runQuantizeTest(-3, 3, np.float32, np.uint16, expected_scale=6/65536)
64 |
65 | def testQuantizeZeroAndPositiveFloats(self):
66 | self._runQuantizeTest(0, 3, np.float32, np.uint8, expected_scale=3/255)
67 | self._runQuantizeTest(0, 3, np.float32, np.uint16, expected_scale=3/65536)
68 |
69 | def testQuantizePositiveFloats(self):
70 | self._runQuantizeTest(1, 3, np.float32, np.uint8, expected_scale=2/255)
71 | self._runQuantizeTest(1, 3, np.float32, np.uint16, expected_scale=2/65536)
72 |
73 | def testQuantizeNegativeInts(self):
74 | self._runQuantizeTest(-3, -1, np.int32, np.uint8, expected_scale=2/255)
75 | self._runQuantizeTest(-3, -1, np.int32, np.uint16, expected_scale=2/65536)
76 |
77 | def testQuantizeNegativeAndZeroInts(self):
78 | self._runQuantizeTest(-3, 0, np.int32, np.uint8, expected_scale=3/255)
79 | self._runQuantizeTest(-3, 0, np.int32, np.uint16, expected_scale=3/65536)
80 |
81 | def testQuantizeNegativeAndPositiveInts(self):
82 | self._runQuantizeTest(-3, 3, np.int32, np.uint8, expected_scale=6/255)
83 | self._runQuantizeTest(-3, 3, np.int32, np.uint16, expected_scale=6/65536)
84 |
85 | def testQuantizeZeroAndPositiveInts(self):
86 | self._runQuantizeTest(0, 3, np.int32, np.uint8, expected_scale=3/255)
87 | self._runQuantizeTest(0, 3, np.int32, np.uint16, expected_scale=3/65536)
88 |
89 | def testQuantizePositiveInts(self):
90 | self._runQuantizeTest(1, 3, np.int32, np.uint8, expected_scale=2/255)
91 | self._runQuantizeTest(1, 3, np.int32, np.uint16, expected_scale=2/65536)
92 |
93 |
94 | if __name__ == '__main__':
95 | unittest.main()
96 |
--------------------------------------------------------------------------------
/tfjs-converter/python/tensorflowjs/version.py:
--------------------------------------------------------------------------------
1 | # @license See the LICENSE file.
2 |
3 | # This code is auto-generated, do not modify this file!
4 | version = '1.2.7'
5 |
--------------------------------------------------------------------------------
/tfjs-converter/rollup.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import node from 'rollup-plugin-node-resolve';
19 | import typescript from 'rollup-plugin-typescript2';
20 | import commonjs from 'rollup-plugin-commonjs';
21 | import uglify from 'rollup-plugin-uglify';
22 |
23 | const PREAMBLE = `/**
24 | * @license
25 | * Copyright ${(new Date).getFullYear()} Google LLC. All Rights Reserved.
26 | * Licensed under the Apache License, Version 2.0 (the "License");
27 | * you may not use this file except in compliance with the License.
28 | * You may obtain a copy of the License at
29 | *
30 | * http://www.apache.org/licenses/LICENSE-2.0
31 | *
32 | * Unless required by applicable law or agreed to in writing, software
33 | * distributed under the License is distributed on an "AS IS" BASIS,
34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35 | * See the License for the specific language governing permissions and
36 | * limitations under the License.
37 | * =============================================================================
38 | */`;
39 |
40 | function minify() {
41 | return uglify({
42 | output: {preamble: PREAMBLE}
43 | });
44 | }
45 |
46 | function config({plugins = [], output = {}}) {
47 | return {
48 | input: 'src/index.ts',
49 | plugins: [
50 | typescript({
51 | tsconfigOverride: {compilerOptions: {module: 'ES2015'}}
52 | }),
53 | node(),
54 | // Polyfill require() from dependencies.
55 | commonjs({
56 | namedExports: {
57 | './src/data/compiled_api.js': ['tensorflow'],
58 | './node_modules/protobufjs/minimal.js': ['roots', 'Reader', 'util']
59 | }
60 | }),
61 | ...plugins
62 | ],
63 | output: {
64 | banner: PREAMBLE,
65 | sourcemap: true,
66 | globals: {'@tensorflow/tfjs-core': 'tf'},
67 | ...output
68 | },
69 | external: ['@tensorflow/tfjs-core'],
70 | onwarn: warning => {
71 | let {code} = warning;
72 | if (code === 'CIRCULAR_DEPENDENCY' ||
73 | code === 'CIRCULAR' || code === 'EVAL') {
74 | return;
75 | }
76 | console.warn('WARNING: ', warning.toString());
77 | }
78 | };
79 | }
80 |
81 | export default [
82 | config({
83 | output: {
84 | format: 'umd',
85 | name: 'tf',
86 | extend: true,
87 | file: 'dist/tf-converter.js'
88 | }
89 | }),
90 | config({
91 | plugins: [minify()],
92 | output: {
93 | format: 'umd',
94 | name: 'tf',
95 | extend: true,
96 | file: 'dist/tf-converter.min.js'
97 | }
98 | }),
99 | config({
100 | plugins: [minify()],
101 | output: {
102 | format: 'es',
103 | file: 'dist/tf-converter.esm.js'
104 | }
105 | })
106 | ];
107 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/build-npm.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Copyright 2018 Google LLC. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # =============================================================================
16 |
17 | # Exit immediately if a command exits with a non-zero status.
18 | set -e
19 |
20 | rimraf dist/
21 | yarn
22 | yarn build
23 | rollup -c
24 |
25 | # Use minified files for miniprogram
26 | mkdir dist/miniprogram
27 | cp dist/tf-converter.min.js dist/miniprogram/index.js
28 | cp dist/tf-converter.min.js.map dist/miniprogram/index.js.map
29 |
30 | echo "Stored standalone library at dist/tf-converter(.min).js"
31 | npm pack
32 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/gen_json.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as fs from 'fs';
19 | import * as path from 'path';
20 |
21 | // tslint:disable-next-line:no-require-imports
22 | const deepEqual = require('deep-equal');
23 |
24 | /**
25 | * Converts the ts files in src/operations/op_list/* to json files and stores
26 | * them in python/tensorflowjs/op_list/. These are then used by the python
27 | * converter.
28 | *
29 | * If this script is called with the `--test` flag, will perform consistency
30 | * test between the two directories instead of actual file sync'ing.
31 | */
32 |
33 | // Make the directory python/tensorflowjs/op_list/ if it doesn't exist.
34 | const destDir = './python/tensorflowjs/op_list/';
35 | if (!fs.existsSync(destDir)) {
36 | fs.mkdirSync(destDir);
37 | }
38 |
39 | // Go over all .ts files in src/operations/op_list and convert them to json.
40 | const srcDir = './src/operations/op_list';
41 | const fileNames = fs.readdirSync(srcDir);
42 |
43 | const testing = process.argv.indexOf('--test') !== -1;
44 |
45 | const tsFilesNamesWithJSONs: string[] = [];
46 | fileNames.forEach(fileName => {
47 | const srcPath = path.join(srcDir, fileName);
48 | if (srcPath.endsWith('_test.ts')) {
49 | return;
50 | }
51 | const m = require('../' + srcPath);
52 | if (m.json == null) {
53 | console.log(`Ignored ${srcPath} due to absent "json" field.`);
54 | return;
55 | }
56 | tsFilesNamesWithJSONs.push(path.basename(srcPath));
57 | const destPath = path.join(destDir, fileName.replace('.ts', '.json'));
58 | if (testing) {
59 | if (!fs.existsSync(destPath) || !fs.lstatSync(destPath).isFile()) {
60 | throw new Error(
61 | `Op JSON consistency test failed: Missing file ${destPath}. ` +
62 | `You may want to run: yarn gen-json`);
63 | }
64 | const destJSON = JSON.parse(fs.readFileSync(destPath, {encoding: 'utf8'}));
65 | if (!deepEqual(m.json, destJSON)) {
66 | throw new Error(
67 | `JSON content of ${destPath} does not match TypeScript file ` +
68 | `${srcPath}. Run the following command to make sure they are ` +
69 | `in sync: yarn gen-json`);
70 | }
71 | } else {
72 | fs.writeFileSync(destPath, JSON.stringify(m.json, null, 2));
73 | console.log('Generated', destPath);
74 | }
75 | });
76 |
77 | if (testing) {
78 | const dirContent = fs.readdirSync(destDir);
79 | dirContent.forEach(itemPath => {
80 | if (!itemPath.endsWith('.json')) {
81 | throw new Error(
82 | `Found non-json file in directory ${destDir}: ${itemPath}`);
83 | }
84 | if (tsFilesNamesWithJSONs.indexOf(itemPath.replace('.json', '.ts')) ===
85 | -1) {
86 | throw new Error(`Found extraneous .json file in ${destDir}: ${itemPath}`);
87 | }
88 | });
89 | }
90 |
91 | console.log('Done!');
92 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/make-version:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | // Copyright 2018 Google LLC. All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | // =============================================================================
16 |
17 | var fs = require('fs');
18 |
19 | var version = JSON.parse(fs.readFileSync('package.json', 'utf8')).version;
20 |
21 | const npmVersionCode =
22 | `/** @license See the LICENSE file. */
23 |
24 | // This code is auto-generated, do not modify this file!
25 | const version = '${version}';
26 | export {version};
27 | `
28 |
29 | fs.writeFile('src/version.ts', npmVersionCode, err => {
30 | if (err != null) {
31 | throw new Error(`Could not save npm version file ${version}: ${err}`);
32 | }
33 | console.log(`Version file for npm version ${version} saved sucessfully.`);
34 | });
35 |
36 | const pipVersionCode =
37 | `# @license See the LICENSE file.
38 |
39 | # This code is auto-generated, do not modify this file!
40 | version = '${version}'
41 | `
42 | fs.writeFile('python/tensorflowjs/version.py', pipVersionCode, err => {
43 | if (err != null) {
44 | throw new Error(`Could not save pip version file ${version}: ${err}`);
45 | }
46 | console.log(`Version file for pip version ${version} saved sucessfully.`);
47 | });
48 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/publish-npm.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Copyright 2018 Google LLC. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # =============================================================================
16 |
17 | # Before you run this script, do this:
18 | # 1) Update the version in package.json
19 | # 2) Run ./scripts/make-version from the base dir of the project.
20 | # 3) Commit to the master branch.
21 |
22 | # Then:
23 | # 4) Run this script as `./scripts/publish-npm.sh` from the master branch.
24 |
25 | set -e
26 |
27 | BRANCH=`git rev-parse --abbrev-ref HEAD`
28 |
29 | if [ "$BRANCH" != "master" ]; then
30 | echo "Error: Switch to the master branch before tagging."
31 | exit
32 | fi
33 |
34 | yarn build-npm
35 | ./scripts/make-version # This is for safety in case you forgot to do 2).
36 | ./scripts/tag-version
37 | npm publish
38 | echo 'Yay! Published a new package to npm.'
39 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/tag-version:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | // Copyright 2018 Google LLC. All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | // =============================================================================
16 |
17 |
18 | // Run this script from the base directory (not the script directory):
19 | // ./scripts/tag-version
20 |
21 | var fs = require('fs');
22 | var exec = require('child_process').exec;
23 |
24 | var version = JSON.parse(fs.readFileSync('package.json', 'utf8')).version;
25 | var tag = `v${version}`;
26 |
27 | exec(`git tag ${tag}`, err => {
28 | if (err) {
29 | throw new Error(`Could not git tag with ${tag}: ${err.message}.`);
30 | }
31 | console.log(`Successfully tagged with ${tag}.`);
32 | });
33 |
34 | exec(`git push --tags`, err => {
35 | if (err) {
36 | throw new Error(`Could not push git tags: ${err.message}.`);
37 | }
38 | console.log(`Successfully pushed tags.`);
39 | });
40 |
--------------------------------------------------------------------------------
/tfjs-converter/scripts/test_snippets.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2019 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 | import {parseAndEvaluateSnippets} from '@tensorflow/tfjs-core/dist/scripts/test_snippets/util';
19 |
20 | import * as tfconv from '../src/index';
21 |
22 | const tf = {
23 | ...tfconv,
24 | ...tfc
25 | };
26 | parseAndEvaluateSnippets(tf);
27 |
--------------------------------------------------------------------------------
/tfjs-converter/src/data/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import {DataType, Tensor} from '@tensorflow/tfjs-core';
18 | import {TensorArray} from '../executor/tensor_array';
19 |
20 | export type NamedTensorMap = {
21 | [key: string]: Tensor
22 | };
23 |
24 | export type NamedTensorsMap = {
25 | [key: string]: Tensor[]
26 | };
27 |
28 | export type TensorArrayMap = {
29 | [key: number]: TensorArray
30 | };
31 |
32 | export interface TensorInfo {
33 | name: string;
34 | shape?: number[];
35 | dtype: DataType;
36 | }
37 |
--------------------------------------------------------------------------------
/tfjs-converter/src/executor/execution_context_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import {ExecutionContext} from './execution_context';
19 | import {TensorArray} from './tensor_array';
20 |
21 | let context: ExecutionContext;
22 | let tensorArray: TensorArray;
23 | describe('ExecutionContext', () => {
24 | beforeEach(() => {
25 | context = new ExecutionContext({}, {});
26 | });
27 |
28 | it('should initialize', () => {
29 | expect(context.currentContext).toEqual([
30 | {id: 0, frameName: '', iterationId: 0}
31 | ]);
32 | expect(context.currentContextId).toEqual('');
33 | });
34 |
35 | describe('tensor array', () => {
36 | beforeEach(() => {
37 | tensorArray = new TensorArray('', 'float32', 10, [1], true, true, true);
38 | });
39 |
40 | it('should be able to add tensor array', () => {
41 | context.addTensorArray(tensorArray);
42 | expect(context.getTensorArray(tensorArray.id)).toBe(tensorArray);
43 | });
44 |
45 | it('should be able to read tensor array', () => {
46 | expect(context.getTensorArray(tensorArray.id)).toBeUndefined();
47 | });
48 | });
49 |
50 | describe('enterFrame', () => {
51 | it('should add new Frame', () => {
52 | context.enterFrame('1');
53 | expect(context.currentContextId).toEqual('/1-0');
54 | expect(context.currentContext).toEqual([
55 | {id: 0, frameName: '', iterationId: 0},
56 | {id: 1, frameName: '1', iterationId: 0}
57 | ]);
58 | });
59 | });
60 |
61 | describe('exitFrame', () => {
62 | it('should remove Frame', () => {
63 | context.enterFrame('1');
64 | context.exitFrame();
65 |
66 | expect(context.currentContextId).toEqual('');
67 | expect(context.currentContext).toEqual([
68 | {id: 0, frameName: '', iterationId: 0}
69 | ]);
70 | });
71 |
72 | it('should remember previous Frame', () => {
73 | context.enterFrame('1');
74 | context.nextIteration();
75 | context.enterFrame('2');
76 | context.exitFrame();
77 |
78 | expect(context.currentContextId).toEqual('/1-1');
79 | expect(context.currentContext).toEqual([
80 | {id: 0, frameName: '', iterationId: 0},
81 | {id: 2, frameName: '1', iterationId: 1}
82 | ]);
83 | });
84 | });
85 |
86 | describe('nextIteration', () => {
87 | it('should increate iteration', () => {
88 | context.enterFrame('1');
89 | context.nextIteration();
90 |
91 | expect(context.currentContextId).toEqual('/1-1');
92 | expect(context.currentContext).toEqual([
93 | {id: 0, frameName: '', iterationId: 0},
94 | {id: 2, frameName: '1', iterationId: 1}
95 | ]);
96 | });
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/tfjs-converter/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | export {GraphModel, loadGraphModel} from './executor/graph_model';
18 | export {deregisterOp, registerOp} from './operations/custom_op/register';
19 | export {GraphNode, OpExecutor} from './operations/types';
20 | export {version as version_converter} from './version';
21 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/custom_op/node_value_impl.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2019 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import {DataType, Tensor} from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {getTensor} from '../executors/utils';
23 | import {getBoolArrayParam, getBoolParam, getDtypeArrayParam, getDtypeParam, getNumberParam, getNumericArrayParam, getStringArrayParam, getStringParam, getTensorShapeArrayParam, getTensorShapeParam} from '../operation_mapper';
24 | import {GraphNode, Node, ValueType} from '../types';
25 |
26 | /**
27 | * Helper class for lookup inputs and params for nodes in the model graph.
28 | */
29 | export class NodeValueImpl implements GraphNode {
30 | public readonly inputs: Tensor[] = [];
31 | public readonly attrs: {[key: string]: ValueType} = {};
32 | constructor(
33 | private node: Node, private tensorMap: NamedTensorsMap,
34 | private context: ExecutionContext) {
35 | this.inputs = node.inputNames.map(name => this.getInput(name));
36 | if (node.rawAttrs != null) {
37 | this.attrs = Object.keys(node.rawAttrs)
38 | .reduce((attrs: {[key: string]: ValueType}, key) => {
39 | attrs[key] = this.getAttr(key);
40 | return attrs;
41 | }, {});
42 | }
43 | }
44 |
45 | /**
46 | * Return the value of the attribute or input param.
47 | * @param name String: name of attribute or input param.
48 | */
49 | private getInput(name: string): Tensor {
50 | return getTensor(name, this.tensorMap, this.context);
51 | }
52 |
53 | /**
54 | * Return the value of the attribute or input param.
55 | * @param name String: name of attribute or input param.
56 | */
57 | private getAttr(name: string, defaultValue?: ValueType): ValueType {
58 | const value = this.node.rawAttrs[name];
59 | if (value.tensor != null) {
60 | return getTensor(name, this.tensorMap, this.context);
61 | }
62 | if (value.i != null || value.f != null) {
63 | return getNumberParam(this.node.rawAttrs, name, defaultValue as number);
64 | }
65 | if (value.s != null) {
66 | return getStringParam(this.node.rawAttrs, name, defaultValue as string);
67 | }
68 | if (value.b != null) {
69 | return getBoolParam(this.node.rawAttrs, name, defaultValue as boolean);
70 | }
71 | if (value.shape != null) {
72 | return getTensorShapeParam(
73 | this.node.rawAttrs, name, defaultValue as number[]);
74 | }
75 | if (value.type != null) {
76 | return getDtypeParam(this.node.rawAttrs, name, defaultValue as DataType);
77 | }
78 | if (value.list != null) {
79 | if (value.list.i != null || value.list.f != null) {
80 | return getNumericArrayParam(
81 | this.node.rawAttrs, name, defaultValue as number[]);
82 | }
83 | if (value.list.s != null) {
84 | return getStringArrayParam(
85 | this.node.rawAttrs, name, defaultValue as string[]);
86 | }
87 | if (value.list.shape != null) {
88 | return getTensorShapeArrayParam(
89 | this.node.rawAttrs, name, defaultValue as number[][]);
90 | }
91 | if (value.list.b != null) {
92 | return getBoolArrayParam(
93 | this.node.rawAttrs, name, defaultValue as boolean[]);
94 | }
95 | if (value.list.type != null) {
96 | return getDtypeArrayParam(
97 | this.node.rawAttrs, name, defaultValue as DataType[]);
98 | }
99 | }
100 |
101 | return defaultValue;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/custom_op/node_value_impl_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2019 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import {scalar, test_util} from '@tensorflow/tfjs-core';
18 |
19 | import * as tensorflow from '../../data/compiled_api';
20 | import {ExecutionContext} from '../../executor/execution_context';
21 | import {Node} from '../types';
22 |
23 | import {NodeValueImpl} from './node_value_impl';
24 |
25 | const NODE: Node = {
26 | name: 'test',
27 | op: 'const',
28 | category: 'custom',
29 | inputNames: ['a', 'b'],
30 | inputs: [],
31 | inputParams: {},
32 | attrParams: {},
33 | children: [],
34 | rawAttrs: {
35 | c: {tensor: {}},
36 | d: {i: 3},
37 | e: {s: 'TkhXQw=='},
38 | f: {type: tensorflow.DataType.DT_FLOAT},
39 | g: {b: true},
40 | h: {f: 4.5},
41 | i: {list: {i: [3, 6, 0]}},
42 | j: {list: {f: [4.5, 5.5, 0.0]}},
43 | k: {list: {s: ['TkhXQw==', 'TkhXQw==', '']}},
44 | l: {
45 | list:
46 | {type: [tensorflow.DataType.DT_FLOAT, tensorflow.DataType.DT_INT32]}
47 | },
48 | m: {shape: {dim: [{name: 'a', size: 1}, {name: 'b', size: 2}]}},
49 | n: {
50 | list: {
51 | shape: [
52 | {dim: [{name: 'a', size: 1}, {name: 'b', size: 2}]},
53 | {dim: [{name: 'c', size: 2}, {name: 'd', size: 3}]}
54 | ]
55 | }
56 | },
57 | o: {list: {b: [true, false]}}
58 | }
59 | };
60 | const TENSOR_MAP = {
61 | 'a': [scalar(1)],
62 | 'b': [scalar(2)],
63 | 'test': [scalar(3)]
64 | };
65 |
66 | let nodeValue: NodeValueImpl;
67 | describe('NodeValueImpl', () => {
68 | beforeEach(() => {
69 | nodeValue =
70 | new NodeValueImpl(NODE, TENSOR_MAP, new ExecutionContext({}, {}));
71 | });
72 | describe('getInput', () => {
73 | it('should find tensor from tensormap', async () => {
74 | const result = nodeValue.inputs[0];
75 | test_util.expectArraysClose(await result.data(), [1]);
76 |
77 | const result2 = nodeValue.inputs[1];
78 | test_util.expectArraysClose(await result2.data(), [2]);
79 | });
80 | });
81 | describe('getAttr', () => {
82 | it('should parse number', () => {
83 | expect(nodeValue.attrs['d']).toEqual(3);
84 | expect(nodeValue.attrs['h']).toEqual(4.5);
85 | });
86 | it('should parse number[]', () => {
87 | expect(nodeValue.attrs['i']).toEqual([3, 6, 0]);
88 | expect(nodeValue.attrs['j']).toEqual([4.5, 5.5, 0.0]);
89 | });
90 | it('should parse string', () => {
91 | expect(nodeValue.attrs['e']).toEqual('nhwc');
92 | });
93 | it('should parse string[]', () => {
94 | expect(nodeValue.attrs['k']).toEqual(['nhwc', 'nhwc', '']);
95 | });
96 | it('should parse boolean', () => {
97 | expect(nodeValue.attrs['g']).toEqual(true);
98 | });
99 | it('should parse boolean[]', () => {
100 | expect(nodeValue.attrs['o']).toEqual([true, false]);
101 | });
102 | it('should parse dtype', () => {
103 | expect(nodeValue.attrs['f']).toEqual('float32');
104 | });
105 | it('should parse dtype[]', () => {
106 | expect(nodeValue.attrs['l']).toEqual(['float32', 'int32']);
107 | });
108 | it('should parse tensor shape', () => {
109 | expect(nodeValue.attrs['m']).toEqual([1, 2]);
110 | });
111 | it('should parse tensor shape[]', () => {
112 | expect(nodeValue.attrs['n']).toEqual([[1, 2], [2, 3]]);
113 | });
114 | });
115 | });
116 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/custom_op/register.ts:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @license
4 | * Copyright 2019 Google LLC. All Rights Reserved.
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | * =============================================================================
17 | */
18 |
19 | import {OpExecutor, OpMapper} from '../types';
20 |
21 | const CUSTOM_OPS: {[key: string]: OpMapper} = {};
22 |
23 | /**
24 | * Register an Op for graph model executor. This allow you to register
25 | * TensorFlow custom op or override existing op.
26 | *
27 | * Here is an example of registering a new MatMul Op.
28 | * ```js
29 | * const customMatmul = (node) =>
30 | * tf.matMul(
31 | * node.inputs[0], node.inputs[1],
32 | * node.attrs['transpose_a'], node.attrs['transpose_b']);
33 | *
34 | * tf.registerOp('MatMul', customMatmul);
35 | * ```
36 | * The inputs and attrs of the node object is based on the TensorFlow op
37 | * registry.
38 | *
39 | * @param name The Tensorflow Op name.
40 | * @param opFunc An op function which is called with the current graph node
41 | * during execution and needs to return a tensor or a list of tensors. The node
42 | * has the following attributes:
43 | * - attr: A map from attribute name to its value
44 | * - inputs: A list of input tensors
45 | */
46 | /** @doc {heading: 'Models', subheading: 'Op Registry'} */
47 | export function registerOp(name: string, opFunc: OpExecutor) {
48 | const opMapper: OpMapper = {
49 | tfOpName: name,
50 | category: 'custom',
51 | inputs: [],
52 | attrs: [],
53 | customExecutor: opFunc
54 | };
55 |
56 | CUSTOM_OPS[name] = opMapper;
57 | }
58 |
59 | /**
60 | * Retrieve the OpMapper object for the registered op.
61 | *
62 | * @param name The Tensorflow Op name.
63 | */
64 | /** @doc {heading: 'Models', subheading: 'Op Registry'} */
65 |
66 | export function getRegisteredOp(name: string): OpMapper {
67 | return CUSTOM_OPS[name];
68 | }
69 |
70 | /**
71 | * Deregister the Op for graph model executor.
72 | *
73 | * @param name The Tensorflow Op name.
74 | */
75 | /** @doc {heading: 'Models', subheading: 'Op Registry'} */
76 | export function deregisterOp(name: string) {
77 | delete CUSTOM_OPS[name];
78 | }
79 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/custom_op/register_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2019 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import {scalar} from '@tensorflow/tfjs-core';
19 |
20 | import {deregisterOp, getRegisteredOp, registerOp} from './register';
21 |
22 | describe('register custom op', () => {
23 | describe('registerCustomOp', () => {
24 | it('should auto fill missing fields', () => {
25 | const executor = () => scalar(1);
26 | registerOp('newOp', executor);
27 | const opMapper = getRegisteredOp('newOp');
28 | expect(opMapper.tfOpName).toEqual('newOp');
29 | expect(opMapper.category).toEqual('custom');
30 | expect(opMapper.inputs).toEqual([]);
31 | expect(opMapper.attrs).toEqual([]);
32 | expect(opMapper.customExecutor).toEqual(executor);
33 | deregisterOp('newOp');
34 | });
35 | });
36 | describe('deregisterOp', () => {
37 | it('should remove the custom op', () => {
38 | registerOp('newOp', () => scalar(1));
39 | expect(getRegisteredOp('newOp')).toBeDefined();
40 | deregisterOp('newOp');
41 | expect(getRegisteredOp('newOp')).not.toBeDefined();
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/arithmetic_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'BiasAdd':
32 | case 'AddV2':
33 | case 'Add': {
34 | return [tfc.add(
35 | (getParamValue('a', node, tensorMap, context) as tfc.Tensor),
36 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
37 | }
38 | case 'AddN': {
39 | return [tfc.addN((
40 | getParamValue('tensors', node, tensorMap, context) as tfc.Tensor[]))];
41 | }
42 | case 'FloorMod':
43 | case 'Mod':
44 | return [tfc.mod(
45 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
46 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
47 | case 'Mul':
48 | return [tfc.mul(
49 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
50 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
51 | case 'RealDiv':
52 | case 'Div': {
53 | return [tfc.div(
54 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
55 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
56 | }
57 | case 'FloorDiv': {
58 | return [tfc.floorDiv(
59 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
60 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
61 | }
62 | case 'Sub': {
63 | return [tfc.sub(
64 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
65 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
66 | }
67 | case 'Minimum': {
68 | return [tfc.minimum(
69 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
70 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
71 | }
72 | case 'Maximum': {
73 | return [tfc.maximum(
74 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
75 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
76 | }
77 | case 'Pow': {
78 | return [tfc.pow(
79 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
80 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
81 | }
82 | case 'SquaredDifference': {
83 | return [tfc.squaredDifference(
84 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
85 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
86 | }
87 | default:
88 | throw TypeError(`Node type ${node.op} is not implemented`);
89 | }
90 | };
91 |
92 | export const CATEGORY = 'arithmetic';
93 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/arithmetic_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import {Node} from '../types';
21 | import {executeOp} from './arithmetic_executor';
22 | import {createTensorAttr, createTensorsAttr} from './test_helper';
23 |
24 | describe('arithmetic', () => {
25 | let node: Node;
26 | const input1 = [tfc.scalar(1)];
27 | const input2 = [tfc.scalar(1)];
28 | const input3 = [tfc.scalar(4)];
29 | const context = new ExecutionContext({}, {});
30 |
31 | beforeEach(() => {
32 | node = {
33 | name: 'test',
34 | op: '',
35 | category: 'arithmetic',
36 | inputNames: ['input1', 'input2'],
37 | inputs: [],
38 | inputParams: {a: createTensorAttr(0), b: createTensorAttr(1)},
39 | attrParams: {},
40 | children: []
41 | };
42 | });
43 |
44 | describe('executeOp', () => {
45 | ['Add', 'Mul', 'Div', 'Sub', 'Maximum', 'Minimum', 'Pow',
46 | 'SquaredDifference', 'Mod', 'FloorDiv']
47 | .forEach((op => {
48 | it('should call tfc.' + op, () => {
49 | const spy =
50 | spyOn(tfc, op.charAt(0).toLowerCase() + op.slice(1) as 'add');
51 | node.op = op;
52 | executeOp(node, {input1, input2}, context);
53 |
54 | expect(spy).toHaveBeenCalledWith(input1[0], input2[0]);
55 | });
56 | }));
57 |
58 | it('AddV2', async () => {
59 | const spy = spyOn(tfc, 'add').and.callThrough();
60 | node.op = 'AddV2';
61 | const res = executeOp(node, {input1, input2}, context) as tfc.Tensor[];
62 | expect(spy).toHaveBeenCalledWith(input1[0], input2[0]);
63 | expect(res[0].dtype).toBe('float32');
64 | expect(res[0].shape).toEqual([]);
65 | tfc.test_util.expectArraysClose(await res[0].data(), 2);
66 | });
67 |
68 | it('AddN', async () => {
69 | const spy = spyOn(tfc, 'addN').and.callThrough();
70 | node.op = 'AddN';
71 | node.inputParams = {tensors: createTensorsAttr(0, 0)};
72 | node.inputNames = ['input1', 'input2', 'input3'];
73 | const res =
74 | executeOp(node, {input1, input2, input3}, context) as tfc.Tensor[];
75 | expect(spy).toHaveBeenCalledWith([input1[0], input2[0], input3[0]]);
76 | expect(res[0].dtype).toBe('float32');
77 | expect(res[0].shape).toEqual([]);
78 | tfc.test_util.expectArraysClose(await res[0].data(), [6]);
79 | });
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/dynamic_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google Inc. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {Node} from '../types';
23 | import {getParamValue} from './utils';
24 |
25 | export async function executeOp(
26 | node: Node, tensorMap: NamedTensorsMap,
27 | context: ExecutionContext): Promise {
28 | switch (node.op) {
29 | case 'NonMaxSuppressionV3':
30 | case 'NonMaxSuppressionV2': {
31 | const boxes =
32 | getParamValue('boxes', node, tensorMap, context) as tfc.Tensor;
33 | const scores =
34 | getParamValue('scores', node, tensorMap, context) as tfc.Tensor;
35 | const maxOutputSize =
36 | getParamValue('maxOutputSize', node, tensorMap, context) as number;
37 | const iouThreshold =
38 | getParamValue('iouThreshold', node, tensorMap, context) as number;
39 | const scoreThreshold =
40 | getParamValue('scoreThreshold', node, tensorMap, context) as number;
41 | return [await tfc.image.nonMaxSuppressionAsync(
42 | boxes as tfc.Tensor2D, scores as tfc.Tensor1D, maxOutputSize,
43 | iouThreshold, scoreThreshold)];
44 | }
45 | case 'Where': {
46 | return [await tfc.whereAsync(
47 | getParamValue('condition', node, tensorMap, context) as tfc.Tensor)];
48 | }
49 | case 'ListDiff': {
50 | return await tfc.setdiff1dAsync(
51 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
52 | getParamValue('y', node, tensorMap, context) as tfc.Tensor);
53 | }
54 | default:
55 | throw TypeError(`Node type ${node.op} is not implemented`);
56 | }
57 | }
58 |
59 | export const CATEGORY = 'dynamic';
60 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/evaluation_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google Inc. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor =
27 | (node: Node, tensorMap: NamedTensorsMap,
28 | context: ExecutionContext): tfc.Tensor[] => {
29 | switch (node.op) {
30 | case 'TopKV2': {
31 | const x = getParamValue('x', node, tensorMap, context) as tfc.Tensor;
32 | const k = getParamValue('k', node, tensorMap, context) as number;
33 | const sorted =
34 | getParamValue('sorted', node, tensorMap, context) as boolean;
35 | const result = tfc.topk(x, k, sorted);
36 | return [result.values, result.indices];
37 | }
38 | default:
39 | throw TypeError(`Node type ${node.op} is not implemented`);
40 | }
41 | };
42 |
43 | export const CATEGORY = 'evaluation';
44 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/evaluation_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google Inc. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 |
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import {Node} from '../types';
21 |
22 | import {executeOp} from './evaluation_executor';
23 | // tslint:disable-next-line:max-line-length
24 | import {createBoolAttr, createNumberAttrFromIndex, createTensorAttr} from './test_helper';
25 |
26 | describe('evaluation', () => {
27 | let node: Node;
28 | const input1 = [tfc.tensor1d([1])];
29 | const input2 = [tfc.scalar(1)];
30 | const context = new ExecutionContext({}, {});
31 |
32 | beforeEach(() => {
33 | node = {
34 | name: 'input1',
35 | op: '',
36 | category: 'evaluation',
37 | inputNames: ['input1', 'input2'],
38 | inputs: [],
39 | inputParams: {},
40 | attrParams: {},
41 | children: []
42 | };
43 | });
44 |
45 | describe('executeOp', () => {
46 | describe('TopKV2', () => {
47 | it('should return input', () => {
48 | node.op = 'TopKV2';
49 | node.inputParams['x'] = createTensorAttr(0);
50 | node.inputParams['k'] = createNumberAttrFromIndex(1);
51 | node.attrParams['sorted'] = createBoolAttr(true);
52 | spyOn(tfc, 'topk').and.callThrough();
53 | executeOp(node, {input1, input2}, context);
54 | expect(tfc.topk).toHaveBeenCalledWith(input1[0], 1, true);
55 | });
56 | });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/graph_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue, getTensor} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'Const': {
32 | return tensorMap[node.name];
33 | }
34 | case 'PlaceholderWithDefault':
35 | const def =
36 | getParamValue('default', node, tensorMap, context) as tfc.Tensor;
37 | return [getTensor(node.name, tensorMap, context) || def];
38 | case 'Placeholder':
39 | return [getTensor(node.name, tensorMap, context)];
40 | case 'Identity':
41 | case 'StopGradient':
42 | case 'FakeQuantWithMinMaxVars': // This op is currently ignored.
43 | return [
44 | (getParamValue('x', node, tensorMap, context) as tfc.Tensor).clone()
45 | ];
46 | case 'IdentityN':
47 | return (getParamValue('x', node, tensorMap, context) as tfc.Tensor[])
48 | .map((t: tfc.Tensor) => t.clone());
49 | case 'Snapshot':
50 | const snapshot =
51 | (getParamValue('x', node, tensorMap, context) as tfc.Tensor);
52 | return [snapshot.clone()];
53 | case 'Shape':
54 | return [tfc.tensor1d(
55 | (getParamValue('x', node, tensorMap, context) as tfc.Tensor).shape,
56 | 'int32')];
57 | case 'ShapeN':
58 | return (getParamValue('x', node, tensorMap, context) as tfc.Tensor[])
59 | .map((t: tfc.Tensor) => tfc.tensor1d(t.shape));
60 | case 'Size':
61 | return [tfc.scalar(
62 | (getParamValue('x', node, tensorMap, context) as tfc.Tensor).size,
63 | 'int32')];
64 | case 'Rank':
65 | return [tfc.scalar(
66 | (getParamValue('x', node, tensorMap, context) as tfc.Tensor).rank,
67 | 'int32')];
68 | case 'NoOp':
69 | return [];
70 | case 'Print':
71 | const input = getParamValue('x', node, tensorMap, context) as tfc.Tensor;
72 | const data =
73 | getParamValue('data', node, tensorMap, context) as tfc.Tensor[];
74 | const message =
75 | getParamValue('message', node, tensorMap, context) as string;
76 | const summarize =
77 | getParamValue('summarize', node, tensorMap, context) as number;
78 | console.warn(
79 | 'The graph has a tf.print() operation,' +
80 | 'usually used for debugging, which slows down performance.');
81 | console.log(message);
82 | for (let i = 0; i < data.length; i++) {
83 | console.log(
84 | Array.prototype.slice.call(data[i].dataSync()).slice(0, summarize));
85 | }
86 | return [input];
87 |
88 | default:
89 | throw TypeError(`Node type ${node.op} is not implemented`);
90 | }
91 | };
92 |
93 | export const CATEGORY = 'graph';
94 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/image_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google Inc. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'ResizeBilinear': {
32 | const images =
33 | getParamValue('images', node, tensorMap, context) as tfc.Tensor;
34 | const size = getParamValue('size', node, tensorMap, context) as number[];
35 | const alignCorners =
36 | getParamValue('alignCorners', node, tensorMap, context) as boolean;
37 | return [tfc.image.resizeBilinear(
38 | images as tfc.Tensor3D | tfc.Tensor4D, [size[0], size[1]],
39 | alignCorners)];
40 | }
41 | case 'ResizeNearestNeighbor': {
42 | const images =
43 | getParamValue('images', node, tensorMap, context) as tfc.Tensor;
44 | const size = getParamValue('size', node, tensorMap, context) as number[];
45 | const alignCorners =
46 | getParamValue('alignCorners', node, tensorMap, context) as boolean;
47 | return [tfc.image.resizeNearestNeighbor(
48 | images as tfc.Tensor3D | tfc.Tensor4D, [size[0], size[1]],
49 | alignCorners)];
50 | }
51 | case 'CropAndResize': {
52 | const image =
53 | getParamValue('image', node, tensorMap, context) as tfc.Tensor;
54 | const boxes =
55 | getParamValue('boxes', node, tensorMap, context) as tfc.Tensor;
56 | const boxInd =
57 | getParamValue('boxInd', node, tensorMap, context) as tfc.Tensor;
58 | const cropSize =
59 | getParamValue('cropSize', node, tensorMap, context) as number[];
60 | const method =
61 | getParamValue('method', node, tensorMap, context) as string;
62 | const extrapolationValue =
63 | getParamValue('extrapolationValue', node, tensorMap, context) as
64 | number;
65 | return [tfc.image.cropAndResize(
66 | image as tfc.Tensor4D, boxes as tfc.Tensor2D, boxInd as tfc.Tensor1D,
67 | cropSize as [number, number], method as 'bilinear' | 'nearest',
68 | extrapolationValue)];
69 | }
70 | default:
71 | throw TypeError(`Node type ${node.op} is not implemented`);
72 | }
73 | };
74 |
75 | export const CATEGORY = 'image';
76 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/logical_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'Equal': {
32 | return [tfc.equal(
33 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
34 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
35 | }
36 | case 'NotEqual': {
37 | return [tfc.notEqual(
38 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
39 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
40 | }
41 | case 'Greater': {
42 | return [tfc.greater(
43 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
44 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
45 | }
46 | case 'GreaterEqual': {
47 | return [tfc.greaterEqual(
48 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
49 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
50 | }
51 | case 'Less': {
52 | return [tfc.less(
53 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
54 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
55 | }
56 | case 'LessEqual': {
57 | return [tfc.lessEqual(
58 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
59 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
60 | }
61 | case 'LogicalAnd': {
62 | return [tfc.logicalAnd(
63 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
64 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
65 | }
66 | case 'LogicalNot': {
67 | return [tfc.logicalNot(
68 | getParamValue('a', node, tensorMap, context) as tfc.Tensor)];
69 | }
70 | case 'LogicalOr': {
71 | return [tfc.logicalOr(
72 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
73 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
74 | }
75 | case 'Select': {
76 | return [tfc.where(
77 | getParamValue('condition', node, tensorMap, context) as tfc.Tensor,
78 | getParamValue('a', node, tensorMap, context) as tfc.Tensor,
79 | getParamValue('b', node, tensorMap, context) as tfc.Tensor)];
80 | }
81 | default:
82 | throw TypeError(`Node type ${node.op} is not implemented`);
83 | }
84 | };
85 |
86 | export const CATEGORY = 'logical';
87 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/logical_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 |
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import {Node} from '../types';
21 |
22 | import {executeOp} from './logical_executor';
23 | import {createTensorAttr} from './test_helper';
24 |
25 | describe('logical', () => {
26 | let node: Node;
27 | const input1 = [tfc.scalar(1)];
28 | const input2 = [tfc.scalar(2)];
29 | const context = new ExecutionContext({}, {});
30 |
31 | beforeEach(() => {
32 | node = {
33 | name: 'test',
34 | op: '',
35 | category: 'logical',
36 | inputNames: ['input1', 'input2'],
37 | inputs: [],
38 | inputParams: {a: createTensorAttr(0), b: createTensorAttr(1)},
39 | attrParams: {},
40 | children: []
41 | };
42 | });
43 |
44 | describe('executeOp', () => {
45 | ['Equal', 'NotEqual', 'Greater', 'GreaterEqual', 'Less', 'LessEqual',
46 | 'LogicalAnd', 'LogicalOr']
47 | .forEach(op => {
48 | it('should call tfc.' + op, () => {
49 | const spy =
50 | spyOn(tfc, op.charAt(0).toLowerCase() + op.slice(1) as 'equal');
51 | node.op = op;
52 | executeOp(node, {input1, input2}, context);
53 |
54 | expect(spy).toHaveBeenCalledWith(input1[0], input2[0]);
55 | });
56 | });
57 | describe('LogicalNot', () => {
58 | it('should call tfc.logicalNot', () => {
59 | spyOn(tfc, 'logicalNot');
60 | node.op = 'LogicalNot';
61 | executeOp(node, {input1}, context);
62 |
63 | expect(tfc.logicalNot).toHaveBeenCalledWith(input1[0]);
64 | });
65 | });
66 |
67 | describe('Select', () => {
68 | it('should call tfc.where', () => {
69 | spyOn(tfc, 'where');
70 | node.op = 'Select';
71 | node.inputNames = ['input1', 'input2', 'input3'];
72 | node.inputParams.condition = createTensorAttr(2);
73 | const input3 = [tfc.scalar(1)];
74 | executeOp(node, {input1, input2, input3}, context);
75 |
76 | expect(tfc.where).toHaveBeenCalledWith(input3[0], input1[0], input2[0]);
77 | });
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/matrices_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'BatchMatMul':
32 | case 'BatchMatMulV2':
33 | case 'MatMul':
34 | return [tfc.matMul(
35 | getParamValue('a', node, tensorMap, context) as tfc.Tensor2D,
36 | getParamValue('b', node, tensorMap, context) as tfc.Tensor2D,
37 | getParamValue('transposeA', node, tensorMap, context) as boolean,
38 | getParamValue('transposeB', node, tensorMap, context) as boolean)];
39 | case 'Transpose':
40 | return [tfc.transpose(
41 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
42 | getParamValue('perm', node, tensorMap, context) as number[])];
43 |
44 | default:
45 | throw TypeError(`Node type ${node.op} is not implemented`);
46 | }
47 | };
48 |
49 | export const CATEGORY = 'matrices';
50 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/matrices_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 |
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import {Node} from '../types';
21 |
22 | import {executeOp} from './matrices_executor';
23 | // tslint:disable-next-line:max-line-length
24 | import {createBoolAttr, createNumericArrayAttr, createTensorAttr} from './test_helper';
25 |
26 | describe('matrices', () => {
27 | let node: Node;
28 | const input1 = [tfc.scalar(1)];
29 | const input2 = [tfc.scalar(2)];
30 | const context = new ExecutionContext({}, {});
31 |
32 | beforeEach(() => {
33 | node = {
34 | name: 'test',
35 | op: '',
36 | category: 'matrices',
37 | inputNames: ['input1', 'input2'],
38 | inputs: [],
39 | inputParams: {a: createTensorAttr(0), b: createTensorAttr(1)},
40 | attrParams: {},
41 | children: []
42 | };
43 | });
44 |
45 | describe('executeOp', () => {
46 | describe('MatMul', () => {
47 | it('should call tfc.matMul', () => {
48 | spyOn(tfc, 'matMul');
49 | node.op = 'MatMul';
50 | node.attrParams.transposeA = createBoolAttr(true);
51 | node.attrParams.transposeB = createBoolAttr(false);
52 | executeOp(node, {input1, input2}, context);
53 |
54 | expect(tfc.matMul)
55 | .toHaveBeenCalledWith(input1[0], input2[0], true, false);
56 | });
57 | });
58 | describe('BatchMatMul', () => {
59 | it('should call tfc.matMul', () => {
60 | spyOn(tfc, 'matMul');
61 | node.op = 'BatchMatMul';
62 | node.attrParams.transposeA = createBoolAttr(true);
63 | node.attrParams.transposeB = createBoolAttr(false);
64 | executeOp(node, {input1, input2}, context);
65 |
66 | expect(tfc.matMul)
67 | .toHaveBeenCalledWith(input1[0], input2[0], true, false);
68 | });
69 | });
70 | describe('BatchMatMulV2', () => {
71 | it('should call tfc.matMul', () => {
72 | spyOn(tfc, 'matMul');
73 | node.op = 'BatchMatMulV2';
74 | node.attrParams.transposeA = createBoolAttr(true);
75 | node.attrParams.transposeB = createBoolAttr(false);
76 | executeOp(node, {input1, input2}, context);
77 |
78 | expect(tfc.matMul)
79 | .toHaveBeenCalledWith(input1[0], input2[0], true, false);
80 | });
81 | });
82 | describe('Transpose', () => {
83 | it('should call tfc.transpose', () => {
84 | spyOn(tfc, 'transpose');
85 | node.op = 'Transpose';
86 | node.inputNames = ['input1', 'input2', 'input3'];
87 | node.inputParams.x = createTensorAttr(0);
88 | node.attrParams.perm = createNumericArrayAttr([1, 2]);
89 | executeOp(node, {input1}, context);
90 |
91 | expect(tfc.transpose).toHaveBeenCalledWith(input1[0], [1, 2]);
92 | });
93 | });
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/normalization_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'FusedBatchNorm':
32 | case 'FusedBatchNormV2': {
33 | return [tfc.batchNorm(
34 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
35 | getParamValue('mean', node, tensorMap, context) as tfc.Tensor,
36 | getParamValue('variance', node, tensorMap, context) as tfc.Tensor,
37 | getParamValue('offset', node, tensorMap, context) as tfc.Tensor,
38 | getParamValue('scale', node, tensorMap, context) as tfc.Tensor,
39 | getParamValue('epsilon', node, tensorMap, context) as number)];
40 | }
41 | case 'FusedBatchNormV3': {
42 | return [tfc.batchNorm(
43 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
44 | getParamValue('mean', node, tensorMap, context) as tfc.Tensor,
45 | getParamValue('variance', node, tensorMap, context) as tfc.Tensor,
46 | getParamValue('offset', node, tensorMap, context) as tfc.Tensor,
47 | getParamValue('scale', node, tensorMap, context) as tfc.Tensor,
48 | getParamValue('epsilon', node, tensorMap, context) as number)];
49 | }
50 | case 'LRN': {
51 | return [tfc.localResponseNormalization(
52 | getParamValue('x', node, tensorMap, context) as tfc.Tensor3D |
53 | tfc.Tensor4D,
54 | getParamValue('radius', node, tensorMap, context) as number,
55 | getParamValue('bias', node, tensorMap, context) as number,
56 | getParamValue('alpha', node, tensorMap, context) as number,
57 | getParamValue('beta', node, tensorMap, context) as number)];
58 | }
59 | case 'Softmax': {
60 | return [tfc.softmax(
61 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
62 | }
63 | case 'LogSoftmax': {
64 | return [tfc.logSoftmax(
65 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
66 | }
67 | case 'SparseToDense': {
68 | return [tfc.sparseToDense(
69 | getParamValue('sparseIndices', node, tensorMap, context) as
70 | tfc.Tensor,
71 | getParamValue('outputShape', node, tensorMap, context) as tfc.Tensor,
72 | getParamValue('sparseValues', node, tensorMap, context) as number[],
73 | getParamValue('defaultValue', node, tensorMap, context) as
74 | tfc.Scalar)];
75 | }
76 | default:
77 | throw TypeError(`Node type ${node.op} is not implemented`);
78 | }
79 | };
80 |
81 | export const CATEGORY = 'normalization';
82 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/reduction_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'Max': {
32 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
33 | const keepDims =
34 | getParamValue('keepDims', node, tensorMap, context) as boolean;
35 | return [tfc.max(
36 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
37 | keepDims)];
38 | }
39 | case 'Mean': {
40 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
41 | const keepDims =
42 | getParamValue('keepDims', node, tensorMap, context) as boolean;
43 | return [tfc.mean(
44 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
45 | keepDims)];
46 | }
47 | case 'Min': {
48 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
49 | const keepDims =
50 | getParamValue('keepDims', node, tensorMap, context) as boolean;
51 | return [tfc.min(
52 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
53 | keepDims)];
54 | }
55 | case 'Sum': {
56 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
57 | const keepDims =
58 | getParamValue('keepDims', node, tensorMap, context) as boolean;
59 | return [tfc.sum(
60 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
61 | keepDims)];
62 | }
63 | case 'All': {
64 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
65 | const keepDims =
66 | getParamValue('keepDims', node, tensorMap, context) as boolean;
67 | return [tfc.all(
68 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
69 | keepDims)];
70 | }
71 | case 'Any': {
72 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
73 | const keepDims =
74 | getParamValue('keepDims', node, tensorMap, context) as boolean;
75 | return [tfc.any(
76 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
77 | keepDims)];
78 | }
79 | case 'ArgMax': {
80 | const axis = getParamValue('axis', node, tensorMap, context) as number;
81 | return [tfc.argMax(
82 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis)];
83 | }
84 | case 'ArgMin': {
85 | const axis = getParamValue('axis', node, tensorMap, context) as number;
86 | return [tfc.argMin(
87 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis)];
88 | }
89 | case 'Prod': {
90 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
91 | const keepDims =
92 | getParamValue('keepDims', node, tensorMap, context) as boolean;
93 | return [tfc.prod(
94 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis,
95 | keepDims)];
96 | }
97 | default:
98 | throw TypeError(`Node type ${node.op} is not implemented`);
99 | }
100 | };
101 |
102 | export const CATEGORY = 'reduction';
103 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/reduction_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 |
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import {Node} from '../types';
21 |
22 | import {executeOp} from './reduction_executor';
23 | // tslint:disable-next-line:max-line-length
24 | import {createBoolAttr, createNumberAttr, createTensorAttr} from './test_helper';
25 |
26 | describe('reduction', () => {
27 | let node: Node;
28 | const input1 = [tfc.scalar(1)];
29 | const context = new ExecutionContext({}, {});
30 |
31 | beforeEach(() => {
32 | node = {
33 | name: 'test',
34 | op: '',
35 | category: 'logical',
36 | inputNames: ['input1'],
37 | inputs: [],
38 | inputParams: {x: createTensorAttr(0)},
39 | attrParams: {keepDims: createBoolAttr(true), axis: createNumberAttr(1)},
40 | children: []
41 | };
42 | });
43 |
44 | describe('executeOp', () => {
45 | ['Max', 'Mean', 'Min', 'Sum', 'All', 'Any', 'Prod'].forEach(op => {
46 | it('should call tfc.' + op, () => {
47 | const spy =
48 | spyOn(tfc, op.charAt(0).toLowerCase() + op.slice(1) as 'max');
49 | node.op = op;
50 | executeOp(node, {input1}, context);
51 |
52 | expect(spy).toHaveBeenCalledWith(input1[0], 1, true);
53 | });
54 | });
55 | describe('ArgMax', () => {
56 | it('should call tfc.argMax', () => {
57 | spyOn(tfc, 'argMax');
58 | node.op = 'ArgMax';
59 | executeOp(node, {input1}, context);
60 |
61 | expect(tfc.argMax).toHaveBeenCalledWith(input1[0], 1);
62 | });
63 | });
64 | describe('ArgMin', () => {
65 | it('should call tfc.argMin', () => {
66 | spyOn(tfc, 'argMin');
67 | node.op = 'ArgMin';
68 | executeOp(node, {input1}, context);
69 |
70 | expect(tfc.argMin).toHaveBeenCalledWith(input1[0], 1);
71 | });
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/spectral_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor =
27 | (node: Node, tensorMap: NamedTensorsMap,
28 | context: ExecutionContext): tfc.Tensor[] => {
29 | switch (node.op) {
30 | case 'FFT': {
31 | return [tfc.fft(
32 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
33 | }
34 | case 'IFFT': {
35 | return [tfc.ifft(
36 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
37 | }
38 | case 'RFFT': {
39 | return [tfc.rfft(
40 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
41 | }
42 | case 'IRFFT': {
43 | return [tfc.irfft(
44 | getParamValue('x', node, tensorMap, context) as tfc.Tensor)];
45 | }
46 | default:
47 | throw TypeError(`Node type ${node.op} is not implemented`);
48 | }
49 | };
50 |
51 | export const CATEGORY = 'spectral';
52 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/spectral_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as tfc from '@tensorflow/tfjs-core';
18 |
19 | import {ExecutionContext} from '../../executor/execution_context';
20 | import * as spectral from '../op_list/spectral';
21 | import {Node, OpMapper} from '../types';
22 |
23 | import {executeOp} from './spectral_executor';
24 | import {createTensorAttr, validateParam} from './test_helper';
25 |
26 | describe('spectral', () => {
27 | let node: Node;
28 | const input1 = [tfc.scalar(1)];
29 | const context = new ExecutionContext({}, {});
30 |
31 | beforeEach(() => {
32 | node = {
33 | name: 'test',
34 | op: '',
35 | category: 'spectral',
36 | inputNames: ['input1'],
37 | inputs: [],
38 | inputParams: {x: createTensorAttr(0)},
39 | attrParams: {},
40 | children: []
41 | };
42 | });
43 |
44 | describe('executeOp', () => {
45 | describe('FFT', () => {
46 | it('should call tfc.fft', () => {
47 | spyOn(tfc, 'fft');
48 | node.op = 'FFT';
49 | executeOp(node, {input1}, context);
50 |
51 | expect(tfc.fft).toHaveBeenCalledWith(input1[0]);
52 | });
53 | it('should match json def', () => {
54 | node.op = 'FFT';
55 |
56 | expect(validateParam(node, spectral.json as OpMapper[])).toBeTruthy();
57 | });
58 | });
59 | describe('IFFT', () => {
60 | it('should call tfc.ifft', () => {
61 | spyOn(tfc, 'ifft');
62 | node.op = 'IFFT';
63 | executeOp(node, {input1}, context);
64 |
65 | expect(tfc.ifft).toHaveBeenCalledWith(input1[0]);
66 | });
67 | it('should match json def', () => {
68 | node.op = 'IFFT';
69 |
70 | expect(validateParam(node, spectral.json as OpMapper[])).toBeTruthy();
71 | });
72 | });
73 | describe('RFFT', () => {
74 | it('should call tfc.rfft', () => {
75 | spyOn(tfc, 'rfft');
76 | node.op = 'RFFT';
77 | executeOp(node, {input1}, context);
78 |
79 | expect(tfc.rfft).toHaveBeenCalledWith(input1[0]);
80 | });
81 | it('should match json def', () => {
82 | node.op = 'RFFT';
83 |
84 | expect(validateParam(node, spectral.json as OpMapper[])).toBeTruthy();
85 | });
86 | });
87 | describe('IRFFT', () => {
88 | it('should call tfc.irfft', () => {
89 | spyOn(tfc, 'irfft');
90 | node.op = 'IRFFT';
91 | executeOp(node, {input1}, context);
92 |
93 | expect(tfc.irfft).toHaveBeenCalledWith(input1[0]);
94 | });
95 | it('should match json def', () => {
96 | node.op = 'IRFFT';
97 |
98 | expect(validateParam(node, spectral.json as OpMapper[])).toBeTruthy();
99 | });
100 | });
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/test_helper.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import {InputParamValue, OpMapper, ParamValue} from '../types';
18 | import {Node} from '../types';
19 |
20 | export function createNumberAttr(value: number): ParamValue {
21 | return {value, type: 'number'};
22 | }
23 |
24 | export function createNumberAttrFromIndex(inputIndex: number): InputParamValue {
25 | return {inputIndexStart: inputIndex, type: 'number'};
26 | }
27 |
28 | export function createStrAttr(str: string): ParamValue {
29 | return {value: str, type: 'string'};
30 | }
31 |
32 | export function createBoolAttr(value: boolean): ParamValue {
33 | return {value, type: 'bool'};
34 | }
35 |
36 | export function createTensorShapeAttr(value: number[]): ParamValue {
37 | return {value, type: 'shape'};
38 | }
39 | export function createNumericArrayAttr(value: number[]): ParamValue {
40 | return {value, type: 'number[]'};
41 | }
42 |
43 | export function createNumericArrayAttrFromIndex(inputIndex: number):
44 | InputParamValue {
45 | return {inputIndexStart: inputIndex, type: 'number[]'};
46 | }
47 |
48 | export function createTensorAttr(index: number): InputParamValue {
49 | return {inputIndexStart: index, type: 'tensor'};
50 | }
51 |
52 | export function createTensorsAttr(
53 | index: number, paramLength: number): InputParamValue {
54 | return {inputIndexStart: index, inputIndexEnd: paramLength, type: 'tensors'};
55 | }
56 |
57 | export function createDtypeAttr(dtype: string): ParamValue {
58 | return {value: dtype, type: 'dtype'};
59 | }
60 |
61 | export function validateParam(
62 | node: Node, opMappers: OpMapper[], tfOpName?: string) {
63 | const opMapper = tfOpName != null ?
64 | opMappers.find(mapper => mapper.tfOpName === tfOpName) :
65 | opMappers.find(mapper => mapper.tfOpName === node.op);
66 | return Object.keys(node.inputParams).every(key => {
67 | const value = node.inputParams[key];
68 | const def = opMapper.inputs.find(param => param.name === key);
69 | return def && def.type === value.type &&
70 | def.start === value.inputIndexStart && def.end === value.inputIndexEnd;
71 | }) &&
72 | Object.keys(node.attrParams).every(key => {
73 | const value = node.attrParams[key];
74 | const def = opMapper.attrs.find(param => param.name === key);
75 | return def && def.type === value.type;
76 | });
77 | }
78 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/transformation_executor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {InternalOpExecutor, Node} from '../types';
23 |
24 | import {getParamValue, split} from './utils';
25 |
26 | export let executeOp: InternalOpExecutor = (node: Node,
27 | tensorMap: NamedTensorsMap,
28 | context: ExecutionContext):
29 | tfc.Tensor[] => {
30 | switch (node.op) {
31 | case 'Cast': {
32 | return [tfc.cast(
33 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
34 | getParamValue('dtype', node, tensorMap, context) as 'int32' |
35 | 'float32' | 'bool')];
36 | }
37 | case 'ExpandDims': {
38 | const axis = getParamValue('axis', node, tensorMap, context) as number;
39 | return [tfc.expandDims(
40 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis)];
41 | }
42 | case 'Squeeze': {
43 | const axis = getParamValue('axis', node, tensorMap, context) as number[];
44 | return [tfc.squeeze(
45 | getParamValue('x', node, tensorMap, context) as tfc.Tensor, axis)];
46 | }
47 |
48 | case 'Reshape': {
49 | return [tfc.reshape(
50 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
51 | getParamValue('shape', node, tensorMap, context) as number[])];
52 | }
53 | case 'PadV2':
54 | case 'Pad': {
55 | return [tfc.pad(
56 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
57 | split(
58 | getParamValue('padding', node, tensorMap, context) as number[],
59 | 2) as Array<[number, number]>,
60 | getParamValue('constantValue', node, tensorMap, context) as number)];
61 | }
62 | case 'SpaceToBatchND': {
63 | const blockShape =
64 | getParamValue('blockShape', node, tensorMap, context) as number[];
65 | const paddings = split(
66 | getParamValue('paddings', node, tensorMap, context) as number[], 2);
67 | return [tfc.spaceToBatchND(
68 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
69 | blockShape, paddings)];
70 | }
71 | case 'BatchToSpaceND': {
72 | const blockShape =
73 | getParamValue('blockShape', node, tensorMap, context) as number[];
74 | const crops = split(
75 | getParamValue('crops', node, tensorMap, context) as number[], 2);
76 | return [tfc.batchToSpaceND(
77 | getParamValue('x', node, tensorMap, context) as tfc.Tensor,
78 | blockShape, crops)];
79 | }
80 | case 'DepthToSpace': {
81 | const blockSize =
82 | getParamValue('blockSize', node, tensorMap, context) as number;
83 | const dataFormat =
84 | (getParamValue('dataFormat', node, tensorMap, context) as
85 | string).toUpperCase() as 'NHWC' |
86 | 'NCHW';
87 | return [tfc.depthToSpace(
88 | getParamValue('x', node, tensorMap, context) as tfc.Tensor4D,
89 | blockSize, dataFormat)];
90 | }
91 | default:
92 | throw TypeError(`Node type ${node.op} is not implemented`);
93 | }
94 | };
95 |
96 | export const CATEGORY = 'transformation';
97 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/executors/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as tfc from '@tensorflow/tfjs-core';
19 |
20 | import {NamedTensorsMap} from '../../data/types';
21 | import {ExecutionContext} from '../../executor/execution_context';
22 | import {Node, ValueType} from '../types';
23 |
24 | export function getParamValue(
25 | paramName: string, node: Node, tensorMap: NamedTensorsMap,
26 | context: ExecutionContext): ValueType {
27 | const inputParam = node.inputParams[paramName];
28 | if (inputParam && inputParam.inputIndexStart !== undefined) {
29 | const start = inputParam.inputIndexStart;
30 | const end = inputParam.inputIndexEnd === 0 ?
31 | undefined :
32 | (inputParam.inputIndexEnd === undefined ? start + 1 :
33 | inputParam.inputIndexEnd);
34 | if (inputParam.type === 'tensor') {
35 | return getTensor(
36 | node.inputNames[inputParam.inputIndexStart], tensorMap, context);
37 | }
38 | if (inputParam.type === 'tensors') {
39 | const inputs = node.inputNames.slice(start, end);
40 |
41 | return inputs.map(name => getTensor(name, tensorMap, context));
42 | }
43 | const data = Array.prototype.slice.call(
44 | getTensor(node.inputNames.slice(start)[0], tensorMap, context)
45 | .dataSync());
46 | return inputParam.type === 'number' ? data[0] : data;
47 | }
48 | const attrParam = node.attrParams[paramName];
49 | return attrParam && attrParam.value;
50 | }
51 |
52 | /**
53 | * Retrieve the tensor based on input name by extracting the node name and
54 | * output index information.
55 | * @param name Node input name
56 | * @param tensorsMap Tensors map keyed by the node
57 | */
58 | export function getTensor(
59 | name: string, tensorsMap: NamedTensorsMap,
60 | context: ExecutionContext): tfc.Tensor {
61 | const [nodeName, index] = parseNodeName(name);
62 | const contextId = context.currentContextIds.find(contextId => {
63 | return !!tensorsMap[getNodeNameWithContextId(nodeName, contextId)];
64 | });
65 |
66 | return contextId !== undefined ?
67 | tensorsMap[getNodeNameWithContextId(nodeName, contextId)][index] :
68 | undefined;
69 | }
70 |
71 | /**
72 | * Retrieve the tensors based on input name for current context.
73 | * @param name Node input name
74 | * @param tensorsMap Tensors map keyed by the node
75 | */
76 | export function getTensorsForCurrentContenxt(
77 | name: string, tensorsMap: NamedTensorsMap,
78 | context: ExecutionContext): tfc.Tensor[] {
79 | return tensorsMap[getNodeNameWithContextId(name, context.currentContextId)];
80 | }
81 |
82 | /**
83 | * Returns the node name and index from the Node input name.
84 | * @param inputName The input name of the node, in format of
85 | * node_name:output_index, i.e. MatMul:0, if the output_index is not set, it is
86 | * default to 0.
87 | */
88 | export function getNodeNameAndIndex(
89 | inputName: string, context?: ExecutionContext): [string, number] {
90 | const [nodeName, index] = parseNodeName(inputName);
91 |
92 | return [
93 | getNodeNameWithContextId(nodeName, context && context.currentContextId),
94 | index
95 | ];
96 | }
97 |
98 | function getNodeNameWithContextId(name: string, contextId?: string): string {
99 | return !!contextId ? `${name}-${contextId}` : name;
100 | }
101 |
102 | export function parseNodeName(name: string): [string, number] {
103 | const index = name.lastIndexOf(':');
104 | if (index === -1) return [name, 0];
105 |
106 | const nodeName = name.substring(0, index);
107 | return [nodeName, Number(name.substring(index + 1))];
108 | }
109 |
110 | export function split(arr: number[], size: number) {
111 | const res = [];
112 | for (let i = 0; i < arr.length; i += size) {
113 | res.push(arr.slice(i, i + size));
114 | }
115 | return res;
116 | }
117 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/dynamic.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'NonMaxSuppressionV2',
23 | 'category': 'dynamic',
24 | 'inputs': [
25 | {'start': 0, 'name': 'boxes', 'type': 'tensor'},
26 | {'start': 1, 'name': 'scores', 'type': 'tensor'},
27 | {'start': 2, 'name': 'maxOutputSize', 'type': 'number'},
28 | {'start': 3, 'name': 'iouThreshold', 'type': 'number'}
29 | ]
30 | },
31 | {
32 | 'tfOpName': 'NonMaxSuppressionV3',
33 | 'category': 'dynamic',
34 | 'inputs': [
35 | {'start': 0, 'name': 'boxes', 'type': 'tensor'},
36 | {'start': 1, 'name': 'scores', 'type': 'tensor'},
37 | {'start': 2, 'name': 'maxOutputSize', 'type': 'number'},
38 | {'start': 3, 'name': 'iouThreshold', 'type': 'number'},
39 | {'start': 4, 'name': 'scoreThreshold', 'type': 'number'}
40 | ]
41 | },
42 | {
43 | 'tfOpName': 'Where',
44 | 'category': 'dynamic',
45 | 'inputs': [
46 | {'start': 0, 'name': 'condition', 'type': 'tensor'},
47 | ],
48 | 'attrs': [
49 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
50 | ]
51 | },
52 | {
53 | 'tfOpName': 'ListDiff',
54 | 'category': 'dynamic',
55 | 'inputs': [
56 | {'start': 0, 'name': 'x', 'type': 'tensor'},
57 | {'start': 1, 'name': 'y', 'type': 'tensor'},
58 | ],
59 | 'attrs': [{
60 | 'tfName': 'T',
61 | 'name': 'dtype',
62 | 'type': 'dtype',
63 | 'notSupported': true
64 | }]
65 | }
66 | ];
67 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/evaluation.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [{
21 | 'tfOpName': 'TopKV2',
22 | 'category': 'evaluation',
23 | 'inputs': [
24 | {'start': 0, 'name': 'x', 'type': 'tensor'},
25 | {'start': 1, 'name': 'k', 'type': 'number'},
26 | ],
27 | 'attrs': [{'tfName': 'sorted', 'name': 'sorted', 'type': 'bool'}]
28 | }];
29 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/graph.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'PlaceholderWithDefault',
23 | 'category': 'graph',
24 | 'inputs': [
25 | {'start': 0, 'name': 'default', 'type': 'tensor'},
26 | ],
27 | 'attrs': [
28 | {'tfName': 'shape', 'name': 'shape', 'type': 'shape'},
29 | {'tfName': 'dtype', 'name': 'dtype', 'type': 'dtype'}
30 | ]
31 | },
32 | {
33 | 'tfOpName': 'Placeholder',
34 | 'category': 'graph',
35 | 'attrs': [
36 | {'tfName': 'shape', 'name': 'shape', 'type': 'shape'},
37 | {'tfName': 'dtype', 'name': 'dtype', 'type': 'dtype'}
38 | ]
39 | },
40 | {'tfOpName': 'Const', 'category': 'graph'}, {
41 | 'tfOpName': 'Identity',
42 | 'category': 'graph',
43 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
44 | },
45 | {
46 | 'tfOpName': 'IdentityN',
47 | 'category': 'graph',
48 | 'inputs': [{'start': 0, 'end': 0, 'name': 'x', 'type': 'tensors'}]
49 | },
50 | {
51 | 'tfOpName': 'Snapshot',
52 | 'category': 'graph',
53 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
54 | },
55 | {
56 | 'tfOpName': 'Rank',
57 | 'category': 'graph',
58 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
59 | },
60 | {
61 | 'tfOpName': 'Size',
62 | 'category': 'graph',
63 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
64 | },
65 | {
66 | 'tfOpName': 'Shape',
67 | 'category': 'graph',
68 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
69 | },
70 | {
71 | 'tfOpName': 'ShapeN',
72 | 'category': 'graph',
73 | 'inputs': [{'start': 0, 'end': 0, 'name': 'x', 'type': 'tensors'}]
74 | },
75 | {
76 | 'tfOpName': 'Print',
77 | 'category': 'graph',
78 | 'inputs': [
79 | {'start': 0, 'name': 'x', 'type': 'tensor'},
80 | {'start': 1, 'name': 'data', 'type': 'tensors'},
81 | ],
82 | 'attrs': [
83 | {'tfName': 'message', 'name': 'message', 'type': 'string'}, {
84 | 'tfName': 'first_n',
85 | 'name': 'firstN',
86 | 'type': 'number',
87 | 'notSupported': true
88 | },
89 | {
90 | 'tfName': 'summarize',
91 | 'name': 'summarize',
92 | 'type': 'number',
93 | 'defaultValue': 3
94 | }
95 | ]
96 | },
97 | {'tfOpName': 'NoOp', 'category': 'graph', 'inputs': []}, {
98 | 'tfOpName': 'StopGradient',
99 | 'category': 'graph',
100 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
101 | },
102 | {
103 | 'tfOpName': 'FakeQuantWithMinMaxVars',
104 | 'category': 'graph',
105 | 'inputs': [
106 | {'start': 0, 'name': 'x', 'type': 'tensor'},
107 | ],
108 | 'attrs': [
109 | {'tfName': 'min', 'name': 'min', 'type': 'number'},
110 | {'tfName': 'max', 'name': 'max', 'type': 'number'}
111 | ]
112 | }
113 | ];
114 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/image.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'ResizeBilinear',
23 | 'category': 'image',
24 | 'inputs': [
25 | {'start': 0, 'name': 'images', 'type': 'tensor'},
26 | {'start': 1, 'name': 'size', 'type': 'number[]'},
27 | ],
28 | 'attrs': [
29 | {'tfName': 'align_corners', 'name': 'alignCorners', 'type': 'bool'},
30 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
31 | ]
32 | },
33 | {
34 | 'tfOpName': 'ResizeNearestNeighbor',
35 | 'category': 'image',
36 | 'inputs': [
37 | {'start': 0, 'name': 'images', 'type': 'tensor'},
38 | {'start': 1, 'name': 'size', 'type': 'number[]'},
39 | ],
40 | 'attrs': [
41 | {'tfName': 'align_corners', 'name': 'alignCorners', 'type': 'bool'},
42 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
43 | ]
44 | },
45 | {
46 | 'tfOpName': 'CropAndResize',
47 | 'category': 'image',
48 | 'inputs': [
49 | {'start': 0, 'name': 'image', 'type': 'tensor'},
50 | {'start': 1, 'name': 'boxes', 'type': 'tensor'},
51 | {'start': 2, 'name': 'boxInd', 'type': 'tensor'},
52 | {'start': 3, 'name': 'cropSize', 'type': 'number[]'},
53 | ],
54 | 'attrs': [
55 | {'tfName': 'method', 'name': 'method', 'type': 'string'}, {
56 | 'tfName': 'extrapolation_value',
57 | 'name': 'extrapolationValue',
58 | 'type': 'number'
59 | }
60 | ]
61 | }
62 | ];
63 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/logical.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'Equal',
23 | 'category': 'logical',
24 | 'inputs': [
25 | {'start': 0, 'name': 'a', 'type': 'tensor'},
26 | {'start': 1, 'name': 'b', 'type': 'tensor'},
27 | ],
28 | 'attrs': [
29 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
30 | ]
31 | },
32 | {
33 | 'tfOpName': 'NotEqual',
34 | 'category': 'logical',
35 | 'inputs': [
36 | {'start': 0, 'name': 'a', 'type': 'tensor'},
37 | {'start': 1, 'name': 'b', 'type': 'tensor'},
38 | ],
39 | 'attrs': [
40 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
41 | ]
42 | },
43 | {
44 | 'tfOpName': 'Greater',
45 | 'category': 'logical',
46 | 'inputs': [
47 | {'start': 0, 'name': 'a', 'type': 'tensor'},
48 | {'start': 1, 'name': 'b', 'type': 'tensor'},
49 | ],
50 | 'attrs': [
51 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
52 | ]
53 | },
54 | {
55 | 'tfOpName': 'GreaterEqual',
56 | 'category': 'logical',
57 | 'inputs': [
58 | {'start': 0, 'name': 'a', 'type': 'tensor'},
59 | {'start': 1, 'name': 'b', 'type': 'tensor'},
60 | ],
61 | 'attrs': [
62 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
63 | ]
64 | },
65 | {
66 | 'tfOpName': 'Less',
67 | 'category': 'logical',
68 | 'inputs': [
69 | {'start': 0, 'name': 'a', 'type': 'tensor'},
70 | {'start': 1, 'name': 'b', 'type': 'tensor'},
71 | ],
72 | 'attrs': [
73 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
74 | ]
75 | },
76 | {
77 | 'tfOpName': 'LessEqual',
78 | 'category': 'logical',
79 | 'inputs': [
80 | {'start': 0, 'name': 'a', 'type': 'tensor'},
81 | {'start': 1, 'name': 'b', 'type': 'tensor'},
82 | ],
83 | 'attrs': [
84 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
85 | ]
86 | },
87 | {
88 | 'tfOpName': 'LogicalAnd',
89 | 'category': 'logical',
90 | 'inputs': [
91 | {'start': 0, 'name': 'a', 'type': 'tensor'},
92 | {'start': 1, 'name': 'b', 'type': 'tensor'},
93 | ],
94 | 'attrs': [
95 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
96 | ]
97 | },
98 | {
99 | 'tfOpName': 'LogicalNot',
100 | 'category': 'logical',
101 | 'inputs': [
102 | {'start': 0, 'name': 'a', 'type': 'tensor'},
103 | ],
104 | 'attrs': [
105 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
106 | ]
107 | },
108 | {
109 | 'tfOpName': 'LogicalOr',
110 | 'category': 'logical',
111 | 'inputs': [
112 | {'start': 0, 'name': 'a', 'type': 'tensor'},
113 | {'start': 1, 'name': 'b', 'type': 'tensor'},
114 | ],
115 | 'attrs': [
116 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
117 | ]
118 | },
119 | {
120 | 'tfOpName': 'Select',
121 | 'category': 'logical',
122 | 'inputs': [
123 | {'start': 0, 'name': 'condition', 'type': 'tensor'},
124 | {'start': 1, 'name': 'a', 'type': 'tensor'},
125 | {'start': 2, 'name': 'b', 'type': 'tensor'},
126 | ],
127 | 'attrs': [{
128 | 'tfName': 'T',
129 | 'name': 'dtype',
130 | 'type': 'dtype',
131 | 'notSupported': true
132 | }]
133 | }
134 | ];
135 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/matrices.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import {OpMapper} from '../types';
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'MatMul',
23 | 'category': 'matrices',
24 | 'inputs': [
25 | {'start': 0, 'name': 'a', 'type': 'tensor'},
26 | {'start': 1, 'name': 'b', 'type': 'tensor'},
27 | ],
28 | 'attrs': [
29 | {
30 | 'tfName': 'transpose_a',
31 | 'name': 'transposeA',
32 | 'type': 'bool',
33 | 'defaultValue': false
34 | },
35 | {
36 | 'tfName': 'transpose_b',
37 | 'name': 'transposeB',
38 | 'type': 'bool',
39 | 'defaultValue': false
40 | },
41 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
42 | ]
43 | },
44 | {
45 | 'tfOpName': 'BatchMatMul',
46 | 'category': 'matrices',
47 | 'inputs': [
48 | {'start': 0, 'name': 'a', 'type': 'tensor'},
49 | {'start': 1, 'name': 'b', 'type': 'tensor'},
50 | ],
51 | 'attrs': [
52 | {
53 | 'tfName': 'adj_x',
54 | 'name': 'transposeA',
55 | 'type': 'bool',
56 | 'defaultValue': false
57 | },
58 | {
59 | 'tfName': 'adj_y',
60 | 'name': 'transposeB',
61 | 'type': 'bool',
62 | 'defaultValue': false
63 | },
64 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
65 | ]
66 | },
67 | {
68 | 'tfOpName': 'BatchMatMulV2',
69 | 'category': 'matrices',
70 | 'inputs': [
71 | {'start': 0, 'name': 'a', 'type': 'tensor'},
72 | {'start': 1, 'name': 'b', 'type': 'tensor'},
73 | ],
74 | 'attrs': [
75 | {
76 | 'tfName': 'adj_x',
77 | 'name': 'transposeA',
78 | 'type': 'bool',
79 | 'defaultValue': false
80 | },
81 | {
82 | 'tfName': 'adj_y',
83 | 'name': 'transposeB',
84 | 'type': 'bool',
85 | 'defaultValue': false
86 | },
87 | {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}
88 | ]
89 | },
90 | {
91 | 'tfOpName': 'Transpose',
92 | 'category': 'matrices',
93 | 'inputs': [
94 | {'start': 0, 'name': 'x', 'type': 'tensor'},
95 | {'start': 1, 'name': 'perm', 'type': 'number[]'},
96 | ],
97 | 'attrs': [{
98 | 'tfName': 'T',
99 | 'name': 'dtype',
100 | 'type': 'dtype',
101 | 'notSupported': true
102 | }]
103 | }
104 | ];
105 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/normalization.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'FusedBatchNorm',
23 | 'category': 'normalization',
24 | 'inputs': [
25 | {'start': 0, 'name': 'x', 'type': 'tensor'},
26 | {'start': 1, 'name': 'scale', 'type': 'tensor'},
27 | {'start': 2, 'name': 'offset', 'type': 'tensor'},
28 | {'start': 3, 'name': 'mean', 'type': 'tensor'},
29 | {'start': 4, 'name': 'variance', 'type': 'tensor'},
30 | ],
31 | 'attrs': [
32 | {
33 | 'tfName': 'epsilon',
34 | 'name': 'epsilon',
35 | 'type': 'number',
36 | 'defaultValue': 0.001
37 | },
38 | {
39 | 'tfName': 'data_format',
40 | 'name': 'dataFormat',
41 | 'type': 'string',
42 | 'notSupported': true
43 | }
44 | ]
45 | },
46 | {
47 | 'tfOpName': 'FusedBatchNormV2',
48 | 'category': 'normalization',
49 | 'inputs': [
50 | {'start': 0, 'name': 'x', 'type': 'tensor'},
51 | {'start': 1, 'name': 'scale', 'type': 'tensor'},
52 | {'start': 2, 'name': 'offset', 'type': 'tensor'},
53 | {'start': 3, 'name': 'mean', 'type': 'tensor'},
54 | {'start': 4, 'name': 'variance', 'type': 'tensor'},
55 | ],
56 | 'attrs': [
57 | {
58 | 'tfName': 'epsilon',
59 | 'name': 'epsilon',
60 | 'type': 'number',
61 | 'defaultValue': 0.001
62 | },
63 | {
64 | 'tfName': 'data_format',
65 | 'name': 'dataFormat',
66 | 'type': 'string',
67 | 'notSupported': true
68 | }
69 | ]
70 | },
71 | {
72 | 'tfOpName': 'FusedBatchNormV3',
73 | 'category': 'normalization',
74 | 'inputs': [
75 | {'start': 0, 'name': 'x', 'type': 'tensor'},
76 | {'start': 1, 'name': 'scale', 'type': 'tensor'},
77 | {'start': 2, 'name': 'offset', 'type': 'tensor'},
78 | {'start': 3, 'name': 'mean', 'type': 'tensor'},
79 | {'start': 4, 'name': 'variance', 'type': 'tensor'},
80 | ],
81 | 'attrs': [
82 | {
83 | 'tfName': 'epsilon',
84 | 'name': 'epsilon',
85 | 'type': 'number',
86 | 'defaultValue': 0.001
87 | },
88 | {
89 | 'tfName': 'data_format',
90 | 'name': 'dataFormat',
91 | 'type': 'string',
92 | 'notSupported': true
93 | }
94 | ]
95 | },
96 | {
97 | 'tfOpName': 'LRN',
98 | 'category': 'normalization',
99 | 'inputs': [
100 | {'start': 0, 'name': 'x', 'type': 'tensor'},
101 | ],
102 | 'attrs': [
103 | {
104 | 'tfName': 'depth_radius',
105 | 'name': 'radius',
106 | 'type': 'number',
107 | 'defaultValue': 5
108 | },
109 | {'tfName': 'bias', 'name': 'bias', 'type': 'number', 'defaultValue': 1.0},
110 | {
111 | 'tfName': 'alpha',
112 | 'name': 'alpha',
113 | 'type': 'number',
114 | 'defaultValue': 1.0
115 | },
116 | {
117 | 'tfName': 'beta',
118 | 'name': 'beta',
119 | 'type': 'number',
120 | 'defaultValue': 0.5
121 | }
122 | ]
123 | },
124 | {
125 | 'tfOpName': 'Softmax',
126 | 'category': 'normalization',
127 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
128 | },
129 | {
130 | 'tfOpName': 'LogSoftmax',
131 | 'category': 'normalization',
132 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
133 | },
134 | {
135 | 'tfOpName': 'SparseToDense',
136 | 'category': 'normalization',
137 | 'inputs': [
138 | {'start': 0, 'name': 'sparseIndices', 'type': 'tensor'},
139 | {'start': 1, 'name': 'outputShape', 'type': 'number[]'},
140 | {'start': 2, 'name': 'sparseValues', 'type': 'tensor'},
141 | {'start': 3, 'name': 'defaultValue', 'type': 'tensor'},
142 | ],
143 | 'attrs': [{
144 | 'tfName': 'validate_indices',
145 | 'name': 'validateIndices',
146 | 'type': 'bool',
147 | 'defaultValue': true,
148 | 'notSupported': true
149 | }]
150 | }
151 | ];
152 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/op_list_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import * as ajv from 'ajv';
19 | import * as schema from '../op_mapper_schema';
20 |
21 | import * as arithmetic from './arithmetic';
22 | import * as basicMath from './basic_math';
23 | import * as control from './control';
24 | import * as convolution from './convolution';
25 | import * as creation from './creation';
26 | import * as dynamic from './dynamic';
27 | import * as evaluation from './evaluation';
28 | import * as graph from './graph';
29 | import * as image from './image';
30 | import * as logical from './logical';
31 | import * as matrices from './matrices';
32 | import * as normalization from './normalization';
33 | import * as reduction from './reduction';
34 | import * as sliceJoin from './slice_join';
35 | import * as transformation from './transformation';
36 |
37 | describe('OpListTest', () => {
38 | const jsonValidator = new ajv();
39 | const validator = jsonValidator.compile(schema.json);
40 | beforeEach(() => {});
41 |
42 | // tslint:disable-next-line:no-any
43 | const mappersJson: any = {
44 | arithmetic,
45 | basicMath,
46 | control,
47 | convolution,
48 | dynamic,
49 | evaluation,
50 | creation,
51 | logical,
52 | image,
53 | graph,
54 | matrices,
55 | normalization,
56 | reduction,
57 | sliceJoin,
58 | transformation
59 | };
60 | Object.keys(mappersJson).forEach(key => {
61 | it('should satisfy the schema: ' + key, () => {
62 | const valid = validator(mappersJson[key].json);
63 | if (!valid) console.log(validator.errors);
64 | expect(valid).toBeTruthy();
65 | });
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/reduction.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'Max',
23 | 'category': 'reduction',
24 | 'inputs': [
25 | {'start': 0, 'name': 'x', 'type': 'tensor'},
26 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
27 | ],
28 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
29 | },
30 | {
31 | 'tfOpName': 'Mean',
32 | 'category': 'reduction',
33 | 'inputs': [
34 | {'start': 0, 'name': 'x', 'type': 'tensor'},
35 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
36 | ],
37 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
38 | },
39 | {
40 | 'tfOpName': 'Min',
41 | 'category': 'reduction',
42 | 'inputs': [
43 | {'start': 0, 'name': 'x', 'type': 'tensor'},
44 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
45 | ],
46 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
47 | },
48 | {
49 | 'tfOpName': 'Sum',
50 | 'category': 'reduction',
51 | 'inputs': [
52 | {'start': 0, 'name': 'x', 'type': 'tensor'},
53 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
54 | ],
55 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
56 | },
57 | {
58 | 'tfOpName': 'All',
59 | 'category': 'reduction',
60 | 'inputs': [
61 | {'start': 0, 'name': 'x', 'type': 'tensor'},
62 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
63 | ],
64 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
65 | },
66 | {
67 | 'tfOpName': 'Any',
68 | 'category': 'reduction',
69 | 'inputs': [
70 | {'start': 0, 'name': 'x', 'type': 'tensor'},
71 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
72 | ],
73 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
74 | },
75 | {
76 | 'tfOpName': 'ArgMax',
77 | 'category': 'reduction',
78 | 'inputs': [
79 | {'start': 0, 'name': 'x', 'type': 'tensor'},
80 | {'start': 1, 'name': 'axis', 'type': 'number'}
81 | ]
82 | },
83 | {
84 | 'tfOpName': 'ArgMin',
85 | 'category': 'reduction',
86 | 'inputs': [
87 | {'start': 0, 'name': 'x', 'type': 'tensor'},
88 | {'start': 1, 'name': 'axis', 'type': 'number'}
89 | ]
90 | },
91 | {
92 | 'tfOpName': 'Prod',
93 | 'category': 'reduction',
94 | 'inputs': [
95 | {'start': 0, 'name': 'x', 'type': 'tensor'},
96 | {'start': 1, 'name': 'axis', 'type': 'number[]'},
97 | ],
98 | 'attrs': [{'tfName': 'keep_dims', 'name': 'keepDims', 'type': 'bool'}]
99 | }
100 | ];
101 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/spectral.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'FFT',
23 | 'category': 'spectral',
24 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
25 | },
26 | {
27 | 'tfOpName': 'IFFT',
28 | 'category': 'spectral',
29 | 'inputs': [{'start': 0, 'name': 'x', 'type': 'tensor'}]
30 | },
31 | {
32 | 'tfOpName': 'RFFT',
33 | 'category': 'spectral',
34 | 'inputs': [
35 | {'start': 0, 'name': 'x', 'type': 'tensor'}, {
36 | 'start': 1,
37 | 'name': 'fft_length',
38 | 'type': 'number',
39 | 'notSupported': true
40 | }
41 | ]
42 | },
43 | {
44 | 'tfOpName': 'IRFFT',
45 | 'category': 'spectral',
46 | 'inputs': [
47 | {'start': 0, 'name': 'x', 'type': 'tensor'}, {
48 | 'start': 1,
49 | 'name': 'fft_length',
50 | 'type': 'number',
51 | 'notSupported': true
52 | }
53 | ]
54 | }
55 | ];
56 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_list/transformation.ts:
--------------------------------------------------------------------------------
1 | import {OpMapper} from '../types';
2 |
3 | /**
4 | * @license
5 | * Copyright 2018 Google LLC. All Rights Reserved.
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | * =============================================================================
18 | */
19 |
20 | export const json: OpMapper[] = [
21 | {
22 | 'tfOpName': 'Cast',
23 | 'category': 'transformation',
24 | 'inputs': [
25 | {'start': 0, 'name': 'x', 'type': 'tensor'},
26 | ],
27 | 'attrs': [
28 | {
29 | 'tfName': 'SrcT',
30 | 'name': 'sdtype',
31 | 'type': 'dtype',
32 | 'notSupported': true
33 | },
34 | {'tfName': 'DstT', 'name': 'dtype', 'type': 'dtype'}
35 | ]
36 | },
37 | {
38 | 'tfOpName': 'ExpandDims',
39 | 'category': 'transformation',
40 | 'inputs': [
41 | {'start': 0, 'name': 'x', 'type': 'tensor'},
42 | {'start': 1, 'name': 'axis', 'type': 'number'}
43 | ]
44 | },
45 | {
46 | 'tfOpName': 'Pad',
47 | 'category': 'transformation',
48 | 'inputs': [
49 | {'start': 0, 'name': 'x', 'type': 'tensor'},
50 | {'start': 1, 'name': 'padding', 'type': 'number[]'},
51 | ],
52 | 'attrs': [{
53 | 'tfName': 'constant_value',
54 | 'name': 'constantValue',
55 | 'type': 'number',
56 | 'defaultValue': 0
57 | }]
58 | },
59 | {
60 | 'tfOpName': 'PadV2',
61 | 'category': 'transformation',
62 | 'inputs': [
63 | {'start': 0, 'name': 'x', 'type': 'tensor'},
64 | {'start': 1, 'name': 'padding', 'type': 'number[]'}, {
65 | 'start': 2,
66 | 'name': 'constantValue',
67 | 'type': 'number',
68 | 'defaultValue': 0
69 | }
70 | ]
71 | },
72 | {
73 | 'tfOpName': 'Reshape',
74 | 'category': 'transformation',
75 | 'inputs': [
76 | {'start': 0, 'name': 'x', 'type': 'tensor'},
77 | {'start': 1, 'name': 'shape', 'type': 'number[]'}
78 | ]
79 | },
80 | {
81 | 'tfOpName': 'Squeeze',
82 | 'category': 'transformation',
83 | 'inputs': [
84 | {'start': 0, 'name': 'x', 'type': 'tensor'},
85 | ],
86 | 'attrs': [{
87 | 'tfName': 'axis',
88 | 'tfDeprecatedName': 'squeeze_dims',
89 | 'name': 'axis',
90 | 'type': 'number[]'
91 | }]
92 | },
93 | {
94 | 'tfOpName': 'SpaceToBatchND',
95 | 'category': 'transformation',
96 | 'inputs': [
97 | {'start': 0, 'name': 'x', 'type': 'tensor'},
98 | {'start': 1, 'name': 'blockShape', 'type': 'number[]'},
99 | {'start': 2, 'name': 'paddings', 'type': 'number[]'}
100 | ]
101 | },
102 | {
103 | 'tfOpName': 'BatchToSpaceND',
104 | 'category': 'transformation',
105 | 'inputs': [
106 | {'start': 0, 'name': 'x', 'type': 'tensor'},
107 | {'start': 1, 'name': 'blockShape', 'type': 'number[]'},
108 | {'start': 2, 'name': 'crops', 'type': 'number[]'}
109 | ]
110 | },
111 | {
112 | 'tfOpName': 'DepthToSpace',
113 | 'category': 'transformation',
114 | 'inputs': [
115 | {'start': 0, 'name': 'x', 'type': 'tensor'},
116 | ],
117 | 'attrs': [
118 | {'tfName': 'block_size', 'name': 'blockSize', 'type': 'number'},
119 | {'tfName': 'data_format', 'name': 'dataFormat', 'type': 'string'}
120 | ]
121 | }
122 | ];
123 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/op_mapper_schema.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | export const json = {
19 | '$schema': 'http://json-schema.org/draft-07/schema#',
20 | 'definitions': {
21 | 'OpMapper': {
22 | 'type': 'object',
23 | 'properties': {
24 | 'tfOpName': {'type': 'string'},
25 | 'category': {'$ref': '#/definitions/Category'},
26 | 'inputs': {
27 | 'type': 'array',
28 | 'items': {'$ref': '#/definitions/InputParamMapper'}
29 | },
30 | 'attrs': {
31 | 'type': 'array',
32 | 'items': {'$ref': '#/definitions/AttrParamMapper'}
33 | },
34 | 'customExecutor': {'$ref': '#/definitions/OpExecutor'}
35 | },
36 | 'required': ['tfOpName'],
37 | 'additionalProperties': false
38 | },
39 | 'Category': {
40 | 'type': 'string',
41 | 'enum': [
42 | 'arithmetic', 'basic_math', 'control', 'convolution', 'custom',
43 | 'dynamic', 'evaluation', 'image', 'creation', 'graph', 'logical',
44 | 'matrices', 'normalization', 'reduction', 'slice_join', 'spectral',
45 | 'transformation'
46 | ]
47 | },
48 | 'InputParamMapper': {
49 | 'type': 'object',
50 | 'properties': {
51 | 'name': {'type': 'string'},
52 | 'type': {'$ref': '#/definitions/ParamTypes'},
53 | 'defaultValue': {
54 | 'anyOf': [
55 | {'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}},
56 | {'type': 'number'}, {'type': 'array', 'items': {'type': 'number'}},
57 | {'type': 'boolean'},
58 | {'type': 'array', 'items': {'type': 'boolean'}}
59 | ]
60 | },
61 | 'notSupported': {'type': 'boolean'},
62 | 'start': {'type': 'number'},
63 | 'end': {'type': 'number'}
64 | },
65 | 'required': ['name', 'start', 'type'],
66 | 'additionalProperties': false
67 | },
68 | 'ParamTypes': {
69 | 'type': 'string',
70 | 'enum': [
71 | 'number', 'string', 'number[]', 'bool', 'shape', 'tensor', 'tensors',
72 | 'dtype'
73 | ]
74 | },
75 | 'AttrParamMapper': {
76 | 'type': 'object',
77 | 'properties': {
78 | 'name': {'type': 'string'},
79 | 'type': {'$ref': '#/definitions/ParamTypes'},
80 | 'defaultValue': {
81 | 'anyOf': [
82 | {'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}},
83 | {'type': 'number'}, {'type': 'array', 'items': {'type': 'number'}},
84 | {'type': 'boolean'},
85 | {'type': 'array', 'items': {'type': 'boolean'}}
86 | ]
87 | },
88 | 'notSupported': {'type': 'boolean'},
89 | 'tfName': {'type': 'string'},
90 | 'tfDeprecatedName': {'type': 'string'}
91 | },
92 | 'required': ['name', 'tfName', 'type'],
93 | 'additionalProperties': false
94 | },
95 | 'OpExecutor': {'type': 'object', 'additionalProperties': false}
96 | },
97 | 'items': {'$ref': '#/definitions/OpMapper'},
98 | 'type': 'array'
99 | };
100 |
--------------------------------------------------------------------------------
/tfjs-converter/src/operations/operation_executor_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 |
18 | import {add, mul, scalar, Tensor, test_util} from '@tensorflow/tfjs-core';
19 |
20 | import {ExecutionContext} from '../executor/execution_context';
21 |
22 | import {deregisterOp, registerOp} from './custom_op/register';
23 | import * as arithmetic from './executors/arithmetic_executor';
24 | import * as basic_math from './executors/basic_math_executor';
25 | import * as convolution from './executors/convolution_executor';
26 | import * as creation from './executors/creation_executor';
27 | import * as dynamic from './executors/dynamic_executor';
28 | import * as evaluation from './executors/evaluation_executor';
29 | import * as graph from './executors/graph_executor';
30 | import * as image from './executors/image_executor';
31 | import * as logical from './executors/logical_executor';
32 | import * as matrices from './executors/matrices_executor';
33 | import * as normalization from './executors/normalization_executor';
34 | import * as reduction from './executors/reduction_executor';
35 | import * as slice_join from './executors/slice_join_executor';
36 | import * as spectral from './executors/spectral_executor';
37 | import * as transformation from './executors/transformation_executor';
38 | import {executeOp} from './operation_executor';
39 | import {Node} from './types';
40 |
41 | describe('OperationExecutor', () => {
42 | let node: Node;
43 | const context = new ExecutionContext({}, {});
44 |
45 | beforeEach(() => {
46 | node = {
47 | name: 'test',
48 | op: 'const',
49 | category: 'graph',
50 | inputNames: [],
51 | inputs: [],
52 | inputParams: {},
53 | attrParams: {},
54 | children: []
55 | };
56 | });
57 |
58 | describe('executeOp', () => {
59 | [arithmetic, basic_math, convolution, creation, dynamic, evaluation, image,
60 | graph, logical, matrices, normalization, reduction, slice_join, spectral,
61 | transformation]
62 | .forEach(category => {
63 | it('should call ' + category.CATEGORY + ' executor', () => {
64 | spyOn(category, 'executeOp');
65 | node.category = category.CATEGORY;
66 | executeOp(node, {}, context);
67 | expect(category.executeOp).toHaveBeenCalledWith(node, {}, context);
68 | });
69 | });
70 | });
71 |
72 | describe('custom op executeOp', () => {
73 | it('should throw exception if custom op is not registered', () => {
74 | node.category = 'custom';
75 | expect(() => executeOp(node, {}, context))
76 | .toThrowError('Custom op const is not registered.');
77 | });
78 | });
79 |
80 | describe('custom op executeOp', () => {
81 | it('should call the registered custom op', async () => {
82 | registerOp('const', () => [scalar(1)]);
83 | registerOp('const2', () => [scalar(2)]);
84 | node.category = 'custom';
85 | const result = executeOp(node, {}, context) as Tensor[];
86 | test_util.expectArraysClose(await result[0].data(), [1]);
87 | deregisterOp('const');
88 | deregisterOp('const2');
89 | });
90 |
91 | it('should handle custom op with inputs and attrs', async () => {
92 | registerOp('const', (node) => {
93 | const a = node.inputs[0];
94 | const b = node.inputs[1];
95 | const attrC = node.attrs['c'] as Tensor;
96 | const attrD = node.attrs['d'] as number;
97 | return [add(mul(attrC.dataSync()[0], a), mul(attrD, b))];
98 | });
99 |
100 | node.category = 'custom';
101 | node.inputNames = ['a', 'b'];
102 | node.rawAttrs = {c: {tensor: {}}, d: {i: 3}};
103 | const result = executeOp(
104 | node, {a: [scalar(1)], b: [scalar(2)], c: [scalar(2)]},
105 | context) as Tensor[];
106 | // result = 2 * 1 + 3 * 2
107 | test_util.expectArraysClose(await result[0].data(), [8]);
108 | deregisterOp('const');
109 | });
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/tfjs-converter/src/version.ts:
--------------------------------------------------------------------------------
1 | /** @license See the LICENSE file. */
2 |
3 | // This code is auto-generated, do not modify this file!
4 | const version = '1.2.7';
5 | export {version};
6 |
--------------------------------------------------------------------------------
/tfjs-converter/src/version_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC.
4 | *
5 | * Use of this source code is governed by an MIT-style
6 | * license that can be found in the LICENSE file or at
7 | * https://opensource.org/licenses/MIT.
8 | * =============================================================================
9 | */
10 |
11 | // tslint:disable-next-line:no-require-imports
12 | const packageJSON = require('../package.json');
13 | import {version_converter} from './index';
14 |
15 | describe('tfjs-core version consistency', () => {
16 | it('dev-peer match', () => {
17 | const tfjsCoreDevDepVersion =
18 | packageJSON.devDependencies['@tensorflow/tfjs-core'];
19 | const tfjsCorePeerDepVersion =
20 | packageJSON.peerDependencies['@tensorflow/tfjs-core'];
21 | expect(tfjsCoreDevDepVersion).toEqual(tfjsCorePeerDepVersion);
22 | });
23 |
24 | it('version.ts matches package version', () => {
25 | // tslint:disable-next-line:no-require-imports
26 | const expected = require('../package.json').version;
27 | expect(version_converter).toBe(expected);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/tfjs-converter/tools/pb2json_converter.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2018 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | import * as fs from 'fs';
18 | import * as Long from 'long';
19 | import * as path from 'path';
20 |
21 | import {tensorflow} from './compiled_api';
22 |
23 | function replacer(key: string, value: any) {
24 | if (value instanceof Long) {
25 | return (value as Long).toString();
26 | }
27 | if (value instanceof Uint8Array) {
28 | return Array.from(value);
29 | }
30 | return value;
31 | }
32 | const PB_MODEL_FILENAME = 'tensorflowjs_model.pb';
33 | const WEIGHT_FILENAME = 'weights_manifest.json';
34 | const JSON_MODEL_FILENAME = 'model.json';
35 | function convert(argv: string[]) {
36 | if (argv.length < 4) {
37 | console.log(
38 | 'Usage: ts-node pb2json.ts pb_model_directory json_model_directory');
39 | return;
40 | }
41 |
42 | const sourcePath = process.argv[2];
43 | console.log('reading pb model directory: ' + sourcePath);
44 | fs.readdir(sourcePath, (err, files) => {
45 | if (![PB_MODEL_FILENAME, WEIGHT_FILENAME].every(
46 | file => files.indexOf(file) !== -1)) {
47 | console.log(
48 | 'Please make sure the pb model directory contains ' +
49 | 'tensorflowjs_model.pb and weights_manifest.json files.');
50 | return;
51 | }
52 |
53 | const modelFile = path.join(sourcePath, PB_MODEL_FILENAME);
54 | console.log('reading pb file: ' + modelFile);
55 | const buffer = fs.readFileSync(modelFile);
56 |
57 | const modelTopology = tensorflow.GraphDef.decode(new Uint8Array(buffer));
58 |
59 | const manifestFile = path.join(sourcePath, WEIGHT_FILENAME);
60 | console.log('reading manifest file: ' + manifestFile);
61 | const weightsManifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
62 |
63 | const destPath = process.argv[3];
64 |
65 | if (!fs.existsSync(destPath)) {
66 | fs.mkdirSync(destPath);
67 | }
68 | const destModelFile = path.join(destPath, JSON_MODEL_FILENAME);
69 | console.log('writing json file: ' + destModelFile);
70 | fs.writeFileSync(
71 | destModelFile,
72 | JSON.stringify({modelTopology, weightsManifest}, replacer));
73 |
74 | files.forEach(file => {
75 | if (file !== PB_MODEL_FILENAME && file !== WEIGHT_FILENAME) {
76 | fs.copyFile(
77 | path.join(sourcePath, file), path.join(destPath, file), (err) => {
78 | if (err) throw err;
79 | console.log(`Weight file: ${file} copied.`);
80 | });
81 | }
82 | });
83 | });
84 | }
85 |
86 | convert(process.argv);
87 |
--------------------------------------------------------------------------------
/tfjs-converter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "moduleResolution": "node",
5 | "noImplicitAny": true,
6 | "sourceMap": true,
7 | "removeComments": false,
8 | "preserveConstEnums": true,
9 | "declaration": true,
10 | "target": "es5",
11 | "lib": [
12 | "es2015",
13 | "dom"
14 | ],
15 | "outDir": "./dist",
16 | "noUnusedLocals": true,
17 | "noImplicitReturns": true,
18 | "noImplicitThis": true,
19 | "alwaysStrict": true,
20 | "noUnusedParameters": false,
21 | "pretty": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "allowUnreachableCode": false
24 | },
25 | "include": [
26 | "src/",
27 | "scripts/"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/tfjs-converter/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-no-circular-imports"],
3 | "linterOptions": {
4 | "exclude": [
5 | "src/data/compiled_api.ts"
6 | ]
7 | },
8 | "rules": {
9 | "array-type": [true, "array-simple"],
10 | "arrow-return-shorthand": true,
11 | "ban": [true,
12 | ["fit"],
13 | ["fdescribe"],
14 | ["xit"],
15 | ["xdescribe"],
16 | ["fitAsync"],
17 | ["xitAsync"],
18 | ["fitFakeAsync"],
19 | ["xitFakeAsync"]
20 | ],
21 | "ban-types": [true,
22 | ["Object", "Use {} instead."],
23 | ["String", "Use 'string' instead."],
24 | ["Number", "Use 'number' instead."],
25 | ["Boolean", "Use 'boolean' instead."]
26 | ],
27 | "class-name": true,
28 | "interface-name": [true, "never-prefix"],
29 | "jsdoc-format": true,
30 | "forin": false,
31 | "label-position": true,
32 | "max-line-length": {
33 | "options": {"limit": 80, "ignore-pattern": "^import |^export "}
34 | },
35 | "new-parens": true,
36 | "no-angle-bracket-type-assertion": true,
37 | "no-any": true,
38 | "no-construct": true,
39 | "no-consecutive-blank-lines": true,
40 | "no-debugger": true,
41 | "no-default-export": true,
42 | "no-inferrable-types": true,
43 | "no-namespace": [true, "allow-declarations"],
44 | "no-reference": true,
45 | "no-require-imports": true,
46 | "no-string-throw": true,
47 | "no-unused-expression": true,
48 | "no-unused-variable": [true, {"ignore-pattern": "^_"}],
49 | "no-var-keyword": true,
50 | "object-literal-shorthand": true,
51 | "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"],
52 | "prefer-const": true,
53 | "radix": true,
54 | "restrict-plus-operands": true,
55 | "semicolon": [true, "always", "ignore-bound-class-methods"],
56 | "switch-default": true,
57 | "triple-equals": [true, "allow-null-check"],
58 | "use-isnan": true,
59 | "variable-name": [
60 | true,
61 | "check-format",
62 | "ban-keywords",
63 | "allow-leading-underscore",
64 | "allow-trailing-underscore"
65 | ]
66 | }
67 | }
68 |
--------------------------------------------------------------------------------