元のページ

ステージ3-1: N-gramモデル + 出現回数による文書ベクトル生成 (nltk.ngrams + nltk.FreqDist) (情報工学実験 3 : データマイニング班)

目次

想定環境

# 環境構築追加
% sudo pip install nltk
% sudo pip install prettyprint

文書集合からn-gramsモデルにより素性集合(コードブック)を作る

#生文書の用意(リストの1要素=1文書)
docs = []
docs.append(u"会場には車で行きます。")
docs.append(u"会場には自動車で行きます。")
docs.append(u"会場には自転車で行きます。")
docs.append(u"お店には自転車で行きます。")
from prettyprint import pp
pp(docs)

#NLTKでn-gram(n=2)してみる
#   n=2: 2-lettersを1つの素性とする。
import nltk
n = 2

def collect_ngram_words(docs, n):
    u'''文書集合 docs から n-gram のコードブックを生成。
    docs は1文書を1要素とするリストで保存しているものとする。
    句読点等の処理は無し。
    '''
    codebook = []
    for doc in docs:
        this_words = nltk.ngrams(doc, n)
        for word in this_words:
            if word not in codebook:
                codebook.append(word)
    return codebook

codebook = collect_ngram_words(docs, n)
codebook.sort()
pp(codebook)




コードブックを素性とする文書ベクトルを作る (直接ベクトル生成)

def make_vectors(docs, codebook, n):
    u'''文書集合docsから、codebook(n-gram)に基づいた文書ベクトルを生成。
    codebook毎に出現回数をカウントし、ベクトルの要素とする。
    出力 vectors[] は、「1文書の特徴ベクトルを1リスト」として準備。
    '''
    vectors = []
    for doc in docs:
        this_words = nltk.ngrams(doc, n)
        fdist = nltk.FreqDist()
        for word in this_words:
            fdist.inc(word)
        this_vector = []
        for word in codebook:
            this_vector.append(fdist[word])
        vectors.append(this_vector)
    return vectors

data = make_vectors(docs, codebook, n)
data
# len(data)=文書数。
# len(data[0]) = ベクトルの要素数 = 全素性数。
# data[0][0] = 文書0におけるcodebook[0]の出現回数。




コードブックを素性とする文書ベクトルを作る (辞書型データからベクトル生成)

def make_dict_vectors(docs, codebook, n):
    '''文書集合docsから、codebook(n-gram)に基づいた文書ベクトルを dict型 で生成。
    codebook毎に出現回数をカウントし、ベクトルの要素とする。
    出力 dict_vectors[] は、「1文書の特徴ベクトルを1辞書 {素性:出現回数}」として準備。
    '''
    dict_vectors = []
    for doc in docs:
        this_words = nltk.ngrams(doc, n)
        fdist = nltk.FreqDist()
        for word in this_words:
            fdist.inc(word)
        dict = {}
        keys = fdist.keys()
        for key in keys:
            dict.update({key:fdist[key]})
        dict_vectors.append(dict)
    
    return dict_vectors

# 辞書型作成したデータからベクトル変換
dict = make_dict_vectors(docs, codebook, n)
from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer()
data = vec.fit_transform(dict).toarray()




コードブックを素性とする文書ベクトルを作る (疎行列データ生成)

# sparse matrix の容量面での違いを確認してみる
#  1000x1000の行列を用意し、対角要素を1とする(=1000カ所のみ値がある)。
#  sp.sparse では値を取る1000 カ所のみ値を保存するが、
#  通常の行列だと 1000x1000 カ所全ての値を保存する。
import numpy as np
import scipy as sp
from scipy import io
sparse_matrix = sp.sparse.lil_matrix((1000, 1000))
sparse_matrix.setdiag( np.ones(1000) )
normal_matrix = sparse_matrix.todense()

# 保存されたファイルサイズを確認してみる。
io.savemat("sparse", {"test":sparse_matrix})
io.savemat("normal", {"test":normal_matrix})
#  % du -sh *.mat
#  8.0Ksparse.mat
#  7.6Mnormal.mat

del sparse_matrix
del normal_matrix

def make_sparse_matrix(docs, n):
    u'''文書集合docsから、n-gramに基づいた文書ベクトルを疎行列 sp.sparse 型で生成。
    文書の特徴を「単語の集まり」として表現しようとするとどうしても疎なデータになる。
    疎なデータをそのまま通常の行列として表現すると、データサイズも無駄な計算も多くなるため、
    値を持たない箇所(存在しない単語)には値を設定しない疎行列 sp.sparse として設定すると、
    データサイズも計算コストも減らすことができる。
    '''
    codebook = {} # map terms to column indices
    data = []        # values (maybe weights)
    row = []         # row (document) indices
    col = []          # column (term) indices
    for i, doc in enumerate(docs):
        terms = nltk.ngrams(doc, n)
        for term in terms:
            j = codebook.setdefault(term, len(codebook))
            data.append(terms.count(term))
            row.append(i)
            col.append(j)
    sparse_matrix = sp.sparse.coo_matrix((data, (row, col)))
    return sparse_matrix


sparse_matrix = make_sparse_matrix(docs, n)

# sparse matrix をデータとして学習できるかを確認してみる。
from sklearn import cluster
k_means = cluster.KMeans(n_clusters=2)
k_means.fit(sparse_matrix)
k_means.labels_




クラスタリング(K-means)してみる

#教師無し学習(K-means)で2クラスタに分類してみる
from sklearn import cluster
k_means = cluster.KMeans(n_clusters=2)
k_means.fit(data)
k_means.labels_





参考サイト一覧