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

训练DenseNet121时第二个Epoch出现AttributeError求助

DenseNet121训练报错:第二个Epoch触发AttributeError且数据耗尽

问题现象

训练DenseNet121时第一个Epoch正常完成,但进入第二个Epoch时触发AttributeError,同时收到输入数据耗尽的警告。

超参数

{'epochs': 30, 'lr': 0.001, 'lr_decay': 0.9, 'lam_recon': 0.392, 'debug': True, 'save_dir': 'saveDir', 'weights': None}

报错日志与堆栈

Epoch 1/30
D:\Instalki\Python312\Lib\site-packages\keras\src\trainers\data_adapters\py_dataset_adapter.py:121: UserWarning: Your <code>PyDataset</code> class should call <code>super().__init__(**kwargs)</code> in its constructor. <code>**kwargs</code> can include <code>workers</code>, <code>use_multiprocessing</code>, <code>max_queue_size</code>. Do not pass these arguments to <code>fit()</code>, as they will be ignored.
self._warn_if_super_not_called()
81/81 ━━━━━━━━━━━━━━━━━━━━ 0s 26s/step - accuracy: 0.6777 - loss: 0.8694
Epoch 1: val_accuracy improved from -inf to 0.77680, saving model to saveDir/weights.weights.h5
81/81 ━━━━━━━━━━━━━━━━━━━━ 2426s 29s/step - accuracy: 0.6791 - loss: 0.8657 - val_accuracy: 0.7768 - val_loss: 0.5253 - learning_rate: 0.0010
Epoch 2/30

D:\Instalki\Python312\Lib\contextlib.py:158: UserWarning: Your input ran out of data; interrupting training. Make sure that your dataset or generator can generate at least <code>steps_per_epoch * epochs</code> batches. You may need to use the <code>.repeat()</code> function when building your dataset.
self.gen.throw(value)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[15], line 27
     24 if args.save_dir is not None and not os.path.exists(args.save_dir):
     25     os.makedirs(args.save_dir)
---&gt; 27 history = train(model=model,
     28                 train_generator=train_generator,
     29                 validation_generator=validation_generator,
     30                 args=args)

Cell In[14], line 16, in train(model, train_generator, validation_generator, args)
     12 tb = TensorBoard(log_dir=args.save_dir + '/logs', histogram_freq=1)
     14 early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
---&gt; 16 model.fit(train_generator,
     17           steps_per_epoch=len(train_generator),
     18           epochs=args.epochs,
     19           validation_data=validation_generator,
     20           validation_steps=len(validation_generator),
     21           callbacks=[log, tb, checkpoint, lr_decay, early_stopping])
     23 model.save_weights(args.save_dir + '/trained_model_weights.h5')
     24 print('Trained model weights saved to \'%s/trained_model_weights.h5\'' % args.save_dir)

File D:\Instalki\Python312\Lib\site-packages\keras\src\utils\traceback_utils.py:122, in filter_traceback.&lt;locals&gt;.error_handler(*args, **kwargs)
    119     filtered_tb = _process_traceback_frames(e.__traceback__)
    120     # To get the full stack trace, call:
    121     # `keras.config.disable_traceback_filtering()`
--&gt; 122     raise e.with_traceback(filtered_tb) from None
    123 finally:
    124     del filtered_tb

File D:\Instalki\Python312\Lib\site-packages\keras\src\backend\tensorflow\trainer.py:350, in TensorFlowTrainer.fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq)
    329         self._eval_epoch_iterator = TFEpochIterator(
    330             x=val_x,
    331             y=val_y,
   (...)
    337             shuffle=False,
    338         )
    339     val_logs = self.evaluate(
    340         x=val_x,
    341         y=val_y,
   (...)
    347         _use_cached_eval_dataset=True,
    348     )
    349     val_logs = {
--&gt; 350         &quot;val_&quot; + name: val for name, val in val_logs.items()
    351     }
    352     epoch_logs.update(val_logs)
    354 callbacks.on_epoch_end(epoch, epoch_logs)

AttributeError: 'NoneType' object has no attribute 'items'

完整代码

import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf
from tensorflow.keras.applications import DenseNet121
from tensorflow.keras.models import Model
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.callbacks import CSVLogger, ModelCheckpoint, LearningRateScheduler, TensorBoard

batch_size = 64
image_shape=(524,524)

train_dir = "brain-tumor-mri-dataset/Training"
test_dir = "brain-tumor-mri-dataset/Testing"
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=10,
    width_shift_range=0.1,
    height_shift_range=0.1,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest',
    validation_split=0.1
)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=image_shape,
    batch_size=batch_size,
    shuffle=True,
    color_mode='rgb',
    class_mode='categorical',
    subset='training'
)

validation_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=image_shape,
    batch_size=batch_size,
    shuffle=True,
    color_mode='rgb',
    class_mode='categorical',
    subset='validation'
)

test_generator = test_datagen.flow_from_directory(
    test_dir,
    target_size=image_shape,
    batch_size=batch_size,
    color_mode='rgb',
    class_mode='categorical'
)
def create_densenet_model(input_shape=(524,524,3), num_classes=4):
    densenet121_base = DenseNet121(weights='imagenet', include_top=False, input_shape=input_shape)

    for layer in densenet121_base.layers:
        layer.trainable = False

    x = densenet121_base.output
    x = GlobalAveragePooling2D()(x)
    x = Dense(1024, activation='relu')(x)
    predictions = Dense(num_classes, activation='softmax')(x)

    model = Model(inputs=densenet121_base.input, outputs=predictions)
    return model

model = create_densenet_model()
model.compile(optimizer='adam',
    loss='categorical_crossentropy',
    metrics=['accuracy'])
model.summary()

def train(model,
          train_generator,
          validation_generator,
          args):

    log = CSVLogger(args.save_dir + '/log.csv')
    checkpoint = ModelCheckpoint(args.save_dir + '/weights.weights.h5', monitor='val_accuracy',
                             save_best_only=True, save_weights_only=True, verbose=1)
    
    lr_decay = LearningRateScheduler(schedule=lambda epoch: args.lr * (args.lr_decay ** epoch))

    tb = TensorBoard(log_dir=args.save_dir + '/logs', histogram_freq=1)

    early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)

    model.fit(train_generator,
              steps_per_epoch=len(train_generator),
              epochs=args.epochs,
              validation_data=validation_generator,
              validation_steps=len(validation_generator),
              callbacks=[log, tb, checkpoint, lr_decay, early_stopping])

    model.save_weights(args.save_dir + '/trained_model_weights.h5')
    print('Trained model weights saved to \'%s/trained_model_weights.h5\'' % args.save_dir)

    return model

class Args:
    def __init__(self, epochs=50, lr=0.001, lr_decay=0.9, lam_recon=0.392, routings=3, shift_fraction=0.1, debug=False, save_dir='./result', testing=False, digit=5, weights=None):
        self.epochs = epochs
        self.lr = lr
        self.lr_decay = lr_decay
        self.lam_recon = lam_recon
        self.debug = debug
        self.save_dir = save_dir
        self.weights = weights

args = Args(
    epochs=30,
    lr=0.001,
    lr_decay=0.9,
    lam_recon=0.392,
    debug=True,
    save_dir='saveDir',
    weights=None
)

print("Hyperparameters:")
print(args.__dict__)

if args.save_dir is not None and not os.path.exists(args.save_dir):
    os.makedirs(args.save_dir)

history = train(model=model,
                train_generator=train_generator,
                validation_generator=validation_generator,
                args=args)

解决方案

1. 修复数据耗尽问题

ImageDataGenerator生成器遍历完所有数据后会停止,不会自动重复,导致第二个Epoch无数据可读。给生成器添加repeat()方法,使其循环生成数据:

train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=image_shape,
    batch_size=batch_size,
    shuffle=True,
    color_mode='rgb',
    class_mode='categorical',
    subset='training'
).repeat()  # 添加repeat()

validation_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=image_shape,
    batch_size=batch_size,
    shuffle=True,
    color_mode='rgb',
    class_mode='categorical',
    subset='validation'
).repeat()  # 添加repeat()

2. 调整训练步数计算

使用repeat()后生成器会无限循环,必须明确指定每个Epoch的步数,避免无限训练。用总样本数除以batch_size计算准确步数:

# 生成器创建后计算步数
train_steps = train_generator.samples // train_generator.batch_size
val_steps = validation_generator.samples // validation_generator.batch_size

# 在model.fit中替换原有的len(generator)
model.fit(train_generator,
          steps_per_epoch=train_steps,
          epochs=args.epochs,
          validation_data=validation_generator,
          validation_steps=val_steps,
          callbacks=[log, tb, checkpoint, lr_decay, early_stopping])

3. 错误根源说明

数据耗尽导致模型验证阶段返回None,后续代码尝试调用val_logs.items()触发AttributeError。解决数据耗尽问题后,该错误会自动消失。


内容的提问来源于stack exchange,提问作者Natalia Marszał

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:25:55