Intergate NBClassifier into NLP pipeline.
This commit is contained in:
parent
347cd5cd9c
commit
30f0eea87c
|
|
@ -10,5 +10,7 @@ namespace BotSharp.Algorithm.Bayes
|
||||||
public List<Probability> LabelDist { get; set; }
|
public List<Probability> LabelDist { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, double> CondProbDictionary { get; set; }
|
public Dictionary<string, double> CondProbDictionary { get; set; }
|
||||||
|
|
||||||
|
public List<String> Values { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,22 @@ namespace BotSharp.Core.Engines.BotSharp
|
||||||
public async Task<bool> Train(Agent agent, NlpDoc doc, PipeModel meta)
|
public async Task<bool> Train(Agent agent, NlpDoc doc, PipeModel meta)
|
||||||
{
|
{
|
||||||
meta.Model = "classification-nb.model";
|
meta.Model = "classification-nb.model";
|
||||||
|
|
||||||
string modelFileName = Path.Combine(Settings.ModelDir, meta.Model);
|
string modelFileName = Path.Combine(Settings.ModelDir, meta.Model);
|
||||||
|
|
||||||
var encoder = new OneHotEncoder();
|
var options = new ClassifyOptions
|
||||||
encoder.Sentences = doc.Sentences.Select(x => new NLP.Sentence
|
{
|
||||||
|
ModelFilePath = modelFileName
|
||||||
|
};
|
||||||
|
var classifier = new ClassifierFactory<NaiveBayesClassifier, SentenceFeatureExtractor>(options, SupportedLanguage.English);
|
||||||
|
|
||||||
|
var sentences = doc.Sentences.Select(x => new Sentence
|
||||||
{
|
{
|
||||||
Label = x.Intent.Label,
|
Label = x.Intent.Label,
|
||||||
Text = x.Text,
|
Text = x.Text,
|
||||||
Words = x.Tokens
|
Words = x.Tokens
|
||||||
}).ToList();
|
}).ToList();
|
||||||
encoder.EncodeAll();
|
|
||||||
|
|
||||||
var options = new ClassifyOptions
|
classifier.Train(sentences);
|
||||||
{
|
|
||||||
TrainingCorpusDir = Path.Combine(Configuration.GetValue<String>("MachineLearning:dataDir"), "Text Classification", "cooking.stackexchange")
|
|
||||||
};
|
|
||||||
var classifier = new ClassifierFactory<NaiveBayesClassifier, SentenceFeatureExtractor>(options, SupportedLanguage.English);
|
|
||||||
|
|
||||||
classifier.Train(encoder.Sentences);
|
|
||||||
|
|
||||||
Console.WriteLine($"Saved model to {modelFileName}");
|
Console.WriteLine($"Saved model to {modelFileName}");
|
||||||
meta.Meta = new JObject();
|
meta.Meta = new JObject();
|
||||||
|
|
@ -51,6 +48,28 @@ namespace BotSharp.Core.Engines.BotSharp
|
||||||
|
|
||||||
public async Task<bool> Predict(Agent agent, NlpDoc doc, PipeModel meta)
|
public async Task<bool> Predict(Agent agent, NlpDoc doc, PipeModel meta)
|
||||||
{
|
{
|
||||||
|
var options = new ClassifyOptions
|
||||||
|
{
|
||||||
|
ModelFilePath = Path.Combine(Settings.ModelDir, meta.Model)
|
||||||
|
};
|
||||||
|
var classifier = new ClassifierFactory<NaiveBayesClassifier, SentenceFeatureExtractor>(options, SupportedLanguage.English);
|
||||||
|
|
||||||
|
var sentence = doc.Sentences.Select(s => new Sentence
|
||||||
|
{
|
||||||
|
Text = s.Text,
|
||||||
|
Words = s.Tokens
|
||||||
|
}).First();
|
||||||
|
|
||||||
|
|
||||||
|
var result = classifier.Classify(sentence);
|
||||||
|
|
||||||
|
doc.Sentences[0].Intent = new TextClassificationResult
|
||||||
|
{
|
||||||
|
Classifier = "BotSharpNBayesClassifier",
|
||||||
|
Label = result.First().Item1,
|
||||||
|
Confidence = (decimal)result.First().Item2
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,17 +36,14 @@ namespace BotSharp.NLP.UnitTest
|
||||||
|
|
||||||
sentences.Shuffle();
|
sentences.Shuffle();
|
||||||
|
|
||||||
var encoder = new OneHotEncoder();
|
|
||||||
encoder.Sentences = sentences;
|
|
||||||
encoder.EncodeAll();
|
|
||||||
|
|
||||||
var options = new ClassifyOptions
|
var options = new ClassifyOptions
|
||||||
{
|
{
|
||||||
|
ModelFilePath = Path.Combine(Configuration.GetValue<String>("MachineLearning:dataDir"), "Text Classification", "cooking.stackexchange", "nb.model"),
|
||||||
TrainingCorpusDir = Path.Combine(Configuration.GetValue<String>("MachineLearning:dataDir"), "Text Classification", "cooking.stackexchange")
|
TrainingCorpusDir = Path.Combine(Configuration.GetValue<String>("MachineLearning:dataDir"), "Text Classification", "cooking.stackexchange")
|
||||||
};
|
};
|
||||||
var classifier = new ClassifierFactory<NaiveBayesClassifier, SentenceFeatureExtractor>(options, SupportedLanguage.English);
|
var classifier = new ClassifierFactory<NaiveBayesClassifier, SentenceFeatureExtractor>(options, SupportedLanguage.English);
|
||||||
|
|
||||||
var dataset = sentences.Split(0.7M);
|
var dataset = sentences.Split(1M);
|
||||||
classifier.Train(dataset.Item1);
|
classifier.Train(dataset.Item1);
|
||||||
|
|
||||||
int correct = 0;
|
int correct = 0;
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,20 @@ namespace BotSharp.NLP.Classify
|
||||||
|
|
||||||
public void Train(List<Sentence> sentences)
|
public void Train(List<Sentence> sentences)
|
||||||
{
|
{
|
||||||
var vectors = new List<Tuple<string, double[]>>();
|
_classifier.Train(sentences, _options);
|
||||||
|
_classifier.SaveModel(_options);
|
||||||
var sents = sentences.Select(x => new Tuple<string, double[]>(x.Label, x.Vector)).ToList();
|
|
||||||
|
|
||||||
_classifier.Train(sents, new double[] { 0, 1 }, _options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tuple<string, double>> Classify(Sentence sentence)
|
public List<Tuple<string, double>> Classify(Sentence sentence)
|
||||||
{
|
{
|
||||||
var options = new ClassifyOptions
|
var options = new ClassifyOptions
|
||||||
{
|
{
|
||||||
|
ModelFilePath = _options.ModelFilePath
|
||||||
};
|
};
|
||||||
|
|
||||||
var classes = _classifier.Classify(sentence.Vector, options);
|
_classifier.LoadModel(options);
|
||||||
|
|
||||||
|
var classes = _classifier.Classify(sentence, options);
|
||||||
|
|
||||||
return classes.OrderByDescending(x => x.Item2).ToList();
|
return classes.OrderByDescending(x => x.Item2).ToList();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,20 @@ namespace BotSharp.NLP.Classify
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Training by feature vector
|
/// Training by feature vector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="featureSets"></param>
|
/// <param name="sentences"></param>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
void Train(List<Tuple<string, double[]>> featureSets, double[] values, ClassifyOptions options);
|
void Train(List<Sentence> sentences, ClassifyOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predict by feature vector
|
/// Predict by feature vector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="features"></param>
|
/// <param name="sentence"></param>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
List<Tuple<string, double>> Classify(double[] features, ClassifyOptions options);
|
List<Tuple<string, double>> Classify(Sentence sentence, ClassifyOptions options);
|
||||||
|
|
||||||
|
String SaveModel(ClassifyOptions options);
|
||||||
|
|
||||||
|
Object LoadModel(ClassifyOptions options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ using BotSharp.Algorithm.Estimators;
|
||||||
using BotSharp.Algorithm.Extensions;
|
using BotSharp.Algorithm.Extensions;
|
||||||
using BotSharp.Algorithm.Features;
|
using BotSharp.Algorithm.Features;
|
||||||
using BotSharp.Algorithm.Statistics;
|
using BotSharp.Algorithm.Statistics;
|
||||||
|
using BotSharp.NLP.Txt2Vec;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -46,14 +48,24 @@ namespace BotSharp.NLP.Classify
|
||||||
|
|
||||||
private Dictionary<string, double> condProbDictionary = new Dictionary<string, double>();
|
private Dictionary<string, double> condProbDictionary = new Dictionary<string, double>();
|
||||||
|
|
||||||
public void Train(List<Tuple<string, double[]>> featureSets, double[] values, ClassifyOptions options)
|
private List<string> words;
|
||||||
|
private double[] features = new double[] { 0, 1 };
|
||||||
|
|
||||||
|
public void Train(List<Sentence> sentences, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
|
var encoder = new OneHotEncoder();
|
||||||
|
encoder.Sentences = sentences;
|
||||||
|
words = encoder.EncodeAll();
|
||||||
|
|
||||||
|
var featureSets = sentences.Select(x => new Tuple<string, double[]>(x.Label, x.Vector)).ToList();
|
||||||
|
|
||||||
labelDist = featureSets.GroupBy(x => x.Item1)
|
labelDist = featureSets.GroupBy(x => x.Item1)
|
||||||
.Select(x => new Probability
|
.Select(x => new Probability
|
||||||
{
|
{
|
||||||
Value = x.Key,
|
Value = x.Key,
|
||||||
Freq = x.Count()
|
Freq = x.Count()
|
||||||
})
|
})
|
||||||
|
.OrderBy(x => x.Value)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
nb.LabelDist = labelDist;
|
nb.LabelDist = labelDist;
|
||||||
|
|
@ -70,30 +82,27 @@ namespace BotSharp.NLP.Classify
|
||||||
{
|
{
|
||||||
for (int x = 0; x < featureCount; x++)
|
for (int x = 0; x < featureCount; x++)
|
||||||
{
|
{
|
||||||
for (int v = 0; v < values.Length; v++)
|
for (int v = 0; v < features.Length; v++)
|
||||||
{
|
{
|
||||||
string key = $"{label.Value} f{x} {values[v]}";
|
string key = $"{label.Value} f{x} {features[v]}";
|
||||||
condProbDictionary[key] = nb.CalCondProb(x, label.Value, values[v]);
|
condProbDictionary[key] = nb.CalCondProb(x, label.Value, features[v]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// save the model
|
|
||||||
var model = new MultinomiaNaiveBayesModel
|
|
||||||
{
|
|
||||||
LabelDist = labelDist,
|
|
||||||
CondProbDictionary = condProbDictionary
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tuple<string, double>> Classify(double[] features, ClassifyOptions options)
|
public List<Tuple<string, double>> Classify(Sentence sentence, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
|
var encoder = new OneHotEncoder();
|
||||||
|
encoder.Words = words;
|
||||||
|
encoder.Encode(sentence);
|
||||||
|
|
||||||
var results = new List<Tuple<string, double>>();
|
var results = new List<Tuple<string, double>>();
|
||||||
|
|
||||||
// calculate prop
|
// calculate prop
|
||||||
labelDist.ForEach(lf =>
|
labelDist.ForEach(lf =>
|
||||||
{
|
{
|
||||||
var prob = nb.CalPosteriorProb(lf.Value, features, lf.Prob, condProbDictionary);
|
var prob = nb.CalPosteriorProb(lf.Value, sentence.Vector, lf.Prob, condProbDictionary);
|
||||||
results.Add(new Tuple<string, double>(lf.Value, prob));
|
results.Add(new Tuple<string, double>(lf.Value, prob));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -105,6 +114,47 @@ namespace BotSharp.NLP.Classify
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string SaveModel(ClassifyOptions options)
|
||||||
|
{
|
||||||
|
// save the model
|
||||||
|
var model = new MultinomiaNaiveBayesModel
|
||||||
|
{
|
||||||
|
LabelDist = labelDist,
|
||||||
|
CondProbDictionary = condProbDictionary,
|
||||||
|
Values = words
|
||||||
|
};
|
||||||
|
|
||||||
|
//save the file
|
||||||
|
using (var bw = new BinaryWriter(new FileStream(options.ModelFilePath, FileMode.Create)))
|
||||||
|
{
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model));
|
||||||
|
bw.Write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.ModelFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object LoadModel(ClassifyOptions options)
|
||||||
|
{
|
||||||
|
string json = String.Empty;
|
||||||
|
|
||||||
|
//read the file
|
||||||
|
using (var br = new BinaryReader(new FileStream(options.ModelFilePath, FileMode.Open)))
|
||||||
|
{
|
||||||
|
byte[] bytes = br.ReadBytes((int)br.BaseStream.Length);
|
||||||
|
|
||||||
|
json = Encoding.UTF8.GetString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = JsonConvert.DeserializeObject<MultinomiaNaiveBayesModel>(json);
|
||||||
|
|
||||||
|
labelDist = model.LabelDist;
|
||||||
|
condProbDictionary = model.CondProbDictionary;
|
||||||
|
words = model.Values;
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FeaturesWithLabel
|
public class FeaturesWithLabel
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ namespace BotSharp.NLP.Classify
|
||||||
return Prediction.PredictLabelsProbability(options.Model, scaled);
|
return Prediction.PredictLabelsProbability(options.Model, scaled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Train(List<Tuple<string, double[]>> featureSets, double[] values, ClassifyOptions options)
|
public void Train(List<Sentence> sentences, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
// SVMClassifierTrain(featureSets, options);
|
// SVMClassifierTrain(featureSets, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Tuple<string, double>> Classify(double[] features, ClassifyOptions options)
|
public List<Tuple<string, double>> Classify(Sentence sentence, ClassifyOptions options)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
@ -154,5 +154,15 @@ namespace BotSharp.NLP.Classify
|
||||||
|
|
||||||
return labeledFeatureSet;
|
return labeledFeatureSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string SaveModel(ClassifyOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
object IClassifier.LoadModel(ClassifyOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,17 @@ namespace BotSharp.NLP.Txt2Vec
|
||||||
{
|
{
|
||||||
public List<Sentence> Sentences { get; set; }
|
public List<Sentence> Sentences { get; set; }
|
||||||
|
|
||||||
private List<string> words;
|
public List<string> Words { get; set; }
|
||||||
|
|
||||||
public void Encode(Sentence sentence)
|
public void Encode(Sentence sentence)
|
||||||
{
|
{
|
||||||
InitDictionary();
|
InitDictionary();
|
||||||
|
|
||||||
var vector = words.Select(x => 0D).ToArray();
|
var vector = Words.Select(x => 0D).ToArray();
|
||||||
|
|
||||||
sentence.Words.ForEach(w =>
|
sentence.Words.ForEach(w =>
|
||||||
{
|
{
|
||||||
int index = words.IndexOf(w.Text.ToLower());
|
int index = Words.IndexOf(w.Text.ToLower());
|
||||||
if(index > 0)
|
if(index > 0)
|
||||||
{
|
{
|
||||||
vector[index] = 1;
|
vector[index] = 1;
|
||||||
|
|
@ -35,24 +35,29 @@ namespace BotSharp.NLP.Txt2Vec
|
||||||
sentence.Vector = vector;
|
sentence.Vector = vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EncodeAll()
|
public List<string> EncodeAll()
|
||||||
{
|
{
|
||||||
InitDictionary();
|
InitDictionary();
|
||||||
|
|
||||||
Sentences.ForEach(sent => Encode(sent));
|
Sentences.ForEach(sent => Encode(sent));
|
||||||
//Parallel.ForEach(Sentences, sent => Encode(sent));
|
//Parallel.ForEach(Sentences, sent => Encode(sent));
|
||||||
|
|
||||||
|
return Words;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitDictionary()
|
private List<string> InitDictionary()
|
||||||
{
|
{
|
||||||
if (words == null)
|
if (Words == null)
|
||||||
{
|
{
|
||||||
words = new List<string>();
|
Words = new List<string>();
|
||||||
Sentences.ForEach(x =>
|
Sentences.ForEach(x =>
|
||||||
{
|
{
|
||||||
words.AddRange(x.Words.Where(w => w.IsAlpha).Select(w => w.Text.ToLower()));
|
Words.AddRange(x.Words.Where(w => w.IsAlpha).Select(w => w.Text.ToLower()));
|
||||||
});
|
});
|
||||||
words = words.Distinct().OrderBy(x => x).ToList();
|
Words = Words.Distinct().OrderBy(x => x).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Words;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue