Esempio n. 1
0
                }
                hist_dict[f'{model}_{depth_level}_{opt}'] = {
                    'train_acc': history.history['acc'],
                    'val_acc': history.history['val_acc'],
                    'train_loss': history.history['loss'],
                    'val_loss': history.history['val_loss'],
                    'epoch': np.arange(1,
                                       len(history.history['acc']) + 1),
                    'model': model,
                    'depth': depth_level,
                    'optmizer': opt
                }

                print(f'{model}_{depth_level}_{opt} okay')
                dict_counter += 1
                reset_keras()

    # print accuracy results
    for k in acc_dict:
        print(acc_dict[k])

    # convert accuracy to dataframe
    df_acc = pd.DataFrame.from_dict(
        acc_dict,
        orient='index',
    )
    # change column names to facilitate legends:
    df_acc = df_acc.rename(columns={
        "opt": "optmizer parameters",
        "opt_mode": "optmizer"
    })
Esempio n. 2
0
def fine_tune(model_filename, model_name, depth_level, data_in, train_dir,
              valid_dir, test_dir, image_dir, model_dir, epochs, opt):
    """
    function that fine tunes a pre-trained cnn model using a small learning rate
    or trains a model from scracth
    :param model_filename: filename of hdf5 file (Keras model) to be loaded
    :param model_name: name of the original cnn model - necessary for preprocessing
        (currently only supports InceptionV3 and VGG19)
    :param depth_level: one of [shallow, intermediate, deep]
    :data_in: a tag to keep track of input data used
    :train_dir: training folder
    :valid_dir: validation folder
    :test_dir:  test folder
    :image_dir: image output location
    :model_dir: model output location
    :epochs: number of epochs to train
    :opt: string that maps to  keras optmizer to be used for training
    :return: 
        the entire trained model
        the test set accuracy
        the test set confusion matrix
    also saves a plot of the training loss/accuracy
    """
    ########
    # common trainig parameters:
    ########

    batch_size = 16
    loss = 'categorical_crossentropy'

    # save the number of classes:
    num_classes = len(os.listdir(train_dir))

    opts = {
        'SGD (1e-2)':
        optimizers.SGD(lr=0.01, momentum=0.0, clipvalue=5.),
        'SGD (1e-2) momentum 0.5':
        optimizers.SGD(lr=0.01, momentum=0.5, clipvalue=5.),
        'SGD (1e-2) momentum 0.9':
        optimizers.SGD(lr=0.01, momentum=0.9, clipvalue=5.),
        'SGD (1e-3)':
        optimizers.SGD(lr=0.001, momentum=0.0, clipvalue=5.),
        'SGD (1e-3) momentum 0.5':
        optimizers.SGD(lr=0.001, momentum=0.5, clipvalue=5.),
        'SGD (1e-3) momentum 0.9':
        optimizers.SGD(lr=0.001, momentum=0.9, clipvalue=5.),
        'RMSprop (1e-3) ':
        optimizers.RMSprop(lr=0.001, rho=0.9, epsilon=None, decay=0.0),
        'Adam (1e-2)':
        optimizers.Adam(lr=0.001,
                        beta_1=0.9,
                        beta_2=0.999,
                        epsilon=None,
                        decay=0.0,
                        amsgrad=False),
        'Adamax (2e-3)':
        optimizers.Adamax(lr=0.002,
                          beta_1=0.9,
                          beta_2=0.999,
                          epsilon=None,
                          decay=0.0)
    }
    opt = opts[opt]

    if model_filename:
        # user provided a filename of a model saved with weights
        tag = 'fine_tune'
        # load the model:
        model = load_model(os.path.join(model_dir, model_filename))
    else:
        print('starting new model with random weights')

        tag = 'randomly_initialized_weights'
        # we start a model from scratch
        base_model = load_part_model(model_name, depth_level, None)

        top_model = Sequential()
        top_model.add(
            GlobalAveragePooling2D(input_shape=base_model.output_shape[1:4]))
        top_model.add(Dense(512, activation='relu'))
        top_model.add(Dropout(rate=0.5))
        # the last layer depends on the number of classes
        top_model.add(Dense(num_classes, activation='softmax'))

        # set the entire model:
        # build the network
        model = Model(inputs=base_model.input,
                      outputs=top_model(base_model.output))

        # delete top model to release memory
        del top_model

    # make sure all layers are trainable
    for layer in model.layers:
        layer.trainable = True

    # set the generator
    datagen = ImageDataGenerator(
        preprocessing_function=model_preprocess(model_name))

    # do the same thing for both training and validation:
    train_generator = datagen.flow_from_directory(
        train_dir,
        target_size=model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=True)

    valid_generator = datagen.flow_from_directory(
        valid_dir,
        target_size=model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    if valid_generator.num_classes != train_generator.num_classes:
        print(
            'Warning! Different number of classes in training and validation')

    #################################
    # generators set
    #################################

    model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
    print(f'model needs {get_model_memory_usage(batch_size, model)} Gb')

    history = model.fit_generator(generator=train_generator,
                                  validation_data=valid_generator,
                                  shuffle=True,
                                  steps_per_epoch=len(train_generator),
                                  validation_steps=len(valid_generator),
                                  epochs=epochs)

    # plot and save the training history:
    plot_history(history, model_name, depth_level, data_in, tag, image_dir)

    # predict test values accuracy
    generator = datagen.flow_from_directory(test_dir,
                                            target_size=model.input_shape[1:3],
                                            batch_size=batch_size,
                                            class_mode='categorical',
                                            shuffle=False)

    pred = model.predict_generator(generator, verbose=0, steps=len(generator))

    # save results as dataframe
    df = pd.DataFrame(pred, columns=generator.class_indices.keys())
    df['file'] = generator.filenames
    df['true_label'] = df['file'].apply(os.path.dirname).apply(str.lower)
    df['pred_idx'] = np.argmax(df[generator.class_indices.keys()].to_numpy(),
                               axis=1)
    # save as the label (dictionary comprehension because generator.class_indices has the
    # key,values inverted to what we want
    df['pred_label'] = df['pred_idx'].map(
        {value: key
         for key, value in generator.class_indices.items()}).apply(str.lower)
    # save the maximum probability for easier reference:
    df['max_prob'] = np.amax(df[generator.class_indices.keys()].to_numpy(),
                             axis=1)

    y_true = df['true_label']
    y_pred = df['pred_label']

    # compute accuracy:
    acc = accuracy_score(y_true=y_true, y_pred=y_pred)

    # compute confusion matrix:
    cfm = confusion_matrix(y_true=y_true, y_pred=y_pred)

    cf_matrix(
        y_true,
        y_pred,
        image_dir,
        plot_name=f"conf_matrix_{data_in}_{model_name}_{depth_level}_{tag}.png",
        dpi=1000)

    # clear memory
    reset_keras()

    return acc, cfm, history
def fine_tune(model_filename, model_name, data_in, train_dir, valid_dir,
              test_dir, image_dir, model_dir, epochs):
    """
    function that fine tunes a pre-trained cnn model using a small learning rate
    :param model_filename: filename of hdf5 file (Keras model) to be loaded
    :param model_name: name of the original cnn model - necessary for preprocessing
        (currently only supports InceptionV3 and VGG19)
    :data_in: a tag to keep track of input data used
    :train_dir: training folder
    :valid_dir: validation folder
    :test_dir:  test folder
    :image_dir: image output location
    :model_dir: model output location
    :epochs: number of epochs to train
    :return: 
        the entire trained model
        the test set accuracy
        the test set confusion matrix
    also saves a plot of the training loss/accuracy
    """
    ########
    # common trainig parameters:
    ########
    batch_size = 32
    lrate = 5 * 1e-5
    loss = 'categorical_crossentropy'
    opt = SGD(lr=lrate, momentum=0.0, clipvalue=5.)

    # load the model:
    model = load_model(os.path.join(model_dir, model_filename))

    # make sure all layers are trainable
    for layer in model.layers:
        layer.trainable = True

    # set the generator
    datagen = ImageDataGenerator(
        preprocessing_function=model_preprocess(model_name))

    # do the same thing for both training and validation:
    train_generator = datagen.flow_from_directory(
        train_dir,
        target_size=model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=True)

    valid_generator = datagen.flow_from_directory(
        valid_dir,
        target_size=model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    if valid_generator.num_classes != train_generator.num_classes:
        print(
            'Warning! Different number of classes in training and validation')

    #################################
    # generators set
    #################################

    model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
    print(f'model needs {get_model_memory_usage(batch_size, model)} Gb')

    history = model.fit_generator(generator=train_generator,
                                  validation_data=valid_generator,
                                  shuffle=True,
                                  epochs=epochs)

    # plot and save the training history:
    plot_history(history, model_name, depth_level, data_in, 'fine_tune',
                 image_dir)

    # predict test values accuracy
    generator = datagen.flow_from_directory(test_dir,
                                            target_size=model.input_shape[1:3],
                                            batch_size=batch_size,
                                            class_mode='categorical',
                                            shuffle=False)

    pred = model.predict_generator(generator, verbose=0, steps=len(generator))

    # save results as dataframe
    df = pd.DataFrame(pred, columns=generator.class_indices.keys())
    df['file'] = generator.filenames
    df['true_label'] = df['file'].apply(os.path.dirname).apply(str.lower)
    df['pred_idx'] = np.argmax(df[generator.class_indices.keys()].to_numpy(),
                               axis=1)
    # save as the label (dictionary comprehension because generator.class_indices has the
    # key,values inverted to what we want
    df['pred_label'] = df['pred_idx'].map(
        {value: key
         for key, value in generator.class_indices.items()}).apply(str.lower)
    # save the maximum probability for easier reference:
    df['max_prob'] = np.amax(df[generator.class_indices.keys()].to_numpy(),
                             axis=1)

    y_true = df['true_label']
    y_pred = df['pred_label']

    # compute accuracy:
    acc = accuracy_score(y_true=y_true, y_pred=y_pred)

    # compute confusion matrix:
    cfm = confusion_matrix(y_true=y_true, y_pred=y_pred)

    cf_matrix(
        y_true,
        y_pred,
        image_dir,
        plot_name=
        f"conf_matrix_{data_in}_{model_name}_{depth_level}_fine_tune.png",
        dpi=200)

    # clear memory
    reset_keras()

    return acc, cfm
Esempio n. 4
0
def train_frozen(model_name, weights, data_in, train_dir, valid_dir, test_dir,
                 image_dir, model_dir, epochs, opt):
    """
    function that freezes a cnn model to extract features and train a small
    classification NN on top of the extracted features. 
    :param model_name: name of the cnn model to be loaded (currently only 
                       supports InceptionV3 and VGG19)
    :param 
    :weights: one of ['imagenet', None]
    :data_in: a tag to keep track of input data used
    :train_dir: training folder
    :valid_dir: validation folder
    :test_dir:  test folder
    :image_dir: image output location
    :model_dir: model output location
    :epochs: number of epochs to train
    :opt:  string that maps to keras optimizer to be used for training
    :return: 
        the test set accuracy
        the test set [y_true, y_pred]
        the training history
    saves the model 
    """
    ########
    # common trainig parameters:
    ########
    batch_size = BATCH_SIZE

    loss = 'categorical_crossentropy'

    opts = {
        'SGD (1e-2)':
        optimizers.SGD(lr=0.01, momentum=0.0, clipvalue=5.),
        'SGD (1e-2) momentum 0.5':
        optimizers.SGD(lr=0.01, momentum=0.5, clipvalue=5.),
        'SGD (1e-2) momentum 0.9':
        optimizers.SGD(lr=0.01, momentum=0.9, clipvalue=5.),
        'SGD (1e-3)':
        optimizers.SGD(lr=0.001, momentum=0.0, clipvalue=5.),
        'SGD (1e-3) momentum 0.5':
        optimizers.SGD(lr=0.001, momentum=0.5, clipvalue=5.),
        'SGD (1e-3) momentum 0.9':
        optimizers.SGD(lr=0.001, momentum=0.9, clipvalue=5.),
        'RMSprop (1e-3) ':
        optimizers.RMSprop(lr=0.001, rho=0.9, epsilon=None, decay=0.0),
        'Adam (1e-3)':
        optimizers.Adam(lr=0.001,
                        beta_1=0.9,
                        beta_2=0.999,
                        epsilon=None,
                        decay=0.0,
                        amsgrad=False),
        'Adamax (2e-3)':
        optimizers.Adamax(lr=0.002,
                          beta_1=0.9,
                          beta_2=0.999,
                          epsilon=None,
                          decay=0.0)
    }
    opt = opts[opt]

    # load the base model:
    base_model = model_app(model_name, weights)
    # freeze layers (layers will not be updated during the first training process)
    for layer in base_model.layers:
        layer.trainable = False

    # save the number of classes:
    num_classes = len(os.listdir(train_dir))

    # set the generator
    datagen = ImageDataGenerator(
        preprocessing_function=model_preprocess(model_name),
        zoom_range=0.05,
        channel_shift_range=0.5,
        rotation_range=5,
        horizontal_flip=True,
        vertical_flip=True,
    )

    # do the same thing for both training and validation:
    train_generator = datagen.flow_from_directory(
        train_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=True)

    valid_generator = datagen.flow_from_directory(
        valid_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    if valid_generator.num_classes != train_generator.num_classes:
        print(
            'Warning! Different number of classes in training and validation')

    #################################
    # generators set
    #################################

    # create the top model:
    top_model = Sequential()
    top_model.add(
        GlobalAveragePooling2D(input_shape=base_model.output_shape[1:4]))
    top_model.add(Dense(512, activation='relu'))
    top_model.add(Dropout(rate=0.5))
    # the last layer depends on the number of classes
    top_model.add(Dense(num_classes, activation='softmax'))

    # set the entire model:
    # build the network
    model = Model(inputs=base_model.input,
                  outputs=top_model(base_model.output))
    model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
    #print(model.summary())
    print(f'model needs {get_model_memory_usage(batch_size, model)} Gb')

    history = model.fit_generator(generator=train_generator,
                                  validation_data=valid_generator,
                                  shuffle=True,
                                  steps_per_epoch=len(train_generator),
                                  validation_steps=len(valid_generator),
                                  epochs=epochs)

    # save model:
    model.save(os.path.join(model_dir, f"{model_name}_{data_in}_frozen.hdf5"))

    # predict test values accuracy
    generator = datagen.flow_from_directory(
        test_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    pred = model.predict_generator(generator, verbose=0, steps=len(generator))

    # save results as dataframe
    df = pd.DataFrame(pred, columns=generator.class_indices.keys())
    df['file'] = generator.filenames
    df['true_label'] = df['file'].apply(os.path.dirname).apply(str.lower)
    df['pred_idx'] = np.argmax(df[generator.class_indices.keys()].to_numpy(),
                               axis=1)
    # save as the label (dictionary comprehension because generator.class_indices has the
    # key,values inverted to what we want
    df['pred_label'] = df['pred_idx'].map(
        {value: key
         for key, value in generator.class_indices.items()}).apply(str.lower)
    # save the maximum probability for easier reference:
    df['max_prob'] = np.amax(df[generator.class_indices.keys()].to_numpy(),
                             axis=1)

    y_true = df['true_label']
    y_pred = df['pred_label']

    # compute accuracy:
    acc = accuracy_score(y_true=y_true, y_pred=y_pred)

    # clear memory
    reset_keras()

    return acc, [y_true, y_pred], history
def train_frozen(model_name, depth_level, weights, data_in, train_dir,
                 valid_dir, test_dir, image_dir, model_dir, epochs):
    """
    function that freezes a cnn model to extract features and train a small
    classification NN on top of the extracted features. 
    :param model_name: name of the cnn model to be loaded (currently only 
                       supports InceptionV3 and VGG19)
    :param depth_level: one of [shallow, intermediate, deep]
    :weights: one of ['imagenet', None]
    :data_in: a tag to keep track of input data used
    :train_dir: training folder
    :valid_dir: validation folder
    :test_dir:  test folder
    :image_dir: image output location
    :model_dir: model output location
    :epochs: number of epochs to train
    :return: 
        the test set accuracy
        the test set confusion matrix
    also saves a plot of the training loss/accuracy
    saves the model 
    saves a plot of the confusion matrix
    """
    ########
    # common trainig parameters:
    ########
    batch_size = 32
    lrate = 1e-3
    loss = 'categorical_crossentropy'
    opt = SGD(lr=lrate, momentum=0.0, clipvalue=5.)

    # load the base model:
    base_model = load_part_model(model_name, depth_level, weights)
    # freeze layers (layers will not be updated during the first training process)
    for layer in base_model.layers:
        layer.trainable = False

    # save the number of classes:
    num_classes = len(os.listdir(train_dir))

    # set the generator
    datagen = ImageDataGenerator(
        preprocessing_function=model_preprocess(model_name))

    # do the same thing for both training and validation:
    train_generator = datagen.flow_from_directory(
        train_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=True)

    valid_generator = datagen.flow_from_directory(
        valid_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    if valid_generator.num_classes != train_generator.num_classes:
        print(
            'Warning! Different number of classes in training and validation')

    #################################
    # generators set
    #################################

    # create the top model:
    top_model = Sequential()
    top_model.add(
        GlobalAveragePooling2D(input_shape=base_model.output_shape[1:4]))
    top_model.add(Dense(512, activation='relu'))
    top_model.add(Dropout(rate=0.5))
    # the last layer depends on the number of classes
    top_model.add(Dense(num_classes, activation='softmax'))

    # set the entire model:
    # build the network
    model = Model(inputs=base_model.input,
                  outputs=top_model(base_model.output))
    model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
    #print(model.summary())
    print(f'model needs {get_model_memory_usage(batch_size, model)} Gb')

    history = model.fit_generator(generator=train_generator,
                                  validation_data=valid_generator,
                                  shuffle=True,
                                  epochs=epochs)

    # save model:
    model.save(
        os.path.join(model_dir,
                     f"{model_name}_{depth_level}_{data_in}_frozen.hdf5"))

    # plot and save the training history:
    plot_history(history, model_name, depth_level, data_in, 'feat_extr',
                 image_dir)

    # predict test values accuracy
    generator = datagen.flow_from_directory(
        test_dir,
        target_size=base_model.input_shape[1:3],
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    pred = model.predict_generator(generator, verbose=0, steps=len(generator))

    # save results as dataframe
    df = pd.DataFrame(pred, columns=generator.class_indices.keys())
    df['file'] = generator.filenames
    df['true_label'] = df['file'].apply(os.path.dirname).apply(str.lower)
    df['pred_idx'] = np.argmax(df[generator.class_indices.keys()].to_numpy(),
                               axis=1)
    # save as the label (dictionary comprehension because generator.class_indices has the
    # key,values inverted to what we want
    df['pred_label'] = df['pred_idx'].map(
        {value: key
         for key, value in generator.class_indices.items()}).apply(str.lower)
    # save the maximum probability for easier reference:
    df['max_prob'] = np.amax(df[generator.class_indices.keys()].to_numpy(),
                             axis=1)

    y_true = df['true_label']
    y_pred = df['pred_label']

    # compute accuracy:
    acc = accuracy_score(y_true=y_true, y_pred=y_pred)

    # compute confusion matrix:
    cfm = confusion_matrix(y_true=y_true, y_pred=y_pred)

    # plot confusion matrix:
    cf_matrix(
        y_true,
        y_pred,
        image_dir,
        plot_name=
        f"conf_matrix_{data_in}_{model_name}_{depth_level}_feat_extr.png",
        dpi=200)

    # clear memory
    reset_keras()

    return acc, cfm