読者です 読者をやめる 読者になる 読者になる

vimで折りたたみの状態を保存する方法

vimを使ってそれなりに経つのに、最近になって設定したのでメモ。

vimにはmkviewという、現在の折りたたみの状態を保存するコマンドと、 loadviewという保存した折りたたみ状態を読み込むコマンドがある。

もちろん自分でコマンドを入力するのはめんどくさいので、 ファイルを保存した時と読み込んだ時に勝手にコマンドを実行して欲しい。

ということでautocmdを使って、勝手に実行してくれるようにするわけだが、 先人が色々なことを考慮した上で mkviewloadviewを自動呼び出しする設定を 公開してくれている。

Hack #84: バッファの表示設定を保存する

細かい説明もリンク先に書いてあるので、自分はリンク先の設定をそのまま以下のようにコピペしただけ。

" Save fold settings.
autocmd BufWritePost * if expand('%') != '' && &buftype !~ 'nofile' | mkview | endif
autocmd BufRead * if expand('%') != '' && &buftype !~ 'nofile' | silent loadview | endif
" Don't save options.
set viewoptions-=options

1つ躓いたところとして、Go言語を開いて保存したときには、その時点での折りたたみが保存されずに 全て折りたたまれる、という現象が起きた。

少し探ってみると、vim-goの設定で、保存時に自動でfmtをかける機能がONなのが原因だった。

ということで、vim-goの設定の一部を以下のように変更して、Fmtコマンドを自動で行ってくれる設定を追加した。

" Not working
" let g:go_fmt_autosave = 1
let g:go_fmt_autosave = 0
autocm BufWritePre *.go silent Fmt

これで大規模なコードをvimで見てもストレスなくコードが書けるように!

機械学習の環境をDIGITSとDockerを使って簡単に整える話

機械学習が何をやっているのか全くわかっていなくても 簡単に画像処理の機械学習を行える環境を整えた話。

この記事の目標は、何もわからなくてもとりあえず画像を機械に学習させる、です。 (細かい部分が全くわからないことの言い訳)

今回は、Digitsという、NVIDIAが作っている Caffeという機械学習ライブラリのWebアプリを使ってみる。 また、めんどくさい導入部分を全部Dockerにやってもらう。

事前準備

  • docker-machine導入済み
  • docker-compose導入済み

docker-machineでdefaultという名前のマシンを作ってある前提で進めます。

docker-machineってなんだよって人は 公式ドキュメントのDocker Machineの項目を 読めばいいと思う。

Digitsが動いているコンテナを立てるまで

以下のdocker-compose.ymlファイルを適当なディレクトリに配置し、 同じ階層でdocker-compose up -dを実行する。以上。

実行が終わったらdocker-machine env defaultコマンドで出てくるIPに ポート5000でアクセスすればWebアプリが動いているのが確認できるはず。

これだけで画像の学習させる環境ができちゃう。便利。

# docker-compose.yml
digits:
  image:
    kaixhin/digits
  ports:
    - "5000:5000"
  volumes:
    - ./images/:/mnt/volumes/images

Docker HubにDigitsのコンテナイメージが あったので、ありがたく使わせて頂いた。

コマンドを実行した時に作られるimagesディレクトリは、 後で学習する画像ファイルを設置する場所になるのでそのままにしておく。 もし作成されてなかったら、imagesディレクトリを作成してから もう一回docker-compose up -d

学習させてみる

とりあえず、右上の「ログイン」ボタンからログインしておく。 出てきたフォームに適当に名前を入れるだけでOK。

データセットの作成

学習させるためには、データセットが必要なのでまずはそれの作成から。

自動で作成されたimagesの中に、学習させたい画像をディレクトリ毎にわけて設置する。 例としてオクスフォード大学で公開されている ペット画像のデータセットを使ってみる。

上記リンク先の「Dataset」から、圧縮されたペット画像をダウンロードして展開。 展開すると、images/ディレクトリとその下に37種類のペットの画像ができるはず。

展開された画像を、以下のようなディレクトリ構造になるように配置する。

ちなみに、全種類の動物を使うと非常に重いので、 お試しの人は、3、4種類ぐらいを配置すればいいと思う。

docker-compose.yml
images
├── Abyssinian
│   ├── Abyssinian_1.jpg
│   ├── Abyssinian_2.jpg
│   ├── Abyssinian_3.jpg
~ ~ ~
│   ├── Abyssinian_197.jpg
│   ├── Abyssinian_198.jpg
│   └── Abyssinian_199.jpg
├── Bengal
│   ├── Bengal_1.jpg
│   ├── Bengal_2.jpg
│   ├── Bengal_3.jpg
~ ~ ~
│   ├── Bengal_197.jpg
│   ├── Bengal_198.jpg
│   └── Bengal_199.jpg

こうすることで、それぞれのペットの画像がディレクトリ名の AbyssinianBengalといったラベルごとに分類されることになる。

次に、分類した画像からCaffeに流し込むためのデータセットを作成する。

下の画像のように、「New Dataset」の「Images」から、「Classification」を選択。

f:id:nametake-1009:20160707010753p:plain

「New Image Classification Dataset」というページに飛んだら、 下図の赤丸の部分を同じように編集する。

f:id:nametake-1009:20160707010759p:plainf:id:nametake-1009:20160707011028p:plain

編集をしたら、「Create」ボタンをクリック。 クリックしたら別ページに飛んでデータセットが作成され始める。

ダウンロードしたペット画像全部を使っていると ちょっと時間がかかるので少しだけ待つ。 飛ばされたページを定期的に更新すれば、今どれぐらい終わったかがわかる。

終わるまで待ったらデータセットの作成は終了。簡単!

モデルの作成

データセットができたら、それを学習したデータモデルを作成する。

データセットを作成した時の横にある「New Model」から、 同じようにClassificationを選択する。

以下の様な画面が出てくるので、赤丸の部分を同じように設定。 設定が終わったら名前をつけて「Create」をクリック。

f:id:nametake-1009:20160707010802p:plain

クリックすると、また別ページに遷移して学習が始まる。 終わったら学習終了。簡単!

学習結果を試す

モデルデータを作成する時に飛ばされるページの下部に、下図のようなフォームがある。

f:id:nametake-1009:20160707010804p:plain

図中の「Test a single image」の「Upload image」から識別させたい画像を選択して、 「Classify One」をクリック。

クリックすると別ページが開き、学習させたモデルから選択した画像が何に近いのかを、 教えてくれる。

下図だと柴犬ちゃんがしっかり「shiba inu」と認識されている。すごい。

f:id:nametake-1009:20160707010809p:plain

DigitsのRest API

なんとDigitsではRest APIが実装されている。

使い方はissueに書いてある通り。

コレを使えば割と簡単に学習結果をアプリケーションに反映させられる。

総括

機械学習の仕組みを全くわからなくても機械学習ができた!

hubotをSlackと連携してデーモン化するまでの手順

Hubotをデーモン化してSlackと連携させた時のメモ。

以下のツールは導入してあること前提。

  • apt
    • nodejs
    • npm
  • npm
    • hubot
    • pm2

hubotとSlackの初期設定

まず、以下のコマンドでhubotを準備。

$ mkdir nametake-bot
$ cd nametake-bot
$ yo hubot
                     _____________________________
                    /                             \
   //\              |      Extracting input for    |
  ////\    _____    |   self-replication process   |
 //////\  /_____\   \                             /
 ======= |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

? Owner: nametake <nametake.kyarabuki@gmail.com>
? Bot name: nametake-bot
? Description: A simple helpful robot for your Company
? Bot adapter: (campfire) slack # ここでSlackをしっかり指定する
? Bot adapter: slack

以下のコマンドで、ちゃんとhubotが動くか確認。

$ ./bin/hubot
~~~
いろいろ出る
~~~
nametake-bot> nametake-bot ping
nametake-bot> PONG

次に、SlackでhubotのConfigure Appsからhubotを連携させる。 連携させた後に、連携させた設定からHUBOT_SLACK_TOKENを取得。

取得したTOKENを元に環境変数を設定。

$ export HUBOT_SLACK_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

環境を設定したらSlackと連携。

$ ./bin/hubot -a slack

Slackで@nametake-bot pingとかやって連携しているか確認。

pm2でhubotをデーモン化

hubotを永続化するためにpm2というnode.js製のツールを使う。

pm2をjsonから起動するために、以下の様なapp.jsonファイルを作成。

{
    "apps":[
        {
            "name"             : "hilbot",
            "args"             : ["-a", "slack"],
            "script"           : "./bin/hubot",
            "exec_mode"        : "fork",
            "exec_interpreter" : "bash",
            "autorestart"      : true,
            "env": {
                "NODE_ENV"           : "production",
                "PORT"               : "8080",
            }
        }
    ]
}

HUBOT_SLACK_TOKENJSONにまとめておきたい場合は、 envの項目を以下のように書き換える。

"env": {
    "NODE_ENV"               : "production",
    "PORT"                   : "8080"
    "HUBOT_SLACK_TOKEN"      : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

以下のコマンドでpm2でhubotをデーモン化。

$ pm2 start app.json

以下のコマンドでちゃんと動いているか確認。

$ pm2 list
┌──────────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name     │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ nametake-bot │ 0  │ fork │ 32342 │ online │ 1       │ 4D     │ 79.945 MB   │ disabled │
└──────────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘

statusの欄がerroredになっていたら、pm2 logsというコマンドでログを確認して エラーを修正する。

今回はリモートリポジトリに上げたhubotのソースをSlack上からpullさせたかったので、 ここnew_update.coffeeを 丸々参考にさせて頂きました。

参考サイト

VimでOpenCVのneocompleteの補完が落ちる問題の解決

pyenvのanacondaで、condaで入れたopencvの補完をしようとすると、

Vim: Caught deadly signal SEGV
Vim: Finished.

と出て、落ちる問題の解決方法。

環境はOSX10.10。brewopencvが導入済み。 使用しているエディタはvimで、補完はneocomplete + jedi-vimで行っている、

結論だけ言うと、$PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/cv.pycv2.soを、/usr/local/Cellar/opencv/*/lib/python2.7/site-packagesにある cv.pycv2.soへのシンボリックリンクに置き換えることで解決した。

以下、全体のコマンド

$ cd $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/
$ mv cv.py cv.py.org    # バックアップ
$ mv cv2.so cv2.so.org  # バックアップ
$ ln -s /usr/local/Cellar/opencv/*/lib/python2.7/site-packages/cv.py $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/cv.py
$ ln -s /usr/local/Cellar/opencv/*/lib/python2.7/site-packages/cv2.so $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/cv2.so

ぶっちゃけ原因がわからなかったので教えて偉い人。

プロパティについて理解したのでまとめる

プロパティについて

研究室で質問されたのでPythonのプロパティについて解説しようと思う。

最初にプロパティとはなにかをIT用語辞典で引いていみると、

プロパティとは、オブジェクト指向プログラミングで使用される オブジェクトが保持している、そのオブジェクトの性質を表すデータ。 例えば、画像データのオブジェクトならば、高さや幅などのデータを プロパティとして持っている。

という定義になっている。 つまり、オブジェクト指向プログラミングでクラスのインスタンスから アクセスできるimg.widthとかimg.heightとかの、widthheightのことである。

しかし、カプセル化の概念からすると、直接プロパティをいじれてしまうのは良くない。 そこで、オブジェクト指向プログラミングでは以下のコードのような アクセサというメソッドとアクセス修飾子を使って、プロパティが 不用意にいじくりまわされるのを防いでいる。以下の例で言うと、 getWidthgetHeightsetWidthsetHeightがアクセサである。

class Image {
    private int width;
    private int height;

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

}

class Spam {
    public static void main(String args[]) {
        Image img = new Image();
        img.setWidth(200);
        img.setHeight(100);
        System.out.println(img.getWidth());
        System.out.println(img.getHeight());
    }
}

JavaC++などではこのようにしてプロパティへアクセス制御をしているが、 ただアクセスを制御したいだけなら、確かに冗長な感じはする。

PythonC#ではプロパティをインスタンス(使う側)からはメンバ変数に見えるが、 クラス(実装)ではコードブロック(メソッド)のようにして制御を行う。

具体的な例をPythonで示してみる。 以下のコードがプロパティを利用してアクセス制御を行っているコードになる。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Image(object):
    def __init__(self):
        self._width = 300
        self._height = 400

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, width):
        self._width = width

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, height):
        self._height = height

if __name__ == '__main__':
    img = Image()
    img.width = 200
    img.height = 100
    print img.width
    print img.height

クラスのメンバである_width_heightに対し、デコレータを利用してwidthメソッドや heightメソッドをproperty型に一度変換している(デコレータに関しては 前の記事を参照)。

propertyの第一引数がgetterになっているため、property型でデコレートした メソッドでは、そのプロパティが返したい値を返している。 また、Pythonのプロパティはsetterメソッドを持っているため、そのsetterメソッドで メソッドをデコレートすることで、img.width = 200のような形でアクセスできるように なっている。

今回の例では代入と読み出しの両方を行っていたが、例えば、 読み出しは許可するが、代入を禁止したい場合、以下のようにsetterメソッドが 呼び出されたら例外を投げるなどをして、プロパティへのアクセス制御が行える。

class Image(object):
    ~~~
    @width.setter
    def width(self, width):
        raise ValueError()
    ~~~

このようにプロパティを制御することで、クラスの設計者が意図しない値の代入や、 読み出し、不用意なプロパティの削除を防ぐことができる。

デコレータの基本について理解したのでまとめる

Pythonのデコレータに関して、理解していることをまとめてみる。

@を使ったデコレータの書き方はただのシンタックスシュガー (読み書きのしやすさのために導入される構文)。

それでは、何に対するシンタックスシュガーなのか。

まず、以下のようなプログラムがあるとする。

def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # egg

実行すると、当たり前だが以下の様な結果になる。

egg

次に、先ほどのファイルにscrambleというメソッドを追加する。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    scramble_egg = scramble(egg)
    print(scramble_egg()) # scramble egg!

scrambleメソッドは、オブジェクトを1つ受け取り、内部に定義されている_scrambleメソッドを返している。 つまり、scrambleメソッド自体は引数に対して何かを行うことはなく、 ただ内部のメソッドを返しているだけの機能しか持っていない。

返される内部メソッドの_scrambleは、scrambleメソッドが受け取ったの返り値に、 "scramble "と"!"という文字列を装飾(デコレート)して返している。

if __name__ == '__main__'では、scrambleメソッドにeggメソッド渡し、 返り値をscramble_eggという変数に格納している。 scrambleメソッドの返り値は_scrambleのため、scramble_egg変数は _scrambleメソッドの内容と同じ挙動をする。

そのため、ファイルを実行すると以下の様な実行結果になる。

scramble egg!

さて、if __name__ == '__main__'の中ではscrambleメソッドの返り値はscramble_eggという変数に格納していた。 ここで、すでに定義されているeggメソッドにscrambleの返り値を格納してみる。 Pythonはメソッドも変数も全てがオブジェクトのため、こういうことができる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    egg = scramble(egg) # この部分と
    print(egg())        # この部分

実行結果は上記と同じscramble egg!

eggに対してscrambleメソッドの返り値を格納しているため、eggメソッドは 元の「"egg"という文字列を返す」という機能が上書きされている。 しかし、ただ上書きされたわけではなく、scrambleメソッドに元の機能を引数として渡しているため、 「"egg"という文字列を返す」機能は、_scramble内で使われることになる。

この状態におけるscrambleメソッドがデコレータとなる。 文字通り別のメソッドを装飾することができる。

これまでの手順を@を使って書き換えると、以下のようなコードになる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

@scramble
def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # scramble egg!

Webサーバをapacheからnginxに移行

今までapacheで動かしていたWebサーバをnginxにようやく切り替えた。 その中でしたnginxの設定とか詰まったところとかメモ。

nginxの設定

全体に関わる設定

http://www.nametake.infoのように、特に設定をしていないサブドメインに アクセスがあっても、全部nametake.infoのページと同じ内容が表示されるようにしたい。 ということで、以下のようにdefault.confファイルを書いて、デフォルトのバーチャルホストを設定する。 また、server_name_にすることで、catch-allサーバとして設定。

default.conf
server {
    listen          80 default_server;
    server_name     _;
    root            /var/www/html;
    ~~~略~~~
}

あとは、nginxのバージョン番号とかOSの情報をレスポンスヘッダに含めたくないので、以下をnginx.confに追記。

nginx.conf
http {
    ~~~略~~~
    server_tokens off;
}

SSLに関わる設定

やりたかったこと

  • nametake.infoサブドメインで動かしているownCloudとかampacheの通信経路を暗号化したい
  • 設定ファイルはサービスごとに分割したい
  • owncloud.confとかampache.confとかに証明書の情報をいちいち書きたくない

やったこと

以下のようにSSL全体の設定も記述(と言ってもほとんどexample_ssl.confのまんま)。 サーバ証明書秘密鍵は、apacheを設定した時に作成したオレオレ証明書があるので、それをそのまま流用。

変更したのはlistenの部分のdefault_serverと、server_nameの部分のバーチャルホスト。

ssl.conf
server {
    listen       443 ssl default_server;
    server_name  .nametake.info;

    ssl_certificate         /my/cert/file;
    ssl_certificate_key     /my/cert/key/file;

    ~~~略~~~
}

上記のようにssl.confとしてSSL全体の設定ファイルを作成することで、それぞれのサービスで SSLを使用するときは、以下のように記述するだけでOK。

owncloud.conf
server {
    # 80番ポートでアクセスしてきたら443番ポートにリダイレクト
    listen 80;
    server_name cloud.nametake.info;
    # enforce https
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name cloud.nametake.info;
    ~~~略~~~
}

SSL通信したいサーバでlisten 443 sslと書くだけ。これで個別のファイルに証明書の情報を書かなくてよくなる。

サービスごとの細かいnginxの設定は[nginx (サービス名)]とかで検索して出てきた結果をコピペ。

詰まったところ

ownCloudのログイン画面でリダイレクトループする

原因

/var/lib/php/session/パーミッションapache:apacheのままになっていた。

解決策

/var/lib/php/session/パーミッションnginx:nginxに変更。

こんなところのパーミッションの設定もapacheになっていたのは想定外だった…… (serverのrootに指定したディレクトリの所有者がapacheのままになっていたのに気付かなくて30分ぐらい悩んだのは内緒)