Не распознается текст капчи переменной длины

Рейтинг: 0Ответов: 1Опубликовано: 30.07.2025

Есть нейронная сеть для распознавания текста с капчи. Если вся капча имеет одинаковую длину, то сеть обучается и распознает текст со 100% результатом. Но с переменной длиной сеть уже не обучается и соответственно не распознает текст. Что я только не пробовал ничего не помогает. Код не весь, а только важные моменты:

char_to_num = keras.layers.StringLookup(vocabulary=chars, invert=False)
blank_char_index = len(char_to_num.get_vocabulary())

encoded_labels = keras.preprocessing.sequence.pad_sequences(
    encoded_labels, maxlen=max_label_length, padding='post', value=blank_char_index
)

# CTC loss
class CTCLayer(keras.layers.Layer):
    def __init__(self, name=None):
        super().__init__(name=name)

    def call(self, y_true, y_pred):
        # Количество примеров в батче
        batch_len = tf.cast(tf.shape(y_true)[0], dtype=tf.int32)
        # Длина последовательности предсказаний (временные шаги)
        input_length = tf.cast(tf.shape(y_pred)[1], dtype=tf.int32)
        # Создаем тензоры длин для всего батча
        input_length = input_length * tf.ones(shape=(batch_len, 1), dtype=tf.int32)
        # Длина целевой текстовой метки
        label_length = tf.math.reduce_sum(tf.cast(tf.math.not_equal(y_true, blank_char_index), tf.int32), axis=1, keepdims=True)
        
        loss = keras.backend.ctc_batch_cost(y_true, y_pred, input_length, label_length)
        self.add_loss(loss)

        return y_pred

# Входной слой
image_input_layer = keras.layers.Input(shape=(image_width, image_height, 1), name="image_input_layer", dtype=tf.float32)
label_input_layer = keras.layers.Input(shape=(max_label_length,), name="label_input_layer", dtype=tf.int32)

# Слой CNN (извлечение признаков)
x = keras.layers.Conv2D(32, (3, 3), activation="relu", kernel_initializer="he_normal", padding="same")(image_input_layer)
x = keras.layers.MaxPooling2D((2, 2))(x)

x = keras.layers.Conv2D(64, (3, 3), activation="relu", kernel_initializer="he_normal", padding="same")(x)
x = keras.layers.MaxPooling2D((2, 2))(x)

# Преобразование в последовательность для RNN
new_rnn_shape = ((image_width // 4), (image_height // 4) * 64)
x = keras.layers.Reshape(target_shape=new_rnn_shape)(x)
x = keras.layers.Dense(64, activation="relu")(x)
x = keras.layers.Dropout(0.2)(x)

# Слой RNN (обработка последовательности)
x = keras.layers.Bidirectional(keras.layers.LSTM(128, return_sequences=True, dropout=0.25))(x)
x = keras.layers.Bidirectional(keras.layers.LSTM(64, return_sequences=True, dropout=0.25))(x)

# Выходной слой
output_layer = keras.layers.Dense(blank_char_index + 1, activation="softmax", name="output_layer")(x)

# CTC loss
ctc_loss_output = CTCLayer(name="ctc_loss_output")(label_input_layer, output_layer)

# Модель
model = keras.models.Model(inputs=[image_input_layer, label_input_layer], outputs=ctc_loss_output)
model.compile(optimizer=keras.optimizers.Adam())
model.summary()

Ответы

▲ 0

Так как вы хотите предсказывать капчи, а капчи обычно имеют ограничение в количестве символов(Да, на картинке 250х50 не может появиться капча с 30 символами). Вы так же используете CTCLoss.

В таком случае самый хороший вариант это заранее задать большой размер выходных данных для вашей модели.

output_layer = keras.layers.Dense( <тут размер выхода>,  
                                     activation="softmax",name="output_layer")(x)

Вы задаёте туда len(char_to_num.get_vocabulary()), вы так же можете дополнить свои тренировочные данные картинками с капчами у которых большое количество символов.

К примеру реализация в 11 блоке блокнота схожа с вашим вариантом, но в данных находятся все варианты размерности капч(5-8 символов)