事前にトレーニングされた質問と回答のTensorFlow.jsモデルをAppPlatformにデプロイする方法

提供:Dev Guides
移動先:案内検索

著者は、 Write forDOnationsプログラムの一環として寄付を受け取るためにCode2040を選択しました。

序章

機械学習(ML)の分野が成長するにつれて、このテクノロジーを使用するための環境のリストも成長します。 これらの環境の1つはWebブラウザーであり、近年、Webベースの機械学習モデルを対象としたデータ関連のフレームワークが急増しています。 これらのフレームワークの例は、 TensorFlow.js です。これは、ブラウザーで機械学習モデルをトレーニング、実行、デプロイするためのTensorFlowのJavaScript対応ライブラリです。 MLの経験が限られている、またはまったくない開発者がTensorFlow.jsにアクセスできるようにするために、ライブラリには、すぐに使用できる事前トレーニング済みのモデルがいくつか付属しています。

事前にトレーニングされた機械学習モデルは、トレーニングする必要のないすぐに使用できる機械学習です。 TensorFlow.jsには、さまざまなユースケースに適した14が含まれています。 たとえば、一般的なオブジェクトを識別するための画像分類モデルと、体の部分を識別するための体のセグメンテーションモデルがあります。 これらのモデルの主な便利さは、名前が示すように、それらをトレーニングする必要がないことです。 代わりに、それらをアプリケーションにロードします。 使いやすさと事前にトレーニングされた性質に加えて、これらは厳選されたモデルです。これらは正確で高速であり、アルゴリズムの作成者によってトレーニングされ、Webブラウザ用に最適化されている場合があります。

このチュートリアルでは、TensorFlow.jsを使用して、 Question and Answer (QnA)の事前トレーニング済みモデルを提供するWebアプリケーションを作成します。 デプロイするモデルは、トランスフォーマーからの双方向エンコーダー表現(BERT)モデルであり、パッセージと質問を入力として使用し、パッセージからの質問に答えようとします。

GitHubなどのソースから数回クリックするだけでアプリケーションを構築、デプロイ、スケーリングするためのマネージドソリューションであるDigitalOceanのAppPlatformにアプリをデプロイします。 作成するアプリは、2つの入力フィールドを持つ静的ページで構成されています。1つはパッセージ用、もう1つは質問用です。 最終的には、GitHubから、TensorFlow.jsの事前トレーニング済みモデルの1つとDigitalOceanのアプリプラットフォームを使用して、質問と回答のアプリケーションを作成してデプロイします。

前提条件

このチュートリアルを完了するには、次のものが必要です。

ステップ1—アプリのインターフェースを作成し、必要なライブラリをインポートする

このステップでは、アプリのHTMLコードを記述します。これにより、インターフェースが定義され、アプリのライブラリがインポートされます。 これらのライブラリの最初のものはTensorFlow.jsであり、パッケージをローカルにインストールする代わりに、コンテンツ配信ネットワークまたはCDNからロードします。 CDNは、インターネットに提供するコンテンツを格納する複数の場所にまたがるサーバーのネットワークです。 このコンテンツには、TensorFlow.jsライブラリなどのJavaScriptファイルが含まれており、CDNからロードすることで、アプリケーションにそれらをパックする必要がなくなります。 同様に、QuestionandAnswerモデルを含むライブラリをインポートします。 次のステップでは、モデルを使用して特定の質問に答えるアプリのJavaScriptを記述します。

このチュートリアルでは、次のようなアプリを構築します。

アプリには5つの主要な要素があります。

  • アプリのテストに使用できる事前定義されたパッセージをロードするボタン。
  • パッセージの入力テキストフィールド(自分で作成またはコピーすることを選択した場合)。
  • 質問の入力テキストフィールド。
  • 質問に答える予測をトリガーするボタン。
  • Answer!ボタンの下にモデルの出力を表示する領域(現在は空白の空白)。

まず、ご希望の場所にtfjs-qna-doという名前の新しいディレクトリを作成します。 このディレクトリで、選択したテキストエディタを使用して、index.htmlという名前の新しいHTMLファイルを作成し、次のコードを貼り付けます。

index.html

<!DOCTYPE html>
<html lang="en-US">

<head>
    <meta charset="utf-8" />
    <!-- Load TensorFlow.js -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <!-- Load the QnA model -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/qna"></script>
    <link href="./style.css" rel="stylesheet">
</head>

<body>
    <div class="main-centered-container">
        <h1>TensorFlow.js Pre-trained "QnA" BERT model</h1>
        <h3 class="header-border">Introduction</h3>
        <p>This application hosts TensorFlow.js' pre-trained Question and Answer model, and it attempts to answer the question
        using a given passage. To use it, write a passage (any!) in the input area below and a question. Then, click the
        "answer!" button to answer the question using the given passage as a reference. You could also click the test
        button to load a pre-defined input text.</p>
        
        <h4>Try the test passage!</h4>
        <div id='test-buttons'></div>

        <div>
            <h4>Enter the model's input passage here</h4>
            <textarea id='input-text' rows="20" cols="100" placeholder="Write the input text..."></textarea>
        </div>
        
        <div>
            <h4>Enter the question to ask</h4>
            <textarea id='question' rows="5" cols="100" placeholder="Write the input text..."></textarea>
        </div>
        <h4>Click to answer the question</h4>
        <div id="answer-button"></div>
        <h4>The model's answers</h4>
        <div id='answer'></div>
        
        <script src="./index.js"></script>

        
    </div>
</body>

</html>

そのHTMLの内訳は次のとおりです。

  • 最初の<html>タグには、メタデータ、スタイルの定義、およびスクリプトのロードに使用される<head>タグがあります。 最初の要素は<meta>で、ここではページのcharsetエンコーディングをutf-8に設定します。 その後、TensorFlow.jsとCDNからの質疑応答モデルの両方をロードするための2つの<script>タグがあります。
  • 2つの<script>タグに続いて、CSSファイル(次に作成する)をロードする<link>タグがあります。
  • 次に、HTMLの<body>、つまりドキュメントのコンテンツがあります。 その中には、ページの要素を含むクラスmain-centered-container<div>タグがあります。 1つ目は<h1>ヘッダーで、アプリケーションタイトルと小さい<h3>ヘッダーがあり、その後にその仕組みを説明する簡単な紹介が続きます。
  • イントロダクションの下には、<h4>ヘッダーと<div>があり、パッセージ入力テキストフィールドにサンプルテキストを入力するボタンを追加します。
  • 次に、アプリの入力フィールドがあります。1つはパッセージ(モデルに読み取らせたいもの)用、もう1つは質問(モデルに答えてもらいたいもの)用です。 サイズを変更する場合は、rowsおよびcols属性を変更してください。
  • テキストフィールドの後に、ID button<div>があります。このボタンをクリックすると、テキストフィールドのテキストを読み取り、モデルへの入力として使用するボタンを後で追加します。
  • 最後に、モデルの出力を表示するために使用されるID answer<div>と、次のセクションで記述するJavaScriptコードを含めるための<script>タグがあります。

次に、CSSをプロジェクトに追加します。 index.htmlファイルを追加したのと同じディレクトリに、style.cssという名前の新しいファイルを作成し、次のコードを追加します。

style.css

body {
  margin: 50px 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial;
}

button {
  margin: 10px 10px;
  font-size: 100%;
}

p {
  line-height: 1.8;
}

.main-centered-container {
  padding: 60px;
  display: flex;
  flex-direction: column;
  margin: 0 auto;
  max-width: 960px;
}

.header-border {
  border: solid #d0d0d0;
  border-width: 0 0 1px;
  padding: 0 0 5px;
}

このCSSは、本文、ボタン、段落(<p>)の3つのHTML要素にスタイルを追加します。 bodyに、余白とパディングを追加し、デフォルトのフォントを変更します。 buttonに、マージンを追加し、フォントサイズを大きくします。 段落pに対して、line-height属性を変更します。 CSSには、コンテンツを中央に配置するクラスmain-centered-containerと、要素に実線を追加する別のクラスheader-borderがあります。

このステップでは、アプリのHTMLファイルを作成しました。 TensorFlow.jsライブラリとQnAモデルをインポートし、後で回答するパッセージと質問を追加するために使用する要素を定義しました。 次の手順では、要素を読み取り、予測をトリガーするJavaScriptコードを記述します。

ステップ2—事前トレーニング済みモデルを使用した予測

このセクションでは、アプリの入力フィールドを読み取り、予測を行い、予測された回答をHTMLで書き込むWebアプリのJavaScriptコードを実装します。 「回答」ボタンをクリックするとトリガーされる1つの機能でそれを行います。 クリックすると、両方の入力フィールドを参照して値を取得し、モデルへの入力として使用して、手順1で定義したIDoutput<div>に出力を書き込みます。 次に、アプリをローカルで実行してテストしてから、GitHubに追加します。

プロジェクトのディレクトリtfjs-qna-do/に、index.jsという名前の新しいファイルを作成し、次の変数を宣言します。

index.js

let model;

// The text field containing the input text
let inputText;

// The text field containing the question
let questionText;

// The div where we will write the model's answer
let answersOutput;

最初の変数modelは、QnAモデルを格納する場所です。 inputTextおよびquestionTextは、入力テキストフィールドと質問テキストフィールドへの参照です。 answersOutputは、出力<div>への参照です。

これらの変数に加えて、アプリのテストに使用するサンプルテキストを格納するための定数が必要になります。 サンプルパッセージとして、DigitalOceanに関するウィキペディアの記事を使用します。 この一節に基づいて、「DigitalOceanの本社はどこにありますか?」などのモデルの質問をすることができます。 そしてうまくいけば、それは「ニューヨーク」を出力します。

このブロックをindex.jsファイルにコピーします。

index.js

// Sample passage from Wikipedia.
const doText = `DigitalOcean, Inc. is an American cloud infrastructure provider[2] headquartered in New York City with data centers worldwide.[3] 
DigitalOcean provides developers cloud services that help to deploy and scale applications that run simultaneously on multiple computers.
DigitalOcean also runs Hacktoberfest which is a month-long celebration (October 1-31) of open source software run in partnership with GitHub and Twilio.
`;

次に、createButton()から始まるアプリの機能を定義します。 この関数はボタンを作成し、それをHTML要素に追加します。

index.js

function createButton(innerText, id, listener, selector, disabled = false) {
  const btn = document.createElement('BUTTON');
  btn.innerText = innerText;
  btn.id = id;
  btn.disabled = disabled;

  btn.addEventListener('click', listener);
  document.querySelector(selector).appendChild(btn);
}

createButton()は、アプリの2つのボタンを作成する関数です。 すべてのボタンが同じように機能するため、この関数はコードの繰り返しを回避します。 この関数には5つのパラメーターがあります。

  • innerText:ボタンのテキスト。
  • id:ボタンのID。
  • listener:ユーザーがボタンをクリックしたときに実行されるコールバック関数。
  • selector:ボタンを追加する<div>要素。
  • disabled:ボタンを無効または有効にするブール値。 このパラメーターのデフォルト値はfalseです。

createButton()は、ボタンのインスタンスを作成し、それを変数btnに割り当てることから始まります。 次に、ボタンのinnerTextid、およびdisabled属性を設定します。 ボタンは、クリックするたびにコールバック関数を実行するクリックイベントリスナーを使用します。 関数の最後の行は、selectorパラメーターで指定された<div>要素にボタンを追加します。

次に、createButton()を2回呼び出してアプリのボタンを作成するsetupButtons()という名前の新しい関数を作成します。 アプリのAnswer!ボタンを作成することから始めます。

index.js

function setupButtons() {
  // Button to predict
  createButton('Answer!', 'answer-btn',
    () => {
      model.findAnswers(questionText.value, inputText.value).then((answers) => {
        // Write the answers to the output div as an unordered list.
        // It uses map create a new list of the answers while adding the list tags.
        // Then, we use join to concatenate the answers as an array with a line break
        // between answers.
        const answersList = answers.map((answer) => `<li>${answer.text} (confidence: ${answer.score})</li>`)
          .join('<br>');

        answersOutput.innerHTML = `<ul>${answersList}</ul>`;
      }).catch((e) => console.log(e));
    }, '#answer-button', true);
}

関数が作成する最初のボタンは、予測をトリガーするボタンです。 最初の2つの引数、innerTextidは、ボタンのテキストとボタンに割り当てる識別子です。 3番目の引数は、リスナーのコールバックです(以下で説明します)。 4番目の引数、selector 、はのIDです

ボタンを追加したい場所(#answer-button )、および5番目の引数、disabled 、 に設定されていますtrue 、ボタンを無効にします(アプリがモデルをロードする前に予測しないようにするため)。 リスナーのコールバックは、 Answer!ボタンをクリックすると実行される関数です。 クリックすると、コールバック関数は最初に事前トレーニング済みモデルの関数findAnswers()を呼び出します。 (findAnswers()機能の詳細については、製品資料を参照してください)。 findAnswers()は、入力パッセージ(questionTextから読み取る)と質問(inputTextから読み取る)を引数として使用します。 モデルの出力を次のような配列で返します。

Model's output[
    {
        "text": "New York City",
        "score": 19.08431625366211,
        "startIndex": 84,
        "endIndex": 97
    },
    {
        "text": "in New York City",
        "score": 8.737937569618225,
        "startIndex": 81,
        "endIndex": 97
    },
    {
        "text": "New York",
        "score": 7.998648166656494,
        "startIndex": 84,
        "endIndex": 92
    },
    {
        "text": "York City",
        "score": 7.5290607213974,
        "startIndex": 88,
        "endIndex": 97
    },
    {
        "text": "headquartered in New York City",
        "score": 6.888534069061279,
        "startIndex": 67,
        "endIndex": 97
    }
]

配列の各要素は、次の4つの属性のオブジェクトです。

  • text:答え。
  • score:モデルの信頼水準。
  • startIndex:質問に答えるパッセージの最初の文字のインデックス。
  • endIndex:回答の最後の文字のインデックス。

モデルが出力を返すときに出力を表示する代わりに、コールバックはmap()関数を使用して、リスト<li>HTMLタグを追加しながらモデルの回答とスコアを含む新しい配列を作成します。 次に、配列の要素を回答間の<br>(改行)で結合し、結果をanswer <div>に割り当てて、リストとして表示します。

次に、createButton()への2番目の呼び出しを追加します。 強調表示された部分を最初のcreateButtonの下のsetupButtons関数に追加します。

index.js

function setupButtons() {
  // Button to predict
  createButton('Answer!', 'answer-btn',
    () => {
      model.findAnswers(questionText.value, inputText.value).then((answers) => {
        // Write the answers to the output div as an unordered list.
        // It uses map create a new list of the answers while adding the list tags.
        // Then, we use join to concatenate the answers as an array with a line break
        // between answers.
        const answersList = answers.map((answer) => `<li>${answer.text} (confidence: ${answer.score})</li>`)
          .join('<br>');

        answersOutput.innerHTML = `<ul>${answersList}</ul>`;
      }).catch((e) => console.log(e));
    }, '#answer-button', true);

  createButton('DigitalOcean', 'test-case-do-btn',
    () => {
     document.getElementById('input-text').value = doText;
    }, '#test-buttons', false);
}

この新しい呼び出しは、test-buttons <div>ボタンに追加され、変数doTextで前に定義したDigitalOceanサンプルテキストをロードします。 関数の最初の引数はボタンのラベル( DigitalOcean )、2番目の引数はid、3番目はパッセージ入力テキスト領域に値を書き込むリスナーのコールバックです。 doText変数、4番目はselector、最後はfalse値です(ボタンが無効にならないようにするため)。

次に、他の関数を呼び出すinit()という名前の関数を作成します。

index.js

async function init() {
  setupButtons();
  answersOutput = document.getElementById('answer');
  inputText = document.getElementById('input-text');
  questionText = document.getElementById('question');

  model = await qna.load();
  document.getElementById('answer-btn').disabled = false;
}

init()は、setupButtons()を呼び出すことから始まります。 次に、スクリプトの上部で定義した変数にアプリのHTML要素の一部を割り当てます。 次に、QnAモデルをロードし、回答(answer-btn)ボタンの無効な属性falseに変更します。

最後に、init()を呼び出します。

index.js

init();

アプリが完成しました。 テストするには、Webブラウザを開き、プロジェクトのディレクトリ絶対パスに/index.htmlを追加してアドレスバーに書き込みます。 ファイルマネージャアプリケーション(MacのFinderなど)を開き、「index.html」をクリックしてWebアプリケーションを開くこともできます。 この場合、アドレスはyour_filepath/tfjs-qna-do/index.htmlのようになります。

絶対パスを見つけるには、ターミナルに移動し、プロジェクトのディレクトリから次のコマンドを実行します。

pwd

その出力は次のようになります。

Output/Users/your_filepath/tfjs-qna-do

アプリを起動した後、モデルをダウンロードしてロードするまで数秒待つ必要があります。 アプリがAnswer!ボタンを有効にすると、準備ができたことがわかります。 次に、テストパッセージボタン(DigitalOcean)を使用するか、独自のパッセージを作成して、質問を入力します。

アプリをテストするには、「DigitalOcean」ボタンをクリックします。 パッセージ入力エリアには、DigitalOceanに関するサンプルテキストが表示されます。 質問入力領域に、「DigitalOceanの本社はどこにありますか?」と記入します。 パッセージから、答えは「ニューヨーク」であることがわかります。 しかし、モデルは同じことを言うでしょうか? Answer!をクリックして確認してください。

出力は次のようになります。

正しい! わずかな違いはありますが、5つの回答のうち4つは、DigitalOceanの本社がニューヨーク市にあると述べています。

このステップでは、Webアプリケーションを完成させてテストしました。 作成したJavaScriptコードは、入力パッセージと質問を読み取り、それをQnAモデルへの入力として使用して質問に回答します。 次に、コードをGitHubに追加します。

ステップ3—アプリをGitHubにプッシュする

このセクションでは、WebアプリケーションをGitHubリポジトリに追加します。 後で、リポジトリをDigitalOceanのApp Platformに接続し、アプリをデプロイします。

GitHubにログインすることから始めます。 メインページから、名前の下にある緑色のボタンまたは画面の右上隅にあるプラス記号をクリックして、新しいリポジトリを作成します。

どちらを選択しても、新しいリポジトリの作成画面が表示されます。 リポジトリ名フィールド(ユーザー名の後)で、リポジトリにtfjs-qna-doという名前を付けます。 プライバシー設定を選択し、リポジトリの作成をクリックして作成します。

ターミナルを開き、プロジェクトのディレクトリtfjs-qna-do/に移動します。 そこで、次のコマンドを実行して、新しいローカルGitリポジトリを作成します。

git init

次に、追跡するファイルをGitでステージングします。この場合は、ディレクトリ内のすべてのものです。

git add .

そしてそれらをコミットします:

git commit -m "initial version of the app"

リポジトリの主分岐の名前をmainに変更します。

git branch -M main

次に、ローカルリポジトリをGitHub上のリモートリポジトリにリンクします。

git remote add origin git@github.com:your-github-username/tfjs-qna-do.git

最後に、ローカルコードベースをリモートリポジトリにプッシュします。

git push -u origin main

コードをGitHubにプッシュするのが初めての場合は、GitHubのクレデンシャルを入力するように求められます。

コードをプッシュしたら、GitHubリポジトリに戻り、ページを更新します。 コードが表示されます。

このステップでは、WebアプリのコードをリモートのGitHubリポジトリにプッシュしました。 次に、このリポジトリをDigitalOceanアカウントにリンクし、アプリをデプロイします。

ステップ4— DigitalOceanAppPlatformにWebアプリケーションをデプロイする

この最後のセクションでは、質問と回答のアプリを、手順3で作成したGitHubリポジトリからDigitalOceanの App Platform )にデプロイします。

まず、DigitalOceanアカウントにログインします。 ログインしたら、右上隅にある Create 緑色のボタンをクリックしてから、Appsをクリックします。 これにより、新しいアプリの作成画面が表示されます。

ここでは、プロジェクトが存在するソースとしてGitHubを選択します。

DigitalOceanアカウントをGitHubにリンクしていない場合は、DigitalOceanにGitHubへのアクセスを許可するように求められます。 その後、リンクするリポジトリtfjs-qna-doを選択します。

[アプリプラットフォーム]ウィンドウに戻り、アプリのリポジトリ( tfjs-qna-do )、アプリのデプロイ元のブランチ( main )を選択し、を確認します。自動デプロイコードの変更ボックス; このオプションにより、コードをmainブランチにプッシュするたびにアプリケーションが再デプロイされます。

次の画面で、アプリを構成します、デフォルトの構成値を使用します。 追加の手順として、WebアプリのHTTPリクエストルートを、たとえばデフォルトのhttps://example.ondigitalocean.app/tfjs-qna-doからに変更する場合https://example.ondigitalocean.app/ my-custom-route HTTPRoutesの下のEditをクリックし、 my- ROUTES入力のcustom-route

次に、サイトに名前を付けます。 ここでも、デフォルトの tfjs-qna-do 名をそのままにするか、別の名前に置き換えることができます。

次へをクリックすると、最後の画面確定して起動が表示され、アプリの料金階層が選択されます。 アプリは静的なウェブページであるため、AppPlatformは無料のスタータープランを自動的に選択します。 最後に、 Starter App ボタンをクリックして、アプリケーションをビルドおよびデプロイします。

アプリがデプロイされている間、次のような画面が表示されます。

デプロイすると、次のように表示されます。

アプリにアクセスするには、アプリのURLをクリックして、クラウドにデプロイされているアプリにアクセスします。

結論

機械学習の分野が拡大するにつれて、Webブラウザを含む到達する環境やプラットフォームとともにそのユースケースも拡大します。

このチュートリアルでは、TensorFlow.jsの事前トレーニング済みモデルを使用するWebアプリケーションを構築してデプロイしました。 Question and Answer Webアプリは、質問とともにパッセージを入力として受け取り、事前にトレーニングされたBERTモデルを使用して、パッセージに従って質問に回答します。 アプリを開発した後、GitHubアカウントをDigitalOceanにリンクし、追加のコードを必要とせずにアプリケーションをAppPlatformにデプロイしました。

今後の作業として、アプリに変更を追加してGitHubにプッシュすることで、アプリの自動デプロイをトリガーできます。 また、新しいテストパッセージを追加し、回答の出力を表としてフォーマットして、読みやすさを向上させることもできます。

TensorFlow.jsの詳細については、その公式ドキュメントを参照してください。