DockerでJupyterLab環境を構築した。
この環境にCodonを追加したいため、Codon+JupyterLab環境を構築する方法を教えて欲しい。
こんなお悩みを解決します。
以前、Docker上に「Codon」の環境を構築する方法について解説しました。
【解説】DockerによるPythonコンパイラ「Codon」の環境構築方法
続きを見る
今回は、CodonをJupyterLab上で利用するための方法について解説します。
公式サイトに紹介されている手順を参考に解説していますので、具体的な手順を知りたい方は、ぜひ最後までご覧ください。
動作構築
Codon+JupyterLabの動作環境は、以下のようになります。
項目 | 内容 |
---|---|
OS | Linux |
ディストリビューション | Ubuntu 22.04.1 LTS |
32bit/64bit | 64bit(x86_64) |
詳細は、以下の記事を参照してください。
【解説】Linuxサーバの構築方法を分かりやすく解説!
続きを見る
ベースとするJupyterLab環境
JupyterLabの環境構築は、以前の記事をベースとします。
【Python】機械学習を用いた競馬予想【環境構築編】
続きを見る
環境構築
以降に、具体的な環境構築方法について解説します。
ディレクトリ構成
今回の環境構築に関連するファイル一式を以下に示します。
./
|-- Dockerfile
|-- docker-compose.yml
|-- config/
| |-- tracker.jupyterlab-settings
| `-- plugin.jupyterlab-settings
`-- workspace/
`-- ...
また、Dockerfileの中で登場する「entrypoint.sh」は、上記のGitHubのプロジェクトや関連記事をご覧ください。
Dockerfile
今回のメイン部分となるDockerfileの内容について解説します。
まず、Dockerfileの全体像を以下に示します。
FROM python:3.9.7-buster
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Tokyo \
CMAKE_VERSION=3.26.3 \
LLVM_VERSION=15 \
CODON_INSTALL_PREFIX=/usr/local
# Install packages and setup timezone
RUN apt-get update \
&& apt-get install -y tzdata \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& useradd -m labuser \
&& mkdir -p /home/labuser/work \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& python3 -m pip install --upgrade pip
# Install python packages
RUN apt-get update \
&& apt-get install -y cargo \
\
# install jupyter packages
\
&& pip install --no-cache-dir \
black \
jupyterlab \
jupyterlab_code_formatter \
jupyterlab-git \
lckr-jupyterlab-variableinspector \
jupyterlab_widgets \
ipywidgets \
import-ipynb \
&& pip install --no-cache-dir \
\
# install basic packages
\
numpy \
pandas \
scipy \
scikit-learn \
pycaret \
xfeat \
featuretools \
matplotlib \
japanize_matplotlib \
mlxtend \
seaborn \
plotly \
requests \
beautifulsoup4 \
lxml \
html5lib \
Pillow \
opencv-python \
\
# install additional packages
\
pydeps \
graphviz \
pandas_profiling \
shap \
umap \
xgboost \
optuna \
hyperopt \
MonthDelta \
lightgbm \
pandarallel \
joblib \
\
# install pytorch (cpu version)
\
&& pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cpu \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install LLVM and Codon
RUN apt-get update \
&& apt-get install -y lsb-release wget software-properties-common gnupg git \
ninja-build libxml2-dev libsodium-dev uuid-dev libssl-dev \
\
# install cmake
\
&& cd /tmp \
&& curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-$(uname -m).sh -o cmake-install.sh \
&& chmod +x cmake-install.sh \
&& ./cmake-install.sh --prefix=/usr/local --exclude-subdir --skip-license \
&& cd / \
\
# install LLVM
\
&& cd /tmp \
&& wget https://apt.llvm.org/llvm.sh -O llvm.sh \
&& chmod +x llvm.sh \
&& ./llvm.sh ${LLVM_VERSION} all \
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_VERSION} 1 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_VERSION} 1 \
&& update-alternatives --install /usr/bin/llvm-config llvm-config++ /usr/bin/llvm-config-${LLVM_VERSION} 1 \
\
# install Codon
\
&& git clone https://github.com/exaloop/codon.git \
&& cd codon \
&& cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_DIR=$(llvm-config --cmakedir) \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
&& cmake --build build \
&& cmake --install build --prefix=${CODON_INSTALL_PREFIX} \
\
# install jupyter plugin
\
&& cmake -S jupyter -B jupyter/build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DLLVM_DIR=$(llvm-config --cmakedir) \
-DCODON_PATH=${CODON_INSTALL_PREFIX} \
-DOPENSSL_ROOT_DIR=$(openssl version -d | cut -d' ' -f2 | tr -d '"') \
-DOPENSSL_CRYPTO_LIBRARY=$(ldconfig -p | grep "libssl.so$" | cut -d' ' -f4) \
-DXEUS_USE_DYNAMIC_UUID=ON \
&& cmake --build jupyter/build \
&& cmake --install jupyter/build \
\
# setup jupyter kernel
\
&& target_dir=$(jupyter kernelspec list | grep python | tr -s "[:space:]" | cut -d' ' -f3 | grep -oP "(.*)(?=/python)")/codon \
&& mkdir -p ${target_dir} \
&& echo '{"display_name":"Codon","argv":["'${CODON_INSTALL_PREFIX}'/bin/codon","jupyter","{connection_file}"],"language":"python"}' > ${target_dir}/kernel.json \
&& cd / \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/*
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh \
&& mkdir -p /home/labuser/.jupyter/lab/user-settings/@jupyterlab/notebook-extension \
&& mkdir -p /home/labuser/.jupyter/lab/user-settings/@jupyterlab/fileeditor-extension \
&& chown labuser:labuser -R /home/labuser/.jupyter/lab
ENTRYPOINT ["/entrypoint.sh"]
CMD ["jupyter-lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--NotebookApp.token=''", "--notebook-dir=/home/labuser/work"]
上記のうち、変化点は、以下の4点となります。
cmake
のインストールLLVM
のインストール- Codonのインストール
- CodonのJupyter Pluginのインストール
以降では、上記のそれぞれに対し、具体的な実施内容について解説します。
また、Codonをソースコードからインストールする都合上、cmake
とLLVM
のバージョンは、以下を満たす必要があります。
対象 | バージョンに関する制約 |
---|---|
cmake | version 3.14以上 |
LLVM | version 15以上 |
これらのバージョンを変更したい場合、Dockerfileの以下の箇所を変更してください。
CMAKE_VERSION=3.26.3 # CMAKEのバージョン
LLVM_VERSION=15 # LLVMのバージョン
cmakeのインストール
cmake
は、バイナリ版のインストーラーを利用します。
インストール時のオプションは、以下のようになります。
オプション | 説明 | 設定値 |
---|---|---|
--prefix | インストール先 | /usr/local |
--exclude-subdir | サブディレクトリの構成 | なし(インストール先の直下にbinやlibを配置する) |
--skip-license | ライセンスに関する承諾事項の省略 | なし |
Docker上で環境構築を行うため基本的に変更は不要ですが、設定を変更したい場合、以下の箇所を修正してください。
./cmake-install.sh --prefix=/usr/local --exclude-subdir --skip-license
LLVMのインストール
LLVMもバイナリ版のインストーラーを利用します。
Codonの公式サイトでは、LLVMもソースコードからインストールする方法が紹介されていますが、非常に時間がかかるため、バイナリ版を用いて所要時間を短縮します。
LLVMには、オートインストーラーが付属しているため、今回は、コチラを利用しましょう。
また、同時にインストールされるコンパイラ(clangやclang++など)は、LLVMのバージョン番号が付与されますが、扱いやすさを考慮し、名称を更新しておきます。
Dockerfile上では、以下がインストール処理に該当する部分となります。
cd /tmp \
&& wget https://apt.llvm.org/llvm.sh -O llvm.sh \
&& chmod +x llvm.sh \
\
# LLVMのインストール
\
&& ./llvm.sh ${LLVM_VERSION} all \
\
# clang、clang++、llvm-configの名称更新
\
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_VERSION} 1 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_VERSION} 1 \
&& update-alternatives --install /usr/bin/llvm-config llvm-config++ /usr/bin/llvm-config-${LLVM_VERSION} 1
Codonのインストール
続いて、Codonのインストールを行います。
Codonは、ソースコードからインストールする必要があるため、先程インストールしたcmake
コマンドを用いて作業を進めます。
インストール時のオプションは、公式サイトをベースにしていますが、prefixだけは以下のようにカスタマイズしています。
オプションの変更箇所 | 変更内容 |
---|---|
--prefix | ・変更前:install ・変更後:/usr/local |
上記の設定の場合、以下のような階層構造でCodonがインストールされます。
/usr/local/
|-- bin/
| |-- codon/
| | `-- ...
| `-- ...
|-- include/
| |-- codon/
| | `-- ...
| `-- ...
`-- lib/
|-- codon/
| `-- ...
`-- ..
また、インストール先を変更したい場合、Dockerfile
の以下の部分を修正してください。
CODON_INSTALL_PREFIX=/usr/local # ここを必要に応じて修正する
CodonのJupyterLab Pluginのインストール
最後に、CodonのJupyterLab Pluginをインストールします。
コード上の該当部分を以下に示します。
cmake -S jupyter -B jupyter/build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DLLVM_DIR=$(llvm-config --cmakedir) \
-DCODON_PATH=${CODON_INSTALL_PREFIX} \
-DOPENSSL_ROOT_DIR=$(openssl version -d | cut -d' ' -f2 | tr -d '"') \
-DOPENSSL_CRYPTO_LIBRARY=$(ldconfig -p | grep "libssl.so$" | cut -d' ' -f4) \
-DXEUS_USE_DYNAMIC_UUID=ON
後は、ホストマシン上で以下のコマンドを実行し、docker imageが作成されるまで、気長に待ちます。
# docker-compose.ymlがあるディレクトリで以下を実行
docker-compose build --no-cache
entrypoint.sh
また、Dockerfile
中にあるentrypoint.sh
のスクリプトの内容は、以下のようになります。
#!/bin/bash
export USER=labuser
export HOME=/home/${USER}
# Check PUID and PGID of environment variable
if [ -n "${PUID}" ] && [ -n "${PGID}" ]; then
# Check value of PUID
if [ ${PUID} -ne 0 ]; then
uid=$(id -u ${USER})
gid=$(id -g ${USER})
# In the case of gid is not equal to PGID
if [ ${gid} -ne ${PGID} ]; then
getent group ${PGID} > /dev/null 2>&1 || groupmod -g ${PGID} ${USER}
chgrp -R ${PGID} ${HOME}
fi
# In the case of uid is not equal to PUID
if [ ${uid} -ne ${PUID} ]; then
usermod -u ${PUID} ${USER}
fi
fi
fi
# Run the command as an USER
exec setpriv --reuid=${USER} --regid=${USER} --init-groups "$@"
docker-compose.yml
docker-composeコマンドを用いて、コンテナの管理を行う方が多いと思います。
今回の変更にあわせてアップデートしている部分を以下に示します。
version: '3.7'
x-logging:
&default-json-logging
driver: json-file
options:
max-file: "3"
max-size: "10m"
services:
jupyterlab:
build:
context: .
dockerfile: Dockerfile
image: jupyterlab
# Docker環境に共有するメモリサイズを指定
shm_size: '3gb'
restart: always
container_name: jupyterlab
environment:
- PUID=1000
- PGID=1000
- CODON_PYTHON=/usr/local/lib/libpython3.9.so
volumes:
- ./workspace:/home/labuser/work
# 以下2行を追加
- ./config/tracker.jupyterlab-settings:/home/labuser/.jupyter/lab/user-settings/@jupyterlab/notebook-extension/tracker.jupyterlab-settings
- ./config/plugin.jupyterlab-settings:/home/labuser/.jupyter/lab/user-settings/@jupyterlab/fileeditor-extension/plugin.jupyterlab-settings
ports:
- 18580:8888
logging: *default-json-logging
JupyterLabで利用するsettingファイルの内容
参考までに、私が利用しているsettingファイルを記載しておきます。
具体的には、文字サイズや1行の文字数をカスタマイズしています。
tracker.jupyterlab-settings
{
"codeCellConfig": {
"autoClosingBrackets": false,
"cursorBlinkRate": 530,
"fontFamily": null,
"fontSize": 20,
"lineHeight": null,
"lineNumbers": true,
"lineWrap": "off",
"matchBrackets": true,
"readOnly": false,
"insertSpaces": true,
"tabSize": 4,
"wordWrapColumn": 80,
"rulers": [],
"codeFolding": false,
"lineWiseCopyCut": true,
"showTrailingSpace": false
},
"defaultCell": "code",
"kernelShutdown": false,
"markdownCellConfig": {
"autoClosingBrackets": false,
"cursorBlinkRate": 530,
"fontFamily": null,
"fontSize": 20,
"lineHeight": null,
"lineNumbers": true,
"lineWrap": "on",
"matchBrackets": false,
"readOnly": false,
"insertSpaces": true,
"tabSize": 4,
"wordWrapColumn": 80,
"rulers": [],
"codeFolding": false,
"lineWiseCopyCut": true,
"showTrailingSpace": false
},
"rawCellConfig": {
"autoClosingBrackets": false,
"cursorBlinkRate": 530,
"fontFamily": null,
"fontSize": 20,
"lineHeight": null,
"lineNumbers": true,
"lineWrap": "on",
"matchBrackets": false,
"readOnly": false,
"insertSpaces": true,
"tabSize": 4,
"wordWrapColumn": 80,
"rulers": [],
"codeFolding": false,
"lineWiseCopyCut": true,
"showTrailingSpace": false
},
"scrollPastEnd": true,
"recordTiming": false,
"numberCellsToRenderDirectly": 99999,
"remainingTimeBeforeRescheduling": 50,
"renderCellOnIdle": true,
"observedTopMargin": "1000px",
"observedBottomMargin": "1000px",
"maxNumberOutputs": 50,
"showEditorForReadOnlyMarkdown": true,
"kernelStatus": {
"showOnStatusBar": false,
"showProgress": true
},
"experimentalDisableDocumentWideUndoRedo": false,
"renderingLayout": "default",
"sideBySideLeftMarginOverride": "10px",
"sideBySideRightMarginOverride": "10px",
"sideBySideOutputRatio": 1
}
plugin.jupyterlab-settings
{
"editorConfig": {
"autoClosingBrackets": false,
"codeFolding": false,
"cursorBlinkRate": 530,
"fontFamily": null,
"fontSize": 20,
"insertSpaces": true,
"lineHeight": null,
"lineNumbers": true,
"lineWrap": "on",
"matchBrackets": true,
"readOnly": false,
"rulers": [],
"showTrailingSpace": false,
"tabSize": 4,
"wordWrapColumn": 80
},
"toolbar": []
}
動作確認
実際にJupyterLabでCodonを利用してみたいと思います。
Codon版のノートブック作成
JupyterLabを起動すると、以下のような画面が表示されるので、Codonと表示されている部分を押下します。
今回の環境構築の方法では、以下のようなエラーが表示されますが、サンプルを動作させる上では無関係だったので、一旦無視します。
表示されたダイアログの「OK」を押下します。
ちなみに、dockerのlogには、以下のようなメッセージが表示されていました。
こちらの原因はもう少し調査が必要になりそうです。
jupyterlab | ERROR: received bad message: No such comm target registered: jupyter.widget.control
jupyterlab | Message content: null
jupyterlab | codon: /tmp/codon/jupyter/build/_deps/xtl-src/include/xtl/xbasic_fixed_string.hpp:109: void xtl::detail::fixed_small_string_storage_impl::set_size(std::size_t) [T = char[56]]: Assertion `sz < N && "setting a small size"' failed.
ライプニッツの公式による円周率の近似計算
前回同様、ライプニッツの公式による円周率の近似計算を行い、処理速度を計測したいと思います。
使用するコードは以下のようになります。
共通
def main():
max_iter = 10 * 1000 * 1000
start_time = time.time()
# estimate pi
hat_pi = Leibniz(max_iter)
elapsed_time = time.time() - start_time
err = math.fabs(hat_pi - math.pi) / math.pi
output = str(timedelta(seconds=elapsed_time))
printf(hat_pi, 'Estimated Pi: {:.15f}')
printf(err, 'Relative Err: {:.15e}')
print(output)
Codon版
import time
from datetime import timedelta
from python import math
@python
def printf(data, _format):
print(_format.format(data))
def Leibniz(max_iter : int):
arr = reversed(range(max_iter))
_sum = 0.0
for k in arr:
_sum += ((-1) ** (k % 2) + 0.0) / (2 * k + 1)
pi = _sum * 4
return pi
python版
import time
from datetime import timedelta
import math
def printf(data, _format):
print(_format.format(data))
def Leibniz(max_iter : int):
arr = reversed(range(max_iter))
_sum = 0.0
for k in arr:
_sum += ((-1) ** (k % 2) + 0.0) / (2 * k + 1)
pi = _sum * 4
return pi
計算結果
実行結果は以下のようになりました。
実行モード | 処理時間 |
---|---|
python(デフォルト) | 6.238235 |
Codon | 0.138318 |
処理時間は、\(\frac{1}{45}\)倍位になっていますね。
ここから、Codonを利用することで、約45倍高速化で来ていることが分かります。
まとめ
今回は、Codon+JupyterLabの環境構築方法について解説しました。
手軽にPythonスクリプトを実行できるJupyterLabとPythonスクリプトを高速化できるCodonを利用することで、時間がかかるスクリプトを短時間で実行することが可能となります。
処理時間がボトルネックになっている方は、一度試してみてください。
効率良く技術習得したい方へ
今回の話の中で、プログラミングについてよく分からなかった方もいると思います。
このような場合、エラーが発生した際に対応できなくなってしまうため、経験者からフォローしてもらえる環境下で勉強することをおすすめします。
詳細は、以下の記事をご覧ください。
【比較】プログラミングスクールおすすめランキング6選【初心者向け】
続きを見る