Transfer Learning

Most of the time you won't want to train a whole convolutional network yourself. Modern ConvNets training on huge datasets like ImageNet take weeks on multiple GPUs. Instead, most people use a pretrained network either as a fixed feature extractor, or as an initial network to fine tune. In this notebook, you'll be using VGGNet trained on the ImageNet dataset as a feature extractor. Below is a diagram of the VGGNet architecture.

VGGNet is great because it's simple and has great performance, coming in second in the ImageNet competition. The idea here is that we keep all the convolutional layers, but replace the final fully connected layers with our own classifier. This way we can use VGGNet as a feature extractor for our images then easily train a simple classifier on top of that. What we'll do is take the first fully connected layer with 4096 units, including thresholding with ReLUs. We can use those values as a code for each image, then build a classifier on top of those codes.

You can read more about transfer learning from the CS231n course notes.

Pretrained VGGNet

We'll be using a pretrained network from https://github.com/machrisaa/tensorflow-vgg. This code is already included in 'tensorflow_vgg' directory, sdo you don't have to clone it.

This is a really nice implementation of VGGNet, quite easy to work with. The network has already been trained and the parameters are available from this link. You'll need to clone the repo into the folder containing this notebook. Then download the parameter file using the next cell.


In [1]:
from urllib.request import urlretrieve
from os.path import isfile, isdir
from tqdm import tqdm

vgg_dir = 'tensorflow_vgg/'
# Make sure vgg exists
if not isdir(vgg_dir):
    raise Exception("VGG directory doesn't exist!")

class DLProgress(tqdm):
    last_block = 0

    def hook(self, block_num=1, block_size=1, total_size=None):
        self.total = total_size
        self.update((block_num - self.last_block) * block_size)
        self.last_block = block_num

if not isfile(vgg_dir + "vgg16.npy"):
    with DLProgress(unit='B', unit_scale=True, miniters=1, desc='VGG16 Parameters') as pbar:
        urlretrieve(
            'https://s3.amazonaws.com/content.udacity-data.com/nd101/vgg16.npy',
            vgg_dir + 'vgg16.npy',
            pbar.hook)
else:
    print("Parameter file already exists!")


Parameter file already exists!

Flower power

Here we'll be using VGGNet to classify images of flowers. To get the flower dataset, run the cell below. This dataset comes from the TensorFlow inception tutorial.


In [2]:
import tarfile

dataset_folder_path = 'flower_photos'

class DLProgress(tqdm):
    last_block = 0

    def hook(self, block_num=1, block_size=1, total_size=None):
        self.total = total_size
        self.update((block_num - self.last_block) * block_size)
        self.last_block = block_num

if not isfile('flower_photos.tar.gz'):
    with DLProgress(unit='B', unit_scale=True, miniters=1, desc='Flowers Dataset') as pbar:
        urlretrieve(
            'http://download.tensorflow.org/example_images/flower_photos.tgz',
            'flower_photos.tar.gz',
            pbar.hook)

if not isdir(dataset_folder_path):
    with tarfile.open('flower_photos.tar.gz') as tar:
        tar.extractall()
        tar.close()

ConvNet Codes

Below, we'll run through all the images in our dataset and get codes for each of them. That is, we'll run the images through the VGGNet convolutional layers and record the values of the first fully connected layer. We can then write these to a file for later when we build our own classifier.

Here we're using the vgg16 module from tensorflow_vgg. The network takes images of size $224 \times 224 \times 3$ as input. Then it has 5 sets of convolutional layers. The network implemented here has this structure (copied from the source code):

self.conv1_1 = self.conv_layer(bgr, "conv1_1")
self.conv1_2 = self.conv_layer(self.conv1_1, "conv1_2")
self.pool1 = self.max_pool(self.conv1_2, 'pool1')

self.conv2_1 = self.conv_layer(self.pool1, "conv2_1")
self.conv2_2 = self.conv_layer(self.conv2_1, "conv2_2")
self.pool2 = self.max_pool(self.conv2_2, 'pool2')

self.conv3_1 = self.conv_layer(self.pool2, "conv3_1")
self.conv3_2 = self.conv_layer(self.conv3_1, "conv3_2")
self.conv3_3 = self.conv_layer(self.conv3_2, "conv3_3")
self.pool3 = self.max_pool(self.conv3_3, 'pool3')

self.conv4_1 = self.conv_layer(self.pool3, "conv4_1")
self.conv4_2 = self.conv_layer(self.conv4_1, "conv4_2")
self.conv4_3 = self.conv_layer(self.conv4_2, "conv4_3")
self.pool4 = self.max_pool(self.conv4_3, 'pool4')

self.conv5_1 = self.conv_layer(self.pool4, "conv5_1")
self.conv5_2 = self.conv_layer(self.conv5_1, "conv5_2")
self.conv5_3 = self.conv_layer(self.conv5_2, "conv5_3")
self.pool5 = self.max_pool(self.conv5_3, 'pool5')

self.fc6 = self.fc_layer(self.pool5, "fc6")
self.relu6 = tf.nn.relu(self.fc6)

So what we want are the values of the first fully connected layer, after being ReLUd (self.relu6). To build the network, we use

with tf.Session() as sess:
    vgg = vgg16.Vgg16()
    input_ = tf.placeholder(tf.float32, [None, 224, 224, 3])
    with tf.name_scope("content_vgg"):
        vgg.build(input_)

This creates the vgg object, then builds the graph with vgg.build(input_). Then to get the values from the layer,

feed_dict = {input_: images}
codes = sess.run(vgg.relu6, feed_dict=feed_dict)

In [3]:
import os

import numpy as np
import tensorflow as tf

from tensorflow_vgg import vgg16
from tensorflow_vgg import utils

In [4]:
data_dir = 'flower_photos/'
contents = os.listdir(data_dir)
classes = [each for each in contents if os.path.isdir(data_dir + each)]

Below I'm running images through the VGG network in batches.

Exercise: Below, build the VGG network. Also get the codes from the first fully connected layer (make sure you get the ReLUd values).


In [5]:
# Set the batch size higher if you can fit in in your GPU memory
batch_size = 16
codes_list = []
labels = []
batch = []

codes = None

with tf.Session() as sess:
    
    # TODO: Build the vgg network here
    vgg = vgg16.Vgg16()
    input_ = tf.placeholder(tf.float32, (None, 224, 224, 3))
    with tf.name_scope('content_vgg'):
        vgg.build(input_)

    for each in classes:
        print("Starting {} images".format(each))
        class_path = data_dir + each
        files = os.listdir(class_path)
        for ii, file in enumerate(files, 1):
            # Add images to the current batch
            # utils.load_image crops the input images for us, from the center
            img = utils.load_image(os.path.join(class_path, file))
            batch.append(img.reshape((1, 224, 224, 3)))
            labels.append(each)
            
            # Running the batch through the network to get the codes
            if ii % batch_size == 0 or ii == len(files):
                
                # Image batch to pass to VGG network
                images = np.concatenate(batch)
                
                # TODO: Get the values from the relu6 layer of the VGG network
                codes_batch = sess.run(vgg.relu6, feed_dict={input_: images})
                
                # Here I'm building an array of the codes
                if codes is None:
                    codes = codes_batch
                else:
                    codes = np.concatenate((codes, codes_batch))
                
                # Reset to start building the next batch
                batch = []
                print('{} images processed'.format(ii))


/home/luo/dlnd/deep-learning/transfer-learning/tensorflow_vgg/vgg16.npy
npy file loaded
build model started
build model finished: 0s
Starting tulips images
16 images processed
32 images processed
48 images processed
64 images processed
80 images processed
96 images processed
112 images processed
128 images processed
144 images processed
160 images processed
176 images processed
192 images processed
208 images processed
224 images processed
240 images processed
256 images processed
272 images processed
288 images processed
304 images processed
320 images processed
336 images processed
352 images processed
368 images processed
384 images processed
400 images processed
416 images processed
432 images processed
448 images processed
464 images processed
480 images processed
496 images processed
512 images processed
528 images processed
544 images processed
560 images processed
576 images processed
592 images processed
608 images processed
624 images processed
640 images processed
656 images processed
672 images processed
688 images processed
704 images processed
720 images processed
736 images processed
752 images processed
768 images processed
784 images processed
799 images processed
Starting sunflowers images
16 images processed
32 images processed
48 images processed
64 images processed
80 images processed
96 images processed
112 images processed
128 images processed
144 images processed
160 images processed
176 images processed
192 images processed
208 images processed
224 images processed
240 images processed
256 images processed
272 images processed
288 images processed
304 images processed
320 images processed
336 images processed
352 images processed
368 images processed
384 images processed
400 images processed
416 images processed
432 images processed
448 images processed
464 images processed
480 images processed
496 images processed
512 images processed
528 images processed
544 images processed
560 images processed
576 images processed
592 images processed
608 images processed
624 images processed
640 images processed
656 images processed
672 images processed
688 images processed
699 images processed
Starting daisy images
16 images processed
32 images processed
48 images processed
64 images processed
80 images processed
96 images processed
112 images processed
128 images processed
144 images processed
160 images processed
176 images processed
192 images processed
208 images processed
224 images processed
240 images processed
256 images processed
272 images processed
288 images processed
304 images processed
320 images processed
336 images processed
352 images processed
368 images processed
384 images processed
400 images processed
416 images processed
432 images processed
448 images processed
464 images processed
480 images processed
496 images processed
512 images processed
528 images processed
544 images processed
560 images processed
576 images processed
592 images processed
608 images processed
624 images processed
633 images processed
Starting roses images
16 images processed
32 images processed
48 images processed
64 images processed
80 images processed
96 images processed
112 images processed
128 images processed
144 images processed
160 images processed
176 images processed
192 images processed
208 images processed
224 images processed
240 images processed
256 images processed
272 images processed
288 images processed
304 images processed
320 images processed
336 images processed
352 images processed
368 images processed
384 images processed
400 images processed
416 images processed
432 images processed
448 images processed
464 images processed
480 images processed
496 images processed
512 images processed
528 images processed
544 images processed
560 images processed
576 images processed
592 images processed
608 images processed
624 images processed
640 images processed
641 images processed
Starting dandelion images
16 images processed
32 images processed
48 images processed
64 images processed
80 images processed
96 images processed
112 images processed
128 images processed
144 images processed
160 images processed
176 images processed
192 images processed
208 images processed
224 images processed
240 images processed
256 images processed
272 images processed
288 images processed
304 images processed
320 images processed
336 images processed
352 images processed
368 images processed
384 images processed
400 images processed
416 images processed
432 images processed
448 images processed
464 images processed
480 images processed
496 images processed
512 images processed
528 images processed
544 images processed
560 images processed
576 images processed
592 images processed
608 images processed
624 images processed
640 images processed
656 images processed
672 images processed
688 images processed
704 images processed
720 images processed
736 images processed
752 images processed
768 images processed
784 images processed
800 images processed
816 images processed
832 images processed
848 images processed
864 images processed
880 images processed
896 images processed
898 images processed

In [6]:
# write codes to file
with open('codes', 'w') as f:
    codes.tofile(f)
    
# write labels to file
import csv
with open('labels', 'w') as f:
    writer = csv.writer(f, delimiter='\n')
    writer.writerow(labels)

Building the Classifier

Now that we have codes for all the images, we can build a simple classifier on top of them. The codes behave just like normal input into a simple neural network. Below I'm going to have you do most of the work.


In [7]:
# read codes and labels from file
import csv

with open('labels') as f:
    reader = csv.reader(f, delimiter='\n')
    labels = np.array([each for each in reader if len(each) > 0]).squeeze()
with open('codes') as f:
    codes = np.fromfile(f, dtype=np.float32)
    codes = codes.reshape((len(labels), -1))

Data prep

As usual, now we need to one-hot encode our labels and create validation/test sets. First up, creating our labels!

Exercise: From scikit-learn, use LabelBinarizer to create one-hot encoded vectors from the labels.


In [11]:
from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer()
labels_vecs = lb.fit_transform(labels) # Your one-hot encoded labels array here

Now you'll want to create your training, validation, and test sets. An important thing to note here is that our labels and data aren't randomized yet. We'll want to shuffle our data so the validation and test sets contain data from all classes. Otherwise, you could end up with testing sets that are all one class. Typically, you'll also want to make sure that each smaller set has the same the distribution of classes as it is for the whole data set. The easiest way to accomplish both these goals is to use StratifiedShuffleSplit from scikit-learn.

You can create the splitter like so:

ss = StratifiedShuffleSplit(n_splits=1, test_size=0.2)

Then split the data with

splitter = ss.split(x, y)

ss.split returns a generator of indices. You can pass the indices into the arrays to get the split sets. The fact that it's a generator means you either need to iterate over it, or use next(splitter) to get the indices. Be sure to read the documentation and the user guide.

Exercise: Use StratifiedShuffleSplit to split the codes and labels into training, validation, and test sets.


In [12]:
from sklearn.model_selection import StratifiedShuffleSplit

ss = StratifiedShuffleSplit(n_splits=1, test_size=0.2)
splitter = ss.split(codes, labels_vecs)

train_idx, val_idx = next(splitter)

half_val = int(len(val_idx) / 2)
test_idx = val_idx[:half_val]
val_idx = val_idx[half_val:]

train_x, train_y = codes[train_idx], labels_vecs[train_idx]
val_x, val_y = codes[val_idx], labels_vecs[val_idx]
test_x, test_y =  codes[test_idx], labels_vecs[test_idx]

In [13]:
print("Train shapes (x, y):", train_x.shape, train_y.shape)
print("Validation shapes (x, y):", val_x.shape, val_y.shape)
print("Test shapes (x, y):", test_x.shape, test_y.shape)


Train shapes (x, y): (2936, 4096) (2936, 5)
Validation shapes (x, y): (367, 4096) (367, 5)
Test shapes (x, y): (367, 4096) (367, 5)

If you did it right, you should see these sizes for the training sets:

Train shapes (x, y): (2936, 4096) (2936, 5)
Validation shapes (x, y): (367, 4096) (367, 5)
Test shapes (x, y): (367, 4096) (367, 5)

Classifier layers

Once you have the convolutional codes, you just need to build a classfier from some fully connected layers. You use the codes as the inputs and the image labels as targets. Otherwise the classifier is a typical neural network.

Exercise: With the codes and labels loaded, build the classifier. Consider the codes as your inputs, each of them are 4096D vectors. You'll want to use a hidden layer and an output layer as your classifier. Remember that the output layer needs to have one unit for each class and a softmax activation function. Use the cross entropy to calculate the cost.


In [22]:
inputs_ = tf.placeholder(tf.float32, shape=[None, codes.shape[1]])
labels_ = tf.placeholder(tf.int64, shape=[None, labels_vecs.shape[1]])

# TODO: Classifier layers and operations
out = tf.layers.dense(inputs_, 256)
out = tf.maximum(0., out)

logits = tf.layers.dense(out, labels_vecs.shape[1]) # output layer logits
cost = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels_) # cross entropy loss
cost = tf.reduce_mean(cost)

optimizer = tf.train.AdamOptimizer().minimize(cost) # training optimizer

# Operations for validation/test accuracy
predicted = tf.nn.softmax(logits)
correct_pred = tf.equal(tf.argmax(predicted, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

Batches!

Here is just a simple way to do batches. I've written it so that it includes all the data. Sometimes you'll throw out some data at the end to make sure you have full batches. Here I just extend the last batch to include the remaining data.


In [23]:
def get_batches(x, y, n_batches=10):
    """ Return a generator that yields batches from arrays x and y. """
    batch_size = len(x)//n_batches
    
    for ii in range(0, n_batches*batch_size, batch_size):
        # If we're not on the last batch, grab data with size batch_size
        if ii != (n_batches-1)*batch_size:
            X, Y = x[ii: ii+batch_size], y[ii: ii+batch_size] 
        # On the last batch, grab the rest of the data
        else:
            X, Y = x[ii:], y[ii:]
        # I love generators
        yield X, Y

Training

Here, we'll train the network.

Exercise: So far we've been providing the training code for you. Here, I'm going to give you a bit more of a challenge and have you write the code to train the network. Of course, you'll be able to see my solution if you need help. Use the get_batches function I wrote before to get your batches like for x, y in get_batches(train_x, train_y). Or write your own!


In [28]:
epochs = 10
iteration = 0

saver = tf.train.Saver()
with tf.Session() as sess:
    
    # TODO: Your training code here
    sess.run(tf.global_variables_initializer())
    
    for e in range(epochs):
        for x, y in get_batches(train_x, train_y):
            loss, _ = sess.run([cost, optimizer], feed_dict={inputs_: x, labels_: y})
            print ('Epoch: {}/{}, iteration: {}, training loss: {:.5f}'.format(e, epochs, iteration, loss)) 
            iteration += 1
            
            if iteration % 5 == 0:
                val_acc = sess.run(accuracy, feed_dict={inputs_: val_x, labels_: val_y})
                print ('Epoch:{}/{}, iteration: {}, validation accuracy: {:.5f}'.
                       format(e, epochs, iteration, val_acc))
    
    saver.save(sess, "checkpoints/flowers.ckpt")


Epoch: 0/10, iteration: 0, training loss: 5.77931
Epoch: 0/10, iteration: 1, training loss: 11.53590
Epoch: 0/10, iteration: 2, training loss: 9.00506
Epoch: 0/10, iteration: 3, training loss: 4.55730
Epoch: 0/10, iteration: 4, training loss: 2.14085
Epoch:0/10, iteration: 5, validation accuracy: 0.69210
Epoch: 0/10, iteration: 5, training loss: 3.40335
Epoch: 0/10, iteration: 6, training loss: 3.08495
Epoch: 0/10, iteration: 7, training loss: 2.13666
Epoch: 0/10, iteration: 8, training loss: 1.82529
Epoch: 0/10, iteration: 9, training loss: 1.13742
Epoch:0/10, iteration: 10, validation accuracy: 0.76839
Epoch: 1/10, iteration: 10, training loss: 1.20243
Epoch: 1/10, iteration: 11, training loss: 1.26994
Epoch: 1/10, iteration: 12, training loss: 0.87660
Epoch: 1/10, iteration: 13, training loss: 0.84066
Epoch: 1/10, iteration: 14, training loss: 0.56738
Epoch:1/10, iteration: 15, validation accuracy: 0.81744
Epoch: 1/10, iteration: 15, training loss: 0.69885
Epoch: 1/10, iteration: 16, training loss: 0.68731
Epoch: 1/10, iteration: 17, training loss: 0.62896
Epoch: 1/10, iteration: 18, training loss: 0.57104
Epoch: 1/10, iteration: 19, training loss: 0.60708
Epoch:1/10, iteration: 20, validation accuracy: 0.85831
Epoch: 2/10, iteration: 20, training loss: 0.26297
Epoch: 2/10, iteration: 21, training loss: 0.46149
Epoch: 2/10, iteration: 22, training loss: 0.42893
Epoch: 2/10, iteration: 23, training loss: 0.38564
Epoch: 2/10, iteration: 24, training loss: 0.38490
Epoch:2/10, iteration: 25, validation accuracy: 0.83924
Epoch: 2/10, iteration: 25, training loss: 0.32203
Epoch: 2/10, iteration: 26, training loss: 0.31605
Epoch: 2/10, iteration: 27, training loss: 0.27102
Epoch: 2/10, iteration: 28, training loss: 0.32201
Epoch: 2/10, iteration: 29, training loss: 0.20880
Epoch:2/10, iteration: 30, validation accuracy: 0.88828
Epoch: 3/10, iteration: 30, training loss: 0.12525
Epoch: 3/10, iteration: 31, training loss: 0.18355
Epoch: 3/10, iteration: 32, training loss: 0.22384
Epoch: 3/10, iteration: 33, training loss: 0.20879
Epoch: 3/10, iteration: 34, training loss: 0.15380
Epoch:3/10, iteration: 35, validation accuracy: 0.88828
Epoch: 3/10, iteration: 35, training loss: 0.19361
Epoch: 3/10, iteration: 36, training loss: 0.19536
Epoch: 3/10, iteration: 37, training loss: 0.12195
Epoch: 3/10, iteration: 38, training loss: 0.13574
Epoch: 3/10, iteration: 39, training loss: 0.12211
Epoch:3/10, iteration: 40, validation accuracy: 0.90463
Epoch: 4/10, iteration: 40, training loss: 0.08165
Epoch: 4/10, iteration: 41, training loss: 0.13323
Epoch: 4/10, iteration: 42, training loss: 0.13388
Epoch: 4/10, iteration: 43, training loss: 0.10186
Epoch: 4/10, iteration: 44, training loss: 0.08156
Epoch:4/10, iteration: 45, validation accuracy: 0.92643
Epoch: 4/10, iteration: 45, training loss: 0.11524
Epoch: 4/10, iteration: 46, training loss: 0.09964
Epoch: 4/10, iteration: 47, training loss: 0.07788
Epoch: 4/10, iteration: 48, training loss: 0.09343
Epoch: 4/10, iteration: 49, training loss: 0.09371
Epoch:4/10, iteration: 50, validation accuracy: 0.93733
Epoch: 5/10, iteration: 50, training loss: 0.04520
Epoch: 5/10, iteration: 51, training loss: 0.07233
Epoch: 5/10, iteration: 52, training loss: 0.06575
Epoch: 5/10, iteration: 53, training loss: 0.06271
Epoch: 5/10, iteration: 54, training loss: 0.04644
Epoch:5/10, iteration: 55, validation accuracy: 0.92371
Epoch: 5/10, iteration: 55, training loss: 0.07765
Epoch: 5/10, iteration: 56, training loss: 0.06040
Epoch: 5/10, iteration: 57, training loss: 0.04229
Epoch: 5/10, iteration: 58, training loss: 0.05101
Epoch: 5/10, iteration: 59, training loss: 0.05630
Epoch:5/10, iteration: 60, validation accuracy: 0.93188
Epoch: 6/10, iteration: 60, training loss: 0.03138
Epoch: 6/10, iteration: 61, training loss: 0.05122
Epoch: 6/10, iteration: 62, training loss: 0.04468
Epoch: 6/10, iteration: 63, training loss: 0.04318
Epoch: 6/10, iteration: 64, training loss: 0.02581
Epoch:6/10, iteration: 65, validation accuracy: 0.94005
Epoch: 6/10, iteration: 65, training loss: 0.03854
Epoch: 6/10, iteration: 66, training loss: 0.04346
Epoch: 6/10, iteration: 67, training loss: 0.02609
Epoch: 6/10, iteration: 68, training loss: 0.03425
Epoch: 6/10, iteration: 69, training loss: 0.03964
Epoch:6/10, iteration: 70, validation accuracy: 0.94005
Epoch: 7/10, iteration: 70, training loss: 0.02209
Epoch: 7/10, iteration: 71, training loss: 0.03471
Epoch: 7/10, iteration: 72, training loss: 0.02984
Epoch: 7/10, iteration: 73, training loss: 0.02543
Epoch: 7/10, iteration: 74, training loss: 0.01653
Epoch:7/10, iteration: 75, validation accuracy: 0.94005
Epoch: 7/10, iteration: 75, training loss: 0.02949
Epoch: 7/10, iteration: 76, training loss: 0.02768
Epoch: 7/10, iteration: 77, training loss: 0.01716
Epoch: 7/10, iteration: 78, training loss: 0.02394
Epoch: 7/10, iteration: 79, training loss: 0.03150
Epoch:7/10, iteration: 80, validation accuracy: 0.93733
Epoch: 8/10, iteration: 80, training loss: 0.01471
Epoch: 8/10, iteration: 81, training loss: 0.02402
Epoch: 8/10, iteration: 82, training loss: 0.02114
Epoch: 8/10, iteration: 83, training loss: 0.01865
Epoch: 8/10, iteration: 84, training loss: 0.01073
Epoch:8/10, iteration: 85, validation accuracy: 0.93733
Epoch: 8/10, iteration: 85, training loss: 0.01624
Epoch: 8/10, iteration: 86, training loss: 0.02251
Epoch: 8/10, iteration: 87, training loss: 0.01289
Epoch: 8/10, iteration: 88, training loss: 0.01582
Epoch: 8/10, iteration: 89, training loss: 0.02258
Epoch:8/10, iteration: 90, validation accuracy: 0.94278
Epoch: 9/10, iteration: 90, training loss: 0.01132
Epoch: 9/10, iteration: 91, training loss: 0.01808
Epoch: 9/10, iteration: 92, training loss: 0.01597
Epoch: 9/10, iteration: 93, training loss: 0.01373
Epoch: 9/10, iteration: 94, training loss: 0.00824
Epoch:9/10, iteration: 95, validation accuracy: 0.94278
Epoch: 9/10, iteration: 95, training loss: 0.01341
Epoch: 9/10, iteration: 96, training loss: 0.01522
Epoch: 9/10, iteration: 97, training loss: 0.00945
Epoch: 9/10, iteration: 98, training loss: 0.01234
Epoch: 9/10, iteration: 99, training loss: 0.01917
Epoch:9/10, iteration: 100, validation accuracy: 0.94278

Testing

Below you see the test accuracy. You can also see the predictions returned for images.


In [29]:
with tf.Session() as sess:
    saver.restore(sess, tf.train.latest_checkpoint('checkpoints'))
    
    feed = {inputs_: test_x,
            labels_: test_y}
    test_acc = sess.run(accuracy, feed_dict=feed)
    print("Test accuracy: {:.4f}".format(test_acc))


Test accuracy: 0.9537

In [30]:
%matplotlib inline

import matplotlib.pyplot as plt
from scipy.ndimage import imread

Below, feel free to choose images and see how the trained classifier predicts the flowers in them.


In [31]:
test_img_path = 'flower_photos/roses/10894627425_ec76bbc757_n.jpg'
test_img = imread(test_img_path)
plt.imshow(test_img)


Out[31]:
<matplotlib.image.AxesImage at 0x7f651fffab00>

In [ ]:
# Run this cell if you don't have a vgg graph built
if 'vgg' in globals():
    print('"vgg" object already exists.  Will not create again.')
else:
    #create vgg
    with tf.Session() as sess:
        input_ = tf.placeholder(tf.float32, [None, 224, 224, 3])
        vgg = vgg16.Vgg16()
        vgg.build(input_)

In [32]:
with tf.Session() as sess:
    img = utils.load_image(test_img_path)
    img = img.reshape((1, 224, 224, 3))

    feed_dict = {input_: img}
    code = sess.run(vgg.relu6, feed_dict=feed_dict)
        
saver = tf.train.Saver()
with tf.Session() as sess:
    saver.restore(sess, tf.train.latest_checkpoint('checkpoints'))
    
    feed = {inputs_: code}
    prediction = sess.run(predicted, feed_dict=feed).squeeze()

In [33]:
plt.imshow(test_img)


Out[33]:
<matplotlib.image.AxesImage at 0x7f6520a65828>

In [34]:
plt.barh(np.arange(5), prediction)
_ = plt.yticks(np.arange(5), lb.classes_)