OPENAIのAPIを使って、ローカル情報から回答をChatGPTが生成

# OPENAIのAPIを使って、ローカル情報から回答をChatGPTが生成

この記事を参照
#ありがとうございます


https://qiita.com/windows222/items/232f05bafa95a9c8874e

## 作業分解

### Ａ：データベースの作成
①PDFを読み込み、PDFをテキストに変換（txtに書き込み、そして再読み込み）
②tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
③text_splitter = RecursiveCharacterTextSplitter
④text_splitter.create_documents([text])
⑤embeddings = OpenAIEmbeddings()
⑥FAISS.from_documents(chunks, embeddings)


### Ｂ：検索
①embeddings = OpenAIEmbeddings()
②FAISS.load_local("faiss_index2", embeddings)
③load_qa_chain(OpenAI(temperature=0,max_tokens=1000), chain_type="stuff")

④docs = db.similarity_search(query)
⑤return chain({"input_documents": docs, "question": query}, return_only_outputs=True)


#### A-1
```
doc = textract.process("bbbb.pdf")
#Word,PowerPoint,PDFなどからテキストを抽出できる「textract」
with open('bbbb.txt', 'w') as f:
    f.write(doc.decode('utf-8'))
with open('bbbb.txt', 'r') as f:
    text = f.read()

※PyPDFLoader方法もあるが、ページ単位でのトークンサイズとなり、
　データ量の関係で
　探索に失敗するため使わない（PDFファイルのメタデータが取れるメリットはあるらしい）
#loader = PyPDFLoader("RFID_API.pdf")
#pages = loader.load_and_split()
#chunks = pages

```
### A-2
```
from transformers import GPT2TokenizerFast
model_id = "gpt2"#gpt2-large等もある
tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
```

・「トークナイザ（Tokenizer）」は、自然言語処理（NLP）の分野でよく用いられるツールで、テキストデータを個々の「トークン」（一般的には単語や文字）に分割する役割を持っています。

・トークナイザは機械学習モデルがテキストを理解しやすい形式に変換するための初期ステップとして重要です。これにより、単語やフレーズ、文のパターンを見つけて予測や分析を行うことが可能になります。

・訓練済みモデルを使うときは，語彙を揃えるために対応するトークナイザーを使います．transformersにはGPT-2のためのトークナイザーとしてGPT2TokenizerFastがあるので，これを使うことにします．
※要するに形態素解析等してくれるツールの一つがtransformerが提供してくれているGPT2モデルという名前のトーカナイザ

### A-3,4
```
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)

#上記の通りlangchainが提供するtext_splitterを使うと、長い文章を分割してくれるツールがあるということ

def count_tokens(text: str) -> int:
    return len(tokenizer.encode(text))


text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 512,
    chunk_overlap  = 24,
    length_function = count_tokens,
)
chunks = text_splitter.create_documents([text])

```

RecursiveCharacterTextSplitterについて説明
＞
ここで知っておくべき重要なパラメータはchunkSizeと ですchunkOverlap。chunkSize最終的なドキュメントの最大サイズ (文字数に関して) を制御します。chunkOverlapチャンク間のオーバーラップの量を指定します。これは、テキストが変に分割されていないことを確認するのに役立つことがよくあります。

他にもあるのを見受けるが、あとで調査
text_splitter.create_documents([text])でチャンク（塊）に分割

```
A-5
embeddings = OpenAIEmbeddings()
#埋め込み生成のためのクラス
https://github.com/hwchase17/langchain/blob/master/langchain/embeddings/openai.py
```

A-6

・ベクトルデータベース
機械学習では、文章、画像、音声、動画などのあらゆるデータを、特徴を抽出したベクトルに変換して扱うことが多いです。ベクトルは、数百から数千の次元の数値として表現されます。ベクトルデータベースは、このような特殊なデータ構造を持つデータを扱うために作られたデータベースです。

ベクトルデータベースを使うことで、ベクトル間の類似性を高速に検索することができます。これによって、文章のセマンティック検索、画像・音声・映像などの類似検索、ランキングやレコメンド、重複検出、異常検出、などに応用することができます。

種類
有料のものもあれば無料のものの
chroma Pinecone 
SQliteも拡張すればベクトル検索対応
https://note.com/mahlab/n/n5d59b19be573

今回はメタ社（Facebook社）の提供する
Faissを使う。
※データベースはどこに存在してるんだろう
　pipでライブラリとしてインストールしてるから
　仮想環境のsite_packageのどこかにある？
　
　→ローカルフォルダに作られてた
参考：
https://github.com/facebookresearch/faiss

```
pip install faiss-cpu
or
pip install faiss-gpu
#vector databaseの作成
db = FAISS.from_documents(chunks, embeddings)
```

チャンクに分けてテキストをembeddingsして
FAISSデータベースに登録

embeddingsとは：
自然言語を計算が可能な形に変換することをEmbeddingと呼ぶようです。
多くの場合、単語や文章等をベクトル表現に変換する操作のことを指しています。

ここまでの概念
![Something went wrong]()

### Ｂ：検索
①embeddings = OpenAIEmbeddings()
②FAISS.load_local("faiss_index2", embeddings)
③load_qa_chain(OpenAI(temperature=0,max_tokens=1000), chain_type="stuff")
④docs = db.similarity_search(query)
⑤return chain({"input_documents": docs, "question": query}, return_only_outputs=True)


#### B-1
```
embeddings = OpenAIEmbeddings()
```
いわずもがな
#### B-2

```
db = FAISS.load_local("faiss_index2", embeddings)
```
データベースのロード
#### B-3
```
from langchain.chains.question_answering import load_qa_chain

load_qa_chain(OpenAI(temperature=0,max_tokens=1000), chain_type="stuff")
```


# まとめ

# データベース構築
```
import os
import pandas as pd
#import requests
import textract
#from bs4 import BeautifulSoup
import matplotlib.pyplot as plt
from transformers import GPT2TokenizerFast
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains.question_answering import load_qa_chain
from langchain.llms import OpenAI
from langchain.chains import ConversationalRetrievalChain

#ここにOpenAIから取得したキーを設定します。
os.environ["OPENAI_API_KEY"] = '個人のAPIキー'

#テキスト取り込み
doc = textract.process("bbbb.pdf")
with open('bbbb.txt', 'w') as f:
    f.write(doc.decode('utf-8'))

with open('bbbb.txt', 'r') as f:
    text = f.read()
#トーカナイザ選択
model_id = "gpt2"#gpt2-large等もある
tokenizer = GPT2TokenizerFast.from_pretrained(model_id)

#トーカナイザとlangchainのメソッドを使ってテキストをチャンクに分割
def count_tokens(text: str) -> int:
    return len(tokenizer.encode(text))
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 512,
    chunk_overlap  = 24,
    length_function = count_tokens,
)
chunks = text_splitter.create_documents([text])


#embeddingのクラスを読み込み
embeddings = OpenAIEmbeddings()

# vector databaseを作成
db = FAISS.from_documents(chunks, embeddings)

#ローカルに保存する
db.save_local("koutei_index")

```

# 検索　streamlit使ってデモ実装
```

import os
import pandas as pd
import textract
import matplotlib.pyplot as plt
from transformers import GPT2TokenizerFast
from transformers import GPT2TokenizerFast
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains.question_answering import load_qa_chain
from langchain.llms import OpenAI
from langchain.chains import ConversationalRetrievalChain
import streamlit as st

os.environ["OPENAI_API_KEY"] = '個人のAPIキー'

embeddings = OpenAIEmbeddings()
db = FAISS.load_local("faiss_index2", embeddings)
chain = load_qa_chain(OpenAI(temperature=0,max_tokens=1000), chain_type="stuff")

def answer_query(query):
    docs = db.similarity_search(query)
    return chain({"input_documents": docs, "question": query}, return_only_outputs=True)

st.title("検索システム")
st.write("質問を入力してエンターを押してください。:")

query = st.text_input("質問:", "")
if query:
    st.write("回答を作成...")
    answer = answer_query(query)
    st.write(answer["output_text"])

```