OpenAIのAPIでは扱えない大きなテキストを要約する
Open AIのAPIには一度に利用できるトークン数に縛りがある。
とはいえ頑張ってOpenAIのAPIでは扱えない大きなテキストを要約してみる。
方法は以下の通り色々考えられるが、今回は一番シンプルな1のアプローチを試す。
- 単純に分割して、細切れになったものを最後に統合して要約する
- 前の方から少しずつ要約して、次のブロックを要約するときには前の要約を添える
- 単純に分割するのではなく、範囲が一部重複するように分割する
ソース
言語はPython。環境はGoogle Colabを利用する。
トークンを数えるためにtiktoken
、OpenAIのAPIを叩くためにopenai
をインストールする。
!pip install tiktoken
!pip install openai
APIキーをプログラムに読み込ませる。APIキーを手に入れるにはOpenAIのサイトで会員登録する必要がある。
from getpass import getpass
secret = getpass('Enter the secret value: ')
要約を試すために走れメロスをダウンロードしてローカルに保存する。
!mkdir ./data
!curl -L -o ./data/走れメロス.txt https://gist.githubusercontent.com/kurehajime/def4e8a65a68f4c95520944b77317213/raw/57a91dc0f845269ef01a1935637bb93c6588e4b0/%25E8%25B5%25B0%25E3%2582%258C%25E3%2583%25A1%25E3%2583%25AD%25E3%2582%25B9.txt
走れメロスのデータを読み込んで、トークン数を数えてみる。
import tiktoken
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
data = None
with open('./data/走れメロス.txt', 'r') as f:
data = f.read()
count = len(encoding.encode(data))
print(count)
トークン数は11200。ChatGPTのAPIでは一度に4000トークンまでしか扱えない。
11200
トークン数に応じてテキストを分割する関数を定義する。
この関数に最大トークン数を決めてテキストを渡すと、それを超えそうに、かつ変なところで分断されないように分割してくれる。
def split_by_token(text,block_size,sep):
lines = text.split(sep)
blocks = []
token = 0
block = ''
for line in lines:
t = len(encoding.encode(line))
if token > 0 and block_size < (token + t):
blocks.append(block)
token = 0
block = ''
token += t
block += line
blocks.append(block)
return blocks
最後に要約を表示する。
まずは各ブロックごとに要約を表示し、最後に全体の要約を表示する。
blocks = split_by_token(data,2000,'\n')
sumally = ''
for block in blocks:
suma = summarize(block,140) + '\n'
print(suma)
sumally += suma
final_sumally = summarize(sumally,140)
print('【要約】')
print(final_sumally)
結果
各ブロックごとの要約
メロスは暴君の王を倒すため、都市へ行く決心をする。しかし、都市は暗く、人々は恐れている。老人から聞いた話によると、王は多くの人々を殺害し、臣下の心まで疑っているという。メロスは王宮に入るが、短剣を持っていたため、警備員に捕まってしまう。暴君は彼を問い返し、民の忠誠を疑っていることを告白する。二人は意見が対立し、物語は終わる。
メロスは、処刑までに3日間の日限を求め、妹に結婚式を挙げさせたいと頼み込むが、暴君は身代わりの男を殺してやることを計画する。メロスは友人に事情を話し、身代わりとして取り残された友人を殺さないように頼む。メロスは村に到着し、妹に結婚式を挙げるが、疲労困憊で倒れてしまう。
メロスは急いで花婿の家に行き、明日に結婚式をしてもらえるよう頼んだ。最初は婿が断ったが、夜明けまでの議論の末、承諾した。しかし、結婚式当日に大雨が降り始めた。祝宴は楽しく続いていたが、メロスは約束の時間までに帰らなければならないと思い、花嫁や花婿に別れを告げ、出発することに。
主人公が友人を救う為に駆け足で走る途中、洪水により橋が壊れ、激流に飲み込まれてしまう。しかし、泳ぎ切って対岸に着き、山賊に襲われたが、一人を倒して武器を手に入れる。
メロスは山賊を倒し、疲れて膝を折り、友を裏切ったことに苦しみながら、定められた不名誉な運命に従うことを決める。彼は自分が一人の裏切り者であることを嘆きながら、眠ってしまう。
メロスは遅れていない、信頼されている。希望を持ち、肉体の疲れを癒やし、正義のため走る。悪魔の囁きを忘れ、愛と誠の力を信じる。フィロストラトスから、友人のセリヌンティウスが死刑になったことを聞くが、メロスは諦めない。最後の力を振り絞り、沈む夕陽の中、刑場に突入し、間に合った。
群衆に埋もれながらも、磔の柱に釣り上げられた友人を救うために、メロスは縄がほどかれる前に叫ぶ。そして、頬を殴り合い、「信実とは、決して空虚な妄想ではなかった」と暴君ディオニスに言われる。最後に、メロスは緋のマントを着て少女に捧げられ、勇者自身は赤面するのであった。
最後から二番目のブロックのフィロストラトスから、友人のセリヌンティウスが死刑になったことを聞くが、メロスは諦めない。
という記述。なんか勝手にセリヌンティウスが殺されている。
おそらく原文の「フィロストラトスでございます。貴方のお友達セリヌンティウス様の弟子でございます。」その若い石工も、メロスの後について走りながら叫んだ。「もう、駄目でございます。むだでございます。走るのは、やめて下さい。もう、あの方をお助けになることは出来ません。」
という記述に引っ張られたのだろう。
全体の要約
メロスは暴君を倒すため都市へ向かうが、警備員に捕まってしまう。友人を救うため身代わりの男を止めるために駆け、洪水と山賊に遭いながら戦い、最後に友人を救出する。正義と愛を信じ、友情を守るために闘い続けたメロスは最後に勝利し、少女の愛を受ける。
警備員という表現が気になるが、原文の巡邏の警吏
を意訳したのだろう。おおむね要約できている。
今回のノートブック
割とシンプルな方法で満足できる要約が手に入った。