「知能」という単語に振り回されて応用を考えると実現性との乖離が大きくなり、完成できないか若しくは、コストが目的に対してかかり過ぎるという事態を往々にして引き起こすと思います。そこで、「知能」ではなく「直感」と考えて適用を考えたほうが良いと昨年からここで書いています。
人工知能系技術の処理内容は基本的に人間の劣化版になります。機械ですので一定の品質で処理量を増やすことは可能でそこが人間と比較した長所となります。
「直感」という単純なものなので、人間の入出力を補う形で利用するのが最も素直な利用法で、大量のデータの近傍探索(というか徘徊)に使うことが良いのではないかと考えていて、最近、会社で実験的なサイトを立ち上げてみました。
画像で旅する(β版) http://trip.deqwas.net
です。12月現在、240万以上のEC商品画像を収録しています。
とりあえずトップページはある程度ランダムに出力していますが、商品画像をクリックすると、特徴の近い順に他の商品が表示されます。近い方(上の方)は密に、遠い方(下の方)は疎にピックアップして並ぶように作ってあります。別の画像をクリックするとその画像起点で表示されます。そうして延々と辿ることができます。
「もっとこういうの」とか「この感じが良いとか」非言語でインタラクティブな画像検索(探索)を主眼に調整しています。
どうでしょうか?
実際使ってみると、一切言語情報を入力すること無く、結構幅広い商品探索ができているでしょう?
こんな感じのシステムを気軽に組むための機能がこれからのデータベースに求められているのかも知れません。
余談ですが、先日紹介したアプリ、PASHALYから画像を提示してそれの特徴量を起点にこの「画像で旅する」が利用できるようにしました。検索結果に「画像で旅する」タブが出てくるので、そこからスタートすることもできます。
これからも自然な形で現状の人工知能技術を利用することについて考えていきましょう。
さらに余談。2018年3月23、24日の「MANABIYA -teratail Developer Days-」( https://manabiya.tech ) にDBセッションとして登壇することになった様なので詳しい話はその中でできるかと思います。
2017年12月14日木曜日
2017年2月15日水曜日
人工知能技術とデータベースの接点を探る旅
ニューラルネットワークの計算モデルを扱える各種フレームワーク(TensorFlowとかCaffeとか)の登場によって、今やニューラルネットワークはシステムを構築する技術要素の1つとして確立されたと考えています。つまりデータベース等と同じ土俵に上がってくるわけですが、少なくとも悲しいかな MySQL とニューラルネットワークの接点は現在非常に遠いです。
私は実践主義者なので、まず具体例を作って世にリリースし、それを元に語っていけたらと思っていましたが、ようやく話せそうな仕事上のネタができてきたのでお知らせします。
画像解析をして、写っている商品に近い特徴の画像を持つECサイトの商品を返すAPIサービスを開発し(てサーバーを立て)ました。そしてそれを利用する無料iPhoneアプリ PASHALY を今週月曜にリリースしました。
以降、このサービスの内部構成を、データベースの将来を意識しつつ、性能や課題などについてお話していきたいと思います。(余裕があればですが。)
乞うご期待。
私は実践主義者なので、まず具体例を作って世にリリースし、それを元に語っていけたらと思っていましたが、ようやく話せそうな仕事上のネタができてきたのでお知らせします。
画像解析をして、写っている商品に近い特徴の画像を持つECサイトの商品を返すAPIサービスを開発し(てサーバーを立て)ました。そしてそれを利用する無料iPhoneアプリ PASHALY を今週月曜にリリースしました。
以降、このサービスの内部構成を、データベースの将来を意識しつつ、性能や課題などについてお話していきたいと思います。(余裕があればですが。)
乞うご期待。
2016年7月14日木曜日
【速報】TensorFlowの性能問題が解決しました。(今日)
いろいろ理解を深めたり、いろいろなハードウェア構成で学習や処理を試していましたが、TensorFlowはどうもCPUリソースを先に使い切ってしまう傾向があり最近困っていました。
調べてみると、TensorFlowのcifar10のサンプルなんかもそうかも知れませんが、
LRNOp、LRNGradOp クラス絡みがCPU側で重くボトルネックになっているみたいでした。
TensorFlowでは、各xxxOpに対して勾配を求めるクラスxxxGradOpも存在しています。
それは、学習の際に使われることになります。
従来、LRN(tf.nn.lrn()又は名前が変わってtf.nn.local_response_normalization())を含むモデルは、そこがどうしてもCPU処理になってしまい、
特に学習の際は、LRNGradOp の計算処理が重くなり、その計算中のGPU負荷は0%で、GPUを高性能なものに変えてもあまり性能が上がらない原因でした。
自分でなんとかGPU側の処理にできないか、とか他のフレームワークに乗り換えようか、とも思っていたのですが、なんと!「今日直ったみたいです」
この問題は、結構前からissueであがっていて
「Add gpu support for LRN」
がそうです。cudnnに該当処理があることは分かっていたみたいですね。
そして遂に、今日masterブランチで修正されたみたいです。
「Roll-forward of "Local Response Normalization GPU support via Stream Executor."」
早速ビルドしなおしてためしてみましたが、
これで、TensorFlowのCPU使いきり問題は「私の手元のモデルでは」解決しました!
皆さんも同じ事象で困っていた人は、最新ソースをビルドして使ってみましょう!
調べてみると、TensorFlowのcifar10のサンプルなんかもそうかも知れませんが、
LRNOp、LRNGradOp クラス絡みがCPU側で重くボトルネックになっているみたいでした。
TensorFlowでは、各xxxOpに対して勾配を求めるクラスxxxGradOpも存在しています。
それは、学習の際に使われることになります。
従来、LRN(tf.nn.lrn()又は名前が変わってtf.nn.local_response_normalization())を含むモデルは、そこがどうしてもCPU処理になってしまい、
特に学習の際は、LRNGradOp の計算処理が重くなり、その計算中のGPU負荷は0%で、GPUを高性能なものに変えてもあまり性能が上がらない原因でした。
自分でなんとかGPU側の処理にできないか、とか他のフレームワークに乗り換えようか、とも思っていたのですが、なんと!「今日直ったみたいです」
この問題は、結構前からissueであがっていて
「Add gpu support for LRN」
がそうです。cudnnに該当処理があることは分かっていたみたいですね。
そして遂に、今日masterブランチで修正されたみたいです。
「Roll-forward of "Local Response Normalization GPU support via Stream Executor."」
早速ビルドしなおしてためしてみましたが、
これで、TensorFlowのCPU使いきり問題は「私の手元のモデルでは」解決しました!
皆さんも同じ事象で困っていた人は、最新ソースをビルドして使ってみましょう!
2016年6月7日火曜日
人工知能(?)とデータベースの接点とか
「人工知能」という単語に過度な期待がかけられている昨今でありますが、その実態は「人工直感」と言った方が正確だと思われます。少なくとも我々がすぐに利用できるものはそうです。過度な期待をせず、この道具を適切に使いこなしていきたいところです。
適用対象を考えるとき、「人工知能で」というよりは「直感で」と言った方がより適切に判断できると思います。
たとえば、
というわけで、
人間の論理的思考への先天的なマッピングはまだまだ先でしょう。暫くは人間が細かにプログラムしなければいけないわけです。または、そうしたプログラム・データがクラウドとかで十分膨大に蓄積されたら、それをもって実現できた様に見えるかもしれません。計算能力よりも、プログラム・データ不足というのが実情でしょう。大手も含め、IT企業各社が前のめり気味に人工知能っぽいサービス・「人工直感」機能を提供しているのは、より沢山利用してもらってデータをためて人工知能っぽいものの完成度競争で優位に立つためかもしれません。
さて、そんな「人工直感」のためのデータベースとはどんなものか、そもそもデータベースなんてまだ必要なのか、という疑問もあると思います。
私は、多分「超多次元空間索引」が必要になると思います。1000次元くらいは欲しいです。分散格納してクラウド全ノードで全探索でも良いですが、それでは将来同時に色々対応できないでしょう。というかそのような検索技術が無ければ、大手クラウド会社の支配する領域となるでしょう。
ニューラルネットワーク等の関連技術では、「直感」の結果は多次元ベクトルで現れます。それがその空間内で近いものは、近い特徴を持ち、遠いものほど関連はありません。また、それを更に判断する全結合層などの学習済みの荷重などから、空間内の多次元平面(空間全体より低次元という意味で)に絞って近傍探索できても役立ちそうです。
とは言っても、経験上2次元の索引でも値の更新がある場合色々面倒なんですよね。でも、逐次、索引内データ(空間内座標)の補正ができなければ知識(まだ認識?)の修正ができません。多次元では正確性を少し犠牲にしただけで結果がぐちゃぐちゃになりそうで怖いですね。
個人的にはまだ直近では必要を感じていませんが、扱うデータ量・分類する精度が細かくなってきたら必要になって数年後に作る羽目になったりするのでしょうか?全く面倒くさいですね…。誰かつくってくれないでしょうか?もうあったら嬉しいですね。
というわけで、「人工直感」状態を脱しない限り、知能っぽさを与える部品はデータベース(というか膨大なデータ)です。 意外と接点は近いです。
では、またの機会に。
(追記)
多次元空間近傍検索は色々試してみましたが、検索のみが目的なら、nmslib の hnsw メソッドが一番良さ気です。もちろんチューニングは必要ですが、性能と精度のバランスが良く、索引は addDataPoint() しなおさなくても、loadIndex() するだけで使えます。pythonバインドもちょっと手を加えれば、結果セットにid以外にスコアも返るようにできますので。
適用対象を考えるとき、「人工知能で」というよりは「直感で」と言った方がより適切に判断できると思います。
たとえば、
- 「直感で」碁を打つ
- 「直感で」絵を描く
- 「直感で」危険を察知
天才棋士ですね
芸術家
なるほど便利
- 「直感で」会話
- 「直感で」医療行為
- 「直感で」自動車を運転
超々々天然丸出し?相手すると頭がおかしくなりそうですね。
利用したいですか?
こんなの走ってたら危ないから外出したくないレベルですね。
というわけで、
- 「直感で」言われたことを予め決まった分類する
- 「直感で」医療画像から異常を検知
- 「直感で」センサーから人・車を検知
人間の論理的思考への先天的なマッピングはまだまだ先でしょう。暫くは人間が細かにプログラムしなければいけないわけです。または、そうしたプログラム・データがクラウドとかで十分膨大に蓄積されたら、それをもって実現できた様に見えるかもしれません。計算能力よりも、プログラム・データ不足というのが実情でしょう。大手も含め、IT企業各社が前のめり気味に人工知能っぽいサービス・「人工直感」機能を提供しているのは、より沢山利用してもらってデータをためて人工知能っぽいものの完成度競争で優位に立つためかもしれません。
さて、そんな「人工直感」のためのデータベースとはどんなものか、そもそもデータベースなんてまだ必要なのか、という疑問もあると思います。
私は、多分「超多次元空間索引」が必要になると思います。1000次元くらいは欲しいです。分散格納してクラウド全ノードで全探索でも良いですが、それでは将来同時に色々対応できないでしょう。というかそのような検索技術が無ければ、大手クラウド会社の支配する領域となるでしょう。
ニューラルネットワーク等の関連技術では、「直感」の結果は多次元ベクトルで現れます。それがその空間内で近いものは、近い特徴を持ち、遠いものほど関連はありません。また、それを更に判断する全結合層などの学習済みの荷重などから、空間内の多次元平面(空間全体より低次元という意味で)に絞って近傍探索できても役立ちそうです。
とは言っても、経験上2次元の索引でも値の更新がある場合色々面倒なんですよね。でも、逐次、索引内データ(空間内座標)の補正ができなければ知識(まだ認識?)の修正ができません。多次元では正確性を少し犠牲にしただけで結果がぐちゃぐちゃになりそうで怖いですね。
個人的にはまだ直近では必要を感じていませんが、扱うデータ量・分類する精度が細かくなってきたら必要になって数年後に作る羽目になったりするのでしょうか?全く面倒くさいですね…。誰かつくってくれないでしょうか?もうあったら嬉しいですね。
というわけで、「人工直感」状態を脱しない限り、知能っぽさを与える部品はデータベース(というか膨大なデータ)です。 意外と接点は近いです。
では、またの機会に。
(追記)
多次元空間近傍検索は色々試してみましたが、検索のみが目的なら、nmslib の hnsw メソッドが一番良さ気です。もちろんチューニングは必要ですが、性能と精度のバランスが良く、索引は addDataPoint() しなおさなくても、loadIndex() するだけで使えます。pythonバインドもちょっと手を加えれば、結果セットにid以外にスコアも返るようにできますので。
2016年3月12日土曜日
MySQLではできないことができるデータベース(広義)達
自分は一応暫くMySQLの開発者だったので、MySQLでできることできないことはすぐわかる訳です。現実的な問題と対峙すること1年間、MySQLは使えることにしか使わないわけで、そうすると構築してしまうと、アラートメールが全く来ないので、水や空気のように存在を忘れてしまいます。でも、使えないことには全く使う気がしないわけで…。というわけでMySQLは結局逆にあまり触れていません。限られた範囲では完成を見ているというわけでしょうか。
データを処理して何か貯めて利用できるものをデータベースとするならば、MySQLを適用する気も起きないような領域があって、近年はそのような領域に挑む別の道具が出てきています。
今回は趣向を変えて、いろいろ現状MySQLでは扱えない問題の解決法を模索したことについて少し触れます。MySQLを離れた話題ですが、いつか遠い未来にMySQLの世界に持って帰る事柄かも知れません。それぞれMySQLに比べると私は初心者なので情報が不正確かもしれませんがご容赦ください。
ええ、これはMySQL以外も掘り下げてハッキングする誘いです。
それぞれ、一般的な説明は他にあるので省略します。
例えば、物凄い超巨大なデータ同士のJOINをして、それぞれのデータに含まれる結合キー以外のキーの組み合せで集計しなければいけなくて、しかも集計結果も下手したら元のデータよりも巨大になるような処理を、一晩で行わなければいけない場合を考えます。偶々現在は1台の高性能なサーバーで誤魔化しながら処理できているけれども、1年後には収まらなくなるかも知れないような状況としましょう。
クエリの並列実行ができても破綻する問題なので、MySQLを使う余地はありません。多数台サーバーで協調してソート・結合・集計を行うしか手はないわけで、Apache Sparkはまさにそういうことの実現に最も近いフレームワークです。
しかし、Apache Sparkを普通に使うだけでは、前述の問題は解決できないでしょう。バージョン1.5系で ソートマージ結合 が導入されましたが、それだけでは足りません。試したのは昨年末にかけてで、1.5系ベースの話ですが、1.6系でも多分一緒だと思います。Sparkの想定する用途は、「集計したら、データは各ノードのメモリに必ず収まる程度に小さくなる」ことが想定されていて、集計しても尚巨大なことは想定外。メモリ不足で落ちてしまうでしょう。
そこで、利用者が工夫する必要があるわけですが、Apache Sparkの素晴らしいところは、そういった「根本の内部クラスの拡張が本体パッケージのリビルド無しで可能」な点です。さらに、Sparkは、ユーザー側のソースコードはScalaで書かれています。自分で、DataFrameクラスを拡張した、拡張メソッドを持つExDataFrameクラスを作っても、
とソース中に暗黙変換の定義を書くだけで、あたかもDataFrameに新しいメソッドが実装されたかのように利用できるわけです。
で、どう拡張するのかですが、Sparkのソースの中には、
sql/core/src/main/scala/org/apache/spark/sql/ExperimentalMethods.scala
というクラス定義があって、
コメントには「勇者へ」みたいな感じで
試してみたこととしては、
但し、バージョンが上がってオプティマイザが賢くなってくると、こちらが拡張して差し込んだ実行計画や実行に必要な情報が勝手に壊されちゃったりするので、そういった情報のクラスは継承する別クラスを作ってオプティマイザから隠したりしなければいけないので注意です。
結果、オプティマイザとの戦いを通して、動作に詳しくなることでしょう。興味深い世界です。
実はこれでもまだ、前述の課題に対しては課題が有ります。結合と集計のキーが違うことです。Sparkやhdfsはキーのハッシュで分散処理しますが、キーを変えると分散のキーも変わるので盛大にノード間でデータの持ち替え(Spark用語でシャッフル)が発生するわけですが、これが効率がまだ良くないように見受けられます。
spark.shuffle.manager=tungsten-sort
でマシにはなりましたが、シャッフルの仕組み自体に結構無駄がある疑いがあります。巨大データのキー変更を含むと、処理時間の大部分はこのシャッフル絡みに費やされることがわかると思います。
実際に使うときにはシャッフルの動作も詳しくチェックして、できたら改造の余地を調べたいところです。
データベースっぽいとは言え、これもMySQLとはまだ無縁な分野でしょう。とは言え、(1)の問題のように物理的な制約ではないので、意外と(1)よりは近くにある課題かも知れません。
一応、例をあげると、画像に写っているのは犬か猫かとか、画像に写っているものに近い特徴の商品は何で誰が買いそうかとか、思い出せない固有名詞について一生懸命説明文を書いてそれを解釈させて近い特徴の単語を調べるとか。
TensorFlowは同分野のニューラルネットワーク向けフレームワークの中でも、特にサービスとして利用することを意識した創りになっています。学習フェーズにせよ、サービスを行うフェーズにせよ、仕組みを理解すれば最適に近い処理で、GPUを無駄なく使うことができます。
という仕組みの話もありますが、チュートリアルと称して提供されているサンプルの質の高さも魅力的です。参考にすることによって、非常に時間のかかる低層の学習(回転拡大縮小)を省略できたりします。
Inception-v3 というサンプルがあって、これはImageNetのILSVRCのコンペティションの「Object localization」をターゲットにしていますが、位置の検出はしないという少しひねたものになっています。写真を1000種類のカテゴリに分類します。使ってみたら判りますが、これは結構面白い精度で位置の特定までしないのが不思議なくらいです。写真から候補領域を切り出して、それぞれInception-v3に入力するだけで反応の高い部分をとりだせばそのまま位置になるんじゃないでしょうか?Googleの中の人がそうした(フル参戦しない)意図は判りませんが、これはハッカーを誘っているのだと思います。
まぁ、候補領域をどうやってあたりをつけるかは今回は置いておいて、効率よくInception-v3で処理することを考えて見ます。
TensorFlowの動作は、実行計画となるGraph(Tensorの演算)を作って、セッションのメソッド run() で実行するのですが、最初の実行時にGraphに従って、GPUのセットアップを行い、2回目以降は同じGraphであれば即処理が始まります。さらに、GPUのコアとメモリに余裕があれば、複数の画像を同時に与えて同時に処理をしたほうがハイスペックなGPUを活かせます。それは難しいことではなく、バッチ処理用の次元を最初から最後までGraphの中で保持しておけば、配列を任意の長さの行列にして run() の feed_dict に与えるだけです。長すぎる場合は勝手に複数回に分けてくれるようです。
run()メソッドでは、Graph内のTensorであれば、入力と出力を任意に指定できます(途中でもいい)。Inception-v3では、バッチ処理に対応していない部分が少しあるのでそこを避けて使ってみます。
ヒントはC++のサンプルプログラムの中にあります。Inception-v3のモデル自体は最初が DecodeJpeg というテンソルで、最後が softmax という1000+αの判別のためのニューロンが並びます。しかし、C++のサンプルは入力には Mul というテンソルの出力を差し替えて行っています。(余談:C++だと、テンソルを入力にできるのですね。Pythonだと配列じゃないと与えられないのです。Operationクラスの_update_input()っていう内部メソッドで無理やり繋げることができるのですが、チェックとか一切入らないので上級者向けです。)
この出力は、画像をデコードしてfloat32にして299x299の固定サイズにリサイズして、-1.0~+1.0に正規化したものです。 最初のディメンションが Dimension(1) ですね。ここで、バッチの余地が潰されています。この後ろのテンソルはバッチ対応のニューラルネットワークで、 実は、バッチ処理で入力するにはここ以降から行う必要があるわけです。 (しかもgpu対応していないオペレーションも手前にはある。resize_bilinear とか、cpu-gpu間のやりとりも最後まで無いほうがいい)
自分で、リサイズ&正規化して行列にして与えようとして次に問題になるのは結構最後のほうです。
softmaxの手前、pool_3 まではちゃんとバッチ処理できます。pool_3 を使いたい人はこれでいいのですが、一応理解を深めるためにsoftmaxまでバッチ処理が通るようにします。
Reshapeで次元整理(x,y座標がもう無いのでカットしている)するときに一緒にバッチで使う次元も1にしてしまっているみたいです。reshape(pool3, [1, 2048]) みたいな感じなのを、reshape(pool3, [-1, 2048]) みたいにすれば通りそうです。
。。。
ロード後に変更するのは面倒くさいから直接もとのファイルを変えることにします(!)。int32なので、"01 00 00 00 00 08 00 00" となっている箇所を "FF FF FF FF 00 08 00 00" とすればOKです。
はい。これで、バンバン299x299の画像をバッチ処理でsoftmaxテンソルにできるようになりました。候補領域を切り出して処理して位置特定もできるかも。
バッチ処理できないと、折角の良いGPUの性能を発揮できなかったので少し調べてみました。
皆さんもいじり倒してみましょう。
データを処理して何か貯めて利用できるものをデータベースとするならば、MySQLを適用する気も起きないような領域があって、近年はそのような領域に挑む別の道具が出てきています。
今回は趣向を変えて、いろいろ現状MySQLでは扱えない問題の解決法を模索したことについて少し触れます。MySQLを離れた話題ですが、いつか遠い未来にMySQLの世界に持って帰る事柄かも知れません。それぞれMySQLに比べると私は初心者なので情報が不正確かもしれませんがご容赦ください。
ええ、これはMySQL以外も掘り下げてハッキングする誘いです。
それぞれ、一般的な説明は他にあるので省略します。
(1) Apache Spark
10年くらい前から、RDBMSの性能が解決したら、次はETLのお化けのような道具が必要になると常々考えていました。Apache Sparkはまさにそれに当たるものです。例えば、物凄い超巨大なデータ同士のJOINをして、それぞれのデータに含まれる結合キー以外のキーの組み合せで集計しなければいけなくて、しかも集計結果も下手したら元のデータよりも巨大になるような処理を、一晩で行わなければいけない場合を考えます。偶々現在は1台の高性能なサーバーで誤魔化しながら処理できているけれども、1年後には収まらなくなるかも知れないような状況としましょう。
クエリの並列実行ができても破綻する問題なので、MySQLを使う余地はありません。多数台サーバーで協調してソート・結合・集計を行うしか手はないわけで、Apache Sparkはまさにそういうことの実現に最も近いフレームワークです。
しかし、Apache Sparkを普通に使うだけでは、前述の問題は解決できないでしょう。バージョン1.5系で ソートマージ結合 が導入されましたが、それだけでは足りません。試したのは昨年末にかけてで、1.5系ベースの話ですが、1.6系でも多分一緒だと思います。Sparkの想定する用途は、「集計したら、データは各ノードのメモリに必ず収まる程度に小さくなる」ことが想定されていて、集計しても尚巨大なことは想定外。メモリ不足で落ちてしまうでしょう。
そこで、利用者が工夫する必要があるわけですが、Apache Sparkの素晴らしいところは、そういった「根本の内部クラスの拡張が本体パッケージのリビルド無しで可能」な点です。さらに、Sparkは、ユーザー側のソースコードはScalaで書かれています。自分で、DataFrameクラスを拡張した、拡張メソッドを持つExDataFrameクラスを作っても、
implicit def DataFrameToExDataFrame(df: DataFrame): ExDataFrame = new ExDataFrame(df)
とソース中に暗黙変換の定義を書くだけで、あたかもDataFrameに新しいメソッドが実装されたかのように利用できるわけです。
で、どう拡張するのかですが、Sparkのソースの中には、
sql/core/src/main/scala/org/apache/spark/sql/ExperimentalMethods.scala
というクラス定義があって、
コメントには「勇者へ」みたいな感じで
* :: Experimental ::
* Holder for experimental methods for the bravest. We make NO guarantee about the stability
* regarding binary compatibility and source compatibility of methods here.
*
* {{{
* sqlContext.experimental.extraStrategies += ...
* }}}
自分で実行計画を拡張できるようなことが書かれていますが(実際できるのですが)、
複雑なことをしようとすると丸ごとクラス群を作る必要があり、量が多すぎるので、
結局は同じ package の中に継承するクラスを作る羽目になります。。。試してみたこととしては、
- ソートベースの集計 sortGroupBy() のようなメソッド
- 使い方は既存の groupBy() と一緒。少なくともメモリ不足で落ちません。堅実に動作します。
- ソートして1個前のレコードと結合 sortMergePrevRec()
- キーとなるカラムのリストを指定しますが、最後のカラム以外のキー単位で処理の分散、最後のカラムでソートを行い、1個前レコードが後ろにくっつきます。最後のカラムを日付とかにすると、前レコードの日付を使って経過時間を出せたり便利かも。
但し、バージョンが上がってオプティマイザが賢くなってくると、こちらが拡張して差し込んだ実行計画や実行に必要な情報が勝手に壊されちゃったりするので、そういった情報のクラスは継承する別クラスを作ってオプティマイザから隠したりしなければいけないので注意です。
結果、オプティマイザとの戦いを通して、動作に詳しくなることでしょう。興味深い世界です。
実はこれでもまだ、前述の課題に対しては課題が有ります。結合と集計のキーが違うことです。Sparkやhdfsはキーのハッシュで分散処理しますが、キーを変えると分散のキーも変わるので盛大にノード間でデータの持ち替え(Spark用語でシャッフル)が発生するわけですが、これが効率がまだ良くないように見受けられます。
spark.shuffle.manager=tungsten-sort
でマシにはなりましたが、シャッフルの仕組み自体に結構無駄がある疑いがあります。巨大データのキー変更を含むと、処理時間の大部分はこのシャッフル絡みに費やされることがわかると思います。
実際に使うときにはシャッフルの動作も詳しくチェックして、できたら改造の余地を調べたいところです。
(2) TensorFlow
層状ニューラルネットワークの「学習-判別」の関係は「格納-取り出し」の関係に似ています。ただ、キーとなるデータが大きかったり曖昧だったり複雑だったりするだけで、データベースの延長のようなものだと考えています。人工知能の重要部品だとはおもいますが、個人的にはこれ自体にはまだ知能は感じないので人工知能の枠組みで語ることには抵抗が有ります。データベースっぽいとは言え、これもMySQLとはまだ無縁な分野でしょう。とは言え、(1)の問題のように物理的な制約ではないので、意外と(1)よりは近くにある課題かも知れません。
一応、例をあげると、画像に写っているのは犬か猫かとか、画像に写っているものに近い特徴の商品は何で誰が買いそうかとか、思い出せない固有名詞について一生懸命説明文を書いてそれを解釈させて近い特徴の単語を調べるとか。
TensorFlowは同分野のニューラルネットワーク向けフレームワークの中でも、特にサービスとして利用することを意識した創りになっています。学習フェーズにせよ、サービスを行うフェーズにせよ、仕組みを理解すれば最適に近い処理で、GPUを無駄なく使うことができます。
という仕組みの話もありますが、チュートリアルと称して提供されているサンプルの質の高さも魅力的です。参考にすることによって、非常に時間のかかる低層の学習(回転拡大縮小)を省略できたりします。
Inception-v3 というサンプルがあって、これはImageNetのILSVRCのコンペティションの「Object localization」をターゲットにしていますが、位置の検出はしないという少しひねたものになっています。写真を1000種類のカテゴリに分類します。使ってみたら判りますが、これは結構面白い精度で位置の特定までしないのが不思議なくらいです。写真から候補領域を切り出して、それぞれInception-v3に入力するだけで反応の高い部分をとりだせばそのまま位置になるんじゃないでしょうか?Googleの中の人がそうした(フル参戦しない)意図は判りませんが、これはハッカーを誘っているのだと思います。
まぁ、候補領域をどうやってあたりをつけるかは今回は置いておいて、効率よくInception-v3で処理することを考えて見ます。
TensorFlowの動作は、実行計画となるGraph(Tensorの演算)を作って、セッションのメソッド run() で実行するのですが、最初の実行時にGraphに従って、GPUのセットアップを行い、2回目以降は同じGraphであれば即処理が始まります。さらに、GPUのコアとメモリに余裕があれば、複数の画像を同時に与えて同時に処理をしたほうがハイスペックなGPUを活かせます。それは難しいことではなく、バッチ処理用の次元を最初から最後までGraphの中で保持しておけば、配列を任意の長さの行列にして run() の feed_dict に与えるだけです。長すぎる場合は勝手に複数回に分けてくれるようです。
run()メソッドでは、Graph内のTensorであれば、入力と出力を任意に指定できます(途中でもいい)。Inception-v3では、バッチ処理に対応していない部分が少しあるのでそこを避けて使ってみます。
ヒントはC++のサンプルプログラムの中にあります。Inception-v3のモデル自体は最初が DecodeJpeg というテンソルで、最後が softmax という1000+αの判別のためのニューロンが並びます。しかし、C++のサンプルは入力には Mul というテンソルの出力を差し替えて行っています。(余談:C++だと、テンソルを入力にできるのですね。Pythonだと配列じゃないと与えられないのです。Operationクラスの_update_input()っていう内部メソッドで無理やり繋げることができるのですが、チェックとか一切入らないので上級者向けです。)
>>> print sess.graph.get_tensor_by_name('Mul:0')
Tensor("Mul:0", shape=TensorShape([Dimension(1), Dimension(299), Dimension(299), Dimension(3)]), dtype=float32)
この出力は、画像をデコードしてfloat32にして299x299の固定サイズにリサイズして、-1.0~+1.0に正規化したものです。 最初のディメンションが Dimension(1) ですね。ここで、バッチの余地が潰されています。この後ろのテンソルはバッチ対応のニューラルネットワークで、 実は、バッチ処理で入力するにはここ以降から行う必要があるわけです。 (しかもgpu対応していないオペレーションも手前にはある。resize_bilinear とか、cpu-gpu間のやりとりも最後まで無いほうがいい)
自分で、リサイズ&正規化して行列にして与えようとして次に問題になるのは結構最後のほうです。
W tensorflow/core/common_runtime/executor.cc:1102] 0x719ce20 Compute status: Invalid argument: Input to reshape is a tensor with 118784 values, but the requested shape has 2048
[[Node: pool_3/_reshape = Reshape[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"](pool_3, pool_3/_reshape/shape)]]
softmaxの手前、pool_3 まではちゃんとバッチ処理できます。pool_3 を使いたい人はこれでいいのですが、一応理解を深めるためにsoftmaxまでバッチ処理が通るようにします。
Reshapeで次元整理(x,y座標がもう無いのでカットしている)するときに一緒にバッチで使う次元も1にしてしまっているみたいです。reshape(pool3, [1, 2048]) みたいな感じなのを、reshape(pool3, [-1, 2048]) みたいにすれば通りそうです。
。。。
ロード後に変更するのは面倒くさいから直接もとのファイルを変えることにします(!)。int32なので、"01 00 00 00 00 08 00 00" となっている箇所を "FF FF FF FF 00 08 00 00" とすればOKです。
はい。これで、バンバン299x299の画像をバッチ処理でsoftmaxテンソルにできるようになりました。候補領域を切り出して処理して位置特定もできるかも。
バッチ処理できないと、折角の良いGPUの性能を発揮できなかったので少し調べてみました。
皆さんもいじり倒してみましょう。
2015年7月6日月曜日
DBT-2 で MySQL と他のRDBMSの性能比較をしている人に騙されないように注意
一応、立場的には第三者に戻った(MySQL/InnoDBの性能追求が仕事ではない)ので、忘れられない暗い過去にも触れてみようと思います。
未だに騙されている人が多いみたいなので、MySQL/InnoDBの名誉のために書き残さなければなりません。何度でも言いますが、性能比較は自分の目的とする処理をちゃんと比較しないとだめです。そうでなくては、騙されて本当は悪い性能のものを掴まされることになります。
DBT-2と言う(TPC-Cをベースにした)ベンチマークがありますが、数多のRDBMS(商用/OSS双方)に対して独自にTPC-Cベンチマークを実装・チューニングして比較した経験のある私から見て、怪しい結果しか出ないので、長年、基本無視のスタンスを取っています。
が、3年前にあろうことかMySQLの性能QAがDBT-2(nonsp:mysql)を利用していて、とある性能FIXに対して問題を指摘してきたのを契機にDBT-2のバグ(MySQL版にだけマイナスに働く)を調べて性能QAのチェックを改めてもらったことがあります。
その情報は、DBT-2の開発側にもフィードバックされたと記憶していますが、3年経った今もバグはそのままです。故意か過失かは不明ですが、DBT-2をMySQLを貶めるために嬉々として利用してきた人には悪意を感じてきました。
TPC-Cの仕様から見て間違っている箇所も何箇所かあるのですが、全RDBMSに共通な間違いはとりあえず触れません。
最も大きい問題の箇所は nonspでの、DELIVERY_1 クエリのあたりです。
DBT-2のnonspでは、
deliveryトランザクションの最初のクエリは、
"SELECT no_o_id FROM new_order WHERE no_w_id = %d AND no_d_id = %d"
ですが、最初の最小の no_o_id しか必要じゃないのでdbt2_sql_fetchrow()が決め打ちで一回呼ばれて終了です。
さて、(方式の相性の)問題はMySQL版のdbt2_sql_execute()の実装がmysql_store_result()を含むことです。なので、件のクエリはMySQL版だけ全件取得されます。全体のスループットが高ければ高いほどその件数は増えるので、MySQL版だけスケールしない結果となります。(性能が上がるほど、見た目のスループットは下がることがある)
考えられる修正としては2通りあります。
1. SQLを明示的に結果を1行にする。
"SELECT COALESCE(MIN(no_o_id),0) FROM new_order WHERE no_w_id = %d AND no_d_id = %d"
※経験上COALESCE()とMIN()が使えなかったRDBMSは無いのでこれで問題はない。
2. mysql_store_result() をやめる。
しかしDBT-2としてはどちらも直す気は無いようです。
そもそも、MySQLをちゃんとチューニングできる人なら、件のSQLが重いことに気づきTPC-Cの仕様を確認して直すはずです。
なので、DBT-2はMySQLの性能チューニングに無知な人が作ったもである、もしくは故意にMySQLの性能を低く見せる意図があると言えると思います。
このような陰険な工作は、逆に味方も騙され油断し、進歩が止まってしまうのではないでしょうか?
私は、このような正々堂々と切磋琢磨しない人たちを
「過去の忘れられた歴史として葬るほど、MySQL/InnoDBの性能を進歩させればいい」
それこそが正しい対抗策だと考えて前職・前前職で精進してきました。
しかし、今だにDBT-2でのRDBMS比較に触れる人がいると、当時名指しで現行犯で徹底批判しても良かったかなと思ったりします。(進歩のためにはそんな暇は無かったでしょうが。)
まぁ、とにかく一貫して言えるのは、「ベンチマークは自分でやるまで信じない」が安全なスタンスです。
未だに騙されている人が多いみたいなので、MySQL/InnoDBの名誉のために書き残さなければなりません。何度でも言いますが、性能比較は自分の目的とする処理をちゃんと比較しないとだめです。そうでなくては、騙されて本当は悪い性能のものを掴まされることになります。
DBT-2と言う(TPC-Cをベースにした)ベンチマークがありますが、数多のRDBMS(商用/OSS双方)に対して独自にTPC-Cベンチマークを実装・チューニングして比較した経験のある私から見て、怪しい結果しか出ないので、長年、基本無視のスタンスを取っています。
が、3年前にあろうことかMySQLの性能QAがDBT-2(nonsp:mysql)を利用していて、とある性能FIXに対して問題を指摘してきたのを契機にDBT-2のバグ(MySQL版にだけマイナスに働く)を調べて性能QAのチェックを改めてもらったことがあります。
その情報は、DBT-2の開発側にもフィードバックされたと記憶していますが、3年経った今もバグはそのままです。故意か過失かは不明ですが、DBT-2をMySQLを貶めるために嬉々として利用してきた人には悪意を感じてきました。
TPC-Cの仕様から見て間違っている箇所も何箇所かあるのですが、全RDBMSに共通な間違いはとりあえず触れません。
最も大きい問題の箇所は nonspでの、DELIVERY_1 クエリのあたりです。
DBT-2のnonspでは、
- dbt2_sql_execute()でクエリを発行
- dbt2_sql_fetchrow()で各行を取得
- dbt2_sql_close_cursor()で終了
deliveryトランザクションの最初のクエリは、
"SELECT no_o_id FROM new_order WHERE no_w_id = %d AND no_d_id = %d"
ですが、最初の最小の no_o_id しか必要じゃないのでdbt2_sql_fetchrow()が決め打ちで一回呼ばれて終了です。
さて、(方式の相性の)問題はMySQL版のdbt2_sql_execute()の実装がmysql_store_result()を含むことです。なので、件のクエリはMySQL版だけ全件取得されます。全体のスループットが高ければ高いほどその件数は増えるので、MySQL版だけスケールしない結果となります。(性能が上がるほど、見た目のスループットは下がることがある)
考えられる修正としては2通りあります。
1. SQLを明示的に結果を1行にする。
"SELECT COALESCE(MIN(no_o_id),0) FROM new_order WHERE no_w_id = %d AND no_d_id = %d"
※経験上COALESCE()とMIN()が使えなかったRDBMSは無いのでこれで問題はない。
2. mysql_store_result() をやめる。
しかしDBT-2としてはどちらも直す気は無いようです。
そもそも、MySQLをちゃんとチューニングできる人なら、件のSQLが重いことに気づきTPC-Cの仕様を確認して直すはずです。
なので、DBT-2はMySQLの性能チューニングに無知な人が作ったもである、もしくは故意にMySQLの性能を低く見せる意図があると言えると思います。
このような陰険な工作は、逆に味方も騙され油断し、進歩が止まってしまうのではないでしょうか?
私は、このような正々堂々と切磋琢磨しない人たちを
「過去の忘れられた歴史として葬るほど、MySQL/InnoDBの性能を進歩させればいい」
それこそが正しい対抗策だと考えて前職・前前職で精進してきました。
しかし、今だにDBT-2でのRDBMS比較に触れる人がいると、当時名指しで現行犯で徹底批判しても良かったかなと思ったりします。(進歩のためにはそんな暇は無かったでしょうが。)
まぁ、とにかく一貫して言えるのは、「ベンチマークは自分でやるまで信じない」が安全なスタンスです。
2015年6月8日月曜日
MySQLプラグイン作成時のWindows版だけにある「シンボルの壁」
また、需要が無いかも知れない情報かもですけど、嵌ったのでメモ代わりに残しておきます。ちなみに、コンパイラの種類、バージョン、オプションをちゃんと合わせないとアライメントずれでバイナリ互換が無くなる等のプラグイン作成基礎知識には触れません。普段Linuxの人が、気楽にWindows版も対応しようとした場合の落とし穴についてのみです。あと、私のon Windows開発歴はほぼ0なので間違っていたらごめんなさい。
MySQLプラグインは基本的に、プラグイン側からmysqld側のシンボルを参照することが多いです。例えばストレージエンジンプラグインならmysqldの中のhandlerクラスを継承して実装する必要があったり、プラグインからmysqldの状態を知ろうとすれば、そのグローバル変数やら、保護しているmutexやらも必要になります。
基本的にLinuxでは、同じヘッダファイルさえ読めば公式配布バイナリでも大概のものにアクセスできますが、Windowsではそうはいきません。Windowsの実行形式(exeとかdllとか)では、ダイナミックリンク時にどのシンボルを見せるか(EXPORT)、見に行くか(IMPORT)を明示的に指定する必要があります。デバッグ用のシンボル(.pdb)はプラグイン読み込み時には関係ありません。
mysqld.exeからプラグインdllを読みに行く場合は、プラグインの情報を記述した構造体を見に行くだけですから問題にはあまりならないでしょう。(プラグインのサンプルに則ればプラグイン側の必要な情報はEXPORTされるはずで、変更の必要はまず無いはず。)
問題はその逆です。mysqld.exeのどのシンボルがプラグインから見えるかは、mysqld.exe のリンク時に決まります。ソースツリーを見ると、mysqld.exe のリンク時に mysqld.def を作って読ませています。win/create_def_file.js はその時に利用される、.libファイルからシンボルを抽出して .def ファイル形式に変換するもので、それは sql/CMakeLists.txt の中で使用されています。その対象は、
"FOREACH (CORELIB sql binlog slave mysys mysys_ssl dbug strings binlogevents_static)"
となっており、innobase.lib は含まれていないので、通常は mysqld.exe のInnoDB関連にはアクセスできません。この行に 'innobase' を加えると、InnoDBのシンボルも参照可能な mysqld.exe がビルドされます。
また、プラグイン側も適切にIMPORT設定しなければいけません。mysqld.exe のリンクの際に、リンクライブラリmysqld.lib(EXPORTの確認用ライブラリ)が出力され、プラグインの作成時にリンクされるようになっていて、エラーが出るので取りこぼすことは無いと思います。リンク時にエラーが出た参照は、インクルードファイルで 'extern' で読むところを、'extern MYSQL_PLUGIN_IMPORT' として明示的にIMPORTする必要があります(実体は"__declspec(dllimport)")。これはプラグイン側だけの問題で、mysqld.exeのビルドには関係しません。
という以上の事情から、たとえば「InnoDBの内部が見れる便利なinformation_schema作ったよ。みんな使ってみて!」と言っても(いや、まだ作ってないですが…)、Windows版だけは折角プラグインとしてビルドしても、mysqld.exeもビルドしなおさなければいけないわけです。
一応バグとして上げましたが、需要が無ければ直るかどうか。。。(Bug#77251)
直れば色々某プラグインとかもWindows用InnoDBダイレクトアクセス版とか作れるようになるのと思うのですががが。(handlerから使うとMySQL形式-InnoDB形式変換が意外と重かったと思うので。) いや、Linux版だけなら作れるわけですががが。
MySQLプラグインは基本的に、プラグイン側からmysqld側のシンボルを参照することが多いです。例えばストレージエンジンプラグインならmysqldの中のhandlerクラスを継承して実装する必要があったり、プラグインからmysqldの状態を知ろうとすれば、そのグローバル変数やら、保護しているmutexやらも必要になります。
基本的にLinuxでは、同じヘッダファイルさえ読めば公式配布バイナリでも大概のものにアクセスできますが、Windowsではそうはいきません。Windowsの実行形式(exeとかdllとか)では、ダイナミックリンク時にどのシンボルを見せるか(EXPORT)、見に行くか(IMPORT)を明示的に指定する必要があります。デバッグ用のシンボル(.pdb)はプラグイン読み込み時には関係ありません。
mysqld.exeからプラグインdllを読みに行く場合は、プラグインの情報を記述した構造体を見に行くだけですから問題にはあまりならないでしょう。(プラグインのサンプルに則ればプラグイン側の必要な情報はEXPORTされるはずで、変更の必要はまず無いはず。)
問題はその逆です。mysqld.exeのどのシンボルがプラグインから見えるかは、mysqld.exe のリンク時に決まります。ソースツリーを見ると、mysqld.exe のリンク時に mysqld.def を作って読ませています。win/create_def_file.js はその時に利用される、.libファイルからシンボルを抽出して .def ファイル形式に変換するもので、それは sql/CMakeLists.txt の中で使用されています。その対象は、
"FOREACH (CORELIB sql binlog slave mysys mysys_ssl dbug strings binlogevents_static)"
となっており、innobase.lib は含まれていないので、通常は mysqld.exe のInnoDB関連にはアクセスできません。この行に 'innobase' を加えると、InnoDBのシンボルも参照可能な mysqld.exe がビルドされます。
また、プラグイン側も適切にIMPORT設定しなければいけません。mysqld.exe のリンクの際に、リンクライブラリmysqld.lib(EXPORTの確認用ライブラリ)が出力され、プラグインの作成時にリンクされるようになっていて、エラーが出るので取りこぼすことは無いと思います。リンク時にエラーが出た参照は、インクルードファイルで 'extern' で読むところを、'extern MYSQL_PLUGIN_IMPORT' として明示的にIMPORTする必要があります(実体は"__declspec(dllimport)")。これはプラグイン側だけの問題で、mysqld.exeのビルドには関係しません。
という以上の事情から、たとえば「InnoDBの内部が見れる便利なinformation_schema作ったよ。みんな使ってみて!」と言っても(いや、まだ作ってないですが…)、Windows版だけは折角プラグインとしてビルドしても、mysqld.exeもビルドしなおさなければいけないわけです。
一応バグとして上げましたが、需要が無ければ直るかどうか。。。(Bug#77251)
直れば色々某プラグインとかもWindows用InnoDBダイレクトアクセス版とか作れるようになるのと思うのですががが。(handlerから使うとMySQL形式-InnoDB形式変換が意外と重かったと思うので。) いや、Linux版だけなら作れるわけですががが。
登録:
投稿 (Atom)