Technology Topics by Brains

ブレインズテクノロジーの研究開発機関「未来工場」で働くエンジニアが、先端オープン技術、機械学習×データ分析(異常検知、予兆検知)に関する取組みをご紹介します。

PythonでのARIMAモデルを使った時系列データの予測の基礎[前編]

こんにちは。ブレインズテクノロジーの岩城です。

今回は、2部構成でPythonを使用してのARIMAモデルの作成・予測の流れを整理したいと思います。

前半の本記事では、時系列データの予測でよく利用される、ARIMAモデルの基礎を簡単におさらいします。

後半の記事では実際にコードを追いながら一連の流れを確認していきますので、先にコードを読みたい場合はこちらをご参照ください。

blog.brains-tech.co.jp

主な参考文献

本記事で紹介するARIMAモデルの理論の部分は、主にこちらの教科書を参考にしています。

沖本竜義(2010), "経済・ファイナンスデータの計量時系列分析", 朝倉書店http://www.asakura.co.jp/G_12.php?isbn=ISBN978-4-254-12792-8

www.asakura.co.jp

きちんと理論を把握したい場合は、是非読んでみてください。

ARIMAとは

まずは、ARIMA(Autoregressive integrated moving average、自己回帰和分移動平均)モデルについて、大まかな内容を確認します。細かな部分は扱いませんので、きちんとした理解が必要な方はご紹介した教科書等をご参照ください。

用語は長いですが、いくつかの用語を組み合わせただけなので、一つずつみていけばそこまで難しくないと思います。

先に、ARIMA過程の定義を先程の教科書からお借りします。

定義 5.3 (ARIMA過程)
d階差分を取った系列が定常かつ反転可能なARMA(p, q)過程に従う過程は次数(p, d, q)の自己回帰和分移動平均過程もしくはARIMA(p, d, q)過程と呼ばれる.
(沖本竜義(2010), "経済・ファイナンスデータの計量時系列分析", P. 106, 朝倉書店より引用)

要はある時系列データに対して、

  • (1)d階差分をとったデータが、
  • (2)定常かつ
  • (3)反転可能な
  • (4)ARMA過程

で表すことができるような過程がARIMA過程で、これを数式化したのがARIMAモデルです。

順に一つずつ確認していきます。

(1)d階差分について

d階差分については問題ないかと思いますが、念のため簡単に説明します。

元の系列が y_0, y_1, y_2, ... , y_tだったとします。

1階差分を取った系列は \Delta y_k = y_k - y_{k-1}  (1\leq k \leq t)で構成される系列です。

2階差分は1階差分の系列から同様の差分を取った系列で、以下d階差分についても同様です。

(4)ARMA過程について ((2)と(3)は後回し)

次は、(2)と(3)は後回しにして、AR過程、MA過程、ARMA過程について説明することにします。

AR過程

AR過程(autoregressive process, 自己回帰過程)は、ある時刻のデータが、過去の時刻のデータから表現することができる過程です。p次のAR過程であるAR(p)は、

  y_t = c + \psi _1 y_{t-1} + \psi _2 y_{t-2} + ... + \psi _p y_{t-p} + \varepsilon _{t}

と表現されます。ここで、 c は定数、 \varepsilon_tは誤差項で、平均0、標準偏差σの正規分布に従います。

MA過程

MA過程(moving average process, 移動平均過程)は、ある時刻のデータが、過去の時刻での誤差項を用いて表現することができる過程で、q次のMA過程MA(q)は、

   y_t = c + \theta_1 \varepsilon_{t-1} + \theta_2 \varepsilon_{t-2} + ... + \theta_q \varepsilon_{t-q} + \varepsilon_t

と表現されます。 \varepsilon_tは先程と同様に誤差項で、各時間の誤差項が平均0、標準偏差σの正規分布に従います。ある時刻と別の時刻の式の中に、共通して存在する誤差項を持たせることにより、相関関係を表現します。

AR過程とMA過程の特徴

AR過程、MA過程ともに、注目しているデータと過去のデータとの関係を表す方法です。

AR過程では過去のデータに係数をかけたものをいくつか組み合わせて注目するデータを表現します。

MA過程では、注目しているデータと過去のデータに共通する項を持たせることで関係性を表現します。

ARMA過程

ARMA(autoregressive moving average, 自己回帰移動平均)過程は、AR過程とMA過程を組み合わせて表現される過程です。ARMA(p, q)は、

   y_t = c + \psi_1 y_{t-1} + \psi_2 y_{t-2} + ... + \psi_py_{t-p} + \theta_1 \varepsilon_{t-1} + \theta_2 \varepsilon_{t-2} + ... + \theta_q \varepsilon_{t-q} + \varepsilon_t

と表現されます。

(2)定常性

定常性は、モデルの特性の一つです。こちらについても、教科書から定義をお借りします。

定義 1.1(弱定常性) 任意のtとkに対して

   E(y_t) = \mu
   Cov(y_t, y_{t-k}) = E[(y_t - \mu)(y_{t-k} - \mu)] = \gamma_{k}

が成立する場合,過程は弱定常(weak stationarity)といわれる.

(沖本竜義(2010), "経済・ファイナンスデータの計量時系列分析", P. 8, 朝倉書店より引用)

もしよく分からない場合は、厳密な表現ではありませんが、

  • 各時刻のデータ y_tの平均値(期待値)が一定で、
  • 任意の時刻kだけ離れた y_tとy_{t-k}の相関係数がkにのみ依存し、tに依存しない。

の二つの条件を満たすデータ系列だと考えてください。
詳細は省きますが、MA過程は常に定常なので、AR過程の部分が定常性を満たせばこの条件を満たすことができます。
また、AR過程が定常である時、AR(p)はMA(∞)で書き直すことができます。

(3)反転可能性

反転可能性は、MA過程に関する条件です。反転可能である時、MA(q)をAR(∞)と書き直すことが可能です。こちらも詳細な説明はできませんが、少しだけ注釈を加えておくと、一般にMA過程は複数存在するのに対し、反転可能性を満たすMA過程は1種類しかないという特徴があります。なお、ARMA過程が反転可能である条件は、ARMAを構成するMA過程が反転可能であることを意味しています。

ARIMAモデルのまとめ

ARIMAモデルの定義と、求められる性質について整理しました。不正確な表現であることを覚悟して、ARIMAモデルを簡潔に表現しようとすると、

「ある時系列データに対して何度か差分を取ったデータがARMAモデルで表現できて、そのARMAモデルは、定常で反転可能であり、 無限に項を使えばAR項だけでもMA項だけでも表現できる」

といったところでしょうか。正確な理解が必要な場合は、やはり参考書をご確認ください。

まとめ

今回の記事では、ARIMAモデルについて、最低限必要な情報を整理しました。大まかな内容だけでも把握していただき、必要に応じて読み直したり、詳しく調べる際の足がかりにしていただれば幸いです。詳細な内容を確認したい場合は、今回何度も引用させていただいている教科書をおすすめします。

後半の記事では実際にPythonを使用して、一連のモデル作成の流れを確認していきます。こちらの記事も是非ご一読ください。 blog.brains-tech.co.jp
ブレインズテクノロジーでは「共に成長できる仲間」を募集中です。
採用ページはこちら

参考文献および参考ウェブサイト

WindowsのDockerで慣れないこと

Neuron開発チームの木村です。

Neuronは、ほとんどの場合Windowsにインストールされるため、テスト環境をWindows上に構築しています。 このテスト環境構築を効率化するため、Docker上でのWindows利用を模索し始めています。

今回は「Docker for Macを使っていた私が、WindowsでDockerを使う際に慣れないこと」を紹介します。

なお、利用しているWindowsのバージョンは
「Windows 10 Pro バージョン 1803 (Version 10.0.17134.48)」です。

以下、OSがWindowsであるコンテナを「Windows OSコンテナ」、OSがLinuxであるコンテナを「Linux OSコンテナ」と呼びます。

目次

どのDockerを使うべきか分からない

(この節は、Windows 10 Pro または Windows Server 2016 のバージョン 1709以上を前提とします。)

macでは、とりあえずDocker for Macをインストールすればよかったのですが、 WindowsでDockerを使用するには、以下の複数の方法があります。

  • Docker Toolbox
  • Docker for Windows
  • Hyper-Vコンテナ + LCOW
  • Windowsコンテナ

Docker Toolbox以外は、Windows OSコンテナもLinux OSコンテナも利用できますが、一体どれを使うべきなのでしょうか。

それぞれの特徴をまとめました。

Docker Toolbox

インストール方法

長所

  • (強いて言えば、)Hyper-Vなしで利用できる
    • 代わりに、VirtualBoxが使われる

短所

  • Windows OSコンテナを利用できない
  • 「Legacy desktop solution.」と説明されるなど、deprecated感がある

Docker for Windows

インストール方法

長所

  • docker composeなどのツール群が一緒にインストールされる

短所

  • Windows OSコンテナの実行にも、Hyper-Vによる仮想化が行われる
  • Linux OSコンテナ用のdocker daemonがMobyLinuxVMというHyper-Vインスタンス上で動作するため、二段の仮想化となり、コンテナ実行時にファイルのpermissionに引っかかるなど面倒なことが起きる可能性がある
    • (experimental機能(LCOW利用)を有効にすれば、MobyLinuxVMは介さないかもしれません)
  • Windows OSコンテナとLinux OSコンテナを同時に操作できない
    • 操作したいコンテナに合わせて、「モードを切り替える」または「experimental機能の--platformオプションを指定する」必要がある

Hyper-Vコンテナ + LCOW

インストール方法

長所

  • Windows OSコンテナとLinux OSコンテナを同時に操作できる
    • 現段階では、docker pullには--platformオプションが必要

短所

  • Windows OSコンテナの実行にも、Hyper-Vによる仮想化が行われる
  • LCOWは、現在experimental
  • 上記のインストール方法では、docker clientにパスを通したり、docker daemonのサービス化は自身で行うこととなる

Windowsコンテナ

インストール方法

長所

  • Windows OSコンテナについては、ホストOSのカーネルを使って実行されるため、Hyper-Vによる仮想化が行われない

短所

  • Windows Server 2016が必要
  • Windows OSコンテナについて、コンテナのWindowsバージョンとホストのWindowsバージョンが、ビルド番号まで一致している必要がある(バージョンが異なる場合、起動がブロックされる


どれを使えばよいかは要件次第なので、今回は、

  • Windows OSコンテナとLinux OSコンテナの両方を使いたい
  • OSをなるべく意識せずに、dockerコマンドを実行したい
  • テスト環境構築に使用するため、パフォーマンスは多少犠牲にしてもよい
  • (experimentalでも使ってみたい)

といった理由から、「Hyper-Vコンテナ + LCOW」を使うこととしました。


Windowsのdockerイメージはどれなのか

まだDocker上でWindowsが動かなかった頃は、どのdockerイメージをpullしてもLinuxのイメージであり、話は単純でした。
さて、では現在、Windowsのイメージをどう見分けてpullするのでしょうか。

結論から言うと、「これはWindows、これはLinux」といった目印は見つけられませんでした。

Windowsのイメージを探す時は、dockerHubのwinamd64microsoftをあたっていくと良いと思います(ただし、microsoftのほうはLinuxのイメージも多い)。

希望のイメージが見つからない場合は、以下のWindows Server 2016基底イメージを使ってDockerfileを書いてゆきましょう。

なお、Windows Server 2012など旧Windowsのイメージは存在しないと思われます。


DockerfileのRUN/CMDは、コマンドプロンプトで実行される

当然なのですが、DockerfileからWindowsのイメージをビルドする時、RUNCMDの内容は、Windows上で実行されます。 つまり、Linuxのshellコマンドをそのまま使うことはできません。

こればかりは、コマンドプロンプト(または、そこから呼び出すPowerShell)のコマンドに慣れてゆくしかありません…
さらに、パス区切りがややこしく、COPYなど/でよい命令と、WORKDIRなど\でなければならない命令が混在しています。

(bashのコマンドからWindowsのコマンドに変換する何かを、docker buildに挟みたい)


おわりに

今回は、dockerのインストールやdockerイメージの作成について、WindowsでDockerを使う際に慣れないことを紹介しました。
次回は、実際にコンテナを動かしていく際のことを紹介する予定です。 何事もなくすんなり使えればよいですが…

実践編に続きます。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら

参考

Dockerコンテナで起動するElasticsearchの性能情報を取得した話

ブレインズテクノロジーの加藤です。

今回は、Dockerコンテナ上で起動するjavaアプリケーションの性能情報の取得方法についてまとめます。
例としてElasticsearchを使用しましたが、javaで起動しているアプリケーションであれば何にでも応用が効くはずです。

経緯

あるお客様の環境で、Dockerコンテナ上にElasticsearchを起動してサービスを提供しているのですが、GC系のERRORが頻発するようになりました。

原因の特定と解決策の検討のため、jstatによる性能情報を取得しようとしたのですが、ホストから見たElasticsearchのプロセスIDが異なるからか、ホスト側からは取得ができず。
仕方なくDockerコンテナに入って取得しようとしたのですが、今度はDockerコンテナにjdkが入っていないため、インストールするところから始めました。

以下はその手順です。

Dockerコンテナにログインする

Elasticsearchが起動していること、およびプロセス名を確認します。
今回のプロセス名は"brave_jepsen"のようです。

$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED                  STATUS              PORTS                NAMES
3596bfd346d4        elasticsearch:latest   "/docker-entrypoint.…"   Less than a second ago   Up 2 seconds        9200/tcp, 9300/tcp   brave_jepsen

docker exec コマンドにより、elasticsearchのコンテナにログインします。

$ docker exec -it brave_jepsen bash
root@3596bfd346d4:/usr/share/elasticsearch#

ログインできました。

Dockerコンテナにjdkをインストールする

試しにDockerコンテナの中で、jstatコマンドを実行してみます。

$ jstat
bash: jstat: command not found

入ってないですね。仕方ないのでインストールします。
インストールにはapt-getコマンドを使用します。

まずはapt-getを最新化しましょう。

$ apt-get update
Ign:1 http://deb.debian.org/debian stretch InRelease
Get:2 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB]
・・・(略)
Fetched 10.3 MB in 3s (3173 kB/s)
Reading package lists... Done

ちなみに環境によっては、apt-getを実行しても失敗することがあります。
その際は、環境変数「http_proxy」にプロキシサーバを設定すれば通るかもしれません。

export http_proxy=${プロキシサーバのホスト}:${プロキシサーバのポート}

続いて、apt-fileをインストールします。

$ apt-get install -y apt-file
Reading package lists... Done
Building dependency tree
Reading state information... Done
・・・(略)
Setting up apt-file (3.1.4) ...
The system-wide cache is empty. You may want to run 'apt-file update'
as root to update the cache.

apt-fileをupdateしろと言っているので、仰せのままに実行します。

$ apt-file update
Ign:1 http://deb.debian.org/debian stretch InRelease
Get:2 http://security.debian.org stretch/updates InRelease [63.0 kB]
・・・(略)
Building dependency tree
Reading state information... Done
3 packages can be upgraded. Run 'apt list --upgradable' to see them.

apt-fileのsearchコマンドにより、jstatが含まれるパッケージを見つけます。

$ apt-file search jstat
libmpj-java: /usr/bin/mpjstatus
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/bin/jstat
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/bin/jstatd
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/ja_JP.UTF-8/man1/jstat.1.gz
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/ja_JP.UTF-8/man1/jstatd.1.gz
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/man1/jstat.1.gz
openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/man1/jstatd.1.gz

「openjdk-8-jdk-headless」に含まれるようです。
なので対象のパッケージをインストールします。

$ apt-get install -y openjdk-8-jdk-head
Reading package lists... Done
Building dependency tree
・・・(略)
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/bin/wsgen to provide /usr/bin/wsgen (wsgen) in auto mode
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/bin/jcmd to provide /usr/bin/jcmd (jcmd) in auto mode

jpsコマンドにより、Elasticsearchのプロセスが表示されれば成功です。
elasticsearchの場合、プロセスIDは1になるようです。

$ jps -v
1 Elasticsearch -Xms2g -Xmx2g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError -Des.path.home=/usr/share/elasticsearch
802 Jps -Dapplication.home=/usr/lib/jvm/java-8-openjdk-amd64 -Xms8m

Javaの性能情報取得

jstatコマンドにより性能情報を取得します。
使用可能なオプションの種類や、出力項目の詳細は、下記のオラクルのページに載っています。

docs.oracle.com

今回は gccauseオプションを使用したいと思います。
コマンドの意味としては、"-h1000"で1000行ごとにヘッダーを表示。
途中の1でelasticsearchのプロセスIDを指定。
1000は、1秒ごとに1000回繰り返すことでその間の性能情報を取得することができます。

$ jstat -gccause -h1000 1 1000 > /usr/share/elasticsearch/logs/gccause.txt
$ cat /usr/share/elasticsearch/logs/gccause.txt
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC
100.00   0.00  98.21   0.53  93.29  83.32      2    0.115     2    0.037    0.152 CMS Final Remark     No GC

GCログはDockerコンテナ内に出力されるので、volume mappingでホスト側と共有しておくと良いです。

ちなみにお客様の環境ではdocker-composeを使っています。
docker-compose.ymlの設定は以下のような感じです。

(抜粋)
  volumes:
        - /home/logs/elasticsearch:/usr/share/elasticsearch/logs

この設定をしておけば、ホスト側の「/home/logs/elasticsearch」ディレクトリにgccause.txtが出力されているはずです。
gccauseだと実行時刻が出力されないため、ホスト側で下記のようなコマンドを実行するといい感じです。

$ tail -f /home/logs/elasticsearch/gccause.txt | awk '{print strftime("%Y/%m/%d %H:%M:%S"), $0; fflush()}' > /home/logs/elasticsearch/gccause_new.txt

(出力結果)
2018/02/23 15:54:50   S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 
2018/02/23 15:54:50  99.35   0.00  81.41   5.55  98.06  94.74     40    2.183     2    0.108    2.292 Allocation Failure   No GC 
2018/02/23 15:54:50  99.35   0.00  81.43   5.55  98.06  94.74     40    2.183     2    0.108    2.292 Allocation Failure   No GC
2018/02/23 15:54:50  99.35   0.00  81.45   5.55  98.06  94.74     40    2.183     2    0.108    2.292 Allocation Failure   No GC
2018/02/23 15:54:50  99.35   0.00  81.46   5.55  98.06  94.74     40    2.183     2    0.108    2.292 Allocation Failure   No GC
2018/02/23 15:54:50  99.35   0.00  81.48   5.55  98.06  94.74     40    2.183     2    0.108    2.292 Allocation Failure   No GC

終わりに

というわけで、無事にDockerコンテナで起動するElasticsearchの性能情報を取得できました。
冒頭でも書きましたが、javaアプリケーションであれば、Elasticsearch以外でもこの方法で取得できるはずです。

この記事が誰かの役に立てればいいと思うー。そだねー。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら

ABEJA Cloud AI Night 〜エッジコンピューティング編〜で発表しました

ブレインズテクノロジー 林です。
ご縁がありまして、ABEJA Cloud AI Night 〜エッジコンピューティング編〜で発表させていただきました。

発表資料はこちらになります。
speakerdeck.com

発表されている内容も共感する内容あり、また、とても勉強になる内容でした。
Q&Aも技術的に深い内容であり、費用対効果をどう考えるかなどビジネス的な観点の質問あり、
多種多様な方々の考えに触れることができ、多くの刺激を受けました。

まだまだ、クラウド x エッジの取り組みは未開拓の領域も多いですが、
技術の移り変わりも早く、新しい動きが起きているので非常に楽しみな分野です。

こういった機会に触れながら、より良いサービスを提供できるよう精進してまいります。
少し短い文章ですが、今回はこのあたりで失礼いたします。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら

Amazon SageMakerのDeepARアルゴリズムで時系列予測

データ分析担当の青木です。

Amazon SageMakerでDeepARというアルゴリズムがリリースされました。今回の記事では下記のページを参考に、Amazon SageMakerのDeepARアルゴリズムを使ってみた内容を書きたいと思います。

aws.amazon.com

Amazon SageMaker

Amazon SageMakerは、機械学習におけるモデルの構築やデプロイ等の処理を容易に行えるようにしたフルマネージドな機械学習サービスです。SageMakerを使うことで、開発者やデータサイエンティスト等の分析者は、機械学習における一連の処理を手軽に素早くできるようになります。

DeepAR

DeepARはRecurrent Neural Networkをベースにした時系列予測の教師ありアルゴリズムで、ARIMAや指数平滑化法などの従来の予測手法に比べて高い精度が期待できる手法です。このDeepARは既存の時系列データがほぼない状況で予測を行うコールドスタート予測や、予測値の信頼区間を算出するなど、実際のビジネスシナリオに即した機能をサポートしています。

SageMakerでDeepARを試す

それではSageMakerでDeepARを試してみたいと思います。

SageMakerでのモデル作成および予測までの流れは、

データ準備 → 学習ジョブの設定・実行 → エンドポイントへのモデルデプロイ→ 予測

といった感じです。今回はコンソール画面から実行する方法を簡単に紹介したいと思います。

データ準備

DeepARでのデータフォーマットは、JSONとParquetの2つのファイルをサポートしています。JSONでの例は

{"start": "2016-01-01 00:00:00", "target": [4.9, 5.3, 3.4, 5.9, ...]}
{"start": "2016-03-01 00:00:00", "target": [2.0, 3.4, 4.5, 3.8, ...]}
{"start": "2016-03-09 00:00:00", "target": [5.6, 9.3, 3.4, 4.7, ...]}

のようになります。

startは開始時刻、targetは実際の数値配列となりますが、各フィールドの詳細はこちらを参照ください。

今回はこちらのサンプルノートブックにあるデータをそのまま使って試してみます。

f:id:brains_aoki:20180112215351p:plain

上記は乱数で発生させた1時間毎のデータで、データ数は400です。前半352個のデータをtrain、後半48個をtestとします。

さらにこのように発生させたデータ系列を200セット用意し、trainデータとtestデータをS3に準備しておきます。

学習ジョブの設定・実行

学習ジョブの設定・実行方法はいくつかありますが、今回はマネージメントコンソールから実行していきます。

まずは、ジョブ名やアルゴリズム、リソース等の設定です。AlgorithmはDeepAR forecastingを選択します。

f:id:brains_aoki:20180112232729p:plain

次にアルゴリズムのハイパーパラメータの設定です。

f:id:brains_aoki:20180112230356p:plain

続いて入力データの設定です。

まずはtrainデータの設定。

f:id:brains_aoki:20180112230516p:plain

続いてtestデータの設定。テストデータの設定は任意です。

f:id:brains_aoki:20180112230636p:plain

以上の設定が完了したらジョブ実行ボタンを押下します。

エンドポイントへのモデルデプロイ

作成したモデルを選択し、エンドポイントを作成してモデルをデプロイします。

f:id:brains_aoki:20180112234119p:plain

予測

notebookから予測を試してみます。

f:id:brains_aoki:20180115012253p:plain

output_typeはquantilesとしてみました。結果がちゃんと得られていますね。

終わりに

Amazon SageMakerでDeepARを試してみましたが、データ準備からDeepARによる予測まで手軽に素早く行うことができました。一連の面倒な処理もこれだけ簡単にできれば、機械学習による検証もより素早く行えるようになりそうだという印象でした。今後はいろいろなデータでこのSageMakerを試して見たいと思います。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら

docker-composeで環境変数を使う

こんにちは。エンジニアの中西です。
今回は docker-compose の yml ファイルにて、環境変数が展開されずにハマった事について書きたいと思います。
docker-compose は Impulse でも使っています。複数のコンテナから構成される Impulse を yml ファイルで管理できて便利です。
yml ファイルに環境変数を定義しておき、実行環境毎に変数値を設定する事も可能なのですが、環境によっては設定した値が展開されず。。という事象に遭遇しました。

実行環境

  • OS : Ubuntu 16.04
  • docker : v17.09.0-ce
  • docker-compose : v1.17.0
  • docker-compose.yml : v3.3

準備

それでは、イメージのバージョンを環境変数で設定できるようにymlファイルを記述してみます。

version: '3.3'

services:
  envtest:
    image: envtest-node:${IMAGE_VERSION:-invalid}
    :
  (省略)

イメージのバージョンを環境変数 IMAGE_VERSION で変更可能としています。 環境変数の指定がない場合は invalid が設定されます。

続いて、イメージが存在している事を確認します。

$ docker images envtest-node
REPOSITORY     TAG     IMAGE ID      CREATED      SIZE
envtest-node   latest  fedc1d9b97ce  1 weeks ago  933MB

バージョンは latest です。

実行してみる

まずは環境変数を指定せずに実行してみます。

$ docker-compose up -d envtest
Pulling envtest (envtest-node:invalid)...
ERROR: pull access denied for envtest-node, repository does not exist or may require 'docker login'

環境変数が設定されていないので、デフォルトのinvalidが設定されました。
当然ですが、envtest-node:invalid というイメージは存在していないので docker pull で取得しようとしていますが、ログインしていないためエラーとなっています。

では、環境変数を指定して実行します。

# バージョン`latest`を設定
$ export IMAGE_VERSION=latest

# 実行
$ docker-compose up -d envtest
Pulling envtest (envtest-node:invalid)...
ERROR: pull access denied for envtest-node, repository does not exist or may require 'docker login'

エラー。
バージョン部分がデフォルト値の invalid となっていますね。。
設定した環境変数が反映されていないようですので、確認してみます。

$ echo ${IMAGE_VERSION}
latest

ちゃんと設定されていますね。

では、docker-compose.yml の environment に設定値含めて追加してみます。

version: '3.3'

services:
  envtest:
    image: envtest-node:${IMAGE_VERSION:-invalid}
  environment:
    - IMAGE_VERSION=latest
    :
  (省略)

再度実行します。

$ docker-compose up -d envtest
Pulling envtest (envtest-node:invalid)...
ERROR: pull access denied for envtest-node, repository does not exist or may require 'docker login'

エラー。同じですね。

どうすれば展開されるのか

docker-composeは .env という環境ファイルにデフォルトの環境変数を定義できるようなので、設定してみます。

$ vi .env
IMAGE_VERSION=latest

上記の1行を設定して保存し、実行してみます。

$ docker-compose up -d envtest
Creating envtest ...
Creating envtest ... done

おぉ、起動したようです! 確認してみます。

$ docker ps
CONTAINER ID  IMAGE                COMMAND                 CREATED        STATUS        PORTS                   NAMES
452f84eed286  envtest-node:latest  "/bin/sh -c 'node sr…"  2 minutes ago  Up 5 seconds  0.0.0.0:3333->3333/tcp  envtest

バージョンlatestで起動されています。

もしかして、.envに環境変数が設定されていれば、実行時の環境変数設定値が反映されるのか?
公式サイトによると、
実行時に環境変数を指定すると、常に .env ファイル中で定義した変数を上書きします。同様にコマンドラインの引数で値を指定した場合も、指定した値を優先します。
とあります。

環境ファイル — Docker-docs-ja 17.06.Beta ドキュメント

試してみます。

$ export IMAGE_VERSION=none

存在しないバージョンを設定したので起動されないはずです。

$ docker-compose up -d envtest
Creating envtest ...
Creating envtest ... done

起動してしまいました。。

$ docker ps
CONTAINER ID  IMAGE                COMMAND                 CREATED        STATUS        PORTS                   NAMES
f50c6736d2e7  envtest-node:latest  "/bin/sh -c 'node sr…"  4 seconds ago  Up 3 seconds  0.0.0.0:3333->3333/tcp  envtest

バージョンは.envに設定したlatestですね。
実行時の環境変数設定が反映されていない事がわかります。

原因は何?

とりあえず、 .envに書いておけば反映される 事は分かりましたが、冒頭で述べた通り環境によっては実行時の環境変数設定が反映されたりと、気持ちの悪い状態が続いていました。
そんな中、何気なくdockerのイメージ一覧を確認していたときでした。

$ docker images
REPOSITORY      TAG     IMAGE ID      CREATED      SIZE
docker/compose  1.17.0  b5a188e247b9  3 weeks ago  19.6MB

docker-compose のイメージが存在しています。
そういえば以前から環境によっては存在していた気がしますが、あまり気にしてはいませんでした。
ちょっと公式サイトを確認してみましょう。

Docker Compose のインストール — Docker-docs-ja 17.06.Beta ドキュメント

docker-composeのインストール方法は3パターン存在するようです。

  1. curlコマンド実行によるインストール
  2. pipコマンド実行によるインストール
  3. dockerコンテナとしてインストール

※ビルドする方法もありますがここでは除外します

今回の環境は3番目ですね。
そういえば、実行時の環境変数設定が反映されている環境はどうなんだろう。
docker-composeのコンテナは存在しているのか確認してみる。

$ docker images docker/compose
REPOSITORY      TAG     IMAGE ID      CREATED      SIZE

ありませんね。 curl、又はpipでインストールされている事になります。

まさか、インストールの方法で動きが変わる???
試しに、NGな環境のdocker-composeを削除し、curlで再インストールしてみましょう。

$ docker rmi <docker-composeのイメージID>
:
$ curl -L --fail https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ docker-compose version
docker-compose version 1.17.0, build ac53b73
:
$ docker images
REPOSITORY     TAG     IMAGE ID      CREATED      SIZE
envtest-node   latest  fedc1d9b97ce  1 weeks ago  933MB

正しくインストールされ、docker-composeのイメージも存在していないですね。
実行する前に、環境変数の値を再確認しておきます。

# .envファイル
IMAGE_VERSION=latest

# 実行時の環境変数
$ echo ${IMAGE_VERSION}
none

デフォルトはlatestですが、実行時にnoneとしているので、noneとなるのが理想です。
実行してみます。

$ docker-compose up -d envtest
Pulling envtest (envtest-node:none)...
ERROR: pull access denied for envtest-node, repository does not exist or may require 'docker login'

なんと! イメージのバージョン指定がnoneとなっています!
これは、.envの設定値を実行時の設定値で上書きされている事になります。
意図した動作をしてくれました。

終わりに

なぜコンテナとしてインストールすると環境変数の値が反映されないのかは調査できていませんが、 もし、同様の事象にハマった場合には、一度dockerのイメージ一覧を確認してみる事をおすすめします。
そこにdocker/composeのイメージが存在していたら。。。


ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら

PowerShellDSCでハマったポイント

すっかり寒くなってきました。
こんにちは、ブレインズテクノロジーの白石です。

今回、業務の中でPowerShellDSCに触れることがありましたので、それについて得た知見を書きます。
というのも、PowerShellDSCの情報ってあんまり載ってないので。。。
こういうやり方あるよ、こういうの知ってるよ、ていうのがあれば是非是非コメントお願いします。

ちなみに、使用するPowerShellのバージョンは5.0以上ですが、一部5.0未満が対象の話題もあります。
特に理由がなければ5.0に上げることをオススメします。

PowerShellDSCとは

PowerShellDSCとは、Windows専用の構成自動化ツールです。
詳しい紹介はこちら。
www.atmarkit.co.jp

Configuration, Node, Resource, ConfigurationData あたりが理解できてると以降の内容は読みやすくなると思います。

管理マシンと構成対象のマシンでPSのバージョンは揃える

そりゃそうだ、という話ですが、直面すると意外に気づきません。
バージョンが異なるとモジュールの配置場所が違ったり仕様が異なったりしていて、あるマシンではうまく動くのに別のマシンでは全く動かない、という奇妙なバグに悩まされます。(体験談)

使用してるマシンのバージョンは $PSVersionTable で確認できるので、DSCで構築を行う前に対象のマシンと管理マシンのバージョンが揃っているか確認しておきましょう。

外部のモジュールは構築対象のマシンにも入れておく

これも言われれば、そりゃそうなんやろね、という感じですが。

PowerShellを使ってると、こういう機能ないの?と色々欲が出ます。そうした場合、同じように考えた人がやはりいて、大抵の場合はライブラリとして提供されているわけです。
そういったライブラリは Install-Module xxx とかで取り込んでいくのですが、
それらは取り込んだマシンでしか使えません。つまりローカルテストでは動きますが、リモートマシンで同じスクリプトを動かそうとすると、そのマシンには対応するライブラリがインストールされてないのでエラーになります。

この辺りは、そのモジュールをインストールするPSスクリプトを書いてリモートで適用するなり、
該当するライブラリをリモートで配置するなりして対応しましょう。

リソースは一意性がある

まず、「リソース」とはなんぞや。
「リソース」とは、PowerShellDSCの用語の1つで、マシンに適用する状態や操作のことだと思ってください。
プログラムを経験してる方には関数といえば何となく伝わるでしょうか。
例えば、フォルダを作る、ファイルを消す、Zipを展開する、環境変数を定める、などなど。

で、このリソースというもの。構成の中で一意性を保証する必要があり、そのためにキーがあります。
なんのこっちゃという感じなのでとりあえず例を。

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample'
}

これはディレクトリを作るリソース(関数)で、リソース名(=CreateDirectory)がキーとなっております。
もちろん動きます。

さて、もう1個ディレクトリ作りたいなーと思い、このリソースを使い回すことを考えたとします。

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample'
}

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample2'
}

エラーになります。2回呼び出しただけなのに、どういうことなのか。
先ほど上でリソースにはリソース名がキーとなっていると申し上げました。
つまり、リソース名(例だとCreateDirectory)は適用する構成全体の中でユニークである必要があるのです。
なのに同じリソース名を使ったため、一意性が失われてエラーになりました。
(ちなみに、リソースが違えばリソース名が同じでも大丈夫です。あくまで同じ種類のリソースで一意ということです。)

なるほど。というわけで、例えば以下のようにして解決します。

$destinationArray=@('C:\sample', 'C:\sample2')
foreach($path in $destinationArray){
 File (CreateDirectory + $path) {
   Ensure='Present'
   Type='Directory'
   DestinationPath=$path
 }
}

リソース名に変数をつけてやれば、リソース名の一意性は保たれます。
foreachとかで回したい時のちょっとしたテクニックです。
関数の感覚で使うと、この仕様はちょっと面食らいます。

キーはリソース名だけじゃない

上の話題は序の口です。
今度は例として、環境変数HOGEを作って削除するという何の意味もない操作を作ることにしましょう。

Environment CreateHoge {
 Ensure='Present'
 Name='HOGE'
 Value='C:\hoge'
}
Environment RemoveHoge {
 Ensure='Absent'
 Name='HOGE'
}

リソース名は別にしたのに怒られます。なぜなのか。
操作をよく見ると、HOGEという環境変数を「存在する」「存在しない」ようにする操作が混在してますね。
このEnvironmentというリソースは、環境変数名であるNameパラメータもキーとなっているため、リソース名だけでなく、Nameもユニークである必要があるのです。
この辺りは各リソースの仕様に依存しているので、設定を見るなり、エラーにぶつかりながら学ぶしかありません。

ちなみに、これを解決するにはScriptリソースを使って無理やり書くしかありません。
もしくはConfigurationそのものを分けるか。


そもそもこういう操作をDSCに求めるのが間違っている、ということだと私は思ってます(それでもやるときはやる)。

Scriptリソース内だと変数展開のタイミングが違う(powershell 5.0未満限定)

まだあるのか!!あるんです。ただし、PowerShell 5.0未満を使っている人だけです。
ここまででリソースにはキーがあり、それはリソース名だけでなく指定するパラメータにもあることを紹介しました。
そして、その関連で最後にハマるであろう箇所がScriptリソースを使用した時です。

本題の前にScriptリソースとはなんぞやという話をしておきましょう。

通常、FileリソースやEnvironmentリソースなど、予め用意されたリソースに所定のパラメータを渡すことによって目的とする操作を行うことができます。
ただ、そうした操作が用意されていない場合、もしくは、もっと細かい操作を書きたい、となった場合、
Scriptリソースを使います。
これ、中の記述で行いたい操作のPowerShellスクリプトを直接書いてしまいます。
詰まる所Scriptリソースさえあれば力づくで何だってできるわけです。
(これをやりだすとべき等性とかどうでもよくなってくる。。。)

話を元に戻します。
Scriptを使って複数のディレクトリを作る処理を書いてみます。
(通常はFileリソース使いますが、説明のためScriptで書きます)

foreach($path in @('C:\test1', 'C:\test2')){
 Script $path {
  GetScript={
   return $null;
  }
  TestScript={
   return (Test-Path $using:path);
  }
  SetScript={
   New-Item $using:path -ItemType Directory;
  }
 }
}

$using:pathとは、Scriptの外から変数を渡す際につける接頭語です。$pathでは解釈されません。
一見うまくいきそうです。少なくとも、Fileリソースで作っていた場合は問題ありませんでした。

これを実行すると以下のようなエラーが出ます。

Add-NodeKeys : キー プロパティの組み合わせ ' (中略) ' が、ノード 'localhost' 内のリソース 'Script' のキー 'GetScript,SetScript,TestScript' で重複しています
。キー プロパティは、ノード内の各リソースで一意になるようにしてください。

なんやそれ。。。
これは、Scriptリソース内で指定した$using:変数の展開がリソースのキーチェックの後に行われるため、キーが重複したと判断されているからです。
これをどう解決すればいいのかというと、一つはPowerShellのバージョンを5.0に上げてしまうことです。
5.0ではmofファイル生成時に$usingで使用している変数について、スクリプトの先頭に代入文が挿入されて解決されるようになっています。
何かしら事情があって5.0へ上げられない人はどうするのか。
安心してほしい。実はこれを解決した先人がいるのです。
www.briantist.com

リンク先にある関数を宣言して、GetScript, TestScript, SetScriptの後ろに " | Replace-Using " と書くと、キーの評価前に変数が展開され、エラーを回避できます。素晴らしい。

コンパイル時の変数展開はコンパイルしたマシン上で行われる

話を変えて、変数の展開、特に環境変数についての話をします。
表題だけ見ると、何言うてんねん、と言いたくなりますが、私はハマってしまいました。
PowerShellスクリプトにおいて、環境変数へは$env:変数名でアクセスすることができます。

で、これは実際に私が書いたコードですが、JAVA_HOME環境変数がnullかどうかでJavaのインストールを行う処理を書いてました。
(パッケージのインストールについてはcChoco(ChocolateyのDSC版)を使うと楽なのですが、当時はライセンス明記が不明瞭だったので使用を避けてました。今はApache v2ライセンスになってるので問題なさそうです。)

if($env:JAVA_HOME -ne null){
 Script InstallJava{
  (Javaインストール処理)
 }
}

ローカルでテストしたら、うまく動いてるようです。
ですが、リモートで別のマシンに適用するとうまく動きません。
ん? そう思ったらうまく動いてる時もあります。一体何がどうなっているのか。

実は、$env:JAVA_HOMEの評価はこのスクリプトコンパイルした際のマシンの値を参照しているのです。
PowerShellDSCでConfigurationファイルをコンパイルする際、変数値は展開され、適用するマシンそれぞれに対応したmofファイルが作られます。
この時、マシン内の情報を参照するような変数を扱った場合、それらはコンパイルしたマシンの情報が適用されてしまいます。

つまり、適用したいマシンのJAVA_HOMEをチェックして書いたつもりが、実はそれはコンパイルを行なったマシンのJAVA_HOMEをチェックしているだけで、適用したいリモートマシンの情報は見れていないのです。

こういう場合、Invoke-Commandなどで事前に情報を取っておいて、
ConfigurationDataに情報を載せるのが良いと思います。
(ただし、結構コードがゴチャゴチャします。もっと楽な方法があるといいのですが・・・。)

ConfigurationDataのNode ‘*’ と、それ以外のNodeのKeyValueについて

PowerShellDSCにはConfigurationDataという、マシンの名前やパラメータの値を書いておく設定ファイルを用意できます。
この時、NodeName=‘*’ とした部分のKeyValueは全ノードに適用されます。

これは、デフォルト値としても利用出来ます。

AllNodes=@(
@{
NodeName=‘*’
Something=‘default’
},
@{
NodeName=‘localhost
Something=‘custom’
}
)

こう書けば、NodeName=‘localhost’の時のSomethingの値はcustomとなっていて、他のノードではデフォルト値としてdefaultが入ります。
ここで注意することは、これがKeyValueの上書きであるということです。
特に、構造体に対して適用する時は本当に注意しましょう。

AllNodes=@(
@{
NodeName=‘*’
Something=@{
arg0=‘sample00.txt’
arg1=‘sample01.txt'
}
},
@{
NodeName=‘localhost
Something=@{
arg0=‘extra.txt’
arg2=‘sample02.txt'
}
}
)

こう書いた場合、localhost のノードでは、Something.arg0の値は上書きされています。
そして、arg2が定義されてるのでそれにもアクセスできるようになります。

ただ、arg1は$null値となります。なぜなら、KeyValueが上書きされてarg1は未定義となるからです。
(PowerShellでは定義されていないKeyにアクセスすると $null が返されます。)
これがバグに繋がっていなければいいですが、いかんせんコンパイル時にエラーにならないのが怖いところです。

マージや継承のような感覚では書けないので注意です。

ConfigurationDataをJSONで書く

上で述べたConfigurationDataですが、
これはコード内に書いて宣言することもできますし、外部ファイルに書いておいて読み出すことも可能です。

いずれにしてもこの設定ファイル、記述がJSONによく似ています。
いっそのことJSONで管理してしまいたい。

JSONを扱うためのCmdletとして、ConvertFrom-JSONが用意されてます。
使い方は以下が詳しいです。
tech.guitarrapc.com


ただし、ConvertFrom-Jsonで読み込んだ場合、その変数の型は PSObject です。
PowerShellDSCでコンパイル時に渡しているConfigurationDataの型は HashTable なので、PSObjectからHashTableに変換する必要があります。
下記リンクを参考に関数を用意。
Topic: Configuration Data for DSC not in JSON

function ConvertPSObjectToHashtable
{
 param (
  [Parameter(ValueFromPipeline)]
  $InputObject
 )

 process
 {
  if ($null -eq $InputObject) { return $null }
  if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]){
   $collection = @(
    foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object }
   )
   Write-Output -NoEnumerate $collection
  } elseif ($InputObject -is [psobject]){
   $hash = @{}

   foreach ($property in $InputObject.PSObject.Properties){
    $hash[$property.Name] = ConvertPSObjectToHashtable $property.Value
   }
   $hash
  } else {
   $InputObject
  }
 }
}

変換して実行する部分はこんな感じ。$jsonFileはもちろん読み込みたいJSONファイル名

$tmpObject = cat $jsonFile | convertFrom-Json
$hash = $tmpObject | ConvertPSObjectToHashtable

Sample -ConfigurationData $hash
Start-DscConfiguration .\Sample -Wait -Force

PowerShell5.0の便利機能

こちらが大変詳しい。
www.buildinsider.net

詳しすぎて特にここで言うこともないのですが、それでもこれだけは強調しておきます。

Get-DscConfigurationStatus超便利

上に貼ったリンク先でも特にオススメされてる機能ですが、本当に助かります。
PowerShellはうまくいかなかった時、「うまくいきませんでした。」くらいの内容しか返してくれなくて(大げさですが)、調査に時間を取られるのですが、このコマンドを打てばどのリソースが成功してどのリソースが失敗したのかが一発でわかります。

特に複数のConfigurationを適用するときは、合間にこのコマンドを打ってログとして残しておくことでどこまで進んだのかわかりますし、
条件分岐をしていた場合にもどのリソースが実行されたかが記録として残されるので、テストにもってこいです。

終わり

PowerShellDSCはあまり情報が載ってなくて、さらにはエラー内容も比較的不親切なこともあって、結構苦戦しました。
1個1個、解決した内容はメモして再発させないことを心がけると幸せになれると思います。

そして、できればその内容を共有をしていただけると、同じように悩む人も幸せになれるので、是非是非お願いいたします。