Anonymous Anonymous - 9 days ago
92 0

No description

Python

test_tesnorflow_12_2

"""
Example TensorFlow script for finetuning a VGG model on your own data.
Uses tf.contrib.data module which is in release v1.2
Based on PyTorch example from Justin Johnson
(https://gist.github.com/jcjohnson/6e41e8512c17eae5da50aebef3378a4c)
Required packages: tensorflow (v1.2)
Download the weights trained on ImageNet for VGG:
```
wget http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz
tar -xvf vgg_16_2016_08_28.tar.gz
rm vgg_16_2016_08_28.tar.gz
```
For this example we will use a tiny dataset of images from the COCO dataset.
We have chosen eight types of animals (bear, bird, cat, dog, giraffe, horse,
sheep, and zebra); for each of these categories we have selected 100 training
images and 25 validation images from the COCO dataset. You can download and
unpack the data (176 MB) by running:
```
wget cs231n.stanford.edu/coco-animals.zip
unzip coco-animals.zip
rm coco-animals.zip
```
The training data is stored on disk; each category has its own folder on disk
and the images for that category are stored as .jpg files in the category folder.
In other words, the directory structure looks something like this:
coco-animals/
  train/
    bear/
      COCO_train2014_000000005785.jpg
      COCO_train2014_000000015870.jpg
      [...]
    bird/
    cat/
    dog/
    giraffe/
    horse/
    sheep/
    zebra/
  val/
    bear/
    bird/
    cat/
    dog/
    giraffe/
    horse/
    sheep/
    zebra/
"""
import matplotlib
matplotlib.use('Agg')
import itertools
import argparse
import os
from vgg_tensorflow import VGG_FACE_16_layer as MyNet
import tensorflow as tf
import random
import matplotlib.pyplot as plt
import numpy as np
import tensorflow.contrib.slim.nets as nets
import tensorflow.contrib.slim as slim

parser = argparse.ArgumentParser()

parser.add_argument('--train_dir', default='ages_split/train/')
parser.add_argument('--val_dir', default='ages_split/val/')
parser.add_argument('--use_faces', default=False,type = str)#False for vgg16 old
parser.add_argument('--use_inceptionres', default=True,type = str)#False for vgg16 old
parser.add_argument('--load_model', default=None,type = str)
parser.add_argument('--max_train', default = 100,type = int)
parser.add_argument('--max_val', default=10,type = int)
parser.add_argument('--model_path', default='inception_v1.ckpt', type=str)
parser.add_argument('--batch_size', default=32, type=int)
parser.add_argument('--num_workers', default=4, type=int)
parser.add_argument('--num_epochs1', default=0, type=int)
parser.add_argument('--num_epochs2', default=10, type=int)
parser.add_argument('--learning_rate1', default=1e-3, type=float)
parser.add_argument('--learning_rate2', default=1e-3, type=float)
parser.add_argument('--dropout_keep_prob', default=0.5, type=float)
parser.add_argument('--weight_decay', default=5e-4, type=float)



def list_images(directory,max_img):
    """
    Get all the images and labels in directory/label/*.jpg
    """
    labels = sorted(os.listdir(directory))
    files_and_labels = []
    for label in labels:
        for f in os.listdir(os.path.join(directory, label)):
            files_and_labels.append((str(os.path.join(directory, label, f)), label))

    filenames, labels = zip(*files_and_labels)
    filenames = list(filenames)
    labels = list(labels)
    unique_labels = list(set(labels))
    print(unique_labels)

    unique_labels = ['(0, 2)','(4, 6)', '(8, 12)', '(15, 20)', '(25, 32)', '(38, 43)',  '(48, 53)', '(60, 100)']
    print(unique_labels)
    label_to_int = {}
    str_labels = list(unique_labels)
    for i, label in enumerate(unique_labels):
        label_to_int[label] = i

    labels = [label_to_int[l] for l in labels]

    if len(filenames) > max_img :
        comb = list(zip(filenames, labels))
        random.shuffle(comb)
        filenames_shuf ,labels_shuf = zip(*comb)
        return filenames_shuf[:max_img], labels_shuf[:max_img], str_labels
    else:
        return filenames, labels, str_labels


def check_accuracy(sess, correct_prediction, is_training, dataset_init_op):
    """
    Check the accuracy of the model on either train or val (depending on dataset_init_op).
    """
    # Initialize the correct dataset
    sess.run(dataset_init_op)
    num_correct, num_samples = 0, 0
    while True:
        try:
            correct_pred = sess.run(correct_prediction, {is_training: False})
            num_correct += correct_pred.sum()
            num_samples += correct_pred.shape[0]
        except tf.errors.OutOfRangeError:
            break

    # Return the fraction of datapoints that were correctly classified
    acc = float(num_correct) / num_samples
    return acc

def comp_confusion_matrix(sess, confusion_matrix, is_training, dataset_init_op):
    """
    Check the accuracy of the model on either train or val (depending on dataset_init_op).
    """
    # Initialize the correct dataset
    sess.run(dataset_init_op)
    matrix = None
    while True:
        try:
            matrix_i = sess.run(confusion_matrix, {is_training: False})
            if matrix is None:
                matrix = matrix_i
            else:
                matrix += matrix_i
        except tf.errors.OutOfRangeError:
            break

    # Return the fraction of datapoints that were correctly classified
    return matrix


def plot_confusion_matrix(cm, classes,save_name,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    #print(cm)
    #argsorted = np.argsort(old_to_new_map)
    #cm = cm[:,argsorted]
    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.savefig(save_name)
    plt.clf()
def main(args):
    # Get the list of filenames and corresponding list of labels for training et validation
    train_filenames, train_labels, train_str= list_images(args.train_dir,args.max_train)
    val_filenames, val_labels, val_str = list_images(args.val_dir,args.max_val)
    num_classes = len(set(train_labels))
    print("Number of training data: " , len(train_filenames))
    print("Number of validation data: ",len(val_filenames))
    print("Number of classes:", num_classes)
    # --------------------------------------------------------------------------
    # In TensorFlow, you first want to define the computation graph with all the
    # necessary operations: loss, training op, accuracy...
    # Any tensor created in the `graph.as_default()` scope will be part of `graph`
    graph = tf.Graph()
    with graph.as_default():
        # Standard preprocessing for VGG on ImageNet taken from here:
        # https://github.com/tensorflow/models/blob/master/slim/preprocessing/vgg_preprocessing.py
        # Also see the VGG paper for more details: https://arxiv.org/pdf/1409.1556.pdf

        # Preprocessing (for both training and validation):
        # (1) Decode the image from jpg format
        # (2) Resize the image so its smaller side is 256 pixels long
        def _parse_function(filename, label):
            image_string = tf.read_file(filename)
            image_decoded = tf.image.decode_jpeg(image_string, channels=3)          # (1)
            image = tf.cast(image_decoded, tf.float32)

            smallest_side = 256.0
            height, width = tf.shape(image)[0], tf.shape(image)[1]
            height = tf.to_float(height)
            width = tf.to_float(width)

            scale = tf.cond(tf.greater(height, width),
                            lambda: smallest_side / width,
                            lambda: smallest_side / height)
            new_height = tf.to_int32(height * scale)
            new_width = tf.to_int32(width * scale)

            resized_image = tf.image.resize_images(image, [new_height, new_width])  # (2)
            return resized_image, label

        # Preprocessing (for training)
        # (3) Take a random 224x224 crop to the scaled image
        # (4) Horizontally flip the image with probability 1/2
        # (5) Substract the per color mean `VGG_MEAN`
        # Note: we don't normalize the data here, as VGG was trained without normalization
        def training_preprocess(image, label):
            crop_image = tf.random_crop(image, [224, 224, 3])                       # (3)
            flip_image = tf.image.random_flip_left_right(crop_image)                # (4)

            return flip_image, label

        # Preprocessing (for validation)
        # (3) Take a central 224x224 crop to the scaled image
        # (4) Substract the per color mean `VGG_MEAN`
        # Note: we don't normalize the data here, as VGG was trained without normalization
        def val_preprocess(image, label):
            crop_image = tf.image.resize_image_with_crop_or_pad(image, 224, 224)    # (3)

            return crop_image, label

        # ----------------------------------------------------------------------
        # DATASET CREATION using tf.contrib.data.Dataset
        # https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/data

        # The tf.contrib.data.Dataset framework uses queues in the background to feed in
        # data to the model.
        # We initialize the dataset with a list of filenames and labels, and then apply
        # the preprocessing functions described above.
        # Behind the scenes, queues will load the filenames, preprocess them with multiple
        # threads and apply the preprocessing in parallel, and then batch the data

        # Training dataset
        train_filenames = tf.constant(train_filenames)
        train_labels = tf.constant(train_labels)
        train_dataset = tf.contrib.data.Dataset.from_tensor_slices((train_filenames, train_labels))
        train_dataset = train_dataset.map(_parse_function,
            num_threads=args.num_workers, output_buffer_size=args.batch_size)
        train_dataset = train_dataset.map(training_preprocess,
            num_threads=args.num_workers, output_buffer_size=args.batch_size)
        train_dataset = train_dataset.shuffle(buffer_size=10000)  # don't forget to shuffle
        batched_train_dataset = train_dataset.batch(args.batch_size)

        # Validation dataset
        val_filenames = tf.constant(val_filenames)
        val_labels = tf.constant(val_labels)
        val_dataset = tf.contrib.data.Dataset.from_tensor_slices((val_filenames, val_labels))
        val_dataset = val_dataset.map(_parse_function,
            num_threads=args.num_workers, output_buffer_size=args.batch_size)
        val_dataset = val_dataset.map(val_preprocess,
            num_threads=args.num_workers, output_buffer_size=args.batch_size)
        batched_val_dataset = val_dataset.batch(args.batch_size)


        # Now we define an iterator that can operator on either dataset.
        # The iterator can be reinitialized by calling:
        #     - sess.run(train_init_op) for 1 epoch on the training set
        #     - sess.run(val_init_op)   for 1 epoch on the valiation set
        # Once this is done, we don't need to feed any value for images and labels
        # as they are automatically pulled out from the iterator queues.

        # A reinitializable iterator is defined by its structure. We could use the
        # `output_types` and `output_shapes` properties of either `train_dataset`
        # or `validation_dataset` here, because they are compatible.
        iterator = tf.contrib.data.Iterator.from_structure(batched_train_dataset.output_types,
                                                           batched_train_dataset.output_shapes)
        images, labels = iterator.get_next()

        train_init_op = iterator.make_initializer(batched_train_dataset)
        val_init_op = iterator.make_initializer(batched_val_dataset)

        # Indicates whether we are in training or in test mode
        is_training = tf.placeholder(tf.bool)

        # ---------------------------------------------------------------------
        # Now that we have set up the data, it's time to set up the model.
        # For this example, we'll use VGG-16 pretrained on ImageNet. We will remove the
        # last fully connected layer (fc8) and replace it with our own, with an
        # output size num_classes=2
        # We will first train the last layer for a few epochs.
        # Then we will train the entire model on our dataset for a few epochs.

        # Get the pretrained model, specifying the num_classes argument to create a new
        # fully connected replacing the last one, called "vgg_16/fc8"
        # Each model has a different architecture, so "vgg_16/fc8" will change in another model.
        # Here, logits gives us directly the predicted scores we wanted from the images.
        # We pass a scope to initialize "vgg_16/fc8" weights with he_initializer

        # vgg = tf.contrib.slim.nets.vgg
        # with slim.arg_scope(vgg.vgg_arg_scope(weight_decay=args.weight_decay)):
        #     logits, _ = vgg.vgg_16(images, num_classes=num_classes, is_training=is_training,
        #                            dropout_keep_prob=args.dropout_keep_prob)

        if args.use_faces:
            net = MyNet({'data': images})
            fc7 = net.layers['fc7'] 
            logits = tf.layers.dense(fc7,num_classes,name = 'fc2')
        elif args.use_inceptionres:
            inception = nets.inception
            with slim.arg_scope(inception.inception_v1_arg_scope(weight_decay=args.weight_decay)):
                logits, _ = inception.inception_v1(images, num_classes=num_classes, is_training=is_training, dropout_keep_prob=args.dropout_keep_prob)
            #logits = tf.layers.dense(output,num_classes,name = 'fc2')
        else:
            vgg = nets.vgg
            with slim.arg_scope(vgg.vgg_arg_scope(weight_decay=args.weight_decay)):
                logits, _ = vgg.vgg_16(images, num_classes=num_classes, is_training=is_training, dropout_keep_prob=args.dropout_keep_prob)

        # Specify where the model checkpoint is (pretrained weights).
        model_path = args.model_path
        assert(os.path.isfile(model_path))
        #print([n.name for n in tf.get_default_graph().as_graph_def().node])
        # Restore only the layers up to fc7 (included)
        if args.use_faces:
            variables_to_restore = tf.contrib.framework.get_variables_to_restore(exclude=['fc8'])
            fc2_variables = tf.contrib.framework.get_variables('fc2')
        elif args.use_inceptionres:
            variables_to_restore = tf.contrib.framework.get_variables_to_restore(exclude =['InceptionV1/Logits'])
            init_fn = tf.contrib.framework.assign_from_checkpoint_fn(model_path, variables_to_restore)
            #fc2_variables = tf.contrib.framework.get_variables('fc2')
            fc2_variables = tf.contrib.framework.get_variables('InceptionV1/Logits')
        else:
            variables_to_restore = tf.contrib.framework.get_variables_to_restore(exclude=['vgg_16/fc8'])
            init_fn = tf.contrib.framework.assign_from_checkpoint_fn(model_path, variables_to_restore)
            fc2_variables = tf.contrib.framework.get_variables('vgg_16/fc8')

        fc2_init = tf.variables_initializer(fc2_variables)


        #init_fn = tf.contrib.framework.assign_from_checkpoint_fn(model_path, variables_to_restore)

        # Initialization operation from scratch for the new "fc8" layers
        # `get_variables` will only return the variables whose name starts with the given pattern
        # ---------------------------------------------------------------------
        # Using tf.losses, any loss is added to the tf.GraphKeys.LOSSES collection
        # We can then call the total loss easily
        tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
        loss = tf.losses.get_total_loss()

        # First we want to train only the reinitialized last layer fc8 for a few epochs.
        # We run minimize the loss only with respect to the fc8 variables (weight and bias).
        fc2_optimizer = tf.train.GradientDescentOptimizer(args.learning_rate1)
        fc2_train_op  = fc2_optimizer.minimize(loss, var_list=fc2_variables)

        # Then we want to finetune the entire model for a few epochs.
        # We run minimize the loss only with respect to all the variables.
        full_optimizer = tf.train.GradientDescentOptimizer(args.learning_rate2)
        full_train_op = full_optimizer.minimize(loss)

        # Evaluation metrics
        old_to_new_map = tf.constant([0,1,2,3,4,5,6,7])#tf.constant([2,4,6,7,3,5,0,1])#tf.constant([4,2,0,7,1,5,6,3])
        prediction = tf.gather(old_to_new_map,tf.to_int32(tf.argmax(logits, 1)))
        correct_prediction = tf.equal(prediction, labels)
        confusion_matrix = tf.confusion_matrix(labels, prediction,num_classes = num_classes)
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    # --------------------------------------------------------------------------
    # The session is the interface to *run* the computational graph.
    # We can call our training operations with `sess.run(train_op)` for instance
    with tf.Session(graph=graph) as sess:
        if args.load_model is not None:
            saver = tf.train.Saver()
            print("Loading Model")
            saver.restore(sess, args.load_model)
            print("Model loaded")
            print("Computing Training accuracy")
            #train_acc = check_accuracy(sess, correct_prediction, is_training, train_init_op)
            #print('Train accuracy: %f' % train_acc)
            print("Computing Validation accuracy")
            val_acc = check_accuracy(sess, correct_prediction, is_training, val_init_op)
            print('Val accuracy: %f\n' % val_acc)

            print("Computing Training Confusion")
            train_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,train_init_op)
            plot_confusion_matrix(train_conf_mat,train_str,'reran_model_train')
            print("Computing Validation Confusion")
            val_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,val_init_op)
            plot_confusion_matrix(val_conf_mat,val_str,'reran_model_val')



        else:
            saver = tf.train.Saver()
            #init_fn(sess)  # load the pretrained weights
            if args.use_faces:
                net.load(args.model_path, sess)
            else:
                init_fn(sess)


            sess.run(fc2_init)  # initialize the new fc2 layer

            # Update only the last layer for a few epochs.
            for epoch in range(args.num_epochs1):
                # Run an epoch over the training data.
                print("----")
                print('Starting epoch %d / %d' % (epoch + 1, args.num_epochs1))
                # Here we initialize the iterator with the training set.
                # This means that we can go through an entire epoch until the iterator becomes empty.
                sess.run(train_init_op)
                while True:
                    try:
                        _ = sess.run(fc2_train_op, {is_training: True})
                    except tf.errors.OutOfRangeError:
                        break

                # Check accuracy on the train and val sets every epoch.
                train_acc = check_accuracy(sess, correct_prediction, is_training, train_init_op)
                val_acc = check_accuracy(sess, correct_prediction, is_training, val_init_op)
                print('Train accuracy: %f' % train_acc)
                print('Val accuracy: %f\n' % val_acc)
                print("Computing Training Confusion")
                train_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,train_init_op)
                plot_confusion_matrix(train_conf_mat,train_str,'epoch-train-a-'+str(epoch))
                print("Computing Validation Confusion")
                val_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,val_init_op)
                plot_confusion_matrix(val_conf_mat,val_str,'epoch-val-a-'+str(epoch))

                saver.save(sess, './epoch-a-'+str(epoch))


            # Train the entire model for a few more epochs, continuing with the *same* weights.
            for epoch in range(args.num_epochs2):
                print('Starting epoch %d / %d' % (epoch + 1, args.num_epochs2))
                sess.run(train_init_op)
                while True:
                    try:
                        _ = sess.run(full_train_op, {is_training: True})
                    except tf.errors.OutOfRangeError:
                        break

                # Check accuracy on the train and val sets every epoch
                train_acc = check_accuracy(sess, correct_prediction, is_training, train_init_op)
                val_acc = check_accuracy(sess, correct_prediction, is_training, val_init_op)
                print('Train accuracy: %f' % train_acc)
                print('Val accuracy: %f\n' % val_acc)

                print("Computing Training Confusion")
                train_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,train_init_op)
                plot_confusion_matrix(train_conf_mat,train_str,'epoch-train-b-'+str(epoch))
                print("Computing Validation Confusion")
                val_conf_mat = comp_confusion_matrix(sess,confusion_matrix,is_training,val_init_op)
                plot_confusion_matrix(val_conf_mat,val_str,'epoch-val-b-'+str(epoch))

                saver.save(sess, './epoch-b-'+str(epoch))

            saver.save(sess, './final-model')

if __name__ == '__main__':
    print("Parsing arguments")
    args = parser.parse_args()
    print("Starting main script")
    main(args)