我已经实现了以下指标,用于查看我认为相关的类别的精确度和召回率。
metrics=[tf.keras.metrics.Recall(class_id=1, name='Bkwd_R'),tf.keras.metrics.Recall(class_id=2, name='Fwd_R'),tf.keras.metrics.Precision(class_id=1, name='Bkwd_P'),tf.keras.metrics.Precision(class_id=2, name='Fwd_P')]
我如何在Tensorflow 2.5中为F1分数实现相同的结果(即仅针对类别1和类别2,而不是类别0,且不使用自定义函数)?
更新
使用以下指标设置:
tfa.metrics.F1Score(num_classes = 3, average = None, name = f1_name)
我在训练过程中得到了以下结果:
13367/13367 [==============================] 465s 34ms/step - loss: 0.1683 - f1_score: 0.5842 - val_loss: 0.0943 - val_f1_score: 0.3314
当我执行model.evaluate时:
224/224 [==============================] - 11s 34ms/step - loss: 0.0665 - f1_score: 0.3325
评分结果是:
Score: [0.06653735041618347, array([0.99740255, 0. , 0. ], dtype=float32)]
问题在于这是基于平均值进行训练的,但我希望基于数组中最后两个值/类别(在这种情况下为0)的合理平均值/每个类别的F1分数进行训练。
编辑
我将接受一个非Tensorflow特定函数,只要它能给出所需的结果(包括完整的函数和在fit过程中调用的代码),但我真的希望能找到使用现有Tensorflow代码的方法,如果有的话)
回答:
正如David Harris的评论中提到的,神经网络模型是基于损失函数进行训练的,而不是基于指标得分。损失函数有助于通过反向传播驱动模型朝着提供准确标签的解决方案前进。指标有助于提供对模型性能的可比性评估,这些评估对人类更易读。
因此,既然如此,我觉得你问题中所说的是“有三个类别,我希望模型更关注其中的后两个”。我想
如果情况确实如此,你可以采取的一个方法是根据标签加权你的样本。假设你有一个数组y_train
中的标签。
# 你想要关注哪些类别classes_i_care_about = [1, 2]# 初始化所有权重为1.0sample_weights = np.ones(shape=(len(y_train),))# 对你关心的类别增加50%的权重sample_weight[np.isin(y_train, classes_i_care_about)] = 1.5...model.fit( x=X_train, y=y_train, sample_weight=sample_weight, epochs=5)
这是我在不了解更多信息的情况下所能提供的最佳建议。如果你正在寻找其他关于如何让你的模型在某些类别上表现得更好的信息,其他信息可能会有用,比如:
- 你的数据集中标签的比例是多少?
- 你的模型架构的最后一层是什么?
Dense(3, activation="softmax")
? - 你在使用什么损失函数?
这是一个更完整的、可复现的示例,展示了我关于样本权重的讨论:
import numpy as npfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import OneHotEncoderfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.optimizers import Adamimport tensorflow_addons as tfairis_data = load_iris() # 加载iris数据集x = iris_data.datay_ = iris_data.target.reshape(-1, 1) # 将数据转换为单列# 对类别标签进行One Hot编码encoder = OneHotEncoder(sparse=False)y = encoder.fit_transform(y_)# 分割数据用于训练和测试train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.20)# 构建模型def get_model(): model = Sequential() model.add(Dense(10, input_shape=(4,), activation='relu', name='fc1')) model.add(Dense(10, activation='relu', name='fc2')) model.add(Dense(3, activation='softmax', name='output')) # Adam优化器,学习率为0.001 optimizer = Adam(lr=0.001) model.compile( optimizer, loss='categorical_crossentropy', metrics=[ 'accuracy', tfa.metrics.F1Score( num_classes=3, average=None, ) ] ) return modelmodel = get_model()model.fit( train_x, train_y, verbose=2, batch_size=5, epochs=25,)results = model.evaluate(test_x, test_y)print('最终测试集损失: {:4f}'.format(results[0]))print('最终测试集准确率: {:4f}'.format(results[1]))print('最终测试F1分数: {}'.format(results[2]))
最终测试集损失: 0.585964最终测试集准确率: 0.633333最终测试F1分数: [1. 0.15384616 0.6206897 ]
现在,我们为类别1和2增加权重:
sample_weight = np.ones(shape=(len(train_y),))sample_weight[ (train_y[:, 1] == 1) | (train_y[:, 2] == 1)] = 1.5model = get_model()model.fit( train_x, train_y, sample_weight=sample_weight, verbose=2, batch_size=5, epochs=25,)results = model.evaluate(test_x, test_y)print('最终测试集损失: {:4f}'.format(results[0]))print('最终测试集准确率: {:4f}'.format(results[1]))print('最终测试F1分数: {}'.format(results[2]))
最终测试集损失: 0.437623最终测试集准确率: 0.900000最终测试F1分数: [1. 0.8571429 0.8571429]
在这里,模型强调学习这些类别,它们的表现得到了改善。