Keras 预测准确性与拟合结果不匹配



我正在尝试使用TensorFlow 2.0+Keras构建一个二进制分类模型。每个目标都具有5特征,我希望这个模型能够预测输入数据是否属于a

然而,fit()predict()的准确度完全不同。最奇怪的是,我把训练数据交给模型进行预测,而模型不返回1。

构建训练数据:(a的特征标记为1,其他为0(

num_train = 50
data = {  # the content is fake, just for understanding the format
'a': [(1, 2, 3, 4, 5), (2, 3, 4, 5, 6), ...],
'b': [(10, 20, 30, 40, 50), (20, 30, 40, 50, 60), ...],
...
}
train_x = []
train_y = []
for name, features in data.items():
for f in features[:num_train]:
train_x.append(f)
train_y.append(1 if name == 'a' else 0)
train_x = np.array(train_x)
train_y = np.array(train_y)

模型如下:

model = Sequential()
model.add(Dense(1, activation='sigmoid', input_dim=5))
model.compile(optimizer='sgd', loss='mse', metrics=['accuracy'])

并调用model.fit():

model.fit(x=train_x, y=train_y, validation_split=0.2, batch_size=10, epochs=50)

历元50之后:

Epoch 50/50
653/653 [==============================] - 0s 80us/sample - loss: 0.0745 - accuracy: 0.9234 - val_loss: 0.0192 - val_accuracy: 1.0000

最后,我用每个人的前3个样本来预测:

for name, features in data.items():
test_x = features[:3]
print(name, np.around(model.predict(test_x), decimals=2))

输出:

a [[0.14] [0.14] [0.14]]
b [[0.14] [0.13] [0.13]]
c [[0.14] [0.14] [0.13]]
...

完整的数据和源代码已上传到Google Drive,请查看链接。

检查源代码后,出现了一些实现问题:

  1. 训练数据和验证数据由Keras随机分配

在您的训练过程中,20%的数据被采样作为验证数据,但您不知道采样的数据是否平衡(即训练和验证数据中类的比例相同(。在您的情况下,由于不平衡,采样的训练数据很可能主要来自类0,因此您的模型没有学到任何有用的东西(因此所有样本的输出都是相同的0.13(。

一个更好的&更可控的方法是在训练前以分层的方式分割数据:

from sklearn.model_selection import train_test_split
num_train = 50
train_x = []
train_y = []
for name, features in data.items():
for f in features[:num_train]:
train_x.append(f)
train_y.append(1 if name == 'a' else 0)
train_x = np.array(train_x)
train_y = np.array(train_y)
# Split your data, and stratify according to the target label `train_y`
# Set a random_state, so that the train-test split is reproducible
x_train, x_test, y_train, y_test = train_test_split(train_x, train_y, test_size=0.2, stratify=train_y, random_state=123)

在列车运行期间,您可以指定validation_data,而不是使用validation_split:

model = Sequential()
model.add(Dense(1, activation='sigmoid', input_dim=5))
model.compile(optimizer='sgd', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(x=x_train, y=y_train, 
validation_data=(x_test, y_test), # Use this instead
class_weight={0:1,1:17},  # See explanation in 2. Imbalanced class
batch_size=10, epochs=500)
  1. 高度不平衡类-类1的频率是类0的17倍

您的类1a比类0小17倍(由其余部分组成(。如果你不调整类权重,你的模型会平等地对待所有样本,通过简单地将所有样本分类为0类,你的模式的准确率将达到94.4%(剩下的5.6%都来自1类,都被这个天真的模型错误地分类了(。

为了解释阶级失衡,一种方法是为少数民族阶级设定更高的损失。在这个例子中,我将类1的类权重设置为类0:的17倍

class_weight={0:1,1:17}

通过这样做,您告诉模型,来自类1的每个样本如果预测错误,将导致比错误分类的类0多17倍的惩罚。因此,该模型被迫更加关注第1类,尽管它是少数群体。

  1. 获取原始预测后未应用阈值

训练后(注意,我将epochs增加到500,模型在大约200个时期后收敛(,对您之前获得的测试集进行预测:

preds = model.predict(x_test)

你会得到这样的东西:

[[0.33624142]
[0.58196825]
[0.5549609 ]
[0.38138568]
[0.45235538]
[0.32419187]
[0.37660158]
[0.37013668]
[0.5794893 ]
[0.5611163 ]
......]

这是神经网络的原始输出,其范围从[0,1],因为最后一个激活层是sigmoid,它将其压缩到该范围。为了将其转换为所需的类预测(类0或1(,需要应用阈值。通常,该阈值被设置为0.5,其中输出大于0.5的预测意味着样本可能来自类别1,否则输出小于0.5。

因此,您需要使用设置输出阈值

threshold_output = np.where(preds > 0.5, 1, 0)

你会得到实际的类预测:

[[0]
[1]
[1]
[0]
[0]
[0]
[0]
[0]
[1]
[1]
...]

为了获得训练和测试的准确性

现在,为了检查训练和测试的准确性,您可以直接使用sklearn.metric,这为您省去了手动计算它们的麻烦:

from sklearn.metrics import accuracy_score
train_preds = np.where(model.predict(x_train) > 0.5, 1, 0)
test_preds = np.where(model.predict(x_test) > 0.5, 1, 0)
train_accuracy = accuracy_score(y_train, train_preds)
test_accuracy = accuracy_score(y_test, test_preds)
print(f'Train Accuracy : {train_accuracy:.4f}')
print(f'Test Accuracy  : {test_accuracy:.4f}')

它为您提供:

Train Accuracy : 0.7443
Test Accuracy  : 0.7073

希望这能回答你的问题!

最新更新