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
|