🐷

LangChainを用いて大量ファイルをロードするVectorDBを作ってみた(2)

2024/05/21に公開

ループを回してデータベースに情報を格納

約2週間かけて、一通り『LangChain完全入門』のサンプルプログラムをコーディングして、動作させることができました。ただ、「chainlit」については「pip install chainlit」でインストールすると、実行時にエラーが出てしまいました。そのため、バージョンを0.5.2まで落とし、「pip install chainlit==0.5.2」としてインストールしました。

ここまで行けたら、次は「大量ファイルをロードするVectorDB」を作れないか?を考えることにしました。

よーく見てみると、「03_retrieval」のサンプルコード「prepare_3.py」がもしかするとやりたいことに近いプログラムではないかと思いました。

prepare_3.py
from langchain.document_loaders import PyMuPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import SpacyTextSplitter
from langchain.vectorstores import Chroma

loader = PyMuPDFLoader("./sample.pdf")
documents = loader.load()

text_splitter = SpacyTextSplitter(
    chunk_size=300, 
    pipeline="ja_core_news_sm"
)
splitted_documents = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings(
    model="text-embedding-ada-002"
)

database = Chroma(  #← Chromaを初期化する
    persist_directory="./.data",
    embedding_function=embeddings
)

database.add_documents(
    splitted_documents,
)

print("データベースの作成が完了しました。")
}

この中の

database.add_documents(
    splitted_documents,
)

この部分に先ずは注目です。
ここでのdatabaseは「SQLite」のようです。
「add_documents」ということは、名称から推測すると、これをループさせてdatabaseに(性能の問題はあるが)無限にデータを蓄積させることができると思いました。

ファイルを1つずつロードして、「add_documents」を呼び出す。

loader = PyMuPDFLoader("./sample.pdf")
documents = loader.load()

この部分をループさせると行けそうです。

ネストしたフォルダ内のファイルを読込みたい

それから、今回の読込元のフォルダは以下のようにしてみました。

.
├─ sample.pdf
├─ 提案依頼書(20240307_3).docx
└─ BOX_URL一覧.xlsx
└─ subfolder
   ├─ 【5月発行】日経トレンディ2024年.pdf
   └─ subsubfolder
          └─ TODO.xlsx

こんな感じでネストしたフォルダ内のファイルを全て読込んでデータベースに蓄積させられればばっちりです。

さてループを回して、フォルダをネストさせてファイルを取得しようと思ったのですが、私の知識と経験では、意外と難しい・・・
そこで色々検索をしてみたところ、それらしいプログラムコードが出てきました。


filepath_list = glob.glob(os.path.join(path_file, "**/*.*"), recursive=True)

for file in filepath_list: 
 # ・・・・・

よし!これを使ってみよう。pythonはサンプルコードやライブラリが充実していてとてもよいプラグラム言語だと思いました。

で、実際にコーディングをしてみました。以下が主な注意点です。

・「os」と「glob」のimport
  フォルダをネストしてファイルを取得するのに必要ですので追加しました。
・「path_file」
  私はWindows系のPCでコーディングをしたため、フォルダ間のセパレータを「\\」とする必要があります。Linux系であれば「/」なのでしょうけどね。
・「databaseの宣言文」「embeddingsの宣言文」
  ループの箇所よりも上の方に持って行きました。
・「database.add_documents」
  ループの中に入れてあげました。

import os
import glob
from langchain.document_loaders import PyMuPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import SpacyTextSplitter
from langchain.vectorstores import Chroma

path_file = "C:\\Users\\*******\\langchain_book\\03_retrieval\\data"
filepath_list = glob.glob(os.path.join(path_file, "**/*.*"), recursive=True)

embeddings = OpenAIEmbeddings(
    model="text-embedding-ada-002"
)

database = Chroma(
    persist_directory="./.data",
    embedding_function=embeddings
)

for file in filepath_list: 
    loader = PyMuPDFLoader(file)
    documents = loader.load()

    text_splitter = SpacyTextSplitter(
        chunk_size=300, 
        pipeline="ja_core_news_sm"
    )
    splitted_documents = text_splitter.split_documents(documents)

    database.add_documents(
        splitted_documents,
    )

print("データベースの作成が完了しました。")

いざ実行です。
お、上手くいったみたいです。ただ、チャンクサイズが設定値を超えているようですね。
これがどのような影響を与えるのか、今後確認していこうと思います。

angchain-openai` and import as `from langchain_openai import OpenAIEmbeddings`.
  warn_deprecated(
Created a chunk of size 630, which is longer than the specified 300
Created a chunk of size 366, which is longer than the specified 300
Created a chunk of size 484, which is longer than the specified 300
Created a chunk of size 328, which is longer than the specified 300
データベースの作成が完了しました。

データベースの中身を確認

では、次にデータベース(SQLite3)ではどうなっているかを見てみました。データベースの中身を確認するために、「DB Browser for SQLite(64bit版)」をPCにインストールして確認することにしました。

image.png

↑↑こんなにテーブルがあるんですかぁ。
これは「Chroma」のデータベースですが、ほかにも「FAISS」というのもあるらしいのですが、それは今度確認することにします。

image.png

↑↑「embedding_fulltext_search_content」というテーブルの中身はこんな風になっています。

image.png

↑↑「embedding_metadata」というテーブルが結構重要ではないかと思います。「key」カラムがあり、その中で「source」のみレコード抽出をすると、右上でファイル名が記載されています。全てのファイルが一応読み込まれているようです。

1つ気になったのですが、PDF以外のファイルもあったのですが、ちゃんとロードできたのでしょうか?
一見上手くいっているようなのですが。。。

今日はここまで。

Discussion