Showing posts with label Metrics. Show all posts
Showing posts with label Metrics. Show all posts

Monday, December 2, 2019

Performance Measures for Machine Learning

Summary

In this blog spot I'm presenting a few performance measures for machine learning tasks. These performance measures come up a lot in the marketing domain.

Take at home lessons:
  • the measure you optimize to makes a difference
  • the measure you report makes a difference
  • use measure appropriate for problem/community
  • accuracy often is not sufficient/appropriate
  • only accuracy generalizes to >2 classes
  • this is not an exhaustive list of performance measures

 

Confusion Matrix

First we construct a confusion matrix for a binary classification problem. Given a classification function f(x)->R and a threshold T that can split the outcomes into {0, 1} we can create a confusion matrix that counts the occurrences of the predicted class given the true label.

 

Accuracy

Accuracy is then measured as the percentage of correct responses (True Positives + True Negatives) over the total amount of responses.


 

Problems with Accuracy

This measure is commonly used but it can be misleading. The problems arise from the domain we are modelling. If one of the class for example is poorly represented into the metric is meaningless as we could predict the same class always and still have a good results.

• Assumes equal cost for both kinds of errors 
  • cost(b-type-error) = cost (c-type-error)
• is 99% accuracy good?
  • can be excellent, good, mediocre, poor, terrible
  • depends on problem
• Base Rate = accuracy of predicting predominant class

 

Weighted (Cost sensitive) Accuracy

A modified version of accuracy is "Weighted Accuracy" were we count the cost of misclassification.


In this scenario we aiming for a model and a threshold that can minimize the total cost.

 

Lift

If we are not interested in the accuracy on the entire dataset but want accurate predictions for 5%, 10% or 20% of the dataset then we can use the lift measure.
Lift measures how much better than random prediction on the fraction of the dataset predicted true (f(x) > threshold).



 

Precision / Recall

  • The Precision measure counts how many of the interest class are correct.
  • The Recall measure counts how many of the interest class does the model return.
In the case below the interest class is a(1). 


We can change the sweep over the threshold calculate Precision/Recall multiple times and graph out what is called the Precision/Recall curve.

At each different threshold we can see a different tradeoff between the two metrics.
  • When the threshold is too high then c (everything is predicted as class 0) becomes zero and then the precision becomes zero.
  • When the threshold is too low then b (everything is predicted as class 1) becomes zero and then the recall becomes zero.
Both of these metrics are flawed in isolation and it is the eye of the modeller on which one better represents the problem.

F-Measure

The F-Measure is an attempt to merge the two measures to construct a more meaningful performance measure.


Receiver Operating Characteristic (ROC)

• Developed in WWII to statistically model false positive and false negative detections of radar operators
• Better statistical foundations than most other measures
• Standard measure in medicine and biology
• Becoming more popular in ML 


Although ROC graphs are apparently simple, there are some common misconceptions and pitfalls when using them in practice.

One of the earliest adopters of ROC graphs in machine learning was Spackman (1989), who demonstrated the value of ROC curves in evaluating and comparing algorithms.


ROC graphs are conceptually simple, but there are some non-obvious complexities that arise when they are used in research. 

ROC Plot

 

• Sweep threshold and plot
  • TPR vs. FPR
  • Sensitivity vs. 1-Specificity
  • P(true|true) vs. P(true|false)
• Sensitivity = a/(a+b) = Recall = LIFT numerator
• 1 - Specificity = 1 - d/(c+d)


A ROC graph depicts relative trade-offs between benefits (true positives) and costs (false positives).

  • The lower left point (0,0) represents the strategy of never issuing a positive classiffication. 
  • The opposite strategy is represented by the upper right point (1,1).
  • The point (0,1) represents perfect classiffication.
  • The diagonal line y = x represents the strategy of randomly guessing a class. 
  • A random classifier will produce an ROC point that "slides" back and forth on the diagonal based on the frequency with which it guesses the positive class. In order to get away from this diagonal into the upper triangular region, the classifier must exploit some information in the data. 
  • Any classifier that appears in the lower right triangle performs worse than random guessing. This triangle is therefore usually empty in ROC graphs.
  • ROC curves have an attractive property: they are insensitive to changes in class distribution.
  • Any performance metric that uses values from both columns of theconfusion matrix will be inherently sensitive to class skews. Metrics such as accuracy, precision, lift and F scores use values from both
    columns of the confusion matrix. 
  • ROC graphs are based upon TP rate and FP rate, in which each dimension is a strict columnar ratio, so do not depend on class distributions.

Wednesday, November 16, 2011

Metrics, Distances and all things pretty

Today I post a self contained block of code that defines many metrics and distances often used in optimization, machine learning and computer vision.

I don't usually show my code in public...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;

namespace Emgu_Tracker_Advanced_V2.Source
{
    public class MathExtended
    {
        /// <summary>
        /// Defines the different metrics for distance 
        /// </summary>
        public enum DISTANCE_METRIC { 
            BRAY_CURTIS, CANBERRA, CHESSBOARD, CORRELATION, 
            EUCLIDEAN, NORMALIZED_SQUARED_EUCLIDEAN, COSINE_SIMILARITY,
            MANHATTAN, SQUARED_EUCLIDEAN};

        /// <summary>
        /// Measure the distance of 2 n-th dimensional vectors using any of the given metrics
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <param name="_metric">Distance metric</param>
        /// <returns>The distance between the two points</returns>
        public static float Distance(float[] _vector1, float[] _vector2, DISTANCE_METRIC _metric = DISTANCE_METRIC.EUCLIDEAN)
        {
            float result = -1.0f;
            switch (_metric)
            {
                case DISTANCE_METRIC.BRAY_CURTIS:
                    result = DistanceBrayCurtis(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.CANBERRA:
                    result = DistanceCanberra(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.CHESSBOARD:
                    result = DistanceChessboard(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.EUCLIDEAN:
                    result = DistanceEuclidean(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.NORMALIZED_SQUARED_EUCLIDEAN:
                    result = DistanceNormalizedSquaredEuclidean(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.COSINE_SIMILARITY:
                    result = DistanceCosineSimilarity(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.CORRELATION:
                    result = DistanceCorrelation(_vector1, _vector2);
                    break;
                case DISTANCE_METRIC.MANHATTAN:
                    result = DistanceManhattan(_vector1,_vector2);
                    break;
                case DISTANCE_METRIC.SQUARED_EUCLIDEAN:
                    result = DistanceSquaredEuclidean(_vector1, _vector2);
                    break;
                default:
                    result = -1;
                    break; 
            }
            return result;
        }

        /// <summary>
        /// Euclidean distance between 2 2-dimensional points
        /// </summary>
        /// <param name="_point1">Point 1</param>
        /// <param name="_point2">Point 2</param>
        /// <returns>Euclidean distance</returns>
        public static float Distance(Point _point1, Point _point2)
        {
            float xDistance = _point1.X - _point2.X;
            float yDistance = _point1.Y - _point2.Y;
            return (float)System.Math.Sqrt(xDistance * xDistance + yDistance * yDistance);
        }

        /// <summary>
        /// Euclidean distance between 2 2-dimensional points
        /// </summary>
        /// <param name="_point1">Point 1</param>
        /// <param name="_point2">Point 2</param>
        /// <returns>Euclidean distance</returns>
        public static float Distance(PointF _point1, PointF _point2)
        {
            float xDistance = _point1.X - _point2.X;
            float yDistance = _point1.Y - _point2.Y;
            return (float)System.Math.Sqrt(xDistance * xDistance + yDistance * yDistance);
        }

        /// <summary>
        /// Euclidean length of n-th dimensional vector
        /// </summary>
        /// <param name="_vector">Vector</param>
        /// <returns>Length of vector</returns>
        public static float Length(float[] _vector)
        {
            float sum = 0.0f;
            if (_vector == null) 
                return -1.0f;
            for (int i = 0; i < _vector.Length; i++)
                sum += _vector[i] * _vector[i];
            return (float)Math.Sqrt(sum);
        }

        /// <summary>
        /// Run through a list of points and find the closest one to the input vector
        /// The metric type is  euclidean distance
        /// </summary>
        /// <param name="point">The point to search for</param>
        /// <param name="pointList">The distance metric</param>
        /// <returns>The minimum distance point</returns>
        public static PointF NearestNeighbour(PointF point, PointF[] pointList)
        {
            return pointList.OrderBy(fromPoint => Distance(point, fromPoint)).First();
        }

        /// <summary>
        /// Run through an array of float vectors and find the nearest one 
        /// to the input vector, given the distance metric type
        /// </summary>
        /// <param name="_vector">The vector to search for</param>
        /// <param name="_vectorsArray">The array of vectors to find the minimum distance to</param>
        /// <param name="_metric">The distance metric</param>
        /// <returns>The minimum distance vector</returns>
        public static float[] NearestNeighbour(
            float[] _vector, 
            float[][] _vectorsArray,
            DISTANCE_METRIC _metric = DISTANCE_METRIC.EUCLIDEAN)
        {
            return _vectorsArray.OrderBy(fromVector => Distance(_vector, fromVector, _metric)).First();
        }

        /// <summary>
        /// Run through an array of float vectors and find the nearest k vectors 
        /// to the input vector, given the distance metric type
        /// </summary>
        /// <param name="_vector">The vector to search for</param>
        /// <param name="_vectorsArray">The array of vectors to find the minimum distance to</param>
        /// <param name="_metric">The distance metric</param>
        /// <returns>The nearest neighbours in (distance vector) key pairs</returns>
        public static KeyValuePair<float, float[]>[] KNearestNeighbour(
            float[] _vector,
            float[][] _vectorsArray,
            int k = 1,
            DISTANCE_METRIC _metric = DISTANCE_METRIC.EUCLIDEAN)
        {
            SortedList<float, float[]> sortedDistances = new SortedList<float, float[]>(_vectorsArray.Length);
            for (int i = 0; i < _vectorsArray.Length; i++)
                sortedDistances.Add(Distance(_vector, _vectorsArray[i], _metric), _vectorsArray[i]);
            return sortedDistances.Take(k).ToArray<KeyValuePair<float, float[]>>();
        }

        /// <summary>
        /// Run through an array of float vectors and find the nearest k vectors 
        /// to the input vector, given the distance metric type
        /// </summary>
        /// <param name="_vector">The vector to search for</param>
        /// <param name="_vectorsArray">The array of vectors to find the minimum distance to</param>
        /// <param name="_metric">The distance metric</param>
        /// <returns>The nearest neighbours in (distance index) key pairs</returns>
        public static KeyValuePair<float, int>[] KNearestNeighboursIndex(
            float[] _vector,
            float[][] _vectorsArray,
            int k = 1,
            DISTANCE_METRIC _metric = DISTANCE_METRIC.EUCLIDEAN)
        {
            SortedList<float, int> sortedDistances = new SortedList<float, int>(_vectorsArray.Length);
            for (int i = 0; i < _vectorsArray.Length; i++)
                sortedDistances.Add(Distance(_vector, _vectorsArray[i], _metric), i);
            return sortedDistances.Take(k).ToArray<KeyValuePair<float,int>>();
        }

        /// <summary>
        /// Sum of absolute differences of two n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Sum of absolute differences else if error -1</returns>
        public static float DistanceManhattan(float[] _vector1, float[] _vector2)
        {
            float sum = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
                sum += Math.Abs(_vector1[i] - _vector2[i]);
            return sum;
        }

        /// <summary>
        /// Sum of squared differences of two n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Sum of squared differences else if error -1</returns>
        public static float DistanceSquaredEuclidean(float[] _vector1, float[] _vector2)
        {
            float sum = 0.0f, tmp = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
            {
                tmp = (_vector1[i] - _vector2[i]);
                sum += tmp * tmp;
            }
            return sum;
        }

        /// <summary>
        /// Cosine similarity is a measure of similarity between 
        /// two n-th dimensional vectors vectors by measuring the cosine of the angle between them
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Cosine similarity else if error -1</returns>
        public static float DistanceCosineSimilarity(float[] _vector1, float[] _vector2)
        {
            float sum = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
                sum += (_vector1[i] * _vector2[i]);
            return sum / (MathExtended.Length(_vector1) * MathExtended.Length(_vector2));
        }

        /// <summary>
        /// Euclidean distance between 2 n-dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Euclidean distance</returns>
        public static float DistanceEuclidean(float[] _vector1, float[] _vector2)
        {
            float tmp, sum = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
            {
                tmp = _vector1[i] - _vector2[i];
                sum += tmp * tmp;
            }
            return (float)Math.Sqrt(sum);
        }

        /// <summary>
        /// Normalized squared Euclidean distance between 2 n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Normalized squared Euclidean distance else if error -1</returns>
        public static float DistanceNormalizedSquaredEuclidean(float[] _vector1, float[] _vector2)
        {
            float sum1 = 0.0f, sum2 = 0.0f, sumTotal1 = 0.0f, sumTotal2 = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
            {
                sum1 += _vector1[i];
                sum2 += _vector2[i];
            }
            sum1 /= _vector1.Length;
            sum2 /= _vector1.Length;
            for (int i = 0; i < _vector1.Length; i++)
            {
                sumTotal1 += (float)Math.Pow(_vector1[i] - sum1 - _vector2[i] + sum2, 2);
                sumTotal2 += (float)Math.Pow(_vector1[i] - sum1, 2) + (float)Math.Pow(_vector2[i] - sum2, 2);
            }
            return sumTotal1 / (2.0f * sumTotal2);
        }

        /// <summary>
        /// Gives the correlation distance between 2 n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Correlation distance else if error -1</returns>
        public static float DistanceCorrelation(float[] _vector1, float[] _vector2)
        {
            float sum1 = 0.0f, sum2 = 0.0f, sumTotal1 = 0.0f, sumTotal2 = 0.0f, sumTotal3 = 0.0f, tmp1 = 0.0f, tmp2 = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
            {
                sum1 += _vector1[i];
                sum2 += _vector2[i];
            }
            sum1 /= _vector1.Length;
            sum2 /= _vector1.Length;
            for (int i = 0; i < _vector1.Length; i++)
            {
                tmp1 = (_vector1[i] - sum1);
                tmp2 = (_vector2[i] - sum2);
                sumTotal1 += tmp1 * tmp2;
                sumTotal2 += tmp1 * tmp1;
                sumTotal3 += tmp2 * tmp2;
            }
            sumTotal2 = (float)Math.Sqrt(sumTotal2);
            sumTotal3 = (float)Math.Sqrt(sumTotal3);
            return 1.0f - sumTotal1 / (sumTotal2 * sumTotal3);
        }

        /// <summary>
        /// Gives the canberra distance between 2 n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Canberra distance else if error -1</returns>
        public static float DistanceCanberra(float[] _vector1, float[] _vector2)
        {
            float sum = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
                sum += (float)Math.Abs(_vector1[i] - _vector2[i]) / 
                    (float)(Math.Abs(_vector1[i]) + Math.Abs(_vector2[i]));
            return sum;
        }

        /// <summary>
        /// Gives the BrayCurtis distance between 2 n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>BrayCurtis distance else if error -1</returns>
        public static float DistanceBrayCurtis(float[] _vector1, float[] _vector2)
        {
            float sum1 = 0.0f, sum2 = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
            {
                sum1 += (float)Math.Abs(_vector1[i] - _vector2[i]);
                sum2 += (float)Math.Abs(_vector1[i] + _vector2[i]);
            }
            return sum1 / sum2;
        }

        /// <summary>
        /// Gives the Chessboard distance between 2 n-th dimensional vectors
        /// </summary>
        /// <param name="_vector1">Vector 1</param>
        /// <param name="_vector2">Vector 2</param>
        /// <returns>Chessboard distance else if error -1</returns>
        public static float DistanceChessboard(float[] _vector1, float[] _vector2)
        {
            float tmp = 0.0f;
            if (_vector1 == null || _vector2 == null)
                return -1.0f;
            if (_vector1.Length != _vector2.Length)
                return -1.0f;
            for (int i = 0; i < _vector1.Length; i++)
                tmp = (float)(Math.Max(Math.Abs(_vector1[i]-_vector2[2]), tmp));
            return tmp;
        }
    }
}