2023年7月20日木曜日

Llama.cppを使ってAmpere A1でLlama 2を動かしてみる

MetaよりLlama 2の提供が開始されました。動かすには沢山のリソースが必要なのだろうなと思っていたら、以下の記事を見つけました。


CPUでも動くとのことで、以前に作成したOracle Cloudの無料枠のAmpere A1のインスタンス(4OCPU、24GB)で動かしてみました。
OpenAI Whisperを使った文字起こしアプリの作成(3) - Flaskを使ったAPIサーバー

HuggingFaceよりTheBloke/Llama-2-13B-chat-GGMLllama-2-13b-chat.ggmlv3.q8_0.binをダウンロードして使用しています。Provided filesのUse caseには、リソース使用量が多く遅いと記載されていますが、メモリについては24GBあるので足りそうですし、動作させてみるのが目的なのでaccuracyが高そうなモデルを選んでいます。

Ampere A1のインスタンスにAPIサーバーを作成し、フロントエンドをAPEXで作成します。

作成したアプリケーションに、プロンプトとして以下を与えました。

User: Who was the Japanese prime minister during Reagan's administration?
Assistant:

応答は以下です。

Assistant: The Japanese Prime Minister during Ronald Reagan's administration (1981-1989) was Yasuhiro Nakasone. He served as Prime Minister from 1982 to 1987.

応答が得られるまでに1分程度かかりました。


プロンプトに日本語を入力してみました。

User: 現在のアメリカの大統領は誰ですか?
Assistant:

応答は以下です。

Assistant: The current President of the United States is Joe Biden. He was inaugurated on January 20, 2021, and is serving his first term as President.

応答は英語でしたが、正しく解釈されています。


日本に関する質問をしてみました。

User: 現在の日本の首相は誰ですか?
Assistant:

日本語で学習していると応答が日本語になることがあるのかもしれません。しかし、3年前の情報です。


これから行った作業について説明します。

Llama.cppのPythonバインディングをインストールします。

pip install llama-cpp-python

ubuntu@mywhisper2:~$ pip install llama-cpp-python

Collecting llama-cpp-python

  Downloading llama_cpp_python-0.1.73.tar.gz (1.6 MB)

     |████████████████████████████████| 1.6 MB 27.6 MB/s 

  Installing build dependencies ... done

  Getting requirements to build wheel ... done

    Preparing wheel metadata ... done

Collecting diskcache>=5.6.1

  Downloading diskcache-5.6.1-py3-none-any.whl (45 kB)

     |████████████████████████████████| 45 kB 4.3 MB/s 

Collecting typing-extensions>=4.5.0

  Downloading typing_extensions-4.7.1-py3-none-any.whl (33 kB)

Requirement already satisfied: numpy>=1.20.0 in /usr/local/lib/python3.8/dist-packages (from llama-cpp-python) (1.23.5)

Building wheels for collected packages: llama-cpp-python

  Building wheel for llama-cpp-python (PEP 517) ... done

  Created wheel for llama-cpp-python: filename=llama_cpp_python-0.1.73-cp38-cp38-linux_aarch64.whl size=250785 sha256=292028cdc30da399c4dfc90d3a465b46a0834d377b1956de529377a79b1e0942

  Stored in directory: /home/ubuntu/.cache/pip/wheels/0e/b1/1c/924b1420d220bbb77b4b7c5e533f23b2cf683d28ad20ee5b62

Successfully built llama-cpp-python

ERROR: tensorflow-cpu-aws 2.13.0 has requirement typing-extensions<4.6.0,>=3.6.6, but you'll have typing-extensions 4.7.1 which is incompatible.

Installing collected packages: diskcache, typing-extensions, llama-cpp-python

Successfully installed diskcache-5.6.1 llama-cpp-python-0.1.73 typing-extensions-4.7.1

ubuntu@mywhisper2:~$


使用するモデルのダウンロード・リンクを取得し、wgetでファイルをダウンロードします。

APIサーバーをllama2-server.pyとして作成します。コードは以下になります。

pythonで実行します。

python llama2-server.py

ubuntu@mywhisper2:~$ python llama2-server.py 

llama.cpp: loading model from /home/ubuntu/llama-2-13b-chat.ggmlv3.q8_0.bin

llama_model_load_internal: format     = ggjt v3 (latest)

llama_model_load_internal: n_vocab    = 32000

llama_model_load_internal: n_ctx      = 512

llama_model_load_internal: n_embd     = 5120

llama_model_load_internal: n_mult     = 256

llama_model_load_internal: n_head     = 40

llama_model_load_internal: n_layer    = 40

llama_model_load_internal: n_rot      = 128

llama_model_load_internal: freq_base  = 10000.0

llama_model_load_internal: freq_scale = 1

llama_model_load_internal: ftype      = 7 (mostly Q8_0)

llama_model_load_internal: n_ff       = 13824

llama_model_load_internal: model size = 13B

llama_model_load_internal: ggml ctx size =    0.09 MB

llama_model_load_internal: mem required  = 15025.96 MB (+ 1608.00 MB per state)

llama_new_context_with_model: kv self size  =  400.00 MB

AVX = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | VSX = 0 | 

 * Serving Flask app 'llama2-server'

 * Debug mode: on

INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

 * Running on all addresses (0.0.0.0)

 * Running on https://127.0.0.1:8443

 * Running on https://10.0.0.131:8443

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.

 * Running on all addresses (0.0.0.0)

 * Running on https://127.0.0.1:8443

 * Running on https://10.0.0.131:8443

INFO:werkzeug:Press CTRL+C to quit

Press CTRL+C to quit

INFO:werkzeug: * Restarting with stat

 * Restarting with stat

llama.cpp: loading model from /home/ubuntu/llama-2-13b-chat.ggmlv3.q8_0.bin

llama_model_load_internal: format     = ggjt v3 (latest)

llama_model_load_internal: n_vocab    = 32000

llama_model_load_internal: n_ctx      = 512

llama_model_load_internal: n_embd     = 5120

llama_model_load_internal: n_mult     = 256

llama_model_load_internal: n_head     = 40

llama_model_load_internal: n_layer    = 40

llama_model_load_internal: n_rot      = 128

llama_model_load_internal: freq_base  = 10000.0

llama_model_load_internal: freq_scale = 1

llama_model_load_internal: ftype      = 7 (mostly Q8_0)

llama_model_load_internal: n_ff       = 13824

llama_model_load_internal: model size = 13B

llama_model_load_internal: ggml ctx size =    0.09 MB

llama_model_load_internal: mem required  = 15025.96 MB (+ 1608.00 MB per state)

llama_new_context_with_model: kv self size  =  400.00 MB

AVX = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | VSX = 0 | 

WARNING:werkzeug: * Debugger is active!

 * Debugger is active!

INFO:werkzeug: * Debugger PIN: 610-498-273

 * Debugger PIN: 610-498-273


これから、このAPIサーバーを呼び出すAPEXアプリケーションを作成します。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。ユーザー・インターフェースはすべてデフォルトで作成されるホーム・ページに実装します。

名前Llama2 UIとし、アプリケーションの作成を実行します。


RESTデータ・ソースを作成して、APIサーバーへの呼び出しを行います。

共有コンポーネントRESTデータ・ソースを開きます。


作成済みのRESTデータ・ソースが一覧されます。作成をクリックします。


RESTデータ・ソースの作成最初からを選択します。

へ進みます。


RESTデータ・ソース・タイプとして簡易HTTPを選択します。名前Llama2とします。URLエンドポイントは、Amere A1で動作しているAPIサーバーを呼び出すURLになります。以下のような形式になります。

https://インスタンスのホスト名/chat

へ進みます。


リモート・サーバーベースURLサービスURLパスが認識されます。

通常は変更不要なので、値を確認してへ進みます。


ページ区切りなしです。

へ進みます。


認証が必要ですオフです。

今回作成するRESTデータ・ソースは特殊なため、自動検出はできません。RESTソースの手動作成を実施します。


RESTデータ・ソースLlama2が作成されます。ただし、ほとんど構成されていません。

RESTデータ・ソースを編集するために開きます。


操作データベース・アクション行のフェッチだけを残し、他を削除します。


データベース・アクション行のフェッチである操作鉛筆アイコンをクリックして開きます。

HTTPメソッドをGETからPOSTに変更します。変更するとリクエスト本文テンプレートの項目が現れるので、以下を記述します。
{
"max_tokens": #MAX_TOKENS#,
"query": "#PROMPT#"
}
上記を入力後、生成をクリックします。


操作パラメータ本文との同期というボタンが現れます。これをクリックすると、リクエスト本文に含まれる置換文字列操作パラメータとして追加されます。

今回は操作パラメータとしてMAX_TOKENSPROMPTが追加されます。


操作パラメータのMAX_TOKENSは、必須オンにし、デフォルト値として64を設定します。


操作パラメータPROMPTについては、後で必須オンにします。サンプル・データを使ってデータ・プロファイルを検出する際に、デフォルト値の無い必須パラメータがあるとエラーが発生します。そのため、データ・プロファイルの検出ができなくなります

パラメータの追加をクリックし、Content-TypeヘッダーとAcceptヘッダーを追加します。両方ともに、値はapplication/jsonです。

パラメータタイプHTTPヘッダー名前Content-Type目的入力です。デフォルト値としてapplication/jsonを指定します。静的オンにすることにより、つねにデフォルト値が送信されます。


同様にパラメータとしてAcceptヘッダーを追加します。

以上で操作の設定は完了です。

変更の適用をクリックします。


続いてデータ・プロファイルの編集を行います。

データ・プロファイルを編集するにあたって、サンプルとなるレスポンスをファイルに落とします。

curlコマンドを使ってAPIサーバーを呼び出します。APIサーバーの応答がsample.jsonに書き込まれます。
curl -X POST -H "Content-Type: application/json" -d '{ "max_tokens": 64, "query": "User: Why is sky blue?\nAssistant:" }' -o sample.json https://ホスト名/chat
sample.jsonは以下のような内容になります。
{
  "id": "cmpl-2293b348-82a9-44f9-b270-e04df749473a",
  "object": "text_completion",
  "created": 1689824214,
  "model": "/home/ubuntu/llama-2-13b-chat.ggmlv3.q8_0.bin",
  "choices": [
    {
      "text": "User: Why is sky blue?\nAssistant: That's a great question! The reason the sky appears blue is because of a phenomenon called Rayleigh scattering. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions,",
      "index": 0,
      "logprobs": null,
      "finish_reason": "length"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 64,
    "total_tokens": 76
  }
}

データ・プロファイルの編集をクリックします。


データ・プロファイル行セレクタとしてchoicesを指定します。再検出サンプル・レスポンスとしてcurlを実行して出力したsample.jsonを指定します。

上記で、データ・プロファイルの再検出をクリックします。


新規列としてTEXTINDEX_などが検出されます。

データ・プロファイルの置換をクリックします。


としてTEXTINDEX_などが追加されていることを確認し、変更の適用をクリックします。


操作パラメータPROMPTの編集画面を再度開き、必須オンにします。


以上でRESTデータ・ソースは完成です。

変更の適用をクリックします。


Llama2のAPIサーバーへの問い合わせを行なう画面を作成します。

ページ・デザイナホーム・ページを開きます。

出力のトークン数を指定するページ・アイテムP1_MAX_TOKENSを作成します。タイプ数値フィールドラベルMax Tokensです。デフォルトタイプ静的値を選び、静的値として64を設定します。


Llama2に渡すプロンプトを入力するページ・アイテムP1_PROMPTを作成します。タイプテキスト領域ラベルPromptとします。


APIサーバーへの問い合わせを発行するボタンQUERYを作成します。ラベルQuery動作アクションとして動的アクションで定義を選択します。必ずしも必要ではありませんが、テンプレート・オプションWidthStrechに変更し、ボタンの幅をページ幅まで広げます。

動的アクションは、対話モード・レポートのリージョンを作成したのちに作成します。


対話モード・レポートのリージョンResponseを作成します。ソース位置としてRESTソースを選択し、RESTソースとしてLlama2を選びます。送信するページ・アイテムP1_MAX_TOKENSP1_PROMPTを含めます。


RESTデータ・ソースLlama2のパラメータMAX_TOKENSには、アイテムP1_MAX_TOKENSが割り当たります。


パラメータPROMPTには、アイテムP1_PROMPTが割り当たります。


TEXTがLlama2によって生成された文字列です。改行が適切に表示されるよう、タイプリッチ・テキストに変更し、設定書式マークダウンにします。


ボタンQUERY動的アクションを作成します。

リージョンResponseのRESTデータ・ソースが行のフェッチを行なうときにAPIリクエストが発行され、そのレスポンスが対話モード・レポートに表示されます。つまり、リージョンResponseをリフレッシュするだけで新しくAPIリクエストが発行され、対話モード・レポートの表示が更新されます。

動的アクション識別名前onClick Refreshとします。タイミングボタンのデフォルトから変更せず、イベントクリック選択タイプボタンボタンQUERYです。


TRUEアクションとしてリフレッシュを選択します。影響を受ける要素選択タイプリージョンリージョンとしてResponseを選択します。クライアントの条件タイプとしてアイテムはnullではないを選択し、アイテムP1_PROMPTを選択します。

ページ・アイテムP1_PROMPTに値がないときにリージョンResponseのリフレッシュはしない、つまりAPI呼び出しも行いません。


以上でアプリケーションは完成です。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/llama2-ui-test.zip

Flaskのサーバーが応答するのに時間がかかるし、応答を返す前に新たなリクエストを受け付けると、Llamaのレスポンスが##################になったりしますが、Llama2のお試しにはなるでしょう。

また、Oracle APEXでノーコードでREST APIを扱うサンプルにもなっています。

Oracle APEXのアプリケーション作成の参考になれば幸いです。