PythonでRinna社が提供している対話LLM rinna/japanese-gpt-neox-3.6b-instruction-sft を動かすことで、ローカルで動作するChatGPTのような対話AIソフトを作成しました!
筆者自身もLLMや自然言語AIに全く詳しくなく1、Pythonに触ったのも久々だったのですが、言語モデルを手元で動かすのは案外簡単(大嘘2)だったため、本記事ではその紹介をしたいと思います!
↑アイキャッチのこれは成果物であるTauri製アプリです。内部でPython3によって前述のrinna言語モデルを動かすことで実現しています(後述)。
リポジトリ: https://github.com/anotherhollow1125/rinna_gpt
リリース: https://github.com/anotherhollow1125/rinna_gpt/releases
Windows限定ですがインストーラ(rinna_gui_0.0.5_x64_ja-JP.msi)からインストールするだけで動きます!(CPUで演算させているので遅いのはご容赦ください...また、Cドライブを7GBほど持っていきます)
なお、タイトルにオンプレと書きましたがLAN内サーバーで動かすのではなく手元のPCで動かす想定です。
ある日
※本物語は完全なるフィクションです。筆者が筆者の上司にあたる人物より該当のソフトウェアの作成を指示されたという事実は全くありません。
上司「今流行りのチャットGPT?ってやつあるじゃんあんな感じでAIと対話できるサービスを作って、社内で利用させたいんだけどできる? 」
筆者「ChatGPTなら、APIが公開されているのでAPIキーを発行すればすぐ組み込めますよ」
上司「...いや、chatGTPは使わないで実現してほしいんだよね」
筆者「(いちいち表記ゆれしてくるの何なんだ...GTPって脂肪肝かよ)」
筆者「何故です...?」
上司「OPEN AIに社内機密が漏えいする可能性があるからね。実際サムスンの例があるのでOpenaiのサービス利用はなしの方向性でお願いしたいです! 」
筆者「(なんだってうちの上司はこういった類のニュースだけはチェックしてるんじゃ...某ソースコード流出事件以降、GitHubの利用すら禁止してくるしよぉ...)」
筆者「つまり、オンプレミス、オフラインで使用できるChatGPTが欲しいということでしょうか?」
上司「そんな感じでイイカンジでお願いしたいかな」
筆者「知りません。他当たってください(技術的には可能かもしれませんがとても高性能なグラボととても高性能なCPUと大容量のメモリをモリモリ積んだハイスペックPCが必要になります)」
上司「本音と建前が逆ぅ... (しかも建前?もなかなか強欲...)」
...という成り行きでChatGPT以外の言語モデルをローカルで動かすことを命じられた筆者。言語モデル入門の足がかりを探します!
とりあえず動く言語モデル探し ~キ○ーピー3分コーディング~
いきなりローカルで環境構築して動かすのも大変なので、公開されている言語モデルを探してきてGoogle Colaboratoryで動かしてみようと思います。
「GPT オープンソース」や「LLM 日本語」みたいに色々と工夫して検索してみた感じ、以下2つのプレスリリースが目に入りました。
他にもありそうですがとりあえずこの2つを動かしてみましょう...!
...と思ったのですが、36億パラメータや68億パラメータモデルは、ColabolatoryのシステムRAM制限に引っかかってしまったので、とりあえず触る目的でパラメータが少ないやつにしてみます。
Rinna GPT
36億(3.6B)モデルは
なのですがColaboratoryの無料枠で使えるシステムRAM12GBメモリを全て消費してしまいエラーになったので、ソースコードは流用し、モデルだけGPT2のもの(パラメータ数は13億)にすげ替えて実行してみます
ではいよいよ早速 Google Colaboratory にアクセスし、ノートブックを新規作成して動かしてみましょう!
Colaboratoryにはtorch
類のパッケージは予め入っているようなので、デフォルトで入っていないtransformers
パッケージをpip
で入れましょう。
!pip install 'transformers[sentencepiece]'
行頭の!
は特殊な命令を実行するために必要なので除かず入れてください。左の を押し実行してtransformers
を入れます。
transformersパッケージは、HuggingFace という機械学習コミュニティが提供しているパッケージで、モデルを扱いやすくするためのAPIを提供してくれているようです...大丈夫です、筆者も今回初めて知りましたがこれ以上の詳細は不要です。さすがAPIパッケージ。
2つ目のセルが本命です!
必須ではありませんが、左上の編集
>ノートブックの設定
>ハードウェアアクセラレータ
をNone
からGPU
に変更することで若干速く実行されます。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
# トークナイザー( 文章をいい感じに分解(トークナイズ)してくれるやつ )指定
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt2-medium", use_fast=False)
tokenizer.do_lower_case = True # due to some bug of tokenizer config loading
# モデル指定
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")
# グラボが使える場合使う
# VRAMが足りない時はこのif文を消す
if torch.cuda.is_available():
model = model.to("cuda")
text = "西田幾多郎は、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")
with torch.no_grad():
output_ids = model.generate(
token_ids.to(model.device),
max_new_tokens=100,
min_new_tokens=100,
do_sample=True,
temperature=0.8,
pad_token_id=tokenizer.pad_token_id,
bos_token_id=tokenizer.bos_token_id,
eos_token_id=tokenizer.eos_token_id
)
output = tokenizer.decode(output_ids.tolist()[0])
print(output)
コピペして実行しましょう!筆者の場合は次のような出力が出ました。
西田幾多郎は、こうした「文化」が、現代において非常に有用な「文化」であり、「古典」であると捉え、現代に活かせる「文化」を、社会や地域、国、そして時代を巻き込んだ「文化」へと変容させていくことを示唆しています。 「文化」は、その時代の変化や社会にそって変化していきますが、それをただ捉え、その時代にふさわしい、よりよいものとするためには、新しい文化を創造し、取り入れる必要があります。 そのためには
それっぽい文章が出ましたね...!第一歩を踏み出せました。
ところで西田幾多郎って誰()
OpenCALM
お次はサイバーエージェントの方を試してみたいと思います!OpenCALMの本命は68億パラメータの
なのですがRinna同様無料枠ではシステムRAMが足りないので、10億パラメータの
の方を動かしてみました
Rinnaの時と同様、transformers
を入れます。(トークナイザーのcentencepiece
の指定はOpenCALMでは不要みたいです)
!pip install transformers
では早速実行!(AutoModelForCausalLM.from_pretrained("cyberagent/open-calm-1b", device_map="auto", torch_dtype=torch.float16)
の、device_map="auto", torch_dtype=torch.float16
はエラーになってしまったので削除しています)
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("cyberagent/open-calm-1b")
tokenizer = AutoTokenizer.from_pretrained("cyberagent/open-calm-1b")
inputs = tokenizer("AIによって私達の暮らしは、", return_tensors="pt").to(model.device)
with torch.no_grad():
tokens = model.generate(
**inputs,
max_new_tokens=64,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.05,
pad_token_id=tokenizer.pad_token_id,
)
output = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(output)
筆者の環境では次のような出力になりました
AIによって私達の暮らしは、より便利に快適なものへと進化してきました。
電気やガスなどのエネルギーはもちろんのこと、「食」「睡眠」といった生活の基本となるものまでをも自動化しています。「IoT(Internet of Things)」と呼ばれる技術の進化により「つながる世界」、つまりモノのインターネットが急速に進化し続けています。」と語るのは株式会社MJI代表取締役社長兼CEOの
ところでOpenCALMのCALMってなんの略かなぁと思っていたのですがサイバーエージェント言語モデル (CyberAgent Langage Model) でしょうか。なんかかっこいい感じがしました
Colaboratoryでの実行所感Q&A
Q. モデルはいつ/どこでダウンロードしたんだ...?
A. transformers
パッケージにより自動的にダウンロードされています。AutoModelForCausalLM.from_pretrained
にてHuggingFaceへの登録名を指定するだけでモデルを取ってきてくれる親切仕様なのです!
Q. じゃあオフラインで実行できなくない...?
A. 最初に一度ダウンロードしてしまうと以降はキャッシュされるのでオフラインでも動いてくれるようになります。パスも特に変更する必要はないです。オフライン実行の詳細は次の記事様が詳しかったです、
が、どのみち最初にはダウンロードが必要ですし、キャッシュが残っていれば特に細工なしでもオフラインで動いたので、本記事ではこれ以上「オフラインであること」にはこだわらないことにします。
Q. 結局何やっているのかわからないのだけど...
A. 筆者にも説明の技量がありません!!! 一言で言うと、「プロンプトの次に来る可能性が最も高い単語を予測している」だけらしいです。ChatGPT-3.5や4を使っていると忘れてしまいがちですが、パラメータの少ないモデルだと実際そのような出力をしているように感じられます。 model.generate
メソッドの各種パラメータにつきましては次の記事様が詳しかったです。
手元で動かしたい ~環境の条件と構築~
Colaboratoryの無料枠では動かせるモデルに限界があったので、ここからはローカルに環境を構築して実行してみたいと思います!
なお最終的に対話モデルを動かしたいのでここからはRinnaの方に絞って解説していきます
実行環境
環境を構築する前に断っておくとここからはマシンパワーが必要です。十分なDRAMに加え、ミドル~ハイエンドCPUあるいはGPUが必要になります。
筆者の環境はCPUパワーでゴリ押しています、一応以下に動作した筆者の環境を書いておきます4。最低スペックでどれぐらい必要かは検証していません。
筆者環境
- Windows 11
- 以降解説も全てWindowsです。LinuxやMacの方はごめんなさい
- CPU: Ryzen 9 3950X
- そのほかの環境では試していないので最低限必要なCPUはわからないです
- 多分スレッドが多くて高性能な程速いです
- メモリ: 96GB
- 最低でも16GBは必要だと思います
- このうちRinna 3.6Bは14GB程を持っていきます
筆者は確認できていませんが、グラフィックボードでゴリ押しする場合はVRAM 12GB以上のモデルが必要らしいです(VRAM 8GBでは無理でした)。
環境構築① Python環境(pyenv+pipenv)の導入
解説自体はシンプルだと思いますが、Pythonの環境構築なので、各種環境に依存した問題がたくさん出るかと思います。その辺の解決は各自頑張っていただけると幸いです。
環境が汚れても良いならばpipでいきなり各種パッケージをインストールしてしまっても良いですが、仮想環境を作る方法を推奨します。
というのもHuggingFace公式も仮想環境を推奨しているのと、依存関係の問題が発生した際に作り直しやすい、他のハードへの移行がし易い等メリットしかないためです。むしろ直接インストールしてしまうと大変なことになります...
ベストプラクティスを知らないのですが、今回はpyenv+pipenvで環境構築を行いました。導入済みであったり他の方法で管理していたりする場合は本節は読み飛ばしてください。
- pyenv: Python本体のバージョンを複数管理するツールです。
- pipenv: フォルダごとにパッケージの管理を行うツールです。
pyenv
PowerShellでの導入が一番楽な気がします。
Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"
running scripts is disabled on this system.
みたいな内容が書かれた真っ赤なエラーが出た場合ExecutionPolicy
がRestricted
になっているのが多分原因です。その場合RemoteSigned
等に変更してください。
Set-ExecutionPolicy RemoteSigned
シェルを再起動するとpyenv
が使えるようになっています。LLMを動かすPythonのバージョンはpipenv側で指定しますが、pipenv導入にPythonを使用するので、デフォルトとなるPythonを入れましょう。最新は3.12
っぽいですが筆者は3.11.3
で各種動作の確認を行ったため、3.11.3
とさせていただきます。
pyenv install 3.11.3
pyenv global 3.11.3
pipenv
こちらはpipで取ってくるツールになります。簡単!
pip isntall pipenv
使い方は次節で実際に使っていくのでそこで解説します。
環境構築② 必要なパッケージのインストール
Rinna GPTを動かすための依存を入れていきます!
まずはプロジェクト用ディレクトリを作成した後、Pythonのバージョンを3.11.3
として仮想環境を作成します。
mkdir rinna_app
cd rinna_app
pipenv --python 3.11.3
PyTorch
そしてまずはPython機械学習のデファクトスタンダードとも言えるライブラリtorch
を入れる...
のですが、CPUで動かすか、グラフィックボードで動かすか、どちらにするかで、打つpipコマンドが変わってきます。PyTorch公式にて環境を入力すると打つべきコマンドを教えてくれるページがあるので、そこを参照します。
グラフィックボード(CUDA)を利用する場合
pip3
をpipenv
にし、またindex-url
オプションはpypi-mirror
オプションにします。
pipenv install torch torchvision torchaudio --pypi-mirror https://download.pytorch.org/whl/cu117
CPUで動かす場合
pipenv install torch torchvision torchaudio
以降大変申し訳無いですがGPUの方は実行確認が取れていないので、本記事では「CPUで動かす場合」を選択したものとしてください。
transformers
Colaboratoryで入れたのと同様、HuggingFace提供のtransformers
を入れます。
pipenv install 'transformers[sentencepiece]'
これで準備は万端です!
いよいよローカルで実行!
先程Colaboratoryで動かせなかった、36億パラメータモデルを動かしてみましょう!
サンプル実行
公式が出しているサンプルコードをそのままコピペして実行です!
prompt = [
{
"speaker": "ユーザー",
"text": "日本のおすすめの観光地を教えてください。"
},
{
"speaker": "システム",
"text": "どの地域の観光地が知りたいですか?"
},
{
"speaker": "ユーザー",
"text": "渋谷の観光地を教えてください。"
}
]
prompt = [
f"{uttr['speaker']}: {uttr['text']}"
for uttr in prompt
]
prompt = "<NL>".join(prompt)
prompt = (
prompt
+ "<NL>"
+ "システム: "
)
print(prompt)
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt-neox-3.6b-instruction-sft", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-neox-3.6b-instruction-sft")
# 筆者環境ではCPUで計算させるので↓は走りません
if torch.cuda.is_available():
model = model.to("cuda")
token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
with torch.no_grad():
output_ids = model.generate(
token_ids.to(model.device),
do_sample=True,
max_new_tokens=128,
temperature=0.7,
pad_token_id=tokenizer.pad_token_id,
bos_token_id=tokenizer.bos_token_id,
eos_token_id=tokenizer.eos_token_id
)
output = tokenizer.decode(output_ids.tolist()[0][token_ids.size(1):])
output = output.replace("<NL>", "\n")
print(output)
pipenv shell
で仮想環境を有効にした後、python main.py
で実行です!
pipenv shell
python main.py
(ちなみにpipenv run python main.py
というコマンドで実行しても構いません)
一番最初に実行するときだけモデルダウンロードが走ります。以下のような出力になれば成功です!
ユーザー: 日本のおすすめの観光地を教えてください。<NL>システム: どの地域の観光地が知りたいですか?<NL>ユーザー: 渋谷の観光地を教えてください。<NL>システム:
Downloading (…)okenizer_config.json: 100%|██████████████████████████████████████████████████████████████████| 284/284 [00:00<?, ?B/s]
Downloading spiece.model: 100%|███████████████████████████████████████████████████████████████████| 786k/786k [00:00<00:00, 8.55MB/s]
Downloading (…)lve/main/config.json: 100%|██████████████████████████████████████████████████████████████████| 534/534 [00:00<?, ?B/s]
Downloading model.safetensors: 100%|████████████████████████████████████████████████████████████| 7.37G/7.37G [01:44<00:00, 70.4MB/s]
あなたが訪れたい場所は何ですか?</s>
返答出力には少し時間がかかります。筆者環境では「あなたが訪れたい場所は何ですか?</s>」という出力になりました!なんか短いし不自然ですが気にしないことにしましょう
対話アプリにしてみる
せっかくの対話モデルなのでインタラクティブなアプリにしてみます!
いきなり完成形で申し訳ないですが、ソースコードは以下になります
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import sys
import io
# Shift_JISから逃れるための呪文なので気にしないでください
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
def _input(prompt):
print(prompt, end='', flush=True)
s = sys.stdin.buffer.readline()
s = s.decode('utf-8') \
.replace("\r", "") \
.replace("\n", "")
return s
# 公式のExampleを参考にしたプロンプト組み立て関数
def make_prompt(log):
prompt = [
f"{uttr['speaker']}: {uttr['text']}"
for uttr in log
]
prompt = "<NL>".join(prompt)
return prompt
# 対話については辞書で持っておくようにする
def add_log(log, role, text):
log.append({
"speaker": role,
"text": text
})
k = 40
max_length = 128
# ちょっとずつ結果を出力してくれるジェネレータ。ChatGPTに聞きました、後述
def gradually_generate(model, tokenizer, token_ids, max_length):
for _ in range(max_length):
with torch.no_grad():
outputs = model(token_ids.to(model.device))
logits = outputs.logits
indices_to_remove = logits < torch.topk(logits, k)[0][..., -1, None]
logits[indices_to_remove] = float('-inf')
probs = torch.nn.functional.softmax(logits[..., -1, :], dim=-1)
next_token_id = torch.multinomial(probs, num_samples=1)
token_ids = torch.cat((token_ids, next_token_id), dim=-1)
output_str = tokenizer.decode(next_token_id[0])
yield output_str.replace("<NL>", "\n")
if "</s>" in output_str:
break
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt-neox-3.6b-instruction-sft", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-neox-3.6b-instruction-sft")
log = []
while "[exit]" not in (user_message := _input("> ")):
add_log(log, "ユーザー", user_message)
prompt = (
make_prompt(log)
+ "<NL>"
+ "システム: "
)
token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
output = ""
for word in gradually_generate(model, tokenizer, token_ids, max_length):
print(word, end='', flush=True)
output += word
print()
add_log(log, "システム", output)
[exit]
が渡されるまで繰り返しプロンプトを打てるCLIアプリケーションにしてみました!
pipenv run python interactive.py
> りんなについて教えてください
りんなは、人工知能アプリケーションで生成された音声アシスタントです。音声を使用して、さまざまなタスクを実行し、質問にも答えることができます。</s>
> 人工知能とはなんですか?
人工知能とは、機械が知能的能力を持つ機械のことです。機械と話をして、機械が答えるべき質問をすると、機械は質問を理解して答えます。また、自然言語処理技術を使用することもあり、自然言語処理は多くの機械が「自然な会話」をするのに役立ちます。</s>
> [exit]
こだわり ~ちょっとずつ出力~
アイキャッチGIFのように、ChatGPTの出力は単語ごとにちょっとずつなされます。筆者は最初、そういうUIなのだと勘違いしていたのですが、どうやら本当に逐次的に単語を予測して少しずつ返答しているらしいです。カッコよくて真似したかったので本家ChatGPT先生に方法を聞いて、それを元にgradually_generate
を書きました。
ChatGPT-4先生との会話
筆者
Hugging Faceのライブラリtransformersを使用して会話生成プログラムを作成しています。
現在ソースコードは以下のような感じです。
(省略)
model.generateが完了するまで出力を一切見られないのですが、ChatGPTのように出力が確定し次第表示されるようにすることは可能でしょうか...?
ChatGPT先生
transformers
ライブラリの現在のバージョンでは、出力を生成しながらそれを表示する機能は直接サポートしていません。model.generate()
関数が出力を生成し終えるまで、その結果は利用できません。しかし、ある程度出力が確定したタイミングで表示するような機能を実装することは可能です。この問題に対する一つのアプローチは、出力を一度に生成するのではなく、一文字または一単語ずつ生成することです。これにより、各ステップで新しく生成された単語を表示することが可能になります。ただし、この方法は各ステップでの推論に対するオーバーヘッドがあるため、全体の生成時間が長くなる可能性があります。
以下に、単語ごとに生成して出力する例を示します:
def generate_and_print(model, tokenizer, token_ids, max_length):
for _ in range(max_length):
with torch.no_grad():
output = model(token_ids.to(model.device))
logits = output.logits
next_token_id = torch.argmax(logits[..., -1, :], dim=-1).unsqueeze(-1)
token_ids = torch.cat((token_ids, next_token_id), dim=-1)
print(tokenizer.decode(next_token_id[0]), end='', flush=True)
print()
# 例えばこんな風に使う
prompt = (
make_prompt(log)
+ "<NL>"
+ "システム: "
)
token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
generate_and_print(model, tokenizer, token_ids, 128)
ChatGPT先生
ここでのキーは
flush=True
オプションを使用することで、出力が即座に表示されるようになります。なお、この例では最も確率が高い単語(argmax
によって選ばれる)を選択していますが、より高度なサンプリング戦略(たとえばトップ-k サンプリング)を適用することも可能です。ただし、これらの高度なサンプリング戦略を適用すると、コードはより複雑になります。
(中略)
筆者
いただいたプログラムを加工して実行したところ返答内で同じ表現を何度も繰り返す現象が見られました。これを改善するにはどうすれば良いでしょうか...?
以下現在のプログラムです。
(省略)
ChatGPT先生
生成されるテキストに同じフレーズや単語が何度も繰り返される現象は、言語モデルの生成においてよく見られる問題です。これは特に一般的な問題であり、あるトークンが生成されると、そのトークンが再び生成される確率が一時的に高まることが一因として考えられます。
解決策の一つとして、生成時にサンプリングの方法を変更することが挙げられます。最も単純な生成方法は、各ステップで最も高い確率を持つトークンを選択する方法です(これを「greedy decoding」や「argmax decoding」と呼びます)。しかし、これは多様性に欠ける生成結果を引き起こし、上述の繰り返し問題を引き起こす可能性があります。
一方、いくつかの高度なサンプリング技術が提案されています。その中でもよく使用されるものに「トップ-k サンプリング」や「トップ-p サンプリング(または「ニュークリーサンプリング」)」があります。これらのテクニックは、適度なランダム性を組み込むことで生成結果の多様性を保ちつつ、一貫性を維持します。
transformersライブラリでは、これらのサンプリング手法を簡単に適用することができます。以下に、トップ-k サンプリングを適用した例を示します:
def generate_and_print(model, tokenizer, token_ids, max_length):
res = ""
for _ in range(max_length):
with torch.no_grad():
# get the model outputs
outputs = model(token_ids.to(model.device))
# get the logits from the model outputs
logits = outputs.logits
# Apply top-k sampling
indices_to_remove = logits < torch.topk(logits, k)[0][..., -1, None]
logits[indices_to_remove] = float('-inf')
# Sample from the logits
probs = torch.nn.functional.softmax(logits[..., -1, :], dim=-1)
next_token_id = torch.multinomial(probs, num_samples=1)
# concatenate the new token ID to the existing sequence
token_ids = torch.cat((token_ids, next_token_id), dim=-1)
output_str = tokenizer.decode(next_token_id[0])
res += output_str
# decode the new token ID and print it
print(output_str.replace("<NL>", "\n"), end='', flush=True)
if "</s>" in output_str:
break
print()
return res
上記の例では、
k
を適切な数値(例えば40や50)に設定することで、各ステップで考慮するトークンの数を制限します。また、torch.multinomial
を使用して、確率分布からランダムにトークンをサンプリングします。
...ぶっちゃけ今でもどうしてちょっとずつ生成できているのかちゃんとは説明できないです(笑)
本記事やLLMでわからない事があったらChatGPT先生に聞いちゃりましょう!上司には内緒です
PyInstallerで実行ファイル化してみる
今回、TauriにPythonアプリを内包して配布したいと考えたわけですが、Pythonランタイムの配布が難しくとても悩みました。
そんな折、PyInstallerを使うとスタンドアロンな実行ファイルにできることを知りました。実行ファイルならば、Tauri(Rust)からは子プロセスとして呼び出して標準入出力を介してやり取りすることができます!
ただ一筋縄ではいかないです...PyInstallerでは隠しインポート?を明示しなければならないらしく、Pythonで動いたからといって、脳死でコマンドを打てば実行ファイルができるわけじゃないみたいです。隠しインポートって何
参考:
途方にくれて検索しまくっていたら自己解決している謎のStack Overflow投稿があったので縋ってみたところ、上手くいったのでこれをそのまま使うことにしました。多分冗長なものがありそうだけど調べてない
sentencepiece
周りでエラーが出たのでその分だけ追記しています。
pyinstaller --onefile --hidden-import=pytorch '
--collect-data torch `
--copy-metadata torch `
--copy-metadata tqdm `
--copy-metadata regex `
--copy-metadata requests `
--copy-metadata packaging `
--copy-metadata filelock `
--copy-metadata numpy `
--copy-metadata tokenizers `
--copy-metadata sentencepiece `
--hidden-import="sklearn.utils._cython_blas" `
--hidden-import="sklearn.neighbors.typedefs" `
--hidden-import="sklearn.neighbors.quad_tree" `
--hidden-import="sklearn.tree" `
--hidden-import="sklearn.tree._utils" `
--hidden-import="sentencepiece" `
interactive.py
PyInstallerはまだ入れていなかったのでpipenvで入れ、上記コマンドを実行すれば無事動く実行ファイルが作れます。
pipenv install pyinstaller
pipenv run pyinstaller --onefile --hidden-import=pytorch --collect-data torch --copy-metadata torch --copy-metadata tqdm --copy-metadata regex --copy-metadata requests --copy-metadata packaging --copy-metadata filelock --copy-metadata numpy --copy-metadata tokenizers --copy-metadata sentencepiece --hidden-import="sklearn.utils._cython_blas" --hidden-import="sklearn.neighbors.typedefs" --hidden-import="sklearn.neighbors.quad_tree" --hidden-import="sklearn.tree" --hidden-import="sklearn.tree._utils" --hidden-import="sentencepiece" interactive.py
結局オプションの指定方法がよく分からずもにょる...何はともあれ、これでdist
ディレクトリにinteractive.exe
という実行ファイルができ、Pythonランタイムなしで実行できるようになりました!
cd dist
.\interactive.exe
> 日本の首都は?
日本の首都は東京です。</s>
> [exit]
Tauriに埋め込んで配布
実行ファイルをTauriのバックエンド役を担うRustから呼び出す話も本当は本記事に書きたかったのですが、長くなりすぎるため別記事とさせていただきます!
2023/06/27 追記
投稿いたしました!ご覧いただけると幸いです5!
まとめ・所感
LLM入門は案外簡単だということを本記事で示せたでしょうか...?
Pythonで簡単にLLMに触れられたことにとても感動しつつも、変なこと(例えば実行ファイル化)をしようとするとなかなか情報がなくて辛かったです。まぁPythonに限った話ではないかもですが...
最近MojoみたいなAlt-Python言語がしばしば注目されますが、Python特有の問題に当たったりすると、やはり別な選択肢も欲しいなと感じます。
今回LLMに入門して、改めて機械学習や深層学習の勉強意欲が湧いてきました!ただ、冒頭みたいに業務として降ってくるのはできれば避けたいです(苦笑)
ここまで読んでいただきありがとうございました...!
参考
-
サイバーエージェントLLMとは?概要〜導入〜所感までを徹底解説! | 株式会社SaaSis
- OpenCALMをGoogle Colaboratoryで動かしています。
-
ローカルで動く大規模言語モデル(Rinna-3.6B)を使ってあなただけのAIパートナーを作ろう - Qiita
- VRAM情報はこちらの記事様を根拠にさせていただきました。
-
pyenvとpipenvで、快適Python生活(Windows向け)
- pyenvとpipenvに関して参考にさせていただきました。
-
用語を誤使用している可能性が高いので、もしその時はコメント等でご指摘くださると幸いです。 ↩
-
改めて後述していますがマシンパワーでゴリ押すスタイルです。動かすだけなら手順は簡単というのが本記事の主張です。 ↩
-
後述していますが、正確にはPyInstallerによって実行ファイルとしたものになります。 ↩
-
自慢(笑)みたいになって嫌だったのですが「動かないんですけど?!」って言われるよりはマシかと思い明記してます... ↩
-
とてもどうでも良いですが1つの記事としてまとめる場合元々考えていたタイトル案は自分の記事シリーズの命名規則に則った「上司からオンプレでチャットAIを作るように指示された話またはRust on Tauriから実行ファイルを呼び出す方法的な何か」でした。長すぎ!! ↩