Free cookie consent management tool by TermsFeed Policy Generator

source: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NcaAlgorithm.cs @ 8470

Last change on this file since 8470 was 8470, checked in by abeham, 12 years ago

#1913: increased point size and converted K to a fixed value parameter

File size: 12.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Threading;
26using HeuristicLab.Algorithms.DataAnalysis;
27using HeuristicLab.Analysis;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Optimization;
32using HeuristicLab.Parameters;
33using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
34using HeuristicLab.PluginInfrastructure;
35using HeuristicLab.Problems.DataAnalysis;
36
37namespace HeuristicLab.Algorithms.NCA {
38  internal delegate void Reporter(double quality, double[] coefficients);
39  /// <summary>
40  /// Neighborhood Components Analysis
41  /// </summary>
42  [Item("Neighborhood Components Analysis (NCA)", "Implementation of Neighborhood Components Analysis based on the description of J. Goldberger, S. Roweis, G. Hinton, R. Salakhutdinov. 2005. Neighbourhood Component Analysis. Advances in Neural Information Processing Systems, 17. pp. 513-520.")]
43  [Creatable("Data Analysis")]
44  [StorableClass]
45  public sealed class NcaAlgorithm : FixedDataAnalysisAlgorithm<IClassificationProblem> {
46    #region Parameter Properties
47    public IFixedValueParameter<IntValue> KParameter {
48      get { return (IFixedValueParameter<IntValue>)Parameters["K"]; }
49    }
50    public IFixedValueParameter<IntValue> DimensionsParameter {
51      get { return (IFixedValueParameter<IntValue>)Parameters["Dimensions"]; }
52    }
53    public IConstrainedValueParameter<INCAInitializer> InitializationParameter {
54      get { return (IConstrainedValueParameter<INCAInitializer>)Parameters["Initialization"]; }
55    }
56    public IFixedValueParameter<IntValue> NeighborSamplesParameter {
57      get { return (IFixedValueParameter<IntValue>)Parameters["NeighborSamples"]; }
58    }
59    public IFixedValueParameter<IntValue> IterationsParameter {
60      get { return (IFixedValueParameter<IntValue>)Parameters["Iterations"]; }
61    }
62    #endregion
63
64    #region Properties
65    private int K {
66      get { return KParameter.Value.Value; }
67      set { KParameter.Value.Value = value; }
68    }
69    private int Dimensions {
70      get { return DimensionsParameter.Value.Value; }
71      set { DimensionsParameter.Value.Value = value; }
72    }
73    private int NeighborSamples {
74      get { return NeighborSamplesParameter.Value.Value; }
75      set { NeighborSamplesParameter.Value.Value = value; }
76    }
77    private int Iterations {
78      get { return IterationsParameter.Value.Value; }
79      set { IterationsParameter.Value.Value = value; }
80    }
81    #endregion
82
83    [StorableConstructor]
84    private NcaAlgorithm(bool deserializing) : base(deserializing) { }
85    private NcaAlgorithm(NcaAlgorithm original, Cloner cloner) : base(original, cloner) { }
86    public NcaAlgorithm()
87      : base() {
88      Parameters.Add(new FixedValueParameter<IntValue>("K", "The K for the nearest neighbor.", new IntValue(1)));
89      Parameters.Add(new FixedValueParameter<IntValue>("Dimensions", "The number of dimensions that NCA should reduce the data to.", new IntValue(2)));
90      Parameters.Add(new ConstrainedValueParameter<INCAInitializer>("Initialization", "Which method should be used to initialize the matrix. Typically LDA (linear discriminant analysis) should provide a good estimate."));
91      Parameters.Add(new FixedValueParameter<IntValue>("NeighborSamples", "How many of the neighbors should be sampled in order to speed up the calculation. This should be at least the value of k and at most the number of training instances minus one.", new IntValue(50)));
92      Parameters.Add(new FixedValueParameter<IntValue>("Iterations", "How many iterations the conjugate gradient (CG) method should be allowed to perform. The method might still terminate earlier if a local optima has already been reached.", new IntValue(20)));
93
94      INCAInitializer defaultInitializer = null;
95      foreach (var initializer in ApplicationManager.Manager.GetInstances<INCAInitializer>().OrderBy(x => x.ItemName)) {
96        if (initializer is LDAInitializer) defaultInitializer = initializer;
97        InitializationParameter.ValidValues.Add(initializer);
98      }
99      if (defaultInitializer != null) InitializationParameter.Value = defaultInitializer;
100
101      Problem = new ClassificationProblem();
102    }
103
104    public override IDeepCloneable Clone(Cloner cloner) {
105      return new NcaAlgorithm(this, cloner);
106    }
107
108    public override void Prepare() {
109      if (Problem != null) base.Prepare();
110    }
111
112    protected override void Run() {
113      var initializer = InitializationParameter.Value;
114
115      var clonedProblem = (IClassificationProblemData)Problem.ProblemData.Clone();
116      var model = Train(clonedProblem, K, Dimensions, NeighborSamples, Iterations, initializer.Initialize(clonedProblem, Dimensions), ReportQuality, CancellationToken.None);
117      Results.Add(new Result("ClassificationSolution", "The classification solution.", model.CreateClassificationSolution(clonedProblem)));
118    }
119
120    public static INcaClassificationSolution CreateClassificationSolution(IClassificationProblemData data, int k, int dimensions, int neighborSamples, int iterations, INCAInitializer initializer) {
121      var clonedProblem = (IClassificationProblemData)data.Clone();
122      var model = Train(clonedProblem, k, dimensions, neighborSamples, iterations, initializer);
123      return model.CreateClassificationSolution(clonedProblem);
124    }
125
126    public static INcaModel Train(IClassificationProblemData problemData, int k, int dimensions, int neighborSamples, int iterations, INCAInitializer initializer) {
127      return Train(problemData, k, dimensions, neighborSamples, iterations, initializer.Initialize(problemData, dimensions), null, CancellationToken.None);
128    }
129
130    public static INcaModel Train(IClassificationProblemData problemData, int k, int neighborSamples, int iterations, double[,] initalMatrix) {
131      var matrix = new double[initalMatrix.Length];
132      for (int i = 0; i < initalMatrix.GetLength(0); i++)
133        for (int j = 0; j < initalMatrix.GetLength(1); j++)
134          matrix[i * initalMatrix.GetLength(1) + j] = initalMatrix[i, j];
135      return Train(problemData, k, initalMatrix.GetLength(1), neighborSamples, iterations, matrix, null, CancellationToken.None);
136    }
137
138    private static INcaModel Train(IClassificationProblemData data, int k, int dimensions, int neighborSamples, int iterations, double[] matrix, Reporter reporter, CancellationToken cancellation) {
139      var scaling = new Scaling(data.Dataset, data.AllowedInputVariables, data.TrainingIndices);
140      var scaledData = AlglibUtil.PrepareAndScaleInputMatrix(data.Dataset, data.AllowedInputVariables, data.TrainingIndices, scaling);
141      var classes = data.Dataset.GetDoubleValues(data.TargetVariable, data.TrainingIndices).ToArray();
142      var attributes = scaledData.GetLength(1);
143
144      alglib.mincgstate state;
145      alglib.mincgreport rep;
146      alglib.mincgcreate(matrix, out state);
147      alglib.mincgsetcond(state, 0, 0, 0, iterations);
148      alglib.mincgsetxrep(state, true);
149      int neighborSampleSize = neighborSamples;
150      Optimize(state, scaledData, classes, dimensions, neighborSampleSize, cancellation, reporter);
151      alglib.mincgresults(state, out matrix, out rep);
152
153      var transformationMatrix = new double[attributes, dimensions];
154      var counter = 0;
155      for (var i = 0; i < attributes; i++)
156        for (var j = 0; j < dimensions; j++)
157          transformationMatrix[i, j] = matrix[counter++];
158
159      return new NcaModel(k, transformationMatrix, data.Dataset, data.TrainingIndices, data.TargetVariable, data.AllowedInputVariables, scaling, data.ClassValues.ToArray());
160    }
161
162    private static void Optimize(alglib.mincgstate state, double[,] data, double[] classes, int dimensions, int neighborSampleSize, CancellationToken cancellation, Reporter reporter) {
163      while (alglib.mincgiteration(state)) {
164        if (cancellation.IsCancellationRequested) break;
165        if (state.needfg) {
166          Gradient(state.x, ref state.innerobj.f, state.innerobj.g, data, classes, dimensions, neighborSampleSize);
167          continue;
168        }
169        if (state.innerobj.xupdated) {
170          if (reporter != null)
171            reporter(state.innerobj.f, state.innerobj.x);
172          continue;
173        }
174        throw new InvalidOperationException("Neighborhood Components Analysis: Error in Optimize() (some derivatives were not provided?)");
175      }
176    }
177
178    private static void Gradient(double[] A, ref double func, double[] grad, double[,] data, double[] classes, int dimensions, int neighborSampleSize) {
179      var instances = data.GetLength(0);
180      var attributes = data.GetLength(1);
181
182      var AMatrix = new Matrix(A, A.Length / dimensions, dimensions);
183
184      alglib.sparsematrix probabilities;
185      alglib.sparsecreate(instances, instances, out probabilities);
186      var transformedDistances = new Dictionary<int, double>(instances);
187      for (int i = 0; i < instances; i++) {
188        var iVector = new Matrix(GetRow(data, i), data.GetLength(1));
189        for (int k = 0; k < instances; k++) {
190          if (k == i) {
191            transformedDistances.Remove(k);
192            continue;
193          }
194          var kVector = new Matrix(GetRow(data, k));
195          transformedDistances[k] = Math.Exp(-iVector.Multiply(AMatrix).Subtract(kVector.Multiply(AMatrix)).SquaredVectorLength());
196        }
197        var sample = transformedDistances.OrderByDescending(x => x.Value).Take(neighborSampleSize).ToArray();
198        var normalization = sample.Sum(x => x.Value);
199        if (normalization > 0) {
200          foreach (var s in sample) {
201            if (s.Value <= 0) break;
202            alglib.sparseset(probabilities, i, s.Key, s.Value / normalization);
203          }
204        }
205      }
206      alglib.sparseconverttocrs(probabilities); // needed to enumerate in order (top-down and left-right)
207
208      int t0 = 0, t1 = 0, r, c;
209      double val;
210      var pi = new double[instances];
211      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
212        if (classes[r].IsAlmost(classes[c])) {
213          pi[r] += val;
214        }
215      }
216
217      var innerSum = new double[attributes, attributes];
218      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
219        var vector = new Matrix(GetRow(data, r)).Subtract(new Matrix(GetRow(data, c)));
220        vector.OuterProduct(vector).Multiply(val * pi[r]).AddTo(innerSum);
221
222        if (classes[r].IsAlmost(classes[c])) {
223          vector.OuterProduct(vector).Multiply(-val).AddTo(innerSum);
224        }
225      }
226
227      func = -pi.Sum();
228
229      r = 0;
230      var newGrad = AMatrix.Multiply(-2.0).Transpose().Multiply(new Matrix(innerSum)).Transpose();
231      foreach (var g in newGrad) {
232        grad[r++] = g;
233      }
234    }
235
236    private void ReportQuality(double func, double[] coefficients) {
237      var instances = Problem.ProblemData.TrainingIndices.Count();
238      DataTable qualities;
239      if (!Results.ContainsKey("Optimization")) {
240        qualities = new DataTable("Optimization");
241        qualities.Rows.Add(new DataRow("Quality", string.Empty));
242        Results.Add(new Result("Optimization", qualities));
243      } else qualities = (DataTable)Results["Optimization"].Value;
244      qualities.Rows["Quality"].Values.Add(-func / instances);
245
246      if (!Results.ContainsKey("Quality")) {
247        Results.Add(new Result("Quality", new DoubleValue(-func / instances)));
248      } else ((DoubleValue)Results["Quality"].Value).Value = -func / instances;
249    }
250
251    private static IEnumerable<double> GetRow(double[,] data, int row) {
252      for (int i = 0; i < data.GetLength(1); i++)
253        yield return data[row, i];
254    }
255  }
256}
Note: See TracBrowser for help on using the repository browser.