python - 使用 Keras 稀疏分类交叉熵进行像素级多类分类
问题描述
我将首先披露我是一名机器学习和 Keras 新手,除了一般的 CNN 二进制分类器之外,我知道的不多。我正在尝试使用 U-Net 架构(TF 后端)对许多 256x256 图像执行像素级多类分类。换句话说,我输入一个 256x256 的图像,我希望它输出一个 256x256 的“掩码”(或标签图像),其中的值是 0-30 之间的整数(每个整数代表一个唯一的类)。我正在使用 2 个 1080Ti NVIDIA GPU 进行训练。
当我尝试执行 one-hot 编码时,我得到一个 OOM 错误,这就是为什么我使用稀疏分类交叉熵作为我的损失函数而不是常规分类交叉熵。但是,在训练我的 U-Net 时,我的损失值从头到尾都是“nan”(它初始化为 nan 并且永远不会改变)。当我通过将所有值除以 30(所以它们从 0-1 开始)来标准化我的“掩码”时,我得到 ~0.97 的准确度,我猜这是因为我的图像中的大多数标签都是 0(它只是输出一堆0)。
这是我正在使用的 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(1, 1, activation = 'softmax')(conv9)
#conv10 = Flatten()(conv10)
#conv10 = Dense(65536, activation = 'softmax')(conv10)
flat10 = Reshape((65536,1))(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 = 'sparse_categorical_crossentropy', metrics = ['sparse_categorical_accuracy'])
#model.compile(optimizer = Adam(lr = 1e-6), loss = 'sparse_categorical_crossentropy', metrics = ['accuracy'])
#model.compile(optimizer = Adam(lr = 1e-4),
#model.summary()
if(pretrained_weights):
model.load_weights(pretrained_weights)
return model
请注意,我需要展平输出只是为了让稀疏分类交叉熵正常运行(由于某种原因它不喜欢我的二维矩阵)。
这是一个训练运行的例子(只有 1 个 epoch,因为无论我运行多少次都是一样的)
model = unet()
model.fit(x=x_train, y=y_train, batch_size=1, epochs=1, verbose=1, validation_split=0.2, shuffle=True)
训练 2308 个样本,验证 577 个样本 Epoch 1/1 2308/2308 [==============================] - 191s 83 毫秒/步 - 损失:nan - sparse_categorical_accuracy:0.9672 - val_loss:nan - val_sparse_categorical_accuracy:0.9667 Out[18]:
让我知道是否需要更多信息来诊断问题。提前致谢!
解决方案
问题在于,对于多类分类,您需要输出一个每个类别一维的向量,它代表该类别的置信度。如果你想识别 30 个不同的类,那么你的最后一层应该是一个 3D 张量,(256, 256, 30)。
conv10 = Conv2D(30, 1, activation = 'softmax')(conv9)
flat10 = Reshape((256*256*30,1))(conv10)
opt = Adam(lr=1e-6,clipvalue=0.01)
model.compile(optimizer = opt, loss = 'sparse_categorical_crossentropy', metrics =
['sparse_categorical_accuracy'])
我假设您的输入是 (256, 256, 1) 浮点张量,其值介于 0 和 1 之间,而您的目标是 (256*256) Int 张量。
这有帮助吗?
推荐阅读
- time-complexity - 蛮力 GCD 算法的复杂性
- xcode - Xcode 10 故事板 UI 组件不可见且速度缓慢?
- server - Squarespace Local Development 似乎不想工作
- vba - VBA 如何使用单值复制/粘贴和偏移创建数据验证循环
- python - 如何在 Python 中找到多边形的内部中心?
- scala - 创建通用 KafkaConsumer 时出错
- vis.js-network - 单击时禁用自动选择节点?
- scala - Jackson scala对象映射器未正确序列化嵌套案例类
- r - 如何使用 tidyverse 确定不同长度的分组变量之间的日期/时间间隔?
- excel - 将数据复制到具有连续每月日期的更多行中的宏