はじめに
PDFファイルを使ってRAGを行う時のPDFファイルの扱い方についてです。 以下の3つで比較しました。
pymupdfでテキストを抽出して手動でchunkに分割する方法
pymupdfでテキストを抽出して全文をファイルにいったん保存する方法
PyPDFLoaderを使う方法
使用したPDFファイル
日本透析医学会が公開している『2015年版 慢性腎臓病患者における腎性貧血治療のガイドライン 』(透析会誌 2016; 49(2): 89-158)を使わせて頂きました。
このPDFの特徴は以下の通りです。
70ページと比較的ボリュームがある
図表が少ない
Pythonスクリプト
Embeddingの結果をいったん保存する
# pip install pymupdf import fitz import re from langchain_community.document_loaders import PyPDFLoader from langchain_community.document_loaders import TextLoader from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings from langchain.indexes import VectorstoreIndexCreator from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.schema.document import Document embed_model_id = "pkshatech/GLuCoSE-base-ja" embeddings = HuggingFaceEmbeddings(model_name=embed_model_id) filename = 'guideline.pdf' # from text doc = fitz.open(filename) all_text ="" for page in doc: text = page.get_text().replace("\n", "").replace("\u3000", "") text = re.sub("^\\d*日本透析医学会雑誌49 巻2 号2016", "", text) all_text += text text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) docs = [Document(page_content=x) for x in text_splitter.split_text(all_text)] index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_docs"} ).from_documents(docs) # save text with open("textfile.txt", "w") as f: f.write(all_text) loader = TextLoader("textfile.txt") index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_textfile"} ).from_loaders([loader]) # from pdf file loader = PyPDFLoader(filename) index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_pdf"} ).from_loaders([loader])
実行
from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings from langchain_ollama import OllamaLLM from langchain.indexes.vectorstore import VectorStoreIndexWrapper query = "鉄補充基準は?" embed_model_id = "pkshatech/GLuCoSE-base-ja" embeddings = HuggingFaceEmbeddings(model_name=embed_model_id) llm = OllamaLLM(model="gemma_low_temperature") # from text data vectorstore = Chroma( persist_directory="./data/vectorstore_from_docs", embedding_function=embeddings ) index = VectorStoreIndexWrapper(vectorstore=vectorstore) answer = index.query(query, llm=llm) print("From text data:") print(answer) # from text file vectorstore = Chroma( persist_directory="./data/vectorstore_from_textfile", embedding_function=embeddings ) index = VectorStoreIndexWrapper(vectorstore=vectorstore) answer = index.query(query, llm=llm) print("From text file:") print(answer) # from pdf vectorstore = Chroma( persist_directory="./data/vectorstore_from_pdf", embedding_function=embeddings ) index = VectorStoreIndexWrapper(vectorstore=vectorstore) answer = index.query(query, llm=llm) print("From pdf file:") print(answer)
結果の比較
temperature=0.01にしています。(解答のランダム性を減らしています) 質問は「鉄補充基準は?」です。
方法1
このテキストによると、鉄補充療法の開始基準は以下の通りです。
1. ESA投与前に鉄補充を考慮するケース:
- ESAも鉄剤も投与されておらず、目標Hb値が維持できない症例において、血清フェリチン値が50 ng/mL未満の場合。
2. ESA投与下で目標Hb値が維持できない場合に鉄補充を考慮するケース:
- 血清フェリチン値が100 ng/mL未満かつTSATが20%未満の場合。
- 鉄利用率を低下させる病態が認められず、血清フェリチン値が100 ng/mL未満またはTSATが20%未満の場合。
重要な注意点:
- 鉄補充療法を行う際には、鉄過剰にならないよう十分注意する必要があります。
方法2
このテキストによると、鉄補充療法の開始基準は以下の通りです。
1. ESA 投与に先行した鉄補充療法:
- ESA 製剤も鉄剤も投与されておらず目標Hb値が維持できない症例において、血清フェリチン値が50 ng/mL 未満の場合、ESA 投与に 先行した鉄補充療法を考慮する。
2. ESA 投与下で目標Hb値が維持できない症例:
- 血清フェリチン値が100 ng/mL 未満かつTSAT が20%未満の場合、鉄補充療法を推奨する。
- 鉄利用率を低下させる病態が認められない場合かつ、血清フェリチン値が100 ng/mL 未満またはTSAT が20%未満の場合には鉄補充 療法を考慮する。
重要な注意点:
- 鉄補充療法を行う場合には鉄過剰にならないよう十分に注意する必要がある。
- TSAT 20%未満で血清フェリチン値100 ng/mL 以上の症例では、慢性炎症等の存在が想定されるため、鉄剤投与開始前に病態を評価し、補充療法の適否について慎重に判断する必要がある。
方法3
The provided text focuses on diagnosing and treating anemia in kidney transplant patients, but it doesn't specify iron supplementation criteria.
To find information about iron supplementation criteria for these patients, you would need to consult additional medical resources such as:
- Clinical practice guidelines: Organizations like the National Kidney Foundation (NKF) or the American Society of Nephrology (ASN) often publish guidelines on managing anemia in CKD and transplant patients.
- Medical textbooks: Nephrology textbooks will have detailed information about iron deficiency anemia and its treatment in kidney disease.
- Research articles: Searching medical databases like PubMed for studies on iron supplementation in kidney transplant recipients can provide specific criteria and recommendations.
Remember, it's crucial to consult with a healthcare professional for personalized advice regarding iron supplementation. Self-treating can be dangerous.
補足1
Gemma2をOllamaから使用しています。 以下のようにModelfileを作成して元のモデルのtemperatureを変更しました。
FROM gemma2:27b PARAMETER temperature 0.01
ollama create gemma_low_temperature -f ./Modelfile
補足2
Windowsの場合には「utf-8」を指定する必要がありました。
# pip install pymupdf import fitz import re from langchain_community.document_loaders import PyPDFLoader from langchain_community.document_loaders import TextLoader from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings from langchain.indexes import VectorstoreIndexCreator from langchain.text_splitter import CharacterTextSplitter from langchain.schema.document import Document embed_model_id = "pkshatech/GLuCoSE-base-ja" embeddings = HuggingFaceEmbeddings(model_name=embed_model_id) filename = 'guideline.pdf' # from text doc = fitz.open(filename) all_text ="" for page in doc: text = page.get_text().replace("\n", "").replace("\u3000", "") text = re.sub("^\\d*日本透析医学会雑誌49 巻2 号2016", "", text) all_text += text text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100) docs = [Document(page_content=x) for x in text_splitter.split_text(all_text)] index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_docs"} ).from_documents(docs) # save text with open("textfile.txt", "w", encoding='utf-8') as f: f.write(all_text) loader = TextLoader("textfile.txt", encoding='utf-8_sig') index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_textfile"} ).from_loaders([loader]) # from pdf file loader = PyPDFLoader(filename) index = VectorstoreIndexCreator( vectorstore_cls=Chroma, embedding=embeddings, vectorstore_kwargs={ "persist_directory": "./data/vectorstore_from_pdf"} ).from_loaders([loader])
補足3
pdfminerを使った場合も試しました。
# pip install pdfminer.six import re from pdfminer.high_level import extract_text text = extract_text("guideline.pdf", page_numbers=list(range(29, 32))) text = text.replace("\n", "") text = text.replace("\x0c", "") text = text.replace("\u3000"," ") text = text.replace("\x07", " ") text = text.replace("(cid:514)", "-") text = re.sub("\\d*日本透析医学会雑誌 49 巻 2 号 2016\\d*", "", text) with open("result.txt", "w", encoding="utf-8") as f: f.write(text)