tensorflow获取模型中间层结果及错误tf.keras.backend.function Layer ‘ + self.name + ‘ has no inbound nodes.

本文探讨了在TensorFlow中尝试获取已训练模型中间层输出时遇到的错误,包括AttributeError和'Layer has no inbound nodes'问题。错误产生的原因是模型的某些层没有输入节点。正确的做法是在构建模型时确保每个层都有输入,使得图层之间连通。通过示例代码展示了如何正确调用中间层结果,避免上述错误。

错误使用

1、构建模型:

import tensorflow as tf
import collections
from efficientnet import tfkeras


class MyModel(tf.keras.Model):

    def __init__(self, height=None,width=None,channel=None):
        super(MyModel,self).__init__()

        self.inputshape = tf.keras.layers.InputLayer(input_shape=(height,width,channel))

        self.effnet = tfkeras.EfficientNetB0(
                include_top=False,
                weights='imagenet',
                input_shape=(height,width,channel)
        )
        self.pool = tf.keras.layers.GlobalAveragePooling2D(name="GlobalAP")
        self.dense3 = tf.keras.layers.Dense(512,activation="relu",kernel_initializer="he_normal")
        self.dense4 = tf.keras.layers.Dense(6,activation="softmax")


    def call(self, input):

        # x = self.resnet50(input)
        # print(x.shape)

        x = self.inputshape(input)
        x = self.effnet(x)
        print(x)


        x = self.pool(x)
        x = self.dense3(x)
        self.out = self.dense4(x)

        return self.out

2、调用已训练模型中间层输出

for index, (train_index, valid_index) in enumerate(kfold.split(train_data,label)):

    print("========= %d =========="%index)
    x_train, x_valid, y_train, y_valid = X_train[train_index], X_train[valid_index], Y_train[train_index], Y_train[valid_index]

    md = model.MyModel(width, height, channel)
    
    # =======================================================================
    ## 获取中间结果
    get_GlobalAveragePooling = tf.keras.backend.function([md.layers[0].input,                                                 
              tf.keras.backend.learning_phase
import numpy as np import tensorflow as tf from tensorflow.keras import layers, models, regularizers from tensorflow.keras.datasets import cifar100 import matplotlib.pyplot as plt import time plt.rcParams["font.sans-serif"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] plt.rcParams['axes.unicode_minus'] = False def resnet_block(input_tensor, filters, stride=1, conv_shortcut=True, attention=None): bn_axis = 3 if tf.keras.backend.image_data_format() == 'channels_last' else 1 if conv_shortcut: shortcut = layers.Conv2D( 4 * filters, 1, strides=stride, kernel_regularizer=regularizers.l2(1e-4))(input_tensor) shortcut = layers.BatchNormalization(axis=bn_axis)(shortcut) else: shortcut = input_tensor x = layers.Conv2D( filters, 1, strides=stride, kernel_regularizer=regularizers.l2(1e-4))(input_tensor) x = layers.BatchNormalization(axis=bn_axis)(x) x = layers.Activation('relu')(x) x = layers.Conv2D(filters, 3, padding='same', kernel_regularizer=regularizers.l2(1e-4))(x) x = layers.BatchNormalization(axis=bn_axis)(x) x = layers.Activation('relu')(x) x = layers.Conv2D(4 * filters, 1, kernel_regularizer=regularizers.l2(1e-4))(x) x = layers.BatchNormalization(axis=bn_axis)(x) x = layers.Add()([shortcut, x]) x = layers.Activation('relu')(x) return x def build_resnet34(input_shape=(32, 32, 3), classes=100, attention=None): inputs = layers.Input(shape=input_shape) x = layers.ZeroPadding2D(padding=(3, 3))(inputs) x = layers.Conv2D( 64, 7, strides=2, use_bias=False, kernel_regularizer=regularizers.l2(1e-4) )(x) x = layers.BatchNormalization()(x) x = layers.Activation('relu')(x) x = layers.MaxPooling2D(3, strides=2, padding='same')(x) def make_layer(x, filters, blocks, stride=1): x = resnet_block(x, filters, stride=stride, conv_shortcut=True, attention=attention) for _ in range(1, blocks): x = resnet_block(x, filters, conv_shortcut=False, attention=attention) return x x = make_layer(x, 64, 3, stride=1) x = make_layer(x, 128, 4, stride=2) x = make_layer(x, 256, 6, stride=2) x = make_layer(x, 512, 3, stride=2) x = layers.GlobalAveragePooling2D()(x) outputs = layers.Dense( classes, activation='softmax', kernel_regularizer=regularizers.l2(1e-4))(x) return models.Model(inputs, outputs) # 改进的通道重要性计算,考虑残差结构 def compute_channel_importance(model): """计算每个卷积层通道的重要性(基于L1范数)""" importance = {} for layer in model.layers: if isinstance(layer, layers.Conv2D): weights = layer.get_weights()[0] # 获取卷积核权重 # 计算每个通道的L1范数 channel_importance = np.sum(np.abs(weights), axis=(0, 1, 2)) importance[layer.name] = channel_importance return importance # 改进的剪枝掩码生成,考虑残差连接的依赖关系 # 仅修改此函数,其他代码保持不变 def generate_prune_mask(importance, prune_ratio, model): """基于通道重要性生成剪枝掩码,确保残差连接通道数匹配""" prune_mask = {} # 分析模型结构,建立层依赖关系 layer_dependencies = {} for layer in model.layers: try: # 获取每层的输入层 inbound_nodes = layer._inbound_nodes[0] input_layers = [node.outbound_layer for node in inbound_nodes.inbound_layers] layer_dependencies[layer.name] = input_layers except: continue # 处理所有卷积层 conv_layers = [layer for layer in model.layers if isinstance(layer, layers.Conv2D)] for layer in conv_layers: layer_name = layer.name imp = importance[layer_name] num_channels = len(imp) num_keep = int(num_channels * (1 - prune_ratio)) # 生成通道掩码 keep_indices = np.argsort(-imp)[:num_keep] mask = np.zeros(num_channels, dtype=bool) mask[keep_indices] = True # 检查是否为残差块的主路径卷积层 # 如果该卷积层的输出被Add层使用,则寻找对应的捷径连接 for add_layer in model.layers: if isinstance(add_layer, layers.Add) and layer_name in [l.name for l in layer_dependencies.get(add_layer.name, [])]: # 找到捷径连接的卷积层 shortcut_layers = [l for l in layer_dependencies[add_layer.name] if l.name != layer_name] for shortcut_layer in shortcut_layers: if isinstance(shortcut_layer, layers.Conv2D): # 强制捷径连接与主路径使用相同的掩码 prune_mask[shortcut_layer.name] = mask prune_mask[layer_name] = mask return prune_mask # 改进的创建剪枝模型函数,处理残差结构 def create_pruned_model(original_model, prune_mask): """根据剪枝掩码创建剪枝后的模型,同步处理输入通道和BN层""" # 提取原始模型的配置 config = original_model.get_config() # 1. 修改卷积层配置(输出通道) for layer_name, mask in prune_mask.items(): for layer in config['layers']: if layer['name'] == layer_name and layer['class_name'] == 'Conv2D': layer['config']['filters'] = int(np.sum(mask)) break # 2. 构建剪枝后的模型结构 pruned_model = models.Model.from_config(config) # 3. 建立层名到剪枝掩码的映射(用于处理输入通道) name_to_mask = {k: v for k, v in prune_mask.items()} # 4. 复制并调整权重 for layer in original_model.layers: if layer.name in prune_mask: # 处理卷积层 if isinstance(layer, layers.Conv2D): weights = layer.get_weights() out_mask = prune_mask[layer.name] # 输出通道掩码 # 处理输入通道:找到前一层的剪枝掩码 in_mask = None try: # 尝试获取该层的输入节点 inbound_nodes = layer._inbound_nodes if inbound_nodes and len(inbound_nodes) > 0: # 获取第一个输入节点的输入层 input_layers = [node.layer for node in inbound_nodes[0].inbound_layers] for input_layer in input_layers: if input_layer.name in name_to_mask: in_mask = name_to_mask[input_layer.name] break except Exception as e: print(f"警告: 获取层 {layer.name} 的输入层失败: {e}") # 调整卷积核权重(输入通道×输出通道) if in_mask is not None: # 输入通道剪枝 pruned_kernel = weights[0][:, :, in_mask, :] # 输出通道剪枝 pruned_kernel = pruned_kernel[:, :, :, out_mask] else: # 仅输出通道剪枝 pruned_kernel = weights[0][:, :, :, out_mask] pruned_weights = [pruned_kernel] if len(weights) > 1: # 偏置剪枝 pruned_weights.append(weights[1][out_mask]) pruned_model.get_layer(layer.name).set_weights(pruned_weights) # 处理批归一化层 elif isinstance(layer, layers.BatchNormalization): weights = layer.get_weights() # 找到对应的卷积层掩码 conv_mask = None try: # 获取该BN层的输入节点 inbound_nodes = layer._inbound_nodes if inbound_nodes and len(inbound_nodes) > 0: # 获取输入层 input_layers = [node.layer for node in inbound_nodes[0].inbound_layers] for input_layer in input_layers: if input_layer.name in name_to_mask and isinstance(input_layer, layers.Conv2D): conv_mask = name_to_mask[input_layer.name] break except Exception as e: print(f"警告: 获取层 {layer.name} 的输入层失败: {e}") if conv_mask is not None: # 调整BN层参数(与卷积输出通道匹配) pruned_weights = [w[conv_mask] for w in weights] pruned_model.get_layer(layer.name).set_weights(pruned_weights) else: # 复制未剪枝层的权重 try: pruned_model.get_layer(layer.name).set_weights(layer.get_weights()) except Exception as e: print(f"警告: 无法设置层 {layer.name} 的权重: {e}") return pruned_model # 评估模型性能 def evaluate_model(model, x_test, y_test): """评估模型的准确率和推理速度""" # 评估准确率 loss, accuracy = model.evaluate(x_test, y_test, verbose=0) # 评估推理速度 start_time = time.time() predictions = model.predict(x_test, batch_size=128) end_time = time.time() inference_time = end_time - start_time samples_per_second = len(x_test) / inference_time return accuracy, samples_per_second # 主函数 def main(): # 加载CIFAR-100数据 (x_train, y_train), (x_test, y_test) = cifar100.load_data() x_train = x_train.astype('float32') / 255.0 x_test = x_test.astype('float32') / 255.0 # 构建基础模型 model = build_resnet34(attention=None) model.load_weights('base_resnet34_cifar100.h5') print("模型权重加载成功") # 编译模型 model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) # 评估原始模型 print("\n评估原始模型...") original_accuracy, original_speed = evaluate_model(model, x_test, y_test) print(f"原始模型准确率: {original_accuracy:.4f}") print(f"原始模型速度: {original_speed:.2f} samples/sec") # 计算通道重要性 importance = compute_channel_importance(model) # 测试不同剪枝比例 prune_ratios = [0.1, 0.2] # 10%和20%的通道剪枝 results = [] for ratio in prune_ratios: print(f"\n=== 剪枝比例: {ratio*100:.0f}% ===") # 生成剪枝掩码(考虑残差结构) prune_mask = generate_prune_mask(importance, ratio, model) # 创建剪枝模型 pruned_model = create_pruned_model(model, prune_mask) # 编译剪枝模型 pruned_model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) # 评估剪枝模型 pruned_accuracy, pruned_speed = evaluate_model(pruned_model, x_test, y_test) print(f"剪枝后准确率: {pruned_accuracy:.4f}") print(f"剪枝后速度: {pruned_speed:.2f} samples/sec") print(f"精度损失: {original_accuracy - pruned_accuracy:.4f}") print(f"加速比: {pruned_speed / original_speed:.2f}x") # 记录结果 results.append({ 'ratio': ratio, 'accuracy': pruned_accuracy, 'speed': pruned_speed, 'accuracy_loss': original_accuracy - pruned_accuracy, 'speedup': pruned_speed / original_speed }) # 可视化结果 plt.figure(figsize=(15, 6)) plt.subplot(1, 2, 1) ratios = [r['ratio'] * 100 for r in results] accuracies = [r['accuracy'] for r in results] plt.plot(ratios, accuracies, 'o-', label='剪枝后准确率') plt.axhline(y=original_accuracy, color='r', linestyle='--', label='原始准确率') plt.xlabel('剪枝比例 (%)') plt.ylabel('准确率') plt.title('不同剪枝比例下的模型准确率') plt.legend() plt.grid(True) plt.subplot(1, 2, 2) speedups = [r['speedup'] for r in results] plt.plot(ratios, speedups, 'o-', label='加速比') plt.axhline(y=1.0, color='r', linestyle='--', label='基线') plt.xlabel('剪枝比例 (%)') plt.ylabel('加速比 (x)') plt.title('不同剪枝比例下的模型加速比') plt.legend() plt.grid(True) plt.tight_layout() plt.show() return results if __name__ == "__main__": results = main()//报错=== 剪枝比例: 10% === 警告: 获取层 conv2d 的输入层失败: 'ZeroPadding2D' object is not iterable 警告: 无法设置层 batch_normalization 的权重: Layer weight shape (57,) not compatible with provided weight shape (64,) 警告: 获取层 conv2d_2 的输入层失败: 'MaxPooling2D' object is not iterable Traceback (most recent call last): File "c:/Users/赵培伊/Desktop/大三下文件存放处/神经网络代码/大作业卷积任务4.py", line 312, in <module> results = main() File "c:/Users/赵培伊/Desktop/大三下文件存放处/神经网络代码/大作业卷积任务4.py", line 257, in main pruned_model = create_pruned_model(model, prune_mask) File "c:/Users/赵培伊/Desktop/大三下文件存放处/神经网络代码/大作业卷积任务4.py", line 170, in create_pruned_model pruned_model.get_layer(layer.name).set_weights(pruned_weights) File "C:\anaconda\envs\tensorflow2.4\lib\site-packages\tensorflow\python\keras\engine\base_layer.py", line 1871, in set_weights raise ValueError(
最新发布
07-08
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值