2025, Dec 16 18:02

Применение карты внимания в Keras: как избежать ошибки итератора

Почему слой Keras нельзя вызывать на итераторе ImageDataGenerator, и как сделать правильно: тензорный вход, tf.convert_to_tensor и сборка через Functional API.

Создание собственного слоя Keras для применения маски, похожей на attention, — частая задача, однако из‑за тонкой ошибки интеграции легко получить непонятное исключение. Суть проблемы в том, что в слой пытаются передать Python‑итератор, который возвращает ImageDataGenerator, тогда как слои ожидают тензоры. Ниже — краткое объяснение, что идет не так и как правильно все связать.

Постановка задачи

Цель — умножать каждое изображение на заранее заданную карту внимания. Реализация объявляет пользовательский слой и пытается вызвать его на наборе данных, полученном из итератора каталога.

import tensorflow as tf
from tensorflow.keras.layers import Layer
class FocusMask(Layer):
    def __init__(self, image_dim, attn_map):
        super().__init__()
        self.image_dim = image_dim
        self.attn_map = attn_map
    def call(self, inputs, *args, **kwargs):
        return tf.math.multiply(inputs, self.attn_map)
mask_layer = FocusMask(img_dim, map_array)
masked_batch = mask_layer(directory_iterator)
ValueError: Only input tensors may be passed as positional arguments.

Почему это не работает

Объект, который возвращает ImageDataGenerator.flow_from_directory, — это Python‑итератор (DirectoryIterator). Слои Keras работают с тензорами. Передача итератора в вызов слоя приводит к указанной выше ошибке ValueError, потому что на вход подается не тензор.

Рабочее решение

Исправление сводится к тому, чтобы на вход слоя попадал тензор, а карту маски тоже привести к тензору. После этого слой можно собрать через Functional API, где входом служит тензор, созданный tf.keras.Input. Карту маски готовим с помощью tf.convert_to_tensor.

import tensorflow as tf
from tensorflow.keras.layers import Layer
class MapOverlay(Layer):
    def __init__(self, mask_tensor):
        super().__init__()
        self.mask_tensor = tf.convert_to_tensor(mask_tensor, dtype=tf.float32)
    def call(self, inputs):
        return inputs * self.mask_tensor
# Собираем модель, которая применяет маску внутри графа
image_input = tf.keras.Input(shape=(img_size, img_size, 3))
masked_output = MapOverlay(mask_tensor)(image_input)
mask_model = tf.keras.Model(inputs=image_input, outputs=masked_output)

В такой конфигурации модель принимает батчи тензоров изображений. Маскирование выполняется внутри вычислительного графа, где оба операнда — тензоры, поэтому ошибка исчезает.

Почему это важно

Понимание различий между итераторами на стороне Python и тензорами помогает избежать конфликтов типов на границе слоя Keras. Слои предназначены для композиции тензорных операций; итераторы датасета должны подавать данные модели, а не передаваться напрямую в вызов слоя.

Итоги

Держите входы слоев тензорными, конвертируйте постоянные массивы (например, карты внимания) через tf.convert_to_tensor и внедряйте свою логику через Functional API. Так конвейер данных остается прозрачным, а операция маскирования — отслеживаемой внутри модели.