Shuffle and Reduce utility functions
This commit is contained in:
parent
3c2755f579
commit
5c3d3fea64
23
BotSharp.Algorithm/Extensions/Reduce.cs
Normal file
23
BotSharp.Algorithm/Extensions/Reduce.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BotSharp.Algorithm.Extensions
|
||||||
|
{
|
||||||
|
public static partial class IListExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// equivalent reduce function in Python
|
||||||
|
/// https://docs.python.org/3/library/functools.html?highlight=reduce#functools.reduce
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TAccumulate"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="func"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TAccumulate Reduce<TAccumulate>(this IList<TAccumulate> source, Func<TAccumulate, TAccumulate, TAccumulate> func)
|
||||||
|
{
|
||||||
|
return source.Skip(1).Aggregate(source[0], func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
BotSharp.Algorithm/Extensions/Shuffle.cs
Normal file
44
BotSharp.Algorithm/Extensions/Shuffle.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BotSharp.Algorithm.Extensions
|
||||||
|
{
|
||||||
|
public static partial class IListExtensions
|
||||||
|
{
|
||||||
|
public static void Shuffle2<T>(this IList<T> list)
|
||||||
|
{
|
||||||
|
var provider = new RNGCryptoServiceProvider();
|
||||||
|
int count = list.Count;
|
||||||
|
while (count > 1)
|
||||||
|
{
|
||||||
|
var box = new byte[1];
|
||||||
|
|
||||||
|
do provider.GetBytes(box);
|
||||||
|
while (!(box[0] < count * (Byte.MaxValue / count)));
|
||||||
|
|
||||||
|
var k = (box[0] % count);
|
||||||
|
count--;
|
||||||
|
|
||||||
|
var value = list[k];
|
||||||
|
list[k] = list[count];
|
||||||
|
list[count] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Shuffle<T>(this IList<T> list)
|
||||||
|
{
|
||||||
|
var rng = new Random();
|
||||||
|
var count = list.Count;
|
||||||
|
while (count > 1)
|
||||||
|
{
|
||||||
|
count--;
|
||||||
|
var k = rng.Next(count + 1);
|
||||||
|
var value = list[k];
|
||||||
|
list[k] = list[count];
|
||||||
|
list[count] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,8 @@ namespace BotSharp.Algorithm.Formulas
|
||||||
public double Prob(List<Probability> dist, string sample)
|
public double Prob(List<Probability> dist, string sample)
|
||||||
{
|
{
|
||||||
// observation x = (x1, ..., xd)
|
// observation x = (x1, ..., xd)
|
||||||
int x = dist.Find(f => f.Value == sample).Freq;
|
var p = dist.Find(f => f.Value == sample);
|
||||||
|
int x = p == null ? 0 : p.Freq;
|
||||||
|
|
||||||
// N trials
|
// N trials
|
||||||
int _N = dist.Sum(f => f.Freq);
|
int _N = dist.Sum(f => f.Freq);
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ If you feel that this project is helpful to you, please Star on the project, we
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BotSharp.NLP" Version="0.3.0" />
|
||||||
<PackageReference Include="Colorful.Console" Version="1.2.9" />
|
<PackageReference Include="Colorful.Console" Version="1.2.9" />
|
||||||
<PackageReference Include="DotNetToolkit" Version="1.6.0" />
|
<PackageReference Include="DotNetToolkit" Version="1.6.0" />
|
||||||
<PackageReference Include="EntityFrameworkCore.BootKit" Version="1.9.1" />
|
<PackageReference Include="EntityFrameworkCore.BootKit" Version="1.9.1" />
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using BotSharp.Algorithm.Extensions;
|
||||||
|
|
||||||
namespace BotSharp.NLP.UnitTest
|
namespace BotSharp.NLP.UnitTest
|
||||||
{
|
{
|
||||||
|
|
@ -31,10 +33,25 @@ namespace BotSharp.NLP.UnitTest
|
||||||
|
|
||||||
corpus.ForEach(x => x.Words = tokenizer.Tokenize(x.Text));
|
corpus.ForEach(x => x.Words = tokenizer.Tokenize(x.Text));
|
||||||
|
|
||||||
classifier.Train(corpus);
|
// 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);
|
||||||
|
|
||||||
string text = "Aamir";
|
var testData = corpus.Take(2000).ToList();
|
||||||
classifier.Classify(new Sentence { Text = text, Words = tokenizer.Tokenize(text) });
|
int correct = 0;
|
||||||
|
testData.ForEach(td =>
|
||||||
|
{
|
||||||
|
var classes = classifier.Classify(td);
|
||||||
|
if(td.Label == classes[0].Item1)
|
||||||
|
{
|
||||||
|
correct++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var accuracy = (float)correct / testData.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Sentence> GetLabeledCorpus(ClassifyOptions options)
|
private List<Sentence> GetLabeledCorpus(ClassifyOptions options)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ Naive Bayes Classifier</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BotSharp.Algorithm" Version="0.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,16 @@ namespace BotSharp.NLP.Classify
|
||||||
_classifier = new IClassify();
|
_classifier = new IClassify();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Classify(Sentence sentence)
|
public List<Tuple<string, double>> Classify(Sentence sentence)
|
||||||
{
|
{
|
||||||
_classifier.Classify(new LabeledFeatureSet
|
var classes = _classifier.Classify(new LabeledFeatureSet
|
||||||
{
|
{
|
||||||
Features = GetFeatures(sentence.Words)
|
Features = GetFeatures(sentence.Words)
|
||||||
}, new ClassifyOptions
|
}, new ClassifyOptions
|
||||||
{
|
{
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return classes.OrderByDescending(x => x.Item2).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Train(List<Sentence> sentences)
|
public void Train(List<Sentence> sentences)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ namespace BotSharp.NLP.Classify
|
||||||
{
|
{
|
||||||
void Train(List<LabeledFeatureSet> featureSets, ClassifyOptions options);
|
void Train(List<LabeledFeatureSet> featureSets, ClassifyOptions options);
|
||||||
|
|
||||||
void Classify(LabeledFeatureSet featureSet, ClassifyOptions options);
|
List<Tuple<string, double>> Classify(LabeledFeatureSet featureSet, ClassifyOptions options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using BotSharp.Algorithm;
|
using BotSharp.Algorithm;
|
||||||
|
using BotSharp.Algorithm.Extensions;
|
||||||
using BotSharp.Algorithm.Formulas;
|
using BotSharp.Algorithm.Formulas;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -32,6 +33,8 @@ namespace BotSharp.NLP.Classify
|
||||||
/// This technique works well for topic classification;
|
/// 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).
|
/// 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
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NaiveBayesClassifier : IClassifier
|
public class NaiveBayesClassifier : IClassifier
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +52,9 @@ namespace BotSharp.NLP.Classify
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var fNames = featureSets[0].Features.Select(x => x.Name).ToList();
|
var fNames = featureSets[0].Features.Select(x => x.Name)
|
||||||
|
.OrderBy(x => x)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// combine all features.
|
// combine all features.
|
||||||
var allFeatureValues = new List<Feature>();
|
var allFeatureValues = new List<Feature>();
|
||||||
|
|
@ -88,25 +93,40 @@ namespace BotSharp.NLP.Classify
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Classify(LabeledFeatureSet featureSet, ClassifyOptions options)
|
public List<Tuple<string, double>> Classify(LabeledFeatureSet featureSet, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
var estimator = new Lidstone();
|
var estimator = new Lidstone();
|
||||||
|
|
||||||
labelDist.ForEach(lf =>
|
labelDist.ForEach(lf =>
|
||||||
{
|
{
|
||||||
|
// prior probability
|
||||||
lf.Prob = estimator.Log2Prob(labelDist, lf.Value);
|
lf.Prob = estimator.Log2Prob(labelDist, lf.Value);
|
||||||
});
|
|
||||||
|
|
||||||
featureDist.ForEach(fd =>
|
// post probability P(X1,...,Xn|Y) = Sum(P(X1|Y) +...+ P(Xn|Y)
|
||||||
{
|
featureSet.Features.ForEach(f =>
|
||||||
fd.FeatureValues.ForEach(fv =>
|
|
||||||
{
|
{
|
||||||
fv.Prob = estimator.Log2Prob(fd.FeatureValues, fv.Value);
|
var fv = featureDist.Find(x => x.Label == lf.Value && x.FeatureName == f.Name).FeatureValues;
|
||||||
|
lf.Prob += estimator.Log2Prob(fv, f.Value);
|
||||||
var p = labelDist.Find(l => l.Value == fd.Label);
|
|
||||||
p.Prob += fv.Prob;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add log
|
||||||
|
double[] logs = labelDist.Select(x => x.Prob).ToArray();
|
||||||
|
|
||||||
|
var sumLogs = logs.Reduce((log1, next) =>
|
||||||
|
{
|
||||||
|
double min = log1;
|
||||||
|
if (next < log1)
|
||||||
|
{
|
||||||
|
min = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min + Math.Log(Math.Pow(2, log1 - min) + Math.Pow(2, next - min), 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
labelDist.ForEach(d => d.Prob -= sumLogs);
|
||||||
|
|
||||||
|
return labelDist.Select(x => new Tuple<string, double>(x.Value, x.Prob)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,9 @@ namespace BotSharp.NLP.Classify
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SVMClassifier : IClassifier
|
public class SVMClassifier : IClassifier
|
||||||
{
|
{
|
||||||
public void Classify(LabeledFeatureSet featureSet, ClassifyOptions options)
|
public List<Tuple<string, double>> Classify(LabeledFeatureSet featureSet, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double[][] Predict(LabeledFeatureSet featureSet, ClassifyOptions options)
|
public double[][] Predict(LabeledFeatureSet featureSet, ClassifyOptions options)
|
||||||
|
|
|
||||||
39
BotSharp.NLP/Corpus/FasttextDataReader.cs
Normal file
39
BotSharp.NLP/Corpus/FasttextDataReader.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace BotSharp.NLP.Corpus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fasttext labeled data reader
|
||||||
|
/// </summary>
|
||||||
|
public class FasttextDataReader
|
||||||
|
{
|
||||||
|
public List<Sentence> Read(ReaderOptions options)
|
||||||
|
{
|
||||||
|
var sentences = new List<Sentence>();
|
||||||
|
using (StreamReader reader = new StreamReader(Path.Combine(options.DataDir, options.FileName)))
|
||||||
|
{
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
string line = reader.ReadLine();
|
||||||
|
if (!String.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
var ms = Regex.Matches(line, @"__label__\w+\s").Cast<Match>().ToList();
|
||||||
|
|
||||||
|
sentences.Add(new Sentence
|
||||||
|
{
|
||||||
|
// Label = lable,
|
||||||
|
Text = line
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sentences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue