以前別の記事でAutoPhraseによりコーパスからキーワード抽出を行う方法を紹介しました。
今回は以下の例のように、AutoPhraseによって用語(キーワード)位置にアノテーションがなされたテキストから用語が1単語となるよう分かち書きを行う方法について紹介します。
入力例:
私は<phrase>ぽんぽんぺいんコーポレーション</phrase>に属しています。
出力例:
["私", "は", "ぽんぽんぺいんコーポレーション", "に", "属し", "て", "い", "ます", "。"]
mecabのインストール
分かち書きを行うエンジンにはMecabを利用します。辞書を含めたインストール手順は以下の通りです。
sudo apt install mecab sudo apt install libmecab-dev sudo apt install mecab-ipadic-utf8 git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git # 辞書更新時はここから cd mecab-ipadic-neologd ./bin/install-mecab-ipadic-neologd -n -a pip install mecab-python
セグメントタグの辞書への登録
続いてMecabのユーザー辞書追加機能を用いて、セグメントタグである<phrase>
や</phrase>
が分かち書きされないように登録します。方法は以下の記事を参考にしました。
UbuntuでMeCabのユーザ辞書に単語を追加してPythonで使えるようにする - Qiita
まずは以下のようなcsvファイルを作成します。(仮にphrase_dict.csvとします。)
<phrase>,,,1,名詞,一般,*,*,*,*,<phrase>,フレーズ,フレーズ,追加エントリ </phrase>,,,1,名詞,一般,*,*,*,*,</phrase>,フレーズ,フレーズ,追加エントリ
続いて先ほどMecabインストール時にダウンロードしたシステム辞書のパス(/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd
)と辞書の出力先パス(仮にdict/phrase.dic
)を指定して以下のように辞書を作成します。
sudo /usr/lib/mecab/mecab-dict-index -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd -u dict/phrase.dic -f utf-8 -t utf-8 phrase_dict.csv
分かち書きの実行
以上で環境の準備は完了です。最後にMecabによる分かち書き結果とAutoPhraseによる用語アノテーションを用いて分かち書きを行います。
以下のスクリプトを実行すると、出力先ファイルの各行に各文の分かち書き結果のリストが出力されます。(jsonモジュールを用いて出力したため日本語がエスケープされていますが、json.loads(line)
のようにjsonモジュールを用いれば正しく読み込めるはずです。)
Note:
- コマンド引数には
<AutoPhraseにより出力されたセグメント結果> <出力先ファイル名>
を与えます。 - 冒頭に利用する辞書ファイルを
SYSTEM_DICT
やUSER_DICT
変数にしていますが、こちらは実際の環境に合わせて書き換えてください。
import json import re import sys import MeCab # 利用する辞書 SYSTEM_DICT = "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd" USER_DICT = "dict/phrase.dic" # 除外するトークン IGNORE_TOKENS = {"EOS", ""} def process_text(mecab, text): # <phrase>や</phrase>の前後に記号があると正しく分かち書きされないことがあるため # 全てのタグ前後に空白を入れる text = re.sub(r"<phrase>", " <phrase> ", text) text = re.sub(r"</phrase>", " </phrase> ", text) result = mecab.parse(text) tokens = [line.split("\t")[0] for line in result.split("\n")] tokens = [tok for tok in tokens if tok not in IGNORE_TOKENS] # <phrase> </phrase>で挟まれた単語列を1単語にまとめる buffer = None new_tokens = [] for tok in tokens: if tok == "<phrase>": buffer = [] elif tok == "</phrase>": if buffer is None: raise Exception(str(tokens)) new_tokens.append("".join(buffer)) buffer = None else: if buffer is not None: buffer.append(tok) else: new_tokens.append(tok) return new_tokens if __name__=="__main__": # コマンド引数に <AutoPhraseにより出力されたセグメント結果> <出力先ファイル名> input_fn, output_fn = sys.argv[1:] mecab = MeCab.Tagger(f"-d {SYSTEM_DICT} -u {USER_DICT}") with open(input_fn) as h, open(output_fn, "w") as h_out: for line in h: line = line.replace("\n", "") if len(line) > 0: tokens = process_text(mecab, line) h_out.write(f"{json.dumps(tokens)}\n")