投資のためのデータサイエンス

個人の投資活動に役立つデータ分析にまつわる話題を綴ります。

TF-IDFとコサイン類似度を用いたおすすめ文書の抽出

昨今のECサイトでは、リコメンド機能によりユーザがさらに買いたいと思うような商品を提案して顧客体験を向上させることが一般的になっています。この背景にある技術は、一つには日本語を自動的に品詞に分解して「わかち書き」にする技術(形態素解析)、もう一つは文章間の「類似度」を評価する技術です。ここでは、あるPCユーザが使用しているアプリ名のリストから、関連するソフトウェアの脆弱性に関するニュース記事を抽出するプログラムを書いてみます。

# ライブラリのインポート
import pandas as pd
import numpy as np
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.tokenfilter import POSStopFilter
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

Python形態素解析のためのライブラリとしてはmecabjanomeがありますが、janomeの方がより高速なのでここではjanomeを用います。まず形態素解析のためのTokenizerインスタンスを生成し、名詞以外の品詞を読み捨てするように設定します。

tokenizer = Tokenizer()
# 読み捨てるトークンの品詞を指定する
token_filters = [POSStopFilter(['記号','助詞','助動詞','動詞'])]
anal = Analyzer(tokenizer=tokenizer, token_filters=token_filters)

次に、あらかじめスクレイピングで取得しておいたソフトウェアの脆弱性に関するニュース記事のデータを読み込みます。

 # 列'header','body'を持つ記事テキストのcsvファイルを読み込む
 df1 = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Biglobe_articles.csv',encoding='cp932')
 df2 = df1.loc[:,['header','body']]
 df2.head(3)

f:id:nicjps230:20211201230005j:plain
次に、対象テキスト文字列を取り出して形態素解析により分かち書き文を作ります。

df2 = df2.assign(wakati='')
# 対象テキスト文字列を一行ずつ取り出して分かち書きに変換する
for i in range(len(df2)):
  texts_flat = df2.iloc[i,1]
  tokens = anal.analyze(texts_flat)
  df2['wakati'][i] = ' '.join([t.surface for t in tokens])
df3 = df2.dropna(how='all')
df3.head(3)

f:id:nicjps230:20211201230033j:plain
次に、類似度評価計算の準備をします。ユーザが使用しているアプリ名を空白で区切って並べ、最後に「脆弱性」を付け加えた文字列を作成します。

# コサイン類似度のしきい値を定める
sim_thresh = 0.05
# 分かち書きをリストに、ヘッダと本文をNumpy配列に格納する
corpus = df3['wakati'].tolist()
header = np.array(df3['header'].tolist())
body = np.array(df3['body'].tolist())
# ユーザの利用しているアプリケーションを記述した文書を指定する
user_app = ['Google Chrome Microsoft Excel PowerPoint Python Anaconda SAKURA Editor Adobe Acrobat 脆弱性']

最後に、ターゲット文書とのコサイン類似度(ベクトル空間モデルにおいて、文書同士を比較する際に用いられる類似度計算手法)を求め、近い文書を取り出します。まずTF-IDF(Term Frequency, Inverse Document Frequency: 文書中に含まれる単語の重要度を評価する手法の1つ)によりスコアベクトルを作成し、それをもとにコサイン類似度を計算します。

# 結果から取り除くエスケープ文字を指定する
bad_chars = ["\n","\t","\r"]
# ユーザアプリの文書をニュース記事リストの先頭に挿入する
docs = user_app + corpus
# TF-IDFベクトル化する
vectorizer = TfidfVectorizer(max_df=0.9)
X = vectorizer.fit_transform(docs)
# コサイン類似度を計算する
sim = cosine_similarity(X)
# ターゲット文書に関わる一行目だけ残し、最初の要素は自分との比較なので取り除く
simil = sim[0][1:]
# コサイン類似度がしきい値を超えるインデックスを取得する。np.where結果はtuple
relev_index = np.where(simil > sim_thresh)[0]
# 取得したインデックスでヘッダと本文を抽出する
relev_head = header[relev_index]
print(relev_head)
relev_body = body[relev_index]
relev_body_r = []
for s in relev_body:
  relev_body_r.append(''.join((filter(lambda i: i not in bad_chars, s))))
print(relev_body_r)

f:id:nicjps230:20211201230103j:plain
使用しているアプリに関連する脆弱性記事が取り出せました。