如何在MobileNetV3Small前后组合层并冻结指定层?问题排查
问题与解决方案
需求
- 在基础模型前添加数据增强层
- 冻结基础模型中除最后一层外的所有层
- 在基础模型后添加额外层
遇到的问题
- 数据增强层输出形状为
(None, None, None, 3),导致MobileNetV3Small输入形状也变为该值,预期应为(None, 224, 224, 3) - 模型摘要前的循环输出显示所有层均为可训练状态,但
model.summary()显示大部分参数不可训练
原代码
def create_model(input_shape, output_shape): inputs = Input(shape=input_shape) data_augmentation = Sequential([ RandomBrightness(0.2), RandomContrast(0.2), RandomFlip(), RandomRotation(0.2), RandomHeight(0.2), RandomWidth(0.2), RandomTranslation(0.2, 0.2), RandomZoom(0.2, 0.2) ]) x = data_augmentation(inputs) base_model = MobileNetV3Small(include_top=False, weights='imagenet', pooling="avg") for layer in base_model.layers[:-1]: layer.trainable = False x = base_model(x) extra =Sequential([ Dense(256), BatchNormalization(), Activation("relu"), Dropout(0.2) ]) x = extra(x) outputs =Dense(output_shape,activation='softmax')(x) model=Model(inputs=inputs, outputs=outputs) model.compile(optimizer=Adam(1e-3), loss='categorical_crossentropy',metrics=['accuracy']) layers = model.layers for i in range(len(layers)): if layers[i].trainable: print(i, layers[i].name, "Trainable:", layers[i].trainable, "Input:", layers[i].input_shape, "Output:", layers[i].output_shape) print(model.summary()) return model
输出结果
0 input_1 Trainable: True Input: [(None, 224, 224, 3)] Output: [(None, 224, 224, 3)] 1 sequential Trainable: True Input: (None, 224, 224, 3) Output: (None, None, None, 3) 2 MobilenetV3small Trainable: True Input: (None, None, None, 3) Output: (None, 576) 3 sequential_1 Trainable: True Input: (None, 576) Output: (None, 256) 4 dense_1 Trainable: True Input: (None, 256) Output: (None, 5) Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 sequential (Sequential) (None, None, None, 3) 0 MobilenetV3small (Functiona (None, 576) 939120 l) sequential_1 (Sequential) (None, 256) 148736 dense_1 (Dense) (None, 5) 1285 ================================================================= Total params: 1,089,141 Trainable params: 149,509 Non-trainable params: 939,632 _________________________________________________________________
解决方案
问题1:数据增强层输出形状不固定
原因:RandomHeight、RandomWidth这类变换会改变输入的空间维度,导致输出形状动态变化。解决方法是在数据增强层末尾添加Resize层,强制固定输出尺寸为输入的目标形状。
修改数据增强部分:
data_augmentation = Sequential([ RandomBrightness(0.2), RandomContrast(0.2), RandomFlip(), RandomRotation(0.2), RandomHeight(0.2), RandomWidth(0.2), RandomTranslation(0.2, 0.2), RandomZoom(0.2, 0.2), Resize(input_shape[0], input_shape[1]) # 固定输出尺寸为输入指定的大小 ])
修改后数据增强层的输出形状会保持为(None, 224, 224, 3),MobileNetV3Small的输入形状也会恢复预期值。
问题2:层的可训练状态显示不一致
原因:你遍历的是顶层模型的layers属性,其中MobilenetV3small是一个嵌套的Functional子模型,它自身的trainable属性为True,但内部大部分层已被冻结。model.summary()统计的是所有参数的真实可训练状态,而你的打印逻辑仅查看了顶层节点,未深入子模型内部。
若要准确查看所有层的可训练状态,需递归遍历子模型的内部层:
def print_all_layers(model): for layer in model.layers: if hasattr(layer, 'layers'): # 判断是否为包含子层的模型节点 print_all_layers(layer) else: print(f"{layer.name} - Trainable: {layer.trainable}") # 在模型编译后调用该函数 print_all_layers(model)
你对基础模型层的冻结操作是有效的,model.summary()的参数统计结果准确,只是原打印逻辑存在局限性。
修改后的完整代码
from tensorflow.keras.layers import Input, Sequential, RandomBrightness, RandomContrast, RandomFlip, RandomRotation, RandomHeight, RandomWidth, RandomTranslation, RandomZoom, Resize, Dense, BatchNormalization, Activation, Dropout from tensorflow.keras.applications import MobileNetV3Small from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam def create_model(input_shape, output_shape): inputs = Input(shape=input_shape) data_augmentation = Sequential([ RandomBrightness(0.2), RandomContrast(0.2), RandomFlip(), RandomRotation(0.2), RandomHeight(0.2), RandomWidth(0.2), RandomTranslation(0.2, 0.2), RandomZoom(0.2, 0.2), Resize(input_shape[0], input_shape[1]) # 固定输出尺寸 ]) x = data_augmentation(inputs) base_model = MobileNetV3Small(include_top=False, weights='imagenet', pooling="avg") # 冻结除最后一层外的所有基础模型层 for layer in base_model.layers[:-1]: layer.trainable = False x = base_model(x) extra = Sequential([ Dense(256), BatchNormalization(), Activation("relu"), Dropout(0.2) ]) x = extra(x) outputs = Dense(output_shape, activation='softmax')(x) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer=Adam(1e-3), loss='categorical_crossentropy', metrics=['accuracy']) # 递归打印所有层的可训练状态 def print_all_layers(model): for layer in model.layers: if hasattr(layer, 'layers'): print_all_layers(layer) else: print(f"{layer.name} - Trainable: {layer.trainable}") print_all_layers(model) print(model.summary()) return model
内容的提问来源于stack exchange,提问作者jl303
相关产品推荐
相关产品推荐

