Ruby+mecabで形態素解析 (with Cygwin)
せっかくRubyでTwitterを使えるようになったし、人工無能的なものを作ったら楽しいかなと思ったので、まずは形態素解析を使ってツイートとか文章を品詞単位に分解できるようにしてみた。形態素解析にはいくつかエンジンがあるが、mecabを利用することに。
mecab本体のインストールは、こちらのサイトを参考にさせていただいた。0.98じゃないとエラーが起きる模様。辞書はIPAのものを選んでおいた。
* Cygwin - 形態素解析 MeCab をインストール! - mk-mode BLOG
これでCygwinのターミナルから、形態素解析をできるようになった。例えば、ターミナルに。
$ mecab 形態素解析を使ってツイートとか文章を品詞単位に分解できるようにしてみた。
と打ってEnterすれば、
形態素解析を使ってツイートとか文章を品詞単位に分解できるようにしてみた。 形態素 名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ 解析 名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 使っ 動詞,自立,*,*,五段・ワ行促音便,連用タ接続,使う,ツカッ,ツカッ て 助詞,接続助詞,*,*,*,*,て,テ,テ ツイート 名詞,固有名詞,一般,*,*,*,* とか 助詞,並立助詞,*,*,*,*,とか,トカ,トカ 文章 名詞,一般,*,*,*,*,文章,ブンショウ,ブンショー を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 品詞 名詞,一般,*,*,*,*,品詞,ヒンシ,ヒンシ 単位 名詞,一般,*,*,*,*,単位,タンイ,タンイ に 助詞,格助詞,一般,*,*,*,に,ニ,ニ 分解 名詞,サ変接続,*,*,*,*,分解,ブンカイ,ブンカイ できる 動詞,自立,*,*,一段,基本形,できる,デキル,デキル よう 名詞,非自立,助動詞語幹,*,*,*,よう,ヨウ,ヨー に 助詞,格助詞,一般,*,*,*,に,ニ,ニ し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ て 助詞,接続助詞,*,*,*,*,て,テ,テ み 動詞,非自立,*,*,一段,連用形,みる,ミ,ミ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 。 記号,句点,*,*,*,*,。,。,。 EOS
こんな感じで結果を表示してくれる。
これをRubyで使うには、MeCab-rubyというラッパーが必要になる。こちらのインストールは、一部Makefileをいじらないと参照先が見つからなくてエラーを吐く。
$ wget http://mecab.googlecode.com/files/mecab-ruby-0.98.tar.gz $ tar zxvf mecab-ruby-0.98.tar.gz $ cd mecab-ruby-0.98 $ ruby extconf.rb
ここまでしたら、Makefileを開き、以下の修正を加える。
- ldflags = -L. → 末尾に 「-L/usr/local/lib」を追加
- LIBS = $(LIBRUBYARG_SHARED) -lstdc++ -ldl -lcrypt →末尾に「-lmecab」を追加
この修正は、以下のサイトを参考にした。
Cygwin1.7にMeCab0.98+MeCab-rubyをインストールしたメモ - <s>gnarl,</s>技術メモ”’<marquee><textarea>¥
これでmake, make installすれば、無事インストールできてRubyでもmecabを使えるようになる。
#!/usr/bin/ruby -Ku require 'MeCab' puts 'i' me = MeCab::Tagger.new() puts me.parse('形態素解析を使ってツイートとか文章を品詞単位に分解できるようにしてみた。')
これを実行すれば、上に上げたのと同じような形態素解析結果が出てくる。
今回はここまで。
RubyでTwitterのUserStreamを使う
前回紹介したRuby Twitter Gemで、ターミナルにテキストでTLを表示するクライアントを作ろうとした。最初に作ったコードは以下のような感じ。
# encoding: utf-8 require 'rubygems' require 'twitter' client = Twitter::REST::Client.new do |config| config.consumer_key = API_KEY config.consumer_secret = API_SECRET config.access_token = ACCESS_TOKEN config.access_token_secret = ACCESS_TOKEN_SECRET end mytl = client.home_timeline #TLを取得 hist = [] while 1 update = mytl - hist #前回取得したTLとの差分 num = update.length - 1 num.downto(0) do |i| id = update[i][:id] # TweetのID。ふぁぼRT等に使用 username = update[i][:user][:screen_name] # 発言者ID contents = update[i][:text] # 発言内容 str = username + ":" + contents puts str end hist = mytl sleep(90) mytl = client.home_timeline end
90秒ごとにclient.home_timelineでTLを取得し、前回取得しなかったぶんを「ID:発言内容」という形式で表示できる。client.home_timelineで取得したTLは新しいものから古いものへと順になった配列になっているため、downtoを使うことで古いものから順に表示させるようにしている。
ここで問題になったのが、APIの制限だ。このgem(以下twitter gem)ではTLを取得するたびにAPIを使用する必要があり、リアルタイムに更新しようとすると15分に15回までというTwitterのAPIの制限に簡単に達してしまう。これを避けるために更新頻度を90秒間隔に落としているが、これでは応答が遅くなってしまう。また、client.home_timelineでは一度に20件しか取得できないようなので、フォロー数の多い場合はTLの取得漏れが起きることもある。
そこで、UserStreamを使える新たなgemを使う。こちらがそれ。
tweetstream/tweetstream · GitHub
これを使って、以下のようなコードでTLの表示をリアルタイムで行うことが出来る。
# 認証 TweetStream.configure do |config| config.consumer_key = API_KEY config.consumer_secret = API_SECRET config.oauth_token = ACCESS_TOKEN config.oauth_token_secret = ACCESS_TOKEN_SECRET config.auth_method = :oauth end # TL取得 streamclient = TweetStream::Client.new streamclient.userstream do |status| username = status[:user][:screen_name] contents = status[:text] id = status[:id] str = username + ":" + contents puts str end
認証部分はtwitter gemとよく似た方法でできる。gemのサイトに使用例がいくつかあるが、streamclient.userstreamの部分を色々変更してやれば、検索やリプに即座に反応するよなbotを作成することも容易だろう。
ただし、これはあくまでUserStreamを利用してツイートを取得するだけなので、botとしてつぶやいたりふぁぼったりといった動作をするにはtwitter gemを利用する必要がある。
以下にtweetstreamとtwitter gemを利用して、起動時に過去のツイート20件を取得し表示してからUserStreamによるリアルタイム更新を行い、特定のワードに反応してリプを飛ばすクライアント兼botのようなものの例を載せる。
# encoding: utf-8 require 'rubygems' require 'twitter' require 'tweetstream' require 'reply_judge.rb' # 反応するかどうかを判定 TweetStream.configure do |config| config.consumer_key = API_KEY config.consumer_secret = API_SECRET config.oauth_token = ACCESS_TOKEN config.oauth_token_secret = ACCESS_TOKEN_SECRET config.auth_method = :oauth end normalclient = Twitter::REST::Client.new do |config| config.consumer_key = API_KEY config.consumer_secret = API_SECRET config.access_token = ACCESS_TOKEN config.access_token_secret = ACCESS_TOKEN_SECRET end puts "Configuration Success" # twitter gemで初回更新 mytl = client.home_timeline 19.downto(0) do |i| id = mytl[i][:id] username = mytl[i][:user][:screen_name] contents = mytl[i][:text] str = username + ":" + contents puts str end # tweetstream使用 streamclient = TweetStream::Client.new streamclient.userstream do |status| username = status[:user][:screen_name] contents = status[:text] id = status[:id] str = username + ":" + contents puts str if reply_judge(username,contents) != 0 time = Random.new.rand 10.0 # time = 0.1 sleep(time) str = "@" + username + " foobar" normalclient.update(str, :in_reply_to_status_id => id) # リプ先のID指定 end end
RubyでTwitterにポスト(その2・解決)
前回どうもうまく行かなかったRuby Twitter gemだが、Ubuntuで最新版(v5.7.1)をインストールしてみたら、何故かうまくツイートできるようになった。
gemのバージョンアップによって、若干認証の書式が変わっていたりする。
まずはdev.twitter.comの"My application"から作ったアプリを開き、「API Keys」タブでAPI keyとAPI secretの文字列を表示させる。そして、実際につぶやくアカウントの認証を行う。認証には、
Ruby OAuth GEMを使うので、必要ならインストール。
このステップは、アプリの開発者とつぶやくアカウントが同じ場合、省略できる(API keyとAPI secretの下の方にあるAccess toke nとAccess secretをそのまま読む)。
require 'rubygems' require 'oauth' @consumer=OAuth::Consumer.new( "API key","API secret", { :site=>"https://api.twitter.com" }) @request_token=@consumer.get_request_token @request_token.authorize_url
"API key"と"API secret"のところに、先の画面に表示された文字列を入れて(引用符を忘れない!)このコードを実行すると、アカウント認証のためのURLが出てくるので、ブラウザ等で開いて実際につぶやきたいアカウントで認証を行う。すると、数字の列(PIN)が表示されるので、これをメモ。続いて以下のコードを実行する。「●PIN●」の位置には先ほどのPINを、こんどは引用符無しで入れる。
@access_token = @request_token.get_access_token(:oauth_verifier => ●PIN●) puts @access_token.token puts @access_token.secret
最初の文字列が「Access token」で、2番めが「Access token secret」になる。この文字列があれば、Rubyでつぶやくことが可能だ。
ここまでが、いわば認証のための準備。次は、いよいよ認証をして実際につぶやいてみる。この最初の認証の部分が、Twitter gemのバージョンアップに伴い若干変更されたので要注意。
require 'rubygems' require 'twitter' client = Twitter::REST::Client.new do |config| config.consumer_key = "API key" config.consumer_secret = "API secret" config.access_token = "Access token" config.access_token_secret = "Access token secret" end client.update("rubytest")
やはり引用符を忘れないように注意。
上手く行けば、認証を行ったアカウントで、「rubytest」とツイートされているはずだ。
実際につぶやく際のメソッドは、このサイトが参考になりそう。
http://route477.net/w/RubyTwitterJa.html
Graphillionと最長片道切符
一部で話題になってた「組み合わせ爆発のお姉さん」の動画、気付いたら新作が出ていた。
元動画はこちら。やたら壮大なスケールで、組み合わせ爆発の怖さがわかる。
『フカシギの数え方』 おねえさんといっしょ! みんなで数えてみよう! - YouTube
そしてこっちが新作、というかフォローみたいな動画。
Graphillion: 数え上げおねえさんを救え / Don't count naively - YouTube
25万年、ひたすら数え上げしていたお姉さんはいったいどうやって救われるのかと思って動画を見てみたら、特に新ストーリーとかではなく、ゆっくりボイスでグラフを扱うスクリプトの紹介をされていた・・・なんだ・・・
と思ったが、このGraphillion、Pythonのモジュールで、複雑なグラフも簡単な記述で扱えちゃうスグレモノらしい。この動画では電力ネットワークの例題が上げられているが、ほかにも東京近郊区間大回りの計算なんかもサクッとできてしまうようだ。この大回り計算は、その名もekillionとしてweb上に公開されていて、だれでも簡単に大回りの経路を列挙して眺めて楽しむことが出来る。
このekillionで遊びながら、これを「最長片道切符」の経路の算出に応用したら結構簡単に経路が出せるのではないかとかふと思った。
最長片道切符のルートの計算については、葛西隆也氏が整数計画法という数学的手法を用いて計算したり(
最長片道きっぷの経路を求める
)、近藤英明氏が整数計画法をExcelのソルバーに落としこむ(EXCELによる最長片道ルート探索)など、既にいくつかの例がある。
葛西氏は商用のソルバーによる計算を行っていたが、フリーのソルバーGLPKを使用する方法もサイト上で紹介されている。私もこれで経路を計算したことがあるが、当時所有のPCでは計算に非常に長い時間がかかり、ツール内部の理解も十分には出来なかった。整数計画法で問題を解くには、片道切符の発券条件を線形の「制約式」に置き換える必要があるが、その制約式の作り方がトリッキーで、自分で組み立てるのは困難であった。
Graphillionを使用すると、制約式を作る作業無しに最長片道切符の経路の探索ができ、問題の変更も比較的簡便にできるようなので、少し遊んでみると楽しそうだ。
RubyでTwitterにポスト(未完)
Cygwin上で動作するRubyのスクリプトプログラムの中で、Twitterに動作状況をポストできるbotを作ろうと思って、少し調べてみた。
RubyでどのようにTwitterを使おうか調べた所、とても簡単なライブラリがあることを知った。
Twitter by sferik
こちらがそのライブラリ(Rubyではこういったライブラリをgemというようだ)の作者におる説明。Rubyはほとんど書いたことないが、これを見る限りとても簡単に扱えそうであった。
(gemに関しての説明はhttp://www.ruby-lang.org/ja/libraries/が詳しい)
http://rubyinstaller.org/downloads/
gemのインストールは簡単で、Cygwinのコンソールやコマンドプロンプトから、
$ gem install twitter
と打つだけで、必要なものが一式まとめてインストールできる。
まずOAuthでアプリの認証をする(このへんは別の記事で)。そして、最初に紹介したサイトにあるように、認証キーの設定をしてポストにチャレンジしてみる。
実際につぶやく部分は1行で済んでおり、ごくごく簡単にTwitterを活用できそうだ。
require 'rubygems' require 'twitter' Twitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRET config.oauth_token = YOUR_OAUTH_TOKEN config.oauth_token_secret = YOUR_OAUTH_TOKEN_SECRET end Twitter.update("rubytest")
これを適当なファイル名で保存し、Cygwinから実行してみたが・・・
・・・残念ながら、下のようなエラーが出てきた。
/usr/lib/ruby/1.9.1/net/http.rb:800:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (Twitter::Error::ClientError) from /usr/lib/ruby/1.9.1/net/http.rb:800:in `block in connect' from /usr/lib/ruby/1.9.1/timeout.rb:69:in `timeout' from /usr/lib/ruby/1.9.1/timeout.rb:100:in `timeout' from /usr/lib/ruby/1.9.1/net/http.rb:800:in `connect' from /usr/lib/ruby/1.9.1/net/http.rb:756:in `do_start' from /usr/lib/ruby/1.9.1/net/http.rb:745:in `start' from /usr/lib/ruby/1.9.1/net/http.rb:1285:in `request' from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/adapter/net_http.rb:75:in `perform_request' (以下略)
Twitterは更新されていなかった。
エラーの文で調べてみると、どうも、SSL通信がうまくいっていないようだ。
SSL通信を行うために必要な証明書関連に問題があるらしい。
SSLの認証を切ったらどうなるか試すため、
require 'openssl' OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
という宣言を入れてみたら・・・
/usr/lib/ruby/gems/1.9.1/gems/twitter-4.8.1/lib/twitter/response/raise_error.rb:21:in `on_complete': Could not authenticate you (Twitter::Error::Unauthorized) from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:9:in `block in call' from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:63:in `on_complete' from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:8:in `call' from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/request/url_encoded.rb:14:in `call' (以下略)
Could not authenticate you・・・そうですか・・・
これはうまく行かなさそうなので、大人しく証明書を何とかすることに。
まず、証明書がどこにあるのかをirbで調べてみた。
irb(main):001:0> require 'openssl' => true irb(main):002:0> p OpenSSL::X509::DEFAULT_CERT_FILE "/usr/ssl/cert.pem" => "/usr/ssl/cert.pem"
このディレクトリを確認したら、なんと"cert.pem"などというファイルが存在しない!
ここでTwitterのSSL通信について、以下のサイトなどを参考にしてみた。
https://dev.twitter.com/docs/security/using-ssl
http://d.hatena.ne.jp/otherworld/20101020/1287551203
http://d.hatena.ne.jp/kesikaran/20100213/1266069166
あまりしっかり理解していないが、"VeriSign Class 3 Extended Validation"なる証明書が必要とされている模様。
そして、この証明書(と他色々な証明書がセットになったファイル)は
http://curl.haxx.se/ca/cacert.pem
からダウンロードできる模様。
ここから証明書を引っ張ってきて、あるべきディレクトリに放り込んだ。そしてbotを実行すると・・・
/usr/lib/ruby/gems/1.9.1/gems/twitter-4.8.1/lib/twitter/response/raise_error.rb:21:in `on_complete': Could not authenticate you (Twitter::Error::Unauthorized) from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:9:in `block in call'
またかよ!ということは、この"Could not authenticate you"というエラーは、証明書関連とは別のところか。
認証がうまくいってないような気がする。
http://stackoverflow.com/questions/15146767/twitter-could-not-authenticate-you-error
http://stackoverflow.com/questions/17060459/how-do-i-retrieve-a-status-from-twitter-with-the-oauth-gem-directly
https://dev.twitter.com/docs/auth/oauth/single-user-with-examples#ruby
http://doruby.kbmj.com/daoka_tips/20100302/ruby_oauth_
ここらへんを見て、頭を抱え中。
gnuplotのPNG出力でアンチエイリアス
gnuplot(Ver4.6)で関数やデータをプロットして、どこかに貼り付けたいというとき、
set terminal XXX set output "(filename)" replot
と打てばいろいろな形式で保存が可能(Windowsなら戻すにはwxtと打てばOK)。
XXXにはだいたい拡張子を入れれば、あまり考えなくても好きな形式で出力できるのは魅力だが、PNGで普通に出力しようとすると以下のような、アンチエイリアスのかかってない画像が出てくる。
(コマンド)
set term png size 400,300 set output "normal.png" plot sin(x),0.5+0.3*cos(x), -sin(x/2+1)
(結果)
見栄えの良いグラフを貼り付けるには、PNGではなくベクタ形式の画像(MS Office系ならemf形式、TeXならepsが良い感じ)にするのが手っ取り早いが、どうしてもラスタ形式で出力したいということもあって悩ましかった。
そんな時は、"png"とは別の"pngcairo"という出力ターミナルを使えばいい。
(コマンド)
set term pngcairo size 400,300 set output "cairo.png" rep
(結果)
こんな感じで、アンチエイリアスのかかったきれいな画像が出力される。その分ファイルサイズも4倍近くに大きくなるが。
GCC(G++)でC++をコンパイルすると動作が止まった
GCCには、C++のコンパイラ(g++)も含まれている。行列演算とかでC++のライブラリを使ってみたくなったのでテストしてみた。
#include <iostream> using namespace std; int main(){ double k=1.0138100; cout << "テスト" << endl; cout << "Hello, C++!!" << endl; cout << k << endl; return 0; }
これをコマンドプロンプトから、
>g++ hello.cpp -o hello.exe >hello
とコンパイルして実行してみたところ、
テスト Hello, C++!!
とまで表示されたはいいものの、「hello.exe は動作を停止しました」というWindowsのエラーメッセージが表示され止まってしまった。何故かと調べていた所、どうやらMinGWのライブラリのリンクがうまく行っていないために、Windowsでは実行できなかったようだ。ちなみに、MSYSのほうからコンパイルして実行すると、
>g++ -static hello.cpp -o hello_s.exe >hello_s テスト Hello, C++!! 1.01381 >
と問題なく実行できて終了した。
これを解決するためには、g++のオプションで「-static」を付け、ライブラリを静的にリンクする必要があるようだ。このオプションを入れると、たしかにコマンドプロンプトからでも最後まで正常に作動するようになった。
ただしこれはMinGWに含まれているライブラリがexeファイル内にリンクされるらしく、ファイルサイズが増えてしまう。実際、このプログラムもstaticオプションなしでは26kBだったが、staticをつけることで1,278kBにもサイズが膨れ上がってしまった。でも、動かないよりはマシかと考え、当面はこれで動かすことに。
ちなみに、このプログラムで動作を止めた犯人は、
cout << k << endl;
の部分らしく、ここを削除したらstatic無しでもエラーを吐かなくなった。とはいえ、数値計算で途中経過をディスプレイに出せないのは痛い。