Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность
предисловие
Метрики играют довольно важную роль в области машинного обучения или глубокого обучения. Мы начинаем с выбора метрик на основе вопроса, чтобы понять базовые оценки для конкретной модели. В этом блоге мы рассмотрим лучшие и наиболее часто используемые показатели для классификации с несколькими метками и их различия.
Далее давайте углубимся в то, что такое классификация с несколькими метками, на всякий случай, если вам это нужно. Если у нас есть данные о характеристиках собаки, мы можем предсказать, к какой породе и категории домашних животных она относится.
В случае обнаружения объектов классификация по нескольким меткам предоставляет нам список всех объектов на изображении, как показано на рисунке ниже. Мы видим, что классификатор обнаружил на изображении 3 объекта. Если общее количество обучающих объектов равно 4, его можно выразить в виде следующего списка[1 0 1 1]
(Соответствующий объект[狗、人、自行车、卡车]
). Эта классификация называется классификацией с несколькими метками.
Разница между классификацией Multiclass и классификацией Multilabel:
- Мультикатегориальная классификация:Классификационные задачи с более чем двумя классами. Мультиклассовая классификация предполагает, что каждый образец относится к одной и только одной этикетке, например, фрукт может быть яблоком или апельсином, но не тем и другим одновременно.
- Многоуровневая классификация:Назначьте одну или несколько меток каждому образцу. Например, новость может относиться как к спорту, так и к развлечениям.
Метрики оценки классификации с несколькими метками
Наиболее распространенные метрики, подходящие для классификации по нескольким меткам, следующие:
- Precision at k
- Avg precision at k
- Mean avg precision at k
- Sampled F1 Score
- Log Loss
Рассмотрим детали этих показателей.
Precision at k (P@K)
Учитывая список фактических и прогнозируемых классов, Precision@K определяется как количество правильных прогнозов с учетом только k лучших элементов, деленное на k лучших элементов каждого прогнозируемого класса. Диапазон значений от 0 до 1.
Пример кода выглядит следующим образом:
def patk(actual, pred, k):
#we return 0 if k is 0 because
# we can't divide the no of common values by 0
if k == 0:
return 0
#taking only the top k predictions in a class
k_pred = pred[:k]
# taking the set of the actual values
actual_set = set(actual)
print(list(actual_set))
# taking the set of the predicted values
pred_set = set(k_pred)
print(list(pred_set))
# 求预测值与真实值得交集
common_values = actual_set.intersection(pred_set)
print(common_values)
return len(common_values)/len(pred[:k])
# defining the values of the actual and the predicted class
y_true = [1 ,2, 0]
y_pred = [1, 1, 0]
if __name__ == "__main__":
print(patk(y_true, y_pred,3))
Результаты приведены ниже:
K:3,真实值:{0, 1, 2}, 预测值:{0, 1}, 交集:{0, 1}, P@k:0.6666666666666666
0.6666666666666666
Average Precision at K (AP@K)
Он определяется как среднее значение всех Precision@K для k = от 1 до k. Для большей ясности давайте посмотрим на код. Диапазон значений от 0 до 1.
import numpy as np
def apatk(acutal, pred, k):
#creating a list for storing the values of precision for each k
precision_ = []
for i in range(1, k+1):
#calculating the precision at different values of k
# and appending them to the list
precision_.append(patk(acutal, pred, i))
#return 0 if there are no values in the list
if len(precision_) == 0:
return 0
#returning the average of all the precision values
return np.mean(precision_)
#defining the values of the actual and the predicted class
y_true = [[1,2,0,1], [0,4], [3], [1,2]]
y_pred = [[1,1,0,1], [1,4], [2], [1,3]]
if __name__ == "__main__":
for i in range(len(y_true)):
for j in range(1, 4):
print(
f"""
y_true = {y_true[i]}
y_pred = {y_pred[i]}
AP@{j} = {apatk(y_true[i], y_pred[i], k=j)}
"""
)
print("-----------")
Результаты приведены ниже:
K:1,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
y_true = [1, 2, 0, 1]
y_pred = [1, 1, 0, 1]
AP@1 = 1.0
K:1,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:0.5
y_true = [1, 2, 0, 1]
y_pred = [1, 1, 0, 1]
AP@2 = 0.75
K:1,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:0.5
K:3,真实值:{0, 1, 2}, 预测值:{0, 1}, 交集:{0, 1}, P@k:0.6666666666666666
y_true = [1, 2, 0, 1]
y_pred = [1, 1, 0, 1]
AP@3 = 0.7222222222222222
-----------
K:1,真实值:{0, 4}, 预测值:{1}, 交集:set(), P@k:0.0
y_true = [0, 4]
y_pred = [1, 4]
AP@1 = 0.0
K:1,真实值:{0, 4}, 预测值:{1}, 交集:set(), P@k:0.0
K:2,真实值:{0, 4}, 预测值:{1, 4}, 交集:{4}, P@k:0.5
y_true = [0, 4]
y_pred = [1, 4]
AP@2 = 0.25
K:1,真实值:{0, 4}, 预测值:{1}, 交集:set(), P@k:0.0
K:2,真实值:{0, 4}, 预测值:{1, 4}, 交集:{4}, P@k:0.5
K:3,真实值:{0, 4}, 预测值:{1, 4}, 交集:{4}, P@k:0.5
y_true = [0, 4]
y_pred = [1, 4]
AP@3 = 0.3333333333333333
-----------
K:1,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
y_true = [3]
y_pred = [2]
AP@1 = 0.0
K:1,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
K:2,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
y_true = [3]
y_pred = [2]
AP@2 = 0.0
K:1,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
K:2,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
K:3,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
y_true = [3]
y_pred = [2]
AP@3 = 0.0
-----------
K:1,真实值:{1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
y_true = [1, 2]
y_pred = [1, 3]
AP@1 = 1.0
K:1,真实值:{1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{1, 2}, 预测值:{1, 3}, 交集:{1}, P@k:0.5
y_true = [1, 2]
y_pred = [1, 3]
AP@2 = 0.75
K:1,真实值:{1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{1, 2}, 预测值:{1, 3}, 交集:{1}, P@k:0.5
K:3,真实值:{1, 2}, 预测值:{1, 3}, 交集:{1}, P@k:0.5
y_true = [1, 2]
y_pred = [1, 3]
AP@3 = 0.6666666666666666
-----------
Mean avg precision at k (MAP@K)
Среднее значение всех значений AP@k по всем обучающим данным называется MAP@k. Это помогает нам точно представлять точность всех данных прогноза. Диапазон значений от 0 до 1.
AP@k измеряет, насколько хороша изученная модель в каждой категории, а MAP@k измеряет, насколько хороша изученная модель во всех категориях. Пример кода выглядит следующим образом:
import numpy as np
def mapk(acutal, pred, k):
#creating a list for storing the Average Precision Values
average_precision = []
#interating through the whole data and calculating the apk for each
for i in range(len(acutal)):
ap = apatk(acutal[i], pred[i], k)
print(f"AP@k: {ap}")
average_precision.append(ap)
#returning the mean of all the data
return np.mean(average_precision)
#defining the values of the actual and the predicted class
y_true = [[1,2,0,1], [0,4], [3], [1,2]]
y_pred = [[1,1,0,1], [1,4], [2], [1,3]]
if __name__ == "__main__":
print(mapk(y_true, y_pred,3))
Результаты приведены ниже:
K:1,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{0, 1, 2}, 预测值:{1}, 交集:{1}, P@k:0.5
K:3,真实值:{0, 1, 2}, 预测值:{0, 1}, 交集:{0, 1}, P@k:0.6666666666666666
AP@k: 0.7222222222222222
K:1,真实值:{0, 4}, 预测值:{1}, 交集:set(), P@k:0.0
K:2,真实值:{0, 4}, 预测值:{1, 4}, 交集:{4}, P@k:0.5
K:3,真实值:{0, 4}, 预测值:{1, 4}, 交集:{4}, P@k:0.5
AP@k: 0.3333333333333333
K:1,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
K:2,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
K:3,真实值:{3}, 预测值:{2}, 交集:set(), P@k:0.0
AP@k: 0.0
K:1,真实值:{1, 2}, 预测值:{1}, 交集:{1}, P@k:1.0
K:2,真实值:{1, 2}, 预测值:{1, 3}, 交集:{1}, P@k:0.5
K:3,真实值:{1, 2}, 预测值:{1, 3}, 交集:{1}, P@k:0.5
AP@k: 0.6666666666666666
0.4305555555555556
Оценка здесь плохая, потому что в наборе прогнозов много ошибок.
Sampled F1 Score
Метрика сначала вычисляет оценку F1 для каждого экземпляра в данных, а затем вычисляет среднее значение оценок F1.
Мы будем использовать реализацию sklearn в коде. Пожалуйста, обратитесь к документации для расчета очков F1.здесь. Диапазон значений от 0 до 1.
Сначала мы преобразуем данные в0-1
формате, а затем рассчитайте для него балл F1.
Пример кода выглядит следующим образом:
from sklearn.metrics import f1_score
from sklearn.preprocessing import MultiLabelBinarizer
def f1_sampled(actual, pred):
# converting the multi-label classification to a binary output
mlb = MultiLabelBinarizer()
actual = mlb.fit_transform(actual)
pred = mlb.fit_transform(pred)
print(f"多标签二值化后的标签值:{mlb.classes_}")
print(f"真实值:\n{actual} \n预测值:\n{pred}")
# fitting the data for calculating the f1 score
f1 = f1_score(actual, pred, average = "samples")
return f1
# defining the values of the actual and the predicted class
# 总共有五个类别
y_true = [[1,2,0,1], [0,4], [3], [1,2]]
y_pred = [[1,1,0,1], [1,4], [2], [1,3]]
if __name__ == "__main__":
print(f1_sampled(y_true, y_pred))
Результаты приведены ниже:
多标签二值化后的标签值:[0 1 2 3 4]
真实值:
[[1 1 1 0 0]
[1 0 0 0 1]
[0 0 0 1 0]
[0 1 1 0 0]]
预测值:
[[1 1 0 0 0]
[0 1 0 0 1]
[0 0 1 0 0]
[0 1 0 1 0]]
0.45
Мы знаем, что оценка F1 находится между 0 и 1, здесь мы получаем оценку 0,45. Это связано с тем, что набор прогнозов не является хорошим. Если бы у нас был лучший набор прогнозов, значение было бы ближе к 1.
Log Loss
Логарифмическая потеря, китайское название - логарифмическая потеря, также известная как логистическая потеря или перекрестная энтропийная потеря.
Эта мера потери ошибок на самом деле является методом, используемым в логистической регрессии для измерения ошибки между прогнозируемой вероятностью и истинной меткой.
Его формула:
в
В формуле x эквивалентно прогнозируемому значению, а y эквивалентно фактическому значению.
Во-первых, вам нужно бинаризировать метки, а затем использовать логарифмическую потерю для каждого столбца. Наконец, вы можете взять среднее значение потерь журнала в каждом столбце, которое также известно как среднее значение потерь журнала столбца. Конечно, вы можете сделать это и другими способами.
Пример кода выглядит следующим образом:
import numpy as np
# y_test = np.random.randint(0,2,(3,4))
# y_pred = np.random.random((3,4))
y_test = np.array([[1, 1, 0, 0], [0, 1, 0, 1]])
y_pred = np.array([[0.2, 0.5, 0, 0], [0.1, 0.5, 0, 0.8]])
print(f"{y_test}\n{y_pred}")
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def log_loss(y_test,y_pred):
y_test = y_test.astype(np.float16)
y_pred = y_pred.astype(np.float16)
N,M = y_test.shape
print(f"列-M:{M}, 行-N:{N}")
a=[]
for m in range(M):
loss=0
for i in range(N):
loss -= (y_test[i,m]*np.log(sigmoid(y_pred[i,m])))+((1.0-y_test[i,m])*np.log(1.0-sigmoid(y_pred[i,m])))
loss = loss/N
a.append(round(loss,8))
print(f"每一列的损失如下:{a}")
return np.mean(a)
a = log_loss(y_test,y_pred)
print(a)
Результаты приведены ниже:
[[1 1 0 0]
[0 1 0 1]]
[[0.2 0.5 0. 0. ]
[0.1 0.5 0. 0.8]]
列-M:4, 行-N:2
每一列的损失如下:[0.67131506, 0.47402386, 0.69314718, 0.53217012]
0.592664055
Приведенный выше код предназначен для нахождения логарифмических потерь для каждого столбца, мы также можем найти логарифмические потери для каждой выборки (строки), и результаты согласуются. Конкретный код выглядит следующим образом:
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def compute_loss_v1(y_true, y_pred):
t_loss = y_true * np.log(sigmoid(y_pred)) + \
(1 - y_true) * np.log(1 - sigmoid(y_pred)) # [batch_size,num_class]
loss = t_loss.mean(axis=-1) # 得到每个样本的损失值
print(f"每个样本的损失如下:{loss}")
return -loss.mean() # 返回整体样本的损失均值(或其他)
if __name__ == '__main__':
y_true = np.array([[1, 1, 0, 0], [0, 1, 0, 1]])
y_pred = np.array([[0.2, 0.5, 0, 0], [0.1, 0.5, 0, 0.8]])
print(compute_loss_v1(y_true, y_pred)) # 0.5926
Результаты приведены ниже:
每个样本的损失如下:[-0.61462755 -0.57068037]
0.5926539631803737
Соответствующий код реализации PyTorch выглядит следующим образом:
import torch
import torch.nn as nn
y_true = torch.tensor([[1, 1, 0, 0], [0, 1, 0, 1]],dtype=torch.int16)
y_pred = torch.tensor([[0.2, 0.5, 0, 0], [0.1, 0.5, 0, 0.8]],dtype=torch.float32)
loss = nn.MultiLabelSoftMarginLoss(reduction='mean')
print(loss(y_pred, y_true)) # 0.5926
Результаты приведены ниже:
tensor(0.5927)
Суммировать
Для сценариев классификации с несколькими метками мы обычно используем MAP@K , Sampled F1 Score или Log Loss, чтобы установить метрики оценки для вашей модели.