大規模言語モデル(LLM)と潜在ディリクレ配分(LDA)アルゴリズムを用いたドキュメントのトピック抽出
Using large-scale language models (LLM) and latent Dirichlet allocation (LDA) algorithm for document topic extraction.
大規模なドキュメントからトピックを効率的に抽出する方法:Large Language Models(LLM)とLatent Dirichlet Allocation(LDA)アルゴリズムを使用したガイド。
はじめに
私は1000ページ以上の大規模なドキュメントを処理できるPDFファイルとのチャット用のWebアプリケーションを開発していました。しかし、ドキュメントとの対話を開始する前に、アプリケーションがユーザーに主要なトピックの要約を提供することで、対話を始めやすくすることを望みました。
これを行う方法の一つは、LangChainを使用してドキュメントを要約することです。しかし、その問題は高い計算コストおよびそれに伴う費用です。1000ページのドキュメントにはおおよそ250,000語が含まれており、それぞれの単語をLLMに入力する必要があります。さらに、結果はマップリデュースの方法でさらに処理する必要があります。gpt-3.5 Turboを4kコンテキストで使用した場合のコストの保守的な見積もりでは、要約だけでも1ドルを超えます。Unofficial HuggingChat APIなどの無料リソースを使用している場合でも、必要なAPI呼び出しの数が多すぎるため、乱用となります。したがって、異なるアプローチが必要でした。
LDAの救世主
Latent Dirichlet Allocation(LDA)アルゴリズムは、このタスクに適した自然な選択肢でした。このアルゴリズムは、一連の「ドキュメント」(このコンテキストでは、「ドキュメント」はテキストの一部を指します)を受け取り、各「ドキュメント」に対してトピックのリストを返します。重要なのは、各トピックに関連付けられた単語のリストです。これらの単語のリストはファイルの内容をエンコードしているため、LLMにフィードして要約を促すことができます。アルゴリズムの詳細な説明については、この記事をおすすめします。
高品質な結果を得るためには、2つの重要な考慮事項を解決する必要があります。LDAアルゴリズムのハイパーパラメータの選択と、出力の形式の決定です。最も重要なハイパーパラメータはトピックの数であり、最終結果に最も大きな影響を与えます。出力の形式に関しては、ネストされた箇条書きリストが非常にうまく機能しました。この形式では、各トピックが箇条書きリストとして表され、さらに詳細なサブエントリでトピックが説明されます。なぜこの方法が機能するのかについては、この形式を使用することで、モデルがリストから内容を抽出する際に接続詞や関係性の複雑さを排除できるためだと考えています。
- 「機械学習チートシートのためのScikit-learn」
- 「FLM-101Bをご紹介します:1010億パラメータを持つ、オープンソースのデコーダのみのLLM」
- iOSアプリの自然言語処理:機能、Siriの使用例、およびプロセス
実装
私はコードをGoogle Colabで実装しました。必要なライブラリは、LDAのためのgensim、PDF処理のためのpypdf、単語処理のためのnltk、およびLangChainのプロントテンプレートとOpenAI APIとのインターフェースのためのLangChainです。
import gensimimport nltkfrom gensim import corporafrom gensim.models import LdaModelfrom gensim.utils import simple_preprocessfrom nltk.corpus import stopwordsfrom pypdf import PdfReaderfrom langchain.chains import LLMChainfrom langchain.prompts import ChatPromptTemplatefrom langchain.llms import OpenAI
次に、入力テキストを処理するためのユーティリティ関数であるpreprocessを定義しました。この関数は、ストップワードと短いトークンを削除するためのものです。
def preprocess(text, stop_words): """ 入力テキストをトークン化および前処理し、ストップワードと短いトークンを削除します。 パラメータ: text(str):前処理する入力テキスト。 stop_words(set):テキストから削除するストップワードのセット。 戻り値: list:前処理されたトークンのリスト。 """ result = [] for token in simple_preprocess(text, deacc=True): if token not in stop_words and len(token) > 3: result.append(token) return result
2番目の関数であるget_topic_lists_from_pdfは、コードのLDA部分を実装しています。この関数は、PDFファイルへのパス、トピックの数、およびトピックごとの単語数を受け取り、リストを返します。このリストの各要素には、各トピックに関連付けられた単語のリストが含まれています。ここでは、PDFファイルの各ページを「ドキュメント」として考えています。
def get_topic_lists_from_pdf(file, num_topics, words_per_topic):
"""PDFドキュメントからトピックとそれに関連する単語を抽出するための
Latent Dirichlet Allocation(LDA)アルゴリズムを使用します。
パラメータ:
file(str):トピック抽出のためのPDFファイルのパス。
num_topics(int):発見するトピックの数。
words_per_topic(int):トピックごとに含める単語の数。
戻り値:
list:num_topicsのサブリストを含むリスト。各トピックに関連する単語が含まれます。
"""
# pdfファイルを読み込む
loader = PdfReader(file)
# 各ページからテキストを抽出してリストに格納します。各ページはドキュメントとして扱われます
documents= []
for page in loader.pages:
documents.append(page.extract_text())
# ドキュメントの前処理
nltk.download('stopwords')
stop_words = set(stopwords.words(['english','spanish']))
processed_documents = [preprocess(doc, stop_words) for doc in documents]
# 辞書とコーパスの作成
dictionary = corpora.Dictionary(processed_documents)
corpus = [dictionary.doc2bow(doc) for doc in processed_documents]
# LDAモデルの構築
lda_model = LdaModel(
corpus,
num_topics=num_topics,
id2word=dictionary,
passes=15
)
# トピックとそれに対応する単語の取得
topics = lda_model.print_topics(num_words=words_per_topic)
# 各トピックから単語のリストを取得し、リストに格納します
topics_ls = []
for topic in topics:
words = topic[1].split("+")
topic_words = [word.split("*")[1].replace('"', '').strip() for word in words]
topics_ls.append(topic_words)
return topics_ls
次の関数、topics_from_pdfはLLMモデルを呼び出します。
先に述べたように、モデルは出力をネストされた箇条書きリストの形式でフォーマットするように指示されました。
def topics_from_pdf(llm, file, num_topics, words_per_topic):
"""PDFドキュメントから抽出したトピックに基づいてLLMに対して記述的なプロンプトを生成します。
この関数は、各トピックに関連する単語のリストの出力として、get_topic_lists_from_pdf関数の出力を受け取り、
目次形式の出力文字列を生成します。
パラメータ:
llm(LLM):応答を生成するためのLarge Language Model(LLM)のインスタンス。
file(str):トピックに関連する単語を抽出するためのPDFファイルのパス。
num_topics(int):考慮するトピックの数。
words_per_topic(int):含めるトピックごとの単語の数。
戻り値:
str:提供されたトピックの単語に基づいて言語モデルによって生成された応答。
"""
# トピックを抽出し、文字列に変換する
list_of_topicwords = get_topic_lists_from_pdf(file, num_topics,
words_per_topic)
string_lda = ""
for l in list_of_topicwords:
string_lda += str(l) + "\n"
# テンプレートの作成
template_string = '''各{num_topics}のダブルクォーテーションで囲まれたリストのトピックを単純な文で説明し、
三つの異なるサブテーマを書き留めます。リストはトピックの発見のためのアルゴリズムの結果です。
紹介文または結論は提供しないでください。トピックを説明する際に「トピック」という単語は使用しないでください。
応答には以下のテンプレートを使用してください。
1: <<<(トピックを説明する文)>>>
- <<<(最初のサブテーマを説明するフレーズ)>>>
- <<<(2番目のサブテーマを説明するフレーズ)>>>
- <<<(3番目のサブテーマを説明するフレーズ)>>>
2: <<<(トピックを説明する文)>>>
- <<<(最初のサブテーマを説明するフレーズ)>>>
- <<<(2番目のサブテーマを説明するフレーズ)>>>
- <<<(3番目のサブテーマを説明するフレーズ)>>>
...
n: <<<(トピックを説明する文)>>>
- <<<(最初のサブテーマを説明するフレーズ)>>>
- <<<(2番目のサブテーマを説明するフレーズ)>>>
- <<<(3番目のサブテーマを説明するフレーズ)>>>
リスト: """{string_lda}""" '''
# LLM呼び出し
prompt_template = ChatPromptTemplate.from_template(template_string)
chain = LLMChain(llm=llm, prompt=prompt_template)
response = chain.run({
"string_lda" : string_lda,
"num_topics" : num_topics
})
return response
前の関数では、単語のリストが文字列に変換されます。次に、ChatPromptTemplateオブジェクトを使用してプロンプトが作成されます。プロンプトは応答の構造を定義します。最後に、関数はchatgpt-3.5 Turboモデルを呼び出します。返り値はLLMモデルによって与えられる応答です。
さあ、関数を呼び出す時間です。まず、APIキーを設定します。 この記事 は、APIキーの取得方法についての手順を提供しています。
openai_key = "sk-p..."llm = OpenAI(openai_api_key=openai_key, max_tokens=-1)
次に、topics_from_pdf関数を呼び出します。トピック数とトピックごとの単語数の値を選択します。また、テスト用にフランツ・カフカの「変身」のような パブリックドメイン の書籍を選びました。ドキュメントは個人のドライブに保存され、gdownライブラリを使用してダウンロードされます。
!gdown https://drive.google.com/uc?id=1mpXUmuLGzkVEqsTicQvBPcpPJW0aPqdLfile = "./the-metamorphosis.pdf"num_topics = 6words_per_topic = 30summary = topics_from_pdf(llm, file, num_topics, words_per_topic)
結果は以下に表示されます:
1: グレゴール・ザムザと彼の家族、下宿人の変身の探求- グレゴールの変身の理解- グレゴールの家族、下宿人の反応の分析- グレゴールの変身が家族に与える影響の分析2: グレゴールの変身の発見にまつわる出来事の分析- グレゴールの家族、下宿人の最初の反応の調査- グレゴールの家族、下宿人の行動の分析- グレゴールの環境の身体的変化の探求3: グレゴールの変身による家族への圧力の分析- グレゴールの家族にかかる経済的負担の調査- グレゴールの家族に与える感情的、心理的な影響の調査- グレゴールの変身による家族のダイナミクスの変化の分析4: グレゴールの変身の結果の分析- グレゴールの環境の身体的変化の調査- グレゴールの家族、下宿人の反応の分析- グレゴールの家族に与える感情的、心理的な影響の調査5: グレゴールの変身が家族に与える影響の探求- グレゴールの家族にかかる経済的負担の分析- グレゴールの変身による家族のダイナミクスの変化の調査- グレゴールの家族に与える感情的、心理的な影響の調査6: グレゴールの環境の身体的変化の調査- グレゴールの家族、下宿人の反応の分析- グレゴールの変身の結果の探求- グレゴールの変身が家族に与える影響の探求
出力はかなり良好で、わずか数秒で完了しました!本の主要なアイデアが正しく抽出されました。
このアプローチは技術書でも機能します。たとえば、ダビド・ヒルベルトの「幾何学の基礎」(1899年)(パブリックドメインでもあります):
1: 幾何学的な図形とその関係の特性の分析- 幾何学の公理の探求- 角度と直線の合同性の分析- 幾何学の定理の調査2: 有理関数と代数方程式の挙動の研究- 問題の直線と点の調査- 関数の係数の調査- 定積分の構成の調査3: 数系の特性の調査- 真の群の定義域の探求- 等しい部分の定理の分析- 任意の変位の円の調査4: 幾何学的な図形の面積の調査- 直線と点の平行性の分析- 三角形の内容の調査- 多角形の測定の分析5: 代数幾何学の定理の調査- 線分の合同性の探求- 乗算のシステムの分析- 有効な定理の調査6: 図形の特性の調査- 三角形の平行線の分析- 辺の結合の方程式の分析- 線分の交差の調査
結論
LDAアルゴリズムとLLMを組み合わせた大規模ドキュメントのトピック抽出は、コストと処理時間を大幅に削減しながら良好な結果を生み出します。API呼び出しは数百回からわずか1回に、処理時間は数分から数秒になりました。
出力の品質はその形式に大きく依存します。この場合、入れ子の箇条書きリストはうまく機能しました。また、トピック数とトピックごとの単語数も結果の品質に重要です。特定のドキュメントに最適なプロンプト、トピック数、およびトピックごとの単語数を試すことをお勧めします。
コードはこのリンクで見つけることができます。
読んでくれてありがとう。あなたの文書でどのように結果が出たか教えてください。始めに言及したアプリケーションの実装についてすぐに書けることを願っています。
LinkedIn: Antonio Jimenez Caballero
GitHub: a-jimenezc
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- 「短期予測を改善したいですか?デマンドセンシングを試してみてください」
- 「場所の言語:生成AIのジオコーディング能力の評価」
- ベイズ最適化とハイパーバンド(BOHB)によるハイパーパラメータ調整の例
- クローズドソース対オープンソース画像注釈
- 「ゲノムと気候の言語の解読:アニマ・アナンドクマールによるジェネレーティブAIの活用によるグローバルな課題への取り組み」
- TensorRT-LLMとは、NVIDIA Tensor Core GPU上の最新のLLMにおいて推論パフォーマンスを高速化し最適化するためのオープンソースライブラリです
- Stability AIが初の日本語ビジョン言語モデルをリリース