You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our high level approach here will be to start with our fine-tuned cats vs dogs model (with dropout), then fine-tune all the dense layers, after removing dropout from them. The steps we will take are:
Re-create and load our modified VGG model with binary dependent (i.e. dogs v cats)
Split the model between the convolutional (conv) layers and the dense layers
Pre-calculate the output of the conv layers, so that we don't have to redundently re-calculate them on every epoch
Create a new model with just the dense layers, and dropout p set to zero
Train this new model using the output of the conv layers as training data.
As before we need to start with a working model, so let's bring in our working VGG 16 model and change it to predict our binary dependent...
In [4]:
model=vgg_ft(2)
model=vgg_ft(2)
...andloadourfine-tunedweights.
In [5]:
model.load_weights(model_path+'finetune3.h5')
We're going to be training a number of iterations without dropout, so it would be best for us to pre-calculate the input to the fully connected layers - i.e. the Flatten() layer. We'llstartbyfindingthislayerinourmodel, andcreatinganewmodelthatcontainsjustthelayersuptoandincludingthislayer:
In [6]:
llayers=model.layersIn [7]:
last_conv_idx= [indexforindex,layerinenumerate(layers)
iftype(layer) isConvolution2D][-1]
In [8]:
last_conv_idxOut[8]:
30In [9]:
layers[last_conv_idx]
layers[last_conv_idx]
Out[9]:
<keras.layers.convolutional.Convolution2Dat0x7f64a680f9d0>In [10]:
conv_layers=layers[:last_conv_idx+1]
conv_model=Sequential(conv_layers)
# Dense layers - also known as fully connected or 'FC' layersfc_layers=layers[last_conv_idx+1:]
Nowwecanusetheexactsameapproachtocreatingfeaturesasweusedwhenwecreatedthelinearmodelfromtheimagenetpredictionsinthelastlesson-it's only the model that has changed. As you'reseeing, there'safairlysmallnumberof"recipes"thatcangetusalongway!
In [4]:
batches=get_batches(path+'train', shuffle=False, batch_size=batch_size)
val_batches=get_batches(path+'valid', shuffle=False, batch_size=batch_size)
val_classes=val_batches.classestrn_classes=batches.classesval_labels=onehot(val_classes)
trn_labels=onehot(trn_classes)
Found23000imagesbelongingto2classes.
Found2000imagesbelongingto2classes.
In [5]:
batches.class_indicesOut[5]:
{'cats': 0, 'dogs': 1}
In [12]:
val_features=conv_model.predict_generator(val_batches, val_batches.nb_sample)
trn_features=conv_model.predict_generator(batches, batches.nb_sample)
In [41]:
save_array(model_path+'train_convlayer_features.bc', trn_features)
save_array(model_path+'valid_convlayer_features.bc', val_features)
save_array(model_path+'train_convlayer_features.bc', trn_features)
save_array(model_path+'valid_convlayer_features.bc', val_features)
In [89]:
trn_features=load_array(model_path+'train_convlayer_features.bc')
val_features=load_array(model_path+'valid_convlayer_features.bc')
In [90]:
trn_features.shapeOut[90]:
(23000, 512, 14, 14)
Forournewfullyconnectedmodel, we'll create it using the exact same architecture as the last layers of VGG 16, so that we can conveniently copy pre-trained weights over from that model. However, we'llsetthedropoutlayer'spvaluestozero, soastoeffectivelyremovedropout.
In [16]:
# Copy the weights from the pre-trained model.# NB: Since we're removing dropout, we want to half the weightsdefproc_wgts(layer): return [o/2foroinlayer.get_weights()]
In [17]:
# Such a finely tuned model needs to be updated very slowly!opt=RMSprop(lr=0.00001, rho=0.7)
In [18]:
defget_fc_model():
model=Sequential([
MaxPooling2D(input_shape=conv_layers[-1].output_shape[1:]),
Flatten(),
Dense(4096, activation='relu'),
Dropout(0.),
Dense(4096, activation='relu'),
Dropout(0.),
Dense(2, activation='softmax')
])
forl1,l2inzip(model.layers, fc_layers): l1.set_weights(proc_wgts(l2))
model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
returnmodelIn [19]:
fc_model=get_fc_model()
Andfitthemodelintheusualway:
In [15]:
fc_model.fit(trn_features, trn_labels, nb_epoch=8,
batch_size=batch_size, validation_data=(val_features, val_labels))
Trainon23000samples, validateon2000samplesEpoch1/823000/23000 [==============================] -17s-loss: 0.2577-acc: 0.9817-val_loss: 0.3478-val_acc: 0.9765Epoch2/823000/23000 [==============================] -17s-loss: 0.2052-acc: 0.9853-val_loss: 0.2789-val_acc: 0.9785Epoch3/823000/23000 [==============================] -17s-loss: 0.1553-acc: 0.9895-val_loss: 0.2358-val_acc: 0.9845Epoch4/823000/23000 [==============================] -17s-loss: 0.1388-acc: 0.9909-val_loss: 0.1914-val_acc: 0.9865Epoch5/823000/23000 [==============================] -17s-loss: 0.1335-acc: 0.9912-val_loss: 0.2181-val_acc: 0.9855Epoch6/823000/23000 [==============================] -17s-loss: 0.1126-acc: 0.9924-val_loss: 0.1850-val_acc: 0.9875Epoch7/823000/23000 [==============================] -17s-loss: 0.1080-acc: 0.9928-val_loss: 0.2226-val_acc: 0.9840Epoch8/823000/23000 [==============================] -17s-loss: 0.1005-acc: 0.9935-val_loss: 0.2256-val_acc: 0.9850Out[15]:
<keras.callbacks.Historyat0x7f66b6bcdd10>In [16]:
fc_model.save_weights(model_path+'no_dropout.h5')
In [ ]:
fc_model.load_weights(model_path+'no_dropout.h5')
# Let's import the mappings from VGG ids to imagenet category ids and descriptions, for display purposes later.FILES_PATH='http://www.platform.ai/models/'; CLASS_FILE='imagenet_class_index.json'# Keras' get_file() is a handy function that downloads files, and caches them for re-use laterfpath=get_file(CLASS_FILE, FILES_PATH+CLASS_FILE, cache_subdir='models')
withopen(fpath) asf: class_dict=json.load(f)
# Convert dictionary with string indexes into an arrayclasses= [class_dict[str(i)][1] foriinrange(len(class_dict))]
# Here's a few examples of the categories we just imported:classes[:5]
>> ['tench', 'goldfish', 'great_white_shark', 'tiger_shark', 'hammerhead']
# Model from scratch## Creating the model involves creating the model architecture, ## and then loading the model weights into that architecture# VGG has just one type of convolutional block, and one type of fully connected ('dense') block. Here's the convolutional block definition:defConvBlock(layers, model, filters):
foriinrange(layers):
model.add(ZeroPadding2D((1,1)))
model.add(Conv2D(filters, (3, 3), activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
# ...and here's the fully-connected definition.defFCBlock(model):
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
# Mean of each channel as provided by VGG researchers# need to make two changes in regards to the original VGG modelvgg_mean=np.array([123.68, 116.779, 103.939]).reshape((3,1,1))
defvgg_preprocess(x):
x=x-vgg_mean# subtract meanreturnx[:, ::-1] # reverse axis bgr->rgb# Define model architecture```pythondefVGG_16():
model=Sequential()
model.add(Lambda(vgg_preprocess, input_shape=(3,224,224)))
ConvBlock(2, model, 64)
ConvBlock(2, model, 128)
ConvBlock(3, model, 256)
ConvBlock(3, model, 512)
ConvBlock(3, model, 512)
model.add(Flatten())
FCBlock(model)
FCBlock(model)
model.add(Dense(1000, activation='softmax'))
returnmodel
We can create the model like any python object:
model = VGG_16()
Weights
We are using pre-trained weights here: Otherwise have to re-train model from scratch.
The setup of the imagenet model is now complete, so all we have to do is grab a batch of images and call predict() on them.
batch_size = 4
Keras provides functionality to create batches of data from directories containing images; all we have to do is to define the size to resize the images to, what type of labels to create, whether to randomly shuffle the images, and how many images to include in each batch. We use this little wrapper to define some helpful defaults appropriate for imagenet data:
The VGG model returns 1,000 probabilities for each image, representing the probability that the model assigns to each possible imagenet category for each image. By finding the index with the largest probability (with np.argmax()) we can find the predicted label.
# Let's grab batches of 4 images from our training folder:batches=vgg.get_batches(path+'train', batch_size=4)
Look at examples
# Batches is just a regular python iterator. Each iteration returns both the images themselves, as well as the labels.imgs,labels=next(batches)
Plot examples
plots(imgs, titles=labels)
We can now pass the images to Vgg16's predict() function to get back probabilities, category indexes, and category names for each image's VGG prediction.
vgg.predict(imgs, True)
>>
(array([ 0.9203, 0.5414, 0.0627, 0.3767], dtype=float32),
array([284, 283, 551, 194]),
['Siamese_cat', 'Persian_cat', 'face_powder', 'Dandie_Dinmont'])
# The category indexes are based on the ordering of categories used in the VGG model - e.g here are the first four:```pythonvgg.classes[:4]
>>
['tench', 'goldfish', 'great_white_shark', 'tiger_shark']
batch_size=16importvgg16; reload(vgg16)
fromvgg16importVgg16vgg=Vgg16()
# Grab a few images at a time for training and validation.# NB: They must be in subdirectories named based on their categorybatches=vgg.get_batches(path+'train', batch_size=batch_size)
val_batches=vgg.get_batches(path+'valid', batch_size=batch_size*2)
vgg.finetune(batches)
vgg.fit(batches, val_batches, batch_size, nb_epoch=1)