如果我在火车上的准确率大约为 100%,为什么我的预测不好(Keras CNN)

数据挖掘 机器学习 Python 喀拉斯 卷积神经网络 vgg16
2021-09-30 23:26:23

在我的 CNN 中,我必须在二进制系统中处理 2 个类,每个类有 700 个图像要训练,其他的要验证。这是我的 train.py:

#import tensorflow as tf
import cv2
import os
import numpy as np

from keras.layers.core import Flatten, Dense, Dropout, Reshape
from keras.models import Model
from keras.layers import Input, ZeroPadding2D, Dropout
from keras import optimizers
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import EarlyStopping

from keras.applications.vgg16 import VGG16

TRAIN_DIR = 'train/'
TEST_DIR = 'test/'
v = 'v/'
BATCH_SIZE = 32
NUM_EPOCHS = 5

def crop_img(img, h, w):
    h_margin = (img.shape[0] - h) // 2 if img.shape[0] > h else 0
    w_margin = (img.shape[1] - w) // 2 if img.shape[1] > w else 0

    crop_img = img[h_margin:h + h_margin,w_margin:w + w_margin,:]

    return crop_img

def subtract_gaussian_blur(img):

    return cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0, 0), 5), -4, 128)

def ReadImages(Path):
    LabelList = list()
    ImageCV = list()
    classes = ["nonPdr", "pdr"]

    FolderList = [f for f in os.listdir(Path) if not f.startswith('.')]
    
    for File in FolderList:
        for index, Image in enumerate(os.listdir(os.path.join(Path, File))):
            
            ImageCV.append(cv2.resize(cv2.imread(os.path.join(Path, File) + os.path.sep + Image), (224,224)))
            
            LabelList.append(classes.index(os.path.splitext(File)[0])) 
            
            img_crop = crop_img(ImageCV[index].copy(), 224, 224)
            
            ImageCV[index] = subtract_gaussian_blur(img_crop.copy())
            
    return ImageCV, LabelList

data, labels = ReadImages(TRAIN_DIR)
valid, vlabels = ReadImages(TEST_DIR)

vgg16_model = VGG16(weights="imagenet", include_top=True)

base_model = Model(input=vgg16_model.input, 
                   output=vgg16_model.get_layer("block5_pool").output)

base_out = base_model.output
base_out = Reshape((25088,))(base_out)
top_fc1 = Dense(4096, activation="relu")(base_out)
top_fc1 = Dropout(0.5)(base_out)
top_fc1 = Dense(4096, activation="relu")(base_out)
top_fc1 = Dropout(0.5)(base_out)
top_fc1 = Dense(64, activation="relu")(base_out)
top_fc1 = Dropout(0.5)(base_out)

top_preds = Dense(1, activation="sigmoid")(top_fc1)

for layer in base_model.layers[0:14]:
    layer.trainable = False

model = Model(input=base_model.input, output=top_preds)
    
sgd = SGD(lr=1e-4, momentum=0.9)
model.compile(optimizer=sgd, loss="binary_crossentropy", metrics=["accuracy"])

data = np.asarray(data)
valid = np.asarray(valid)

data = data.astype('float32')
valid = valid.astype('float32')

data /= 255
valid /= 255
labels = np.array(labels)

perm = np.random.permutation(len(data))
data = data[perm]
labels = labels[perm]

datagen = ImageDataGenerator(
    featurewise_center=True,
    featurewise_std_normalization=True,
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True)

datagen.fit(data) 
mean = datagen.mean #This result I put manually in predict.py  
std = datagen.std #This result I put manually in predict.py

print(mean, "mean")
print(std, "std")

es = EarlyStopping(monitor='val_loss', verbose=1)

model.fit_generator(datagen.flow(data, np.array(labels), batch_size=32), 
                    steps_per_epoch=len(data) / 32, epochs=15,
                    validation_data=(valid, np.array(vlabels)),
                    nb_val_samples=72, callbacks=[es])

model.save('model.h5')

在运行这段代码之后,它会在 5 或 6 个 epoch 后返回一个大约 100% 准确率的奇怪结果。所以我尝试运行我的 predict.py 代码:(我知道我必须封装一些方法,但现在我只是从 train 复制并粘贴所有内容)

from keras.models import load_model
import cv2
import os
import numpy as np

TEST_DIR = 'v/0/'
pdr = 0
nonPdr = 0

model = load_model('model.h5')

def normalize(x, mean, std):
    x[..., 0] -= mean[0]
    x[..., 1] -= mean[1]
    x[..., 2] -= mean[2]
    x[..., 0] /= std[0]
    x[..., 1] /= std[1]
    x[..., 2] /= std[2]
    return x

def crop_img(img, h, w):
    h_margin = (img.shape[0] - h) // 2 if img.shape[0] > h else 0
    w_margin = (img.shape[1] - w) // 2 if img.shape[1] > w else 0

    crop_img = img[h_margin:h + h_margin,w_margin:w + w_margin,:]

    return crop_img

def subtract_gaussian_blur(img):

    return cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0, 0), 5), -4, 128)

for filename in os.listdir(r'v/0/'):
    if filename.endswith(".jpg") or filename.endswith(".ppm") or filename.endswith(".jpeg") or filename.endswith(".png"):
        ImageCV = cv2.resize(cv2.imread(os.path.join(TEST_DIR) + filename), (224,224))

        img_crop = crop_img(ImageCV.copy(), 224, 224)
            
        ImageCV = subtract_gaussian_blur(img_crop.copy())

        ImageCV = np.asarray(ImageCV)
        
        ImageCV = ImageCV.astype('float32')
        
        ImageCV /= 255  
        
        ImageCV = np.expand_dims(ImageCV, axis=0)
        ImageCV = normalize(ImageCV, [0.23883381, 0.23883381, 0.23883381], [0.20992693, 0.25749, 0.26330808]) #Values from train

        prob = model.predict(ImageCV)
        if prob <= 0.75:  #.75 = 80% | .70=79% >>>> .70 = 82% | .75 = 79%
            print("nonPDR >>>", filename)
            nonPdr += 1
        else:
            print("PDR >>>", filename)
            pdr += 1
        print(prob)
print("Number of retinas with PDR: ",pdr)
print("Number of retinas without PDR: ",nonPdr)

问题是:当我尝试预测时,我几乎所有的预测都很差(对所有图像的预测都是 nonPdr 或 0 类)。我已经尝试切断数据增强进行测试,结果并没有改变我想要的方式。我也尝试改变我的模型,改变预处理(这个预处理是我可以用于这个项目的最好的)并且永远不会发生。

我该如何处理?

更新

正如@serali 所说,我试图削减一些层以减少过度拟合。这是我现在的模型:

vgg16_model = VGG16(weights="imagenet", include_top=True)
 
    #visualize layers
print("VGG16 model layers")
for i, layer in enumerate(vgg16_model.layers):
    print(i, layer.name, layer.output_shape)

# (2) remove the top layer
base_model = Model(input=vgg16_model.input, 
                   output=vgg16_model.get_layer("block1_pool").output)

# (3) attach a new top layer
base_out = base_model.output
top_fc1 = GlobalAveragePooling2D()(base_out)
top_fc2 = Dense(16, activation='relu')(top_fc1)
top_fc3 = Dropout(0.5)(top_fc2)
top_preds = Dense(1, activation="sigmoid")(top_fc3)

# (5) create new hybrid model
model = Model(input=base_model.input, output=top_preds)

如您所见,我切入了第一个卷积块,因此我的模型如下所示:

0 input_1 (None, 224, 224, 3)
1 block1_conv1 (None, 224, 224, 64)
2 block1_conv2 (None, 224, 224, 64)
3 block1_pool (None, 112, 112, 64)
top_fc1 = GlobalAveragePooling2D()(base_out)
top_fc2 = Dense(16, activation='relu')(top_fc1)
top_fc3 = Dropout(0.5)(top_fc2)
top_preds = Dense(1, activation="sigmoid")(top_fc3)

但是,当我尝试预测我训练过的相同图像时,预测是错误的(对于外国图像,结果是相同的)。那么,我该如何改进呢?

2个回答

这种现象称为过拟合简而言之,这意味着你的 CNN 已经记住了数据集,实现了100%训练准确率。然而,这种知识并不能很好地推广到看不见的数据。

我建议阅读这篇文章,了解有关过度拟合和对抗它的方法的更多详细信息。

在 6 个 epoch 后获得 100% 的结果时,几乎可以肯定(至少根据我的经验)在训练的早期阶段出现问题......我将首先调试并验证 ReadImages 中的标签提取是否按预期工作并手动比较至少一些预测。不太可能的可能性是训练/验证集本身可能有问题......您可以尝试检查它们是否具有足够的可变性,例如