Orangeすごいなあと思った

世間ではCloud AutoMLが流行しているようだ。

Google先生の戦略は「生産手段の私的所有が基本となっており、生産手段を持つ資本家が労働力を購入して商品を生産する。」みたいな大昔マルクス先生が主張された世界観を一世紀遅れで実現しているような気がして、あまり好きになれない。

生産手段を誰もが手にするためには、データセンターをみんなが作れるようになる必要があり、誰でもCPUを生産できるようにする必要がある。けれども、さまざまな政治的な思惑から、技術を禁止しようとする動きがあちこちで起きているようである。

これはそんなにおもしろい話ではない。なので注目している人が少ないであろうOrangeの話を書く。

Orangeは、スロベニアの リュブリャナ大学が開発しているScikit-Learn, Numpy, ScipyなどをGUIで操作できるようにするラッパーである。Githuのリポジトリをちらっと見た感じだと、全部Pythonで書かれているようだ。

なぜOrangeを知ったかというと、Anaconda-Navigatorがゴリ押ししてきたからである。ゴリ押しだなあ!と思ったので最初はいい印象がなかったが、とあることから使ってみて「これはすごい」と思った。

少なくとも、僕のようなど素人が、せこせことPythonやRubyのコードを、拾ってきてはJupyterにコピペするよりも、遥かに高速にかつ確実に解析ワークフローが作れるのである。Orangeが実現したUIは、将来的にどこか大企業が製品を発表して、Excelのように普及していくのではないだろうかと思った。MSがパトロンをやっているAnacondaがOrangeをゴリ押ししているので、Microsoft製品がそっちの方向に進化していく可能性もありそうだ。

しかし「すごい」と「普及」はまったく次元の違う話である。OrangeはPythonですべてGUIを書いている。ここから想像されるように、動作はもっさりしており、信頼性はあまり高い感じではない。そういうわけでOrange自体が今の形で普及することはないだろう。

なによりもスロベニアの大学が開発しているところが厳しい。プロダクトが普及するかどうかは、そのプロダクトの最初のコンセプトの輝きよりも、開発団体の体力で決まってしまうところが大きい。たとえ開発が継続できても、より大きなプロジェクトがやってきて飲み込まれてしまう。

そういうわけでOrangeは10年後にはなくなっている可能性が高いとは思うけど、言いたい。私はすごいと思いました。

どうしてOrangeがマイナーな存在なのか、ということを考えると、「データのローカリティ」ということが思い浮かぶ。Ruby用のクラスタ計算ソフトPwrakeでもデータのローカリティが計算速度に大きな影響を及ぼすとしている。ゲノム解析ではデータのサイズが大きすぎるので、データのダウンロードが一番大変な作業である。

OrangeはWebを用いたGUIではなく、Qtを使用している。もちろんsshでX11を使うという方法もあるかもしれないが、このソフトウェアは、基本的には手元のPCで手元のデータを処理することを考えているといっていいだろう。

私たちは、データは価値そのものだという現代的な価値観に染まりすぎているので、データのローカリティについて考える時、どうしても何か感情が邪魔をする。そんなことを考えた。

Rumale の分類器を比較してみる その1

Rumaleの分類器を比較してみた。
Gnuplotを使っているのだが、missing blank lineという残念な仕様があったりして、なかなかコードがキレイにならない。Gnuplotは高機能だけど一貫性がなくて流行らないわけだと思う。
今日できたところまで。ちゃんと整理ついたらQiitaとかに投稿したい。

RubyとDaruのイテレータについて

最近RubyのDaruを使っていて、もやもやと考えていることがある。

しかし頭がまとまらない。理由の一つはRubyのコードしか書いたことがないので知らないプログラミング概念がたくさんあるからかもしれない。そういうのを勉強すれば、もっとクリアな理解が得られるかもしれないという気がする。

習熟できなくても、概念に触れることはできるはずで、無理だと思わずに、C++やらrustやら難しいものを適当にかじってみた方がいいのかもしれない。しかし、今考えていることをアウトプットしておく。

Daruの使い方を調べていて、混乱したのが、イテレータであった。map や each である。私はmapが大好きだ。しかし、どうも最近mapがいかがわしく感じるときがある。

DaruにはVectorというクラスがある。データフレームは、ArrayとHashとNumPyの機能が全部必要だから、新しいクラスが必要なのだ。mapすると、Arrayが返される。これが直感に反する。Arrayではなく、Vectorが返ってきた方がよいのではないか。

それに、Daruを全面的に信頼できなくて、結局to_aを使ってしまうのである。こうなると、ArrayやHashを使うことになり、Daruの機能を使わないことになる。

そして、一度mapに疑問を持つと、いかがわしく見えてくるものがある。ブロックである。

私は上のコードが好きだ。わかりやすい。一方で、自分でイテレータや、yield を書くほど、プログラミングできるわけではない。ブロックを受け取るようなメソッドを自分で作ることない。

話がとぶが、Rubyを使い始めた頃、メソッドがオブジェクトではないのがすっきりしなかった。動的にメソッドを定義したり、削除したりすることはできても、メソッド自体はオブジェクトではない。

みたいな書き方ができない。Rubyでは、メソッド、Proc、lambda、lambdaのあたらしい書き方、ブロックなど、関数っぽいもがたくさんあって、ゴチャゴチャしているのである。

最初はDaruのイテレータが混沌としているのは、Daruの設計が悪いからだと思った。

けれども、ひょっとするとこれはArrayやHashを使わずに独自のクラスを設計すると常に発生する問題かもしれないと感じたのである。なんでもやってくれるArrayと、mapと、オブジェクトではないブロックの組み合わせは、ひょっとすると、本当はいびつなのかもしれない。どんな作業でもArrayを使うからあまり気にならないだけで。

だとすると、Arrayによく似たオブジェクトとイテレータを設計するのは難しい。もしApache ArrowのようなバックエンドのライブラリがRuby用の新しいデータフレームに採用されても、Rubyらしいイテレータを作成しようとするとまた困ってしまう。

map はブロックではなく、オブジェクトを引数に取る方がきれいかもしれない。そして、ブロック、メソッドは1種類のオブジェクトの方が美しいかもしれない。mapはArray以外のオブジェクトに対しても適応されることがあるので、Array以外のオブジェクトを返却する方が自然かもしれない、などと素人考えで思ったりする。

ruby 2.6 が出たので触ってみて、python と比較してみた
という記事を読んでみて、lambdaをフル活用したRubyが新鮮だったこともあり、そんなことを考えた。

こういう素朴な記事は、よく知っている人にとっては、恥ずかしくて書けないんだろうと思う。わからないことだらけだ。

想像だけど、きっとブロックをオブジェクトにするには、なにか実装に大きな問題があるのだと思う。そうでなければ、何でもオブジェクトにしたがるRubyで、ここまでグチャグチャしているはずがない。ブロックは、周囲のスコープを眺めることができる。つまり、ブロックは定義された位置に依存する存在なので、オブジェクトにするのには何か支障があるのかもしれない。

日本語でもそういうことはよくある。「県立図書館」がどの建物を意味するのかは、その単語が使われた都道府県によって全然違うだろう。ヒトは情報を処理する時に、そういう位置依存・文脈依存的な処理をフル活用している。自分だけではなくて、周囲環境が考えてくれてると言ってもいいかもしれない。

データをクレンジングを行う作業のうちいくらかの部分は、そのような文脈依存を剥ぎ取るための作業だと感じるときがある。

Daruのメリットを考える

Qiitaに投稿したDaruを使うから主観が強すぎるところをブログに移動しました。

Daruのメリットを考える

データサイエンスのツールとしてプログラミングをやりたい人が気にするのは次のような点だろう。

  • Daruは信頼できるのか?
  • Daruには継続性があるのか?
  • Daruのバグは少ないのか?

 正直に言うと、いずれもあてはまらない。Daruの信頼性はあまり高くなく、熱心に使い始めると小さなバグをそこかしこに見つけることができるだろう。Daruはビジネス上の緊急の問題解決に応えるためのツールではないのである。しかし、Daruのソースコードは小さく、速度に最適化されていないため、詳しくない人でも読むことができる。

 Daruを使ったデータ分析は、ビジネス上の問題を解決することや、Kaggleで高得点を出すことから、少し離れたところにある。一番効率的なツールを使って、最高の手段で問題解決に資したいと思うのは自然なことだ。そして、そのことで周囲から評価されたらなお嬉しいと思うだろう。
 ところが、そうやって問題解決のために自分を最適化することは、よいことばかりではなく、一歩引いて見るとある種過学習のような状態におちいっているケースがある。
 よくもわるくも、Daruにはそのような心配がない。これがRubyでデータ処理をやろうとする隠れたメリットの1つかもしれない。

もちろん、そのようなメリットがあるのは理解できるけれどもデメリットの方が大きいだろう、というのが一般的な考え方だろうなとは思います。

numo-narray に依存している Gem の一覧を作ってみた

Qiitaの記事:特定の gem に依存している gem の一覧を調べるによると、RubyGems.org の提供している下記のAPIを叩くことで、簡単に依存しているGemを調べることができるらしい。

これでnumo-narrayの依存しているGemを検索してみるとしよう。

2019年1月6日現在で

numo-narray に依存する Gem

Gemの名前内容
ruby-dnnディープニューラルネットワーク
gray_scott_gtk3反応拡散系
svmkit機械学習
smalltext文書分類
cumoGPU対応
numo-linalgBLAS/LAPACK バインディング(公式)
red-chainerディープニューラルネットワーク
nnニューラルネットワーク
rubexRuby拡張のためのプログラミング言語
machine_learning_workbenchベンチマーク??
hanny最小近傍法
numo-gslGNU Scientific Library 対応(公式)
neural_network_rbニューラルネットワーク
ffi-gdalGDAL(地理空間データ)
dicom医用画像
magnifier-ruby異常検知
numoネームスペース(公式)
numo-fftw離散フーリエ変換
red-arrow-numo-narrayApache Arrow
rblearn機械学習

こんな感じだ。こうやってリストにして表示してみると趣がある、

 個別のプロジェクトを眺めていても、プロジェクトや作者の隠せない個性みたいなものがちらっと見えたりして、とそれなりに楽しい。作者の名前もだいたい1回は見たことがある気がする。完全に観測範囲外だったのが、ruby-dnn と、ffi-gdal と magnifier-ruby。

 numo-narray が依存しているプロジェクト全体の広がりみたいのも結構面白い。

 こうやって眺めていると、一般の人がどんどんプログラミングができるようになって…開発者とユーザーの境界がなくなっていく、みたいなのは幻想なんだろうな、とふと思った。pythonやnumpyのような過密地域ではどうだか知らないが、Rubyのデータ処理のような過疎地域では、ユーザーの集合が、そのまま開発者の集合みたいな世界になっていて、みんなコツコツ自給自足しているようだ。そして、そういう世界では、プログラムはものすごくできるか、全然できないか、のどっちかしかありえない。初心者が生きるには厳しすぎ、上級者はあまりにも長くそこに定住して上級者になっていく人たちだからだ。

 そんななか、ひたすら開発者の道具をつかっていくユーザーが開発者の3倍ぐらいる状況がたまたま誕生すると、コミュニティは大きくなっていくと思う。そのような状況下でのみ、コミュニティが拡大し、コミュニティが拡大するほんの短い期間だけ、初心者と、中級者と、上級者がグラデーションのように存在するのだろう。それ以外の時間は、消滅する初心者と、ひたすら強い上級者だけが存在し、多様性が失われる。

 データの裏付けは何もないが、そんなことを考えた。

 考えてみると、数年前からこのブログでnumo-narrayの宣伝をしているが、ブログを読んで「パソコンに強い人たち」が numo-narray を使っていろいろ「すごいもの」を作ってくれたら、私は非エンジニアとしてRubyを使い続けながらその成果物を利用できるんじゃないか。みたいな下心が強く渦巻いていたことは否めない。というか今でもある。実際にはそんなことは起きなかったけれども。

 しかし、上記の結果をぼーっと眺めていると、「パソコンに強い人たち」がRubyで「すごいもの」を作る現象は、これからも起きにくいんじゃないかという気がする。それに、なんというか、最近は、本当に面白くて大事なところって、「そこ」じゃないんだろうな〜、という気もしている。これは最近生まれた気持ちなので、どういうことなのか自分でもよくわからない。

 あとは、何というか自分のような人間は思っている以上に希少ということが再確認された気がして、微妙にうれしくない。まさか「初心者が numo-narray を使って役に立たないけど面白いものを作ってみました」的なGemがこんなに少ないとはね。オリジナリティがあるのは結構だが、Rubyの代わりにPython勉強した方がよかったかな。

Cumo/NArray をつかってみた

(Cumo/NArray によって生成された画像(GIMPによる加工あり))

https://github.com/sonots/cumo

 結論から言うと、思ったよりよかった。

 Cumo/NArray とは、RubyでGPUを使って行列計算するためのライブラリ。開発している方の所属をみると、PFNとかDeNAがちょっと関わっているようである?

 世の中のGPUの90%ぐらいは、GPUをゲームシーンの表示のために使っていると思う。残りも多くは、3Dレンダリングなど、画像・動画表示編集ソフトなどに使われているだろう。

 そこからさらに少ない人が、GPUで仮想通貨のマイニングをしたり、DeepLearningをしている。マイニングやDeepLearningのアルゴリズムを自分で考えるのは普通無理なので、他の人が書いた行列計算のセットを実行している人がほとんどだと思う。

 GPUでカスタムな行列計算を大量にしようとか思う人はもっと少ない。

 そういう行列計算をたくさんする人は、だいたい科学者とか、コンピュータのエンジニアとか、そういう特殊な人たちだ。コンピュータに詳しくない人も詳しい人が行列計算をする必要に迫られた場合でも、まずは信頼性と情報量の多さを考えてPythonとかC言語とかを使うのが普通なので、RubyでDeepLearningとも仮想通貨とも関係がない高速な行列計算がしたい人は世界でも、まあ、多く見積もって10〜30人ぐらいしかいないんじゃないかな。

 そういう、世界全体でもほとんど発生しない、すごくニッチな需要が、昨日エンジニアでもなんでもなく趣味でパソコンをやってるだけの自分の中で発生した。自分の中で発生すればそれは1件でも100%である。よって、はじめてCumo/NArray をつかってみた。

 そもそも何でいままでCumoを試してこなかったかというと、メインのパソコンをUbuntu 18.10 に移行したからだ。18.10には公式のCudaが提供されておらず、インストールが面倒くさい状況になっていた。DeepLearning で遊ぶためだけにUbuntuをダウングレードして手元の環境を壊すのもちょっとな…と思った。

 それに Cumo/NArray にも期待していなかった。ひとたびGPUが絡むと、Tensorflowのように有名なライブラリですら、Cudaのバージョン、Pythonのバージョン、Anaconda、Ubuntuのバージョン、必要なライブラリやらアップグレードやらで、インストールやコンパイルは複雑怪奇・悲喜こもごもになる傾向がある。もはや個人で遊ぶには迷惑な存在なのである。Docker とかAWSとか知らんし。

 ところが、先日Numo/NArrayでちょこちょこ遊んでいる(Pythonのコードを参考に参考に書き写した)Gray-Scottのモデルの計算があまりにも遅いので、どうにかしたいと感じるようになった。何しろ、ちょっと大きな画像を得ようとすると、それだけで結果が出るまで半日かかる。

 そこでいよいよ Cumo/NArrayでも使ってみますか(それでだめだったらcupyでも試してみるか)、ということになったのである。気は進まなかったが、GeForce GTX 980 Ti を搭載したちょっと昔の自作PCを立ち上げて、Cudaアップグレードして、Cumo/NArray をインストールしてみた。

 結論から言うと、インストールは拍子抜けするほど簡単だった。

これがまず予想外。エラーがたくさん出てインストールできないと思っていた。エラーが出ても、ユーザーが少ないのでStackOverFlowを頼ることはできない。途中で挫折して、GitHubのissue欄に「エラーが出てインストールできません > < (Help me!)」というクソコメを残して撤退する予感があったので、あまりに簡単にインストールが終了して拍子抜けした。

 インストールは成功したように見えたが、ちゃんと動作するかまるで信用できなかったので、rake compile を実行してあからさまなエラーが発生しないことを確認した。さらに rake test まで自分で実行してみた。これ、普段なら絶対にやろうとも思わないやつである。全部パス。ここでまたびっくりした。

 しかし、本当にこいつGPUで動いているのか? もしかして、GPUが使用できないときは、バックグラウンドでNumo/NArrayやRubyで計算してヌカ喜びさせているだけちゃうんか?という不安が襲ってきたので、ターミナルで watch -n 1 nvidia-smi を実行。テストの実行中に見事にGPUの使用率が上昇しているのを確認した。最初からちゃんと動いていたのである。ここまでインストールしただけだが、感動の連続であった。

 そして、Numo::NArrayのコードをCumoに置き換えたが、これがまた普通に動く。メソッド #linespace と #clip には対応していないようだが、ちょっと書きかえればいいだけなので大きな問題にはならない。むしろ予想したよりも広くNArrayの基本的なクラスやメソッドをカバーしているみたいでとてもよい。

 さらに、計算速度も早い。実は、計算速度も全然期待していなかったのである。WebGLを利用して行列計算をしているというgpgpu.js の公式サイトに、5倍ぐらい高速になりますよ、と書かれていたので、Cumo/NArrayも5倍ぐらい高速になればいいかな〜と思っていた。ところが、ディープラーニングでCPUをGPUに切り替えたときのアレみたいに、普通にめちゃくちゃ計算が早くて!!また感動してしまった。

 このようにCumo/NArrayを使ってみた印象はとてもよかった。最初からあまり期待していない方、インストールに対する感動の閾値が低くなっている方にはおすすめです。(もうちょっと遊んでいると印象が変わってくるかもしれないけれども)

 使ってる人があんまりいないと思われるので、とりあえずブログを書いてググラビリティを側方支援、と。

Gemを作って気がついたこと

 書捨ての100行ぐらいのスクリプトではなくて、RubyのGemを公開するのは2回目である。前回のGemは変なGemだったので、ある程度ちゃんとしたGemは今回が初めてだと思う。いくつかやっているうちに気がついたことがあるので、忘れないうちに文章にして記録する。

ソフトを世界中に公開できる手段があるというのは気持ちいい

 まずはポジティブなところから。やっぱり gem install で簡単にソフトウェアを流通できる環境といいうのは便利でものすごいことだと思う。アイディアを形にしたら発表して、地球の反対側に住んでいる人にも瞬時に使ってもられる。これって控えめに言っても、大変なことだと思います。

シミュレーションが作りたいのかな

 私は大量のデータから、単純な回答を求めるものよりも、シンプルなルールから複雑な結果を生み出すものが好きらしい。シミュレーションを作る場合、シミュレーションを実行するクラスと、GUIを表示する2つのクラスを作成するのがよいようだ。大量にデータを扱う場合は、drubyやApacheArrowなどを挟んで非同期的にするとスピードが早くなるのかもしれない‥けどわからない。大量のデータを検索するようなものが必要になったら sqliteや ActiveRecord みたいなものを勉強していけばよいのかな。

Gemには命名規則がある

 知っている人には当たり前の話なのだろうけれども、単語の区切りは _(アンダーバー)、既存のGemやクラスの拡張は – (ハイフン)でつなぐ。Gemの命名はディレクトリの構造と一致するようにする。

Vimで複数の文章を開いてコピペするのが苦手

 普段はvimで満足している(基本機能しか使いこなせていないけれど)。Jupyterのように一つの100行ぐらいの書捨てのスクリプトを書けば満足なので初心者向けの機能で十分なのだ。しかし、Gemを作る場合には、クラスごとにファイルを作成するから、AのファイルからBのファイルに飛び回る、コピペするといった操作が必要になる。これが私にはできなかった。geditとかを開いてGUIでっている。しかし、本当は勉強すればVimでもできるはずだ。とりあえず画面のスプリット機能を使いはじめてたけれども、慣れるまでかなり時間がかかりそう。

クラスを作成する場合や、メソッドを作成する場合に、自分のいるスコープの外側に何があるか気にする

 書捨てのスクリプトだと、いつでもトップレベルからスタートするので、自分の外側の世界に何があるか、ということを気にしなくてもよかった。トップレベルからrequireしたり、includeしたり、ネストしたクラスを作る場合でも、基本的には内側へ内側へとモジュールが広がっていくので、外側の世界を気を配る必要はなかった。しかしGemを作る場合や、既存のライブラリを拡張する場合は、このメソッドはどこに生やすべきか、そのメソッドがいる場所から外側にはどんな世界が見えるべきか、ということを考える必要がある。

 メソッドがどの場所に生えているか、どのスコープに存在するか、モジュールとモジュールの内包構造をどうするか、という問題について、オブジェクト指向の強いRubyでも、特殊な記法を採用しているように見える。new とか使わないで人間の視覚に訴えかけるように、モジュールやクラスを定義するという感じで。

 あるモジュールから動的に特定の条件のメソッドだけ引っこ抜いて、よそのクラスに打ち込んだり、特異メソッドを互いにやりとしたり、その時々の状況に応じて、クラスの継承関係がダイナミックに逆転したり、といったことはあんまりやらないみたいだ。

 そう考えると、どのクラスがどのクラスを内包するか、メソッドがどこの場所にあるか、という場所をあとから変更するのは用意ではなく、一旦その骨格が決まってしまうと、あるパッケージ・ライブラリの進化の律速‥などという言葉があるのかわからないけれども‥のようなものになりやすくなっていると思われる。思いつきだが、ランダムにスコープやモジュール構成や、メソッドの範囲が変化するようにしておいて、どのような構成の時に最も落ち着くか、焼きなますようにしてシミュレーションしていくと面白いかもしれない。その場合、ユースケース(評価関数)に応じて局所最適解がいくつもあって、そのために同じような機能でも、ライブラリが複数発生するのだとしたらおもしろいと思う。こんどぜひともやってみたいけれども、本当にそんなことできるかな? そういうシミュレーションはAIによるプログラミングにつながっている気がする。GANのように、APIを使うAIと、設計するAIが、相互作用のなかで学習してライブラリを成長させていく。そんな妄想。

 自然界であ、AとBの2つのモノの相互作用の近くに複雑な構造が現れていくというものが多いから、そう考えると、クラスやモジュールの膜の表面に複雑なメソッドがはえていくのも、相互作用の中で表面が複雑化していく自然現象に似ている気がする。クラスやモジュールの内包関係が一旦規定してされてしまうと、なかなか変化せず、持続的な進歩を行うよりも、まっさらな状態から新しく作り直した方が早いケースが多いところも似ている。

 やや話は変わるけれども、普段の生活でも、この場やスペースがどういう場所で、職権がどの範囲まででとか、そういうことを考えなければならないシーンは多い気がする。モジュールの外側の世界を気にし始めたら、実世界でもそういう自分の外側にあるスコープがやけに気になるようになった。

 うーん、考えていたらずいぶんと遠いところにきてしまった。

やっぱりそれ以前にモジュールとか、特異メソッドとか、モジュールのインスタンス変数とか、クラス変数とかよくわかっていない。もちろんlambdaとprocの違いも

 ここのへんはRuby関連の読み物の中ではしょっちゅう出てくるけれども、未だにさっぱりわからない。悲しい。

トップレベルにメソッドを生やしたい場合は、Kernelモジュールを拡張すればいい

 タイトルの通りだけれども、awesome_print のようにトップレベルにメソッドを生やすタイプのライブラリーってどうやって定義しているのだろうかと思って調べて知った。

Gemは $LOAD_PATH にファイルを追加している

 これは gemspec を見れば気がつくことだけれども、今まで見過ごしていた。思ったより簡単な仕組みなんだなと思った。今はたまたまGemという仕組みが普及しているけれども、パッケージの作成、管理、配布を行う別の方法があってもおかしくないと感じた。

 $LOAD_PATHをRubyドキュメントで調べて、Kernel モジュールの中に特殊変数という名前のグローバル変数があることを知った。この特殊変数たち、大事なんだろうけれども、何となく見た目があまりかわいくない感じがするな。あくまでイメージの話だけど。

GUIを作るのは案外むずかしい

 世の中はプロが作った使いやすいGUIがあふれているため、GUIを作るのは簡単だという気がしてしまう。でも、それは思い違いだと思った。上の話と似ているけれども、あとから、この機能がほしい、あの機能がほしいという感じで少しずつGUIを修正して改変していくと、最終的に出来上がったものが微妙になっていく傾向があるみたいだ。

 GUIを考えるときは、一旦頭の中を博しにして、PCの電源を切って、コードのことは忘れて鉛筆と上で図面を書きながら考えることが必要みたい。

 少なくとも既存のGUIを眺めながらこっちの方がいい、あっちの方がいいと逐次的に修正を繰り返すだけではよいGUIを作るには不十分みたいである。GUIビルダーで仕上がりを観察しながら作れば、コードを手打ちするよりも便利なGUIができる、というわけでもないんだなということがわかった。

 市販のソフトで、GUIとデザインに半分以上のお金を払っているんじゃないかと感じることは多い。そういうものを「ギミック」と軽視する向きもあるけれども、やっぱりプロの仕事というものはすごいものだと思う。そういう無形の価値も評価されて、正当な対価が支払われるといいと思う。

今のところRuby/GTKでGIOのリソースやcomposite classを使う意味は薄いかな

 使ったほうが便利な場面も出てくるのかもしれないけれども、私のシンプルな使い方では compsit class を使うメリットはあんまりない感じがした。Windowsだとglib-compileresourcesとか、個別の環境では動かないこともあるし、とりあえず素人が書くぶんには、クラスを継承して、GObjectとRubyのオブジェクトを一致させようとか考えずに、委譲を中心にやっていった方がいいみたい‥というか難しくてそれしかできない。

Ruby以外の言語のことも勉強した方がいい気がした

 Rubyのこともよくわかっていないし、他の言語を勉強する能力も余力もないけれども、やっぱり世界にはたくさんすごい人がいて、その人たちはRubyではなくて、いろいろな言語でソフトを書いている場合が多いので、そういうものを雰囲気だけでも、やっぱり他の言語もちょこっとだけでも触れてみる方が人生が豊かになるんだろうなと思った。時間は有限だけど。

Gray-Scott というのを作ってみた記録

Qiitaへ投稿しようかとも思ったが、わからないことだらけなので、おとなしくブログにしておこうと思った。

  • 行列計算 : Numo::NArray
  • GUI : Ruby/GTK3

Git: https://github.com/kojix2/Gray-Scott

 f = 0.04, k = 0.06 の場合は上のような模様が表示される。

 まず、Gray-Scottとは一体何なのか、私にはさっぱりわかっていない。難しいのでNumpyのサンプルをNumo::NArrayに書き換えているだけである。中身はちっともわかっていない。

 書店でこの本を見つけて、買ってしまった。まだ読んでいないのだが、とりあえず一番最初に出てくるGray-Scott がすごい。ググるとWebGLを使ってヌルヌル動くデモがヒットするのだが、素晴らしくて度肝を抜かれた。自分でも作りたくなった。本のサンプルはPythonやNumpyで書かれているので、スピードや見た目よりもわかりやすさ重視である。素人がNArrayに逐語訳して勉強した気になるには最適である。

 CPUだと遅いのでこの機会にCumo/NArrayを試してみようかしらん、とも思ったが、Ubuntu18.10には公式のCudaが提供されてない。Cuda/Nvidia-driverをインストールするときは、高確率でブラックスクリーンに遭遇して時間を消耗するのでせっかく調子よく動いている手元のパソコンでは試さないことにしておく。

 今はRuby/GTKの勉強中なので、Ruby/Tkを使えば簡単なのにという気持ちもあったが、Ruby/GTKで作ってみることにした。

 GNOMEの画像を扱うっぽいオブジェクトはたくさんあって、どれがどういう関係なのか、きちんと把握できていない。まずCairoというライブラリがある。これはサーフェイスを作って、サーフェイスに書き込んで、PNGやPDFに保存するという仕組みらしい。次にPixbufというのがある。これは行列のような感じで、スケールを変更したり、bmpやpngで保存したりといった基本的な機能だけ持っているようである。GtkImageというのが、Gtkで画像を表示するウィジェットらしい。このほかにGtkDrawingAreaというものがある。これはCairoと組み合わせて使うらしい。動画の再生のサンプルなどでもDrawingAreaと組み合わせて使っている例がある。今回DrawingAreaを使うか、GtkImageを使うか迷ったが、StackOverflowで、DrawingAreaよりもGtkImageの方が軽いと主張しているサイトを見つけたので、GtkImageにNArrayから作ったデータを入れたPixbufを流し込むようにしてみた。一瞬RedArrowという単語が頭をかすめたがそんな難しいものを使う必要はないであろう。GtkImageはマウスのイベントを補足してくれないので、マウスの位置情報などを取得したい場合は、イベントボックスを親に配置する必要があるようである。本当にこのやり方が良いのかはわからない。そのほかにClutterとか、Gstreamerとかもあるが、これらはもっと難しそうである。

 さて、Ruby/Gtk が Ruby/Tk と比較して特に優れているところは、Gladeを見ながらGUIを作れるところだと思う。Gladeは不安定でクラッシュしやすいが、メインウィンドウに表示されているウィジェットをあれこれクリックしなければかなりクラッシュの頻度を下げられるという知見を得た。

 今回Gladeで気になったのは「シグナルハンドラー」の欄の右隣にある「ユーザーデータ」という項目である。これが何を意味するのかわからないが、シグナルが呼ばれたときに、一緒にウィジェットを渡せるという意味ではないかと推測している。しかしRuby/GTKではそのようには動作しなかった。使い方が間違っているのかもしれない。Glade は Microsot Office のように項目が膨大なので、使いこなせるようになるにはかなり時間がかかりそうだ。

 そのほか気になったのは、GLib::Timeout.add の部分だ。GUIのライブラリはメインループを回しているのでループしたいときに処理を待つ必要が出てくる。Ruby/Tkの場合はスレッドとの相性が非常に悪いことで有名で、drubyサーバーのプロセスや、処理専用のプロセスを別に立てて動かすとうまくいく。しかし、Ruby/Gtkで遊んでいる人はあまりいないみたいなので、どうすればいいのかわからない。GLibを勉強して、なるべくGLibの仕組みに乗っかるのがいいのか、やっぱりdrubyサーバーを立てるのか、Ruby本体をもっと勉強すればなんとかなるのか‥。そもそもGLibやRubyの非同期処理を理解することは可能なのか。少なくとも、GLib::Timeout.add で秒数を指定するだけだと、処理が終わらなかった時に、フリーズするみたいだ。

 NArray の演算は inplaceを使った方が2割ぐらい早いらしいので、今回は積極的にinplaceを使ってみた。inplaceという表記は長くて、現実的ではないので、横着だけどエイリアスをつけて_(アンダーバー)に置き換えることにした。Alifeの元本では、laplacianの計算に、rollというメソッドを使っていたが、NArrayではこれは未実装であったので、その部分は逐語訳した。そのほか濃度を彩度に変換した方が見栄えがいいのでインターネット上に公開されているNumpyのコードをありがたく参考にして、HSV→RGBの処理をNArrayで書いた(書き写した)が、この時にマスキングの不具合をみつけた。きちんと計測したわけではないが、HSV→RGB部分の処理はかなり時間がかかっているようで、改善の余地があると思う。

 できあがったものは、WebGLに比べるとだいぶ速度は遅いものの、だいたい期待通り動作するようだった。Ruby/Tk で作ったソフトよりも見栄えがいいのが嬉しい。

 さて、これで何を考えるか、というと、いろいろと思うところはあるのである。f や k の値をいろいろ変えて遊んでみたけれども、いずれのパラメータのときも、生命現象に非常によく似ていて、めちゃくちゃ興味深いと思った。でも慌ててなにか考える前にしばらくは頭の中にぼんやりとどめておくことにする。


Ruby/Gtk のつらいところなど

 Ruby/Gtk をさわり続けようとしているけれども、やっぱりいろいろな意味でドキュメントが無いのはつらい。

 意外だったのは、GNOME に関する日本語の文章が少ないところ。書きたい初心者いっぱいいると思ったんだけどいない。技術的な文章は、読んでもわからないからよいとして、Macでありがちな思想を語る感じのエモい文章、そういうのも読みたいと思ったけど全然ない。GNOME 思想でググると、リーナスがGNOME3に不満とか、GNOMEの思想は腐っておるとか、そういう末期感あふれるページばかりが上位にヒットする。2018年の地球はUbuntuがUnityやめてGNOMEに戻ってきた世界線なんですけどね。やっぱり Linux のデスクトップ環境を使っている人は少ないのだろうか。

 そもそもGNOMEの思想というものは存在するのだろうか。私はRuby-GNOME2に触れるまで、勝手にGTKは保守本流だと思っていた。FirefoxとかThunderbirdとかのイメージがあるし、キュートで緑なアイツに比べると古くて落ち着いて枯れた感じがする。Qtは開発企業がどんどん変わってスれてしまい、ちょっと幸薄なイメージだ。KDEアプリはGNOMEアプリよりクラッシュしやすい気がする。(まあGlade先生は鬼のようにクラッシュしますけどね)

 けれども、歴史を振り返ってみるとGNOMEは互換性のない変更をかなりやらかしているらしい。Ruby-GNOME2のパッケージもごっそりと大量に入れ替わっている。昔からLinuxデスクトップ使っていた人には常識なのだろう。私は最近までMacやWindowsばかり使っていたし、エンジニアでもなんでもないので全然知らなかった。GNOMEが枯れた感じになったのも最近のことなのだろう。

 GNOMEの創始者のイカザさんという方は、私の中ではMonoDevelopを作っている人のイメージである。調べると会社がすでにMicrosoftの一部になっていたり、途中Mac環境を使用していることを公言したり。そりゃあ創始者がそう言うぐらいならGNOMEは混乱して当然なのだろう。そういう紆余曲折もあって、思想なんかぐっちゃぐちゃで、したがって熱く語る人もいないのかも知れないない。

 さて、日本語のドキュメントが少ないのは仕方がないとして、英語の初心者向けドキュメントも少ないのにはがっかりした。RubyでダメなときはPythonの英語のドキュメントをググると大体解決する。PythonのGTKバインディングは、今はPyGobjectと言うらしい。ところがこれが、GitHubで71しかスターがないのである。少なすぎである。

 もちろんGitHubはGNOMEにとって本家ではなくて、GitLabが本家である。そういう理由もいろいろあるのだろうが、それを考慮しても71はマジで少ない。率直に言って、人気がないのだろう。

 そんなPyGobjectだが、英語の公式ドキュメントも、いまひとつ魅力がないのであった。あまり読みたい気持ちにならない。

 考えてみると、それも当たり前の話で、gobject introspectionを使うということは、APIを設計しない、という意味する。つまり人々が使いやすいようにするという意識を何度も通過してPyGobjectを作るわけではないため、その点については思い入れが薄くなるだろう。だからバインディングの作者は公式のドキュメントを書くモチベーションが低くなるのだろう。また書くべきですらないかもしれない。本家GNOMEリファレンスを見れば常に最新の情報が手に入るわけで、そうするべきかも知れない。

 他のやり方としては、ドキュメントを自動生成するのも悪くない。Rubyの場合はyardでドキュメントがサクサク閲覧できると嬉しいのだが今のところそうはなっていない。yard-gobject-introspection というのが開発中らしい。しかし、仮に自動生成できたとして、生成されたドキュメントに魅力があるかということは別問題である。

 プログラミングの素人には、Cの粒度はいつ見ても細かすぎて難しいと感じる。というか何をみてもだいたい難しい。エンジニアの人は難しいって言ったら負けだろうけど、私はエンジニアじゃないので正直言って、コンピュータの全てがこんがらがっていて難しく感じられる。ああ難しい難しい。

 GUIのライブラリーは、Shoesみたいに簡単なのがいいのかも知れない。でも個人的にはShoesはなんか嫌い。Shoesは最初に触ったときからずーっと嫌いだからもう変わらないと思う。Shoes系は簡単だけど、全然軽くない。やっぱりTkの手軽さ、書き味の柔らかさが過小評価されているんじゃないかなと思う。でもLinuxをメインにしてから、Tkのウィジェットは見た目が汚なすぎて、使う気が起きないけどな。

Crystal言語がはやい

すごい!

素人でも気軽にたのしくRubyで高速計算する方法を探してきた。そしてNArrayをみつけた。しかし、数年ぶりにNArrayを超えるかもしれない逸材を発見してしまった。Crystal言語である。Crystal言語は、マルチプロセスに対応する気もあるらしく、xargsやparalellジェムを日々愛用している私はとても期待している。そんなCrystalのメモ。

開発者

アルゼンチンに住んでいる

コンパイル

速度比較

色々な言語で計算速度を比較してみたを参考にライプニッツ級数の計算で測定した。グラフを見ると、Rubyと全く同じコードにも関わらず、コンパイル時間を含めてもNArrayより早いことがわかる。これはただ事ではない。PCによってはもっと差が出る印象。

Ruby & Crystal

NArray

 realusersys
Ruby61.887s61.812s0.028s
Crystal (コンパイル&実行)7.923s7.900s0.196s
Crystal (最適化コンパイルしたものを実行)6.821s6.812s0.000s
Numo::NArray10.163s9.860s0.256s

ほんの少しさわってイマイチに感じるところ

半日さわった印象としては、型のキャストがわかりにくい。たとえばUInt8をfloat32やfloat32に変換する方法をググってもしばらく見つけられなかった。.to_fでよいらしいが、.as(Float64)とかで統一してくれた方わかりやすい気もする。あと、どうしてもコンパイルに時間がかかる。kemal(sinatra相当)使ったコードをコンパイルするだけで体感数秒以上かかる。GUIライブラリが弱くてキャンバスに図形描写したりするの苦手っぽい。ライブラリ眺めてる pointerof とか出てくる。コンピュータに強い人にとっては長所なんだろうけど。

よいところ

crystal shards コマンドが優秀。Rubyもこうあるべきだと思う。htop眺めてると、files = Dir.glob files.each とかやったときに自動的にマルチスレッドしてくれているっぽい。だいたいRubyと同じ。コミュニティの雰囲気も洗練されていて悪くなさそう。とにかく処理が早い。

追記:さらに1日さわってRubyとの違いを感じたところ

上記は、数値計算のことだけを考えていたので、オブジェクト指向の部分、クラスやメソッドなども少し触ってみたらRubyとかなり違いがあった。まずは型チェックが厳しい。ある程度はコンパイラ?が型を推測してくれるが、必ずしも推測万能ではないので、推測が難しいところは自分で型を指定していく必要がある。特に引数のチェックは厳しい。Arrayとかは最初に型を指定する。こんな制約もあるので何でもかんでもArrayを使うべきではなく、構造体とかタプルとかSliceとか用途に合った型の採用を検討するほうがいいかも。デバックの時にRubyとはかなり頭の切り替えが必要そう。いろんな型を受け取るメソッドはオーバーライドという書き方も使って行く。ゲッター、セッターは、attr_readerの代わりにgetterとか名前が違う。irbがないので、crystal playを使う。(こういうのはRubyにも搭載されているべき。Jupyterに依存するべきではないと思う。)cloneとかequalとかto_jsonとか動かないので、自分でコードを書く必要がある。そして便利なマクロが用意されているので、それを使う。マクロは便利そうだが直感的になんとなく闇を感じる。2つobjectをreternするときはタプルが便利。injectのかわりにreduce、””と”意味が違う、require_relativeない。プライベートメソッドはprivate def hogeと書く。Threadとかも何気に動くっぽいけど、spawn使えとのこと。forkもあるっぽいが試していない。RubyはC言語のライブラリをラッパーになって処理を呼んでるだけの場合も多く、そういうものはCrystalに書き直しても大して早くならない。そのため「Crystalizationで爆速!」が(俺の趣味レベルで)実現できる範囲は思ったより狭いかも。コミュニティ若いので、ちょっとドキュメント書くだけでも歓迎される雰囲気がある。すごい。

使い分け

NArrayの方が可読性がよいので、特にスピードを求める部分だけCrystalで書けばいいと思う。素人なので、難しいことは考えずにパイプでつないだり、普通に実行結果をファイルに保存して、処理をつないでいけばよいと思う。Rubexとかもいいけど、やっぱり簡単な方がいい。