训练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) ---> 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) ---> 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.<locals>.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()` --> 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 = { --> 350 "val_" + 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ł
相关产品推荐
相关产品推荐

