見出し画像

Google Colab 無料版でELYZA-japanese-Llama-2-7b-instruct(4bit量子化 GPTQ)を爆速ストリーミングしてみた

ELYZA 様から商用利用可能な日本語LLM「ELYZA-japanese-Llama-2-7b」がリリースされました!

ELYZA 様のモデルは性能が高いだけでなく、実際に人手で評価されたスプレッドシートまで公開されています。
ベンチマークよりも実際の出力結果を見たいことが多いため、こういった活動は大変有り難いですね。

今回は、「ELYZA-japanese-Llama-2-7b-instruct」(fastではない方)を GPTQで4bitに量子化したモデルと、同時に公開いただいた「ELYZA-tasks-100」をお借りして、爆速ストリーミングを実施しました!

爆速ストリーミングの様子
(縦長の画面だと見やすいです)

ライブラリのバージョンと容量

ソースコード

Google Colab のランタイムを「T4 GPU」に変更してお使いください。
VRAM 15 GB中 4.7 GB程度の使用量でしたので、無料版でも十分稼働します。

%%capture
#@title インストール
!pip install -q -U transformers==4.32.1 peft==0.5.0 accelerate==0.22.0 optimum==1.12.0
!pip install auto-gptq --extra-index-url https://huggingface.github.io/autogptq-index/whl/cu117/
#@title 主要なモジュールのバージョン
!pip list | egrep "auto-gptq|peft|accelerate|transformers|optimum|torch"
#@title 4bit の GPTQ 量子化モデルを動かす
import time
import gc
import torch
from transformers import AutoTokenizer, TextIteratorStreamer
from auto_gptq import AutoGPTQForCausalLM
from threading import Thread
from datasets import load_dataset

# 取り出したい数を制御する(ELYZA-taskは最大100件のデータセット)
QA_NUM = 10     # 最大値は100
elyza_tasks_dataset = load_dataset("elyza/ELYZA-tasks-100", split="test")
elyza_tasks_dataset_shuffled = elyza_tasks_dataset.shuffle()
elyza_tasks_dataset_shuffled = elyza_tasks_dataset_shuffled.select(range(QA_NUM))

quantized_model_dir = "TFMC/ELYZA-japanese-Llama-2-7b-instruct-GPTQ-4bit-64g"

tokenizer = AutoTokenizer.from_pretrained(quantized_model_dir, trust_remote_code=True)

print()
print("======= special token の確認 ==========")
print(tokenizer.special_tokens_map)
print(tokenizer.eos_token, tokenizer.eos_token_id)
print(tokenizer.bos_token, tokenizer.bos_token_id)
print(tokenizer.pad_token, tokenizer.pad_token_id)
print("=======================================")
print()

model = AutoGPTQForCausalLM.from_quantized(
        quantized_model_dir,
        use_safetensors=True,
        disable_exllama=False,
        inject_fused_attention=False,
        device="cuda:0",
        trust_remote_code=True,
        )
# モデルは評価モードで読み込む
model.eval()

B_INST, E_INST = "[INST]", "[/INST]"
B_SYS, E_SYS = "<<SYS>>\n", "\n<</SYS>>\n\n"
# AoTプロンプトの一部を流用した
SYSTEM_PROMPT = """あなたは誠実で優秀な日本人のアシスタントです。
# 振る舞い方の指示
1. 複雑な問題をより扱いやすいサブ問題に分解する
2. それぞれを解決して全体の問題を解決する"""


# torchキャッシュのクリア
def clear_torch_cache():
    gc.collect()
    torch.cuda.empty_cache()


for i in range(len(elyza_tasks_dataset_shuffled)):
    # キャッシュクリア
    clear_torch_cache()
    # ストリーミングの準備
    streamer = TextIteratorStreamer(tokenizer, timeout=10., skip_prompt=True, skip_special_tokens=True)

    # 処理の開始時間を記録
    start_time = time.time()
    print("-------------------------------------")
    print(f'\033[45;1;37m*** Task({i+1})\033[0m')
    print("-------------------------------------")
    print(elyza_tasks_dataset_shuffled[i].get('input'))
    print()

    prompt = "{bos_token}{b_inst} {system}{prompt} {e_inst} ".format(
        bos_token=tokenizer.bos_token,
        b_inst=B_INST,
        system=f"{B_SYS}{SYSTEM_PROMPT}{E_SYS}",
        prompt=elyza_tasks_dataset_shuffled[i].get('input'),
        e_inst=E_INST,
    )

    with torch.no_grad():
        token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")

        def generate(input):
            output_ids=model.generate(
                input_ids=input,
                streamer=streamer,
                max_new_tokens=256,
                do_sample=True,
                temperature=0.1,
                top_p=0.9,
                pad_token_id=tokenizer.pad_token_id,
                bos_token_id=tokenizer.bos_token_id,
                eos_token_id=tokenizer.eos_token_id,
                repetition_penalty=1.1,
                )

        t = Thread(target=generate, args=(token_ids.to("cuda:0"),))
        t.start()

    print("-------------------------------------")
    print(f'\033[44;1;37m*** Generate({i+1})\033[0m')
    print("-------------------------------------")
    for new_text in streamer:
        print(new_text.replace(" ",""), end="")
    print()
    print()
    # 処理の終了時間を記録
    end_time = time.time()
    execution_time = end_time - start_time
    print("====================================")
    print(f'\033[1;32m処理時間({i+1}): {execution_time} 秒\033[0m')
    print("====================================")
    print()
    print()

使用したモデル:  TFMC/ELYZA-japanese-Llama-2-7b-instruct-GPTQ-4bit-64g

AoT(Algorithm of Thoughts)フレームワークを一部応用してシステムプロンプトを変更しました。こちらの方が精度が高いような気がしたため採用しています。

感想

  • 4bitに量子化されたモデルでは、元のモデルよりも精度が下がってしまった印象です。

    • プロンプトを工夫することで少し精度は上がったような気がします。

  • タスクによってはきちんと正解できているものもありましたが、まだ不安定な出力でした。


もっと精度が上がるプロンプトやパラメータを見つけられた方は、ぜひ共有いただけると嬉しいです!

謝辞

ストリーミング生成の部分は清水様のコードをお借りしました。


この記事が気に入ったらサポートをしてみませんか?