生成AIの重いトークン化処理をWebアプリに組み込む

2023年12月15日掲載

キービジュアル

本記事はソフトバンク アドベントカレンダー 2023の15日目の記事です。

共通プラットフォーム開発本部の宮田銀河です。普段はAzure OpenAI Service スターターパッケージのWebアプリ (ChatUI) の開発を担当しています。

本記事では生成AIの重いトークン化処理をWebアプリに組み込む方法を紹介します。まず、トークン化について説明した後に、トークン化処理に必要な時間を計測し、最後に重いトークン化処理をWebアプリに組み込む方法を紹介します

目次

  • 生成AIにおけるトークン化について解説します
  • トークン化処理時間の計測をtiktokenとgpt-3-encoder それぞれについて実施し、比較をします
  • 速度では劣る gpt-3-encoder でもトークン化処理を工夫することで高速に処理出来る方法を解説します

トークン化とは

MicrosoftドキュメントのHow does tokenization work?によると、生成AIにおけるトークン化は、入力テキストと出力テキストをLLM で処理できる小さな単位に分割する処理です

さらに、補足情報としてOpenAIドキュメントのtokenizerを翻訳します。

> OpenAIの大規模言語モデル(GPTとも呼ばれます)は、テキストをトークンと呼ばれる単位で処理します。トークンは、一連のテキストに共通する文字列のことです。これらのモデルは、これらのトークンの間の統計的な関係を理解することを学び、トークンのシーケンスの次のトークンを生成することに優れています。なお、トークン化の過程はモデルによって異なります。GPT-3やCodexなどの従来のモデルとは異なるTokenizerを使用する、より新しいモデル(GPT-3.5やGPT-4など)では、同じ入力テキストに対して異なるトークンが生成されます。一般的な英語テキストにおいて、一つのトークンは通常、テキストの約4文字に相当するというのが一つの目安です。これはおおよそ3/4の単語に相当します(つまり、100のトークンは約75の単語に相当します)。テキストをトークン化するためのプログラムインターフェースが必要な場合は、Python向けのtiktokenパッケージをご覧ください。JavaScriptの場合、コミュニティがサポートする@tiktokenパッケージは、ほとんどのGPTモデルと連携します。translated by GPT3.5

以前はtokenizerのライブラリにgpt-3-encoderを使うのが一般的でしたが、現在だとtiktokenが使われているようです。

トークン化処理時間の計測

この節では、トークン化のライブラリのgpt-3-encoderとtiktokenの処理時間を比較します。

1,000文字から300,000文字の日本の文章をトークン化するときの処理時間を計測しました。1,000文字ずつ増やして計算しました。トークン化に使用した日本語は前回のブログの最初の1,000文字です。

使用したPCのスペック 

  • MacBook Pro 13-inch, 2020
  • Mac Os Ventura 13.6
  • 2.3 GHz クアッドコアIntel Core i7
  • 16 GB RAM

使用したコード

以下は、gpt-3-encoderのトークン化処理時間を計測するために使用したTypescirptのコードです。なお、tiktokenを使用する場合は*①のコメントアウトを解除してコードを実行してください。


import * as fs from "fs";
import { encode } from "gpt-3-encoder";
// *①tiktokenの計測時に使う
// import { get_encoding } from "tiktoken";
// const encode = get_encoding("cl100k_base").encode;
const outputFilepath = "encodeResult.csv";
const inputFilepath = "input.txt";

// sliceを用いてテキストの先頭の1000文字を取得
const blogText = fs.readFileSync(inputFilepath, "utf-8").slice(0, 1000); 
let text = "";
console.log("start");
const csvData = [];
for (let i = 0; i < 300; i++) {
  text = text + blogText;
  const startTime = performance.now(); // 開始時間
  // token化処理
  encode(text).length;
  const endTime = performance.now(); // 終了時間

  csvData.push({
    i: i + 1,
    length: text.length,
    time: endTime - startTime,
  });
}
appendToCSV(csvData, outputFilepath);

const appendToCSV = (
  data: { i: number; length: number; time: number }[],
  filepath: string
) => {
  const csvData = data
    .map(({ i, length, time }) => `${i},${length},${time}`)
    .join("\n");
  fs.appendFileSync(filepath, csvData);
};

計測結果

トークン化処理時間と文字数の関係

gpt-3-encoderの処理時間は二次関数的に増加しましたが、tiktokenの処理時間は線形のままでした。

以下は結果の一部を抜粋した表です。文字数が1,000文字から300倍の300,000文字になった時、gpt-3-encoderの処理時間は約12,000倍に増加しましたが、tiktokenは約77倍の増加にとどまりました文章のトークン化を行う場合は、tiktokenの方が効率的な処理ができるようです

文字数gpt-3-encoderの処理時間 (s)tiktokenの処理時間(s)
1,0000.000.002
10,000
0.01
0.005
50,0001.030.024
100,0003.420.044
200,00013.890.083
300.00033.850.129

重い処理をWebアプリに組み込むリスクとその対策方法

重い処理をWebアプリに組み込むリスク

上述の計測よりgpt-3-encoderよりもtiktokenの処理時間の方が早いことがわかりましたが、gpt-3-encoderを使っている人もいると思いますので、この節ではgpt-3-encoderのような重い処理をWebアプリケーションに実装する方法を検討していきます

この場合、JavaScriptを使用したフロントエンドのWebアプリやサーバサイドには以下のリスクが発生します。

フロントエンドに実装していた場合は、Webページが硬直したように見えてしまい、ユーザビリティが低下します。サーバサイドに実装していた場合は、フロントエンドにレスポンスを返せなくなり、その間のユーザのリクエストを処理できなくなります。

このようなリスクを回避または軽減するためには、以下のような対策が考えられます。

リスクへの対策方法

上記リスクに対してリスク回避、リスク軽減(低減)の観点で対策を考えます。

【リスク回避】:

  •  ユーザに大量の文章を入力させないように制限する。

【リスク軽減】:

  • 処理時間の短いトークン化ライブラリを使用する(例:tiktokenを使用する)。
  • 別のスレッドやプロセスで処理を行う。
  • 非同期処理を行うためにQueueなどを使用する。
  • テキストを分割してからトークン化処理を行う。

これらの対策のうち、システムに影響が少ないと考えられる”Textを分割してトークン化処理を行う”方法について検証を行いました。

以下は、処理時間を検証するためのサンプルコードです。

Textを分割してからgpt-3-encoderを呼び出す

処理時間を検証するために、以下のようなコードを準備しました。


    let encodeText = blogText.repeat(100); // 100,000文字に増やす
    const start = performance.now(); 

    const chunkCount = 5; //文章の分割数。この場合は文章を5分割する
    const encodeTextLength = encodeText.length;
    const chunkSize = encodeTextLength / chunkCount;

    let length = 0;
    for (let i = 0; i < encodeText.length; i += chunkSize) {
      const chunk = encodeText.slice(i, i + chunkSize); // textを分割する 
      const encodedChunk = encode(chunk);   // encode処理
      length = length + encodedChunk.length;
    }
    const end = performance.now(); 
    console.log(
      `文字数:${encodeText.length}, encode 処理時間(ms):${
        end - start
      },token:${length}`
    );
    console.log("end request");

70,000文字と100,000文字のテキストを2分割、3分割、10分割した場合のトークン化処理時間を計測しました。下記の図は、それぞれの分割数ごとのgpt-3-encoderの処理時間が平均値で示されています。

文章を2分割、3分割、10分割した場合のトークン化処理時間
文字数 原文のgpt-3-encoderの処理時間 (s)2分割した時の処理時間 (s)3分割した時の処理時間 (s)10分割した時の処理時間 (s)
70,001.850.980.500.11
100,0004.021.991.210.15

この表から次のことがわかりました。

  • gpt-3-encoderを使った場合、70,000文字のトークン化処理には平均1.85秒、100,000文字は平均4秒の処理時間が必要です。
  • 一方で、テキストを分割してトークン化し、それぞれの結果を結合することで処理時間を短縮できます
  • テキストを10分割した場合、70,000文字の処理時間は1.85秒から平均0.11秒に、100,000文字は4秒から平均0.15秒に短縮することができます。

ただし、一部のテキストの分割箇所によってトークン数に差分が出る可能性があります。例えば、「hello world」が「hel」「lo word」のように分割される場合、トークン化処理の結果に差が生じます。このような場合、トークン化の結果を正確に結合する必要があります。

まとめ

本記事では、生成AIの重いトークン化処理をWebアプリに組み込む方法を紹介しました。トークン処理に用いられるgpt-3-encoderとtiktokenの処理時間を計測した結果、文章量が増えるとgpt-3-encoderの処理時間は二次関数的に増加する一方、tiktokenの処理時間は線形のままであることがわかりました。

また、gpt-3-encoderのような二次関数的に増加する処理がWebアプリに実装されていた場合の対策方法についても述べました。その対策の一つとして、テキストを分割してからトークン化処理を行う方法についても検証しました。分割することで処理時間を劇的に短縮することができ、特に10分割した場合は20倍以上も処理時間が短縮されることがわかりました

 それでは、アドベントカレンダー16日目にバトンを渡します!

関連サービス

MSPサービス

MSP(Managed Service Provider)サービスは、お客さまのパブリッククラウドの導入から運用までをトータルでご提供するマネージドサービスです。

Azure OpenAI Service スターターパッケージ

Azure OpenAIを利用できる環境を迅速に構築し、検証することができるサービスです。セキュアな環境で安心してAIを活用することができます。

Azure OpenAIスタータパッケージ

Azure OpenAIを利用できる環境を迅速に構築し、検証することができるサービスです。セキュアな環境で安心してAIを活用することができます。

 

おすすめの記事

条件に該当するページがございません