如何使用CNN修复欠拟合/为什么我的代码无法正确识别图像?



我对神经网络很陌生,并尝试编写自己的代码来对图像进行分类。我一直在使用混凝土裂缝图像进行分类(https://data.mendeley.com/datasets/5y9wdsg2zt/2(来分类图像是否有裂缝或没有缺陷。从该数据集中,我随机提取了 2.000 张图像,其中 1.400 张用于我的训练集,300 张用于我的验证和测试集。一半的图像是正的/显示裂缝,另一半是负片/没有缺陷。

对于分类,我使用的是在ImageNet上预先训练的VGG16。在下面,您可以看到我的完整代码,我使用尝试解决类似任务的不同教程将其组合在一起。

不幸的是,它无法识别单个裂缝图像,并将所有内容归类为负面/无缺陷。 我尝试了不同的批量大小、纪元数量、图像数量,在没有经过预先训练的情况下尝试过,但似乎没有任何效果,我完全不知道为什么。我真的很感激一些帮助,所以提前谢谢你!

如果还有任何问题,请随时提问。

import tensorflow as tf
import os
import numpy as np
import keras
from keras.preprocessing import image
from PIL import Image
import os.path, sys
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import scipy as sp
from tensorflow.keras.applications import vgg16
from tensorflow.keras.preprocessing.image import load_img, img_to_array, array_to_img, ImageDataGenerator
from tensorflow.keras.models import *
from tensorflow.keras.layers import *
from tensorflow.keras import optimizers
from tensorflow.keras.utils import *
import requests
from io import BytesIO
import random
import pickle
import itertools
from sklearn.metrics import classification_report, confusion_matrix
# load data
PATH = '/Volumes/ConcreteCrackImages'
train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')
test_dir = os.path.join(PATH, 'test')
train_negative_dir = os.path.join(train_dir, 'Negative')  # directory with our training negative pictures
train_crack_dir = os.path.join(train_dir, 'Positive')  # directory with our training crack pictures
validation_negative_dir = os.path.join(validation_dir, 'Negative')  # directory with our validation negative pictures
validation_crack_dir = os.path.join(validation_dir, 'Positive')  # directory with our validation crack pictures
test_negative_dir = os.path.join(test_dir, 'Negative')  # directory with our test negative pictures
test_crack_dir = os.path.join(test_dir, 'Positive')  # directory with our test crack pictures
# understand the data
num_negative_tr = len(os.listdir(train_negative_dir))
num_crack_tr = len(os.listdir(train_crack_dir))
num_negative_val = len(os.listdir(validation_negative_dir))
num_crack_val = len(os.listdir(validation_crack_dir))
num_negative_test = len(os.listdir(test_negative_dir))
num_crack_test = len(os.listdir(test_crack_dir))
total_train = num_negative_tr + num_crack_tr
total_val = num_negative_val + num_crack_val
total_test = num_negative_test + num_crack_test
print('total training negative images:', num_negative_tr)
print('total training crack images:', num_crack_tr)
print('total validation negative images:', num_negative_val)
print('total validation crack images:', num_crack_val)
print('total test negative images:', num_negative_test)
print('total test crack images:', num_crack_test)
print("--")
print("Total training images:", total_train)
print("Total validation images:", total_val)
print("Total test images:", total_test)
# variables for pre-processing
batch_size = 32
epochs = 40
IMG_HEIGHT = 224
IMG_WIDTH = 224
# data preparation
train_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our training data
validation_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our validation data
test_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our test data
train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
directory=train_dir,
shuffle=True,
target_size=(IMG_HEIGHT, IMG_WIDTH),
class_mode='binary')
val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
directory=validation_dir,
target_size=(IMG_HEIGHT, IMG_WIDTH),
class_mode='binary')
test_data_gen = test_image_generator.flow_from_directory(batch_size=batch_size,
directory=test_dir,
target_size=(IMG_HEIGHT, IMG_WIDTH),
class_mode='binary')
# visualize training images
sample_training_images, _ = next(train_data_gen)
# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.
# =============================================================================
def plotImages(images_arr):
fig, axes = plt.subplots(1, 5, figsize=(20,20))
axes = axes.flatten()
for img, ax in zip( images_arr, axes):
ax.imshow(img)
ax.axis('off')
plt.tight_layout()
plt.show()
plotImages(sample_training_images[:5])
# =============================================================================
# create the model/ import vgg16
vgg_conv = vgg16.VGG16(weights='imagenet', include_top=False, input_shape = (224, 224, 3))
# Freeze the layers except the last 4 layers
for layer in vgg_conv.layers[:-8]:
layer.trainable = False
# Check the trainable status of the individual layers
for layer in vgg_conv.layers:
print(layer, layer.trainable)
### MODIFY VGG STRUCTURE ###
x = vgg_conv.output
x = GlobalAveragePooling2D()(x)
x = Dense(1, activation="sigmoid")(x)
model = Model(vgg_conv.input, x)
model.compile(loss = "binary_crossentropy", optimizer = optimizers.SGD(lr=0.00001, momentum=0.9), metrics=["accuracy"])
model.summary()
# train the model
history = model.fit(
train_data_gen,
steps_per_epoch=total_train // batch_size,
epochs=epochs,
validation_data=val_data_gen,
validation_steps=total_val // batch_size
)

# visualize training results
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss=history.history['loss']
val_loss=history.history['val_loss']
epochs_range = range(epochs)
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
# Evaluate the model on the test data using `evaluate`
print('nEvaluate on test data')
results = model.evaluate(test_data_gen,
verbose = 1)
print('test loss, test acc:', results)
#Confusion Matrix and Classification Report
Y_pred = model.predict(test_data_gen)
y_pred = np.argmax(Y_pred, axis=1)
print('Confusion Matrix')
print(confusion_matrix(test_data_gen.classes, y_pred))
print('Classification Report')
target_names = ['Negative', 'Crack']
print(classification_report(test_data_gen.classes, y_pred, target_names=target_names))

您可以检查几个问题。

  1. 由于您使用的是 VGG 和 ImageDataGenerator,因此必须确保图像数据生成器执行与所需的 VGG 预训练模型相同的预处理。VGG使用imagenet_utils.preprocessing_input进行训练,模式设置为"caffe"。总共有三种模式,咖啡,tf和火炬。通过不同的预处理保留不同的模型。

  2. 启动 VGG 模型时,将包含顶部设置为 false。然后你得到 VGG 的输出,做了一个全局池,然后添加一个密集的输出。如果你深入研究VGG实现的源代码,顶部只是softmax层,还有FC层。FC 层是在提取的 VGG 特征上生成抽象的方法。如果您没有足够的 FC 层,则您的模型不够复杂,无法很好地学习特征空间。

您至少可以尝试这两个,看看它们是否有帮助

最新更新