Boy Meets ML

機械工出身者が機械学習やその界隈の知識を考える

dockerについて #事前知識

はじめに

従来はインフラエンジニアとアプリケーションエンジニアに分かれて、 サービスをつくるための仕事を実施していた(らしいです)。 正直、私はアプリケーションエンジニアであり、 インフラエンジニアは必要ですが後述する理由によってインフラエンジニアになる人は かなり少数になってしまうのだと思います。 そこで、最近はPaaS(Platform as a Service)やDockerと言われる仮想技術化によってインフラ構築をサクッと 終わらせることが多くなっています。 本記事では、このようなDockerに関わる話題を記載する予定です。

インフラエンジニアにならない人たち

はじめにで述べたように私自身もアプリケーションエンジニアであると思います。 出身も機械系のロボット工学が専攻ですので、色々な理由からインフラエンジニアになりたいと思うことが少なかったです。

まず、インフラエンジニアにならない人たちが多い理由としては、私の個人的な意見ですが次のことが挙げられると思います。

  1. 学生であれば、限られた期間でNEWな研究開発の実績をあげることが難しい
  2. 社会人であれば、インフラエンジニアは評価されにくい(日本企業は特に)
  3. Webネイティブな世代や物心ついたころから携帯電話やインターネットを利用していた世代にとって、サービスは常時提供されていることが当たり前であり、意識的に知覚できるコトはアプリケーションエンジニアの実績となる側面が多い

物議を醸してしまうかもしれませんが、少なくとも私がインフラエンジニアに前向きにならなかった理由は上記の通りでした。

大学院生のときは、ロボット工学におけるサービス面を探求し、どのような機能のユーザエクスペリエンスを変えれば自動運転が当たり前の世の中になるのか?といった側面から、技術的な探求や新しい手法の実装・評価を行っていました。

現在はIT関連の一部上場企業で勤務していますが、悪い意味で日本的な特色をもつ企業です。 サービスは当たり前に運用され続け、問題が生じたときはバッシングを受けます。 普通に稼動していることは褒められることではなく、評価されにくいです。 さらに社内における自身のアピールポイントとして訴求しにくい側面がインフラエンジニアにはあると思います。 0から1をつくりあげ、新しく利益を上げるアイデアはアプリケーションエンジニアが立ち上げ、 立ち上げた1を0に戻さないように努力し続ける人がインフラエンジニアであると捉えています。 3番目にあげた項目も似ており、サービスが24時間・365日通常通りに稼動していることは当たり前で、問題が起きたときにだけ文句を言われてしまいます。 例えば、少し前にみんなが遊んでいたポケモンGOも障害が発生すればTwitterなどで不平不満があがりますが、 正常運行しているときは誰も褒めるようなTweetを書くことはないでしょう。 "今日も無事ログインできて、快適なポケモンライフを送っているぜ!"なんてTweetをする人はいないでしょう。

このような評価されにくい、褒められにくい、といった側面がインフラエンジニアに重くのしかかり、 目立つ側面であるアプリケーションエンジニアばかりが増えてしまうのだと私は思っています。

仮想化技術でどう変わっているのか

私もインフラ構築や維持は、時間がかかる上に褒められることが(少)なく、好んで取り組みたいとは思いません。 しかしインフラエンジニアは必須です。 人間が生きていく上で呼吸をしないと生命を維持できないことと同じで、必ずいるのです。 そこでアプリケーションエンジニアにとって便利なものが仮想化技術です。 ご存知かと思いますが、以下に記載するdockerは仮想化技術を活用することで、アプリケーションエンジニアがインフラ構築、維持管理を行いやすくなり、新たなサービスや技術に対するtry&errorを高速化することが可能になります。

dockerの概要

公式のリファレンス1や各種サイト、書籍を読めば沢山書いてあるので、ここでも自分用のメモとして残すことが主眼になります。 特に本記事では文献2を参考にしています。 最低限必要と考えられるインフラ周りのワードについても触れられているので、私のようなインフラエンジニア初心者にとってイメージしやすい良書だと思います。

dockerの特徴

  1. linuxカーネルを基盤として、この機能をうまく使うことで構成されている

  2. dockerfileによりinfrastructure as codeを実現し、docker imageを作り出す。更にdocker imageを鋳型にすることでdocker containerの構築を楽にする
    また、docker fileをgit連携することで、変更箇所が明確にすることが可能

  3. Docker Hubによって作成したdocker imageを共有することが可能で、公式や開発者が構築済みのimageを簡単に自分の環境でcontainer化することができる

dockerまわりの便利なやつら

巷でいうdockerは、いわゆるdocker engineというクジラさんがキャラクタなものを指しています。 クジラさんは前述したような特徴がある主人公みたいなやつで、さらにクジラさんを助けてくれる愉快な仲間たちがコンポーネントと呼ばれます。

愉快な仲間たち

  1. docker kitematic(エイさん)
    • エイさんはGUIツールを司ります
    • docker engineはCUIなのでグラフィカルにいじりたい人はエイさんの力を借りることになる
  2. docker registry(カイさん)
    • カイさんは作成したimageをみんなで共有する仲良しさんです
  3. docker compose(タコさん)
    • タコさんは沢山ある足を活かして、多数のcontainerを一元管理する仕事ができるマンです
  4. docker machine(クルマさん)
    • クルマさんはクラウド環境にdockerの実行環境をつくる働き者です。クジラさんが生活できる場を作ってくれます
  5. docker swarm(コクジラさん)3
    • docker engineのクジラさんは各マシンに生息して各自が自由に生活をしています。これを統括する役割がコクジラさんです。

まとめ

今回はdockerについて概説しました。 かなり荒っぽい説明になってしまいましたが、やはり各機能は触ってみないと分かりにくいと思います。 本記事も適宜、図解にして補足を充実させていこうと考えています。 また不定期更新になりますがdockerについても次回以降はハンズオン形式にしてみようと思います。

参考文献

統計的因果探索について #3:構造方程式の導入

はじめに

前回の続きです。
mlengineer.hatenablog.com 前回は、観測した変数間に相関が見られても、観測できていない未観測共通原因というものによって擬似相関が発生する可能性について述べました。 今回は因果関係についてと、構造方程式といわれる考え方の導入について話します。

因果関係とはなんぞや

前回と同様にscikit-learnのboston toyデータセットで考えてみます。 なんだかんだ、bostonデータセットだとわかりにくい気もしますので、わかりやすいサイトを貼っておきます。 dataに入っている13種類の特徴量に対して、targetには各地域の住宅価格(中央値)が入っていました。 このdataの各特徴量をtargetに対する原因と考え、targetを結果と考えます。

An example of Causal relationship
An example of Causal relationship
こんなイメージです。 ???は未観測共通原因で、今のところdata(の一部でもいいですが)とtargetの両者に影響を与えているものです。 何でもいいですが、そのときの各市の市長さんが誰なのか?や諸外国とアメリカの経済関係性などなど、沢山考えることができます。 このように、何かが原因となり、何かが結果として現れる関係性を因果関係といいます。

bostonデータセットの13種類の変数の中でも、各変数は独立ではないでしょう。 前回見たように変数間のなかでもいくつかの相関が見られたので、特徴量は絡み合っている気がします。 そこで、前回あげたDIS、NOX、INDUSの3変数の関係を考えてみます。 このうちINDUSを未観測共通原因であるとみなしてみると、原因と結果は次のパターンがありそうです。

Three patterns Casual relation
Three patterns Casual relation

Pattern(1)はDISが原因で、NOXが結果の場合、 Pattern(2)はNOXが原因で、DISが結果の場合、 Pattern(3)はINDUSがDISとNOXの原因であり、NOXとDIS間には因果関係がない場合です。 しかし、本当にINDUSがDISとNOXの未観測共通原因と言えるのでしょうか? 各変数の因果関係の向き、ありなしというパターンを網羅していこうとすると、3変数間の関係だけで、27通り存在します。 bostonデータセットは13種類の変数があり、相関があるもの同士はさらに少なくはなりますが、それでもかなりの組み合わせ数になります。 しかもどの組み合わせであろうと、変数間に現れる相関値は変わりません。 それでは、どうやって本当の因果関係を探し出すのでしょうか。。

因果関係の大きさとはなんぞや

色々なパターンの組み合わせがありそうですが、どの組み合わせが正しくて、それは他の組み合わせとどうやって比べればいいのでしょうか?

因果推論の根本問題(fundamental problem of causal inference)

bostonデータセットのうち1サンプル、つまり1つの地域(街)を考えます。 これをひとまずA街と名付けます。 ボストンはかなり昔から雇用施設があったとして、それはA街ができる前からであるとします。 つまり、A街をつくればDISが決まります。 しかし、A街の特徴のうちDISを変えたいとします。 もしA街を別の場所にまるごと移動することができたとして、DISを変えることができたなら、NOXはどう変化するでしょうか? DISとNOXは正の相関があったので、DISを小さくできたらならばNOXは小さくなりそうです。 逆にNOXを小さくしたらDISは小さくなるのでしょうか? しかし今A街はDISという距離にあるわけで、NOXを動かしても今さらDISの変化を確認することは事実上不可能ですが、 実は因果関係の向きは、このように2変数のうち片方aを動かす(介入:interventionという)ことで、 もう片方bも変化したらばaはbの原因といえ、bはaの結果と言えるため、その方向がわかります。 このように実際起きていることと逆の状況を考えて、比較するという考えを反事実モデルと呼びます。

でもやはり、DISはA街を今の位置から動かすことができなく、今観測できる情報はA街がDISにあるときのA街のNOXだけです。 もう街の位置は決めてしまったもので、戻せませんので、観測できるデータは因果関係の1側面だけです。 このような問題を因果推論の根本問題という驚きの呼び方をするようです。

A街だけ考えても因果関係の向きはわからない、ということです。 このように1サンプルだけで考えていることを、個体レベルの因果関係といいます。

さて、1サンプルでは因果関係の根本問題によって、その因果関係わかりませんので、 他の街を情報を使って考えてみたくなります。 これを集団レベルの因果と呼びます。

たくさんの街のDISによって、各街のNOXの様子をみるのです。 このときDISが小さい場合のNOXと、DISが大きい場合のNOXのように分けて比べることで、 因果関係がわかりそうです。 しかし、もしたくさんの街が同じDISに位置するのであれば、個体レベルの因果関係と同じように 因果関係の根本問題にハマります。

構造方程式(structural equation)

集団の因果関係を考えて、原因によって結果が生じているとすれば、どれくらい原因が結果に寄与しているのか?とその大きさを測りたくなってきます。 そこで構造方程式といわれるモデルを構築して、各変数に対する介入を、条件付き確率で表現していくイメージです。 細かい話はまた次回にしますが、構造方程式とは次のようなものです。

An example of structural equation
An example of structural equation

変数 xは、撹乱変数(または誤差変数)と呼ぶe_xによって定められているとします。 また x yの原因であるとして、 yにも e_yという撹乱変数の影響があるとします。 これをまた未観測共通原因はないとして、構造方程式で表したものが上図の右側に記載した式となります。 左側にある書き方は因果グラフといいます。  y x e_yの関係が線形であるならば、  f_yは例えば、 y=x+e_yとも書くことができます。 実はこの図では、 e_x e_yは独立であることを仮定していて、 従属関係にある場合は e_x e_yを矢印でつなぐことになります。 目的変数である変数 x yは内生変数(endogenous variable)といい、 説明変数である e_x e_yは外生変数(exogenous variable)とも呼ばれ、 内生変数は外生変数が絡み合い、影響を与えることで生成されています。

先ほどのDIS、NOX、INDUSを因果グラフと構造方程式で記載してみると次のようなものになります。

An example of structural causal model
An example of structural causal model
ここでは未観測共通原因をINDUSとしています。 このような構造方程式を立てて、複数の因果関係の妥当性を測っていくことになるわけです。 文献1の著者である清水先生がわかりやすい資料を公表2していますので、こちらも非常に参考になるかと思います。

まとめ

今回は、因果関係を探るためには複数の可能性があることを考え、 構造方程式という表現方法について触れました。 次回はbostonのデータセットに対して実際に因果関係を探ってみたいと思います。

文献

統計的因果探索について #2:擬似相関を見てみる

目次

はじめに

前回の記事の続きです。 mlengineer.hatenablog.com 以下の記事は、自分なりのメモとしてまとめることを第一にしているので、わかりにくい点や間違いがあるかもしれません。 参考文献はMLPシリーズの「統計的因果探索」1で、この中のChapter1の内容を自分なりの解釈でまとめたものです。

擬似相関

前回は、Kaggleでも利用されているtitanicのデータセットを利用して、変数間の相関を見てみました。 本当はその中で擬似相関といわれる現象が見えることを期待していたのですが、無相関に近い結果となってしまったので、今回は異なるデータセットを利用します。 よく利用されているボストン郊外の地域別の住宅価格についてのtoyデータセットがscikit-learnで利用できるので、これにします。 一先ずはこのデータセットをロードします。

from sklearn.datasets import load_boston
boston = load_boston()
print(boston.__dir__())  # show the keys of boston

boston has 5 keys
boston has 5 keys
ロードしたbostonは5つのkeyがあり、以下のようなデータです。

key type description
data numpy.ndarray 地域ごとの特徴量
target numpy.ndarray 地域ごとの住宅価格(1,000 ドル単位で所有住宅価格の中央値)
feature_names numpy.ndarray 特徴量の名前
DESCR str データセットの説明書
filename str bostonデータセットcsv)の場所

filenameはpprintすると、sklearn/datasets/data/boston_house_prices.csvといった保存場所を教えてくれます。 data.shapeは(506, 13)、target.shapeは(506,)です。 DESCRをpprintすると細かい説明が確認できます。 bostonデータセットには、506行のサンプル(地域)、13列の特徴量が含まれています。

pd.DataFrame(boston.data, columns=boston.feature_names).head()

top 5 data of boston dataset
top 5 data of boston dataset
それぞれの特徴量は次の表のとおりです。2

Attribute description
CRIM 人口 1 人当たりの犯罪発生数
ZN 25,000平方フィート以上の住居区画の占める割合
INDUS 小売業以外の商業が占める面積の割合
CHAS チャールズ川によるダミー変数 (1: 川の周辺, 0: それ以外)
NOX NOx の濃度
RM 住居の平均部屋数
AGE 1940 年より前に建てられた物件の割合
DIS 5 つのボストン市の雇用施設からの距離 (重み付け済)
RAD 環状高速道路へのアクセスしやすさ
TAX $10,000 ドルあたりの不動産税率の総計
PTRATIO 町毎の児童と教師の比率
B 町毎の黒人 (Bk) の比率を次の式で表したもの。 1000(Bk – 0.63)2
LSTAT 給与の低い職業に従事する人口の割合 (%)

各特徴量は変数間が絡み合って、最終的には住宅価格に影響を与えていると考えられます。 とにかく、2変数間に相関があるのか確認するためにプロットしてみます。 こちらのソースコードをお借りしています3

Pair plot between two variables of boston dataset
Pair plot between two variables of boston dataset
この結果からNOXとDIS(雇用施設からの距離)は-0.769なので、距離が離れるほどNOXが低減するようです。 やはり雇用施設が集まっていると交通量の影響などによってNOXの多くなることが考えられます。
加えてNOXとINDUSが0.764と正の相関があり、小売以外の商業施設が多いとNOXが増加するようです。 さらに、INDUSとTAXは0.721と正の相関があり、小売以外の商業施設が多いと不動産税率の総計が高くなります。 これは小売以外の商業施設が多いと、中心街に近く不動産税率の総計が高くなる、と考えることもできます。
でも待ってください。 本当に相関から上記の考えが妥当であると言えるのでしょうか? AGEとINDUSの間には0.645と正の相関がありますが、古い建物が多いほど小売以外の商業施設が多いというのは意味がわかりません。 さらにAGEとNOX間には0.731と正の相関がありますが、これも古い建物が多いほどNOXが増えるということになります。

Hidden Cause and Relation between two observed variables
Hidden Cause and Relation between two observed variables

そこで、例えばAGEとINDUSとは別に観測できていない変数 Resident Age(居住者の年齢)があったとします。 居住者の年齢が高ければ、長く住んでいるため建物も古い可能性があります。 そして居住者の年齢が高ければ、小売よりもマッサージやさんや娯楽施設といった商業施設が多くなりそうです。 このように、観測できていないが実は各変数に影響を及ぼすことで、観測されているAGEとINDUSに相関が出てしまった状況がありえます。 このような隠れている変数であり観測できている複数の変数に対して影響を及ぼす変数を、未観測共通原因(hidden common cause)と言い、 未観測共通原因を通じて直接は関係がない変数間で相関がでることを擬似相関(spurious correlation)といいます。

つまり、相関を鵜呑みにしてはいけません。
そこで物事の因果関係を深く探っていくことが重要になり、 因果探索というテーマにつながっていきます。

まとめ

今回はようやく擬似相関について触れることができました。 観測変数間に相関がみられても、未観測共通原因の影響を受けている可能性があることがわかりました。 データ分析において、自分がどういったバイアスをもってデータを眺めてしまっているのか常に注意が必要です。 次回は因果とはなんぞや?という話からできればと考えています。

参考文献

統計的因果探索について #1:相関をみてみる

はじめに

データ分析を行うとき、対象データをグラフに書きまくる、さらに変数間の相関をみることはとても当たり前に行うと思います。 でも、相関が高いからといって軽々と変数間が影響しあっていると考えることは時期尚早です。 これはデータを生成している事象の因果関係をしっかり捉えないと間違えてしまうので、意外に注意が必要なわけです。 以下の記事は、自分なりのメモとしてまとめることを第一にしているので、わかりにくい点や間違いがあるかもしれません。 メインの参考文献はMLPシリーズの「統計的因果探索」1です。 まずは基本中の基本である、データの相関について手を動かしながら見ていきます。

Hands on

相関

相関 R(x_1,x_2)とはあるデータに対して、一つの特徴量を説明変数 x_1、また他の特徴量を目的変数 x_2としたとき、 これらの変数どうしが互いに関わり合うように変化しているのか?を調べるものです。  R(x_1,x_2)\lt0として正の相関があれば、 x_1が増加すると x_2も増加する傾向がある。 逆に R(x_1,x_2)\gt0として負の相関があれば、 x_1が増加すると x_2は減少する傾向があることになります。 しかし、相関があるからといって、互いの変数が直接影響しあっているといえるわけではなく、 データ分析で簡単に求められる値ですが奥が深いものです。

データセットの特徴をつかむ

以下では例を示しながら進めたほうが楽しいので、pythonのseabornを利用することで簡単に利用できるtitanicのデータを見ながらすすめることにします。 kaggle2でも利用されているので、seabornを利用しなくても入手できるものです。

import seaborn as sns

dataset_name = sns.get_dataset_names()
dataset = sns.load_dataset(dataset_name[-1])

titanicデータセットを覗いてみると次のようなカラムから構成されています。 どうやら意味が類似するカラムがあるためか、kaggleのcsvとは少しだけ異なるみたいです。

dataset.head()

titanic dataset
top 5 data of titanic dataset
各カラムの意味は次の通りで、typeはdataset.ftypesで参照したもの。 columnに取り消し線を引いているものは今回の解析では使わないため、delして扱います。

column type description
survived int64 生存者であるか
pclass int64 チケットのクラス
sex object 性別
age float64 年齢
sibsp int64 同乗した兄弟/配偶者の数
parch int64 同乗した親/子供の数
fare float64 運賃
embarked object 出港した港の名前
class category チケットのクラス、Fisrt/Second/Third
who object sexがmaleならman, femaleならwoman
adult_male bool sexがmaleならTrueだと思う
deck category 客室番号?A~Gまである
embark_town object 出港した街、{nan, 'Southampton', 'Queenstown', 'Cherbourg'}のいずれか
alive object 生きているかどうか
alone bool ぼっちかどうか、sibspが0かつparchが0ならばTrue

このデータをいくつかグラフ化してみます。 ただし注意が必要な点は現実データと同様にtitanicデータにも欠損値が存在しており、ageなどが抜けているところがあります。 欠損値を補完する方法を考え始めると一大テーマになってしまうので、pandas.DataFrame.dropna()メソッドを使いNaN(欠損値)が含まれるデータは捨ててしまうことにしました。 なぜ注意が必要なのかというと、一部のカラムが欠損しているかといってそのサンプルを捨てると、データセットの傾向を歪ませる可能性があるからです。 例えば、欠損値を含むデータセットのまま基本統計量を見ると、

dataset.describe()

Statistical Variables
Statistical Variables
となりますが、欠損値を含むサンプルを捨ててしまうことにすると、

dataset.dropna().describe()

Statistical Variables without NaN
Statistical Variables without NaN
となります。 見比べると明らかですが、データセットの全体のサンプル数は、891から182に激減してしまい、 平均Ageは29.699118から35.623187へ、 平均Fareは32.204208から78.919735と倍以上の値になってしまいました。 Fareの標準偏差1 {\sigma}は49.693429から76.490774となり、幅広く分布している可能性がわかります。

年齢Ageが上がれば稼ぎも増える気がするので運賃Fareも高くなるかも?という仮説を立てて、この2変数間の関係を見てることにしました。 欠損値をdropしてからAgeとFareのプロットを行い、Sexによって色分けをおこなってみます。 sns.pairplot()で行うと縦軸の表記がおかしかったため、それぞれを別個に作図しました。

dataset_wona = dataset.dropna()

g = sns.FacetGrid(pd.DataFrame([dataset_wona.age, dataset_wona.sex]).T, hue='sex')
g = g.map(sns.distplot, 'age')
plt.legend()
plt.show()
g = sns.FacetGrid(pd.DataFrame([dataset_wona.fare, dataset_wona.sex]).T, hue='sex')
g = g.map(sns.distplot, 'fare')
plt.legend()
plt.show()

Histogram of Fare, colorized by SexHistogram of Age, colorized by Sex
Histogram of Fare(left) and Age(right), colorized by Sex

sns.scatterplot(x=dataset_wona.fare, y=dataset_wona.age, hue=dataset_wona.sex)
plt.show()

Scatter plot between Fare and Age, colorized by Sex
Scatter plot between Fare and Age, colorized by Sex

欠損を含むサンプルを除去したデータであること念頭にいれつつ結果を眺めると、 性別のヒストグラムからほぼ同等の比率でタイタニック号に乗船していたことがわかります。 若干Femaleのほうが若いことから当時の外国人男性は自身の年齢よりも年下の女性を好んでいたのかもしれません。 一方で、Fareのヒストグラムからは一部に富裕層の影が見て取れますが、多くの乗客は同等の運賃を払っているようです。 ただし運賃だけでなく出港地も鑑みないと、これも富裕層であるのか、はたまた他の人より長距離乗船しているだけなのかわかりません。 データ分析をおこなう上では、非観測変数の存在を考えて、あらゆる可能性を踏まえておくことが重要です。 さて、AgeとFareの散布図を見てみるとこの2変数のデータに相関はないように見えます。 実際に、AgeとFareの相関値は-0.0907と非常に相関値が低く、ほぼ無相関と言えます。

dataset_wona.corr()['fare']['age']

見るからに相関もないので無駄かもしれませんが、説明変数をFare、目的変数をAgeとして線形回帰モデルをフィットさせて、回帰係数と決定係数の様子を確認します。 回帰係数は線形回帰を行うときの傾きであり、これは説明変数の重みを意味することになります。 決定係数は予測式(ここでは線形回帰)がどれだけデータにうまくフィットしているのかを示し、大きいほど当てはまりがよく、小さいほど悪い。 なお決定係数と相関係数の関係はこちらのサイト3にわかりやすくまとめられています。

from sklearn import linear_model
clf = linear_model.LinearRegression(normalize=True)
 
x1 = dataset_wona.fare.values.reshape(-1, 1)
x2 = dataset_wona.age.values.reshape(-1, 1)

clf.fit(x1, x2)

print(clf.coef_)  # 回帰係数
print(clf.score(x1, x2))  # 決定係数

A Result of Linear Regression
A Result of Linear Regression

回帰係数は-0.01858906、決定係数は0.00823となりました。 つまり説明変数としたFareはあまりAgeを説明できているとはいえず、 線形回帰モデルも適していないことが言えます。 検定を行うことで有意に相関がないことを示す必要もありますが、思いの外長くなってしまったので、 今回は"年齢Ageが上がれば稼ぎも増える気がするので運賃Fareも高くなるかも?という仮説"は誤っていそう、というところで止めにしておきます。 しかし、例えばですが、年齢が上がると、稼ぎも増えるかもしれませんが、遠出することが億劫になり運賃は大して上がらないかもしれません。 次回は多変数間の因果関係にまで踏み込んで見たいと思います。

まとめ

今回はtitanicデータセットを使い簡単なデータ分析を行いました。 相関はかんたんな値ですが、事象の因果関係や非観測変数の存在を加味しながら眺めなければいけません。 次回は、擬似相関や多変数間の因果関係の話を考えてみます。

参考文献