def score_model(self, model, predictions):
        val_with_targ, pred_with_targ, val_no_targ, pred_no_targ, val_following, pred_following = predictions

        print('Scoring Model...')
        # Scores for while the quad is following behind the target. 
        true_pos1, false_pos1, false_neg1, iou1 = scoring_utils.score_run_iou(val_following, pred_following)
        total1 = 543.
        self.data['Scores']['Following Target'] = {}
        self.data['Scores']['Following Target']['True Positives'] = true_pos1
        self.data['Scores']['Following Target']['False Positives'] = false_pos1
        self.data['Scores']['Following Target']['False Negatives'] = false_neg1
        self.data['Scores']['Following Target']['IOU'] = iou1
        self.data['Scores']['Following Target']['Percent False Positives'] = false_pos1/total1
        self.data['Scores']['Following Target']['Percent False Negatives'] = false_neg1/total1
        self.data['Scores']['Following Target']['Percent True Positives'] = true_pos1/total1

        # Scores for images while the quad is on patrol and the target is not visable
        true_pos2, false_pos2, false_neg2, iou2 = scoring_utils.score_run_iou(val_no_targ, pred_no_targ)
        total2 = 271.
        self.data['Scores']['No Target'] = {}
        self.data['Scores']['No Target']['True Positives'] = true_pos2
        self.data['Scores']['No Target']['False Positives'] = false_pos2
        self.data['Scores']['No Target']['False Negatives'] = false_neg2
        self.data['Scores']['No Target']['IOU'] = iou2
        self.data['Scores']['No Target']['Percent False Positives'] = false_pos2/total2
        self.data['Scores']['No Target']['Percent False Negatives'] = false_neg2/total2
        self.data['Scores']['No Target']['Percent True Positives'] = true_pos2/total2

        # This score measures how well the neural network can detect the target from far away
        true_pos3, false_pos3, false_neg3, iou3 = scoring_utils.score_run_iou(val_with_targ, pred_with_targ)
        total3 = 323.
        self.data['Scores']['Far from Target'] = {}
        self.data['Scores']['Far from Target']['True Positives'] = true_pos3
        self.data['Scores']['Far from Target']['False Positives'] = false_pos3
        self.data['Scores']['Far from Target']['False Negatives'] = false_neg3
        self.data['Scores']['Far from Target']['IOU'] = iou3
        self.data['Scores']['Far from Target']['Percent False Positives'] = false_pos3/total3
        self.data['Scores']['Far from Target']['Percent False Negatives'] = false_neg3/total3
        self.data['Scores']['Far from Target']['Percent True Positives'] = true_pos3/total3

        # Sum all the true positives, etc from the three datasets to get a weight for the score
        true_pos = true_pos1 + true_pos2 + true_pos3
        false_neg = false_neg1 + false_neg2 + false_neg3
        false_pos = false_pos1 + false_pos2 + false_pos3
        total = total1 + total2 + total3
        self.data['Scores']['Overall'] = {}
        self.data['Scores']['Overall']['True Positives'] = true_pos
        self.data['Scores']['Overall']['False Positives'] = false_pos
        self.data['Scores']['Overall']['False Negatives'] = false_neg
        self.data['Scores']['Overall']['Percent False Positives'] = false_pos/total
        self.data['Scores']['Overall']['Percent False Negatives'] = false_neg/total
        self.data['Scores']['Overall']['Percent True Positives'] = true_pos/total

        weight = true_pos/(true_pos+false_neg+false_pos)
        self.data['Scores']['Overall']['Weight'] = weight

        # The IoU for the dataset that never includes the hero is excluded from grading
        final_IoU = (iou1 + iou3)/2
        self.data['Scores']['Overall']['IOU'] = final_IoU

        # And the final grade score is 
        final_score = final_IoU * weight
        self.data['Scores']['Overall']['Score'] = final_score

        print('Model Results for {}:'.format(self.data['File Name']))
        print('    Weight: {}'.format(weight))
        print('    Final IoU: {}'.format(final_IoU))
        print('    Final Score: {}'.format(final_score))
    run_num = 'run_' + run

    val_with_targ, pred_with_targ = model_tools.write_predictions_grade_set(
        model, run_num, 'patrol_with_targ', 'evaluation')

    val_no_targ, pred_no_targ = model_tools.write_predictions_grade_set(
        model, run_num, 'patrol_non_targ', 'evaluation')

    val_following, pred_following = model_tools.write_predictions_grade_set(
        model, run_num, 'following_images', 'evaluation')

    print(
        "--------------------------------------------------------------------------------"
    )
    print("Scores for while the quad is following behind the target")
    true_pos1, false_pos1, false_neg1, iou1 = scoring_utils.score_run_iou(
        val_following, pred_following)
    print("true_pos1: " + str(true_pos1))
    print("false_pos1: " + str(false_pos1))
    print("false_neg1: " + str(false_neg1))
    print("iou1: " + str(iou1))

    print(
        "--------------------------------------------------------------------------------"
    )
    print(
        "Scores for while the quad is on patrol and the target is not visable")
    true_pos2, false_pos2, false_neg2, iou2 = scoring_utils.score_run_iou(
        val_no_targ, pred_no_targ)
    print("true_pos2: " + str(true_pos2))
    print("false_pos2: " + str(false_pos2))
    print("false_neg2: " + str(false_neg2))
def train_net(x):
    """Trains the network with the suggested hyper-parameters passed in x
    argument. Returns -final_score as a result to let ski-opt library find the
    desired minimum, corresponding to the maximum final_score"""

    learning_rate = x[0]  # 0.001
    batch_size = x[1]  # 32
    num_epochs = x[2]  # 12
    layers_num = x[3]  #2
    conv_layers_num = x[4]  #1
    external_features = x[5]  #128
    internal_features = x[6]  #16
    conv_features = x[7]  #16

    print()
    print("learning_rate", learning_rate)
    print("batch_size", batch_size)
    print("num_epochs", num_epochs)
    print("layers_num", layers_num)
    print("conv_layers_num", conv_layers_num)
    print("external_features", external_features)
    print("internal_features", internal_features)
    print("conv_features", conv_features)

    image_hw = 160
    image_shape = (image_hw, image_hw, 3)
    inputs = layers.Input(image_shape)
    num_classes = 3

    # Call fcn_model()
    output_layer = fcn_model(inputs, num_classes, layers_num,
                             external_features, internal_features,
                             conv_layers_num, conv_features)

    # Define the Keras model and compile it for training
    model = models.Model(inputs=inputs, outputs=output_layer)

    model.compile(optimizer=keras.optimizers.Adam(learning_rate),
                  loss='categorical_crossentropy')

    # Data iterators for loading the training and validation data
    train_iter = data_iterator.BatchIteratorSimple(batch_size=batch_size,
                                                   data_folder=os.path.join(
                                                       '..', 'data',
                                                       'train_combined'),
                                                   image_shape=image_shape,
                                                   shift_aug=True)

    val_iter = data_iterator.BatchIteratorSimple(batch_size=batch_size,
                                                 data_folder=os.path.join(
                                                     '..', 'data',
                                                     'validation'),
                                                 image_shape=image_shape)

    model.fit_generator(
        train_iter,
        steps_per_epoch=steps_per_epoch,  # the number of batches per epoch,
        epochs=num_epochs,  # the number of epochs to train for,
        validation_data=val_iter,  # validation iterator
        validation_steps=
        validation_steps,  # the number of batches to validate on
        workers=workers)

    run_num = 'run_1'

    val_with_targ, pred_with_targ = model_tools.write_predictions_grade_set(
        model, run_num, 'patrol_with_targ', 'sample_evaluation_data')

    val_no_targ, pred_no_targ = model_tools.write_predictions_grade_set(
        model, run_num, 'patrol_non_targ', 'sample_evaluation_data')

    val_following, pred_following = model_tools.write_predictions_grade_set(
        model, run_num, 'following_images', 'sample_evaluation_data')

    # Scores for while the quad is following behind the target.
    true_pos1, false_pos1, false_neg1, iou1 = scoring_utils.score_run_iou(
        val_following, pred_following)

    # Scores for images while the quad is on patrol and the target is not
    # visible
    true_pos2, false_pos2, false_neg2, iou2 = scoring_utils.score_run_iou(
        val_no_targ, pred_no_targ)

    # This score measures how well the neural network can detect the target
    # from far away
    true_pos3, false_pos3, false_neg3, iou3 = scoring_utils.score_run_iou(
        val_with_targ, pred_with_targ)

    # Sum all the true positives, etc from the three datasets to get a weight
    # for the score
    true_pos = true_pos1 + true_pos2 + true_pos3
    false_pos = false_pos1 + false_pos2 + false_pos3
    false_neg = false_neg1 + false_neg2 + false_neg3

    weight = true_pos / (true_pos + false_neg + false_pos)

    # The IoU for the dataset that never includes the hero is excluded from
    # grading
    final_IoU = (iou1 + iou3) / 2

    # And the final grade score is
    final_score = final_IoU * weight

    weight_file_name = 'model_weights_' + str(final_score)
    model_tools.save_network(model, weight_file_name)
    print("Saved", weight_file_name)

    print("final_score", final_score)
    print()

    return -final_score