🤖

自然言語処理で扱うテキストのchunkingについて

2023/09/12に公開

概要

自然言語処理やLLMを扱っている際、長文を何かしらの方法で分割したいケースがあります。
分割することを「Text chunking」というのですが、その方式について纏めた記事がありました。

https://towardsdatascience.com/how-to-chunk-text-data-a-comparative-analysis-3858c4a0997a

要約するとchunkingには様々な方式があり、長所短所があるそうです。
代表的なやり方は以下です。

Langchain Character Text Splitter

Langchainのテキストスプリッターを使った方式。
全体の文章をセンテンス(句読点等で区切った文)に分割した後、指定した長さの文字数に収まるようにチャンクとして連結する。
センテンスの配列の先頭から、チャンクに詰められていき、チャンクに含まれる文字列長が一定を超えそうなら新たなチャンクに詰め込まれる感じ。

https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter
https://github.com/langchain-ai/langchain/blob/8b5662473f4c7daeef1ad7dbbb95b758acbfcd43/libs/langchain/langchain/text_splitter.py#L674-L686
※コードはこのあたり

文章の意味に関係なく、各チャンクの文字数がある程度均一になります。

NLTK Sentence Tokenizer and Spacy Sentence Splitter

NLTK、Spacyはそれぞれ有名な文字列処理ライブラリ。
やることはほぼ同じで、全体の文章をセンテンスに分割するだけです。

Adjacent Sequence Clustering

こちらが記事で実装されている独自の分割パターン。

全体の文章をセンテンスに分割した後、チャンクに詰めていくのだが、その際に直前のセンテンスと処理中のセンテンスの意味的類似度を比較して、意味が離れているものは次のチャンクに詰めるというもの。

これにより、各チャンクには意味の近いセンテンスが入ることになり一貫性をもたせることが出来るらしい。

今回はこちらの方式を実装してみます。

解説

google colabで実行します。

ライブラリのインストール

!pip install PyPDF2 pycryptodome==3.15.0 spacy
!python -m spacy download ja_core_news_sm

ライブラリのimport

import itertools
import requests
import numpy as np
import spacy
import PyPDF2

from io import BytesIO

PDFをテキスト化

初等中等教育段階における生成AIの利用に関する暫定的なガイドライン
今回はこちらのPDFをテキスト化した後チャンクにしてみたいと思います。

# PDFファイルのURL
pdf_url = "https://www.mext.go.jp/content/20230704-mxt_shuukyo02-000003278_003.pdf"

# requestsでPDFをダウンロード
response = requests.get(pdf_url)

# レスポンスをバイトストリームとして読み込む
pdf_file = BytesIO(response.content)

# PyPDF2でPDFを読み込む
pdf_reader = PyPDF2.PdfReader(pdf_file)

# ページ毎のテキストをリストとして保持する
text_by_page = [page.extract_text() for page in pdf_reader.pages]

print('--- len(text_by_page)')
print(len(text_by_page))

print('--- text_by_page[0][:100]')
print(text_by_page[0][:100])

print('--- text_by_page[-1][-100:]')
print(text_by_page[-1][-100:])
--- len(text_by_page)
16
--- text_by_page[0][:100]
 5文科初第 758号  
令和5年7月4日   
 
各都道府県教育委員会教育 長 
各指定都市教育委員会教育 長 
各都道府県知 事 
附属学校を置く各国公立大学長   殿 
小中高等学校を設置す
--- text_by_page[-1][-100:]
中等教育分科会デジタル学習基盤特別委員会委員名簿
24/24※本ガイドラインのとりまとめにおいては、以上の方々の意見を可能 な限り取り入れたが、最終的な内容は文部科学省の責任において確定させ ている。

spacyを使ってテキストをセンテンスに分割

nlp = spacy.load('ja_core_news_sm')

# 各ベージをセンテンスにする
sents_list = [nlp(text).sents for text in text_by_page]

# 全ページのセンテンスをフラットにして結合
sentences = list(itertools.chain.from_iterable(sents_list))

# 空白文字だけのセンテンスがあったので、取り除く
def remove_empty_strings(lst):
    return [s for s in lst if s.text.strip()]

sentences = remove_empty_strings(sentences)

print('--- len(sentences)')
print(len(sentences))

print('--- sentences[0]')
print(sentences[0])
--- len(sentences)
148
--- sentences[0]
5文科初第 758号  
令和5年7月4日   
 
各都道府県教育委員会教育 長 
各指定都市教育委員会教育 長 
各都道府県知 事 
附属学校を置く各国公立大学長   殿 
小中高等学校を設置する学校設置会社を  
所轄する構造改革特別区域法第12 条 
第1項の認定を受けた各地方公共団体の長  
 
 
文部科学省初等中等教育局長  
藤 原 章 夫 
  
 
「初等中等教育段階 における生成 AIの利用に関する暫定的なガイドライン」 の 
作成について( 通知) 
  
教育現場における生成 AIの利用については様々な議論が あるところですが、 差し当
たり文部科学省では 、生成AIに関する政府全体の議論や G7教育大臣会合における認
識の共有、 幅広い有識者や、中央教育審議会委員からの意見聴取を経て、 主として対
話型の文章生成 AIについて、 学校関係者が 現時点での 活用の適否を判断する際の参考
資料として、 別添のとおりガイドライン を取りまとめました。

「。」で区切っているみたいですね。

全センテンスのベクトルを作成

# 全センテンスのベクトルを作成
sent_with_vecs = [{
    'sent': sent,
    'vec': sent.vector / sent.vector_norm
} for sent in sentences]

print('--- len(sent_with_vecs)')
print(len(sent_with_vecs))

print("--- sent_with_vecs[0]['vec']")
print(sent_with_vecs[0]['vec'])

print("--- np.linalg.norm(sent_with_vecs[0]['vec'])")
print(np.linalg.norm(sent_with_vecs[0]['vec'])) # norm
--- len(sent_with_vecs)
148
--- sent_with_vecs[0]['vec']
[ 0.03118219 -0.18205877  0.1698854   0.06928792  0.10555292  0.05149024
  0.01674678 -0.03111113 -0.01904045  0.023228    0.00518887 -0.10310517
  0.10386676  0.15682274  0.03524103 -0.11692934  0.02388306 -0.11261518
  0.03874207  0.07849204 -0.01820301 -0.02118509 -0.00268686 -0.17283623
  0.07079953  0.16018182 -0.13074157  0.08670066 -0.10726762 -0.04448345
 -0.0576772   0.05196775 -0.08864068  0.1632612   0.10443623 -0.22804458
  0.12412974  0.13708101 -0.13659596  0.02301415 -0.03119287  0.03284624
  0.09959831 -0.08824287 -0.08136023  0.08629557  0.04828652 -0.19945347
 -0.13422064  0.13210404  0.10093536  0.08967453  0.07564711 -0.00536458
 -0.02203497  0.12235291 -0.05519997 -0.00946453  0.16141419 -0.11264461
  0.00541811  0.07035315 -0.11068623  0.19762631 -0.16374497  0.08015213
  0.04485235 -0.00711381 -0.0701573   0.02083724 -0.05602141  0.05697931
  0.1700639  -0.16930118  0.07564478  0.03526498 -0.04087519 -0.00753129
 -0.1664347  -0.11277794  0.02510644  0.04599821  0.01695928  0.06278847
  0.0271684  -0.05858672 -0.2520602   0.01831445 -0.0305533   0.00528905
 -0.22902146  0.07225347 -0.00426335  0.07915818  0.06074137 -0.17826158]
--- np.linalg.norm(sent_with_vecs[0]['vec'])
1.0

クラスタリング処理

# 意味が似ている隣り合うセンテンスをクラスタリング
def clustering(sent_with_vecs, threshold):
    clusters = [[sent_with_vecs[0]]]
    for i in range(1, len(sent_with_vecs)):
        # 直前のセンテンスと意味が似ていなければ別のクラスターに格納
        if np.dot(sent_with_vecs[i]['vec'], sent_with_vecs[i-1]['vec']) < threshold:
            clusters.append([])
        clusters[-1].append(sent_with_vecs[i])

    all_clusters = []
    for cluster in clusters:
        # クラスターに含まれるすべての文字列が規定の文字数より大きければ更に分割
        cluster_txt = ' '.join([sent_with_vec['sent'].text for sent_with_vec in cluster])
        if len(cluster_txt) > 3000:
          all_clusters.extend(clustering(cluster, threshold + 0.1))
        else:
          all_clusters.append(cluster)
    return all_clusters

clusters = clustering(sent_with_vecs, 0.5)


# 元々のセンテンス数
print('--- len(sent_with_vecs)')
print(len(sent_with_vecs))

# 分割されたクラスター数
print('--- len(clusters)')
print(len(clusters))

# 最も多くのセンテンスが含まれたチャンク
max_size_cluster = max(clusters, key=lambda cluster:len(cluster))

print('--- len(max_size_cluster)')
print(len(max_size_cluster))

max_size_cluster_txt = ' '.join([sent_with_vec['sent'].text for sent_with_vec in max_size_cluster])
print('--- len(max_size_cluster_txt)')
print(len(max_size_cluster_txt))

print('--- max_size_cluster_txt')
print(max_size_cluster_txt)
--- len(sent_with_vecs)
148
--- len(clusters)
46
--- len(max_size_cluster)
17
--- len(max_size_cluster_txt)
2810
--- max_size_cluster_txt
⽣成AIを取り巻く 懸念やリスクに
⼗分な対策を講じることができる⼀部の学校 において、個⼈情報保護やセキュリティ、著作権等に⼗分に留意しつつ、 パイロ
ット的な取組 を進め、 成果・課題を⼗分に検証 し、今後の 更なる議論に資する ことが必要である。 ② その⼀⽅、学校外で使われる可能性を踏まえ、 全ての学校 で、情報の真偽を確かめること (いわゆる ファクトチェック )の習
慣付けも含め、情報活⽤能⼒を育む教育活動を⼀層充実 させ、AI時代に必要な資質・能⼒の向上 を図る必要がある。 ③教員研修 や校務での適切な活⽤ に向けた取組を推進し、 教師のAIリテラシー向上 や働き⽅改⾰ に繋げる必要がある。 以上を踏まえ、教育利⽤に当たっては、 利⽤規約の遵守 はもとより、事前に ⽣成AIの性質やメリット・デメリット、AIには⾃
我や⼈格がないこと、⽣成AIに全てを委ねるのではなく⾃⼰の判断や考えが重要 であることを ⼗分に理解させる ことや、発
達の段階や⼦供の実態を踏まえ、 そうした教育活動が可能であるかどうかの⾒極めが重要 と考えられる。 その上で、個別の
学習活動での活⽤の適否については、学習指導要領に⽰す 資質・能⼒の育成を阻害しないか、教育活動の⽬的を達成す
る観点で効果的か否かで判断 すべきである(⽣成AIの性質等を理解できない段階、学習⽬的達成につながらない、適正な
評価の阻害や不正⾏為に繋がる等の場合は活⽤すべきでない)。 こうした判断を適切に⾏うためには 教師の側にも⼀定の
AIリテラシー が必要である。
 また、忘れてはならないことは、真偽の程は別として⼿軽に回答を得られるデジタル時代であるからこそ、根本に⽴ち返り、 学ぶ
ことの意義についての理解を深める指導が重要となる。 また、⼈間中⼼の発想で⽣成AIを使いこなしていくためにも、 各教科
等で学ぶ知識や⽂章を読み解く⼒ 、物事を批判的に考察する⼒、問題意識を常に持ち、問を⽴て続けることや、その前提と
しての「学びに向かう⼒、⼈間性等」の涵養がこれまで以上に重要 になる。 そうした教育を拡充するためには、 体験活動の充
実をはじめ、教育活動における デジタルとリアルのバランスや調和 に⼀層留意する必要がある。
 総合的に勘案
4/24
①⽣成AI⾃体の性質やメリット・デメリットに関する学習を⼗分に⾏っていない など、情報モラルを含む 情報活⽤能⼒が⼗分育成されて
いない段階において 、⾃由に使わせること
②各種コンクールの作品やレポート・⼩論⽂ などについて、 ⽣成AIによる⽣成物をそのまま⾃⼰の成果物として応募・提出 すること
(コンクールへの応募を推奨する場合は応募要項等を踏まえた十分な指導が必要)
③ 詩や俳句の創作、⾳楽・美術等の表現・鑑賞など ⼦供の感性や独創性を発揮させたい場⾯、初発の感想を求める場⾯ などで最初か
ら安易に使わせる こと
④テーマに基づき調べる場⾯ などで、教科書等の 質の担保された教材を⽤いる前に安易に使わせ ること
⑤ 教師が正確な知識に基づきコメント・評価すべき場⾯で、教師の代わりに 安易に⽣成AIから⽣徒に対し回答させる こと
⑥定期考査や⼩テストなどで⼦供達に使わせること (学習の進捗や成果を把握・評価するという目的に合致しない。 CBTで行う場合も、フィルタリング等に
より、生成AIが使用しうる状態とならないよう十分注意すべき)
⑦ 児童⽣徒の学習評価を、 教師がAIからの出⼒ のみをもって⾏うこと
⑧ 教師が専⾨性を発揮し、⼈間的な触れ合いの中で⾏うべき教育指導を実施せずに、安易に⽣成AIに相談させること(2)⽣成AI活⽤の適否に関する暫定的な考え⽅
① 情報モラル教育の⼀環として、 教師が⽣成AIが⽣成する誤りを含む回答を教材として使⽤し、その性質や限界等を⽣徒に気付かせること。
 ② ⽣成AIをめぐる社会的論議について⽣徒⾃⾝が主体的に考え、議論する過程で、その素材として活⽤させること
③グループの考えをまとめたり、アイデアを出す活動の途中段階 で、⽣徒同⼠で⼀定の議論やまとめをした上で、 ⾜りない視点を⾒つけ
議論を深める ⽬的で活⽤させること
④英会話 の相⼿として活⽤したり、より ⾃然な英語表現への改善 や⼀⼈⼀⼈の興味関⼼に応じた単語リストや例⽂リストの作成に活⽤
させること、外国⼈児童⽣徒等の⽇本語学習のために活⽤させること
⑤ ⽣成AIの活⽤⽅法を学ぶ⽬的で、⾃ら作った⽂章を⽣成AIに修正させたものを「たたき台」として、⾃分なりに何度も推敲して、より 良い
⽂章として修正した過程・結果をワープロソフトの校閲機能を使って提出させること
⑥ 発展的な学習として、 ⽣成AIを⽤いた⾼度なプログラミング を⾏わせること
⑦ ⽣成AIを活⽤した問題発⾒・課題解決能⼒を積極的に評価する観点からパフォーマンステストを⾏うこと
1.適切でないと考えられる例 ※あくまでも例示であり、個別具体に照らして判断する必要がある
 2.活⽤が考えられる例※あくまでも例示であり、個別具体に照らして判断する必要がある
5/24利用規約:ChatGPT…13歳以上、18歳未満は保護者同意 Bing Chat…成年、未成年は保護者同意 Bard…18歳以上 従前から⾏われてきたような形で、読書感想⽂や⽇記、レポート等を課題として課す場合、 外部のコンクールへの応募など
を推奨したり、課題として課したりする場合には、次のような留意事項が考えられる。 ① AIの利⽤を想定していないコンクールの作品やレポートなどについて、⽣成AIによる⽣成物をそのまま⾃⼰の成果物として
応募・提出することは評価基準や応募規約によっては不適切⼜は不正な⾏為に当たること、活動を通じた学びが得られず、⾃分のためにならないこと等について⼗分に指導する(保護者に対しても、⽣成AIの不適切な使⽤が⾏われないよう周知し理解を得ることが必要)。
 ② その上で、単にレポートなどの課題を出すのではなく、例えば、⾃分⾃⾝の経験を踏まえた記述になっているか、レポートの
前提となる学習活動を踏まえた記述となっているか、事実関係に誤りがないか等、レポートなどを評価する際の視点を予め設定することも考えられる。
 ③ 仮に提出された課題をその後の学習評価に反映させる場合は、例えば、クラス全体⼜はグループ単位等での⼝頭発表の
機会を設けるなど、まとめた内容が⼗分理解され、⾃分のものになっているか等を確認する活動を設定する等の⼯夫も考えられる。 ① 課題研究等の過程で、⾃らが作成したレポートの素案に⾜りない観点などを補充するために⽣成AIを活⽤させることも考
えられる。

記事にあるコードを少し調整しました。
クラスタリング処理は用途に応じてカスタマイズする必要がありそうです。

まとめ

chunkingも色々あることが分かりました。

LLMでコーパスを集める時もこんな感じのことをしてるのでしょうか?
結構地味で大変な作業です。

Discussion