機械学習なdockerfileを書くときに気をつけとくと良いこと
みなさん機械学習系の環境構築はどうやってますか? 僕は最近は Docker を使った管理を行っています。
特に師匠も居なかったので、ぐぐったり人のイメージを見たり手探りで docker をつかいつかいしている中で、最初からやっとけばよかったなーということがいくつかあるのでメモとして残しておきます。
大きく2つです。
- キャッシュは消す
- テストを書く
キャッシュは消す
ライブラリをいろいろと install すると大抵の場合ダウンロードしたファイルを保存されている場合が多いです。何かのタイミングで再びそのライブラリをインストールする際にはダウンロードしたファイルを使って、素早くインストールすることができます (この仕組みがキャッシュです)。
キャッシュがあると容量が重くなるという欠点があります。重たいイメージは pull に単に時間がかかりますから、システムとしてデプロイする時にトラフィックが多く発生してしまう + 更新に時間がかかるといったデメリットがあります。
ですのでライブラリを入れたあとにはキャッシュを削除すると吉です。
機械学習系だと基本的に ubuntu を base image として使うことが多いと思いますので、以下ではすべて ubuntu を使用していることを想定しています。 また、機械学習に必要なライブラリを入れるために使うコマンドは基本的に
- apt-get (or apt 絶対使う)
- pip (基本使う. conda only なら別.)
- conda (場合によって使う)
3つだと思いますので、それぞれ述べていきます。
apt-get の場合
ubuntu では apt-get
(もしくは単に apt
) でライブラリを入れますが、コマンドとしてキャッシュを消す機能がついていますので、それを使うのが便利です。
RUN apt-get autoremove -y &&\ apt-get clean &&\ rm -rf /usr/local/src/*
順番に
autoremove
で必要がないライブラリを削除してclean
でキャッシュされているすべてのライブラリを消して- インストールに使ったソースコードがある場合消す
ということをやっています。
pip の場合
pip には残念ながらキャッシュディレクトリを消すコマンドが用意されていません。一番簡単なのは pip install する時には cache を作らないようにする方法です。pip>=6 であれば no-cache-dir option をつけるとキャッシュせずに install が行われます。
pip install --no-cache-dir scipy
pip を最新版にしてからインストールすることが多いと思いますのでワンライナーで以下のように記述するといい感じです。
RUN pip install -U pip &&\ pip install --no-cache-dir scipy
インストールするライブラリはたくさんでてきくるので pip freeze で作れる requirements.txt
で管理する場合も多いと思います。その場合にも単に --no-cache-dir
をつけるだけです。
COPY /path/to/requirements.txt requirements.txt # あらかじめ requirements.txt を ADD or COPY などで # docker 内部に送っておくことが必要 RUN pip install -U pip &&\ pip install --no-cache-dir -r requrements.txt
もしくはインストールしたあとに cache directory を消すという方法もあります。Ubuntu なら場所は ~/.cache/pip
ですので全部ふっ飛ばせばよいです。
RUN pip install -r requirements.txt &&\ rm -rf ~/.cache/pip
Conda の場合
conda にはキャッシュ削除の clean
が用意されています。https://docs.conda.io/projects/conda/en/latest/commands/clean.html
いろいろインストールした最後に呼び出しましょう。
RUN conda install -y \ numpy \ scipy &&\ conda clean -i -t -y
その他
バイナリからインストールしたりする場合には使わないソースコードは消すようにしましょう。例えば noto font をインストールしたあとに元の .zip
を消すとかそういうことです。
テストを書く
スゴーク簡単でも良いので、いつも使うライブラリを import + 定型的な処理を行なうコードを .py
で書いておいて build 時にテストすると精神的に良いです。
例えばですが Mecab を使って文字列パースするような処理を書いていると、pip でインストールされていることはもちろんですが apt の方でもちゃんと mecab が入っていることが確認できます。
def test_mecab(): import MeCab tokenizer = MeCab.Tagger('-Ochasen') tokenizer.parse('おはようせかい')
具体的にはこれを build したあとのイメージに入れて pytest
を実行してあげます。
docker run -v ./test.py:/analysis/ my-image pytest ./
僕の場合はイメージの build は gitlab CI 上で行っているので CI で build したあとに registry に登録する前にテストを動かすような構成にしています。(gitlab ci の yaml 形式ですが script にあるコマンドが順番に実行されるということだけわかれば雰囲気はわかるかと思います。)
.build-docker: stage: build before_script: - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY script: - echo ${REGISTORY_URL} - export TAG=${REGISTORY_URL}/${IMAGE_NAME} - docker build -t ${TAG}:latest -f ${DOCKERFILE} ${CONTEXT} - docker tag ${TAG}:latest ${TAG}:${CI_COMMIT_SHA} - docker run -v ${PWD}:/analysis ${TAG}:latest pytest tests/${IMAGE_NAME} # < ここがそう - docker push ${TAG}:latest - docker push ${TAG}:${CI_COMMIT_SHA} variables: IMAGE_NAME: DOCKERFILE: # default values. if you use own values, override in variables scope. CONTEXT: . # ex.) registry.gitlab.com/atma_inc/analysis-template REGISTORY_URL: ${CI_REGISTRY}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}
こうしておくとテストに失敗すると登録自体が行われないので、登録されている == テストは通っているという安心感を得られます😃
例
自分がいつも使っているイメージ: https://gitlab.com/nyker510/analysis-template/-/blob/master/docker/cpu.Dockerfile
このリポジトリ analysis-template では dockerfile のコード管理 + master merge と tag を切った時に CI でビルド & registry に push ということをやっています。 内容に関しては昔詳しく書いたことがあるので、こちらも参考にしてみてください。
ドキュメントもあるよ!
こうしたほうがいい! 自分はこういうことをやっている とかあればコメント貰えると嬉しいですmm