なぜRubyのデータ解析の未来が(ほかの人が思っているよりは)明るいと思うのかを無理やり言語化してみた

 機械学習やAIの時代になった。世間はPythonが大流行して、Rubyを使ったデータ解析の話はあまり聞こえてこない。にもかかわらず、私はデータ解析や機械学習の分野で、Rubyの未来は(多くの人が思っているよりは)まずまず明るいだろうなという見通しを持っている。その理由は論理的ではないのだが、頑張って言語化してみた。

(以下、個人の直感だけで語るポエム度の高い文章がずーっと続きます。そういうのが苦手な方はご容赦ください。また文章も全体的に悪文であることを自覚していますが、解像度の低すぎるぼんやりとした理解を無理して言語化しているので改善が難しいです。ご容赦ください。)

 要点を言うと、データ解析や機械学習に対する期待は、永久機関に対する幻想と似ておりいずれブームは飽和する。だから、ぞれぞれの言語である程度データ解析の手段が再実装されるだろうと考える。

 まずは、データ解析と期待学習に対する期待が、永久機関に対する幻想とよく似ているという点。

 AIや機械学習に対する過剰な期待をよく表す単語が「シンギュラリティ」である。(この言葉を聞いただけで嫌な顔をする人も多い)これは私の理解では「人間よりはるかに優れた知性が登場したとき、その知性がもっと優れた知性を作るという現象が起き、加速度的に進歩の速度が上がる」という仮説である。

 シンギュラリティは、永久機関とよく似ている。永久機関を作れば、永久機関のエネルギーを利用して永久機関を作ることがでる。そうやって永久機関を作り続ければ無限のエネルギーを手にすることができる。21世紀の世の中では永久機関が作れないことは誰でも知っているが、18世紀の科学者は永久機関を実現するために真剣に研究していたという。

 AIや機械学習は、永久機関に似ている。データ解析は、精度を上げると、必要な労力が上昇する。その割に利益は思ったほど上がらない。どこかに費用対効果の壁のようなものがあるようだ。データマイニングという言葉がある。大きすぎて人間には全体が見えないデータをコンピュータで解析する。言うのは簡単だが、実際に行うのは大変だと思う。雑然とした情報から、コンピュータで扱えるデータを取り出す(作り出す)のには時間と労力がかかる。精製されたデータは有効な情報を含んでいる。機械学習とは、天然鉱石から高い精度で貴金属を抽出する方法に似ている。ここでは機械学習の精度が高くなればなるほど収益は上がる。しかし機械学習のアルゴリズムが向上しても利益の上昇は思ったほど伸びない。どんなに精製の技術が高まっても、鉱石に含まれている貴金属の量は一定だからだ。

このように考えるとデータ解析は、有限なフロンティアである。データがあらかじめ含有している以上の価値を取り出すことはできない。にも関わらず、私達がデータサイエンスや機械学習に大きな夢や可能性を感じるのは、データという天然鉱石があちこちに転がっているように考えているからである。

 データは価値そのものであるという発想は今日では当たり前になっている。この考え方は、古くて新しい。しかし今日的な意味でのデータの価値は、 Googleが教えてくれたように思う。私達は、Googleが提供する無料のサービスを喜んで使っている。Googleはサービスの対価として消費者から情報を集めている。これは今でこそ常識だが、昔はグーグルが集めているようなデータが価値だとはあまり考えられていなかった。Google社が集めているような情報を有効活用する方法を私達が知らなかったからである。どうしてこんな優秀なサービスが無料で提供されているのだろうかと不思議に感じた日本人は少なくなかったと思う。少なくとも私はそうだった。

 私達は、さまざまなデータを日々生産している。データの生産と観測には、現象とセンサーが必要である。ここで

  • 自然現象とセンサーがデータを産生する側
  • 機械学習の過程をデータから精製しなんらかのアクションを起こして価値を発生させる側

の2つのサイドを考える。このように考えると、機械学習で得られる利益は、最初の自然現象の情報の持つエネルギーよりも小さくなる傾向があるようだ。具体的に考えると何が何やらさっぱりなので、全部妄想かもしれない。

 直感だが、データのエネルギー保存則のようなものが働いていそうだ。けれども、これがどういう学問になるのか知らない。情報理論というのがそれに当たるのかな。

 データの生産から価値を精製するまでの過程で、その価値は必ず低減すると仮定すると、機械学習で得られた情報エネルギーで、さらなる情報を産生するといったことはできない。だから、加速度的な進歩ということはまず起きないと思う。そう書いてみたが、何を言ってるのか、書いた本人にもわからない。。永久機関のWikipediaを眺めていたら、マクスウェルの悪魔というのが出てきて、データの観測や処理にはそれ自体エントロピーの発散が…とか、そういう難しい話も世間にはあるみたいだから、深く考えるとそういうところまで関係してくるのかもしれない。知らん。

 より具体的にすると、データ解析による意思決定は、最終的には次元削減をしていることが多い。次元を削減すると、一定の条件のもとで何らかの利益が得られるようだ。しかしその過程でデータの情報は失われる。解析側だけでなく、データの生産側も似たような構造になっているんだと思う。解析側と比較して、データ生産・観測側の仕組みは無数にあるので統一的な議論が難しそうだ。

 「データの力」の自給自足ができない以上は、データサイエンスによって収益を上げるためには、つねにデータのフロンティアをあちこち探し回って、多くの価値を含有しているデータを掘削(収奪)し続ける必要がある。永久機関とのアナロジーで考えるならば、「情報勾配」といったものも考えなければならないかもしれない。たぶん、データの持つエネルギーのようなものは、そのデータ単体では決まらず、周囲との格差のようなもので決まるようなところもあるはずである。(…などと書いているが、アナロジー以上の意味があるのかはわからない。)

 このような、機械学習と永久機関の類似性が遠因となって、Pythonは…いや機械学習はサチると思う。たとえばGoogleが恐ろしく立派な人工知能を作り上げたとしても、さほどの利益を生むとは思えない。あらかじめデータが含有している情報エネルギー?は決まっているので、機械学習ライブラリの精度を高めたからといって、無限に価値が取り出せるわけではない。

 Google社もその前提で動いており、Google Colabなどを使ってますますユーザーの行動やデータを吸い上げることに専念しているようにみえる。これから本当にAIによる大きな社会変化が起きるなら、真っ先に投資して買い占めるべき対象は、コンピュータ技術かどうか疑わしい。それらは勝手に発展するのだから、むしろコンピュータを動かす電気を発電する石油資源を買い占めるべきだろう。同じような発想で、データの囲い込みが熱心に行われているようである。

 もっとも、私のようなエンジニアではなく趣味的にパソコン遊びをしている人にとって(エンジニアの人でも大半はそうだと思うけど)Amazon、Googleのような大手ITが保有しているような大規模なデータ解析のツールというのは全くわからない。Apacheソフトウェア財団のオープンソースソフトウェアは、大量のコンピュータを使って情報を並列処理を行うものが多いけど、一般の人たちはそれらのソフトウェアを動かす装置を保有しないのだから。だからデータのことばかりに注目してしまう。しかし、本当はデータセンターを支えるハードウェア・ソフトウェア技術こそが大事なのかもしれない。そこにアクセスできるすごい人はがんばってください。

 一方で、社会のあちこちで、Pythonによって作られたライブラリの利用者が増える。データを眺めて、そのうらにある現象を理解するということは人間にとって楽しい営みだ。そういう人々によってデータライブラリ向けのライブラリはさまざまな言語で再実装される。RからPythonへ。PythonからJuliaへ。誰も見たことがないものを実装するには天才が必要かもしれないが、一回見たことがあるものを実装するのなら秀才だって負けてはいない。 n回目の車輪の発明は、n-1回目の車輪より改善されていることが多い。だから、Rubyの未来は明るい。Rubyのデータサイエンスのツールは、小さくて綺麗な実装であってほしい。

 Pythonの価値は、共通言語としての存在である。Pythonで書けば他の人もわかる、評価してくれる、そういった人と人との総合作用や評価のネットワークがプログラムを書く人同士を結びつける。これは幸福であるが、実際には負の部分もあると思う。長い目で見るとデータサイエンスブームは、放置されているデータが大量にあるという一時的な状態によって発生したバブルである可能性が高いからだ。バブルが終わるときは痛みを伴う。

 GithubでPythonのデータ解析を支えるライブラリをチラッと眺めると泥臭い感じがする。莫大なスターがついていても、アクティブにライブラリをメンテナンスしている人は意外と少ないものだ。データ解析や機械学習に対する情熱が覚めたとき、本当に今のPythonのライブラリ群が維持できるかは怪しい。

 かといって、Rubyのデータ解析や機械学習の世界が、すごく大きくなるとは思わない。なぜならば、Rubyを使っている人たちは平均してそういうことに、あまり興味がないからだ。反対に、Rは今後もデータサイエンスの分野では力を保ち続けるだろう。なぜならばユーザーはそういうことばかり考える人たちだからだ。プログラミング言語の世界は、コードの中でなくユーザーの頭の中にあるのだと思う。

 そんなわけで、私は中間的立場に立っている。Rubyの機械学習は決してPythonやRを追い抜かしたりはしないだろう。しかし、多くの人が考えているよりも遥かに発展していくはずだ。Rubyは改造が容易な言語である。これは、利用者に自由を与える一方で、ライブラリの作成者には多くのことを求めるやり方だと思う。これは、大人数でデータサイエンス用のライブラリを実装するときは不利に働くが、最初から目標とするものがしっかり見えている場合には、素晴らしいものが作られていくのではないだろうか。

 RubyはPythonではない。PythonはPython、RubyはRubyらしくゆっくり進んでいけばいいと思う。

 そしてこれからは

  • 情報を生産する技術
  • 手に入った情報にどの程度の価値があるのか予め推計する技術
  • 情報の生産から消費までの持続可能性
  • あるアウトプットを出すための極小のアルゴリズムとは何か?

みたいなものにフォーカスが当たっていくのではないだろうか。

 以上ポエ散らかした。なんとなく、そんな気がしている。たぶんこいった議論は世界のどこかで頭の良い人達がいろいろ考えてとっくに定式化されているのだろう。予想が当たるといいな。

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とかもいいけど、やっぱり簡単な方がいい。