ステージ3-1: N-gramモデル + 出現回数による文書ベクトル生成 (nltk.ngrams + nltk.FreqDist) (情報工学実験 3 : データマイニング班)
目次- 想定環境
- 文書集合からn-gramsモデルにより素性集合(コードブック)を作る
素性=特徴ベクトルを構成する要素。素性が10個なら、1文書を10次元の特徴ベクトルとして表現することになる。
- case1: コードブックを素性とする文書ベクトルを作る (直接ベクトル生成)
- case2: コードブックを素性とする文書ベクトルを作る (辞書型データからベクトル生成)
- case3: コードブックを素性とする文書ベクトルを作る (疎行列データ生成)
- クラスタリング(K-means)してみる
想定環境
- OS: Mac OS X 10.8.x (10.7.x以降であれば同じ方法で問題無いはず)
- Python: 2.7.x
- Mercurial: 2.2
- numpy: 1.9.0.dev (numpy.version)
- scipy: 0.14.0.dev (scipy.version)
- NLTK: 2.0.4 (nltk.__version__)
文書集合からn-gramsモデルにより素性集合(コードブック)を作る
- nltk.util.ngrams(): 入力された文字列を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)
コードブックを素性とする文書ベクトルを作る (直接ベクトル生成)
- nltk.probability.FreqDist(): 素性集合の出現分布を計算。
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]の出現回数。
コードブックを素性とする文書ベクトルを作る (辞書型データからベクトル生成)
- feature_extraction.DictVectorizer(): {素性:値}という辞書型特徴表現をベクトル表現に変換。
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 = {}
for key, value in fdist.iteritems():
dict.update({key:value})
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()
コードブックを素性とする文書ベクトルを作る (疎行列データ生成)
- sp.sparse: 疎行列パッケージ
- sp.sparse.todense(): 疎行列を通常行列に変換
# 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_