![見出し画像](https://assets.st-note.com/production/uploads/images/62485540/rectangle_large_type_2_dfd9097bcadc1acbd856a384e648add4.png?width=1200)
悔しいくらい簡単に文書要約できるsumyを少し解説。
投稿がまちまちになりつつあるこの頃。
あれこれやらないとな〜って思っていたり、ちょっと注意力が散漫だったりとちょっと計画の修正が必要かな〜なんて思っていますが、
ま、本題にいきましょう。
では、今回もよろしくお願いいたします。
1. 何するのか?
Dockerでの環境構築を使ってなんかしたいなっていうのが根底にありまして、考えたところ
自然言語処理を扱ってきたし、楽天トラベルのレビューページをスクレイピングして、評価のロジスティック回帰・各評価ごとの分類をしてみましょうかしら〜
なんてほざき始めました。
で、いろいろしたのですが、その中で文書要約でgensim使いたい!って思ってたのですが、ちょっととっかかりにくいと感じたので、他にあるのかなと調べるとsumyというライブラリに行き着きました
githubのコードを少しばかりみてみると、nltkとかtinysegmenter(janomeよりもさらに簡素な形態素解析器、たぶん)が使われていました。
今回、形態素解析に関してはMeCabを使いますが、ほんとにすぐに使いたい!って方はsumyのモジュールの中にtokenizeできるものがあるので、ほんとにsumyだけで完結しようと思えばできます。
しかし、要約モデルについての理解はかなり個人的に怪しいので、とりあえず使ってみて「へぇ〜〜」って感じてから、追々理解を深めていくことにします。
(理論愛好家の方はつまらない投稿になると思いますw)
2. 環境準備・データ準備を爆速に
最近の(個人的)テーマである、「ローカル依存の脱却」をもとに活動しているため、環境は全てDockerで行い、データもGitHub上での管理をしています。
自分と同じ環境で作業したい方は
docker pull dockyupy/nlp-mecab-python:sumy
という一行でDockerのコンテナを使うことができます。
起動は
docker run -p 8888:8888 -v (作業したい環境):/(コンテナ上で作業する際のフォルダ名。自由に命名) dockyupy/nlp-mecab-python:sumy
でいけます。
つまり、環境構築に2行。
githubはhttps://github.com/Yu0130fri/Scraping-NLPにあるので、
git cloneで1行。
合計3行のコマンドで準備が終わりますw
git cloneしてからdocker run するなら
docker run -p 8888:8888 -v (~~)/Scraping-NLP:/work dockyupy/nlp-mecab-python:sumy
とかになります(作業フォルダをworkと命名)
かなり説明を省略している感が否めないですww
(dockerのがっつりした解説は今回は扱わないです、いつかします)
・データ準備
今回はサンプルでscrapingしてきたホテルレビューのCSVを使います(sample_csvフォルダに格納。もしご自身で取得したい!って方は
python scraping.py (楽天トラベルの地域まで絞り込んだ状態のURL)
を実行すれば取得できたりしますし、好きなようにスクレイピングしたものを使うのもおすすめ。)
データ読み込みとかライブラリimportは以下。
import MeCab
import pandas as pd
from sumy.parsers.plaintext import PlaintextParser
from sumy.nlp.tokenizers import Tokenizer
from sumy.summarizers.lex_rank import LexRankSummarizer
# Mecabにて利用する辞書(mecab-ipadic-neologdなど)のURL
tagger = MeCab.Tagger("-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd")
tagger.parse("")
data = pd.read_csv('sample.csv')
data['Comments'] = [word.strip() for word in data['Comments']]
data.head(2)
3. sumyを体感。
では、本題。
実は本当にsumyのコードはあっさりしてます。(ちゃんとした要約コードでもないと思いますが、体感することが目的だったりするのでご了承くださいませ)
# 文章要約メソッド
def summy_test(text):
key = tagger.parse(text)
corpus = []
for row in key.split("\n"):
word = row.split("\t")[0]
if word == "EOS":
break
else:
corpus.append(word)
# from_string(string, tokenizer) from builtins.type
parser = PlaintextParser.from_string(text, Tokenizer('japanese'))
summarizer = LexRankSummarizer() # LexRankSummarizer https://dl.acm.org/doi/10.5555/1622487.1622501
summarizer.stop_words = ['']
# sentences_countにて、sentence(文章の数)を選びます。
# If you choose 3 of sentences_count, you can get the 3 summarized sentences
summary = summarizer(document=parser.document, sentences_count=3)
b = []
for sentence in summary:
b.append(sentence.__str__())
return "\n".join(b) # print時にみやすくするために改行を入れているが、純粋にlistで使用したい時は'.join
最初はMeCabを使った形態素解析をしているだけで、そこから
PlaintextParser.from_string(text, Tokenizer('japanese'))で、sumyのフォーマットに従って再度トークナイズさせます。
そこからLexRankSummarizerというクラスのインスタンス化を行います
LexRankという、(本当にざっくりした言い方をすれば)読み込んだ文書のうち、重要そうな文書をまるっと抜き出してくる方法です
(裏ではTFIDFとか使われているそう。)
これを使って、
summarizer(document=parser.document, sentences_count=3)
でparser.documentで読み込んだテキストのdocument形式を引数に、sentences_countで取得したい要約の文書数を決めて後はリストにGO!です。
裏の数式的な理論は理解不足ですが、非常にコードとしてはシンプルですな。。
では、肝心の結果。
テーマとしては各評価ごとの代表的な文書抜き出しです。
for rep in sorted(data.Reputations.unique()):
text = data.query('Reputations == @rep')['Comments'].tolist()
text =''.join(text)
print(f'Reputation: {rep}')
print(summy_test(text))
print('-'*10)
みっっにくいですが、今回使用データがそもそも400ssくらいだったので、reputationが1(5が最高評価で、1はworst)の人がいなかったです。
しかし、他の文書をみてみると、確かになんとなく代表的な文章を取ってきてるような気がしなくもないですよねw
・終わり(余談)
ほんと一瞬。
いままでこのような包括的なライブラリはちょっと避けていた(学習段階として仕組みが理解できないのもあるので、、)のですが、やはりどんどん便利になっているのは確か。。
他の方のsumyのコードではjanome, spacyなどが使われてたりもします。(ちなみに、今回使ったdocker imageに本来spaCyのインストールとかも済ませたかったのですが、なぜかdocker上でginzanのインストールに挫折したという背景があります。ローカルではできるのに。。。)
sumyにはLexRank以外にもめちゃたくさんの要約するメソッドがあるので、いろいろ探索するのも面白いと思います!
では、また次回〜〜
===余談===
実際はgensimとかの実装をしたいのもあるのですが、最近LDA(潜在ディリクレ)の類似でLSIというものを知り、gensimちゃんと勉強しないとってなってますが、その背景にある数学に頭が割れそうになりますw
ちなみに、モデル評価とかはgit上にipynbを置いてるので、興味ある人はどぞ!大したことはしてないですがw
=======