辞書 追加 igo-ruby
以前はmecabを利用してプログラムを動かしていたけど、今回はrubyで形態素解析出来るライブラリがあったのでそれを利用することにしました。
ruby-igoでは辞書生成が出来ないのでjavaのigoを利用する
javaのIgoのサイト
http://igo.sourceforge.jp/
mecab...まぁ、コスト計算方法のサイト
http://www.mwsoft.jp/programming/munou/mecab_nitteretou.html
このサイトをメインに作業を行った。配布されてるプログラムをrubyに落としたら良いんだけど、そこまでの能力が無い。
http://blog.livedoor.jp/techblog/archives/65828235.html
他にも細々したサイト色々見たけど全部記録してなかったので上記3サイトだけ
作成したプログラムは継ぎ接ぎしただけだから修正したほうがいいかな〜っと思ってるけど。時間無いので後で?
以下作業-----
javaのIgoのサイトから
igo-0.4.4.jarを拾ってくるなりソースをビルドするなりする
naist-jdicは下からまたは別のところから
http://sourceforge.jp/projects/naist-jdic/downloads/48487/mecab-naist-jdic-0.6.3-20100801.tar.gz
tar xzvf mecab-naist-jdic-0.6.3-20100810.tar.gz cd mecab-naist-jdic-0.6.3-20100810 // naist-jdic.csvはそのままだと使用できないので利用出来る形式にする grep -v -E '^\"' naist-jdic.csv > naist-jdic.tmp; mv naist-jdic.tmp naist-jdic.csv cd ../ // -Xmx1000Mは環境によっては実行時にメモリ関係?でエラーがでるので必要に応じて入力する // java [コマンド] -cp igoファイル net.reduis.igo.bin.BuilDic 生成するフォルダ名 naistのフォルダ名 エンコード文字列 java -Xmx1000M -cp igo-0.4.3.jar net.reduls.igo.bin.BuildDic jdic mecab-naist-jdic-0.6.3-20100801 UTF-8
これで辞書生成は終了したからigo-rubyは普通に使えるんだけど、そのままの辞書じゃ使い勝手が悪いのでユーザ辞書を追加する
この際だからそのあたりもigo-rubyでやってしまおう!
プログラムでやってること
Arrayクラス拡張→データ確認用
Igoファイルも拡張 parseToNodeで文脈idとか返すようにした
node_cost は辞書生成するときの文書コスト計算用
# Arrayクラス拡張 あんまりよくないけど、これだけに利用するからいいかな class Array def dup_node srt = "WordId\tLeftId\tRightId\tCost\tSurface\tLength\tStart\tFeature\n" self.each do |s| srt << "#{s.word_id}\t#{s.left_id}\t#{s.right_id}\t#{s.cost}\t#{s.surface}\t#{s.length}\t#{s.start}\t#{s.feature}\n" end srt end end # 直接Igoを書きなおしてもいいけど、元ファイル弄りたくないからこれも拡張 module Igo class MorphemeToNode attr_accessor :surface, :feature, :cost, :is_space, :left_id, :length, :right_id, :word_id, :start def initialize(surface, feature, status) @surface = surface @feature = feature @cost = status.cost @is_space = status.is_space @left_id = status.left_id @right_id = status.right_id @length = status.length @word_id = status.word_id @start = status.start end end class Tagger attr_accessor :wdc, :unk, :mtx #コスト以外も返すようにする def parseToNode(text, result=[]) vn = impl(text, result) txt = text.unpack("U*") while vn surface = txt.slice(vn.start, vn.length).pack("U*") s = @wdc.word_data(vn.word_id) feature = NKF.nkf('-W16Lo --utf8', s) result.push(MorphemeToNode.new(surface, feature, vn)) vn = vn.prev end return result end #文章コスト計算用 def node_cost node left_id = 0 cost = 0 node.each do |n| cost += n.cost cost += mtx.link_cost(left_id, n.right_id) left_id = n.right_id end cost += mtx.link_cost(left_id, 0) cost - 745 end end end
まず辞書生成にwikiとはてなを利用します。
このあたりのことについてはgoogleで「mecab 辞書生成 wiki」などで調べればすぐに出てきます。
ファイルが無ければ下のコメントのチェックを外します。
file はnaist-jdic.csvのファイルパスを指定
file1,2 はwikiとはてなのファイル名
file3 は顔文字を登録する場合利用
translatorはひらがなかたかな変換用 はてなキーワードで利用する
まずnaist-jdic.csvから固有名詞, 一般の文字を抜き出す
それを文字列の長さごとに文書生成コストを足していき 文書数で割る
#ファイルが無ければチェックを外す #はてなとwikiのurl #wiki = "http://dumps.wikimedia.org/jawiki/latest/jawiki-latest-all-titles-in-ns0.gz" #hatena = "http://d.hatena.ne.jp/images/keyword/keywordlist_furigana.csv" #open(file1, "w+"){ |f| f.puts open(wiki, 'rb'){|sio| Zlib::GzipReader.wrap(sio).read}} #open(file2, "w+"){ |f| f.puts open(hatena).read.toutf8} #保存するファイル名 file = "naist-jdic.csv" #ファイル先を記述 file1 = "wiki.csv" file2 = "hatena.csv" file3 = "kaomozi.txt" def translator(from, to) lambda {|str| str.tr(from, to)} end upto = translator("a-z", "A-Z") downto = translator("A-Z", "a-z") hira2kata = translator("ぁ-ん", "ァ-ン") kata2hira = translator("ァ-ン", "ぁ-ん") #平均値テーブルを作成するための固有名詞のものを抜きだす list = [] open(file).each do |line| text = line.toutf8.strip.split(",") list << text if text[5] =~ /固有名詞/ && text[6] =~ /一般/ end #文字列の長さごとにコストを計算 #ここの部分もっとうまい書き方したい(´・ω・`) なんかきもち悪い cost = [] list.each do |l| s = l[0].size cost[s] ||= [] cost[s][0] ||= 0 cost[s][0] += l[3].to_i cost[s][1] ||= 0 cost[s][1] += 1 end #各コストの平均を計算 cost.map!{|c|c[0] /= c[1] if c != nil}
はてなキーワードから辞書生成用
平均値テーブルに無い単語は2850に設定している
17文字以上になると生成コストはこの値よりもあまり下がらないため
open(file2).each do |line| title_list = line.split("\t") next if title_list.length < 2 title = title_list[1] title.strip! furigana = title_list[0] furigana.strip! katakana = hira2kata.call(furigana) # 登録したくないものをスキップ next if title =~ /[\+\-\.\$\(\)\?\*!"'_,]+/ next if title =~ /^[0-9\-]+$/ next if title =~ /^h?ttp/ next if title =~ /[0-9]{4}(\/|\-)[0-9]{2}(\/|\-)[0-9]{2}/ next if title =~ /[0-9]{4}年/ next if title =~ /[0-9]{1,2}月[0-9]{1,2}日/ # 制御文字、HTML特殊文字が入ったものは外す next if title =~ /[[:cntrl:]]/ next if title =~ /\&\#/ # タイトルの長さ len = title.split(//u).length # スコア計算 score = tagger.node_cost(tagger.parseToNode(title)) * 0.7 if cost[len] == nil score2 = 2850 else score2 = cost[len] end score = score2 if score > score2 # 3文字より大きい場合だけ if len > 3 mecab.puts "#{title},1360,1360,#{score.to_i},名詞,固有名詞,一般,*,*,*,#{title},#{katakana},#{furigana},はてなキーワード," end end
open(file1).each do |title| title.strip! # 登録したくないものをスキップ next if title =~ /[\+\-\.\$\(\)\?\*!"'_,]+/ next if title =~ /^[0-9\-]+$/ next if title =~ /^h?ttp/ next if title =~ /[0-9]{4}(\/|\-)[0-9]{2}(\/|\-)[0-9]{2}/ next if title =~ /[0-9]{4}年/ next if title =~ /[0-9]{1,2}月[0-9]{1,2}日/ # 制御文字、HTML特殊文字が入ったものは外す next if title =~ /[[:cntrl:]]/ next if title =~ /\&\#/ # タイトルの長さ len = title.split(//u).length #スコア計算 score = tagger.node_cost(tagger.parseToNode(title)) * 0.7 if cost[len] == nil score2 = 2850 else score2 = cost[len] end score = score2 if score > score2 # 3文字より大きい場合だけ if len > 3 mecab.puts "#{title},1360,1360,#{score.to_i},名詞,固有名詞,一般,*,*,*,#{title},*,*,wikipedia," end end
顔文字辞書生成用
記号とか登録するのでフィルタはかけないけど
コンマが含まれる単語はピリオドするなりスキップするなりする
顔文字の場合記号ばかり続いたりすると文章コストが-400000とかになってshort integerに変換出来ないので
マイナス32778以下の数値の場合は以下の計算式を入れる
score = [-32678.0 ,-400 *(title.size**1.5)].max.to_i
もう一つ計算式があったけどこれ↓
score = [-32768.0, (6000 - 200 *(title.size**1.3))].max.to_i
顔文字の場合はうまく行かなかったので上の方を選択した
後は他と一緒
open(file3).each do |line| title_list = line.split("\s") title_list[1].gsub!(",",".") title = title_list[1] title.strip! furigana = title_list[0] furigana.strip! katakana = hira2kata.call(furigana) #スコア計算 score = tagger.node_cost(tagger.parseToNode(title)) * 0.7 len = title.size if cost[len] == nil score2 = 2850 else score2 = cost[len] end score = score2 if score > score2 if score < -32678 score = [-32678.0 ,-400 *(title.size**1.5)].max.to_i end # 3文字より大きい場合だけ if len > 3 mecab.puts "#{title},5,5,#{score.to_i},名詞,記号,一般,*,*,*,#{title},#{katakana},#{furigana},顔文字," end end
すべてをつなぎ合わせたものがしたのが↓のファイルだけど、リファクタしてないから気持ちが悪い。
上のファイルを実行するとmecab.csv(ファイル名が悪いけど気にしない)
grep -v -E '^\"' mecab.csv > mecab.tmp; mv mecab.tmp mecab.csv mv mecab.csv mecab-naist-jdic-0.6.3-20100801 java -Xmx1000M -cp igo-0.4.3.jar net.reduls.igo.bin.BuildDic jdic mecab-naist-jdic-0.6.3-20100801 UTF-8 // 文字コード関係でエラーが出たら nkf -w --overwrite ファイル名 などのコマンドで変換する
はい、まとめ完了!
顔文字は適当なところから集めまくる。