第3章クラスタリング:関連のある文章を見つける

目の前にあるデータだけからパターンを見つける方法を学ぶ。本の例では、Q&Aサイトでユーザがした質問と同じような過去の質問を選ぶことを行う。

本章の前半はgitからダウンロードした中にないので、自分で手打ちする。それが普通か。

../data/toy に。01.txt〜05.txt の5つのテキストファイルを置いた。

$ cd ../data/toy
$ cat *
This is a toy post about machine learning. Actually, it contains not much interesting stuff.
Imaging databases can get huge.
Most imaging databases safe images permanently.
Imaging databases store images.
Imaging databases store images. Imaging databases store images. Imaging databases store images.



準備を整えて、以下を動かすと、、、

$ more ch03-1.py 
import os
import sys
import scipy as sp

DIR = r"../data/toy"
posts = [open(os.path.join(DIR, f)).read() for f in os.listdir(DIR)]
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(min_df=1)

print(vectorizer)




min_df はなに?って怒られる。

$ python ch03-1.py 
Traceback (most recent call last):
  File "ch03-1.py", line 8, in <module>
    vectorizer = CountVectorizer(min_df=1.0)
TypeError: __init__() got an unexpected keyword argument 'min_df'
$


ググってみると、どうも sklearnの version が低いせいかも。

$ python
Python 2.7.3 (default, Mar 18 2014, 05:13:23) 
[GCC 4.6.3] on linux2
>>> import sklearn
>>> sklearn.__version__
'0.11'
>>> 
 

versionを上げてみる。

$ sudo pip install -U scikit-learn

$ python
Python 2.7.3 (default, Mar 18 2014, 05:13:23) 
[GCC 4.6.3] on linux2
>>> import sklearn
>>> sklearn.__version__
'0.16.1'
>>> 


$ python ch03-1.py 
CountVectorizer(analyzer=u'word', binary=False, decode_error=u'strict',
        dtype=<type 'numpy.int64'>, encoding=u'utf-8', input=u'content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(1, 1), preprocessor=None, stop_words=None,
        strip_accents=None, token_pattern=u'(?u)\\b\\w\\w+\\b',
        tokenizer=None, vocabulary=None)

確かに動いた。

新しい文書(new_post= "imaging databases")と 既存の文書(01.txt〜05.txt)のユークリッド距離を計算し、既存文書との類似度を計算する。


$ more ch03-1.py 
import os
import sys
import scipy as sp

DIR = r"../data/toy"
posts = [open(os.path.join(DIR, f)).read() for f in os.listdir(DIR)]
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(min_df=1)

x_train=vectorizer.fit_transform(posts)
num_samples, num_features=x_train.shape
print("#samples: %d, #features: %d" % (num_samples, num_features))

print("--------------------------------------------\n")
print(vectorizer.get_feature_names())
print("--------------------------------------------\n")

new_post = "imaging databases"
new_post_vec=vectorizer.transform([new_post])

print(new_post)
print(new_post_vec)
print(new_post_vec.toarray())

print("--------------------------------------------\n")

# norm 
def dist_raw(v1, v2):
    delta = v1 - v2
    return sp.linalg.norm(delta.toarray())


#
best_dist = sys.maxsize
best_i = None

for i in range(0, num_samples):
    post = posts[i]
    if post == new_post:
        continue
    post_vec = x_train.getrow(i)
    d = dist_raw(post_vec, new_post_vec)

    print("=== Post %i with dist=%.2f: %s" % (i, d, post))
    print(x_train.getrow(i).toarray())

    if d < best_dist:
        best_dist = d
        best_i = i

print("*** Best post is %i with dist=%.2f" % (best_i, best_dist))

$

実行すると。

$ python ch03-1.py 
#samples: 5, #features: 24
--------------------------------------------

[u'about', u'actually', u'can', u'contains', u'databases', u'get', u'huge', u'images', u'imaging', u'interesting', u'is', u'it', u'learning', u'machine', u'most', u'much', u'not', u'permanently', u'post', u'safe', u'store', u'stuff', u'this', u'toy']
--------------------------------------------

imaging databases
  (0, 4) 1
  (0, 8) 1
[[0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
--------------------------------------------

=== Post 0 with dist=4.00: This is a toy post about machine learning. Actually, it contains not much interesting stuff.

[[1 1 0 1 0 0 0 0 0 1 1 1 1 1 0 1 1 0 1 0 0 1 1 1]]
=== Post 1 with dist=1.73: Imaging databases can get huge.

[[0 0 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
=== Post 2 with dist=2.00: Most imaging databases safe images permanently.

[[0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0]]
=== Post 3 with dist=5.10: Imaging databases store images. Imaging databases store images. Imaging databases store images.

[[0 0 0 0 3 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0]]
=== Post 4 with dist=1.41: Imaging databases store images.

[[0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]]
*** Best post is 4 with dist=1.41

で、Post 3 と Post 4 で同じ単語が3回繰り返されているときの評価(dist)をもうちょっといけてる感じにかえるために、次に正規化する。


ここまでやってきて、本に載っているプログラムや出力結果の値は、いまいち信用できないようだ。
その点を踏まえて、しっかり自分の頭で理解していかないといけない。その意味で良い教科書だ。