Esempio n. 1
0
def build_metrics(global_step,p_lab,d_lab,p_log,d_logs,training):
  with tf.variable_scope('Formatting') as scope:
    # If we're training, we want to not use the plant network output, rather the
    #    plant label. This ensures that the disease layers train properly.
    #    NOTE: The disease loss function only trains based on this final layer.
    #          IE: The disease gradient does not flow through the whole network,
    #              using the plant network as its preprocessing.
    index = p_lab #if training else tf.argmax(p_log)
    index = tf.cast(index,tf.int32)

    size = [1,1,FLAGS.num_disease_classes]
    # Extract the disease logit per example in batch
    d_log = []
    d_logs = tf.reshape(d_logs,[FLAGS.num_plant_classes,FLAGS.batch_size,FLAGS.num_disease_classes])
    for x in range(FLAGS.batch_size):
      start = [index[x],x,0]
      val = tf.slice(d_logs,start,size)
      d_log.append(val)
    d_log = tf.stack(d_log)
    d_log = tf.reshape(d_log,[FLAGS.batch_size,FLAGS.num_disease_classes])

  # Get the losses and metrics

  with tf.variable_scope('Metrics') as scope:
    # Seperate the variables out of each network, so that we do not train the
    # process network on disease loss.
    p_vars = [var for var in tf.trainable_variables() if 'Process_Network' in var.name or 'Plant_Neurons' in var.name]
    d_vars = [var for var in tf.trainable_variables() if 'Disease_Neurons' in var.name]

    # Calculate the losses per network
    p_log = tf.reshape(p_log,[FLAGS.batch_size,FLAGS.num_plant_classes])
    p_loss = ops.xentropy_loss(p_lab,p_log,p_vars,l2 = True ,name = "Plant_Loss")
    d_loss = ops.xentropy_loss(d_lab,d_log,d_vars,l2 = False,name = "Disease_Loss")

    # Flatten the logits so that we only have one output instead of 10
    p_log = tf.argmax(p_log,-1)
    d_log = tf.argmax(d_log,-1)

    # Log the accuracy
    p_acc  = ops.accuracy(p_lab,p_log,name = 'Plant_Accuracy')
    d_acc  = ops.accuracy(d_lab,d_log,name = 'Disease_Accuracy')

    # Create a variable in order to get these operations out of the network
    metrics = (p_loss,d_loss,p_acc,d_acc)

  # Setup the trainer
  with tf.variable_scope('Trainer') as scope:
    train = tf.assign_add(global_step,1,name = 'Global_Step')
    if training:
      p_train = trainer(global_step,p_loss,p_vars,fancy = True ,learning_rate = 1e-5)
      d_train = trainer(global_step,d_loss,d_vars,fancy = False,learning_rate = 1e-4)
      train   = (train,p_train,d_train)

  return p_log,d_log,train,metrics
def build_metrics(global_step, d_lab, d_logs, training):
    # Get the losses and metrics
    with tf.variable_scope('Metrics') as scope:
        # Seperate the variables out of each network, so that we do not train the
        # process network on disease loss.
        d_vars = [
            var for var in tf.trainable_variables()
            if 'Process_Network' in var.name or 'Disease_Neurons' in var.name
        ]

        # Calculate the losses per network
        d_loss = ops.xentropy_loss(d_lab,
                                   d_logs,
                                   d_vars,
                                   l2=True,
                                   name="Disease_Loss")

        # Flatten the logits so that we only have one output instead of 10
        d_log = tf.argmax(d_logs, -1)

        # Log the accuracy
        d_acc = ops.accuracy(d_lab, d_log, name='Disease_Accuracy')

        # Create a variable in order to get these operations out of the network
        metrics = (d_loss, d_acc)

    # Setup the trainer
    with tf.variable_scope('Trainer') as scope:
        train = tf.assign_add(global_step, 1, name='Global_Step')
        if training:
            d_train = trainer(global_step, d_loss, d_vars, fancy=True)
            train = (train, d_train)

    return d_log, train, metrics
Esempio n. 3
0
    def create_model(self):
        layers = [26, 52, 52]

        self.x = tf.placeholder(dtype=tf.float32,
                                shape=[None, INPUT_SIZE * INPUT_SIZE])
        self.y_target = tf.placeholder(dtype=tf.float32, shape=[None, 10])

        labels = self.y_target
        signal = self.x

        signal = tf.reshape(signal, [-1, INPUT_SIZE, INPUT_SIZE])
        signal = augment(signal, layers[0])

        for i in range(1, len(layers)):
            hidden_n = layers[i]
            input_n = layers[i - 1]
            name = "lstm_{}".format(i)
            signal = bidirect_lstm(signal, hidden_n, input_n, name=name)

        signal = get_last_row(signal, layers[-1])
        signal = fully_connected(signal, 10)

        self.global_step = tf.get_variable('global_step', initializer=0)
        update_global_step = tf.assign(self.global_step, self.global_step + 1)

        self.loss = loss_function(signal, labels)
        self.accuracy = accuracy(signal, labels)

        with tf.control_dependencies([update_global_step]):
            self.train_step = tf.train.AdamOptimizer().minimize(self.loss)

        loss_sum = tf.summary.scalar('loss', self.loss)
        acc_sum = tf.summary.scalar('accuracy', self.accuracy)
        self.all_summaries = tf.summary.merge([loss_sum, acc_sum])
Esempio n. 4
0
    def create_model(self):
        self.x = tf.placeholder(dtype=tf.float32,
                                shape=[None, INPUT_SIZE * INPUT_SIZE])
        self.y_target = tf.placeholder(dtype=tf.float32, shape=[None, 10])

        labels = self.y_target
        signal = self.x
        signal = tf.reshape(signal, [-1, INPUT_SIZE, INPUT_SIZE])

        signal = lstm(signal, INPUT_SIZE, INPUT_SIZE, INPUT_SIZE)

        signal = fully_connected(signal, 10)

        self.global_step = tf.get_variable('global_step', initializer=0)
        update_global_step = tf.assign(self.global_step, self.global_step + 1)

        self.loss = loss_function(signal, labels)
        self.accuracy = accuracy(signal, labels)

        with tf.control_dependencies([update_global_step]):
            self.train_step = tf.train.AdamOptimizer().minimize(self.loss)

        loss_sum = tf.summary.scalar('loss', self.loss)
        acc_sum = tf.summary.scalar('accuracy', self.accuracy)
        self.all_summaries = tf.summary.merge([loss_sum, acc_sum])
    def __init__(self,
                 lr=0.0001,
                 optimizer=tf.train.Optimizer,
                 fine_tuning=True,
                 dropout=False,
                 adaptive_ratio=1.0):
        '''

        ----------Hyperparameters -------------
        :param fine_tuning: If True, the parameters of CNN layers will also be fine-tuned.
                             Otherwise, only the parameters of FC layers will be trained.
        :param dropout: If True, dropout is applied to all fully connected layers except for the last one.
                        Also, dropout_keep_prob should be fed. (default value is 1.0)
        :param adaptive_ratio: If True, the learning rate of convolutional layer will be learning rate * adaptive_ratio
        :return:
        '''
        self.desc = "Learning rate : {}, optimizer : {}, fine_tuning : {}, dropout : {}, adaptive ratio : {}"\
            .format(lr, optimizer.__name__, fine_tuning, dropout, adaptive_ratio)
        print(self.desc)
        self.params = {
            'lr': lr,
            'optimizer': optimizer,
            'fine_tuning': fine_tuning,
            'dropout': dropout,
            'adaptive_ratio': adaptive_ratio
        }
        self.xs = tf.placeholder(tf.float32, [None, 32, 32, 3])
        self.ys = tf.placeholder(tf.int32, [None])
        self.dropout_keep_prob = tf.placeholder_with_default(1.0, None)

        pool5 = self.build_convnet(fine_tuning)
        fc3 = self.build_fcnet(pool5, dropout)
        self.probs = tf.nn.softmax(fc3, name='softmax')

        self.loss = ops.loss(logits=self.probs, labels=self.ys, one_hot=False)
        self.accuracy = ops.accuracy(logits=self.probs,
                                     labels=self.ys,
                                     one_hot=False)
        if adaptive_ratio < 1.0:
            self.train = ops.train(self.loss,
                                   optimizer=optimizer,
                                   conv_lr=lr * adaptive_ratio,
                                   fc_lr=lr)
        else:
            self.train = optimizer(learning_rate=lr).minimize(self.loss)
    def build_model(self, inputs, labels, is_training=False):
        self.network = ops.convolution(inputs,
                                       self.channels,
                                       50,
                                       5,
                                       50,
                                       is_training=is_training,
                                       scope='conv1')

        self.network = ops.pooling(self.network, scope='pool1')

        self.network = ops.convolution(self.network,
                                       50,
                                       20,
                                       5,
                                       20,
                                       is_training=is_training,
                                       scope='conv2')

        self.network = ops.pooling(self.network, scope='pool2')

        self.network = ops.flatten(self.network, scope='flatten')

        self.network = ops.dense(self.network,
                                 self.network.get_shape().as_list()[1],
                                 200,
                                 scope='fc1')

        self.network = ops.dense(self.network, 200, 50, scope='fc2')

        self.network = ops.dense(self.network,
                                 50,
                                 10,
                                 activation=None,
                                 scope='fc3')

        self.loss = ops.loss(self.network, labels, scope='loss')
        self.accuracy = ops.accuracy(self.network, labels, scope='accuracy')

        if is_training:
            self.optimizer = ops.optimize(self.loss,
                                          self.learning_rate,
                                          scope='update')
Esempio n. 7
0
# Gradient calculations:
# Initialize gradient descent optimizer
optimizer = ops.optimizer(learning_rate)

# Step 1: Calculate gradient
gradients = ops.calc_gradient(optimizer, loss)

# Step 2: Calculate gradient norm for stopping criteria
gradient_norm = ops.gradient_norm(gradients)

# Step 3: Apply gradients and update model
training_step = ops.apply_gradient(gradients, optimizer)

# Post training: Determine accuracy
accuracy = ops.accuracy(logit, target)

# Setup Tensorboard
# ===========================================================================
writer = tf.train.SummaryWriter(logs_path, graph=tf.get_default_graph())

# Keep track of loss
tf.scalar_summary("Loss", loss)
summary_op = tf.merge_all_summaries()

# Run TensorFlow Session
# ===========================================================================

# Initializing the variables
init = tf.initialize_all_variables()
Esempio n. 8
0
def inference(global_step,
              images,
              p_lab,
              d_lab,
              training,
              name,
              trainable=True):
    with tf.variable_scope(name) as scope:
        # Preform some image preprocessing
        net = images
        net = ops.delist(net)
        net = tf.cast(net, tf.float32)
        net = ops.batch_norm(net, training, trainable)
        # net = tf.fft2d(net)
        # If we receive image channels in a format that shouldn't be normalized,
        #   that goes here.
        with tf.variable_scope('Process_Network'):
            # Run two dense reduction modules to reduce the number of parameters in
            #   the network and for low parameter feature extraction.
            net = ops.dense_reduction(net,
                                      training,
                                      filters=4,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_1')
            net = ops.dense_reduction(net,
                                      training,
                                      filters=8,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_2')
            net = ops.dense_reduction(net,
                                      training,
                                      filters=4,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_3')
            net = ops.dense_reduction(net,
                                      training,
                                      filters=8,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_4')

            # Run the network over some resnet modules, including reduction
            #   modules in order to further reduce the parameters and have a powerful,
            #   proven network architecture.
            net = ops.inception_block_a(net,
                                        training,
                                        trainable,
                                        name='inception_block_a_1')
            net = ops.dense_reduction(net,
                                      training,
                                      filters=16,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_5')

            net = ops.inception_block_a(net,
                                        training,
                                        trainable,
                                        name='inception_block_a_2')
            net = ops.inception_block_a(net,
                                        training,
                                        trainable,
                                        name='inception_block_a_3')
            net = ops.dense_reduction(net,
                                      training,
                                      filters=24,
                                      kernel=3,
                                      stride=2,
                                      activation=tf.nn.leaky_relu,
                                      trainable=trainable,
                                      name='Dense_Block_6')

            net = ops.inception_block_b(net,
                                        training,
                                        trainable,
                                        name='inception_block_b_1')
            net = ops.inception_block_b(net,
                                        training,
                                        trainable,
                                        name='inception_block_b_2')

            # Commenting out for proof of concept
            # net = ops.inception_block_c(net,training,trainable,name='inception_block_c_1')
            # net = ops.inception_block_c(net,training,trainable,name='inception_block_c_1')

            # We're leaving the frequency domain in order to preform fully connected
            #    inferences. Fully connected inferences do not work with imaginary
            #    numbers. The error would always have an i/j term that will not be able
            #    to generate a correct gradient for.
            # net = tf.ifft2d(net)

        with tf.variable_scope('Plant_Neurons') as scope:
            # Theoretically, the network will be 8x8x128, for 8192 neurons in the first
            #    fully connected network.
            net = util.squish_to_batch(net)
            _b, neurons = net.get_shape().as_list()

            # Fully connected network with number of neurons equal to the number of
            #    parameters in the network at this point
            net = tf.layers.dense(net, neurons)

            # Fully connected layer to extract the plant classification
            #    NOTE: This plant classification will be used to extract the proper
            #          disease classification matrix
            p_log = tf.layers.dense(net, FLAGS.num_plant_classes)

        with tf.variable_scope('Disease_Neurons') as scope:
            # Construct a number of final layers for diseases equal to the number of
            # plants.
            d_net = []
            for x in range(FLAGS.num_plant_classes):
                chan = tf.layers.dense(net,
                                       FLAGS.num_disease_classes,
                                       name='Disease_%d' % x)
                d_net.append(chan)
            d_net = tf.stack(d_net)

            # If we're training, we want to not use the plant network output, rather the
            #    plant label. This ensures that the disease layers train properly.
            #    NOTE: The disease loss function only trains based on this final layer.
            #          IE: The disease gradient does not flow through the whole network,
            #              using the plant network as its preprocessing.
            index = d_lab  #if training else tf.argmax(p_log)
            index = tf.cast(index, tf.int32)

            size = [1, 1, FLAGS.num_disease_classes]
            # Extract the disease logit per example in batch
            d_log = []
            for x in range(FLAGS.batch_size):
                start = [index[x], x, 0]
                val = tf.slice(d_net, start, size)
                d_log.append(val)
            d_log = tf.stack(d_log)
            d_log = tf.reshape(d_log,
                               [FLAGS.batch_size, FLAGS.num_disease_classes])

        # Get the losses and metrics

    with tf.variable_scope('Metrics') as scope:
        p_loss = ops.xentropy_loss(labels=p_lab,
                                   logits=p_log,
                                   name="Plant_Metrics")
        d_loss = ops.xentropy_loss(labels=d_lab,
                                   logits=d_log,
                                   name="Disease_Metrics")
        p_log = tf.argmax(p_log, -1)
        d_log = tf.argmax(d_log, -1)
        p_acc = ops.accuracy(p_lab, p_log, name='Plant_Accuracy')
        d_acc = ops.accuracy(d_lab, d_log, name='Disease_Accuracy')

        metrics = (p_loss, d_loss, p_acc, d_acc)

        p_vars = [
            var for var in tf.trainable_variables()
            if 'Process_Network' in var.name or 'Plant_Neurons' in var.name
        ]
        d_vars = [
            var for var in tf.trainable_variables()
            if 'Disease_Neurons' in var.name
        ]

    with tf.variable_scope('Trainer') as scope:
        train = tf.assign_add(global_step, 1, name='Global_Step')
        if training:
            p_train = trainer(global_step, p_loss, p_vars)
            d_train = trainer(global_step, d_loss, d_vars)
            train = (train, p_train, d_train)

    return p_log, d_log, train, metrics
    def build_model(self, inputs, labels, is_training):
        # pad inputs to size 224x224x3 - NOTE: may change to bilinear upsampling
        pad = int((self.image_size - self.height) / 2)
        inputs = tf.pad(inputs, [[0, 0], [pad, pad], [pad, pad], [0, 0]])

        # convolution with 11x11 kernel and stride 4 (new size: 55x55x96)
        self.network = ops.convolution(inputs,
                                       self.channels,
                                       96,
                                       11,
                                       96,
                                       stride=4,
                                       is_training=is_training,
                                       scope='conv1')

        # pooling with 3x3 kernel and stride 2 (new size: 27x27x96)
        self.network = ops.pooling(self.network, k_size=3, scope='pool1')

        # convolution with 5x5 kernel and stride 1 (new size: 27x27x256)
        self.network = ops.convolution(self.network,
                                       96,
                                       256,
                                       5,
                                       256,
                                       is_training=is_training,
                                       scope='conv2')

        # pooling with 3x3 kernel and stride 2 (new size: 13x13x256)
        self.network = ops.pooling(self.network, k_size=3, scope='pool2')

        # convolution with 3x3 kernel and stride 1 (new size: 13x13x384)
        self.network = ops.convolution(self.network,
                                       256,
                                       384,
                                       3,
                                       384,
                                       batch_norm=False,
                                       is_training=is_training,
                                       scope='conv3')

        # convolution with 3x3 kernel and stride 1 (new size: 13x13x384)
        self.network = ops.convolution(self.network,
                                       384,
                                       384,
                                       3,
                                       384,
                                       batch_norm=False,
                                       is_training=is_training,
                                       scope='conv4')

        # convolution with 3x3 kernel and stride 1 (new size: 13x13x256)
        self.network = ops.convolution(self.network,
                                       384,
                                       256,
                                       3,
                                       256,
                                       batch_norm=False,
                                       is_training=is_training,
                                       scope='conv5')

        # pooling with 3x3 kernel and stride 2 (new size: 6x6x256)
        self.network = ops.pooling(self.network, k_size=3, scope='pool3')

        # flatten (new size: 9216)
        self.network = ops.flatten(self.network, scope='flatten')

        # fully connected layer (new size: 4096)
        self.network = ops.dense(self.network,
                                 9216,
                                 4096,
                                 dropout=True,
                                 dropout_rate=0.2,
                                 is_training=is_training,
                                 scope='fc1')

        # fully connected layer (new size: 1024) -- Original Paper Size: 4096 (for ImageNet)
        self.network = ops.dense(self.network,
                                 4096,
                                 1024,
                                 dropout=True,
                                 dropout_rate=0.2,
                                 is_training=is_training,
                                 scope='fc2')

        # output layer (new size: 10) -- Original Paper Size: 1000 (for ImageNet)
        self.network = ops.dense(self.network,
                                 1024,
                                 10,
                                 activation=None,
                                 is_training=is_training,
                                 scope='fc3')

        self.loss = ops.loss(self.network, labels, scope='loss')
        self.accuracy = ops.accuracy(self.network, labels, scope='accuracy')

        if is_training:
            self.optimizer = ops.optimize(self.loss,
                                          self.learning_rate,
                                          scope='update')
    def build_model(self, inputs, labels, is_training):
        pad = int((self.image_size - self.height) / 2)
        inputs = tf.pad(inputs, [[0, 0], [pad, pad], [pad, pad], [0, 0]])

        # convolution with 7x7 kernel and stride 2 (new size: 112x112x64)
        self.network = ops.convolution(inputs,
                                       self.channels,
                                       64,
                                       7,
                                       64,
                                       stride=2,
                                       is_training=is_training,
                                       scope='conv1')

        # pooling with 3x3 kernel and stride 2 (new size: 56x56x64)
        self.network = ops.pooling(self.network, k_size=3, scope='pool1')

        # convolution with 1x1 kernel and stride 1 (new size: 56x56x192)
        self.network = ops.convolution(self.network,
                                       64,
                                       192,
                                       1,
                                       192,
                                       batch_norm=False,
                                       is_training=is_training,
                                       scope='conv2')

        # convolution with 3x3 kernel and stride 1 (new size: 56x56x192)
        self.network = ops.convolution(self.network,
                                       192,
                                       192,
                                       3,
                                       192,
                                       is_training=is_training,
                                       scope='conv3')

        # pooling with 3x3 kernel and stride 2 (new size: 28x28x192)
        self.network = ops.pooling(self.network, k_size=3, scope='pool2')

        # inception module (3a)
        self.network = self.inception_module(self.network,
                                             [[64, 96, 16], [128, 32, 32]],
                                             scope='incept1')

        # inception module (3b)
        self.network = self.inception_module(self.network,
                                             [[128, 128, 32], [192, 96, 64]],
                                             final_pool=True,
                                             scope='incept' + str(i))

        # inception module (4a)
        self.network = self.inception_module(self.network,
                                             [[192, 96, 16], [208, 48, 64]],
                                             scope='incept' + str(i))

        # auxiliary classifier
        if is_training:
            aux_loss1 = self.aux_classifier(self.network,
                                            labels,
                                            512,
                                            is_training,
                                            scope='auxclass1')

        # inception module (4b)
        self.network = self.inception_module(self.network,
                                             [[160, 112, 24], [224, 64, 64]],
                                             scope='incept' + str(i))

        # inception module (4c)
        self.network = self.inception_module(self.network,
                                             [[128, 128, 24], [256, 64, 64]],
                                             scope='incept' + str(i))

        # inception module (4d)
        self.network = self.inception_module(self.network,
                                             [[112, 144, 32], [288, 64, 64]],
                                             scope='incept' + str(i))

        # auxiliary classifier
        if is_training:
            aux_loss2 = self.aux_classifier(self.network,
                                            labels,
                                            528,
                                            is_training,
                                            scope='auxclass2')

        # inception module (4e)
        self.network = self.inception_module(self.network,
                                             [[256, 160, 32], [320, 128, 128]],
                                             final_pool=True,
                                             scope='incept' + str(i))

        # inception module (5a)
        self.network = self.inception_module(self.network,
                                             [[256, 160, 32], [320, 128, 128]],
                                             scope='incept' + str(i))

        # inception module (5b)
        self.network = self.inception_module(self.network,
                                             [[384, 192, 48], [384, 128, 128]],
                                             scope='incept' + str(i))

        # pooling with 7x7 kernel and stride 1 (new size: 1x1x1024)
        with tf.variable_scope('final_pool', reuse=tf.AUTO_REUSE):
            self.network = tf.nn.avg_pool(self.network,
                                          7,
                                          1,
                                          'SAME',
                                          scope='pool')

        # flatten (new size: 1024)
        self.network = ops.flatten(self.network, scope='flatten')

        # fully connected layer (new size: 1024)
        self.network = ops.dense(self.network,
                                 1024,
                                 1024,
                                 dropout=True,
                                 dropout_rate=0.4,
                                 is_training=is_training,
                                 scope='fc1')

        # output layer (new size: 10) -- Original Paper Size: 1000 (for ImageNet)
        self.network = ops.dense(self.network,
                                 1024,
                                 10,
                                 activation=None,
                                 is_training=is_training,
                                 scope='fc2')

        loss = ops.loss(self.network, labels, scope='loss')
        self.accuracy = ops.accuracy(self.network, labels, scope='accuracy')

        if is_training:  # if training use auxiliary classifiers as well
            self.loss = loss + aux_loss1 + aux_loss2
            self.optimizer = ops.optimize(self.loss,
                                          self.learning_rate,
                                          scope='update')
        else:
            self.loss = loss
    def build_model(self, inputs, labels, is_training):
        def res_block(inputs, in_channels, out_channels, is_training, idx):
            net = ops.convolution(inputs,
                                  in_channels[0],
                                  out_channels[0],
                                  1,
                                  out_channels[0],
                                  is_training=is_training,
                                  scope='res%s_conv1' % idx)

            net = ops.convolution(net,
                                  in_channels[1],
                                  out_channels[1],
                                  3,
                                  out_channels[1],
                                  is_training=is_training,
                                  scope='res%s_conv2' % idx)

            net = ops.convolution(net,
                                  in_channels[2],
                                  out_channels[2],
                                  1,
                                  out_channels[2],
                                  activation=None,
                                  is_training=is_training,
                                  scope='res%s_conv3' % idx)

            return tf.nn.relu(inputs + net, scope='res%s_relu' % idx)

        def res_conv_block(inputs, in_channel, out_channel, stride,
                           is_training, idx):
            skip = ops.convolution(inputs,
                                   in_channels[0],
                                   out_channels[2],
                                   1,
                                   out_channels[2],
                                   stride=stride,
                                   activation=None,
                                   is_training=is_training,
                                   scope='res%s_skip' % idx)

            net = ops.convolution(inputs,
                                  in_channels[0],
                                  out_channels[0],
                                  1,
                                  out_channels[0],
                                  is_training=is_training,
                                  scope='res%s_conv1' % idx)

            net = ops.convolution(net,
                                  in_channels[1],
                                  out_channels[1],
                                  3,
                                  out_channels[1],
                                  is_training=is_training,
                                  scope='res%s_conv2' % idx)

            net = ops.convolution(net,
                                  in_channels[2],
                                  out_channels[2],
                                  1,
                                  out_channels[2],
                                  stride=stride,
                                  activation=None,
                                  is_training=is_training,
                                  scope='res%s_conv3' % idx)

            return tf.nn.relu(skip + net, scope='res%s_relu' % idx)

        # pad inputs to size 224x224x3 - NOTE: may change to bilinear upsampling
        pad = int((self.image_size - self.height) / 2)
        inputs = tf.pad(inputs, [[0, 0], [pad, pad], [pad, pad], [0, 0]])

        # convolution with 7x7 kernel and stride 2 (new size: 112x112x64)
        self.network = ops.convolution(inputs,
                                       self.channels,
                                       64,
                                       7,
                                       64,
                                       stride=2,
                                       is_training=is_training,
                                       scope='conv1')

        # pooling with 3x3 kernel and stride 2 (new size: 56x56x64)
        self.network = ops.pooling(self.network, k_size=3, scope='pool1')

        # residual block 1
        stride = 1
        out_channels = [64, 64, 256]
        self.network = res_conv_block(self.network, [64, 64, 64], out_channels,
                                      stride, is_training, 1)
        self.network = res_block(self.network, [256, 64, 64], out_channels,
                                 is_training, 2)
        self.network = res_block(self.network, [256, 64, 64], out_channels,
                                 is_training, 3)

        # residual block 2
        stride = 2
        out_channels = [128, 128, 512]
        self.network = res_conv_block(self.network, [256, 128, 128],
                                      out_channels, stride, is_training, 4)
        self.network = res_block(self.network, [512, 128, 128], out_channels,
                                 is_training, 5)
        self.network = res_block(self.network, [512, 128, 128], out_channels,
                                 is_training, 6)
        self.network = res_block(self.network, [512, 128, 128], out_channels,
                                 is_training, 7)

        # residual block 3
        stride = 2
        out_channels = [256, 256, 1024]
        self.network = res_conv_block(self.network, [512, 256, 256],
                                      out_channels, stride, is_training, 8)
        self.network = res_block(self.network, [1024, 256, 256], out_channels,
                                 is_training, 9)
        self.network = res_block(self.network, [1024, 256, 256], out_channels,
                                 is_training, 10)
        self.network = res_block(self.network, [1024, 256, 256], out_channels,
                                 is_training, 11)
        self.network = res_block(self.network, [1024, 256, 256], out_channels,
                                 is_training, 12)
        self.network = res_block(self.network, [1024, 256, 256], out_channels,
                                 is_training, 13)

        # residual block 4
        stride = 2
        out_channels = [512, 512, 2048]
        self.network = res_conv_block(self.network, [1024, 512, 512],
                                      out_channels, stride, is_training, 14)
        self.network = res_block(self.network, [2048, 512, 512], out_channels,
                                 is_training, 15)
        self.network = res_block(self.network, [2048, 512, 512], out_channels,
                                 is_training, 16)

        # average pooling
        self.network = tf.nn.avg_pool(self.network,
                                      7,
                                      1,
                                      'SAME',
                                      scope='avg_pool')
        self.network = ops.flatten(self.network, scope='flatten')

        # fully connected
        self.network = ops.dense(self.network,
                                 2048,
                                 10,
                                 activation=None,
                                 is_training=is_training,
                                 scope='fc')

        self.loss = ops.loss(self.network, labels, scope='loss')
        self.accuracy = ops.accuracy(self.network, labels, scope='accuracy')

        if is_training:
            self.optimizer = ops.optimize(self.loss,
                                          self.learning_rate,
                                          scope='update')
    def build_model(self, inputs, labels, is_training):
        # pad inputs to size 224x224x3 - NOTE: may change to bilinear upsampling
        pad = int((self.image_size - self.height) / 2)
        inputs = tf.pad(inputs, [[0, 0], [pad, pad], [pad, pad], [0, 0]])

        # convolution with 3x3 kernel and stride 1 (new size: 224x224x64)
        self.network = ops.convolution(inputs,
                                       self.channels,
                                       64,
                                       3,
                                       64,
                                       is_training=is_training,
                                       scope='conv1')

        # convolution with 3x3 kernel and stride 1 (new size: 224x224x64)
        self.network = ops.convolution(self.network,
                                       64,
                                       64,
                                       3,
                                       64,
                                       is_training=is_training,
                                       scope='conv2')

        # pooling with 2x2 kernel and stride 2 (new size: 112x112x64)
        self.network = ops.pooling(self.network, scope='pool1')

        # convolution with 3x3 kernel and stride 1 (new size: 112x112x128)
        self.network = ops.convolution(self.network,
                                       64,
                                       128,
                                       3,
                                       128,
                                       is_training=is_training,
                                       scope='conv3')

        # convolution with 3x3 kernel and stride 1 (new size: 112x112x128)
        self.network = ops.convolution(self.network,
                                       128,
                                       128,
                                       3,
                                       128,
                                       is_training=is_training,
                                       scope='conv4')

        # pooling with 2x2 kernel and stride 2 (new size: 56x56x128)
        self.network = ops.pooling(self.network, scope='pool2')

        # convolution with 3x3 kernel and stride 1 (new size: 56x56x256)
        self.network = ops.convolution(self.network,
                                       128,
                                       256,
                                       3,
                                       256,
                                       is_training=is_training,
                                       scope='conv5')

        # 3 convolutions with 3x3 kernel and stride 1 (new size: 56x56x256)
        for idx in range(6, 9):
            self.network = ops.convolution(self.network,
                                           256,
                                           256,
                                           3,
                                           256,
                                           is_training=is_training,
                                           scope='conv' + str(idx))

        # pooling with 2x2 kernel and stride 2 (new size: 28x28x256)
        self.network = ops.pooling(self.network, scope='pool3')

        # convolution with 3x3 kernel and stride 1 (new size: 28x28x512)
        self.network = ops.convolution(self.network,
                                       256,
                                       512,
                                       3,
                                       512,
                                       is_training=is_training,
                                       scope='conv9')

        # 3 convolutions with 3x3 kernel and stride 1 (new size: 28x28x512)
        for idx in range(10, 13):
            self.network = ops.convolution(self.network,
                                           512,
                                           512,
                                           3,
                                           512,
                                           is_training=is_training,
                                           scope='conv' + str(idx))

        # pooling with 2x2 kernel and stride 2 (new size: 14x14x512)
        self.network = ops.pooling(self.network, scope='pool4')

        # 4 convolutions with 3x3 kernel and stride 1 (new size: 14x14x512)
        for idx in range(13, 17):
            self.network = ops.convolution(self.network,
                                           512,
                                           512,
                                           3,
                                           512,
                                           is_training=is_training,
                                           scope='conv' + str(idx))

        # pooling with 2x2 kernel and stride 2 (new size: 7x7x512)
        self.network = ops.pooling(self.network, scope='pool5')

        # flatten (new size: 25088)
        self.network = ops.flatten(self.network, scope='flatten')

        # fully connected layer (new size: 4096)
        self.network = ops.dense(self.network,
                                 25088,
                                 4096,
                                 dropout=True,
                                 dropout_rate=0.2,
                                 is_training=is_training,
                                 scope='fc1')

        # fully connected layer (new size: 1024) -- Original Paper Size: 4096 (for ImageNet)
        self.network = ops.dense(self.network,
                                 4096,
                                 1024,
                                 dropout=True,
                                 dropout_rate=0.2,
                                 is_training=is_training,
                                 scope='fc2')

        # output layer (new size: 10) -- Original Paper Size: 1000 (for ImageNet)
        self.network = ops.dense(self.network,
                                 1024,
                                 10,
                                 activation=None,
                                 is_training=is_training,
                                 scope='fc3')

        self.loss = ops.loss(self.network, labels, scope='loss')
        self.accuracy = ops.accuracy(self.network, labels, scope='accuracy')

        if is_training:
            self.optimizer = ops.optimize(self.loss,
                                          self.learning_rate,
                                          scope='update')