JavaScriptでBERTタスク用のカスタムExcel関数
JavaScriptでBERT NLPタスク用のONNX Runtimeカスタム Excel関数
Section titled “JavaScriptでBERT NLPタスク用のONNX Runtimeカスタム Excel関数”このチュートリアルでは、ONNX Runtime WebでBERT NLPモデルを実装するカスタムExcel関数(ORT.Sentiment()とORT.Question())を作成して、スプレッドシートタスクでディープラーニングを有効にする方法を見ていきます。推論はExcel内でローカルに実行されます!
- Node.js
- Microsoft 365サブスクリプションに接続されたOffice(Office on the webを含む)。まだOfficeをお持ちでない場合は、Microsoft 365開発者プログラムに参加して無料の90日間更新可能なMicrosoft 365サブスクリプションを開発中に使用できます。
- 詳細については、Office Add-insチュートリアルを参照してください
カスタム関数とは?
Section titled “カスタム関数とは?”Excelには、おそらくよく知っているSUM()などの多くのネイティブ関数があります。カスタム関数は、アドインの一部としてJavaScriptでそれらの関数を定義することで、Excelに新しい関数を作成して追加するための便利なツールです。これらの関数は、Excelのネイティブ関数と同じようにExcel内でアクセスできます。
カスタム関数プロジェクトの作成
Section titled “カスタム関数プロジェクトの作成”カスタム関数が何かがわかったので、セル内のテキストの感情を取得したり、質問をして答えがセルに返されることでセルから情報を抽出したりするために、モデルをローカルで推論する関数を作成する方法を見てみましょう。
-
一緒に進める場合は、このブログで説明するプロジェクトをクローンしてください。このプロジェクトは、Yeoman CLIのテンプレートプロジェクトで作成されました。ベースプロジェクトについてはこのクイックスタートで詳しく学んでください。
-
以下のコマンドを実行してパッケージをインストールし、プロジェクトをビルドします。
npm installnpm run build- 以下のコマンドは、Excel webでアドインを実行し、コマンドで提供されたスプレッドシートにアドインをサイドロードします。
// Webで実行するコマンド。// "{url}"をExcelドキュメントのURLに置き換えてください。npm run start:web -- --document {url}- Excelクライアントで実行するには、以下のコマンドを使用します。
// デスクトップ(WindowsまたはMac)で実行するコマンドnpm run start:desktop-
プロジェクトを初回実行する際には、2つのプロンプトが表示されます:
- 1つは
Enable Developer Modeを求めるものです。これはプラグインのサイドローディングに必要です。 - 次に、プラグインサービスの証明書を受け入れるよう求められます。
- 1つは
-
カスタム関数にアクセスするには、空のセルに
=ORT.Sentiment("TEXT")と=ORT.Question("QUESTION","CONTEXT")と入力し、パラメータを渡します。
これでコードに飛び込む準備ができました!
manifest.xmlファイル
Section titled “manifest.xmlファイル”manifest.xmlファイルは、すべてのカスタム関数がORT名前空間に属することを指定します。Excelでカスタム関数にアクセスするために名前空間を使用します。manifest.xmlの値をORTに更新します。
<bt:String id="Functions.Namespace" DefaultValue="ORT"/><ProviderName>ORT</ProviderName>マニフェストファイルの設定についてはこちらで詳しく学んでください。
functions.tsファイル
Section titled “functions.tsファイル”function.tsファイルでは、関数名、パラメータ、ロジック、戻り値の型を定義します。
function.tsファイルの上部で関数inferenceQuestionとinferenceSentimentをインポートします。(これらの関数のロジックについては、このチュートリアルの後半で説明します。)
/* global console */import { inferenceQuestion } from "./bert/inferenceQuestion";import { inferenceSentiment } from "./bert/inferenceSentiment";- 次に
sentimentとquestion関数を追加します。
/*** 文字列の感情を返します。* @customfunction* @param text テキスト文字列* @returns 感情文字列。*/export async function sentiment(text: string): Promise<string> {const result = await inferenceSentiment(text);console.log(result[1][0]);return result[1][0].toString();}/** * 文字列の感情を返します。 * @customfunction * @param question 質問文字列 * @param context コンテキスト文字列 * @returns 回答文字列。 */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";}inferenceQuestion.tsファイル
Section titled “inferenceQuestion.tsファイル”inferenceQuestion.tsファイルには、質問応答BERTモデルを処理するロジックがあります。このモデルはこのチュートリアルを使用して作成されました。その後、ORT量子化ツールを使用してモデルのサイズを削減しました。量子化についてはこちらで詳しく学んでください。
- まず
onnxruntime-webとquestion_answer.tsからのヘルパー関数をインポートします。question_answer.tsはこちらにあるtensorflowの例から編集されたバージョンです。このプロジェクトのソースで編集されたバージョンをこちらで見つけることができます。
/* eslint-disable no-undef */import * as ort from "onnxruntime-web";import { create_model_input, Feature, getBestAnswers, Answer } from "./utils/question_answer";inferenceQuestion関数は質問とコンテキストを受け取り、推論結果に基づいて回答を提供します。次に、モデルへのパスを設定します。このパスはCopyWebpackPluginを使用してwebpack.config.jsで設定されます。このプラグインは、ビルド時に必要なアセットをdistフォルダにコピーします。
export async function inferenceQuestion(question: string, context: string): Promise<Answer[]> { const model: string = "./bert-large-uncased-int8.onnx";- 次に、ONNX Runtime推論セッションを作成してオプションを設定しましょう。すべての
SessionOptionsについてこちらで詳しく学んでください。
// セッションを作成し、オプションを設定 const options: ort.InferenceSession.SessionOptions = { executionProviders: ["wasm"], // executionProviders: ['webgl'] graphOptimizationLevel: "all", }; console.log("Creating session"); const session = await ort.InferenceSession.create(model, options);- 次に、
question_answer.tsのcreate_model_input関数を使用してquestionとcontextをエンコードします。これはFeatureを返します。
// テキストトークナイザーからエンコードされたIDを取得 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 };}- エンコードされた
Featureを取得したので、ort.Tensor入力を作成するためにBigInt型の配列(input_ids、attention_mask、token_type_ids)を作成する必要があります。
// 正しい長さの配列を作成 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);
// encoded.input_idsを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);Arraysからort.Tensorを作成します。
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]);- 推論を実行する準備ができました!ここで
OnnxValueMapType(入力オブジェクト)とFetchesType(戻りラベル)を作成します。型を宣言せずにオブジェクトと文字列配列を送信できますが、型を追加することは有用です。
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;- 次に結果をループし、結果の
start_logitsとend_logitsからnumber配列を作成します。
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])); }- 最後に
question_answer.tsからgetBestAnswersを呼び出します。これは結果を受け取り、推論結果から回答を得るための後処理を行います。
const answers: Answer[] = getBestAnswers( start_logits, end_logits, encoded.origTokens, encoded.tokenToOrigMap, context ); console.log("answers", answers); return answers;}answersはfunctions.tsのquestionに返され、結果の文字列が返されてExcelセルに入力されます。
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";}- 以下のコマンドを実行して、アドインをビルドしてExcelスプレッドシートにサイドロードできます!
// Webで実行するコマンド。// "{url}"をExcelドキュメントのURLに置き換えてください。npm run start:web -- --document {url}これがORT.Question()カスタム関数の内訳です。次にORT.Sentiment()の実装方法を分解します。
inferenceSentiment.tsファイル
Section titled “inferenceSentiment.tsファイル”inferenceSentiment.tsは、Excelセル内のテキストの感情を推論して取得するロジックです。ここのコードはこの例から拡張されています。この部分がどのように機能するかを学びましょう。
- まず必要なパッケージをインポートしましょう。このチュートリアルでわかるように、
bertProcessing関数がモデル入力を作成します。bert_tokenizerはBERTモデル用のJavaScriptトークナイザーです。onnxruntime-webはブラウザでのJavaScript推論を可能にします。
/* eslint-disable no-undef */import * as bertProcessing from "./bertProcessing";import * as ort from "onnxruntime-web";import { EMOJIS } from "./emoji";import { loadTokenizer } from "./bert_tokenizer";- 次に、感情分析用にファインチューニングされた量子化BERTモデルを読み込みましょう。次に
ort.InferenceSessionとort.InferenceSession.SessionOptionsを作成します。
export async function inferenceSentiment(text: string) { // モデルパスを設定 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);- 次に、テキストをトークン化して
model_inputを作成し、出力ラベルoutput_0と一緒にsession.runに送信して推論結果を取得します。
// テキストトークナイザーからエンコードされたIDを取得 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);- 次に、出力を解析してトップ結果を取得し、ラベル、スコア、絵文字にマップします。
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;}result_listが返され、解析されてトップ結果がExcelセルに返されます。
export async function sentiment(text: string): Promise<string> { const result = await inferenceSentiment(text); console.log(result[1][0]); return result[1][0].toString();}- 以下のコマンドを実行して、アドインをビルドしてExcelスプレッドシートにサイドロードできます!
// Webで実行するコマンド。// "{url}"をExcelドキュメントのURLに置き換えてください。npm run start:web -- --document {url}ここでは、ONNX Runtime Webとオープンソースモデルを活用してJavaScriptでExcelアドインのカスタム関数を作成するために必要なロジックについて説明しました。ここから、このロジックを取得して、特定のモデルまたは使用ケースに更新できます。上記のタスクを完了するためのトークナイザーと前処理/後処理を含む完全なソースコードを必ず確認してください。