/// <summary>
        ///   Attempts to retrieve the value of the kernel function
        ///   from the diagonal of the kernel matrix. If the value
        ///   is not available, it is immediatelly computed and inserted
        ///   in the cache.
        /// </summary>
        ///
        /// <param name="i">Index of the point to compute.</param>
        ///
        /// <remarks>The result of the kernel function k(p[i], p[i]).</remarks>
        ///
        public double GetOrCompute(int i)
        {
            if (!Enabled)
            {
                return(kernel.Function(inputs[i], inputs[i]));
            }

            hits++; // Diagonal elements are always available

            return(diagonal[i]);
        }
Example #2
0
        /// <summary>
        ///   Constructs a new <see cref="KernelFunctionCache"/>.
        /// </summary>
        ///
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The inputs values.</param>
        /// <param name="cacheSize">
        ///   The size for the cache, measured in number of
        ///   elements from the <paramref name="inputs"/> set.
        ///   Default is to use all elements.</param>
        ///
        public KernelFunctionCache(IKernel kernel, double[][] inputs, int cacheSize)
        {
            if (cacheSize < 0)
            {
                throw new ArgumentOutOfRangeException("cacheSize",
                                                      "The cache size must be non-negative.");
            }

            this.kernel = kernel;
            this.inputs = inputs;

            this.size = cacheSize;
            if (cacheSize > inputs.Length)
            {
                this.size = inputs.Length;
            }


            if (cacheSize > inputs.Length)
            {
                // Create whole cache.
                matrix = new double[inputs.Length][];
                for (int i = 0; i < inputs.Length; i++)
                {
                    double[] row = matrix[i] = new double[inputs.Length - 1];
                    for (int j = 0; j < row.Length - 1; j++)
                    {
                        matrix[i][j] = kernel.Function(inputs[i], inputs[j]);
                    }
                }
            }

            else if (cacheSize > 0)
            {
                this.capacity = (size * (size - 1)) / 2;
                int collectionCapacity = (int)(1.1f * capacity);

                // Create lookup tables
                this.lruIndices            = new LinkedList <int>();
                this.lruIndicesLookupTable =
                    new Dictionary <int, LinkedListNode <int> >(collectionCapacity);

                // Create cache for off-diagonal elements
                this.data = new Dictionary <int, double>(collectionCapacity);
            }

            // Create cache for diagonal elements
            this.diagonal = new double[inputs.Length];
            for (int i = 0; i < inputs.Length; i++)
            {
                this.diagonal[i] = kernel.Function(inputs[i], inputs[i]);
            }
        }
Example #3
0
        /// <summary>
        ///   Computes the given input to produce the corresponding output.
        /// </summary>
        ///
        /// <remarks>
        ///   For a binary decision problem, the decision for the negative
        ///   or positive class is typically computed by taking the sign of
        ///   the machine's output.
        /// </remarks>
        ///
        /// <param name="inputs">An input vector.</param>
        /// <param name="output">The output of the machine. If this is a
        ///   <see cref="SupportVectorMachine.IsProbabilistic">probabilistic
        ///   </see> machine, the output is the probability of the positive
        ///   class. If this is a standard machine, the output is the distance
        ///   to the decision hyperplane in feature space.</param>
        ///
        /// <returns>The decision label for the given input.</returns>
        ///
        public override int Compute(double[] inputs, out double output)
        {
            output = Threshold;

            if (IsCompact)
            {
                for (int i = 0; i < Weights.Length; i++)
                {
                    output += Weights[i] * inputs[i];
                }
            }
            else
            {
                for (int i = 0; i < SupportVectors.Length; i++)
                {
                    output += Weights[i] * kernel.Function(SupportVectors[i], inputs);
                }
            }

            if (IsProbabilistic)
            {
                output = Link.Inverse(output);
                return(output >= 0.5 ? +1 : -1);
            }

            return(output >= 0 ? +1 : -1);
        }
Example #4
0
        /// <summary>
        ///   Computes the given input to produce the corresponding output.
        /// </summary>
        ///
        /// <remarks>
        ///   For a binary decision problem, the decision for the negative
        ///   or positive class is typically computed by taking the sign of
        ///   the machine's output.
        /// </remarks>
        ///
        /// <param name="inputs">An input vector.</param>
        /// <returns>The output for the given input.</returns>
        ///
        public override double Compute(double[] inputs)
        {
            double s = Threshold;

            for (int i = 0; i < SupportVectors.Length; i++)
            {
                s += Weights[i] * kernel.Function(SupportVectors[i], inputs);
            }

            return(s);
        }
        private double compute(double[] point)
        {
            double sum = machine.Threshold;

            for (int i = 0; i < alpha.Length; i++)
            {
                sum += alpha[i] * kernel.Function(supportVectors[i], point);
            }

            return(sum);
        }
Example #6
0
        /// <summary>
        ///   Estimates the <see cref="Complexity">complexity parameter C</see>
        ///   for a given kernel and a given data set.
        /// </summary>
        ///
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The input samples.</param>
        ///
        /// <returns>A suitable value for C.</returns>
        ///
        public static double EstimateComplexity(IKernel kernel, double[][] inputs)
        {
            // Compute initial value for C as the number of examples
            // divided by the trace of the input sample kernel matrix.

            double sum = 0.0;

            for (int i = 0; i < inputs.Length; i++)
            {
                sum += kernel.Function(inputs[i], inputs[i]);
            }
            return(inputs.Length / sum);
        }
        /// <summary>
        ///   Estimates the <see cref="Complexity">complexity parameter C</see>
        ///   for a given kernel and an unbalanced data set by summing every element
        ///   on the diagonal of the kernel matrix and using an heuristic based on it.
        /// </summary>
        ///
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The input samples.</param>
        /// <param name="outputs">The output samples.</param>
        ///
        /// <returns>A suitable value for positive C and negative C, respectively.</returns>
        ///
        public static Tuple <double, double> EstimateComplexity(IKernel kernel, double[][] inputs, int[] outputs)
        {
            // Compute initial value for C as the number of examples
            // divided by the trace of the input sample kernel matrix.

            double negativeSum = 0.0;
            double positiveSum = 0.0;

            int negativeCount = 0;
            int positiveCount = 0;

            for (int i = 0; i < inputs.Length; i++)
            {
                if (outputs[i] == -1)
                {
                    negativeSum += kernel.Function(inputs[i], inputs[i]);
                    negativeCount++;
                }
                else // outputs[i] == +1
                {
                    positiveSum += kernel.Function(inputs[i], inputs[i]);
                    positiveCount++;
                }

                if (Double.IsNaN(positiveSum) || Double.IsNaN(negativeSum))
                {
                    throw new OverflowException();
                }
            }

            return(Tuple.Create
                   (
                       positiveCount / positiveSum,
                       negativeCount / negativeSum
                   ));
        }
Example #8
0
        /// <summary>
        ///   Runs the LS-SVM algorithm.
        /// </summary>
        ///
        /// <param name="computeError">
        ///   True to compute error after the training
        ///   process completes, false otherwise. Default is true.
        /// </param>
        ///
        /// <returns>
        ///   The misclassification error rate of
        ///   the resulting support vector machine.
        /// </returns>
        ///
        public double Run(bool computeError)
        {
            // Create kernel function cache
            cache    = new KernelFunctionCache(kernel, inputs);
            diagonal = new double[inputs.Length];
            for (int i = 0; i < diagonal.Length; i++)
            {
                diagonal[i] = kernel.Function(inputs[i], inputs[i]) + gamma;
            }


            // 1. Solve to find nu and eta
            double[] eta = conjugateGradient(outputs);
            double[] nu  = conjugateGradient(ones);


            // 2. Compute  s = Y' eta
            double s = 0;

            for (int i = 0; i < outputs.Length; i++)
            {
                s += outputs[i] * eta[i];
            }


            // 3. Find solution
            double b = 0;

            for (int i = 0; i < eta.Length; i++)
            {
                b += eta[i];
            }
            b /= s;

            double[] alpha = new double[nu.Length];
            for (int i = 0; i < alpha.Length; i++)
            {
                alpha[i] = (nu[i] - eta[i] * b) * outputs[i];
            }

            machine.SupportVectors = inputs;
            machine.Weights        = alpha;
            machine.Threshold      = b;

            // Compute error if required.
            return((computeError) ? ComputeError(inputs, outputs) : 0.0);
        }
        /// <summary>
        ///   Computes the SVM output for a given point.
        /// </summary>
        ///
        private double compute(double[] point)
        {
            double sum = -(b_lower + b_upper) / 2;

            if (isLinear)
            {
                for (int i = 0; i < weights.Length; i++)
                {
                    sum += weights[i] * point[i];
                }
            }
            else
            {
                foreach (int i in activeExamples)
                {
                    sum += alpha[i] * outputs[i] * kernel.Function(inputs[i], point);
                }
            }

            return(sum);
        }
        /// <summary>
        ///   Constructs a new <see cref="KernelFunctionCache"/>.
        /// </summary>
        ///
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The inputs values.</param>
        /// <param name="cacheSize">The size for the cache.</param>
        ///
        public KernelFunctionCache(IKernel kernel, double[][] inputs, int cacheSize)
        {
            this.kernel = kernel;
            this.inputs = inputs;
            this.size   = inputs.Length;

            if (cacheSize < 0)
            {
                Enabled = false;
                return;
            }
            else
            {
                Enabled = true;
            }

            if (cacheSize == 0)
            {
                cacheSize = inputs.Length;
            }


            this.cacheSize = cacheSize;
            this.capacity  = (size * size) / 2 + 1;

            // Create lookup tables
            this.lruIndices            = new LinkedList <int>();
            this.lruIndicesLookupTable = new Dictionary <int, LinkedListNode <int> >(capacity);

            // Create cache for off-diagonal elements
            this.data = new Dictionary <int, double>(capacity);

            // Create cache for diagonal elements
            this.diagonal = new double[inputs.Length];
            for (int i = 0; i < inputs.Length; i++)
            {
                this.diagonal[i] = kernel.Function(inputs[i], inputs[i]);
            }
        }
        /// <summary>
        ///   Estimates the <see cref="Complexity">complexity parameter C</see>
        ///   for a given kernel and a given data set.
        /// </summary>
        /// 
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The input samples.</param>
        /// 
        /// <returns>A suitable value for C.</returns>
        /// 
        public static double EstimateComplexity(IKernel kernel, double[][] inputs)
        {
            // Compute initial value for C as the number of examples
            // divided by the trace of the input sample kernel matrix.

            double sum = 0.0;
            for (int i = 0; i < inputs.Length; i++)
            {
                sum += kernel.Function(inputs[i], inputs[i]);

                if (Double.IsNaN(sum))
                    throw new OverflowException();
            }
            return inputs.Length / sum;
        }
Example #12
0
        /// <summary>
        ///   Constructs a new <see cref="KernelFunctionCache"/>.
        /// </summary>
        /// 
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The inputs values.</param>
        /// <param name="cacheSize">
        ///   The size for the cache, measured in number of 
        ///   elements from the <paramref name="inputs"/> set.
        ///   Default is to use all elements.</param>
        /// 
        public KernelFunctionCache(IKernel kernel, double[][] inputs, int cacheSize)
        {
            if (cacheSize < 0)
                throw new ArgumentOutOfRangeException("cacheSize",
                    "The cache size must be non-negative.");

            this.kernel = kernel;
            this.inputs = inputs;

            this.size = cacheSize;
            if (cacheSize > inputs.Length)
                this.size = inputs.Length;


            if (cacheSize > inputs.Length)
            {
                // Create whole cache.
                matrix = new double[inputs.Length][];
                for (int i = 0; i < inputs.Length; i++)
                {
                    double[] row = matrix[i] = new double[inputs.Length - 1];
                    for (int j = 0; j < row.Length - 1; j++)
                        matrix[i][j] = kernel.Function(inputs[i], inputs[j]);
                }
            }

            else if (cacheSize > 0)
            {
                this.capacity = (size * (size - 1)) / 2;
                int collectionCapacity = (int)(1.1f * capacity);

                // Create lookup tables
                this.lruIndices = new LinkedList<int>();
                this.lruIndicesLookupTable =
                    new Dictionary<int, LinkedListNode<int>>(collectionCapacity);

                // Create cache for off-diagonal elements
                this.data = new Dictionary<int, double>(collectionCapacity);
            }

            // Create cache for diagonal elements
            this.diagonal = new double[inputs.Length];
            for (int i = 0; i < inputs.Length; i++)
                this.diagonal[i] = kernel.Function(inputs[i], inputs[i]);
        }
Example #13
0
 /// <summary>
 ///   Computes the kernel distance for a kernel function even if it doesn't
 ///   implement the <see cref="Accord.Math.Distances.IDistance"/> interface. Can be used to check
 ///   the proper implementation of the distance function.
 /// </summary>
 ///
 /// <param name="kernel">The kernel function whose distance needs to be evaluated.</param>
 /// <param name="x">An input point <c>x</c> given in input space.</param>
 /// <param name="y">An input point <c>y</c> given in input space.</param>
 ///
 /// <returns>
 ///   The distance between <paramref name="x"/> and <paramref name="y"/> in kernel (feature) space.
 /// </returns>
 ///
 public static double Distance(this IKernel kernel, double[] x, double[] y)
 {
     return(kernel.Function(x, x) + kernel.Function(y, y) - 2 * kernel.Function(x, y));
 }
Example #14
0
        /// <summary>
        ///   Constructs a new <see cref="KernelFunctionCache"/>.
        /// </summary>
        /// 
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The inputs values.</param>
        /// <param name="cacheSize">The size for the cache.</param>
        /// 
        public KernelFunctionCache(IKernel kernel, double[][] inputs, int cacheSize)
        {
            this.kernel = kernel;
            this.inputs = inputs;
            this.size = inputs.Length;

            if (cacheSize < 0)
            {
                Enabled = false;
                return;
            }
            else
            {
                Enabled = true;
            }

            if (cacheSize == 0)
                cacheSize = inputs.Length;


                this.cacheSize = cacheSize;
                this.capacity = (size * size) / 2 + 1;

                // Create lookup tables
                this.lruIndices = new LinkedList<int>();
                this.lruIndicesLookupTable = new Dictionary<int, LinkedListNode<int>>(capacity);

                // Create cache for off-diagonal elements
                this.data = new Dictionary<int, double>(capacity);

                // Create cache for diagonal elements
                this.diagonal = new double[inputs.Length];
                for (int i = 0; i < inputs.Length; i++)
                    this.diagonal[i] = kernel.Function(inputs[i], inputs[i]);
        }
Example #15
0
        /// <summary>
        ///   Analytically solves the optimization problem for two Lagrange multipliers.
        /// </summary>
        ///
        private bool takeStep(int i1, int i2)
        {
            if (i1 == i2)
            {
                return(false);
            }

            // Lagrange multipliers
            double alpha1a = alpha_a[i1];
            double alpha1b = alpha_b[i1];
            double alpha2a = alpha_a[i2];
            double alpha2b = alpha_b[i2];

            // Errors
            double e1    = errors[i1];
            double e2    = errors[i2];
            double delta = e1 - e2;

            // Kernel evaluation
            double k11   = kernel.Function(inputs[i1], inputs[i1]);
            double k12   = kernel.Function(inputs[i1], inputs[i2]);
            double k22   = kernel.Function(inputs[i2], inputs[i2]);
            double eta   = k11 + k22 - 2.0 * k12;
            double gamma = alpha1a - alpha1b + alpha2a - alpha2b;

            // Assume the kernel is positive definite.
            if (eta < 0)
            {
                eta = 0;
            }



            #region Optimize
            bool case1 = false;
            bool case2 = false;
            bool case3 = false;
            bool case4 = false;

            bool changed  = false;
            bool finished = false;

            double L, H, a1, a2;

            while (!finished)
            {
                if (!case1 &&
                    (alpha1a > 0 || (alpha1b == 0 && delta > 0)) &&
                    (alpha2a > 0 || (alpha2b == 0 && delta < 0)))
                {
                    // Compute L and H (w.r.t. alpha1, alpha2)
                    L = Math.Max(0, gamma - this.c);
                    H = Math.Min(this.c, gamma);

                    if (L < H)
                    {
                        if (eta > 0)
                        {
                            a2 = alpha2a - (delta / eta);

                            if (a2 > H)
                            {
                                a2 = H;
                            }
                            else if (a2 < L)
                            {
                                a2 = L;
                            }
                        }
                        else
                        {
                            double Lobj = -L * delta;
                            double Hobj = -H * delta;

                            if (Lobj > Hobj)
                            {
                                a2 = L;
                            }
                            else
                            {
                                a2 = H;
                            }
                        }

                        a1 = alpha1a - (a2 - alpha2a);

                        // Update alpha1, alpha2 if change is larger than some epsilon
                        if (Math.Abs(a1 - alpha1a) > roundingEpsilon ||
                            Math.Abs(a2 - alpha2a) > roundingEpsilon)
                        {
                            alpha1a = a1;
                            alpha2a = a2;
                            changed = true;
                        }
                    }
                    else
                    {
                        finished = true;
                    }

                    case1 = true;
                }

                else if (!case2 &&
                         (alpha1a > 0 || (alpha1b == 0 && delta > 2 * epsilon)) &&
                         (alpha2b > 0 || (alpha2a == 0 && delta > 2 * epsilon)))
                {
                    // Compute L and H  (w.r.t. alpha1, alpha2*)
                    L = Math.Max(0, -gamma);
                    H = Math.Min(this.c, -gamma + this.c);

                    if (L < H)
                    {
                        if (eta > 0)
                        {
                            a2 = alpha2b + ((delta - 2 * epsilon) / eta);

                            if (a2 > H)
                            {
                                a2 = H;
                            }
                            else if (a2 < L)
                            {
                                a2 = L;
                            }
                        }
                        else
                        {
                            double Lobj = L * (-2 * epsilon + delta);
                            double Hobj = H * (-2 * epsilon + delta);

                            if (Lobj > Hobj)
                            {
                                a2 = L;
                            }
                            else
                            {
                                a2 = H;
                            }
                        }
                        a1 = alpha1a + (a2 - alpha2b);

                        // Update alpha1, alpha2* if change is larger than some epsilon
                        if (Math.Abs(a1 - alpha1a) > roundingEpsilon ||
                            Math.Abs(a2 - alpha2b) > roundingEpsilon)
                        {
                            alpha1a = a1;
                            alpha2b = a2;
                            changed = true;
                        }
                    }
                    else
                    {
                        finished = true;
                    }

                    case2 = true;
                }

                else if (!case3 &&
                         (alpha1b > 0 || (alpha1a == 0 && delta < -2 * epsilon)) &&
                         (alpha2a > 0 || (alpha2b == 0 && delta < -2 * epsilon)))
                {
                    // Compute L and H (w.r.t. alpha1*, alpha2)
                    L = Math.Max(0, gamma);
                    H = Math.Min(this.c, this.c + gamma);

                    if (L < H)
                    {
                        if (eta > 0)
                        {
                            a2 = alpha2a - ((delta + 2 * epsilon) / eta);

                            if (a2 > H)
                            {
                                a2 = H;
                            }
                            else if (a2 < L)
                            {
                                a2 = L;
                            }
                        }
                        else
                        {
                            double Lobj = -L * (2 * epsilon + delta);
                            double Hobj = -H * (2 * epsilon + delta);

                            if (Lobj > Hobj)
                            {
                                a2 = L;
                            }
                            else
                            {
                                a2 = H;
                            }
                        }
                        a1 = alpha1b + (a2 - alpha2a);

                        // Update alpha1*, alpha2 if change is larger than some epsilon
                        if (Math.Abs(a1 - alpha1b) > roundingEpsilon ||
                            Math.Abs(a2 - alpha2a) > roundingEpsilon)
                        {
                            alpha1b = a1;
                            alpha2a = a2;
                            changed = true;
                        }
                    }
                    else
                    {
                        finished = true;
                    }

                    case3 = true;
                }

                else if (!case4 &&
                         (alpha1b > 0 || (alpha1a == 0 && delta < 0)) &&
                         (alpha2b > 0 || (alpha2a == 0 && delta > 0)))
                {
                    // Compute L and H (w.r.t. alpha1*, alpha2*)
                    L = Math.Max(0, -gamma - this.c);
                    H = Math.Min(this.c, -gamma);

                    if (L < H)
                    {
                        if (eta > 0)
                        {
                            a2 = alpha2b + delta / eta;

                            if (a2 > H)
                            {
                                a2 = H;
                            }
                            else if (a2 < L)
                            {
                                a2 = L;
                            }
                        }
                        else
                        {
                            double Lobj = L * delta;
                            double Hobj = H * delta;

                            if (Lobj > Hobj)
                            {
                                a2 = L;
                            }
                            else
                            {
                                a2 = H;
                            }
                        }

                        a1 = alpha1b - (a2 - alpha2b);

                        // Update alpha1*, alpha2* if change is larger than some epsilon
                        if (Math.Abs(a1 - alpha1b) > roundingEpsilon ||
                            Math.Abs(a2 - alpha2b) > roundingEpsilon)
                        {
                            alpha1b = a1;
                            alpha2b = a2;
                            changed = true;
                        }
                    }
                    else
                    {
                        finished = true;
                    }

                    case4 = true;
                }

                else
                {
                    finished = true;
                }

                // Update the delta
                delta += eta * ((alpha2a - alpha2b) - (alpha_a[i2] - alpha_b[i2]));
            }


            // If nothing has changed, return false.
            if (!changed)
            {
                return(false);
            }
            #endregion



            #region Update error cache
            // Update error cache using new Lagrange multipliers
            foreach (int i in I0)
            {
                if (i != i1 && i != i2)
                {
                    // Update all in set i0 except i1 and i2 (because we have the kernel function cached for them)
                    errors[i] += ((alpha_a[i1] - alpha_b[i1]) - (alpha1a - alpha1b)) * kernel.Function(inputs[i1], inputs[i])
                                 + ((alpha_a[i2] - alpha_b[i2]) - (alpha2a - alpha2b)) * kernel.Function(inputs[i2], inputs[i]);
                }
            }

            // Update error cache using new Lagrange multipliers for i1 and i2
            errors[i1] += ((alpha_a[i1] - alpha_b[i1]) - (alpha1a - alpha1b)) * k11
                          + ((alpha_a[i2] - alpha_b[i2]) - (alpha2a - alpha2b)) * k12;
            errors[i2] += ((alpha_a[i1] - alpha_b[i1]) - (alpha1a - alpha1b)) * k12
                          + ((alpha_a[i2] - alpha_b[i2]) - (alpha2a - alpha2b)) * k22;
            #endregion

            // to prevent precision problems
            double m_Del = 1e-10;
            if (alpha1a > c - m_Del * c)
            {
                alpha1a = c;
            }
            else if (alpha1a <= m_Del * c)
            {
                alpha1a = 0;
            }
            if (alpha1b > c - m_Del * c)
            {
                alpha1b = c;
            }
            else if (alpha1b <= m_Del * c)
            {
                alpha1b = 0;
            }
            if (alpha2a > c - m_Del * c)
            {
                alpha2a = c;
            }
            else if (alpha2a <= m_Del * c)
            {
                alpha2a = 0;
            }
            if (alpha2b > c - m_Del * c)
            {
                alpha2b = c;
            }
            else if (alpha2b <= m_Del * c)
            {
                alpha2b = 0;
            }


            #region Store the new Lagrange multipliers
            // Store the changes in the alpha, alpha* arrays
            alpha_a[i1] = alpha1a;
            alpha_b[i1] = alpha1b;
            alpha_a[i2] = alpha2a;
            alpha_b[i2] = alpha2b;
            #endregion


            #region Update the sets of indices
            // Update the sets of indices (for i1)
            if ((0 < alpha1a && alpha1a < this.c) || (0 < alpha1b && alpha1b < this.c))
            {
                I0.Add(i1);
            }
            else
            {
                I0.Remove(i1);
            }

            if (alpha1a == 0 && alpha1b == 0)
            {
                I1.Add(i1);
            }
            else
            {
                I1.Remove(i1);
            }

            if (alpha1a == 0 && alpha1b == this.c)
            {
                I2.Add(i1);
            }
            else
            {
                I2.Remove(i1);
            }

            if (alpha1a == this.c && alpha1b == 0)
            {
                I3.Add(i1);
            }
            else
            {
                I3.Remove(i1);
            }

            // Update the sets of indices (for i2)
            if ((0 < alpha2a && alpha2a < this.c) || (0 < alpha2b && alpha2b < this.c))
            {
                I0.Add(i2);
            }
            else
            {
                I0.Remove(i2);
            }

            if (alpha2a == 0 && alpha2b == 0)
            {
                I1.Add(i2);
            }
            else
            {
                I1.Remove(i2);
            }

            if (alpha2a == 0 && alpha2b == this.c)
            {
                I2.Add(i2);
            }
            else
            {
                I2.Remove(i2);
            }

            if (alpha2a == this.c && alpha2b == 0)
            {
                I3.Add(i2);
            }
            else
            {
                I3.Remove(i2);
            }
            #endregion


            #region Compute the new thresholds
            biasLower      = Double.MinValue;
            biasUpper      = Double.MaxValue;
            biasLowerIndex = -1;
            biasUpperIndex = -1;

            foreach (int i in I0)
            {
                if (0 < alpha_b[i] && alpha_b[i] < this.c &&
                    errors[i] + epsilon > biasLower)
                {
                    biasLower      = errors[i] + epsilon;
                    biasLowerIndex = i;
                }
                else if (0 < alpha_a[i] && alpha_a[i] < this.c &&
                         errors[i] - epsilon > biasLower)
                {
                    biasLower      = errors[i] - epsilon;
                    biasLowerIndex = i;
                }
                if (0 < alpha_a[i] && alpha_a[i] < this.c &&
                    errors[i] - epsilon < biasUpper)
                {
                    biasUpper      = errors[i] - epsilon;
                    biasUpperIndex = i;
                }
                else if (0 < alpha_b[i] && alpha_b[i] < this.c &&
                         errors[i] + epsilon < biasUpper)
                {
                    biasUpper      = errors[i] + epsilon;
                    biasUpperIndex = i;
                }
            }

            if (!I0.Contains(i1))
            {
                if (I2.Contains(i1) && errors[i1] + epsilon > biasLower)
                {
                    biasLower      = errors[i1] + epsilon;
                    biasLowerIndex = i1;
                }
                else if (I1.Contains(i1) && errors[i1] - epsilon > biasLower)
                {
                    biasLower      = errors[i1] - epsilon;
                    biasLowerIndex = i1;
                }

                if (I3.Contains(i1) && errors[i1] - epsilon < biasUpper)
                {
                    biasUpper      = errors[i1] - epsilon;
                    biasUpperIndex = i1;
                }
                else if (I1.Contains(i1) && errors[i1] + epsilon < biasUpper)
                {
                    biasUpper      = errors[i1] + epsilon;
                    biasUpperIndex = i1;
                }
            }

            if (!I0.Contains(i2))
            {
                if (I2.Contains(i2) && errors[i2] + epsilon > biasLower)
                {
                    biasLower      = errors[i2] + epsilon;
                    biasLowerIndex = i2;
                }
                else if (I1.Contains(i2) && errors[i2] - epsilon > biasLower)
                {
                    biasLower      = errors[i2] - epsilon;
                    biasLowerIndex = i2;
                }

                if (I3.Contains(i2) && errors[i2] - epsilon < biasUpper)
                {
                    biasUpper      = errors[i2] - epsilon;
                    biasUpperIndex = i2;
                }
                else if (I1.Contains(i2) && errors[i2] + epsilon < biasUpper)
                {
                    biasUpper      = errors[i2] + epsilon;
                    biasUpperIndex = i2;
                }
            }

            if (biasLowerIndex == -1 || biasUpperIndex == -1)
            {
                throw new Exception();
            }

            #endregion


            // Success.
            return(true);
        }
        //---------------------------------------------


        #region Public Methods
        /// <summary>
        ///   Computes the Multi-Class Kernel Discriminant Analysis algorithm.
        /// </summary>
        ///
        public override void Compute()
        {
            // Get some initial information
            int dimension = Source.GetLength(0);

            double[,] source = Source;
            double total = dimension;


            // Create the Gram (Kernel) Matrix
            double[,] K = new double[dimension, dimension];
            for (int i = 0; i < dimension; i++)
            {
                double[] row = source.GetRow(i);
                for (int j = i; j < dimension; j++)
                {
                    double s = kernel.Function(row, source.GetRow(j));
                    K[i, j] = s; // Assume K will be symmetric
                    K[j, i] = s;
                }
            }


            // Compute entire data set measures
            base.Means = Statistics.Tools.Mean(K);
            base.StandardDeviations = Statistics.Tools.StandardDeviation(K, Means);


            // Initialize the kernel analogous scatter matrices
            double[,] Sb = new double[dimension, dimension];
            double[,] Sw = new double[dimension, dimension];


            // For each class
            for (int c = 0; c < Classes.Count; c++)
            {
                // Get the Kernel matrix class subset
                double[,] Kc = K.Submatrix(Classes[c].Indices);
                int count = Kc.GetLength(0);

                // Get the Kernel matrix class mean
                double[] mean = Statistics.Tools.Mean(Kc);


                // Construct the Kernel equivalent of the Within-Class Scatter matrix
                double[,] Swi = Statistics.Tools.Scatter(Kc, mean, (double)count);

                // Sw = Sw + Swi
                for (int i = 0; i < dimension; i++)
                {
                    for (int j = 0; j < dimension; j++)
                    {
                        Sw[i, j] += Swi[i, j];
                    }
                }


                // Construct the Kernel equivalent of the Between-Class Scatter matrix
                double[] d = mean.Subtract(base.Means);
                double[,] Sbi = Matrix.OuterProduct(d, d).Multiply(total);

                // Sb = Sb + Sbi
                for (int i = 0; i < dimension; i++)
                {
                    for (int j = 0; j < dimension; j++)
                    {
                        Sb[i, j] += Sbi[i, j];
                    }
                }


                // Store additional information
                base.ClassScatter[c]            = Swi;
                base.ClassCount[c]              = count;
                base.ClassMeans[c]              = mean;
                base.ClassStandardDeviations[c] = Statistics.Tools.StandardDeviation(Kc, mean);
            }


            // Add regularization to avoid singularity
            for (int i = 0; i < dimension; i++)
            {
                Sw[i, i] += regularization;
            }


            // Compute the generalized eigenvalue decomposition
            GeneralizedEigenvalueDecomposition gevd = new GeneralizedEigenvalueDecomposition(Sb, Sw);

            if (gevd.IsSingular) // check validity of the results
            {
                throw new SingularMatrixException("One of the matrices is singular. Please retry " +
                                                  "the method with a higher regularization constant.");
            }


            // Get the eigenvalues and corresponding eigenvectors
            double[] evals = gevd.RealEigenvalues;
            double[,] eigs = gevd.Eigenvectors;

            // Sort eigenvalues and vectors in descending order
            eigs = Matrix.Sort <double, double>(evals, eigs, new GeneralComparer(ComparerDirection.Descending, true));


            if (threshold > 0)
            {
                // We will be discarding less important
                // eigenvectors to conserve memory.

                // Calculate component proportions
                double sum = 0.0; // total variance
                for (int i = 0; i < dimension; i++)
                {
                    sum += Math.Abs(evals[i]);
                }

                if (sum > 0)
                {
                    int keep = 0;

                    // Now we will detect how many components we have
                    //  have to keep in order to achieve the level of
                    //  explained variance specified by the threshold.

                    while (keep < dimension)
                    {
                        // Get the variance explained by the component
                        double explainedVariance = Math.Abs(evals[keep]);

                        // Check its proportion
                        double proportion = explainedVariance / sum;

                        // Now, if the component explains an
                        // enough proportion of the variance,
                        if (proportion > threshold)
                        {
                            keep++; // We can keep it.
                        }
                        else
                        {
                            break;  // Otherwise we can stop, since the
                        }
                        // components are ordered by variance.
                    }

                    if (keep > 0)
                    {
                        // Resize the vectors keeping only needed components
                        eigs  = eigs.Submatrix(0, dimension - 1, 0, keep - 1);
                        evals = evals.Submatrix(0, keep - 1);
                    }
                    else
                    {
                        // No component will be kept.
                        eigs  = new double[dimension, 0];
                        evals = new double[0];
                    }
                }
            }


            // Store information
            base.Eigenvalues         = evals;
            base.DiscriminantMatrix  = eigs;
            base.ScatterBetweenClass = Sb;
            base.ScatterWithinClass  = Sw;


            // Project into the kernel discriminant space
            this.Result = K.Multiply(eigs);


            // Compute feature space means for later classification
            for (int c = 0; c < Classes.Count; c++)
            {
                ProjectionMeans[c] = ClassMeans[c].Multiply(eigs);
            }


            // Computes additional information about the analysis and creates the
            //  object-oriented structure to hold the discriminants found.
            CreateDiscriminants();
        }
Example #17
0
        /// <summary>
        ///   Analytically solves the optimization problem for two Lagrange multipliers.
        /// </summary>
        private bool takeStep(int i1, int i2)
        {
            if (i1 == i2)
            {
                return(false);
            }

            double[] p1 = inputs[i1];  // Input point at index i1
            double   a1 = alpha[i1];   // Classification label for p1
            double   y1 = outputs[i1]; // Lagrange multiplier for p1

            // SVM output on p1 - y1. Check if it has already been computed
            double e1 = (a1 > 0 && a1 < c) ? errors[i1] : compute(p1) - y1;

            double[] p2 = inputs[i2];  // Input point at index i2
            double   a2 = alpha[i2];   // Classification label for p2
            double   y2 = outputs[i2]; // Lagrange multiplier for p2

            // SVM output on p2 - y2. Check if it has already been computed
            double e2 = (a2 > 0 && a2 < c) ? errors[i2] : compute(p2) - y2;


            double s = y1 * y2;


            // Compute L and H GABIZ.Baseing to equations (13) and (14) (Platt, 1998)
            double L, H;

            if (y1 != y2)
            {
                double d = a2 - a1;
                L = System.Math.Max(0, d);
                H = System.Math.Min(c, c + d);
            }
            else
            {
                double d = a2 + a1;
                L = System.Math.Max(0, d - c);
                H = System.Math.Min(c, d);
            }

            if (L == H)
            {
                return(false);
            }

            double k11, k22, k12, eta;

            k11 = kernel.Function(p1, p1);
            k12 = kernel.Function(p1, p2);
            k22 = kernel.Function(p2, p2);
            eta = k11 + k22 - 2.0 * k12;

            // Compute new values for the lagrange multipliers a1 and a2
            double new_a1, new_a2;

            if (eta > 0)
            {
                new_a2 = a2 - y2 * (e2 - e1) / eta;

                if (new_a2 < L)
                {
                    new_a2 = L;
                }
                else if (new_a2 > H)
                {
                    new_a2 = H;
                }
            }
            else
            {
                double c1   = eta / 2;
                double c2   = y2 * (e1 - e2) + eta * a2;
                double Lobj = c1 * L * L + c2 * L;
                double Hobj = c1 * H * H + c2 * H;

                if (Lobj > Hobj + epsilon)
                {
                    new_a2 = L;
                }
                else if (Lobj < Hobj - epsilon)
                {
                    new_a2 = H;
                }
                else
                {
                    new_a2 = a2;
                }
            }

            if (System.Math.Abs(new_a2 - a2) < epsilon * (new_a2 + a2 + epsilon))
            {
                return(false);
            }

            new_a1 = a1 + s * (a2 - new_a2);

            if (new_a1 < 0)
            {
                new_a2 += s * new_a1;
                new_a1  = 0;
            }
            else if (new_a1 > c)
            {
                double d = new_a1 - c;
                new_a2 += s * d;
                new_a1  = c;
            }


            // Update threshold (bias) to reflect change in Lagrange multipliers
            double b1 = 0, b2 = 0;
            double new_b = 0, delta_b;

            if (new_a1 > 0 && new_a1 < c)
            {
                // a1 is not at bounds
                new_b = e1 + y1 * (new_a1 - a1) * k11 + y2 * (new_a2 - a2) * k12 + bias;
            }
            else
            {
                if (new_a2 > 0 && new_a2 < c)
                {
                    // a1 is at bounds but a2 is not.
                    new_b = e2 + y1 * (new_a1 - a1) * k12 + y2 * (new_a2 - a2) * k22 + bias;
                }
                else
                {
                    // Both new Lagrange multipliers are at bound. SMO algorithm
                    // chooses the threshold to be halfway in between b1 and b2.
                    b1    = e1 + y1 * (new_a1 - a1) * k11 + y2 * (new_a2 - a2) * k12 + bias;
                    b2    = e2 + y1 * (new_a1 - a1) * k12 + y2 * (new_a2 - a2) * k22 + bias;
                    new_b = (b1 + b2) / 2;
                }
            }

            delta_b = new_b - bias;
            bias    = new_b;


            // Update error cache using new Lagrange multipliers
            double t1 = y1 * (new_a1 - a1);
            double t2 = y2 * (new_a2 - a2);

            for (int i = 0; i < inputs.Length; i++)
            {
                if (0 < alpha[i] && alpha[i] < c)
                {
                    double[] point = inputs[i];
                    errors[i] +=
                        t1 * kernel.Function(p1, point) +
                        t2 * kernel.Function(p2, point) -
                        delta_b;
                }
            }

            errors[i1] = 0f;
            errors[i2] = 0f;


            // Update lagrange multipliers
            alpha[i1] = new_a1;
            alpha[i2] = new_a2;


            return(true);
        }
Example #18
0
        /// <summary>
        ///   Attempts to retrieve the kernel function evaluated between point at index i
        ///   and j. If it is not cached, it will be computed and the cache will be updated.
        /// </summary>
        ///
        /// <param name="i">The index of the first point <c>p</c> to compute.</param>
        /// <param name="j">The index of the second point <c>p</c> to compute.</param>
        ///
        /// <remarks>The result of the kernel function k(p[i], p[j]).</remarks>
        ///
        public double GetOrCompute(int i, int j)
        {
            if (data == null)
            {
                return(kernel.Function(inputs[i], inputs[j]));
            }

            if (i == j)
            {
                return(diagonal[i]);
            }

            // Keys should always be given as in
            // the order (i, j) with i > j, so we
            // always have (higher{i}, lower{j})

            if (j > i)
            {
                int t = i;
                i = j;
                j = t;
            }

            if (matrix != null)
            {
                return(matrix[i][j]);
            }

            int key = (i * (i - 1)) / 2 + j;

            double value;

            // Check if the data is in the cache
            if (!data.TryGetValue(key, out value))
            {
                // It is not. Compute the function and update
                value = kernel.Function(inputs[i], inputs[j]);

                // Save evaluation
                data[key] = value;

                // If we are over capacity,
                if (data.Count > capacity)
                {
                    // The first entry must be removed to leave
                    // room for the previously computed value

                    var first        = lruIndices.First;
                    int discardedKey = first.Value;

                    // Remove the cached value for
                    // the least recently used key

                    data.Remove(discardedKey);
                    lruIndicesLookupTable.Remove(discardedKey);

                    // Avoid allocating memory by reusing the
                    // previously first node to hold the new
                    // data value and re-insert it at the end

                    lruIndices.RemoveFirst();
                    first.Value = key;
                    lruIndices.AddLast(first);

                    // Update the index lookup table
                    lruIndicesLookupTable[key] = first;
                }
                else
                {
                    // Register the use of the variable in the LRU list
                    lruIndicesLookupTable[key] = lruIndices.AddLast(key);
                }

                misses++;
            }
            else
            {
                // It is. Update the LRU list to
                // indicate the item has been used.

                var node = lruIndicesLookupTable[key];

                // Remove from middle and add to the end
                lruIndices.Remove(node);
                lruIndices.AddLast(node);

                // Update the lookup table
                lruIndicesLookupTable[key] = node;

                hits++;
            }

            return(value);
        }
Example #19
0
 double Q(int i, int j)
 {
     return(kernel.Function(inputs[i], inputs[j]));
 }
        /// <summary>
        ///   Estimates the <see cref="Complexity">complexity parameter C</see>
        ///   for a given kernel and an unbalanced data set.
        /// </summary>
        /// 
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The input samples.</param>
        /// <param name="outputs">The output samples.</param>
        /// 
        /// <returns>A suitable value for positive C and negative C, respectively.</returns>
        /// 
        public static Tuple<double, double> EstimateComplexity(IKernel kernel, double[][] inputs, int[] outputs)
        {
            // Compute initial value for C as the number of examples
            // divided by the trace of the input sample kernel matrix.

            double negativeSum = 0.0;
            double positiveSum = 0.0;

            int negativeCount = 0;
            int positiveCount = 0;

            for (int i = 0; i < inputs.Length; i++)
            {
                if (outputs[i] == -1)
                {
                    negativeSum += kernel.Function(inputs[i], inputs[i]);
                    negativeCount++;
                }
                else // outputs[i] == +1
                {
                    positiveSum += kernel.Function(inputs[i], inputs[i]);
                    positiveCount++;
                }

                if (Double.IsNaN(positiveSum) || Double.IsNaN(negativeSum))
                    throw new OverflowException();
            }

            return Tuple.Create
            (
                positiveCount / positiveSum,
                negativeCount / negativeSum
            );
        }
        /// <summary>
        ///   Computes the Kernel Principal Component Analysis algorithm.
        /// </summary>
        ///
        public void Compute(int components)
        {
            if (components < 0 || components > Source.GetLength(0))
            {
                throw new ArgumentException(
                          "The number of components must be between 0 and " +
                          "the number of rows in your source data matrix.");
            }

            int dimension = Source.GetLength(0);

            // If needed, center the source matrix
            sourceCentered = Adjust(Source, Overwrite);


            // Create the Gram (Kernel) Matrix
            this.kernelMatrix = new double[dimension, dimension];
            for (int i = 0; i < dimension; i++)
            {
                double[] row = sourceCentered.GetRow(i);
                for (int j = i; j < dimension; j++)
                {
                    double k = kernel.Function(row, sourceCentered.GetRow(j));
                    kernelMatrix[i, j] = k; // Kernel matrix is symmetric
                    kernelMatrix[j, i] = k;
                }
            }


            // Center the Gram (Kernel) Matrix if requested
            double[,] Kc = centerFeatureSpace ? centerKernel(kernelMatrix) : kernelMatrix;


            // Perform the Eigenvalue Decomposition (EVD) of the Kernel matrix
            EigenvalueDecomposition evd = new EigenvalueDecomposition(Kc, assumeSymmetric: true);

            // Gets the Eigenvalues and corresponding Eigenvectors
            double[] evals = evd.RealEigenvalues;
            double[,] eigs = evd.Eigenvectors;


            // Sort eigenvalues and vectors in descending order
            eigs = Matrix.Sort(evals, eigs, new GeneralComparer(ComparerDirection.Descending, true));


            // Eliminate unwanted components
            if (components != Source.GetLength(0))
            {
                eigs  = eigs.Submatrix(null, 0, components - 1);
                evals = evals.Submatrix(0, components - 1);
            }

            if (threshold > 0)
            {
                // We will be discarding less important
                // eigenvectors to conserve memory.

                // Calculate component proportions
                double sum = 0.0; // total variance
                for (int i = 0; i < evals.Length; i++)
                {
                    sum += Math.Abs(evals[i]);
                }

                if (sum > 0)
                {
                    int keep = 0;

                    // Now we will detect how many components we have
                    //  have to keep in order to achieve the level of
                    //  explained variance specified by the threshold.

                    while (keep < components)
                    {
                        // Get the variance explained by the component
                        double explainedVariance = Math.Abs(evals[keep]);

                        // Check its proportion
                        double proportion = explainedVariance / sum;

                        // Now, if the component explains an
                        // enough proportion of the variance,
                        if (proportion > threshold)
                        {
                            keep++; // We can keep it.
                        }
                        else
                        {
                            break;  // Otherwise we can stop, since the
                        }
                        // components are ordered by variance.
                    }

                    if (keep != components)
                    {
                        if (keep > 0)
                        {
                            // Resize the vectors keeping only needed components
                            eigs  = eigs.Submatrix(0, components - 1, 0, keep - 1);
                            evals = evals.Submatrix(0, keep - 1);
                        }
                        else
                        {
                            // No component will be kept.
                            eigs  = new double[dimension, 0];
                            evals = new double[0];
                        }
                    }
                }
            }


            // Normalize eigenvectors
            if (centerFeatureSpace)
            {
                for (int j = 0; j < evals.Length; j++)
                {
                    double val = Math.Sqrt(Math.Abs(evals[j]));
                    for (int i = 0; i < eigs.GetLength(0); i++)
                    {
                        eigs[i, j] = eigs[i, j] / val;
                    }
                }
            }



            // Set analysis properties
            this.SingularValues  = new double[evals.Length];
            this.Eigenvalues     = evals;
            this.ComponentMatrix = eigs;


            // Project the original data into principal component space
            this.Result = Kc.Multiply(eigs);


            // Computes additional information about the analysis and creates the
            //  object-oriented structure to hold the principal components found.
            CreateComponents();
        }
Example #22
0
        //---------------------------------------------


        #region Public Methods
        /// <summary>
        ///   Computes the Multi-Class Kernel Discriminant Analysis algorithm.
        /// </summary>
        public override void Compute()
        {
            // Get some initial information
            int dimension = Source.GetLength(0);

            double[,] source = Source;
            double total = dimension;


            // Create the Gram (Kernel) Matrix
            double[,] K = new double[dimension, dimension];
            for (int i = 0; i < dimension; i++)
            {
                for (int j = i; j < dimension; j++)
                {
                    double s = kernel.Function(source.GetRow(i), source.GetRow(j));
                    K[i, j] = s; // Assume K will be symmetric
                    K[j, i] = s;
                }
            }


            // Compute entire data set measures
            base.Means = GABIZ.Base.Statistics.Tools.Mean(K);
            base.StandardDeviations = GABIZ.Base.Statistics.Tools.StandardDeviation(K, Means);


            // Initialize the kernel analogous scatter matrices
            double[,] Sb = new double[dimension, dimension];
            double[,] Sw = new double[dimension, dimension];


            // For each class
            for (int c = 0; c < Classes.Count; c++)
            {
                // Get the Kernel matrix class subset
                double[,] Kc = K.Submatrix(Classes[c].Indexes);
                int count = Kc.GetLength(0);

                // Get the Kernel matrix class mean
                double[] mean = GABIZ.Base.Statistics.Tools.Mean(Kc);


                // Construct the Kernel equivalent of the Within-Class Scatter matrix
                double[,] Swi = GABIZ.Base.Statistics.Tools.Scatter(Kc, mean, (double)count);

                // Sw = Sw + Swi
                for (int i = 0; i < dimension; i++)
                {
                    for (int j = 0; j < dimension; j++)
                    {
                        Sw[i, j] += Swi[i, j];
                    }
                }


                // Construct the Kernel equivalent of the Between-Class Scatter matrix
                double[] d = mean.Subtract(base.Means);
                double[,] Sbi = Matrix.OuterProduct(d, d).Multiply(total);

                // Sb = Sb + Sbi
                for (int i = 0; i < dimension; i++)
                {
                    for (int j = 0; j < dimension; j++)
                    {
                        Sb[i, j] += Sbi[i, j];
                    }
                }


                // Store additional information
                base.ClassScatter[c]            = Swi;
                base.ClassCount[c]              = count;
                base.ClassMeans[c]              = mean;
                base.ClassStandardDeviations[c] = GABIZ.Base.Statistics.Tools.StandardDeviation(Kc, mean);
            }


            // Add regularization to avoid singularity
            for (int i = 0; i < dimension; i++)
            {
                Sw[i, i] += regularization;
            }


            // Compute the generalized eigenvalue decomposition
            GeneralizedEigenvalueDecomposition gevd = new GeneralizedEigenvalueDecomposition(Sb, Sw);

            if (gevd.IsSingular) // check validity of the results
            {
                throw new SingularMatrixException("One of the matrices is singular. Please retry " +
                                                  "the method with a higher regularization constant.");
            }


            // Get the eigenvalues and corresponding eigenvectors
            double[] evals = gevd.RealEigenvalues;
            double[,] eigs = gevd.Eigenvectors;

            // Sort eigenvalues and vectors in descending order
            eigs = Matrix.Sort(evals, eigs, new GeneralComparer(ComparerDirection.Descending, true));


            if (threshold > 0)
            {
                // Calculate proportions earlier
                double sum = 0.0;
                for (int i = 0; i < dimension; i++)
                {
                    sum += System.Math.Abs(evals[i]);
                }

                if (sum > 0)
                {
                    sum = 1.0 / sum;

                    // Discard less important eigenvectors to conserve memory
                    int keep = 0; while (keep < dimension &&
                                         System.Math.Abs(evals[keep]) * sum > threshold)
                    {
                        keep++;
                    }
                    eigs  = eigs.Submatrix(0, dimension - 1, 0, keep - 1);
                    evals = evals.Submatrix(0, keep - 1);
                }
            }


            // Store information
            base.Eigenvalues         = evals;
            base.DiscriminantMatrix  = eigs;
            base.ScatterBetweenClass = Sb;
            base.ScatterWithinClass  = Sw;


            // Project into the kernel discriminant space
            this.Result = K.Multiply(eigs);


            // Compute feature space means for later classification
            for (int c = 0; c < Classes.Count; c++)
            {
                double[] mean = new double[eigs.GetLength(1)];
                for (int i = 0; i < eigs.GetLength(0); i++)
                {
                    for (int j = 0; j < eigs.GetLength(1); j++)
                    {
                        mean[j] += ClassMeans[c][i] * eigs[i, j];
                    }
                }
                kernelClassMeans[c] = mean;
            }


            // Computes additional information about the analysis and creates the
            //  object-oriented structure to hold the discriminants found.
            CreateDiscriminants();
        }
Example #23
0
        /// <summary>
        ///   Analytically solves the optimization problem for two Lagrange multipliers.
        /// </summary>
        ///
        private bool takeStep(int i1, int i2)
        {
            if (i1 == i2)
            {
                return(false);
            }

            double[] p1    = inputs[i1];  // Input point at index i1
            double   alph1 = alpha[i1];   // Lagrange multiplier for p1
            double   y1    = outputs[i1]; // Classification label for p1

            // SVM output on p1 - y1. Check if it has already been computed
            double e1 = (alph1 > 0 && alph1 < c) ? errors[i1] : compute(p1) - y1;

            double[] p2    = inputs[i2];  // Input point at index i2
            double   alph2 = alpha[i2];   // Lagrange multiplier for p2
            double   y2    = outputs[i2]; // Classification label for p2

            // SVM output on p2 - y2. Check if it has already been computed
            double e2 = (alph2 > 0 && alph2 < c) ? errors[i2] : compute(p2) - y2;


            double s = y1 * y2;


            // Compute L and H according to equations (13) and (14) (Platt, 1998)
            double L, H;

            if (y1 != y2)
            {
                // If the target y1 does not equal the target           (13)
                // y2, then the following bounds apply to a2:
                L = Math.Max(0, alph2 - alph1);
                H = Math.Min(c, c + alph2 - alph1);
            }
            else
            {
                // If the target y1 does equal the target               (14)
                // y2, then the following bounds apply to a2:
                L = Math.Max(0, alph2 + alph1 - c);
                H = Math.Min(c, alph2 + alph1);
            }

            if (L == H)
            {
                return(false);
            }

            double k11, k22, k12, eta;

            k11 = kernel.Function(p1, p1);
            k12 = kernel.Function(p1, p2);
            k22 = kernel.Function(p2, p2);
            eta = k11 + k22 - 2.0 * k12;

            double a1, a2;

            if (eta > 0)
            {
                a2 = alph2 - y2 * (e2 - e1) / eta;

                if (a2 < L)
                {
                    a2 = L;
                }
                else if (a2 > H)
                {
                    a2 = H;
                }
            }
            else
            {
                // Compute objective function Lobj and Hobj at
                //  a2=L and a2=H respectively, using (eq. 19)

                double L1   = alph1 + s * (alph2 - L);
                double H1   = alph1 + s * (alph2 - H);
                double f1   = y1 * (e1 + bias) - alph1 * k11 - s * alph2 * k12;
                double f2   = y2 * (e2 + bias) - alph2 * k22 - s * alph1 * k12;
                double Lobj = -0.5 * L1 * L1 * k11 - 0.5 * L * L * k22 - s * L * L1 * k12 - L1 * f1 - L * f2;
                double Hobj = -0.5 * H1 * H1 * k11 - 0.5 * H * H * k22 - s * H * H1 * k12 - H1 * f1 - H * f2;

                if (Lobj > Hobj + epsilon)
                {
                    a2 = L;
                }
                else if (Lobj < Hobj - epsilon)
                {
                    a2 = H;
                }
                else
                {
                    a2 = alph2;
                }
            }

            if (Math.Abs(a2 - alph2) < epsilon * (a2 + alph2 + epsilon))
            {
                return(false);
            }

            a1 = alph1 + s * (alph2 - a2);

            if (a1 < 0)
            {
                a2 += s * a1;
                a1  = 0;
            }
            else if (a1 > c)
            {
                double d = a1 - c;
                a2 += s * d;
                a1  = c;
            }


            // Update threshold (bias) to reflect change in Lagrange multipliers
            double b1 = 0, b2 = 0;
            double new_b = 0, delta_b;

            if (a1 > 0 && a1 < c)
            {
                // a1 is not at bounds
                new_b = e1 + y1 * (a1 - alph1) * k11 + y2 * (a2 - alph2) * k12 + bias;
            }
            else
            {
                if (a2 > 0 && a2 < c)
                {
                    // a1 is at bounds but a2 is not.
                    new_b = e2 + y1 * (a1 - alph1) * k12 + y2 * (a2 - alph2) * k22 + bias;
                }
                else
                {
                    // Both new Lagrange multipliers are at bound. SMO algorithm
                    // chooses the threshold to be halfway in between b1 and b2.
                    b1    = e1 + y1 * (a1 - alph1) * k11 + y2 * (a2 - alph2) * k12 + bias;
                    b2    = e2 + y1 * (a1 - alph1) * k12 + y2 * (a2 - alph2) * k22 + bias;
                    new_b = (b1 + b2) / 2;
                }
            }

            delta_b = new_b - bias;
            bias    = new_b;


            // Update error cache using new Lagrange multipliers
            double t1 = y1 * (a1 - alph1);
            double t2 = y2 * (a2 - alph2);

            for (int i = 0; i < inputs.Length; i++)
            {
                if (0 < alpha[i] && alpha[i] < c)
                {
                    double[] point = inputs[i];
                    errors[i] +=
                        t1 * kernel.Function(p1, point) +
                        t2 * kernel.Function(p2, point) -
                        delta_b;
                }
            }

            errors[i1] = 0f;
            errors[i2] = 0f;


            // Update lagrange multipliers
            alpha[i1] = a1;
            alpha[i2] = a2;


            return(true);
        }
Example #24
0
        //---------------------------------------------


        #region Public Methods
        /// <summary>Computes the Kernel Principal Component Analysis algorithm.</summary>
        public override void Compute()
        {
            int rows = Source.GetLength(0);

            // Center (adjust) the source matrix
            sourceCentered = Adjust(Source, Overwrite);


            // Create the Gram (Kernel) Matrix
            double[,] K = new double[rows, rows];
            for (int i = 0; i < rows; i++)
            {
                for (int j = i; j < rows; j++)
                {
                    double k = kernel.Function(sourceCentered.GetRow(i), sourceCentered.GetRow(j));
                    K[i, j] = k; // Kernel matrix is symmetric
                    K[j, i] = k;
                }
            }

            // Center the Gram (Kernel) Matrix
            if (centerFeatureSpace)
            {
                K = centerKernel(K);
            }


            // Perform the Eigenvalue Decomposition (EVD) of the Kernel matrix
            EigenvalueDecomposition evd = new EigenvalueDecomposition(K, true);

            // Gets the eigenvalues and corresponding eigenvectors
            double[] evals = evd.RealEigenvalues;
            double[,] eigs = evd.Eigenvectors;

            // Sort eigenvalues and vectors in descending order
            eigs = Matrix.Sort(evals, eigs, new GeneralComparer(ComparerDirection.Descending, true));


            if (threshold > 0)
            {
                // Calculate proportions in advance
                double sum = 0.0;
                for (int i = 0; i < evals.Length; i++)
                {
                    sum += System.Math.Abs(evals[i]);
                }

                if (sum > 0)
                {
                    sum = 1.0 / sum;

                    // Discard less important eigenvectors to conserve memory
                    int keep = 0; while (keep < evals.Length &&
                                         System.Math.Abs(evals[keep]) * sum > threshold)
                    {
                        keep++;
                    }
                    eigs  = eigs.Submatrix(0, evals.Length - 1, 0, keep - 1);
                    evals = evals.Submatrix(0, keep - 1);
                }
            }


            // Normalize eigenvectors
            if (centerFeatureSpace)
            {
                for (int j = 0; j < evals.Length; j++)
                {
                    double eig = System.Math.Sqrt(System.Math.Abs(evals[j]));
                    for (int i = 0; i < eigs.GetLength(0); i++)
                    {
                        eigs[i, j] = eigs[i, j] / eig;
                    }
                }
            }


            // Set analysis properties
            this.SingularValues  = new double[evals.Length];
            this.Eigenvalues     = evals;
            this.ComponentMatrix = eigs;


            // Project the original data into principal component space
            this.Result = K.Multiply(eigs);


            // Computes additional information about the analysis and creates the
            //  object-oriented structure to hold the principal components found.
            CreateComponents();
        }
        /// <summary>
        ///   Estimates the <see cref="Complexity">complexity parameter C</see>
        ///   for a given kernel and a given data set.
        /// </summary>
        /// 
        /// <param name="kernel">The kernel function.</param>
        /// <param name="inputs">The input samples.</param>
        /// 
        /// <returns>A suitable value for C.</returns>
        /// 
        public static double EstimateComplexity(IKernel kernel, double[][] inputs)
        {
            // Compute initial value for C as the number of examples
            // divided by the trace of the input sample kernel matrix.

            double sum = 0.0;
            for (int i = 0; i < inputs.Length; i++)
                sum += kernel.Function(inputs[i], inputs[i]);
            return inputs.Length / sum;
        }
Example #26
0
        /// <summary>
        /// Аналитически решается задача оптимизации двух множителей Лагранжа
        /// </summary>
        private bool TakeStep(int i1, int i2)
        {
            if (i1 == i2)
            {
                return(false);
            }

            double[] p1    = inputs[i1];
            double   alph1 = alpha[i1];
            double   y1    = outputs[i1];

            double e1 = (alph1 > 0 && alph1 < c) ? errors[i1] : Compute(p1) - y1;

            double[] p2    = inputs[i2];
            double   alph2 = alpha[i2];
            double   y2    = outputs[i2];

            double e2 = (alph2 > 0 && alph2 < c) ? errors[i2] : Compute(p2) - y2;


            double s = y1 * y2;


            double L, H;

            if (y1 != y2)
            {
                L = Math.Max(0, alph2 - alph1);
                H = Math.Min(c, c + alph2 - alph1);
            }
            else
            {
                L = Math.Max(0, alph2 + alph1 - c);
                H = Math.Min(c, alph2 + alph1);
            }

            if (L == H)
            {
                return(false);
            }

            double k11, k22, k12, eta;

            k11 = kernel.Function(p1, p1);
            k12 = kernel.Function(p1, p2);
            k22 = kernel.Function(p2, p2);
            eta = k11 + k22 - 2.0 * k12;

            double a1, a2;

            if (eta > 0)
            {
                a2 = alph2 - y2 * (e2 - e1) / eta;

                if (a2 < L)
                {
                    a2 = L;
                }
                else if (a2 > H)
                {
                    a2 = H;
                }
            }
            else
            {
                double L1   = alph1 + s * (alph2 - L);
                double H1   = alph1 + s * (alph2 - H);
                double f1   = y1 * (e1 + bias) - alph1 * k11 - s * alph2 * k12;
                double f2   = y2 * (e2 + bias) - alph2 * k22 - s * alph1 * k12;
                double Lobj = -0.5 * L1 * L1 * k11 - 0.5 * L * L * k22 - s * L * L1 * k12 - L1 * f1 - L * f2;
                double Hobj = -0.5 * H1 * H1 * k11 - 0.5 * H * H * k22 - s * H * H1 * k12 - H1 * f1 - H * f2;

                if (Lobj > Hobj + epsilon)
                {
                    a2 = L;
                }
                else if (Lobj < Hobj - epsilon)
                {
                    a2 = H;
                }
                else
                {
                    a2 = alph2;
                }
            }

            if (Math.Abs(a2 - alph2) < epsilon * (a2 + alph2 + epsilon))
            {
                return(false);
            }

            a1 = alph1 + s * (alph2 - a2);

            if (a1 < 0)
            {
                a2 += s * a1;
                a1  = 0;
            }
            else if (a1 > c)
            {
                double d = a1 - c;
                a2 += s * d;
                a1  = c;
            }


            double b1 = 0, b2 = 0;
            double new_b = 0, delta_b;

            if (a1 > 0 && a1 < c)
            {
                new_b = e1 + y1 * (a1 - alph1) * k11 + y2 * (a2 - alph2) * k12 + bias;
            }
            else
            {
                if (a2 > 0 && a2 < c)
                {
                    new_b = e2 + y1 * (a1 - alph1) * k12 + y2 * (a2 - alph2) * k22 + bias;
                }
                else
                {
                    b1    = e1 + y1 * (a1 - alph1) * k11 + y2 * (a2 - alph2) * k12 + bias;
                    b2    = e2 + y1 * (a1 - alph1) * k12 + y2 * (a2 - alph2) * k22 + bias;
                    new_b = (b1 + b2) / 2;
                }
            }

            delta_b = new_b - bias;
            bias    = new_b;


            double t1 = y1 * (a1 - alph1);
            double t2 = y2 * (a2 - alph2);

            for (int i = 0; i < inputs.Length; i++)
            {
                if (0 < alpha[i] && alpha[i] < c)
                {
                    double[] point = inputs[i];
                    errors[i] +=
                        t1 * kernel.Function(p1, point) +
                        t2 * kernel.Function(p2, point) -
                        delta_b;
                }
            }

            errors[i1] = 0f;
            errors[i2] = 0f;


            alpha[i1] = a1;
            alpha[i2] = a2;


            return(true);
        }