TIM Labs

皆さん、Railsの開発でデバッガ使ってますか? 私は最近ようやく使うようになりました。

それまでは、Rubyistなら誰もがやっているであろう、ppデバッグ(ppで変数を表示)のみでデバッグを行なっていました。 C#でプログラムを開発していた頃は、

  • 「デバッガ無しで開発とかありえない!大きなプロジェクトでppデバッグとか、そんな手法使えるわけないだろ」

と思っていましたが、Rubyだとデバッガ無しでも案外なんとかなってしまいます。 ppを埋め込んで変数を表示、などもお手軽にできるのが、軽量言語の素晴らしい所でもあると思います。

しかし、流石にプロジェクトの規模が大きくなるに連れて、ppデバッグだけではプログラムの流れを追いにくい、という問題に直面していました。 エラーがどこで発生したのかを確認するために、ppを至る所に埋め込んでデバッグ。そんな手法に限界を感じていた、ある日、同じプロジェクトのメンバーがBetter Errorsという素晴らしいデバッガを紹介してくれました。 このデバッガを一度使ってしまったら、もうppデバッグなんてバカらしくてやってられなくなりました。

Rubyでもデバッガは必要です。今回はRubyで使える便利なデバッガ達を紹介します。

遺伝的アルゴリズムで説明に使われている「ナップサック問題」はBool型の配列で染色体をモデル化できるのだが、こんな単純な例はほとんどない。

最近よくやっているのが、2次元上の指示された数百、数千箇所もある制御点の値を適当に調整する問題だ。環境適合度関数、要するに評価関数が、制御点の値すべて引数とする多変数関数なのだが、この関数値が目標値(通常は0)になる場合の制御点の値の適当な組み合わせを求めよ、というものだ。

だが、制御点の値は、独立に何でも良いわけではなく、互いに複雑に影響し合っていて、こっちを変えればあっちが変わり、それに対応しているとさらに別のところが影響を受け、収拾がつかないような問題が多い。この互いの影響の調整が済まないと、評価関数での評価の意味がないことが多い。

でも、とりあえず調整が済み、評価できるうようになったとしよう。次世代を作るときに、選択として、まず今の世代から、比較的望ましい評価がされた染色体を次世代にコピーする。次に交叉であるが、現世代から2つの染色体を選んで掛け合わせる。でも、2次元のデータ2つを掛け合わせるといっても、実際にどうやるかは色々考えられるが、1つやった方法を紹介しよう。

2つの染色体の制御点の位置は一致しているので、この2つの染色体(板)を重ねた状態で、適当なところに直線を引き分割する。次に、それぞれの側から異なる切断された染色体を取り出し、無理やり貼り付けるのだが、切断線付近は壊された遺伝子も多数あり、それらの遺伝子をとりのぞき、空白地帯を作る。しかし、空白地帯のままでは、評価不能なので、空白地帯に詰め物をして、評価可能な状態にする。この空白地帯をなんとかするのは、まさに生物の染色体の修復作業と似たようなものかも知れない。

別の例で言うならば、2枚の絵を重ねて切断し、違う絵同士を切断線で貼り付け、切断線が分からないように細工をするような感じだ。

こうして、何とか2つの親染色体の性質をできるだけ引き継いだ子染色体ができる。切断後の修復がうまく行くと、子染色体の評価が良くなることがよくある。しかし、切断後の傷跡が深いと、子染色体の評価は著しく落ちて、早々に廃棄しないといけなくなる。

実際には、以上のような考えをどうプログラミングするかが遺伝的アルゴリズムでは非常に重要で、性能にも非常に影響する。
遺伝的アルゴリズムは、生命の遺伝そっくりの方法で問題解決を図る。
個体(染色体)を進化をさせて目的の個体を得る。
このちき、正常系の遺伝と、異常系の遺伝を用意する。
正常系の遺伝では、2つの個体から、両方の性質を普通に混ぜ合わせた個体をつsくる。
でも、正常系の遺伝だけでは、凡人と秀才しかいないつまらない世界になり、進化は止まってしまう。
進化が停滞してしまうのを打破するために、突然変異という仕掛けが必要である。
親の染色体の一部をランダムに変更する。すると、ほとんどの場合は酷い状態になってしまい、捨てられる。
しかし、まれに非常に優秀な個体ができる。
そして、この優秀な個体は、正常な遺伝によって子孫に広がっていく。
正常系と異常系のバランスが非常に重要である。
正常系だけで解けるような問題に遺伝的アルゴリズムをわざわざ使うのは無駄だろう。
どうやったら解けるかどうか分からない、手がつけられないような問題に適用すると良い結果が得られることがある。
突然変異の作り方は、扱っている問題によってどう実装するのが良いかは異なる。本とかには基本的なやり方は書かれているが、問題の特徴を考えて、突然変異の起こし方をそれぞれの場合に応じて調整しないと、うまく進化は進まない。
この調整は、なかなか難しいが、一番面白いところでもある。
生命の遺伝においても、高等生物が誕生したのは、突然変異が奇跡的にうまくいったからであろう。
突然変異は重要で、進歩のきっかけをもたらすのは奇人・変人であることが、遺伝的アルゴリズムをやることで実感できる。平均的人間ばかりでは社会は停滞する訳だ。
遺伝的アルゴリズムでは、対象を染色体という形でモデル化する。
染色体というと、生物の染色体のイメージがあるので、数個の値しか取らない数値が延々と並んだ配列とかリストを考えるのではないかと思う。
多くの場合そうなのだが、遺伝的アルゴリズムでの染色体はヒモ状のものである必要はないし、個々の遺伝子も、0/1あるいは、AGCTの様に数個の値に限定する必要もない。
単純な数値である必要もなく、座標値、形状データなど、実は何でも良い。
重要なのは、コンピュータで扱えること、2つの染色体から子供を普通に作る方法と、突然変異した子供を作る方法が用意できればOKである。
言葉による説明だけでは分かり難いだろうから、実例を紹介しよう。

一般的には、ナップサック問題とか巡回セールスマン問題で遺伝的アルゴリズムを体験するようだ。
でも、それだけでは、あまり面白味がないだろう。
と思っていたら、車のデザインに利用した例があった。

BoxCar 2D


適当にデザインして走らして、交叉や突然変異で次世代車を作り、また走らせて、そして選択してというのを続けていると、次第に理想的な形の車になって行きます。
この例では、実際に走行シミュレーションさせて評価するので、良い車になるまでにかなりの時間がかかります。プログラムを走らせたまま一晩放置しておけば、かなり良い状態になっていることでしょう。

「遺伝的アルゴリズム」というのをご存知だろうか。
生命の遺伝のメカニズムそっくりの方法で問題を解決しようという方法である。
アルゴリズムと言いながら、実はアルゴリズムがないというのが特徴のアルゴリズムである。
生命は、延々と遺伝が行われ進化して、人とか非常に高度で複雑なことができる生命が生まれてきた。
染色体が交差や突然変異によって次の世代ができ、環境適応性の良い個体が生き延びることで進化したと考えられる。
これを、そのまま様々な問題に応用しようというのである。

解説書はあまり多くはないが、実はとんでもなく単純である。
なのに、なぜか難しいと思い込まれているところが残念である。
ネットにも情報がいろいろあるし、遺伝的アルゴリズムを研究している大学の研究室も簡単に見つかるだろう。
遺伝的アルゴリズムは一種のフレームワークというか、もっと大雑把に捉えるべきだろう。
コンピュータで実現する場合、対象を何とか染色体にモデル化する。
そして、染色体の交差と突然変異に対する操作を決める。ここで乱数を使う。
世代毎に、多数の染色体から次世代に生き延びさせる染色体の選択方式を決める。
選択のために、各染色体を評価しなければならないので、環境適応度関数をでっちあげる。
あと、最初に、かなりデタラメでよいから、適当な個数の染色体をでっちあげる。
これだけ作って、スタートさせると、うまくいくと、そのうち、何十世代か何百世代後に、欲しかった染色体ができたりする。

成功するかどうかは、神のみぞ知るようなアルゴリズムなので、これをアルゴリズムと言って良いものか、少なからず疑問である。
でも、どうすれば解決できるか分からない、あるいは非常に面倒な問題を、単純な方法で解決できたりする。
今、この方法を使って、簡単に組合せ爆発が起きる分野に対して、実際に生産に使う実験をやっていて、かなり良い結果が出つつあるので、紹介していこうと思う。

書影

WEB+DB PRESS plus シリーズ の最新刊「開発ツール徹底攻略」に Vim の記事を寄稿しました。

「オペレーターやテキストオブジェクト等の有用な標準機能の活用術」「キーバインドを始めとした各種設定方法や便利な設定例」「プラグインの導入や活用方法」「プラグイン作成のいろは」といった内容を駆け足で解説しています。内容としては2009年8月に発売された WEB+DB PRESS Vol.52 の特集記事とほぼ同じですが、2013年に合わせてほんのり差し替えているところもあります。是非買ってください。

RSpecでテストを書いていると、特定のSpecの、特定のテストケースのみを実行したい場合があります。

プロジェクト初期のうちはSpecも少なく、全てのテストケースを実行していても気にならないかもしれません。 しかし、プロジェクトが進むに連れて、Specファイルがどんどん膨れ上がっていきます。 そんなSpecに新しいテストケースを追加して実行なんてしようものなら、まあテストに時間がかかって、イライラします。 全体テストなんてJenkinsにやらせればいいんです。私は今追加したテストケースだけをテストしたいんです。

RSpecには、特定のテストケースのみを実行する機能が備わっています。

問題

Vim プラグイン開発でも継続的インテグレーションがしたい! (Travis CI 編)」 では Vim プラグイン開発でも簡単に継続的インテグレーション(CI)を導入できることを解説しました。 ところで CI はユニットテストを書くことが前提です。 上記の記事では「どうユニットテストを書くか」についてはほとんど触れていませんでした。 実際にテストを書こうとすると、

  • テスト結果が実行環境に左右されないよう、無設定の状態の Vim を起動し、さらに関係のないプラグインや設定は読み込まれないよう細工をした上でテストを実行する必要がある。
  • テスト対象のプラグインが他のプラグインに依存している場合、適切なバージョンのものを取得した上でそれも利用可能な状態でテストを実行する必要がある。

という割と面倒臭い問題があります。 しかしもう2013年です。 どうにかして簡単にユニットテストを書けないものでしょうか。

問題

昨今は継続的インテグレーション(CI)が大ブームです。 そしてオープンソースのプロジェクトなら Travis CI を使うことで簡単にCIを始めることができます。 という訳でこのビッグウェーブに乗るしかありません。 それも Vim プラグイン開発で。

しかし、

  • Vim プラグインのテストはどう書けば......
  • 他のプラグインに依存しているプラグインだとどうしよう......

という割と面倒臭い問題があります。 しかしもう2013年です。 Vim プラグイン開発でも CI したいお年頃です。 一体どうすれば良いのでしょうか。

前回、GHC単独でアニメーションが動くところまでやった。
しかし、おおいなる問題があったのだ。

それは、こんな風に動くのだと言いながら、示したのは静止画だったのだ。
ここは何とか、実際にどのように動くのかを見せねば良くないだろう。

1つの方法はHaskellが直接動くようにして見せることだが、それは大掛かり過ぎる。
もっと、どこからでも、手軽に見える、確認できるようにしたい。
ということで、動いている動画を、アニメーションGIFというファイル形式で録画して見せることにしよう。
アニメーションGIFなら、ほとんどのブラウザで見えるはずだ。

動画をどうやったら録画できるのだろうか、勉強しなくちゃいけない。

ということで、ちょっと調べた。今回は、このあたりの話をする。

使っているパソコンは、Ubuntu/Linuxなのだ。ここには膨大なフリーソフトがつまっている。
何かきっとあるだろう。
実際、いくつかあったのだが、極めて安直にウィンドウの録画ができそうなのを調べたら、

Making a GIF screencast が出てきた。byzanzというソフトがUbuntuにはあり、GIFファイルを吐き出してくれる。ピッタシ!

インストールは、Synapticパッケージマネージャでやったので、説明は省略。ちょっとツンツンしただけだ。

fuji$ byzanz-record --help
Usage:
  byzanz-record [OPTION...] record your current desktop session

Help Options:
  -?, --help               Show help options
  --help-all               Show all help options
  --help-gtk               Show GTK+ Options

Application Options:
  -d, --duration=SECS      Duration of animation (default: 10 seconds)
  --delay=SECS             Delay before start (default: 1 second)
  -l, --loop               Let the animation loop
  -c, --cursor             Record mouse cursor
  -x, --x=PIXEL            X coordinate of rectangle to record
  -y, --y=PIXEL            Y coordinate of rectangle to record
  -w, --width=PIXEL        Width of recording rectangle
  -h, --height=PIXEL       Height of recording rectangle
  -v, --verbose            Be verbose
  --display=DISPLAY        X display to use

ということで早速録画しようとしたのだが、全画面を録画するのは良いのだが、指定したウィンドウだけを録画しようとしたら、録画するウィンドウの位置を指定しないといけないのだった。

ということで、今度は、xwininfo を起動し、Haskell/Glossのアニメーションウィンドウをクリックし、配置を確認した。

fuji$ xwininfo

xwininfo: Please select the window about which you
          would like information by clicking the
          mouse in that window.

xwininfo: Window id: 0x2ebe5b8 (has no name)

  Absolute upper-left X:  10
  Absolute upper-left Y:  10
  Relative upper-left X:  10
  Relative upper-left Y:  10
  Width: 246
  Height: 266
  Depth: 0
  Visual: 0x21
  ..................以下省略

ということで、左上隅が (10,10)で、246x266の部分を録画すればよいことがわかった。

fuji$ byzanz-record -d 60 --delay=0 -x 20 -y 10 -w 246 -h 266  byzanz-record-sample.gif

とやると、リターンキーを押した直後から60秒間録画され、最後に指定したファイルにアニメーションGIFファイルが作られる。

今回試しにつくったのがこれだ。サイズは、300KBと、動きが単純なこともあり、たいして大きくない。



これで、作られたアニメーションがどんなものか見せることができるようになった。

ということで、今日はここまで。



最近のコメント