Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
352 lines (310 sloc) 12.8 KB
// Accord Imaging Library
// The Accord.NET Framework
// http://accord-framework.net
//
// Copyright © César Souza, 2009-2017
// cesarsouza at gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
namespace Accord.MachineLearning
{
using Accord.Math;
using System;
using System.Linq;
using System.Threading;
using Accord.Compat;
using System.Threading.Tasks;
using System.Data;
using Accord.Diagnostics;
/// <summary>
/// Base class for Bag of Visual Words implementations.
/// </summary>
///
[Serializable]
public class BaseBagOfWords<TModel, TInput, TClustering> : ParallelLearningBase,
IBagOfWords<TInput[]>, IUnsupervisedLearning<TModel, TInput[], double[]>,
ITransform<TInput, Sparse<double>>
where TModel : BaseBagOfWords<TModel, TInput, TClustering>, ITransform<TInput[], int[]>, ITransform<TInput[], double[]>
where TClustering : IUnsupervisedLearning<IClassifier<TInput, int>, TInput, int>
{
private IClassifier<TInput, int> classifier;
/// <summary>
/// Gets the number of words in this codebook.
/// </summary>
///
public int NumberOfWords
{
get
{
if (classifier == null)
return 0;
return classifier.NumberOfClasses;
}
}
/// <summary>
/// Gets the clustering algorithm used to create this model.
/// </summary>
///
public TClustering Clustering { get; set; }
/// <summary>
/// Gets the number of inputs accepted by the model.
/// </summary>
/// <value>The number of inputs.</value>
public int NumberOfInputs
{
get { return -1; }
}
/// <summary>
/// Gets the number of outputs generated by the model.
/// </summary>
/// <value>The number of outputs.</value>
public int NumberOfOutputs
{
get { return NumberOfWords; }
}
int ITransform.NumberOfInputs
{
get { return NumberOfInputs; }
set { throw new ReadOnlyException("This property is read-only."); }
}
int ITransform.NumberOfOutputs
{
get { return NumberOfOutputs; }
set { throw new ReadOnlyException("This property is read-only."); }
}
/// <summary>
/// Constructs a new <see cref="BagOfWords"/>.
/// </summary>
///
protected BaseBagOfWords()
{
}
/// <summary>
/// Initializes this instance.
/// </summary>
///
protected void Init(TClustering algorithm)
{
this.Clustering = algorithm;
}
internal KMeans KMeans(int numberOfWords)
{
return new KMeans(numberOfWords)
{
ComputeCovariances = false,
UseSeeding = Seeding.KMeansPlusPlus,
ParallelOptions = ParallelOptions
};
}
#region Transform
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <param name="result">The location to where to store the
/// result of this transformation.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public int[] Transform(TInput[] input, int[] result)
{
// Detect all activation centroids
Parallel.For(0, input.Length, ParallelOptions, i =>
{
int j = classifier.Decide(input[i]);
Interlocked.Increment(ref result[j]);
});
return result;
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <param name="result">The location to where to store the
/// result of this transformation.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public double[] Transform(TInput[] input, double[] result)
{
// Detect all activation centroids
if (ParallelOptions.MaxDegreeOfParallelism == 1)
{
for (int i = 0; i < input.Length; i++)
{
int j = classifier.Decide(input[i]);
result[j]++;
};
}
else
{
Parallel.For(0, input.Length, ParallelOptions, i =>
{
int j = classifier.Decide(input[i]);
InterlockedEx.Increment(ref result[j]);
});
}
return result;
}
#endregion
#region Learn
/// <summary>
/// Learns a model that can map the given inputs to the desired outputs.
/// </summary>
/// <param name="inputs">The model inputs.</param>
/// <param name="weights">The weight of importance for each input sample.</param>
/// <returns>A model that has learned how to produce suitable outputs
/// given the input data <paramref name="inputs" />.</returns>
public TModel Learn(TInput[][] inputs, double[] weights = null)
{
if (weights != null)
throw new ArgumentException(Accord.Properties.Resources.NotSupportedWeights, "weights");
if (inputs.Length <= NumberOfWords)
{
throw new InvalidOperationException("Not enough data points to cluster. Please try "
+ "to adjust the feature extraction algorithm to generate more points.");
}
int total = inputs.Select(x => x.Length).Sum();
var allSamples = new TInput[total];
for (int i = 0, k = 0; i < inputs.Length; i++)
for (int j = 0; j < inputs[i].Length; j++, k++)
allSamples[k] = inputs[i][j];
double[] allWeights = null;
if (weights != null)
{
allWeights = new double[total];
for (int i = 0, k = 0; i < inputs.Length; i++)
for (int j = 0; j < inputs[i].Length; j++, k++)
allWeights[k] = weights[i];
}
this.classifier = this.Clustering.Learn(allSamples, allWeights);
Debug.Assert(classifier.NumberOfClasses == NumberOfWords);
return (TModel)this;
}
double[] IBagOfWords<TInput[]>.GetFeatureVector(TInput[] value)
{
throw new NotImplementedException();
}
/// <summary>
/// Applies the transformation to an input, producing an associated output.
/// </summary>
/// <param name="input">The input data to which the transformation should be applied.</param>
/// <returns>The output generated by applying this transformation to the given input.</returns>
public double[] Transform(TInput[] input)
{
return Transform(input, new double[NumberOfWords]);
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public double[][] Transform(TInput[][] input)
{
return Transform(input, Jagged.Create<double>(input.Length, NumberOfWords));
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <param name="result">The location to where to store the
/// result of this transformation.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public double[][] Transform(TInput[][] input, double[][] result)
{
for (int i = 0; i < result.Length; i++)
Transform(input[i], result[i]);
return result;
}
int[] ICovariantTransform<TInput[], int[]>.Transform(TInput[] input)
{
return Transform(input, new int[NumberOfWords]);
}
int[][] ICovariantTransform<TInput[], int[]>.Transform(TInput[][] input)
{
return Transform(input, Jagged.Create<int>(input.Length, NumberOfWords));
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <param name="result">The location to where to store the
/// result of this transformation.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public int[][] Transform(TInput[][] input, int[][] result)
{
for (int i = 0; i < result.Length; i++)
Transform(input[i], result[i]);
return result;
}
/// <summary>
/// Applies the transformation to an input, producing an associated output.
/// </summary>
/// <param name="input">The input data to which the transformation should be applied.</param>
/// <returns>The output generated by applying this transformation to the given input.</returns>
public Sparse<double> Transform(TInput input)
{
return Transform(new[] { input }, new Sparse<double>[1])[0];
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
Sparse<double>[] ICovariantTransform<TInput, Sparse<double>>.Transform(TInput[] input)
{
return Transform(input, new Sparse<double>[input.Length]);
}
/// <summary>
/// Applies the transformation to a set of input vectors,
/// producing an associated set of output vectors.
/// </summary>
/// <param name="input">The input data to which
/// the transformation should be applied.</param>
/// <param name="result">The location to where to store the
/// result of this transformation.</param>
/// <returns>The output generated by applying this
/// transformation to the given input.</returns>
public Sparse<double>[] Transform(TInput[] input, Sparse<double>[] result)
{
for (int i = 0; i < input.Length; i++)
{
// Detect all feature words
foreach (TInput word in input)
{
int j = classifier.Decide(word);
result[i][j]++;
}
}
return result;
}
#endregion
}
}
You can’t perform that action at this time.