オタク日記
(Mac と Linux, 2015Q4)

目次

2015-12-31 (Thu): お願いしますよ、DoCoMo さん
2015-12-25 (Fri): 新プリンタでも躓く
2015-12-22 (Tue): プリンタを新調した
2015-12-09 (Wed): Django Project で Virtual Host
2015-11-12 (Thu): BLAS/LAPACK と Numpy (その 2)
2015-11-11 (Wed): BLAS/LAPACK と Numpy (その 1)
2015-11-04 (Wed): やっぱり Time Machine の実現は難しかった
2015-11-01 (Sun): Raspberry Pi で Netatalk-3
2015-10-31 (Sat): Backup 体制を見直し
2015-10-25 (Sun): 自宅サーバのバッテリを交換
2015-10-21 (Wed): Python/NumPy Update (その 2)
2015-10-18 (Sun): Python/NumPy Update
2015-10-16 (Fri): iPython 再訪

古い日記:
2015Q3   2015Q2   2015Q1   2014Q4   2014Q3  
2014Q2   2014Q1   2013Q4   2013Q3   2013Q2   2013Q1  
2012 年   2011 年   2010 年   2009 年   2008 年   2007 年  
2006 年   2005 年   2004 年   2003 年   2002 年   2001 年


2015-12-31 (Thu): お願いしますよ、DoCoMo さん

SB から DoCoMo へキャリアを変えた時、パケット通信の料金体系も変った。 DoCoMo の「シェアパック 15」というパッケージを導入して、一ヶ月あたり 15GB を家族 4人でシェアしている。 (これは 4 人の転送量の総計が 15GB を超えると、全員に 128 kbps への転送速度制限がかかる、というもの。) 最初は 15GB で足りるかなと、ちょっと心配だったが、 しかし、最近はあまり外出しないし、ましてや MBA を持ち出して tethering する事などは殆どないという事もあり、この制限にひっかかった事はなかった……

ところが、帰省の新幹線の中でこれ(帯域制限)を体験する羽目になった。 元々、新幹線の中は接続が不安定なので、 道中のどのあたりでそうなったのかよく分らない。(今思えば、 MBA (Mavericks, Hawk) の Security Update が引き金になったのかも。 何とも無謀な事をやったもんだ。)症状は、FB にアクセスしたら永久に転送が完了しない……、Twitter で RT ができない…… 等々、致命的とまでは行かないけど、イライラさせられる状態が続く。

パケット通信を一番よく使うのが自分で、かつ今月は殆んど使っていない (自宅では WiFi) という思い込みがあり、まさか 15 GB (厳密には、ボーナスパケットを合わせて 20 GB) のリミットをトリップしているとは思いもしなかったので、 なかなか原因が分らなかったが、「ひょっとして」と疑い始めたら、 あっさり「まさにその通り」と判明。 DoCoMo さんの MyDoCoMo の分かり難い藪の中を(しかも 128 kbps で!)うろうろする事数時間で、 確かに「制限にひっかかっている」事、また、 「12月の私の使用量は、 2.2 GB に過ぎず、残りの大半を使っているのは、我が娘である」事等が分った。

それにしても、DoCoMo さんのウェブサイト、分り難いねぇ。キャリアを DoCoMo さんに替えてから、半年近くも MyDoCoMo アカウントさえ作らなかったという自分の怠慢の所為もあるんだろうけど、 上の事柄をすっきり 1 ページに纏めるなんて、そんなに難しい事じゃあなかろうに……

ともあれ、同サイト(の違うページ :-p)で、1 GB を通常速度で転送するオプションを追加購入(1080 円也)して、 ようやく「普通の生活」が復活した。

で、なんやかやで、丸二日程、128 kbps で生活した訣だが、 この体験にはかなり感慨深いものが有った。 以前使っていた ISDN が 64 kbps (後の方では、128 kbps?) なので、 128 kbps と言えばその頃と同程度。実際、「ssh で自宅サーバにログインする」 とか、「同サーバと重要ファイルを rsync で同期する」はたまた、 「同サーバ上のウェブページを表示する」については全く違和感が無い。 (この 10 年間、自宅サーバに殆んど進歩が無かったとも言う。) 問題は、多くの「商用ウェブページ(?)」で、例えば、Facebook, Twitter, Amazon.com/Amazon.co.jp などは、 まともに動いてくれない——ボタンクリックへの応答どころか、 最初のページ表示さえ完了しない事がある。 (何故か Firefox より、Safari の方が応答が良い事を発見した。) 何より「かなわんな」と思ったのは、Emacs/Wanderlust から、gmail.com へ imaps (port: 993) 越しのアクセスが不安定、というかやたら時間がかかるようになる事。

最初は、iPhone6 と MBA 双方の設定を疑い、いろいろ弄ってみたが、 そのうち、USB 接続越しの tethering ができなくなった。 どうやっても、System Preferences > Network で "iPhone USB" を "connected" にできない……。 思い余って、デバイスを一新しようと、"iPhone USB" を一旦削除したら、今度は、新規に "iPhone USB" を add できない(下図のプルダウンメニューで、"iPhone USB" が出てこない。)Mavericks と iOS のリブート、Wi-Fi/Personal Hostspot の ON/OFF とケーブル接続の順序等を散々試してみたが、いずれも効果無し。

Wi-Fi 接続で用は足りるので、一旦は諦めた。1GB 分を買い足して 128 kbps の制限が取れても状況は変わらなかった。が、今度は、iPhone (6) に充電されなくなった……。MBA の USB port に挿しても、iPhone 側に「接続」マークは出るものの、充電されない(残量の % 表示がゆっくり減って行く)——プチ・パニック!

また、iOS/Mavericks の reboot、USB AC アダプタへ替えてみる、等をやっているうちに何時の間にか直った。 つまり、どれが功を奏したのか未詳。やれやれ、と思いつつ、 上記の「デバイス追加」を試みたら、プルダウン・メニューに "iPhone USB" が現れ、それを選択する事で、あっさり USB 越しに Tethering 接続ができた…… (どうやら、MBA の方のドライバがおかしくなっていた、という事のようだ。)

iPhone USB Setting
System Preference > Network で iPhone USB を選ぶ

Yosemite 限定かも知れないが、新たに確認した tethering に関する知見のいくつか……


2015-12-25 (Fri): 新プリンタでも躓く

はがき印刷も含めて一応一通りは確認した上で、 「なかなかイケてるかも」と書いたのだが、 実際に宛名書きを始めたら、いきなり問題が……宛名面の右端と下側に 5mm くらいの黒い筋が入る。 つまり「ここぞ、という時にコケて」しまったのだった。

使っているアプリは、 「はがきデザインキット」というもの。 去年も使ったけど、こんな問題は無かったし、今年の版(毎年更新される)でも、 裏側(通信面)では問題が出ない……。

こうなると、このアプリとプリンタの「設定」の問題だろうが、 その煩雑さ(整理のされてなさ?)が禍して、なかなか系統的な「カット & トライ」ができない。

Google さんに聞いてみても、それらしい Q&A は見付からない。 ただ、EPSON さんのプリンタについて、「ドライバが Airprint ではなく、CUPS でないとダメ」というのが有った。 元の質問した人が「それでもダメでした」と言ってるし、そもそも EPSON の話だし、 と最初は無視していたのだが、 何時間か他の事をやってみた後「万策尽きた感」が出てきたので、 これを試してみる事にした。

とはいうものの、そもそもプリンタ・ドライバ(のインストーラ)は、 一種類しかないので、少々右往左往したが、 結局ドライバをインストールした後、System Preferences > Printers & Scanners の Add 画面(下記)で、「CUPS 版を使う」とすれば良いらしい (ディフォルトは何故か Airprint.)

CUPS driver
printer driver で CUPS を選ぶ
これで、何とか「黒枠」の問題は克服できたが、 今度は「郵便番号」の位置が赤枠からかなりずれるようになってしまい、 手で修正する必要があった…… Airprint 版ではこれに関しては問題無かったのになあ。

という事で、(根本原因が「はがきデザインキット」側にあるのか、 DCP の方なのかは未詳だが)「なかなかイケてる」は 「使えなくもない」あたりに格下げ。けどまあ、はがきトレーからの紙送りとか、 トレーに紙を補充した後の印刷再開の際とかの「HP プリンタにありがちだった問題」 が皆無だった事は、公平のために付け加えておきたい。


2015-12-22 (Tue): プリンタを新調した

Brother の DCP-J963N というモデルを Amazon から購入した。 なかなか良さげ……

さらば HP Printer

もう思い出せない程大昔に最初に買ったプリンタが HP で、それ以降(家族向けのも含めると 5 代くらい?)忠実な顧客だった。 なんでそんなに惚れ込んだのか解らない…… その前の(測定器用の)HPIB のドットインパクト・プリンタが気に入っていたから? 当時としてはドライバに難が少なかった (Macintosh のためのドライバがよくできていた)から? Linux からでも比較的無難に使えたから?…… 今では、もう忘却の彼方だが。とにかく、2 代目だったかの Deskjet 540C は名機だったと思う。

ともあれ、(米国で買って使っていた)先代 (Photosmart 3210) は、そこそこ快適に使えた (まあ、 ドライバの更新に関しては色々有ったが :-) なので、5 年近くも使った後でも、日本へ持ち帰ったのだった。 が、対応するインクが日本では買えない事が判明。 型名が違うが全く同型で問題なく装着もできるインクカートリッジが、 プリンタの F/W に弾かれて使えない…… それでは、と、それまで使っていたカートリッジを Amazon.com に注文しようとしたら、日本へは出荷できない、と言われ「怒り心頭」

もう HP なんか二度と買うもんか、と思っていたが、COSTCO に Photosmart 6520 が 4,000 円台で売られていた……なんだか嫌だな、でも、家族向けなら…… と、つい日和ってしまう。設定もスンナリ行って、すぐに使えるようになったが、 しばらくすると「なんだかなぁ」が出てきた。 まず、ウザイと思ったのは (3210 にもその傾向は有ったが) 使おうとすると殆んど毎回、 長々とメンテナンスモードに入る——そうでない場合も、 かなり長いキャリブレーションをやってくれる。 しかし、それよりウンザリさせられたのは、 とにかくインク周りのトラブルが多い事。

私自身はそんなにしょっちゅう使う訣ではないが、 どうしてもすぐに印刷できないと困るという折がある(請求書の印刷とか :-)。 なのに、上の「かすれ」には、二度やられた。

Brother に……

米国の会社では Brother のモノクロ・レーザープリンタ (HL-L2xxx?) を専有して使っていた。 A4 専用で $150 程度 (?) の廉価版ながら、 なかなか優れもので、コンパクトなのにスタートアップも印刷も速いし、ジャムは皆無……。 難点と言えば、起動の時の音が(とても)うるさい事くらい。

この「難点」はオフィスでは我慢できても、home use となると、致命的。 (あとスキャナも欲しいし。)で、HL-シリーズは断念したが、Brother のブランドイメージは相当良くなっていた。

DCP-J963N の WiFi

というわけで、このモデルに決めた。というか、大分前から決めていたのだが、 「first lot は避ける」を実行しているうちに、6520 が「ここぞという時」にコケてくれて、それに背中を押されて、ポチった。 (勿論、そろそろ年賀状を印刷せねば、というのもある……)

設定はスムース……と言いたいところだけど、お手の物の筈の WiFi の設定でちょっと引掛った。 実は何が悪いのか解るまでに何時間もかかったのだが、 右往左往をすっとばして結論だけ書くと、 要は「暗証/暗号化方式」のコンパチビリティの問題。 我が家の AP は WPA/WPA2 + TKIP に設定してあり、 これで、iPhone, HP printer, Macintosh, MBA (MacBook Air) 全てで問題なく繋る。 しかし、DCP-J963N はこれに未対応だと主張する……。 接続に失敗すると、何とその都度、詳細をプリントアウトするのだが、 それには、WPA + TKIP, WPA2 + AES をサポートする、 と書いてある。ならば、AP の WPA(/WPA2) + TKIP で OK の筈なのだが……(Brother さん、暗証/暗号化方式の推定のロジックを間違えてないかい?) しかし、まあ、ここで「べき論」を云々しても虚しいので、AP の方を WPA/WPA2 + AES に変えてみたら、あっさり繋がった (Password の再入力さえ不要。)

とは言え、この変更で我が家に 10 個以上ある WiFi デバイスに繋らなくなったら大変——という事で、iPhone, MBA と Mac-mini について確認してみたが、設定を一切弄らなくても AP に接続できた。 とりあえず良かった。

実は、AP の変更に行く前に、AOSS によるペアリングも試みた。AOSS の出始めの頃試してみた事があるが AP とデバイスを近付ける、ってのが面倒で、ずっと使ってなかった。 そもそも、デバイス側の接続方式の推定が優秀になって、「パスワード」(実は「暗号化キー」 だったり「事前共有化キー」だったりする)を入力するだけで済むので、 然程必要無かった、という事もある。だが、今回それがうまく行かず、 よい機会でもあるので久し振りに使ってみた。どうやら AOSS もしっかり進歩しているようで、なんと AP (WZR-HP-AG300H) の制御・設定用ウェブ画面に AOSS のスタートボタンが有る!おかげで、AP と物理的に近付けなくてすんだ。しかし「AP 本体のボタンを同時に押す」のが、 セキュリティ確保の眼目だった筈なんだけどこれでええんやろか? :-)

とにかくかくにも繋ったのはとても有難いが、AOSS 専用の SSID が増設されたりして、 気色が悪い(家内 DDNS との干渉が心配。)なので、 程なく AOSS は disable したが、一旦繋ったおかげで、注意が AP の「暗号化方式」の設定に注意が向いたのだった。

第一印象

まだ、三日程しか使ってないが、とりあえずの印象を…… 等と、まあ色々と文句も書いたが、今の所は「当り」の印象。

2015-12-09 (Wed): Django Project で Virtual Host

Mod_wsgi の進化

前述のように、既に一年前に Ubuntu-14.04 LTS で mod_wsgi を使い始めたのだった。 この時、使った mod_wsgi は -3.4 で、 libapache2-mod-wsgi-py3 (3.4-4ubuntu2) という deb package で供給されたものだった。 -3.4 は 2012-08 にリリースされているので、二年以上経っていて、 もう開発は終っているのか、と思われた。 が、何と今や mod_wsgi はPyPI (Python の module package index) に入っていて、そちらではもう -4.4.x にまで上っている。Apache の module が PyPI に入っている、というのもさる事ながら、それを「Python に Install する」(Installation into Python) というあたりにも大いに面喰った。 が、なんとかインストールでき、使い始める事ができたのだった。

しかし、これは Virtual Host になってない、つまり、 mod_wsgi-express を走らせて、httpd.conf を作った Django Project しかカバーできない。今回のお題を一言で言えば「これを何とかしたい」 すなわち「最新の mod_wsgi と Virtual Host で、複数の PyVenv 上の Django Project を公開できるようにする事」となるだろう。(こう書いてしまうと、 つぎ込んだ時間の割にあまりにも「ささやか」な目標になってしまうので、ちょっと寂しい :-))

Ubuntu-16.04 LTS と pyvenv

Ubuntu はコマンド群のバージョンアップに比較的迅速に追随してくれるが、 偶には、(例えば Netatalk のように)「何年も放置」なんて事もある。 今回の mod_wsgi も、最新版(4.4 以降)が deb package になってくれれば、 諸々の事が簡素かつ安定になるのだが…… という事で、来年春の正式リリースまでに相当多数の更新が有ると期待される 16.04 をインストールしておこうと思い立ったのだった。 (あわよくば、Netatalk も mod_wsgi も最新になるのではないかと…… :-)

まだ β 版でもないようなので、ちょっと心配だったが、 インストールには何も問題が無かった。 インストールの後、ほぼ毎日猛烈な勢いでパッケージが更新されていくが、 残念ながら今のところ、Netatalk も mod_wsgi も古いままである。 (ちなみに systemd への移行もまだ中途半端。)

しかし、リブートは滅茶苦茶速い。Mac-mini のストレージが(Fusion drive のせいで)速くなっている、という事もあるだろうが、Ubuntu 側の改善もあるに違いない。 (15.04 が約 10 秒かかるのに対し、16.04 は 6-7 秒!)

インストール・メモ:

  1. xenial-desktop-amd64.iso (1.2GB) をダウンロード(約 18分)
  2. VMware Fusion の VM に easy install で、install (約 5分)
  3. 一旦シャットダウンして、VM の設定を変更: CPU/Memory: 2 core/2GB, Network: Bridge.
  4. boot して、追加の設定: timezone, hostname を設定(hostname は /etc/hostname を編集する)
  5. 追加のパッケージをインストール
    fukuda@xenial:~$ sudo apt-get install ssh emacs lv
    fukuda@xenial:~$ sudo apt-get install python3.4-dev apache2-dev git
    fukuda@xenial:~$ sudo apt-get install python3.5-dev python3.5-venv    
    fukuda@xenial:~$ sudo apt-get build-dep mod-wsgi
    fukuda@xenial:~$ sudo apt-get build-dep matplotlib
  6. pyvenv を構築。
    fukuda@xenial:~$ pvenv-3.5 pve35
    fukuda@xenial:~$ cd pve35/
    fukuda@xenial:~/pve35$ . bin/activate
    (pve35) fukuda@xenial:~/pve35$ 
    (pve35) fukuda@xenial:~/pve35$ pip install --upgrade pip
    (pve35) fukuda@xenial:~/pve35$ emacs -nw requirements.in
    (pve35) fukuda@xenial:~/pve35$ cat requirements.in
    django==1.8.7
    mod_wsgi
    django-timezone-field
    matplotlib    
    (pve35) fukuda@xenial:~/pve35$ pip-compile
    (pve35) fukuda@xenial:~/pve35$ pip-sync
    (pve35) fukuda@xenial:~/pve35$ pip freeze | grep mod-wsgi
    mod-wsgi==4.4.21

試験用ネットワーク環境を作る

上記のように、既に Ubuntu のディフォルトの設定で、DHCP で、xenial という hostname にその時空いている IP address の一つがアサインされるようになっている(以下仮にこれを '192.169.0.119' とする。) Virtual Host の設定を試験するために、この IP をもつ hostname を設定する——勿論 DDNS (= dhcpd + bind9) が走っているホスト (Lark)上で実行。
fukuda@lark:/var/cache/bind% sudo nsupdate -d -k /etc/bind/rndc.key
Creating key...
Creating key...
namefromtext
keycreate
> update add xenial1.otacky.jp 3600 IN A 192.168.0.119     
> send
> update add xenial2.otacky.jp.  3600 IN A 192.168.0.119
> send
fukuda@lark:/var/cache/bind% dig xenial2.otacky.jp
....
;; ANSWER SECTION:
xenial2.otacky.jp.	3600	IN	A	192.168.0.119 
これでは勿論、xenial にアサインされる IP address が変わったらうまく行かなくなり、とてもスマートとは言えないが、 とりあえずの試験用としては使えるだろう。(ここでは CNAME は使えない。)

構想としては

のように、Name-based Virtual Host を実現する事を目指す。

Django Project を種々のサーバで公開してみる

まずはテスト用の Django Project (上の 「その 1」) を git:
(pve35) fukuda@xenial:~/pve35$ git clone git://otacky.jp/wrm_tts.git
(pve35) fukuda@xenial:~/pve35$ ls wrm_tts -l
total 43220
-rw-rw-r-- 1 fukuda www-data 44234752 Dec  8 14:48 db.sqlite3
drwxrwxr-x 4 fukuda fukuda       4096 Dec  6 10:16 htdocs
-rwxrwxr-x 1 fukuda fukuda        249 Dec  6 08:48 manage.py
drwxrwxr-x 3 fukuda fukuda       4096 Dec  6 09:31 mysite
drwxrwxr-x 3 fukuda fukuda       4096 Dec  6 08:48 templates
drwxrwxr-x 6 fukuda fukuda       4096 Dec  6 08:49 tts
(pve35) fukuda@xenial:~/pve35$ ls wrm_tts/mysite -l
total 20
-rw-rw-r-- 1 fukuda fukuda    0 Dec  6 08:48 __init__.py
-rw-rw-r-- 1 fukuda fukuda 3024 Dec  6 09:31 mod_settings.py
drwxrwxr-x 2 fukuda fukuda 4096 Dec  6 08:50 __pycache__
-rw-rw-r-- 1 fukuda fukuda 3349 Dec  6 08:48 settings.py
-rw-rw-r-- 1 fukuda fukuda  457 Dec  6 08:48 urls.py
-rw-rw-r-- 1 fukuda fukuda  393 Dec  6 08:48 wsgi.py 
ここで、wsgi.py を若干変更して
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.mod_settings")
application = get_wsgi_application()     
のようにし、これによって を使うようにしてある。

これを、Django の dev-server (runserver) で公開する……

(pve35) fukuda@xenial:~/pve35/wrm_tts$ python manage.py migrate
(pve35) fukuda@xenial:~/pve35/wrm_tts$ python manage.py runserver 0.0.0.0:8000
Performing system checks...

System check identified no issues (0 silenced).
December 08, 2015 - 15:10:33
Django version 1.8.7, using settings 'mysite.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
....
Firefox から http://xenia:8000/ でアクセスして OK を確認。(まだ、 Virtual Host を使ってないので、URL の詳細は関係しない。)

次に(mod_wsgi に含まれている)runmodwsgi を使って httpd.conf を作り、Apache で公開する……

#1
(pve35) fukuda@xenial:~/pve35$ sudo chown :www-data wrm_tts              
#2    
(pve35) fukuda@xenial:~/pve35/wrm_tts$ python manage.py collectstatic    
#3    
(pve35) fukuda@xenial:~/pve35/wrm_tts$ sudo ../bin/python ./manage.py  \ 
    runmodwsgi --setup-only --port=80 --user www-data --group www-data \
    --server-root=/etc/mod_wsgi-express-80 
Successfully ran command.
Server URL   : http://localhost/
Server Root  : /etc/mod_wsgi-express-80
Server Conf  : /etc/mod_wsgi-express-80/httpd.conf
Error Log File     : /etc/mod_wsgi-express-80/error_log (warn)
Environ Variables  : /etc/mod_wsgi-express-80/envvars
Control Script     : /etc/mod_wsgi-express-80/apachectl
    .....
#4
fukuda@xenial:~/pve35$ sudo /etc/mod_wsgi-express-80/apachectl start 
ここに、
  1. #1) db.sqlite3 とその直上のディレクトリは、apache からの write permission が要るので、group を www-data としている。(必須!)
  2. #2) mod_wsgi + Apache では (runserver と違って) static/media ファイルの位置を良きにはからってくれないので、 特別な場所 (wrm_tts/htdocs/) に集めておく。
  3. #3) sudo は pyvenv の PATH を引き継がないので、使う python を明示的に指示してやる必要がある。
  4. #4) これで、apache daemon が起動される。 スタートアップ時に自動で起動するには、別途工夫が要る。
これで、上記同様 http://xenial にアクセスして、応答を確認できた。 (ちなみに、上の #1, #2 はこの後の Virtual Host の実現に必須。)

Debian Virtual Host で公開

以上までで、Apache + mod_wsgi-express で Django Project を公開できた訣だが、 前述のように、これを Virtual Host にする方法がよく解らなかった。

一方で、古い mod_wsgi (-3.4) では既に Virtual Host の一つにできている。 なので、今回も……という事で、その古い wrm_tts.conf を使って、その HostName, WSGIScriptAlias ディレクティブだけ変更して

fukuda@xenial:~/pve35$ sudo a2enmod wsgi
Module wsgi already enabled
fukuda@xenial:~/pve35$ sudo a2ensite wrm_tts.conf
Site wrm_tts already enabled
fukuda@xenial:~/pve35$ sudo systemctl start apache2 
とやってみたが、Firefox からアクセスを試みた結果は Internal Server Error ……。 error.log には、 "ImportError: cannot import name 'multiarray'" などと有って、前途多難を感じさせる。

それでは、という事で、 (PyVenv の) pip でインストールした最新版 mod_wsgi-4.4.21 を、deb-package のように一気にインストールする方法を捜したが、なかなか見付からない。 (少なくとも、mod_wsgi モジュールには含まれてないのは確か。) なので、libapache2-mod-wsgi-py3 の mod_wsgi.so の実体だけを置き換える事を試みた。つまり……

fukuda@xenial:~/pve35$ dpkg-query -L libapache2-mod-wsgi-py3
    .....
/usr/lib/apache2
/usr/lib/apache2/modules
/usr/lib/apache2/modules/mod_wsgi.so-3.4
/usr/lib/apache2/modules/mod_wsgi.so-3.5
/etc
/etc/apache2
/etc/apache2/mods-available
/etc/apache2/mods-available/wsgi.conf
/etc/apache2/mods-available/wsgi.load
/usr/lib/apache2/modules/mod_wsgi.so    
fukuda@xenial:~/pve35$ ls -l /usr/lib/apache2/modules/mod_wsgi* 
lrwxrwxrwx 1 root root     15 Dec  8 17:05 /usr/lib/apache2/modules/mod_wsgi.so -> mod_wsgi.so-3.4
-rw-r--r-- 1 root root 207952 Nov 30 02:10 /usr/lib/apache2/modules/mod_wsgi.so-3.4
-rw-r--r-- 1 root root 207952 Nov 30 02:10 /usr/lib/apache2/modules/mod_wsgi.so-3.5
となっているので、
fukuda@xenial:/usr/lib/apache2/modules$ sudo unlink mod_wsgi.so
fukuda@xenial:/usr/lib/apache2/modules$ sudo ln -s \
    /home/fukuda/pve35/lib/python3.5/site-packages/mod_wsgi/server/mod_wsgi-py35.cpython-35m-x86_64-linux-gnu.so \
    mod_wsgi.so    
fukuda@xenial:/usr/lib/apache2/modules$ sudo systemctl restart apache2
とやったところ、No Error で上手く動いてくれた。

そこで、改めて /etc/apache2/sites-available/wrm_tts.conf を編集して、(WSGI の) daemon mode に特化した設定にする……

#1
# WSGIPythonPath /home/fukuda/pve35/lib/python3.5/site-packages:/home/fukuda/pve35/ne_tts    
<VirtualHost *:80>
  ServerName xenial2.otacky.jp
#2
  WSGIScriptAlias / /home/fukuda/pve35/wrm_tts/mysite/wsgi.py
  WSGIApplicationGroup %{GLOBAL}
#3
  WSGIDaemonProcess xenial2.otacky.jp python-path=/home/fukuda/pve35/wrm_tts:/home/fukuda/pve35/lib/python3.5/site-packages
  WSGIProcessGroup xenial2.otacky.jp
#4
  Alias /static/ /home/fukuda/pve35/wrm_tts/htdocs/
  Alias /favicon.ico /home/fukuda/pve35/wrm_tts/htdocs/tts/img/favicon.ico
  <Directory /home/fukuda/pve35/wrm_tts/htdocs>
   	 Require all granted
  </Directory>
  <Directory /home/fukuda/pve35/wrm_tts/mysite >
    <Files wsgi.py>
      Require all granted
    </Files>
  </Directory>
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>    
ここに、
  1. #1) daemon mode では、WSGIPythonPath を使えない。
  2. #2) 最重要の directive. どの Django Project を呼ぶかは、ここで決まる。
  3. #3) WSGIPythonPath の代りに、この python-path オプションで、呼ぶべき Python とそのモジュールを指定する。
  4. #4) Django Project の static/media file を集めたディレクトリを指定
結果的に、Django Project を指定するディレクトリは、全て <VirtualHost> </VirtualHost> 内にあるので、複数の Django Project を公開できるのでは、という期待が高まる……

ともあれ、mod_wsgi + Apache で Django Project が公開できて、下のような動作をしている事が確認できた。

server-status
Apache + mod_wsgi で、Django Project を公開
(クリックして拡大)
Keep Alive の時間が短かすぎて、この図では Server Status のプロセス(スレッド?)しか表示されていないが、 ページを要求した直後は、xenial2.otacky.jp:80 へのアクセスも表示される。

複数の Django Project を公開

通常の(mod_wsgi を使わない)ページでは、Name-based Virtual Host は実現できていて、実際、otacky.jp, waremo.com 合わせて、 六つ程のページを公開している。それに、さらに、mod_wsgi 経由の Django Project を一つ加える事もできている(未公開だが)。

ここでさらに複数の Django Project を加えたい、 という今回のお題にとっては、WSGIPythonPath を、 <VirtualHost :80></VirtualHost> の中に置けない、 というのがネックだった訣だが、以上の検討の中で、 これを WSGIDaemonProcess の python-path パラメータの中に置ける事が確認できた。 つまり、それぞれの <VirtualHost>の中で、ServerName, WSGISCriptAlias, WSGIDaemonProcess (の python-path) を対応する Django Project を指すようにしてやれば、 全く独立した host にできる。

実際に、000-otacky.conf (http://xenial.otacky.jp), net_tts.conf (http://xenial1.otacky.jp), wrm_tts.conf (http://xenial2.otacky.jp) を立てて実験してみたが、相互に干渉する事なく、また、 まったく独立に a2ensite/a2dissite で抜き差しできた。 言うまでもないが、後の二つの Django project は独立に db.sqlite3 を保持できる。

まとめ

Ubuntu-16.04 において、 ことで、複数の Django Project について Name-based VirtualHost を実現できた。

2015-11-12 (Thu): BLAS/LAPACK と NumPy (その 2)

「今や BLAS は(サブルーチン集ではなく) API」を知らなかったというのもちょっと恥ずかしいが、 その性能改善の度合いが、 (「並列化」ではなく)いかに主メモリへのアクセスを減らすかで決まる、 を知らなかったのもかなり……

ともあれ、Ubuntu on (VMware) 上の比較で、Mac-mini の Core i5 が(core 数が半分でも)Core i7 と遜色ない性能を出している事から、Fusion を介さず 直接 OS X の上で走る NumPy の処理能力に期待が高まる……

OS X での Numpy (vecLib 版)

この頃は、Python で何かを試したいという時、 pyvenv (Python の Virtual Environment) の中でやる事が増えたので、NumPy についても、 pip (pip-tools) でインストールしたものを使う事が多い。 MacPorts 版より更に安定性が向上し、 かつアップグレード(本家への追随)が迅速なような気がする。 (先の '@' の「味見」には Python-3.5 と numpy-1.10 が必要だったので、 これ(pyvenv 環境下)で試したのだった。)

(pve35) fukuda@falcon:~/pve35% pip freeze
ipython==4.0.0
ipython-genutils==0.1.0
    ....
numpy==1.10.1
    ....
(pve35) fukuda@falcon:~/pve35% ipython   
Python 3.5.0 (default, Sep 15 2015, 23:58:24) 
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import numpy as np

In [2]: np.__version__
Out[2]: '1.10.1'

In [3]: np.show_config()
    ....
blas_opt_info:
    define_macros = [('NO_ATLAS_INFO', 3), ('HAVE_CBLAS', None)]
    extra_compile_args = ['-msse3', '-I/System/Library/Frameworks/vecLib.framework/Headers']
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
atlas_3_10_blas_threads_info:
  NOT AVAILABLE
lapack_opt_info:
    define_macros = [('NO_ATLAS_INFO', 3), ('HAVE_CBLAS', None)]
    extra_compile_args = ['-msse3']
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    ....

In [4]: A = np.random.rand(1000, 1000)
In [5]: %timeit B = np.linalg.inv(A)
10 loops, best of 3: 70.5 ms per loop

In [7]: C = np.random.rand(1024, 1024)
In [8]: %timeit D = np.fft.fft2(C)
10 loops, best of 3: 70.6 ms per loop
show_config() の出力にあるように、 BLAS/LAPACK は vecLib を使っていて、linalg.inv() に 70.5 ms かかっており、 Vivid の OpenBLAS (82.6 ms) に比べて、僅かに速い。 (fft.fft2() については 70.6 ms 対 53.3 ms でかなり負けているが。)

OS X での Numpy (MacPorts の OpenBLAS)

Core 数が半分の vivid に対して、あまり改善されていない(fft.fft2 についてははっきり劣化している)事が癪で、 むりやり OpenBLAS を作ってみる事にした。(fft は BLAS に依存していないので改善される見込みはないだろう、 という理性の声は圧殺して……)

pip にはインストール時のパラメータを追加する事はできなさそうなので、 MacPorts 版で openblas を付け加える事を試みた。

fukuda@falcon:~% sudo port install py35-numpy +openblas +scalapack
fukuda@falcon:~% sudo port installed py35-numpy
The following ports are currently installed:
  py35-numpy @1.10.1_1+gfortran+openblas (active)       #1)
fukuda@falcon:~% ipython-3.5
Python 3.5.0 (default, Sep 15 2015, 23:58:24) 
    ....
In [1]: import numpy as np
In [2]: np.__version__
Out[2]: '1.10.1'
In [3]: np.show_config()
    ....
lapack_opt_info:
    define_macros = [('NO_ATLAS_INFO', 3), ('HAVE_CBLAS', None)]
    extra_compile_args = ['-msse3']
    extra_link_args = ['-Wl,-framework', '-Wl,Accelerate']
    ....
openblas_info:                                         #2)
    language = c
    define_macros = [('HAVE_CBLAS', None)]
    libraries = ['openblas']
    library_dirs = ['/opt/local/lib']
blas_opt_info:
    language = c
    define_macros = [('HAVE_CBLAS', None)]
    libraries = ['openblas']
    library_dirs = ['/opt/local/lib']

In [4]: A = np.random.rand(1000, 1000)
In [5]: %timeit B = np.linalg.inv(A)
10 loops, best of 3: 70.9 ms per loop

In [6]: C = np.random.rand(1024, 1024)
In [7]: %timeit D = np.fft.fft2(C)
10 loops, best of 3: 71.3 ms per loop 
ここに、
  1. #1) '+openblass +scalapack' としても、variant としては、openblas しか入らないし
  2. #2) 実際にリンクされるのも BLAS として openblas のみ。 (LAPACK はディフォルトのまま。)
結果的に「速さ」は vecLib の BLAS とリンクされた場合と変わらない。

OpenBLAS と Numpy のビルド

そろそろ諦めムードが漂い始めるが、まあ、ここまで来たので、 BLAS/LAPACK をともども(Ubuntu のように)OpenBLAS からのライブラリで揃えてみよう、と決心した。

そのためには、OpenBLAS を(LAPACK 込みで)ビルドし、 そのライブラリを指して、NumPy をビルドすれば良いようだ。

まず OpenBLAS から。

http://www.openblas.net/ から OpenBLAS-0.2.15.tar.gz をダウンロード。

fukuda@falcon:~/build% tar xzvf Downloads/OpenBLAS-0.2.15.tar.gz
fukuda@falcon:~/build% cd OpenBLAS-0.2.15
fukuda@falcon:~/build/OpenBLAS-0.2.15% make NO_LAPACK=0 NO_LAPCKE=0    
fukuda@falcon:~/build/OpenBLAS-0.2.15% sudo make install
fukuda@falcon:~/build/OpenBLAS-0.2.15% ls /opt/OpenBLAS/lib -l
total 49232
drwxr-xr-x 3 root wheel      102 Nov 10 11:19 cmake/
lrwxr-xr-x 1 root wheel       30 Nov 10 18:18 libopenblas.a -> libopenblas_haswellp-r0.2.15.a
lrwxr-xr-x 1 root wheel       34 Nov 10 18:18 libopenblas.dylib -> libopenblas_haswellp-r0.2.15.dylib*
-rw-r-*-r-*- 1 root wheel 27388432 Nov 10 18:17 libopenblas_haswellp-r0.2.15.a
-rwxr-xr-x 1 root wheel 23014448 Nov 10 18:18 libopenblas_haswellp-r0.2.15.dylib*
Install guide には事もなげに、「単に make すればよい」なんて書いてあるけど、で、多分 Linux ではそれでよいのだろうけど、OS X ではそんなにすんなり行かなかった。

次に、NumPy そのもののビルド。Git で clone したソースの中にある cite.cfg.example を編集して対のようなエントリを作る([openblas] のエントリの uncomment だけで OK?)

# cite.cfg
[ALL]
#library_dirs = /usr/local/lib
#include_dirs = /usr/local/include
library_dirs = /opt/OpenBLAS/lib
include_dirs = /opt/OpenBLAS/include
[openblas]
libraries = openblas
library_dirs = /opt/OpenBLAS/lib
include_dirs = /opt/OpenBLAS/include
runtime_library_dirs = /opt/OpenBLAS/lib
その後、setup.py でビルドする。
(pve35) fukuda@falcon:~/pve35/Git% git clone http://github.com/numpy/numpy    
(pve35) fukuda@falcon:~/pve35/Git/numpy% python setup.py build_ext --inplace -j 4
この、setup.py build_ext --inplace が重要で、こうしておいて、ここから ipython を立ち上げると、 今ビルドした NumPy が import される。
(pve35) fukuda@falcon:~/pve35/Git/numpy% ipython 
Python 3.5.0 (default, Sep 15 2015, 23:58:24) 
    ....
In [1]: import numpy as np
In [2]: np.__version__
Out[2]: '1.11.0.dev0+cdef8d3'

In [3]: np.show_config()
openblas_info:
    runtime_library_dirs = ['/opt/OpenBLAS/lib']
    define_macros = [('HAVE_CBLAS', None)]
    library_dirs = ['/opt/OpenBLAS/lib']
    libraries = ['openblas', 'openblas']
    language = c
openblas_lapack_info:
    runtime_library_dirs = ['/opt/OpenBLAS/lib']
    define_macros = [('HAVE_CBLAS', None)]
    library_dirs = ['/opt/OpenBLAS/lib']
    libraries = ['openblas', 'openblas']
    language = c
lapack_opt_info:
    runtime_library_dirs = ['/opt/OpenBLAS/lib']
    define_macros = [('HAVE_CBLAS', None)]
    library_dirs = ['/opt/OpenBLAS/lib']
    libraries = ['openblas', 'openblas']
    language = c
blas_opt_info:
    runtime_library_dirs = ['/opt/OpenBLAS/lib']
    define_macros = [('HAVE_CBLAS', None)]
    library_dirs = ['/opt/OpenBLAS/lib']
    libraries = ['openblas', 'openblas']
    language = c

In [4]: A = np.random.rand(1000, 1000)
In [6]: %timeit B = np.linalg.inv(A)
10 loops, best of 3: 71.2 ms per loop
    
In [8]: C = np.random.rand(1024, 1024)
In [9]: %timeit D = np.fft.fft2(C)
10 loops, best of 3: 72 ms per loop
np.__version__np.show_config() から、確かに新しくビルドした NumPy が import されており、 しかもその NumPy は BLAS も LAPACK も先程の OpneBLAS でビルドした library をリンクしている、 取り敢えず目出度い……

が、パフォーマンスは殆んど変化がない(全く改善されていない。)

とすると、 Mac-mini (2-core/4-thread) の Yosemite と、 それから 2-vCore のみアサインした Fusion 上の Ubuntu の間でそれ程パフォーマンスが変わらないのは何故か、という疑問が残るが、 これは多分、Hyper Threading でパフォーマンスが 2 倍になる訣ではなく、高々 1.2 ~ 1.3 倍に過ぎない事を反映しているのだと思う。

まとめ

先の Ubuntu での結果も含めて、大まかに次の事が言えるのではないか……

2015-11-11 (Wed): BLAS/LAPACK と NumPy (その 1)

ここしばらくは NumPy を使う機会は極稀にしかなく、それも matplotlib に食わせるデータを作る等が主だった。 つまり、本来の(?)行列演算をガシガシなんて事は殆んど無かった訣だが、 行列乗算の二項演算子 ('@') が新たに導入された事をきっかけに、 そのあたり(行列演算)もちょっと弄ってみた。で、その変わり様に驚いた。

ただ、「変った」と言っても、その比較の対象というか「変わる前」がとても曖昧で、 Numarray, Numeric などが、NumPy の統合されたあたりの記憶が、 FORTRAN で「QR 分解」のルーチンを書いていた頃の話とごっちゃになっている (要は、15 年くらいの幅があるかも……ってくらいいい加減。) 当然「どう変わったか」についてはよく解らないので、 今はどうなっている(と思っている)かを纏めてお茶を濁す。

ちなみに、使った環境は

  1. falcon: OS X Yosemite (10.10.5), on Mac-mini (late 2014)
    1. CPU: Core i5, 2.6GHz 2 cores, 4 threads,
    2. Memory: 16GB, 1600GHz, DDR3
    3. Storage: Fusion Drive (2TB)
  2. vivid: Ubuntu-15.04, on VMware Fusion (7.1.2) on falcon. Fusion にアサインした H/W:
    1. CPU: 2 processor cores
    2. Memory: 4GB
    3. Storage: 20GB
Zsh (Bash) のプロンプトにこれらのホスト名を表示してある。

そもそも BLAS/LAPACK って何?

言うまでもなく、BLAS/LAPACK (かつては LINPACK だった?) は、行列計算を「高速」に実行するルーチン集 と言いたいところだが(実際 BLAS は、Basic Linear Algebra Subprograms の略)、 今や "Library API" になっている……今さら、と笑うなかれ、 これは私にとっては「大発見」だった。

で、その「実装」に Intel KML, ACML, GotoBLAS, OpenBLAS, Accelerate, etc. etc. が有る。(ATLAS や reference BLAS も「実装」の一つ。)

で、長くてややこしい話を煎じ詰めると

一方の LAPACK も 'Library API' と考えれば、かなりすっきりする。つまり「BLAS に基づいた 線形代数演算のルーチン(の規格)で、色々な実装がある」という塩梅……だが、 どうもこの認識は古くなってしまったようだ。 netlib.org のサイトには、LAPACK vendor library や Linux Distribution のリストがあるが、そのうちの多くがリンク切れか、 全く無関係なページに飛ばされる。なので、有り体に言うと:

NumPy module を新たにビルドする時「何を link すべきか」という話なら、以上で「御仕舞」だが、 BLAS/LAPACK をビルドするとなると、そのソースが必要になる。 BLAS は OpenBLAS で「決まり」だとしても、LAPACK がよく解らなかった。 しかし、OpenBLAS-0.2.15 をダウンロードして展開すると、 その中に lapack/lapack-netlib/ というディレクトリがあり、それぞれ、C と FORTRAN のソースが入っている (後者は、LAPACK-3.5.0 最新版)。試しにコンパイルしてみると
fukuda@falcon:~/build/OpenBLAS-0.2.15% make TARGET=HASWEL
    .....
 OpenBLAS build complete. (BLAS CBLAS LAPACK LAPACKE)

  OS               ... Darwin             
  Architecture     ... x86_64               
  BINARY           ... 64bit                 
  C compiler       ... CLANG  (command line : clang)
  Fortran compiler ... GFORTRAN  (command line : gfortran)
  Library Name     ... libopenblas_haswellp-r0.2.15.a
 (Multi threaded; Max num-threads is 4)    
のような結果となった。(LAPACKE は LAPACK の C-binding.) つまり、OpenBLAS を build すれば、BLAS と LAPACK の両方を含んだ dynamic link library ができあがるという事らしい。

Ubuntu-15.04 での NumPy

Debian (Ubuntu) では、先の BLAS/LAPACK の実装がパッケージとして用意されており、 しかも、インストールの後その中から実際にリンクする組合せを選べるようになっている。 つまり、NumPy は /usr/lib/{libblas.so.3|liblapack.so.3} をリンクして import されるが、そのリンク先を次のようにして変更する……
fukuda@vivid:~$ sudo update-alternatives --config libblas.so.3
There are 3 choices for the alternative libblas.so.3 (providing /usr/lib/libblas.so.3).

  Selection    Path                                    Priority   Status
------------------------------------------------------------
* 0            /usr/lib/openblas-base/libblas.so.3      40        auto mode
  1            /usr/lib/atlas-base/atlas/libblas.so.3   35        manual mode
  2            /usr/lib/libblas/libblas.so.3            10        manual mode
  3            /usr/lib/openblas-base/libblas.so.3      40        manual mode

fukuda@vivid:~$ sudo update-alternatives --config liblapack.so.3
There are 3 choices for the alternative liblapack.so.3 (providing /usr/lib/liblapack.so.3).

  Selection    Path                                      Priority   Status
------------------------------------------------------------
* 0            /usr/lib/openblas-base/liblapack.so.3      40        auto mode
  1            /usr/lib/atlas-base/atlas/liblapack.so.3   35        manual mode
  2            /usr/lib/lapack/liblapack.so.3             10        manual mode
  3            /usr/lib/openblas-base/liblapack.so.3      40        manual mode
(勿論、libopenblas-dev や、libatlas-base-dev は予め apt-get install しておく必要がある。)

この条件(以下、(blas, lapack) = (0, 0) と略記)の下で、簡単にパフォーマンスを確認してみた。

fukuda@vivid:~$ ipython3
Python 3.4.3 (default, Mar 26 2015, 22:03:40) 
Type "copyright", "credits" or "license" for more information.

IPython 2.3.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import numpy as np

In [2]: np.__version__
Out[2]: '1.8.2'                                         #1)

In [3]: np.show_config()                                #2)
    ....
lapack_opt_info:
    define_macros = [('NO_ATLAS_INFO', 1)]
    language = f77
    library_dirs = ['/usr/lib']
    libraries = ['lapack', 'blas']
    ....
blas_info:
    language = f77
    library_dirs = ['/usr/lib']
    libraries = ['blas']
blas_opt_info:
    define_macros = [('NO_ATLAS_INFO', 1)]
    language = f77
    library_dirs = ['/usr/lib']
    libraries = ['blas']
    ....
lapack_info:
    language = f77
    library_dirs = ['/usr/lib']
    libraries = ['lapack']
    ....

In [4]: A = np.random.rand(1000, 1000)

In [5]: %timeit B = np.linalg.inv(A)                     #3)
10 loops, best of 3: 82.6 ms per loop

In [6]: C = np.random.rand(1024, 1024)

In [7]: %timeit D = np.fft.fft2(C)                       #4)
10 loops, best of 3: 53.5 ms per loop
  1. #1) Vivid で得られる最新版を使ったが Python-3/ipython ともども、ちょっと古い。
  2. #2) numpy モジュールはこのようなメソッドを持っていて、 ビルド時のパラメータを表示する。この場合、上述のように、 /usr/lib/ 以下にある symbolic link を指している。
  3. #3) numpy.linalg.inv は逆行列の計算。%timeit は iPython のディレクティブで、CPU 時間を正確に測定する。
  4. #4) リファレンスのため、fft.fft2 (2 次元 FFT) の計算時間を測定。
このような簡易ベンチマークを、BLAS/LAPACK の実装ごとに実施した結果を以下に纏める。
BLAS/LAPACK のパフォーマンス比較
Config linalg.inv()
(ms)
fft.fft2()
(ms)
Notes *1)
(0, 0)82.6 53.3OpenBLAS
(1, 1)269 54.1 ATLAS
(2, 2)1030 53.5reference BLAS(?)

FFT の計算時間に殆んど変化が無いのに対し、 逆行列の方は、きれいに、OpenBLAS > ATLAS > reference BLAS の順になっている。(ATLAS との差がこれ程大きいのはちょっと意外。)

絶対値がどれ程のものか見当をつけるために kashi さん のところでの結果と比べられるよう、追加の測定をしてみた。

In [2]: A = np.random.rand(5000, 5000)

In [3]: %timeit B = np.linalg.inv(A)
1 loops, best of 3: 6.87 s per loop

In [4]: import time 

In [5]: t=time.time(); B = np.linalg.inv(A); time.time() - t
Out[5]: 7.031937837600708
5000 x 5000 の行列についての計算時間は、7.0 秒と出たが、 Kashi さんの結果は、6.0 秒との事。H/W と OS は、それぞれ
Kashi さん:
OS: Ubuntu-14.04 (on VMware)
CPU: i7 3770T, 4 cores, 4 GB, 
    
今回:
OS: Ubuntu-15.04 (on VMware Fusion)
CPU: i5 4278U, 2 cores (threads?), 4GB, 2.6 GHz 
なので、妥当な性能が出ている、と言ってよいだろう。 (Core 数とクロック周波数からすると、今回の結果は出来過ぎ?)

2015-11-04 (Wed): やっぱり Time Machine の実現は難しかった

いや、実は OSX の TM の話。(Netatalk といい、Time Machine といい、ネタにするには格好の……)

Netatalk を 3.1.7 にして、ようやく動き始めた、と思っていたが、 また偶に Time Machine がバックアップに失敗するようになった。 大概は「バックアップが完了しませんでした」だが、 一度など、「コネクションが張れません」とやられた (ssh などはちゃんと動いているので、そんな筈はないのだが……)

もう一遍だけ、first backup からやり直す事にした。 今度は、もし実験に成功したらそのまま実稼働に移行できるように、fstab を設定する。(ついでに、Time Machine が乗っている Hawk の default user の fukuda に固有のバックアップにする事も試みる。)

pi@raspi06:~$ sudo umount /home/share
pi@raspi06:~$ sudo systemctl stop netatalk
pi@raspi06:~$ sudo adduser fukuda
pi@raspi06:~$ sudo addgroup fukuda sudo
pi@raspi06:~$ su fukuda
fukuda@raspi06:~$ mkdir tm_hawk
#1)
fukuda@raspi06:~$ sudo chmod 777 tm_hawk
fukuda@raspi06:~$ exit
pi@raspi06:~$ sudo blkid
    .....
/dev/sda1: LABEL="EFI" UUID="67E3-17ED" TYPE="vfat" \
    PARTLABEL="EFI System Partition" PARTUUID="1ba9a00f-4f1a-495c-9fd3-9366ab1e5357"
    ....
/dev/sda5: UUID="434f1e3f-f871-42a7-8352-7be11f6b9bef" TYPE="ext4" \
    PARTLABEL="Untitled 4" PARTUUID="189c0679-699b-4a4c-9a1b-5cc8009596f8"

pi@raspi06:~$ sudo emacs /etc/fstab
pi@raspi06:~$ cat /etc/fstab
proc            /proc           proc    defaults          0       0
/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1
    .....
#2)
UUID="434f1e3f-f871-42a7-8352-7be11f6b9bef"  /home/fukuda/tm_hawk ext4 defaults 0 2
ここで、
  1. #1) 最初これをやるのをさぼったら、Time Machine からアクセスできなかった。早くも目論見は外れた訣だが、 その下にできるバックアップファイルは
    fukuda@raspi06:~$ ls -l tm_hawk
    total 24
    drwx------ 2 root   root   16384 Oct 30 17:58 lost+found
    drwx--S--- 3 fukuda fukuda  4096 Nov  5 04:45 Taka Fukuda’s MacBook Air.sparsebundle
    
    のようになるので、一応のセキュリティは実現できたように思う。
  2. #2) ここで最後のところを '0 2' にするのは大事。うっかり '0 0' などとやると、Raspi が正常に立ち上がらなくなる……
それに応じて、 /usr/local/etc/afp.conf に、以下のエントリを追加
[Hawks Time Machine]
    path = /home/fukuda/tm_hawk
    time machine = yes
    vol size limit = 512000
以下
% pi@raspi06:~$ sudo mount -a
% pi@raspi06:~$ sudo systemctl restart netatalk 
として、Hawk から、Time Machine backup を試み、 正常にスタートする事を確認してから、
% pi@raspi06:~$ sudo reboot
とし、Raspi が立ち上がるのを確認して、Hawk の Time Machine から first full-backup を開始。

Full-backup にはやはり 10 時間かかった。その後、Raspi のリブート、 Time Machine の ON/OFF、手動の backup 開始、などの意地悪テストを丸一日続けているが、不具合は出ていない。

まとめ(の代りの)メモ:


2015-11-01 (Sun): Raspberry Pi で Netatalk-3

Raspi (Raspbian Jessie on Raspberry 2 B) 上の netatalk、「意外にすんなり動いたぜ」と思ったが、 Time Machine による最初の full-backup の後、(最初、もしくは二度目の) 「定時(部分)バックアップ」でいきなり躓いた……転送が終った後の cleaning up が終らない。10 時間あまりも時間をかけて full-backup を取った後の、何とも微妙な転け方で、 どうしたものかと悩んだが、 事が backup 関連だけに、 ここでいい加減に済ませては先々後悔しそうな気がして、 この際とことん付き合ったろやないか、と。

何故か Debian (従って、Ubuntu や Raspbian) のパッケージは、2.2.2 か 2.2.5 どまり (一昨日使ったのは、 この 2.2.5) だが、実は netatalk は -3.1.7 になっている。 これを build してみる事にした。(コマンドの build なんて何年ぶりやろか。)

既に Ubuntu-15.04 のためのインストールマニュアルは有る。これが既に「大変そう」だが、Raspbian も Debian 由来なので、なんとかなるのではないか。

まず、今走っている netatalk-2.2.5 を止め、package を消去。 netatalk-3.1.7 のビルドに必要なヘッダ群をインストールする。

pi@raspi06:~$ sudo systemctl stop netatalk
pi@raspi06:~$ sudo systemctl disable netatalk
pi@raspi06:~$ sudo apt-get remove netatalk
pi@raspi06:~$ sudo apt-get install build-essential
pi@raspi06:~$ sudo apt-get install libevent-dev libssl-dev libgcrypt11-dev \
    libkrb5-dev libpam0g-dev libwrap0-dev libdb-dev libtdb-dev libmysqlclient-dev

pi@raspi06:~$ sudo apt-get install libavahi-client-dev libacl1-dev \
    libldap2-dev libcrack2-dev systemtap-sdt-dev libdbus-1-dev

pi@raspi06:~$ sudo apt-get install libdbus-glib-1-dev libglib2.0-dev \
    tracker libtracker-sparql-1.0-dev libtracker-miner-1.0-dev 
ついで、netatalk-3.1.7 のソースをダウンロードして、ビルド・インストール:
pi@raspi06:~$ wget
   http://sourceforge.net/projects/netatalk/files/netatalk/3.1.7/netatalk-3.1.7.tar.gz
pi@raspi06:~$ tar xzvf netatalk-3.1.7.tar.gz
pi@raspi06:~$ cd netatalk-3.1.7/
pi@raspi06:~/netatalk-3.1.7$ ./configure \
    --with-init-style=debian-systemd --without-libevent --without-tdb \
    --with-cracklib --enable-krbV-uam --with-pam-confdir=/etc/pam.d \
    --with-dbus-sysconf-dir=/etc/dbus-1/system.d \
    --with-tracker-pkgconfig-version=1.0 
  
pi@raspi06:~/netatalk-3.1.7$ make
pi@raspi06:~/netatalk-3.1.7$ sudo make install
ここで最初の config parameter を debian-sysv から debian-systemd に変更した。また、tracker-pkgconfig-version は各自確認するように、とあったが、まだ 1.0 のままだった。 先の xxxx-dev のインストールや、configure の option に間違いなければ、無事コンパイル・インストールは終る筈。 しかし、「10時間かけて full-backup を終えた後に不具合が見つかる」なんて悲劇を避けるために、 きちんと確認した方が良い。
pi@raspi06:~/netatalk-3.1.7$ afpd -V
afpd 3.1.7 - Apple Filing Protocol (AFP) daemon of Netatalk
.....
afpd has been compiled with support for these features:

          AFP versions:	2.2 3.0 3.1 3.2 3.3 3.4 
         CNID backends:	dbd last tdb mysql 
      Zeroconf support:	Avahi
  TCP wrappers support:	Yes
         Quota support:	Yes
   Admin group support:	Yes
    Valid shell checks:	Yes
      cracklib support:	Yes
            EA support:	ad | sys
           ACL support:	Yes
          LDAP support:	Yes
         D-Bus support:	Yes
     Spotlight support:	Yes
         DTrace probes:	Yes

              afp.conf:	/usr/local/etc/afp.conf
           extmap.conf:	/usr/local/etc/extmap.conf
       state directory:	/usr/local/var/netatalk/
    afp_signature.conf:	/usr/local/var/netatalk/afp_signature.conf
      afp_voluuid.conf:	/usr/local/var/netatalk/afp_voluuid.conf
       UAM search path:	/usr/local/lib/netatalk//
  Server messages path:	/usr/local/var/netatalk/msg/
各項目がこうなっていなければ、それは多分、 開発用ヘッダのインストールの際に漏れが有ったか、configure のオプションに誤りが有ったという事。

きちんと、インストールできている事を確認の後、先の例と同様に USB-HDD パーティションをマウントし、それに応じて afp.conf を修正した後、daemon をスタートする

pi@raspi06:~/netatalk-3.1.7$ sudo chmod 777 /home/share
pi@raspi06:~/netatalk-3.1.7$ sudo mkfs.ext4 /dev/sda2
pi@raspi06:~/netatalk-3.1.7$ sudo mount /dev/sda2 /home/share
pi@raspi06:~/netatalk-3.1.7$ sudo nano /usr/local/etc/afp.conf
pi@raspi06:~/netatalk-3.1.7$ cat /usr/local/etc/afp.conf
; Netatalk 3.x configuration file

[Global]
; Global server settings

[Homes]
  basedir regex = /home

[My Time Machine Volume]
    path = /home/share
    time machine = yes
    vol size limit = 512000
pi@raspi06:~/netatalk-3.1.7$ sudo systemctl restart avahi-daemon
pi@raspi06:~/netatalk-3.1.7$ sudo systemctl start netatalk 
ここで触るのが /etc/netatalk/AppleVolumes.default ではなく、 /usr/local/etc/afp.conf である事に注意。 ここまでで、Hawk の [Select Disk ...] に、 My Time Machine Volume on 'raspi06' が表われるので、それを選択して、バックアップを開始する。
Time Machine Preference-2
Raspi06 + netatalk-3.1.7 で Time Machine
(Disk を選択した後の確認画面)

ここでまた一晩待つ。(バックアップ容量 80 GB, WiFi 11n 越しで 12 時間掛った。)

待ち草臥れて、終るのを見逃してしまい、 気がついた時には最初の「部分バックアップ」が終っていた。 その後 10 回程、自動(部分)バックアップをやらせてみた結果が以下。

Time Machine Preference-3
全バックアップを終了後、自動バックアップを 10 回程やらせてみた
また、Time Machine に入ってみたりもした。 どうやらうまく動いているようだ。

2015-10-31 (Sat): Backup 体制を見直し

先週自宅サーバ (Ubuntu-14.04LTS + ThinkPad X200) の電池を新しくしたので、 来年四月 (Ubuntu-16.04LTS のリリース時期)と言わず、もうちょっと先まで使い続けたい、ついてはその HDD が心配なので、そのバックアップが要るだろう……が事の発端。

MacPro (Quadra) が健在だった頃は、2TB の内蔵 HDD を二台積んで、 そのうちの一つを Time Machine の HDD にしていた。 上記自宅サーバ (Lark) や、MacBook (Mavericks, Hawk) は、 自分の作ったファイルだけを rsync で Quadra と同期し、 それを丸ごと、Time Machine に入れていた。 少々オーバーキルの憾みはあるが、ホントに手間いらずだった。

が、その大元になっていた Quadra が逝ってしまい、 その後を継いだ Mac-mini (Falcon) に、HDD 二台持ちのバージョンが無くなっていた、というあたりから、 少々悲しい状態になっていた。つまり 2 TB の USB-HDD に二つの partition を切って、Falcon と Hawk でそれを共有する(普段は Falcon に継いでいて、 たまに、手で Hawk に継ぎ換える……)勿論、Hawk のバックアップは、一月とか 1 Quarter に 1回などというペースになる。

このあたりを何とかもっとスマートにしたい、に加えて、 Lark のフルバックアップを、というのが今回のお題である。

Falcon (Main Desktop) は、専用 USB HDD へ

全ファイルサーバを NAS で統一……と当初は思ったが、 お値段が張るのと、Time Machine との相性が心配。 特に、この部分は全てのバックアップ(冗長性)の大元になるので、 信頼性を第一にしたいという事で、WD の Red (2 TB) を載せた LogiTech の USB-HDD を直結する事にした。 現状と代わり映えしない、という憾みもあるが、 2 TB を専有させる事で、「ケーブルの抜き差し」とか、 「満杯になって古いファイルを消す」際の不安(少し前に、 これで Volume 全体が壊れた)は、 かなり軽減できるのではないか、と思う。

もうひとつの重大な要素は「ファイルフォーマット」 これまでは、as is の NTFS をそのまま使っていたが、 今回、OSX につないでから、 HFS+ (Journaled, case-insensitive) で再フォーマットした。どうやらこれは大正解だったみたいで、 前の HDD で最初のバックアップを取った時、10 時間以上かかったものが、今回(ディスクの容量は増えているのに)5 時間で済んだ。

その他のホスト (OSX + Ubuntu) はファイルサーバで

これらのバックアップがいい加減で良い、という訣ではないのだが、 という事で、Raspberry Pi 2 B と USB HDD でファイルサーバを構築する事を思い立った。

ちょっと調べた限りでは、なかなか大変なように見えたが、 やってみると意外にスンナリ行った……

pi@raspi06:~$ sudo apt-get install netatalk
pi@raspi06:~$ apt list --installed netatalk
Listing... Done
netatalk/stable,now 2.2.5-1+b1 armhf [installed]
pi@raspi06:~$ sudo mkdir /home/share
pi@raspi06:~$ sudo chmod 777 /home/share    
pi@raspi06:~$ nano /etc/netatalk/AppleVolumes.default    
pi@raspi06:~$ tail /etc/netatalk/AppleVolumes.default
.....
# The line below sets some DEFAULT, starting with Netatalk 2.1.
:DEFAULT: options:upriv,usedots

# By default all users have access to their home directories.
# ~/			"Home Directory"            #1)
/home/share "Time Capsule" options:tm           #2)
# End of FileListing... Done
    
pi@raspi06:~$ lsblk 
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0   1.8T  0 disk 
├─sda1        8:1    0   200M  0 part 
├─sda2        8:2    0 465.8G  0 part 
├─sda3        8:3    0 465.9G  0 part 
├─sda4        8:4    0 465.8G  0 part    #3) 
└─sda5        8:5    0 465.1G  0 part 
mmcblk0     179:0    0  29.7G  0 disk 
├─mmcblk0p1 179:1    0    56M  0 part /boot
└─mmcblk0p2 179:2    0  29.7G  0 part /
    
pi@raspi06:~$ sudo mkfs.ext4 /dev/sda4    
pi@raspi06:~$ sudo chmod 777 /home/share
pi@raspi06:~$ sudo mount /dev/sda4 /home/share 
pi@raspi06:~$ sudo systemctl restart netatalk 
  1. #1) この行はコメントアウト(しなくても問題無さそうだが)
  2. #2) 追加したのはこれだけ。"Time Capsule" が、Time Machine から見える(ちょっと紛らわしい名前にしてしまったかも。) options:tm がミソ。
  3. #3) この USB-HDD は、Mac OSX で partition を切った。 使ってないこの partition を以下で、ext4 にフォーマットして使う
ここまでで、Hawk の Time Machine で、'Time Capsule on "raspi06"' が認識され、それを選べば Backup が始まる……

date plot with NPT
Raspi06 + netatalk-2.2.5 で Time Machine サーバ
最初のフルバックアップに 8 時間どころか、都合 10 時間もかかったが、 とりあえずは完了した。

色々右往左往したが、ポイントは

という事のようだ。

2015-10-25 (Sun): 自宅サーバのバッテリを交換

ThinkPad X200 + Ubuntu-14.04LTS

X200 を自宅サーバにしてから、もう 2年になるのかぁ。最初の頃 (Ubuntu-13.10) は結構色々なトラブルが有ったが、 Ubuntu-14.04 にしてからの 1年半は、問題らしい問題は一度も無かったように思う。

ただ、X200 は 2008 年に買ったものなので、もう 7 年になる。X23 の例からすると、そろそろ乗り換えを考えても良い頃かも…… という事で、色々な「野心的な案」を検討してみた……

  1. OS を freeBSD に: OSX では専ら MacPorts なので、FreeBSD にしたら、port コマンドでスイスイ…… と長らく憧れていたのだが、今回何年ぶりかで Fusion に最新版 (-10.2) を入れてみた。 PC H/W に直接インストールしていた頃は、四苦八苦した記憶があるが、 今回はすんなりインストールが完了した。 Xorg が入ってない、というのにまず驚くが、「まあ、サーバにするのが目的だし」 と自分に言い聞かせて次に行こうとするが、パッケージ管理は pkg を使うのだった。他にも、(sudo も su も使えない等) Linux との違いは多々あるが、それらは追々修正できるだろうが、pkg が MacPorts の port とあまり似ていないのはどうしようもなさそうで、 とってもガッカリした。で、早々に諦めた。
  2. H/W を Mac に: Notebook をサーバとして使うと LCD が宝の持ち腐れとなるのが癪だった。 一方値段の割に Mac-mini が素晴しいので、 OSX の MacPorts のコマンド群を使ってサーバにするか、もしそれが難しければ、 Fusion で Linux を入れて……。しかし、これ、OSX の元からある機能とのバッティングが心配。(例えば、mDNS が、 dhcpd とうまく協調してくれない。)何より、UPS が要る。
  3. MacBook: 今の MacBook Air をサーバに。しかし、これ、 上の OSX 共通の問題に加えて、 1) メモリが 2GB しか乗ってない、 2) SSD が 128G しかついていなくて、かつ、そのうちの 15% しか空きがない、という問題が。
  4. VPS: ちらちらと横目で見ていたが、なかなか良さげ。 一月 1700 円くらいで、2GB/200GB のサーバが持てる(現在の X200 は、 2GB/160GB) 。各社プライスパフォーマンスは似たりよったりだが、 さくらの VPS さんの「OS は何でも OK」というあたりに惹かれている。 最初は「安いなあ」と感心したものの、12 万円の ThinkPad を 7 年保たせたら、トントンになる……
などと妄想を膨らませつつ、結局は ThinkPad かなぁ、でも、 サーバのために新品を買うのはちょっと……

バッテリが逝ってしまった

などと思い悩んでいたのだが、先週だったか、 件の ThinkPad のバッテリインジケータが、 オレンジの点滅になっているのに気がついた。 これはしかし "Low Battery" の意味なので、然程緊急の問題とも思っていなかった。 が、一昨日ログインしたついでに、調べてみたら、

fukuda@lark:~% upower -i /org/freedesktop/UPower/devices/battery_BAT0
  native-path:          BAT0
  power supply:         yes
  updated:              Fri 23 Oct 2015 06:27:49 PM JST (23 seconds ago)
  has history:          yes
  has statistics:       yes
  battery
    present:             yes
    rechargeable:        yes
    state:               charging
    energy:              0 Wh
    energy-empty:        0 Wh
    energy-full:         0 Wh
    energy-full-design:  0 Wh
    energy-rate:         0 W
    percentage:          0%
    capacity:            100%
となっているではないか…… これは、もうバッテリが死んでいるという事なので、 今停電したら、もしくは、電源ケーブルを抜いたら、 即クラッシュする事になる。 (大昔 X23 で、態々試して確認済み——好奇心に勝てずに、 ついつい電源ケーブルを抜いたら、クラッシュ……)

慌てて上の「新サーバオプション」を前倒しで実現する事を考えたが、 「停電したらクラッシュ」に背中を押されて、 とりあえずバッテリを新しくする事にした。 今回は、来年の四月まで保てばいいや、という事で互換モデル (World Plus 製) にした。金 4000 円也。

発注の翌日に届いて(さすがアマゾンさん)、 電源ケーブルを差したまま、バッテリを入れ替え、様子を見てみると

fukuda@lark:~% upower -i /org/freedesktop/UPower/devices/battery_BAT0
  native-path:          BAT0
  vendor:               SANYO
  model:                42T4646
  serial:               8030
  power supply:         yes
  updated:              Sun 25 Oct 2015 04:04:22 PM JST (0 seconds ago)
  has history:          yes
  has statistics:       yes
  battery
    present:             yes
    rechargeable:        yes
    state:               charging
    energy:              28.47 Wh
    energy-empty:        0 Wh
    energy-full:         48.6 Wh
    energy-full-design:  47.52 Wh
    energy-rate:         34.337 W
    voltage:             11.816 V
    time to full:        35.2 minutes
    percentage:          58%
    capacity:            100%
    technology:          lithium-ion
  History (charge):
    .....
という事で、目出たく復旧した模様。

この電池交換は、自宅サーバ更新とは一応別の「応急処置」のつもりだったが、 こうして 4000円也をはたいてみると、来年の 4月に、do-release-upgrade で、さくっと 16.04LTS にして、このままさらに一年くらい使えるかな……なんてスケベ心が出てきた。 とすると、次に「問題」が出るとしたら、それは多分 HDD がらみなので、いずれにせよ full-backup の心配をせねば。


2015-10-21 (Sun): Python/NumPy Update (その 2)

「行列演算」再訪

先の記事では、ndarray の行列の掛け算の二項演算子 '@' が導入された……という話を書いたが、我ながら「頓珍漢」だったなぁ。

というのも、NumPy には、ndarray に加えて、matrix という class も有って、そちらでは '*' が、 普通に行列の掛け算になっている……

In [1]: import numpy as np

In [2]: A = np.matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]])

In [3]: A
Out[3]: 
matrix([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])

In [4]: x = np.matrix([1, 1, 0])

In [5]: x
Out[5]: matrix([[1, 1, 0]])

In [6]: x.T
Out[6]: 
matrix([[1],
        [1],
        [0]])

In [7]: A * x.T
Out[7]: 
matrix([[ 1],
        [ 7],
        [13]])

In [8]: A @ x.T
Out[8]: 
matrix([[ 1],
        [ 7],
        [13]]) 
    
In [9]: A_arr = np.array(A)

In [10]: A_arr
Out[10]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]]) 
また、ベクトルの転置も(reshape() して云々という手間をかけずに)自然にできている (In/Out[9])。 つまり、'*' で行列の乗算をしたければ、 numpy.matrix でやれば良い……。それを、また ndarray に戻すのも簡単。それでは何故 '@' が必要なのか、という事になるが、それがすっきりと納得できず、 いい加減にお茶を濁したのだった。

PEP 465 には、色々「必要性」が書いてあるが、今一納得しきれなかった。 しかし、色々弄っているうちに、「まあ、それも良いかな」から、 はっきり「これは確かに嬉しいかも」と思えるようになった。

つまり

  1. np.dot(A, B) は煩雑になるので何とかしたい。
  2. '*' の overloading で、 両方の行列乗算を表すようにする、というのも一案。 しかし、それでは A * B とあった時、A, B がどういう dtype なのかを知らないと、要素毎の乗算なのか、 行列乗算なのか直ぐには解らない。
  3. 新しい '@' を導入する事で、 必ず「行列乗算」である事を示せる。numpy.matrix についても '@' が行列乗算を表すので (上のリストの In/Out [8])、これを必ず使う事にすれば、A, B の dtype にかかわりなく、A @ B とあれば、行列乗算であるとすぐに解る。
こうやって整理すると (「PEP で力説されている事そのままぢゃん」という感じもするが) ようやく、'@' 導入のメリットが、 「記号選定」への違和感を上回ると納得できた。

Axis の「順序」

これに関する「違和感」も、どう言い表せばよいのかちょっと悩んでしまう。 NumPy の ndarray や matrix の Axis というのは、 つまるところ「行」とか「列」とかの、Index が伸びて行く方向の事 (「行ベクトルは」、列軸だけを持つ事に注意。) 端的に言うと、reshape() method の引数や、 .shape attribute の並び方の問題。で、二次元配列(行列)までは、どちらも、 (行, 列) となっている。問題はこの先で、 次の次元(とりえあず「高さ」次元)を加える時、(FORTRAN からの連想だと)(行, 列, 高) となってしかるべきだと思うが、 NumPy では(高, 行, 列) となるのだった。 順番が逆というより、一貫していないように思える…… FORTRAN の三次元配列が、例えば A(I, J, K) と表わされるなら、 NumPy のは(A(K, J, I) ではなく)、A(K, I, J) となる……のが不満だと。
In [52]: A2 = np.arange(12).reshape(3, 4)

In [53]: A2
Out[53]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [54]: A3 = np.arange(24).reshape(2, 3, 4)

In [55]: A3
Out[55]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

しかし、「曰く言い難い違和感」をこのように整理してみると、自分にとって「Axis の順序が憶え難い」という事の他には不具合は無さそうな気がしてきた。 reshape() に際しては、(高, 行, 列) の順序で、サイズを指定すれば良いのだし、 indexing もこの順序で OK。 transpose() に際しては、(0, 1, 2) が (高, 行, 列) で、 その行と列を入れ替えるなら、(0, 2, 1) とすれば良い、という塩梅。

In [56]: A3[1, :, :]
Out[56]: 
array([[12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
# 「高さ」方向の二つ目の要素を切り出した
In [57]: A3[:, :, 1:3]
Out[57]: 
array([[[ 1,  2],
        [ 5,  6],
        [ 9, 10]],

       [[13, 14],
        [17, 18],
        [21, 22]]])
# 「行」方向の二つ目と三つめの要素を切り出した
In [58]: A3.transpose()
Out[58]: 
array([[[ 0, 12],
        [ 4, 16],
        [ 8, 20]],

       [[ 1, 13],
        [ 5, 17],
        [ 9, 21]],

       [[ 2, 14],
        [ 6, 18],
        [10, 22]],

       [[ 3, 15],
        [ 7, 19],
        [11, 23]]])

In [61]: (A3.transpose() == A3.transpose(2, 1, 0)).all()
Out[61]: True
# ディフォルトが 0-軸(列) と 2-軸(高さ)の入れ替えになっている
# (.all() は、要素毎の比較の結果の AND を取る、という意味)
In [62]: (A3.transpose() == A3.T).all()
Out[62]: True
# .T アトリビュートは上のディフォルトと同じ
In [63]: A3T = A3.transpose(0, 2, 1)
# 行と列を入れ替える変換
In [64]: A3T.shape
Out[64]: (2, 4, 3)

In [65]: A3T @ A3
Out[65]: 
array([[[  80,   92,  104,  116],
        [  92,  107,  122,  137],
        [ 104,  122,  140,  158],
        [ 116,  137,  158,  179]],

       [[ 800,  848,  896,  944],
        [ 848,  899,  950, 1001],
        [ 896,  950, 1004, 1058],
        [ 944, 1001, 1058, 1115]]]) 
# 2 つの (4, 3) 行列と 2 つの (3, 4) 行列の積の結果、二つの (4, 4) 
# 行列ができている

2015-10-18 (Sun): Python/NumPy Update

ずっと Django の事を書いて来ているが、でも、これはどうも「Python の話」という気がしない。(ホントは Django に飽きてしまった、もしくは、その Learning Curve の急峻さに嫌気が差したのかも。) では、何にぞくぞくするか、と言われるとやはり NumPy だろう。 先の McKinney さんの本には、NumPy の概要を纏めた章も有って、 久方ぶりに brush-up した。

Ndarray の乗算

Python-3.5.0 の What's New に、"a new matrix multiplication operator: a @ b" というのが出ていた。当初、 「これを実装したモジュールはまだ無い」とあったが、程なく、 「NumPy 1.10 はこれをサポートする予定」との記述に変わった。 これはちょっかいを出さない訣には……

「Python-3.5 にするのはもっと先」と決心した筈なんだけど、pyvenv + pip なら簡単に試せそうなので、そっちでやってみた。

fukuda@falcon:~% sudo port install python35    
fukuda@falcon:~% pyvenv-3.5 pve35    
fukuda@falcon:~% cd pve35
fukuda@falcon:~/pve35% . bin/activate              
(pve35) fukuda@falcon:~/pve35% pip-compile
    .....
gnureadline==6.3.3        # via ipython
ipython==4.0.0
matplotlib==1.4.3
numpy==1.10.1
    .....
(pve35) fukuda@falcon:~/pve35% pip-sync   
これだけで、準備は完了。早速 iPython を立ち上げて、
(pve35) fukuda@falcon:~/pve35% ipython
Python 3.5.0 (default, Sep 15 2015, 23:58:24) 
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import numpy as np

In [2]: A = np.arange(9).reshape(3, 3)

In [3]: A
Out[3]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [4]: B = np.arange(-4, 5).reshape(3, 3)

In [5]: B
Out[5]: 
array([[-4, -3, -2],
       [-1,  0,  1],
       [ 2,  3,  4]])

In [6]: A * B
Out[6]: 
array([[ 0, -3, -4],
       [-3,  0,  5],
       [12, 21, 32]])

In [7]: np.dot(A, B)
Out[7]: 
array([[  3,   6,   9],
       [ -6,   6,  18],
       [-15,   6,  27]])

In [8]: A @ B
Out[8]: 
array([[  3,   6,   9],
       [ -6,   6,  18],
       [-15,   6,  27]]) 
となるのを確かめた。つまり、A * B は、要素ごとの掛け算、 numpy.dot(A, B)A @ B は、行列同志の掛け算となっている。(この後の方が、 Python-3.5 と NumPy-1.10 による今回の新機軸。) ちなみに、Numpy-1.9.2 ではエラーとなる——MacPorts の py35-numpy (-1.9.2) で、実験してみた
fukuda@falcon:~% python3.5
Python 3.5.0 (default, Sep 15 2015, 23:58:24) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> np.__version__
>>> '1.9.2'

>>> A = np.arange(9).reshape(3, 3)                                          
>>> B = np.arange(-4, 5).reshape(3, 3)
>>> np.dot(A, B)
>>> array([[  3,   6,   9],
       [ -6,   6,  18],
       [-15,   6,  27]])

>>> A @ B
>>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for @: 'numpy.ndarray' and 'numpy.ndarray'

NumPy を最初に習え憶えた頃、A * B が、行列の要素ごとの掛け算だった事にかなり違和感を感じた。 ちょっと後になって、これは、Universal Function を素直に表現するため、というか、それと混在するためには自然な方法だ、 と納得した——A +/- B, A ± k, k * A (k はスカラ) 等と並べれば、 A * B が要素ごとの掛け算としたい、という気持はまあ解る。

ただ、なぜ '@' なのか、という違和感が拭えない…… それでも、まあ、infix operator (C で言うなら、binary operator?) の効用は大きいだろうから、この違和感もそのうち消えるだろう。 なにしろ、

S = dot((dot(H, beta) - r).T, dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))
のように書かざるを得なかったものが、
S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r) 
とできるのだから。(例は Python-3.5 の What's New から取った。)

Vector と Array

この他にも「最初ワケワカだったけど今はなんとなく使えている」 という例はいくつか有って、その大物の一つは ベクトル (vector) の扱い。線形代数では、例えば Ax= b と書いた時の x, b は列ベクトルで a11 a12 a13 a21 a22 a23 a31 a32 a33 x1 x2 x3 = b1 b2 b3 という意味。行ベクトル xT , bT については、これは、 xT AT= bT すなわち、 x1 x2 x3 a11 a21 a31 a12 a22 a32 a13 a23 a33 = b1 b2 b3 となる。

こんな事は基本のキなので、NumPy 使えば、ひょいひょいとやれる筈……

In [4]: A = np.arange(9).reshape(3, 3)
In [5]: A
Out[5]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
# iPython の Array の表示は美しい
In [6]: x = np.array([1, 1, 0])
# x は行ベクトル (?)
In [7]: A @ x.T
Out[7]: array([ 1,  7, 13])
# x.T が列ベクトルなので、 上の Ax を計算している
In [8]: np.dot(A, x.T)
Out[8]: array([ 1,  7, 13])
# np.dot (既存の演算)でも確認
In [9]: x @ A.T
Out[9]: array([ 1,  7, 13])
# 行ベクトルでも、正しい答が
In [10]: x @ A
Out[10]: array([3, 5, 7])
# 転置を忘れると答が違う、ここまでは So Good
In [11]: A @ x
Out[11]: array([ 1,  7, 13])
# あれ、行ベクトルのままでも同じ結果が
In [12]: x.T
Out[12]: array([1, 1, 0])
# そもそも、転置が起きていない……
で、もうワケワカ…… 私の「当惑」を整理すると概略このようになるだろうか。 後知恵だが、Out[7] で答が「列ベクトル」になってないあたりで、 「おかしい」と気付くべきだろうが、行ベクトルを入れて、 行ベクトルが出てくる(Out[11]) ので、まあ、こういうものかな、 と納得して、その後はずっと使ってきた。

しかし、 Infix Operator の '@' が、ほんとに np.dot() と等価かどうかを確かめている過程で、 どうもこのあたりが気色悪く思えてきて、ちょっと追及してみた。

自分の混乱の根源は 「ndarray (n 次元の array) は『行列』とは違う」のを忘れていた事に尽きそう。 行列としての演算(掛け算、転置等)は、 オペランドの ndarray が、双方ともに二次元の時だけ意味を持つ。 言い換えれば、array([1, 1, 0]) は(行列の演算が適用される)「行ベクトル」ではない。 x をちゃんとした行ベクトル(1 行 n 列の行列)とするところから始めると

In [14]: x = np.array([1, 1, 0])
In [15]: x.shape
Out[15]: (3,)
# x は 1 次元の array。'(3,)' は要素数 1 のタプルを表わす。
In [16]: xrow = x.reshape(1, 3)
In [17]: xrow.shape
Out[17]: (1, 3)
# これで、1 行 3 列の行列(行ベクトル)になった。
In [20]: xrow
Out[20]: array([[1, 1, 0]])
# '[' が二重になっている。
In [21]: xrow.T
Out[21]: 
array([[1],
       [1],
       [0]])
# 転置の結果もちゃんと列ベクトルになっている
In [22]: A @ xrow.T
In [23]: A = np.arange(9).reshape(3, 3)
# あらためて、Ax を計算
In [24]: A @ xrow.T
Out[24]: 
array([[ 1],
       [ 7],
       [13]])
# 結果もちゃんと列ベクトルになっている
In [26]: xrow @ A.T
Out[26]: array([[ 1,  7, 13]])
# xrow A^T も期待通りの値となっている。
という具合に、全て「期待通り」となる……。

それではなぜ紛らわしい(結果だけは正しい)「現象」が表われたのか…… これは勿論 "broadcasting" のせい(というか、お陰)。 k 行 l 列の行列 A(A.shape == (k, l))と B (B.shape == (m, n)) の掛け算が意味を持つためには、 l = m でないといけない。 ここまでは素直に理解できる。ここで、もし B が行列でなく、 一次元の array の場合(つまり B.shape == (m,) の場合) (m,) => (m, 1) とする事によって掛け算を実行、 その結果の (k, 1) から、1 を抜いて、k 要素の一次元 array に戻す、という事をやっていた訣だ。

以上を明示的に書くと、

In [30]: A @ x
Out[30]: array([ 1,  7, 13])

In [31]: (A @ x.reshape(3, 1)).reshape(3)
Out[31]: array([ 1,  7, 13])

In [32]: x @ A.T
Out[32]: array([ 1,  7, 13])
    
In [33]: (x.reshape(1, 3) @ A.T).reshape(3)
Out[33]: array([ 1,  7, 13]) 
となる。[31] では、reshape(3, 1) となっているのに、 [33] で、reshape(1, 3) となっているところが巧妙なところ。 (言語仕様では「外側に次元を追加する」となっている。)

分ってしまえば、So what? (そう、それで?)と言われそうな話だが、 これ(broadcast) で謎解きできるミステリーは幾つかある。 たとえば vector 同士の掛け算……

In [55]: xrow.T @ xrow
Out[55]: 
array([[1, 1, 0],
       [1, 1, 0],
       [0, 0, 0]])
    
In [56]: xrow @ xrow.T
Out[56]: array([[2]])

In [57]: x @ x
Out[57]: 2 
最後の例では、(3,) × (3,) ⇒ (1, 3) × (3, 1) ⇒ (1, 1) (直前の結果と同じく行列)とした後、 (両方の「次元」を剥ぎ取って)無次元(すなわち、スカラー)としている。 結果として、より忠実な「内積」を実現している訣だ。

2015-10-16 (Fri): iPython 再訪

ちょっと前に、Wes McKinney, "Python for Data Analysis" を買って積読になっていたが、ひょんなキッカケで、その中の iPython と NumPy にかかわる章を読んだ。特に新しい事は書かれていないが、 要領良く纏まっていて、要点を思い出す(自分の勘違いを訂正する) のにとっても役に立った。

使わなかった理由……

iPython に限らず、IDLE や、Emacs 上の Python-mode (の py-shell) などを試して、また実際に何度か使い始めてみたものの、その都度「立ち消え」 になって、今では "coloring のみの Emacs/python-mode"と、 Terminal.app 上の純正 interactive shell だけ、に戻っている。 (退化と言えば退化だが、Django の DEBUG_MODE が素晴らしいので、 これで十分だった……)

しかし、思い返せば、「iPython……なんだかなぁ」という感想は、 かなり不運な(実は頓馬な)勘違いから来ているようにも思える。

  1. IronPython と取り違えていた: 大昔の事だが、折角 S田氏が「(Enthought の) iPython いいですよぉ」って勧めてくれたのに、この勘違いのせいで無視してしまった。 いくら IronPython にがっかりした直後だったとは言え、何とも頓馬な……
  2. Matplotlib/NumPy との強すぎる「連携」が嫌: iPython のウリの一つに、
    (pve35) fukuda@falcon:~/pve35% ipython --pylab 
    として立ち上げておけば、NumPy や matplotlib が自動で import され、いきなり、array([[1, 1], [2, 2]]) とか、はたまた、 plot() などとやれてしまう、という事がある。 また、それに加えて、np.array(), np.plot() も OK :-) 一見便利そうだが、NumPy も matplotlib.pyplot も非常に多量の method や attribute を含むから、 「ワケワカ」の元になりがち。
  3. Notebook が OSX では使えない: ipython notebook は、当初から互換性が今一だった——必要とする library の多さからして無理もない? 現在では相当改善されているが、 OSX では、matplotlib のグラフが inline で表示されない。 (これでは、態々 notebook を使う意味がない……) pyvenv-3.5 で新たに環境を作って確認しようとしたら、そもそも notebook を入れたら、pip-compile でエラーになる。
  4. prompt がダサい: オリジナルのシェルに比べて、何とも洗練されてない。 in と out の区別は本当に必要だろうか?私は個人的には、CPython のオリジナルのプロンプト ('>>>' (input), '' (output)) で十分だと思っている。 あと、無用な空行が入るのも「何だかなぁ」である。

再挑戦の結果

先になって「また止めてしまう」という可能性も勿論有るが、今のところは、 使い続けたい、と思っている。 それには、
  1. インストールは pyvenv 内で、pip-compile, pip-sync で行う。 (requirements.in に ipython の一行を入れる。)
  2. 当面 python-3.5 (pyvenv-3.5) で notebook は使わない
  3. ipython --pylab も使わない。(どうやら、--pylab は「推奨」から外されたようだ。)
という条件を付け加えれば良さそう。

こうすれば、大体上の教科書に書いてある「メリット」全般を享受できる。 とりわけ次のような事に感銘を受けた。

等に、えらく感銘を受けた。

これらの(言わば「今更」の)メリットに加えて、 私の特殊事情の下での「なかなかやるね」は

という事で、iPython、もうしばらく使ってみる事にする。
177/1,059,836 Valid CSS! Valid HTML 5.0
Taka Fukuda
Last modified: 2016-01-19 (Tue) 10:42:18 JST