Keras实现CNN图像分类器:遍历DirectoryIterator报错求助
嘿,我来帮你梳理下问题所在和解决办法:
1. 直接触发报错的核心原因
你在flow_from_directory里设置了class_mode=None,这个参数的作用是让生成器只输出图像数据,不生成任何标签。所以当你写for inputs_batch, labels_batch in train_generator的时候,迭代器每次只返回一个元素(批量图像),你却试图把它拆成两个变量,自然会报“无法解包”之类的错误——这就是遍历失败的直接原因。
2. 关于类别定义的本质问题
flow_from_directory的设计逻辑就是靠子文件夹来自动识别类别:训练文件夹下必须给每个类别单独建一个子文件夹,比如:
MO444_dogs/train/ golden_retriever/ img_001.jpg img_002.jpg bulldog/ img_003.jpg ...
如果你的训练图像没有按类别分文件夹,那flow_from_directory确实没法自动生成标签,这时候有两种处理思路:
方案一:调整文件夹结构(最推荐)
如果能重新整理你的训练/验证图像,按类别创建子文件夹,那修改起来最省心:
- 把
class_mode改成'categorical'(对应多分类场景),这样生成器会自动输出one-hot编码的标签:
train_generator = datagen.flow_from_directory( train_dir, target_size=(224, 224), batch_size=batch_size, class_mode='categorical', # 这里修改 shuffle=True)
- 你原来定义的
train_labels = np.zeros(shape=(nTrain,3))是对的(假设是3分类),不用改。 - 现在你的循环代码就能正常跑了,因为此时迭代器每次会返回
(输入图像批量, 标签批量)的元组,刚好匹配你的解构逻辑。
方案二:不调整文件夹,手动维护标签
如果没法修改文件夹结构,那你得自己搞定每个图像的标签映射(比如靠文件名规则、外部CSV记录等),然后换一种生成器写法:
第一步:整理图像路径和对应标签
先把所有训练图像的路径列出来,再根据你的规则生成标签:
# 遍历训练文件夹,筛选出所有图像文件 train_img_paths = [ os.path.join(train_dir, fname) for fname in os.listdir(train_dir) if fname.lower().endswith(('.jpg', '.png', '.jpeg')) ] # 这里需要你自己实现标签逻辑,举个例子: def get_image_label(fname): # 假设文件名里的"type1"对应类别0,"type2"对应类别1,以此类推 if 'type1' in fname: return 0 elif 'type2' in fname: return 1 else: return 2 # 生成one-hot编码的标签数组 train_labels = to_categorical( [get_image_label(os.path.basename(path)) for path in train_img_paths], num_classes=3 )
第二步:用flow方法创建生成器
先把所有图像加载成数组,再用ImageDataGenerator的flow方法(而不是flow_from_directory)来生成带标签的迭代器:
# 批量加载并预处理图像 train_imgs = [] for path in train_img_paths: img = load_img(path, target_size=(224, 224)) img_array = img_to_array(img) train_imgs.append(img_array) train_imgs = np.array(train_imgs) / 255.0 # 归一化到0-1区间 # 创建带标签的生成器 train_generator = datagen.flow( train_imgs, train_labels, batch_size=batch_size, shuffle=True )
第三步:正常运行循环
现在你的循环代码就能正常执行了,因为flow生成的迭代器会返回(inputs_batch, labels_batch)的元组,完全匹配你的解构需求。
额外小技巧:简化特征提取代码
其实你做预训练模型特征提取的时候,完全不用手动写循环,用Keras自带的方法就能搞定:
# 当train_generator是带标签的生成器时 train_features = conv_base.predict(train_generator, steps=np.ceil(nTrain / batch_size)) # 直接获取所有标签并转成one-hot编码 train_labels = to_categorical(train_generator.classes, num_classes=3)
这样既避免了手动循环可能踩的坑,代码也更简洁高效。
内容的提问来源于stack exchange,提问作者Pvic

