Rest Term

Blog

常時SSL化とHTTP/2対応しました

ドメイン rest-term.com の常時SSL化とHTTP/2に対応しました。

本サイトのコンテンツはCloudflareで配信しているので常時SSL化自体は簡単でした。後はコンテンツ内でのhttp/https混在(mixed content)を解消したり、パフォーマンス改善のためにWordPressプラグインを整理したり、一部は自作したものに置き換えたりとか細かい作業をしていました。SSL化関連の作業メモについてはWikiの方に書いてますので興味があれば。CloudflareだけでなくLet's Encryptの証明書発行作業等についても触れています。

現在、RSSフィードは旧URLと新URL(https://rest-term.com/feed/atom/)の両方を配信してます。いずれは新URLの方だけにする予定です。

HTTPS化したのでHTTP/2対応も併せて行いました。早くHTTP/2が当たり前の世界になって欲しいですね。

HTTPS化するとはてなブックマークなどのSNSへのシェア数がリセットされる問題は有名かと思いますが、SNS Count Cacheプラグインを利用すればHTTP時代の数値を合算表示させるのは簡単に実装できました。あとはGitHubバッジのウィジェットが重いのでセルフホスティングできるタイプのプラグインを自作しようかと思っています。

近い内に各Webブラウザが非HTTPSサイトを警告表示する取り組みが実施されるかと思いますが、常時SSL化が間に合って良かったです。まだ一部不具合が残っているページもあるかもしれませんが確認次第対応していきます。

 

Tags: ,

PythonのWebアプリケーションフレームワーク Sanicを試す

PythonのWebアプリケーションフレームワークについて、Flaskからの移行先としてSanicが有力そうなので調べています。

Sanic is a Flask-like Python 3.5+ web server that's written to go fast.

Sanicは著名なイベントループライブラリであるuvloopを利用しており、Node.jsのように非同期I/Oによる高効率なHTTPリクエスト処理が可能です。また、SanicはFlaskとよく似たシンタックスを提供しているため、他のフレームワークよりも移行コストを抑えることができそうです。

環境

サーバ環境

  • Ubuntu 16.04 (x86_64)
  • CPU 3.30GHz 4core / 8GB RAM
  • Python 3.6.4
  • Sanic 0.7.0 / Flask 0.12

クライアント環境

  • macOS High Sierra 10.13.4 (x86_64)
  • CPU 1.70GHz 4core / 8GB RAM

ネットワーク環境

注意点としてはSanicは asyncio および async/await 機能を利用しているためPython3.5以降が必要です。Python3移行は2018年現在なら世間的にもけっこう進んでいるとは思いますが、3.5以降となると業務で使っている人はまだ少ないかもしれません。

使い方

公式でFlask-likeと謳っているとおりインタフェースはFlaskとよく似ているので移行作業自体は楽だと思います。

Flaskでhello, world

Sanicだと以下のようになります。まさにFlask-likeですね。

上記SanicのコードではPython3.5以降で正式に利用できるasyncキーワードが現れています。C#やNode.JSなど他言語におけるasync/awaitと同等の機能が言語標準で提供されるようになりました。Sanicを使う場合は、個人的にNode.js開発でよくやってしまうawaitの置き忘れも気にしなくて良いのは助かります。

JSON対応

JSONレスポンスを返すには以下のように書きます。FlaskもSanicどちらも使いやすいと思います。パッケージ構成はSanicの方が整理されてる気はしますがどうでもいいレベル。

ちなみにJSONパーサーは独自実装ではなく外部モジュールが使われています。Flaskを使う場合はsimplejsonをセットで入れておくと良いです。Sanicはujsonが必須となっています。

  • Flask: simplejson or json
  • Sanic: ujson

JSONモジュールのベンチマークに関しては以下のサイトが参考になります。

ujson優勢みたいなのでJSONレスポンスを含めたベンチマークだとFlaskが不利だと思うので、テキスト("hello,world")のみを返すシンプルなコードで計測します。ただ、有利不利とか言ってしまうとuvloopに乗っかっているSanicはズルいという話になってしまいますけども。。

ベンチマーク

前述のサンプルコード('hello, world'文字列を返却)で組み込みHTTPサーバのベンチマーク。同一LAN内でクライアント/サーバの2台用意して計測、ツールはwrk2を利用しました。

ネットワーク周りのカーネルパラメータは以下の通りです。TCPのsyn backlogをsomaxconnの値と合わせて増やす程度のチューニングはしてあります。TCP接続を使い回す設定にもしていますが、今回のベンチマークでは意味ないですね。

まずは4スレッド同時100コネクション、2000rpsで30秒間負荷を与えました。

レイテンシ周りの計測値を見てみるとわかりますが、これだけでもかなり性能差があることが読み取れますね。低スペックのサーバで動かしているのですがSanicの方はまだまだ余裕がありそうです。次は4スレッド同時200コネクション、4000rpsで30秒間負荷を与えました。

Flaskの方は限界が見えてますね。タイムアウトも発生しています。ネットワーク周りのカーネルパラメータが未調整だと他のソケット関連エラー等も併せて出てくるはずです。一方でSanicの方は安定した性能です。uvloopすごい。

(横軸の数値が見えてないですが、1/ (1 - Percentile) 刻みの対数表示となっています)

おわりに

Pythonのasyncioに対応したフレームワークはSanic以外にもいくつかありますが、既存のFlask製アプリケーションの移行コストを最小化するために、Flask-likeなSanicを採用するのは悪くない選択だと思います。2,3年前にFlaskの代替として、Falconというフレームワークに移行する作業もやったことがあるのですが、使い方がFlaskと結構違うので移行コストが高かったです。その点、Sanicを使えばその辺りの面倒を省くことができますね。Flaskプラグインの移植性も高く、ベストプラクティスの流用も行いやすいのではないでしょうか。

また、Sanicはasyncioのdrop-in replacement実装としてuvloopを組み込んでおり、uvloop自体はlibuvをベースにしているので実績も十分、性能も保証されています。Python3.5以降が必要なので実業務で採用するのはまだ少しネックになりそうですが、そこは時間が解決してくれる部分でしょう(スタートアップのサービスとかなら勢いで採用しても良いかも?)。他にuvloopをベースにしたフレームワークとしてはjaprontoというのもあります。Flask-likeでなくても良いならこちらも選択肢としてアリですね。

まずはプライベートワークで作っているPython製WebAPIはSanicに移行してみようと思いました。前回紹介した pyvips(libvips) を利用すれば高速な画像処理サーバもPythonで簡単に書けそうです。

参考

 

Tags: ,

libvipsで高速省メモリな画像処理

今回は高速省メモリな画像処理ライブラリである libvips を使ってみます。

libvips is a demand-driven, horizontally threaded image processing library. Compared to similar libraries, libvips runs quickly and uses little memory. It has around 300 operations covering arithmetic, histograms, convolution, morphological operations, frequency filtering, colour, resampling, statistics and others. - libvips: A fast image processing library with low memory needs.

libvips自体は著名な画像処理プロダクトですが、各スクリプト言語のバインディングライブラリや公式サイトが2017年に刷新されたようで、今後はさらに利用が普及することが予想されます。また、PythonバインディングのpyvipsはTop 10 Python libraries of 2017でも紹介されており、注目度も高くなっています。今回はこのpyvipsを使って簡単な使い方を学びます。

環境

  • Ubuntu 16.04.3 LTS (Bash on Ubuntu on Windows10 Pro)
  • Python 3.6.3 (conda 4.3)
  • libvips 8.6.1
  • pyvips 2.0.4

導入

* apt を利用する場合

aptでインストールするのは簡単ですが、バージョンがけっこう古いようなので注意してください。それに必須ではない依存パッケージもいろいろインストールされるようなのでミニマリストの方にはあまり印象は良くないかも。最新版かつ好きな構成で使いたいならgithubからソースをダウンロードしてきて手動でコンパイル・インストールします。

* ソースからインストールする場合

./configure時に対応ファイルフォーマットが標準出力に表示されるので、必要な依存ライブラリ(libjpeg、libpngなど)も併せてインストールしておいてください。

* 確認

libvips本体のインストールが完了したら続けてPythonバインディングのpyvipsをインストールします。こちらはpipを使えば簡単に入ります。

libvips本体が事前にインストールされていない場合、pyvipsのimport時にOSErrorが出ます。

使い方

今回は動作確認を兼ねて画像入出力や簡単な画像処理を試してみます。以降はJupyter Notebookで書いたドキュメントを貼り付けます。

上記のように、pivipsとnumpy間のデータ相互変換(pyvips.Image <-> numpy.ndarray)が可能なので、OpenCVとの連携も簡単に行うことができそうですね。

おわりに

libvipsは比較的簡単な画像処理用途に特化したプロダクトで、高速省メモリな処理が特徴となっています(Speed and memory use)。もし画像処理を手軽に行いたいだけならOpenCVを導入するのは大袈裟なので、要件に応じて適切なプロダクトを採用したいですね。今回は導入部分の紹介でしたが、次回はより深掘りして調査できればと思います。

ちなみに今回のエントリーからJupyter Notebookで書いたドキュメントを時々記事に貼ろうと考えているので、GitHubにNotebook用のリポジトリを作りました。こちらも興味があれば。

 

Tags: ,

JavaScriptで機械学習の実装 5 Gradient Boosting

少し間が空いてしまいましたけど、今回はGradient Boosting (勾配ブースティング)と呼ばれる機械学習アルゴリズムを試してみます。機械学習関連のコンペでも大人気の手法ですね。かなり昔(2011年)に決定木をJavaScriptで実装したことはあるので勾配ブースティングも併せて学んでおこうと思います。

Gradient Boosting (勾配ブースティング)

ブースティングについてWikipediaを引用すると、

ブースティング(英: Boosting)とは、教師あり学習を実行するための機械学習メタアルゴリズムの一種。ブースティングは、Michael Kearns の提示した「一連の弱い学習機をまとめることで強い学習機を生成できるか?」という疑問に基づいている[1]。弱い学習機は、真の分類と若干の相関のある分類器と定義される。- ブースティング - Wikipedia

ブースティングは逐次的(弱学習器を1つずつ順番に構築)に弱学習器を構築していく手法で、新しい弱学習器を構築する際にはそれまでに構築された全ての弱学習器の予測結果を利用するという特徴があります。Gradient Boosting (勾配ブースティング)では正解値と弱学習器の予測値との差を負の勾配と見なして、それを最小化するように逐次的に弱学習器を学習させていきます。弱学習器として決定木が用いられるため Gradient Boosting Decision Tree (GBDT) と呼ばれます。ちなみにOpenCVに顔検出機能はAdaBoostと呼ばれるブースティングアルゴリズムを利用していますね。AdaBoostは学生時代に特にお世話になりました。

今回は回帰問題を扱うので弱学習器には回帰木を用います。JavaScript(ES2015)での学習処理部分の実装を抜粋しますが、ここを見るだけでも勾配ブースティングの特徴がよく表れているのではないでしょうか。

検証

データセットはBoston housingを用い、人口 1 人当たりの犯罪発生数やNOxの濃度などの情報からボストン市の住宅価格を予測します。

今回もJavaScript(ES2015)で実装したのですが、いつものようにscikit-learn風なインタフェースにしていて、パラメータのデフォルト値はscikit-learnのGBDT実装(sklearn.ensemble.GradientBoostingRegressor)を参考にしました。もちろん外からパラメータ変更もできます。また、ファイルからのデータ読み込み処理は省略してデータセット自体も1つのモジュールとして埋め込んでいます。ソースコードもいつものようにGitHubに置いてあるので興味があれば。

クライアント側では以下のように使います。

* 出力結果例

モデル評価用にRMSEやR^2スコア(決定係数)計算用のモジュールも併せて作りました。まずは残差プロットでざっくりとモデルの性能を眺めてみます。最近はGoogleスプレッドシートでグラフを描くことが増えてきました。さすがにExcelよりは機能不足ですけど簡単なグラフならすぐ作成できるので便利に使っています。

良い感じに残差が均一に分散しています。それなりに上手く学習できているようです。

定量的な確認も少しだけ。データをシャッフルして3回計測したRMSEとR^2スコア(決定係数)の平均値を載せます。GBDTの学習率は0.1で固定して、回帰木の本数(ブースティングの繰り返し回数)と深さを変更しました。

RMSE R^2 score (決定係数)
回帰木の本数: 100, 深さ: 3 3.503 0.817
回帰木の本数: 200, 深さ: 4 3.156 0.851

ある程度想定した通りの結果が出ているので問題なさそうです。パラメータ調整についてはまだ理論を理解しきれていない部分もあるので適当なのですが、いろいろパラメータを変えて試してみた所、回帰木は深くしすぎてもダメみたいです。3 ~ 5くらいがちょうど良いんでしょうか。

おわりに

勾配ブースティングは以前からずっと作ってみたいと思っていたので、今回実装する機会を得ることができて良かったです。ところでロシアのYandexが7月18日にCatBoostというすばらしい名前の勾配ブースティング実装を公開しました。今はこちらを使っていろいろ勉強中です。まだ日本語のドキュメント類は無いみたいなので情報がまとまったらCatBoostに関するエントリーを書くかもしれません。またよろしくおねがいします。

参考

 

Tags: , ,