2014-12-18

はじめに

@making さんからオファーをいただいて、12/17 (水) の JJUG ナイトセミナーで Java でカジュアルに機械学習するお話をしてきました。

Java というコミュニティ的に機械学習ガチ勢の方が圧倒的に少ないことが予想されたので、あんまり小難しい技術の話は含めず初学者向けの内容構成とし、機械学習をシステムに組み込んでいく上でこれは大事だよね… と個人的に思っているポイントを整理してスライドにしていったら、文字が多くなってしまった上に若干エモくて Java 的な話がほんのり程度になってしまったのが今回の反省点です。

なお講演時の動画が YouTube にアップロードされています ので、より詳しい話を知りたい場合はこちらをご覧ください。

Java と機械学習

Java で動く機械学習ライブラリ・フレームワークはわりと数多あって、でも Java で機械学習を利用する際のワークフローをすべてカバーするのがいいかというとそんなことはない、と私は考えています。

特に、モデルを作る際の feature enginnering やそのモデルの評価など、試行錯誤的に・繰り返し手早く実行したい操作については、Java のソースコードをコンパイルして一連の処理を最初から実行する… みたいなことをするよりも、R や IPython (Notebook) などを使ってインタラクティブ・アドホックな操作ができたほうが断然効率がいいのではないでしょうか (僕は R も IPython もほとんど使ったことないので、この辺りは僕の主観が色濃く出ています…)。

じゃあ Java で機械学習する価値がないか、というとそんなことはなくて、何だかんだで Java のコードはそこそこ速く動いてくれるので、サービス的に速度・レイテンシ的な要求がある場合や、機械学習を組み込もうとしているシステムが Java で作られているならば機械学習の部分も Java で統一したい… など主にシステム化以降のところで Java の機械学習を活用するのがいいんじゃないかと思っています。

指標値・メトリクスを設定して機械学習を活用する

これは懇親会の席でとある方から伺ったお話にもあったのですが、ビジネス的 or 精度的なメトリクスを設定せず、ただ何となく流行っているから的な理由で機械学習を導入する… という話をちらほらと耳にします。

わりと身近なレコメンデーション一つをとっても、そのレコメンデーションによって全体の売上が何 % 向上したのか、みたいなビジネス上の指標値を設定することは可能だと思います。そのような指標値をせずに機械学習を導入してしまうと、それはチューニングはできない・機械学習アルゴリズムをおいそれと変更することもできない、ただただ辛みのつまったパンドラの箱に成り下がってしまうので、僕の身の回りでこういうことにならないように、気をつけないといけないよなー、と思った次第でした。

まとめ・感想

JJUG 主催のセミナーにはいままで聴講者としてたまに顔を出していましたが、まさか機械学習のネタで登壇する日が来るとは思ってもみませんでした (以前の納涼 LT 大会にスピーカーとしてエントリしたものの、会社イベントを優先する必要があって止む無く断念したこともありましたが…)。

JJUG は渋谷java ほど カジュアル (= 自分の好き勝手・気ままに話したいことを話す) なコミュニティではないので、発表する内容は理解を優先した構成にしてみました。その分、「この内容は果たして聴講者に伝わるんであろうか」とヤキモキしたりすることもあって、久々に資料作りで消耗しました。でも楽しかったですね!

2014-12-13

はじめに

会社の合宿予定が入っていて参加できそうになかったので、やむなく参加を見送っていた 12/13 (土) の 第九回 渋谷java でしたが、思ったより早く東京に戻ってくることができたので、下記のように

と参加できそうなのかつぶやいてみたところ、カジュアル無茶ぶりコメントをたけぞー先生よりいただいたので適当にネタをでっちあげて LT 発表してきたのでした。

SmartNews ♥ Slack

@amachino さんが先日 Slack の Advent calendar に投稿したとおり、スマートニュースでは 社内のコミュニケーションに Slack を活用しています。 通常の人同士の会話だけでなく Jenkins からの通知や Datadog からのアラートなども Slack に集約しており、普段扱っているコミュニケーションツールの中でも特段利用時間が長い状況になっています。

そのため、「プロダクション環境で動いている Java アプリケーションの実行状況・進捗や、例外が発生したときの詳細を Slack 経由で見たい・知りたい」という要望が Java エンジニア的には自然と沸き起こってくるわけです。

Logback の Appender を実装する

そういうわけで、Java アプリケーションからお手軽に Slack にメッセージをポストする方法として、Logback の Appender 実装を用意する方法を紹介しました。

プロダクション環境で利用するにあたって、HTTP(S) 経由で Slack API を呼び出す際に

  • ロガーメソッドを呼び出す元の実行をブロックしないように (すぐに制御が戻るように) 非同期処理する
  • 非同期処理で Slack API を叩くにしてもログのイベントが前後するのは嫌なので (Array)BlockingQueue を使ってログイベントを直列化する
  • HTTP 通信で失敗したときのためにリトライ処理を入れる

などの仕組みを入れ込んだ実装としています (発表中にお見せしたコードはまだ整備しきれていないので現在は非公開としていますが、いずれ公開する予定です)。

会場参加者からのツッコミ

これは発表の際に java-ja から来られた方 からいただいたツッコミなのですが、Appender 内部で非同期処理をする場合は、 ContextAwareBase クラス (AppenderBase クラスの親クラス) が context フィールドで保持しているオブジェクトの Context#getExecutorService() メソッドの戻り値を使って、その ExecutorService オブジェクト経由でスレッドを立ち上げるといいそうです。

まとめ・感想

  • Logback の Appender 実装はツボさえ押さえれば、あんまり難しくないよ
  • Logback のインタフェースで Slack API を叩けるようになれば、Logback 使っている既存の Java アプリケーションなら容易に Slack に通知を出せるようになるね!
  • java-ja から来た人、ちょっとコワイけど、丁寧な説明だったりアドバイスをいただけたりと、とっても親切だね!

2014-10-22

最近 Redshift を触っていて、圧縮エンコードについて調べることがあったのでメモメモしておきます。なお、2014 年 10 月時点での情報であることと、わりとざっくりとした確認だったので不正確な情報が混じっているかもしれないのでご承知おきください。

文字列データ (VARCHAR など)

  • 選択肢としては、 text255, text32k, bytedict, lzo あたり
    • 空間効率的には text255 text32k はあんまりよろしくなさげ
    • また、text255 はカラムサイズが 255 を超えるカラムに適用することができない (厳密には、255 バイトを超える文字列が入っている場合に適用できない、となる)
  • 値の種類数が少なく (目安として、256 個以下ぐらい)、かつ種類が増える可能性が低い場合
    • bytedict もしくは lzo を選ぶのがよい
    • 種類数が 256 を超える場合であっても、出現頻度に偏りがある場合は bytedict を選択するのもありっぽい
    • ただし、文字列の平均長が長い場合は bytedict はあんまり性能よろしくない?
  • 最初は値の種類数が少ないが、時間経過とともに使われる値の種類が徐々に増える場合
    • lzo を選ぶのがよい
  • 時間経過とともに使われる値の種類が増えるが、ソートキーを考慮すると使われる値の種類が少数に限定されると想定できる場合
    • lzo を選ぶのがよい
  • 値の種類数が多い場合
    • lzo を選ぶのがよい

というわけで、迷ったら lzo を選べばだいたいいい感じだと思う。

数値データ

  • 選択肢としては、 bytedict, delta/delta32k, mostly8/16/32 あたり
    • lzo はあんまり向いてなさげ
  • 値の種類数が少なく、かつ種類が増える可能性が低い場合
    • 0 付近の値が頻出する場合は mostly8 を選ぶのがよい
    • それ以外では bytedict を選ぶのがよい
  • 時間経過とともに使われる値の種類が増えるが、ソートキーを考慮すると使われる値の種類が少数に限定されると想定できる場合
    • bytedict でよさそう
  • 値の種類数は多いが、出現する値に偏りがある場合
    • 0 近辺に偏っているなら、 mostly8/16/32 などを選ぶのがよい
    • それ以外なら bytedict がよさそう
  • ソートキーを考慮したときに値が昇順に並ぶ場合
    • delta/delta32k を選ぶのがよい
  • それ以外の場合
    • 諦めて、圧縮エンコードは設定しない (raw にする)

正直言って、数値データは実際のデータで性能を確認してみないとわからないと思う…

その他

  • runlength の使いどころがよくわからない…
  • 時間計算量
    • bytedict は時間計算量ちょっと高めな傾向がみられる
    • lzo はバランスがとれている感じ

2014-09-21

はじめに

毎回、他の発表者の方々が有益情報を発表してくれる中で僕一人が誰得情報をひたすら発信しているわけですが、今回もご多分に漏れず Java 8 で認証系を自作する、というニッチな話をしてきました。

昨今のパスワード流出系のセキュリティインシデントが業界内で話題になるたびに「認証系なんてものは自作するようなもんじゃない」という認識が醸成されつつあると思います。しかし、マイナー or オレオレ Web アプリケーションフレームワークなどを利用しているとそうもいってはおられず、認証系が用意されていないときはやむなく認証系の自前実装が要求されることもあるでしょう。自分自身も周りを見渡していても、過去何度かそういうことがありました。

ただ、実装者の認証セキュリティに対する認識や知識によって脆弱な認証系を作り込むことも少なくなく、そういうのをなるべく減らしたいなー、という思いを込めて今回の発表に至りました。

どうしても認証系を自前実装しなければならないときに気をつけたいこと

前述した発表資料に書いてあることにはなりますが、今一度文章に起こしておきます。

アカウント別 salt の生成と暗号論的擬似乱数生成器の利用

セキュリティを少しかじったことがある人であれば「salt ってやつとパスワードを組み合わせたものをハッシュする」ということぐらいは知っているものと思います。ただ、どうも salt だけがひとり歩きしていることがあったりして、アプリケーションで共通の事前に用意された salt を使いまわす、みたいなアレな実装を見たことがある人はそこそこいるんじゃないでしょうか?

本来は、認証単位であるアカウント別に salt を生成すべきであり、かつ salt の生成には暗号論的擬似乱数生成器を用いるべきです。この salt の生成を Java で実現するならば、

    static byte[] newSalt(int length) {
        try {
            byte[] result = new byte[length];

            SecureRandom.getInstance(
                    // "NativePRNGNonBlocking"
                    // "NativePRNGBlocking"
                    "SHA1PRNG"
            ).nextBytes(result);

            return result;

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

のような実装になることでしょう。 SecureRandom.getInstance() の呼び出しで指定しているのは擬似乱数生成アルゴリズムで、Java 7 以前は SHA1PRNG だけだったのが、Java 8 では新たに

  • NativePRNG
  • NativePRNGBlocking
  • NativePRNGNonBlocking

が (Windows を除く) *nix-like なプラットフォーム向け JRE にて導入されたようです。

暗号学的ハッシュ関数の利用とストレッチング

salt を生成したら、あとはパスワードと組み合わせてハッシュ化、なのですが、これも単に SHA-* を使えばいい、というほど簡単ではありません。

結論から言ってしまえば、PBKDF2 などのキー派生 (導出?) 関数を使ってストレッチングすべき、となります。

Java 7 では PBKDF2 の実装は PBKDF2WithHmacSHA1 しか用意されていませんでしたが、Java 8 にて

  • PBKDF2WithHmacSHA224
  • PBKDF2WithHmacSHA256
  • PBKDF2WithHmacSHA384
  • PBKDF2WithHmacSHA512

の 4 つが導入されました。この PBKDF2 を用いたパスワードのハッシュ化コードは以下になります。

    static byte[] hash(String password, byte[] salt, int numIterations, int numHashLength) {
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, numIterations, numHashLength);

        SecretKeyFactory factory;
        try {
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

        try {
            return factory.generateSecret(keySpec)
                    .getEncoded();

        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

まとめ

なんだかんだ書いてはいますが、やっぱり僕含めて素人は認証系の実装をするのはやめたほうがいいですね (過去何度もダメな認証系を作ってしまったし…)。

既存ライブラリの Apache Shiro とか Spring Security とか、僕は使ったことないですが、多分このあたりの面倒なお仕事をこなしえくれるのではないでしょうか。

なお、

こういうライブラリもあるようですよ!

参考文献

2014-09-17

VM 上の Ubuntu に apt-get で mysql-server-5.6 をインストールし、VM の外から VM 内部の mysqld に接続しようとしたときにこのエラーが発生した。

原因は my.cnf に

bind-address = 127.0.0.1
が設定されていたためであった。

この行を my.cnf から削りとって、ようやくVM 外部から mysqld に接続できるようになったけど、毎回こんな感じのトラブルで時間を喰われてしまっているわけで、MySQL 力が低いと何かと生きるのが辛い…

2014-09-15

「えーマジ native2ascii!?」「プロパティエディタプラグインが許されるのは J2SE 5.0 までだよねー」

ジャバエンジニアはそろそろ、プロパティファイルを扱う場合に native2ascii やら各種プロパティエディタプラグインやらの呪縛から解放されてもいいと思うのです…

概要

Java でアプリケーションの各種設定を記述したプロパティファイル (.properties) を取り扱う場合、 Properties クラスもしくは ResourceBundle クラスのお世話になるかと思います。

一昔前までは、プロパティファイル内で日本語などいわゆるマルチバイト文字を含む場合に、 native2ascii コマンドを利用して ASCII コードの文字のみで表現できる ユニコードエスケープ (!= UTF-xx エンコーディング) 表現に変換することが必要とされていました。

しかし、Java は 7 or 8 がメインストリームになり、また UTF-8 エンコーディングされたテキストを取り扱うことがわりと当たり前になりつつある昨今、いまだに Eclispe にわざわざプロパティエディタプラグインを導入してプロパティファイルをしこしこ編集したり、プロダクション環境でユニコードエスケープされたプロパティファイルを四苦八苦しながらパラメータ調整したりする現場があったりなかったりするようなので、ここで native2ascii とかプロパティエディタプラグインを必要と せずに UTF-8 エンコーディングされたプロパティファイルを扱う方法についてメモしておきます。

UTF-8 エンコーディングされたプロパティファイルを取り扱う方法

Properties クラスと ResourceBundle クラスそれぞれについて説明します。

その 1 : Properties クラスを使う場合

Properties#load(Reader) メソッドを呼び出す際の Reader オブジェクトを、UTF-8 エンコーディング指定した InputStreamReader オブジェクトにすれば OK です。

すなわち、以下のような実装になるでしょう。

その 2 : ResourceBundle クラスを使う場合

こちらは若干複雑になりますが、 ResourceBundle.Control クラスのサブクラスを用意して対処する方法をとります。

同クラスの newBundle() メソッドのオーバーライド実装にて、 Properties のときと同様に UTF-8 エンコーディング指定した InputStreamReader オブジェクトを用意し、 PropertyResourceBundle クラスのコンストラクタ引数にそのオブジェクトを指定します。

そして実際に ResourceBundle クラスのオブジェクトを取得する際に、その ResourceBundle.Control サブクラスのオブジェクトを ResourceBundle.getBundle(String, ResourceBundle.Control) メソッドの二つ目の引数に指定してやることで、UTF-8 エンコーディングなプロパティファイルを取り扱うことができるようになります。

実装は以下のとおり。

まとめ

  • PropertiesResourceBundle もどちらも、文字エンコーディング指定した InputStreamReader オブジェクトを扱うようにすれば、 native2ascii 的な呪縛から逃れることができるよ
  • UTF-8 エンコーディングなどにしておけば、プロダクション環境上でプロパティファイルをどうしても確認しなきゃいけなくなった場合でも、プロパティファイルに記述した日本語コメントを頼りにスムーズな確認ができるようになるよね
  • プロパティエディタプラグインはもうオワコン扱いでいいよね

2014-09-11

「何をいまさら」な感じの機能追加だけど、Java 8 から 標準のクラスライブラリで Base64 エンコーディング できるようになった。これはこれで嬉しいことなので、使い方とそのパフォーマンスをメモっておく。

使い方

まずはデモコードから。

エンコーダは以下のメソッド呼び出しで得られる 3 つの種類がある。

  • Base64.getEncoder()
  • Base64.getUrlEncoder()
  • Base64.getMimeEncoder()

対応するデコーダがそれぞれ用意されている。

  • Base64.getDecoder()
  • Base64.getUrlDecoder()
  • Base64.getMimeDecoder()

Base64.getUrlEncoder() で得られるエンコード文字列は、普通の Base64 エンコーディングではファイルパスや URI での利用時に問題となりうる +/ の文字の代わりに -_ を使ってエンコードしてくれる。「Base64 エンコーディングした文字列をファイル名に利用したい!」みたいなケースだととっても有用だと思う。

パフォーマンス

Java 7 以前は Apache Commons CodecBase64 クラス を使うことが一般的だったぽいので、それとの性能比較をしてみた。

今回は、メソッド呼び出しにおけるオーバーヘッドを見るスイート (上段) と、Base64 の変換処理の性能を見るスイート (下段) の二種類を用意した。

この結果から、

  • 全般的に、Java 8 で導入された Base64 クラスの方が処理性能的に優れている
  • 特に、Java 8 側の変換メソッドを呼び出す際のオーバーヘッドが Commons Codec のそれより明らかに小さいことがわかる
  • 変換処理の性能は、エンコーディングが 6 倍程度、デコーディングが 2 倍ちょっと、Java 8 の Base64 クラスの方が速い

ということがわかるかと思う。

なお、性能測定に用いたプログラムは以下のとおり。

まとめ

  • Base64 だけについて言えば、Commons Codec はその役目を終えた感がある
  • これからは Java 8 の Base64 を積極的に使うのがよさそうだね!

2014-08-21

気づいたらいつの間にか Docker Official な MySQL イメージ が公開されていたので、ちょうどとある製品の検証目的で使い捨てられる MySQL の環境が欲しかったついでに試してみたところ、ちょっとハマって数時間を無駄にしてしまったことにカッとして書き殴ったメモ。

(photo by Mark Interrante)

達成したいこと

達成したかったことは以下のとおり。

  • お手軽にポイ捨てできる MySQL 環境を構築・用意したい
  • Dockerfile をシコシコつくるのは面倒だったので、既存の Docker イメージを利用したい
  • boot2docker はあんまりいい思い出がなかったので、Vagrant で構築した VM 上に Docker 環境を構築したい

やろうとしたこと

やろうとしたことの具体的な手順は以下のとおり。

  1. Ubuntu 14.04 の VM 環境を Vagrant で用意する
  2. 上記の VM 環境上に Docker をインストールする
  3. MySQL な Docker イメージを pull する
  4. pull した Docker イメージを run する

実際はどうだったのか?

1. Ubuntu 14.04 な VM 環境を Vagrant で用意する

Ubuntu 14.04 の daily Cloud Image を利用して vagrant init && vagrant up してみた。

Docker をインストール & docker pull するあたりまではうまくいったものの、MySQL イメージを docker run するところで、mysqld が起動しないという問題に遭遇してしまった。

以前、別件で orchardup/mysql の Docker イメージを使ったときはこんな問題は起こらなかったのになあ、と思いつつ docker logs container-id としてコンテナのログを確認してみると、

2014-08-20 16:31:09 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2014-08-20 16:31:10 1 [Warning] Buffered warning: Performance schema disabled (reason: init failed).

2014-08-20 16:31:10 1 [Note] Plugin 'FEDERATED' is disabled.
2014-08-20 16:31:11 1 [Note] InnoDB: Using atomics to ref count buffer pool pages
2014-08-20 16:31:11 1 [Note] InnoDB: The InnoDB memory heap is disabled
2014-08-20 16:31:11 1 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2014-08-20 16:31:11 1 [Note] InnoDB: Memory barrier is not used
2014-08-20 16:31:11 1 [Note] InnoDB: Compressed tables use zlib 1.2.3
2014-08-20 16:31:11 1 [Note] InnoDB: Using Linux native AIO
2014-08-20 16:31:11 1 [Note] InnoDB: Not using CPU crc32 instructions

…ログはここで途切れている。

2014-08-20 16:31:10 1 [Warning] Buffered warning: Performance schema disabled (reason: init failed).

の行が怪しいなあ、ということでこのエラーメッセージでぐぐってみると、Oracle のページ が引っかかった。

当該ページにある

you may have specified other Performance Schema variables with values too large for memory allocation to succeed.

を読む限り、「これメモリが足りないんじゃね?」疑惑が湧いてきたので、Vagrantfile を以下のようにいじいじして、メモリサイズを 1GB にしてみたら、ちゃんと起動するようになった。めでたしめでたし。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # (省略)

  config.vm.provider "virtualbox" do |vb|
    vb.customize [ "modifyvm", :id, "--memory", 1024 ]
  end

  # (省略)
end

2. 上記の VM 環境上に Docker をインストールする

apt-get install docker.io でインストールできる Docker は 0.9 だった。

せっかくなので新しいバージョンを使ってみたいので、

curl -s https://get.docker.io/ubuntu/ | sh

として Ubuntu 向け最新のバージョンをインストールするのであった。

3. MySQL な Docker イメージを pull する

以前使っていた orchardup/mysql は GitHub の README を見るとオワコン宣言されていたので、Docker official らしい Docker イメージに乗り換えるのであった。

4. pull した Docker イメージを run する

とりあえず

docker run \
  --name hogehoge \
  -e MYSQL_ROOT_PASSWORD=hogehoge_password \
  -d \
  mysql

で mysql イメージのコンテナを立ち上げて、Docker コンテナをホストしている Ubuntu 上の MySQL CLI で

mysql -u root -phogehoge_password -h 127.0.0.1

としてみたんだけど、

Can't connect to MySQL server on '127.0.0.1' (111)

などと悲しいことを仰る MySQL CLI さんに、僕は涙を浮かべずにはいられなかった。

ps コマンドでコンテナ上の mysqld が稼動していることは確認できたものの、それ以上のことはわからず試行錯誤したりいろいろぐぐったりしているうちに、「これコンテナのポートが開いてないんじゃね?」疑惑が湧いてきて Difference between “expose” and “publish” in docker のページにたどり着いた。

当該 Stackoverflow のページの回答をかいつまんで説明すると、

  • Dockerfile にて EXPOSE でポート番号を設定すると、コンテナ間でポートを通じた通信ができるようになる
    • コンテナ間の通信をしたい場合は、これを設定すべき
  • Dockerfile での EXPOSE のポート番号指定に加えて、 docker run する際の -p hostPort:containerPort を指定すると、 コンテナ側のポート containerPort をホストのポート hostPort でパブリッシュする
    • コンテナ間だけでなく、Docker の外の世界と通信したい場合は、 -p も設定すべき

ということになる。そんなわけで、

docker run \
  -p 3306:3306 \
  --name hogehoge \
  -e MYSQL_ROOT_PASSWORD=hogehoge_password \
  -d \
  mysql

としてコンテナを立ち上げてみたところ、ようやく MySQL CLI からコンテナ上の mysqld にご挨拶をするハッピーエンディングを迎えることができたのであった。

まとめ

Docker ネタのブログエントリって、たいてい Dockerfile の手組みから始まる系のやつが多い印象なんだけど、既存の Docker イメージを活用するカジュアル利用的なブログエントリももうちょっと増えてもいいと思う。

おまけ

できあがった Vagrantfile はこちら。

2014-06-10

はじめに

ちょこっとご報告が遅れてしまった気もしますが、タイトルにあるとおり、 @chezou さん主催の Machine Learning Casual Talks #1 @クックパッド社にて発表をしてきました。

当 Casual Talks 開催に至った背景は以下のとおり。

4/22(火)に開催された、QA エンジニア向けの勉強会、 第 4 回 Ques で登壇・発表したのをきっかけに、機械学習に関してカジュアル(≠初心者的)に話せる勉強会があるといいよね、という流れができ、その後の @chezou さんのご尽力もあってめでたく開催、となりました。主催の @chezou さんおよび会場提供のクックパッド社には感謝です!

発表内容について

今回の Casual Talks では、その Ques で QA エンジニアの方々向けに発表した内容を機械学習寄りのエンジニア向けに再構成・焼き直しして、機械学習なアプリケーションへのテスト自動化の適用について、基礎的なお話をさせていただきました。

僕自身、機械学習を用いたアプリケーションへのテスト自動化の取り組みは暗中模索な意味合いが強かったのですが、他の発表者の資料を拝見するに、思いのほか以下のモデルを参考にしていただいている方々が多いようで、ちょっと嬉しかったですね!

機械学習の分野で「テスト自動化」とか「品質」というと、肌感覚的に後回しにされがちな傾向があるように思うものの、僕個人の思いとしては他の機能と同様に、ちゃんとテスタビリティを高めたり再現性を確保したりと、品質維持のための努力をきちんとしていきたいなあ、と思うばかりであります。

もちろん、「テスト自動化」自体が直接的な利益を生み出すわけではないので、過剰に注力するのも考えものなんですけどね…

他の発表について

今回のトークの目玉はなんといっても @unnonouno さんの Jubatus における機械学習のテスト でした。「あーそうそうあるあるあるよねーそれ(白目」と、自分の古傷をえぐられてる感じがしたのはきっと気のせいでして、パラメータ一つの設定方法にしても、論文を是とするのではなく利用者の観点に立って考えた時に整合性のあるインタフェースを提供することが大事という考えにとても共感できました。

その他の登壇者の発表内容は各ブログに詳しく掲載されていますので、そちらをご参照ください。

雑感

"Casual Talks" の名にふさわしい、機械学習に関する自由な発表ができる場として、この Machine Learning Casual Talks はとても良い場所だと思います。「テスト自動化」という機械学習ならぬ発表のわりに connpass 上で登録されていた方々の人数も相当でしたし、第2回の登壇者もちょっとずつ決まって来つつある雰囲気があるので、次回開催も恐らく時間の問題だと思いますね!

2014-04-07

ようやく Ansible 始めたけど右も左も分からない初心者なので、忘れないようにメモっておきます。

ssh 接続に関する情報をどこに記述すればいいのか?

方法 1: .ssh/config に記述する

ssh コマンドが参照する .ssh/config ファイルに、ssh 接続に必要な情報を記述する方法です。

Host hoge.example.com
    HostName     hoge.example.com
    User         deployguy
    IdentityFile ~/.ssh/id_rsa.hoge.example.com

普段お使いの .ssh/config を Ansible にそのまま流用・活用することができるため、 開発者ごとにサーバへのログインアカウントが異なる場合に適している方法と言えるでしょう。

一方で、Ansible 自体のバージョン (1.2 以前) や OS にインストールされている OpenSSH のバージョン (具体的には、 ControlPersist をサポートしていないちょっと古いバージョン) によっては、 ssh コマンドではなく Paramiko という Python 製の SSH クライアント (?) が使われるようで、 その場合には .ssh/config が参照されませんので、注意が必要です。

上記の条件に当てはまる場合で、Paramiko ではなく ssh コマンドを使いたいときは、 以下のように Inventory ファイルで ansible_connection パラメータに ssh を指定すれば いいようです。

[server-hoge]
hoge.example.com  ansible_connection=ssh

方法 2: Ansible の Inventory ファイルに記述する

ansible-playbook コマンドのオプション -i で指定する Inventory ファイルに、 接続設定を記述する方法です。

[server-hoge]
hoge.example.com  ansible_ssh_user=deployguy  ansible_ssh_private_key_file=~/.ssh/id_rsa.hoge.example.com

各開発者の .ssh/config に依存しないことと、方法 1 にあったような Ansible / OpenSSH のバージョンによる ssh 接続手段の違いを意識する必要がないため、この Inventory ファイルをリポジトリで共有することで環境依存・属人的な状態を小さく保つことが出来るでしょう。

一方で、ログインアカウントの情報を Inventory ファイルに記述する必要があるため、開発者ごとにサーバへのログインアカウントが異なる場合にはこの方法はあまり向きません。

参考文献

2014-03-31

お手軽に各種バンディットアルゴリズムの特性・性能を比較することができる Web ベースのバンディットアルゴリズムシミュレータを、Yeoman, Bower, Grunt, D3.js, NVD3.js, AngularJS, Bootstrap なんかを使って作ってみましたよ、というお話です。

デモサイト&ソースコード

シミュレータのデモサイトとソースコードはそれぞれ以下になります。

動機

とあるお仕事で各種バンディットアルゴリズムの特性を調査・検証したことがあって、そのときは Java で各種アルゴリズム&シミュレータを実装 → TSV ファイルにシミュレーション結果を出力して Excel でグラフ化、なんてことをやっていました。けど、これが結構な手間でして、どうにかならないかなーと悩んでいたところで、「あれ、これって JS でバンディットアルゴリズムを実装しちゃえば、d3.js とか使って Web ブラウザでお手軽にシミュレーション&可視化できるんじゃね?」と思い立ったので作ってみた次第です。

あと最近、仕事・プライベート問わず JS に触れる機会がめっきり減ってしまったことで、フロントエンドの開発事情にめっきり疎くなてしまったこともあり、そんな状況に危機感を抱いていて作ってみた、という副次的な動機もあったりします。

所感

作ってみた所感など。

  • この程度の規模の Web アプリケーションでも AngularJS を使って開発をしておくと、モジュール構成を疎結合にできてよい感じです。
  • Bower の依存モジュールの管理・解決の仕組み、フロントエンド開発でもこういうことが実現できるのは素晴らしいですね!
  • Grunt は詳細機能を使いこなす程度に慣れるまでに、ちょっと学習コストが掛かりそう。JSON でもりもりと設定を書かなきゃいけないのはやっぱり辛い…
  • D3.js を生で取り扱うとなると、結構辛いですね… 特に AngularJS と統合して使うのはかなり苦労するかも。
  • でも、出来合いのチャート表示をしたいだけなら NVD3.js を組み合わせて使うのがおすすめ! これを使うとすごく楽ちんです!

ToDo

もうちょっと機能を追加したり改善したいと思っています。

  • 1,000 回のイテレーションでも結構重いので、進捗を表示したい
  • 2 腕バンディット問題のシミュレーションしかできていないけど、n 腕対応したい
  • 何回かシミュレーションした結果を箱ひげ図表示したい

2014-02-27

lz4-ruby の開発を進めるために、LZ4 の近況を確認してみたところ、 それなりに活発に開発されているようで変更点が多かったため、メモを兼ねてエントリにまとめてみました。

r76 から r113 までの変更履歴を追う形で、主だった変更を列挙していきます。

API が増えた

r76 の時点ではマクロを含めて 6 個だった API が、r113 では obsolete を除いても 36 個と大幅に増えました。

LZ4 の基本機能は以前とはそう変わらないものの、後述するストリーム処理用 API など、 利用シーンごとに適した API を拡充しているようです。

liblz4 が作成されるようになった

r111 より。

以前の LZ4 は Makefile はあれど make してもライブラリは生成されず、 そのためアプリケーションから LZ4 の圧縮・伸長機能を利用しようとすると、 アプリケーションのビルド時に lz4.c などをあわせてコンパイルし、 同ソースコードのオブジェクトファイルをリンクする必要がありました。

この方法では LZ4 のリビジョンをアップデートしてアプリケーションを 再ビルドする手順がちょっと複雑になるデメリットがありましたが、 r111 で更新された Makefile によって、ライブラリ liblz4.a / liblz4.so が生成されるようになり、この問題は多少緩和されたのではないかと思います。

ただ、make install でのライブラリ / ヘッダファイルのインストールには 対応しているものの、 yum やら apt-get でインストールできるわけではないので、 依存パッケージの自動的な解決などはまだ期待できません。

ストリーミング処理に対応した

r102 より。

以前の LZ4 では、圧縮したい単位で圧縮対象のデータを API に渡し、また圧縮後のデータを記録する ヒープを十分に割り当てした上でAPI を呼び出す必要がありました。 この API 構成は zlib などの API とは大きく異なり、利用ケースによっては使い辛いこともあったようです。 (圧縮・伸長の時間性能を稼ぐためには仕方のない API 設計ではあったのですが…)

r102 で導入されたストリーム処理の API によって、ストリーミング的な圧縮処理を実現することができるようになりました。 (詳しい利用方法などは確認中です…)

LZ4 HC の圧縮レベルを指定できるようになった

r113 より。

LZ4 には、時間的な圧縮処理性能を犠牲にしつつ、空間効率的によりよい (= 圧縮率を重視した) 結果を得るためのモード、 LZ4 HC が存在します。

直近のリビジョンである r113 にて、この LZ4 HC に「圧縮レベル」パラメータが追加され、 圧縮率と処理時間のトレードオフのバランスを開発者がチューニングできるようになりました。

圧縮・伸長の時間効率がさらに向上した

LZ4 の一番の特徴である高速な圧縮・伸長処理は継続的に改善されているようです。 特にプラットフォームやコンパイラごとの細かな最適化がなされている印象です。

まとめ

更新履歴 を見るとそれなりに開発が継続されているようですし、 イシュー を見てもまだやるべきことは残されているようなので、今後も開発は継続されていくことでしょう。まだまだ要注目ですね!

2014-01-21

はじめに

2.x 系が公開されているこのご時世に、やんごとなき理由により OpenCV 1.0.0 をソースコードビルド&インストールしなければならない状況、エンジニアの皆さんなら一度や二度は経験していますよね。ね?

今回、Ubuntu 12.04 LTS 上で OpenCV 1.0.0 を苦労しながらソースコードからビルドしてみたので、そのメモを残しておきます。

1. パッケージのインストール

パッケージのインストールなどを先に済ませます。個人的な好みで aptitude を使っていますが、 apt-get でも同様にインストールできるはずです。

$ sudo aptitude install build-essential

# コンパイルエラー/リンクエラーを阻止するために、これはインストール必須。
$ sudo aptitude install libv4l-dev
$ sudo ln -s /usr/include/libv4l1-videodev.h /usr/include/linux/videodev.h

# 必要に応じて、取捨選択する
$ sudo aptitude install libjpeg-dev libpng-dev

2. OpoenCV 1.0.0 のダウンロード

ブラウザから、こちら のページより tar.gz ファイルをダウンロードし、適当な作業用ディレクトリ上に展開しておきます。

3. OpenCV をビルドする

まずは作業用ディレクトリ上で

$ ./configure

します。問題なく実行完了がしたところで、 make ... をする前に、こちら のページの "1) Error message:" にあるとおり、 cxcore/include/cxmisc.h の条件式なしの #elif#else に書き換えておきます。この書き換えをしておかないと、早々にコンパイルエラーが発生してしまいます (残りの 2, 3 の Error message については、 libv4l-dev パッケージのインストールとその後のシンボリックリンク作成で対処できるようなので、スキップして構いません)。

cxmisc.h の書き換えを終えたところでようやく

$ make
$ sudo make install

でインストールは完了します。