-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.py
282 lines (239 loc) · 11.6 KB
/
model.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import matplotlib
matplotlib.use("Agg")
import os
import sys
from keras.preprocessing.image import load_img, ImageDataGenerator, img_to_array
from keras.applications.vgg16 import preprocess_input as vgg_preprocess_input
from keras.applications.vgg16 import decode_predictions
from keras.applications.vgg16 import VGG16
from keras.applications.inception_v3 import InceptionV3, preprocess_input
from sklearn.model_selection import train_test_split
from keras.utils import to_categorical
from keras.models import Model, Sequential, load_model
from keras.datasets import mnist
from keras.layers import Dense, GlobalAveragePooling2D, Input
from keras.optimizers import SGD
import matplotlib.pyplot as plt
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
import numpy as np
import argparse
import cv2
from imutils import paths
from config import Config
class SketchModel(object):
def __init__(self, Config):
self.NB_EPOCHS = 80
self.BAT_SIZE = 32
self.FC_SIZE = 1024
self.image_width = 224
self.image_height = 224
self.data = []
self.labels = []
self.config = Config()
def process_image_for_vgg(self, image_path):
image = load_img(image_path, target_size=(self.image_width, self.image_height))
# convert the image pixels to a numpy array
image = img_to_array(image)
# reshape data for the model
image = image.reshape((image.shape[0], image.shape[1], image.shape[2]))
# prepare the image for the VGG model
image = vgg_preprocess_input(image)
return image
def process_image_for_inception(self, image_path):
image = load_img(image_path, target_size=(299,299))
image = img_to_array(image)
# reshape data for the model
image = image.reshape((image.shape[0], image.shape[1], image.shape[2]))
image = preprocess_input(image)
return image
def process_image_for_custom(self, image_path):
image = load_img(image_path, grayscale=True, target_size=(200, 200))
# convert the image pixels to a numpy array
image = img_to_array(image)
return image
def prepare_test_train_data(self, model_name):
classes = self.config.load_classes()
for c in enumerate(classes):
for filename in os.listdir('images/' + c[1]['directory']):
if filename != '.DS_Store':
if model_name == 'vgg16':
image = self.process_image_for_vgg('images/{}/{}'.format(c[1]['directory'],filename))
if model_name == 'inception':
image = self.process_image_for_inception('images/{}/{}'.format(c[1]['directory'], filename))
if model_name == 'custom':
image = self.process_image_for_custom('images/{}/{}'.format(c[1]['directory'], filename))
self.data.append(image)
self.labels.append(c[1]['id'])
# partition the data into training and testing splits using 75% of
# the data for training and the remaining 25% for testing
self.data = np.array(self.data, dtype="float")
self.labels = np.array(self.labels)
(self.trainX, self.testX, trainY, testY) = train_test_split(self.data, self.labels, test_size=0.25, random_state=42)
# convert the labels from integers to vectors
self.trainY = to_categorical(trainY, num_classes=self.config.num_classes)
self.testY = to_categorical(testY, num_classes=self.config.num_classes)
def add_new_last_layer(self, baseModel, num_classes):
"""Add last layer to the convnet
Args:
base_model: keras model excluding top
nb_classes: # of classes
Returns:
new keras model with last layer
"""
x = baseModel.output
x = GlobalAveragePooling2D()(x)
x = Dense(self.FC_SIZE, activation='relu')(x) # new FC layer, random init
predictions = Dense(num_classes, activation='softmax')(x) # new softmax layer
newModel = Model( inputs=baseModel.inputs, outputs=predictions )
return newModel
def trainVGG(self):
self.prepare_test_train_data('vgg16')
aug = ImageDataGenerator(
rotation_range=30,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode="nearest")
baseModel = VGG16(weights="imagenet", include_top=False, input_shape=(self.image_width, self.image_height, 3))
for layer in baseModel.layers[:5]:
layer.trainable = False
model = self.add_new_last_layer(baseModel, self.config.num_classes)
model.compile(optimizer=SGD(lr=0.001, momentum=0.9), loss='categorical_crossentropy', metrics=['accuracy'])
aug.fit(self.trainX)
H = model.fit_generator(aug.flow(self.trainX, self.trainY, batch_size=self.BAT_SIZE),
validation_data=(self.testX, self.testY), steps_per_epoch=len(self.trainX) // self.BAT_SIZE,
epochs=self.NB_EPOCHS, verbose=1)
model.save('my-vgg-model.h5')
def trainInception(self):
self.prepare_test_train_data('inception')
# create the base pre-trained model
base_model = InceptionV3(weights='imagenet', include_top=False, input_shape=(299, 299, 3))
# add a global spatial average pooling layer
x = base_model.output
x = GlobalAveragePooling2D()(x)
# let's add a fully-connected layer
x = Dense(1024, activation='relu')(x)
x = Dense(1024, activation='relu')(x)
x = Dense(512, activation='relu')(x)
x = Dense(256, activation='relu')(x)
# and a logistic layer -- let's say we have 200 classes
predictions = Dense(self.config.num_classes, activation='sigmoid')(x)
# this is the model we will train
model = Model(inputs=base_model.input, outputs=predictions)
# first: train only the top layers (which were randomly initialized)
# i.e. freeze all convolutional InceptionV3 layers
for layer in base_model.layers:
layer.trainable = False
# compile the model (should be done *after* setting layers to non-trainable)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
aug = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=False,
fill_mode="nearest")
aug.fit(self.trainX)
# train the model on the new data for a few epochs
model.fit_generator(aug.flow(self.trainX, self.trainY, batch_size=self.BAT_SIZE),
validation_data=(self.testX, self.testY), steps_per_epoch=len(self.trainX) // self.BAT_SIZE,
epochs=3, verbose=1)
# at this point, the top layers are well trained and we can start fine-tuning
# convolutional layers from inception V3. We will freeze the bottom N layers
# and train the remaining top layers.
# let's visualize layer names and layer indices to see how many layers
# we should freeze:
for i, layer in enumerate(base_model.layers):
print(i, layer.name)
# we chose to train the top 2 inception blocks, i.e. we will freeze
# the first 249 layers and unfreeze the rest:
for layer in model.layers[:6]:
layer.trainable = False
for layer in model.layers[6:]:
layer.trainable = True
# we need to recompile the model for these modifications to take effect
# we use SGD with a low learning rate
from keras.optimizers import SGD
model.compile(optimizer=SGD(lr=0.001, momentum=0.9), loss='categorical_crossentropy', metrics=['accuracy'])
# we train our model again (this time fine-tuning the top 2 inception blocks
# alongside the top Dense layers
h = model.fit_generator(aug.flow(self.trainX, self.trainY, batch_size=self.BAT_SIZE),
validation_data=(self.testX, self.testY), steps_per_epoch=len(self.trainX) // self.BAT_SIZE,
epochs=20, verbose=1)
model.save('my-inception-model.h5')
self.plot_training(h)
def plot_training(self, H):
# plot the training loss and accuracy
plt.style.use("ggplot")
plt.figure()
N = self.NB_EPOCHS
print(H.history)
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
plt.title("Training Loss for Sketch Model")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig('results.pdf')
def predict(self, image_path):
image = self.process_image_for_custom(image_path)
image = image.reshape(1, 200, 200, 1).astype('float32')
image = image / 255
model = load_model('./models/custom-model-latest.h5')
yhat = model.predict_classes(image)
markup = self.get_markup(yhat[0])
return markup
def get_markup(self, id):
return [c for c in self.config.load_classes() if c['id'] == id][0]
def custom_model(self):
# create model
model = Sequential()
model.add(Conv2D(30, (5, 5), input_shape=(200, 200, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(15, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(50, activation='relu'))
model.add(Dense(self.num_classes, activation='softmax'))
# Compile model
model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
return model
def train_custom_model(self):
self.prepare_test_train_data('custom')
# reshape to be [samples][pixels][width][height]
self.trainX = self.trainX.reshape(self.trainX.shape[0], 200, 200, 1).astype('float32')
self.testX = self.testX.reshape(self.testX.shape[0], 200, 200, 1).astype('float32')
# normalize inputs from 0-255 to 0-1
self.trainX = self.trainX / 255
self.testX = self.testX / 255
# one hot encode outputs
self.num_classes = self.testY.shape[1]
model = self.custom_model()
aug = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=False,
fill_mode="nearest")
aug.fit(self.trainX)
# Fit the model
h = model.fit_generator(aug.flow(self.trainX, self.trainY, batch_size=32), validation_data=(self.testX, self.testY), steps_per_epoch=len(self.trainX) // 32, epochs=self.NB_EPOCHS)
# Final evaluation of the model
scores = model.evaluate(self.testX, self.testY, verbose=0)
model.save('./models/custom-model-latest.h5')
self.plot_training(h)
print("Large CNN Error: %.2f%%" % (100 - scores[1] * 100))
# SketchModel(Config).train_custom_model()