LLAMA2を使った自作Chatbotを自宅PCで作成してみた

## Llama2とは
Meta社が開発した商用利用可能なLLM（大規模言語モデル）

###### ３つの事前学習モデル
　70億(7B)のパラメーターで学習されたモデル
　130億(13B)のパラメーターで学習されたモデル
　700億(70B)のパラメーターで学習されたモデル

  一般的には、パラメータ数が多いほど回答の精度が高くなるが、膨大になると計算コストがかかる。
　※計算コストとは、より高性能なグラフィックボード（GRAM)が必要になったり、回答出力までの時間がかかる　等

　chatGPTのモデルの大きさについてはこちらの記事を
　https://meditech-ai.com/gpt-ai-evolution/

## LLAMA2　量子化モデル
とはいえ、いわゆるチャットAIモデルを動作させるために、十～数百GBのVRAMなど非常に高いマシンスペックを要求するのが一般的。※Meta社に直接申請してダウンロードしたモデルは、うちのPCだとスペック不足でダメでした（そもそもGPU複数枚刺し前提）
そこで量子化という手法を用いることで、モデルを軽量化することによって、本来の要求スペックより引くいスペックのマシンでも動作可能とした。

#### そもそも量子化とは

下記が一番簡潔でわかりやすかった

https://laboro.ai/activity/column/engineer/%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E3%82%92%E8%BB%BD%E9%87%8F%E5%8C%96%E3%81%99%E3%82%8B%E3%83%A2%E3%83%87%E3%83%AB%E5%9C%A7%E7%B8%AE/

・TensorFlowやPyTorchのフレームワークで扱われるモデルは、32ビット浮動小数点精度（float型）の配列で表現される。この32ビットがサイズを大きくする要因
・このビット数を8ビットや4ビットでの表現に置き換えることによってサイズを圧縮する。



# 実装
進め方やコードはほとんど下記の方の引用です。

https://qiita.com/matsxxx/items/55044b5ec19c223a4264

#### モデルのダウンロード
下記から量子化されたモデルをダウンロードします。
※今回はllama-2-7bの中でも、「llama-2-7b-chat.ggmlv3.q4_K_M.bin」をチョイス

https://huggingface.co/TheBloke/Dolphin-Llama2-7B-GGML

###### ※選定基準
こちらの記事を基に決定

https://note.com/bakushu/n/n1badaf7a91a0

## ライブラリ
```
pip install  gradio
pip install llama-cpp-python
```
pip install llama-cpp-python　の前に
CMAKEのインストールや、環境変数の設定がいるかも

$env:CMAKE_ARGS = "-DLLAMA_OPENBLAS=on"
$env:FORCE_CMAKE = 1


# 全コード
```
import gradio as gr
from llama_cpp import Llama

##日本語も英語に変換してから扱うために入れた翻訳ライブラリ
from deep_translator import GoogleTranslator

model_path = "model/llama-2-7b-chat.ggmlv3.q4_K_M.bin"
llama = Llama(model_path)

def predict(message, history):
    messages = []
    for human_content, system_content in history:
        message_human = {
            "role":"user",
            "content": human_content+"\n",
        }
        message_system = {
            "role":"system",
            "content": system_content+"\n",
        }
        messages.append(message_human)
        messages.append(message_system)
    message_human = {
        "role":"user",
        "content":message+"\n",
    }
    messages.append(message_human)
    # Llama2回答
    streamer = llama.create_chat_completion(messages, stream=True)
    

    partial_message = ""
    for msg in streamer:
        message = msg['choices'][0]['delta']
        if 'content' in message:
            partial_message += message['content']
            yield partial_message

    #translated = GoogleTranslator(source='auto',target='ja').translate(partial_message)
    #return translated

gr.ChatInterface(predict).queue().launch()

```

## ちょっと解説
yield partial_message

yieldとは、関数を一時的に実行停止させることが出来る機能を持つ文
つまりは、その時点での値をreturnして、また続きの処理を再開する。

```
for msg in streamer:
        message = msg['choices'][0]['delta']
        if 'content' in message:
            partial_message += message['content']
            yield partial_message
```
これによって回答が少しずつ描画されていくような演出になる。
※ほんとはメモリ節約のためとかに使用されるらしい
![yield_image.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507211/7c9ab360-6d0d-ab29-73e2-fc5818fbc244.jpeg)
まとめて表示したい場合はfor分を抜けた後に
return partial_message

とする。

# 最後に
こんな簡単に実装できるとは・・