之前我试图建立一个通用的CNN,可以做图像分类。我在扑克牌上测试过,效果很好。我现在正试图修改输入和输出管道,以便它可以进行对象定位(返回类和边界框数据)。我不知道为什么,但这个模特不会训练。准确性不会提高。Rn模型有两个输出节点:分类器头(用于图像类)和回归器头(用于BBox)。如果我将回归头修改为具有0个单位(强制它只在类上训练,而不是在Bbox上训练),则分类器头的精度会提高。所有其他修改超参数的尝试都没有明显的效果。
有问题的代码:
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
import random
import numpy as np
import matplotlib.pyplot as plt
batch_size = 32
img_shape = 150
class_size = 52
shuffle_val = 200000
checkpoint_path = "D:\Python\Datasets\Playing Cards\My Generated DataSet\Checkpoints\weights-improvement-{epoch:02d}-{val_accuracy:.4f}.hdf5"
train_ds = tf.data.Dataset.list_files('D:\Python\Datasets\Playing Cards\Misc\Images with Black BG\Training\*\*', shuffle = False)
train_ds = train_ds.shuffle(shuffle_val) #accepts shuffle buffer
for i in train_ds.take(10):
print(i) #prints x number of the paths read. Used to confirm if shuffling is done properly.
print("\n\n Training ds len: ", len(train_ds))
def get_train_Bbox(train_file_path):
mylabel = tf.strings.split(train_file_path, os.sep)[-1] #returns entire file name
t_x1 = tf.strings.to_number(tf.strings.split(mylabel, ",")[2], tf.int32)
t_y1 = tf.strings.to_number(tf.strings.split(mylabel, ",")[3], tf.int32)
t_x2 = tf.strings.to_number(tf.strings.split(mylabel, ",")[4], tf.int32)
t_y2 = tf.strings.to_number(tf.strings.split(mylabel, ",")[5], tf.int32)
return t_x1, t_y1, t_x2, t_y2
def get_label(training_file_path):
label = tf.strings.split(training_file_path, os.sep)[-1]
label_class = (tf.strings.split(label, ",")[-2])
label = label_class
label = tf.strings.to_number(label, tf.int32)
label = tf.one_hot(label, class_size)
print("from label function,num of classes:", len(label))
print("Label shape after one-hot encoding:", label.shape)
return label
def process_image(training_file_path):
label = get_label(training_file_path)
img = tf.io.read_file(training_file_path)
img = tf.image.decode_jpeg(img)
img = tf.image.resize(img, (img_shape,img_shape))
img = tf.image.random_brightness(img, 0.2)
train_img = img/255
t_x1, t_y1, t_x2, t_y2 = get_train_Bbox(training_file_path)
print("Label shape after processing image:", label.shape)
return train_img, {'classifier_head': label, 'regressor_head': (t_x1, t_y1, t_x2, t_y2)}
train_ds = train_ds.map(process_image)
train_ds = train_ds.batch(batch_size)
for img, label in train_ds.take(1):
print("Training: ", label)
valid_ds = tf.data.Dataset.list_files('D:\Python\Datasets\Playing Cards\Misc\Images with Black BG\Validating\*\*', shuffle = False)
print("Len of Valid ds:", len(valid_ds))
def get_valid_label(valid_file_path):
valid_label = tf.strings.split(valid_file_path, os.sep)[-1]
valid_label_class = (tf.strings.split(valid_label, ",")[-2])
valid_label = valid_label_class
valid_label = tf.strings.to_number(valid_label, tf.int32)
valid_label = tf.one_hot(valid_label, class_size) #review this later
print("Validation Label shape after one-hot encoding:", valid_label.shape)
return valid_label
def get_valid_Bbox(valid_file_path):
v_label = tf.strings.split(valid_file_path, os.sep)[-1] #returns entire file name
v_x1 = tf.strings.to_number(tf.strings.split(v_label, ",")[2], tf.int32)
v_y1 = tf.strings.to_number(tf.strings.split(v_label, ",")[3], tf.int32)
v_x2 = tf.strings.to_number(tf.strings.split(v_label, ",")[4], tf.int32)
v_y2 = tf.strings.to_number(tf.strings.split(v_label, ",")[5], tf.int32)
return v_x1, v_y1, v_x2, v_y2
def process_valid_image(valid_file_path):
valid_label = get_valid_label(valid_file_path)
valid_img = tf.io.read_file(valid_file_path) #read the actual image file ie. the array
valid_img = tf.image.decode_jpeg(valid_img)
valid_img = tf.image.resize(valid_img, (img_shape,img_shape))
valid_img = valid_img/255
v_x1, v_y1, v_x2, v_y2 = get_train_Bbox(valid_file_path)
print("Valid Label shape after processing image:", valid_label.shape)
return valid_img, {'classifier_head': valid_label, 'regressor_head': (v_x1, v_y1, v_x2, v_y2)}
valid_ds = valid_ds.map(process_valid_image, num_parallel_calls=tf.data.AUTOTUNE)
valid_ds = valid_ds.batch(batch_size)
for img, label in valid_ds.take(1):
print("Valid: ", label)
def build_feature_extractor(inputs):
x = tf.keras.layers.Conv2D(32, (3,3), activation = "relu", input_shape=(img_shape,img_shape,3), padding = "same")(inputs)
x = tf.keras.layers.MaxPooling2D(2,2)(x)
x = tf.keras.layers.Conv2D(64, (3,3), activation = "relu", input_shape=(img_shape,img_shape,3), padding = "same")(x)
x = tf.keras.layers.MaxPooling2D(2,2)(x)
x = tf.keras.layers.Conv2D(128, (3,3), activation = "relu", input_shape=(img_shape,img_shape,3), padding = "same")(x)
x = tf.keras.layers.MaxPooling2D(2,2)(x)
x = tf.keras.layers.Conv2D(512, (3,3), activation = "relu", input_shape=(img_shape,img_shape,3), padding = "same")(x)
x = tf.keras.layers.MaxPooling2D(2,2)(x)
return x
def build_model_adaptor(inputs):
x = tf.keras.layers.Flatten()(inputs)
x = tf.keras.layers.Dense(512, activation= "relu")(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(128, activation= "relu")(x)
x = tf.keras.layers.Dense(64, activation= "relu")(x)
return x
def build_classifier_head(inputs):
return tf.keras.layers.Dense(52, activation= "softmax", name = 'classifier_head')(inputs)
def build_regressor_head(inputs):
return tf.keras.layers.Dense(4, name = 'regressor_head')(inputs)
def build_model(inputs):
feature_extractor = build_feature_extractor(inputs)
model_adaptor = build_model_adaptor(feature_extractor)
classification_head = build_classifier_head(model_adaptor)
regressor_head = build_regressor_head(model_adaptor)
model = tf.keras.Model(inputs = inputs, outputs = [classification_head, regressor_head])
model.compile(optimizer = 'adam', loss = {'classifier_head' : 'categorical_crossentropy', 'regressor_head' : 'mse'}, metrics = {'classifier_head' : 'accuracy', 'regressor_head' : 'mse'})
return model
model = build_model(tf.keras.layers.Input(shape=(img_shape, img_shape, 3,)))
model.summary()
epochs=25
history = model.fit(train_ds,steps_per_epoch=int(np.ceil(26000 / float(batch_size))), epochs=epochs,validation_data=valid_ds, validation_steps=int(np.ceil(6500 / float(batch_size))))````
I've changed the kernel sizes, batch sizes, image sizes, epochs, number of layers. Attempted to change the loss from categorical cross entrophy to sparce categorical cross entrophy.
NB. The image data that is being used for training has already been augmented prior to being imported into the code. In the code I am just applying some extra brightness adjustments.