はじめに
先日Googleから発表されたLLM(Gemma)を触ってみましたので、簡単にまとめてみます。
実行環境にはGoogle Colab(無料版)を使用しています。
今回やること
ただ動かすだけでは面白くないので、会社のHP( https://www.haw.co.jp )情報をLangChainのRetrievalQAを使用してLLM(Gemma)で扱えるようにして、会社に関するQAのチャットボットっぽい物を作成したいと思います。
Gemmaとは?
Googleが2024年2月22日に公開した、オープンソースの大規模言語モデルマルチモーダルAIのGeminiよりも軽量で、商用利用も可能です。
モデルについて
現在公開されているのは以下の4種です。
- gemma-2b
- gemma-2b-it
- gemma-7b
- gemma-7b-it
何もないのがベースモデルで、it
がついているのが命令モデルのようです。
LangChain・RetrievalQAについて
LangChainは、自然言語処理(NLP)の機能拡張を効率的に実装するためのライブラリです。
そのLangChainの中にある機能の1つが、RetrievalQA(検索型質問応答)です。
RetrievalQAを使用することで、大量のテキストデータから関連する情報を検索し、質問に対する回答を生成することができます。
実装
VectorDBに会社のHPから取得したテキストデータをベクトル化して保存しておき、ユーザからの質問をRetrievalQAに渡し、VectorDBに検索をかけて、その結果をGemmaに渡す流れです。
事前準備
Googleのライセンスに同意する
Gemmaを使用するにはHugging FaceアカウントでGoogleのライセンスに同意する必要があります。
以下のような画面が出てくると思いますので、進めて同意してください。
HF_TOKEN設定
Hugging Faceのアカウントページからトークンを発行し、それをcolabのシークレット(左の鍵マーク)にHF_TOKEN
として設定します。
インストール
transformersの最新版とその他、使用するパッケージをインストールします。
!pip install -U transformers > /dev/null
!pip install bitsandbytes accelerate > /dev/null
!pip install langchain > /dev/null
Gemma
とりあえず動かす
試しに以下のサンプルを動かしてみます。
https://huggingface.co/google/gemma-7b-it
今回はcolab無料版(T4)で普通では当然動くわけないので、8ビット量子化設定で動かします。
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
tokenizer = AutoTokenizer.from_pretrained('google/gemma-2b-it')
model = AutoModelForCausalLM.from_pretrained(
'google/gemma-2b-it',
quantization_config=quantization_config
)
GPU RAMがかなりギリギリです。
(撮影ミスで暗くなっていますが気にしないでください。)
適当に質問してみます。
input_text = '日本で一番高い山の名前と標高は何ですか?'
input_ids = tokenizer(input_text, return_tensors='pt').to('cuda')
outputs = model.generate(**input_ids)
print(tokenizer.decode(outputs[0]))
出力のデフォルトが20トークンまでみたいなので途中で切れました。
max_new_tokens
を増やして再度実行してみます。
余計なものついていたり、同じことを繰り返したりしてますが、問題なさそうですね。
Langchainで使用できるようにする
pipeline
を通すことにより、Langchainで使用できるようになります。
from langchain.llms import HuggingFacePipeline
from transformers import pipeline
pipe = pipeline(
'text-generation',
model=model,
tokenizer=tokenizer,
max_new_tokens=100,
)
local_llm = HuggingFacePipeline(pipeline=pipe)
先ほどのプロンプトをLCELで書き直して実行すると同じ結果が得られます。
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema import StrOutputParser
base_template = '''
<bos><start_of_turn>user
{question}
<end_of_turn><start_of_turn>model
'''
qa_chain = (
{'question': RunnablePassthrough()}
| PromptTemplate.from_template(base_template)
| local_llm
| StrOutputParser()
)
res = qa_chain.invoke('日本で一番高い山の名前と標高は何ですか?')
print(res)
VectorDB構築
VectorDBは色々とありますが、今回はFAISS(Facebook AI Similarity Search)を使用します。
その他の設定は以下のようにします。
- データ範囲: 会社HPのTOPページから直接アクセスできる自社ドメインのページ全て
- テキストの分割チャンク: 100文字(適当)
- embeddingモデル: 'intfloat/multilingual-e5-large'
from langchain.indexes import VectorstoreIndexCreator
urls = [
'https://www.haw.co.jp/',
...
]
docs = UnstructuredURLLoader(urls=urls).load()
# チャンクの分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=100, # チャンクの最大文字数
chunk_overlap=10, # オーバーラップの最大文字数
)
# VectorDB構築
vectorstore = FAISS.from_documents(
docs,
embedding=HuggingFaceEmbeddings(
model_name='intfloat/multilingual-e5-base'
)
)
QABotの構築
vectorstoreとLLMでchainを構築します。
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema import StrOutputParser
base_template = '''
<bos><start_of_turn>system
あなたはAIアシスタントです。
次の情報のみに基づいて、userの質問に答えてください。
{context}
<end_of_turn>
<start_of_turn>user
{question}
<end_of_turn>
<start_of_turn>model
'''
qa_chain = (
{'context': vectorstore.as_retriever(search_kwargs={'k': 3}), 'question': RunnablePassthrough()}
| PromptTemplate.from_template(base_template)
| local_llm
| StrOutputParser()
)
これで完成したので実際に質問してみます。
qa_chain.invoke('会社について教えて')
メモリが足りませんでした。。。
色々と試しましたが、どうしようもなかったので、今回は下位モデルの2b
を使用します。
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
tokenizer = AutoTokenizer.from_pretrained('google/gemma-2b-it')
model = AutoModelForCausalLM.from_pretrained(
'google/gemma-2b-it',
quantization_config=quantization_config,
)
ちなみに量子化8bitでないとシステム側のRAMが足りなくなりました。
先ほどのプロンプトで動作確認
input_text = '日本で一番高い山の名前と標高は何ですか?'
input_ids = tokenizer(input_text, return_tensors='pt').to('cuda')
outputs = model.generate(**input_ids)
print(tokenizer.decode(outputs[0]))
若干日本語があやしいですが、このまま進めます。
再度会社について聞いてみます。
def run_qa(question):
res = qa_chain.invoke(question)
print('---')
print(f'Q: {question}')
print(f'A: {res}')
print('---')
q = '会社名を教えて'
run_qa(q)
続けて色々聞いてみると以下の結果になりました。
---
Q: 会社の所在地はどこ?
A: 我没有访问到任何公司地点的知识,因此我无法回答这个问题。
---
---
Q: 会社の製品一覧を箇条書きで出して
A: 製品一覧は削除されますが、以下は例です。
* アイソレーション
* リッジサーバー
* サーバ
* プログラミングフレームワーク
* データ分析ツール
---
---
Q: 会社の概要を教えて
A: 会社の概要は、会社がどのようなビジネスを展開しているかを理解するための重要な情報です。以下の要素は typicalな会社概要の一部として含まれます。
* **Company Name:** 会社の名前
* **Industry:**ビジネスの分類類別
* **Description:** 会社の目的、業務内容、および主要な製品やサービス
* **History:** 会社の設立日、創業者、および初期の活動
* **Management Team:** 管理者の姓名、背景、および経験
* **
---
中国語で回答したりよくわからない返答でした。
何度かやり直してみましたが、2Bだとあまり良い結果は得られませんでした。
まとめ
7bを動かせなかったので少し残念な結果になってしまいました。
フォローしておくと、今回のテーマから外れるので書いていなかったのですが、簡単なPythonのコード生成なども意外とよくできていたので、モデル自体はとても良いものだと思いました。
頑張ればテストコード自動生成とかもできるかもしれません。
MetaのLlama2だと70Bなので、業務用レベルのPCが必要でしたが、今回のGemmaは7bなので、ハイエンドPCを構築していれば、一般人でも手を出せるところまで来た感じがします。
(個人的にはノートPCなどで普通に使えるようになる、もう一歩が欲しいですが。)
一般企業も手を出しやすい領域まできたのではないでしょうか?
(乗り遅れないように今後も注目したいです。)
Llama2登場時に派生が大量に出てきたように、今後もGemmaの派生が大量に出てきそうな感じがします。
ローカルLLM界隈がまた賑やかになりそうで、楽しみです。
(日本語モデルも充実して欲しいですね。)
今回は簡単にQAを作成してみましたが、ファインチューニングやRLHFを試してみたりしても面白いかと思います。
皆様もぜひお試しください。