Add intent classifier

This commit is contained in:
Wenbo Cao 2023-08-31 17:00:31 -05:00
parent 890df8743a
commit 7762627f82
4 changed files with 121 additions and 12 deletions

View file

@ -46,6 +46,10 @@ public class ResponseTemplateService : IResponseTemplateService
// Find response template
var agentService = _services.GetRequiredService<IAgentService>();
var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
if (!Directory.Exists(dir))
{
return string.Empty;
}
var responses = Directory.GetFiles(dir)
.Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == message.IntentName)
.ToList();
@ -62,8 +66,15 @@ public class ResponseTemplateService : IResponseTemplateService
// Convert args and execute data to dictionary
var dict = new Dictionary<string, object>();
ExtractArgs(JsonSerializer.Deserialize<JsonDocument>(message.FunctionArgs), dict);
ExtractExecuteData(message.ExecutionData, dict);
if (!string.IsNullOrEmpty(message.FunctionArgs))
{
ExtractArgs(JsonSerializer.Deserialize<JsonDocument>(message.FunctionArgs), dict);
}
if (message.ExecutionData != null)
{
ExtractExecuteData(message.ExecutionData, dict);
}
var text = render.Render(template, dict);

View file

@ -16,6 +16,10 @@ using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Tensorflow.Keras;
using BotSharp.Abstraction.Knowledges.Settings;
using System.Numerics;
using Newtonsoft.Json;
using Tensorflow.Keras.Layers;
using BotSharp.Abstraction.Agents;
namespace BotSharp.Plugin.RoutingSpeeder.Providers;
@ -47,12 +51,14 @@ public class IntentClassifier
return;
}
var vector = _services.GetRequiredService<ITextEmbedding>();
var layers = new List<ILayer>
{
keras.layers.InputLayer((300), name: "Input"),
keras.layers.InputLayer((vector.Dimension), name: "Input"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(256, activation:"relu"),
keras.layers.Dense(_settings.LabelMappingDict.Count, activation: keras.activations.Softmax)
keras.layers.Dense(GetLabels().Length, activation: keras.activations.Softmax)
};
_model = keras.Sequential(layers);
@ -98,10 +104,13 @@ public class IntentClassifier
public string LoadWeights()
{
var weightsFile = Path.Combine(_settings.MODEL_DIR, $"intent-classifier.h5");
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
var weightsFile = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, $"intent-classifier.h5");
if (File.Exists(weightsFile))
{
_model.load_weights(weightsFile);
_isModelReady = true;
Console.WriteLine($"Successfully load the weights!");
}
else
@ -113,11 +122,11 @@ public class IntentClassifier
public (NDArray x, NDArray y) Vectorize(List<DialoguePredictionModel> items)
{
var vector = _services.GetRequiredService<ITextEmbedding>();
var x = np.zeros((items.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((items.Count, 1), dtype: np.float32);
var vector = _services.GetRequiredService<ITextEmbedding>();
for (int i = 0; i < items.Count; i++)
{
x[i] = vector.GetVector(TextClean(items[i].text));
@ -129,13 +138,65 @@ public class IntentClassifier
return (x, y);
}
public float[] GetTextEmbedding(string text)
public NDArray GetTextEmbedding(string text)
{
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var embedding = _services.GetServices<ITextEmbedding>()
.FirstOrDefault(x => x.GetType().FullName.EndsWith(knowledgeSettings.TextEmbedding));
return embedding.GetVector(text);
var x = np.zeros((1, embedding.Dimension), dtype: np.float32);
x[0] = embedding.GetVector(text);
return x;
}
public (NDArray, NDArray) PrepareLoadData()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
if (!Directory.Exists(rootDirectory))
{
throw new Exception($"No training data found! Please put training data in this path: {rootDirectory}");
}
var vector = _services.GetRequiredService<ITextEmbedding>();
var vectorList = new List<float[]>();
var labelList = new List<string>();
foreach (var filePath in GetFiles())
{
var texts = File.ReadAllLines(filePath, Encoding.UTF8).Select(x => TextClean(x)).ToList();
vectorList.AddRange(vector.GetVectors(texts));
string fileName = Path.GetFileNameWithoutExtension(filePath);
labelList.AddRange(Enumerable.Repeat(fileName, texts.Count).ToList());
}
var uniqueLabelList = labelList.Distinct().ToList();
var x = np.zeros((vectorList.Count, vector.Dimension), dtype: np.float32);
var y = np.zeros((vectorList.Count, 1), dtype: np.float32);
for (int i = 0; i < vectorList.Count; i++)
{
x[i] = vectorList[i];
y[i] = (float)uniqueLabelList.IndexOf(labelList[i]);
}
return (x, y);
}
public string[] GetFiles()
{
var agentService = _services.CreateScope().ServiceProvider.GetRequiredService<IAgentService>();
string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
return Directory.GetFiles(rootDirectory).OrderBy(x => x).ToArray();
}
public string[] GetLabels()
{
return GetFiles().Select(x => Path.GetFileNameWithoutExtension(x)).ToArray();
}
public string TextClean(string text)
@ -148,4 +209,37 @@ public class IntentClassifier
processedText = processedText.Replace(" ", " ").ToLower();
return processedText;
}
public string Predict(NDArray vector)
{
if (!_isModelReady)
{
InitClassifer();
}
var prob = _model.predict(vector);
var probLabel = tf.arg_max(prob, -1).numpy();
// var prediction = _settings.LabelMappingDict.First(x => x.Value == probLabel[0]).Key;
var prediction = GetLabels()[probLabel[0]];
// var prediction = GetLabels().Where((x, i) => i == probLabel[0]).First();
return prediction;
}
public void InitClassifer()
{
Reset();
Build();
LoadWeights();
}
public void Train()
{
var trainingParams = new TrainingParams();
Reset();
Build();
(var x, var y) = PrepareLoadData();
Fit(x, y, trainingParams);
}
}

View file

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using BotSharp.Plugin.RoutingSpeeder.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Plugin.RoutingSpeeder.Providers;
using System.Runtime.InteropServices;
namespace BotSharp.Plugin.RoutingSpeeder;
@ -27,8 +28,11 @@ public class RoutingConversationHook: ConversationHookBase
var intentClassifier = _services.GetRequiredService<IntentClassifier>();
var vector = intentClassifier.GetTextEmbedding(message.Content);
// intentClassifier.Train();
// Utilize local discriminative model to predict intent
message.IntentName = "greeting";
var predText = intentClassifier.Predict(vector);
message.IntentName = predText;
// Render by template
var templateService = _services.GetRequiredService<IResponseTemplateService>();

View file

@ -13,6 +13,6 @@ public class ClassifierSetting
{"other", 2f}
};
public string RAW_DATA_DIR { get; set; } = "";
public string MODEL_DIR { get; set; } = "";
public string RAW_DATA_DIR { get; set; } = "raw_data";
public string MODEL_DIR { get; set; } = "models";
}