しばらく遠ざかっていた長文要約の自動化を、ちゃんとやりたくなりました。
字幕起し翻訳の対訳表の下に、各字幕を連結してじっくり読めるようにした文章をつけていますが、これの要約があれば記事を読む人の助けになるだろうと思いました。
各記事のハイライト部分は筆者が判断してセンテンスを選んでいましたが、ここを機械の助けを借りて、より客観的なものにしようと思います。
Ubuntu 22.04.4 LTS で Python 3.10.12が動いています。
これらの記事を参考に、自分の用途に合うようにちょっと修正しました。
# mlnSumy.py
# Pythonで翻訳文の要約
# 対象テキストファイルのパス
import myConstants as my # 作業フォルダへのパスをまとめて記載
textDir = my.textDir
matomeTxt = textDir + "matome.txt"
kekkaTxt = textDir + "kekka.txt"
'''
コーパスの作成
要約したい文章をsumyに渡すためにコーパスを作成します。
流れとしては1文ずつ形態素解析を行なって、単語を空白区切にします。
日本語と英語の場合で若干処理が異なるため、クラスを2つ作成。
'''
import spacy
import neologdn
import re
import emoji
import mojimoji
from janome.analyzer import Analyzer
from janome.charfilter import UnicodeNormalizeCharFilter, RegexReplaceCharFilter
from janome.tokenizer import Tokenizer as JanomeTokenizer # sumyのTokenizerと名前が被るため
from janome.tokenfilter import POSKeepFilter, ExtractAttributeFilter
class JapaneseCorpus:
# ① spacyで用いる辞書の読み込み・形態素解析機の準備
def __init__(self):
self.nlp = spacy.load('ja_ginza')
# 極端に長い入力でコケる。文字数制限を拡張する方法を模索中
# self.nlp.max_length = 1500000 <--- 効かなかった
self.analyzer = Analyzer(
char_filters = [UnicodeNormalizeCharFilter(), RegexReplaceCharFilter(r'[(\)「」、。]', ' ')],
tokenizer = JanomeTokenizer(),
token_filters = [POSKeepFilter(['名詞', '形容詞', '副詞', '動詞']), ExtractAttributeFilter('base_form')]
)
# ② テキストを前処理にかける。
def preprocessing(self, text):
# まとめ文に入っている< >と《 》の部分を除去
text = re.sub(r'<.*?>', '', text)
text = re.sub(r'《.*?》', '', text)
# 一般的な処理
text = re.sub(r'\n', '', text)
text = re.sub(r'\r', '', text)
text = re.sub(r'\s', '', text)
text = text.lower()
text = mojimoji.zen_to_han(text, kana=True)
text = mojimoji.han_to_zen(text, digit=False, ascii=False)
text = ''.join(c for c in text if c not in emoji.EMOJI_DATA) #'UNICODE_EMOJI'から変更された
text = neologdn.normalize(text)
return text
# ③ 文章を1文ずつに分ける。返り値はDocオブジェクトで、1文ずつに分割した結果と形態素解析の結果を保持している。
def make_sentence_list(self, sentences):
doc = self.nlp(sentences)
self.ginza_sents_object = doc.sents
sentence_list = [s for s in doc.sents]
return sentence_list
# ④ 1文ずつ単語を空白で区切る。日本語の場合は形態素解析が必要。
# janomeで形態素解析を行う。形態素解析の部分では名詞、副詞、形容詞、動詞の単語のみを残して空白区切り。
def make_corpus(self):
corpus = [' '.join(self.analyzer.analyze(str(s))) + '。' for s in self.ginza_sents_object]
return corpus
class EnglishCorpus(JapaneseCorpus):
# ①
def __init__(self):
self.nlp = spacy.load('en_core_web_sm')
# ②
def preprocessing(self, text):
# パラグラフにまとめた文章に入っている< >と《 》の部分を除去
text = re.sub(r'<.*?>', '', text)
text = re.sub(r'《.*?》', '', text)
# 一般的な処理
text = re.sub(r'\n', '', text)
text = re.sub(r'\r', '', text)
text = mojimoji.han_to_zen(text, digit=False, ascii=False)
text = mojimoji.zen_to_han(text, kana=True)
text = ''.join(c for c in text if c not in emoji.UNICODE_EMOJI)
text = neologdn.normalize(text)
return text
# ④
def make_corpus(self):
corpus = []
for s in self.ginza_sents_object:
tokens = [str(t) for t in s]
corpus.append(' '.join(tokens))
return corpus
# :*:・。,☆゚"・:*:・。,。・:*:・゚"☆,。・:*:*:・。,☆゚"・:*:・。,。・:*:・゚"☆。:*:・。,☆゚"・:*:・。,。・:*:・゚"☆,。・:*:*:・。,☆゚"・:*:・。,。・:*:
'''
要約
sumyを使った要約を行う。コーパスを渡すだけで要約結果を出力してくれる。
あらかじめ機能として備わっているLexRank、TextRank、LSA、KL、 Luhn、 Reduction、SumBasicのアルゴリズムを使えるような実装。
sentences_count 要約後の文章の数を指定
'''
from sumy.parsers.plaintext import PlaintextParser
from sumy.nlp.tokenizers import Tokenizer
from sumy.utils import get_stop_words
# algorithms
from sumy.summarizers.lex_rank import LexRankSummarizer
from sumy.summarizers.text_rank import TextRankSummarizer
from sumy.summarizers.lsa import LsaSummarizer
from sumy.summarizers.kl import KLSummarizer
from sumy.summarizers.luhn import LuhnSummarizer
from sumy.summarizers.reduction import ReductionSummarizer
from sumy.summarizers.sum_basic import SumBasicSummarizer
algorithm_dic = {"lex": LexRankSummarizer(), "tex": TextRankSummarizer(), "lsa": LsaSummarizer(),\
"kl": KLSummarizer(), "luhn": LuhnSummarizer(), "redu": ReductionSummarizer(),\
"sum": SumBasicSummarizer()}
def summarize_sentences(sentences, sentences_count=3, algorithm="lex", language="japanese"):
# ①
if language == "japanese":
corpus_maker = JapaneseCorpus()
else:
corpus_maker = EnglishCorpus()
preprocessed_sentences = corpus_maker.preprocessing(sentences)
preprocessed_sentence_list = corpus_maker.make_sentence_list(preprocessed_sentences)
corpus = corpus_maker.make_corpus()
parser = PlaintextParser.from_string(" ".join(corpus), Tokenizer(language))
# ②
try:
summarizer = algorithm_dic[algorithm]
except KeyError:
print("algorithm name:'{}'is not found.".format(algorithm))
summarizer.stop_words = get_stop_words(language)
summary = summarizer(document=parser.document, sentences_count=sentences_count)
# ③
if language == "japanese":
return "".join([str(preprocessed_sentence_list[corpus.index(sentence.__str__())]) for sentence in summary])
else:
return " ".join([sentence.__str__() for sentence in summary])
# :*:・。,☆゚"・:*:・。,。・:*:・゚"☆,。・:*:*:・。,☆゚"・:*:・。,。・:*:・゚"☆。:*:・。,☆゚"・:*:・。,。・:*:・゚"☆,。・:*:*:・。,☆゚"・:*:・。,。・:*:
if __name__ == "__main__":
# まとめ文から読込
with open(matomeTxt, "r") as f:
text = f.read()
sentences_count = 3
# "lex": LexRankSummarizer()
# "tex": TextRankSummarizer()
# "lsa": LsaSummarizer()
# "kl": KLSummarizer()
# "luhn": LuhnSummarizer()
# "redu": ReductionSummarizer()
# "sum": SumBasicSummarizer()
algorithmList = ["lex", "tex", "lsa", "kl","luhn", "redu", "sum"]
language="japanese"
# 結果ファイルの初期化
with open(kekkaTxt, "w") as ff:
ff.write("")
# リストにあるアルゴリズムの結果を全部書き出す
for i in algorithmList:
sum_sentences = summarize_sentences(
text,
sentences_count = sentences_count,
algorithm = i,
language=language
)
resultList = sum_sentences.split("。")
resultList = resultList[:-1]
outTxt = "\n" + i + " ----------------------------------\n"
for j in resultList:
outTxt = outTxt + j + "。\n"
with open(kekkaTxt, "a") as ff:
ff.write(outTxt)
print(outTxt)
動作結果
最近翻訳した「グレート・リセット」を阻止せよ。それは人類に劇的な結果をもたらす。の和訳全文で試した結果です。
lex |
|
tex |
|
lsa |
|
kl |
|
luhn |
|
redu |
|
sum |
|
まとめ
上の表で2回以上出たセンテンスをまとめると以下のようになりました。
-
何年もの間、世界経済フォーラム(wef)をはじめとする金融寡頭勢力によって作られた組織が、「グレート・リセット」の必要性を宣言してきました。
-
様々な歴史的リセットがありましたが、そのいずれもが人類にとって劇的な損失を伴っていたことを指摘しているのです。
-
そして神経科学者たちは、今日の人類が本来持っている脳力の10%しか使えていないことが証明されたと見ています。
-
今回の少々厳しい内容の番組は、皆さんが検討し、考え、議論し、行動するよう促すことを目的としています。
-
それというのもこの番組は、優生学的な動機に基づく金融寡頭政治が現在意図している「グレート・リセット」の計画が、いかに重大なものであるかということを示すからです。
ちなみにこれは、筆者が重要だと思ったことの箇条書きです。
-
金融寡頭勢力による「グレート・リセット」は人類にとって劇的な損失をもたらす。
-
私たちは既にリセットの犠牲者であり、遺伝的に縮小された人間なのだろうか? 歴史的証拠は?
-
優生学的な動機に基づくグレート・リセットを、毅然として立ち向かい逆転する動機にしよう。
ふむふむ。プログラムの結果も筆者の要約も似てますね。これは今後の翻訳文要約に使えそうです。 嬉しい。