【自然言語処理】形態素解析で文章を単語に分ける | python

今回は自然言語処理の入門編ということで、形態素解析を行なってみます。

pythonの環境がある方向けに、MeCabのインストール方法も併せてご紹介します。

形態素解析

具体的な作業に入る前に、形態素解析をざっくり解説します。

形態素解析とは、「文章を単語に分割する技術」のことです。

「人間の言葉をプログラムが理解するために行う処理」である自然言語処理(Natural Language Processing)には、必須の技術です。

また、形態素解析以外にも自然言語処理の基礎技術となるタスクはあります。

例えば、構文解析や意味解析、文脈解析などが挙げられます。

形態素解析には、MeCabやJanomeというライブラリを用いることで簡単に行うことができます。

ちなみに、Mecabの開発者はメカブが好きで、この名前にしたそうです。

今回は、オープンソースライブラリ「MeCab」を使って形態素解析を行います。

形態素解析

MeCabのインストール

ターミナルに以下のコマンドを順に入れると、MeCabをインストールできます。

Homebrewからインストールします。

#手順1

brew install mecab
#手順2

brew install mecab-ipadic

このipadicというのは、MeCabで使う最もメジャーな辞書です。

辞書は、形態素解析で単語を区切る上で必須のものです。プログラムは辞書がないと、文章を単語に分けられません。

優れた辞書ほど、正確な解析ができます。

#手順3

pip install mecab-python3

最後はこちらのコードです。pythonからMeCabをインストールをするためのコードです。

では、準備ができたので実際のコードで形態素解析をやってみましょう。

Code

今回は、以下のような文章を使います。

ダッシュボードを作るデータサイエンティストが何をやっているのかというと、上司の「アクティブなユーザの割合を知りたい」という曖昧なオーダーを「課金ユーザのうち、30日以内に1回以上ログインしているユーザー」みたいに考えを引き出して具体的な条件へ落とし込んだりするのが仕事です。
import MeCab
tagger = MeCab.Tagger()
text = "ダッシュボードを作るデータサイエンティストが何をやっているのかというと、上司の「アクティブなユーザの割合を知りたい」という曖昧なオーダーを「課金ユーザのうち、30日以内に1回以上ログインしているユーザー」みたいに考えを引き出して具体的な条件へ落とし込んだりするのが仕事です。"
words = tagger.parse(text)
words

このような出力結果になりました。

1行目でMeCabライブラリをインポートしてきます。2行目では、初期化を行なっています。

3行目で今回解析する文章を代入します。

tagger.parseで形態素解析を行います。

ぐちゃぐちゃで何がなんだかわかりにくいですが、\nで単語で区切っています。また、\t以降では区切った単語の品詞などの情報が載っています。

では、分類した単語のみをリストに入れてみましょう。

#分割した単語をリストに入れてみましょう。
words = tagger.parse(text).splitlines()
words_arr = []
for i in words:
    if i == "EOS":continue
    word_tmp = i.split()[0]
    words_arr.append(word_tmp)
words_arr

載せきれないので、出力結果の一部を載せています。先ほどの文章を単語に分けたものが、リスト構造で入っていますね。

では、名詞だけ取り出してリストに入れましょう。

#名詞のみを抽出してみます。

text="ダッシュボードを作るデータサイエンティストが何をやっているのかというと、上司の「アクティブなユーザの割合を知りたい」という曖昧なオーダーを「課金ユーザのうち、30日以内に1回以上ログインしているユーザー」みたいに考えを引き出して具体的な条件へ落とし込んだりするのが仕事です。"
words = tagger.parse(text).splitlines()
words_arr = []
parts = ["名詞"]
words = tagger.parse(text).splitlines()
words_arr = []
for i in words:
    if i == "EOS" or i=="":continue
    word_tmp = i.split()[0]
    part = i.split()[1].split(",")[0]
    if not (part in parts):continue
    words_arr.append(word_tmp)
words_arr

調べた単語が、名詞ではない場合にcontinueすることで、配列words_arrに追加されないという仕組みになっています。

だいぶ数が減りましたね。ただ、「の」など不要な単語が少し残っていますね。

「の」を消してみましょう。

#名詞のみを抽出してみます。
text="ダッシュボードを作るデータサイエンティストが何をやっているのかというと、上司の「アクティブなユーザの割合を知りたい」という曖昧なオーダーを「課金ユーザのうち、30日以内に1回以上ログインしているユーザー」みたいに考えを引き出して具体的な条件へ落とし込んだりするのが仕事です。"
words = tagger.parse(text).splitlines()
words_arr = []
parts = ["名詞"]
stop_words = ["の"]
words = tagger.parse(text).splitlines()
words_arr = []
for i in words:
    if i == "EOS" or i=="":continue
    word_tmp = i.split()[0]
    part = i.split()[1].split(",")[0]
    if not (part in parts):continue

    #stop_wordsだった場合に、words_arrに加えないようにする。
    if word_tmp in stop_words:continue

    words_arr.append(word_tmp)
words_arr

「の」が消えました。

追加したのは、繰り返し処理の前のstop_words = [“の”]と、繰り返し処理の中の if word_tmp in stop_words:continueです。

単語がstop_wordsである場合、continueされ、words_arrには追加されない仕組みになります。

parts = [“名詞”]とし、part = i.split()[0]でその単語の情報を入れます。

part in partsによって、分割した単語の情報に”名詞”が含まれているものだけをwords_arrに入れます。

[]の中の数字が何が良いかについては、word_tmpを出力して、リストの何番目に品詞名がくるのかを確認してください。

分析に応じて、他の単語も消したりしてみましょう。このように、自然言語処理は泥臭い処理が大半です。

例えば。アンケートから形態素解析をして作ったリストで、「顧客満足度が高いアンケートに多く登場する単語はどれだろう??」「顧客満足度が低いアンケートに多く登場する単語はどれだろう??」など、さまざまな分析を行います。

自然言語処理について、どんどんコンテンツを投稿する予定です。

形態素解析した後のテキストのベクトル化については、【N-gram】テキストをベクトルで表現するには | 自然言語処理をご覧ください。

ベクトル化した文章の類似度を測る、「コサイン類似度」については、【python】コサイン類似度は高校数学の知識で理解できます!をご覧ください。

自然言語処理の歴史

ここまで、形態素解析を扱ってきましたが、自然言語処理の手法は多岐にわたります。

興味がある方だけみてください。

全体の流れとしては、単語の出現頻度から意味(連続量)、そして文章全体の文脈の理解へ推移していきます。

言葉の意味をどうやって表現するか

Bags-of-wordsの時代です。

文書内の単語の出現頻度をベクトル化します。ここでの意味ベクトルは、語彙数に結びつき、数十万次元に及びます。

短い文章では、値をもつ次元が少ないため表現力が不足していました。そのため、長い文章向きの技術でした。

ここでいう単語の意味は、平均的な意味を使います。

例えば、文脈によってはeatとは不安にさせるという意味がありますが、基本は「食べる」なので「食べる」を採用します。

この時点で既に形態素解析はできます。かなり昔からの技術ですね。

次は、機械学習を使って文脈的な意味を推測する時代です。2010年代からです。

ELMo/BERT以降の事前学習モデルでは、言葉の意味を文脈を考慮して捉えることができます。

事前学習モデルは、単語の穴埋め問題(教師あり学習)をひたすら解かせて文脈的な意味をベクトル化させることに成功しています。

教師データはインターネットです。

意味データは768から1024次元くらいまでで、以前よりもかなり少なくなっています。

また、各次元の値は連続値です。以前は離散型の値でした。

BERTに関しては、いずれ取り扱おうと思います。

FOLLOW ME !

PAGE TOP