我的目标是用EfficientNet进行多标签图像分类.它应该以一张图片作为输入,例如告诉用户它在图片上看到了一个人和一只狗,这意味着概率不会加到1 --每个类的概率从0到1。简而言之,我想把一个多类的解决方案转换成一个多标签的解决方案。
我正在使用来自Kaggle的COCO数据集的一个小子集,您可以找到它的这里。它包含大约100 k的图像,精确地包含了80个类。labels.csv包含一个带有文件名的列和一个用于目标输出的热编码列。
我在子集label.csv中添加了标题,以了解哪些列引用哪个标签。我还将所有的图像文件复制到一个目录中(数据集/coco_subset/train),因为标签信息也在一个.csv文件中,并且无法让DataGenerators单独检索图像。但这不是我的主要问题!(不过,对于如何正确处理这件事,我还是很感激。)
我得到了模型来编写并开始训练。首先我尝试了20次,然后是50次,最后是100次。在花费了大量时间( 6GB VRAM大约12小时)的100个历次之后,我仍然无法达到可接受的精度。实际上,在整个100个年代中,精度停滞在20%左右。同样的损失也发生在4-5左右,如下图所示:
在下一节中,您可以找到关联的代码。EfficientNet作为新的多标签分类方法的基本模型.对于EfficientNets预先训练的权重,我选择了imagenet权重。我用扁平、Dropout和密集层替换了原来的顶层,节点数=可能的输出数。
我不希望模型库是可训练的,因为我读到,通过这种方式,它使用预先训练过的imagenet权重来提取特征。同时,将这个属性设置为True意味着需要更多的时间进行培训。
我用乙状结肠代替了最终的激活功能。作为损失函数,我使用了二元交叉熵。我将EfficientNetB0用于性能目的。当我从训练变体B4获得好的结果时,我想切换到B5或B0。
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Dense, Flatten
from keras.optimizers import Adam
import pandas as pd
# define input shape and batch size
input_shape = (224, 224, 3)
batch_size = 256
# paths
train_dir = "datasets/coco_subset/images/train"
# test_dir = "datasets/coco_subset/images/test" # didn't work with ImageDataGeneartor.flow_from_dataframe
csv_dir = "datasets/coco_subset/labels/labels_train.csv"
label_names_dir = "datasets/coco_subset/labels/categories.csv"
# read csv data for loading image label information
df = pd.read_csv(csv_dir)
df_labels = pd.read_csv(label_names_dir)
label_names = list(df_labels["Labels"])
x_col = df.columns[0]
y_cols = list(df.columns[1:len(label_names)+1])
# load input images and split into training, test and validation
datagen = ImageDataGenerator(rescale=1./255,validation_split=.25)
train_generator = datagen.flow_from_dataframe(
df,
directory=train_dir,
x_col=x_col,
y_col=y_cols,
subset="training",
target_size=input_shape[0:2],
color_mode="rgb",
class_mode="raw", # for multilabel output
batch_size=batch_size,
shuffle=True,
seed=42,
interpolation="bilinear",
validate_filenames=False
)
test_generator = datagen.flow_from_dataframe(
df,
directory=train_dir,
x_col=x_col,
y_col=y_cols,
subset="validation",
target_size=input_shape[0:2],
color_mode="rgb",
class_mode="raw",
batch_size=batch_size,
shuffle=True,
seed=42,
interpolation="bilinear",
validate_filenames=False
)
# build model
n_outputs = len(label_names)
model_base = EfficientNetB0(weights='imagenet', include_top=False, input_shape=input_shape)
model_base.trainable = False
model = Sequential([
model_base,
Dropout(0.25),
Flatten(),
Dense(n_outputs, activation="sigmoid")
])
model.summary()
# compile model
opt = Adam(learning_rate=0.01)
model.compile(optimizer=opt, loss="binary_crossentropy", metrics=["accuracy"])
# define training and validation steps
steps_per_epoch = train_generator.samples // train_generator.batch_size
validation_steps = test_generator.samples // test_generator.batch_size
# train model
hist = model.fit(
train_generator,
epochs=100, steps_per_epoch=steps_per_epoch,
validation_data=test_generator,
validation_steps=validation_steps).history
总结一下我的问题:
发布于 2022-07-02 02:51:39
你的代码看起来是正确的。特别是,使用sigmoid激活是正确的(因为您需要多个热输出,使用softmax没有意义)和binary_cross_entropy (考虑到问题不是二进制的,但每个决定都是这样)。
那它为什么不起作用?我怀疑问题出在你训练过的体重上。当您使用冻结的、预先训练过的权重时,您必须使用与导出权重时完全相同的预处理。作为一个健全检查,您可以获取原始ImageNet的一个子集,并验证您对这些图像的评估准确性与论文所得到的大致相同。
您还会询问是否应该将基本模型设置为可培训的。如果这样做,您将不那么依赖于规范化方案,并且您将得到更好地调整到特定问题的特性。另一方面,由于您的数据集比ImageNet更小,这些特性实际上可能不太好(而且需要更长的时间来训练)。
发布于 2021-05-15 10:42:22
乙状结肠激活和binary_cross_entropy损失用于二值分类。
应用softmax激活和categorical_cross_entropy损失进行多类分类.
...
model = Sequential([
model_base,
Dropout(0.25),
Flatten(),
Dense(n_outputs, activation="softmax")
])
...
model.compile(optimizer=opt, loss="categorical_cross_entropy", metrics=["accuracy"])
```
https://datascience.stackexchange.com/questions/94408
复制相似问题