アーカイブ
opencv-haartrainingの進行状況をtwitterに流すbot
OpenCVをソースからビルドするとhaarlike分類器(顔認識などに使われているやつ)の学習ツールが手に入るんだけど、たくさんのマシンでたくさん学習させているとそれぞれの進行状況をチェックするのが面倒になってくる。
でも、入力した画像ファイルが壊れていると学習が強制終了してしまったり、データがばらつきすぎてて収束しなくてあきらめて終了されたりするので、プロセスが死んでいたらパラメータを直してすぐやり直しをさせたい。学習中は予断を許さない状況が続く。
なので、進行状況を監視してtwitterアカウントshokai_logにpostするbotを作った。
5分間隔でopencv-haartrainingの作業ディレクトリとプロセスが生きているかをチェックする。
学習stageが進む毎に適当に通知し、プロセスが強制終了していた場合は激しくreplyしてくれる。これで安心して寝れる。OpenCV1.0/2.0両方対応。
一見何言ってるのかわかりにくいpostもあるが、「ドドドド」だったらstage4が終わったという意味。

辞書はコード内にある。
第2引数にopencv-haartrainingの-dataオプションで渡した「結果の書き出し先ディレクトリ名」を指定する。第3引数は無しでもいいが、twitter投稿の末尾にメモを付けられる。複数のマシンで実行していてどれの進行状況かわからなくなる時は、マシンの名前を入れておけばいい。
ruby tweet-haartraining.rb /Users/sho/path/to/training/dir/ "Macbook黒"
tweet-haartraining.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'twitter'
# setting
USER = 'your-account'
PASS = 'your-password'
INTERVAL = 300 # sleep sec
YOU = 'shokai' # 時々replyしてくる nilでreplyなし
NOPOST = false # debug用
def post(message)
return if !message
message = "@#{YOU} #{message}" if rand(3)<1 if !(message =~ /@#{YOU}/) && rand(2)<1
puts message + "\t" + Time.now.to_s
return if NOPOST
httpAuth = Twitter::HTTPAuth.new(USER, PASS)
tw = Twitter::Base.new(httpAuth)
tw.update(message)
end
if ARGV.size < 1
puts '結果が出力されるディレクトリへのパスが必要です。メモも付けられます(オプション)'
puts 'e.g. ruby tweet-haartraining.rb /path/to/haar/training/dir/ "研究室の学習用パソコン"'
exit 1
end
puts path = ARGV.shift
memo = ARGV.shift || ""
dir_path = path
if path =~ /\/$/
dir_path = path
xml_path = path[0...path.size-1]+'.xml'
else
dir_path = path+'/'
xml_path = path+'.xml'
end
if stage_p = Dir.glob(dir_path+'*').delete_if{|i| File::ftype(i) != 'directory'}.map{|i| i.split(/\//).last.to_i}.max
post "ステージ#{stage_p}から開始" + " " + memo
else
messages = ["開始。",
"はじめ",
"起床",
"おきた",
"start",
"スタートしました",
"hello world",
"hello work",
"はじめますわっ",
"スタンバイレディ セタップ"]
post messages[rand(messages.size)] + " " + memo
end
while true do
sleep INTERVAL
stage = Dir.glob(dir_path+'*').delete_if{|i| File::ftype(i) != 'directory'}.map{|i| i.split(/\//).last.to_i}.max
if File.exists? xml_path
messages = ["全行程完了(ステージ#{stage})。お疲れ様でした。",
"全部オワタ(#{stage})",
"修了しました",
"寝る。#{stage}時に起きる。",
"終わったので、#{stage}時に帰ります",
"全段階完了しました。データを回収し、電源を落としてください(#{xml_path.split(/\//).last})",
"全ステージ完了しました(#{xml_path.split(/\//).last})",
"ⓢⓤⓨⓐⓡⓘ"]
post "@#{YOU} " + messages[rand(messages.size)] + " " + memo
exit 0
end
if nil == `ps aux | grep opencv-haartraining`.split(/[\r\n]/).delete_if{|m|m=~/grep opencv-haartraining/}.first
messages = ["#{stage}段階目まで来たけど異常終了したかも",
"落ちてる",
"ERROR! haartraining is not working. please restart \(^o^)/",
"異常終了",
"異常です",
"動いてないっぽい・・・",
"死んだかも",
"だめっぽい・・",
"おい、異常終了してるぞ",
"冒 険 の 書 (#{stage}) は 消 え ま し た",
"おお、死んでしまうとは情けない",
"\(^o^)/"*stage,
"ピッコロの気が消えた",
"なん・・だと・・",
"#{stage}面でピチュった"]
post "@#{YOU} " + messages[rand(messages.size)] + " " + memo
sleep INTERVAL*2
next
end
next if stage == stage_p or stage == nil
stage_p = stage
messages = ["#{stage}段階目まで進みました",
"バリバリです(stage#{stage})",
"ばっちりですわっ",
"------ここまで読んだ(#{stage})------",
"がんばってます(#{stage})",
"stage #{stage}",
"ステージ#{stage}なう",
"now finished stage#{stage}.",
"よし!ステージ#{stage}まで終わった!!!",
"うわ"+"あ"*stage,
"ド"*stage,
"ゴ"*stage,
"ゴ"+"ー"*stage,
"もりもり",
"ふむふむなるほど"+"・"*stage,
"頭が"*stage+"おかしくなりそうだ",
"もういや",
"無理"*stage,
"ズザ"+"ー"*stage,
"帰りたい",
"まだ#{stage}段階目だ",
"もうstage#{stage}まで終わった。超はやい",
"もうstage#{stage}まで終わった",
"stage#{stage}まで終わった",
"stage#{stage}まで終わったし",
"ククク・・遂に#{stage}界までまで昇ってきたか・・・",
"ⓢⓤⓨⓐ"*stage]
post messages[rand(messages.size)] + " " + memo
end
Twitterの地名なうbotを全blockするOAuthアプリ
http://shokai.mag.keio.ac.jp/block_nowbots/から使える。
(8月15日:URL変更しました)
とりあえずSinatraとOAuthの組み合わせを試してみたかったので、DBは使わないものを作りたかった。取得したOAuthのtokenなどはその場で捨てている。blockコマンドを送る権限だけを一時的に委譲してもらう。
以下技術的なことのメモ。
■OAuthアプリの登録
まずhttp://twitter.com/oauth_clientsでアプリを登録し、consumer keyとconsumer secretを取得する。
で、下のコード中のCONSUMER_KEY, CONSUMER_SECRETを書き換える。
■必要なライブラリ
必要なgemをインストールする。最新版にした。
sudo gem install oauth twitter sinatraそれぞれ0.3.5, 0.6.13, 0.9.4がインストールされた。
twitterは内部でoauthに依存していて、oauthはバージョン毎に関数がけっこう変わっている。このバージョンの組み合わせなら動く。
■動かす
そして起動。
ruby block-nowbots.rb -p 2692 -s mongrelthinだと複数クライアントから同時にアクセスした時1クライアントずつしか対応してくれなかったんだけど、mongrelは全クライアントに同時に応答してくれた。mongrelにいつのまにかそういう機能がついたのか、sinatraのバグでthinがthread処理されないのかはよくわからない。
sinatraもrailsと同じくデプロイまわりを工夫した方がよさそう。passenger使うのがいいのかな?
参考:
- OAuth関連
- Sinatra関連
block-nowbots.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
require 'oauth'
require 'twitter'
HOST_AND_PORT = "localhost:2692"
CONSUMER_KEY = "your-consumer-key"
CONSUMER_SECRET = "your-consumer-secret"
BOTS = 'nishinipporinow,nippori_now,kanda_now,tabata_now,sugamo_now,komagome_now,shinokubo_now,mejiro_now,takadanobabanow,okachimachi_now,uguisudani_now,ikebukuro_now,otsuka_now,akiba_now,tokyo_now,harajuku_now,shibuya_now,shibuya_now,yoyogi_now,shinjuku_now,ebisu_now'
set :sessions, true
def consumer
OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET,
:site => "http://twitter.com")
end
template :layout do
'<html>
<body>
<h1>地名なうbotを全blockするOAuthアプリ</h1>
<%= yield %>
<hr style="margin-top: 100px" />
<a href="http://shokai.org">http://shokai.org</a><br /><a href="http://shokai.org/blog/archives/4698">解説</a>
</body>
</html>'
end
get '/' do
@request_token = consumer.get_request_token(:oauth_callback => "http://#{HOST_AND_PORT}/auth")
session[:request_token] = @request_token.token
session[:request_token_secret] = @request_token.secret
erb %{
<p><%= BOTS %><br />をblock設定します</p>
<p>OAuth認証してください</p>
<a href="<%= @request_token.authorize_url %>">認証する!</a>
}
end
get '/auth' do
@request_token = OAuth::RequestToken.new(consumer,
session[:request_token],
session[:request_token_secret])
@access_token = @request_token.get_access_token({},
:oauth_token => params[:oauth_token],
:oauth_verifier => params[:oauth_verifier])
session[:access_token] = @access_token.token
session[:access_token_secret] = @access_token.secret
erb %{
<p>認証成功</p>
<p><a href="/block_now_bots">地名なうbotをblockする</a>(全部で<%= BOTS.split(",").size*3 %>秒くらいかかる)</p>
<a href='/'>戻る</a>
}
end
get '/block_now_bots' do
oauth = Twitter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
oauth.authorize_from_access(session[:access_token], session[:access_token_secret])
twit = Twitter::Base.new(oauth)
BOTS.split(",").each{|bot|
twit.block(bot)
puts "block #{bot}"
sleep 3
}
erb %{
<p>たぶんblockしました</p>
<p><a href="http://twitter.com/yamanote_now/following">確認</a></p>
}
end
buzztterでtimelineを作るYahoo Pipes
buzztter.comでtwitter上の最近の頻出単語の統計を出してくれている。
頻出語の上位5単語で検索してタイムラインを作るYahoo Pipesを作った。
→ Pipes: buzztter timeline
1週間ぐらい見ているが、だいたいみんなテレビとかゲームとか選挙とか芸能人の話題、眠気や月曜日出勤したくないなどの体調の問題を喋っている。ワイドショー的。
上位3語や10語も試したけど、5語ぐらいが話題がバラけすぎもせず偏りすぎもせずちょうどよかった。
前作「twitter上で親しい人のタイムラインを動的に生成するYahoo Pipes」は自分に近い人がでてくるけど、今度のはtwitterの中心近くで騒いでいる人たちが見える。
twitter上で親しい人のタイムラインを動的に生成するYahoo Pipes
修論を出した後のテンションでまたYahooPipesを作った。
→ Pipes: twitter dynamic timeline
親しい人で動的にタイムラインを作るpipes。
しくみは単純で、最近favを付けたり付けられたり、replyしたりされたりした相手でタイムラインを作る。
自分の発言内容やそれに対する相手の反応によってメンバーが動的に入れ替わる。
これの面白いのは、キーワードでの振り分けと異なりごく普通にtwitterを使っているだけでタイムラインが親しいメンツに変わっていく。
一見流動性が無いように見えるが、自分をふぁぼったりreplyしてきた人は入ってくるし、その人にreplyしたりfavし返せばしばらくはdynamic timelineに入り続ける。相手をfollowする必要すらない。
忙しくてtwitter見れない時は、これだけ見ておけば親しい人のpostはだいたいチェックできるかもしれない。
RSSにはusericons.relucks.orgを使ってアイコンを表示した。ユーザ名やURLもリンクになるようにしてある。

pipes全体図

一番上ですぐ分岐させて、左から順に
- favをもらった相手:favotterのユーザページからHTMLスクレイピングしてる
- 最近replyを受けた相手:オフィシャルtwitter検索でユーザ名検索してatom feedから取得
- favをつけた相手:ユーザのfavページからatom feedで取得
- 最近自分がreplyした人:自分のpostからatom feedで取得
YahooPipesは処理時間が長くなりすぎるか、pipesから相手へのアクセスが多くなりすぎるとエラーが出てしまう。
処理時間が長くならないようにするには並列化。pipeを縦につなげないようにする。
重要なのはpipesから外部サイトへのHTTPリクエストからレスポンスまでの時間だと思う。米yahooの強力なサーバーなので正規表現でページ解析する処理時間はたぶん無視していい。
今回上の方で4つに分けているように、早めにsplitモジュールで分けてそれぞれでHTTPリクエストを発行させてレスポンス待ちにすると速い。splitで分けられたそれぞれと、Loopモジュール内でFetch PageやFetch Feedモジュールは自動的にpipes内で非同期処理になってそうな気がする。
※この時どれかで取得した結果を他のsplit先に使おうとすると、結局縦に1本につなげているのと変わらなくなってしまうので注意しましょう
pipesから他のサイトへのアクセスについては、twitterとyahooが地理的に遠くないからなのか制限が緩くなっているのかよくわからないが、3秒で100アクセスぐらいしてるのにエラーはでない。
ahokaiに適当にtimelineの空気を読んで発言する機能、NGワード機能を追加、gemのアップデートに対応など
ahokaiという、俺の発言を収集・再構築して喋るtwitter botに
- ruby twitter gem0.6あたりから認証方法が変わったので、現最新版(0.6.6)に対応
- タイムラインに含まれる名詞の出現回数をチェックしてその単語から文章を作る
- post前に発言を検閲し、NGワードが含まれていたら発言を再度作り直す
あとは、replyとNGユーザ機能を気の向いた時に付ける予定。
ahokaiについては橋本商会 スーパーボット大戦およびymrl.net – 裏Twitterへ。
勝手にbot化しても許してくれそうな人の発言を収集し、賛同者?の方々と共にすこしずつbotが増やしています。ありがとうございます。
■既にahokaiを動かしていて、バージョンアップしたい場合
http://bitbucket.org/shokai/bot-ahokai/ からhg pullしてhg upするか、*.rbファイルを最新版のソースコードに置き換えてください。
DBやconfig.yaml等は書き換えなくても動作します。
ruby twitter gemはアップデートして最新のものを使わなければ動きません
sudo gem update twitter –remote
windowsの場合はsudoはいりません。
—–(ここから下の機能は、設定しなくても今まで通り動くので面倒ならやらなくて良い)—–
■ruby twitter gemの認証方法の変更への対応
コンストラクタに渡す引数が変わってた → twitter gemのHTTPAuth – 橋本詳解
■タイムラインに含まれる名詞の出現回数をチェックしてその単語から文章を作る
Buzzwords.rbに実装した。
timelineをMeCabで名詞だけ抽出して、出現回数をチェックして適当に名詞を選びそこから左右にマルコフ連鎖を伸ばす。
以前実装したbuzztterのRSSから発言を作る機能を拡張したので一瞬で完成した。
config.yamlのbuzzratioという項目で頻度が設定できます。sample.config.yamlを参考にしてください。
# twitter user/pass to post
usernum : “3631571″ # see your Feed of twitter
user : “username”
pass : “password”
## post with buzztter ratio
buzzratio : 0.3 # 30%
## search words for refav
#searchwords : ["ahokai", "あほか"]
## block NG words
#blockngwords : “true”
## for debug
#nopost : “true”
0.0にすれば動作しなくなり、1.0にすると確実に空気を読みます。
なお、バズワードの取得元は自分のtimelineだけではなく、buzztterからもランダムに取得します。
■post前に発言を検閲し、NGワードが含まれていたら発言を再度作り直す
bot元にしたらbotが確実に犯罪予告しそうな人がいたので、安全な言葉を出すまで発言を再構築させて犯罪防止をする機能を追加。
ngwordsに正規表現を書いておく。
.*殺.*
.*死.*
.*糞.*
.*うんこ.*
.*爆破.*
.*爆発.*
安全な言葉を出すまで10回再提出させて、無理ならあきらめる仕様なのであまり多くすると何もしゃべれなくなる。
config.yamlで
## block NG words
#blockngwords : “true”
を
## block NG words
blockngwords : “true”
とコメントアウトを外せば検閲機能が有効になります。
検閲はpost3gram.rbの中で実装してます

最近のコメント