Excelファイルの類似度検索 TF-IDF
Windows 10 / Visual Stadio Community 2019 / .NET Framework 4.7.2 / Python 3.9.8
指定したExcelシートまたは入力した文章から、それに類似したExcelシートを検索する。機能は、前半と後半に分かれ、それぞれ別のアプリケーションで実行する。
前半は、Excelシートからテキストを抽出し、形態素解析を行い、シートごとに形態素のデータセットを作成する。これは Excelシートのテキストに対し形態素解析を行うにて解説する。下図の青い四角の部分にあたる。
後半は、本章で解説する検索処理である。下図の赤い四角の部分にあたる。類似度による検索は 単語の集合を TF-IDFアルゴリズムによりベクトル化し、検索語との類似度をコサイン類似度により求め、スコアの高い順に表示するというものである。
![[構成図]](/dot_net/12/ExcelAnalyzer5.png)
計算の部分は Python の scikit-learnライブラリが全てやってくれる。中で何を行っているかは、同じことを JavaScriptで自前で実装してみたのでそちらも参考にしてください。文書の類似度検索を JavaScriptで実装してみる
指定したExcelシートに類似したExcelシート/ファイルを検索する
「解析データフォルダ」には、Excelから抽出した形態素データセットと XMLファイルを格納したフォルダを指定する。XMLファイルは検索文に Excelシートを指定した場合には必要になる。「ファイル・シートによる検索」タブを選び、類似度を求める Excelシートを指定し検索する。
検索の結果、指定した Excelシートに類似した Excelシートが類似度の高い順に表示される。レコードを選択してコンテキストメニューの「Excelファイルを開く」を選ぶとファイルを開くことができる。
![[image2]](/dot_net/12/image2.png)
任意の文章に類似した Excelシートを検索する
「任意の文による検索」タブを選び、文章を入力し検索する。結果は上記と同じ。
![[image3]](/dot_net/12/image3.png)
処理概要
検索処理の中核部分は Pythonスクリプトが行う。
検索母集団の単語リストは、Excelファイルから形態素を抽出する で作成された XMLファイルに格納されている。検索処理は XMLファイルからテキストを取り出し、空白で区切られた単語のリストを tf-idfのロジックに基づきベクトル化する。
一方、検索語にあたる文章または Excelシートも、検索の時点でテキストの形態素解析を行い、単語のリストを同様にベクトル化する。両者のベクトルのコサイン類似度が、二つのテキストの類似度に相当する。
C#プログラムは、検索語の文章またはExcelシートの入力を受付け、Pythonスクリプトを呼び出し、実行結果を編集し表示する。
C#からPythonスクリプトを実行する
当該部分のコードを以下に示す。Processクラスの機能を使用して、Pythonスクリプトを実行するプロセスを起動する。プロセスが書き出す標準出力を読み込むまで、プロセスの完了を待つことができる。
ソースコード
C#プログラム
Form1.cs ユーザインタフェース、Pythonの起動、結果の編集出力
Pythonスクリプト
search.py 検索語に Ecelシートを指定した場合の類似度検索
search2.py 検索語に文章を指定した場合の類似度検索
Python 実装のポイント
簡単な例をあげて解説してみよう。
ベースの文書の集合と、指定した文書との類似度を求める。
ベースの文書は、'赤 赤 赤', '赤 白 白', '赤 白 黒' という三つの文書。"白 黒"という文書との類似度を求める。
実行結果;類似度は 0から1の範囲で表す。ひとつめは全く似てない(0)、二つめはそこそこ似ている(0.5)、三つめは非常に似ている(0.9)という計算結果となる。
コードの解説
実装は sklearn(機械学習)ライブラリを利用する。
まず、ベースの文書集合の tf-idf値を求める。CountVectorizerクラスの fit_transformメソッドにより、ベースの文書集合の tf値を求め、TfidfTransformerクラスの fit_transformメソッドにより、その tf値から tf-idf値を求める。
次に、上と同じような方法で検索用の文書の tf-idf値を求める。このとき注意すべきことは、tf値は、ベースの文書集合から tf値を求めたときの CountVectorizerオブジェクトに対して、tranformメソッドを実行して求める。(fit_transformではない)
また tf-idf値も同様に、ベースの文書集合の tf-idf値を求めたときの TfidfTransformerオブジェクト対して、tranformメソッドを実行して求める。(fit_transformではない)
fitとは、データを学習するということ、tranformは、それに基づいて実際にデータを変換するということらしい。色々な種類のクラスに、fitと tranformの組み合わせが定義されている。
tf値、tf-idf値を求めたときの CountVectorizerオブジェクト、TfidfTransformerオブジェクトは、ベースの文書集合を学習(fit)したものでる。検索に指定する文書は、ベースの文書集合の部分なので、そのオブジェクトを共用する必要がある。
言い換えると、tf値、tf-idf値は、単語リストを列に持つベクトルである。比較しようとする二つの tf-idf値の構成要素となる単語リストが、それぞれ別個に作成されていたら、これら二つを比較することはできない。検索に指定する文書はベースの文書集合の単語リストを列に持つベクトルでなければならない。
これらを無理に比較しようとすると cosine_similarityメソッドで内部エラーが起きる。たまたま、ベース文書の集合と検索に指定する文書の単語リストが全く同一であれば落ちないが、まずそういうことはあり得ない。
tf-idf値を求める別の手順
tf-idf値を一遍に求める。TfidfVectorizerクラスを使用して tf値の取得と tf-idf値の計算を合わせて行うことができる。
tf値/ tf-idf値を求めるとき、fitと transformを別に行う。(実用性はあまりないが、何を行っているか理解の助けになるので紹介)
類似度検索の特徴
類似度検索と全文検索によるキーワード検索との違いを端的に表した例として(偏ったサンプルではあるが)私のPCのExcelファイルを対象として次の検索を行ってみた。
(1) "地震を想定した避難訓練の実施" という文で類似度検索を行う。
(2) "地震" "想定" "避難訓練" "実施" という四つの語によりに全文検索のOR検索を行う。
結果は、(1)類似度検索の方が (2)全文検索より「精度」が高かった。
類似度検索では、上位に表示されるのは全て、地元自治会の防災訓練のお知らせや要項に関するものだが、全文検索では全く関連のない行事についての文書も、ちらほら上位に紛れてくる。これは検索語の "実施" の影響によるもので、"地震" や "避難訓練" という言葉がなくても "実施" という言葉を多用すればその文書が上位にくるのは当然である。一方、類似度検索ではそれらの文書は弾かれ、かなりうまくいっているようだが何故なのだろう?
もし私が全文検索により避難訓練に関する文書を検索しようと思えば、"地震" や "避難訓練" というキーワード以外に、"想定" や "実施" をキーワードにすることは避けるであろう。それは無意味であり、無関係な文書が検索される可能性が高いことを自明のことと理解しているからである。
ではその判断基準は何か。"実施" という言葉は「ありきたり」だからである。どのような種類の文書にも均等に使われる可能性のある単語である。一方 "避難訓練" は、あまり「ありきたり」ではない、文書の集合全体からみれば稀に発生する言葉である。言い換えれば、ある文書を特徴づける「重要」な言葉であると言える。
類似度検索では、この「ありきたり」度が非常に重要な指標となる。文書に発生する全ての単語に「ありきたり」な度合いに応じて統一的な重みをつけ、検索の際の判断基準にするのである。検索の際に、ありきたりな単語は軽く扱い、ありきたりでない稀な単語は重要視する。このようなことを数値的に処理する。
「ありきたり」であるか否かは、本来言葉に対する価値判断を含むが、これを数学的に解決してしまうのが、本章で使用しているTF-IDFアルゴリズムである。TF-IDFでは、検索対象の全文書を一括して読み込み、各文書におけるそれぞれの単語の発生頻度から、各単語の「ありきたり」度を決定し、結果はベクトル化したデータとして保持する。文書の類似度はベクトルの類似度として求めることができる。(ここらへんの理屈については専門的な記事がたくさんあるのでそちらを参考にされたい)
本例の "地震を想定した避難訓練の実施" という文を形態素に分割すると、"地震" "想定" "避難訓練" "実施" になる。これは全文検索のOR検索のキーワードと同じである。類似度検索においては、TF-IDFの演算の結果、単語の重みづけはおそらく、 "避難訓練" ≒ "地震" > "想定" > "実施" になっているのではないだろうか。これにより、より重みのある言葉を使用している文書が相対的に上位に現れ、逆に重みのない言葉はいくら使用しても相対的に下位になるという結果になる。全ては単語の重み次第ということである。
ところで「ありきたり」度は、文書集合全体との関係で決まる。例えば文書集合が「防災」に関するものだけで構成されているとしたら、"地震" や "避難訓練" という言葉はきわめてありきたりな言葉であるだろう。文書集合における単語の「ありきたり」度は、個々の文書集合の特徴に依存するものである。TF-IDFは、それらの特徴を機械的に導き出すことができる汎用的で優れ方法だと思う。