Tech Blog

Information Technology / Machine Learning / Data Analysis / Big Data / System Integration

PythonでWordNetを利用して、テキストの特徴行列を単語ベース→概念ベースに変換する

目的

「RとRMeCabでテキストファイルをクラスタリングする」 では、各テキストから単語を抽出し、各単語の出現頻度を元に、各テキストの特徴ベクトルを生成した。このとき、例えば"愛"と"あい"という単語は同一のものとして扱われたが、"愛"と"恋"という単語はまったく別のものとして扱われた。この結果、テキスト間の距離が人間が感じる距離と異なる場合が出てきた。そこで、本稿では、同じ概念の単語をひとまとめにして特徴ベクトルを作成することで、概念レベルでのテキストのクラスタリングを目指す。
f:id:tkdmah:20130105151528p:plain  

手法

クラスタリングの手順は以下のとおり。手順2以外については、以前の記事とほぼ同様なので、ここでは詳細は割愛する。

  1. Rで複数のテキストファイルからtf*idf行列を作成し、テキストファイルに出力する。
  2. tf*idf行列の書かれたテキストファイルを、PythonWordNetを用いて変換し、テキストファイルに出力する。
  3. 再びRで修正されたtf*idf行列を読込み、クラスタリングを行う。

手順2では、単語ベースのtf*idf行列を、概念ベースのtf*idf行列に変換する。同じ概念の単語をひとまとめにするために、WordNetを用いる。WordNetとは、単語がその意味でグルーピングされ、それらグループの関係性が記述された、概念辞書である。日本語WordNetはver0.9が2009年に公開され、無償で使用、複写、改変、頒布することが許可されている。また、Perl, Python, Java, Ruby等のフロントエンドも用意されている。今回は、Pythonを用いてWordNetを扱う。(このせいで文字コードに大変苦労させられたわけだが…。)WordNetPythonから使うには、Python2.6(Python3系は不可), sqlite3, WordNet-0.9が必要。

f:id:tkdmah:20130105151532p:plain

Pythonのスクリプトは以下のように書ける。
・wndriver.py: WordNetの自作ドライバ(WordNetのPythonAPIであるwn.pyを利用)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wn
 
def getSenseId(argv):
  '''単語を引数とし、単語の概念IDのリストを返す関数'''
  words = wn.getWords(argv.decode('utf-8'))
  senseIds = [ ]
  if words:
    senses = wn.getSenses(words[0])
    for sense in senses:
      senseIds.append(sense[0])
  return senseIds

・convDocMatrix.py: 特徴行列を変換するメインモジュール

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wndriver 

# prepare file
fin = codecs.open('C:/LyricsWorkspace/DocMatrix/Mr.Children_single_utf8.txt','r',"utf_8")
fout = codecs.open('C:/LyricsWorkspace/DocMatrix/Mr.Children_single_utf8_rev.txt','w',"utf_8")
 
# function definition
def add(a,b):
  c = [ ]
  for i in range(len(a)):
    c.append(float(a[i])+float(b[i]))
  return c
 
# make lyricsList
line = fin.readline()
fout.write(line)
lyricsList = line[1:-3].split('" "')
  
# read lines and get senseids
docDic = {} 
while True:
  line = fin.readline()
  if not line:
    break
  tfidfList = line[:-2].split(' ')
  word = tfidfList[0]
  if not word:
    break 
  senseIds=wndriver.getSenseId(word[1:-1].encode('utf-8'))
  length = len(senseIds)
  for senseId in senseIds:
    if senseId in docDic:
      docDic[senseId] = add(docDic[senseId],[float(x)/length for x in tfidfList[1:]])
    else:
      docDic[senseId] = [float(x)/length for x in tfidfList[1:]]

# write file 
for key in docDic.keys():
  fout.write('"'+key+'" ')
  for score in docDic[key]:
    fout.write(str(score)+' ')
  fout.write('\r\n') 
fin.close()
fout.close()

参考ページ