Commit
·
4a6db11
1
Parent(s):
522203e
update to inference-engine
Browse files- MiniLMv6.sentis +0 -3
- README.md +13 -25
- MiniLMv6.cs → RunMiniLM.cs +56 -90
- vocab.txt → data/vocab.txt +0 -0
- info.json +4 -4
- MiniLMv6.onnx → models/MiniLMv6.onnx +0 -0
MiniLMv6.sentis
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:c9a2597ce9edce4c09b32e993b7f906cce91fceb2f461a597b974f71ee70453d
|
3 |
-
size 90898400
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -2,36 +2,24 @@
|
|
2 |
license: apache-2.0
|
3 |
library_name: unity-sentis
|
4 |
pipeline_tag: sentence-similarity
|
|
|
|
|
5 |
---
|
6 |
|
7 |
-
# Mini LM
|
8 |
-
*Version 1.3.0 Sentis files are not compatible with Sentis 1.4.0 and need to be recreated/downloaded
|
9 |
|
10 |
-
This is the [Mini LM v6 model](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
|
11 |
|
12 |
## How to Use
|
13 |
|
14 |
-
* Create a new scene in Unity
|
15 |
-
* Install com.unity.
|
16 |
-
* Add the
|
17 |
-
*
|
18 |
-
*
|
19 |
-
* Press play, the results will show in the Console
|
20 |
|
21 |
-
##
|
22 |
-
|
23 |
|
24 |
-
|
25 |
-
|
26 |
-
string1 = "That is a happy person"
|
27 |
-
|
28 |
-
string2 = "That is a happy dog"
|
29 |
-
```
|
30 |
-
|
31 |
-
# Example Outputs
|
32 |
-
```
|
33 |
-
Similarity Score: 0.6945773
|
34 |
-
```
|
35 |
-
|
36 |
-
## Unity Sentis
|
37 |
-
Sentis is the inference engine for Unity. More can be found about it [here](https://unity.com/products/sentis)
|
|
|
2 |
license: apache-2.0
|
3 |
library_name: unity-sentis
|
4 |
pipeline_tag: sentence-similarity
|
5 |
+
tags:
|
6 |
+
- unity-inference-engine
|
7 |
---
|
8 |
|
9 |
+
# Mini LM in Unity 6 with Inference Engine
|
|
|
10 |
|
11 |
+
This is the [Mini LM v6 model](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) model running in Unity 6 with Inference Engine. Mini LM is a sentence similarity model that compares different sentences and gives a score depending on how similar they are.
|
12 |
|
13 |
## How to Use
|
14 |
|
15 |
+
* Create a new scene in Unity 6;
|
16 |
+
* Install `com.unity.ai.inference` from the package manager;
|
17 |
+
* Add the `RunMiniLM.cs` script to the Main Camera;
|
18 |
+
* Drag the `MiniLMv6.onnx` asset from the `models` folder into the `Model Asset` field;
|
19 |
+
* Drag the `vocab.txt` asset from the `data` folder into the `Vocab Asset` field;
|
|
|
20 |
|
21 |
+
## Preview
|
22 |
+
Enter play mode. If working correctly the sentence similarity score will be logged to the console.
|
23 |
|
24 |
+
## Inference Engine
|
25 |
+
Inference Engine is a neural network inference library for Unity. Find out more [here](https://docs.unity3d.com/Packages/com.unity.ai.inference@latest).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MiniLMv6.cs → RunMiniLM.cs
RENAMED
@@ -1,51 +1,35 @@
|
|
1 |
-
using System
|
|
|
|
|
2 |
using UnityEngine;
|
3 |
-
|
4 |
-
|
5 |
-
using System.Text;
|
6 |
-
using FF = Unity.Sentis.Functional;
|
7 |
-
|
8 |
-
/*
|
9 |
-
* Tiny Stories Inference Code
|
10 |
-
* ===========================
|
11 |
-
*
|
12 |
-
* Put this script on the Main Camera
|
13 |
-
*
|
14 |
-
* In Assets/StreamingAssets put:
|
15 |
-
*
|
16 |
-
* MiniLMv6.sentis
|
17 |
-
* vocab.txt
|
18 |
-
*
|
19 |
-
* Install package com.unity.sentis
|
20 |
-
*
|
21 |
-
*/
|
22 |
-
|
23 |
-
|
24 |
-
public class MiniLM : MonoBehaviour
|
25 |
{
|
|
|
|
|
26 |
const BackendType backend = BackendType.GPUCompute;
|
27 |
|
28 |
-
string string1 = "That is a happy person";
|
29 |
|
30 |
-
//Choose a string to
|
31 |
-
string string2 = "That is a happy dog";
|
32 |
-
//string string2 = "That is a very happy person";
|
33 |
-
//string string2 = "Today is a sunny day";
|
34 |
|
35 |
//Special tokens
|
36 |
-
const int START_TOKEN = 101;
|
37 |
-
const int END_TOKEN = 102;
|
38 |
|
39 |
//Store the vocabulary
|
40 |
string[] tokens;
|
41 |
|
42 |
const int FEATURES = 384; //size of feature space
|
43 |
|
44 |
-
|
45 |
|
46 |
void Start()
|
47 |
{
|
48 |
-
tokens =
|
49 |
|
50 |
engine = CreateMLModel();
|
51 |
|
@@ -54,87 +38,70 @@ public class MiniLM : MonoBehaviour
|
|
54 |
var tokens1 = GetTokens(string1);
|
55 |
var tokens2 = GetTokens(string2);
|
56 |
|
57 |
-
using
|
58 |
-
using
|
59 |
|
60 |
float score = GetDotScore(embedding1, embedding2);
|
61 |
|
62 |
Debug.Log("Similarity Score: " + score);
|
63 |
}
|
64 |
|
65 |
-
float GetDotScore(
|
66 |
{
|
67 |
-
|
68 |
-
|
69 |
-
{ "input_0", A },
|
70 |
-
{ "input_1", B }
|
71 |
-
};
|
72 |
-
dotScore.Execute(inputs);
|
73 |
-
var output = dotScore.PeekOutput() as TensorFloat;
|
74 |
-
output.CompleteOperationsAndDownload();
|
75 |
return output[0];
|
76 |
}
|
77 |
|
78 |
-
|
79 |
{
|
80 |
-
int N =
|
81 |
-
using var input_ids = new
|
82 |
-
using var token_type_ids = new
|
83 |
int[] mask = new int[N];
|
84 |
for (int i = 0; i < mask.Length; i++)
|
85 |
{
|
86 |
mask[i] = 1;
|
87 |
}
|
88 |
-
using var attention_mask = new
|
89 |
|
90 |
-
|
91 |
-
{
|
92 |
-
{"input_0", input_ids },
|
93 |
-
{"input_1", attention_mask },
|
94 |
-
{"input_2", token_type_ids}
|
95 |
-
};
|
96 |
|
97 |
-
engine.
|
98 |
-
|
99 |
-
var output = engine.TakeOutputOwnership("output_0") as TensorFloat;
|
100 |
return output;
|
101 |
}
|
102 |
|
103 |
-
|
104 |
{
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
);
|
115 |
-
|
116 |
-
return WorkerFactory.CreateWorker(backend, modelWithMeanPooling);
|
117 |
}
|
118 |
|
119 |
//Get average of token embeddings taking into account the attention mask
|
120 |
FunctionalTensor MeanPooling(FunctionalTensor tokenEmbeddings, FunctionalTensor attentionMask)
|
121 |
{
|
122 |
-
var mask = attentionMask.Unsqueeze(-1).BroadcastTo(new[] { FEATURES });
|
123 |
-
var A =
|
124 |
-
var B = A / (
|
125 |
-
var C =
|
126 |
-
return B / C;
|
127 |
}
|
128 |
|
129 |
-
|
130 |
{
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
);
|
136 |
-
|
137 |
-
return WorkerFactory.CreateWorker(backend, dotScoreModel);
|
138 |
}
|
139 |
|
140 |
List<int> GetTokens(string text)
|
@@ -152,10 +119,10 @@ public class MiniLM : MonoBehaviour
|
|
152 |
foreach (var word in words)
|
153 |
{
|
154 |
int start = 0;
|
155 |
-
for(int i = word.Length; i >= 0;i--)
|
156 |
{
|
157 |
-
string subword = start == 0 ? word.Substring(start, i) : "##" + word.Substring(start, i-start);
|
158 |
-
int index =
|
159 |
if (index >= 0)
|
160 |
{
|
161 |
ids.Add(index);
|
@@ -169,15 +136,14 @@ public class MiniLM : MonoBehaviour
|
|
169 |
|
170 |
ids.Add(END_TOKEN);
|
171 |
|
172 |
-
Debug.Log("Tokenized
|
173 |
|
174 |
return ids;
|
175 |
}
|
176 |
|
177 |
-
|
178 |
-
{
|
179 |
dotScore?.Dispose();
|
180 |
engine?.Dispose();
|
181 |
}
|
182 |
-
|
183 |
}
|
|
|
1 |
+
using System;
|
2 |
+
using System.Collections.Generic;
|
3 |
+
using Unity.InferenceEngine;
|
4 |
using UnityEngine;
|
5 |
+
|
6 |
+
public class RunMiniLM : MonoBehaviour
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
{
|
8 |
+
public ModelAsset modelAsset;
|
9 |
+
public TextAsset vocabAsset;
|
10 |
const BackendType backend = BackendType.GPUCompute;
|
11 |
|
12 |
+
string string1 = "That is a happy person"; // similarity = 1
|
13 |
|
14 |
+
//Choose a string to compare with string1:
|
15 |
+
string string2 = "That is a happy dog"; // similarity = 0.695
|
16 |
+
//string string2 = "That is a very happy person"; // similarity = 0.943
|
17 |
+
//string string2 = "Today is a sunny day"; // similarity = 0.257
|
18 |
|
19 |
//Special tokens
|
20 |
+
const int START_TOKEN = 101;
|
21 |
+
const int END_TOKEN = 102;
|
22 |
|
23 |
//Store the vocabulary
|
24 |
string[] tokens;
|
25 |
|
26 |
const int FEATURES = 384; //size of feature space
|
27 |
|
28 |
+
Worker engine, dotScore;
|
29 |
|
30 |
void Start()
|
31 |
{
|
32 |
+
tokens = vocabAsset.text.Split("\r\n");
|
33 |
|
34 |
engine = CreateMLModel();
|
35 |
|
|
|
38 |
var tokens1 = GetTokens(string1);
|
39 |
var tokens2 = GetTokens(string2);
|
40 |
|
41 |
+
using Tensor<float> embedding1 = GetEmbedding(tokens1);
|
42 |
+
using Tensor<float> embedding2 = GetEmbedding(tokens2);
|
43 |
|
44 |
float score = GetDotScore(embedding1, embedding2);
|
45 |
|
46 |
Debug.Log("Similarity Score: " + score);
|
47 |
}
|
48 |
|
49 |
+
float GetDotScore(Tensor<float> A, Tensor<float> B)
|
50 |
{
|
51 |
+
dotScore.Schedule(A, B);
|
52 |
+
var output = (dotScore.PeekOutput() as Tensor<float>).DownloadToNativeArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
return output[0];
|
54 |
}
|
55 |
|
56 |
+
Tensor<float> GetEmbedding(List<int> tokenList)
|
57 |
{
|
58 |
+
int N = tokenList.Count;
|
59 |
+
using var input_ids = new Tensor<int>(new TensorShape(1, N), tokenList.ToArray());
|
60 |
+
using var token_type_ids = new Tensor<int>(new TensorShape(1, N), new int[N]);
|
61 |
int[] mask = new int[N];
|
62 |
for (int i = 0; i < mask.Length; i++)
|
63 |
{
|
64 |
mask[i] = 1;
|
65 |
}
|
66 |
+
using var attention_mask = new Tensor<int>(new TensorShape(1, N), mask);
|
67 |
|
68 |
+
engine.Schedule(input_ids, attention_mask, token_type_ids);
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
+
var output = engine.PeekOutput().ReadbackAndClone() as Tensor<float>;
|
|
|
|
|
71 |
return output;
|
72 |
}
|
73 |
|
74 |
+
Worker CreateMLModel()
|
75 |
{
|
76 |
+
var model = ModelLoader.Load(modelAsset);
|
77 |
+
var graph = new FunctionalGraph();
|
78 |
+
var inputs = graph.AddInputs(model);
|
79 |
+
var tokenEmbeddings = Functional.Forward(model, inputs)[0];
|
80 |
+
var attention_mask = inputs[1];
|
81 |
+
var output = MeanPooling(tokenEmbeddings, attention_mask);
|
82 |
+
var modelWithMeanPooling = graph.Compile(output);
|
83 |
+
|
84 |
+
return new Worker(modelWithMeanPooling, backend);
|
|
|
|
|
|
|
85 |
}
|
86 |
|
87 |
//Get average of token embeddings taking into account the attention mask
|
88 |
FunctionalTensor MeanPooling(FunctionalTensor tokenEmbeddings, FunctionalTensor attentionMask)
|
89 |
{
|
90 |
+
var mask = attentionMask.Unsqueeze(-1).BroadcastTo(new[] { FEATURES }); //shape=(1,N,FEATURES)
|
91 |
+
var A = Functional.ReduceSum(tokenEmbeddings * mask, 1); //shape=(1,FEATURES)
|
92 |
+
var B = A / (Functional.ReduceSum(mask, 1) + 1e-9f); //shape=(1,FEATURES)
|
93 |
+
var C = Functional.Sqrt(Functional.ReduceSum(Functional.Square(B), 1, true)); //shape=(1,FEATURES)
|
94 |
+
return B / C; //shape=(1,FEATURES)
|
95 |
}
|
96 |
|
97 |
+
Worker CreateDotScoreModel()
|
98 |
{
|
99 |
+
var graph = new FunctionalGraph();
|
100 |
+
var input1 = graph.AddInput<float>(new TensorShape(1, FEATURES));
|
101 |
+
var input2 = graph.AddInput<float>(new TensorShape(1, FEATURES));
|
102 |
+
var output = Functional.ReduceSum(input1 * input2, 1);
|
103 |
+
var dotScoreModel = graph.Compile(output);
|
104 |
+
return new Worker(dotScoreModel, backend);
|
|
|
105 |
}
|
106 |
|
107 |
List<int> GetTokens(string text)
|
|
|
119 |
foreach (var word in words)
|
120 |
{
|
121 |
int start = 0;
|
122 |
+
for (int i = word.Length; i >= 0; i--)
|
123 |
{
|
124 |
+
string subword = start == 0 ? word.Substring(start, i) : "##" + word.Substring(start, i - start);
|
125 |
+
int index = Array.IndexOf(tokens, subword);
|
126 |
if (index >= 0)
|
127 |
{
|
128 |
ids.Add(index);
|
|
|
136 |
|
137 |
ids.Add(END_TOKEN);
|
138 |
|
139 |
+
Debug.Log("Tokenized sentence = " + s);
|
140 |
|
141 |
return ids;
|
142 |
}
|
143 |
|
144 |
+
void OnDestroy()
|
145 |
+
{
|
146 |
dotScore?.Dispose();
|
147 |
engine?.Dispose();
|
148 |
}
|
|
|
149 |
}
|
vocab.txt → data/vocab.txt
RENAMED
File without changes
|
info.json
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
{
|
2 |
"code": [
|
3 |
-
"
|
4 |
],
|
5 |
"models": [
|
6 |
-
"MiniLMv6.
|
7 |
],
|
8 |
"data": [
|
9 |
-
"vocab.txt"
|
10 |
],
|
11 |
"version": [
|
12 |
-
"
|
13 |
]
|
14 |
}
|
|
|
1 |
{
|
2 |
"code": [
|
3 |
+
"RunMiniLM.cs"
|
4 |
],
|
5 |
"models": [
|
6 |
+
"models/MiniLMv6.onnx"
|
7 |
],
|
8 |
"data": [
|
9 |
+
"data/vocab.txt"
|
10 |
],
|
11 |
"version": [
|
12 |
+
"2.2.0"
|
13 |
]
|
14 |
}
|
MiniLMv6.onnx → models/MiniLMv6.onnx
RENAMED
File without changes
|