PythonのrequestsでmultipartでPOST
PythonのHTTP clientとしてrequestsという便利なライブラリがあります。それをつかってMultipartデータをPOSTするメモです。
POSTするデータは、(ファイル名, データ, Content-Type)
あるいは(ファイル名, データ, Content-Type, ヘッダー)
というタプルで与えます。
クライアントからname, email, imageを送信する例です。
>>> import requests >>> r = requests.post("http://localhost:8080/add", files = { "name": ("", "hoge", "text/plain; charset=UTF-8"), "email": ("", "hoge@example.com", "plain/text; charset=UTF-8"), "image": ("hoge.png", open('hoge.png', 'rb'), "image/png") })
サーバー側での受信結果。ファイル名を空にすると、multipartの各要素のfilenameプロパティが与えられません。
--0a3d87b93d8c468b9b4dc7a493fbeee3 Content-Disposition: form-data; name="image"; filename="hoge.png" Content-Type: image/png ..................<image data>............................. --0a3d87b93d8c468b9b4dc7a493fbeee3 Content-Disposition: form-data; name="name" Content-Type: text/plain; charset=UTF-8 hoge --0a3d87b93d8c468b9b4dc7a493fbeee3 Content-Disposition: form-data; name="email" Content-Type: plain/text; charset=UTF-8 hoge@example.com --0a3d87b93d8c468b9b4dc7a493fbeee3--
OpenCV 3でPythonからSIFT
OpenCV 3では、ライセンスやパテント上の理由だろうか、SIFTやSURFはopencv_contribという別モジュールに分かれてしまっている。
opencv_contribを使うには、インストールの際に--with-contrib
オプションを付ける必要があるようだ。Mac OSだとHomebrewで以下のようにインストールできる。
brew install python brew install opencv3 --with-contrib brew link opencv3 --force
これでSIFTが使えるようになる。
# -*- coding: utf-8 -*- import cv2 def main(): img = cv2.imread('image.jpg', 0) # detector = cv2.FeatureDetector_create('ORB') detector = cv2.xfeatures2d.SIFT_create() keypoints = detector.detect(img) out = cv2.drawKeypoints(img, keypoints, None) cv2.imshow('sift', out) cv2.waitKey(0) if __name__ == '__main__': main()
玉音放送をMacからGET
Dockerの使い方
Dockerの使い方について調べたことをメモします。
インストールと起動
sudo yum install docker sudo chkconfig docker on sudo service docker start
以降の操作は全てrootで行うと仮定。
イメージの取得
pull
コマンドでイメージを取得する。
# docker pull centos:6.6
ここではcentosの6.6のイメージを指定している。イメージは、レポジトリとタグにより指定される。centosがリポジトリで、6.6がタグである。
デフォルトでは、Docker Hubという「レジストリ」からイメージを取得することになっている。
レジストリにどのようなリポジトリがあるかは、searchコマンドで検索できる。ただしタグは検索できない。
# docker search <search-keyword>
イメージ一覧の表示
images
コマンドでイメージがダウンロードされていることを確認する。
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos 6.6 8b44529354f3 2 weeks ago 202.6 MB
コンテナの起動
run
コマンドでイメージと何らかのコマンドを指定することで、コンテナが起動する。ここではコマンドとしてbash
を起動する。
# docker run -ti centos:6.6 /bin/bash
-t
は仮想端末を割り当てるオプション、-i
はSTDINを開いたままにするオプションで、シェルのようにインタラクティブに操作する場合はいずれも必須。
コンテナの中で操作
コンテナでhttpdをインストールしてみる。
# docker run -ti centos:6.6 /bin/bash [root@11dd19074528 /]# yum install -y httpd [root@11dd19074528 /]# echo 'hello world' > /var/www/html/index.html [root@11dd19074528 /]# exit
exitすると/bin/bashが死んでコンテナが停止する。しかしコンテナが停止しても、httpdのインストールがなされた状態のままで残る。どのようなコンテナが残っているのかは、docker ps -a
コマンドで確認できる。
# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11dd19074528 centos:6.6 "/bin/bash" About a minute ago Exited (0) 5 seconds ago condescending_shockley
イメージ化
コンテナをイメージとして保存(commit)することで、後に使いまわせるようになる。ここではcentos:httpd_installedというイメージ名でイメージ化する。
# docker commit -m 'install httpd' <image-id> centos:httpd_installed # docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos httpd_installed 53c383ebc5dc 15 seconds ago 270.9 MB centos 6.6 8b44529354f3 2 weeks ago 202.6 MB
なお、イメージ化してもコンテナは残り続けるので、rm
コマンドで明示的にコンテナを削除する。
# docker rm `docker ps -aq`
イメージからhttpdを起動
上で作成したイメージcentos:httpd_installedから、httpdを走らせてみる。
2つの方法がある。1つは、docker run
でhttpdを指定する方法。
# docker run -d -p 8080:80 centos:httpd_installed /usr/sbin/httpd -DFOREGROUND
ここでのポイントは以下のとおり。
もう1つは、一度コンテナで/bin/bashを走らせ、httpdをバックグラウンドで起動後、Ctrl-P, Ctrl-Qでコンテナからデタッチする方法。
# docker run -p 8080:80 -ti centos:httpd_installed /bin/bash [root@9964dde4e945 /]# service httpd start [root@9964dde4e945 /]# # detach; Ctrl-P Ctrl-Q #
1コンテナ1プロセスが定石とされていることから、1つめのほうが望ましそうだ。参照: http://docs.docker.jp/engine/articles/dockerfile_best-practice.html
どちらの方法でも、ホストからwgetを打てばhttpdが動作していることを確認できる。
# wget http://localhost:8080/index.html
httpdを監視
コンテナの中のhttpdを監視するケースを考える。
1つめの方法の場合(docker run
でhttpdを直接立ち上げた場合)、docker exec
を使うことで、コンテナで一時的に/bin/bashを走らすことができる。
# docker exec -it `docker ps -q` /bin/bash [root@9beb6db80222 /]# /etc/init.d/httpd status # 状態確認 httpd (pid 1) is running... [root@9beb6db80222 /]# exit exit
また、2つめの方法の場合(docker run
で一度コンテナに入ったあとにバックグラウンドでhttpd起動)、docker attach <container-id>
を使うことで、デタッチしたコンテナに再びアタッチできる。
コンテナの停止
stop
/kill
コマンド。
# docker stop <container-id>
参考) stopとkillの違い: http://elendia.hatenablog.com/entry/2014/09/16/040659
再び起動するときはdocker start
コマンド。
# docker start 9beb
イメージの削除
イメージを削除したい場合は、docker rmi
コマンド。
# docker rmi <image id>
スクリプト化
Dockerfileにより、イメージ作成をスクリプト化できる。
以下の内容でDockerfileという名前のファイルを置く。
FROM centos:6.6 RUN yum -y install httpd RUN echo 'Hello world' > /var/www/html/index.html EXPOSE 80 ENTRYPOINT /usr/sbin/httpd -DFOREGROUND
Dockerfile作成後、build
コマンドでイメージを作成。
# docker build -t centos:httpd_installed .
-t
でイメージ名を指定する。末尾の.
は、「Dockerfileを含むディレクトリ」を指定している。
そしてrun
でコンテナを起動すると、DockerfileのENTRYPOINTで指定したコマンドが実行される。
# docker run -p 8080:80 -d centos:httpd_installed
AWSでCentOS 5をインポート
CentOS 5の大雑把なAWSインポート手順。以下のページを参考に、比較的簡単にできた。
参考) http://understeer.hatenablog.com/entry/2014/04/09/172554
実行環境はMac OS X. 手順としては、
- VirtualBoxでVMを作成
- VBoxManageコマンドでRAW形式に変換
- ec2-import-instanceでec2インスタンスとしてインポート
ec2-api-toolsとawscli, あとVirtualBoxをインストールしておく。
# brew install ec2-api-tools # brew install awscli
1. VirtualBoxでCentOS 5.Xのイメージ作成
CentOS 5のisoをどこかからダウンロードしてインストール。VMの形式はvdiを選択しておく(*)
(*) VMWareの形式であるvmdkを選択することもできる。実際、後のec2-import-instanceでvmdkがサポートされているので、vmdkで作ったほうがベターである。しかし、ec2-import-instanceの際に、なぜか暗号の方式が違うというエラーで失敗。なので、一旦vdiで作って次の手順でrawに変換している。なお、Virtual PCの形式であるvhdを選択することもできるが、試してない。
なお、rootパスワードでsshログイン出来るようにしておく。デフォルト設定では出来る。
2. vdiからrawに変換
# VBoxManage clonehd /path/to/CentOS5.11.vdi /path/to/CentOS5.11.raw --format RAW
3. VMイメージをインポート
あらかじめS3のバケットを作っておき、そこにVMイメージをアップロードする。
# ec2-import-instance /path/to/CentOS5.11.raw -t t2.micro \ -f RAW \ -a x86_64 \ -p Linux \ --region ap-northeast-1 \ --availability-zone ap-northeast-1a \ --bucket bucket_name \ --subnet subnet-aaaaaaaa \ -o S3_OWNER_ACCESS_KEY \ -w S3_OWNER_SECRET_KEY \ -O AWS_ACCESS_KEY \ -W AWS_SECRET_KEY
S3にアップロード後、ec2インスタンスが作成される。ec2インスタンスを立ち上げ、sshでrootログインできることを確認したら(*), 一旦stop.
(*) 運用時はもちろんもっとセキュアにすべき。。。
4. AMIを作成
マネージメント・コンソールなどからAMIを作成。これで基本的には完了。
5. S3に上げたVMイメージを削除
最後にS3に残ったVMイメージを削除。おしまい。
# aws s3 rm --recursive s3://bucket_name/raw_image_uuid
高遠 桜祭りの夜桜
長野 高遠の桜は、2015年は4/11, 12あたりがピークだったようだ。まさにその期間に夜桜を見に行った。
東京の上野公園などとは大違いで、酒臭くない。宴会は禁止のようだ。まさに天下の桜を愛でることができたのだ(^^
ハードマージンSupport Vector Machine
ハードマージンSVM(Support Vector Machine)の勉強のまとめ。以下の資料に基づく。
資料: http://cs229.stanford.edu/notes/cs229-notes3.pdf
1. はじめに
正例と負例を分割する識別平面を求める際、識別平面のマージン、つまり識別平面の両サイドで訓練データが存在しない領域が、なるべく大きくなるようにしたい。
SVMを使うと、訓練データが線形識別可能な場合、マージンを最大化する識別平面を求めることが出来る。
2. 記法
\(x\)を特徴ベクトル、\(y\)を特徴ベクトルが正例か負例かを表すラベルとする。正例の場合は\(y=+1\), 負例の場合は\(y=-1\).
重みベクトル\(w\)としきい値\(b\)をつかって、識別平面を\(w^Tx+b = 0\)と定義する。\(w^Tx+b\)の正負により正例か負例かを判定することになるので、識別器\(h_{w, b}\)は以下のように定義される。 $$ h_{w,b}(x) = \begin{cases} +1 & & (if\ w^Tx+b \geq 0)\\ -1 & & (otherwise) \end{cases} $$
訓練データとして与える特徴ベクトルを\(x^{(i)}\), そのラベルを\(y^{(i)}\)とする。
識別平面が訓練データを正しく識別することは、全ての\(i\)について \(y^{(i)}(w^Tx^{(i)}+b)\geq 0\)が成り立つことと同値である。
3. マージンの定義
訓練データと識別平面が与えられた時の"マージン"を定義する。
まずは訓練データ\(x^{(i)}\)と識別平面\(w^Tx+b = 0\)の距離\(\gamma^{(i)}\)について考えてみる。
訓練データ\(x^{(i)}\)が正例(\(y^{(i)}=+1\))である場合、 識別平面の垂直ベクトルは\(w/\|w\|\)であり、かつ正例の空間を向いていることから、 \(x^{(i)}\)から識別平面に垂線を下ろした時の交点は、\(x^{(i)}-\gamma^{(i)}\cdot w/\|w\|\)となる。 交点は識別平面上であることから、以下が成り立つ。 $$ w^T(x^{(i)}-\gamma^{(i)}\cdot w/\|w\|) - b = 0 $$ 従って、\(\gamma^{(i)}\)は以下のようになる。 $$ \gamma^{(i)} = \frac{w^Tx^{(i)}+b}{\|w\|} $$
一方で、訓練データが\(x^{(i)}\)が負例である場合は、 $$ \gamma^{(i)} = -\frac{w^Tx^{(i)}+b}{\|w\|} $$ となる。 従って、識別平面が訓練データを正しく識別する場合は、正例・負例を問わず、\(\gamma^{(i)}\)は以下のように書ける。 $$ \gamma^{(i)} = y^{(i)}\cdot\frac{w^Tx^{(i)}+b}{\|w\|} $$
ここで、"マージン"を\(\gamma^{(i)}\)の中で最小のもの、つまり\(\min_i \gamma^{(i)}\)と定義する。
4. マージンの最大化
マージンを最大化する\(w\)と\(b\), つまり、 $$ \begin{eqnarray} \max_{w, b}\min_i \gamma^{(i)} &=& \max_{w, b}\min_i y^{(i)}\cdot\frac{w^Tx^{(i)}+b}{\|w\|}\\ &=& \max_{w, b}\left(\frac{1}{\|w\|}\min_i y^{(i)}(w^Tx^{(i)}+b)\right) \end{eqnarray} $$ を与える\(w\)と\(b\)を求めていきたい。
ここで、\(w\)と\(b\)に任意の正の定数をかけても、識別平面の意味は変わらない。つまり、\(\min_i y^{(i)}(w^Tx^{(i)}+b)=1\)という条件を設けても構わない。すると、 $$ \begin{eqnarray} & &\max_{w, b}\frac{1}{\|w\|} \\ & & s.t.\ \ \forall i.\ y^{(i)}(w^Tx^{(i)}+b) \geq 1 \end{eqnarray} $$ という問題を解けばよいことになる。計算を解きやすくするために、以下の同値の問題に変換する。 $$ \begin{eqnarray} & &\min_{w, b}\frac{1}{2}\|w\|^2 \\ & &s.t.\ \ 1 - y^{(i)}(w^Tx^{(i)}+b) \leq 0\ \ (\forall i) \end{eqnarray} $$ この最適化問題の解となる\(w, b\)を求めていく。
ところで、\(\min_i y^{(i)}(w^Tx^{(i)}+b)=1\)を与える\(x^{(i)}\),つまり識別平面に最も近い訓練データをサポートベクターと呼び、このアルゴリズム名の由来となっている。
5. 双対問題への変換
上記の最適化問題をそのまま二次計画問題として解くこともできるが、より効率的に解くために、 ラグランジュの双対性を用いる。制約条件を\(g_i(w, b)=1 - y^{(i)}(w^Tx^{(i)}+b)\) と置いた上で、ラグランジュ乗数\(a_i\ (\geq 0)\)を用いてラグランジュ関数に変換する。 $$ L(w, b, a)=\frac{1}{2}\|w\|^2 + \sum_i a_i g_i(w, b) $$ すると、元々解こうとしていた最適化問題(primal; 主問題)は、 $$ \begin{eqnarray} & & \min_{w, b}\max_{a; a_i\geq 0}L(w, b, a)\\ & & s.t.\ \ g_i(w, b) \leq 0\ \ (\forall i) \end{eqnarray} $$ と書ける(\(g_i(w, b) = 1 - y^{(i)}(w^Tx^{(i)}+b) \leq 0\ \ (\forall i)\)の条件で \(\max_{a; a_i\geq 0}L(w, b, a)=\frac{1}{2}\|w\|^2\)であるから).
これの双対問題(dual)は、minとmaxを入れ替えたもの、つまり \(\max_{a; a_i\geq 0}\min_{w, b}L(w, b, a)\)と定義される。 主問題と双対問題の間では以下の関係が成り立つ(弱双対定理)。 $$ \max_{a; a_i\geq 0}\min_{w, b}L(w, b, a) \leq \min_{w, b}\max_{a; a_i\geq 0}L(w, b, a) $$ さらに、
- 目的関数(\(\frac{1}{2}\|w\|^2\))が凸であり、
- かつ各制約関数\(g_i\)がアフィン関数であり、
- かつ\(g_i\)が実行可能である(\(g_i(w, b)\leq 0\ \ (\forall i\)を満たす\(w, b\)が存在する; the weak Slater's condition)
場合、上記の式で等号が成り立つ(強双対定理)。実際にこれらの条件を満たすので、 主問題の代わりに双対問題を解けば、主問題の最適解が見つかったことになる。
6. 双対問題の最適解
そこで、双対問題\(\max_{a; a_i\geq 0}\min_{w, b}L(w, b, a)\)を解いていく。 まずは、\(L(w, b, a)\)を\(w\)と\(b\)に関して最小化する。 \(\nabla_w L(w, b, a)=0\)という条件から、 $$ w=\sum_i a_iy^{(i)}x^{(i)} $$ が導かれる。これを\(L(w, b, a)\)に代入すると以下の式が得られる。 $$ L(w, b, a)=\sum_i a_i - \frac{1}{2}\sum_{i, j}y^{(i)}y^{(j)}a_ia_j(x^{(i)})^Tx^{(j)} - b\sum_i a_iy^{(i)} $$
さらに\(\frac{\partial}{\partial b}=0\)という条件から $$ \sum_ia_iy^{(i)}=0 $$ が導かれる。そのため、上記\(L(w, b, a)\)の最後の項は\(0\)となる。 以上から、\(L(w, b, a)\)を\(w\)と\(b\)に関して最小化した結果は、 $$ L(w, b, a)=\sum_i a_i - \frac{1}{2}\sum_{i, j}y^{(i)}y^{(j)}a_ia_j(x^{(i)})^Tx^{(j)} $$ となる。
次のステップは、上記を\(a\)について最大化する。 $$ \begin{eqnarray} & & \max_a & & \sum_i a_i - \frac{1}{2}\sum_{i, j}y^{(i)}y^{(j)}a_ia_j(x^{(i)})^Tx^{(j)}\\ & & s.t. & & a_i \leq 0\ \ (\forall i)\\ & & & & \sum_i a_i y^{(i)} = 0 \end{eqnarray} $$ この双対問題はSMO(Sequential Minimall Optimization)というアルゴリズムで解ける。 基本的には、ベクトル\(a\)の中から2変数を選択し、\(\sum_i a_i y^{(i)} = 0\) を満たすように動かすことで最適解に近づいていく、というもののようだ。
SMOにより最適な\(a\)(\(a^\ast\)とする)が見つかったとすると、 \(w=\sum_i a_iy^{(i)}x^{(i)}\)という条件から、最適な\(w\)が以下のように求まる。 $$ w^\ast=\sum_i a_i^\ast y^{(i)}x^{(i)} $$
\(b\)については以下のように求まる。主問題での制約条件 \(1 - y^{(i)}(w^Tx^{(i)}+b) \leq 0\ \ (\forall i)\)から、\(b\)は $$ 1-w^{\ast T} x_{I^+}\leq b \leq -1-w^{\ast T}x_{I^-} $$ を満たさねければならない。 ここで、\(x_{I^+}=\arg\!\min_{x_i} \{w^{\ast T} x_i \mid y_i = +1\}\), \(x_{I^-}=\arg\!\max_{x_i} \{w^{\ast T} x_i \mid y_i = -1\}\)である。 そこで、上記の右辺と左辺の中間をとるのが最適なので、 $$ b^\ast = - \frac{1}{2}(w^{\ast T} x_{I^+} + w^{\ast T}x_{I^-}) $$ となる。
以上より、識別平面が求まった。 $$ \begin{eqnarray} w^{\ast T}x + b^\ast &=& (\sum_i a_i^\ast y^{(i)}x^{(i)})^T x + b^\ast \\ &=& \sum_i a_i^\ast y^{(i)}\langle x^{(i)}, x\rangle - \frac{1}{2}(w^{\ast T} x_{I^+} + w^{\ast T}x_{I^-}) \end{eqnarray} $$
7. KKT条件
強双対条件が成り立つ場合、最適解\(w^\ast, b^\ast, a^\ast\)に関して、 KKT条件と呼ばれるいくつかの条件式が成り立つ。 そのうちの1つが、KKT相補性条件と呼ばれる以下のものである。 $$ a_i^\ast g_i(w^\ast, b^\ast) = 0\ \ (\forall i)$$ この条件から、もし\(g_i(w^\ast, b^\ast) < 0\), つまり \(1 < y^{(i)}((w^\ast)^Tx^{(i)}+b^\ast)\)ならば、\(a_i^\ast = 0\)という 制約が導かれる。今、 \(\min_i y^{(i)}(w^Tx^{(i)}+b)=1\)という条件のもとで 問題を解いていた。つまり、最も識別平面に近い訓練データ (サポートベクター)ではない場合、 \(1 < y^{(i)}(w^Tx^{(i)}+b)\)のはずなので、 \(a_i^\ast = 0\)となる。つまり、サポートベクター以外の大多数の訓練データについて\(a_i^\ast = 0\)となるので、 求まった識別平面による判別は十分効率的である。