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

![sky.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/672ff8d2-a037-5b8d-e303-85259a251fd7.jpeg)
# はじめに
　前回までの投稿(※)で、VectorDBに「Chroma」を適用して大量ファイルを読み込ませて、「chainlit」で質問を受ける画面を立ち上げるようにしたのですが、ファイル数が多くなるにつれて、求めている回答が得られないという事象が発生しました。
 「チャンクサイズ」や「emmbeddingモデル」にも左右されると思うのですが、あまりお金をかけたくないのでvectorDBで精度の違いを確認してみることにして、最適なものを利用することにしました。

https://qiita.com/ogi_kimura/items/fa16888c60dfae0a6479

https://qiita.com/ogi_kimura/items/ecedd7c85ca1b0b0fc58

https://qiita.com/ogi_kimura/items/155a83163849db593b51

# VectorDBの選定
　今回比較対象とするvectorDBは、以下です。
 
 ### 1．Chroma
  これは、以前投稿した「LangChainを用いて大量ファイルをロードするVectorDBを作ってみた(1)～(3)」で試したことがあるので、あまり問題ないです。何とかなるでしょう。
  
 ### 2．Qdrant
https://zenn.dev/kun432/scraps/8eab42294bde75
 
  kun432さんの記事を見ながら見様見真似でコーディングをしてみました。
  kun432さんありがとうございます！大変参考になりました。

 ### 3．FAISS
https://qiita.com/tatsuki-tsuchiyama/items/b1ab77c7f8787dbbbfae

　土山竜輝さんの記事を見ながら見様見真似でコーディングをしてみました。
  土山さんありがとうございます！ 大変参考になりました。

# 早速コーディング
　３つそれぞれちょっとしたプログラム方言があるので、別ファイルでプログラムを作成し、vectorDBをローカルPCに格納するような処理としました。今回は読込み対象のファイルはxmlファイルのみにしました。
 　`PyMuPDFLoader`は本当にすごくて、今回のプログラミングで、ExcelファイルやWordファイルも読み込んでくれることがわかりました:sunny:。これは便利です。
 

 ### 1．Chroma
 ```python:chroma.py
import glob
import os
from dotenv import load_dotenv
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.document_loaders import PyMuPDFLoader
from langchain.vectorstores import Chroma

load_dotenv()

docs = []

files = glob.glob(os.path.join("C:\\Users\\***\\langchain_book\\", "**/*.*"), recursive=True)
for file in files:
    base, ext = os.path.splitext(file)
    if ext == '.xml':
        topic_name = os.path.splitext(os.path.basename(file))[0]
        print(file)
        loader = PyMuPDFLoader(file)
        documents = loader.load()
        text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
        for document in documents:
            doc = text_splitter.create_documents(texts=[document.page_content], metadatas=[{"name": topic_name, "source": file}])
            docs.extend(doc)

embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
db = Chroma(persist_directory="C:\\Users\\***\\langchain_book\\local_chroma", embedding_function=embeddings)

# トークン数制限のため、500 documentずつ処理をする
intv = 500
ln = len(docs)
max_loop = int(ln / intv) + 1
for i in range(max_loop):
    splitted_documents = text_splitter.split_documents(docs[intv * i : intv * (i+1)])
    db.add_documents(splitted_documents)
```

最後の方に「トークン数制限のため、500 documentずつ処理をする」という部分があるのですが、いっぺんにChatGPTに読み込ませようとしたらファイルによってはエラーになる可能性もあるので、`documents`の中の`document`を500ずつに分けました。

 ### 2．Qdrant
 ```python:qdrant.py
import glob
import os
from dotenv import load_dotenv
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.document_loaders import PyMuPDFLoader
from langchain.vectorstores import Qdrant

load_dotenv()

docs = []

files = glob.glob(os.path.join("C:\\Users\\***\\langchain_book\\", "**/*.*"), recursive=True)
for file in files:
    base, ext = os.path.splitext(file)
    if ext == '.xml':
        topic_name = os.path.splitext(os.path.basename(file))[0]
        print(file)
        loader = PyMuPDFLoader(file)
        documents = loader.load()
        text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
        for document in documents:
            doc = text_splitter.create_documents(texts=[document.page_content], metadatas=[{"name": topic_name, "source": file}])
            docs.extend(doc)

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

# トークン数制限のため、500 documentずつ処理をする
intv = 500
ln = len(docs)
max_loop = int(ln / intv) + 1
db = Qdrant.from_documents(docs[0:intv], embeddings, path="C:\\Users\\***\\langchain_book\\local_qdrant", collection_name="manga_data")
for i in range(max_loop):   
    db.add_documents(docs[intv * (i+1) : intv * (i+2)])
  ```
Qdrantのコーディングは、少し苦労をしました。
 Chromaのように、Qdrantでも最初にコンストラクタを定義したかったのですが、書込み時のコンストラクタは存在しないようです。
 仕方なく、doc[0～500]の場合は`from_documents`を呼んで、その後doc[500～]の場合は`add_documents`を呼ぶようにしました。

 ### 3．FAISS

 ```python:faiss_.py
import glob
import os
from dotenv import load_dotenv
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.document_loaders import PyMuPDFLoader
from langchain.vectorstores import FAISS

load_dotenv()

docs = []

files = glob.glob(os.path.join("C:\\Users\\***\\langchain_book\\", "**/*.*"), recursive=True)
for file in files:
    base, ext = os.path.splitext(file)
    if ext == '.xml':
        topic_name = os.path.splitext(os.path.basename(file))[0]
        print(file)
        loader = PyMuPDFLoader(file)
        documents = loader.load()
        text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
        for document in documents:
            doc = text_splitter.create_documents(texts=[document.page_content], metadatas=[{"name": topic_name, "source": file}])
            docs.extend(doc)

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

# トークン数制限のため、500 documentずつ処理をする
intv = 500
ln = len(docs)
max_loop = int(ln / intv)
db = FAISS.from_documents(docs[0:intv], embeddings)
for i in range(max_loop):
    db.add_documents(docs[intv * (i+1) : intv * (i+2)])
db.save_local("C:\\Users\\***\\langchain_book\\local_faiss")
 ```

 FAISSの場合は相当苦労しました。
 FAISSでは、Qdrantと同じく`from_documents`と`add_documents`の問題は同様に発生しました。
 また、最終行の`save_local`ですが、forループの中に書くと、ローカルデータベースを上書きしてしまうこともわかりました。そのため、forループの中で`add_documents`を呼ぶようにして、ループを抜けて最後に１回だけ`save_local`を呼ぶことにしました。


 それから、３種類全体に関わることなのですが、`embedding`に`text-embedding-ada-002`を利用しているのですが、OpenAIのマイページで「USAGE」(利用料金の画面)を確認すると、結構コストがかかっていました。
 サイトを見てみると`text-embedding-3-small`の方が安いようです。
 ただ、今回はファイル数が多いのと、ファイル自体が大きいため、`text-embedding-ada-002`にしました。今回のプログラムを流すだけで2～3千円かかりました。💦

 # インプットに利用したファイル
 今回は、特許庁の公報発行サイトのxmlファイル群を読み込んで、vectorDBへ格納することにしました。

 
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/d2804a67-2f81-1103-8d03-9f64bf0d6160.png)

 ここからダウンロードファイル(zip)を解凍して、所定のところに置きます。

その中の1つをxmlファイルのサンプルとして示します。

```xml:0007354483.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../../../../../XSL/JPRegisteredPatentPublication.xsl"?>
<jppat:RegisteredPatentPublication xmlns:jpcom="http://www.jpo.go.jp/standards/XMLSchema/ST96/JPCommon"
                                   xmlns:jppat="http://www.jpo.go.jp/standards/XMLSchema/ST96/JPPatent"
                                   xmlns:com="http://www.wipo.int/standards/XMLSchema/ST96/Common"
                                   xmlns:pat="http://www.wipo.int/standards/XMLSchema/ST96/Patent"
                                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                   xsi:schemaLocation="http://www.jpo.go.jp/standards/XMLSchema/ST96/JPPatent ../../../../../XSD/JPRegisteredPatentPublication_V1_0.xsd"
                                   com:languageCode="ja"
                                   com:st96Version="V3_1"
                                   com:ipoVersion="JP_V1_0">
   <com:IPOfficeCode>JP</com:IPOfficeCode>
   <jppat:RegisteredPatentPublicationBibliographicData com:languageCode="ja">
      <com:IPOfficeCode>JP</com:IPOfficeCode>
      <jppat:PatentPublicationIdentification>
         <com:IPOfficeCode>JP</com:IPOfficeCode>
         <pat:PublicationNumber>7354483</pat:PublicationNumber>
         <com:PublicationDate>2023-10-02</com:PublicationDate>
      </jppat:PatentPublicationIdentification>
      <pat:PlainLanguageDesignationText>特許公報(B1)</pat:PlainLanguageDesignationText>
      <com:RegistrationDate>2023-09-22</com:RegistrationDate>
      <jppat:ApplicationIdentification>
         <com:ApplicationNumber>
            <com:ApplicationNumberText>2023544159</com:ApplicationNumberText>
         </com:ApplicationNumber>
         <pat:FilingDate>2023-03-27</pat:FilingDate>
      </jppat:ApplicationIdentification>
      <pat:InventionTitle>感熱記録体</pat:InventionTitle>
      <jppat:RegisteredPatentPublicationPartyBag>
         <jppat:ApplicantsRegisteredPractitionersBag>
            <jppat:ApplicantRegisteredPractitionerBag com:sequenceNumber="1">
               <jppat:Applicant com:sequenceNumber="1">
                  <com:PartyIdentifier>000183484</com:PartyIdentifier>
                  <jpcom:Contact>
                     <com:Name>
                        <com:EntityName>日本製紙株式会社</com:EntityName>
                     </com:Name>
                     <com:PostalAddressBag>
                        <com:PostalAddress>
                           <com:PostalAddressText>東京都北区王子１丁目４番１号</com:PostalAddressText>
                        </com:PostalAddress>
                     </com:PostalAddressBag>
                  </jpcom:Contact>
               </jppat:Applicant>
               <jppat:RegisteredPractitioner com:sequenceNumber="1">
                  <jppat:AgentCategory>Representative</jppat:AgentCategory>
                  <pat:RegisteredPractitionerRegistrationNumber>100113022</pat:RegisteredPractitionerRegistrationNumber>
                  <jppat:RegisteredPractitionerCategory>Attorney</jppat:RegisteredPractitionerCategory>
                  <jpcom:Contact>
                     <com:Name>
                        <com:EntityName>赤尾  謙一郎</com:EntityName>
                     </com:Name>
                  </jpcom:Contact>
               </jppat:RegisteredPractitioner>
            </jppat:ApplicantRegisteredPractitionerBag>
         </jppat:ApplicantsRegisteredPractitionersBag>
         <jppat:InventorBag>
            <jppat:Inventor com:sequenceNumber="1">
               <jpcom:Contact>
                  <com:Name>
                     <com:EntityName>越  達朗</com:EntityName>
                  </com:Name>
                  <com:PostalAddressBag>
                     <com:PostalAddress>
                        <com:PostalAddressText>東京都北区王子５－２１－１  日本製紙株式会社内</com:PostalAddressText>
                     </com:PostalAddress>
                  </com:PostalAddressBag>
               </jpcom:Contact>
            </jppat:Inventor>
            <jppat:Inventor com:sequenceNumber="2">
               <jpcom:Contact>
                  <com:Name>
                     <com:EntityName>緑川  佳美</com:EntityName>
                  </com:Name>
                  <com:PostalAddressBag>
                     <com:PostalAddress>
                        <com:PostalAddressText>東京都北区王子５－２１－１  日本製紙株式会社内</com:PostalAddressText>
                     </com:PostalAddress>
                  </com:PostalAddressBag>
               </jpcom:Contact>
            </jppat:Inventor>
            <jppat:Inventor com:sequenceNumber="3">
               <jpcom:Contact>
                  <com:Name>
                     <com:EntityName>平井  健二</com:EntityName>
                  </com:Name>
                  <com:PostalAddressBag>
                     <com:PostalAddress>
                        <com:PostalAddressText>東京都北区王子５－２１－１  日本製紙株式会社内</com:PostalAddressText>
                     </com:PostalAddress>
                  </com:PostalAddressBag>
               </jpcom:Contact>
            </jppat:Inventor>
         </jppat:InventorBag>
      </jppat:RegisteredPatentPublicationPartyBag>
      <jppat:PriorityClaimBag>
         <jppat:PriorityClaim com:sequenceNumber="1">
            <com:IPOfficeCode>JP</com:IPOfficeCode>
            <com:ApplicationNumber>
               <com:ApplicationNumberText>2022055915</com:ApplicationNumberText>
            </com:ApplicationNumber>
            <pat:FilingDate>2022-03-30</pat:FilingDate>
            <jppat:ApplicationFilingCategory>Patent</jppat:ApplicationFilingCategory>
         </jppat:PriorityClaim>
      </jppat:PriorityClaimBag>
      <jppat:IPCClassification>
         <com:Edition/>
         <pat:MainClassification>B41M   5/40        20060101AFI20230925BHJP        </pat:MainClassification>
         <pat:FurtherClassification>B41M   5/323       20060101ALI20230925BHJP        </pat:FurtherClassification>
      </jppat:IPCClassification>
      <jppat:NationalClassification>
         <com:IPOfficeCode>JP</com:IPOfficeCode>
         <jppat:MainNationalClassification>
            <pat:PatentClassificationText>B41M5/40 220</pat:PatentClassificationText>
         </jppat:MainNationalClassification>
         <jppat:FurtherNationalClassification>
            <pat:PatentClassificationText>B41M5/323 220</pat:PatentClassificationText>
         </jppat:FurtherNationalClassification>
      </jppat:NationalClassification>
      <jppat:SearchField>
         <jppat:PatentClassificationBag>
            <jppat:IPCClassification>
               <com:Edition/>
            </jppat:IPCClassification>
         </jppat:PatentClassificationBag>
         <pat:SearchFieldText>Ｂ４１Ｍ      ５／２８－５／４８</pat:SearchFieldText>
         <pat:SearchFieldText>ＣＡｐｌｕｓ／ＲＥＧＩＳＴＲＹ（ＳＴＮ）</pat:SearchFieldText>
      </jppat:SearchField>
      <pat:ReferenceCitationBag>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="1">
               <com:PatentCitationText>特開平１１－２０８１１５（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="2">
               <com:PatentCitationText>特開２０１１－００８０８９（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="3">
               <com:PatentCitationText>特開平５－０３２０４４（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="4">
               <com:PatentCitationText>特開平９－２６５１４９（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="5">
               <com:PatentCitationText>特開２００６－０８４６０７（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
         <pat:ReferenceCitation>
            <com:PatentCitation com:sequenceNumber="6">
               <com:PatentCitationText>特開２００９－２５５５５１（ＪＰ，Ａ）      </com:PatentCitationText>
            </com:PatentCitation>
         </pat:ReferenceCitation>
      </pat:ReferenceCitationBag>
      <jppat:ClaimTotalQuantitySet>
         <pat:ClaimTotalQuantity>4</pat:ClaimTotalQuantity>
         <jppat:AdoptedLawCategory>Claim</jppat:AdoptedLawCategory>
      </jppat:ClaimTotalQuantitySet>
      <jppat:InternationalFilingData>
         <jppat:ApplicationIdentification>
            <com:ApplicationNumber>
               <com:ApplicationNumberText>JP2023012159</com:ApplicationNumberText>
            </com:ApplicationNumber>
            <pat:FilingDate>2023-03-27</pat:FilingDate>
         </jppat:ApplicationIdentification>
      </jppat:InternationalFilingData>
      <com:ExaminationRequestDate>2023-07-20</com:ExaminationRequestDate>
      <jppat:AcceleratedApplication>Accelerated examination</jppat:AcceleratedApplication>
      <pat:ExaminerBag>
         <pat:PrimaryExaminer>
            <com:Name>
               <com:PersonName>
                  <com:PersonFullName>高草木  綾音</com:PersonFullName>
               </com:PersonName>
            </com:Name>
         </pat:PrimaryExaminer>
      </pat:ExaminerBag>
   </jppat:RegisteredPatentPublicationBibliographicData>
   <jppat:Description>
      <pat:TechnicalField>
         <com:P com:pNumber="0001">
  この発明は、背面に水蒸気バリア性を有する感熱記録体に関する。</com:P>
      </pat:TechnicalField>
      <pat:BackgroundArt>
         <com:P com:pNumber="0002">
  一般に、感熱記録体は、無色ないし淡色の電子供与性ロイコ染料（以下、「ロイコ染料」ともいう。）と電子受容性顕色剤（以下、「顕色剤」ともいう。）とを含有する塗工液を、紙、合成紙、フィルム、プラスチック等の支持体に塗工して感熱記録層を設けたものであり、サーマルヘッド、ホットスタンプ、熱ペン、レーザー光等の加熱による瞬時の化学反応により発色し、記録画像が得られる。感熱記録体は、ファクシミリ、コンピューターの端末プリンター、自動券売機、計測用レコーダー、スーパーマーケットやコンビニなどのレシート等の記録媒体として広範囲に使用されている。<com:Br/>
  良好な耐水性を得るために、ポリビニルアルコール等の接着剤を含有するバックコート層を設けた感熱記録材料が知られている（特許文献1等）。また、エチレン共重合ポリビニルアルコールやエチレン・アクリル共重合体を含む紙基材が水蒸気バリア性を有することが知られている（特許文献２～４等）。</com:P>
      </pat:BackgroundArt>
      <com:CitationBag>
         <com:PatentCitationBag>
            <com:P com:pNumber="0003">
               <com:PatentCitation com:sequenceNumber="1">
                  <com:PatentCitationText>特開２００３－１７５６７１</com:PatentCitationText>
               </com:PatentCitation>
               <com:PatentCitation com:sequenceNumber="2">
                  <com:PatentCitationText>特開２０１３－１６９９８８</com:PatentCitationText>
               </com:PatentCitation>
               <com:PatentCitation com:sequenceNumber="3">
                  <com:PatentCitationText>特開２０１４－１７３２０１</com:PatentCitationText>
               </com:PatentCitation>
               <com:PatentCitation com:sequenceNumber="4">
                  <com:PatentCitationText>特開２０２０－１９００６３</com:PatentCitationText>
               </com:PatentCitation>
            </com:P>
         </com:PatentCitationBag>
      </com:CitationBag>
      <pat:InventionSummary>
         <pat:TechnicalProblem>
            <com:P com:pNumber="0004">
・・・・・
```

ざっくり話すと、２０２３年１０月２日に「日本製紙株式会社」が「背面に水蒸気バリア性を有する感熱記録体に関する特許」を出願したようです。難しくて内容はよくわかりませんが:sob:

では、次回の記事でこの内容がちゃんと確認できるかをデータベースの中を見ていきます。

今日は疲れたのでこれで終わりにします。