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

基于LSTM的TensorFlow验证码模型训练形状不兼容问题求助

问题分析与解决方案

核心错误原因

  1. 标签处理逻辑错误:使用class_mode='input'会将输入图像本身作为标签,而你的实际标签是验证码字符串,直接导致模型接收的标签形状与输出形状完全不匹配。
  2. 标签未做序列编码:模型输出形状为(None, 10, 21)(对应10个字符位置,每个位置21类),但原始标签是未编码的字符串,未转换为匹配的序列格式。
  3. 变量名笔误:模型定义时赋值给self.model,但编译和训练时误用self.modelo,导致训练的是未正确初始化的模型。

分步解决方案

1. 重构数据加载与标签编码

放弃ImageDataGenerator的默认标签处理,改用tf.data.Dataset构建适配序列标签的数据集,确保标签形状与模型输出匹配:

import tensorflow as tf

# 复用已有的字符转数字层,确保词汇表正确
char_to_num = tf.keras.layers.StringLookup(vocabulary=list(self.char_to_num.get_vocabulary()), mask_token=None)
num_classes = len(char_to_num.get_vocabulary()) + 1  # 包含padding类别

# 定义标签处理函数:字符串转10位数字序列,再转one-hot编码
def process_label(label):
    # 拆分字符串为单个字符
    chars = tf.strings.unicode_split(label, input_encoding='UTF-8')
    # 转换为数字序列
    num_seq = char_to_num(chars)
    # 强制序列长度为10(与模型RepeatVector参数一致),不足补0,过长截断
    num_seq = tf.ensure_shape(num_seq, (10,))
    # 转one-hot编码,匹配模型输出形状
    return tf.one_hot(num_seq, depth=num_classes)

# 定义图像加载与预处理函数
def load_image(filename, label):
    img = tf.io.read_file(tf.strings.join([self.datadir, '/', filename]))
    img = tf.image.decode_png(img, channels=3)  # 若图像是jpg格式,改用decode_jpeg
    img = tf.image.resize(img, (224, 224))
    img = img / 255.0  # 归一化处理
    return img, process_label(label)

# 构建并划分数据集
def load_data():
    image_names = os.listdir(self.datadir)
    labels = [name.split('.')[0] for name in image_names]
    
    # 过滤长度不为10的验证码(与模型参数匹配)
    valid_pairs = [(img, lbl) for img, lbl in zip(image_names, labels) if len(lbl) == 10]
    image_names, labels = zip(*valid_pairs)

    dataset = tf.data.Dataset.from_tensor_slices((list(image_names), list(labels)))
    dataset = dataset.map(load_image, num_parallel_calls=tf.data.AUTOTUNE)
    
    # 划分训练/验证集
    total_size = len(image_names)
    train_size = int(0.8 * total_size)
    self.train_dataset = dataset.take(train_size).shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE)
    self.valid_dataset = dataset.skip(train_size).batch(32).prefetch(tf.data.AUTOTUNE)

2. 修正模型定义的笔误与细节

将模型编译的变量名从self.modelo改为self.model,同时统一类别数定义:

mobilnet = hub.KerasLayer(self.url, input_shape=(224, 224, 3), dtype="float32", name="img")
self.model = keras.Sequential(
    [
        mobilnet,
        layers.Dropout(0.25),
        layers.Flatten(),
        layers.RepeatVector(10),  # 必须与验证码长度一致
        layers.LSTM(128, return_sequences=True),
        layers.Dense(num_classes, activation="softmax"),  # 使用统一的类别数
    ]
)
self.model.summary()
self.model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss="categorical_crossentropy", metrics=["accuracy"])

3. 修正训练代码的变量名

把训练时的self.modelo.fit改为self.model.fit:

log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    
callbacks = [
    keras.callbacks.EarlyStopping(monitor="val_loss", patience=10, restore_best_weights=True),
    keras.callbacks.ModelCheckpoint(filepath='best_model_weights.h5', save_best_only=True, save_weights_only=True),
    tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)
]
    
self.model.fit(
    self.train_dataset,
    validation_data=self.valid_dataset,
    epochs=self.epochs,
    callbacks=callbacks,
)

额外优化建议

  • 如果不想使用one-hot编码,可改用SparseCategoricalCrossentropy损失函数,此时只需保留数字序列标签(去掉process_label中的tf.one_hot步骤),损失函数修改为:
    self.model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=["accuracy"])
    
  • 如果验证码长度不是10,需同步修改RepeatVector参数、process_label中的序列长度限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 23:42:37