46.6kデモを予約するログイン無料で始める

UnstructuredとMilvusによるEPUBコンテンツのベクトル化とクエリ

この投稿では、MilvusとUnstructuredフレームワークを使用してEPUBデータのベクトル化と検索を探求し、LLMのパフォーマンスを向上させるための実用的な洞察を開発者に提供します。

#はじめに

デジタルの世界には、何世紀にもわたる文学、研究、物語を保存した電子書籍があふれている。ジョージ・オーウェルの『1984年』のような不朽の名作から、J.K.ローリングの『ハリー・ポッター』のような現代のベストセラーまで、これらのテキストには、多様な社会規範や歴史的文脈を反映した、膨大な言語的・文化的洞察が隠されている。大規模言語モデル(LLMs は、このような豊富なリポジトリから非常に大きな利益を得ることができ、人間の言語を正確に理解するために多様なデータセットを活用することができる。電子書籍で学習することにより、LLMは文体、イディオム、登場人物の会話などのニュアンスを理解できるようになり、要約や感情分析アプリケーションへの適応性が高まる。

その優れた能力にもかかわらず、LLMは限られた公開データに依存しているため、幻覚やドメイン固有の知識の欠如といった課題に遭遇する。これらの問題を軽減するために、開発者は検索拡張生成(RAG)技術を採用することができ、精度を向上させるために法的文書のような追加の知識ソースでLLMを補うことができる。埋め込みと類似検索は、RAGプロセスにおいて極めて重要な段階を形成し、効率的なデータ変換と検索を促進する。

この投稿では、MilvusとUnstructuredフレームワークを使用してEPUBデータのベクトル化と検索を探求し、LLMのパフォーマンスを向上させるための実用的な洞察を開発者に提供します。

UnstructuredとMilvusによるEPUBベクトル化

Milvusは、非常に高速なオープンソースのベクトルデータベースです。エンベッディング類似検索とGenAIアプリケーションを強化し、あらゆる組織がベクトルデータベースにアクセスできるように努めている。Milvusは、ディープニューラルネットワークやその他の機械学習(ML)モデルによって生成された10億以上の埋め込みベクトルを保存、インデックス付け、管理することができます。

Unstructured](https://github.com/unstructured-io/unstructured)フレームワークは、EPUBやPDFやPowerPointのような他の非構造化データ形式をベクトル化するための構造化されたパイプラインを提供します。テキスト情報を効率的に抽出・処理し、下流のAIアプリケーションのために機械可読にする。このフレームワークは、生のテキストをデータの意味的本質を捉えるベクトルに変換することに特化している。

UnstructuredとMilvusはどのようにEPUBデータを埋め込み、クエリするのでしょうか?主な手順は以下の通りだ:

  • ステップ1:データ抽出と前処理:** Unstructuredは、EPUBファイルからテキストを抽出し、前処理するために自然言語処理ツールを採用しています。生ファイルの読み取り、データのクリーニング、ベクトル化のための構造化が含まれます。

  • ステップ2:ベクトル化:*** このフレームワークは、BERTのような深層学習モデルの力を利用して、テキストをデータの意味的な意味を表す数値ベクトルに変換します。これらのモデルはテキストを高密度のベクトルに変換し、意味の最も微妙なニュアンスさえも捉えます。

  • ステップ3: Milvusへの取り込み:** データをベクトル化したら、Milvusを使ってベクトルを保存し、取り出すことができます。Milvusは高速な類似検索を可能にし、大規模なアプリケーションに最適です。

  • ステップ4: インデックス作成と検索:** Milvusはインデックス作成により検索プロセスを最適化し、検索クエリに一致するベクトルを迅速かつ正確に検索します。このステップにより、大規模なデータセットから関連するコンテンツを簡単に見つけることができます。

EPUBからインサイトへ:ベクトル化の旅

UnstructuredフレームワークでEPUBをベクトル化し、Milvusデータベースインスタンスに取り込むのは簡単です。このセクションでは、このオープンソースフレームワークを使用して、生のEPUBデータから完全にベクトル化されたフォーマットに移行する手順を概説します。

Unstructuredフレームワークによる電子書籍のベクトル化](https://assets.zilliz.com/How_the_Unstructured_Framework_Vectorizes_Ebooks_1440d9e770.png)

これらのステップには、データの分割、クリーニング、チャンキング、埋め込み(ベクトル化)が含まれます。最後に、Milvusのベクトルデータベースを用いてインデックスを作成し、検索を行う。

1) データ分割

まず、必要なライブラリをインストールする。以下の非構造化フレームワークのインストールは、プレーンテキストファイルに対して機能します。HTML、XML、JSON、Eメールは、余分な依存関係を必要としません。

pip install unstructured

epubのベクトル化の場合は、インストールコマンドでライブラリと一緒にepubのインストールを指定する必要がある:

pip install "unstructured[epub,]"

データタイプの形式によっては、追加の依存ライブラリが必要です。epubデータのベクトル化の場合、 pandoc ライブラリも必要である。

make install-pandoc

すべての依存関係をインストールしたら、以下のコード行を利用してepubファイルを分割することができる。ここでのパーティショニング機能は、生のドキュメントを標準的な構造化された要素に分割します。

from unstructured.partition.auto import partition
elements = partition(filename="example-docs/eml/book.epub")
print("¦".join([str(el) for el in elements]))

2)データのクリーニング

Unstructuredライブラリーは、データのクリーニングなど、他の必要な機能を備えている。Unstructuredライブラリを使用したクリーニング処理は非常に簡単です。

replace_unique_quotesメソッドのような定義済みのクリーニング関数を利用すると、âx80x99` 表現を人間が読める表現であるアポストロフィ(または')に置き換えることができる。

from unstructured.cleaners.core import replace_unicode_quotes
replace_unicode_quotes("Philadelphia Eagles優勝")

以下のapply関数を使うと、新しい要素をインスタンス化することなく、document要素にテキストクリーニングを適用することができます。

from unstructured.documents.elements import Text

element = Text("フィラデルフィア・イーグルス優勝")
element.apply(replace_unicode_quotes)
print(element)

上記のコードは、入力 "Philadelphia Eaglesâx80↩x99 victory "を、よりクリーンで人間が読みやすい表現 "Philadelphia Eagles' victory "に変更する。

ユーザは、カスタムデータ準備タスクのために、簡単にクリーニング関数を含めることもできる。以下の例では、テキストのセクションから引用を削除している。

インポート re

remove_citations = lambda text: re.sub("♪[♪d{1,3}]", "", text)

要素 = テキスト("[1] ジオロケートされた戦闘映像により、スヴァトーブ北西のドヴォリチュネ地域でロシア軍の優勢が確認された。")
element.apply(remove_citations)
print(element)

非構造化フレームワークのクリーニング・パーティションの詳細については、以下のクリーニング・ドキュメントを参照してください。

3) チャンキング

チャンキングとは、数値ベクトルに変換する前に、大きなテキストを扱いやすい小さなセグメント(チャンク)に分割することです。

チャンキングとはどういう意味か](https://assets.zilliz.com/What_does_chunking_mean_127c39a57e.png)

チャンキングは、書籍や研究論文のような長い文書を扱うときに不可欠です。機械学習モデルに入力するデータのサイズと複雑さを減らすことで、より正確で効率的な分析が可能になる。

なぜチャンキングが重要なのか?

第一に、膨大なテキストデータは、512トークンの制限を持つBERTのような多くのモデルの入力制限を超える可能性があります。従って、チャンキングは大きな文書をより小さなセクションに分割します。

さらに、チャンキングはあらゆるサイズの LLM にメリットがあります。チャンクを小さくすることで、各セグメントに一貫性のあるコンテンツが確保され、モデルがコンテキストをより理解しやすくなるからです。

最後に、効率的なチャンキングは、より小さなデータ部分を処理することで、メモリーの過負荷を防ぐ。

チャンキング処理とパーティショニング処理を区別することは非常に重要です。チャンキングは文書要素に対して行うもので、その前に行うパーティショニングとは異なります。チャンキングをパーティショニングと同時に行うことは可能ですが、これらのステップを分けることで、明快さとカスタマイズ性が高まります。チャンキングをパーティショニングの後の別個のフェーズとして扱うことで、開発者はデータ変換プロセスに対する理解を深め、より大きなコントロールを得ることができます。

Unstructuredフレームワークは複数のチャンキング・オプションを提供します:

1)max_characters: int (default=500) - チャンクの最大サイズを指定します。チャンクがこの文字数を超えることはありません。

2)2) new_after_n_chars: int (default=max_characters) - チャンクの「ソフト」最大サイズ。この文字数を超えるチャンクは、次の要素が指定されたハードな最大文字数を超えずに収まるとしても、拡張されません。

3)3) overlap: int (default=0) - 大きすぎるチャンクを分割するためにテキスト分割を使用する場合のみ、次のチャンクのプレフィックスとして、前のチャンクの終わりからこの文字数を含めます。

4)4) overlap_all: bool (default=False) - 特大の要素を分割するためにテキスト分割をするときだけでなく、「通常の」チャンク間のオーバーラップも適用します。通常のチャンクはきれいな意味的境界を持つ要素全体から形成されるため、このオプションは通常のチャンクを「汚す」可能性があります。このオプションが正しいかどうかは、使用例に基づいて判断してください。

さらに、Unstructuredフレームワークでは、チャンキング戦略を主に2つの方法に分けています:

基本的なチャンキング戦略は、指定されたmax_characters(ハードマックス)とnew_after_n_chars(ソフトマックス)オプション値の両方を尊重しながら、各チャンクを最大限に埋めるために連続した要素を組み合わせます。

by_title チャンク戦略は、セクションと、オプションでページの境界も保持します。ここで言う "保持 "とは、1つのチャンクが2つの異なるセクションで発生したテキストを決して含まないことを意味します。新しいセクションが始まると、既存のチャンクは閉じられ、次の要素が前のチャンクに合う場合でも、新しいチャンクが始まります。

下図のように、どちらかを選択するには、好みの関数をインポートし、選択したチャンキング・タイプをパーティション分割されたデータに対して実行します。

from unstructured.chunking.basic import chunk_elements
from unstructured.partition.html import partition_html

url = "https://understandingwar.org/backgrounder/russian-offensive-campaign-assessment-august-27-2023-0"
要素 = partition_html(url=url)
チャンク = chunk_elements(elements)

# OR

from unstructured.chunking.title import chunk_by_title

chunks = chunk_by_title(elements)

for chunks in chunks:
    print(chunk)
    print("ⅳ" + "-"*80)
    input()

4)埋め込み(ベクトル化)

埋め込みは、単語、フレーズ、またはテキスト全体を数値ベクトルに変換する、重要かつ最終的なステップである。このプロセスにより、計算モデルは人間の言語を理解し、効果的に処理できるようになる。

このステップでは、テキストを数値として表現し、埋め込みによってテキスト情報を固定サイズの数値ベクトルにマッピングする。

Unstructuredフレームワークは、以下のコードで利用されている有名なOpenAIEmbeddingEncoderのような複数のエンコーダを提供している。

インポート os
from unstructured.documents.elements import Text
from unstructured.embed.openai import OpenAIEmbeddingConfig, OpenAIEmbeddingEncoder

# エンコーダをOpenAIの認証情報で初期化する。
embedding_encoder = OpenAIEmbeddingEncoder(config=OpenAIEmbeddingConfig(api_key=os.environ["OPENAI_API_KEY"]))

# 要素のリストを埋め込む
elements = embedding_encoder.embed_documents(
    elements=[Text("This is sentence 1"), Text("This is sentence 2")]、
)

# 単一のクエリー文字列を埋め込む
query = "これはクエリです"
query_embedding = embedding_encoder.embed_query(query=query)

# 埋め込みを印刷する
[print(e.embeddings, e) for e in elements] # 埋め込みを表示する
print(query_embedding, query)
print(embedding_encoder.is_unit_vector(), embedding_encoder.num_of_dimensions())

5) Milvusによる保存、インデックス、クエリ

a) Milvusのセットアップ

まず、Docker Hubのリポジトリから最新のMilvus Dockerイメージを取得し、Milvusコンテナを実行する。

docker pull milvusdb/milvus
docker run -d --name milvus -p 19530:19530 milvusdb/milvus:latest

次に、以下のコマンドでPyMilvusライブラリをインストールする。

pip install pymilvus

最後に、ポート19530で動作しているMilvusインスタンスに接続する。

from pymilvus import connections
connections.connect("default", host="localhost", port="19530")

b) ベクトルデータベーススキーマの定義と新しいコレクションの作成

先のステップでインストールした "pymilvus "ライブラリから必要なモジュールとクラスをインポートする。

from pymilvus import Collection, FieldSchema, CollectionSchema, DataType

次に、コレクションスキーマの Fields を定義する。簡単にするために、2つのフィールドだけを作成する。まず、データ型がINT64(64ビット整数)の "id "というフィールドをプライマリキーとする。2番目のフィールドは "embedding "で、データ型はFLOAT_VECTOR、ディメンションは768である。このフィールドにはベクトルの埋め込みが格納される。

fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True)、
    フィールドスキーマ(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
]

次に、定義されたフィールドを使用してCollectionSchemaを作成し、説明("EPUB Embeddings")を提供します。このスキーマはコレクションの構造を定義します。

schema = CollectionSchema(fields, "EPUB Embeddings")
コレクション = コレクション("epub_embeddings", schema)

c) ベクトル化されたデータをコレクションに挿入する

まず、OpenAI の認証情報を使って OpenAI エンコーダを初期化します。

embedding_encoder = OpenAIEmbeddingEncoder(config=OpenAIEmbeddingConfig(api_key=os.environ["OPENAI_API_KEY"]))

テキスト要素のリストの例を追加します(これらのフィールドをEPUB要素に置き換えてください)。

要素 = [
    Text("これは文章1です")、
    Text("This is sentence 2")、
]

このステップでは、選択した要素を埋め込みます。このステップは、上記のステップと同じであることに注意してください。

  1. 埋め込み(ベクトル化)ステップ。
embedded_elements = embedding_encoder.embed_documents(elements=elements)

次に、新しく生成された埋め込み要素を抽出し、それぞれにIDを生成します。

embeddings = [e.embeddings for e in embedded_elements].
ids = [i for i in range(len(embeddings))] とします。

IDとembeddingsの数が同じであることを確認するために、Milvusにデータを挿入する前にアサーションチェックを追加することができます。

このチェックにより、矛盾を早期に発見し、挿入プロセス中の問題を防ぐことができます。

assert len(embeddings) == len(ids)

次に、以下に示す構造を使って、挿入するためのデータを準備します。

data = [ids、embeddings] です。

最後に、埋め込みデータをMilvusベクトルデータベースに挿入します。

collection.insert(データ)

d) インデックスの作成

以下の index_params では、"metric_type" に "L2" という値を指定することで、類似性検索に使用する距離メトリックが L2 (ユークリッド距離) であることを指定している。

index_type "には "IVF_FLAT "という値が与えられ、使用するインデックスのタイプを指定する。IVF_FLAT (Inverted File with Flat)はMilvusがサポートするインデックス作成方法の一つである。大規模な類似検索に適しています。

params":params": {"nlist":128}はインデックスの追加パラメータを定義する。"nlist "はインデックスで使用するクラスタ(または転置リスト)の数です。nlist "の値を大きくすると検索精度が向上しますが、より多くのメモリと計算資源を必要とします。

from pymilvus import Index, IndexType

インデックスパラメータ
    "metric_type":"L2",
    「index_type":"IVF_FLAT"、
    「params":{"nlist":128}
}

コレクション.create_index(field_name="embedding", index_params=index_params)

作成したコレクションを読み込みます。

コレクションをロードします。

e) クエリ(類似ベクトルの検索)

まず、ベクトルデータベース内を検索するための検索クエリを定義します。以下のquery_vectorには、0から1の間の768個のランダムな浮動小数点数のリストが含まれます。

query_vector = [random.random() for _ in range(768)] 検索クエリを定義します。
search_params = {"metric_type":"L2", "params":{"nprobe":10}}

次に、与えられたクエリーベクトルを使って類似度検索を行う。

results = collection.search(
    data=[query_vector]、
    anns_field="embedding"、
    param=search_params、
    limit=10、
    expr=None
)

結論

ベクトル埋め込みは、電子書籍のような非構造化データの数値表現であり、機械がそのようなデータを効果的に理解し分析することを可能にする。推薦システム、AIチャットボット、様々なGenAIアプリケーションにおいて極めて重要な役割を果たし、パーソナライズされた体験や洞察に満ちたインタラクションを促進します。このブログでは、このようなアプリケーションの構築におけるベクトル化と類似性検索の重要性について掘り下げてきました。

また、EPUBコンテンツをベクトル埋め込みにシームレスに変換するUnstructured Frameworkと、最も関連性の高い結果を保存・検索するMilvusベクトルデータベースのパワーを活用する方法についても説明しました。包括的なステップ・バイ・ステップのガイドを提供することで、読者のアプリケーションにこれらの操作をシームレスに実装するための知識とツールを提供しました。