def finetune(self, train_dataset, val_dataset=None, nclasses=2, top_layer_epochs=1, unfreeze_proportions=[0.5], all_layer_epochs=5, class_weight=None, optimizer_top='rmsprop', optimizer_full='sgd', callbacks=None, num_workers=8, load_weights_path=None, save_weights_path=None):
    ' Finetunes the Imagenet model iteratively on a smaller set of images with (potentially) a smaller set of classes.\n            First finetunes last layer then freezes bottom N layers and retrains the rest\n                :param train_dataset: (X, y) pair of tf.constant tensors for training \n                :param val_dataset: (X, y) pair of tf.constant tensors for validation, optional \n                :param nclasses: number of classes \n                :param top_layer_epochs: how many epochs for which to finetune classification head (happens first)\n                :param unfreeze_proportions: list of proportions representing how much of the base ImageNet model one wants to\n                    unfreeze (later layers unfrozen) for another round of finetuning\n                :param all_layer_epochs: how many epochs for which to finetune entire model (happens second)\n                :param class_weight: class weights (used for both training steps)\n                :param optimizer_top: optimizer to use for training of classification head\n                :param optimizer_full: optimizer to use for training full classification model\n                    * suggest to use lower learning rate / more conservative optimizer for this step to \n                      prevent catastrophic forgetting \n                :param callbacks: optional list of callbacks to use for each round of finetuning\n                :param num_workers: number of workers to use for multiprocess data loading\n                :param load_weights_path: optional filepath from which to try to load weights\n                :param save_weights_path: optional filepath to which to store weights\n        '
    finetune_model = self._load_finetune_model(nclasses=nclasses, weights_path=load_weights_path)
    fitting_histories = []
    for layer in self.model.layers:
        layer.trainable = False
    finetune_model.compile(optimizer=optimizer_top, loss='categorical_crossentropy')
    fitting_histories.append(finetune_model.fit(train_dataset, validation_data=val_dataset, epochs=top_layer_epochs, class_weight=class_weight, shuffle=True, use_multiprocessing=True, workers=num_workers, callbacks=callbacks))
    finetune_model.compile(optimizer=optimizer_full, loss='categorical_crossentropy')
    for p in unfreeze_proportions:
        freeze_count = int((len(self.model.layers) * p))
        for layer in finetune_model.layers[:freeze_count]:
            layer.trainable = False
        for layer in finetune_model.layers[freeze_count:]:
            layer.trainable = True
        fitting_histories.append(finetune_model.fit(train_dataset, validation_data=val_dataset, epochs=all_layer_epochs, class_weight=class_weight, shuffle=True, use_multiprocessing=True, workers=num_workers, callbacks=callbacks))
    if (save_weights_path is not None):
        finetune_model.save_weights(save_weights_path)
    return fitting_histories
Finetunes the Imagenet model iteratively on a smaller set of images with (potentially) a smaller set of classes.
First finetunes last layer then freezes bottom N layers and retrains the rest
    :param train_dataset: (X, y) pair of tf.constant tensors for training 
    :param val_dataset: (X, y) pair of tf.constant tensors for validation, optional 
    :param nclasses: number of classes 
    :param top_layer_epochs: how many epochs for which to finetune classification head (happens first)
    :param unfreeze_proportions: list of proportions representing how much of the base ImageNet model one wants to
        unfreeze (later layers unfrozen) for another round of finetuning
    :param all_layer_epochs: how many epochs for which to finetune entire model (happens second)
    :param class_weight: class weights (used for both training steps)
    :param optimizer_top: optimizer to use for training of classification head
    :param optimizer_full: optimizer to use for training full classification model
        * suggest to use lower learning rate / more conservative optimizer for this step to 
          prevent catastrophic forgetting 
    :param callbacks: optional list of callbacks to use for each round of finetuning
    :param num_workers: number of workers to use for multiprocess data loading
    :param load_weights_path: optional filepath from which to try to load weights
    :param save_weights_path: optional filepath to which to store weights
def finetune(self, train_dataset, val_dataset=None, nclasses=2, top_layer_epochs=1, unfreeze_proportions=[0.5], all_layer_epochs=5, class_weight=None, optimizer_top='rmsprop', optimizer_full='sgd', callbacks=None, num_workers=8, load_weights_path=None, save_weights_path=None):
    ' Finetunes the Imagenet model iteratively on a smaller set of images with (potentially) a smaller set of classes.\n            First finetunes last layer then freezes bottom N layers and retrains the rest\n                :param train_dataset: (X, y) pair of tf.constant tensors for training \n                :param val_dataset: (X, y) pair of tf.constant tensors for validation, optional \n                :param nclasses: number of classes \n                :param top_layer_epochs: how many epochs for which to finetune classification head (happens first)\n                :param unfreeze_proportions: list of proportions representing how much of the base ImageNet model one wants to\n                    unfreeze (later layers unfrozen) for another round of finetuning\n                :param all_layer_epochs: how many epochs for which to finetune entire model (happens second)\n                :param class_weight: class weights (used for both training steps)\n                :param optimizer_top: optimizer to use for training of classification head\n                :param optimizer_full: optimizer to use for training full classification model\n                    * suggest to use lower learning rate / more conservative optimizer for this step to \n                      prevent catastrophic forgetting \n                :param callbacks: optional list of callbacks to use for each round of finetuning\n                :param num_workers: number of workers to use for multiprocess data loading\n                :param load_weights_path: optional filepath from which to try to load weights\n                :param save_weights_path: optional filepath to which to store weights\n        '
    finetune_model = self._load_finetune_model(nclasses=nclasses, weights_path=load_weights_path)
    fitting_histories = []
    for layer in self.model.layers:
        layer.trainable = False
    finetune_model.compile(optimizer=optimizer_top, loss='categorical_crossentropy')
    fitting_histories.append(finetune_model.fit(train_dataset, validation_data=val_dataset, epochs=top_layer_epochs, class_weight=class_weight, shuffle=True, use_multiprocessing=True, workers=num_workers, callbacks=callbacks))
    finetune_model.compile(optimizer=optimizer_full, loss='categorical_crossentropy')
    for p in unfreeze_proportions:
        freeze_count = int((len(self.model.layers) * p))
        for layer in finetune_model.layers[:freeze_count]:
            layer.trainable = False
        for layer in finetune_model.layers[freeze_count:]:
            layer.trainable = True
        fitting_histories.append(finetune_model.fit(train_dataset, validation_data=val_dataset, epochs=all_layer_epochs, class_weight=class_weight, shuffle=True, use_multiprocessing=True, workers=num_workers, callbacks=callbacks))
    if (save_weights_path is not None):
        finetune_model.save_weights(save_weights_path)
    return fitting_histories