ご無沙汰しております。
taanatsuです。
今回は珍しくPHPの記事を書いていこうと思います。
ExciteといえばPHPですからね!しらんけど。
形態素解析
皆さんは「形態素解析」という言葉を耳にしたことがありますでしょうか?
機械学習だ!AIだ!と騒がれる昨今、文章の解析で使われる手法の一つがこの形態素解析です。
私は漢字が4つ以上並ぶと読めなくなるので、形態素解析という言葉が苦手ではあります。
形態素解析とは、文章を「形態素」、いわゆる名詞・動詞・形容詞・副詞のような、日本語の最小単位の単語に分割する処理のことを言います。
形態素解析器「MeCab」
形態素解析を行ってくれるツールです。
今回はよく使われる「MeCab」を利用していきたいと思います。
で、Windowsの方はすいません。。。
会社のPCがMacなので、この記事はMac用になります。
私個人はWindows機を利用していて、できることは知っていますので、Windowsの方は頑張ってください!
(確かバイナリをダウンロードして、ダブルクリックしてインストールするだけだったはず…!)
MeCabのインストール
Homebrewを使えば一発です。
$ brew install mecab
以上!
……と言いたいところですが、文章を単語に分割するための「辞書」が必要になります。
辞書も入れましょう。
$ brew install mecab-ipadic
以上です。
ターミナル上でmecab
とタイプしてEnterを押してみてください。
入力待ちになります。
この状態で「すもももももももものうち」と入力してEnterを押してみましょう。
$ mecab すもももももももものうち すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ も 助詞,係助詞,*,*,*,*,も,モ,モ もも 名詞,一般,*,*,*,*,もも,モモ,モモ も 助詞,係助詞,*,*,*,*,も,モ,モ もも 名詞,一般,*,*,*,*,もも,モモ,モモ の 助詞,連体化,*,*,*,*,の,ノ,ノ うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
きれいに分割されていますね!
これが形態素解析です。
PHPからMeCabを使う
さて、こういう分野ではPythonがブイブイ言わせていますが、弊社はPHPが盛んな会社ですので、PHPで使っていきましょう。
PHP7.1くらいまでは php-mecab
というPHPでMeCabを使えるようにするバインディングツールがあったのですが、どうやらPHP8には対応していなさそう…
なので、ちょっと強引ですが、PHPのexec
を使っていきます。
参考
<?php $result = []; $text = 'すもももももももものうち'; exec('echo ' . $text . ' | mecab', $result); var_dump($result);
↓
array(8) { [0] => string(61) "すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ" [1] => string(40) "も 助詞,係助詞,*,*,*,*,も,モ,モ" [2] => string(49) "もも 名詞,一般,*,*,*,*,もも,モモ,モモ" [3] => string(40) "も 助詞,係助詞,*,*,*,*,も,モ,モ" [4] => string(49) "もも 名詞,一般,*,*,*,*,もも,モモ,モモ" [5] => string(40) "の 助詞,連体化,*,*,*,*,の,ノ,ノ" [6] => string(63) "うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ" [7] => string(3) "EOS" }
形態素解析を使った文章の類似度
それでは形態素解析を使って文章の類似度を出してみましょう。
2つの文章に登場した形態素に対して、文章がその形態素を持っていれば1、持っていなければ0として計算してみましょう。
<?php $text1 = '新宿は豪雨'; $text2 = '渋谷は豪雨'; echo cosineSimilarity($text1, $text2); /** * 分かち書きのリストを作成します * * @param string $text 分かち書きを作成したい文章 */ function getWakachiList(string $text): array { $result = ''; exec('echo ' . $text . ' | mecab -Owakati', $result); if (!is_array($result) || count($result) !== 1) { return []; } return explode(' ', $result[0]); } /** * 文章の類似度をコサイン類似度を用いて求めます * * @param string $text1 文章1つ目 * @param string $text2 文章2つ目 */ function cosineSimilarity(string $text1, string $text2): float { // 文章を形態素に分解 $text1Corpus = getWakachiList($text1); $text2Corpus = getWakachiList($text2); // 2つの文章の形態素を抽出 $allCorpus = array_unique(array_merge($text1Corpus, $text2Corpus)); // コサイン類似度の計算に必要な分子分母の変数 $c = 0; $m1 = 0; $m2 = 0; foreach ($allCorpus as $word) { // 文章1に対象の形態素があるかどうか(あれば1、なければ0) $n1 = (array_search($word, $text1Corpus) !== false) ? 1 : 0; // 文章2に対象の形態素があるかどうか(あれば1、なければ0) $n2 = (array_search($word, $text2Corpus) !== false) ? 1 : 0; // コサイン類似度に利用する分子分母の数値を計算 $c += ($n1 * $n2); $m1 += $n1 * $n1; $m2 += $n2 * $n2; } // コサイン類似度の計算 if ($m1 === 0 || $m2 === 0) { return 0; } return $c / (sqrt($m1) * sqrt($m2)); }
新宿は豪雨
渋谷は豪雨
以上のテキストから形態素を抽出します。
また各文章にその形態素があるかないかも確認します。
新宿 | は | 豪雨 | 渋谷 | |
---|---|---|---|---|
テキスト1 | 1(形態素を持っている) | 1 | 1 | 0(形態素を持っていない) |
テキスト2 | 0 | 1 | 1 | 1 |
上記の1と0を使って、コサイン類似度を算出します。
このように形態素解析を使うと、単語単位で分解できるため、精度良く文章の類似度を出すことができます。
それでは今回はこのあたりで!