You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Keras CNN模型中添加Luong注意力层?解决维度兼容问题

为Keras CNN模型正确添加Luong注意力层的解决方案

问题背景

用户实现了一个处理64x64x1单通道图像的Keras函数式CNN模型,包含6个卷积层,希望通过添加Luong注意力层提升性能,但连接时出现维度不匹配错误:

ValueError: Input 0 of layer "conv2d" is incompatible with the layer: expected min_ndim=4, found ndim=2. Full shape received: (None, 200)

原CNN模型代码:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model

num_classes = 5
def get_model():
    # 创建CNN函数式模型
    input_ = keras.layers.Input(shape=[64, 64,1])
    # 第一组卷积层
    Conv1  = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(input_)  # 修正原代码input_layer笔误
    Conv12 = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(Conv1)
    Conv13 = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(Conv12)
    Max1   = keras.layers.MaxPool2D(2, strides=2)(Conv13)
    # 第二组卷积层
    Conv2  = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Max1)
    Conv21 = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Conv2)
    Conv23 = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Conv21)
    Max2   = keras.layers.MaxPool2D(2, strides=2)(Conv23)
    # 原Flatten层计划替换为注意力层
    Flat = keras.layers.Flatten()(Max2)
    Dense1 = keras.layers.Dense(2048, activation=tf.nn.relu)(Flat)
    Dense2 = keras.layers.Dense(700, activation=tf.nn.relu)(Dense1)
    output = keras.layers.Dense(num_classes, activation=tf.nn.softmax)(Dense2)
    
    model = Model(inputs=input_, outputs=output)
    # 编译模型
    optimize_rmsprop = keras.optimizers.RMSprop(learning_rate=0.001, epsilon=1e-08, decay=0.0)
    model.compile(loss="sparse_categorical_crossentropy", optimizer=optimize_rmsprop, metrics=["accuracy"])  # 修正optimizer变量名错误
    return model

用户尝试添加的注意力层代码(原代码为序列数据设计,不适合图像):

# 已导入相关库
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model
from keras.layers import Dropout

# 针对序列数据的输入定义
query_input = tf.keras.Input(shape=(4096,), dtype='int32')
value_input = tf.keras.Input(shape=(4096,), dtype='int32')

# 嵌入层(处理整数序列)
token_embedding = tf.keras.layers.Embedding(input_dim=1000, output_dim=64)
query_embeddings = token_embedding(query_input)
value_embeddings = token_embedding(value_input)

# 1D卷积层(处理序列)
cnn_layer = tf.keras.layers.Conv1D(
    filters=100,
    kernel_size=4,
    padding='same')
query_seq_encoding = cnn_layer(query_embeddings)
value_seq_encoding = cnn_layer(value_embeddings)

# 注意力层
query_value_attention_seq = tf.keras.layers.Attention()(
    [query_seq_encoding, value_seq_encoding])

# 全局池化
query_encoding = tf.keras.layers.GlobalAveragePooling1D()(query_seq_encoding)
query_value_attention = tf.keras.layers.GlobalAveragePooling1D()(query_value_attention_seq)

# 拼接特征
input_layer = tf.keras.layers.Concatenate()([query_encoding, query_value_attention])

错误原因分析

  1. 维度不匹配:原注意力层基于1D序列设计,而CNN的Max2输出是4D张量(None, H, W, C)(H、W为池化后的特征图尺寸,C为通道数),直接连接会导致维度不兼容。
  2. 层用途错误:Embedding层用于处理整数类型的序列数据(如文本 token),而图像特征是连续数值的张量,不需要嵌入处理。
  3. 输入定义冗余:原注意力层单独定义了query_input和value_input,没有与CNN的特征输出关联。

修正后的完整代码

我们将注意力层适配到CNN的特征图上,把特征图转换为注意力层可处理的序列格式,同时保留原CNN的特征提取能力:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model

num_classes = 5
def get_model_with_attention():
    # CNN输入层
    input_ = keras.layers.Input(shape=[64, 64, 1])
    
    # 第一组卷积层
    Conv1  = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(input_)
    Conv12 = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(Conv1)
    Conv13 = keras.layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)(Conv12)
    Max1   = keras.layers.MaxPool2D(2, strides=2)(Conv13)
    
    # 第二组卷积层
    Conv2  = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Max1)
    Conv21 = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Conv2)
    Conv23 = keras.layers.Conv2D(64, kernel_size=5, activation=tf.nn.relu)(Conv21)
    Max2   = keras.layers.MaxPool2D(2, strides=2)(Conv23)
    
    # -------------------------- 注意力层适配部分 --------------------------
    # 将4D特征图 (batch, H, W, C) 转换为3D序列格式 (batch, seq_len, feature_dim)
    # seq_len = H*W,feature_dim = C
    batch_size, H, W, C = Max2.shape.as_list()
    seq_len = H * W
    feature_dim = C
    
    # 展平空间维度,保留通道作为特征维度
    seq_features = keras.layers.Reshape((seq_len, feature_dim))(Max2)
    
    # 使用Luong注意力:以自身作为query和value(也可从不同层提取query/value)
    attention_layer = keras.layers.Attention()
    # query和value均为seq_features,输出形状为 (batch, seq_len, feature_dim)
    attention_output = attention_layer([seq_features, seq_features])
    
    # 融合原始特征与注意力加权特征
    concatenated = keras.layers.Concatenate(axis=-1)([seq_features, attention_output])
    
    # 全局平均池化降维,或直接展平
    pooled = keras.layers.GlobalAveragePooling1D()(concatenated)
    # -------------------------- 注意力层适配结束 --------------------------
    
    # 全连接层
    Dense1 = keras.layers.Dense(2048, activation=tf.nn.relu)(pooled)
    Dense2 = keras.layers.Dense(700, activation=tf.nn.relu)(Dense1)
    output = keras.layers.Dense(num_classes, activation=tf.nn.softmax)(Dense2)
    
    model = Model(inputs=input_, outputs=output)
    
    # 编译模型
    optimize_rmsprop = keras.optimizers.RMSprop(learning_rate=0.001, epsilon=1e-08, decay=0.0)
    model.compile(loss="sparse_categorical_crossentropy", optimizer=optimize_rmsprop, metrics=["accuracy"])
    
    return model

# 测试模型
model = get_model_with_attention()
model.summary()

关键说明

  1. 特征格式转换:通过Reshape将CNN输出的4D特征图转换为3D序列(batch, H*W, channels),让注意力层可以对每个空间位置的特征进行加权。
  2. 注意力层使用:这里采用自注意力(query和value为同一特征序列),也可以根据需求从CNN的不同层提取query和value(比如用第一组池化后的特征作为query,第二组池化后的特征作为value)。
  3. 特征融合:将原始序列特征与注意力加权后的特征拼接,保留更多信息,再通过池化或展平连接全连接层。
  4. 修正原代码错误:修复了原CNN模型中input_layer变量名笔误、optimizer_rmsprop变量名错误等问题。

内容的提问来源于stack exchange,提问作者user1655410

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:45:38