Skip to content

Commit 40e1800

Browse files
authored
Merge branch 'master' into add/conv3d
2 parents 003e04b + 37801a5 commit 40e1800

File tree

15 files changed

+407
-50
lines changed

15 files changed

+407
-50
lines changed

docs/supported_ops.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@
9494
|Tensorflow Op Name|Tensorflow.js Op Name|
9595
|---|---|
9696
|AvgPool|avgPool|
97+
|AvgPool3D|avgPool3d|
9798
|Conv1D|conv1d|
9899
|Conv2D|conv2d|
99100
|Conv2DBackpropInput|conv2dTranspose|
100101
|DepthwiseConv2d|depthwiseConv2d|
101102
|DepthwiseConv2dNative|depthwiseConv2d|
102103
|MaxPool|maxPool|
104+
|MaxPool3D|maxPool3d|
103105
|Not mapped|pool|
104106
|Not mapped|separableConv2d|
105107

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
},
1616
"license": "Apache-2.0",
1717
"peerDependencies": {
18-
"@tensorflow/tfjs-core": "1.2.7"
18+
"@tensorflow/tfjs-core": "1.2.8"
1919
},
2020
"devDependencies": {
21-
"@tensorflow/tfjs-core": "1.2.7",
21+
"@tensorflow/tfjs-core": "1.2.8",
2222
"@types/deep-equal": "^1.0.1",
2323
"@types/jasmine": "~2.8.6",
2424
"@types/long": "~3.0.32",

python/run-python-tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ TEST_FILES="$(find "${SCRIPTS_DIR}" -name '*_test.py')"
2424

2525
pip install virtualenv
2626

27-
TMP_VENV_DIR="$(mktemp -d)"
27+
TMP_VENV_DIR="$(mktemp -d --suffix=_venv)"
2828
virtualenv -p "python" "${TMP_VENV_DIR}"
2929
source "${TMP_VENV_DIR}/bin/activate"
3030

python/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def _get_requirements(file):
2626
return requirements.readlines()
2727

2828
CONSOLE_SCRIPTS = [
29-
'tensorflowjs_converter = tensorflowjs.converters.converter:main',
29+
'tensorflowjs_converter = tensorflowjs.converters.converter:pip_main',
3030
]
3131

3232
setuptools.setup(

python/tensorflowjs/converters/BUILD

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ py_binary(
101101
visibility = ["//visibility:public"],
102102
)
103103

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+
104114
py_test(
105115
name = "converter_test",
106116
srcs = ["converter_test.py"],
@@ -113,3 +123,12 @@ py_test(
113123
"//tensorflowjs:version",
114124
],
115125
)
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+
)

python/tensorflowjs/converters/converter.py

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import json
2323
import os
2424
import shutil
25+
import sys
2526
import tempfile
2627

2728
import h5py
@@ -434,7 +435,8 @@ def _parse_quantization_bytes(quantization_bytes):
434435
raise ValueError('Unsupported quantization bytes: %s' % quantization_bytes)
435436

436437

437-
def setup_arguments():
438+
def get_arg_parser():
439+
"""Create the argument parser for the converter binary."""
438440
parser = argparse.ArgumentParser('TensorFlow.js model converters.')
439441
parser.add_argument(
440442
'input_path',
@@ -522,39 +524,57 @@ def setup_arguments():
522524
default=None,
523525
help='Shard size (in bytes) of the weight files. Currently applicable '
524526
'only to output_format=tfjs_layers_model.')
525-
return parser.parse_args()
527+
return parser
526528

527529

528-
def main():
529-
FLAGS = setup_arguments()
530-
if FLAGS.show_version:
530+
def pip_main():
531+
"""Entry point for pip-packaged binary.
532+
533+
Note that pip-packaged binary calls the entry method without
534+
any arguments, which is why this method is needed in addition to the
535+
`main` method below.
536+
"""
537+
main([' '.join(sys.argv[1:])])
538+
539+
540+
def main(argv):
541+
args = get_arg_parser().parse_args(argv[0].split(' '))
542+
543+
if args.show_version:
531544
print('\ntensorflowjs %s\n' % version.version)
532545
print('Dependency versions:')
533546
print(' keras %s' % keras.__version__)
534547
print(' tensorflow %s' % tf.__version__)
535548
return
536549

550+
if not args.input_path:
551+
raise ValueError(
552+
'Missing input_path argument. For usage, use the --help flag.')
553+
if not args.output_path:
554+
raise ValueError(
555+
'Missing output_path argument. For usage, use the --help flag.')
556+
537557
weight_shard_size_bytes = 1024 * 1024 * 4
538-
if FLAGS.weight_shard_size_bytes:
539-
if FLAGS.output_format != 'tfjs_layers_model':
558+
if args.weight_shard_size_bytes:
559+
if args.output_format != 'tfjs_layers_model':
540560
raise ValueError(
541561
'The --weight_shard_size_byte flag is only supported under '
542562
'output_format=tfjs_layers_model.')
543-
weight_shard_size_bytes = FLAGS.weight_shard_size_bytes
563+
weight_shard_size_bytes = args.weight_shard_size_bytes
544564

545-
if FLAGS.input_path is None:
565+
if args.input_path is None:
546566
raise ValueError(
547567
'Error: The input_path argument must be set. '
548568
'Run with --help flag for usage information.')
549569

550570
input_format, output_format = _standardize_input_output_formats(
551-
FLAGS.input_format, FLAGS.output_format)
571+
args.input_format, args.output_format)
552572

553573
quantization_dtype = (
554-
quantization.QUANTIZATION_BYTES_TO_DTYPES[FLAGS.quantization_bytes]
555-
if FLAGS.quantization_bytes else None)
574+
quantization.QUANTIZATION_BYTES_TO_DTYPES[args.quantization_bytes]
575+
if args.quantization_bytes else None)
556576

557-
if (FLAGS.signature_name and input_format not in
577+
if (args.signature_name and input_format not in
558578
('tf_saved_model', 'tf_hub')):
559579
raise ValueError(
560580
'The --signature_name flag is applicable only to "tf_saved_model" and '
@@ -565,62 +585,62 @@ def main():
565585
# branches below.
566586
if input_format == 'keras' and output_format == 'tfjs_layers_model':
567587
dispatch_keras_h5_to_tfjs_layers_model_conversion(
568-
FLAGS.input_path, output_dir=FLAGS.output_path,
588+
args.input_path, output_dir=args.output_path,
569589
quantization_dtype=quantization_dtype,
570-
split_weights_by_layer=FLAGS.split_weights_by_layer)
590+
split_weights_by_layer=args.split_weights_by_layer)
571591
elif input_format == 'keras' and output_format == 'tfjs_graph_model':
572592
dispatch_keras_h5_to_tfjs_graph_model_conversion(
573-
FLAGS.input_path, output_dir=FLAGS.output_path,
593+
args.input_path, output_dir=args.output_path,
574594
quantization_dtype=quantization_dtype,
575-
skip_op_check=FLAGS.skip_op_check,
576-
strip_debug_ops=FLAGS.strip_debug_ops)
595+
skip_op_check=args.skip_op_check,
596+
strip_debug_ops=args.strip_debug_ops)
577597
elif (input_format == 'keras_saved_model' and
578598
output_format == 'tfjs_layers_model'):
579599
dispatch_keras_saved_model_to_tensorflowjs_conversion(
580-
FLAGS.input_path, FLAGS.output_path,
600+
args.input_path, args.output_path,
581601
quantization_dtype=quantization_dtype,
582-
split_weights_by_layer=FLAGS.split_weights_by_layer)
602+
split_weights_by_layer=args.split_weights_by_layer)
583603
elif (input_format == 'tf_saved_model' and
584604
output_format == 'tfjs_graph_model'):
585605
tf_saved_model_conversion_v2.convert_tf_saved_model(
586-
FLAGS.input_path, FLAGS.output_path,
587-
signature_def=FLAGS.signature_name,
588-
saved_model_tags=FLAGS.saved_model_tags,
606+
args.input_path, args.output_path,
607+
signature_def=args.signature_name,
608+
saved_model_tags=args.saved_model_tags,
589609
quantization_dtype=quantization_dtype,
590-
skip_op_check=FLAGS.skip_op_check,
591-
strip_debug_ops=FLAGS.strip_debug_ops)
610+
skip_op_check=args.skip_op_check,
611+
strip_debug_ops=args.strip_debug_ops)
592612
elif (input_format == 'tf_hub' and
593613
output_format == 'tfjs_graph_model'):
594614
tf_saved_model_conversion_v2.convert_tf_hub_module(
595-
FLAGS.input_path, FLAGS.output_path, FLAGS.signature_name,
596-
FLAGS.saved_model_tags, skip_op_check=FLAGS.skip_op_check,
597-
strip_debug_ops=FLAGS.strip_debug_ops)
615+
args.input_path, args.output_path, args.signature_name,
616+
args.saved_model_tags, skip_op_check=args.skip_op_check,
617+
strip_debug_ops=args.strip_debug_ops)
598618
elif (input_format == 'tfjs_layers_model' and
599619
output_format == 'keras'):
600-
dispatch_tensorflowjs_to_keras_h5_conversion(FLAGS.input_path,
601-
FLAGS.output_path)
620+
dispatch_tensorflowjs_to_keras_h5_conversion(args.input_path,
621+
args.output_path)
602622
elif (input_format == 'tfjs_layers_model' and
603623
output_format == 'keras_saved_model'):
604-
dispatch_tensorflowjs_to_keras_saved_model_conversion(FLAGS.input_path,
605-
FLAGS.output_path)
624+
dispatch_tensorflowjs_to_keras_saved_model_conversion(args.input_path,
625+
args.output_path)
606626
elif (input_format == 'tfjs_layers_model' and
607627
output_format == 'tfjs_layers_model'):
608628
dispatch_tensorflowjs_to_tensorflowjs_conversion(
609-
FLAGS.input_path, FLAGS.output_path,
610-
quantization_dtype=_parse_quantization_bytes(FLAGS.quantization_bytes),
629+
args.input_path, args.output_path,
630+
quantization_dtype=_parse_quantization_bytes(args.quantization_bytes),
611631
weight_shard_size_bytes=weight_shard_size_bytes)
612632
elif (input_format == 'tfjs_layers_model' and
613633
output_format == 'tfjs_graph_model'):
614634
dispatch_tfjs_layers_model_to_tfjs_graph_conversion(
615-
FLAGS.input_path, FLAGS.output_path,
616-
quantization_dtype=_parse_quantization_bytes(FLAGS.quantization_bytes),
617-
skip_op_check=FLAGS.skip_op_check,
618-
strip_debug_ops=FLAGS.strip_debug_ops)
635+
args.input_path, args.output_path,
636+
quantization_dtype=_parse_quantization_bytes(args.quantization_bytes),
637+
skip_op_check=args.skip_op_check,
638+
strip_debug_ops=args.strip_debug_ops)
619639
else:
620640
raise ValueError(
621641
'Unsupported input_format - output_format pair: %s - %s' %
622642
(input_format, output_format))
623643

624644

625645
if __name__ == '__main__':
626-
main()
646+
tf.app.run(main=main, argv=[' '.join(sys.argv[1:])])
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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}"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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)

0 commit comments

Comments
 (0)