MENU

テキストデータを数値で表現し、データベースを作成する(BoW)

# 全てのテキストを巡回して単語データベースを作成する
import os, glob
import MeCab
import numpy as np
import pickle

# 保存ファイル名
savefile = "./ok-spam.pickle"
# MeCabの準備 --- (*1)
# 変数の準備 --- (*2)
word_dic = {"__id": 0} # 単語辞書
files = # 読み込んだ単語データを追加する

# 指定したディレクトリ内のファイル一覧を読む --- (*3)
def read_files(dir, label):
# テキストファイルの一覧を得る
files = glob.glob(dir + '/*.txt')
for f in files:
  read_file(f, label)

# ファイルを読む --- (*4)
def read_file(filename, label):
words =
# ファイルの内容を読む
with open(filename, "rt", encoding="utf-8") as f:
text = f.read()
files.append({
"label": label,
"words": text_to_ids(text)
})

# テキストを単語IDのリストに変換
def text_to_ids(text):
# 形態素解析 --- (*5)
word_s = tagger.parse(text)
words =
# 単語を辞書に登録 --- (*6)
for line in word_s.split("\n"):
if line == 'EOS' or line == '': continue
word = line.split("\t")[0]
params = line.split("\t")[4].split("-")
hinsi = params[0] # 品詞
hinsi2 = params[1] if len(params) > 1 else '' # 品詞の説明
org = line.split("\t")[3] # 単語の原型
# 助詞・助動詞・記号・数字は捨てる --- (*7)
if not (hinsi in ['名詞', '動詞', '形容詞']): continue
if hinsi == '名詞' and hinsi2 == '数詞': continue
# 単語をidに変換 --- (*8)
id = word_to_id(org)
words.append(id)
return words

# 単語をidに変換 --- (*9)
def word_to_id(word):
# 単語が辞書に登録されているか?
if not (word in word_dic):
# 登録されていないので新たにIDを割り振る
id = word_dic["__id"]
word_dic["__id"] += 1
word_dic[word] = id
else:
# 既存の単語IDを返す
id = word_dic[word]
return id

# 単語の頻出頻度のデータを作る --- (*10)
def make_freq_data_allfiles():
y =
x =
for f in files:
y.append(f['label'])
x.append(make_freq_data(f['words']))
return y, x

def make_freq_data(words):
# 単語の出現回数を調べる
cnt = 0
dat = np.zeros(word_dic["__id"], 'float')
for w in words:
dat[w] += 1
cnt += 1
# 回数を出現頻度に直す --- (*11)
dat = dat / cnt
return dat

# ファイルの一覧から学習用のデータベースを作る
if __name__ == "__main__":
read_files("ok", 0)
read_files("spam", 1)
y, x = make_freq_data_allfiles()
# ファイルにデータを保存
pickle.dump([y, x, word_dic], open(savefile, 'wb'))
print("単語頻出データ作成完了")

このプログラムではスパムのテキストデータと非スパムのテキストデータからBoWと言う手法で文章中にどんな単語があるのか数値で表しデータベースとして外部に保存すると言うプログラムである。

 

このプログラムの準備に関しては

hanamichi-sukusuku.hatenablog.com

こちらの記事で紹介しています。

 

ファイルの一覧から学習用のデータベースを作る

if __name__ == "__main__":
  read_files("ok", 0)
  read_files("spam", 1)
  y, x = make_freq_data_allfiles()
  # ファイルにデータを保存
  pickle.dump([y, x, word_dic], open(savefile, 'wb'))
  print("単語頻出データ作成完了")

 

read_files()関数で単語にIDを振ったリストデータの作成と指定したディレクトリからファイルを読み込んでいる。

 

そして、make_freq_data_allfiles()関数で単語の出現頻度のデータを作成する。yはラベルデータ、xは出現頻度のデータ。

 

pickle.dump()で変数y,xに格納されているラベルデータと出現頻度のデータとword_dicに格納されている単語とその単語にふられているIDのデータを保存して学習用のデータベースの作成を行っている。

 

read_files()関数、指定したディレクトリ内のファイル一覧を読み込む

def read_files(dir, label):
 # テキストファイルの一覧を得る
 files = glob.glob(dir + '/*.txt')
 for f in files:
  read_file(f, label)

 

引数のdirはファイル名、labelは非スパムファイルが0、スパムファイルが1を渡している。

 

glob.glob()で引数に指定したディレクトリから.txtとマッチするファイル名を全て取得している。

for f in files:で取得したファイル名のリストから一つずつファイル名を取得し、read_file()関数でファイルを読み込んでいる。

 

read_file()関数、ファイルの読み込み、全体で使える変数filesにファイルのラベルデータとIDを単語ごとに振り分けその単語のIDで文章が表現されているリストデータを追加

def read_file(filename, label):
  words =
  # ファイルの内容を読む
  with open(filename, "rt", encoding="utf-8") as f:
  text = f.read()
  files.append({
    "label": label,
    "words": text_to_ids(text)
  })

 

with openでファイルの読み込み。

f.read()でファイルの中身の文字列を取得。

files.append()で全体で使用できる変数のfilesにラベルデータとtext_to_ids()関数でIDを単語ごとに振り分け、その単語のIDで文章が表現されているリストデータを追加する。

 

text_to_ids()関数、テキストを単語IDのリストに変換

def text_to_ids(text):
 # 形態素解析 --- (*5)
 word_s = tagger.parse(text)
 words =
 # 単語を辞書に登録 --- (*6)
 for line in word_s.split("\n"): 
  if line == 'EOS' or line == '': continue
  word = line.split("\t")[0]
  params = line.split("\t")[4].split("-")
  hinsi = params[0] # 品詞
  hinsi2 = params[1] if len(params) > 1 else '' # 品詞の説明
  org = line.split("\t")[3] # 単語の原型
  # 助詞・助動詞・記号・数字は捨てる --- (*7)
  if not (hinsi in ['名詞', '動詞', '形容詞']): continue
  if hinsi == '名詞' and hinsi2 == '数詞': continue
  # 単語をidに変換 --- (*8)
  id = word_to_id(org)
  words.append(id)
 return words

 

ファイルから読み込んだ文字列が格納されている変数textを引数として受け取り、tagger.parse()で形態素解析

word_s.split("\n")で改行する箇所で区切って単語をそれぞれ取得し、処理していく。

line.split("\t")ではタブ区切りの文字列をリストに変換している。タブ区切りは文字列に空白のスペースで区切っているような文字列のこと。

 

line.split("\t")[0]では単語の表層形。

line.split("\t")[4].split("-")では品詞やどんな品詞かや活用形の情報を取得し"-"で繋がっているものを区切り、リストに変換している。

hinsi2 = params[1]の部分では変数paramsに格納されている要素数が1より大きい場合、hinsi2 = params[1]でどんな品詞かを格納している。名詞なら普通名詞とかの品詞の説明にあたる品詞情報。

 

そして、if文で品詞が名詞、動詩、形容詞ではない場合と品詞が名詞でも数字であれば処理を飛ばしている。

 

id = word_to_id(org)ではword_to_id()関数で渡した単語の原型のIDを取得しており、単語辞書にその単語のIDがなければ追加しそのIDを返している。

 

words.append(id)で文章をIDで表現したリストを呼び出し元に返す。

 

word_to_id()関数、単語をIDに変換

def word_to_id(word):
  # 単語が辞書に登録されているか?
  if not (word in word_dic):
  # 登録されていないので新たにIDを割り振る
   id = word_dic["__id"]
   word_dic["__id"] += 1
   word_dic[word] = id
  else:
  # 既存の単語IDを返す
   id = word_dic[word]
  return id

 

引数で渡されているのは単語の原型。

if not (word in word_dic):で単語辞書にその単語が登録されていない場合の時、辞書にその単語がキーで現在の要素数の次の値をそのキーの値として辞書に追加している。

word_dic["__id"]では現在の最後のID、つまり辞書に登録されている要素数の値。

word_dic["__id"]で新しく辞書が追加されるので値を1足す。

word_dic[word] = id で辞書にまだ登録されていない単語をキー、現在の要素数に1足した数を値に持つ要素を追加。

 

else: では既に辞書に登録されていた時、その単語のIDを取得し呼び出し元に返す。

 

if __name__ == "__main__":
 read_files("ok", 0)
 read_files("spam", 1)
 y, x = make_freq_data_allfiles()
  # ファイルにデータを保存
 pickle.dump([y, x, word_dic], open(savefile, 'wb'))
   print("単語頻出データ作成完了")

read_files()関数で辞書の登録などが終わり、次にmake_freq_data_allfiles()関数で単語の出現頻度のデータを作成していく。yはラベルデータ、xは単語の出現頻度のデータ。

 

make_freq_data_allfiles()関数、単語の出現頻度のデータを作成する

def make_freq_data_allfiles():
 y =
 x = []
 for f in files:
  y.append(f['label'])
  x.append(make_freq_data(f['words']))
 return y, x

 

filesにはラベルデータと単語をIDで表現したデータが格納されており、単語をIDで表現しているデータをmake_freq_data()関数に渡し、各単語の出現回数から単語の総出現回数を割ることでその単語の出現頻度を示すデータを作成し、呼び出し元に返している。

 

make_freq_data()関数、単語の出現頻度を調べ単語の総出現回数を割ることで出現頻度のデータを作成。

def make_freq_data(words):
# 単語の出現回数を調べる
cnt = 0
dat = np.zeros(word_dic["__id"], 'float')
for w in words:
dat[w] += 1
cnt += 1
# 回数を出現頻度に直す --- (*11)
dat = dat / cnt
return dat

 

変数filesからデータを取得しその中の単語をIDで表現しているデータ(関数の引数words)を元に出現頻度のデータを作成していく。

np.zeros()で単語辞書の要素数だけ0で初期化された配列を作成。floatはデータ型、省略可能。

新しく作成した配列datの要素にwordsから要素を一つずつ取得し、カウントしていく。

cntはIDの総数。

出現したIDの回数が格納されたdatから出現したIDの総数を割り、結果を呼び出し元に返す。

 

if __name__ == "__main__":
 read_files("ok", 0)
 read_files("spam", 1)
 y, x = make_freq_data_allfiles()
 # ファイルにデータを保存
 pickle.dump([y, x, word_dic], open(savefile, 'wb'))
   print("単語頻出データ作成完了")

最後にplick.dump()でy(ラベルデータ)、x(単語の出現頻度のデータ)、word_dic(単語をIDで表現しているデータ)を保存。