Хорошо, я решил внедрить взвешенную RMSE. напрямую не учитывает отношения упорядочения рангов, но система взвешивания автоматически выделяет эти записи в верхней части списка.
Только для обзора (для тех, кто не знаком с RMSE), уравнение выглядит следующим образом, предполагая два разных классификатора A и B, результаты которых содержатся в массиве с одинаковым именем:
Среднеквадратичное уравнение http://benjismith.net/images/rmse.png
В Java реализация выглядит следующим образом:
double[] A = getAFromSomewhere();
double[] B = getBFromSomewhere();
// Assumes that A and B have the same length. If not, your classifier is broken.
int count = A.length;
double sumSquaredError = 0;
for (int i = 0; i < count; i++) {
double aElement = A[i];
double bElement = B[i];
double error = aElement - bElement;
double squaredError = error * error;
sumSquaredError += squaredError;
}
double meanSquaredError = sumSquaredError / count;
double rootMeanSquaredError = Math.sqrt(meanSquaredError);
Это отправная точка для моей модифицированной реализации. Мне нужно было придумать систему взвешивания, которая учитывает объединенную величину двух значений (из обоих классификаторов). Поэтому я умножу каждое значение квадрата ошибки на SQRT(Ai^2 + Bi^2)
, которое является простой евклидовой функцией расстояния.
Конечно, поскольку я использую взвешенную ошибку в числителе, мне нужно также использовать сумму всех весовых коэффициентов в знаменателе, чтобы мои результаты перенормировались обратно в диапазон (0,0, 1,0).
Я называю новую метрику "RMWSE", так как это среднее значение Взвешенное Квадратная ошибка. Вот как выглядит новое уравнение:
Уравнение RMWSE http://benjismith.net/images/rmwse.png
А вот как это выглядит в Java:
double[] A = getAFromSomewhere();
double[] B = getBFromSomewhere();
// Assumes that A and B have the same length. If not, your classifier is broken.
int count = A.length;
double sumWeightedSquaredError = 0;
double sumWeights = 0;
for (int i = 0; i < count; i++) {
double aElement = A[i];
double bElement = B[i];
double error = aElement - bElement;
double squaredError = error * error;
double weight = Math.sqrt((aElement * aElement) + (bElement * bElement));
double weightedSquaredError = weight * squaredError;
sumWeightedSquaredError += weightedSquaredError;
sumWeights += weight;
}
double meanWeightedSquaredError = sumWeightedSquaredError / sumWeights;
double rootMeanWeightedSquaredError = Math.sqrt(meanWeightedSquaredError);
Чтобы дать вам представление о том, как этот вес работает на практике, скажем, два моих классификатора выдают значения 0.95
и 0.85
для некоторой категории. Ошибка между этими двумя значениями составляет 0.10
, но вес равен 1.2748
(к которому я пришел, используя SQRT(0.95^2 + 0.85^2)
). Взвешенная ошибка 0.12748
.
Аналогичным образом, если классификаторы выдают 0.45
и 0.35
для какой-либо другой категории, ошибка по-прежнему составляет всего 0.10
, но вес составляет только 0.5701
, и поэтому взвешенная ошибка составляет всего 0.05701
.
Таким образом, любая категория с высокими значениями из обоих классификаторов будет иметь больший вес, чем категории с высоким значением только из одного классификатора или категории с низкими значениями из обоих классификаторов.
Это работает лучше всего, когда мои значения классификации перенормированы, так что максимальные значения в A и B равны 1,0, а все остальные значения пропорционально увеличены. Следовательно, измерения больше не составляют до 1,0 для любого данного классификатора, но это не имеет никакого значения, поскольку я не использовал это свойство для чего-либо полезного.
Кстати, я очень доволен результатами, которые это дает в моем наборе данных, но если у кого-то есть другие идеи по улучшению, я буду полностью открыт для предложений!