使用深度学习防止在多类分类中过度拟合特定类

Posted

技术标签:

【中文标题】使用深度学习防止在多类分类中过度拟合特定类【英文标题】:Prevent overfitting to specific class in multi-class classification using deep learning 【发布时间】:2019-06-06 14:43:43 【问题描述】:

我正在使用 Keras 中的 U-Net 架构(TF 后端)对许多 256x256 图像执行逐像素多类分类。我使用数据生成器对我的输出进行了一次热编码,使我的输出为 256x256x32 数组(我有 32 个不同的类,这些类表示为像素值,它们是 256x256“掩码”图像中 0-31 的整数)。

但是,大多数地面实况数组都是空的 - 换句话说,到目前为止最常见的类是 0。当我训练我的 U-Net 时,它似乎过拟合到 0 类。损失很低,准确率很高,但这只是因为大约 99% 的 ground truth 是 0,所以 U-Net 只输出一堆 0,而我只真正关心其他 31 个类(例如,有多好它可以对ground truth中的其余类进行分类)。

在计算损失函数时,有没有办法让某些类比其他类更“加权”(如果有,这种方法是否合适)?我不确定这是我的数据的内在问题,还是我的方法的问题。这是我的 U-Net:

def unet(pretrained_weights = None,input_size = (256,256,1)):
inputs = keras.engine.input_layer.Input(input_size)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
#drop4 = Dropout(0.5)(conv4)
drop4 = SpatialDropout2D(0.5)(conv4)
pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)

conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
#drop5 = Dropout(0.5)(conv5)
drop5 = SpatialDropout2D(0.5)(conv5)

up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))
merge6 = concatenate([drop4,up6], axis = 3)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)

up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))
merge7 = concatenate([conv3,up7], axis = 3)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)

up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))
merge8 = concatenate([conv2,up8], axis = 3)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)

up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
merge9 = concatenate([conv1,up9], axis = 3)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
conv9 = Conv2D(32, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
conv10 = Conv2D(32, 1, activation = 'softmax')(conv9)
#conv10 = Flatten()(conv10)
#conv10 = Dense(65536, activation = 'softmax')(conv10)
flat10 = Reshape((65536,32))(conv10)
#conv10 = Conv1D(1, 1, activation='linear')(conv10)

model = Model(inputs = inputs, outputs = flat10)

opt = Adam(lr=1e-6,clipvalue=0.01)
model.compile(optimizer = opt, loss = 'categorical_crossentropy', metrics = ['categorical_accuracy'])
#model.compile(optimizer = Adam(lr = 1e-6), loss = 'binary_crossentropy', metrics = ['accuracy'])
#model.compile(optimizer = Adam(lr = 1e-4),

#model.summary()

if(pretrained_weights):

    model.load_weights(pretrained_weights)

return model

如果需要更多信息来诊断问题,请告诉我。

【问题讨论】:

您也许可以尝试加权分类交叉熵函数,并为稀有类分配更多权重?这可能是一个可能的实现:gist.github.com/wassname/ce364fddfc8a025bfab4348cf5de852d 了解加权类和/或样本。一个相关的问题可以在这里找到:***.com/questions/43459317/… 和另一个在这里:datascience.stackexchange.com/questions/13490/… 啊,是的,我忽略了这一点,不需要自定义损失函数:) 【参考方案1】:

处理不平衡类的一个常见解决方案是对某些类赋予比其他类更高的权重。这在 Keras 中很容易,在训练期间使用可选的class_weight 参数。

model.fit(x, y, class_weight=class_weight)

您可以在字典中自己定义类权重:

class_weight = 0: 1, 1: 100

或者,您可以使用sklearn 函数compute_class_weight 从您的数据中自动生成权重。

class_weights = class_weight.compute_class_weight('balanced', np.unique(y), y)

【讨论】:

感谢您的帮助!

以上是关于使用深度学习防止在多类分类中过度拟合特定类的主要内容,如果未能解决你的问题,请参考以下文章

如何解决基于 NLP 的 CNN 模型中的过度拟合问题,以使用词嵌入进行多类文本分类?

动手学深度学习第一课:从上手到多类分类-Autograd

动手学深度学习第一课:从上手到多类分类-NDArray

CNN模型在多类分类上过拟合

深度学习防止过拟合的方法

用于多类分类的深度神经网络的广义二次损失