ヴェズルフェルニルの研究ノート

座右の銘「ただ一人犀の角のように歩め」的な研究活動ノート

seq2point-nilmのDockerfileを作る

あるサイトのエンジニア募集ページに記載されていた情報から、NILM(Nonintrusive Load Monitoring:非侵入型負荷監視)という技術が存在することを知りました。

en.wikipedia.org電源ラインの一次配電盤に取り付けたセンサーによって電圧や電流の変化データを取得し、そのデータから電源ライン上の装置や機器の消費電力や稼働状況をモニタリングする技術らしいです。

こういう技術が在ることは聞いたことがありましたが、これには機械学習が使われていることは知りませんでした。NILMの一つの実装例としてseq2point-nilmというものが在ることも上述のページで知りました。

github.com

コロナ拡大の社会情勢下で電力消費が増大しており、エネルギー・エコ化も永続的な社会トレンドとなっている中で、さらにIoTにも繋がる技術としてNILMへの注目度が急上昇しているらしいです。

 

NILMがどういう技術で、その動作原理や使われているアルゴリズムがどんなものかを知りたくなったので、手始めに、このseq2point-nilmを動かしてみることにしました。ちなみに、seq2point-nilmの機械学習処理はTensorFlowとKerasを使って実現されているようです。

 

このような機械学習プログラムを手っ取り早く動かすにはDockerを利用するのが一番楽な方法です。そこで、Dockerfileを作りながらseq2point-nilmの動作確認を試みました。

以下が、私が自作したseq2point-nilmのDockerfileです。

FROM continuumio/anaconda3:2021.05

# install prerequistites
ENV DEBCONF_NOWARNINGS yes
RUN apt-get update && \
    apt-get install -y wget git unzip p7zip-full && \
    apt-get autoremove -y

# copy the seq2point-nilm
COPY ./seq2point-nilm /seq2point-nilm

# create a Python environment for seq2point-nilm installing python prerequistites
WORKDIR /seq2point-nilm
RUN conda env create -f environment.yml

ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES utility,compute

# download the REFIT dataset file
WORKDIR /seq2point-nilm/dataset_management/refit
RUN wget https://pureportal.strath.ac.uk/files/62090184/CLEAN_REFIT_081116.7z && \
    7z x -oCLEAN_REFIT_081116 CLEAN_REFIT_081116.7z && \
    rm -f CLEAN_REFIT_081116.7z

# download the UK-DALE datasets file
WORKDIR /seq2point-nilm/dataset_management/ukdale
RUN wget http://data.ukedc.rl.ac.uk/simplebrowse/edc/efficiency/residential/EnergyConsumption/Domestic/UK-DALE-2017/UK-DALE-FULL-disaggregated/ukdale.zip && \
    unzip ukdale.zip -d ukdale && \
    rm -f ukdale.zip

# download the REDD datasets file
WORKDIR /seq2point-nilm/dataset_management/redd
RUN wget --http-user="redd" --http-password="disaggregatetheenergy" http://redd.csail.mit.edu/data/low_freq.tar.bz2 && \
    tar -jxvf low_freq.tar.bz2 && \
    rm -f low_freq.tar.bz2

ARG AGGREGATE_MEAN=522
ARG AGGREGATE_STD=814

# create a REFIT dataset
WORKDIR /seq2point-nilm/dataset_management/refit
RUN mkdir dataset && \
    cd dataset && \
    python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'kettle' --save_path './' && \
    python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'microwave' --save_path './' && \
    python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'fridge' --save_path './' && \
    python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'dishwasher' --save_path './' && \
    python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'washingmachine' --save_path './'
    #python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'kettle' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './' && \
    #python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'microwave' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './' && \
    #python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'fridge' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './' && \
    #python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'dishwasher' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './' && \
    #python ../create_dataset.py --data_dir '../CLEAN_REFIT_081116/' --appliance_name 'washingmachine' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './'

# create a UK-DALE dataset
WORKDIR /seq2point-nilm/dataset_management/ukdale
RUN mkdir dataset && \
    mkdir dataset/kettle dataset/microwave dataset/fridge dataset/dishwasher dataset/washingmachine &&& \
    PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'kettle' --save_path './dataset/kettle/' && \
    PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'microwave' --save_path './dataset/microwave/' && \
    PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'fridge' --save_path './dataset/fridge/' && \
    PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'dishwasher' --save_path './dataset/dishwasher/' && \
    PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'washingmachine' --save_path './dataset/washingmachine/'
    #PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'kettle' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/kettle/' && \
    #PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'microwave' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/microwave/' && \
    #PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'fridge' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/fridge/' && \
    #PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'dishwasher' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/dishwasher/' && \
    #PYTHONPATH="${PYTHONPATH}:../" python create_trainset_ukdale.py --data_dir './ukdale/' --appliance_name 'washingmachine' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/washingmachine/'

# create a REDD dataset
WORKDIR /seq2point-nilm/dataset_management/redd
RUN mkdir dataset && \
    mkdir dataset/microwave dataset/fridge dataset/dishwasher dataset/washingmachine && \
    python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'microwave' --save_path './dataset/microwave/' && \
    python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'fridge' --save_path './dataset/fridge/' && \
    python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'dishwasher' --save_path './dataset/dishwasher/' && \
    python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'washingmachine' --save_path './dataset/washingmachine/'
    #python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'microwave' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/microwave/'
    #python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'fridge' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/fridge/'
    #python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'dishwasher' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/dishwasher/'
    #python create_trainset_redd.py --data_dir './low_freq/' --appliance_name 'washingmachine' --aggregate_mean AGGREGATE_MEAN --aggregate_std AGGREGATE_STD --save_path './dataset/washingmachine/'

WORKDIR /seq2point-nilm
ENTRYPOINT ["/bin/bash"]

Dockerfileは何度か作っていますが、これの難易度は中程度位でした。

このDockerfileは私のGitHubリポジトリに置いてあります。

github.comこのDockerfileの使い方は簡単で、まず以下の手順によって上のDockerfileが格納されたリポジトリ取得します。

$ git clone https://github.com/ketus-ix/seq2point-nilm_docker.git
$ cd seq2point-nilm_docker
$ git submodule update --init --recursive

その後、以下のコマンドを実行すると、seq2point-nilmのDockerイメージを作成できます。

$ cp docker/Dockerfile.conda .
$ docker build . -t seq2point-nilm_conda -f Dockerfile.conda

このDockerコンテナの中で、REFIT(514.3MB)、UK-DALE(3.59GB)、REDD(169.4MB)という3つのデータセットのダウンロードを行っています。そのため、Dockerイメージが生成されるのにかなり時間がかかります。これらのデータセットはseq2point-nilmによって利用されます。

 

このDockerfileによって作成したDockerコンテナは、すべてのデータセットがseq2point-nilmが使用する形式に変換・作成されるところまで確認してあります。

以下のディレクトリにseq2point-nilmが使用する形式の各データセットが格納されます。

 

 /seq2point-nilm/dataset_management/refit/dataset

 /seq2point-nilm/dataset_management/ukdale/dataset

 /seq2point-nilm/dataset_management/redd/dataset

 

seq2point-nilmの学習処理とテスト処理はこの作成済みのデータセットを用いて実行することができます。機械学習の肝であるこれらの処理も確認してみたかったのですが、残念ながらそれはできませんでした。いま手元にNVIDIA GPUボードを持っていないからです。

 

過去の仕事で利用していた機械学習環境が整ったPCを使っているので、GPUボードさえ有ればすぐに動作確認を行えるのですが、いまはGPUボードを手放してしまっています。

 

ここまでだと中途半端なので、上のDockerfileから作成したイメージを使って、seq2point-nilmの動作確認を行う手順を一応書いておきます。

 

まず、seq2point-nilmのDockerコンテナをGPUアクセスを有効にした状態で起動します。

$ docker run -it --rm --gpus all seq2point-nilm_conda

ただし、このコマンドを実行したときに、以下のようなエラーに遭遇してDockerコンテナを起動できないことがあります。

$ docker: Error response from daemon: could not select device driver "" with capabilities: gpu

nvidia-driverがインストールされておりnvidia-smiコマンドも使える状態なのに、このようなエラーが起きる場合は、システムにNVIDIA Container Runtimeが入っていないからです。

このようなケースでは、以下の手順を実行して、NVIDIA Container Runtimeをインストールしてください。

$ vi nvidia-container-runtime-script.sh
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | \
  sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
sudo apt-get update
$ sh nvidia-container-runtime-script.sh
$ sudo apt-get install nvidia-container-runtime
$ sudo systemctl restart docker.service

seq2point-nilmのDockerコンテナを起動できたら、その中で作業ディレクトリが /seq2point-nilm になっていることを確認してください。

(base) root@9d1c222ac40e:/seq2point-nilm# ls
README.md    appliance_data.py	dataset_management  images     model_structure.py		   remove_space.py    seq2point_train.py  train_main.py
__pycache__  data_feeder.py	environment.yml     model.png  nvidia-container-runtime-script.sh  seq2point_test.py  test_main.py

この状態で以下のコマンドを実行すると、REFITデータセットから抽出した ケトルの観測電気データに対するseq2point-nilmの学習処理を開始することがきます。

# python train_main.py --appliance_name 'kettle' --training_directory './dataset_management/refit/dataset/kettle/kettle_training_.csv' --validation_directory './dataset_management/refit/dataset/kettle/kettle_validation_H5.csv' --crop 10000

さらに以下のコマンドによって、REFITデータセットのケトルの観測電気データに対するテスト処理も実行できます。

# python test_main.py --appliance_name 'kettle' --test_directory './dataset_management/refit/dataset/kettle/kettle_test_H2.csv' --crop 10000

このテスト処理では、先に実行した学習処理によって生成された学習モデルが使われます。

上の2つの操作は実際には試していませんが、多分動くんじゃないかと思います。

 

近いうちにGPUボードを入手する予定なので、そのときにseq2point-nilmの学習処理とテスト処理もやってみるつもりです。その結果は、また別の記事として投稿する予定です。

 

参照リンク

arxiv.org

 

OpenVSLAM(stella_vslam)のUbuntu 18.04でのビルド

ある人から教えてもらった情報が元なんですが、画像認識の一分野としてSLAM(Simultaneous Localization and Mapping:自己位置推定)という技術が在ることを知りました。現存の物だと、お掃除ロボット、未来の物だと、自動運転車などに利用される技術らしいです。

github.com

画像認識はいまの自分の主たる研究テーマの一つなのに、こんな興味深い技術をいままで知らないでいたとは不覚です。慌てて、SLAMについて調べ始めています。

入力データの形式によってSLAMにも複数の種類があるようですが、その中でもVisual SLAMに注目しています。

オープンソースとして公開されているVisual SLAMの代表的な実装例として、OpenVSLAMというものがあります。これは国立研究開発法人産業技術総合研究所が開発したものだそうです。

本記事執筆時点でOpenVSLAMのソースは非公開になっているのですが、OpenVSLAMのコミュニティ・フォーク版がGithHub上にいくつか存在しているので、ソースを入手することはできます。

これらの中からstella_vslamを選んで、Ubuntu 18.04上でのビルドと動作確認までやってみました。

stella_vslamのビルドに必要なパッケージの導入

$ sudo apt install -y build-essential pkg-config cmake git wget curl unzip
$ sudo apt install -y libatlas-base-dev libsuitesparse-dev
$ sudo apt install -y libgtk-3-dev
$ sudo apt install -y ffmpeg
$ sudo apt install -y libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
$ sudo apt install -y gfortran
$ sudo apt install -y binutils-dev
$ sudo apt install -y libyaml-cpp-dev libgflags-dev
$ sudo apt install -y libglew-dev
$ sudo apt install -y libsqlite3-dev

stella_vslamとリンクされるライブラリのビルドとインストール

Eigenのビルドとインストール

$ git clone -b 3.3 https://gitlab.com/libeigen/eigen.git
$ cd eigen
$ mkdir build && cd build
$ cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local \
      ..
$ make -j4
$ sudo make install

OpenCVのビルドとインストール

$ wget -q https://github.com/opencv/opencv/archive/3.4.0.zip
$ unzip -q 3.4.0.zip
$ cd opencv-3.4.0
$ mkdir build && cd build
$ cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local \
      -DENABLE_CXX11=ON \
      -DBUILD_DOCS=OFF \
      -DBUILD_EXAMPLES=OFF \
      -DBUILD_JASPER=OFF \
      -DBUILD_OPENEXR=OFF \
      -DBUILD_PERF_TESTS=OFF \
      -DBUILD_TESTS=OFF \
      -DWITH_EIGEN=ON \
      -DWITH_FFMPEG=ON \
      -DWITH_OPENMP=ON \
      -DWITH_CUDA=OFF \
      -DWITH_NVCUVID=OFF \
      ..
$ make -j4
$ sudo make install

FBoWのビルドとインストール

$ git clone https://github.com/stella-cv/FBoW.git
$ cd FBoW
$ mkdir build && cd build
$ cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local \
      ..
$ make -j4
$ sudo make install

g2oのビルドとインストール

$ git clone https://github.com/RainerKuemmerle/g2o.git
$ cd g2o
$ git checkout 9b41a4ea5ade8e1250b9c1b279f3a9c098811b5a
$ mkdir build && cd build
$ cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local \
      -DCMAKE_CXX_FLAGS=-std=c++11 \
      -DBUILD_SHARED_LIBS=ON \
      -DBUILD_UNITTESTS=OFF \
      -DG2O_USE_CHOLMOD=OFF \
      -DG2O_USE_CSPARSE=ON \
      -DG2O_USE_OPENGL=OFF \
      -DG2O_USE_OPENMP=OFF \
      ..
$ make -j4
$ sudo make install

PangolinViewerのビルドとインストール

$ git clone https://github.com/stevenlovegrove/Pangolin.git
$ cd Pangolin
$ git checkout ad8b5f83222291c51b4800d5a5873b0e90a0cf81
$ mkdir build && cd build
$ cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local \
      ..
$ make -j4
$ sudo make install

stella_vslamのビルドとインストール

$ git clone https://github.com/stella-cv/stella_vslam.git
$ cd stella_vslam
$ git submodule update -i --recursive
$ mkdir build && cd build
$ cmake \
      -DUSE_PANGOLIN_VIEWER=ON \
      -DINSTALL_PANGOLIN_VIEWER=ON \
      -DUSE_SOCKET_PUBLISHER=OFF \
      -DBUILD_TESTS=ON \
      -DBUILD_EXAMPLES=ON \
      ..
$ make -j4
$ sudo make install

stella_vslamの動作確認

$ ./run_kitti_slam -h
Allowed options:
  -h, --help               produce help message
  -v, --vocab arg          vocabulary file path
  -d, --data-dir arg       directory path which contains dataset
  -c, --config arg         config file path
  --frame-skip arg (=1)    interval of frame skip
  --no-sleep               not wait for next frame in real time
  --wait-loop-ba           wait until the loop BA is finished
  --auto-term              automatically terminate the viewer
  --log-level arg (=info)  log level
  --eval-log               store trajectory and tracking times for evaluation
  -p, --map-db arg         store a map database at this path after SLAM

上のような結果になれば、stella_vslamのビルドとインストールは成功しているはず。

 

上記のビルド手順は、stella_vslamのドキュメントサイトに掲載されている情報とほぼ同じですが、一点だけ相違があり、ビルドに必要なパッケージとしてlibsqlite3-devを追加インストールしています。これを入れないと、stella_vslamの "cmake ..." 過程で以下のようなエラーに遭遇します。

CMake Error at /usr/share/cmake-3.10/Modules/FindPackageHandleStandardArgs.cmake:137 (message):
  Could NOT find SQLite3 (missing: SQLite3_INCLUDE_DIR SQLite3_LIBRARY)
Call Stack (most recent call first):
  /usr/share/cmake-3.10/Modules/FindPackageHandleStandardArgs.cmake:378 (_FPHSA_FAILURE_MESSAGE)
  cmake/FindSQLite3.cmake:57 (find_package_handle_standard_args)
  src/stella_vslam/CMakeLists.txt:10 (find_package)


-- Configuring incomplete, errors occurred!

ビルドできたんなら当然動かしてみたい。プログラムを動かしてみるのが、その中で使われている技術を理解する早道だし。

公式サイトで推薦されているKITTI Visual Odometryという車載画像データセットを利用してstella_vslamの動作確認をやってみました。

下のような手順によって、このデータセットを使ってstella_vslamの動作確認ができます。

# at the stella_vsalm/build directory 
$ mkdir ../vocab
$ mkdir ../dataset
$ cd ../vocab
$ curl -sL "https://github.com/stella-cv/FBoW_orb_vocab/raw/main/orb_vocab.fbow" -o orb_vocab.fbow
$ cd ../dataset
# Download KITTI Odometry dataset (grayscale, 22 GB)
$ wget -q HTTPS_URLPATH_TO_KITTI_DATASET/data_odometry_gray.zip
$ unzip -q data_odometry_gray.zip $ cd ../build $ ./run_kitti_slam -v ../vocab/orb_vocab.fbow -d ../dataset/dataset/sequences/00/ -c ../example/kitti/KITTI_mono_00-02.yaml --frame-skip 1 --no-sleep --map-db map-KITTI_mono_00-02.msg

上の手順中のdata_odometry_gray.zipというのが対象のデータセットが格納された圧縮ファイルですが、これはKITTI(The KITTI Vision Benchmark Suite)のサイトでアカウント登録をしないとダウンロードできません。

このデータセットでstella_vslamを作動させると、下の動画のように、Visual SLAMの動きが良く解ります。 

www.youtube.com

 

参照リンク

arxiv.org

qiita.com