将二进制分类转换为多标签?
Posted
技术标签:
【中文标题】将二进制分类转换为多标签?【英文标题】:Converting binary classification to multi label? 【发布时间】:2020-10-04 21:40:45 【问题描述】:我正在尝试将我的二值图像分类模型转换为多标签,但我在使用 NumPy 数组时遇到了错误 期望值 二维值 谁能帮我编辑 代码。
我尝试使用 multilabelBinazier,但运气不好,这里有人可以帮我吗?
errors#用法
# python train.py --dataset dataset
# import the necessary packages
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import VGG16
from tensorflow.keras.layers import AveragePooling2D
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import to_categorical
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import cv2
import os
# construct the argument parser and parse the arguments
# initialize the initial learning rate, number of epochs to train for,
# and batch size
INIT_LR = 1e-3
EPOCHS = 40
BS = 66
# grab the list of images in our dataset directory, then initialize
# the list of data (i.e., images) and class images
print("[INFO] loading images...")
imagePaths = list(paths.list_images('/content/drive/My Drive/testset/'))
data = []
labels = []
# loop over the image paths
for imagePath in imagePaths:
# extract the class label from the filename
label = imagePath.split(os.path.sep)[-2]
# load the image, swap color channels, and resize it to be a fixed
# 224x224 pixels while ignoring aspect ratio
image = cv2.imread(imagePath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = cv2.resize(image, (224, 224))
# update the data and labels lists, respectively
data.append(image)
labels.append(label)
# convert the data and labels to NumPy arrays while scaling the pixel
# intensities to the range [0, 255]
data = np.array(data) / 255.0
labels = np.array(labels)
# perform one-hot encoding on the labels
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)
# partition the data into training and testing splits using 80% of
# the data for training and the remaining 20% for testing
(trainX, testX, trainY, testY) = train_test_split(data, labels,
test_size=0.20, stratify=labels, random_state=42)
# initialize the training data augmentation object
trainAug = ImageDataGenerator(
rotation_range=15,
fill_mode="nearest")
# load the VGG16 network, ensuring the head FC layer sets are left
# off
baseModel = VGG16(weights="imagenet", include_top=False,
input_tensor=Input(shape=(224, 224, 3)))
# construct the head of the model that will be placed on top of the
# the base model
headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(4, 4))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(64, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)
# place the head FC model on top of the base model (this will become
# the actual model we will train)
model = Model(inputs=baseModel.input, outputs=headModel)
# loop over all layers in the base model and freeze them so they will
# *not* be updated during the first training process
for layer in baseModel.layers:
layer.trainable = False
# compile our model
print("[INFO] compiling model...")
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])
# train the head of the network
print("[INFO] training head...")
H = model.fit(
trainAug.flow(trainX, trainY, batch_size=BS),
steps_per_epoch=len(trainX) // BS,
validation_data=(testX, testY),
validation_steps=len(testX) // BS,
epochs=EPOCHS)
# make predictions on the testing set
print("[INFO] evaluating network...")
predIdxs = model.predict(testX, batch_size=BS)
# for each image in the testing set we need to find the index of the
# label with corresponding largest predicted probability
predIdxs = np.argmax(predIdxs, axis=1)
# show a nicely formatted classification report
print(classification_report(testY.argmax(axis=1), predIdxs,
target_names=lb.classes_))
# compute the confusion matrix and and use it to derive the raw
# accuracy, sensitivity, and specificity
cm = confusion_matrix(testY.argmax(axis=1), predIdxs)
total = sum(sum(cm))
acc = (cm[0, 0] + cm[1, 1]) / total
sensitivity = cm[0, 0] / (cm[0, 0] + cm[0, 1])
specificity = cm[1, 1] / (cm[1, 0] + cm[1, 1])
# show the confusion matrix, accuracy, sensitivity, and specificity
print(cm)
print("acc: :.4f".format(acc))
print("sensitivity: :.4f".format(sensitivity))
print("specificity: :.4f".format(specificity))
# plot the training loss and accuracy
N = EPOCHS
plt.style.use("ggplot")
plt.figure()
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy on COVID-19 Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig("plot.png")
# serialize the model to disk
print("[INFO] saving COVID-19 detector model...")
model.save('/content/drive/My Drive/setcovid/model.h5', )
【问题讨论】:
你想做什么?您的损失仍然是 binary_crossentropy,所以您想预测 2 个类别吗?我最初的猜测是您的标签形状不正确。假设你有 4 个类,如果是第二类,你不希望第一个标签是 [0,1,0,0] 吗? 【参考方案1】:取决于您如何预处理标签
有三点
1.第一个:标签形状
观察你的标签形状。从您的代码中,我猜测您的标签形状必须是一维的,例如(5000,)
,您可以使用labels.shape
看到它。
要使其成为二维,请使用此
labels = np.expand_dims(labels,axis=1)
2. 第二个:最后一个dense layer的单元
您在最后一个dense
第 2 层中使用了单元数,所以基本上它是一个多类分类,其行为类似于二元分类。我建议如果你在做二进制分类,那么最后使用 1 个单元 dense
层和激活 softmax
以外的。如果您的数据集有 3 个或超过 3 个类,则使用带有输出类数量的 dense
层和 softmax
激活。但是对于这种情况,标签应该是one hot encoded
。
3.第三个:改变损失函数
如果您仍将 2 单位输出用于最后一个密集层。将损失函数从 binary_crossentropy
更改为 categorical_crossentropy
【讨论】:
【参考方案2】:取决于您的 TF 版本,但如果您的输入是生成器,也许您应该使用 fit_generator (https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit_generator) 方法而不是 model.fit。
还要检查之前提到的答案
【讨论】:
【参考方案3】:我认为你错了
将numpy数组列表转换为单个numpy数组,可以使用
data = np.stack( data, axis=0 )/255
或
data = np.concatenate( data, axis=0 )
# 它会返回 shape (bath_size, dim_height * dim_width, channel) 然后重塑数据
data.reshape(bath_size, dim_height, dim_width, channel)
或
data = np.vstack(data)/255.0
another answer
使用 lb.fit_transform(labels) 后,您无需使用 to_categorical(labels)
不清楚你想要什么,多标签还是多类?这不一样。如果要更改为多标签情况,则 binary_crossentropy 是解决方案之一。但是你会混淆准确度(默认)作为度量,在多标签中是错误的,你可以使用准确度计算进行多标签分类。并且不要在最终激活输出中使用 softmax,请使用 sigmoid。或者,如果它的多类将损失更改为 categorical_crossentropy,如果它的标签是一种热编码,或者如果它的标签是数字非热编码,则将损失更改为 sparse_categorical_crossentropy。
如果它之前的二值图像分类,输出模型必须是1(1或0)而不是2
希望这个答案成为你的解决方案
【讨论】:
以上是关于将二进制分类转换为多标签?的主要内容,如果未能解决你的问题,请参考以下文章