如何将Keras Conv3D输出转换为用于分类任务的扁平层?

gc0ot86w  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(90)

**我正在尝试构建一个可以同时分割和分类3D图像的模型。**我使用U-net 3D和keras Functional API,在模型的最后,我们得到两个输出分支,一个用于3DMap的分割,另一个用于癌症类型的分类问题。

该模型工作得很好,当我只试图解决分割问题,但结合两者抛出一些错误。

**这个问题是关于将Keras Conv 3D输出转换为用于softmax分类的flatten层。我使用了GlobalAveragePooling 3D和Flatten(),但问题仍然存在。

import segmentation_models_3D as sm
from keras.models import Model
from keras.layers import Input, Conv3D, MaxPooling3D, concatenate, Conv3DTranspose, BatchNormalization, Dropout, Lambda, Flatten, Dense , GlobalAveragePooling3D
#from keras.optimizers import Adam
import keras.backend as K    
from tensorflow.keras.optimizers import Adam
from keras.metrics import MeanIoU

kernel_initializer =  'he_uniform' #Try others if you want

def unet_model(IMG_HEIGHT, IMG_WIDTH, IMG_DEPTH, IMG_CHANNELS, num_classes):
#Build the model
    inputs = Input((IMG_HEIGHT, IMG_WIDTH, IMG_DEPTH, IMG_CHANNELS), name="img")
    #s = Lambda(lambda x: x / 255)(inputs)   #No need for this if we normalize our inputs beforehand
    s = inputs

    #Contraction path
    c1 = Conv3D(16, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(s)
    c1 = Dropout(0.1)(c1)
    c1 = Conv3D(16, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c1)
    p1 = MaxPooling3D((2, 2, 2))(c1)

    c2 = Conv3D(32, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(p1)
    c2 = Dropout(0.1)(c2)
    c2 = Conv3D(32, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c2)
    p2 = MaxPooling3D((2, 2, 2))(c2)

    c3 = Conv3D(64, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(p2)
    c3 = Dropout(0.2)(c3)
    c3 = Conv3D(64, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c3)
    p3 = MaxPooling3D((2, 2, 2))(c3)

    c4 = Conv3D(128, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(p3)
    c4 = Dropout(0.2)(c4)
    c4 = Conv3D(128, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c4)
    p4 = MaxPooling3D(pool_size=(2, 2, 2))(c4)

    c5 = Conv3D(256, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(p4)
    c5 = Dropout(0.3)(c5)
    c5 = Conv3D(256, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c5)

    #Expansive path
    u6 = Conv3DTranspose(128, (2, 2, 2), strides=(2, 2, 2), padding='same')(c5)
    u6 = concatenate([u6, c4])
    c6 = Conv3D(128, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(u6)
    c6 = Dropout(0.2)(c6)
    c6 = Conv3D(128, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c6)

    u7 = Conv3DTranspose(64, (2, 2, 2), strides=(2, 2, 2), padding='same')(c6)
    u7 = concatenate([u7, c3])
    c7 = Conv3D(64, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(u7)
    c7 = Dropout(0.2)(c7)
    c7 = Conv3D(64, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c7)

    u8 = Conv3DTranspose(32, (2, 2, 2), strides=(2, 2, 2), padding='same')(c7)
    u8 = concatenate([u8, c2])
    c8 = Conv3D(32, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(u8)
    c8 = Dropout(0.1)(c8)
    c8 = Conv3D(32, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c8)

    u9 = Conv3DTranspose(16, (2, 2, 2), strides=(2, 2, 2), padding='same')(c8)
    u9 = concatenate([u9, c1])
    c9 = Conv3D(16, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(u9)
    c9 = Dropout(0.1)(c9)
    c9 = Conv3D(16, (3, 3, 3), activation='relu', kernel_initializer=kernel_initializer, padding='same')(c9)

    output_mask = Conv3D(num_classes, (1, 1, 1), activation='softmax', name ='mask')(c9)
    output_mask_1 = Conv3D(num_classes, (1, 1, 1), activation='softmax')(c9)
    #outputs2 = Conv3D(num_classes, (1, 1, 1), activation='softmax')(c9)
    # addding 1d part

    # ..........................................................................  
    # aggregated = Lambda(lambda x: K.sum(x, axis=1))(output_mask_1)
    # flat = Flatten()(aggregated)
    # output_label = Dense(3, activation="relu")(flat)
    # output_label = Dense(1,name="lab",activation="relu")(output_label)
    #......................................................................

    output_label = GlobalAveragePooling3D()(output_mask_1)
    
    
    output_label = Dense(units=4, activation="relu")(output_label)
    output_label = Dense(units=3, activation="softmax",name="lab")(output_label)    
    
    
    model = Model(inputs=inputs, outputs=[output_mask
                                            , output_label
                                            ], name = "final_output")
    #compile model outside of this function to make it flexible.
    model.summary()

    return model

数据集预处理,其中我产生1个输入图像和2组标签。

def imageLoader(img_dir, img_list, mask_dir, mask_list, label_dir, label_list, batch_size): # some changes
    L = 5#len(img_list)
    #keras needs the generator infinite, so we will use while true
    while True:
        batch_start = 0
        batch_end =  batch_size # L-1
        while batch_start < L:
            limit = min(batch_end, L)
            X = load_img(img_dir, img_list[batch_start:limit])
            Y = load_img(mask_dir, mask_list[batch_start:limit])

            # add classification label.
            
            yield ({"img":X}, {"mask":Y,"lab":np.array([[0,1,0],[0,1,0]]) }) # one hot encoding for 3 class classification.
            
            

            batch_start += batch_size
            batch_end += batch_size

基本设置。

import segmentation_models_3D as sm
dice_loss = sm.losses.DiceLoss(class_weights=np.array([wt0, wt1, wt2, wt3]))
focal_loss = sm.losses.CategoricalFocalLoss()
total_loss = dice_loss + (1 * focal_loss)

metrics = [ sm.metrics.IOUScore(threshold=0.5), 'accuracy']

LR = 0.0001
#optim = keras.optimizers.Adam(LR)
optim=tf.keras.optimizers.Adam(LR)

model = unet_model(IMG_HEIGHT=128,
                          IMG_WIDTH=128,
                          IMG_DEPTH=128,
                          IMG_CHANNELS=3,
                          num_classes=4)

model.compile(optimizer = optim, loss={"mask":total_loss, 
                                       "lab": keras.losses.BinaryCrossentropy()
                                       }
              
              , metrics=metrics)
print(model.summary())

history=model.fit_generator(
          train_img_datagen,
          steps_per_epoch=steps_per_epoch,
          epochs=3,
          verbose=1,
          validation_data=val_img_datagen,
          validation_steps=val_steps_per_epoch,
          )

我无法理解这个错误。
ValueError:在用户代码中:
文件“/usr/local/lib/python3.10/dist-packages/keras/engine/training.py”,line 1021,in train_function * return step_function(self,iterator)File“/usr/local/lib/python3.10/dist-packages/segmentation_models_3D/metrics.py”,第62行,在**call *self中。submodules文件“/usr/local/lib/python3.10/dist-packages/segmentation_models_3D/base/functional.py”,第93行,在iou_score * intersection = backend中。sum(gt * pr,axis=axes)文件“/usr/local/lib/python3.10/dist-packages/keras/backend.py”,第2544行,在sum return tf中。reduce_sum(x,axis,keepdims)
ValueError:具有2个维度的输入的减少维度2无效。对于具有输入形状的“{{node Sum_10}} = Sum[T=DT_FLOAT,Tidx=DT_INT32,keep_dims=false](mul_5,Sum_10/reduction_indices)”:[3],[4]和计算输入Tensor:输入1 =<0 1 2 3>.

h9a6wy2h

h9a6wy2h1#

这是一个很好的例子,为什么完整的堆栈跟踪是重要的,为什么一个人应该张贴更多的比最后一行。最后一行显示了错误是什么,前面的行显示了(希望如此)它的起源。
文件“/usr/local/lib/python3.10/dist-packages/keras/engine/training.py”,line 1021,in train_function * return step_function(self,iterator)File“/usr/local/lib/python3.10/dist-packages/segmentation_models_3D/metrics.py”,第62行,在call *self中。submodules File“/usr/local/lib/python3.10/dist-packages/segmentation_models_3D/base/*functional.py”,第93行,在iou_score * intersection = backend中。sum(gt * pr,axis=axes)文件“/usr/local/lib/python3.10/dist-packages/keras/backend.py”,第2544行,在sum return tf中。reduce_sum(x,axis,keepdims)
ValueError:具有2个维度的输入的减少维度2无效。对于具有输入形状的“{{node Sum_10}} = Sum[T=DT_FLOAT,Tidx=DT_INT32,keep_dims=false](mul_5,Sum_10/reduction_indices)”:[3],[4]和计算输入Tensor:input1 =<0 1 2 3>.
我突出显示了我怀疑抛出错误的行。您指定了要为哪个输出使用哪个损失,我怀疑您也必须为指标这样做,因为IOUMetric仅用于图像输出,并且似乎它试图在不存在的轴上对lab输出求和。
以便您可以改变

metrics = [ sm.metrics.IOUScore(threshold=0.5), 'accuracy']

metrics = {'mask': sm.metrics.IOUScore(threshold=0.5), 'lab': 'accuracy'}

你也可以将accuracy添加到mask中,然后它看起来像这样:

metrics = {'mask': [sm.metrics.IOUScore(threshold=0.5), 'accuracy'], 'lab': 'accuracy'}

我没有测试这个解决方案,所以让我知道它是否适合你。

相关问题