ステージ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_