実践LangChain！RAGによる特化LLMシステムの作り方

# はじめに

ChatGPTを初めとした大規模言語モデル (LLM) が話題になっており、ちょっとした質問でも非常に優れたアウトプットが得られるようになりました。一方、ChatGPTを企業で使用する場合、社内情報をChatGPTは保持していないため、社内情報について答えられないという課題があり、社内特化LLMシステムを構築する必要があります。  
特化システムを作るためには、[こちらの記事](https://qiita.com/t_serizawa/items/6613ea0bc2a13822f5cb)でも紹介している通りLLMそのものをfine tuningする方法と、LangChainを使ってVector MatchingするRetrieval Augmented Generation (RAG) の2つがあります。ただ、LLMのfine tuningは非常に大きな計算コストとデータセット構築作業が必要になります。  
本記事では、RAGによる特化LLMシステムの構築方法を紹介します。本記事内で使用するLLMはGPU不要のものを用いているため、一般的なPC環境でも試せると思います。

# RAGの手順

RAGのフローは以下の図のような形となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542065/ca88e88d-4c9e-f3e4-e959-79d10628d4f9.png)
まず社内情報など追加で与えたい (特化させたい) 情報をまとめたtxtやPDFなどのファイルから文章を抽出してEmbeddingを取ることで、その言葉のVector DBを構築します。その後、LLMにユーザが質問をしたとき、LLMはDBを参照しながら回答を作成します。

実装手順としては、大きく以下の2つのステップが必要となります。
1. Vector DBの構築
    追加で与えたい情報のデータセットに対し、EmbeddingをすることでVector DBを構築します。
2. LLMとVector DBの連携
    プロンプトで質問をした際、質問をEmbeddingしてVector DBと類似性検索をします。類似度の高い情報をDBから抽出し、その情報をLLMが整理してアウトプットを返します。

# 実装
### 1. Vector DBの構築
#### 1.1. 環境構築
必要となるライブラリをインストールします。ライブラリの更新が早いため、バージョンによっては動作しない可能性がありますが、少なくとも以下のバージョンでは動作確認をしています。

```python
!pip install langchain==0.0.163
!pip install pygpt4all==1.1.0
!pip install transformers
!pip install datasets
!pip install chromadb
!pip install tiktoken
```

インストールが終わったら、以下の通り使用するライブラリをインポートします。

```python
import pandas as pd
from datasets import load_dataset

from langchain import PromptTemplate, LLMChain
from langchain.llms import GPT4All
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain import PromptTemplate
from langchain.document_loaders import TextLoader
from langchain import PromptTemplate
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

from langchain.document_loaders import TextLoader
from langchain.indexes import VectorstoreIndexCreator
from langchain.embeddings import HuggingFaceEmbeddings
```

#### 1.2. 追加データのダウンロードと成形
このスクリプトでは、[Hugging Faceにある医療データ](https://huggingface.co/datasets/medical_dialog)を用いています。`description`と`utterances`の2つのカラムで構成されていますが、今回はこの中の`utterances`にある患者と医者のやり取りを使用しています。

```python
dataset = load_dataset('medical_dialog', 'processed.en')
df = pd.DataFrame(dataset['train'])

dialog = []

# 患者と医者の発言をそれぞれ抽出した後、順にリストに格納
patient, doctor = zip(*df['utterances'])
for i in range(len(patient)):
  dialog.append(patient[i])
  dialog.append(doctor[i])

df_dialog = pd.DataFrame({"dialog": dialog})

# 成形終了したデータセットを保存
df_dialog.to_csv('medical_data.txt', sep=' ', index=False)
```

#### 1.3. 追加データをEmbedding
`TextLoader`を使用してテキストをロード後、Embeddingします。  
今回はテキストファイルからロードしていますが、csvやPDFからもロードしたりできるようです。
`VectorstoreIndexCreator`でVector Storeを利用しています。Vector StoreはLangChainのデフォルトではChromaDBが指定されており、今回はそのまま使用します。ChromaDB以外にも、Elasticsearchなど[公式のこちら](https://api.python.langchain.com/en/latest/modules/vectorstores.html)にあるものを利用できます。
また、Embeddingにはいくつか種類がありますが、今回はHuggingFaceEmbeddingを使用します。LnagChainで使用できるEmbeddingは[公式のこちら](https://api.python.langchain.com/en/latest/modules/embeddings.html)に載っています。

```python
loader = TextLoader('medical_data.txt', encoding="utf-8")
index = VectorstoreIndexCreator(embedding= HuggingFaceEmbeddings()).from_loaders([loader])
```

### 2. LLMとVector DBの連携

#### 2.1. LLMのセッティング
今回使用するLLMのセッティングをします。今回は[LangChain LLMs](https://api.python.langchain.com/en/latest/modules/llms.html)にあるGPT4allを使用します。GPT4allはGPU無しでも動くLLMとなっており、ちょっと試してみたいときに最適です。

```python
llm_path = './model/ggml-gpt4all-j-v1.3-groovy.bin'  # replace with your desired local file path
callbacks = [StreamingStdOutCallbackHandler()]
llm = GPT4All(model=llm_path, callbacks=callbacks, verbose=True, backend='gptj')
```

#### 2.2. 類似性検索

ここで、プロンプトとして与える入力に対する類似性検索を行います。
今回はモデル、追加データ共に英語のため、英語で"what is the solution for soar throat" (のどの痛みに対する解決策は何ですか？) という質問を行います。すると、類似性の高い上位4件 (k=4) の発言をVector DBから抽出してきており、それらを結合して1つのコンテキストとしてまとめます。

```python
results = index.vectorstore.similarity_search("what is the solution for soar throat", k=4)
context = "\n".join([document.page_content for document in results])
print(f"{context}")
```

#### 2.3. LLMで回答生成
2.1.で準備したLLM (GPT4all) と2.2.で抽出したコンテキストを用いて回答を作成します。
最初に`template`を作成します。"Please use the following context to answer questions" (コンテキストを元に回答してください) とし、その上で質問から回答を行うよう指定しておきます。
作成した`template`を`PromptTemplate`としてまとめ、`LLMChain`に渡すことでLangChainを実行する準備をします。
後は`llmcahin.run`で質問を投げることで、LLMで回答を作成します。

```python
template = """
Please use the following context to answer questions.
Context: {context}
---
Question: {question}
Answer: Let's think step by step."""

prompt = PromptTemplate(template=template, input_variables=["context", "question"]).partial(context=context)

llm_chain = LLMChain(prompt=prompt, llm=llm)
print(llm_chain.run("what is the solution for soar throat"))
```

[LLMによる回答と訳]
```
 Firstly, identify if there's an allergy that can be a problem or the cold can make your sore throat worse. So if your throat feels bad after your fever or when it gets wet and drips. And when it dries out after using warm liquids to quench the fever, this will usually give your sore throat an idea about its throat, even though your fever will lower to its peak before getting higher to anther one again, after 3 to 7 days you should consult with the doctor for any medical conditions such as infections. You have mentioned your canker sore but we still do not have the confirmation for Covid19 infection as there might be different viruses, even there's some but that could also affect other things than coronavirus like viral myloidoma or parainfluenza, for a coronavirus we also should use different medication but as your allergy, this time to cure sore throat or is just there as your regular allergic. I mean there might be something to take medicine as we did but your regular antihistamines don't always cure or heal all kinds of symptoms that could have many underlying issues or virus and antibiotics also could take away from some underlying infection which your sore throat and regular antibiotic just is treating but with it it would kill your bad micro Firstly, identify if there's an allergy that can be a problem or the cold can make your sore throat worse. So if your throat feels bad after your fever or when it gets wet and drips. And when it dries out after using warm liquids to quench the fever, this will usually give your sore throat an idea about its throat, even though your fever will lower to its peak before getting higher to anther one again, after 3 to 7 days you should consult with the doctor for any medical conditions such as infections. You have mentioned your canker sore but we still do not have the confirmation for Covid19 infection as there might be different viruses, even there's some but that could also affect other things than coronavirus like viral myloidoma or parainfluenza, for a coronavirus we also should use different medication but as your allergy, this time to cure sore throat or is just there as your regular allergic. I mean there might be something to take medicine as we did but your regular antihistamines don't always cure or heal all kinds of symptoms that could have many underlying issues or virus and antibiotics also could take away from some underlying infection which your sore throat and regular antibiotic just is treating but with it it would kill your bad micro

[訳]
まず、問題となる可能性のあるアレルギーがあるかどうか、または風邪によって喉の痛みが悪化する可能性があるかどうかを特定します。  
そのため、熱が出た後や喉が濡れて滴り落ちたときに喉の調子が悪くなる場合があります。 そして、熱を抑えるために温かい液体を使用した後、喉が乾くと、通常、3〜7日後に熱が最高点まで下がってから再び高くなる前に、喉の痛みが喉の痛みに気づくようになります。  
感染症などの病状については医師に相談してください。  
口内炎について言及されましたが、別のウイルスが存在する可能性があるため、Covid19 感染の確認はまだ得られていません。  
たとえ存在しても、ウイルス性ミロイドーマやパラインフルエンザなど、コロナウイルス以外のものにも影響を与える可能性があります。コロナウイルスについては、別のウイルスを使用する必要もあります。  
薬ですが、アレルギーとして、今回は喉の痛みを治すため、または通常のアレルギーとしてただそこにあります。  
つまり、私たちと同じように薬を服用することはあるかもしれませんが、通常の抗ヒスタミン薬は、多くの根本的な問題を抱えている可能性のあるあらゆる種類の症状を必ずしも治したり治すわけではなく、ウイルスや抗生物質は、喉の痛みや喉の痛みなどの根本的な感染症を取り除く可能性もあります。   
通常の抗生物質は治療にすぎませんが、抗生物質を使用すると悪い微生物を殺すことになります。 まず、問題となる可能性のあるアレルギーがあるかどうか、または風邪によって喉の痛みが悪化する可能性があるかどうかを特定します。  
そのため、熱が出た後や喉が濡れて滴り落ちたときに喉の調子が悪くなる場合があります。 そして、熱を抑えるために温かい液体を使用した後、喉が乾くと、通常、3〜7日後に熱が最高点まで下がってから再び高くなる前に、喉の痛みが喉の痛みに気づくようになります。 感染症などの病状については医師に相談してください。 口内炎について言及されましたが、別のウイルスが存在する可能性があるため、Covid19 感染の確認はまだ得られていません。  
たとえ存在しても、ウイルス性ミロイドーマやパラインフルエンザなど、コロナウイルス以外のものにも影響を与える可能性があります。コロナウイルスについては、別のウイルスを使用する必要もあります。 薬ですが、アレルギーとして、今回は喉の痛みを治すため、または通常のアレルギーとしてただそこにあります。  
つまり、私たちと同じように薬を服用することはあるかもしれませんが、通常の抗ヒスタミン薬は、多くの根本的な問題を抱えている可能性のあるあらゆる種類の症状を必ずしも治したり治すわけではなく、ウイルスや抗生物質は、喉の痛みや喉の痛みなどの根本的な感染症を取り除く可能性もあります。 通常の抗生物質は治療するだけですが、それを使用すると悪い微生物を殺してしまいます
```

Vector DBを使うことでどの程度出力結果が変わったかを確認するために、GPT4allだけで同じ質問をした際の回答と訳も確認します。

```
1. If you have a sore throat, it is important to first determine the cause.
2. If you have a sore throat caused by a cold, you can take over-the-counter pain relievers such as acetaminophen or ibuprofen to help reduce pain and inflammation.
3. If you have a sore throat caused by a bacterial infection, you can take antibiotics prescribed by a doctor.
4. If you have a sore throat caused by a viral infection, you can take over-the-counter pain relievers such as acetaminophen or ibuprofen to help reduce pain and inflammation.
5. If you have a sore throat caused by a fungal infection, you can take antifungal medication prescribed by a doctor.
6. If you have a sore throat caused by a chemical exposure, you can seek medical attention immediately.
7. If you have a sore throat caused by a physical injury, you can seek medical attention.
8. If you have a sore throat caused by a psychological issue, you can seek professional help.
9. If you have a sore throat caused by a food allergy, you can seek medical attention.
10. If you have a sore throat caused by a medication
...(同じ出力がもう一回繰り返し)

[訳]
1. 喉に痛みがある場合は、まず原因を特定することが重要です。
2. 風邪による喉の痛みがある場合は、痛みや炎症を軽減するために、アセトアミノフェンやイブプロフェンなどの市販の鎮痛剤を服用できます。
3. 細菌感染による喉の痛みがある場合は、医師が処方した抗生物質を服用できます。
4. ウイルス感染による喉の痛みがある場合は、痛みや炎症を軽減するためにアセトアミノフェンやイブプロフェンなどの市販の鎮痛剤を服用できます。
5. 真菌感染症による喉の痛みがある場合は、医師が処方する抗真菌薬を服用できます。
6. 化学物質への曝露によって喉に痛みがある場合は、すぐに医師の診察を受けることができます。
7. 身体的な怪我が原因で喉に痛みがある場合は、医師の診察を受けることができます。
8. 心理的な問題が原因で喉の痛みがある場合は、専門家の助けを求めることができます。
9. 食物アレルギーによる喉の痛みがある場合は、医師の診察を受けることができます。
10. 薬によりのどの痛みがある場合
```

LLMだけの出力では一般的なのどの痛みに対する対策を答えている一方、今回のVector DBから医者と患者のやり取りを引用しているRAGでは医療知識を踏まえた回答が作成されていることが分かります。

# まとめ

以上、LLMとLangChainを使ったRAGを紹介しました。手元の環境でも簡単に試せるので、ぜひ動かしてみてください！
この記事がLLM tuningを始めるきっかけになれば幸いです。

# 参考

- [FineTuning Local Large Language Models on Your Data Using LangChain](https://pub.towardsai.net/finetuning-local-large-language-models-on-your-data-using-langchain-9229da66ad9b)