diff --git a/BotSharp.Algorithm.UnitTest/BayesianTest.cs b/BotSharp.Algorithm.UnitTest/BayesianTest.cs deleted file mode 100644 index 1665a43b..00000000 --- a/BotSharp.Algorithm.UnitTest/BayesianTest.cs +++ /dev/null @@ -1,201 +0,0 @@ -using BotSharp.Algorithm.Bayesian; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace BotSharp.Algorithm.UnitTest -{ - [TestClass] - public class BayesianTest - { - /// - /// The training set - /// - private ITrainingSet _trainingSet; - - /// - /// The classifier - /// - private IClassifier _classifier; - - /// - /// The spam class - /// - private static IClass _spamClass; - - /// - /// The ham class - /// - private static IClass _hamClass; - - /// - /// Sets up. - /// - public void SetUp() - { - _trainingSet = BuildTrainingSet(); - _classifier = BuildClassifier(_trainingSet); - } - - /// - /// Builds the classifier. - /// - /// Classifier<StringClass, StringToken>. - private IClassifier BuildClassifier(ITrainingSetAccessor trainingSet) - { - var classifier = new NaiveClassifier(trainingSet) - { - // disable smoothing for exact probabilities - SmoothingAlpha = 0.0D - }; - - return classifier; - } - - /// - /// Builds the training set. - /// - /// ITrainingSet<StringClass, StringToken>. - private static ITrainingSet BuildTrainingSet() - { - var trainingSet = new TrainingSet(); - - // build data sets - var spamSet = BuildSpamDataSet(); - var hamSet = BuildHamDataSet(); - - // monkey test - //spamSet.SetSize.Should() - //.Be(hamSet.SetSize, "because this test relies on identical set sizes for exact probability testing"); - - // register classes - _spamClass = spamSet.Class; - _hamClass = hamSet.Class; - - // add the sets and return - trainingSet.Add(spamSet, hamSet); - return trainingSet; - } - - /// - /// Builds the spam data set. - /// - /// IDataSet<StringClass, StringToken>. - private static IDataSet BuildSpamDataSet() - { - return BuildDataSet("spam", 0.5D, "rolex", "watches", "viagra", "prince", "money", "send", "xyzzy"); - } - - /// - /// Builds the spam data set. - /// - /// IDataSet<StringClass, StringToken>. - private static IDataSet BuildHamDataSet() - { - return BuildDataSet("ham", 0.5D, "love", "flowers", "unicorn", "friendship", "money", "send", "send"); - } - - /// - /// Builds the data set. - /// - /// Name of the class. - /// The class probability. - /// The token. - /// The additional tokens. - /// IDataSet<StringClass, StringToken>. - private static IDataSet BuildDataSet(string className, double classProbability, string token, params string[] additionalTokens) - { - var @class = new StringClass(className, classProbability); - var dataSet = new DataSet(@class); - - dataSet.AddToken(new StringToken(token)); - dataSet.AddToken(additionalTokens.Select(t => new StringToken(t))); - - return dataSet; - } - - [TestMethod] - public void CalculateProbabilityReturnsOneHundredPercentForAKnownSpamWord() - { - var token = new StringToken("rolex"); - - var probability = _classifier.CalculateProbability(_spamClass, token); - //probability.Should().BeApproximately(1.0D, 0.0001D, "because the word is known the be a spam word"); - } - - [TestMethod] - public void CalculateProbabilityReturnsOneHundredPercentForAKnownHamWord() - { - var token = new StringToken("unicorn"); - - var probability = _classifier.CalculateProbability(_hamClass, token); - //probability.Should().BeApproximately(1.0D, 0.0001D, "because the word is known the be a ham word"); - } - - [TestMethod] - public void CalculateProbabilitiesWithHamWordReturnsProbabilitiesForAllClasses() - { - var token = new StringToken("unicorn"); - - var probabilities = _classifier.CalculateProbabilities(token).ToList(); - /*probabilities.Single(p => p.Class.Equals(_spamClass)) - .Probability.Should() - .BeApproximately(0D, 0.000001D, "because the token is known to be a ham word"); - - probabilities.Single(p => p.Class.Equals(_hamClass)) - .Probability.Should() - .BeApproximately(1D, 0.000001D, "because the token is known to be a ham word");*/ - } - - [TestMethod] - public void CalculateProbabilitiesWithMixedWordReturnsProbabilitiesForAllClasses() - { - var token = new StringToken("money"); - - var probabilities = _classifier.CalculateProbabilities(token).ToList(); - /*probabilities.Single(p => p.Class.Equals(_spamClass)) - .Probability.Should() - .BeApproximately(0.5D, 0.000001D, "because the token is known to be a ham and spam word"); - - probabilities.Single(p => p.Class.Equals(_hamClass)) - .Probability.Should() - .BeApproximately(0.5D, 0.000001D, "because the token is known to be a ham and spam word");*/ - } - - [TestMethod] - public void CalculateProbabilitiesWithMixedWordThatIsMoreLikelyHamThanSpamReturnsProbabilitiesForAllClasses() - { - var token = new StringToken("send"); - - var probabilities = _classifier.CalculateProbabilities(token).ToList(); - /*probabilities.Single(p => p.Class.Equals(_spamClass)) - .Probability.Should() - .BeApproximately(1 / 3D, 0.000001D, "because the token is more likely to be a ham than spam word"); - - probabilities.Single(p => p.Class.Equals(_hamClass)) - .Probability.Should() - .BeApproximately(2 / 3D, 0.000001D, "because the token is more likely to be a ham than spam word");*/ - } - - [TestMethod] - public void CalculateProbabilitiesWithRareTokensAndSmoothingAlphaIsUnambiguous() - { - var token1 = new StringToken("rolex"); - var token2 = new StringToken("unicorn"); - var token3 = new StringToken("send"); - - const double smoothingAlpha = 1.0D; - var probabilities = _classifier.CalculateProbabilities(new IToken[] { token1, token2, token3 }, smoothingAlpha).ToList(); - - /*probabilities.Single(p => p.Class.Equals(_spamClass)) - .Probability.Should() - .BeLessThan(0.5D, "because we used more ham than spam tokens"); - - probabilities.Single(p => p.Class.Equals(_hamClass)) - .Probability.Should() - .BeGreaterThan(0.5D, "because we used more ham than spam tokens");*/ - } - } -} diff --git a/BotSharp.Algorithm/Bayes/NaiveBayes.cs b/BotSharp.Algorithm/Bayes/NaiveBayes.cs new file mode 100644 index 00000000..764b14f9 --- /dev/null +++ b/BotSharp.Algorithm/Bayes/NaiveBayes.cs @@ -0,0 +1,91 @@ +using BotSharp.Algorithm.Extensions; +using BotSharp.Algorithm.Formulas; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BotSharp.Algorithm.Bayes +{ + /// + /// https://en.wikipedia.org/wiki/Bayes%27_theorem + /// + public class NaiveBayes + { + /// + /// smoothing function + /// + private Lidstone smoother; + + public List FeatureDist { get; set; } + + public List LabelDist { get; set; } + + public NaiveBayes() + { + smoother = new Lidstone(); + } + + /// + /// calculate posterior probability P(Y|X) + /// X is feature set, Y is label + /// P(X1,...,Xn|Y) = Sum(P(X1|Y) +...+ P(Xn|Y) + /// P(X, Y) = P(Y|X)P(X) = P(X|Y)P(Y) => P(Y|X) = P(Y)P(X|Y)/P(X) + /// + /// label + /// + /// + public double PosteriorProb(string Y, LabeledFeatureSet featureSet) + { + double prob = 0; + + // prior probability + prob = smoother.Log2Prob(LabelDist, Y); + + // posterior probability P(X1,...,Xn|Y) = Sum(P(X1|Y) +...+ P(Xn|Y) + featureSet.Features.ForEach(f => + { + var fv = FeatureDist.Find(x => x.Label == Y && x.FeatureName == f.Name).FeatureValues; + prob += smoother.Log2Prob(fv, f.Value); + }); + + return prob; + } + } + + public class Feature + { + public string Name { get; set; } + public string Value { get; set; } + + public Feature(string name, string value) + { + Name = name; + Value = value; + } + } + + public class FeatureFrequencyDistribution + { + public string Label { get; set; } + + public string FeatureName { get; set; } + + public List FeatureValues { get; set; } + + public override string ToString() + { + return $"{Label} {FeatureName} {FeatureValues.Count}"; + } + } + + public class LabeledFeatureSet + { + public List Features { get; set; } + public string Label { get; set; } + public LabeledFeatureSet() + { + this.Features = new List(); + } + } +} diff --git a/BotSharp.Algorithm/Bayesian/ClassBase.cs b/BotSharp.Algorithm/Bayesian/ClassBase.cs deleted file mode 100644 index 47e95e6a..00000000 --- a/BotSharp.Algorithm/Bayesian/ClassBase.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Diagnostics; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class ClassBase. - /// - [DebuggerDisplay("Class {Name}, base P = {Probability}")] - public abstract class ClassBase : IClass - { - /// - /// Gets the name. - /// - /// The name. - public string Name { get; private set; } - - /// - /// Gets the class' base probability. - /// - /// The probability. - public double Probability { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The name. - /// The probability. - /// name - /// - /// probability;Class base probability must be greater than or equal to zero. - /// or - /// probability;Class base probability must be less than or equal to one. - /// - protected ClassBase(string name, double probability) - { - if (ReferenceEquals(name, null)) throw new ArgumentNullException("name"); - if (probability < 0) throw new ArgumentOutOfRangeException("probability", "Class base probability must be greater than or equal to zero."); - if (probability > 1) throw new ArgumentOutOfRangeException("probability", "Class base probability must be less than or equal to one."); - - Name = name; - Probability = probability; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public abstract bool Equals(IClass other); - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/CombinedConditionalProbabilities.cs b/BotSharp.Algorithm/Bayesian/CombinedConditionalProbabilities.cs deleted file mode 100644 index f5ee13fe..00000000 --- a/BotSharp.Algorithm/Bayesian/CombinedConditionalProbabilities.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Struct CombinedConditionalProbability - /// - [DebuggerDisplay("P({Class}|{TokenProbabilities.Count} tokens)={Probability}")] - public struct CombinedConditionalProbability - { - /// - /// The class - /// - public readonly IClass Class; - - /// - /// The token probabilities - /// - public ICollection TokenProbabilities; - - /// - /// The probability - /// - public double Probability; - - /// - /// Initializes a new instance of the struct. - /// - /// The class. - /// The probability. - /// The tokenProbabilities. - public CombinedConditionalProbability(IClass @class, double probability, ICollection tokenProbabilities) - { - if (ReferenceEquals(@class, null)) throw new ArgumentNullException("class"); - if (ReferenceEquals(tokenProbabilities, null)) throw new ArgumentNullException("tokenProbabilities"); - if (probability < 0) throw new ArgumentOutOfRangeException("probability", probability, "Probability must greater than or equal to zero"); - if (probability > 1) throw new ArgumentOutOfRangeException("probability", probability, "Probability must less than or equal to one"); - - Class = @class; - TokenProbabilities = tokenProbabilities; - Probability = probability; - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/ConditionalProbability.cs b/BotSharp.Algorithm/Bayesian/ConditionalProbability.cs deleted file mode 100644 index a1788c78..00000000 --- a/BotSharp.Algorithm/Bayesian/ConditionalProbability.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Diagnostics; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Struct ConditionalProbability - /// - [DebuggerDisplay("P({Class}|{Token})={Probability}")] - public struct ConditionalProbability : IEquatable - { - /// - /// The class - /// - public readonly IClass Class; - - /// - /// The token - /// - public readonly IToken Token; - - /// - /// The conditional probability - /// - public readonly double Probability; - - /// - /// The occurrence of the token during the training phase. - /// - public readonly long Occurrence; - - /// - /// Initializes a new instance of the struct. - /// - /// The class. - /// The token. - /// The probability. - /// The occurrence. - /// @class - /// or - /// token - /// probability;Probability must greater than or equal to zero - /// or - /// probability;Probability must less than or equal to one - public ConditionalProbability(IClass @class, IToken token, double probability, long occurrence) - { - if (ReferenceEquals(@class, null)) throw new ArgumentNullException("class"); - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (probability < 0) throw new ArgumentOutOfRangeException("probability", probability, "Probability must greater than or equal to zero"); - if (probability > 1) throw new ArgumentOutOfRangeException("probability", probability, "Probability must less than or equal to one"); - if (probability < 0) throw new ArgumentOutOfRangeException("occurrence", occurrence, "Occurrence must greater than or equal to zero"); - - Class = @class; - Token = token; - Probability = probability; - Occurrence = occurrence; - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// Another object to compare to. - /// if the specified is equal to this instance; otherwise, . - public override bool Equals(object obj) - { - if (ReferenceEquals(obj, null)) return false; - return obj is ConditionalProbability && Equals((ConditionalProbability) obj); - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// true if the current object is equal to the parameter; otherwise, false. - public bool Equals(ConditionalProbability other) - { - return Class.Equals(other.Class) - && Token.Equals(other.Token) - && Probability.Equals(other.Probability); - } - - /// - /// Returns a hash code for this instance. - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() - { - var hash = 27; - hash = (13 * hash) + Class.GetHashCode(); - hash = (13 * hash) + Token.GetHashCode(); - hash = (13 * hash) + Probability.GetHashCode(); - return hash; - } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return String.Format("P({0}|{1})={2:P}", Class, Token, Probability); - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/DataSet.cs b/BotSharp.Algorithm/Bayesian/DataSet.cs deleted file mode 100644 index 91a56ac7..00000000 --- a/BotSharp.Algorithm/Bayesian/DataSet.cs +++ /dev/null @@ -1,358 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; -using System.Threading; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class DataSet. - /// - [DebuggerDisplay("Data set for class P({Class.Name})={Class.Probability}")] - public sealed class DataSet : IDataSet - { - /// - /// The default smoothing alpha - /// - public const double DefaultSmoothingAlpha = 0D; - - /// - /// The token count - /// - private readonly ConcurrentDictionary _tokenCount = new ConcurrentDictionary(); - - /// - /// The set size, i.e. the number of all tokens - /// - private long _setSize; - - /// - /// Gets the number of distinct tokens, - /// i.e. every token counted at exactly once. - /// - /// The token count. - /// - public long TokenCount - { - get { return _tokenCount.Count; } - } - - /// - /// Gets the size of the set. - /// - /// The size of the set. - /// - public long SetSize - { - get { return _setSize; } - } - - /// - /// Gets the class. - /// - /// The class. - public IClass Class { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The class. - /// @class - public DataSet(IClass @class) - { - if (ReferenceEquals(@class, null)) throw new ArgumentNullException("class"); - Class = @class; - } - - /// - /// Gets the with the specified token. - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// TokenInformation<IToken>. - /// token - public TokenInformation this[IToken token, double alpha = DefaultSmoothingAlpha] - { - get - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - - long count; - if (!_tokenCount.TryGetValue(token, out count)) - { - return new TokenInformation(token, 0L, 0D); - } - - var percentage = GetPercentage(count, alpha); - return new TokenInformation(token, count, percentage); - } - } - - /// - /// Gets the number of occurrences of the given token. - /// - /// The token. - /// System.Int64. - /// - public long GetCount(IToken token) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - - long count; - return !_tokenCount.TryGetValue(token, out count) ? 0 : count; - } - - /// - /// Gets the approximated percentage of the given - /// in this data set - /// by determining its occurrence count over the whole population. - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - /// token - /// - public double GetPercentage(IToken token, double alpha = DefaultSmoothingAlpha) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (alpha < 0) throw new ArgumentOutOfRangeException("alpha", alpha, "Smoothing parameter alpha must be greater than or equal to zero."); - - var count = GetCount(token); - return GetPercentage(count, alpha); - } - - /// - /// Gets the approximated percentage of the given - /// in this data set - /// by determining its occurrence count over the whole population. - /// - /// The token count. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - /// token - /// - private double GetPercentage(long tokenCount, double alpha = DefaultSmoothingAlpha) - { - Debug.Assert(alpha >= 0, "alpha >= 0"); - Debug.Assert(tokenCount >= 0, "tokenCount >= 0"); - - var totalCount = _setSize; // TODO: cache inverse set size - var vocabularySize = TokenCount; - - return (double)(tokenCount + alpha)/(double)(totalCount + alpha*vocabularySize); - } - - /// - /// Adds the given tokens a single time, incrementing the - /// and, at the first addition, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - public void AddToken(IToken token, params IToken[] additionalTokens) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens"); - - _tokenCount.AddOrUpdate(token, AddFirsIToken, IncremenITokenCount); - Interlocked.Increment(ref _setSize); - - AddToken(additionalTokens); - } - - /// - /// Adds the given tokens a single time, incrementing the - /// and, at the first addition, the . - /// - /// The tokens. - /// tokens - public void AddToken(IEnumerable tokens) - { - if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens"); - - foreach (var token in tokens) - { - _tokenCount.AddOrUpdate(token, AddFirsIToken, IncremenITokenCount); - Interlocked.Increment(ref _setSize); - } - } - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - /// - public void RemoveTokenOnce(IToken token, params IToken[] additionalTokens) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens"); - - RemoveSingleTokenInternal(token); - RemoveTokenOnce(additionalTokens); - } - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The tokens. - /// tokens - /// - public void RemoveTokenOnce(IEnumerable tokens) - { - if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens"); - - foreach (var token in tokens) - { - RemoveSingleTokenInternal(token); - } - } - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - /// - public void PurgeToken(IToken token, params IToken[] additionalTokens) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens"); - - PurgeTokenInternal(token); - PurgeToken(additionalTokens); - } - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The tokens. - /// tokens - /// - public void PurgeToken(IEnumerable tokens) - { - if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens"); - - foreach (var token in tokens) - { - PurgeTokenInternal(token); - } - } - - /// - /// Purges the tokens fulfilling the given predicate. - /// - /// The predicate. - public void PurgeWhere(Predicate predicate) - { - var candidateForPurge = from pair in _tokenCount - let tokenCount = new TokenCount(pair.Key, pair.Value) - where predicate(tokenCount) - select pair.Key; - PurgeToken(candidateForPurge); - } - - /// - /// Removes the single token internally. - /// - /// The token. - private void RemoveSingleTokenInternal(IToken token) - { - long count; - while (_tokenCount.TryGetValue(token, out count)) - { - var newValue = count - 1; - var collectionUpdated = _tokenCount.TryUpdate(token, newValue: newValue, comparisonValue: count); - if (!collectionUpdated) continue; - Interlocked.Decrement(ref _setSize); - - if (newValue == 0) - { - // explicit removal if the count is zero - var collection = _tokenCount as ICollection>; - collection.Remove(new KeyValuePair(token, 0)); - } - - break; - } - } - - /// - /// Purges a single token internally. - /// - /// The token. - private void PurgeTokenInternal(IToken token) - { - long count; - if (!_tokenCount.TryRemove(token, out count)) return; - - // decrement 'count' times - // TODO: use Interlocked.CompareExchange - for (int i = 0; i < count; ++i) - { - Interlocked.Decrement(ref _setSize); - } - } - - /// - /// Factory to initialize the value in for the given . - /// - /// The token. - /// System.Int64. - private static long AddFirsIToken(IToken token) - { - return 1; - } - - /// - /// Factory to increment the value in for the given . - /// - /// The token. - /// The number of tokens. - /// System.Int64. - private static long IncremenITokenCount(IToken token, long count) - { - return count + 1; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A that can be used to iterate through the collection. - public IEnumerator GetEnumerator() - { - return _tokenCount.Select(token => new TokenCount(token.Key, token.Value)).GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/EmptyDataSet.cs b/BotSharp.Algorithm/Bayesian/EmptyDataSet.cs deleted file mode 100644 index a9a2690d..00000000 --- a/BotSharp.Algorithm/Bayesian/EmptyDataSet.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class EmptyDataSet. This class cannot be inherited. - /// - internal sealed class EmptyDataSet : IDataSet - { - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A that can be used to iterate through the collection. - public IEnumerator GetEnumerator() - { - yield break; - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Gets the token count. - /// - /// The token count. - public long TokenCount { get { return 0; } } - - /// - /// Gets the size of the set. - /// - /// The size of the set. - public long SetSize { get { return 0; } } - - /// - /// Gets the class. - /// - /// The class. - public IClass Class { get; private set; } - - /// - /// Gets the with the specified token. - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// TokenInformation<IToken>. - public TokenInformation this[IToken token, double alpha = 0D] - { - get { return new TokenInformation(token, 0L, 0D); } - } - - /// - /// Initializes a new instance of the class. - /// - /// The class. - /// class - public EmptyDataSet(IClass @class) - { - if (ReferenceEquals(null, @class)) throw new ArgumentNullException("class"); - Class = @class; - } - - /// - /// Gets the count. - /// - /// The token. - /// System.Int64. - public long GetCount(IToken token) - { - return 0L; - } - - /// - /// Gets the percentage. - /// - /// The token. - /// The alpha. - /// System.Double. - /// - public double GetPercentage(IToken token, double alpha = 0) - { - return 0D; - } - - /// - /// Adds the token. - /// - /// The token. - /// The additional tokens. - /// Adding data to the empty data set is not allowed. - public void AddToken(IToken token, params IToken[] additionalTokens) - { - throw new InvalidOperationException("Adding data to the empty data set is not allowed."); - } - - /// - /// Adds the token. - /// - /// The tokens. - /// Adding data to the empty data set is not allowed. - public void AddToken(IEnumerable tokens) - { - throw new InvalidOperationException("Adding data to the empty data set is not allowed."); - } - - /// - /// Removes the token once. - /// - /// The token. - /// The additional tokens. - public void RemoveTokenOnce(IToken token, params IToken[] additionalTokens) - { - } - - /// - /// Removes the token once. - /// - /// The tokens. - public void RemoveTokenOnce(IEnumerable tokens) - { - } - - /// - /// Purges the token. - /// - /// The token. - /// The additional tokens. - public void PurgeToken(IToken token, params IToken[] additionalTokens) - { - } - - /// - /// Purges the token. - /// - /// The tokens. - public void PurgeToken(IEnumerable tokens) - { - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/IClass.cs b/BotSharp.Algorithm/Bayesian/IClass.cs deleted file mode 100644 index e0d9c0fd..00000000 --- a/BotSharp.Algorithm/Bayesian/IClass.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.ComponentModel; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface IClass - /// - public interface IClass : IEquatable - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - - /// - /// Gets or sets the class' base probability. - /// - /// The probability. - [DefaultValue(1)] - double Probability { get; set; } - } -} diff --git a/BotSharp.Algorithm/Bayesian/IClassifier.cs b/BotSharp.Algorithm/Bayesian/IClassifier.cs deleted file mode 100644 index f1a2fa79..00000000 --- a/BotSharp.Algorithm/Bayesian/IClassifier.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface IClassifier - /// - public interface IClassifier - { - /// - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// - /// Laplace smoothing is required in the context or rare (i.e. untrained) tokens or tokens - /// that do not appear in some classes. With smoothing disabled, these tokens result - /// in a zero probability for the whole class. To counter that, a positive ("alpha") - /// value for smoothing can be set. - /// - /// - double SmoothingAlpha { get; set; } - - /// - /// Calculates the probability of having the - /// given the occurrence of the . - /// - /// The class under test. - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied, setting to defaults to the values set in . - /// System.Double. - double CalculateProbability(IClass classUnderTest, IToken token, double? alpha = null); - - /// - /// Calculates the probability of having the - /// - /// given the occurrence of the - /// . - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied, setting to defaults to the values set in . - /// System.Double. - IEnumerable CalculateProbabilities(IToken token, double? alpha = null); - - /// - /// Calculates the probability of having the - /// - /// given the occurrence of the - /// . - /// - /// The tokens. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied, setting to defaults to the values set in . - /// System.Double. - IEnumerable CalculateProbabilities(ICollection tokens, double? alpha = null); - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/IDataSet.cs b/BotSharp.Algorithm/Bayesian/IDataSet.cs deleted file mode 100644 index f9118c50..00000000 --- a/BotSharp.Algorithm/Bayesian/IDataSet.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface IDataSet - /// - public interface IDataSet : IDataSetAccessor, ITokenRegistration - { - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/IDataSetAccessor.cs b/BotSharp.Algorithm/Bayesian/IDataSetAccessor.cs deleted file mode 100644 index 3278bb60..00000000 --- a/BotSharp.Algorithm/Bayesian/IDataSetAccessor.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface IDataSetAccessor - /// - public interface IDataSetAccessor : IEnumerable - { - /// - /// Gets the number of distinct tokens, - /// i.e. every token counted at exactly once. - /// - /// The token count. - /// - long TokenCount { get; } - - /// - /// Gets the size of the set. - /// - /// The size of the set. - /// - long SetSize { get; } - - /// - /// Gets the class. - /// - /// The class. - IClass Class { get; } - - /// - /// Gets the with the specified token. - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// TokenInformation<IToken>. - /// token - TokenInformation this[IToken token, double alpha] { get; } - - /// - /// Gets the number of occurrences of the given token. - /// - /// The token. - /// System.Int64. - /// token - /// - long GetCount(IToken token); - - /// - /// Gets the approximated percentage of the given - /// in this data set - /// by determining its occurrence count over the whole population. - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - /// token - /// - double GetPercentage(IToken token, double alpha); - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/IToken.cs b/BotSharp.Algorithm/Bayesian/IToken.cs deleted file mode 100644 index 165191ab..00000000 --- a/BotSharp.Algorithm/Bayesian/IToken.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface IToken - /// - public interface IToken : IEquatable - { - } -} diff --git a/BotSharp.Algorithm/Bayesian/ITokenRegistration.cs b/BotSharp.Algorithm/Bayesian/ITokenRegistration.cs deleted file mode 100644 index bf2e53d9..00000000 --- a/BotSharp.Algorithm/Bayesian/ITokenRegistration.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Collections.Generic; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface ITokenRegistration - /// - public interface ITokenRegistration - { - /// - /// Adds the given tokens a single time, incrementing the - /// and, at the first addition, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - void AddToken(IToken token, params IToken[] additionalTokens); - - /// - /// Adds the given tokens a single time, incrementing the - /// and, at the first addition, the . - /// - /// The tokens. - /// tokens - void AddToken(IEnumerable tokens); - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - /// - void RemoveTokenOnce(IToken token, params IToken[] additionalTokens); - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The tokens. - /// tokens - /// - void RemoveTokenOnce(IEnumerable tokens); - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The token. - /// The additional tokens. - /// - /// token - /// or - /// additionalTokens - /// - /// - void PurgeToken(IToken token, params IToken[] additionalTokens); - - /// - /// Removes the given tokens a single time, decrementing the and, - /// eventually, the . - /// - /// The tokens. - /// tokens - /// - void PurgeToken(IEnumerable tokens); - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/ITrainingSet.cs b/BotSharp.Algorithm/Bayesian/ITrainingSet.cs deleted file mode 100644 index dd6b0353..00000000 --- a/BotSharp.Algorithm/Bayesian/ITrainingSet.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface ITrainingSet - /// - public interface ITrainingSet : ITrainingSetAccessor - { - /// - /// Adds the specified data set. - /// - /// The data set. - /// The additional data sets. - /// dataSet - /// A data set for a given class was already registered. - void Add(IDataSet dataSet, params IDataSet[] additionalDataSets); - - /// - /// Adds the specified data sets. - /// - /// The data sets. - /// dataSets - /// A data set for a given class was already registered. - void Add(IEnumerable dataSets); - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/ITrainingSetAccessor.cs b/BotSharp.Algorithm/Bayesian/ITrainingSetAccessor.cs deleted file mode 100644 index 3eebfe42..00000000 --- a/BotSharp.Algorithm/Bayesian/ITrainingSetAccessor.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Interface ITrainingSetAccerssor - /// - public interface ITrainingSetAccessor : IEnumerable - { - /// - /// Gets the with the specified class. - /// - /// The class. - /// IDataSet<TClass, TToken>. - /// No data set was registered for the given class;class - IDataSet this[IClass @class] { get; } - } -} \ No newline at end of file diff --git a/BotSharp.Algorithm/Bayesian/LinqExtensions.cs b/BotSharp.Algorithm/Bayesian/LinqExtensions.cs deleted file mode 100644 index d97440ac..00000000 --- a/BotSharp.Algorithm/Bayesian/LinqExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; - - -namespace BotSharp.Algorithm.Bayesian -{ - internal static class LinqExtensions - { - /// - /// Converts an enumerable to a collection - /// - /// - /// The enumerable. - /// ICollection<T>. - public static ICollection ToCollection(this IEnumerable enumerable) - { - var type = enumerable.GetType(); - if (type.IsGenericCollectionType()) return (ICollection)enumerable; - - var collection = new Collection(); - foreach (var t in enumerable) - { - collection.Add(t); - } - return collection; - } - - /// - /// Forces evaluation of the enumerable - /// - /// - /// The enumerable. - public static void Run(this IEnumerable enumerable) - { - var type = enumerable.GetType(); - if (type.IsGenericCollectionType()) return; - - foreach (var item in enumerable) - { - } - } - - /// - /// The cache for - /// - private static readonly ConcurrentDictionary IsGenericCollectionTypeCache = new ConcurrentDictionary(); - - /// - /// Determines whether the specified type is a (generic) collection. - /// - /// The type. - /// true if the specified type is collection; otherwise, false. - public static bool IsGenericCollectionType(this Type type) - { - return IsGenericCollectionTypeCache.GetOrAdd(type, t => type.GetInterfaces() - .Any(ti => ti.IsGenericType - && - ti.GetGenericTypeDefinition() == - typeof (ICollection<>))); - - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/NaiveClassifier.cs b/BotSharp.Algorithm/Bayesian/NaiveClassifier.cs deleted file mode 100644 index f045503d..00000000 --- a/BotSharp.Algorithm/Bayesian/NaiveClassifier.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class NaiveClassifier. This class cannot be inherited. - /// - /// Assumes that all token occurrences are statistically independent. - /// - /// - public sealed class NaiveClassifier : IClassifier - { - /// - /// The training sets - /// - private readonly ITrainingSetAccessor _trainingSets; - - /// - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// - private double _smoothingAlpha = 0.01D; - - /// - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// - [DefaultValue(0.01D)] - public double SmoothingAlpha - { - get { return _smoothingAlpha; } - set - { - if (value <= 0) throw new ArgumentOutOfRangeException("value", value, "Value must be greater than zero."); - _smoothingAlpha = value; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The training sets. - /// trainingSets - public NaiveClassifier(ITrainingSetAccessor trainingSets) - { - if (ReferenceEquals(trainingSets, null)) throw new ArgumentNullException("trainingSets"); - _trainingSets = trainingSets; - } - - /// - /// Calculates the probability of having the - /// given the occurrence of the . - /// - /// The class under test. - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - public double CalculateProbability(IClass classUnderTest, IToken token, double? alpha = null) - { - var smoothingAlpha = alpha ?? _smoothingAlpha; - - ICollection remainingSets; - var setForClassUnderTest = SplitDataSets(classUnderTest, out remainingSets); - - // calculate the token's probability in the class under test - var percentageInClassUnderTest = setForClassUnderTest.GetPercentage(token, smoothingAlpha); - var probabilityInClassUnderTest = percentageInClassUnderTest * classUnderTest.Probability; - - // calculate the token's probabilities for the remaining classes - double sumOfRemainingProbabilites; - CalculateTokenProbabilityGivenClass(token, remainingSets, out sumOfRemainingProbabilites, smoothingAlpha).Run(); - - // calculate total probability - var totalProbability = probabilityInClassUnderTest + sumOfRemainingProbabilites; - - // calculate the class' probability given the token - var probabilityForClass = probabilityInClassUnderTest/totalProbability; - - // correct for rare words - return probabilityForClass; - } - - /// - /// Calculates the probability of having the - /// - /// given the occurrence of the - /// . - /// - /// The token. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - public IEnumerable CalculateProbabilities(IToken token, double? alpha = null) - { - var smoothingAlpha = alpha ?? _smoothingAlpha; - - // calculate the token's probabilities for all classes - double totalProbability; - var probabilities = CalculateTokenProbabilityGivenClass(token, _trainingSets, out totalProbability, smoothingAlpha); - - // apply Bayes theorem - var inverseOfTotalProbability = 1.0D/totalProbability; - return from cp in probabilities - let conditionalProbability = cp.Probability * inverseOfTotalProbability - select new ConditionalProbability(cp.Class, cp.Token, conditionalProbability, cp.Occurrence); - } - - /// - /// Calculates the probability of having the - /// - /// given the occurrence of the - /// . - /// - /// The tokens. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// System.Double. - public IEnumerable CalculateProbabilities(ICollection tokens, double? alpha = null) - { - var smoothingAlpha = alpha ?? _smoothingAlpha; - - var cpgs = tokens - .SelectMany(token => CalculateProbabilities(token, smoothingAlpha)) - .GroupBy(cp => cp.Class) - .ToCollection(); - - return from @group in cpgs - let cps = @group.ToCollection() - let eta = cps.Select(cp => cp.Probability) - .Sum(p => Math.Log(1 - p) - Math.Log(p)) - let probability = 1/(1 + Math.Exp(eta)) - select new CombinedConditionalProbability(@group.Key, probability, cps); - } - - /// - /// Calculates the token probabilities given a class. - /// - /// The token. - /// The sets. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// IEnumerable<ConditionalProbability<IClass, IToken>>. - private IEnumerable CalculateTokenProbabilityGivenClass(IToken token, IEnumerable sets, double alpha) - { - return from set in sets - let @class = set.Class - let classProbability = @class.Probability - let percentageInClass = set.GetPercentage(token, alpha) - let countInClass = set.GetCount(token) - let probabilityInClass = percentageInClass*classProbability - select new ConditionalProbability(@class, token, probabilityInClass, countInClass); - } - - /// - /// Calculates the token probabilities given a class. - /// - /// The token. - /// The sets. - /// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied. - /// The total probability for the given classes. - /// IEnumerable<ConditionalProbability<IClass, IToken>>. - private IEnumerable CalculateTokenProbabilityGivenClass(IToken token, IEnumerable sets, out double totalProbability, double alpha) - { - var probabilities = CalculateTokenProbabilityGivenClass(token, sets, alpha).ToCollection(); - totalProbability = probabilities.Sum(p => p.Probability); - return probabilities; - } - - /// - /// Splits the data sets. - /// - /// The class under test. - /// The remaining sets. - /// IDataSet<IClass, IToken>. - private IDataSetAccessor SplitDataSets(IClass classUnderTest, out ICollection remainingSets) - { - IDataSet setForClassUnderTest = null; - remainingSets = new Collection(); - - // split data sets by selected class and other classes - foreach (var trainingSet in _trainingSets) - { - // select the set for the class under test - if (trainingSet.Class.Equals(classUnderTest)) - { - Debug.Assert(setForClassUnderTest == null, - "The class under test must not have multiple sets registered in the DataSet"); - setForClassUnderTest = trainingSet; - continue; - } - - // select remaining sets - remainingSets.Add(trainingSet); - } - - // return the found set or an empty set - return setForClassUnderTest ?? new EmptyDataSet(classUnderTest); - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/StringClass.cs b/BotSharp.Algorithm/Bayesian/StringClass.cs deleted file mode 100644 index 7f0b9fa1..00000000 --- a/BotSharp.Algorithm/Bayesian/StringClass.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class StringClass. This class cannot be inherited. - /// - public sealed class StringClass : ClassBase - { - /// - /// Initializes a new instance of the class. - /// - /// The name. - /// The probability. - /// name - /// - /// probability;Class base probability must be greater than or equal to zero. - /// or - /// probability;Class base probability must be less than or equal to one. - /// - public StringClass(string name, double probability) - : base(name, probability) - { - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - public override bool Equals(IClass other) - { - var otherAsObject = (object) other; - return Equals(otherAsObject); - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - private bool Equals(StringClass other) - { - if (ReferenceEquals(other, null)) return false; - if (ReferenceEquals(other, this)) return true; - return String.Equals(Name, other.Name) - && Math.Abs(Probability - other.Probability) < 0.0001D; - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj is StringClass && Equals((StringClass) obj); - } - - /// - /// Returns a hash code for this instance. - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() - { - var hash = 27; - hash = (13 * hash) + Name.GetHashCode(); - hash = (13 * hash) + Probability.GetHashCode(); - return hash; - } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return Name; - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/StringToken.cs b/BotSharp.Algorithm/Bayesian/StringToken.cs deleted file mode 100644 index 6f19633c..00000000 --- a/BotSharp.Algorithm/Bayesian/StringToken.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Diagnostics; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class StringToken. This class cannot be inherited. - /// - [DebuggerDisplay("{Value}")] - public sealed class StringToken : IToken - { - /// - /// Gets the value. - /// - /// The value. - public string Value { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The value. - public StringToken(string value) - { - if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); - Value = value; - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - private bool Equals(StringToken other) - { - return string.Equals(Value, other.Value); - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - bool IEquatable.Equals(IToken other) - { - var otherAsObject = (object)other; - return Equals(otherAsObject); - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with the current . - /// if the specified is equal to this instance; otherwise, . - public override bool Equals(object obj) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj is StringToken && Equals((StringToken) obj); - } - - /// - /// Returns a hash code for this instance. - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - public override int GetHashCode() - { - return Value.GetHashCode(); - } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return Value; - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/TokenCount.cs b/BotSharp.Algorithm/Bayesian/TokenCount.cs deleted file mode 100644 index bc72d351..00000000 --- a/BotSharp.Algorithm/Bayesian/TokenCount.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Struct TokenCount - /// - public struct TokenCount - { - /// - /// The token - /// - public readonly IToken Token; - - /// - /// The number of occurrences - /// - public readonly long Count; - - /// - /// Initializes a new instance of the struct. - /// - /// The token. - /// The count. - /// token - /// count;Count must be positive or zero. - public TokenCount(IToken token, long count) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (count < 0) throw new ArgumentOutOfRangeException("count", count, "Count must be positive or zero."); - - Token = token; - Count = count; - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/TokenInformation.cs b/BotSharp.Algorithm/Bayesian/TokenInformation.cs deleted file mode 100644 index 44fbad95..00000000 --- a/BotSharp.Algorithm/Bayesian/TokenInformation.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Struct TokenInformation - /// - public struct TokenInformation - where TToken: IToken - { - /// - /// The token - /// - public readonly TToken Token; - - /// - /// The count in the class - /// - public long Count; - - /// - /// The occurrence percentage of the token in the class. - /// - public double Percentage; - - /// - /// Initializes a new instance of the struct. - /// - /// The token. - /// The count. - /// The percentage. - /// token - /// - /// count;Count must be positive or zero - /// or - /// percentage;Percentage must be positive or zero - /// - public TokenInformation(TToken token, long count, double percentage) - { - if (ReferenceEquals(token, null)) throw new ArgumentNullException("token"); - if (count < 0) throw new ArgumentOutOfRangeException("count", count, "Count must be positive or zero"); - if (percentage < 0) throw new ArgumentOutOfRangeException("percentage", percentage, "Percentage must be positive or zero"); - - Token = token; - Count = count; - Percentage = percentage; - } - } -} diff --git a/BotSharp.Algorithm/Bayesian/TrainingSet.cs b/BotSharp.Algorithm/Bayesian/TrainingSet.cs deleted file mode 100644 index 8d9984fc..00000000 --- a/BotSharp.Algorithm/Bayesian/TrainingSet.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - - -namespace BotSharp.Algorithm.Bayesian -{ - /// - /// Class TrainingSet. This class cannot be inherited. - /// - public sealed class TrainingSet : ITrainingSet - { - /// - /// The data sets - /// - private readonly ConcurrentDictionary _dataSets = new ConcurrentDictionary(); - - /// - /// Initializes a new instance of the class. - /// - public TrainingSet() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The data sets. - public TrainingSet(IEnumerable dataSets) - { - Add(dataSets); - } - - /// - /// Initializes a new instance of the class. - /// - /// The data set. - /// The additional data sets. - public TrainingSet(IDataSet dataSet, params IDataSet[] additionalDataSets) - { - Add(dataSet, additionalDataSets); - } - - /// - /// Gets the with the specified class. - /// - /// The class. - /// IDataSet<IClass, IToken>. - /// No data set was registered for the given class;class - public IDataSet this[IClass @class] - { - get - { - IDataSet set; - if (_dataSets.TryGetValue(@class, out set)) return set; - throw new ArgumentException("No data set was registered for the given class", "class"); - } - } - - /// - /// Adds the specified data set. - /// - /// The data set. - /// The additional data sets. - /// dataSet - /// A data set for a given class was already registered. - public void Add(IDataSet dataSet, params IDataSet[] additionalDataSets) - { - if (ReferenceEquals(dataSet, null)) throw new ArgumentNullException("dataSet"); - - try - { - AddInternal(dataSet); - } - catch (ArgumentException e) - { - throw new ArgumentException("A data set for a given class was already registered.", e); - } - - // may throw, that's anticipated - Add(additionalDataSets); - } - - /// - /// Adds the specified data sets. - /// - /// The data sets. - /// dataSets - /// A data set for a given class was already registered. - public void Add(IEnumerable dataSets) - { - if (ReferenceEquals(dataSets, null)) throw new ArgumentNullException("dataSets"); - - try - { - foreach (var dataSet in dataSets) - { - AddInternal(dataSet); - } - } - catch (ArgumentException e) - { - throw new ArgumentException("A data set for a given class was already registered.", e); - } - } - - - /// - /// Adds the data set internally. - /// - /// The data set. - /// Data set for the given class was already registered. - private void AddInternal(IDataSet dataSet) - { - if (!_dataSets.TryAdd(dataSet.Class, dataSet)) - { - throw new ArgumentException("Data set for the given class was already registered."); - } - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A that can be used to iterate through the collection. - public IEnumerator GetEnumerator() - { - return _dataSets.Select(dataSet => dataSet.Value).GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/BotSharp.Core/Engines/BotSharp/BotSharpSVMClassifier.cs b/BotSharp.Core/Engines/BotSharp/BotSharpSVMClassifier.cs index d0dfcd34..b7dcc0ac 100644 --- a/BotSharp.Core/Engines/BotSharp/BotSharpSVMClassifier.cs +++ b/BotSharp.Core/Engines/BotSharp/BotSharpSVMClassifier.cs @@ -1,4 +1,5 @@ -using BotSharp.Core.Abstractions; +using BotSharp.Algorithm.Bayes; +using BotSharp.Core.Abstractions; using BotSharp.Core.Agents; using BotSharp.NLP.Classify; using DotNetToolkit; @@ -29,10 +30,10 @@ namespace BotSharp.Core.Engines.BotSharp string predictFileName = Path.Combine(Settings.TempDir, "svm-predict-tempfile.txt"); File.WriteAllText(predictFileName, doc.Sentences[0].Text); - NLP.Classify.SVMClassifier svmClassifier = new NLP.Classify.SVMClassifier(); + var svmClassifier = new NLP.Classify.SVMClassifier(); Args args = new Args(); args.ModelFile = Path.Combine(Configuration.GetValue("BotSharpSVMClassifier:wordvec"), "wordvec_enu.bin"); - LabeledFeatureSet featureSet = svmClassifier.FeatureSetsGenerator(new VectorGenerator(args).SingleSentence2Vec(doc.Sentences[0].Text), ""); + var featureSet = svmClassifier.FeatureSetsGenerator(new VectorGenerator(args).SingleSentence2Vec(doc.Sentences[0].Text), ""); /* // var client = new RestClient("http://10.2.21.200:5005"); diff --git a/BotSharp.NLP.UnitTest/NaiveBayesClassifierTest.cs b/BotSharp.NLP.UnitTest/NaiveBayesClassifierTest.cs index 44979a00..8c16a8e1 100644 --- a/BotSharp.NLP.UnitTest/NaiveBayesClassifierTest.cs +++ b/BotSharp.NLP.UnitTest/NaiveBayesClassifierTest.cs @@ -69,9 +69,10 @@ namespace BotSharp.NLP.UnitTest corpus.ForEach(x => x.Words = tokenizer.Tokenize(x.Text)); - // classifier.Train(corpus); - // string text = "Bridget"; - // classifier.Classify(new Sentence { Text = text, Words = tokenizer.Tokenize(text) }); + classifier.Train(corpus); + string text = "Bridget"; + classifier.Classify(new Sentence { Text = text, Words = tokenizer.Tokenize(text) }); + corpus.Shuffle(); var trainingData = corpus.Skip(2000).ToList(); classifier.Train(trainingData); diff --git a/BotSharp.NLP/Classify/ClassifierFactory.cs b/BotSharp.NLP/Classify/ClassifierFactory.cs index 24df05e2..4cf2247a 100644 --- a/BotSharp.NLP/Classify/ClassifierFactory.cs +++ b/BotSharp.NLP/Classify/ClassifierFactory.cs @@ -1,4 +1,5 @@ -using BotSharp.NLP.Corpus; +using BotSharp.Algorithm.Bayes; +using BotSharp.NLP.Corpus; using BotSharp.NLP.Tokenize; using System; using System.Collections.Generic; diff --git a/BotSharp.NLP/Classify/IClassifier.cs b/BotSharp.NLP/Classify/IClassifier.cs index fc72882e..833a2732 100644 --- a/BotSharp.NLP/Classify/IClassifier.cs +++ b/BotSharp.NLP/Classify/IClassifier.cs @@ -1,4 +1,5 @@ -using System; +using BotSharp.Algorithm.Bayes; +using System; using System.Collections.Generic; using System.Text; diff --git a/BotSharp.NLP/Classify/NaiveBayesClassifier.cs b/BotSharp.NLP/Classify/NaiveBayesClassifier.cs index a565cb99..afbb51d1 100644 --- a/BotSharp.NLP/Classify/NaiveBayesClassifier.cs +++ b/BotSharp.NLP/Classify/NaiveBayesClassifier.cs @@ -17,6 +17,7 @@ */ using BotSharp.Algorithm; +using BotSharp.Algorithm.Bayes; using BotSharp.Algorithm.Extensions; using BotSharp.Algorithm.Formulas; using System; @@ -33,8 +34,6 @@ namespace BotSharp.NLP.Classify /// This technique works well for topic classification; /// say we have a set of academic papers, and we want to classify them into different topics (computer science, biology, mathematics). /// Naive Bayes is best for Less training data - /// P(X, Y) = P(Y|X)P(X) = P(X|Y)P(Y) => P(Y|X) = P(Y)P(X|Y)/P(X) - /// Y is label, X is features. /// public class NaiveBayesClassifier : IClassifier { @@ -95,19 +94,14 @@ namespace BotSharp.NLP.Classify public List> Classify(LabeledFeatureSet featureSet, ClassifyOptions options) { - var estimator = new Lidstone(); + var nb = new NaiveBayes(); + nb.LabelDist = labelDist; + nb.FeatureDist = featureDist; labelDist.ForEach(lf => { // prior probability - lf.Prob = estimator.Log2Prob(labelDist, lf.Value); - - // post probability P(X1,...,Xn|Y) = Sum(P(X1|Y) +...+ P(Xn|Y) - featureSet.Features.ForEach(f => - { - var fv = featureDist.Find(x => x.Label == lf.Value && x.FeatureName == f.Name).FeatureValues; - lf.Prob += estimator.Log2Prob(fv, f.Value); - }); + lf.Prob = nb.PosteriorProb(lf.Value, featureSet); }); // add log @@ -129,54 +123,4 @@ namespace BotSharp.NLP.Classify return labelDist.Select(x => new Tuple(x.Value, x.Prob)).ToList(); } } - - public class LabeledFeatureSet - { - public List Features { get; set; } - public string Label { get; set; } - public LabeledFeatureSet() - { - this.Features = new List(); - } - } - - public class Feature - { - public string Name { get; set; } - public string Value { get; set; } - - public Feature(string name, string value) - { - Name = name; - Value = value; - } - } - - public class FeatureProbabilityDistribution - { - public string Label { get; set; } - - public string FeatureName { get; set; } - - public int Count { get; set; } - - public override string ToString() - { - return $"{Label} {FeatureName} {Count}"; - } - } - - public class FeatureFrequencyDistribution - { - public string Label { get; set; } - - public string FeatureName { get; set; } - - public List FeatureValues { get; set; } - - public override string ToString() - { - return $"{Label} {FeatureName} {FeatureValues.Count}"; - } - } } diff --git a/BotSharp.NLP/Classify/SVMClassifier.cs b/BotSharp.NLP/Classify/SVMClassifier.cs index 6ab2c8ff..26bd1dd4 100644 --- a/BotSharp.NLP/Classify/SVMClassifier.cs +++ b/BotSharp.NLP/Classify/SVMClassifier.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using BotSharp.Algorithm.Bayes; using SVM.BotSharp.MachineLearning; using Txt2Vec;