├── .gitignore ├── .pylintrc ├── .style.yapf ├── LICENSE.md ├── README.md ├── convert.py ├── examples ├── imagenet │ ├── README.md │ ├── classify.py │ ├── dataset.py │ ├── imagenet-classes.txt │ ├── models │ │ ├── __init__.py │ │ ├── alexnet.py │ │ ├── caffenet.py │ │ ├── googlenet.py │ │ ├── helper.py │ │ ├── nin.py │ │ ├── resnet.py │ │ └── vgg.py │ └── validate.py └── mnist │ ├── README.md │ ├── finetune_mnist.py │ ├── lenet.prototxt │ └── lenet_iter_10000.caffemodel └── kaffe ├── __init__.py ├── caffe ├── __init__.py ├── caffepb.py └── resolver.py ├── errors.py ├── graph.py ├── layers.py ├── shapes.py ├── tensorflow ├── __init__.py ├── network.py └── transformer.py └── transformers.py /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X temporary metadata 2 | ._* 3 | *.DS_Store 4 | 5 | # Extracted parameters 6 | *.params 7 | 8 | # Python cache 9 | *.pyc 10 | 11 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | ignore=caffepb.py 3 | 4 | [MESSAGES CONTROL] 5 | disable=missing-docstring,invalid-name,wildcard-import,unused-wildcard-import,bad-builtin,no-self-use,locally-disabled 6 | 7 | [MISCELLANEOUS] 8 | # Exclude TODOs 9 | notes= 10 | 11 | [TYPECHECK] 12 | ignored-classes=numpy,cv2,NodeKind,LayerType,NetParameter,NpzFile 13 | 14 | [DESIGN] 15 | # Maximum number of arguments for function / method 16 | max-args=20 17 | # Maximum number of locals for function / method body 18 | max-locals=30 19 | # Maximum number of return / yield for function / method body 20 | max-returns=10 21 | # Maximum number of branch for function / method body 22 | max-branches=12 23 | # Maximum number of statements in function / method body 24 | max-statements=200 25 | # Maximum number of attributes for a class (see R0902). 26 | max-attributes=100 27 | # Maximum number of public methods for a class (see R0904). 28 | max-public-methods=200 29 | # Maximum number of boolean expressions in a if statement 30 | max-bool-expr=10 31 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = chromium 3 | column_limit = 100 4 | indent_width = 4 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Each contributor holds copyright over their contributions to Caffe-Tensorflow. In particular: 4 | 5 | - Any included network model is provided under its original license. 6 | 7 | - Any portion derived from Caffe is provided under its original license. 8 | 9 | - Caffe-tensorflow is provided under the MIT license, as specified below. 10 | 11 | # The MIT License (MIT) 12 | 13 | Copyright (c) 2016 Saumitro Dasgupta 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Caffe to TensorFlow 2 | 3 | Convert [Caffe](https://github.com/BVLC/caffe/) models to [TensorFlow](https://github.com/tensorflow/tensorflow). 4 | 5 | ## Usage 6 | 7 | Run `convert.py` to convert an existing Caffe model to TensorFlow. 8 | 9 | Make sure you're using the latest Caffe format (see the notes section for more info). 10 | 11 | The output consists of two files: 12 | 13 | 1. A data file (in NumPy's native format) containing the model's learned parameters. 14 | 2. A Python class that constructs the model's graph. 15 | 16 | ### Examples 17 | 18 | See the [examples](examples/) folder for more details. 19 | 20 | ## Verification 21 | 22 | The following converted models have been verified on the ILSVRC2012 validation set using 23 | [validate.py](examples/imagenet/validate.py). 24 | 25 | | Model | Top 5 Accuracy | 26 | |:------------------------------------------------------|---------------:| 27 | | [ResNet 152](http://arxiv.org/abs/1512.03385) | 92.92% | 28 | | [ResNet 101](http://arxiv.org/abs/1512.03385) | 92.63% | 29 | | [ResNet 50](http://arxiv.org/abs/1512.03385) | 92.02% | 30 | | [VGG 16](http://arxiv.org/abs/1409.1556) | 89.88% | 31 | | [GoogLeNet](http://arxiv.org/abs/1409.4842) | 89.06% | 32 | | [Network in Network](http://arxiv.org/abs/1312.4400) | 81.21% | 33 | | [CaffeNet](http://arxiv.org/abs/1408.5093) | 79.93% | 34 | | [AlexNet](http://goo.gl/3BilWd) | 79.84% | 35 | 36 | ## Notes 37 | 38 | - Only the new Caffe model format is supported. If you have an old model, use the `upgrade_net_proto_text` and `upgrade_net_proto_binary` tools that ship with Caffe to upgrade them first. Also make sure you're using a fairly recent version of Caffe. 39 | 40 | - It appears that Caffe and TensorFlow cannot be concurrently invoked (CUDA conflicts - even with `set_mode_cpu`). This makes it a two-stage process: first extract the parameters with `convert.py`, then import it into TensorFlow. 41 | 42 | - Caffe is not strictly required. If PyCaffe is found in your `PYTHONPATH`, and the `USE_PYCAFFE` environment variable is set, it will be used. Otherwise, a fallback will be used. However, the fallback uses the pure Python-based implementation of protobuf, which is astoundingly slow (~1.5 minutes to parse the VGG16 parameters). The experimental CPP protobuf backend doesn't particularly help here, since it runs into the file size limit (Caffe gets around this by overriding this limit in C++). A cleaner solution here would be to implement the loader as a C++ module. 43 | 44 | - Only a subset of Caffe layers and accompanying parameters are currently supported. 45 | 46 | - Not all Caffe models can be converted to TensorFlow. For instance, Caffe supports arbitrary padding whereas TensorFlow's support is currently restricted to `SAME` and `VALID`. 47 | 48 | - The border values are handled differently by Caffe and TensorFlow. However, these don't appear to affect things too much. 49 | 50 | - Image rescaling can affect the ILSVRC2012 top 5 accuracy listed above slightly. VGG16 expects isotropic rescaling (anisotropic reduces accuracy to 88.45%) whereas BVLC's implementation of GoogLeNet expects anisotropic (isotropic reduces accuracy to 87.7%). 51 | 52 | - The support class `kaffe.tensorflow.Network` has no internal dependencies. It can be safely extracted and deployed without the rest of this library. 53 | 54 | - The ResNet model uses 1x1 convolutions with a stride of 2. This is currently only supported in the master branch of TensorFlow (the latest release at time of writing being v0.8.0, which does not support it). 55 | -------------------------------------------------------------------------------- /convert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import numpy as np 6 | import argparse 7 | from kaffe import KaffeError, print_stderr 8 | from kaffe.tensorflow import TensorFlowTransformer 9 | 10 | 11 | def fatal_error(msg): 12 | print_stderr(msg) 13 | exit(-1) 14 | 15 | 16 | def validate_arguments(args): 17 | if (args.data_output_path is not None) and (args.caffemodel is None): 18 | fatal_error('No input data path provided.') 19 | if (args.caffemodel is not None) and (args.data_output_path is None): 20 | fatal_error('No output data path provided.') 21 | if (args.code_output_path is None) and (args.data_output_path is None): 22 | fatal_error('No output path specified.') 23 | 24 | 25 | def convert(def_path, caffemodel_path, data_output_path, code_output_path, phase): 26 | try: 27 | transformer = TensorFlowTransformer(def_path, caffemodel_path, phase=phase) 28 | print_stderr('Converting data...') 29 | if caffemodel_path is not None: 30 | data = transformer.transform_data() 31 | print_stderr('Saving data...') 32 | with open(data_output_path, 'wb') as data_out: 33 | np.save(data_out, data) 34 | if code_output_path: 35 | print_stderr('Saving source...') 36 | with open(code_output_path, 'wb') as src_out: 37 | src_out.write(transformer.transform_source()) 38 | print_stderr('Done.') 39 | except KaffeError as err: 40 | fatal_error('Error encountered: {}'.format(err)) 41 | 42 | 43 | def main(): 44 | parser = argparse.ArgumentParser() 45 | parser.add_argument('def_path', help='Model definition (.prototxt) path') 46 | parser.add_argument('--caffemodel', help='Model data (.caffemodel) path') 47 | parser.add_argument('--data-output-path', help='Converted data output path') 48 | parser.add_argument('--code-output-path', help='Save generated source to this path') 49 | parser.add_argument('-p', 50 | '--phase', 51 | default='test', 52 | help='The phase to convert: test (default) or train') 53 | args = parser.parse_args() 54 | validate_arguments(args) 55 | convert(args.def_path, args.caffemodel, args.data_output_path, args.code_output_path, 56 | args.phase) 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /examples/imagenet/README.md: -------------------------------------------------------------------------------- 1 | # ImageNet Examples 2 | 3 | This folder contains two examples that demonstrate how to use converted networks for 4 | image classification. Also included are sample converted models and helper scripts. 5 | 6 | ## 1. Image Classification 7 | 8 | `classify.py` uses a GoogleNet trained on ImageNet, converted to TensorFlow, for classifying images. 9 | 10 | The architecture used is defined in `models/googlenet.py` (which was auto-generated). You will need 11 | to download and convert the weights from Caffe to run the example. The download link for the 12 | corresponding weights can be found in Caffe's `models/bvlc_googlenet/` folder. 13 | 14 | You can run this example like so: 15 | 16 | $ ./classify.py /path/to/googlenet.npy ~/pics/kitty.png ~/pics/woof.jpg 17 | 18 | You should expect to see an output similar to this: 19 | 20 | Image Classified As Confidence 21 | ---------------------------------------------------------------------- 22 | kitty.png Persian cat 99.75 % 23 | woof.jpg Bernese mountain dog 82.02 % 24 | 25 | 26 | ## 2. ImageNet Validation 27 | 28 | `validate.py` evaluates a converted model against the ImageNet (ILSVRC12) validation set. To run 29 | this script, you will need a copy of the ImageNet validation set. You can run it as follows: 30 | 31 | $ ./validate.py alexnet.npy val.txt imagenet-val/ --model AlexNet 32 | 33 | The validation results specified in the main readme were generated using this script. 34 | 35 | ## Helper Scripts 36 | 37 | In addition to the examples above, this folder includes a few additional files: 38 | 39 | - `dataset.py` : helper script for loading, pre-processing, and iterating over images 40 | - `models/` : contains converted models (auto-generated) 41 | - `models/helper.py` : describes how the data should be preprocessed for each model 42 | -------------------------------------------------------------------------------- /examples/imagenet/classify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import numpy as np 4 | import tensorflow as tf 5 | import os.path as osp 6 | 7 | import models 8 | import dataset 9 | 10 | 11 | def display_results(image_paths, probs): 12 | '''Displays the classification results given the class probability for each image''' 13 | # Get a list of ImageNet class labels 14 | with open('imagenet-classes.txt', 'rb') as infile: 15 | class_labels = map(str.strip, infile.readlines()) 16 | # Pick the class with the highest confidence for each image 17 | class_indices = np.argmax(probs, axis=1) 18 | # Display the results 19 | print('\n{:20} {:30} {}'.format('Image', 'Classified As', 'Confidence')) 20 | print('-' * 70) 21 | for img_idx, image_path in enumerate(image_paths): 22 | img_name = osp.basename(image_path) 23 | class_name = class_labels[class_indices[img_idx]] 24 | confidence = round(probs[img_idx, class_indices[img_idx]] * 100, 2) 25 | print('{:20} {:30} {} %'.format(img_name, class_name, confidence)) 26 | 27 | 28 | def classify(model_data_path, image_paths): 29 | '''Classify the given images using GoogleNet.''' 30 | 31 | # Get the data specifications for the GoogleNet model 32 | spec = models.get_data_spec(model_class=models.GoogleNet) 33 | 34 | # Create a placeholder for the input image 35 | input_node = tf.placeholder(tf.float32, 36 | shape=(None, spec.crop_size, spec.crop_size, spec.channels)) 37 | 38 | # Construct the network 39 | net = models.GoogleNet({'data': input_node}) 40 | 41 | # Create an image producer (loads and processes images in parallel) 42 | image_producer = dataset.ImageProducer(image_paths=image_paths, data_spec=spec) 43 | 44 | with tf.Session() as sesh: 45 | # Start the image processing workers 46 | coordinator = tf.train.Coordinator() 47 | threads = image_producer.start(session=sesh, coordinator=coordinator) 48 | 49 | # Load the converted parameters 50 | print('Loading the model') 51 | net.load(model_data_path, sesh) 52 | 53 | # Load the input image 54 | print('Loading the images') 55 | indices, input_images = image_producer.get(sesh) 56 | 57 | # Perform a forward pass through the network to get the class probabilities 58 | print('Classifying') 59 | probs = sesh.run(net.get_output(), feed_dict={input_node: input_images}) 60 | display_results([image_paths[i] for i in indices], probs) 61 | 62 | # Stop the worker threads 63 | coordinator.request_stop() 64 | coordinator.join(threads, stop_grace_period_secs=2) 65 | 66 | def main(): 67 | # Parse arguments 68 | parser = argparse.ArgumentParser() 69 | parser.add_argument('model_path', help='Converted parameters for the GoogleNet model') 70 | parser.add_argument('image_paths', nargs='+', help='One or more images to classify') 71 | args = parser.parse_args() 72 | 73 | # Classify the image 74 | classify(args.model_path, args.image_paths) 75 | 76 | 77 | if __name__ == '__main__': 78 | main() 79 | -------------------------------------------------------------------------------- /examples/imagenet/dataset.py: -------------------------------------------------------------------------------- 1 | '''Utility functions and classes for handling image datasets.''' 2 | 3 | import os.path as osp 4 | import numpy as np 5 | import tensorflow as tf 6 | 7 | 8 | def process_image(img, scale, isotropic, crop, mean): 9 | '''Crops, scales, and normalizes the given image. 10 | scale : The image wil be first scaled to this size. 11 | If isotropic is true, the smaller side is rescaled to this, 12 | preserving the aspect ratio. 13 | crop : After scaling, a central crop of this size is taken. 14 | mean : Subtracted from the image 15 | ''' 16 | # Rescale 17 | if isotropic: 18 | img_shape = tf.to_float(tf.shape(img)[:2]) 19 | min_length = tf.minimum(img_shape[0], img_shape[1]) 20 | new_shape = tf.to_int32((scale / min_length) * img_shape) 21 | else: 22 | new_shape = tf.pack([scale, scale]) 23 | img = tf.image.resize_images(img, new_shape[0], new_shape[1]) 24 | # Center crop 25 | # Use the slice workaround until crop_to_bounding_box supports deferred tensor shapes 26 | # See: https://github.com/tensorflow/tensorflow/issues/521 27 | offset = (new_shape - crop) / 2 28 | img = tf.slice(img, begin=tf.pack([offset[0], offset[1], 0]), size=tf.pack([crop, crop, -1])) 29 | # Mean subtraction 30 | return tf.to_float(img) - mean 31 | 32 | 33 | class ImageProducer(object): 34 | ''' 35 | Loads and processes batches of images in parallel. 36 | ''' 37 | 38 | def __init__(self, image_paths, data_spec, num_concurrent=4, batch_size=None, labels=None): 39 | # The data specifications describe how to process the image 40 | self.data_spec = data_spec 41 | # A list of full image paths 42 | self.image_paths = image_paths 43 | # An optional list of labels corresponding to each image path 44 | self.labels = labels 45 | # A boolean flag per image indicating whether its a JPEG or PNG 46 | self.extension_mask = self.create_extension_mask(self.image_paths) 47 | # Create the loading and processing operations 48 | self.setup(batch_size=batch_size, num_concurrent=num_concurrent) 49 | 50 | def setup(self, batch_size, num_concurrent): 51 | # Validate the batch size 52 | num_images = len(self.image_paths) 53 | batch_size = min(num_images, batch_size or self.data_spec.batch_size) 54 | if num_images % batch_size != 0: 55 | raise ValueError( 56 | 'The total number of images ({}) must be divisible by the batch size ({}).'.format( 57 | num_images, batch_size)) 58 | self.num_batches = num_images / batch_size 59 | 60 | # Create a queue that will contain image paths (and their indices and extension indicator) 61 | self.path_queue = tf.FIFOQueue(capacity=num_images, 62 | dtypes=[tf.int32, tf.bool, tf.string], 63 | name='path_queue') 64 | 65 | # Enqueue all image paths, along with their indices 66 | indices = tf.range(num_images) 67 | self.enqueue_paths_op = self.path_queue.enqueue_many([indices, self.extension_mask, 68 | self.image_paths]) 69 | # Close the path queue (no more additions) 70 | self.close_path_queue_op = self.path_queue.close() 71 | 72 | # Create an operation that dequeues a single path and returns a processed image 73 | (idx, processed_image) = self.process() 74 | 75 | # Create a queue that will contain the processed images (and their indices) 76 | image_shape = (self.data_spec.crop_size, self.data_spec.crop_size, self.data_spec.channels) 77 | processed_queue = tf.FIFOQueue(capacity=int(np.ceil(num_images / float(num_concurrent))), 78 | dtypes=[tf.int32, tf.float32], 79 | shapes=[(), image_shape], 80 | name='processed_queue') 81 | 82 | # Enqueue the processed image and path 83 | enqueue_processed_op = processed_queue.enqueue([idx, processed_image]) 84 | 85 | # Create a dequeue op that fetches a batch of processed images off the queue 86 | self.dequeue_op = processed_queue.dequeue_many(batch_size) 87 | 88 | # Create a queue runner to perform the processing operations in parallel 89 | num_concurrent = min(num_concurrent, num_images) 90 | self.queue_runner = tf.train.QueueRunner(processed_queue, 91 | [enqueue_processed_op] * num_concurrent) 92 | 93 | def start(self, session, coordinator, num_concurrent=4): 94 | '''Start the processing worker threads.''' 95 | # Queue all paths 96 | session.run(self.enqueue_paths_op) 97 | # Close the path queue 98 | session.run(self.close_path_queue_op) 99 | # Start the queue runner and return the created threads 100 | return self.queue_runner.create_threads(session, coord=coordinator, start=True) 101 | 102 | def get(self, session): 103 | ''' 104 | Get a single batch of images along with their indices. If a set of labels were provided, 105 | the corresponding labels are returned instead of the indices. 106 | ''' 107 | (indices, images) = session.run(self.dequeue_op) 108 | if self.labels is not None: 109 | labels = [self.labels[idx] for idx in indices] 110 | return (labels, images) 111 | return (indices, images) 112 | 113 | def batches(self, session): 114 | '''Yield a batch until no more images are left.''' 115 | for _ in xrange(self.num_batches): 116 | yield self.get(session=session) 117 | 118 | def load_image(self, image_path, is_jpeg): 119 | # Read the file 120 | file_data = tf.read_file(image_path) 121 | # Decode the image data 122 | img = tf.cond( 123 | is_jpeg, 124 | lambda: tf.image.decode_jpeg(file_data, channels=self.data_spec.channels), 125 | lambda: tf.image.decode_png(file_data, channels=self.data_spec.channels)) 126 | if self.data_spec.expects_bgr: 127 | # Convert from RGB channel ordering to BGR 128 | # This matches, for instance, how OpenCV orders the channels. 129 | img = tf.reverse(img, [False, False, True]) 130 | return img 131 | 132 | def process(self): 133 | # Dequeue a single image path 134 | idx, is_jpeg, image_path = self.path_queue.dequeue() 135 | # Load the image 136 | img = self.load_image(image_path, is_jpeg) 137 | # Process the image 138 | processed_img = process_image(img=img, 139 | scale=self.data_spec.scale_size, 140 | isotropic=self.data_spec.isotropic, 141 | crop=self.data_spec.crop_size, 142 | mean=self.data_spec.mean) 143 | # Return the processed image, along with its index 144 | return (idx, processed_img) 145 | 146 | @staticmethod 147 | def create_extension_mask(paths): 148 | 149 | def is_jpeg(path): 150 | extension = osp.splitext(path)[-1].lower() 151 | if extension in ('.jpg', '.jpeg'): 152 | return True 153 | if extension != '.png': 154 | raise ValueError('Unsupported image format: {}'.format(extension)) 155 | return False 156 | 157 | return [is_jpeg(p) for p in paths] 158 | 159 | def __len__(self): 160 | return len(self.image_paths) 161 | 162 | 163 | class ImageNetProducer(ImageProducer): 164 | 165 | def __init__(self, val_path, data_path, data_spec): 166 | # Read in the ground truth labels for the validation set 167 | # The get_ilsvrc_aux.sh in Caffe's data/ilsvrc12 folder can fetch a copy of val.txt 168 | gt_lines = open(val_path).readlines() 169 | gt_pairs = [line.split() for line in gt_lines] 170 | # Get the full image paths 171 | # You will need a copy of the ImageNet validation set for this. 172 | image_paths = [osp.join(data_path, p[0]) for p in gt_pairs] 173 | # The corresponding ground truth labels 174 | labels = np.array([int(p[1]) for p in gt_pairs]) 175 | # Initialize base 176 | super(ImageNetProducer, self).__init__(image_paths=image_paths, 177 | data_spec=data_spec, 178 | labels=labels) 179 | -------------------------------------------------------------------------------- /examples/imagenet/imagenet-classes.txt: -------------------------------------------------------------------------------- 1 | tench, Tinca tinca 2 | goldfish, Carassius auratus 3 | great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias 4 | tiger shark, Galeocerdo cuvieri 5 | hammerhead, hammerhead shark 6 | electric ray, crampfish, numbfish, torpedo 7 | stingray 8 | cock 9 | hen 10 | ostrich, Struthio camelus 11 | brambling, Fringilla montifringilla 12 | goldfinch, Carduelis carduelis 13 | house finch, linnet, Carpodacus mexicanus 14 | junco, snowbird 15 | indigo bunting, indigo finch, indigo bird, Passerina cyanea 16 | robin, American robin, Turdus migratorius 17 | bulbul 18 | jay 19 | magpie 20 | chickadee 21 | water ouzel, dipper 22 | kite 23 | bald eagle, American eagle, Haliaeetus leucocephalus 24 | vulture 25 | great grey owl, great gray owl, Strix nebulosa 26 | European fire salamander, Salamandra salamandra 27 | common newt, Triturus vulgaris 28 | eft 29 | spotted salamander, Ambystoma maculatum 30 | axolotl, mud puppy, Ambystoma mexicanum 31 | bullfrog, Rana catesbeiana 32 | tree frog, tree-frog 33 | tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui 34 | loggerhead, loggerhead turtle, Caretta caretta 35 | leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea 36 | mud turtle 37 | terrapin 38 | box turtle, box tortoise 39 | banded gecko 40 | common iguana, iguana, Iguana iguana 41 | American chameleon, anole, Anolis carolinensis 42 | whiptail, whiptail lizard 43 | agama 44 | frilled lizard, Chlamydosaurus kingi 45 | alligator lizard 46 | Gila monster, Heloderma suspectum 47 | green lizard, Lacerta viridis 48 | African chameleon, Chamaeleo chamaeleon 49 | Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis 50 | African crocodile, Nile crocodile, Crocodylus niloticus 51 | American alligator, Alligator mississipiensis 52 | triceratops 53 | thunder snake, worm snake, Carphophis amoenus 54 | ringneck snake, ring-necked snake, ring snake 55 | hognose snake, puff adder, sand viper 56 | green snake, grass snake 57 | king snake, kingsnake 58 | garter snake, grass snake 59 | water snake 60 | vine snake 61 | night snake, Hypsiglena torquata 62 | boa constrictor, Constrictor constrictor 63 | rock python, rock snake, Python sebae 64 | Indian cobra, Naja naja 65 | green mamba 66 | sea snake 67 | horned viper, cerastes, sand viper, horned asp, Cerastes cornutus 68 | diamondback, diamondback rattlesnake, Crotalus adamanteus 69 | sidewinder, horned rattlesnake, Crotalus cerastes 70 | trilobite 71 | harvestman, daddy longlegs, Phalangium opilio 72 | scorpion 73 | black and gold garden spider, Argiope aurantia 74 | barn spider, Araneus cavaticus 75 | garden spider, Aranea diademata 76 | black widow, Latrodectus mactans 77 | tarantula 78 | wolf spider, hunting spider 79 | tick 80 | centipede 81 | black grouse 82 | ptarmigan 83 | ruffed grouse, partridge, Bonasa umbellus 84 | prairie chicken, prairie grouse, prairie fowl 85 | peacock 86 | quail 87 | partridge 88 | African grey, African gray, Psittacus erithacus 89 | macaw 90 | sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita 91 | lorikeet 92 | coucal 93 | bee eater 94 | hornbill 95 | hummingbird 96 | jacamar 97 | toucan 98 | drake 99 | red-breasted merganser, Mergus serrator 100 | goose 101 | black swan, Cygnus atratus 102 | tusker 103 | echidna, spiny anteater, anteater 104 | platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus 105 | wallaby, brush kangaroo 106 | koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus 107 | wombat 108 | jellyfish 109 | sea anemone, anemone 110 | brain coral 111 | flatworm, platyhelminth 112 | nematode, nematode worm, roundworm 113 | conch 114 | snail 115 | slug 116 | sea slug, nudibranch 117 | chiton, coat-of-mail shell, sea cradle, polyplacophore 118 | chambered nautilus, pearly nautilus, nautilus 119 | Dungeness crab, Cancer magister 120 | rock crab, Cancer irroratus 121 | fiddler crab 122 | king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica 123 | American lobster, Northern lobster, Maine lobster, Homarus americanus 124 | spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish 125 | crayfish, crawfish, crawdad, crawdaddy 126 | hermit crab 127 | isopod 128 | white stork, Ciconia ciconia 129 | black stork, Ciconia nigra 130 | spoonbill 131 | flamingo 132 | little blue heron, Egretta caerulea 133 | American egret, great white heron, Egretta albus 134 | bittern 135 | crane 136 | limpkin, Aramus pictus 137 | European gallinule, Porphyrio porphyrio 138 | American coot, marsh hen, mud hen, water hen, Fulica americana 139 | bustard 140 | ruddy turnstone, Arenaria interpres 141 | red-backed sandpiper, dunlin, Erolia alpina 142 | redshank, Tringa totanus 143 | dowitcher 144 | oystercatcher, oyster catcher 145 | pelican 146 | king penguin, Aptenodytes patagonica 147 | albatross, mollymawk 148 | grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus 149 | killer whale, killer, orca, grampus, sea wolf, Orcinus orca 150 | dugong, Dugong dugon 151 | sea lion 152 | Chihuahua 153 | Japanese spaniel 154 | Maltese dog, Maltese terrier, Maltese 155 | Pekinese, Pekingese, Peke 156 | Shih-Tzu 157 | Blenheim spaniel 158 | papillon 159 | toy terrier 160 | Rhodesian ridgeback 161 | Afghan hound, Afghan 162 | basset, basset hound 163 | beagle 164 | bloodhound, sleuthhound 165 | bluetick 166 | black-and-tan coonhound 167 | Walker hound, Walker foxhound 168 | English foxhound 169 | redbone 170 | borzoi, Russian wolfhound 171 | Irish wolfhound 172 | Italian greyhound 173 | whippet 174 | Ibizan hound, Ibizan Podenco 175 | Norwegian elkhound, elkhound 176 | otterhound, otter hound 177 | Saluki, gazelle hound 178 | Scottish deerhound, deerhound 179 | Weimaraner 180 | Staffordshire bullterrier, Staffordshire bull terrier 181 | American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier 182 | Bedlington terrier 183 | Border terrier 184 | Kerry blue terrier 185 | Irish terrier 186 | Norfolk terrier 187 | Norwich terrier 188 | Yorkshire terrier 189 | wire-haired fox terrier 190 | Lakeland terrier 191 | Sealyham terrier, Sealyham 192 | Airedale, Airedale terrier 193 | cairn, cairn terrier 194 | Australian terrier 195 | Dandie Dinmont, Dandie Dinmont terrier 196 | Boston bull, Boston terrier 197 | miniature schnauzer 198 | giant schnauzer 199 | standard schnauzer 200 | Scotch terrier, Scottish terrier, Scottie 201 | Tibetan terrier, chrysanthemum dog 202 | silky terrier, Sydney silky 203 | soft-coated wheaten terrier 204 | West Highland white terrier 205 | Lhasa, Lhasa apso 206 | flat-coated retriever 207 | curly-coated retriever 208 | golden retriever 209 | Labrador retriever 210 | Chesapeake Bay retriever 211 | German short-haired pointer 212 | vizsla, Hungarian pointer 213 | English setter 214 | Irish setter, red setter 215 | Gordon setter 216 | Brittany spaniel 217 | clumber, clumber spaniel 218 | English springer, English springer spaniel 219 | Welsh springer spaniel 220 | cocker spaniel, English cocker spaniel, cocker 221 | Sussex spaniel 222 | Irish water spaniel 223 | kuvasz 224 | schipperke 225 | groenendael 226 | malinois 227 | briard 228 | kelpie 229 | komondor 230 | Old English sheepdog, bobtail 231 | Shetland sheepdog, Shetland sheep dog, Shetland 232 | collie 233 | Border collie 234 | Bouvier des Flandres, Bouviers des Flandres 235 | Rottweiler 236 | German shepherd, German shepherd dog, German police dog, alsatian 237 | Doberman, Doberman pinscher 238 | miniature pinscher 239 | Greater Swiss Mountain dog 240 | Bernese mountain dog 241 | Appenzeller 242 | EntleBucher 243 | boxer 244 | bull mastiff 245 | Tibetan mastiff 246 | French bulldog 247 | Great Dane 248 | Saint Bernard, St Bernard 249 | Eskimo dog, husky 250 | malamute, malemute, Alaskan malamute 251 | Siberian husky 252 | dalmatian, coach dog, carriage dog 253 | affenpinscher, monkey pinscher, monkey dog 254 | basenji 255 | pug, pug-dog 256 | Leonberg 257 | Newfoundland, Newfoundland dog 258 | Great Pyrenees 259 | Samoyed, Samoyede 260 | Pomeranian 261 | chow, chow chow 262 | keeshond 263 | Brabancon griffon 264 | Pembroke, Pembroke Welsh corgi 265 | Cardigan, Cardigan Welsh corgi 266 | toy poodle 267 | miniature poodle 268 | standard poodle 269 | Mexican hairless 270 | timber wolf, grey wolf, gray wolf, Canis lupus 271 | white wolf, Arctic wolf, Canis lupus tundrarum 272 | red wolf, maned wolf, Canis rufus, Canis niger 273 | coyote, prairie wolf, brush wolf, Canis latrans 274 | dingo, warrigal, warragal, Canis dingo 275 | dhole, Cuon alpinus 276 | African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus 277 | hyena, hyaena 278 | red fox, Vulpes vulpes 279 | kit fox, Vulpes macrotis 280 | Arctic fox, white fox, Alopex lagopus 281 | grey fox, gray fox, Urocyon cinereoargenteus 282 | tabby, tabby cat 283 | tiger cat 284 | Persian cat 285 | Siamese cat, Siamese 286 | Egyptian cat 287 | cougar, puma, catamount, mountain lion, painter, panther, Felis concolor 288 | lynx, catamount 289 | leopard, Panthera pardus 290 | snow leopard, ounce, Panthera uncia 291 | jaguar, panther, Panthera onca, Felis onca 292 | lion, king of beasts, Panthera leo 293 | tiger, Panthera tigris 294 | cheetah, chetah, Acinonyx jubatus 295 | brown bear, bruin, Ursus arctos 296 | American black bear, black bear, Ursus americanus, Euarctos americanus 297 | ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus 298 | sloth bear, Melursus ursinus, Ursus ursinus 299 | mongoose 300 | meerkat, mierkat 301 | tiger beetle 302 | ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle 303 | ground beetle, carabid beetle 304 | long-horned beetle, longicorn, longicorn beetle 305 | leaf beetle, chrysomelid 306 | dung beetle 307 | rhinoceros beetle 308 | weevil 309 | fly 310 | bee 311 | ant, emmet, pismire 312 | grasshopper, hopper 313 | cricket 314 | walking stick, walkingstick, stick insect 315 | cockroach, roach 316 | mantis, mantid 317 | cicada, cicala 318 | leafhopper 319 | lacewing, lacewing fly 320 | dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk 321 | damselfly 322 | admiral 323 | ringlet, ringlet butterfly 324 | monarch, monarch butterfly, milkweed butterfly, Danaus plexippus 325 | cabbage butterfly 326 | sulphur butterfly, sulfur butterfly 327 | lycaenid, lycaenid butterfly 328 | starfish, sea star 329 | sea urchin 330 | sea cucumber, holothurian 331 | wood rabbit, cottontail, cottontail rabbit 332 | hare 333 | Angora, Angora rabbit 334 | hamster 335 | porcupine, hedgehog 336 | fox squirrel, eastern fox squirrel, Sciurus niger 337 | marmot 338 | beaver 339 | guinea pig, Cavia cobaya 340 | sorrel 341 | zebra 342 | hog, pig, grunter, squealer, Sus scrofa 343 | wild boar, boar, Sus scrofa 344 | warthog 345 | hippopotamus, hippo, river horse, Hippopotamus amphibius 346 | ox 347 | water buffalo, water ox, Asiatic buffalo, Bubalus bubalis 348 | bison 349 | ram, tup 350 | bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis 351 | ibex, Capra ibex 352 | hartebeest 353 | impala, Aepyceros melampus 354 | gazelle 355 | Arabian camel, dromedary, Camelus dromedarius 356 | llama 357 | weasel 358 | mink 359 | polecat, fitch, foulmart, foumart, Mustela putorius 360 | black-footed ferret, ferret, Mustela nigripes 361 | otter 362 | skunk, polecat, wood pussy 363 | badger 364 | armadillo 365 | three-toed sloth, ai, Bradypus tridactylus 366 | orangutan, orang, orangutang, Pongo pygmaeus 367 | gorilla, Gorilla gorilla 368 | chimpanzee, chimp, Pan troglodytes 369 | gibbon, Hylobates lar 370 | siamang, Hylobates syndactylus, Symphalangus syndactylus 371 | guenon, guenon monkey 372 | patas, hussar monkey, Erythrocebus patas 373 | baboon 374 | macaque 375 | langur 376 | colobus, colobus monkey 377 | proboscis monkey, Nasalis larvatus 378 | marmoset 379 | capuchin, ringtail, Cebus capucinus 380 | howler monkey, howler 381 | titi, titi monkey 382 | spider monkey, Ateles geoffroyi 383 | squirrel monkey, Saimiri sciureus 384 | Madagascar cat, ring-tailed lemur, Lemur catta 385 | indri, indris, Indri indri, Indri brevicaudatus 386 | Indian elephant, Elephas maximus 387 | African elephant, Loxodonta africana 388 | lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens 389 | giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca 390 | barracouta, snoek 391 | eel 392 | coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch 393 | rock beauty, Holocanthus tricolor 394 | anemone fish 395 | sturgeon 396 | gar, garfish, garpike, billfish, Lepisosteus osseus 397 | lionfish 398 | puffer, pufferfish, blowfish, globefish 399 | abacus 400 | abaya 401 | academic gown, academic robe, judge's robe 402 | accordion, piano accordion, squeeze box 403 | acoustic guitar 404 | aircraft carrier, carrier, flattop, attack aircraft carrier 405 | airliner 406 | airship, dirigible 407 | altar 408 | ambulance 409 | amphibian, amphibious vehicle 410 | analog clock 411 | apiary, bee house 412 | apron 413 | ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin 414 | assault rifle, assault gun 415 | backpack, back pack, knapsack, packsack, rucksack, haversack 416 | bakery, bakeshop, bakehouse 417 | balance beam, beam 418 | balloon 419 | ballpoint, ballpoint pen, ballpen, Biro 420 | Band Aid 421 | banjo 422 | bannister, banister, balustrade, balusters, handrail 423 | barbell 424 | barber chair 425 | barbershop 426 | barn 427 | barometer 428 | barrel, cask 429 | barrow, garden cart, lawn cart, wheelbarrow 430 | baseball 431 | basketball 432 | bassinet 433 | bassoon 434 | bathing cap, swimming cap 435 | bath towel 436 | bathtub, bathing tub, bath, tub 437 | beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon 438 | beacon, lighthouse, beacon light, pharos 439 | beaker 440 | bearskin, busby, shako 441 | beer bottle 442 | beer glass 443 | bell cote, bell cot 444 | bib 445 | bicycle-built-for-two, tandem bicycle, tandem 446 | bikini, two-piece 447 | binder, ring-binder 448 | binoculars, field glasses, opera glasses 449 | birdhouse 450 | boathouse 451 | bobsled, bobsleigh, bob 452 | bolo tie, bolo, bola tie, bola 453 | bonnet, poke bonnet 454 | bookcase 455 | bookshop, bookstore, bookstall 456 | bottlecap 457 | bow 458 | bow tie, bow-tie, bowtie 459 | brass, memorial tablet, plaque 460 | brassiere, bra, bandeau 461 | breakwater, groin, groyne, mole, bulwark, seawall, jetty 462 | breastplate, aegis, egis 463 | broom 464 | bucket, pail 465 | buckle 466 | bulletproof vest 467 | bullet train, bullet 468 | butcher shop, meat market 469 | cab, hack, taxi, taxicab 470 | caldron, cauldron 471 | candle, taper, wax light 472 | cannon 473 | canoe 474 | can opener, tin opener 475 | cardigan 476 | car mirror 477 | carousel, carrousel, merry-go-round, roundabout, whirligig 478 | carpenter's kit, tool kit 479 | carton 480 | car wheel 481 | cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM 482 | cassette 483 | cassette player 484 | castle 485 | catamaran 486 | CD player 487 | cello, violoncello 488 | cellular telephone, cellular phone, cellphone, cell, mobile phone 489 | chain 490 | chainlink fence 491 | chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour 492 | chain saw, chainsaw 493 | chest 494 | chiffonier, commode 495 | chime, bell, gong 496 | china cabinet, china closet 497 | Christmas stocking 498 | church, church building 499 | cinema, movie theater, movie theatre, movie house, picture palace 500 | cleaver, meat cleaver, chopper 501 | cliff dwelling 502 | cloak 503 | clog, geta, patten, sabot 504 | cocktail shaker 505 | coffee mug 506 | coffeepot 507 | coil, spiral, volute, whorl, helix 508 | combination lock 509 | computer keyboard, keypad 510 | confectionery, confectionary, candy store 511 | container ship, containership, container vessel 512 | convertible 513 | corkscrew, bottle screw 514 | cornet, horn, trumpet, trump 515 | cowboy boot 516 | cowboy hat, ten-gallon hat 517 | cradle 518 | crane 519 | crash helmet 520 | crate 521 | crib, cot 522 | Crock Pot 523 | croquet ball 524 | crutch 525 | cuirass 526 | dam, dike, dyke 527 | desk 528 | desktop computer 529 | dial telephone, dial phone 530 | diaper, nappy, napkin 531 | digital clock 532 | digital watch 533 | dining table, board 534 | dishrag, dishcloth 535 | dishwasher, dish washer, dishwashing machine 536 | disk brake, disc brake 537 | dock, dockage, docking facility 538 | dogsled, dog sled, dog sleigh 539 | dome 540 | doormat, welcome mat 541 | drilling platform, offshore rig 542 | drum, membranophone, tympan 543 | drumstick 544 | dumbbell 545 | Dutch oven 546 | electric fan, blower 547 | electric guitar 548 | electric locomotive 549 | entertainment center 550 | envelope 551 | espresso maker 552 | face powder 553 | feather boa, boa 554 | file, file cabinet, filing cabinet 555 | fireboat 556 | fire engine, fire truck 557 | fire screen, fireguard 558 | flagpole, flagstaff 559 | flute, transverse flute 560 | folding chair 561 | football helmet 562 | forklift 563 | fountain 564 | fountain pen 565 | four-poster 566 | freight car 567 | French horn, horn 568 | frying pan, frypan, skillet 569 | fur coat 570 | garbage truck, dustcart 571 | gasmask, respirator, gas helmet 572 | gas pump, gasoline pump, petrol pump, island dispenser 573 | goblet 574 | go-kart 575 | golf ball 576 | golfcart, golf cart 577 | gondola 578 | gong, tam-tam 579 | gown 580 | grand piano, grand 581 | greenhouse, nursery, glasshouse 582 | grille, radiator grille 583 | grocery store, grocery, food market, market 584 | guillotine 585 | hair slide 586 | hair spray 587 | half track 588 | hammer 589 | hamper 590 | hand blower, blow dryer, blow drier, hair dryer, hair drier 591 | hand-held computer, hand-held microcomputer 592 | handkerchief, hankie, hanky, hankey 593 | hard disc, hard disk, fixed disk 594 | harmonica, mouth organ, harp, mouth harp 595 | harp 596 | harvester, reaper 597 | hatchet 598 | holster 599 | home theater, home theatre 600 | honeycomb 601 | hook, claw 602 | hoopskirt, crinoline 603 | horizontal bar, high bar 604 | horse cart, horse-cart 605 | hourglass 606 | iPod 607 | iron, smoothing iron 608 | jack-o'-lantern 609 | jean, blue jean, denim 610 | jeep, landrover 611 | jersey, T-shirt, tee shirt 612 | jigsaw puzzle 613 | jinrikisha, ricksha, rickshaw 614 | joystick 615 | kimono 616 | knee pad 617 | knot 618 | lab coat, laboratory coat 619 | ladle 620 | lampshade, lamp shade 621 | laptop, laptop computer 622 | lawn mower, mower 623 | lens cap, lens cover 624 | letter opener, paper knife, paperknife 625 | library 626 | lifeboat 627 | lighter, light, igniter, ignitor 628 | limousine, limo 629 | liner, ocean liner 630 | lipstick, lip rouge 631 | Loafer 632 | lotion 633 | loudspeaker, speaker, speaker unit, loudspeaker system, speaker system 634 | loupe, jeweler's loupe 635 | lumbermill, sawmill 636 | magnetic compass 637 | mailbag, postbag 638 | mailbox, letter box 639 | maillot 640 | maillot, tank suit 641 | manhole cover 642 | maraca 643 | marimba, xylophone 644 | mask 645 | matchstick 646 | maypole 647 | maze, labyrinth 648 | measuring cup 649 | medicine chest, medicine cabinet 650 | megalith, megalithic structure 651 | microphone, mike 652 | microwave, microwave oven 653 | military uniform 654 | milk can 655 | minibus 656 | miniskirt, mini 657 | minivan 658 | missile 659 | mitten 660 | mixing bowl 661 | mobile home, manufactured home 662 | Model T 663 | modem 664 | monastery 665 | monitor 666 | moped 667 | mortar 668 | mortarboard 669 | mosque 670 | mosquito net 671 | motor scooter, scooter 672 | mountain bike, all-terrain bike, off-roader 673 | mountain tent 674 | mouse, computer mouse 675 | mousetrap 676 | moving van 677 | muzzle 678 | nail 679 | neck brace 680 | necklace 681 | nipple 682 | notebook, notebook computer 683 | obelisk 684 | oboe, hautboy, hautbois 685 | ocarina, sweet potato 686 | odometer, hodometer, mileometer, milometer 687 | oil filter 688 | organ, pipe organ 689 | oscilloscope, scope, cathode-ray oscilloscope, CRO 690 | overskirt 691 | oxcart 692 | oxygen mask 693 | packet 694 | paddle, boat paddle 695 | paddlewheel, paddle wheel 696 | padlock 697 | paintbrush 698 | pajama, pyjama, pj's, jammies 699 | palace 700 | panpipe, pandean pipe, syrinx 701 | paper towel 702 | parachute, chute 703 | parallel bars, bars 704 | park bench 705 | parking meter 706 | passenger car, coach, carriage 707 | patio, terrace 708 | pay-phone, pay-station 709 | pedestal, plinth, footstall 710 | pencil box, pencil case 711 | pencil sharpener 712 | perfume, essence 713 | Petri dish 714 | photocopier 715 | pick, plectrum, plectron 716 | pickelhaube 717 | picket fence, paling 718 | pickup, pickup truck 719 | pier 720 | piggy bank, penny bank 721 | pill bottle 722 | pillow 723 | ping-pong ball 724 | pinwheel 725 | pirate, pirate ship 726 | pitcher, ewer 727 | plane, carpenter's plane, woodworking plane 728 | planetarium 729 | plastic bag 730 | plate rack 731 | plow, plough 732 | plunger, plumber's helper 733 | Polaroid camera, Polaroid Land camera 734 | pole 735 | police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria 736 | poncho 737 | pool table, billiard table, snooker table 738 | pop bottle, soda bottle 739 | pot, flowerpot 740 | potter's wheel 741 | power drill 742 | prayer rug, prayer mat 743 | printer 744 | prison, prison house 745 | projectile, missile 746 | projector 747 | puck, hockey puck 748 | punching bag, punch bag, punching ball, punchball 749 | purse 750 | quill, quill pen 751 | quilt, comforter, comfort, puff 752 | racer, race car, racing car 753 | racket, racquet 754 | radiator 755 | radio, wireless 756 | radio telescope, radio reflector 757 | rain barrel 758 | recreational vehicle, RV, R.V. 759 | reel 760 | reflex camera 761 | refrigerator, icebox 762 | remote control, remote 763 | restaurant, eating house, eating place, eatery 764 | revolver, six-gun, six-shooter 765 | rifle 766 | rocking chair, rocker 767 | rotisserie 768 | rubber eraser, rubber, pencil eraser 769 | rugby ball 770 | rule, ruler 771 | running shoe 772 | safe 773 | safety pin 774 | saltshaker, salt shaker 775 | sandal 776 | sarong 777 | sax, saxophone 778 | scabbard 779 | scale, weighing machine 780 | school bus 781 | schooner 782 | scoreboard 783 | screen, CRT screen 784 | screw 785 | screwdriver 786 | seat belt, seatbelt 787 | sewing machine 788 | shield, buckler 789 | shoe shop, shoe-shop, shoe store 790 | shoji 791 | shopping basket 792 | shopping cart 793 | shovel 794 | shower cap 795 | shower curtain 796 | ski 797 | ski mask 798 | sleeping bag 799 | slide rule, slipstick 800 | sliding door 801 | slot, one-armed bandit 802 | snorkel 803 | snowmobile 804 | snowplow, snowplough 805 | soap dispenser 806 | soccer ball 807 | sock 808 | solar dish, solar collector, solar furnace 809 | sombrero 810 | soup bowl 811 | space bar 812 | space heater 813 | space shuttle 814 | spatula 815 | speedboat 816 | spider web, spider's web 817 | spindle 818 | sports car, sport car 819 | spotlight, spot 820 | stage 821 | steam locomotive 822 | steel arch bridge 823 | steel drum 824 | stethoscope 825 | stole 826 | stone wall 827 | stopwatch, stop watch 828 | stove 829 | strainer 830 | streetcar, tram, tramcar, trolley, trolley car 831 | stretcher 832 | studio couch, day bed 833 | stupa, tope 834 | submarine, pigboat, sub, U-boat 835 | suit, suit of clothes 836 | sundial 837 | sunglass 838 | sunglasses, dark glasses, shades 839 | sunscreen, sunblock, sun blocker 840 | suspension bridge 841 | swab, swob, mop 842 | sweatshirt 843 | swimming trunks, bathing trunks 844 | swing 845 | switch, electric switch, electrical switch 846 | syringe 847 | table lamp 848 | tank, army tank, armored combat vehicle, armoured combat vehicle 849 | tape player 850 | teapot 851 | teddy, teddy bear 852 | television, television system 853 | tennis ball 854 | thatch, thatched roof 855 | theater curtain, theatre curtain 856 | thimble 857 | thresher, thrasher, threshing machine 858 | throne 859 | tile roof 860 | toaster 861 | tobacco shop, tobacconist shop, tobacconist 862 | toilet seat 863 | torch 864 | totem pole 865 | tow truck, tow car, wrecker 866 | toyshop 867 | tractor 868 | trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi 869 | tray 870 | trench coat 871 | tricycle, trike, velocipede 872 | trimaran 873 | tripod 874 | triumphal arch 875 | trolleybus, trolley coach, trackless trolley 876 | trombone 877 | tub, vat 878 | turnstile 879 | typewriter keyboard 880 | umbrella 881 | unicycle, monocycle 882 | upright, upright piano 883 | vacuum, vacuum cleaner 884 | vase 885 | vault 886 | velvet 887 | vending machine 888 | vestment 889 | viaduct 890 | violin, fiddle 891 | volleyball 892 | waffle iron 893 | wall clock 894 | wallet, billfold, notecase, pocketbook 895 | wardrobe, closet, press 896 | warplane, military plane 897 | washbasin, handbasin, washbowl, lavabo, wash-hand basin 898 | washer, automatic washer, washing machine 899 | water bottle 900 | water jug 901 | water tower 902 | whiskey jug 903 | whistle 904 | wig 905 | window screen 906 | window shade 907 | Windsor tie 908 | wine bottle 909 | wing 910 | wok 911 | wooden spoon 912 | wool, woolen, woollen 913 | worm fence, snake fence, snake-rail fence, Virginia fence 914 | wreck 915 | yawl 916 | yurt 917 | web site, website, internet site, site 918 | comic book 919 | crossword puzzle, crossword 920 | street sign 921 | traffic light, traffic signal, stoplight 922 | book jacket, dust cover, dust jacket, dust wrapper 923 | menu 924 | plate 925 | guacamole 926 | consomme 927 | hot pot, hotpot 928 | trifle 929 | ice cream, icecream 930 | ice lolly, lolly, lollipop, popsicle 931 | French loaf 932 | bagel, beigel 933 | pretzel 934 | cheeseburger 935 | hotdog, hot dog, red hot 936 | mashed potato 937 | head cabbage 938 | broccoli 939 | cauliflower 940 | zucchini, courgette 941 | spaghetti squash 942 | acorn squash 943 | butternut squash 944 | cucumber, cuke 945 | artichoke, globe artichoke 946 | bell pepper 947 | cardoon 948 | mushroom 949 | Granny Smith 950 | strawberry 951 | orange 952 | lemon 953 | fig 954 | pineapple, ananas 955 | banana 956 | jackfruit, jak, jack 957 | custard apple 958 | pomegranate 959 | hay 960 | carbonara 961 | chocolate sauce, chocolate syrup 962 | dough 963 | meat loaf, meatloaf 964 | pizza, pizza pie 965 | potpie 966 | burrito 967 | red wine 968 | espresso 969 | cup 970 | eggnog 971 | alp 972 | bubble 973 | cliff, drop, drop-off 974 | coral reef 975 | geyser 976 | lakeside, lakeshore 977 | promontory, headland, head, foreland 978 | sandbar, sand bar 979 | seashore, coast, seacoast, sea-coast 980 | valley, vale 981 | volcano 982 | ballplayer, baseball player 983 | groom, bridegroom 984 | scuba diver 985 | rapeseed 986 | daisy 987 | yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum 988 | corn 989 | acorn 990 | hip, rose hip, rosehip 991 | buckeye, horse chestnut, conker 992 | coral fungus 993 | agaric 994 | gyromitra 995 | stinkhorn, carrion fungus 996 | earthstar 997 | hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa 998 | bolete 999 | ear, spike, capitulum 1000 | toilet tissue, toilet paper, bathroom tissue 1001 | -------------------------------------------------------------------------------- /examples/imagenet/models/__init__.py: -------------------------------------------------------------------------------- 1 | from helper import * 2 | -------------------------------------------------------------------------------- /examples/imagenet/models/alexnet.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class AlexNet(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(11, 11, 96, 4, 4, padding='VALID', name='conv1') 7 | .lrn(2, 2e-05, 0.75, name='norm1') 8 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool1') 9 | .conv(5, 5, 256, 1, 1, group=2, name='conv2') 10 | .lrn(2, 2e-05, 0.75, name='norm2') 11 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool2') 12 | .conv(3, 3, 384, 1, 1, name='conv3') 13 | .conv(3, 3, 384, 1, 1, group=2, name='conv4') 14 | .conv(3, 3, 256, 1, 1, group=2, name='conv5') 15 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool5') 16 | .fc(4096, name='fc6') 17 | .fc(4096, name='fc7') 18 | .fc(1000, relu=False, name='fc8') 19 | .softmax(name='prob')) 20 | -------------------------------------------------------------------------------- /examples/imagenet/models/caffenet.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class CaffeNet(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(11, 11, 96, 4, 4, padding='VALID', name='conv1') 7 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool1') 8 | .lrn(2, 2e-05, 0.75, name='norm1') 9 | .conv(5, 5, 256, 1, 1, group=2, name='conv2') 10 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool2') 11 | .lrn(2, 2e-05, 0.75, name='norm2') 12 | .conv(3, 3, 384, 1, 1, name='conv3') 13 | .conv(3, 3, 384, 1, 1, group=2, name='conv4') 14 | .conv(3, 3, 256, 1, 1, group=2, name='conv5') 15 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool5') 16 | .fc(4096, name='fc6') 17 | .fc(4096, name='fc7') 18 | .fc(1000, relu=False, name='fc8') 19 | .softmax(name='prob')) 20 | -------------------------------------------------------------------------------- /examples/imagenet/models/googlenet.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class GoogleNet(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(7, 7, 64, 2, 2, name='conv1_7x7_s2') 7 | .max_pool(3, 3, 2, 2, name='pool1_3x3_s2') 8 | .lrn(2, 2e-05, 0.75, name='pool1_norm1') 9 | .conv(1, 1, 64, 1, 1, name='conv2_3x3_reduce') 10 | .conv(3, 3, 192, 1, 1, name='conv2_3x3') 11 | .lrn(2, 2e-05, 0.75, name='conv2_norm2') 12 | .max_pool(3, 3, 2, 2, name='pool2_3x3_s2') 13 | .conv(1, 1, 64, 1, 1, name='inception_3a_1x1')) 14 | 15 | (self.feed('pool2_3x3_s2') 16 | .conv(1, 1, 96, 1, 1, name='inception_3a_3x3_reduce') 17 | .conv(3, 3, 128, 1, 1, name='inception_3a_3x3')) 18 | 19 | (self.feed('pool2_3x3_s2') 20 | .conv(1, 1, 16, 1, 1, name='inception_3a_5x5_reduce') 21 | .conv(5, 5, 32, 1, 1, name='inception_3a_5x5')) 22 | 23 | (self.feed('pool2_3x3_s2') 24 | .max_pool(3, 3, 1, 1, name='inception_3a_pool') 25 | .conv(1, 1, 32, 1, 1, name='inception_3a_pool_proj')) 26 | 27 | (self.feed('inception_3a_1x1', 28 | 'inception_3a_3x3', 29 | 'inception_3a_5x5', 30 | 'inception_3a_pool_proj') 31 | .concat(3, name='inception_3a_output') 32 | .conv(1, 1, 128, 1, 1, name='inception_3b_1x1')) 33 | 34 | (self.feed('inception_3a_output') 35 | .conv(1, 1, 128, 1, 1, name='inception_3b_3x3_reduce') 36 | .conv(3, 3, 192, 1, 1, name='inception_3b_3x3')) 37 | 38 | (self.feed('inception_3a_output') 39 | .conv(1, 1, 32, 1, 1, name='inception_3b_5x5_reduce') 40 | .conv(5, 5, 96, 1, 1, name='inception_3b_5x5')) 41 | 42 | (self.feed('inception_3a_output') 43 | .max_pool(3, 3, 1, 1, name='inception_3b_pool') 44 | .conv(1, 1, 64, 1, 1, name='inception_3b_pool_proj')) 45 | 46 | (self.feed('inception_3b_1x1', 47 | 'inception_3b_3x3', 48 | 'inception_3b_5x5', 49 | 'inception_3b_pool_proj') 50 | .concat(3, name='inception_3b_output') 51 | .max_pool(3, 3, 2, 2, name='pool3_3x3_s2') 52 | .conv(1, 1, 192, 1, 1, name='inception_4a_1x1')) 53 | 54 | (self.feed('pool3_3x3_s2') 55 | .conv(1, 1, 96, 1, 1, name='inception_4a_3x3_reduce') 56 | .conv(3, 3, 208, 1, 1, name='inception_4a_3x3')) 57 | 58 | (self.feed('pool3_3x3_s2') 59 | .conv(1, 1, 16, 1, 1, name='inception_4a_5x5_reduce') 60 | .conv(5, 5, 48, 1, 1, name='inception_4a_5x5')) 61 | 62 | (self.feed('pool3_3x3_s2') 63 | .max_pool(3, 3, 1, 1, name='inception_4a_pool') 64 | .conv(1, 1, 64, 1, 1, name='inception_4a_pool_proj')) 65 | 66 | (self.feed('inception_4a_1x1', 67 | 'inception_4a_3x3', 68 | 'inception_4a_5x5', 69 | 'inception_4a_pool_proj') 70 | .concat(3, name='inception_4a_output') 71 | .conv(1, 1, 160, 1, 1, name='inception_4b_1x1')) 72 | 73 | (self.feed('inception_4a_output') 74 | .conv(1, 1, 112, 1, 1, name='inception_4b_3x3_reduce') 75 | .conv(3, 3, 224, 1, 1, name='inception_4b_3x3')) 76 | 77 | (self.feed('inception_4a_output') 78 | .conv(1, 1, 24, 1, 1, name='inception_4b_5x5_reduce') 79 | .conv(5, 5, 64, 1, 1, name='inception_4b_5x5')) 80 | 81 | (self.feed('inception_4a_output') 82 | .max_pool(3, 3, 1, 1, name='inception_4b_pool') 83 | .conv(1, 1, 64, 1, 1, name='inception_4b_pool_proj')) 84 | 85 | (self.feed('inception_4b_1x1', 86 | 'inception_4b_3x3', 87 | 'inception_4b_5x5', 88 | 'inception_4b_pool_proj') 89 | .concat(3, name='inception_4b_output') 90 | .conv(1, 1, 128, 1, 1, name='inception_4c_1x1')) 91 | 92 | (self.feed('inception_4b_output') 93 | .conv(1, 1, 128, 1, 1, name='inception_4c_3x3_reduce') 94 | .conv(3, 3, 256, 1, 1, name='inception_4c_3x3')) 95 | 96 | (self.feed('inception_4b_output') 97 | .conv(1, 1, 24, 1, 1, name='inception_4c_5x5_reduce') 98 | .conv(5, 5, 64, 1, 1, name='inception_4c_5x5')) 99 | 100 | (self.feed('inception_4b_output') 101 | .max_pool(3, 3, 1, 1, name='inception_4c_pool') 102 | .conv(1, 1, 64, 1, 1, name='inception_4c_pool_proj')) 103 | 104 | (self.feed('inception_4c_1x1', 105 | 'inception_4c_3x3', 106 | 'inception_4c_5x5', 107 | 'inception_4c_pool_proj') 108 | .concat(3, name='inception_4c_output') 109 | .conv(1, 1, 112, 1, 1, name='inception_4d_1x1')) 110 | 111 | (self.feed('inception_4c_output') 112 | .conv(1, 1, 144, 1, 1, name='inception_4d_3x3_reduce') 113 | .conv(3, 3, 288, 1, 1, name='inception_4d_3x3')) 114 | 115 | (self.feed('inception_4c_output') 116 | .conv(1, 1, 32, 1, 1, name='inception_4d_5x5_reduce') 117 | .conv(5, 5, 64, 1, 1, name='inception_4d_5x5')) 118 | 119 | (self.feed('inception_4c_output') 120 | .max_pool(3, 3, 1, 1, name='inception_4d_pool') 121 | .conv(1, 1, 64, 1, 1, name='inception_4d_pool_proj')) 122 | 123 | (self.feed('inception_4d_1x1', 124 | 'inception_4d_3x3', 125 | 'inception_4d_5x5', 126 | 'inception_4d_pool_proj') 127 | .concat(3, name='inception_4d_output') 128 | .conv(1, 1, 256, 1, 1, name='inception_4e_1x1')) 129 | 130 | (self.feed('inception_4d_output') 131 | .conv(1, 1, 160, 1, 1, name='inception_4e_3x3_reduce') 132 | .conv(3, 3, 320, 1, 1, name='inception_4e_3x3')) 133 | 134 | (self.feed('inception_4d_output') 135 | .conv(1, 1, 32, 1, 1, name='inception_4e_5x5_reduce') 136 | .conv(5, 5, 128, 1, 1, name='inception_4e_5x5')) 137 | 138 | (self.feed('inception_4d_output') 139 | .max_pool(3, 3, 1, 1, name='inception_4e_pool') 140 | .conv(1, 1, 128, 1, 1, name='inception_4e_pool_proj')) 141 | 142 | (self.feed('inception_4e_1x1', 143 | 'inception_4e_3x3', 144 | 'inception_4e_5x5', 145 | 'inception_4e_pool_proj') 146 | .concat(3, name='inception_4e_output') 147 | .max_pool(3, 3, 2, 2, name='pool4_3x3_s2') 148 | .conv(1, 1, 256, 1, 1, name='inception_5a_1x1')) 149 | 150 | (self.feed('pool4_3x3_s2') 151 | .conv(1, 1, 160, 1, 1, name='inception_5a_3x3_reduce') 152 | .conv(3, 3, 320, 1, 1, name='inception_5a_3x3')) 153 | 154 | (self.feed('pool4_3x3_s2') 155 | .conv(1, 1, 32, 1, 1, name='inception_5a_5x5_reduce') 156 | .conv(5, 5, 128, 1, 1, name='inception_5a_5x5')) 157 | 158 | (self.feed('pool4_3x3_s2') 159 | .max_pool(3, 3, 1, 1, name='inception_5a_pool') 160 | .conv(1, 1, 128, 1, 1, name='inception_5a_pool_proj')) 161 | 162 | (self.feed('inception_5a_1x1', 163 | 'inception_5a_3x3', 164 | 'inception_5a_5x5', 165 | 'inception_5a_pool_proj') 166 | .concat(3, name='inception_5a_output') 167 | .conv(1, 1, 384, 1, 1, name='inception_5b_1x1')) 168 | 169 | (self.feed('inception_5a_output') 170 | .conv(1, 1, 192, 1, 1, name='inception_5b_3x3_reduce') 171 | .conv(3, 3, 384, 1, 1, name='inception_5b_3x3')) 172 | 173 | (self.feed('inception_5a_output') 174 | .conv(1, 1, 48, 1, 1, name='inception_5b_5x5_reduce') 175 | .conv(5, 5, 128, 1, 1, name='inception_5b_5x5')) 176 | 177 | (self.feed('inception_5a_output') 178 | .max_pool(3, 3, 1, 1, name='inception_5b_pool') 179 | .conv(1, 1, 128, 1, 1, name='inception_5b_pool_proj')) 180 | 181 | (self.feed('inception_5b_1x1', 182 | 'inception_5b_3x3', 183 | 'inception_5b_5x5', 184 | 'inception_5b_pool_proj') 185 | .concat(3, name='inception_5b_output') 186 | .avg_pool(7, 7, 1, 1, padding='VALID', name='pool5_7x7_s1') 187 | .fc(1000, relu=False, name='loss3_classifier') 188 | .softmax(name='prob')) 189 | -------------------------------------------------------------------------------- /examples/imagenet/models/helper.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path as osp 3 | import numpy as np 4 | 5 | # Add the kaffe module to the import path 6 | sys.path.append(osp.realpath(osp.join(osp.dirname(__file__), '../../../'))) 7 | 8 | from googlenet import GoogleNet 9 | from vgg import VGG16 10 | from alexnet import AlexNet 11 | from caffenet import CaffeNet 12 | from nin import NiN 13 | from resnet import ResNet50, ResNet101, ResNet152 14 | 15 | 16 | class DataSpec(object): 17 | '''Input data specifications for an ImageNet model.''' 18 | 19 | def __init__(self, 20 | batch_size, 21 | scale_size, 22 | crop_size, 23 | isotropic, 24 | channels=3, 25 | mean=None, 26 | bgr=True): 27 | # The recommended batch size for this model 28 | self.batch_size = batch_size 29 | # The image should be scaled to this size first during preprocessing 30 | self.scale_size = scale_size 31 | # Whether the model expects the rescaling to be isotropic 32 | self.isotropic = isotropic 33 | # A square crop of this dimension is expected by this model 34 | self.crop_size = crop_size 35 | # The number of channels in the input image expected by this model 36 | self.channels = channels 37 | # The mean to be subtracted from each image. By default, the per-channel ImageNet mean. 38 | # The values below are ordered BGR, as many Caffe models are trained in this order. 39 | # Some of the earlier models (like AlexNet) used a spatial three-channeled mean. 40 | # However, using just the per-channel mean values instead doesn't affect things too much. 41 | self.mean = mean if mean is not None else np.array([104., 117., 124.]) 42 | # Whether this model expects images to be in BGR order 43 | self.expects_bgr = True 44 | 45 | 46 | def alexnet_spec(batch_size=500): 47 | '''Parameters used by AlexNet and its variants.''' 48 | return DataSpec(batch_size=batch_size, scale_size=256, crop_size=227, isotropic=False) 49 | 50 | 51 | def std_spec(batch_size, isotropic=True): 52 | '''Parameters commonly used by "post-AlexNet" architectures.''' 53 | return DataSpec(batch_size=batch_size, scale_size=256, crop_size=224, isotropic=isotropic) 54 | 55 | # Collection of sample auto-generated models 56 | MODELS = (AlexNet, CaffeNet, GoogleNet, NiN, ResNet50, ResNet101, ResNet152, VGG16) 57 | 58 | # The corresponding data specifications for the sample models 59 | # These specifications are based on how the models were trained. 60 | # The recommended batch size is based on a Titan X (12GB). 61 | MODEL_DATA_SPECS = { 62 | AlexNet: alexnet_spec(), 63 | CaffeNet: alexnet_spec(), 64 | GoogleNet: std_spec(batch_size=200, isotropic=False), 65 | ResNet50: std_spec(batch_size=25), 66 | ResNet101: std_spec(batch_size=25), 67 | ResNet152: std_spec(batch_size=25), 68 | NiN: std_spec(batch_size=500), 69 | VGG16: std_spec(batch_size=25) 70 | } 71 | 72 | 73 | def get_models(): 74 | '''Returns a tuple of sample models.''' 75 | return MODELS 76 | 77 | 78 | def get_data_spec(model_instance=None, model_class=None): 79 | '''Returns the data specifications for the given network.''' 80 | model_class = model_class or model_instance.__class__ 81 | return MODEL_DATA_SPECS[model_class] 82 | -------------------------------------------------------------------------------- /examples/imagenet/models/nin.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class NiN(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(11, 11, 96, 4, 4, padding='VALID', name='conv1') 7 | .conv(1, 1, 96, 1, 1, name='cccp1') 8 | .conv(1, 1, 96, 1, 1, name='cccp2') 9 | .max_pool(3, 3, 2, 2, name='pool1') 10 | .conv(5, 5, 256, 1, 1, name='conv2') 11 | .conv(1, 1, 256, 1, 1, name='cccp3') 12 | .conv(1, 1, 256, 1, 1, name='cccp4') 13 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool2') 14 | .conv(3, 3, 384, 1, 1, name='conv3') 15 | .conv(1, 1, 384, 1, 1, name='cccp5') 16 | .conv(1, 1, 384, 1, 1, name='cccp6') 17 | .max_pool(3, 3, 2, 2, padding='VALID', name='pool3') 18 | .conv(3, 3, 1024, 1, 1, name='conv4-1024') 19 | .conv(1, 1, 1024, 1, 1, name='cccp7-1024') 20 | .conv(1, 1, 1000, 1, 1, name='cccp8-1024') 21 | .avg_pool(6, 6, 1, 1, padding='VALID', name='pool4') 22 | .softmax(name='prob')) 23 | -------------------------------------------------------------------------------- /examples/imagenet/models/resnet.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class ResNet50(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(7, 7, 64, 2, 2, relu=False, name='conv1') 7 | .batch_normalization(relu=True, name='bn_conv1') 8 | .max_pool(3, 3, 2, 2, name='pool1') 9 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch1') 10 | .batch_normalization(name='bn2a_branch1')) 11 | 12 | (self.feed('pool1') 13 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2a_branch2a') 14 | .batch_normalization(relu=True, name='bn2a_branch2a') 15 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2a_branch2b') 16 | .batch_normalization(relu=True, name='bn2a_branch2b') 17 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch2c') 18 | .batch_normalization(name='bn2a_branch2c')) 19 | 20 | (self.feed('bn2a_branch1', 21 | 'bn2a_branch2c') 22 | .add(name='res2a') 23 | .relu(name='res2a_relu') 24 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2b_branch2a') 25 | .batch_normalization(relu=True, name='bn2b_branch2a') 26 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2b_branch2b') 27 | .batch_normalization(relu=True, name='bn2b_branch2b') 28 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2b_branch2c') 29 | .batch_normalization(name='bn2b_branch2c')) 30 | 31 | (self.feed('res2a_relu', 32 | 'bn2b_branch2c') 33 | .add(name='res2b') 34 | .relu(name='res2b_relu') 35 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2c_branch2a') 36 | .batch_normalization(relu=True, name='bn2c_branch2a') 37 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2c_branch2b') 38 | .batch_normalization(relu=True, name='bn2c_branch2b') 39 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2c_branch2c') 40 | .batch_normalization(name='bn2c_branch2c')) 41 | 42 | (self.feed('res2b_relu', 43 | 'bn2c_branch2c') 44 | .add(name='res2c') 45 | .relu(name='res2c_relu') 46 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res3a_branch1') 47 | .batch_normalization(name='bn3a_branch1')) 48 | 49 | (self.feed('res2c_relu') 50 | .conv(1, 1, 128, 2, 2, biased=False, relu=False, name='res3a_branch2a') 51 | .batch_normalization(relu=True, name='bn3a_branch2a') 52 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3a_branch2b') 53 | .batch_normalization(relu=True, name='bn3a_branch2b') 54 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3a_branch2c') 55 | .batch_normalization(name='bn3a_branch2c')) 56 | 57 | (self.feed('bn3a_branch1', 58 | 'bn3a_branch2c') 59 | .add(name='res3a') 60 | .relu(name='res3a_relu') 61 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b_branch2a') 62 | .batch_normalization(relu=True, name='bn3b_branch2a') 63 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b_branch2b') 64 | .batch_normalization(relu=True, name='bn3b_branch2b') 65 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b_branch2c') 66 | .batch_normalization(name='bn3b_branch2c')) 67 | 68 | (self.feed('res3a_relu', 69 | 'bn3b_branch2c') 70 | .add(name='res3b') 71 | .relu(name='res3b_relu') 72 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3c_branch2a') 73 | .batch_normalization(relu=True, name='bn3c_branch2a') 74 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3c_branch2b') 75 | .batch_normalization(relu=True, name='bn3c_branch2b') 76 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3c_branch2c') 77 | .batch_normalization(name='bn3c_branch2c')) 78 | 79 | (self.feed('res3b_relu', 80 | 'bn3c_branch2c') 81 | .add(name='res3c') 82 | .relu(name='res3c_relu') 83 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3d_branch2a') 84 | .batch_normalization(relu=True, name='bn3d_branch2a') 85 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3d_branch2b') 86 | .batch_normalization(relu=True, name='bn3d_branch2b') 87 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3d_branch2c') 88 | .batch_normalization(name='bn3d_branch2c')) 89 | 90 | (self.feed('res3c_relu', 91 | 'bn3d_branch2c') 92 | .add(name='res3d') 93 | .relu(name='res3d_relu') 94 | .conv(1, 1, 1024, 2, 2, biased=False, relu=False, name='res4a_branch1') 95 | .batch_normalization(name='bn4a_branch1')) 96 | 97 | (self.feed('res3d_relu') 98 | .conv(1, 1, 256, 2, 2, biased=False, relu=False, name='res4a_branch2a') 99 | .batch_normalization(relu=True, name='bn4a_branch2a') 100 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4a_branch2b') 101 | .batch_normalization(relu=True, name='bn4a_branch2b') 102 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4a_branch2c') 103 | .batch_normalization(name='bn4a_branch2c')) 104 | 105 | (self.feed('bn4a_branch1', 106 | 'bn4a_branch2c') 107 | .add(name='res4a') 108 | .relu(name='res4a_relu') 109 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b_branch2a') 110 | .batch_normalization(relu=True, name='bn4b_branch2a') 111 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b_branch2b') 112 | .batch_normalization(relu=True, name='bn4b_branch2b') 113 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b_branch2c') 114 | .batch_normalization(name='bn4b_branch2c')) 115 | 116 | (self.feed('res4a_relu', 117 | 'bn4b_branch2c') 118 | .add(name='res4b') 119 | .relu(name='res4b_relu') 120 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4c_branch2a') 121 | .batch_normalization(relu=True, name='bn4c_branch2a') 122 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4c_branch2b') 123 | .batch_normalization(relu=True, name='bn4c_branch2b') 124 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4c_branch2c') 125 | .batch_normalization(name='bn4c_branch2c')) 126 | 127 | (self.feed('res4b_relu', 128 | 'bn4c_branch2c') 129 | .add(name='res4c') 130 | .relu(name='res4c_relu') 131 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4d_branch2a') 132 | .batch_normalization(relu=True, name='bn4d_branch2a') 133 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4d_branch2b') 134 | .batch_normalization(relu=True, name='bn4d_branch2b') 135 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4d_branch2c') 136 | .batch_normalization(name='bn4d_branch2c')) 137 | 138 | (self.feed('res4c_relu', 139 | 'bn4d_branch2c') 140 | .add(name='res4d') 141 | .relu(name='res4d_relu') 142 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4e_branch2a') 143 | .batch_normalization(relu=True, name='bn4e_branch2a') 144 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4e_branch2b') 145 | .batch_normalization(relu=True, name='bn4e_branch2b') 146 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4e_branch2c') 147 | .batch_normalization(name='bn4e_branch2c')) 148 | 149 | (self.feed('res4d_relu', 150 | 'bn4e_branch2c') 151 | .add(name='res4e') 152 | .relu(name='res4e_relu') 153 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4f_branch2a') 154 | .batch_normalization(relu=True, name='bn4f_branch2a') 155 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4f_branch2b') 156 | .batch_normalization(relu=True, name='bn4f_branch2b') 157 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4f_branch2c') 158 | .batch_normalization(name='bn4f_branch2c')) 159 | 160 | (self.feed('res4e_relu', 161 | 'bn4f_branch2c') 162 | .add(name='res4f') 163 | .relu(name='res4f_relu') 164 | .conv(1, 1, 2048, 2, 2, biased=False, relu=False, name='res5a_branch1') 165 | .batch_normalization(name='bn5a_branch1')) 166 | 167 | (self.feed('res4f_relu') 168 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res5a_branch2a') 169 | .batch_normalization(relu=True, name='bn5a_branch2a') 170 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5a_branch2b') 171 | .batch_normalization(relu=True, name='bn5a_branch2b') 172 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5a_branch2c') 173 | .batch_normalization(name='bn5a_branch2c')) 174 | 175 | (self.feed('bn5a_branch1', 176 | 'bn5a_branch2c') 177 | .add(name='res5a') 178 | .relu(name='res5a_relu') 179 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5b_branch2a') 180 | .batch_normalization(relu=True, name='bn5b_branch2a') 181 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5b_branch2b') 182 | .batch_normalization(relu=True, name='bn5b_branch2b') 183 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5b_branch2c') 184 | .batch_normalization(name='bn5b_branch2c')) 185 | 186 | (self.feed('res5a_relu', 187 | 'bn5b_branch2c') 188 | .add(name='res5b') 189 | .relu(name='res5b_relu') 190 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5c_branch2a') 191 | .batch_normalization(relu=True, name='bn5c_branch2a') 192 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5c_branch2b') 193 | .batch_normalization(relu=True, name='bn5c_branch2b') 194 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5c_branch2c') 195 | .batch_normalization(name='bn5c_branch2c')) 196 | 197 | (self.feed('res5b_relu', 198 | 'bn5c_branch2c') 199 | .add(name='res5c') 200 | .relu(name='res5c_relu') 201 | .avg_pool(7, 7, 1, 1, padding='VALID', name='pool5') 202 | .fc(1000, relu=False, name='fc1000') 203 | .softmax(name='prob')) 204 | 205 | 206 | class ResNet101(Network): 207 | def setup(self): 208 | (self.feed('data') 209 | .conv(7, 7, 64, 2, 2, biased=False, relu=False, name='conv1') 210 | .batch_normalization(relu=True, name='bn_conv1') 211 | .max_pool(3, 3, 2, 2, name='pool1') 212 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch1') 213 | .batch_normalization(name='bn2a_branch1')) 214 | 215 | (self.feed('pool1') 216 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2a_branch2a') 217 | .batch_normalization(relu=True, name='bn2a_branch2a') 218 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2a_branch2b') 219 | .batch_normalization(relu=True, name='bn2a_branch2b') 220 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch2c') 221 | .batch_normalization(name='bn2a_branch2c')) 222 | 223 | (self.feed('bn2a_branch1', 224 | 'bn2a_branch2c') 225 | .add(name='res2a') 226 | .relu(name='res2a_relu') 227 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2b_branch2a') 228 | .batch_normalization(relu=True, name='bn2b_branch2a') 229 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2b_branch2b') 230 | .batch_normalization(relu=True, name='bn2b_branch2b') 231 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2b_branch2c') 232 | .batch_normalization(name='bn2b_branch2c')) 233 | 234 | (self.feed('res2a_relu', 235 | 'bn2b_branch2c') 236 | .add(name='res2b') 237 | .relu(name='res2b_relu') 238 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2c_branch2a') 239 | .batch_normalization(relu=True, name='bn2c_branch2a') 240 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2c_branch2b') 241 | .batch_normalization(relu=True, name='bn2c_branch2b') 242 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2c_branch2c') 243 | .batch_normalization(name='bn2c_branch2c')) 244 | 245 | (self.feed('res2b_relu', 246 | 'bn2c_branch2c') 247 | .add(name='res2c') 248 | .relu(name='res2c_relu') 249 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res3a_branch1') 250 | .batch_normalization(name='bn3a_branch1')) 251 | 252 | (self.feed('res2c_relu') 253 | .conv(1, 1, 128, 2, 2, biased=False, relu=False, name='res3a_branch2a') 254 | .batch_normalization(relu=True, name='bn3a_branch2a') 255 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3a_branch2b') 256 | .batch_normalization(relu=True, name='bn3a_branch2b') 257 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3a_branch2c') 258 | .batch_normalization(name='bn3a_branch2c')) 259 | 260 | (self.feed('bn3a_branch1', 261 | 'bn3a_branch2c') 262 | .add(name='res3a') 263 | .relu(name='res3a_relu') 264 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b1_branch2a') 265 | .batch_normalization(relu=True, name='bn3b1_branch2a') 266 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b1_branch2b') 267 | .batch_normalization(relu=True, name='bn3b1_branch2b') 268 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b1_branch2c') 269 | .batch_normalization(name='bn3b1_branch2c')) 270 | 271 | (self.feed('res3a_relu', 272 | 'bn3b1_branch2c') 273 | .add(name='res3b1') 274 | .relu(name='res3b1_relu') 275 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b2_branch2a') 276 | .batch_normalization(relu=True, name='bn3b2_branch2a') 277 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b2_branch2b') 278 | .batch_normalization(relu=True, name='bn3b2_branch2b') 279 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b2_branch2c') 280 | .batch_normalization(name='bn3b2_branch2c')) 281 | 282 | (self.feed('res3b1_relu', 283 | 'bn3b2_branch2c') 284 | .add(name='res3b2') 285 | .relu(name='res3b2_relu') 286 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b3_branch2a') 287 | .batch_normalization(relu=True, name='bn3b3_branch2a') 288 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b3_branch2b') 289 | .batch_normalization(relu=True, name='bn3b3_branch2b') 290 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b3_branch2c') 291 | .batch_normalization(name='bn3b3_branch2c')) 292 | 293 | (self.feed('res3b2_relu', 294 | 'bn3b3_branch2c') 295 | .add(name='res3b3') 296 | .relu(name='res3b3_relu') 297 | .conv(1, 1, 1024, 2, 2, biased=False, relu=False, name='res4a_branch1') 298 | .batch_normalization(name='bn4a_branch1')) 299 | 300 | (self.feed('res3b3_relu') 301 | .conv(1, 1, 256, 2, 2, biased=False, relu=False, name='res4a_branch2a') 302 | .batch_normalization(relu=True, name='bn4a_branch2a') 303 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4a_branch2b') 304 | .batch_normalization(relu=True, name='bn4a_branch2b') 305 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4a_branch2c') 306 | .batch_normalization(name='bn4a_branch2c')) 307 | 308 | (self.feed('bn4a_branch1', 309 | 'bn4a_branch2c') 310 | .add(name='res4a') 311 | .relu(name='res4a_relu') 312 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b1_branch2a') 313 | .batch_normalization(relu=True, name='bn4b1_branch2a') 314 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b1_branch2b') 315 | .batch_normalization(relu=True, name='bn4b1_branch2b') 316 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b1_branch2c') 317 | .batch_normalization(name='bn4b1_branch2c')) 318 | 319 | (self.feed('res4a_relu', 320 | 'bn4b1_branch2c') 321 | .add(name='res4b1') 322 | .relu(name='res4b1_relu') 323 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b2_branch2a') 324 | .batch_normalization(relu=True, name='bn4b2_branch2a') 325 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b2_branch2b') 326 | .batch_normalization(relu=True, name='bn4b2_branch2b') 327 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b2_branch2c') 328 | .batch_normalization(name='bn4b2_branch2c')) 329 | 330 | (self.feed('res4b1_relu', 331 | 'bn4b2_branch2c') 332 | .add(name='res4b2') 333 | .relu(name='res4b2_relu') 334 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b3_branch2a') 335 | .batch_normalization(relu=True, name='bn4b3_branch2a') 336 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b3_branch2b') 337 | .batch_normalization(relu=True, name='bn4b3_branch2b') 338 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b3_branch2c') 339 | .batch_normalization(name='bn4b3_branch2c')) 340 | 341 | (self.feed('res4b2_relu', 342 | 'bn4b3_branch2c') 343 | .add(name='res4b3') 344 | .relu(name='res4b3_relu') 345 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b4_branch2a') 346 | .batch_normalization(relu=True, name='bn4b4_branch2a') 347 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b4_branch2b') 348 | .batch_normalization(relu=True, name='bn4b4_branch2b') 349 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b4_branch2c') 350 | .batch_normalization(name='bn4b4_branch2c')) 351 | 352 | (self.feed('res4b3_relu', 353 | 'bn4b4_branch2c') 354 | .add(name='res4b4') 355 | .relu(name='res4b4_relu') 356 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b5_branch2a') 357 | .batch_normalization(relu=True, name='bn4b5_branch2a') 358 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b5_branch2b') 359 | .batch_normalization(relu=True, name='bn4b5_branch2b') 360 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b5_branch2c') 361 | .batch_normalization(name='bn4b5_branch2c')) 362 | 363 | (self.feed('res4b4_relu', 364 | 'bn4b5_branch2c') 365 | .add(name='res4b5') 366 | .relu(name='res4b5_relu') 367 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b6_branch2a') 368 | .batch_normalization(relu=True, name='bn4b6_branch2a') 369 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b6_branch2b') 370 | .batch_normalization(relu=True, name='bn4b6_branch2b') 371 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b6_branch2c') 372 | .batch_normalization(name='bn4b6_branch2c')) 373 | 374 | (self.feed('res4b5_relu', 375 | 'bn4b6_branch2c') 376 | .add(name='res4b6') 377 | .relu(name='res4b6_relu') 378 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b7_branch2a') 379 | .batch_normalization(relu=True, name='bn4b7_branch2a') 380 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b7_branch2b') 381 | .batch_normalization(relu=True, name='bn4b7_branch2b') 382 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b7_branch2c') 383 | .batch_normalization(name='bn4b7_branch2c')) 384 | 385 | (self.feed('res4b6_relu', 386 | 'bn4b7_branch2c') 387 | .add(name='res4b7') 388 | .relu(name='res4b7_relu') 389 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b8_branch2a') 390 | .batch_normalization(relu=True, name='bn4b8_branch2a') 391 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b8_branch2b') 392 | .batch_normalization(relu=True, name='bn4b8_branch2b') 393 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b8_branch2c') 394 | .batch_normalization(name='bn4b8_branch2c')) 395 | 396 | (self.feed('res4b7_relu', 397 | 'bn4b8_branch2c') 398 | .add(name='res4b8') 399 | .relu(name='res4b8_relu') 400 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b9_branch2a') 401 | .batch_normalization(relu=True, name='bn4b9_branch2a') 402 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b9_branch2b') 403 | .batch_normalization(relu=True, name='bn4b9_branch2b') 404 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b9_branch2c') 405 | .batch_normalization(name='bn4b9_branch2c')) 406 | 407 | (self.feed('res4b8_relu', 408 | 'bn4b9_branch2c') 409 | .add(name='res4b9') 410 | .relu(name='res4b9_relu') 411 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b10_branch2a') 412 | .batch_normalization(relu=True, name='bn4b10_branch2a') 413 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b10_branch2b') 414 | .batch_normalization(relu=True, name='bn4b10_branch2b') 415 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b10_branch2c') 416 | .batch_normalization(name='bn4b10_branch2c')) 417 | 418 | (self.feed('res4b9_relu', 419 | 'bn4b10_branch2c') 420 | .add(name='res4b10') 421 | .relu(name='res4b10_relu') 422 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b11_branch2a') 423 | .batch_normalization(relu=True, name='bn4b11_branch2a') 424 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b11_branch2b') 425 | .batch_normalization(relu=True, name='bn4b11_branch2b') 426 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b11_branch2c') 427 | .batch_normalization(name='bn4b11_branch2c')) 428 | 429 | (self.feed('res4b10_relu', 430 | 'bn4b11_branch2c') 431 | .add(name='res4b11') 432 | .relu(name='res4b11_relu') 433 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b12_branch2a') 434 | .batch_normalization(relu=True, name='bn4b12_branch2a') 435 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b12_branch2b') 436 | .batch_normalization(relu=True, name='bn4b12_branch2b') 437 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b12_branch2c') 438 | .batch_normalization(name='bn4b12_branch2c')) 439 | 440 | (self.feed('res4b11_relu', 441 | 'bn4b12_branch2c') 442 | .add(name='res4b12') 443 | .relu(name='res4b12_relu') 444 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b13_branch2a') 445 | .batch_normalization(relu=True, name='bn4b13_branch2a') 446 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b13_branch2b') 447 | .batch_normalization(relu=True, name='bn4b13_branch2b') 448 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b13_branch2c') 449 | .batch_normalization(name='bn4b13_branch2c')) 450 | 451 | (self.feed('res4b12_relu', 452 | 'bn4b13_branch2c') 453 | .add(name='res4b13') 454 | .relu(name='res4b13_relu') 455 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b14_branch2a') 456 | .batch_normalization(relu=True, name='bn4b14_branch2a') 457 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b14_branch2b') 458 | .batch_normalization(relu=True, name='bn4b14_branch2b') 459 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b14_branch2c') 460 | .batch_normalization(name='bn4b14_branch2c')) 461 | 462 | (self.feed('res4b13_relu', 463 | 'bn4b14_branch2c') 464 | .add(name='res4b14') 465 | .relu(name='res4b14_relu') 466 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b15_branch2a') 467 | .batch_normalization(relu=True, name='bn4b15_branch2a') 468 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b15_branch2b') 469 | .batch_normalization(relu=True, name='bn4b15_branch2b') 470 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b15_branch2c') 471 | .batch_normalization(name='bn4b15_branch2c')) 472 | 473 | (self.feed('res4b14_relu', 474 | 'bn4b15_branch2c') 475 | .add(name='res4b15') 476 | .relu(name='res4b15_relu') 477 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b16_branch2a') 478 | .batch_normalization(relu=True, name='bn4b16_branch2a') 479 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b16_branch2b') 480 | .batch_normalization(relu=True, name='bn4b16_branch2b') 481 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b16_branch2c') 482 | .batch_normalization(name='bn4b16_branch2c')) 483 | 484 | (self.feed('res4b15_relu', 485 | 'bn4b16_branch2c') 486 | .add(name='res4b16') 487 | .relu(name='res4b16_relu') 488 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b17_branch2a') 489 | .batch_normalization(relu=True, name='bn4b17_branch2a') 490 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b17_branch2b') 491 | .batch_normalization(relu=True, name='bn4b17_branch2b') 492 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b17_branch2c') 493 | .batch_normalization(name='bn4b17_branch2c')) 494 | 495 | (self.feed('res4b16_relu', 496 | 'bn4b17_branch2c') 497 | .add(name='res4b17') 498 | .relu(name='res4b17_relu') 499 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b18_branch2a') 500 | .batch_normalization(relu=True, name='bn4b18_branch2a') 501 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b18_branch2b') 502 | .batch_normalization(relu=True, name='bn4b18_branch2b') 503 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b18_branch2c') 504 | .batch_normalization(name='bn4b18_branch2c')) 505 | 506 | (self.feed('res4b17_relu', 507 | 'bn4b18_branch2c') 508 | .add(name='res4b18') 509 | .relu(name='res4b18_relu') 510 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b19_branch2a') 511 | .batch_normalization(relu=True, name='bn4b19_branch2a') 512 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b19_branch2b') 513 | .batch_normalization(relu=True, name='bn4b19_branch2b') 514 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b19_branch2c') 515 | .batch_normalization(name='bn4b19_branch2c')) 516 | 517 | (self.feed('res4b18_relu', 518 | 'bn4b19_branch2c') 519 | .add(name='res4b19') 520 | .relu(name='res4b19_relu') 521 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b20_branch2a') 522 | .batch_normalization(relu=True, name='bn4b20_branch2a') 523 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b20_branch2b') 524 | .batch_normalization(relu=True, name='bn4b20_branch2b') 525 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b20_branch2c') 526 | .batch_normalization(name='bn4b20_branch2c')) 527 | 528 | (self.feed('res4b19_relu', 529 | 'bn4b20_branch2c') 530 | .add(name='res4b20') 531 | .relu(name='res4b20_relu') 532 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b21_branch2a') 533 | .batch_normalization(relu=True, name='bn4b21_branch2a') 534 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b21_branch2b') 535 | .batch_normalization(relu=True, name='bn4b21_branch2b') 536 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b21_branch2c') 537 | .batch_normalization(name='bn4b21_branch2c')) 538 | 539 | (self.feed('res4b20_relu', 540 | 'bn4b21_branch2c') 541 | .add(name='res4b21') 542 | .relu(name='res4b21_relu') 543 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b22_branch2a') 544 | .batch_normalization(relu=True, name='bn4b22_branch2a') 545 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b22_branch2b') 546 | .batch_normalization(relu=True, name='bn4b22_branch2b') 547 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b22_branch2c') 548 | .batch_normalization(name='bn4b22_branch2c')) 549 | 550 | (self.feed('res4b21_relu', 551 | 'bn4b22_branch2c') 552 | .add(name='res4b22') 553 | .relu(name='res4b22_relu') 554 | .conv(1, 1, 2048, 2, 2, biased=False, relu=False, name='res5a_branch1') 555 | .batch_normalization(name='bn5a_branch1')) 556 | 557 | (self.feed('res4b22_relu') 558 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res5a_branch2a') 559 | .batch_normalization(relu=True, name='bn5a_branch2a') 560 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5a_branch2b') 561 | .batch_normalization(relu=True, name='bn5a_branch2b') 562 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5a_branch2c') 563 | .batch_normalization(name='bn5a_branch2c')) 564 | 565 | (self.feed('bn5a_branch1', 566 | 'bn5a_branch2c') 567 | .add(name='res5a') 568 | .relu(name='res5a_relu') 569 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5b_branch2a') 570 | .batch_normalization(relu=True, name='bn5b_branch2a') 571 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5b_branch2b') 572 | .batch_normalization(relu=True, name='bn5b_branch2b') 573 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5b_branch2c') 574 | .batch_normalization(name='bn5b_branch2c')) 575 | 576 | (self.feed('res5a_relu', 577 | 'bn5b_branch2c') 578 | .add(name='res5b') 579 | .relu(name='res5b_relu') 580 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5c_branch2a') 581 | .batch_normalization(relu=True, name='bn5c_branch2a') 582 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5c_branch2b') 583 | .batch_normalization(relu=True, name='bn5c_branch2b') 584 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5c_branch2c') 585 | .batch_normalization(name='bn5c_branch2c')) 586 | 587 | (self.feed('res5b_relu', 588 | 'bn5c_branch2c') 589 | .add(name='res5c') 590 | .relu(name='res5c_relu') 591 | .avg_pool(7, 7, 1, 1, padding='VALID', name='pool5') 592 | .fc(1000, relu=False, name='fc1000') 593 | .softmax(name='prob')) 594 | 595 | 596 | class ResNet152(Network): 597 | def setup(self): 598 | (self.feed('data') 599 | .conv(7, 7, 64, 2, 2, biased=False, relu=False, name='conv1') 600 | .batch_normalization(relu=True, name='bn_conv1') 601 | .max_pool(3, 3, 2, 2, name='pool1') 602 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch1') 603 | .batch_normalization(name='bn2a_branch1')) 604 | 605 | (self.feed('pool1') 606 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2a_branch2a') 607 | .batch_normalization(relu=True, name='bn2a_branch2a') 608 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2a_branch2b') 609 | .batch_normalization(relu=True, name='bn2a_branch2b') 610 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2a_branch2c') 611 | .batch_normalization(name='bn2a_branch2c')) 612 | 613 | (self.feed('bn2a_branch1', 614 | 'bn2a_branch2c') 615 | .add(name='res2a') 616 | .relu(name='res2a_relu') 617 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2b_branch2a') 618 | .batch_normalization(relu=True, name='bn2b_branch2a') 619 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2b_branch2b') 620 | .batch_normalization(relu=True, name='bn2b_branch2b') 621 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2b_branch2c') 622 | .batch_normalization(name='bn2b_branch2c')) 623 | 624 | (self.feed('res2a_relu', 625 | 'bn2b_branch2c') 626 | .add(name='res2b') 627 | .relu(name='res2b_relu') 628 | .conv(1, 1, 64, 1, 1, biased=False, relu=False, name='res2c_branch2a') 629 | .batch_normalization(relu=True, name='bn2c_branch2a') 630 | .conv(3, 3, 64, 1, 1, biased=False, relu=False, name='res2c_branch2b') 631 | .batch_normalization(relu=True, name='bn2c_branch2b') 632 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res2c_branch2c') 633 | .batch_normalization(name='bn2c_branch2c')) 634 | 635 | (self.feed('res2b_relu', 636 | 'bn2c_branch2c') 637 | .add(name='res2c') 638 | .relu(name='res2c_relu') 639 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res3a_branch1') 640 | .batch_normalization(name='bn3a_branch1')) 641 | 642 | (self.feed('res2c_relu') 643 | .conv(1, 1, 128, 2, 2, biased=False, relu=False, name='res3a_branch2a') 644 | .batch_normalization(relu=True, name='bn3a_branch2a') 645 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3a_branch2b') 646 | .batch_normalization(relu=True, name='bn3a_branch2b') 647 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3a_branch2c') 648 | .batch_normalization(name='bn3a_branch2c')) 649 | 650 | (self.feed('bn3a_branch1', 651 | 'bn3a_branch2c') 652 | .add(name='res3a') 653 | .relu(name='res3a_relu') 654 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b1_branch2a') 655 | .batch_normalization(relu=True, name='bn3b1_branch2a') 656 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b1_branch2b') 657 | .batch_normalization(relu=True, name='bn3b1_branch2b') 658 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b1_branch2c') 659 | .batch_normalization(name='bn3b1_branch2c')) 660 | 661 | (self.feed('res3a_relu', 662 | 'bn3b1_branch2c') 663 | .add(name='res3b1') 664 | .relu(name='res3b1_relu') 665 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b2_branch2a') 666 | .batch_normalization(relu=True, name='bn3b2_branch2a') 667 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b2_branch2b') 668 | .batch_normalization(relu=True, name='bn3b2_branch2b') 669 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b2_branch2c') 670 | .batch_normalization(name='bn3b2_branch2c')) 671 | 672 | (self.feed('res3b1_relu', 673 | 'bn3b2_branch2c') 674 | .add(name='res3b2') 675 | .relu(name='res3b2_relu') 676 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b3_branch2a') 677 | .batch_normalization(relu=True, name='bn3b3_branch2a') 678 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b3_branch2b') 679 | .batch_normalization(relu=True, name='bn3b3_branch2b') 680 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b3_branch2c') 681 | .batch_normalization(name='bn3b3_branch2c')) 682 | 683 | (self.feed('res3b2_relu', 684 | 'bn3b3_branch2c') 685 | .add(name='res3b3') 686 | .relu(name='res3b3_relu') 687 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b4_branch2a') 688 | .batch_normalization(relu=True, name='bn3b4_branch2a') 689 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b4_branch2b') 690 | .batch_normalization(relu=True, name='bn3b4_branch2b') 691 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b4_branch2c') 692 | .batch_normalization(name='bn3b4_branch2c')) 693 | 694 | (self.feed('res3b3_relu', 695 | 'bn3b4_branch2c') 696 | .add(name='res3b4') 697 | .relu(name='res3b4_relu') 698 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b5_branch2a') 699 | .batch_normalization(relu=True, name='bn3b5_branch2a') 700 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b5_branch2b') 701 | .batch_normalization(relu=True, name='bn3b5_branch2b') 702 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b5_branch2c') 703 | .batch_normalization(name='bn3b5_branch2c')) 704 | 705 | (self.feed('res3b4_relu', 706 | 'bn3b5_branch2c') 707 | .add(name='res3b5') 708 | .relu(name='res3b5_relu') 709 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b6_branch2a') 710 | .batch_normalization(relu=True, name='bn3b6_branch2a') 711 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b6_branch2b') 712 | .batch_normalization(relu=True, name='bn3b6_branch2b') 713 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b6_branch2c') 714 | .batch_normalization(name='bn3b6_branch2c')) 715 | 716 | (self.feed('res3b5_relu', 717 | 'bn3b6_branch2c') 718 | .add(name='res3b6') 719 | .relu(name='res3b6_relu') 720 | .conv(1, 1, 128, 1, 1, biased=False, relu=False, name='res3b7_branch2a') 721 | .batch_normalization(relu=True, name='bn3b7_branch2a') 722 | .conv(3, 3, 128, 1, 1, biased=False, relu=False, name='res3b7_branch2b') 723 | .batch_normalization(relu=True, name='bn3b7_branch2b') 724 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res3b7_branch2c') 725 | .batch_normalization(name='bn3b7_branch2c')) 726 | 727 | (self.feed('res3b6_relu', 728 | 'bn3b7_branch2c') 729 | .add(name='res3b7') 730 | .relu(name='res3b7_relu') 731 | .conv(1, 1, 1024, 2, 2, biased=False, relu=False, name='res4a_branch1') 732 | .batch_normalization(name='bn4a_branch1')) 733 | 734 | (self.feed('res3b7_relu') 735 | .conv(1, 1, 256, 2, 2, biased=False, relu=False, name='res4a_branch2a') 736 | .batch_normalization(relu=True, name='bn4a_branch2a') 737 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4a_branch2b') 738 | .batch_normalization(relu=True, name='bn4a_branch2b') 739 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4a_branch2c') 740 | .batch_normalization(name='bn4a_branch2c')) 741 | 742 | (self.feed('bn4a_branch1', 743 | 'bn4a_branch2c') 744 | .add(name='res4a') 745 | .relu(name='res4a_relu') 746 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b1_branch2a') 747 | .batch_normalization(relu=True, name='bn4b1_branch2a') 748 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b1_branch2b') 749 | .batch_normalization(relu=True, name='bn4b1_branch2b') 750 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b1_branch2c') 751 | .batch_normalization(name='bn4b1_branch2c')) 752 | 753 | (self.feed('res4a_relu', 754 | 'bn4b1_branch2c') 755 | .add(name='res4b1') 756 | .relu(name='res4b1_relu') 757 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b2_branch2a') 758 | .batch_normalization(relu=True, name='bn4b2_branch2a') 759 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b2_branch2b') 760 | .batch_normalization(relu=True, name='bn4b2_branch2b') 761 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b2_branch2c') 762 | .batch_normalization(name='bn4b2_branch2c')) 763 | 764 | (self.feed('res4b1_relu', 765 | 'bn4b2_branch2c') 766 | .add(name='res4b2') 767 | .relu(name='res4b2_relu') 768 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b3_branch2a') 769 | .batch_normalization(relu=True, name='bn4b3_branch2a') 770 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b3_branch2b') 771 | .batch_normalization(relu=True, name='bn4b3_branch2b') 772 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b3_branch2c') 773 | .batch_normalization(name='bn4b3_branch2c')) 774 | 775 | (self.feed('res4b2_relu', 776 | 'bn4b3_branch2c') 777 | .add(name='res4b3') 778 | .relu(name='res4b3_relu') 779 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b4_branch2a') 780 | .batch_normalization(relu=True, name='bn4b4_branch2a') 781 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b4_branch2b') 782 | .batch_normalization(relu=True, name='bn4b4_branch2b') 783 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b4_branch2c') 784 | .batch_normalization(name='bn4b4_branch2c')) 785 | 786 | (self.feed('res4b3_relu', 787 | 'bn4b4_branch2c') 788 | .add(name='res4b4') 789 | .relu(name='res4b4_relu') 790 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b5_branch2a') 791 | .batch_normalization(relu=True, name='bn4b5_branch2a') 792 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b5_branch2b') 793 | .batch_normalization(relu=True, name='bn4b5_branch2b') 794 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b5_branch2c') 795 | .batch_normalization(name='bn4b5_branch2c')) 796 | 797 | (self.feed('res4b4_relu', 798 | 'bn4b5_branch2c') 799 | .add(name='res4b5') 800 | .relu(name='res4b5_relu') 801 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b6_branch2a') 802 | .batch_normalization(relu=True, name='bn4b6_branch2a') 803 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b6_branch2b') 804 | .batch_normalization(relu=True, name='bn4b6_branch2b') 805 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b6_branch2c') 806 | .batch_normalization(name='bn4b6_branch2c')) 807 | 808 | (self.feed('res4b5_relu', 809 | 'bn4b6_branch2c') 810 | .add(name='res4b6') 811 | .relu(name='res4b6_relu') 812 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b7_branch2a') 813 | .batch_normalization(relu=True, name='bn4b7_branch2a') 814 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b7_branch2b') 815 | .batch_normalization(relu=True, name='bn4b7_branch2b') 816 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b7_branch2c') 817 | .batch_normalization(name='bn4b7_branch2c')) 818 | 819 | (self.feed('res4b6_relu', 820 | 'bn4b7_branch2c') 821 | .add(name='res4b7') 822 | .relu(name='res4b7_relu') 823 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b8_branch2a') 824 | .batch_normalization(relu=True, name='bn4b8_branch2a') 825 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b8_branch2b') 826 | .batch_normalization(relu=True, name='bn4b8_branch2b') 827 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b8_branch2c') 828 | .batch_normalization(name='bn4b8_branch2c')) 829 | 830 | (self.feed('res4b7_relu', 831 | 'bn4b8_branch2c') 832 | .add(name='res4b8') 833 | .relu(name='res4b8_relu') 834 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b9_branch2a') 835 | .batch_normalization(relu=True, name='bn4b9_branch2a') 836 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b9_branch2b') 837 | .batch_normalization(relu=True, name='bn4b9_branch2b') 838 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b9_branch2c') 839 | .batch_normalization(name='bn4b9_branch2c')) 840 | 841 | (self.feed('res4b8_relu', 842 | 'bn4b9_branch2c') 843 | .add(name='res4b9') 844 | .relu(name='res4b9_relu') 845 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b10_branch2a') 846 | .batch_normalization(relu=True, name='bn4b10_branch2a') 847 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b10_branch2b') 848 | .batch_normalization(relu=True, name='bn4b10_branch2b') 849 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b10_branch2c') 850 | .batch_normalization(name='bn4b10_branch2c')) 851 | 852 | (self.feed('res4b9_relu', 853 | 'bn4b10_branch2c') 854 | .add(name='res4b10') 855 | .relu(name='res4b10_relu') 856 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b11_branch2a') 857 | .batch_normalization(relu=True, name='bn4b11_branch2a') 858 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b11_branch2b') 859 | .batch_normalization(relu=True, name='bn4b11_branch2b') 860 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b11_branch2c') 861 | .batch_normalization(name='bn4b11_branch2c')) 862 | 863 | (self.feed('res4b10_relu', 864 | 'bn4b11_branch2c') 865 | .add(name='res4b11') 866 | .relu(name='res4b11_relu') 867 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b12_branch2a') 868 | .batch_normalization(relu=True, name='bn4b12_branch2a') 869 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b12_branch2b') 870 | .batch_normalization(relu=True, name='bn4b12_branch2b') 871 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b12_branch2c') 872 | .batch_normalization(name='bn4b12_branch2c')) 873 | 874 | (self.feed('res4b11_relu', 875 | 'bn4b12_branch2c') 876 | .add(name='res4b12') 877 | .relu(name='res4b12_relu') 878 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b13_branch2a') 879 | .batch_normalization(relu=True, name='bn4b13_branch2a') 880 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b13_branch2b') 881 | .batch_normalization(relu=True, name='bn4b13_branch2b') 882 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b13_branch2c') 883 | .batch_normalization(name='bn4b13_branch2c')) 884 | 885 | (self.feed('res4b12_relu', 886 | 'bn4b13_branch2c') 887 | .add(name='res4b13') 888 | .relu(name='res4b13_relu') 889 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b14_branch2a') 890 | .batch_normalization(relu=True, name='bn4b14_branch2a') 891 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b14_branch2b') 892 | .batch_normalization(relu=True, name='bn4b14_branch2b') 893 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b14_branch2c') 894 | .batch_normalization(name='bn4b14_branch2c')) 895 | 896 | (self.feed('res4b13_relu', 897 | 'bn4b14_branch2c') 898 | .add(name='res4b14') 899 | .relu(name='res4b14_relu') 900 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b15_branch2a') 901 | .batch_normalization(relu=True, name='bn4b15_branch2a') 902 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b15_branch2b') 903 | .batch_normalization(relu=True, name='bn4b15_branch2b') 904 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b15_branch2c') 905 | .batch_normalization(name='bn4b15_branch2c')) 906 | 907 | (self.feed('res4b14_relu', 908 | 'bn4b15_branch2c') 909 | .add(name='res4b15') 910 | .relu(name='res4b15_relu') 911 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b16_branch2a') 912 | .batch_normalization(relu=True, name='bn4b16_branch2a') 913 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b16_branch2b') 914 | .batch_normalization(relu=True, name='bn4b16_branch2b') 915 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b16_branch2c') 916 | .batch_normalization(name='bn4b16_branch2c')) 917 | 918 | (self.feed('res4b15_relu', 919 | 'bn4b16_branch2c') 920 | .add(name='res4b16') 921 | .relu(name='res4b16_relu') 922 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b17_branch2a') 923 | .batch_normalization(relu=True, name='bn4b17_branch2a') 924 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b17_branch2b') 925 | .batch_normalization(relu=True, name='bn4b17_branch2b') 926 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b17_branch2c') 927 | .batch_normalization(name='bn4b17_branch2c')) 928 | 929 | (self.feed('res4b16_relu', 930 | 'bn4b17_branch2c') 931 | .add(name='res4b17') 932 | .relu(name='res4b17_relu') 933 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b18_branch2a') 934 | .batch_normalization(relu=True, name='bn4b18_branch2a') 935 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b18_branch2b') 936 | .batch_normalization(relu=True, name='bn4b18_branch2b') 937 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b18_branch2c') 938 | .batch_normalization(name='bn4b18_branch2c')) 939 | 940 | (self.feed('res4b17_relu', 941 | 'bn4b18_branch2c') 942 | .add(name='res4b18') 943 | .relu(name='res4b18_relu') 944 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b19_branch2a') 945 | .batch_normalization(relu=True, name='bn4b19_branch2a') 946 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b19_branch2b') 947 | .batch_normalization(relu=True, name='bn4b19_branch2b') 948 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b19_branch2c') 949 | .batch_normalization(name='bn4b19_branch2c')) 950 | 951 | (self.feed('res4b18_relu', 952 | 'bn4b19_branch2c') 953 | .add(name='res4b19') 954 | .relu(name='res4b19_relu') 955 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b20_branch2a') 956 | .batch_normalization(relu=True, name='bn4b20_branch2a') 957 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b20_branch2b') 958 | .batch_normalization(relu=True, name='bn4b20_branch2b') 959 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b20_branch2c') 960 | .batch_normalization(name='bn4b20_branch2c')) 961 | 962 | (self.feed('res4b19_relu', 963 | 'bn4b20_branch2c') 964 | .add(name='res4b20') 965 | .relu(name='res4b20_relu') 966 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b21_branch2a') 967 | .batch_normalization(relu=True, name='bn4b21_branch2a') 968 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b21_branch2b') 969 | .batch_normalization(relu=True, name='bn4b21_branch2b') 970 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b21_branch2c') 971 | .batch_normalization(name='bn4b21_branch2c')) 972 | 973 | (self.feed('res4b20_relu', 974 | 'bn4b21_branch2c') 975 | .add(name='res4b21') 976 | .relu(name='res4b21_relu') 977 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b22_branch2a') 978 | .batch_normalization(relu=True, name='bn4b22_branch2a') 979 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b22_branch2b') 980 | .batch_normalization(relu=True, name='bn4b22_branch2b') 981 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b22_branch2c') 982 | .batch_normalization(name='bn4b22_branch2c')) 983 | 984 | (self.feed('res4b21_relu', 985 | 'bn4b22_branch2c') 986 | .add(name='res4b22') 987 | .relu(name='res4b22_relu') 988 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b23_branch2a') 989 | .batch_normalization(relu=True, name='bn4b23_branch2a') 990 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b23_branch2b') 991 | .batch_normalization(relu=True, name='bn4b23_branch2b') 992 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b23_branch2c') 993 | .batch_normalization(name='bn4b23_branch2c')) 994 | 995 | (self.feed('res4b22_relu', 996 | 'bn4b23_branch2c') 997 | .add(name='res4b23') 998 | .relu(name='res4b23_relu') 999 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b24_branch2a') 1000 | .batch_normalization(relu=True, name='bn4b24_branch2a') 1001 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b24_branch2b') 1002 | .batch_normalization(relu=True, name='bn4b24_branch2b') 1003 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b24_branch2c') 1004 | .batch_normalization(name='bn4b24_branch2c')) 1005 | 1006 | (self.feed('res4b23_relu', 1007 | 'bn4b24_branch2c') 1008 | .add(name='res4b24') 1009 | .relu(name='res4b24_relu') 1010 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b25_branch2a') 1011 | .batch_normalization(relu=True, name='bn4b25_branch2a') 1012 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b25_branch2b') 1013 | .batch_normalization(relu=True, name='bn4b25_branch2b') 1014 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b25_branch2c') 1015 | .batch_normalization(name='bn4b25_branch2c')) 1016 | 1017 | (self.feed('res4b24_relu', 1018 | 'bn4b25_branch2c') 1019 | .add(name='res4b25') 1020 | .relu(name='res4b25_relu') 1021 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b26_branch2a') 1022 | .batch_normalization(relu=True, name='bn4b26_branch2a') 1023 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b26_branch2b') 1024 | .batch_normalization(relu=True, name='bn4b26_branch2b') 1025 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b26_branch2c') 1026 | .batch_normalization(name='bn4b26_branch2c')) 1027 | 1028 | (self.feed('res4b25_relu', 1029 | 'bn4b26_branch2c') 1030 | .add(name='res4b26') 1031 | .relu(name='res4b26_relu') 1032 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b27_branch2a') 1033 | .batch_normalization(relu=True, name='bn4b27_branch2a') 1034 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b27_branch2b') 1035 | .batch_normalization(relu=True, name='bn4b27_branch2b') 1036 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b27_branch2c') 1037 | .batch_normalization(name='bn4b27_branch2c')) 1038 | 1039 | (self.feed('res4b26_relu', 1040 | 'bn4b27_branch2c') 1041 | .add(name='res4b27') 1042 | .relu(name='res4b27_relu') 1043 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b28_branch2a') 1044 | .batch_normalization(relu=True, name='bn4b28_branch2a') 1045 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b28_branch2b') 1046 | .batch_normalization(relu=True, name='bn4b28_branch2b') 1047 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b28_branch2c') 1048 | .batch_normalization(name='bn4b28_branch2c')) 1049 | 1050 | (self.feed('res4b27_relu', 1051 | 'bn4b28_branch2c') 1052 | .add(name='res4b28') 1053 | .relu(name='res4b28_relu') 1054 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b29_branch2a') 1055 | .batch_normalization(relu=True, name='bn4b29_branch2a') 1056 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b29_branch2b') 1057 | .batch_normalization(relu=True, name='bn4b29_branch2b') 1058 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b29_branch2c') 1059 | .batch_normalization(name='bn4b29_branch2c')) 1060 | 1061 | (self.feed('res4b28_relu', 1062 | 'bn4b29_branch2c') 1063 | .add(name='res4b29') 1064 | .relu(name='res4b29_relu') 1065 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b30_branch2a') 1066 | .batch_normalization(relu=True, name='bn4b30_branch2a') 1067 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b30_branch2b') 1068 | .batch_normalization(relu=True, name='bn4b30_branch2b') 1069 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b30_branch2c') 1070 | .batch_normalization(name='bn4b30_branch2c')) 1071 | 1072 | (self.feed('res4b29_relu', 1073 | 'bn4b30_branch2c') 1074 | .add(name='res4b30') 1075 | .relu(name='res4b30_relu') 1076 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b31_branch2a') 1077 | .batch_normalization(relu=True, name='bn4b31_branch2a') 1078 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b31_branch2b') 1079 | .batch_normalization(relu=True, name='bn4b31_branch2b') 1080 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b31_branch2c') 1081 | .batch_normalization(name='bn4b31_branch2c')) 1082 | 1083 | (self.feed('res4b30_relu', 1084 | 'bn4b31_branch2c') 1085 | .add(name='res4b31') 1086 | .relu(name='res4b31_relu') 1087 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b32_branch2a') 1088 | .batch_normalization(relu=True, name='bn4b32_branch2a') 1089 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b32_branch2b') 1090 | .batch_normalization(relu=True, name='bn4b32_branch2b') 1091 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b32_branch2c') 1092 | .batch_normalization(name='bn4b32_branch2c')) 1093 | 1094 | (self.feed('res4b31_relu', 1095 | 'bn4b32_branch2c') 1096 | .add(name='res4b32') 1097 | .relu(name='res4b32_relu') 1098 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b33_branch2a') 1099 | .batch_normalization(relu=True, name='bn4b33_branch2a') 1100 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b33_branch2b') 1101 | .batch_normalization(relu=True, name='bn4b33_branch2b') 1102 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b33_branch2c') 1103 | .batch_normalization(name='bn4b33_branch2c')) 1104 | 1105 | (self.feed('res4b32_relu', 1106 | 'bn4b33_branch2c') 1107 | .add(name='res4b33') 1108 | .relu(name='res4b33_relu') 1109 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b34_branch2a') 1110 | .batch_normalization(relu=True, name='bn4b34_branch2a') 1111 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b34_branch2b') 1112 | .batch_normalization(relu=True, name='bn4b34_branch2b') 1113 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b34_branch2c') 1114 | .batch_normalization(name='bn4b34_branch2c')) 1115 | 1116 | (self.feed('res4b33_relu', 1117 | 'bn4b34_branch2c') 1118 | .add(name='res4b34') 1119 | .relu(name='res4b34_relu') 1120 | .conv(1, 1, 256, 1, 1, biased=False, relu=False, name='res4b35_branch2a') 1121 | .batch_normalization(relu=True, name='bn4b35_branch2a') 1122 | .conv(3, 3, 256, 1, 1, biased=False, relu=False, name='res4b35_branch2b') 1123 | .batch_normalization(relu=True, name='bn4b35_branch2b') 1124 | .conv(1, 1, 1024, 1, 1, biased=False, relu=False, name='res4b35_branch2c') 1125 | .batch_normalization(name='bn4b35_branch2c')) 1126 | 1127 | (self.feed('res4b34_relu', 1128 | 'bn4b35_branch2c') 1129 | .add(name='res4b35') 1130 | .relu(name='res4b35_relu') 1131 | .conv(1, 1, 2048, 2, 2, biased=False, relu=False, name='res5a_branch1') 1132 | .batch_normalization(name='bn5a_branch1')) 1133 | 1134 | (self.feed('res4b35_relu') 1135 | .conv(1, 1, 512, 2, 2, biased=False, relu=False, name='res5a_branch2a') 1136 | .batch_normalization(relu=True, name='bn5a_branch2a') 1137 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5a_branch2b') 1138 | .batch_normalization(relu=True, name='bn5a_branch2b') 1139 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5a_branch2c') 1140 | .batch_normalization(name='bn5a_branch2c')) 1141 | 1142 | (self.feed('bn5a_branch1', 1143 | 'bn5a_branch2c') 1144 | .add(name='res5a') 1145 | .relu(name='res5a_relu') 1146 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5b_branch2a') 1147 | .batch_normalization(relu=True, name='bn5b_branch2a') 1148 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5b_branch2b') 1149 | .batch_normalization(relu=True, name='bn5b_branch2b') 1150 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5b_branch2c') 1151 | .batch_normalization(name='bn5b_branch2c')) 1152 | 1153 | (self.feed('res5a_relu', 1154 | 'bn5b_branch2c') 1155 | .add(name='res5b') 1156 | .relu(name='res5b_relu') 1157 | .conv(1, 1, 512, 1, 1, biased=False, relu=False, name='res5c_branch2a') 1158 | .batch_normalization(relu=True, name='bn5c_branch2a') 1159 | .conv(3, 3, 512, 1, 1, biased=False, relu=False, name='res5c_branch2b') 1160 | .batch_normalization(relu=True, name='bn5c_branch2b') 1161 | .conv(1, 1, 2048, 1, 1, biased=False, relu=False, name='res5c_branch2c') 1162 | .batch_normalization(name='bn5c_branch2c')) 1163 | 1164 | (self.feed('res5b_relu', 1165 | 'bn5c_branch2c') 1166 | .add(name='res5c') 1167 | .relu(name='res5c_relu') 1168 | .avg_pool(7, 7, 1, 1, padding='VALID', name='pool5') 1169 | .fc(1000, relu=False, name='fc1000') 1170 | .softmax(name='prob')) 1171 | -------------------------------------------------------------------------------- /examples/imagenet/models/vgg.py: -------------------------------------------------------------------------------- 1 | from kaffe.tensorflow import Network 2 | 3 | class VGG16(Network): 4 | def setup(self): 5 | (self.feed('data') 6 | .conv(3, 3, 64, 1, 1, name='conv1_1') 7 | .conv(3, 3, 64, 1, 1, name='conv1_2') 8 | .max_pool(2, 2, 2, 2, name='pool1') 9 | .conv(3, 3, 128, 1, 1, name='conv2_1') 10 | .conv(3, 3, 128, 1, 1, name='conv2_2') 11 | .max_pool(2, 2, 2, 2, name='pool2') 12 | .conv(3, 3, 256, 1, 1, name='conv3_1') 13 | .conv(3, 3, 256, 1, 1, name='conv3_2') 14 | .conv(3, 3, 256, 1, 1, name='conv3_3') 15 | .max_pool(2, 2, 2, 2, name='pool3') 16 | .conv(3, 3, 512, 1, 1, name='conv4_1') 17 | .conv(3, 3, 512, 1, 1, name='conv4_2') 18 | .conv(3, 3, 512, 1, 1, name='conv4_3') 19 | .max_pool(2, 2, 2, 2, name='pool4') 20 | .conv(3, 3, 512, 1, 1, name='conv5_1') 21 | .conv(3, 3, 512, 1, 1, name='conv5_2') 22 | .conv(3, 3, 512, 1, 1, name='conv5_3') 23 | .max_pool(2, 2, 2, 2, name='pool5') 24 | .fc(4096, name='fc6') 25 | .fc(4096, name='fc7') 26 | .fc(1000, relu=False, name='fc8') 27 | .softmax(name='prob')) 28 | -------------------------------------------------------------------------------- /examples/imagenet/validate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | '''Validates a converted ImageNet model against the ILSVRC12 validation set.''' 3 | 4 | import argparse 5 | import numpy as np 6 | import tensorflow as tf 7 | import os.path as osp 8 | 9 | import models 10 | import dataset 11 | 12 | 13 | def load_model(name): 14 | '''Creates and returns an instance of the model given its class name. 15 | The created model has a single placeholder node for feeding images. 16 | ''' 17 | # Find the model class from its name 18 | all_models = models.get_models() 19 | lut = {model.__name__: model for model in all_models} 20 | if name not in lut: 21 | print('Invalid model index. Options are:') 22 | # Display a list of valid model names 23 | for model in all_models: 24 | print('\t* {}'.format(model.__name__)) 25 | return None 26 | NetClass = lut[name] 27 | 28 | # Create a placeholder for the input image 29 | spec = models.get_data_spec(model_class=NetClass) 30 | data_node = tf.placeholder(tf.float32, 31 | shape=(None, spec.crop_size, spec.crop_size, spec.channels)) 32 | 33 | # Construct and return the model 34 | return NetClass({'data': data_node}) 35 | 36 | 37 | def validate(net, model_path, image_producer, top_k=5): 38 | '''Compute the top_k classification accuracy for the given network and images.''' 39 | # Get the data specifications for given network 40 | spec = models.get_data_spec(model_instance=net) 41 | # Get the input node for feeding in the images 42 | input_node = net.inputs['data'] 43 | # Create a placeholder for the ground truth labels 44 | label_node = tf.placeholder(tf.int32) 45 | # Get the output of the network (class probabilities) 46 | probs = net.get_output() 47 | # Create a top_k accuracy node 48 | top_k_op = tf.nn.in_top_k(probs, label_node, top_k) 49 | # The number of images processed 50 | count = 0 51 | # The number of correctly classified images 52 | correct = 0 53 | # The total number of images 54 | total = len(image_producer) 55 | 56 | with tf.Session() as sesh: 57 | coordinator = tf.train.Coordinator() 58 | # Load the converted parameters 59 | net.load(data_path=model_path, session=sesh) 60 | # Start the image processing workers 61 | threads = image_producer.start(session=sesh, coordinator=coordinator) 62 | # Iterate over and classify mini-batches 63 | for (labels, images) in image_producer.batches(sesh): 64 | correct += np.sum(sesh.run(top_k_op, 65 | feed_dict={input_node: images, 66 | label_node: labels})) 67 | count += len(labels) 68 | cur_accuracy = float(correct) * 100 / count 69 | print('{:>6}/{:<6} {:>6.2f}%'.format(count, total, cur_accuracy)) 70 | # Stop the worker threads 71 | coordinator.request_stop() 72 | coordinator.join(threads, stop_grace_period_secs=2) 73 | print('Top {} Accuracy: {}'.format(top_k, float(correct) / total)) 74 | 75 | 76 | 77 | def main(): 78 | # Parse arguments 79 | parser = argparse.ArgumentParser() 80 | parser.add_argument('model_path', help='Path to the converted model parameters (.npy)') 81 | parser.add_argument('val_gt', help='Path to validation set ground truth (.txt)') 82 | parser.add_argument('imagenet_data_dir', help='ImageNet validation set images directory path') 83 | parser.add_argument('--model', default='GoogleNet', help='The name of the model to evaluate') 84 | args = parser.parse_args() 85 | 86 | # Load the network 87 | net = load_model(args.model) 88 | if net is None: 89 | exit(-1) 90 | 91 | # Load the dataset 92 | data_spec = models.get_data_spec(model_instance=net) 93 | image_producer = dataset.ImageNetProducer(val_path=args.val_gt, 94 | data_path=args.imagenet_data_dir, 95 | data_spec=data_spec) 96 | 97 | # Evaluate its performance on the ILSVRC12 validation set 98 | validate(net, args.model_path, image_producer) 99 | 100 | 101 | if __name__ == '__main__': 102 | main() 103 | -------------------------------------------------------------------------------- /examples/mnist/README.md: -------------------------------------------------------------------------------- 1 | ### LeNet Example 2 | 3 | _Thanks to @Russell91 for this example_ 4 | 5 | This example showns you how to finetune code from the [Caffe MNIST tutorial](http://caffe.berkeleyvision.org/gathered/examples/mnist.html) using Tensorflow. 6 | First, you can convert a prototxt model to tensorflow code: 7 | 8 | $ ./convert.py examples/mnist/lenet.prototxt --code-output-path=mynet.py 9 | 10 | This produces tensorflow code for the LeNet network in `mynet.py`. The code can be imported as described below in the Inference section. Caffe-tensorflow also lets you convert `.caffemodel` weight files to `.npy` files that can be directly loaded from tensorflow: 11 | 12 | $ ./convert.py examples/mnist/lenet.prototxt --caffemodel examples/mnist/lenet_iter_10000.caffemodel --data-output-path=mynet.npy 13 | 14 | The above command will generate a weight file named `mynet.npy`. 15 | 16 | #### Inference: 17 | 18 | Once you have generated both the code weight files for LeNet, you can finetune LeNet using tensorflow with 19 | 20 | $ ./examples/mnist/finetune_mnist.py 21 | 22 | At a high level, `finetune_mnist.py` works as follows: 23 | 24 | ```python 25 | # Import the converted model's class 26 | from mynet import MyNet 27 | 28 | # Create an instance, passing in the input data 29 | net = MyNet({'data':my_input_data}) 30 | 31 | with tf.Session() as sesh: 32 | # Load the data 33 | net.load('mynet.npy', sesh) 34 | # Forward pass 35 | output = sesh.run(net.get_output(), ...) 36 | ``` 37 | -------------------------------------------------------------------------------- /examples/mnist/finetune_mnist.py: -------------------------------------------------------------------------------- 1 | # Import the converted model's class 2 | import numpy as np 3 | import random 4 | import tensorflow as tf 5 | from tensorflow.examples.tutorials.mnist import input_data 6 | 7 | from mynet import LeNet as MyNet 8 | 9 | mnist = input_data.read_data_sets('MNIST_data', one_hot=True) 10 | batch_size = 32 11 | 12 | def gen_data(source): 13 | while True: 14 | indices = range(len(source.images)) 15 | random.shuffle(indices) 16 | for i in indices: 17 | image = np.reshape(source.images[i], (28, 28, 1)) 18 | label = source.labels[i] 19 | yield image, label 20 | 21 | def gen_data_batch(source): 22 | data_gen = gen_data(source) 23 | while True: 24 | image_batch = [] 25 | label_batch = [] 26 | for _ in range(batch_size): 27 | image, label = next(data_gen) 28 | image_batch.append(image) 29 | label_batch.append(label) 30 | yield np.array(image_batch), np.array(label_batch) 31 | 32 | 33 | images = tf.placeholder(tf.float32, [batch_size, 28, 28, 1]) 34 | labels = tf.placeholder(tf.float32, [batch_size, 10]) 35 | net = MyNet({'data': images}) 36 | 37 | ip2 = net.layers['ip2'] 38 | pred = tf.nn.softmax(ip2) 39 | 40 | loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(ip2, labels), 0) 41 | opt = tf.train.RMSPropOptimizer(0.001) 42 | train_op = opt.minimize(loss) 43 | 44 | with tf.Session() as sess: 45 | # Load the data 46 | sess.run(tf.initialize_all_variables()) 47 | net.load('mynet.npy', sess) 48 | 49 | data_gen = gen_data_batch(mnist.train) 50 | for i in range(1000): 51 | np_images, np_labels = next(data_gen) 52 | feed = {images: np_images, labels: np_labels} 53 | 54 | np_loss, np_pred, _ = sess.run([loss, pred, train_op], feed_dict=feed) 55 | if i % 10 == 0: 56 | print('Iteration: ', i, np_loss) 57 | -------------------------------------------------------------------------------- /examples/mnist/lenet.prototxt: -------------------------------------------------------------------------------- 1 | name: "LeNet" 2 | layer { 3 | name: "data" 4 | type: "Input" 5 | top: "data" 6 | input_param { shape: { dim: 64 dim: 1 dim: 28 dim: 28 } } 7 | } 8 | layer { 9 | name: "conv1" 10 | type: "Convolution" 11 | bottom: "data" 12 | top: "conv1" 13 | param { 14 | lr_mult: 1 15 | } 16 | param { 17 | lr_mult: 2 18 | } 19 | convolution_param { 20 | num_output: 20 21 | kernel_size: 5 22 | stride: 1 23 | weight_filler { 24 | type: "xavier" 25 | } 26 | bias_filler { 27 | type: "constant" 28 | } 29 | } 30 | } 31 | layer { 32 | name: "pool1" 33 | type: "Pooling" 34 | bottom: "conv1" 35 | top: "pool1" 36 | pooling_param { 37 | pool: MAX 38 | kernel_size: 2 39 | stride: 2 40 | } 41 | } 42 | layer { 43 | name: "conv2" 44 | type: "Convolution" 45 | bottom: "pool1" 46 | top: "conv2" 47 | param { 48 | lr_mult: 1 49 | } 50 | param { 51 | lr_mult: 2 52 | } 53 | convolution_param { 54 | num_output: 50 55 | kernel_size: 5 56 | stride: 1 57 | weight_filler { 58 | type: "xavier" 59 | } 60 | bias_filler { 61 | type: "constant" 62 | } 63 | } 64 | } 65 | layer { 66 | name: "pool2" 67 | type: "Pooling" 68 | bottom: "conv2" 69 | top: "pool2" 70 | pooling_param { 71 | pool: MAX 72 | kernel_size: 2 73 | stride: 2 74 | } 75 | } 76 | layer { 77 | name: "ip1" 78 | type: "InnerProduct" 79 | bottom: "pool2" 80 | top: "ip1" 81 | param { 82 | lr_mult: 1 83 | } 84 | param { 85 | lr_mult: 2 86 | } 87 | inner_product_param { 88 | num_output: 500 89 | weight_filler { 90 | type: "xavier" 91 | } 92 | bias_filler { 93 | type: "constant" 94 | } 95 | } 96 | } 97 | layer { 98 | name: "relu1" 99 | type: "ReLU" 100 | bottom: "ip1" 101 | top: "ip1" 102 | } 103 | layer { 104 | name: "ip2" 105 | type: "InnerProduct" 106 | bottom: "ip1" 107 | top: "ip2" 108 | param { 109 | lr_mult: 1 110 | } 111 | param { 112 | lr_mult: 2 113 | } 114 | inner_product_param { 115 | num_output: 10 116 | weight_filler { 117 | type: "xavier" 118 | } 119 | bias_filler { 120 | type: "constant" 121 | } 122 | } 123 | } 124 | layer { 125 | name: "prob" 126 | type: "Softmax" 127 | bottom: "ip2" 128 | top: "prob" 129 | } 130 | -------------------------------------------------------------------------------- /examples/mnist/lenet_iter_10000.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereon/caffe-tensorflow/d870c51e8fa3452cb210b378c78be7ba4dcc8cf0/examples/mnist/lenet_iter_10000.caffemodel -------------------------------------------------------------------------------- /kaffe/__init__.py: -------------------------------------------------------------------------------- 1 | from .graph import GraphBuilder, NodeMapper 2 | from .errors import KaffeError, print_stderr 3 | 4 | from . import tensorflow 5 | -------------------------------------------------------------------------------- /kaffe/caffe/__init__.py: -------------------------------------------------------------------------------- 1 | from .resolver import get_caffe_resolver, has_pycaffe 2 | -------------------------------------------------------------------------------- /kaffe/caffe/resolver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | SHARED_CAFFE_RESOLVER = None 4 | 5 | class CaffeResolver(object): 6 | def __init__(self): 7 | self.import_caffe() 8 | 9 | def import_caffe(self): 10 | self.caffe = None 11 | try: 12 | # Try to import PyCaffe first 13 | import caffe 14 | self.caffe = caffe 15 | except ImportError: 16 | # Fall back to the protobuf implementation 17 | from . import caffepb 18 | self.caffepb = caffepb 19 | show_fallback_warning() 20 | if self.caffe: 21 | # Use the protobuf code from the imported distribution. 22 | # This way, Caffe variants with custom layers will work. 23 | self.caffepb = self.caffe.proto.caffe_pb2 24 | self.NetParameter = self.caffepb.NetParameter 25 | 26 | def has_pycaffe(self): 27 | return self.caffe is not None 28 | 29 | def get_caffe_resolver(): 30 | global SHARED_CAFFE_RESOLVER 31 | if SHARED_CAFFE_RESOLVER is None: 32 | SHARED_CAFFE_RESOLVER = CaffeResolver() 33 | return SHARED_CAFFE_RESOLVER 34 | 35 | def has_pycaffe(): 36 | return get_caffe_resolver().has_pycaffe() 37 | 38 | def show_fallback_warning(): 39 | msg = ''' 40 | ------------------------------------------------------------ 41 | WARNING: PyCaffe not found! 42 | Falling back to a pure protocol buffer implementation. 43 | * Conversions will be drastically slower. 44 | * This backend is UNTESTED! 45 | ------------------------------------------------------------ 46 | 47 | ''' 48 | sys.stderr.write(msg) 49 | -------------------------------------------------------------------------------- /kaffe/errors.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | class KaffeError(Exception): 4 | pass 5 | 6 | def print_stderr(msg): 7 | sys.stderr.write('%s\n' % msg) 8 | -------------------------------------------------------------------------------- /kaffe/graph.py: -------------------------------------------------------------------------------- 1 | from google.protobuf import text_format 2 | 3 | from .caffe import get_caffe_resolver 4 | from .errors import KaffeError, print_stderr 5 | from .layers import LayerAdapter, LayerType, NodeKind, NodeDispatch 6 | from .shapes import TensorShape 7 | 8 | class Node(object): 9 | 10 | def __init__(self, name, kind, layer=None): 11 | self.name = name 12 | self.kind = kind 13 | self.layer = LayerAdapter(layer, kind) if layer else None 14 | self.parents = [] 15 | self.children = [] 16 | self.data = None 17 | self.output_shape = None 18 | self.metadata = {} 19 | 20 | def add_parent(self, parent_node): 21 | assert parent_node not in self.parents 22 | self.parents.append(parent_node) 23 | if self not in parent_node.children: 24 | parent_node.children.append(self) 25 | 26 | def add_child(self, child_node): 27 | assert child_node not in self.children 28 | self.children.append(child_node) 29 | if self not in child_node.parents: 30 | child_node.parents.append(self) 31 | 32 | def get_only_parent(self): 33 | if len(self.parents) != 1: 34 | raise KaffeError('Node (%s) expected to have 1 parent. Found %s.' % 35 | (self, len(self.parents))) 36 | return self.parents[0] 37 | 38 | @property 39 | def parameters(self): 40 | if self.layer is not None: 41 | return self.layer.parameters 42 | return None 43 | 44 | def __str__(self): 45 | return '[%s] %s' % (self.kind, self.name) 46 | 47 | def __repr__(self): 48 | return '%s (0x%x)' % (self.name, id(self)) 49 | 50 | 51 | class Graph(object): 52 | 53 | def __init__(self, nodes=None, name=None): 54 | self.nodes = nodes or [] 55 | self.node_lut = {node.name: node for node in self.nodes} 56 | self.name = name 57 | 58 | def add_node(self, node): 59 | self.nodes.append(node) 60 | self.node_lut[node.name] = node 61 | 62 | def get_node(self, name): 63 | try: 64 | return self.node_lut[name] 65 | except KeyError: 66 | raise KaffeError('Layer not found: %s' % name) 67 | 68 | def get_input_nodes(self): 69 | return [node for node in self.nodes if len(node.parents) == 0] 70 | 71 | def get_output_nodes(self): 72 | return [node for node in self.nodes if len(node.children) == 0] 73 | 74 | def topologically_sorted(self): 75 | sorted_nodes = [] 76 | unsorted_nodes = list(self.nodes) 77 | temp_marked = set() 78 | perm_marked = set() 79 | 80 | def visit(node): 81 | if node in temp_marked: 82 | raise KaffeError('Graph is not a DAG.') 83 | if node in perm_marked: 84 | return 85 | temp_marked.add(node) 86 | for child in node.children: 87 | visit(child) 88 | perm_marked.add(node) 89 | temp_marked.remove(node) 90 | sorted_nodes.insert(0, node) 91 | 92 | while len(unsorted_nodes): 93 | visit(unsorted_nodes.pop()) 94 | return sorted_nodes 95 | 96 | def compute_output_shapes(self): 97 | sorted_nodes = self.topologically_sorted() 98 | for node in sorted_nodes: 99 | node.output_shape = TensorShape(*NodeKind.compute_output_shape(node)) 100 | 101 | def replaced(self, new_nodes): 102 | return Graph(nodes=new_nodes, name=self.name) 103 | 104 | def transformed(self, transformers): 105 | graph = self 106 | for transformer in transformers: 107 | graph = transformer(graph) 108 | if graph is None: 109 | raise KaffeError('Transformer failed: {}'.format(transformer)) 110 | assert isinstance(graph, Graph) 111 | return graph 112 | 113 | def __contains__(self, key): 114 | return key in self.node_lut 115 | 116 | def __str__(self): 117 | hdr = '{:<20} {:<30} {:>20} {:>20}'.format('Type', 'Name', 'Param', 'Output') 118 | s = [hdr, '-' * 94] 119 | for node in self.topologically_sorted(): 120 | # If the node has learned parameters, display the first one's shape. 121 | # In case of convolutions, this corresponds to the weights. 122 | data_shape = node.data[0].shape if node.data else '--' 123 | out_shape = node.output_shape or '--' 124 | s.append('{:<20} {:<30} {:>20} {:>20}'.format(node.kind, node.name, data_shape, 125 | tuple(out_shape))) 126 | return '\n'.join(s) 127 | 128 | 129 | class GraphBuilder(object): 130 | '''Constructs a model graph from a Caffe protocol buffer definition.''' 131 | 132 | def __init__(self, def_path, phase='test'): 133 | ''' 134 | def_path: Path to the model definition (.prototxt) 135 | data_path: Path to the model data (.caffemodel) 136 | phase: Either 'test' or 'train'. Used for filtering phase-specific nodes. 137 | ''' 138 | self.def_path = def_path 139 | self.phase = phase 140 | self.load() 141 | 142 | def load(self): 143 | '''Load the layer definitions from the prototxt.''' 144 | self.params = get_caffe_resolver().NetParameter() 145 | with open(self.def_path, 'rb') as def_file: 146 | text_format.Merge(def_file.read(), self.params) 147 | 148 | def filter_layers(self, layers): 149 | '''Filter out layers based on the current phase.''' 150 | phase_map = {0: 'train', 1: 'test'} 151 | filtered_layer_names = set() 152 | filtered_layers = [] 153 | for layer in layers: 154 | phase = self.phase 155 | if len(layer.include): 156 | phase = phase_map[layer.include[0].phase] 157 | if len(layer.exclude): 158 | phase = phase_map[1 - layer.include[0].phase] 159 | exclude = (phase != self.phase) 160 | # Dropout layers appear in a fair number of Caffe 161 | # test-time networks. These are just ignored. We'll 162 | # filter them out here. 163 | if (not exclude) and (phase == 'test'): 164 | exclude = (layer.type == LayerType.Dropout) 165 | if not exclude: 166 | filtered_layers.append(layer) 167 | # Guard against dupes. 168 | assert layer.name not in filtered_layer_names 169 | filtered_layer_names.add(layer.name) 170 | return filtered_layers 171 | 172 | def make_node(self, layer): 173 | '''Create a graph node for the given layer.''' 174 | kind = NodeKind.map_raw_kind(layer.type) 175 | if kind is None: 176 | raise KaffeError('Unknown layer type encountered: %s' % layer.type) 177 | # We want to use the layer's top names (the "output" names), rather than the 178 | # name attribute, which is more of readability thing than a functional one. 179 | # Other layers will refer to a node by its "top name". 180 | return Node(layer.name, kind, layer=layer) 181 | 182 | def make_input_nodes(self): 183 | ''' 184 | Create data input nodes. 185 | 186 | This method is for old-style inputs, where the input specification 187 | was not treated as a first-class layer in the prototext. 188 | Newer models use the "Input layer" type. 189 | ''' 190 | nodes = [Node(name, NodeKind.Data) for name in self.params.input] 191 | if len(nodes): 192 | input_dim = map(int, self.params.input_dim) 193 | if not input_dim: 194 | if len(self.params.input_shape) > 0: 195 | input_dim = map(int, self.params.input_shape[0].dim) 196 | else: 197 | raise KaffeError('Dimensions for input not specified.') 198 | for node in nodes: 199 | node.output_shape = tuple(input_dim) 200 | return nodes 201 | 202 | def build(self): 203 | ''' 204 | Builds the graph from the Caffe layer definitions. 205 | ''' 206 | # Get the layers 207 | layers = self.params.layers or self.params.layer 208 | # Filter out phase-excluded layers 209 | layers = self.filter_layers(layers) 210 | # Get any separately-specified input layers 211 | nodes = self.make_input_nodes() 212 | nodes += [self.make_node(layer) for layer in layers] 213 | # Initialize the graph 214 | graph = Graph(nodes=nodes, name=self.params.name) 215 | # Connect the nodes 216 | # 217 | # A note on layers and outputs: 218 | # In Caffe, each layer can produce multiple outputs ("tops") from a set of inputs 219 | # ("bottoms"). The bottoms refer to other layers' tops. The top can rewrite a bottom 220 | # (in case of in-place operations). Note that the layer's name is not used for establishing 221 | # any connectivity. It's only used for data association. By convention, a layer with a 222 | # single top will often use the same name (although this is not required). 223 | # 224 | # The current implementation only supports single-output nodes (note that a node can still 225 | # have multiple children, since multiple child nodes can refer to the single top's name). 226 | node_outputs = {} 227 | for layer in layers: 228 | node = graph.get_node(layer.name) 229 | for input_name in layer.bottom: 230 | assert input_name != layer.name 231 | parent_node = node_outputs.get(input_name) 232 | if (parent_node is None) or (parent_node == node): 233 | parent_node = graph.get_node(input_name) 234 | node.add_parent(parent_node) 235 | if len(layer.top)>1: 236 | raise KaffeError('Multiple top nodes are not supported.') 237 | for output_name in layer.top: 238 | if output_name == layer.name: 239 | # Output is named the same as the node. No further action required. 240 | continue 241 | # There are two possibilities here: 242 | # 243 | # Case 1: output_name refers to another node in the graph. 244 | # This is an "in-place operation" that overwrites an existing node. 245 | # This would create a cycle in the graph. We'll undo the in-placing 246 | # by substituting this node wherever the overwritten node is referenced. 247 | # 248 | # Case 2: output_name violates the convention layer.name == output_name. 249 | # Since we are working in the single-output regime, we will can rename it to 250 | # match the layer name. 251 | # 252 | # For both cases, future references to this top re-routes to this node. 253 | node_outputs[output_name] = node 254 | 255 | graph.compute_output_shapes() 256 | return graph 257 | 258 | 259 | class NodeMapper(NodeDispatch): 260 | 261 | def __init__(self, graph): 262 | self.graph = graph 263 | 264 | def map(self): 265 | nodes = self.graph.topologically_sorted() 266 | # Remove input nodes - we'll handle them separately. 267 | input_nodes = self.graph.get_input_nodes() 268 | nodes = [t for t in nodes if t not in input_nodes] 269 | # Decompose DAG into chains. 270 | chains = [] 271 | for node in nodes: 272 | attach_to_chain = None 273 | if len(node.parents) == 1: 274 | parent = node.get_only_parent() 275 | for chain in chains: 276 | if chain[-1] == parent: 277 | # Node is part of an existing chain. 278 | attach_to_chain = chain 279 | break 280 | if attach_to_chain is None: 281 | # Start a new chain for this node. 282 | attach_to_chain = [] 283 | chains.append(attach_to_chain) 284 | attach_to_chain.append(node) 285 | # Map each chain. 286 | mapped_chains = [] 287 | for chain in chains: 288 | mapped_chains.append(self.map_chain(chain)) 289 | return self.commit(mapped_chains) 290 | 291 | def map_chain(self, chain): 292 | return [self.map_node(node) for node in chain] 293 | 294 | def map_node(self, node): 295 | map_func = self.get_handler(node.kind, 'map') 296 | mapped_node = map_func(node) 297 | assert mapped_node is not None 298 | mapped_node.node = node 299 | return mapped_node 300 | 301 | def commit(self, mapped_chains): 302 | raise NotImplementedError('Must be implemented by subclass.') 303 | -------------------------------------------------------------------------------- /kaffe/layers.py: -------------------------------------------------------------------------------- 1 | import re 2 | import numbers 3 | from collections import namedtuple 4 | 5 | from .shapes import * 6 | 7 | LAYER_DESCRIPTORS = { 8 | 9 | # Caffe Types 10 | 'AbsVal': shape_identity, 11 | 'Accuracy': shape_scalar, 12 | 'ArgMax': shape_not_implemented, 13 | 'BatchNorm': shape_identity, 14 | 'BNLL': shape_not_implemented, 15 | 'Concat': shape_concat, 16 | 'ContrastiveLoss': shape_scalar, 17 | 'Convolution': shape_convolution, 18 | 'Deconvolution': shape_not_implemented, 19 | 'Data': shape_data, 20 | 'Dropout': shape_identity, 21 | 'DummyData': shape_data, 22 | 'EuclideanLoss': shape_scalar, 23 | 'Eltwise': shape_identity, 24 | 'Exp': shape_identity, 25 | 'Flatten': shape_not_implemented, 26 | 'HDF5Data': shape_data, 27 | 'HDF5Output': shape_identity, 28 | 'HingeLoss': shape_scalar, 29 | 'Im2col': shape_not_implemented, 30 | 'ImageData': shape_data, 31 | 'InfogainLoss': shape_scalar, 32 | 'InnerProduct': shape_inner_product, 33 | 'Input': shape_data, 34 | 'LRN': shape_identity, 35 | 'MemoryData': shape_mem_data, 36 | 'MultinomialLogisticLoss': shape_scalar, 37 | 'MVN': shape_not_implemented, 38 | 'Pooling': shape_pool, 39 | 'Power': shape_identity, 40 | 'ReLU': shape_identity, 41 | 'Scale': shape_identity, 42 | 'Sigmoid': shape_identity, 43 | 'SigmoidCrossEntropyLoss': shape_scalar, 44 | 'Silence': shape_not_implemented, 45 | 'Softmax': shape_identity, 46 | 'SoftmaxWithLoss': shape_scalar, 47 | 'Split': shape_not_implemented, 48 | 'Slice': shape_not_implemented, 49 | 'TanH': shape_identity, 50 | 'WindowData': shape_not_implemented, 51 | 'Threshold': shape_identity, 52 | } 53 | 54 | LAYER_TYPES = LAYER_DESCRIPTORS.keys() 55 | 56 | LayerType = type('LayerType', (), {t: t for t in LAYER_TYPES}) 57 | 58 | class NodeKind(LayerType): 59 | 60 | @staticmethod 61 | def map_raw_kind(kind): 62 | if kind in LAYER_TYPES: 63 | return kind 64 | return None 65 | 66 | @staticmethod 67 | def compute_output_shape(node): 68 | try: 69 | val = LAYER_DESCRIPTORS[node.kind](node) 70 | return val 71 | except NotImplementedError: 72 | raise KaffeError('Output shape computation not implemented for type: %s' % node.kind) 73 | 74 | 75 | class NodeDispatchError(KaffeError): 76 | 77 | pass 78 | 79 | 80 | class NodeDispatch(object): 81 | 82 | @staticmethod 83 | def get_handler_name(node_kind): 84 | if len(node_kind) <= 4: 85 | # A catch-all for things like ReLU and tanh 86 | return node_kind.lower() 87 | # Convert from CamelCase to under_scored 88 | name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', node_kind) 89 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() 90 | 91 | def get_handler(self, node_kind, prefix): 92 | name = self.get_handler_name(node_kind) 93 | name = '_'.join((prefix, name)) 94 | try: 95 | return getattr(self, name) 96 | except AttributeError: 97 | raise NodeDispatchError('No handler found for node kind: %s (expected: %s)' % 98 | (node_kind, name)) 99 | 100 | 101 | class LayerAdapter(object): 102 | 103 | def __init__(self, layer, kind): 104 | self.layer = layer 105 | self.kind = kind 106 | 107 | @property 108 | def parameters(self): 109 | name = NodeDispatch.get_handler_name(self.kind) 110 | name = '_'.join((name, 'param')) 111 | try: 112 | return getattr(self.layer, name) 113 | except AttributeError: 114 | raise NodeDispatchError('Caffe parameters not found for layer kind: %s' % (self.kind)) 115 | 116 | @staticmethod 117 | def get_kernel_value(scalar, repeated, idx, default=None): 118 | if scalar: 119 | return scalar 120 | if repeated: 121 | if isinstance(repeated, numbers.Number): 122 | return repeated 123 | if len(repeated) == 1: 124 | # Same value applies to all spatial dimensions 125 | return int(repeated[0]) 126 | assert idx < len(repeated) 127 | # Extract the value for the given spatial dimension 128 | return repeated[idx] 129 | if default is None: 130 | raise ValueError('Unable to determine kernel parameter!') 131 | return default 132 | 133 | @property 134 | def kernel_parameters(self): 135 | assert self.kind in (NodeKind.Convolution, NodeKind.Pooling) 136 | params = self.parameters 137 | k_h = self.get_kernel_value(params.kernel_h, params.kernel_size, 0) 138 | k_w = self.get_kernel_value(params.kernel_w, params.kernel_size, 1) 139 | s_h = self.get_kernel_value(params.stride_h, params.stride, 0, default=1) 140 | s_w = self.get_kernel_value(params.stride_w, params.stride, 1, default=1) 141 | p_h = self.get_kernel_value(params.pad_h, params.pad, 0, default=0) 142 | p_w = self.get_kernel_value(params.pad_h, params.pad, 1, default=0) 143 | return KernelParameters(k_h, k_w, s_h, s_w, p_h, p_w) 144 | 145 | 146 | KernelParameters = namedtuple('KernelParameters', ['kernel_h', 'kernel_w', 'stride_h', 'stride_w', 147 | 'pad_h', 'pad_w']) 148 | -------------------------------------------------------------------------------- /kaffe/shapes.py: -------------------------------------------------------------------------------- 1 | import math 2 | from collections import namedtuple 3 | 4 | from .errors import KaffeError 5 | 6 | TensorShape = namedtuple('TensorShape', ['batch_size', 'channels', 'height', 'width']) 7 | 8 | 9 | def get_filter_output_shape(i_h, i_w, params, round_func): 10 | o_h = (i_h + 2 * params.pad_h - params.kernel_h) / float(params.stride_h) + 1 11 | o_w = (i_w + 2 * params.pad_w - params.kernel_w) / float(params.stride_w) + 1 12 | return (int(round_func(o_h)), int(round_func(o_w))) 13 | 14 | 15 | def get_strided_kernel_output_shape(node, round_func): 16 | assert node.layer is not None 17 | input_shape = node.get_only_parent().output_shape 18 | o_h, o_w = get_filter_output_shape(input_shape.height, input_shape.width, 19 | node.layer.kernel_parameters, round_func) 20 | params = node.layer.parameters 21 | has_c_o = hasattr(params, 'num_output') 22 | c = params.num_output if has_c_o else input_shape.channels 23 | return TensorShape(input_shape.batch_size, c, o_h, o_w) 24 | 25 | 26 | def shape_not_implemented(node): 27 | raise NotImplementedError 28 | 29 | 30 | def shape_identity(node): 31 | assert len(node.parents) > 0 32 | return node.parents[0].output_shape 33 | 34 | 35 | def shape_scalar(node): 36 | return TensorShape(1, 1, 1, 1) 37 | 38 | 39 | def shape_data(node): 40 | if node.output_shape: 41 | # Old-style input specification 42 | return node.output_shape 43 | try: 44 | # New-style input specification 45 | return map(int, node.parameters.shape[0].dim) 46 | except: 47 | # We most likely have a data layer on our hands. The problem is, 48 | # Caffe infers the dimensions of the data from the source (eg: LMDB). 49 | # We want to avoid reading datasets here. Fail for now. 50 | # This can be temporarily fixed by transforming the data layer to 51 | # Caffe's "input" layer (as is usually used in the "deploy" version). 52 | # TODO: Find a better solution for this. 53 | raise KaffeError('Cannot determine dimensions of data layer.\n' 54 | 'See comments in function shape_data for more info.') 55 | 56 | 57 | def shape_mem_data(node): 58 | params = node.parameters 59 | return TensorShape(params.batch_size, params.channels, params.height, params.width) 60 | 61 | 62 | def shape_concat(node): 63 | axis = node.layer.parameters.axis 64 | output_shape = None 65 | for parent in node.parents: 66 | if output_shape is None: 67 | output_shape = list(parent.output_shape) 68 | else: 69 | output_shape[axis] += parent.output_shape[axis] 70 | return tuple(output_shape) 71 | 72 | 73 | def shape_convolution(node): 74 | return get_strided_kernel_output_shape(node, math.floor) 75 | 76 | 77 | def shape_pool(node): 78 | return get_strided_kernel_output_shape(node, math.ceil) 79 | 80 | 81 | def shape_inner_product(node): 82 | input_shape = node.get_only_parent().output_shape 83 | return TensorShape(input_shape.batch_size, node.layer.parameters.num_output, 1, 1) 84 | -------------------------------------------------------------------------------- /kaffe/tensorflow/__init__.py: -------------------------------------------------------------------------------- 1 | from .transformer import TensorFlowTransformer 2 | from .network import Network 3 | -------------------------------------------------------------------------------- /kaffe/tensorflow/network.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | 4 | DEFAULT_PADDING = 'SAME' 5 | 6 | 7 | def layer(op): 8 | '''Decorator for composable network layers.''' 9 | 10 | def layer_decorated(self, *args, **kwargs): 11 | # Automatically set a name if not provided. 12 | name = kwargs.setdefault('name', self.get_unique_name(op.__name__)) 13 | # Figure out the layer inputs. 14 | if len(self.terminals) == 0: 15 | raise RuntimeError('No input variables found for layer %s.' % name) 16 | elif len(self.terminals) == 1: 17 | layer_input = self.terminals[0] 18 | else: 19 | layer_input = list(self.terminals) 20 | # Perform the operation and get the output. 21 | layer_output = op(self, layer_input, *args, **kwargs) 22 | # Add to layer LUT. 23 | self.layers[name] = layer_output 24 | # This output is now the input for the next layer. 25 | self.feed(layer_output) 26 | # Return self for chained calls. 27 | return self 28 | 29 | return layer_decorated 30 | 31 | 32 | class Network(object): 33 | 34 | def __init__(self, inputs, trainable=True): 35 | # The input nodes for this network 36 | self.inputs = inputs 37 | # The current list of terminal nodes 38 | self.terminals = [] 39 | # Mapping from layer names to layers 40 | self.layers = dict(inputs) 41 | # If true, the resulting variables are set as trainable 42 | self.trainable = trainable 43 | # Switch variable for dropout 44 | self.use_dropout = tf.placeholder_with_default(tf.constant(1.0), 45 | shape=[], 46 | name='use_dropout') 47 | self.setup() 48 | 49 | def setup(self): 50 | '''Construct the network. ''' 51 | raise NotImplementedError('Must be implemented by the subclass.') 52 | 53 | def load(self, data_path, session, ignore_missing=False): 54 | '''Load network weights. 55 | data_path: The path to the numpy-serialized network weights 56 | session: The current TensorFlow session 57 | ignore_missing: If true, serialized weights for missing layers are ignored. 58 | ''' 59 | data_dict = np.load(data_path).item() 60 | for op_name in data_dict: 61 | with tf.variable_scope(op_name, reuse=True): 62 | for param_name, data in data_dict[op_name].iteritems(): 63 | try: 64 | var = tf.get_variable(param_name) 65 | session.run(var.assign(data)) 66 | except ValueError: 67 | if not ignore_missing: 68 | raise 69 | 70 | def feed(self, *args): 71 | '''Set the input(s) for the next operation by replacing the terminal nodes. 72 | The arguments can be either layer names or the actual layers. 73 | ''' 74 | assert len(args) != 0 75 | self.terminals = [] 76 | for fed_layer in args: 77 | if isinstance(fed_layer, basestring): 78 | try: 79 | fed_layer = self.layers[fed_layer] 80 | except KeyError: 81 | raise KeyError('Unknown layer name fed: %s' % fed_layer) 82 | self.terminals.append(fed_layer) 83 | return self 84 | 85 | def get_output(self): 86 | '''Returns the current network output.''' 87 | return self.terminals[-1] 88 | 89 | def get_unique_name(self, prefix): 90 | '''Returns an index-suffixed unique name for the given prefix. 91 | This is used for auto-generating layer names based on the type-prefix. 92 | ''' 93 | ident = sum(t.startswith(prefix) for t, _ in self.layers.items()) + 1 94 | return '%s_%d' % (prefix, ident) 95 | 96 | def make_var(self, name, shape): 97 | '''Creates a new TensorFlow variable.''' 98 | return tf.get_variable(name, shape, trainable=self.trainable) 99 | 100 | def validate_padding(self, padding): 101 | '''Verifies that the padding is one of the supported ones.''' 102 | assert padding in ('SAME', 'VALID') 103 | 104 | @layer 105 | def conv(self, 106 | input, 107 | k_h, 108 | k_w, 109 | c_o, 110 | s_h, 111 | s_w, 112 | name, 113 | relu=True, 114 | padding=DEFAULT_PADDING, 115 | group=1, 116 | biased=True): 117 | # Verify that the padding is acceptable 118 | self.validate_padding(padding) 119 | # Get the number of channels in the input 120 | c_i = input.get_shape()[-1] 121 | # Verify that the grouping parameter is valid 122 | assert c_i % group == 0 123 | assert c_o % group == 0 124 | # Convolution for a given input and kernel 125 | convolve = lambda i, k: tf.nn.conv2d(i, k, [1, s_h, s_w, 1], padding=padding) 126 | with tf.variable_scope(name) as scope: 127 | kernel = self.make_var('weights', shape=[k_h, k_w, c_i / group, c_o]) 128 | if group == 1: 129 | # This is the common-case. Convolve the input without any further complications. 130 | output = convolve(input, kernel) 131 | else: 132 | # Split the input into groups and then convolve each of them independently 133 | input_groups = tf.split(3, group, input) 134 | kernel_groups = tf.split(3, group, kernel) 135 | output_groups = [convolve(i, k) for i, k in zip(input_groups, kernel_groups)] 136 | # Concatenate the groups 137 | output = tf.concat(3, output_groups) 138 | # Add the biases 139 | if biased: 140 | biases = self.make_var('biases', [c_o]) 141 | output = tf.nn.bias_add(output, biases) 142 | if relu: 143 | # ReLU non-linearity 144 | output = tf.nn.relu(output, name=scope.name) 145 | return output 146 | 147 | @layer 148 | def relu(self, input, name): 149 | return tf.nn.relu(input, name=name) 150 | 151 | @layer 152 | def max_pool(self, input, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING): 153 | self.validate_padding(padding) 154 | return tf.nn.max_pool(input, 155 | ksize=[1, k_h, k_w, 1], 156 | strides=[1, s_h, s_w, 1], 157 | padding=padding, 158 | name=name) 159 | 160 | @layer 161 | def avg_pool(self, input, k_h, k_w, s_h, s_w, name, padding=DEFAULT_PADDING): 162 | self.validate_padding(padding) 163 | return tf.nn.avg_pool(input, 164 | ksize=[1, k_h, k_w, 1], 165 | strides=[1, s_h, s_w, 1], 166 | padding=padding, 167 | name=name) 168 | 169 | @layer 170 | def lrn(self, input, radius, alpha, beta, name, bias=1.0): 171 | return tf.nn.local_response_normalization(input, 172 | depth_radius=radius, 173 | alpha=alpha, 174 | beta=beta, 175 | bias=bias, 176 | name=name) 177 | 178 | @layer 179 | def concat(self, inputs, axis, name): 180 | return tf.concat(concat_dim=axis, values=inputs, name=name) 181 | 182 | @layer 183 | def add(self, inputs, name): 184 | return tf.add_n(inputs, name=name) 185 | 186 | @layer 187 | def fc(self, input, num_out, name, relu=True): 188 | with tf.variable_scope(name) as scope: 189 | input_shape = input.get_shape() 190 | if input_shape.ndims == 4: 191 | # The input is spatial. Vectorize it first. 192 | dim = 1 193 | for d in input_shape[1:].as_list(): 194 | dim *= d 195 | feed_in = tf.reshape(input, [-1, dim]) 196 | else: 197 | feed_in, dim = (input, input_shape[-1].value) 198 | weights = self.make_var('weights', shape=[dim, num_out]) 199 | biases = self.make_var('biases', [num_out]) 200 | op = tf.nn.relu_layer if relu else tf.nn.xw_plus_b 201 | fc = op(feed_in, weights, biases, name=scope.name) 202 | return fc 203 | 204 | @layer 205 | def softmax(self, input, name): 206 | input_shape = map(lambda v: v.value, input.get_shape()) 207 | if len(input_shape) > 2: 208 | # For certain models (like NiN), the singleton spatial dimensions 209 | # need to be explicitly squeezed, since they're not broadcast-able 210 | # in TensorFlow's NHWC ordering (unlike Caffe's NCHW). 211 | if input_shape[1] == 1 and input_shape[2] == 1: 212 | input = tf.squeeze(input, squeeze_dims=[1, 2]) 213 | else: 214 | raise ValueError('Rank 2 tensor input expected for softmax!') 215 | return tf.nn.softmax(input, name=name) 216 | 217 | @layer 218 | def batch_normalization(self, input, name, scale_offset=True, relu=False): 219 | # NOTE: Currently, only inference is supported 220 | with tf.variable_scope(name) as scope: 221 | shape = [input.get_shape()[-1]] 222 | if scale_offset: 223 | scale = self.make_var('scale', shape=shape) 224 | offset = self.make_var('offset', shape=shape) 225 | else: 226 | scale, offset = (None, None) 227 | output = tf.nn.batch_normalization( 228 | input, 229 | mean=self.make_var('mean', shape=shape), 230 | variance=self.make_var('variance', shape=shape), 231 | offset=offset, 232 | scale=scale, 233 | # TODO: This is the default Caffe batch norm eps 234 | # Get the actual eps from parameters 235 | variance_epsilon=1e-5, 236 | name=name) 237 | if relu: 238 | output = tf.nn.relu(output) 239 | return output 240 | 241 | @layer 242 | def dropout(self, input, keep_prob, name): 243 | keep = 1 - self.use_dropout + (self.use_dropout * keep_prob) 244 | return tf.nn.dropout(input, keep, name=name) 245 | -------------------------------------------------------------------------------- /kaffe/tensorflow/transformer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..errors import KaffeError, print_stderr 4 | from ..graph import GraphBuilder, NodeMapper 5 | from ..layers import NodeKind 6 | from ..transformers import (DataInjector, DataReshaper, NodeRenamer, ReLUFuser, 7 | BatchNormScaleBiasFuser, BatchNormPreprocessor, ParameterNamer) 8 | 9 | from . import network 10 | 11 | 12 | def get_padding_type(kernel_params, input_shape, output_shape): 13 | '''Translates Caffe's numeric padding to one of ('SAME', 'VALID'). 14 | Caffe supports arbitrary padding values, while TensorFlow only 15 | supports 'SAME' and 'VALID' modes. So, not all Caffe paddings 16 | can be translated to TensorFlow. There are some subtleties to 17 | how the padding edge-cases are handled. These are described here: 18 | https://github.com/Yangqing/caffe2/blob/master/caffe2/proto/caffe2_legacy.proto 19 | ''' 20 | k_h, k_w, s_h, s_w, p_h, p_w = kernel_params 21 | s_o_h = np.ceil(input_shape.height / float(s_h)) 22 | s_o_w = np.ceil(input_shape.width / float(s_w)) 23 | if (output_shape.height == s_o_h) and (output_shape.width == s_o_w): 24 | return 'SAME' 25 | v_o_h = np.ceil((input_shape.height - k_h + 1.0) / float(s_h)) 26 | v_o_w = np.ceil((input_shape.width - k_w + 1.0) / float(s_w)) 27 | if (output_shape.height == v_o_h) and (output_shape.width == v_o_w): 28 | return 'VALID' 29 | return None 30 | 31 | 32 | class TensorFlowNode(object): 33 | '''An intermediate representation for TensorFlow operations.''' 34 | 35 | def __init__(self, op, *args, **kwargs): 36 | # A string corresponding to the TensorFlow operation 37 | self.op = op 38 | # Positional arguments for the operation 39 | self.args = args 40 | # Keyword arguments for the operation 41 | self.kwargs = list(kwargs.items()) 42 | # The source Caffe node 43 | self.node = None 44 | 45 | def format(self, arg): 46 | '''Returns a string representation for the given value.''' 47 | return "'%s'" % arg if isinstance(arg, basestring) else str(arg) 48 | 49 | def pair(self, key, value): 50 | '''Returns key=formatted(value).''' 51 | return '%s=%s' % (key, self.format(value)) 52 | 53 | def emit(self): 54 | '''Emits the Python source for this node.''' 55 | # Format positional arguments 56 | args = map(self.format, self.args) 57 | # Format any keyword arguments 58 | if self.kwargs: 59 | args += [self.pair(k, v) for k, v in self.kwargs] 60 | # Set the node name 61 | args.append(self.pair('name', self.node.name)) 62 | args = ', '.join(args) 63 | return '%s(%s)' % (self.op, args) 64 | 65 | 66 | class MaybeActivated(object): 67 | 68 | def __init__(self, node, default=True): 69 | self.inject_kwargs = {} 70 | if node.metadata.get('relu', False) != default: 71 | self.inject_kwargs['relu'] = not default 72 | 73 | def __call__(self, *args, **kwargs): 74 | kwargs.update(self.inject_kwargs) 75 | return TensorFlowNode(*args, **kwargs) 76 | 77 | 78 | class TensorFlowMapper(NodeMapper): 79 | 80 | def get_kernel_params(self, node): 81 | kernel_params = node.layer.kernel_parameters 82 | input_shape = node.get_only_parent().output_shape 83 | padding = get_padding_type(kernel_params, input_shape, node.output_shape) 84 | # Only emit the padding if it's not the default value. 85 | padding = {'padding': padding} if padding != network.DEFAULT_PADDING else {} 86 | return (kernel_params, padding) 87 | 88 | def map_convolution(self, node): 89 | (kernel_params, kwargs) = self.get_kernel_params(node) 90 | h = kernel_params.kernel_h 91 | w = kernel_params.kernel_w 92 | c_o = node.output_shape[1] 93 | c_i = node.parents[0].output_shape[1] 94 | group = node.parameters.group 95 | if group != 1: 96 | kwargs['group'] = group 97 | if not node.parameters.bias_term: 98 | kwargs['biased'] = False 99 | assert kernel_params.kernel_h == h 100 | assert kernel_params.kernel_w == w 101 | return MaybeActivated(node)('conv', kernel_params.kernel_h, kernel_params.kernel_w, c_o, 102 | kernel_params.stride_h, kernel_params.stride_w, **kwargs) 103 | 104 | def map_relu(self, node): 105 | return TensorFlowNode('relu') 106 | 107 | def map_pooling(self, node): 108 | pool_type = node.parameters.pool 109 | if pool_type == 0: 110 | pool_op = 'max_pool' 111 | elif pool_type == 1: 112 | pool_op = 'avg_pool' 113 | else: 114 | # Stochastic pooling, for instance. 115 | raise KaffeError('Unsupported pooling type.') 116 | (kernel_params, padding) = self.get_kernel_params(node) 117 | return TensorFlowNode(pool_op, kernel_params.kernel_h, kernel_params.kernel_w, 118 | kernel_params.stride_h, kernel_params.stride_w, **padding) 119 | 120 | def map_inner_product(self, node): 121 | #TODO: Axis 122 | assert node.parameters.axis == 1 123 | #TODO: Unbiased 124 | assert node.parameters.bias_term == True 125 | return MaybeActivated(node)('fc', node.parameters.num_output) 126 | 127 | def map_softmax(self, node): 128 | return TensorFlowNode('softmax') 129 | 130 | def map_lrn(self, node): 131 | params = node.parameters 132 | # The window size must be an odd value. For a window 133 | # size of (2*n+1), TensorFlow defines depth_radius = n. 134 | assert params.local_size % 2 == 1 135 | # Caffe scales by (alpha/(2*n+1)), whereas TensorFlow 136 | # just scales by alpha (as does Krizhevsky's paper). 137 | # We'll account for that here. 138 | alpha = params.alpha / float(params.local_size) 139 | return TensorFlowNode('lrn', int(params.local_size / 2), alpha, params.beta) 140 | 141 | def map_concat(self, node): 142 | axis = (2, 3, 1, 0)[node.parameters.axis] 143 | return TensorFlowNode('concat', axis) 144 | 145 | def map_dropout(self, node): 146 | return TensorFlowNode('dropout', node.parameters.dropout_ratio) 147 | 148 | def map_batch_norm(self, node): 149 | scale_offset = len(node.data) == 4 150 | kwargs = {} if scale_offset else {'scale_offset': False} 151 | return MaybeActivated(node, default=False)('batch_normalization', **kwargs) 152 | 153 | def map_eltwise(self, node): 154 | operations = {0: 'multiply', 1: 'add', 2: 'max'} 155 | op_code = node.parameters.operation 156 | try: 157 | return TensorFlowNode(operations[op_code]) 158 | except KeyError: 159 | raise KaffeError('Unknown elementwise operation: {}'.format(op_code)) 160 | 161 | def commit(self, chains): 162 | return chains 163 | 164 | 165 | class TensorFlowEmitter(object): 166 | 167 | def __init__(self, tab=None): 168 | self.tab = tab or ' ' * 4 169 | self.prefix = '' 170 | 171 | def indent(self): 172 | self.prefix += self.tab 173 | 174 | def outdent(self): 175 | self.prefix = self.prefix[:-len(self.tab)] 176 | 177 | def statement(self, s): 178 | return self.prefix + s + '\n' 179 | 180 | def emit_imports(self): 181 | return self.statement('from kaffe.tensorflow import Network\n') 182 | 183 | def emit_class_def(self, name): 184 | return self.statement('class %s(Network):' % (name)) 185 | 186 | def emit_setup_def(self): 187 | return self.statement('def setup(self):') 188 | 189 | def emit_parents(self, chain): 190 | assert len(chain) 191 | s = '(self.feed(' 192 | sep = ', \n' + self.prefix + (' ' * len(s)) 193 | s += sep.join(["'%s'" % parent.name for parent in chain[0].node.parents]) 194 | return self.statement(s + ')') 195 | 196 | def emit_node(self, node): 197 | return self.statement(' ' * 5 + '.' + node.emit()) 198 | 199 | def emit(self, name, chains): 200 | s = self.emit_imports() 201 | s += self.emit_class_def(name) 202 | self.indent() 203 | s += self.emit_setup_def() 204 | self.indent() 205 | blocks = [] 206 | for chain in chains: 207 | b = '' 208 | b += self.emit_parents(chain) 209 | for node in chain: 210 | b += self.emit_node(node) 211 | blocks.append(b[:-1] + ')') 212 | s = s + '\n\n'.join(blocks) 213 | return s 214 | 215 | 216 | class TensorFlowTransformer(object): 217 | 218 | def __init__(self, def_path, data_path, verbose=True, phase='test'): 219 | self.verbose = verbose 220 | self.phase = phase 221 | self.load(def_path, data_path, phase) 222 | self.params = None 223 | self.source = None 224 | 225 | def load(self, def_path, data_path, phase): 226 | # Build the graph 227 | graph = GraphBuilder(def_path, phase).build() 228 | 229 | if data_path is not None: 230 | # Load and associate learned parameters 231 | graph = DataInjector(def_path, data_path)(graph) 232 | 233 | # Transform the graph 234 | transformers = [ 235 | # Fuse split batch normalization layers 236 | BatchNormScaleBiasFuser(), 237 | 238 | # Fuse ReLUs 239 | # TODO: Move non-linearity application to layer wrapper, allowing 240 | # any arbitrary operation to be optionally activated. 241 | ReLUFuser(allowed_parent_types=[NodeKind.Convolution, NodeKind.InnerProduct, 242 | NodeKind.BatchNorm]), 243 | 244 | # Rename nodes 245 | # Slashes are used for scoping in TensorFlow. Replace slashes 246 | # in node names with underscores. 247 | # (Caffe's GoogLeNet implementation uses slashes) 248 | NodeRenamer(lambda node: node.name.replace('/', '_')) 249 | ] 250 | self.graph = graph.transformed(transformers) 251 | 252 | # Display the graph 253 | if self.verbose: 254 | print_stderr(self.graph) 255 | 256 | def transform_data(self): 257 | if self.params is None: 258 | transformers = [ 259 | 260 | # Reshape the parameters to TensorFlow's ordering 261 | DataReshaper({ 262 | # (c_o, c_i, h, w) -> (h, w, c_i, c_o) 263 | NodeKind.Convolution: (2, 3, 1, 0), 264 | 265 | # (c_o, c_i) -> (c_i, c_o) 266 | NodeKind.InnerProduct: (1, 0) 267 | }), 268 | 269 | # Pre-process batch normalization data 270 | BatchNormPreprocessor(), 271 | 272 | # Convert parameters to dictionaries 273 | ParameterNamer(), 274 | ] 275 | self.graph = self.graph.transformed(transformers) 276 | self.params = {node.name: node.data for node in self.graph.nodes if node.data} 277 | return self.params 278 | 279 | def transform_source(self): 280 | if self.source is None: 281 | mapper = TensorFlowMapper(self.graph) 282 | chains = mapper.map() 283 | emitter = TensorFlowEmitter() 284 | self.source = emitter.emit(self.graph.name, chains) 285 | return self.source 286 | -------------------------------------------------------------------------------- /kaffe/transformers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A collection of graph transforms. 3 | 4 | A transformer is a callable that accepts a graph and returns a transformed version. 5 | ''' 6 | 7 | import numpy as np 8 | 9 | from .caffe import get_caffe_resolver, has_pycaffe 10 | from .errors import KaffeError, print_stderr 11 | from .layers import NodeKind 12 | 13 | 14 | class DataInjector(object): 15 | ''' 16 | Associates parameters loaded from a .caffemodel file with their corresponding nodes. 17 | ''' 18 | 19 | def __init__(self, def_path, data_path): 20 | # The .prototxt file defining the graph 21 | self.def_path = def_path 22 | # The .caffemodel file containing the learned parameters 23 | self.data_path = data_path 24 | # Set to true if the fallback protocol-buffer based backend was used 25 | self.did_use_pb = False 26 | # A list containing (layer name, parameters) tuples 27 | self.params = None 28 | # Load the parameters 29 | self.load() 30 | 31 | def load(self): 32 | if has_pycaffe(): 33 | self.load_using_caffe() 34 | else: 35 | self.load_using_pb() 36 | 37 | def load_using_caffe(self): 38 | caffe = get_caffe_resolver().caffe 39 | net = caffe.Net(self.def_path, self.data_path, caffe.TEST) 40 | data = lambda blob: blob.data 41 | self.params = [(k, map(data, v)) for k, v in net.params.items()] 42 | 43 | def load_using_pb(self): 44 | data = get_caffe_resolver().NetParameter() 45 | data.MergeFromString(open(self.data_path, 'rb').read()) 46 | pair = lambda layer: (layer.name, self.normalize_pb_data(layer)) 47 | layers = data.layers or data.layer 48 | self.params = [pair(layer) for layer in layers if layer.blobs] 49 | self.did_use_pb = True 50 | 51 | def normalize_pb_data(self, layer): 52 | transformed = [] 53 | for blob in layer.blobs: 54 | if len(blob.shape.dim): 55 | dims = blob.shape.dim 56 | c_o, c_i, h, w = map(int, [1] * (4 - len(dims)) + list(dims)) 57 | else: 58 | c_o = blob.num 59 | c_i = blob.channels 60 | h = blob.height 61 | w = blob.width 62 | data = np.array(blob.data, dtype=np.float32).reshape(c_o, c_i, h, w) 63 | transformed.append(data) 64 | return transformed 65 | 66 | def adjust_parameters(self, node, data): 67 | if not self.did_use_pb: 68 | return data 69 | # When using the protobuf-backend, each parameter initially has four dimensions. 70 | # In certain cases (like FC layers), we want to eliminate the singleton dimensions. 71 | # This implementation takes care of the common cases. However, it does leave the 72 | # potential for future issues. 73 | # The Caffe-backend does not suffer from this problem. 74 | data = list(data) 75 | squeeze_indices = [1] # Squeeze biases. 76 | if node.kind == NodeKind.InnerProduct: 77 | squeeze_indices.append(0) # Squeeze FC. 78 | for idx in squeeze_indices: 79 | data[idx] = np.squeeze(data[idx]) 80 | return data 81 | 82 | def __call__(self, graph): 83 | for layer_name, data in self.params: 84 | if layer_name in graph: 85 | node = graph.get_node(layer_name) 86 | node.data = self.adjust_parameters(node, data) 87 | else: 88 | print_stderr('Ignoring parameters for non-existent layer: %s' % layer_name) 89 | return graph 90 | 91 | 92 | class DataReshaper(object): 93 | 94 | def __init__(self, mapping, replace=True): 95 | # A dictionary mapping NodeKind to the transposed order. 96 | self.mapping = mapping 97 | # The node kinds eligible for reshaping 98 | self.reshaped_node_types = self.mapping.keys() 99 | # If true, the reshaped data will replace the old one. 100 | # Otherwise, it's set to the reshaped_data attribute. 101 | self.replace = replace 102 | 103 | def has_spatial_parent(self, node): 104 | try: 105 | parent = node.get_only_parent() 106 | s = parent.output_shape 107 | return s.height > 1 or s.width > 1 108 | except KaffeError: 109 | return False 110 | 111 | def map(self, node_kind): 112 | try: 113 | return self.mapping[node_kind] 114 | except KeyError: 115 | raise KaffeError('Ordering not found for node kind: {}'.format(node_kind)) 116 | 117 | def __call__(self, graph): 118 | for node in graph.nodes: 119 | if node.data is None: 120 | continue 121 | if node.kind not in self.reshaped_node_types: 122 | # Check for 2+ dimensional data 123 | if any(len(tensor.shape) > 1 for tensor in node.data): 124 | print_stderr('Warning: parmaters not reshaped for node: {}'.format(node)) 125 | continue 126 | transpose_order = self.map(node.kind) 127 | weights = node.data[0] 128 | if (node.kind == NodeKind.InnerProduct) and self.has_spatial_parent(node): 129 | # The FC layer connected to the spatial layer needs to be 130 | # re-wired to match the new spatial ordering. 131 | in_shape = node.get_only_parent().output_shape 132 | fc_shape = weights.shape 133 | output_channels = fc_shape[0] 134 | weights = weights.reshape((output_channels, in_shape.channels, in_shape.height, 135 | in_shape.width)) 136 | weights = weights.transpose(self.map(NodeKind.Convolution)) 137 | node.reshaped_data = weights.reshape(fc_shape[transpose_order[0]], 138 | fc_shape[transpose_order[1]]) 139 | else: 140 | node.reshaped_data = weights.transpose(transpose_order) 141 | 142 | if self.replace: 143 | for node in graph.nodes: 144 | if hasattr(node, 'reshaped_data'): 145 | # Set the weights 146 | node.data[0] = node.reshaped_data 147 | del node.reshaped_data 148 | return graph 149 | 150 | 151 | class SubNodeFuser(object): 152 | ''' 153 | An abstract helper for merging a single-child with its single-parent. 154 | ''' 155 | 156 | def __call__(self, graph): 157 | nodes = graph.nodes 158 | fused_nodes = [] 159 | for node in nodes: 160 | if len(node.parents) != 1: 161 | # We're only fusing nodes with single parents 162 | continue 163 | parent = node.get_only_parent() 164 | if len(parent.children) != 1: 165 | # We can only fuse a node if its parent's 166 | # value isn't used by any other node. 167 | continue 168 | if not self.is_eligible_pair(parent, node): 169 | continue 170 | # Rewrite the fused node's children to its parent. 171 | for child in node.children: 172 | child.parents.remove(node) 173 | parent.add_child(child) 174 | # Disconnect the fused node from the graph. 175 | parent.children.remove(node) 176 | fused_nodes.append(node) 177 | # Let the sub-class merge the fused node in any arbitrary way. 178 | self.merge(parent, node) 179 | transformed_nodes = [node for node in nodes if node not in fused_nodes] 180 | return graph.replaced(transformed_nodes) 181 | 182 | def is_eligible_pair(self, parent, child): 183 | '''Returns true if this parent/child pair is eligible for fusion.''' 184 | raise NotImplementedError('Must be implemented by subclass.') 185 | 186 | def merge(self, parent, child): 187 | '''Merge the child node into the parent.''' 188 | raise NotImplementedError('Must be implemented by subclass') 189 | 190 | 191 | class ReLUFuser(SubNodeFuser): 192 | ''' 193 | Fuses rectified linear units with their parent nodes. 194 | ''' 195 | 196 | def __init__(self, allowed_parent_types=None): 197 | # Fuse ReLUs when the parent node is one of the given types. 198 | # If None, all node types are eligible. 199 | self.allowed_parent_types = allowed_parent_types 200 | 201 | def is_eligible_pair(self, parent, child): 202 | return ((self.allowed_parent_types is None or parent.kind in self.allowed_parent_types) and 203 | child.kind == NodeKind.ReLU) 204 | 205 | def merge(self, parent, _): 206 | parent.metadata['relu'] = True 207 | 208 | 209 | class BatchNormScaleBiasFuser(SubNodeFuser): 210 | ''' 211 | The original batch normalization paper includes two learned 212 | parameters: a scaling factor \gamma and a bias \beta. 213 | Caffe's implementation does not include these two. However, it is commonly 214 | replicated by adding a scaling+bias layer immidiately after the batch norm. 215 | 216 | This fuser merges the scaling+bias layer with the batch norm. 217 | ''' 218 | 219 | def is_eligible_pair(self, parent, child): 220 | return (parent.kind == NodeKind.BatchNorm and child.kind == NodeKind.Scale and 221 | child.parameters.axis == 1 and child.parameters.bias_term == True) 222 | 223 | def merge(self, parent, child): 224 | parent.scale_bias_node = child 225 | 226 | 227 | class BatchNormPreprocessor(object): 228 | ''' 229 | Prescale batch normalization parameters. 230 | Concatenate gamma (scale) and beta (bias) terms if set. 231 | ''' 232 | 233 | def __call__(self, graph): 234 | for node in graph.nodes: 235 | if node.kind != NodeKind.BatchNorm: 236 | continue 237 | assert node.data is not None 238 | assert len(node.data) == 3 239 | mean, variance, scale = node.data 240 | # Prescale the stats 241 | scaling_factor = 1.0 / scale if scale != 0 else 0 242 | mean *= scaling_factor 243 | variance *= scaling_factor 244 | # Replace with the updated values 245 | node.data = [mean, variance] 246 | if hasattr(node, 'scale_bias_node'): 247 | # Include the scale and bias terms 248 | gamma, beta = node.scale_bias_node.data 249 | node.data += [gamma, beta] 250 | return graph 251 | 252 | 253 | class NodeRenamer(object): 254 | ''' 255 | Renames nodes in the graph using a given unary function that 256 | accepts a node and returns its new name. 257 | ''' 258 | 259 | def __init__(self, renamer): 260 | self.renamer = renamer 261 | 262 | def __call__(self, graph): 263 | for node in graph.nodes: 264 | node.name = self.renamer(node) 265 | return graph 266 | 267 | 268 | class ParameterNamer(object): 269 | ''' 270 | Convert layer data arrays to a dictionary mapping parameter names to their values. 271 | ''' 272 | 273 | def __call__(self, graph): 274 | for node in graph.nodes: 275 | if node.data is None: 276 | continue 277 | if node.kind in (NodeKind.Convolution, NodeKind.InnerProduct): 278 | names = ('weights',) 279 | if node.parameters.bias_term: 280 | names += ('biases',) 281 | elif node.kind == NodeKind.BatchNorm: 282 | names = ('mean', 'variance') 283 | if len(node.data) == 4: 284 | names += ('scale', 'offset') 285 | else: 286 | print_stderr('WARNING: Unhandled parameters: {}'.format(node.kind)) 287 | continue 288 | assert len(names) == len(node.data) 289 | node.data = dict(zip(names, node.data)) 290 | return graph 291 | --------------------------------------------------------------------------------