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

迁移学习模型K折交叉验证遇TypeError报错,求解决方案

迁移学习模型K折交叉验证报错解决

问题背景

在图像分类任务中,基于迁移学习搭建的模型单独运行正常,但执行K折交叉验证时出现报错。使用tf.keras.utils.image_dataset_from_directory加载数据并拆分图像和标签,相关代码及报错信息如下:

K折交叉验证代码

# Model configuration
batch_size = 32
img_width, img_height, img_num_channels = 32, 32, 3
loss_function = sparse_categorical_crossentropy
no_classes = 5
no_epochs = 10
optimizer = Adam()
verbosity = 1
num_folds = 10

# Define the K-fold Cross Validator
kfold = KFold(n_splits=num_folds, shuffle=True)

# K-fold Cross Validation model evaluation
fold_no = 1
for train, test in kfold.split(inputs, targets):
  
  # Load Model
  base_model = keras.applications.vgg19.VGG19(
    include_top=False,
    weights='imagenet',
    input_shape=(224,224,3)
  )

  # Freeze base_model
  base_model.trainable = False

  #
  inputs = keras.Input(shape=(224,224,3))
  x = data_augmentation(inputs) #apply data augmentation

  # Preprocessing
  x = tf.keras.applications.vgg19.preprocess_input(x)

  # The base model contains batchnorm layers. We want to keep them in inference mode
  # when we unfreeze the base model for fine-tuning, so we make sure that the
  # base_model is running in inference mode here.
  x = base_model(x, training=False)
  x = keras.layers.GlobalAveragePooling2D()(x)
  x = keras.layers.Dropout(0.2)(x)  # Regularize with dropout
  outputs = keras.layers.Dense(5, activation="softmax")(x)
  model = keras.Model(inputs, outputs)

  model.compile(
    loss=loss_function, 
    optimizer=optimizer, 
    metrics=['accuracy'])

  # Generate a print
  print('------------------------------------------------------------------------')
  print(f'Training for fold {fold_no} ...')

  hist = model.fit(inputs[train], targets[train],
                   batch_size=batch_size,
                   epochs=no_epochs)
  
  base_model.trainable = True

  model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=0.00001),  # Low learning rate
    loss=loss_function, 
    metrics=['accuracy'])

  hist_2 = model.fit(inputs[train], targets[train],
                    batch_size=batch_size,
                    epochs=no_epochs)

  # Generate generalization metrics
  scores = model.evaluate(inputs[test], targets[test], verbose=0)
  print(f'Score for fold {fold_no}: {model.metrics_names[0]} of {scores[0]}; {model.metrics_names[1]} of {scores[1]*100}%')
  acc_per_fold.append(scores[1] * 100)
  loss_per_fold.append(scores[0])

  # Increase fold number
  fold_no = fold_no + 1

首次报错信息

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/lib/python3.8/dist-packages/sklearn/utils/validation.py in _num_samples(x)
    277     try:
--> 278         return len(x)
    279     except TypeError as type_error:

6 frames
/usr/local/lib/python3.8/dist-packages/keras/engine/keras_tensor.py in __len__(self)
    220   def __len__(self):
--> 221     raise TypeError('Keras symbolic inputs/outputs do not '
    222                     'implement `__len__`. You may be '

TypeError: Keras symbolic inputs/outputs do not implement `__len__`. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model. This error will also get raised if you try asserting a symbolic input/output directly.

The above exception was the direct cause of the following exception:

TypeError                                 Traceback (most recent call last)
<ipython-input-37-6292815144e5> in <module>
      1 # K-fold Cross Validation model evaluation
      2 fold_no = 1
----> 3 for train, test in kfold.split(inputs, targets):
      4 
      5   # Load Model

/usr/local/lib/python3.8/dist-packages/sklearn/model_selection/_split.py in split(self, X, y, groups)
    328             The testing set indices for that split.
    329         """
--> 330         X, y, groups = indexable(X, y, groups)
    331         n_samples = _num_samples(X)
    332         if self.n_splits > n_samples:

/usr/local/lib/python3.8/dist-packages/sklearn/utils/validation.py in indexable(*iterables)
    376 
    377     result = [_make_indexable(X) for X in iterables]
--> 378     check_consistent_length(*result)
    379     return result
    380 

/usr/local/lib/python3.8/dist-packages/sklearn/utils/validation.py in check_consistent_length(*arrays)
    327     """
    328 
--> 329     lengths = [_num_samples(X) for X in arrays if X is not None]
    330     uniques = np.unique(lengths)
    331     if len(uniques) > 1:

/usr/local/lib/python3.8/dist-packages/sklearn/utils/validation.py in <listcomp>(.0)
    327     """
    328 
--> 329     lengths = [_num_samples(X) for X in arrays if X is not None]
    330     uniques = np.unique(lengths)
    331     if len(uniques) > 1:

/usr/local/lib/python3.8/dist-packages/sklearn/utils/validation.py in _num_samples(x)
    278         return len(x)
    279     except TypeError as type_error:
--> 280         raise TypeError(message) from type_error
    281 
    282 

TypeError: Expected sequence or array-like, got <class 'keras.engine.keras_tensor.KerasTensor'>

修正数据加载后新报错信息

Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5
80134624/80134624 [==============================] - 4s 0us/step
------------------------------------------------------------------------
Training for fold 1 ...
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-6292815144e5> in <module>
     38   print(f'Training for fold {fold_no} ...')
     39 
--> 40   hist = model.fit(inputs[train], targets[train],
     41                    batch_size=batch_size,
     42                    epochs=no_epochs)

2 frames
/usr/local/lib/python3.8/dist-packages/keras/utils/traceback_utils.py in error_handler(*args, **kwargs)
     65     except Exception as e:  # pylint: disable=broad-except
     66       filtered_tb = _process_traceback_frames(e.__traceback__)
--> 67       raise e.with_traceback(filtered_tb) from None
     68     finally:
     69       del filtered_tb

TypeError: Exception encountered when calling layer "tf.__operators__.getitem_1" (type SlicingOpLambda).

Only integers, slices (`:`), ellipsis (`...`), tf.newaxis (`None`) and scalar tf.int32/tf.int64 tensors are valid indices, got array([   0,    1,    2, ..., 2872, 2873, 2874])

Call arguments received by layer "tf.__operators__.getitem_1" (type SlicingOpLambda):
  • tensor=tf.Tensor(shape=(None, 224, 224, 3), dtype=float32)
  • slice_spec=array([   0,    1,    2, ..., 2872, 2873, 2874])
  • var=None

数据加载代码

train_ds = tf.keras.utils.image_dataset_from_directory(
  directory ='/gdrive/My Drive/Flies_dt/224x224',
  validation_split=0.4,
  subset="training",
  seed=123,
  image_size=(224, 224),
  batch_size=32)

val_ds = tf.keras.utils.image_dataset_from_directory(
  directory ='/gdrive/My Drive/Flies_dt/224x224',
  validation_split=0.4,
  subset="validation",
  seed=123,
  image_size=(224, 224),
  batch_size=32)

train_images = np.concatenate(list(train_ds.map(lambda x, y:x)))
train_labels = np.concatenate(list(train_ds.map(lambda x, y:y)))

val_images = np.concatenate(list(val_ds.map(lambda x, y:x)))
val_labels = np.concatenate(list(val_ds.map(lambda x, y:y)))

inputs = np.concatenate((train_images, val_images), axis=0)
targets = np.concatenate((train_labels, val_labels), axis=0)

报错原因及解决步骤

1. 首次报错:变量名冲突

  • 原因:循环内部重新定义inputs = keras.Input(shape=(224,224,3)),覆盖了外部存储训练数据的numpy数组inputs,导致kfold.split()接收的是Keras张量而非数组,触发类型错误。
  • 解决:修改模型输入层的变量名,避免与数据变量冲突,例如改为input_layer:
    # 替换原代码中的inputs定义
    input_layer = keras.Input(shape=(224,224,3))
    x = data_augmentation(input_layer)  # 应用数据增强
    # 后续代码中所有对应的inputs都替换为input_layer
    model = keras.Model(input_layer, outputs)
    

2. 第二次报错:索引逻辑混淆

  • 原因:变量名冲突修正后,model.fit中使用的inputs本应是numpy数组,但由于之前的变量覆盖问题,代码逻辑仍将Keras张量作为数据传入,导致切片索引不兼容。
  • 解决:确保循环内模型输入层变量与数据变量完全隔离,训练时直接使用numpy数组切片,或转换为TensorFlow数据集(大场景下更高效):
    # 方式1:直接使用numpy数组切片
    hist = model.fit(inputs[train_idx], targets[train_idx],
                     batch_size=batch_size,
                     epochs=no_epochs)
    
    # 方式2:转换为TF数据集(内存友好)
    train_ds_fold = tf.data.Dataset.from_tensor_slices((inputs[train_idx], targets[train_idx])).batch(batch_size)
    hist = model.fit(train_ds_fold, epochs=no_epochs)
    

3. 其他关键优化

  • 初始化存储列表:在循环前必须初始化acc_per_fold和loss_per_fold,避免列表未定义报错:
    acc_per_fold = []
    loss_per_fold = []
    
  • 模型复用优化:将模型构建封装为函数,避免每个fold重复加载VGG19权重,提升效率:
    def build_model():
        base_model = keras.applications.vgg19.VGG19(
            include_top=False,
            weights='imagenet',
            input_shape=(224,224,3)
        )
        base_model.trainable = False
        input_layer = keras.Input(shape=(224,224,3))
        x = data_augmentation(input_layer)
        x = tf.keras.applications.vgg19.preprocess_input(x)
        x = base_model(x, training=False)
        x = keras.layers.GlobalAveragePooling2D()(x)
        x = keras.layers.Dropout(0.2)(x)
        outputs = keras.layers.Dense(5, activation="softmax")(x)
        return keras.Model(input_layer, outputs)
    

修正后的完整代码示例

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.model_selection import KFold
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import sparse_categorical_crossentropy

# 数据加载
train_ds = tf.keras.utils.image_dataset_from_directory(
  directory ='/gdrive/My Drive/Flies_dt/224x224',
  validation_split=0.4,
  subset="training",
  seed=123,
  image_size=(224, 224),
  batch_size=32)

val_ds = tf.keras.utils.image_dataset_from_directory(
  directory ='/gdrive/My Drive/Flies_dt/224x224',
  validation_split=0.4,
  subset="validation",
  seed=123,
  image_size=(224, 224),
  batch_size=32)

train_images = np.concatenate(list(train_ds.map(lambda x, y:x)))
train_labels = np.concatenate(list(train_ds.map(lambda x, y:y)))

val_images = np.concatenate(list(val_ds.map(lambda x, y:x)))
val_labels = np.concatenate(list(val_ds.map(lambda x, y:y)))

inputs = np.concatenate((train_images, val_images), axis=0)
targets = np.concatenate((train_labels, val_labels), axis=0)

# 数据增强(根据实际需求调整)
data_augmentation = keras.Sequential(
    [
        layers.RandomFlip("horizontal"),
        layers.RandomRotation(0.1),
    ]
)

# 模型配置
batch_size = 32
no_classes = 5
no_epochs = 10
optimizer = Adam()
verbosity = 1
num_folds = 10

# 初始化结果存储列表
acc_per_fold = []
loss_per_fold = []

# 定义K折验证器
kfold = KFold(n_splits=num_folds, shuffle=True, random_state=123)

# 模型构建函数
def build_model():
    base_model = keras.applications.vgg19.VGG19(
        include_top=False,
        weights='imagenet',
        input_shape=(224,224,3)
    )
    base_model.trainable = False
    input_layer = keras.Input(shape=(224,224,3))
    x = data_augmentation(input_layer)
    x = tf.keras.applications.vgg19.preprocess_input(x)
    x = base_model(x, training=False)
    x = keras.layers.GlobalAveragePooling2D()(x)
    x = keras.layers.Dropout(0.2)(x)
    outputs = keras.layers.Dense(5, activation="softmax")(x)
    return keras.Model(input_layer, outputs)

# K折交叉验证主循环
fold_no = 1
for train_idx, test_idx in kfold.split(inputs, targets):
    # 构建新模型
    model = build_model()

    # 编译冻结阶段模型
    model.compile(
        loss=sparse_categorical_crossentropy, 
        optimizer=optimizer, 
        metrics=['accuracy'])

    # 打印fold信息
    print('------------------------------------------------------------------------')
    print(f'Training for fold {fold_no} ...')

    # 训练冻结阶段
    hist = model.fit(inputs[train_idx], targets[train_idx],
                     batch_size=batch_size,
                     epochs=no_epochs,
                     verbose=verbosity)
    
    # 解冻基础模型
    model.get_layer('vgg19').trainable = True

    # 编译微调阶段模型
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=1e-5),
        loss=sparse_categorical_crossentropy, 
        metrics=['accuracy'])

    # 训练微调阶段
    hist_2 = model.fit(inputs[train_idx], targets[train_idx],
                      batch_size=batch_size,
                      epochs=no_epochs,
                      verbose=verbosity)

    # 评估模型
    scores = model.evaluate(inputs[test_idx], targets[test_idx], verbose=0)
    print(f'Score for fold {fold_no}: {model.metrics_names
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:55:17