OpenCVをソースからビルドするとhaarlike分類器(顔認識などに使われているやつ)の学習ツールが手に入るんだけど、たくさんのマシンでたくさん学習させているとそれぞれの進行状況をチェックするのが面倒になってくる。

でも、入力した画像ファイルが壊れていると学習が強制終了してしまったり、データがばらつきすぎてて収束しなくてあきらめて終了されたりするので、プロセスが死んでいたらパラメータを直してすぐやり直しをさせたい。学習中は予断を許さない状況が続く。

なので、進行状況を監視してtwitterアカウントshokai_logにpostするbotを作った。
5分間隔でopencv-haartrainingの作業ディレクトリとプロセスが生きているかをチェックする。
学習stageが進む毎に適当に通知し、プロセスが強制終了していた場合は激しくreplyしてくれる。これで安心して寝れる。OpenCV1.0/2.0両方対応。

プロセスが落ちていると教えてくれたり、段階が進む毎にtwitterに投稿したりする。
一見何言ってるのかわかりにくいpostもあるが、「ドドドド」だったらstage4が終わったという意味。
tweet-haartraining.rb
辞書はコード内にある。


第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