Python初心者必見!自然言語処理100本ノック2025 第4章: 「言語解析」(#30-#39)を解説

自然言語処理 100本ノック解説の第4章 プログラミング

始める前に


Pythonで使える形態素解析ライブラリには、Mecab,Janome,SudachiPy,GiNZAなどありますが、Mecabを使用して実装していきます。

MeCab: Yet Another Part-of-Speech and Morphological Analyzer
第4章: 言語解析 — 言語処理100本ノック 2025

第四章

30. 動詞

形態素解析をするために、MeCabをインポートし、MeCab.Tagger()でオブジェクトを生成します。

nodes=parseToNode(text)は与えられた文を解析し、Nodeオブジェクトを返します。このNodeオブジェクトは、解析した一つの単語を指し、品詞などの詳細情報はnodes.feature・単語自体を見るにはnodes.surfaceを使います。


この時、featuresは文字列になっているので、区切り文字を使い区切り、features[0]にある品詞を取得します。

そして、nodesは先頭から順にnodes=nodes.nextで解析した文を走査することができます。


これらを使い、動詞のみをtokensに格納し、出力します。

31. 動詞の原型

動詞を基本形にするには、features[6]で取得することができます。


もしこのインデックスがなかったらエラーになるため、len(features)>7で条件分岐させます。

32. 「AのB」

nodesのままだとリストのようにindexアクセスができないため、条件付けが少し難しくなります。

そのため、一旦(単語、品詞)をリストに格納し、最後にif文で出力します。

33. 係り受け解析

構文解析を行うために、spaCyという自然言語処理ライブラリを使用します。
日本語の構文解析を行うために、日本語モデル(ja_core_news_sm)をロードします。


次に、for token in doc: のようにして文から各トークン(単語)を順に取り出し、
token.head(係り先) が自分自身 と同じ場合除外します。
token.text で単語(トークン)の文字列を、token.head.text でその単語が係っている先の単語を取得できます。

34. 主述の関係

token.dep_ は構文上の依存関係を文字列で取得できます。今回はメロスが主語であるという条件が必要なため、token.dep_ == “nsubj(名詞主語)”とします。


このラベリングについては以下の論文でわかりやすくまとめられていました。

https://www.jstage.jst.go.jp/article/jnlp/26/1/26_3/_pdf
https://www.anlp.jp/proceedings/annual_meeting/2015/pdf_dir/E3-4.pdf

35. 係り受け木

係り受け木を描画するには、displacyをインポートします。

displacyには、nlpで作ったオブジェクトを渡すことで係り関係を視覚化します。

また、style="dep"は文の係り受け構造(依存関係)を矢印付きで可視化する指定です。
jupyter=TrueはJupyter NotebookやColab上で結果を直接表示
する設定で、HTML文字列ではなく図として描画されまた。

36. 単語の出現頻度

第三章のコードを使って、マークアップを除去するclean_text関数を実装しています。そして、json化、マークアップ除去、トークン取得をget_token_array関数にまとめました。


配列の要素の頻度を辞書としてまとめるには、Counterを使います。このCounterに先ほど作った配列を渡し、most_common(20)で出現頻度が高い20個を出力します。

37. 名詞の出現頻度

37と違うのは、名詞の頻出度に着目する点です。つまり、トークン化する関数を名詞のみ取得できるトークン関数に変更します。

今回では、30を参考にfeatures[0] == “名詞”にして、トークンを取り出します。

38. TF・IDF

tf-idfについては下記に簡単にまとめましたので、確認したい方はご参考にどうぞ。


今回の問題を解くに当たって、問題に2つの解釈があると思います。一つ目は、全記事のtf-idfから日本の記事の名詞のスコアを出す。2つ目は日本における記事だけでtf-idfを求める。

この問題では、前者に基づいて問題を解いていきます。(文章一個の単位を一つの国とします。)

tf-idfを計算するために、日本での名詞数japan_cnt、日本での名詞の割合japan_noun_freq 、文章(国)の数total_docs 、全文章中での名詞が含まれる頻度doc_freqを用意し、with open内で実装しています。

実際のtf-idfの計算は、get_df_tfidf関数内でjapan_noun_freqをイテレーションし、tf,idf,tf-idfを計算します。


そして、pandasでデータを扱うためにも、dataという配列にタプルで格納します。


tf-idfとは


$$
\mathrm{tf}(t, d) = \frac{\text{文章d中の単語tの出現回数}}{\text{文章dの総単語数}}
$$

$$
\mathrm{idf}(t) = \log \frac{\text{全文章数 } N}{\text{単語 } t \text{ が含まれる文章数 } }
$$

tf・idfの役割を簡単に言うと、

tfが「文章に占める単語の割合」、idfが「ある単語が含まれる文章の確率(の逆数)」です。

これらの積が文章d中の単語のスコアとなります。


特に、注意したいのが、tfは特定の文章d中に着目し、idfは文章全体に着目します。これは、用意したコーパス内では、idfは共通のものとして利用しますが、tfは文章ごとに異なる値になっているので、実装時には気を付けましょう。

39. Zipfの法則

Zipfの法則とは、「単語の出現頻度と順位の間に一定の法則がある」という言語統計の経験則です。これを39では両対数グラスにプロットし、確かめていきます。

pandasまでは、38と同様に単語の頻度を数え上げて、データを格納していきます。


両対数グラフをプロットするには、matplotlibを使用します。

先ほど取得した順位と出現頻度をそれぞれx,yに入れて、plt.plotで作図します。そして、両対数グラフにするために、ax = plt.gca()で描画に関する細かい設定を行います。


ここから、ax.set_yscale(‘log’) ax.set_xscale(‘log’) を使いx、y軸に対して対数化をすることで簡単に両対数グラフを出力することができるのです。

まとめ

形態素解析や構文解析、tf-idfと自然言語処理の基礎となる部分を問題を通して身につけることができたと思います。

これらの知識を復習と発展を繰り返し、次回の「第5章: 大規模言語モデル」に挑みましょう!

コメント

タイトルとURLをコピーしました