Janomeで形態素解析を行う際にノイズクリーニングしてみる

2021年06月10日

前回 の続きとなります。

概要

前回、このブログの全記事を形態素解析して名詞の出現頻度ランキングを出力しましたが、記号などのノイズが混じってしまいました。

マークダウンで記事を書いているので、解析対象テキストの中にMDXの自作タグやマークダウン記法の記号が名詞判定されてしまってました。

今回は形態素解析器の単語分割精度を上げるために、ノイズのクリーニング処理を行いたいと思います。

名詞の出現頻度を標準出力するコード例

最初にフルコードを掲載します。

名詞の出現頻度出力
from janome.charfilter import RegexReplaceCharFilter, UnicodeNormalizeCharFilter
from janome.analyzer import Analyzer
from janome.tokenfilter import POSKeepFilter, TokenCountFilter

analyzer = Analyzer(
    char_filters=[
        UnicodeNormalizeCharFilter(),
        RegexReplaceCharFilter('[#!:;<>{}・`.,()-=$/_\d\'"\[\]\|]+', ' '),
    ],
    token_filters=[
        POSKeepFilter(['名詞']),
        TokenCountFilter(sorted=True),
    ],
)

# read_some_files()でテキストをファイルから読み込んで形態素解析する
for word, count in analyzer.analyze(read_some_files())
    print(word, count)

Analyzerを使って名詞のみ抽出し、出現頻度順に単語を標準出力するコード例です。

Analyzerはテキストの前処理用フックを提供してくれています。コンストラクタのchar_filtersにFilterクラスを指定して、特定のパターンの文字列を形態素解析前に除去することができます。

janome.charfilter の仕様

Filterを作成するためのインタフェースが用意されています。

■ CharFilter
Filterクラスのベースクラスです。このクラスを継承して、独自のFilterクラスを定義することができます。

「a」を除去するFilter例
class CustomeCharFilter(CharFilter):
    def apply(self, text):
        '''「a」をstripするFilterクラス'''
        return text.replace('a', '')

半角英文字「a」をテキストから除外するFilterクラスの定義例です。 日本語以外は除去する、正規表現でルールベースの文字列除去、数字だけ除去、など実装者が自由にルールを定義できます。

■ UnicodeNormalizeCharFilter
Unicodeテキストの正規化用Filterです。 Unicode正規化形式には4種類あり以下のいずれかを指定できます。

  • NFC
  • NFD
  • NFKC (default)
  • NFKD

正規化形式については今回は説明を省略するので、詳細が気になる方は「Unicode正規化」で検索してみてください。

■ RegexReplaceCharFilter

半角英数字を除去するFilter例
RegexReplaceCharFilter('[a-zA-Z0-9]', '')

正規表現によるFilterクラスです。 ルールベースでテキストのFilteringを行えるのでこれは便利。

今回の前処理

analyzer = Analyzer(
    char_filters=[
        # Unicode正規化する
        UnicodeNormalizeCharFilter(),
        # 記号を除去する
        RegexReplaceCharFilter('[#!:;<>{}・`.,()-=$/_\d\'"\[\]\|]+', ' '),
    ],
# ...

形態素解析対象とする私のブログ記事はコードの掲載が多いので記号を除去します。 数字列もあまり意味を持たないので除去しておきます。

前後比較

■ 前処理なし
Analyzerのchar_filters指定なしの結果です。

$ python main.py | head -n 10

. 413
- 314
``` 220
_ 191
: 173
3 172
" 134
/ 132
> 126
( 121

記号が誤判定されて無意味な結果となってしまってます。

■ 前処理あり
Analyzerのchar_filtersに前述したFilterルールを指定したときの結果です。

$ python main.py | head -n 10
テスト 96
StrongBlueText 69
こと 60
Python 60
データ 51
コード 50
index 49
gatsby 46
実行 45
python 43

良い結果になりました。MDXで自作した <StrongBlueText> タグの中身が入ってしまっているのでまだ洗練の余地がありますね。

まとめ

いかがでしたでしょうか。

今回は文書のノイズクリーニングをして形態素解析精度を向上させてみました。

洗練の余地はありますが、自分のブログ記事のキーワード傾向把握に活用できそうです。

それでは、良きPythonライフを!


Web系エンジニアでPython好き。バックエンド/フロントエンド問わずマルチな方面でエンジニアリングしています。