新年あけましておめでとうございます
今年もよろしくお願いします。
335bytes
Windows 11 + WSL2 Ubuntu 22.04 の中で pytorch LLM を実行してみる
tag: wsl2, windows11, windows10, cuda, pytorch, rinna, llm, youri
■概要
Windows上のpythonだとtransformers.AutoModelForCausalLM.from_pretrained のオプション load_in_4bit=True, bitsandbytes使用が動作しなかったので、WSL2 Ubuntu-22.04上のpythonを試してみた。
結果としては WSL2 Ubuntu-22.04上のpython だと load_in_4bit=True, bitsandbytes使用 も動作するし、 load_in_8bit=True も動作した。
WSL2 Linux環境の方が動きが良くてワロタw
しかし、WSL2の中で自然にCUDA使えるの凄いな...。
■WSL2のセットアップとアップグレード
WSL2側にDockerエンジンを直接インストールする方向でセットアップしてみる。
WSL2のインストールと最新化。まだやっていない人はこちら。PowerShellの中で操作。
既にWSL2をインストールしている人は wsl.exe --update のみ実施する。
wsl.exe --list --all --verbose wsl.exe --set-version 2 wsl.exe --install -d Ubuntu-22.04 wsl.exe --update wsl.exe --set-default Ubuntu-22.04 wsl.exe --list --all --verbose
■WSL2の中のdocker環境 と NVIDIA Container Toolkit セットアップ
Windows側にDocker Desktopをインストールしている場合、WSL2側でもDocker DesktopにPATHが通っている。
この状態だとWSL2側のdockerインストールが失敗するとのことなので、~/.bashrcの中でWSL2側のPATHからdocker desktop関連と、ついでにnVIDIA CUDA関連を消去する。
ファイル ~/.bashrc の内部に記述
# PATHから削除する。
# ない場合は何もしない。
function f-path-remove() {
local removepath="$1"
local resultpath=
if [ -z "$removepath" ]; then
echo "f-path-remove path"
return 0
fi
local tempfile=$( mktemp $TMP/f-path-remove-XXXXXXXX.tmp )
echo "$PATH" | sed -e 's/:/\n/g' | while read ans
do
if [ x"$ans"x = x"$removepath"x ]; then
# echo "remove PATH ... $removepath"
continue
fi
echo "$ans" >> $tempfile
done
resultpath=$( cat $tempfile | sed -e 's/*/-/g' | tr '\n' ':' )
resultpath=$( echo $resultpath | sed -e 's/:$//g' )
/bin/rm $tempfile
if [ -n "$resultpath" ]; then
PATH=$resultpath
export PATH
fi
}
# PATH調整 WLS2 Ubuntu側のバイナリを使うので、Windows側のPATHがあれば削除
f-path-remove "/mnt/c/Program Files/Git/usr/bin"
f-path-remove "/mnt/c/Program Files/Git/mingw64/bin"
f-path-remove "/mnt/c/Program Files/Git/cmd"
f-path-remove "/mnt/c/Program Files/Go/bin"
f-path-remove "/mnt/c/Program Files/nodejs/"
f-path-remove "/mnt/c/Program Files/Git/bin"
f-path-remove "/mnt/c/HashiCorp/Vagrant/bin"
f-path-remove "/mnt/c/Program Files/Oracle/VirtualBox"
f-path-remove "/mnt/c/Program Files/Docker/Docker/resources/bin"
f-path-remove "/mnt/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.8/bin"
f-path-remove "/mnt/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.8/libnvvp"
f-path-remove "/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR"
f-path-remove "/mnt/c/Program Files/NVIDIA Corporation/Nsight Compute 2022.3.0/"
f-path-remove "/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common"
PATHを削り終わったら公式ドキュメントに従ってWSL2の中で操作。
# WSL に Docker エンジンを直接インストールします curl https://get.docker.com | sh sudo service docker start # NVIDIA Container Toolkitをインストール。 distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-docker-keyring.gpg curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-docker-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-docker.list # 次のコマンドを実行して、NVIDIA ランタイム パッケージと依存関係をインストールします sudo apt-get update sudo apt-get install -y nvidia-docker2
■python仮想環境の中にpytorchなどをインストール
load_in_4bit とか load_in_8bit が動くかどうか実験したいので、りんなのYouri-7Bを使用。
WSL2の中で操作。
# python --version は 3.10.12 # python 仮想環境作成 python -m venv venv # python 仮想環境を有効化 . ./venv/bin/activate # pytorch インストール pip3 install torch torchvision torchaudio # bitsandbytes インストール pip3 install accelerate bitsandbytes # transformers インストール pip3 install transformers sentencepiece scipy
pythonのメイン処理を書く。
youri_7.0b_sample_main.py
import os
import sys
import json
import time
import argparse
from typing import (List, Dict)
from logging import getLogger
import logging.config
import torch
import transformers
# 使用するモデル名
model_name = "rinna/youri-7b-chat"
def chomp_last_sequence_messages(messages: list) -> (List[Dict], str):
""" 過去発言のリストの中から、チャット内容のフラッシュを意味する"----"から始まる文字列が
最後に出現した位置より下のみのリストを作って返却する。
"""
# 最後に出現したflush位置を取得
i: int = 0
idx: int = 0
gentime: str = None
for d in messages:
role: str = d['role']
txt: str = d['content']
if role == 'system':
if txt.startswith('----'):
idx = i
if 'gentime' in d:
gentime = d['gentime']
i = i + 1
# 新しくリストを作って返却
result = []
i = 0
for d in messages:
if idx < i:
result.append(d)
i = i + 1
# gentimeがNoneなら現在時刻を設定
if gentime is None:
gentime = time.strftime("%Y%m%d-%H%M%S")
return (result, gentime)
def generate_prompt(messages: list) -> (str, str):
# flush 後のプロンプトのみ切り出す
selected_list, gen_time = chomp_last_sequence_messages(messages)
# prompt文字列を生成する
prompt_str = "設定: 次の質問に答えてください。\n"
for d in selected_list:
role: str = d['role']
txt: str = d['content']
if role == "user":
prompt_str += "ユーザー: {}\n".format(txt)
if role == "assistant":
prompt_str += "システム: {}\n".format(txt)
return (prompt_str, gen_time)
def main(argv: list[str]):
# 引数解析初期化
parser = argparse.ArgumentParser(
description='Youri 7B program')
parser.add_argument('--prompt', help='chat stream mode')
parser.add_argument('--dtype', help='set torch_dtype float16 or float32')
parser.add_argument('--load-in-4bit', action='store_true', help='use BitsAndBytes')
parser.add_argument('--load-in-8bit', action='store_true', help='use load_in_8bit flag')
args = parser.parse_args(argv)
# 発言履歴
messages = []
# データタイプ取得
torch_dtype = torch.float16
if args.dtype:
if args.dtype == "float16":
torch_dtype = torch.float16
if args.dtype == "float32":
torch_dtype = torch.float32
# load in 4bit flag
load_in_4bit = False
if args.load_in_4bit:
load_in_4bit = True
# load in 8bit flag
load_in_8bit = False
if args.load_in_8bit:
load_in_8bit = True
# load tokenizer
tokenizer = transformers.AutoTokenizer.from_pretrained(model_name)
# load model
start = time.time()
# load model パラメータ # bitsandbytes が別途必要。 Windows では動作しない。
if load_in_4bit:
quantization_config = transformers.BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch_dtype
)
# load model GPU指定(VRAM12GB用。8GBはNG)
# 引数設定
kwargs_dic = {
"device_map": "auto",
"torch_dtype" : torch_dtype,
# torch_dtype=torch.bfloat16, # bfloat16 は TPU の浮動小数点格納方式らしい。指数部のビット数はfloat32と変わらないので大きな数値が表現できる。
# bitsandbytes が別途必要。 Windows では動作しない。
# quantization_config=quantization_config,
# load_in_8bit=True, # bitsandbytes が別途必要。 Windows では動作しない。
# load_in_4bit=True, # bitsandbytes が別途必要。 Windows では動作しない。
}
if load_in_4bit:
kwargs_dic["quantization_config"] = quantization_config
kwargs_dic["load_in_4bit"] = True
if load_in_8bit:
kwargs_dic["load_in_8bit"] = True
model = transformers.AutoModelForCausalLM.from_pretrained(
model_name, **kwargs_dic
)
end = time.time()
print("model load time:{} s".format(end - start))
# チャットのループ繰り返し(抜けるのはCtl+c or quit)
question = ""
(prompt, gen_time) = generate_prompt(messages)
while True:
# 質問を入力。引数に指定があれば引数から取得。
if args.prompt and question == "":
question = args.prompt
print("質問をどうぞ: {}".format(question))
else:
question = input("質問をどうぞ: ")
if question.lower() == 'clear':
prompt = "設定: 次の質問に答えてください。\n"
question = ""
gen_time = time.strftime("%Y%m%d-%H%M%S")
d = {
"role": "system",
"content": "--------",
"gentime": gen_time
}
messages.append(d)
continue
if question.lower() == 'history':
print("\n\n\nhistory\n\n")
print(prompt)
continue
if question.lower() == 'exit' or question.lower() == 'quit':
break
# プロンプト文字列作成。
prompt = prompt + f"ユーザー: {question}\nシステム: "
# 時間計測開始
start = time.time()
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),
max_new_tokens=256,
do_sample=True,
temperature=0.5,
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):])
# 時間表示
end = time.time()
calc_time = "time: {}".format(end - start)
print(calc_time)
output = output[0:-len("</s>")].strip()
print(output)
prompt = prompt + output + "\n"
# end of chat loop
if __name__ == '__main__':
main(sys.argv[1:])
プログラムの実行
text=$( cat << "EOF" pythonで整数型の乱数を生成するサンプルコードを教えて。 EOF ) python ./youri_7.0b_sample_main.py --dtype float16 --load-in-8bit --prompt "$text" text=$( cat << "EOF" pythonで整数型の乱数を生成するサンプルコードを教えて。 EOF ) python ./youri_7.0b_sample_main.py --dtype float16 --load-in-4bit --prompt "$text"
結論としては、WSL2 Ubuntu-22.04の中のpytorchの方が動きが良かった。
■参考文献
石川県能登半島沖で大地震
元旦から飛ばしてくるな。。
つか日本海側でも大地震とか津波とかってやっぱりあるんだな。
震度7に変わった。 というか 16時06分の震度5強に続いて 16時10分に最大の震度7、マグニチュード7.6が発生したのか。 断続的に震度5強がきていておそろしや。
大津波警報が出た。
16bitセンセーション
tag: anime
PC-9801の現物を使ったことがある人には懐かしいフォントが再現されてたり。 当時の人気エロゲが実名で登場していたり。
労力のかけ方がおかしいw
ストーリーは普通(?)というか最後はあまり感動がない印象。
羽田で日本航空の機体が炎上中っぽい
今年の正月は中止ですかね。。。
読売新聞のサイトが凝ってる 令和6年能登半島地震被災状況マップ
tag: press
カメラ画像に撮影日時とGPS位置と撮影した方向を記録してるのかな?
マップと連動して被災位置がわかりやすい。
こんな感じで情報収集&まとめ作成できたら便利そう。
輸送艦おおすみ+LCAC(ホバークラフト)のビーチング
ビーチング(LCACで砂浜に乗りつけ)した先は輪島市の町野町の大川浜らしい。
輪島市の町野町は、山に囲まれた地形で道路が崖崩れで全部埋まって孤立してるとのこと。 周囲の道路状況は以下の写真を参照。
ただしLCAC(ホバークラフト)がビーチングできる条件は結構限られていて、遠浅で付近に磯岩は無く、満潮時でも奥行50m程度は砂浜orゆるやかな斜面があって、舗装道路がすぐ近くまで来ていると都合が良くて、波もない海象でないといけないらしい。
NHKの動画みると結構波が高かったけど、LCACとしてはかなりヤバい状態だったっぽい。
p.s. 1/6
LCACでドコモの基地局車両を運び込んだらしい。
Vue Composition API に書き換え
tag: wiki
Vue Composition API に書き換えるの、面倒くさいなw
1モジュール書き換えて力尽きたww
気が向いたらぼちぼちやるか。
twitterとえっち絵と軍隊の補給
twitterとえっち絵を、軍隊の補給になぞらえて解説した名文(?)。
わかりやすいww
twitterの代わりになりうるSNSが持つべき特性についても考えさせられる。
「拡散力と持続性と公平性と速度」を担保するインフラの有用性と、限定的な補給を司るものの役割と限界であり、またそれが崩壊した時の難しさと言えるでしょう。
twitterの特性は「拡散力と持続性と公平性と速度」を持った補給になぞらえることができて、 掲示板とかskebの特性は(非常に希少で特定の場所でしか使わない)限定的な補給になぞらえることができると。
「それがどれだけ容易に拡散出来るか。どれだけ広範囲に及ぶか。どれだけの速度を持つか。どれだけの期間を保障出来るか」という要素が輸送や補給では非常に重要となります。 そしてこれらの要素は「供給の公平性」に寄与するのです。
物量が豊富にあってガンガン物資が届く状態だと供給の公平性の状態がより良くなると。
ARMORED CORE VI ver 1.05 全ミッションクリア
tag: game, armored-core-6
4周目にしてようやっと全ミッションクリア。
ついつい特務機体の撃破を選択してしまっていたせいでカタクラフトの撃破が最後になった。
OS TUNEとかがMAXだったのでなんとか勝てたけど、コレ1周目だと何度か死んでたな・・・。
Windowsの環境変数とLinuxの環境変数を相互に引き継ぎ
tag: wsl2, windows11, windows10
以下、元ネタのまんまコピペしてメモ。
Windowsの環境変数とLinuxの環境変数を相互に引き継げる。
引き継ぎたい環境変数名は環境変数WSLENVに「:」で区切りで指定する。
$env:WSLENV = "EDITOR:VERSION:DISPLAY"
また各環境変数にはサフィックスを指定し、引き継ぎ方法を制御することもできる。
| サフィックス | 概要 |
| p | 環境変数の内容が単一のパスを表しており、WindowsのパスとLinuxのパスを変換して環境変数を引き継ぐ |
| l | 環境変数の内容が複数のパスを表しており、WindowsのパスとLinuxのパスを変換して環境変数を引き継ぐ。この時パスの区切りは、Windows環境では「;」で区切り、Linux環境では「:」で区切るように変換される |
| u | Windows環境からLinux環境を起動した時にのみ、環境変数を引き継ぐ |
| w | Linux環境からWindows環境を起動した時にのみ、環境変数を引き継ぐ |
複数のサフィックスを指定する場合は、「/pu」のように記述する。
記述例は以下。
$env:WSLENV = "GOPATH/l:USERPROFILE/pu:DISPLAY"
パスの変換と引き継ぎについて
Windows環境とLinux環境では、パスの位置が異なる。 例えばWindows環境における「C:\」は、Linux環境では「/mnt/c/」になる。
Linux環境起動時にボリュームがマウントされていない等の理由でパスの変換に失敗した場合、その変換に失敗したパスは引き継がれない。