public void KernelFunctionCacheConstructorTest() { IKernel kernel = new Linear(1); int cacheSize = 0; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(0, target.Size); Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Misses); for (int i = 0; i < inputs.Length; i++) { double expected = i * i + 1; double actual = target.GetOrCompute(i); Assert.AreEqual(expected, actual); } Assert.AreEqual(0, target.Hits); for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = i * j + 1; double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Usage); }
//--------------------------------------------- /// <summary> /// Runs the SMO 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) { // The SMO algorithm chooses to solve the smallest possible optimization problem // at every step. At every step, SMO chooses two Lagrange multipliers to jointly // optimize, finds the optimal values for these multipliers, and updates the SVM // to reflect the new optimal values. // // Reference: http://research.microsoft.com/en-us/um/people/jplatt/smoTR.pdf // The algorithm has been updated to implement the improvements suggested // by Keerthi et al. The code has been based on the pseudo-code available // on the author's technical report. // // Reference: http://www.cs.iastate.edu/~honavar/keerthi-svm.pdf // Initialize variables int samples = inputs.Length; int dimension = inputs[0].Length; if (useComplexityHeuristic) c = EstimateComplexity(kernel, inputs); // Lagrange multipliers Array.Clear(alpha, 0, alpha.Length); if (isLinear) // Hyperplane weights Array.Clear(weights, 0, weights.Length); // Error cache Array.Clear(errors, 0, errors.Length); // Kernel evaluations cache this.kernelCache = new KernelFunctionCache(kernel, inputs, cacheSize); // [Keerthi] Initialize b_up to -1 and // i_up to any one index of class 1: this.b_upper = -1; this.i_upper = outputs.Find(x => x == +1)[0]; // [Keerthi] Initialize b_low to +1 and // i_low to any one index of class 2: this.b_lower = +1; this.i_lower = outputs.Find(x => x == -1)[0]; // [Keerthi] Set error cache for i_low and i_up: this.errors[i_lower] = +1; this.errors[i_upper] = -1; // Prepare indice sets activeExamples = new HashSet<int>(); nonBoundExamples = new HashSet<int>(); atBoundsExamples = new HashSet<int>(); // Algorithm: int numChanged = 0; bool examineAll = true; while (numChanged > 0 || examineAll) { numChanged = 0; if (examineAll) { // loop I over all training examples for (int i = 0; i < samples; i++) if (examineExample(i)) numChanged++; } else { if (strategy == SelectionStrategy.Sequential) { // loop I over examples not at bounds for (int i = 0; i < alpha.Length; i++) { if (alpha[i] != 0 && alpha[i] != c) { if (examineExample(i)) numChanged++; if (b_upper > b_lower - 2.0 * tolerance) { numChanged = 0; break; } } } } else // strategy == Strategy.WorstPair { bool success; do { success = takeStep(i_upper, i_lower); } while ((b_upper <= b_lower - 2.0 * tolerance) && success); numChanged = 0; } } if (examineAll) examineAll = false; else if (numChanged == 0) examineAll = true; } // Store information about bounded examples for (int i = 0; i < alpha.Length; i++) if (alpha[i] == c) atBoundsExamples.Add(i); if (isCompact) { // Store the hyperplane directly machine.SupportVectors = null; machine.Weights = weights; machine.Threshold = -(b_lower + b_upper) / 2.0; } else { // Store Support Vectors in the SV Machine. Only vectors which have lagrange multipliers // greater than zero will be stored as only those are actually required during evaluation. int activeCount = activeExamples.Count; int[] idx = new int[activeCount]; activeExamples.CopyTo(idx); machine.SupportVectors = new double[activeCount][]; machine.Weights = new double[activeCount]; for (int i = 0; i < idx.Length; i++) { int j = idx[i]; machine.SupportVectors[i] = inputs[j]; machine.Weights[i] = alpha[j] * outputs[j]; } machine.Threshold = -(b_lower + b_upper) / 2; } // Clear function cache this.kernelCache.Clear(); // Compute error if required. return (computeError) ? ComputeError(inputs, outputs) : 0.0; }
/// <summary> /// Runs the learning algorithm. /// </summary> /// /// <param name="token">A token to stop processing when requested.</param> /// <param name="c">The complexity for each sample.</param> protected override void Run(CancellationToken token, double[] c) { // The SMO algorithm chooses to solve the smallest possible optimization problem // at every step. At every step, SMO chooses two Lagrange multipliers to jointly // optimize, finds the optimal values for these multipliers, and updates the SVM // to reflect the new optimal values. // // Reference: http://research.microsoft.com/en-us/um/people/jplatt/smoTR.pdf // The algorithm has been updated to implement the improvements suggested // by Keerthi et al. The code has been based on the pseudo-code available // on the author's technical report. // // Reference: http://www.cs.iastate.edu/~honavar/keerthi-svm.pdf // Initialize variables int samples = Inputs.Length; int dimension = Inputs[0].Length; this.c = c; // Lagrange multipliers Array.Clear(alpha, 0, alpha.Length); if (IsLinear) // Hyperplane weights Array.Clear(weights, 0, weights.Length); // Error cache Array.Clear(errors, 0, errors.Length); // Kernel evaluations cache this.kernelCache = new KernelFunctionCache(Kernel, Inputs, cacheSize); // [Keerthi] Initialize b_up to -1 and // i_up to any one index of class 1: this.b_upper = -1; this.i_upper = Outputs.First(x => x > 0); // [Keerthi] Initialize b_low to +1 and // i_low to any one index of class 2: this.b_lower = +1; this.i_lower = Outputs.First(x => x < 0); // [Keerthi] Set error cache for i_low and i_up: this.errors[i_lower] = +1; this.errors[i_upper] = -1; // Prepare indices sets activeExamples.Clear(); nonBoundExamples.Clear(); atBoundsExamples.Clear(); // Algorithm: int numChanged = 0; int wholeSetChecks = 0; bool examineAll = true; bool diverged = false; bool shouldStop = false; while ((numChanged > 0 || examineAll) && !shouldStop) { numChanged = 0; if (examineAll) { // loop I over all training examples for (int i = 0; i < samples; i++) if (examineExample(i)) numChanged++; wholeSetChecks++; } else { if (strategy == SelectionStrategy.Sequential) { // loop I over examples not at bounds for (int i = 0; i < alpha.Length; i++) { if (alpha[i] != 0 && alpha[i] != c[i]) { if (examineExample(i)) numChanged++; if (b_upper > b_lower - 2.0 * tolerance) { numChanged = 0; break; } } } } else // strategy == Strategy.WorstPair { int attempts = 0; do { attempts++; if (!takeStep(i_upper, i_lower)) break; if (attempts > samples * maxChecks) break; } while ((b_upper <= b_lower - 2.0 * tolerance)); numChanged = 0; } } if (examineAll) examineAll = false; else if (numChanged == 0) examineAll = true; if (wholeSetChecks > maxChecks) shouldStop = diverged = true; if (token.IsCancellationRequested) shouldStop = true; } // Store information about bounded examples for (int i = 0; i < alpha.Length; i++) { if (alpha[i] == c[i]) atBoundsExamples.Add(i); } if (isCompact) { // Store the hyperplane directly Machine.SupportVectors = null; Machine.Weights = weights; Machine.Threshold = -(b_lower + b_upper) / 2.0; } else { // Store Support Vectors in the SV Machine. Only vectors which have Lagrange multipliers // greater than zero will be stored as only those are actually required during evaluation. int activeCount = activeExamples.Count; int[] idx = new int[activeCount]; activeExamples.CopyTo(idx); Machine.SupportVectors = new double[activeCount][]; Machine.Weights = new double[activeCount]; for (int i = 0; i < idx.Length; i++) { int j = idx[i]; Machine.SupportVectors[i] = Inputs[j]; Machine.Weights[i] = alpha[j] * Outputs[j]; } Machine.Threshold = -(b_lower + b_upper) / 2; } // Clear function cache this.kernelCache.Clear(); this.kernelCache = null; if (diverged) { throw new ConvergenceException("Convergence could not be attained. " + "Please reduce the cost of misclassification errors by reducing " + "the complexity parameter C or try a different kernel function."); } }
public void KernelFunctionCacheConstructorTest2() { IKernel kernel = new Linear(1); int cacheSize = inputs.Length; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(10, target.Size); Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Misses); for (int i = 0; i < inputs.Length; i++) { double expected = i * i + 1; double actual = target.GetOrCompute(i); Assert.AreEqual(expected, actual); } Assert.AreEqual(0, target.Hits); int[] hits = { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 }; int[] miss = { 9, 17, 24, 30, 35, 39, 42, 44, 45, 45 }; for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = i * j + 1; double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } Assert.AreEqual(hits[i], target.Hits); Assert.AreEqual(miss[i], target.Misses); } for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = i * j + 1; double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } Assert.AreEqual(45, target.Misses); Assert.AreEqual(135, target.Hits); Assert.AreEqual(1.0, target.Usage); }
public void KernelFunctionCacheConstructorTest4() { IKernel kernel = new Linear(); int cacheSize = 100; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(inputs.Length, target.Size); }
public void KernelFunctionCacheConstructorTest3() { IKernel kernel = new Linear(1); int cacheSize = 5; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(5, target.Size); Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Misses); for (int i = 0; i < inputs.Length; i++) { double expected = i * i + 1; double actual = target.GetOrCompute(i); Assert.AreEqual(expected, actual); } Assert.AreEqual(0, target.Hits); for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = i * j + 1; double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } Assert.AreEqual(9, target.Hits); Assert.AreEqual(81, target.Misses); var snapshot = target.GetDataCache(); foreach (var entry in snapshot) { double a = target.GetOrCompute(entry.Key.Item1, entry.Key.Item2); double b = target.GetOrCompute(entry.Key.Item2, entry.Key.Item1); Assert.AreEqual(a, b); } Assert.AreEqual(81, target.Misses); Assert.AreEqual(29, target.Hits); Assert.AreEqual(1.0, target.Usage); }
public void KernelFunctionCacheConstructorTest7() { double[][] inputs = { new double[] { 0, 1 }, new double[] { 1, 0 }, new double[] { 1, 1 }, }; IKernel kernel = new Polynomial(2); int cacheSize = inputs.Length; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(3, target.Size); Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Misses); // upper half for (int i = 0; i < inputs.Length; i++) { for (int j = i + 1; j < inputs.Length; j++) { double expected = kernel.Function(inputs[i], inputs[j]); double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } var lruList1 = target.GetLeastRecentlyUsedList(); Assert.AreEqual(3, target.Misses); Assert.AreEqual(0, target.Hits); Assert.AreEqual(1.0, target.Usage); // upper half, backwards for (int i = inputs.Length - 1; i >= 0; i--) { for (int j = inputs.Length - 1; j >= i; j--) { double expected = kernel.Function(inputs[i], inputs[j]); double actual = target.GetOrCompute(j, i); Assert.AreEqual(expected, actual); } } var lruList2 = target.GetLeastRecentlyUsedList(); Assert.IsTrue(lruList2.SequenceEqual(lruList1.Reverse())); Assert.AreEqual(3, target.Misses); Assert.AreEqual(3, target.Hits); Assert.AreEqual(1.0, target.Usage); }
public void KernelFunctionCacheConstructorTest6() { IKernel kernel = new Gaussian(0.6); int cacheSize = inputs.Length; KernelFunctionCache target = new KernelFunctionCache(kernel, inputs, cacheSize); Assert.AreEqual(10, target.Size); Assert.AreEqual(0, target.Hits); Assert.AreEqual(0, target.Misses); for (int i = 0; i < inputs.Length; i++) { double expected = kernel.Function(inputs[i], inputs[i]); double actual = target.GetOrCompute(i); Assert.AreEqual(expected, actual); } Assert.AreEqual(0, target.Hits); for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = kernel.Function(inputs[i], inputs[j]); double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } for (int i = 0; i < inputs.Length; i++) { for (int j = 0; j < inputs.Length; j++) { double expected = kernel.Function(inputs[i], inputs[j]); double actual = target.GetOrCompute(i, j); Assert.AreEqual(expected, actual); } } Assert.AreEqual(45, target.Misses); Assert.AreEqual(135, target.Hits); Assert.AreEqual(1.0, target.Usage); }
/// <summary> /// Runs the SMO algorithm. /// </summary> /// /// <param name="computeError"> /// True to compute error after the training /// process completes, false otherwise. Default is true. /// </param> /// <param name="token"> /// A <see cref="CancellationToken"/> which can be used /// to request the cancellation of the learning algorithm /// when it is being run in another thread. /// </param> /// /// <returns> /// The misclassification error rate of /// the resulting support vector machine. /// </returns> /// public double Run(bool computeError, CancellationToken token) { // The SMO algorithm chooses to solve the smallest possible optimization problem // at every step. At every step, SMO chooses two Lagrange multipliers to jointly // optimize, finds the optimal values for these multipliers, and updates the SVM // to reflect the new optimal values. // // Reference: http://research.microsoft.com/en-us/um/people/jplatt/smoTR.pdf // The algorithm has been updated to implement the improvements suggested // by Keerthi et al. The code has been based on the pseudo-code available // on the author's technical report. // // Reference: http://www.cs.iastate.edu/~honavar/keerthi-svm.pdf // Initialize variables int samples = inputs.Length; int dimension = inputs[0].Length; // Initialization heuristics if (useComplexityHeuristic) c = EstimateComplexity(kernel, inputs); int[] positives = outputs.Find(x => x == +1); int[] negatives = outputs.Find(x => x == -1); // If all examples are positive or negative, terminate // learning early by directly setting the threshold. if (positives.Length == 0) { machine.SupportVectors = new double[0][]; machine.Weights = new double[0]; machine.Threshold = -1; return 0; } if (negatives.Length == 0) { machine.SupportVectors = new double[0][]; machine.Weights = new double[0]; machine.Threshold = +1; return 0; } if (useClassLabelProportion) WeightRatio = positives.Length / (double)negatives.Length; // Lagrange multipliers Array.Clear(alpha, 0, alpha.Length); if (isLinear) // Hyperplane weights Array.Clear(weights, 0, weights.Length); // Error cache Array.Clear(errors, 0, errors.Length); // Kernel evaluations cache this.kernelCache = new KernelFunctionCache(kernel, inputs, cacheSize); // [Keerthi] Initialize b_up to -1 and // i_up to any one index of class 1: this.b_upper = -1; this.i_upper = positives[0]; // [Keerthi] Initialize b_low to +1 and // i_low to any one index of class 2: this.b_lower = +1; this.i_lower = negatives[0]; // [Keerthi] Set error cache for i_low and i_up: this.errors[i_lower] = +1; this.errors[i_upper] = -1; // Prepare indice sets activeExamples.Clear(); nonBoundExamples.Clear(); atBoundsExamples.Clear(); // Balance classes bool balanced = positiveWeight == 1 && negativeWeight == 1; positiveCost = c * positiveWeight; negativeCost = c * negativeWeight; // Algorithm: int numChanged = 0; int wholeSetChecks = 0; bool examineAll = true; bool diverged = false; bool shouldStop = false; while ((numChanged > 0 || examineAll) && !shouldStop) { numChanged = 0; if (examineAll) { // loop I over all training examples for (int i = 0; i < samples; i++) if (examineExample(i)) numChanged++; wholeSetChecks++; } else { if (strategy == SelectionStrategy.Sequential) { if (balanced) // Assume balanced data { // loop I over examples not at bounds for (int i = 0; i < alpha.Length; i++) { if (alpha[i] != 0 && alpha[i] != c) { if (examineExample(i)) numChanged++; if (b_upper > b_lower - 2.0 * tolerance) { numChanged = 0; break; } } } } else // Use different weights for classes { // loop I over examples not at bounds for (int i = 0; i < alpha.Length; i++) { if (alpha[i] != 0) { if (outputs[i] == +1) { if (alpha[i] == positiveCost) continue; } else // outputs[i] == -1 { if (alpha[i] == negativeCost) continue; } if (examineExample(i)) numChanged++; if (b_upper > b_lower - 2.0 * tolerance) { numChanged = 0; break; } } } } } else // strategy == Strategy.WorstPair { int attempts = 0; do { attempts++; if (!takeStep(i_upper, i_lower)) break; if (attempts > samples * maxChecks) break; } while ((b_upper <= b_lower - 2.0 * tolerance)); numChanged = 0; } } if (examineAll) examineAll = false; else if (numChanged == 0) examineAll = true; if (wholeSetChecks > maxChecks) shouldStop = diverged = true; if (token.IsCancellationRequested) shouldStop = true; } // Store information about bounded examples if (balanced) { // Assume equal weights for classes for (int i = 0; i < alpha.Length; i++) if (alpha[i] == c) atBoundsExamples.Add(i); } else { // Use different weights for classes for (int i = 0; i < alpha.Length; i++) { if (outputs[i] == +1) { if (alpha[i] == positiveCost) atBoundsExamples.Add(i); } else // outputs[i] == -1 { if (alpha[i] == negativeCost) atBoundsExamples.Add(i); } } } if (isCompact) { // Store the hyperplane directly machine.SupportVectors = null; machine.Weights = weights; machine.Threshold = -(b_lower + b_upper) / 2.0; } else { // Store Support Vectors in the SV Machine. Only vectors which have lagrange multipliers // greater than zero will be stored as only those are actually required during evaluation. int activeCount = activeExamples.Count; int[] idx = new int[activeCount]; activeExamples.CopyTo(idx); machine.SupportVectors = new double[activeCount][]; machine.Weights = new double[activeCount]; for (int i = 0; i < idx.Length; i++) { int j = idx[i]; machine.SupportVectors[i] = inputs[j]; machine.Weights[i] = alpha[j] * outputs[j]; } machine.Threshold = -(b_lower + b_upper) / 2; } // Clear function cache this.kernelCache.Clear(); this.kernelCache = null; if (diverged) { throw new ConvergenceException("Convergence could not be attained. " + "Please reduce the cost of misclassification errors by reducing " + "the complexity parameter C or try a different kernel function."); } // Compute error if required. return (computeError) ? ComputeError(inputs, outputs) : 0.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; }