Benutzerdefinierte Excel-Funktionen für BERT NLP-Aufgaben in JavaScript mit ONNX Runtime
In diesem Tutorial erfahren Sie, wie Sie benutzerdefinierte Excel-Funktionen (ORT.Sentiment() und ORT.Question()) erstellen können, um BERT NLP-Modelle mit ONNX Runtime Web zu implementieren und Deep Learning in Tabellenkalkulationsaufgaben zu ermöglichen. Die Inferenz erfolgt lokal, direkt in Excel!

Inhalt
- Voraussetzungen
- Was sind benutzerdefinierte Funktionen?
- Erstellen des Projekts für benutzerdefinierte Funktionen
- Die Datei
manifest.xml - Die Datei
functions.ts - Die Datei
inferenceQuestion.ts - Die Datei
inferenceSentiment.ts - Schlussfolgerung
- Zusätzliche Ressourcen
Voraussetzungen
- Node.js
- Office, das mit einem Microsoft 365-Abonnement verbunden ist (einschließlich Office im Web). Wenn Sie noch kein Office haben, können Sie dem Microsoft 365 Developer Program beitreten, um ein kostenloses, 90 Tage lang verlängerbares Microsoft 365-Abonnement für die Entwicklung zu erhalten.
- Weitere Informationen finden Sie im Tutorial zu Office Add-Ins
Was sind benutzerdefinierte Funktionen?
Excel verfügt über viele native Funktionen wie SUM(), mit denen Sie wahrscheinlich vertraut sind. Benutzerdefinierte Funktionen sind ein nützliches Werkzeug, um neue Funktionen für Excel zu erstellen und hinzuzufügen, indem diese Funktionen in JavaScript als Teil eines Add-Ins definiert werden. Diese Funktionen können in Excel genauso aufgerufen werden, wie Sie jede native Funktion in Excel aufrufen würden.
Erstellen des Projekts für benutzerdefinierte Funktionen
Nachdem wir nun wissen, was benutzerdefinierte Funktionen sind, sehen wir uns an, wie wir Funktionen erstellen können, die ein Modell lokal inferieren, um die Sentiment-Texte in einer Zelle zu erhalten, oder Informationen aus einer Zelle extrahieren, indem wir eine Frage stellen und die Antwort in die Zelle zurückgeben.
-
Wenn Sie mitmachen möchten, klonen Sie das Projekt, das wir in diesem Blogbeitrag besprechen werden. Dieses Projekt wurde mit der Vorlage des Yeoman CLI erstellt. Erfahren Sie mehr in dieser Schnellstartanleitung über die Basisprojekte.
-
Führen Sie die folgenden Befehle aus, um die Pakete zu installieren und das Projekt zu erstellen.
npm install
npm run build
- Der folgende Befehl führt das Add-In in Excel Web aus und lädt das Add-In seitenweise in die im Befehl angegebene Tabellenkalkulation.
// Command to run on the web.
// Replace "{url}" with the URL of an Excel document.
npm run start:web -- --document {url}
- Verwenden Sie den folgenden Befehl, um im Excel-Client auszuführen.
// Command to run on desktop (Windows or Mac)
npm run start:desktop
- Beim ersten Ausführen des Projekts werden zwei Aufforderungen angezeigt
- Eine fragt nach der
Aktivierung des Entwicklermodus. Dies ist für das seitenweise Laden von Plug-ins erforderlich. - Akzeptieren Sie als Nächstes, wenn Sie dazu aufgefordert werden, das Zertifikat für den Plug-in-Dienst.
- Eine fragt nach der
- Um auf die benutzerdefinierte Funktion zuzugreifen, geben Sie
=ORT.Sentiment("TEXT")und=ORT.Question("FRAGE","KONTEXT")in eine leere Zelle ein und übergeben Sie die Parameter.
Jetzt sind wir bereit, in den Code einzutauchen!
Die Datei manifest.xml
Die Datei manifest.xml gibt an, dass alle benutzerdefinierten Funktionen zum Namespace ORT gehören. Sie verwenden den Namespace, um auf die benutzerdefinierten Funktionen in Excel zuzugreifen. Aktualisieren Sie die Werte in manifest.xml auf ORT.
<bt:String id="Functions.Namespace" DefaultValue="ORT"/>
<ProviderName>ORT</ProviderName>
Erfahren Sie mehr über die Konfiguration der Manifestdatei hier.
Die Datei functions.ts
In der Datei function.ts definieren wir den Funktionsnamen, die Parameter, die Logik und den Rückgabetyp.
- Importieren Sie die Funktionen
inferenceQuestionundinferenceSentimentam Anfang der Dateifunction.ts. (Die Logik in diesen Funktionen behandeln wir später in diesem Tutorial.)
/* global console */
import { inferenceQuestion } from "./bert/inferenceQuestion";
import { inferenceSentiment } from "./bert/inferenceSentiment";
- Fügen Sie als Nächstes die Funktionen
sentimentundquestionhinzu.
/**
* Returns the sentiment of a string.
* @customfunction
* @param text Text string
* @returns sentiment string.
*/
export async function sentiment(text: string): Promise<string> {
const result = await inferenceSentiment(text);
console.log(result[1][0]);
return result[1][0].toString();
}
/**
* Returns the sentiment of a string.
* @customfunction
* @param question Question string
* @param context Context string
* @returns answer string.
*/
export async function question(question: string, context: string): Promise<string> {
const result = await inferenceQuestion(question, context);
if (result.length > 0) {
console.log(result[0].text);
return result[0].text.toString();
}
return "Unable to find answer";
}
Die Datei inferenceQuestion.ts
Die Datei inferenceQuestion.ts enthält die Logik zur Verarbeitung des Question-and-Answer-BERT-Modells. Dieses Modell wurde mithilfe dieses Tutorials erstellt. Anschließend haben wir das ORT Quantization Tool verwendet, um die Größe des Modells zu reduzieren. Erfahren Sie mehr über Quantisierung hier.
- Importieren Sie zuerst
onnxruntime-webund die Hilfsfunktionen ausquestion_answer.ts.question_answer.tsist eine bearbeitete Version aus dem TensorFlow-Beispiel, das hier zu finden ist. Sie können die bearbeitete Version im Quellcode dieses Projekts hier finden.
/* eslint-disable no-undef */
import * as ort from "onnxruntime-web";
import { create_model_input, Feature, getBestAnswers, Answer } from "./utils/question_answer";
- Die Funktion
inferenceQuestionnimmt die Frage und den Kontext entgegen und liefert die Antworten basierend auf dem Inferenz-Ergebnis. Dann legen wir den Pfad zum Modell fest. Dieser Pfad wird inwebpack.config.jsmit demCopyWebpackPlugingesetzt. Dieses Plugin kopiert die beim Erstellen benötigten Assets in dendist-Ordner.
export async function inferenceQuestion(question: string, context: string): Promise<Answer[]> {
const model: string = "./bert-large-uncased-int8.onnx";
- Erstellen wir nun die ONNX Runtime Inferenzsitzung und legen die Optionen fest. Erfahren Sie mehr über alle
SessionOptionshier.
// create session, set options
const options: ort.InferenceSession.SessionOptions = {
executionProviders: ["wasm"],
// executionProviders: ['webgl']
graphOptimizationLevel: "all",
};
console.log("Creating session");
const session = await ort.InferenceSession.create(model, options);
- Als Nächstes kodieren wir die
Frageund denKontextmit der Funktioncreate_model_inputausquestion_answer.ts. Dies gibt dasFeaturezurück.
// Get encoded ids from text tokenizer.
const encoded: Feature = await create_model_input(question, context);
console.log("encoded", encoded);
export interface Feature {
input_ids: Array<any>;
input_mask: Array<any>;
segment_ids: Array<any>;
origTokens: Token[];
tokenToOrigMap: { [key: number]: number };
}
- Nachdem wir das
kodierteFeaturehaben, müssen wir Arrays (input_ids,attention_maskundtoken_type_ids) vom TypBigInterstellen, um einenort.Tensor-Input zu erstellen.
// Create arrays of correct length
const length = encoded.input_ids.length;
var input_ids = new Array(length);
var attention_mask = new Array(length);
var token_type_ids = new Array(length);
// Get encoded.input_ids as BigInt
input_ids[0] = BigInt(101);
attention_mask[0] = BigInt(1);
token_type_ids[0] = BigInt(0);
var i = 0;
for (; i < length; i++) {
input_ids[i + 1] = BigInt(encoded.input_ids[i]);
attention_mask[i + 1] = BigInt(1);
token_type_ids[i + 1] = BigInt(0);
}
input_ids[i + 1] = BigInt(102);
attention_mask[i + 1] = BigInt(1);
token_type_ids[i + 1] = BigInt(0);
console.log("arrays", input_ids, attention_mask, token_type_ids);
- Erstellen Sie
ort.Tensoraus denArrays.
const sequence_length = input_ids.length;
var input_ids_tensor: ort.Tensor = new ort.Tensor("int64", BigInt64Array.from(input_ids), [1, sequence_length]);
var attention_mask_tensor: ort.Tensor = new ort.Tensor("int64", BigInt64Array.from(attention_mask), [ 1, sequence_length]);
var token_type_ids_tensor: ort.Tensor = new ort.Tensor("int64", BigInt64Array.from(token_type_ids), [ 1, sequence_length]);
- Wir sind bereit für die Inferenz! Hier erstellen wir die
OnnxValueMapType(Input-Objekt) undFetchesType(Return-Labels). Sie können das Objekt und das String-Array ohne Deklaration des Typs übergeben, aber das Hinzufügen von Typen ist nützlich.
const model_input: ort.InferenceSession.OnnxValueMapType = {
input_ids: input_ids_tensor,
input_mask: attention_mask_tensor,
segment_ids: token_type_ids_tensor,
};
const output_names: ort.InferenceSession.FetchesType = ["start_logits", "end_logits"];
const output = await session.run(model_input, output_names);
const result_length = output["start_logits"].data.length;
- Schleifen Sie als Nächstes durch das Ergebnis und erstellen Sie ein
number-Array aus den resultierendenstart_logitsundend_logits.
const start_logits: number[] = Array();
const end_logits: number[] = Array();
console.log("start_logits", start_logits);
console.log("end_logits", end_logits);
for (let i = 0; i <= result_length; i++) {
start_logits.push(Number(output["start_logits"].data[i]));
}
for (let i = 0; i <= result_length; i++) {
end_logits.push(Number(output["end_logits"].data[i]));
}
- Zuletzt rufen wir
getBestAnswersausquestion_answer.tsauf. Dies nimmt das Ergebnis und führt die Nachbearbeitung durch, um die Antwort aus dem Inferenz-Ergebnis zu erhalten.
const answers: Answer[] = getBestAnswers(
start_logits,
end_logits,
encoded.origTokens,
encoded.tokenToOrigMap,
context
);
console.log("answers", answers);
return answers;
}
- Die
Antwortenwerden dann zurück an diefunctions.tsquestion-Funktion zurückgegeben, der resultierende String wird zurückgegeben und in die Excel-Zelle gefüllt.
export async function question(question: string, context: string): Promise<string> {
const result = await inferenceQuestion(question, context);
if (result.length > 0) {
console.log(result[0].text);
return result[0].text.toString();
}
return "Unable to find answer";
}
- Jetzt können Sie den folgenden Befehl ausführen, um das Add-In zu erstellen und seitenweise in Ihre Excel-Tabellenkalkulation zu laden!
// Command to run on the web.
// Replace "{url}" with the URL of an Excel document.
npm run start:web -- --document {url}
Dies ist eine Aufschlüsselung für die benutzerdefinierte Funktion ORT.Question(). Als Nächstes werden wir aufschlüsseln, wie ORT.Sentiment() implementiert ist.
Die Datei inferenceSentiment.ts
Die Datei inferenceSentiment.ts enthält die Logik zur Inferenz und zum Abrufen des Sentiments für Text in einer Excel-Zelle. Der Code hier wurde aus diesem Beispiel erweitert. Lassen Sie uns eintauchen und lernen, wie dieser Teil funktioniert.
- Zuerst importieren wir die benötigten Pakete. Wie Sie in diesem Tutorial sehen werden, erstellt die Funktion
bertProcessingunseren Modell-Input.bert_tokenizerist der JavaScript-Tokenizer für BERT-Modelle.onnxruntime-webermöglicht die Inferenz in JavaScript im Browser.
/* eslint-disable no-undef */
import * as bertProcessing from "./bertProcessing";
import * as ort from "onnxruntime-web";
import { EMOJIS } from "./emoji";
import { loadTokenizer } from "./bert_tokenizer";
- Laden wir nun das quantisierte BERT-Modell, das für die Sentiment-Analyse feinabgestimmt wurde. Erstellen Sie dann die
ort.InferenceSessionundort.InferenceSession.SessionOptions.
export async function inferenceSentiment(text: string) {
// Set model path.
const model: string = "./xtremedistill-go-emotion-int8.onnx";
const options: ort.InferenceSession.SessionOptions = {
executionProviders: ["wasm"],
// executionProviders: ['webgl']
graphOptimizationLevel: "all",
};
console.log("Creating session");
const session = await ort.InferenceSession.create(model, options);
- Als Nächstes tokenisieren wir den Text, um den
model_inputzu erstellen und ihn ansession.runmit dem Ausgabe-Labeloutput_0zu senden, um das Inferenz-Ergebnis zu erhalten.
// Get encoded ids from text tokenizer.
const tokenizer = loadTokenizer();
const encoded = await tokenizer.then((t) => {
return t.tokenize(text);
});
console.log("encoded", encoded);
const model_input = await bertProcessing.create_model_input(encoded);
console.log("run session");
const output = await session.run(model_input, ["output_0"]);
const outputResult = output["output_0"].data;
console.log("outputResult", outputResult);
- Als Nächstes parsen wir die Ausgabe, um das Top-Ergebnis zu erhalten und es dem Label, dem Score und dem Emoji zuzuordnen.
let probs = [];
for (let i = 0; i < outputResult.length; i++) {
let sig = bertProcessing.sigmoid(outputResult[i]);
probs.push(Math.floor(sig * 100));
}
console.log("probs", probs);
const result = [];
for (var i = 0; i < EMOJIS.length; i++) {
const t = [EMOJIS[i], probs[i]];
result[i] = t;
}
result.sort(bertProcessing.sortResult);
console.log(result);
const result_list = [];
result_list[0] = ["Emotion", "Score"];
for (i = 0; i < 6; i++) {
result_list[i + 1] = result[i];
}
console.log(result_list);
return result_list;
}
- Die
result_listwird zurückgegeben und geparst, um das Top-Ergebnis an die Excel-Zelle zurückzugeben.
export async function sentiment(text: string): Promise<string> {
const result = await inferenceSentiment(text);
console.log(result[1][0]);
return result[1][0].toString();
}
- Jetzt können Sie den folgenden Befehl ausführen, um das Add-In zu erstellen und seitenweise in Ihre Excel-Tabellenkalkulation zu laden!
// Command to run on the web.
// Replace "{url}" with the URL of an Excel document.
npm run start:web -- --document {url}
Fazit
Hier haben wir die Logik zur Erstellung benutzerdefinierter Funktionen in einem Excel Add-In mit JavaScript unter Verwendung von ONNX Runtime Web und Open-Source-Modellen behandelt. Von hier aus könnten Sie diese Logik nehmen und sie für ein bestimmtes Modell oder Anwendungsfall aktualisieren. Schauen Sie sich unbedingt den vollständigen Quellcode an, der die Tokenizer und die Vor-/Nachbearbeitung zur Vollendung der oben genannten Aufgaben enthält.