元理系院生の新入社員がPythonとJavaで色々頑張るブログ

プログラミングや機械学習について調べた事を書いていきます

Pythonでcos類似度とTF-IDFを用いた文章の類似度算出

この記事に書かれていること

cos類似度について
Pythonによるcos類似度の実装
Pythonによるcos類似度とTF-IDFを複合した文章類似度算出の実装

cos類似度って何?

cos類似度はベクトルの内積をベクトル間の類似度の指標に用いる手法で、0~1の値を取ります。
値は1に近づくほどベクトル同士が類似しており、0に近づくほど類似していないです。

cos類似度は次の式で定義されています。
 \cos(\boldsymbol{A}, \boldsymbol{B})=\frac{\boldsymbol{A}\times \boldsymbol{B}}{|\boldsymbol{A}| |\boldsymbol{B}|}

Pythonによるcos類似度の実装

__author__ = 'emoson'


def cosine_similarity(v1, v2):
    """
    ベクトルv1, v2のcos類似度の算出
    """
    return sum([a*b for a, b in zip(v1, v2)])/(sum(map(lambda x: x*x, v1))**0.5 * sum(map(lambda x: x*x, v2))**0.5)

cos類似度とTF-IDFを複合した文章間類似度の算出

cos類似度はベクトル間の類似度を求められることが分かりました。
このcos類似度と前回扱ったTF-IDFを組合せる事で、文章同士の類似度を求めることが出来ます。

PythonによるTF-IDF - 理系大学生がPythonで色々頑張るブログ


ざっくりおさらいすると、TF-IDFは文章に出現する単語が、どれだけその文章を特徴付けているかを算出する手法でした。
文章同士の類似度を求める際には、文章毎に算出したTF-IDF値のcos類似度を求めます。

Pythonによる文章の類似度算出

__author__ = 'emoson'


def cosine_similarity(v1, v2):
    """
    ベクトルv1, v2のcos類似度の算出
    """
    return sum([a*b for a, b in zip(v1, v2)])/(sum(map(lambda x: x*x, v1))**0.5 * sum(map(lambda x: x*x, v2))**0.5)


def tf(terms, document):
    """
    TF値の計算。単語リストと文章を渡す
    :param terms:
    :param document:
    :return:
    """
    tf_values = [document.count(term) for term in terms]
    return list(map(lambda x: x/sum(tf_values), tf_values))


def idf(terms, documents):
    """
    IDF値の計算。単語リストと全文章を渡す
    :param terms:
    :param documents:
    :return:
    """
    import math
    return [math.log10(len(documents)/sum([bool(term in document) for document in documents])) for term in terms]


def tf_idf(terms, documents):
    """
    TF-IDF値を計算。文章毎にTF-IDF値を計算
    :param terms:
    :param documents:
    :return:
    """
    return [[_tf*_idf for _tf, _idf in zip(tf(terms, document), idf(terms, documents))] for document in documents]


if __name__ == "__main__":
    #単語リスト
    _terms = ["リンゴ", "ゴリラ", "ラッパ"]
    #文章リスト
    _documents = ["リンゴ、リンゴ", "リンゴとゴリラ", "ゴリラとラッパ"]
    tf_idfs = tf_idf(_terms, _documents)

    #文章0と文章1の類似度
    print(cosine_similarity.cosine_similarity(tf_idfs[0], tf_idfs[1]))
    #文章0と文章2の類似度
    print(cosine_similarity.cosine_similarity(tf_idfs[0], tf_idfs[2]))
    #文章1と文章2の類似度
    print(cosine_similarity.cosine_similarity(tf_idfs[1], tf_idfs[2]))

実行結果は次のようになります。

0.7071067811865476
0.0
0.24482975009584626

文章0(「リンゴ、リンゴ」)と文章1(「リンゴとゴリラ」)は非常に似ている事が分かりますね。