Quantcast
Viewing all articles
Browse latest Browse all 522

【PHP8】Jupyter と PHP8.0 カーネルのノートブックを Mac やラズパイや Windows の Docker 上で動かす

PHP8.0 でも Jupyter の Notebook を使いたい。macOS や RaspberryPi や Windows で。

でも、いろいろインストールしてローカルを汚したくない。

そんな遊び心満載なユーザー向けなものが Dockerどこかにないものか。

TL; DR

Jupyter-PHP の PHP8 用修正パッチと macOS/Windows10/Raspbian で動く JupyterLab の Dockerfile を作ってみました。

作り方や経緯、「Jupyter?何それ。typoってない?」と言う方は TS; DR をご覧下さい。

PHP8 カーネル入り Dockerfile の URL

使い方

ダウンロードとビルド
$# Dockerfile 一式のクローン(ダウンロードでも可)$git clone https://github.com/KEINOS/Jupyter-PHP8.git
...
$# Docker イメージのビルド$cd Jupyter-PHP8
$docker build -t jupyter:local ....(いささか時間がかかります)...
コンテナ(Jupyterサーバ)の起動
$# Docker イメージからコンテナ(Jupyter サーバ)の起動。$# ローカルのポート 8001 をコンテナの 8000 ポート(Jupyter サーバのポート)につなげて起動する$docker run --rm-p 8001:8000 -v$(pwd)/data:/workspace jupyter:local
...(途中でアクセストークンが表示されるのでコピーしておく)
[I 12:43:22.853 LabApp] Jupyter Notebook 6.1.5 is running at:
[I 12:43:22.854 LabApp] http://44a5fb18069a:8000/?token=375a1f68224543bde87ffd8d5ce97b89ee707e3591ddcf90
[I 12:43:22.854 LabApp]  or http://127.0.0.1:8000/?token=375a1f68224543bde87ffd8d5ce97b89ee707e3591ddcf90
[I 12:43:22.855 LabApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
...(アクセス・ログが表示されていく。サーバを終了するには Ctrl+C)
  • 上記で Jupyter サーバのコンテナが起動したら、ブラウザからアクセスします。
    • ポートは 8001 です。8001 で都合が悪い場合は、上記の docker runで 8001 を他のポート番号にしてください。
    • ローカル(同一マシン)からアクセスする場合:
      • http://localhost:8001/?token=<アクセストークン>
    • 同じネットワークの別のマシンからアクセスする場合
      • http://<ホストのIPアドレス>:8001/?token=<アクセストークン>

イメージ・インフォ

イメージの諸情報

  • Intel/AMD および ARM v7l の CPU で動作確認しています。
    • macOS Catalina (OSX 10.15.7, MacBook Pro Early 2015, Docker version 19.03.13)
    • Raspbian Stretch (Debian 9, Raspberry Pi3+, Docker version 19.03.13)
    • Windows 10 Pro 64bit (20H2 ビルド 19042.630, Intel Pentium Silver N5000, Docker v20.10.0-rc1)
  • Docker のベースイメージ: keinos/php8-jit:latest Alpine ベース
  • PHP のバージョン: PHP8.0.0(2020/11/28 時点の master)+ JIT有効
  • デフォルトで JupyterLab が起動しますが、以下のサービスもインストールされています。
    • jupyter core
    • jupyter-notebook
    • qtconsole
    • ipython
    • ipykernel
    • jupyter client
    • jupyter lab
    • nbconvert
    • pywidgets
    • nbformat
    • traitlets

TS; DR

Jupyter や Jupyter Notebook をご存知でしょうか。私は知りませんでした。

いや、名前や存在自体は知っていたのです。ペチペチ PHPer の私は「Python かぁ... Python かぁ...」とか言いながら、完全に理解すらしていない機械学習の記事や本を読んでいると、Jupyter やらノートブックと言う単語にチョイチョイ出くわしていたのです。それが何であるかの説明もなく、当然のごとく話しが進むのです。

しかし、どうもテキスト・エディタや IDE のように見えます。なんてっちゃって IDLEみたいなものかな?、と。Win の頃は秀丸エディタ、サクラ・エディタ、Mac に移ってからは Coda2、Atom、XCode を経て、やっと VSCodeに落ち着いた自分としては新たなエディタとして使うには汎用性に欠けると思い込んでおり、避けていました。

すると、先日(2020/11/18)とあるニュースが目に飛び込んできました。

『Microsoft、「Visual Studio Code」の新しい拡張機能「Jupyter」を発表』
「Python」言語拡張と切り離して、それ以外のプログラミング言語でも利用可能に

『Microsoft、「Visual Studio Code」の新しい拡張機能「Jupyter」を発表』@ インプレス Watch より)

「ん? VSCode で Jupyter?どう言うこと?」となりました。「VSCode で秀丸エディタが使える!」と言うような意味不明なニュースに見えたからです。

そこで、偏見や食わず嫌いなのかもしれない、と思い。改めて調べてみました。

Jupyter/Jupyter Notebook とは

プログラムのソースコードに、注釈や補助情報としてコメントを入れることがあると思います。

恐れずに言うなら、Jupyter は「その逆」です。つまり、Jupyter は Markdown のドキュメントにプログラムをコメントとして埋め込んでステップ実行できるものと言えそうです。

そのファイルが「ノートブック」(正しくは「Jupyter ノートブック」)と呼ばれ、JSON 形式の「Jupyter ノートブック・フォーマット」で書かれたものです。拡張子は .ipynbiPython NoteBookの略です。

例えば、以下のような「なんちゃって論文」を Markdown ドキュメントで書きたいとします。

# All about Hello, world

```python3
print("HelloWorld")
```

しかし、コードブロック内の print()をユーザー(読者)が実行して試せるようにしたいので、Jupyter ノートブック形式で作りたいとします。

最終的に以下のような JSON 形式になるのですが、ざっと構成だけみて下さい。「へぇ」と思う反面、手打ちで作成するには結構面倒だと思います。

sample.ipynb
{"cells":[{"cell_type":"markdown","metadata":{},"source":["# All about Hello, world"]},{"cell_type":"code","metadata":{},"source":["print(\"HelloWorld\")"]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.8"}},"nbformat":4,"nbformat_minor":4}

上記をみると、Markdown 行のブロックと、コード行のブロックで分かれており、Jupyter のフォーマットでは、この各ブロックを「セル」(cell)と呼んでいます。そして最後に実行環境のメタデータが埋め込まれているのがわかると思います。シンプルですが、柔軟性があるフォーマットだと思います。

つまり、エディタを使ってドキュメントを書く時は以下を繰り返して、積み上げるイメージで作成して行きます。

  1. セルを挿入する
  2. 各々のセルに「markdown」や「code」といった属性を付ける
  3. セルに内容(テキスト)を記入

そして、これらのファイルのエディタやビューアとなるのが Jupyter NotebookJupyterLabと呼ばれる Python で書かれた Web アプリです。

Jupyter Notebookは従来からあるシンプルなエディタ&ビューアーですが、ファイルとしてのJupyter Notebookなのか、アプリとしての Jupyter Notebookなのかがわかりづらいことから、「クラシック」(Classic Jupyter Notebook)とも呼ばれます。

JupyterLabClassic Jupyter Notebookの後続の上位バージョンです。より多機能になっており、IDE に近いアプリになっています。

公式のオンラインのデモが触れるので試してみて下さい。

どちらも画面上部に「▶︎」の再生ボタンが表示されるので、押していって動きをみてください。ステップ実行的に実行されていくのがわかると思います。本記事は、これに PHP8 を動かすための記事ということになります。

Jupyter Notebook で書くと何が良いのか

何回目か分からない人工知能(AI)ブームの昨今、毎日のように機械学習の新しい論文やアイデアが発表されているそうです。そして、どの分野の論文もそうですが査読、つまり「内容の確認と検証」などを経て認知されます。

しかし、論文と同じ環境を用意したり、論文中に書かれたコードが本当に同じ結果を出すのか、写経のごとく別途コードを入力して検証するのも大変です。

Jupyter のノートブック形式であれば、「▶︎」の再生ボタンを押して行けば Markdown 中に埋め込まれた順にコードが実行されていきます。コードエディタや IDE で言うステップ実行と同じ感覚です。

つまり、論文と共に検証実証もセットで配布できる仕組みが研究者に好まれているのだと思います。

さすが Python パイセン。後輩の PHP が、止まることより動き続けることに重きを置くフラジャイル開発に始まり(嘘)、型々言うな、小さな気合と根性の積み重ねが大事なんだ、とスタミナ勝負のアジャガイル開発を続け(嘘)、問題を後任へ後任へとパスしながら前へ進むスクラム開発を経て(違)PHP が一世を風靡したのは今や昔(本当)。

体育会系の PHP と違い、教育を念頭に力を入れてきた理系の Python が、ここに来て真価が現れてきたのだと思います。

これは PHPer の私も見習わないと!、と思い Jupyter で PHP は使えないのか調べ始めました。

Jupyter のカーネルとは

木星の Jupiterでなく Jupyterなのは、元々は「Julia、Pythonと R 言語向けだったから」だそうで、それらを組み合わせて Ju-Pyt-Erつまり Jupyterなんだそうです。お洒落ですね。PHP が Programmers Hate PHP なのとは大違い(違)。

その後、「この仕組みが勉強やアイデアの共有には便利」と言うことで他の言語でも使えるようになりました。その際の仲介役、つまり Jupyter から受け取ったスクリプトを該当するプログラム言語で実行するプログラムを「カーネル」と呼ぶようになります。

つまり、PHP を Jupyter で動かすためには PHP カーネルが必要になります。

しかし、残念なことに 2020/11/19 現在、PHP カーネルは IPHPJupyter-PHPの2つしか存在していません。

しかも IPHPは開発は中止、Jupyter-PHPを見ろと言いつつも、肝心の Jupyter-PHPは事実上放置されています。

🐒 【カーネルとは】

OS では Linux や Darwin のカーネルがあったり、機械学習ではカーネル関数があったりと、「カーネル」と言う言葉をあちこちで聞きます。ケンタの白髭のおっさんのカーネル(Colonel)ではなく、Kernelです。

Kernelとは「何かの中核や本質をなすもので、何かに覆われており見つけにくいもの」を言います。語源は中期オランダ語の「cornel」(粗食)から中高ドイツ語の「kornel」(穀物)を経て、cornの語源でもある古英語の「cyrnel」(種や、木を見た時の根っこなど。corn+-nelと同義の cyrnel)から来ています。日本語で「種」「核」「(牡蠣などの)身」的なニュアンスです。

例えば、OS の場合は、シェルなどを通してしかユーザーがアクセスできない OS の中核をなすプログラム群を「カーネル」と言います。機械学習では、一見すると A と B に直線で分けづらい(線引きしづらい) X,Y の2次元データに、もう 1 データ加えて 3 次元にすると面で線引きできる(現れてくる)データを「カーネル」と言います。
Image may be NSFW.
Clik here to view.

ディープラーニングで動画の顔画像の入れ替え実施編&そこから学ぶ機械学習の基礎の基礎 @ Qiita より)

Jupyter-PHP を PHP8 で動かす

公式の Docker の PHP イメージ(php:8.0-rc-alpine)は RaspberryPi Zero で動かなかったり、JIT がデフォルトで有効になっていなかったり、リージョンが日本になっていなかったり、mbstringが有効になっていなかったり、と、すぐに使うには色々と面倒臭いので設定済みのイメージを使います。

これをベースに Dockerfile を構築することにしました。最終的な成果物はこちらです。

さて、問題の PHP カーネルですがゼロから作るよりは既存のカーネルを修正して動くなら、それを使うのがベストと言うもの。IPHPJupyter-PHPで違いを調べてみました。

IPHPIPythonが使えれば composer だけで使えていたらしいのですが不具合や限界も多く、Jupyter-PHPは、さらに PHP エクステンションの zmqを必要としています。

しかし、理論上 Jupyterフォーマットが変わらない限り、受け取った PHP コードをパースして PHP ランタイムに渡せば良いだけです。そこで、なんとか Jupyter-PHPを使えないものか奮闘してみました。

Jupyter-PHP のソースコードを見てみると composer.jsonがあるので、composer requireでインストールできるのかと思いきや、エラーでできません。

composer.jsonを覗くと "require":{"php":">=7.0"}と PHP のバージョンを制限していました。そこで --ignore-platform-reqsオプションを付けてみたところ、今度はエラーは出ないものの、やはり動きません。Jupyter にカーネルが現れないのです。どうやら、肝心のカーネルがしかるべきパスに設置されないようです。

つまり、Jupyter-PHPのカーネルをスコープ(許された範囲)外に設置する必要があります。しかし、composerだけでは、どこのパスに Jupyter のカーネル設置先のディレクトリがあるか分からず、検索するにしても OS にも依存するため composer.json単体では完結できないのです。

そこで、次に README にあるようにインストーラーを実行してみましたが、やはりエラーでインストールできませんでした。

インストーラーの中身を確認してみると、カーネルのインストール先ディレクトリを検索して見つけたのち、内部で composerコマンドを呼び出して composer installしていました。

この時に --ignore-platform-reqsオプションが指定されていないため「PHP8 ではバージョンが合わない」とエラーが出ていたのです。

そのため、Jupyter-PHP を PHP8 で動かすためには、主に2カ所の修正が必要です。

  • Jupyter-PHP-Installer の composer.jsonにある PHP バージョン指定の削除
  • Jupyter-PHP-Installer の srcにあるインストール・スクリプト内に --ignore-platform-reqsを加え、依存パッケージの PHP バージョンを無視させる

そして、これらの修正を行うパッチを作成しました。Jupyter-PHP-Installer のリポジトリをクローンかダウンロードして適用します。

また、JupyterLab に表示されるカーネルの選択パネル(アイコン?)の表記が「PHP」だったので、わかりやすいように「PHP8」に変更するパッチも作成しました。

Jupyter-PHP-Installer でインストール後、PHP のカーネル kernel.jsonを検索して、パッチをあてて下さい。

PHP に関する注意点としては、composerのバージョンと、Jupyter-PHP が依存する PECL パッケージの zmqの PHP エクステンション(PHP 拡張機能)です。

  1. composerのバージョンを v1系にすること。最新の v2 系だと、追随していない Composer パッケージが多いため、エラーがでまくります。
  2. zmqはソースから PECL パッケージをインストールすること。pecl install zmqで入るコンパイル済みパッケージは古すぎるため動きません。また、zmqが依存する zipの PHP エクステンションも一緒に入れておく必要があります。

あとは、JupyterLab や Classic Jupyter Notebook を動かすのに必要な Python3 と、インストールに使う pipを入れます。

  1. Python3 を Alpine Docker に入れる
  2. pipを Alpine に入れる
  3. Jupyter 一式をインストールする

ここまで来て、やっと出来上がったのですが、1つだけ後悔していることがあります。いつものクセで Docker のベースイメージを Alpine にしたことです。

Docker の Alpine Linux ベースのイメージは軽量であるため、複数のコンテナを起動して利用するのには向いています。つまり、1コンテナに1つの機能(サービス)を動かして使う場合です。しかし、軽量である反面、Alpine はクセが強いため、1つのコンテナにアレコレとインストールするのには向いていません。

Alpine にも apkと言うパッケージマネージャーがあるのですが、お世辞にも Ubuntu や Debian などの aptほど素直にインストールできません。そのため、PHP やら Python やら node.js を入れてアレコレとアプリをインストールするなら、サイズは巨大になるものの Ubuntu や Debian ベースの Docker イメージの方が慣れていない人には楽だったのかもしれないと感じました。

参考文献


Viewing all articles
Browse latest Browse all 522

Trending Articles