From 0d017f1e863c7809190b9ea7fe1ccabefee6f557 Mon Sep 17 00:00:00 2001
From: Wenbo Cao <104199@smsassist.com>
Date: Thu, 31 Aug 2023 09:52:09 -0500
Subject: [PATCH 1/3] Add DialoguePrediction
---
.../BotSharp.Plugin.RoutingSpeeder.csproj | 6 +
.../Providers/DialogueClassifier.cs | 144 ++++++++++++++++++
.../Models/DialoguePredictionModel.cs | 13 ++
.../Providers/fastTextEmbeddingProvider.cs | 70 +++++++++
.../RoutingConversationHook.cs | 16 ++
.../RoutingSpeederPlugin.cs | 8 +
.../Settings/classifierSetting.cs | 21 +++
.../Settings/fastTextSetting.cs | 7 +
.../Settings/routerSpeedSettings.cs | 11 ++
.../Settings/trainingParams.cs | 13 ++
10 files changed, 309 insertions(+)
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
create mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
index 79a54b27..e0a8e7af 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
@@ -11,4 +11,10 @@
+
+
+
+
+
+
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs
new file mode 100644
index 00000000..788eb641
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs
@@ -0,0 +1,144 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using Tensorflow;
+using static Tensorflow.KerasApi;
+using Tensorflow.Keras.Engine;
+using Tensorflow.NumPy;
+using static Tensorflow.Binding;
+using Tensorflow.Keras.Callbacks;
+using System.Text.RegularExpressions;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Abstraction.MLTasks;
+using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
+using Microsoft.Extensions.DependencyInjection;
+using System.Linq;
+using Tensorflow.Keras;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers;
+
+public class DialogueClassifier
+{
+ private readonly IServiceProvider _services;
+ Model _model;
+ public Model model => _model;
+ private bool _isModelReady;
+ public bool isModelReady => _isModelReady;
+ private classifierSetting _settings;
+
+ public DialogueClassifier(IServiceProvider services, classifierSetting settings)
+ {
+ _services = services;
+ _settings = settings;
+ }
+
+ private void Reset()
+ {
+ keras.backend.clear_session();
+ _isModelReady = false;
+ }
+
+ private void Build()
+ {
+ if (_isModelReady)
+ {
+ return;
+ }
+
+ var layers = new List
+ {
+ keras.layers.InputLayer((300), name: "Input"),
+ keras.layers.Dense(256, activation:"relu"),
+ keras.layers.Dense(256, activation:"relu"),
+ keras.layers.Dense(_settings.labelMappingDict.Count, activation: keras.activations.Softmax)
+ };
+ _model = keras.Sequential(layers);
+
+#if DEBUG
+ Console.WriteLine();
+ _model.summary();
+#endif
+ _isModelReady = true;
+ }
+
+ private void Fit(NDArray x, NDArray y, TrainingParams trainingParams)
+ {
+ // release more memory
+ var vector = _services.GetRequiredService();
+ // vector.UnloadModel();
+
+ _model.compile(optimizer: keras.optimizers.Adam(trainingParams.LearningRate),
+ loss: keras.losses.SparseCategoricalCrossentropy(),
+ metrics: new[] { "accuracy" }
+ );
+
+ CallbackParams callback_parameters = new CallbackParams
+ {
+ Model = _model,
+ Epochs = trainingParams.Epochs,
+ Verbose = 1,
+ Steps = 10
+ };
+
+ ICallback earlyStop = new EarlyStopping(callback_parameters, "accuracy");
+
+ var callbacks = new List() { earlyStop };
+
+ var weights = LoadWeights();
+
+ _model.fit(x, y,
+ batch_size: trainingParams.BatchSize,
+ epochs: trainingParams.Epochs,
+ callbacks: callbacks,
+ // validation_split: 0.1f,
+ shuffle: true);
+
+ _model.save_weights(weights);
+
+ _isModelReady = true;
+ }
+
+ public string LoadWeights()
+ {
+ var weightsFile = Path.Combine(_settings.MODEL_DIR, $"wo-dialogue-classifier.h5");
+ if (File.Exists(weightsFile))
+ {
+ _model.load_weights(weightsFile);
+ Console.WriteLine($"Successfully load the weights!");
+ }
+ else
+ {
+ Console.WriteLine("No available weights.");
+ }
+ return weightsFile;
+ }
+
+ public (NDArray x, NDArray y) Vectorize(List items)
+ {
+ var x = np.zeros((items.Count, 300), dtype: np.float32);
+ var y = np.zeros((items.Count, 1), dtype: np.float32);
+
+ var vector = _services.GetRequiredService();
+
+ for (int i = 0; i < items.Count; i++)
+ {
+ x[i] = vector.GetVector(TextClean(items[i].text));
+ if (_settings.labelMappingDict.ContainsKey(items[i].label))
+ {
+ y[i] = _settings.labelMappingDict[items[i].label];
+ }
+ }
+ return (x, y);
+ }
+ public string TextClean(string text)
+ {
+ // Remove punctuation
+ // Remove digits
+ // To lowercase
+ var processedText = Regex.Replace(text, "[AB0-9]", " ");
+ processedText = string.Join("", processedText.Select(c => char.IsPunctuation(c) ? ' ' : c).ToList());
+ processedText = processedText.Replace(" ", " ").ToLower();
+ return processedText;
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs
new file mode 100644
index 00000000..4641b9cd
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
+
+public class DialoguePredictionModel
+{
+ public int Id { get; set; }
+ public string text { get; set; }
+ public string? label { get; set; }
+ public string? prediction { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
new file mode 100644
index 00000000..79925418
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime;
+using System.Text;
+using System.Text.RegularExpressions;
+using BotSharp.Abstraction.MLTasks;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using FastText.NetWrapper;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers;
+
+public class fastTextEmbeddingProvider : ITextEmbedding
+{
+ private FastTextWrapper _fastText;
+ private readonly fastTextSetting _settings;
+
+ public int Dimension
+ {
+ get
+ {
+ if (!_fastText.IsModelReady())
+ {
+ _fastText.LoadModel(_settings.ModelPath);
+ }
+ return _fastText.GetModelDimension();
+ }
+ }
+
+ public fastTextEmbeddingProvider(fastTextSetting settings)
+ {
+ _settings = settings;
+
+ }
+
+ public float[] GetVector(string text)
+ {
+ LoadModel();
+ return _fastText.GetSentenceVector(text);
+ }
+
+ public List GetVectors(List texts)
+ {
+ LoadModel();
+ var vectors = new List();
+ for (int i = 0; i < texts.Count; i++)
+ {
+ vectors.Add(GetVector(texts[i]));
+ }
+ return vectors;
+ }
+
+ private void LoadModel()
+ {
+ if (_fastText == null)
+ {
+ if (!File.Exists(_settings.ModelPath))
+ {
+ throw new FileNotFoundException($"Can't load pre-trained word vectors from {_settings.ModelPath}.\n Try to download from https://fasttext.cc/docs/en/english-vectors.html.");
+ }
+
+ _fastText = new FastTextWrapper();
+
+ if (!_fastText.IsModelReady())
+ {
+ _fastText.LoadModel(_settings.ModelPath);
+ }
+ }
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
index 50d74fe8..bc37e117 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
@@ -1,13 +1,29 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
+using BotSharp.Abstraction.MLTasks;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Linq;
using System.Threading.Tasks;
+using FastText.NetWrapper;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
namespace BotSharp.Plugin.RoutingSpeeder;
public class RoutingConversationHook: ConversationHookBase
{
+ private readonly IServiceProvider _services;
+ private routerSpeedSettings _settings;
+ public RoutingConversationHook(IServiceProvider service, routerSpeedSettings settings)
+ {
+ _services = service;
+ _settings = settings;
+ }
public override async Task BeforeCompletion(RoleDialogModel message)
{
+ var embedding = _services.GetServices()
+ .FirstOrDefault(x => x.GetType().FullName.EndsWith(_settings.TextEmbedding));
+
// Utilize local discriminative model to predict intent
message.Content = "response content";
message.StopCompletion = true;
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
index 28c856ba..bcb91335 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
@@ -1,5 +1,8 @@
using BotSharp.Abstraction.Conversations;
+using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Plugins;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Plugin.RoutingSpeeder.Providers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -9,6 +12,11 @@ public class RoutingSpeederPlugin : IBotSharpPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
+ var settings = new routerSpeedSettings();
+ config.Bind("routerSpeed", settings);
+ services.AddSingleton(x => settings);
+ services.AddSingleton(x => settings.fastText);
services.AddScoped();
+ services.AddSingleton();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
new file mode 100644
index 00000000..2fe13237
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class classifierSetting
+{
+
+ public Dictionary labelMappingDict { get; set; } = new Dictionary()
+ {
+ {"goodbye", 0f},
+ {"greeting", 1f},
+ {"other", 2f},
+ {"wo-followup", 3f},
+ {"wo-identifer", 4f},
+ {"wo-scheduler", 5}
+ };
+ public string RAW_DATA_DIR { get; set; } = "C:\\new_wenbocao\\one_brain\\WebStarter\\data\\raw_data";
+ public string MODEL_DIR { get; set; } = "C:\\new_wenbocao\\one_brain\\WebStarter\\data\\models";
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
new file mode 100644
index 00000000..e5554402
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
@@ -0,0 +1,7 @@
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class fastTextSetting
+{
+ public string ModelPath { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
new file mode 100644
index 00000000..300ec9e0
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class routerSpeedSettings
+{
+ public fastTextSetting fastText { get; set; }
+ public string TextEmbedding { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs
new file mode 100644
index 00000000..fcf15ce3
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class TrainingParams
+{
+ public int ClientId { get; set; }
+ public int Epochs { get; set; } = 10;
+ public int BatchSize { get; set; } = 16;
+ public float LearningRate { get; set; } = 1.0e-4f;
+}
From 890df8743a86ffd7016614f21923681751f3cfd5 Mon Sep 17 00:00:00 2001
From: Wenbo Cao <104199@smsassist.com>
Date: Thu, 31 Aug 2023 10:48:59 -0500
Subject: [PATCH 2/3] Add intent classifier in routing speeder.
---
.../Settings}/KnowledgeBaseSettings.cs | 2 +-
.../Plugins/Knowledges/KnowledgeBasePlugin.cs | 1 +
.../Knowledges/Services/KnowledgeService.cs | 2 +-
.../Controllers/KnowledgeController.cs | 2 +-
.../BotSharp.Plugin.RoutingSpeeder.csproj | 6 +-
...logueClassifier.cs => IntentClassifier.cs} | 31 ++++----
.../Models/TrainingParams.cs} | 2 +-
.../Providers/fastTextEmbeddingProvider.cs | 70 -------------------
.../RoutingConversationHook.cs | 11 +--
.../RoutingSpeederPlugin.cs | 10 +--
.../Settings/classifierSetting.cs | 15 ++--
.../Settings/fastTextSetting.cs | 7 --
.../Settings/routerSpeedSettings.cs | 4 +-
src/WebStarter/appsettings.json | 13 ++--
14 files changed, 53 insertions(+), 123 deletions(-)
rename src/Infrastructure/{BotSharp.Core/Plugins/Knowledges => BotSharp.Abstraction/Knowledges/Settings}/KnowledgeBaseSettings.cs (81%)
rename src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/{DialogueClassifier.cs => IntentClassifier.cs} (79%)
rename src/Plugins/BotSharp.Plugin.RoutingSpeeder/{Settings/trainingParams.cs => Providers/Models/TrainingParams.cs} (82%)
delete mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
delete mode 100644 src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
similarity index 81%
rename from src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs
rename to src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
index 0cb08a48..97f7f55c 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Core.Plugins.Knowledges;
+namespace BotSharp.Abstraction.Knowledges.Settings;
public class KnowledgeBaseSettings
{
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
index e0c74e83..8f862ca4 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.Extensions.Configuration;
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
index 9ed87e6a..3a627ec9 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
@@ -1,7 +1,7 @@
using BotSharp.Abstraction.Knowledges.Models;
+using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.VectorStorage;
-using System.Text.Json;
namespace BotSharp.Core.Plugins.Knowledges.Services;
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
index 3824f476..bea5597b 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http;
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig;
using BotSharp.Core.Plugins.Knowledges;
-
+using BotSharp.Abstraction.Knowledges.Settings;
namespace BotSharp.OpenAPI.Controllers;
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
index e0a8e7af..fb145e8d 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
@@ -8,13 +8,11 @@
-
+
-
-
-
+
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
similarity index 79%
rename from src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs
rename to src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
index 788eb641..ea74f54d 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/DialogueClassifier.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
@@ -15,19 +15,20 @@ using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Tensorflow.Keras;
+using BotSharp.Abstraction.Knowledges.Settings;
namespace BotSharp.Plugin.RoutingSpeeder.Providers;
-public class DialogueClassifier
+public class IntentClassifier
{
private readonly IServiceProvider _services;
Model _model;
public Model model => _model;
private bool _isModelReady;
public bool isModelReady => _isModelReady;
- private classifierSetting _settings;
+ private ClassifierSetting _settings;
- public DialogueClassifier(IServiceProvider services, classifierSetting settings)
+ public IntentClassifier(IServiceProvider services, ClassifierSetting settings)
{
_services = services;
_settings = settings;
@@ -51,7 +52,7 @@ public class DialogueClassifier
keras.layers.InputLayer((300), 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(_settings.LabelMappingDict.Count, activation: keras.activations.Softmax)
};
_model = keras.Sequential(layers);
@@ -64,10 +65,6 @@ public class DialogueClassifier
private void Fit(NDArray x, NDArray y, TrainingParams trainingParams)
{
- // release more memory
- var vector = _services.GetRequiredService();
- // vector.UnloadModel();
-
_model.compile(optimizer: keras.optimizers.Adam(trainingParams.LearningRate),
loss: keras.losses.SparseCategoricalCrossentropy(),
metrics: new[] { "accuracy" }
@@ -101,7 +98,7 @@ public class DialogueClassifier
public string LoadWeights()
{
- var weightsFile = Path.Combine(_settings.MODEL_DIR, $"wo-dialogue-classifier.h5");
+ var weightsFile = Path.Combine(_settings.MODEL_DIR, $"intent-classifier.h5");
if (File.Exists(weightsFile))
{
_model.load_weights(weightsFile);
@@ -116,7 +113,7 @@ public class DialogueClassifier
public (NDArray x, NDArray y) Vectorize(List items)
{
- var x = np.zeros((items.Count, 300), dtype: np.float32);
+ 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();
@@ -124,13 +121,23 @@ public class DialogueClassifier
for (int i = 0; i < items.Count; i++)
{
x[i] = vector.GetVector(TextClean(items[i].text));
- if (_settings.labelMappingDict.ContainsKey(items[i].label))
+ if (_settings.LabelMappingDict.ContainsKey(items[i].label))
{
- y[i] = _settings.labelMappingDict[items[i].label];
+ y[i] = _settings.LabelMappingDict[items[i].label];
}
}
return (x, y);
}
+
+ public float[] GetTextEmbedding(string text)
+ {
+ var knowledgeSettings = _services.GetRequiredService();
+ var embedding = _services.GetServices()
+ .FirstOrDefault(x => x.GetType().FullName.EndsWith(knowledgeSettings.TextEmbedding));
+
+ return embedding.GetVector(text);
+ }
+
public string TextClean(string text)
{
// Remove punctuation
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs
similarity index 82%
rename from src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs
rename to src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs
index fcf15ce3..f3c822ac 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/trainingParams.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
-namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
public class TrainingParams
{
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
deleted file mode 100644
index 79925418..00000000
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/fastTextEmbeddingProvider.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime;
-using System.Text;
-using System.Text.RegularExpressions;
-using BotSharp.Abstraction.MLTasks;
-using BotSharp.Plugin.RoutingSpeeder.Settings;
-using FastText.NetWrapper;
-
-namespace BotSharp.Plugin.RoutingSpeeder.Providers;
-
-public class fastTextEmbeddingProvider : ITextEmbedding
-{
- private FastTextWrapper _fastText;
- private readonly fastTextSetting _settings;
-
- public int Dimension
- {
- get
- {
- if (!_fastText.IsModelReady())
- {
- _fastText.LoadModel(_settings.ModelPath);
- }
- return _fastText.GetModelDimension();
- }
- }
-
- public fastTextEmbeddingProvider(fastTextSetting settings)
- {
- _settings = settings;
-
- }
-
- public float[] GetVector(string text)
- {
- LoadModel();
- return _fastText.GetSentenceVector(text);
- }
-
- public List GetVectors(List texts)
- {
- LoadModel();
- var vectors = new List();
- for (int i = 0; i < texts.Count; i++)
- {
- vectors.Add(GetVector(texts[i]));
- }
- return vectors;
- }
-
- private void LoadModel()
- {
- if (_fastText == null)
- {
- if (!File.Exists(_settings.ModelPath))
- {
- throw new FileNotFoundException($"Can't load pre-trained word vectors from {_settings.ModelPath}.\n Try to download from https://fasttext.cc/docs/en/english-vectors.html.");
- }
-
- _fastText = new FastTextWrapper();
-
- if (!_fastText.IsModelReady())
- {
- _fastText.LoadModel(_settings.ModelPath);
- }
- }
- }
-}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
index 2307c079..e742f169 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
@@ -7,24 +7,25 @@ using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;
-using FastText.NetWrapper;
using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Abstraction.Templating;
+using BotSharp.Plugin.RoutingSpeeder.Providers;
namespace BotSharp.Plugin.RoutingSpeeder;
public class RoutingConversationHook: ConversationHookBase
{
private readonly IServiceProvider _services;
- private routerSpeedSettings _settings;
- public RoutingConversationHook(IServiceProvider service, routerSpeedSettings settings)
+ private RouterSpeederSettings _settings;
+ public RoutingConversationHook(IServiceProvider service, RouterSpeederSettings settings)
{
_services = service;
_settings = settings;
}
public override async Task BeforeCompletion(RoleDialogModel message)
{
- var embedding = _services.GetServices()
- .FirstOrDefault(x => x.GetType().FullName.EndsWith(_settings.TextEmbedding));
+ var intentClassifier = _services.GetRequiredService();
+ var vector = intentClassifier.GetTextEmbedding(message.Content);
// Utilize local discriminative model to predict intent
message.IntentName = "greeting";
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
index bcb91335..c3dac3a2 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
@@ -12,11 +12,13 @@ public class RoutingSpeederPlugin : IBotSharpPlugin
{
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
- var settings = new routerSpeedSettings();
- config.Bind("routerSpeed", settings);
+ var settings = new RouterSpeederSettings();
+ config.Bind("RouterSpeeder", settings);
services.AddSingleton(x => settings);
- services.AddSingleton(x => settings.fastText);
+
+ services.AddSingleton();
+
services.AddScoped();
- services.AddSingleton();
+ services.AddSingleton();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
index 2fe13237..e6352694 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
@@ -4,18 +4,15 @@ using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Settings;
-public class classifierSetting
+public class ClassifierSetting
{
-
- public Dictionary labelMappingDict { get; set; } = new Dictionary()
+ public Dictionary LabelMappingDict { get; set; } = new Dictionary()
{
{"goodbye", 0f},
{"greeting", 1f},
- {"other", 2f},
- {"wo-followup", 3f},
- {"wo-identifer", 4f},
- {"wo-scheduler", 5}
+ {"other", 2f}
};
- public string RAW_DATA_DIR { get; set; } = "C:\\new_wenbocao\\one_brain\\WebStarter\\data\\raw_data";
- public string MODEL_DIR { get; set; } = "C:\\new_wenbocao\\one_brain\\WebStarter\\data\\models";
+
+ public string RAW_DATA_DIR { get; set; } = "";
+ public string MODEL_DIR { get; set; } = "";
}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
deleted file mode 100644
index e5554402..00000000
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/fastTextSetting.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-namespace BotSharp.Plugin.RoutingSpeeder.Settings;
-
-public class fastTextSetting
-{
- public string ModelPath { get; set; }
-}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
index 300ec9e0..fbedf581 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
@@ -4,8 +4,6 @@ using System.Text;
namespace BotSharp.Plugin.RoutingSpeeder.Settings;
-public class routerSpeedSettings
+public class RouterSpeederSettings
{
- public fastTextSetting fastText { get; set; }
- public string TextEmbedding { get; set; }
}
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 42ef6319..e409738f 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -47,11 +47,14 @@
}
},
- "MetaAi": {
- "fastText": {
- "ModelPath": "crawl-300d-2M-subword.bin"
- }
- },
+ "MetaAi": {
+ "fastText": {
+ "ModelPath": "crawl-300d-2M-subword.bin"
+ }
+ },
+
+ "RoutingSpeeder": {
+ },
"MetaMessenger": {
"Endpoint": "https://graph.facebook.com",
From 7762627f8254d885b1eeea0ef1765c2920c682e2 Mon Sep 17 00:00:00 2001
From: Wenbo Cao <104199@smsassist.com>
Date: Thu, 31 Aug 2023 17:00:31 -0500
Subject: [PATCH 3/3] Add intent classifier
---
.../Templating/ResponseTemplateService.cs | 15 ++-
.../Providers/IntentClassifier.cs | 108 ++++++++++++++++--
.../RoutingConversationHook.cs | 6 +-
.../Settings/classifierSetting.cs | 4 +-
4 files changed, 121 insertions(+), 12 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs
index b7be790a..ef2c23ba 100644
--- a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs
+++ b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs
@@ -46,6 +46,10 @@ public class ResponseTemplateService : IResponseTemplateService
// Find response template
var agentService = _services.GetRequiredService();
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();
- ExtractArgs(JsonSerializer.Deserialize(message.FunctionArgs), dict);
- ExtractExecuteData(message.ExecutionData, dict);
+ if (!string.IsNullOrEmpty(message.FunctionArgs))
+ {
+ ExtractArgs(JsonSerializer.Deserialize(message.FunctionArgs), dict);
+ }
+
+ if (message.ExecutionData != null)
+ {
+ ExtractExecuteData(message.ExecutionData, dict);
+ }
var text = render.Render(template, dict);
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
index ea74f54d..382b3a98 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
@@ -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();
+
var layers = new List
{
- 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();
+
+ 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 items)
{
+ var vector = _services.GetRequiredService();
+
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();
-
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();
var embedding = _services.GetServices()
.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();
+ 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();
+
+
+ var vectorList = new List();
+
+ var labelList = new List();
+ 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();
+ 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);
+
+ }
}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
index e742f169..fbb63acc 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
@@ -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();
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();
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
index e6352694..d051f2e7 100644
--- a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
@@ -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";
}