TensorFlow 1.10自定义Model子类调用fit_generator报错求解决
这个报错的核心原因是:在TensorFlow 1.10版本中,继承tf.keras.Model的子类化模型(也就是你自定义call方法的这种)还未支持fit_generator。这类模型属于"未构建"状态,只有在第一次接收输入时才会初始化权重和计算图,而旧版本的fit_generator没有处理这个预构建逻辑,因此抛出了NotImplementedError。
下面给你三种可行的解决方案,你可以根据自己的需求选择:
方案一:手动触发模型构建(最简单的修改)
在调用compile之前,先传入一个符合输入形状的张量调用模型,让模型提前完成构建。这样fit_generator就能正常识别模型的结构了。同时注意:fit_generator需要传入生成器实例,你原来的代码里写的generate是函数本身,要改成generate()。
修改后的主函数代码:
if __name__ == "__main__": test_net = TestNet(class_count=5) # 手动构建模型:传入带batch维度的dummy输入(模型输入要求是(batch_size, seq_len, features)) dummy_input = np.zeros(shape=(1, 100, 3)) test_net(dummy_input) optimizer = tf.keras.optimizers.Adam() test_net.compile(optimizer, loss=segmentation_loss) # 注意这里要调用generate()得到生成器实例 history = test_net.fit_generator(generate(), steps_per_epoch=1000, epochs=10)
方案二:改用函数式API定义模型
如果不想手动处理模型构建,可以改用Keras的函数式API来定义模型。函数式API的模型在定义时就已经完成了构建,天然支持fit_generator,代码结构也很清晰:
import tensorflow as tf import numpy as np # 用函数式API重定义模型 def TestNet(class_count): inputs = tf.keras.Input(shape=(None, 3)) conv_output = tf.keras.layers.Conv1D(class_count, kernel_size=1)(inputs) return tf.keras.Model(inputs=inputs, outputs=conv_output) # 损失函数和生成器保持不变 def segmentation_loss(labels, logits): cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( labels=labels, logits=logits) return tf.reduce_mean(cross_entropy) def generate(): while True: yield (np.zeros(shape=(100,3)), np.zeros(shape=(100))) if __name__ == "__main__": test_net = TestNet(class_count=5) optimizer = tf.keras.optimizers.Adam() test_net.compile(optimizer, loss=segmentation_loss) history = test_net.fit_generator(generate(), steps_per_epoch=1000, epochs=10)
方案三:手动实现训练循环(完全自定义)
如果需要更灵活的训练逻辑,可以放弃fit_generator,自己写循环迭代生成器,用train_on_batch或者手动计算梯度更新。这种方式完全避开了旧版本的API限制:
if __name__ == "__main__": test_net = TestNet(class_count=5) optimizer = tf.keras.optimizers.Adam() # 先手动构建模型 dummy_input = np.zeros(shape=(1, 100, 3)) test_net(dummy_input) epochs = 10 steps_per_epoch = 1000 for epoch in range(epochs): print(f"Epoch {epoch+1}/{epochs}") total_loss = 0.0 for step in range(steps_per_epoch): # 从生成器获取单样本,添加batch维度(模型要求输入带batch轴) x, y = next(generate()) x_batch = np.expand_dims(x, axis=0) y_batch = np.expand_dims(y, axis=0) # 使用train_on_batch执行一次梯度更新 loss = test_net.train_on_batch(x_batch, y_batch) total_loss += loss # 每100步打印一次损失 if step % 100 == 0: print(f"Step {step:4d} | Loss: {loss:.4f}") avg_loss = total_loss / steps_per_epoch print(f"Epoch {epoch+1} | Average Loss: {avg_loss:.4f}\n")
额外注意点
你原来的生成器返回的样本是(100,3)和(100),没有batch维度,而Keras模型的输入默认需要带batch轴(形状为(batch_size, seq_len, features)),所以在喂给模型时一定要用np.expand_dims添加batch维度,否则会报形状不匹配的错误。
内容的提问来源于stack exchange,提问作者Miiller

