-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathtrain_iwpodnet_tf2.py
222 lines (177 loc) · 6.31 KB
/
train_iwpodnet_tf2.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
"""
Main file for training IWPOD-NET license plate detector.
In the paper training was performed in a per-batch manner, but in this
file training is performed per-epoch.
In general, you should run at least 100-150K iterations for the paper dataset.
Hence, choose the number of epochs based on the number of iterations per epoch
@author: Claudio Rosito Jung
"""
import tensorflow as tf
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras.optimizers import Adam
import numpy as np
import cv2
import pandas as pd
import argparse
from tensorflow import keras
from os.path import isfile, isdir, splitext
from os import makedirs
from src.label import readShapes, Shape
from src.loss import iwpodnet_loss
from src.utils import image_files_from_folder
from src.data_generator_tf2 import ALPRDataGenerator
from create_model_iwpodnet import create_model_iwpodnet
from tensorflow.keras.callbacks import LearningRateScheduler
#
# LR scheduler - can use it to reduce learning rate
#
def lr_scheduler(ChangeEpoch = 20000, initial_lr = 1e-3):
def scheduler(epoch):
if epoch < ChangeEpoch:
return initial_lr
elif epoch < 2*ChangeEpoch:
return initial_lr/5
else:
return initial_lr/25
return scheduler
def load_network(modelpath, input_dim):
#
# Creates model topology
#
model = create_model_iwpodnet()
#
# Loads weights -- if they exist
#
if isfile(modelpath + '.h5'):
model.load_weights(modelpath + '.h5')
print('Loaded weights')
else:
print('Training from scratch')
input_shape = (input_dim,input_dim,3)
# Fixed input size for training
inputs = keras.layers.Input(shape=(input_dim,input_dim, 3))
#
# Gets size of output layer
#
outputs = model(inputs)
if tf.__version__.startswith('1'):
output_shape = tuple([s.value for s in outputs.shape[1:]])
else:
output_shape = tuple([s for s in outputs.shape[1:]])
output_dim = output_shape[1]
model_stride = input_dim / output_dim
assert input_dim % output_dim == 0, \
'The output resolution must be divisible by the input resolution'
assert model_stride == 2**4, \
'Make sure your model generates a feature map with resolution ' \
'16x smaller than the input'
return model, model_stride, input_shape, output_shape
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-md' ,'--model-dir' ,type=str , default = 'weights' ,help='Directory containing models and weights')
parser.add_argument('-cm' ,'--cur_model' ,type=str , default = 'fake_name' ,help='Pre-trained model')
parser.add_argument('-n' ,'--name' ,type=str , default = 'iwpodnet_retrained' ,help='Output model name')
parser.add_argument('-tr' ,'--train-dir' ,type=str , default = 'train_dir' ,help='Input data directory for training')
parser.add_argument('-e' ,'--epochs' ,type=int , default = 60000 ,help='Number of epochs (default = 1.500)')
parser.add_argument('-bs' ,'--batch-size' ,type=int , default = 52 ,help='Mini-batch size (default = 64)')
parser.add_argument('-lr' ,'--learning-rate' ,type=float , default = 0.001 ,help='Learning rate (default = 0.001)')
parser.add_argument('-se' ,'--save-epochs' ,type = int , default = 2000 ,help='Freqnecy for saving checkpoints (in epochs) ')
args = parser.parse_args()
#
# Training parameters
#
MaxEpochs = args.epochs
batch_size = args.batch_size
learning_rate = args.learning_rate
save_epochs = args.save_epochs
netname = args.name
train_dir = args.train_dir
modeldir = args.model_dir
train_dir = args.train_dir
modelname = '%s/%s' % (modeldir, args.cur_model)
#
# Additional parameters
#
dim = 208 # spatial dimension of images in training stage
opt = Adam(learning_rate = learning_rate) # Optimizer -- can change
if not isdir(modeldir):
makedirs(modeldir)
#
# Loads model with pre-trained weights - if present
#
model, model_stride, xshape, yshape = load_network(modelname, dim)
model_path_final = '%s/%s' % (modeldir, netname)
#
# Loads training data
#
print ('Loading training data...')
Files = image_files_from_folder(train_dir)
#
# Defines size of "fake" tiny LP annotation, used when no LP is present
#
fakepts = np.array([[0.5, 0.5001, 0.5001, 0.5], [0.5, 0.5, 0.5001, 0.5001]])
fakeshape = Shape(fakepts)
Data = []
ann_files = 0
for file in Files:
labfile = splitext(file)[0] + '.txt'
if isfile(labfile):
ann_files += 1
L = readShapes(labfile)
I = cv2.imread(file)
if len(L) > 0:
Data.append([I, L])
else:
#
# Appends a "fake" plate to images without any annotation
#
I = cv2.imread(file)
Data.append( [I, [fakeshape] ] )
print ('%d images with labels found' % len(Data) )
print ('%d annotation files found' % ann_files )
#
# Training generator with lots of data augmentation
#
train_generator = ALPRDataGenerator(Data, batch_size = batch_size, dim = dim, stride = int(model_stride), shuffle=True, OutputScale = 1.0)
#
# Compiles Model
#
model.compile(
loss = iwpodnet_loss,
optimizer = opt,
)
#
# Callbacks
#
# -> Model Chekcpoints -- save evey "save_epochs" epochs
ckpt = ModelCheckpoint(
filepath = model_path_final + '_epoch{epoch:03d}.h5',
save_freq= int( np.floor(len(Data)/batch_size)*save_epochs) # defines frequency of checkpoints
)
# -> Learning rate control -- can also reduce learning rate dynamically
learn_control = LearningRateScheduler(lr_scheduler(ChangeEpoch = MaxEpochs//3, initial_lr = learning_rate))
# -> early stopping criteria -- not currently used
es = EarlyStopping(monitor='loss',
patience = MaxEpochs//30,
restore_best_weights = False)
#
# Trains model
#
print('Starting to train the model')
history = model.fit(x = train_generator,
steps_per_epoch = np.floor(len(Data)/batch_size),
epochs = MaxEpochs,
verbose = 1,
callbacks=[learn_control, ckpt])
print('Finished to train the model')
#
# Saves training details to excel file
#
df = pd.DataFrame(history.history)
df.to_excel(model_path_final + '.xlsx')
#
# Saves trained weights and model (TF2) format
#
print ('Saving model (%s)' % model_path_final)
model.save_weights(model_path_final + '.h5', save_format ='h5')
model.save(model_path_final)