TensorFlow再入門 ~単層パーセプトロン~

初めに

TensorFlow再入門シリーズ第3回です。今回は世界で最も有名なデータセットであるMNISTを使い、単層パーセプトロンで画像認識を実行します。

TensorFlowで単層パーセプトロンをする際の要素

これまで利用した関数などを用いることでほぼ実装可能ですが、一部新しい関数も利用しています。

ミニバッチ学習

大規模なデータを扱って学習をする場合、訓練データからランダムにデータを取り出して計算し、その平均誤差を利用することで、効率的に学習を進めることができます。その際の取り出すデータサイズをミニバッチサイズと言います。

今回はtensorflow.examples.tutorials.mnist.input_dataに組み込まれたread_data_sets関数を利用します。

read_data_sets(train_dir,
                   fake_data=False,
                   one_hot=False,
                   dtype=dtypes.float32,
                   reshape=True,
                   validation_size=5000
)

ソースコードはこちらになります

評価関数

今回はMNISTのデータセットに含まれる0~9の10種類の数字から、同じ数字を推測できているかどうかで評価を行っています。

推測の出力結果は0~9のどの数字に近いかが確率として表示されています。 まずtf.argmaxを用いて、そのリストの中で最も大きな値となるラベル(=正解と予測されたラベル)を取り出し、実際のラベルと比較します。比較の際にはtf.equalという関数を利用します。2つの引数を比べ、True/Falseのboolで返します。

次に返ってきたboolを数値に変換し、評価に利用します。 その際にtf.castという関数を利用します。これはデータの型(dtype)を別のものに変更する関数です。

# 使い方
tf.math.argmax(
    input,
    axis=None,
    name=None,
    dimension=None,
    output_type=tf.dtypes.int64
)

tf.math.equal(
    x,
    y,
    name=None
)

tf.cast(
    x,
    dtype,
    name=None
)

公式ページは以下になります。

tf.argmax

tf.equal

tf.cast

実装

実際のコード

まずは実際のコードを載せます。

# ライブラリのインポート
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# データセット
mnist = input_data.read_data_sets("data", one_hot=True)

train_x, train_y = mnist.train.next_batch(100)
test_x = mnist.test.images
test_y = mnist.test.labels

# ハイパーパラメータ
num_epochs = 10000
learning_rate = 0.01

# 計算グラフ
X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])
 
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

out = tf.nn.softmax(tf.matmul(X, W) + b)

## 損失関数
loss = tf.losses.softmax_cross_entropy(Y, out)

## 最適化関数
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

## 評価
correct = tf.equal(tf.argmax(out, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

# 変数の初期化
init = tf.global_variables_initializer()

# 計算処理
with tf.Session() as sess:
  sess.run(init)

  for epoch in range(num_epochs):
    sess.run(train_step, feed_dict={X: train_x, Y: train_y})
    if epoch != 0 and epoch % 1000 == 0:
      train_loss, accuracy_value = sess.run(loss, feed_dict={X: train_x, Y: train_y}), \
                                   accuracy.eval(feed_dict={X: train_x, Y: train_y})
      print("epoch: ", epoch, " train_loss: ", train_loss, " accuracy_value: ", accuracy_value)
      
tf.reset_default_graph()

0. ライブラリのインポート

TensorFlowに組み込まれたMNISTデータセットを呼び出します。

# ライブラリのインポート
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

ソースコードはこちらです

1. データセットの読み込み

データセットをミニバッチ化し、訓練データとテストデータに分けますensorFlowの提供するMNISTデータセットでは、初めからtrain、testに分かれているので、それぞれ別の変数に入れます。

# データセット
mnist = input_data.read_data_sets("data", one_hot=True)

train_x, train_y = mnist.train.next_batch(100)
test_x = mnist.test.images
test_y = mnist.test.labels

2. データフローグラフの構築

データセットの入れ物となるX Y、変数となるW b、出力結果を格納するoutなどを指定します。

また損失関数・最適化関数に加え、上述した評価関数も指定します。

# 計算グラフ
X = tf.placeholder(tf.float32, [None, 784])
Y = tf.placeholder(tf.float32, [None, 10])
 
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

out = tf.nn.softmax(tf.matmul(X, W) + b)

## 損失関数
loss = tf.losses.softmax_cross_entropy(Y, out)

## 最適化関数
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

## 評価
correct = tf.equal(tf.argmax(out, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

3. 各変数の初期化

変数を含むので初期化をします。

# 変数の初期化
init = tf.global_variables_initializer()

4. 各関数の実行(実行フェーズ)

これまでと同様、セッションを開始してtf.Session.runで計算を実行します。

# 計算処理
with tf.Session() as sess:
  sess.run(init)

  for epoch in range(num_epochs):
    sess.run(train_step, feed_dict={X: train_x, Y: train_y})

5. 計算結果の確認・評価

実行結果を確認します。ここでは損失関数の値と評価関数の値を見ています。

    if epoch != 0 and epoch % 1000 == 0:
      train_loss, accuracy_value = sess.run(loss, feed_dict={X: train_x, Y: train_y}), \
                                   accuracy.eval(feed_dict={X: train_x, Y: train_y})
      print("epoch: ", epoch, " train_loss: ", train_loss, " accuracy_value: ", accuracy_value)

6. 計算グラフのリセット

最後に計算グラフのリセットを行います。

tf.reset_default_graph()

計算結果

実行結果を載せます。ハイパーパラメーターを変更すれば、より精度は向上します。

epoch:  1000  train_loss:  1.8928964  accuracy_value:  0.72
epoch:  2000  train_loss:  1.7550746  accuracy_value:  0.8
epoch:  3000  train_loss:  1.6716672  accuracy_value:  0.89
epoch:  4000  train_loss:  1.6261232  accuracy_value:  0.91
epoch:  5000  train_loss:  1.6011841  accuracy_value:  0.92
epoch:  6000  train_loss:  1.5857533  accuracy_value:  0.92
epoch:  7000  train_loss:  1.5758034  accuracy_value:  0.92
epoch:  8000  train_loss:  1.5691535  accuracy_value:  0.92
epoch:  9000  train_loss:  1.5644498  accuracy_value:  0.92

その他

ミニバッチを使わない場合

ミニバッチ化せずに実行した場合も載せます。実行時間はミニバッチと比べてかなりかかります(30~40分程度)。

# コード変更箇所

#train_x, train_y = mnist.train.next_batch(100)
train_x = mnist.train.images
train_y = mnist.train.labels
test_x = mnist.test.images
test_y = mnist.test.labels

# 実行結果

epoch:  1000  train_loss:  2.03642  accuracy_value:  0.59145457
epoch:  2000  train_loss:  1.8735378  accuracy_value:  0.73687273
epoch:  3000  train_loss:  1.8022996  accuracy_value:  0.78023636
epoch:  4000  train_loss:  1.7664666  accuracy_value:  0.7906909
epoch:  5000  train_loss:  1.7446582  accuracy_value:  0.7978909
epoch:  6000  train_loss:  1.7297618  accuracy_value:  0.8022182
epoch:  7000  train_loss:  1.7188071  accuracy_value:  0.80572724
epoch:  8000  train_loss:  1.7103248  accuracy_value:  0.8087636
epoch:  9000  train_loss:  1.7034981  accuracy_value:  0.8113091

公式ページにあるチュートリアルコード

TensorFlowはバージョンが上がるにつれ、TensorFlow単体での利用からKerasの利用を推奨しているようです。チュートリアルの初めのページでは、kerasを用いてとてもコンパクトにMNISTの学習を実行しています。

# チュートリアルのコード
import tensorflow as tf
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

# 結果
Epoch 1/5
60000/60000 [==============================] - 14s 234us/sample - loss: 0.2200 - acc: 0.9348
Epoch 2/5
60000/60000 [==============================] - 14s 230us/sample - loss: 0.0972 - acc: 0.9699
Epoch 3/5
60000/60000 [==============================] - 13s 224us/sample - loss: 0.0692 - acc: 0.9782
Epoch 4/5
60000/60000 [==============================] - 13s 220us/sample - loss: 0.0537 - acc: 0.9832
Epoch 5/5
60000/60000 [==============================] - 13s 224us/sample - loss: 0.0418 - acc: 0.9862
10000/10000 [==============================] - 1s 57us/sample - loss: 0.0634 - acc: 0.9818
[0.06343337026094087, 0.9818]

参考リンク

Tensorflow run() vs eval() と InteractiveSession() vs Session()

TensorFlowのMNISTはどうなってるのか?

CentOS7にk3sを立てる ~nginxをデプロイするまで~

はじめに

2019年2月26日、Rancher社からKubernetesの軽量版であるk3sが発表されました

f:id:FY0323:20190227232403j:plain

詳細についてはこちらの記事をご確認ください。

すでにラズパイ上に構築した方もいらっしゃるようですが、ひとまずCentOS7上にserver、agentを構築し、nginxをデプロイしてcurlが返ってくるまでをやってみました。

構築環境

今回はserver、agentそれぞれ1台ずつ構築しました。

Server構築手順

構築手順は公式ページにあるようにやっただけです。以下のコマンドを実行することでインストール・起動までが完了します。

curl -sfL https://get.k3s.io | sh -

以下のようにログが表示されます。

[root@ip-10-0-0-160 ~]# curl -sfL https://get.k3s.io | sh -
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  Downloading https://github.com/rancher/k3s/releases/download/v0.1.0/sha256sum-amd64.txt
[INFO]  Downloading https://github.com/rancher/k3s/releases/download/v0.1.0/k3s
[INFO]  Verifying download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  systemd: Creating /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink from /etc/systemd/system/multi-user.target.wants/k3s.service to /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s
[root@ip-10-0-0-160 ~]# 

動作確認をしてみます。

# サービスの起動状態確認
[root@ip-10-0-0-160 ~]# systemctl status -l k3s
● k3s.service - Lightweight Kubernetes
   Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2019-02-27 13:11:01 UTC; 39s ago
     Docs: https://k3s.io
  Process: 26706 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
  Process: 26692 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
 Main PID: 26712 (k3s-server)
    Tasks: 84
   Memory: 519.3M
   CGroup: /system.slice/k3s.service
           ├─26728 containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd
           ├─27047 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/87a6e2d1d8139324cc2f21c56dbb70b1dc6f1a39914f32277b59a8584ba688ab -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ├─27292 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/021bd92dd893fcac228603b4464d5bbce7ae48ec3b48b0354ae0def9eb24f29c -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ├─27862 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/a7bb126da2cfa0b8a0ef0b7f62a128465529d56793df2443d1592452df1765ef -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ├─28051 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/a80e0bd67827ef302f68a887a1254f217e134cf91470cb1b5495c39cab5e7e76 -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ├─28180 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/41e0cda9075dd80b9f66beb6920e1f9ed2c1f6d94a87e59ce2b5c4050a54d350 -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ├─28307 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/f1235ab531ee41c4048e0f8caebf205163e57c826de04a5376c9051e252844e7 -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           └─28488 containerd-shim -namespace k8s.io -workdir /var/lib/rancher/k3s/agent/containerd/io.containerd.runtime.v1.linux/k8s.io/d543d67fe59ebbf048cb595cd89d1dadb0d0e919e2ecc4b9b5b627675c0a52c8 -address /run/k3s/containerd/containerd.sock -containerd-binary /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin/containerd
           ‣ 26712 /usr/local/bin/k3s server

Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:09.646094506Z" level=info msg="Connecting to proxy" url="wss://localhost:6443/v1-k3s/connect"
Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:09.650274810Z" level=info msg="Handling backend connection request [ip-10-0-0-160.us-west-2.compute.internal]"
Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:09.651270063Z" level=warning msg="Disabling CPU quotas due to missing cpu.cfs_period_us"
Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:09.651314991Z" level=info msg="Running kubelet --healthz-bind-address 127.0.0.1 --read-only-port 0 --allow-privileged=true --cluster-domain cluster.local --kubeconfig /var/lib/rancher/k3s/agent/kubeconfig.yaml --eviction-hard imagefs.available<5%,nodefs.available<5% --eviction-minimum-reclaim imagefs.available=10%,nodefs.available=10% --fail-swap-on=false --cgroup-driver cgroupfs --root-dir /var/lib/rancher/k3s/agent/kubelet --cert-dir /var/lib/rancher/k3s/agent/kubelet/pki --seccomp-profile-root /var/lib/rancher/k3s/agent/kubelet/seccomp --cni-conf-dir /var/lib/rancher/k3s/agent/etc/cni/net.d --cni-bin-dir /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin --cluster-dns 10.43.0.10 --container-runtime remote --container-runtime-endpoint unix:///run/k3s/containerd/containerd.sock --address 127.0.0.1 --anonymous-auth=false --client-ca-file /var/lib/rancher/k3s/agent/client-ca.pem --hostname-override ip-10-0-0-160.us-west-2.compute.internal --cpu-cfs-quota=false"
Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: Flag --allow-privileged has been deprecated, will be removed in a future version
Feb 27 13:11:09 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:09.670323459Z" level=info msg="waiting for node ip-10-0-0-160.us-west-2.compute.internal: nodes \"ip-10-0-0-160.us-west-2.compute.internal\" not found"
Feb 27 13:11:11 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:11.672082577Z" level=info msg="waiting for node ip-10-0-0-160.us-west-2.compute.internal CIDR not assigned yet"
Feb 27 13:11:13 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:13.673690247Z" level=info msg="waiting for node ip-10-0-0-160.us-west-2.compute.internal CIDR not assigned yet"
Feb 27 13:11:15 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:15.675320853Z" level=info msg="waiting for node ip-10-0-0-160.us-west-2.compute.internal CIDR not assigned yet"
Feb 27 13:11:17 ip-10-0-0-160.us-west-2.compute.internal k3s[26712]: time="2019-02-27T13:11:17.676902518Z" level=info msg="waiting for node ip-10-0-0-160.us-west-2.compute.internal CIDR not assigned yet"
[root@ip-10-0-0-160 ~]# 


# kubectlコマンド確認
[root@ip-10-0-0-160 ~]#  k3s kubectl get node
NAME                                          STATUS   ROLES    AGE   VERSION
ip-10-0-0-160.us-west-2.compute.internal   Ready    <none>   27s   v1.13.3-k3s.6
[root@ip-10-0-0-160 ~]# 

Agent構築手順

続いてAgentを構築します。

[root@ip-10-0-0-180 ~]# wget https://github.com/rancher/k3s/releases/download/v0.1.0/k3s
[root@ip-10-0-0-180 ~]# chmod +x k3s 
[root@ip-10-0-0-180 ~]# mv ./k3s /usr/bin/k3s

# k3sコマンドが使えるか確認
[root@ip-10-0-0-180 ~]# k3s -h
NAME:
   k3s - Kubernetes, but small and simple

USAGE:
   k3s [global options] command [command options] [arguments...]

VERSION:
   dev (HEAD)

COMMANDS:
     server   Run management server
     agent    Run node agent
     kubectl  Run kubectl
     crictl   Run crictl
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug        Turn on debug logs
   --help, -h     show help
   --version, -v  print the version
[root@ip-10-0-0-180 ~]#

次にk3s agentコマンドを使ってAgentサーバを構築します。コマンド実行時のtokenは、Serverの/var/lib/rancher/k3s/server/node-tokenに書かれた値を使用します。

[root@ip-10-0-0-180 ~]# k3s agent --server https://10.0.0.160:6443 --token K10cb0d10675f99ff349678412de856285fdd3c6e183422510954ea9c1a0d8f0b96::node:cf80a0b3438f4d85fa31044e5b284652

INFO[2019-02-27T13:21:42.137288318Z] Starting k3s agent v0.1.0 (91251aa)          
INFO[2019-02-27T13:21:42.440462109Z] Logging containerd to /var/lib/rancher/k3s/agent/containerd/containerd.log 
INFO[2019-02-27T13:21:42.440595159Z] Running containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s/agent/containerd 
INFO[2019-02-27T13:21:42.441121820Z] Waiting for containerd startup: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial unix /run/k3s/containerd/containerd.sock: connect: no such file or directory" 
WARN[2019-02-27T13:21:43.445500783Z] failed to write value 1 at /proc/sys/net/bridge/bridge-nf-call-iptables: open /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory 
INFO[2019-02-27T13:21:43.447338345Z] Connecting to wss://10.0.0.160:6443/v1-k3s/connect 
INFO[2019-02-27T13:21:43.447375378Z] Connecting to proxy                           url="wss://10.0.0.160:6443/v1-k3s/connect"
WARN[2019-02-27T13:21:43.453824555Z] Disabling CPU quotas due to missing cpu.cfs_period_us 
INFO[2019-02-27T13:21:43.453962443Z] Running kubelet --healthz-bind-address 127.0.0.1 --read-only-port 0 --allow-privileged=true --cluster-domain cluster.local --kubeconfig /var/lib/rancher/k3s/agent/kubeconfig.yaml --eviction-hard imagefs.available<5%,nodefs.available<5% --eviction-minimum-reclaim imagefs.available=10%,nodefs.available=10% --fail-swap-on=false --cgroup-driver cgroupfs --root-dir /var/lib/rancher/k3s/agent/kubelet --cert-dir /var/lib/rancher/k3s/agent/kubelet/pki --seccomp-profile-root /var/lib/rancher/k3s/agent/kubelet/seccomp --cni-conf-dir /var/lib/rancher/k3s/agent/etc/cni/net.d --cni-bin-dir /var/lib/rancher/k3s/data/4df430e1473d0225734948e562863c82f20d658ed9c420c77e168aec42eccdb5/bin --cluster-dns 10.43.0.10 --container-runtime remote --container-runtime-endpoint unix:///run/k3s/containerd/containerd.sock --address 127.0.0.1 --anonymous-auth=false --client-ca-file /var/lib/rancher/k3s/agent/client-ca.pem --hostname-override ip-10-0-0-180.us-west-2.compute.internal --cpu-cfs-quota=false 
Flag --allow-privileged has been deprecated, will be removed in a future version
W0227 13:21:43.454968   26662 server.go:194] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
W0227 13:21:43.463453   26662 proxier.go:493] Failed to load kernel module ip_vs with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0227 13:21:43.465574   26662 proxier.go:493] Failed to load kernel module ip_vs_rr with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0227 13:21:43.467630   26662 proxier.go:493] Failed to load kernel module ip_vs_wrr with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0227 13:21:43.469688   26662 proxier.go:493] Failed to load kernel module ip_vs_sh with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
W0227 13:21:43.471776   26662 proxier.go:493] Failed to load kernel module nf_conntrack_ipv4 with modprobe. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules
INFO[2019-02-27T13:21:43.488293741Z] waiting for node ip-10-0-0-180.us-west-2.compute.internal: nodes "ip-10-0-0-180.us-west-2.compute.internal" not found 
W0227 13:21:43.488676   26662 node.go:103] Failed to retrieve node info: nodes "ip-10-0-0-180.us-west-2.compute.internal" not found
I0227 13:21:43.488693   26662 server_others.go:148] Using iptables Proxier.
W0227 13:21:43.488787   26662 proxier.go:314] invalid nodeIP, initializing kube-proxy with 127.0.0.1 as nodeIP
I0227 13:21:43.488849   26662 server_others.go:178] Tearing down inactive rules.
E0227 13:21:43.560292   26662 proxier.go:232] Error removing userspace rule: error checking rule: exit status 2: iptables v1.6.2: Couldn't find target `KUBE-PORTALS-HOST'

Try `iptables -h' or 'iptables --help' for more information.
E0227 13:21:43.562894   26662 proxier.go:238] Error removing userspace rule: error checking rule: exit status 2: iptables v1.6.2: Couldn't find target `KUBE-PORTALS-CONTAINER'

Try `iptables -h' or 'iptables --help' for more information.
E0227 13:21:43.570220   26662 proxier.go:246] Error removing userspace rule: error checking rule: exit status 2: iptables v1.6.2: Couldn't find target `KUBE-NODEPORT-HOST'

Try `iptables -h' or 'iptables --help' for more information.
E0227 13:21:43.572657   26662 proxier.go:252] Error removing userspace rule: error checking rule: exit status 2: iptables v1.6.2: Couldn't find target `KUBE-NODEPORT-CONTAINER'

Try `iptables -h' or 'iptables --help' for more information.
E0227 13:21:43.577197   26662 proxier.go:259] Error removing userspace rule: error checking rule: exit status 2: iptables v1.6.2: Couldn't find target `KUBE-NODEPORT-NON-LOCAL'

Try `iptables -h' or 'iptables --help' for more information.
I0227 13:21:43.587024   26662 server.go:464] Version: v1.13.3-k3s.6
I0227 13:21:43.597345   26662 conntrack.go:103] Set sysctl 'net/netfilter/nf_conntrack_max' to 131072
I0227 13:21:43.597376   26662 conntrack.go:52] Setting nf_conntrack_max to 131072
I0227 13:21:43.613730   26662 conntrack.go:83] Setting conntrack hashsize to 32768
I0227 13:21:43.618286   26662 conntrack.go:103] Set sysctl 'net/netfilter/nf_conntrack_tcp_timeout_established' to 86400
I0227 13:21:43.618341   26662 conntrack.go:103] Set sysctl 'net/netfilter/nf_conntrack_tcp_timeout_close_wait' to 3600
I0227 13:21:43.618787   26662 config.go:202] Starting service config controller
I0227 13:21:43.618802   26662 controller_utils.go:1027] Waiting for caches to sync for service config controller
I0227 13:21:43.618822   26662 config.go:102] Starting endpoints config controller
I0227 13:21:43.618828   26662 controller_utils.go:1027] Waiting for caches to sync for endpoints config controller
I0227 13:21:43.718998   26662 controller_utils.go:1034] Caches are synced for endpoints config controller
I0227 13:21:43.719093   26662 controller_utils.go:1034] Caches are synced for service config controller
E0227 13:21:43.765317   26662 proxier.go:1335] Failed to execute iptables-restore: exit status 2 (iptables-restore v1.6.2: Couldn't find target `KUBE-MARK-DROP'

Error occurred at line: 50
Try `iptables-restore -h' or 'iptables-restore --help' for more information.
)
I0227 13:21:44.111855   26662 server.go:393] Version: v1.13.3-k3s.6
I0227 13:21:44.116347   26662 server.go:630] --cgroups-per-qos enabled, but --cgroup-root was not specified.  defaulting to /
I0227 13:21:44.116556   26662 container_manager_linux.go:247] container manager verified user specified cgroup-root exists: []
I0227 13:21:44.116570   26662 container_manager_linux.go:252] Creating Container Manager object based on Node Config: {RuntimeCgroupsName: SystemCgroupsName: KubeletCgroupsName: ContainerRuntime:remote CgroupsPerQOS:true CgroupRoot:/ CgroupDriver:cgroupfs KubeletRootDir:/var/lib/rancher/k3s/agent/kubelet ProtectKernelDefaults:false NodeAllocatableConfig:{KubeReservedCgroupName: SystemReservedCgroupName: EnforceNodeAllocatable:map[pods:{}] KubeReserved:map[] SystemReserved:map[] HardEvictionThresholds:[{Signal:imagefs.available Operator:LessThan Value:{Quantity:<nil> Percentage:0.05} GracePeriod:0s MinReclaim:<nil>} {Signal:nodefs.available Operator:LessThan Value:{Quantity:<nil> Percentage:0.05} GracePeriod:0s MinReclaim:<nil>}]} QOSReserved:map[] ExperimentalCPUManagerPolicy:none ExperimentalCPUManagerReconcilePeriod:10s ExperimentalPodPidsLimit:-1 EnforceCPULimits:false CPUCFSQuotaPeriod:100ms}
I0227 13:21:44.116647   26662 container_manager_linux.go:271] Creating device plugin manager: true
I0227 13:21:44.116747   26662 state_mem.go:36] [cpumanager] initializing new in-memory state store
I0227 13:21:44.118399   26662 kubelet.go:297] Watching apiserver
I0227 13:21:44.126379   26662 kuberuntime_manager.go:192] Container runtime containerd initialized, version: 1.2.3+unknown, apiVersion: v1alpha2
W0227 13:21:44.126807   26662 probe.go:271] Flexvolume plugin directory at /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ does not exist. Recreating.
I0227 13:21:44.127470   26662 server.go:946] Started kubelet
E0227 13:21:44.133436   26662 cri_stats_provider.go:320] Failed to get the info of the filesystem with mountpoint "/var/lib/rancher/k3s/agent/containerd/io.containerd.snapshotter.v1.overlayfs": unable to find data in memory cache.
E0227 13:21:44.133457   26662 kubelet.go:1229] Image garbage collection failed once. Stats initialization may not have completed yet: invalid capacity 0 on image filesystem
I0227 13:21:44.133717   26662 server.go:133] Starting to listen on 127.0.0.1:10250
I0227 13:21:44.134352   26662 server.go:318] Adding debug handlers to kubelet server.
I0227 13:21:44.135949   26662 fs_resource_analyzer.go:66] Starting FS ResourceAnalyzer
I0227 13:21:44.135977   26662 status_manager.go:152] Starting to sync pod status with apiserver
I0227 13:21:44.135992   26662 kubelet.go:1735] Starting kubelet main sync loop.
I0227 13:21:44.136002   26662 kubelet.go:1752] skipping pod synchronization - [container runtime status check may not have completed yet PLEG is not healthy: pleg has yet to be successful]
I0227 13:21:44.136218   26662 volume_manager.go:248] Starting Kubelet Volume Manager
I0227 13:21:44.137605   26662 desired_state_of_world_populator.go:130] Desired state populator starts to run
W0227 13:21:44.143794   26662 container.go:409] Failed to create summary reader for "/system.slice": none of the resources are being tracked.
W0227 13:21:44.143956   26662 container.go:409] Failed to create summary reader for "/system.slice/rsyslog.service": none of the resources are being tracked.
W0227 13:21:44.144089   26662 container.go:409] Failed to create summary reader for "/system.slice/postfix.service": none of the resources are being tracked.
W0227 13:21:44.144351   26662 container.go:409] Failed to create summary reader for "/system.slice/irqbalance.service": none of the resources are being tracked.
W0227 13:21:44.144486   26662 container.go:409] Failed to create summary reader for "/system.slice/polkit.service": none of the resources are being tracked.
W0227 13:21:44.144706   26662 container.go:409] Failed to create summary reader for "/system.slice/tuned.service": none of the resources are being tracked.
W0227 13:21:44.144837   26662 container.go:409] Failed to create summary reader for "/system.slice/network.service": none of the resources are being tracked.
W0227 13:21:44.144964   26662 container.go:409] Failed to create summary reader for "/system.slice/systemd-logind.service": none of the resources are being tracked.
W0227 13:21:44.145097   26662 container.go:409] Failed to create summary reader for "/system.slice/system-getty.slice": none of the resources are being tracked.
W0227 13:21:44.145353   26662 container.go:409] Failed to create summary reader for "/system.slice/systemd-journald.service": none of the resources are being tracked.
W0227 13:21:44.145484   26662 container.go:409] Failed to create summary reader for "/system.slice/dbus.service": none of the resources are being tracked.
W0227 13:21:44.145622   26662 container.go:409] Failed to create summary reader for "/system.slice/rpcbind.service": none of the resources are being tracked.
W0227 13:21:44.146228   26662 container.go:409] Failed to create summary reader for "/system.slice/sshd.service": none of the resources are being tracked.
W0227 13:21:44.146424   26662 container.go:409] Failed to create summary reader for "/system.slice/chronyd.service": none of the resources are being tracked.
W0227 13:21:44.146552   26662 container.go:409] Failed to create summary reader for "/system.slice/system-serial\\x2dgetty.slice": none of the resources are being tracked.
W0227 13:21:44.146681   26662 container.go:409] Failed to create summary reader for "/system.slice/auditd.service": none of the resources are being tracked.
W0227 13:21:44.146898   26662 container.go:409] Failed to create summary reader for "/system.slice/gssproxy.service": none of the resources are being tracked.
I0227 13:21:44.156419   26662 kubelet_node_status.go:267] Setting node annotation to enable volume controller attach/detach
I0227 13:21:44.160912   26662 cpu_manager.go:155] [cpumanager] starting with none policy
I0227 13:21:44.160924   26662 cpu_manager.go:156] [cpumanager] reconciling every 10s
I0227 13:21:44.160934   26662 policy_none.go:42] [cpumanager] none policy: Start
W0227 13:21:44.183073   26662 manager.go:527] Failed to retrieve checkpoint for "kubelet_internal_checkpoint": checkpoint is not found
E0227 13:21:44.186324   26662 eviction_manager.go:243] eviction manager: failed to get summary stats: failed to get node info: node "ip-10-0-0-180.us-west-2.compute.internal" not found
I0227 13:21:44.236439   26662 kubelet_node_status.go:267] Setting node annotation to enable volume controller attach/detach
E0227 13:21:44.236441   26662 kubelet.go:2167] node "ip-10-0-0-180.us-west-2.compute.internal" not found
I0227 13:21:44.238401   26662 kubelet_node_status.go:70] Attempting to register node ip-10-0-0-180.us-west-2.compute.internal
I0227 13:21:44.240852   26662 kubelet_node_status.go:73] Successfully registered node ip-10-0-0-180.us-west-2.compute.internal
I0227 13:21:44.247515   26662 kuberuntime_manager.go:930] updating runtime config through cri with podcidr 10.42.1.0/24
I0227 13:21:44.247888   26662 kubelet_network.go:69] Setting Pod CIDR:  -> 10.42.1.0/24
I0227 13:21:44.337799   26662 reconciler.go:154] Reconciler: start to sync state
I0227 13:21:45.492100   26662 flannel.go:89] Determining IP address of default interface
I0227 13:21:45.492367   26662 flannel.go:99] Using interface with name eth0 and address 10.0.0.180
I0227 13:21:45.493439   26662 kube.go:127] Waiting 10m0s for node controller to sync
I0227 13:21:45.493468   26662 kube.go:306] Starting kube subnet manager
I0227 13:21:46.493677   26662 kube.go:134] Node controller sync successful
I0227 13:21:46.493769   26662 vxlan.go:120] VXLAN config: VNI=1 Port=0 GBP=false DirectRouting=false
I0227 13:21:46.512663   26662 flannel.go:75] Wrote subnet file to /run/flannel/subnet.env
I0227 13:21:46.512681   26662 flannel.go:79] Running backend.
I0227 13:21:46.512688   26662 vxlan_network.go:60] watching for new subnet leases
I0227 13:21:46.514167   26662 iptables.go:145] Some iptables rules are missing; deleting and recreating rules
I0227 13:21:46.514179   26662 iptables.go:167] Deleting iptables rule: -s 10.42.0.0/16 -d 10.42.0.0/16 -j RETURN
I0227 13:21:46.514649   26662 iptables.go:167] Deleting iptables rule: -s 10.42.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE --random-fully
I0227 13:21:46.515118   26662 iptables.go:167] Deleting iptables rule: ! -s 10.42.0.0/16 -d 10.42.1.0/24 -j RETURN
I0227 13:21:46.515591   26662 iptables.go:167] Deleting iptables rule: ! -s 10.42.0.0/16 -d 10.42.0.0/16 -j MASQUERADE --random-fully
I0227 13:21:46.516027   26662 iptables.go:155] Adding iptables rule: -s 10.42.0.0/16 -d 10.42.0.0/16 -j RETURN
I0227 13:21:46.517217   26662 iptables.go:145] Some iptables rules are missing; deleting and recreating rules
I0227 13:21:46.517229   26662 iptables.go:167] Deleting iptables rule: -s 10.42.0.0/16 -j ACCEPT
I0227 13:21:46.517742   26662 iptables.go:155] Adding iptables rule: -s 10.42.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE --random-fully
I0227 13:21:46.518320   26662 iptables.go:167] Deleting iptables rule: -d 10.42.0.0/16 -j ACCEPT
I0227 13:21:46.518807   26662 iptables.go:155] Adding iptables rule: ! -s 10.42.0.0/16 -d 10.42.1.0/24 -j RETURN
I0227 13:21:46.519592   26662 iptables.go:155] Adding iptables rule: -s 10.42.0.0/16 -j ACCEPT
I0227 13:21:46.520047   26662 iptables.go:155] Adding iptables rule: ! -s 10.42.0.0/16 -d 10.42.0.0/16 -j MASQUERADE --random-fully
I0227 13:21:46.521296   26662 iptables.go:155] Adding iptables rule: -d 10.42.0.0/16 -j ACCEPT

表示されるログを見るとエラーがかなり表示されていますが、Server側でコマンドを実行すると、ちゃんとAgentノードが登録されているのがわかります。

[root@ip-10-0-0-160 ~]# k3s kubectl get nodes
NAME                                       STATUS   ROLES    AGE     VERSION
ip-10-0-0-160.us-west-2.compute.internal   Ready    <none>   13m     v1.13.3-k3s.6
ip-10-0-0-180.us-west-2.compute.internal   Ready    <none>   2m56s   v1.13.3-k3s.6
[root@ip-10-0-0-160 ~]# 

nginxコンテナをデプロイ

最後にnginxのコンテナをデプロイし、curlで確認するまでを実行します。

# nginxデプロイ
[root@ip-10-0-0-160 ~]# k3s kubectl run nginx --image=nginx:latest
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/nginx created
[root@ip-10-0-0-160 ~]# 

# Deploymentの確認
[root@ip-10-0-0-160 ~]# k3s kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           22s

[root@ip-10-0-0-160 ~]# k3s kubectl describe deployment nginx
Name:                   nginx
Namespace:              default
CreationTimestamp:      Wed, 27 Feb 2019 13:34:11 +0000
Labels:                 run=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=nginx
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  run=nginx
  Containers:
   nginx:
    Image:        nginx:latest
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-585fddf4b (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  74s   deployment-controller  Scaled up replica set nginx-585fddf4b to 1
[root@ip-10-0-0-160 ~]#

# クラスター外に公開
[root@ip-10-0-0-160 ~]# k3s kubectl expose deployment/nginx --type=NodePort --port=80
service/nginx exposed
[root@ip-10-0-0-160 ~]# 

# Serviceの確認
[root@ip-10-0-0-160 ~]# k3s kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP        23m
nginx        NodePort    10.43.153.128   <none>        80:30936/TCP   9s
[root@ip-10-0-0-160 ~]# 

# curlコマンド
[root@ip-10-0-0-160 ~]# curl http://10.0.0.180:30936
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@ip-10-0-0-160 ~]# 

参照リンク

Rancher Labs、エッジ向けKubernetes軽量ディストリビューションOSSプロジェクト「k3s」を開始

k3s Webinar動画

k3sの呼び方に関するissue

TensorFlow再入門 ~線形回帰~

はじめに

先日の記事の続きです。今回はTensorFlowで線形回帰をする際の流れや利用できる関数などを紹介します。また今回から損失関数や最適化関数などを利用するので、そちらの紹介もします。

TensorFlowで線形回帰をする際の要素

TensorFlowで線形回帰をする際には、前回記事の要素に加えて損失関数最適化関数(optimizer)が必要になります。実際のコードを見る前に、新しく登場したこれらの要素について、簡単に紹介します。

損失関数

機械学習の中でも教師あり学習では、実際の数値と予測した数値との誤差を小さくしようとすることで、より正確に予測ができることを目指します。そのような「正解ラベルの数値と予測した数値との誤差」を損失関数として表します。

最適化(optimizer)

損失関数によって正解値と予測値との間でどれほど差分があるかわかりましたが、次にその差分を埋めるためにモデルを修正する必要があります。そのような関数を最適化(optimizer)関数と言います。

実装

今回はScikit-Learnで提供されているデータセットの中から、boston house-prices datasetを使います。

実際のコード

最初に全体像を紹介します。

# 線形回帰
## 必要なライブラリのインポート
import tensorflow as tf
import numpy as np
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

## ボストン住宅街のデータセット
boston = load_boston()

# 特徴量とラベルにデータを分ける
## boston.data: 説明変数(13のラベル)
## boston.target: 目的関数(住宅価格の中央値)
data_x = np.array(boston.data)
data_y = np.array(boston.target)

## 正則化
ss = StandardScaler()
ss.fit(data_x)
data_x = ss.transform(data_x)

## 訓練データとテストデータに分ける
train_x, test_x , train_y, test_y = train_test_split(data_x, data_y, test_size=0.2, random_state=42)

## データの変形
train_y = train_y.reshape(404, 1)
test_y = test_y.reshape(102, 1)


## ハイパーパラメータ
learning_rate = 0.01
num_epochs = 1000


# 計算グラフの作成
X = tf.placeholder(tf.float32, [None, 13])
Y = tf.placeholder(tf.float32, [None, 1])

W = tf.Variable(tf.zeros([13, 1]))
b = tf.Variable(0.0)

## 線形関数
y = tf.add(tf.matmul(X, W), b)

## 損失関数(二乗和誤差)
loss = tf.losses.mean_squared_error(labels = y, predictions = Y)

## 最適化
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

# 変数の初期化
init = tf.global_variables_initializer()

# 計算処理
with tf.Session() as sess:
  sess.run(init)

  for epoch in range(num_epochs):
    sess.run(train_step, feed_dict={X: train_x, Y: train_y})
    if epoch != 0 and epoch % 100 == 0:
      train_loss = sess.run(loss, feed_dict={X: train_x, Y: train_y})
      print("train_loss: ", train_loss)
      
# 評価
  pred_y = sess.run(y, feed_dict={X: test_x})
  for i in range(9):
    print("実際の不動産価格: ", test_y[i,0], "  予測した不動産価格: ", pred_y[i,0])
  

# 計算グラフの初期化
tf.reset_default_graph()

0. ライブラリのインポート

今回はTensorFlow以外にも複数のライブラリをインポートします。

# 必要なライブラリのインポート
import tensorflow as tf
import numpy as np
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

1. データセットの読み込み

データセットを読み込んでからは、実際の計算処理をする上で効率よく処理できるように前処理をする必要があります。

  • データの分割

まずは与えられたデータセットを説明変数と目的変数とに分けます。今回のデータセットdatatargetと言うキーを持っており、それぞれ説明変数と目的変数とに対応しています。

機械学習では、モデルの予測性能を向上させるため、データセット正則化・正規化を行うことがあります。ここでも正則化を行っていますが、正則化を行わないと損失関数の値が発散してしまい、NaN(Not A Number)を表示してしまいます。

  • 訓練データとテストデータ

教師あり学習では、与えられたデータセットを、訓練用のデータセットと汎化性能を見るためのテストデータとに分けて学習を行います。ここではScikit-Learnのtrain_test_splitメソッドを利用してデータセットを分けます。

## ボストン住宅街のデータセット
boston = load_boston()

# 特徴量とラベルにデータを分ける
## boston.data: 説明変数(13のラベル)
## boston.target: 目的関数(住宅価格の中央値)
data_x = np.array(boston.data)
data_y = np.array(boston.target)

## 正則化
ss = StandardScaler()
ss.fit(data_x)
data_x = ss.transform(data_x)

## 訓練データとテストデータに分ける
train_x, test_x , train_y, test_y = train_test_split(data_x, data_y, test_size = 0.2, random_state = 42)

## データの変形
train_y = train_y.reshape(404, 1)
test_y = test_y.reshape(102, 1)

2. データフローグラフの構築

TensorFlowにおける損失関数

TensorFlowでは損失関数を設定する際には、tf、tf.mathモジュールに加え、tf.nntf.lossesモジュールなどを利用できます。Deep Learningでよく使われるものは組み込み関数として提供されていますが、組み合わせて関数を作成することも可能です。

関数名 用途
tf.reduce_mean テンソルにまたがる各要素の平均を計算する
tf.math.reduce_mean テンソルにまたがる各要素の平均を計算する
tf.reduce_sum テンソルにまたがる各要素の和を計算する
tf.math.reduce_sum テンソルにまたがる各要素の和を計算する
tf.nn.softmax_cross_entropy_with_logits logitsで与えられた入力に対してソフトマックス交差エントロピーを計算する
tf.nn.softmax_cross_entropy_with_logits_v2 logits又はlabelsで与えられた入力に対してソフトマックス交差エントロピーを計算する
tf.nn.sigmoid_cross_entropy_with_logits logitsで与えられた入力に対してシグモイド交差エントロピーを計算する
tf.losses.mean_squared_error 正解値と予測値から二乗和誤差を計算する
tf.losses.sigmoid_cross_entropy マルチクラスの正解値とlogitsからシグモイド交差エントロピーを計算する
tf.losses.softmax_cross_entropy one-hot変換された正解値とlogitsからソフトマックス交差エントロピーを計算する

今回は二乗和誤差を利用します。二乗和誤差を表現する方法は複数あります。

# 二乗和誤差
## labels: 正解値
## predictions: 予測値

tf.losses.mean_squared_error(
    labels,
    predictions,
    weights=1.0,
    scope=None,
    loss_collection=tf.GraphKeys.LOSSES,
    reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
)


tf.reduce_mean(tf.square(labels - predictions))

tf.reduce_sum(tf.pow(labels - predictions, 2) / (2 * n_samples))

また、交差エントロピーを表現する方法も以下のようになります。

# 交差エントロピー
## labels: 正解値
## predictions: 予測値

tf.nn.softmax_cross_entropy_with_logits(
    _sentinel=None,
    labels=None,
    logits=None,
    name=None
)

tf.losses.softmax_cross_entropy(
    onehot_labels,
    logits,
    weights=1.0,
    label_smoothing=0,
    scope=None,
    loss_collection=tf.GraphKeys.LOSSES,
    reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
)

tf.reduce_sum(labels*tf.log(predictions))
TensorFlowにおける最適化関数

TensorFlowには最適化関数も複数用意されており、多くはtf.trainで利用できます。

関数名 用途
tf.train.GradientDescentOptimizer 勾配降下法を実行する
tf.train.MomentumOptimizer Momentum法を実行する
tf.train.AdagradOptimizer Adagrad法を実行する
tf.train.AdamOptimizer Adam法を実行する

今回はtf.train.GradientDescentOptimizerを使っています。またminimizeメソッドを使うことで、損失関数を最小化するように計算を実行します。

# 計算グラフの作成
X = tf.placeholder(tf.float32, [None, 13])
Y = tf.placeholder(tf.float32, [None, 1])

W = tf.Variable(tf.zeros([13, 1]))
b = tf.Variable(0.0)

## 線形関数
y = tf.add(tf.matmul(X, W), b)

## 損失関数(二乗和誤差)
loss = tf.losses.mean_squared_error(labels = y, predictions = Y)

## 最適化
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_step = optimizer.minimize(loss)

3. 各変数の初期化

今回は変数(tf.Variable)があるので、変数の初期化を行います。

# 変数の初期化
init = tf.global_variables_initializer()

4. 各関数の実行(実行フェーズ)

今回は繰り返し計算を実行し、損失関数を最小化するようモデルを改善し続ける必要があります。そのためfor文を使って計算を繰り返します。

また訓練結果を100回ごとに表示しています。

# 計算処理
with tf.Session() as sess:
  sess.run(init)

  for epoch in range(num_epochs):
    sess.run(train_step, feed_dict={X: train_x, Y: train_y})
    if epoch != 0 and epoch % 100 == 0:
      train_loss = sess.run(loss, feed_dict={X: train_x, Y: train_y})
      print("train_loss: ", train_loss)

5. 計算結果の確認・評価

線形回帰の計算結果はRMSE(Root Mean Squared Error)で評価するのが正しいのですが、今回は簡易的に予測結果と実際の値を並べて表示しています。

# 評価
  pred_y = sess.run(y, feed_dict={X: test_x})
  for i in range(9):
    print("実際の不動産価格: ", test_y[i,0], "  予測した不動産価格: ", pred_y[i,0])

6. 計算グラフのリセット

TensorFlowで繰り返し計算を実行する際は、計算グラフをリセットしましょう。一番最後にあるtf.reset_default_graph()が計算グラフの初期化を実行する関数になります。

計算結果

それでは今回のコードの計算結果を表示します。なおハイパーパラメータを変更しても、計算結果にほとんど影響はありませんでした。

train_loss:  32.010765
train_loss:  22.759813
train_loss:  22.208197
train_loss:  21.997263
train_loss:  21.878563
train_loss:  21.80662
train_loss:  21.760534
train_loss:  21.729523
train_loss:  21.70777
実際の不動産価格:  23.6   予測した不動産価格:  28.982582
実際の不動産価格:  32.4   予測した不動産価格:  35.82864
実際の不動産価格:  13.6   予測した不動産価格:  15.828376
実際の不動産価格:  22.8   予測した不動産価格:  25.011866
実際の不動産価格:  16.1   予測した不動産価格:  18.713873
実際の不動産価格:  20.0   予測した不動産価格:  23.408972
実際の不動産価格:  17.8   予測した不動産価格:  17.68681
実際の不動産価格:  14.0   予測した不動産価格:  14.559787
実際の不動産価格:  19.6   予測した不動産価格:  22.812624

注意点

データのreshapeを行わないとどうなるか

データセットの変形を行った際reshapeを行っています。これはTensorFlowで計算を行う際、そのままではデータの型が合わず、エラーを吐いてしまうからです。

# reshapeを行わなかった場合のエラーメッセージ
ValueError: Cannot feed value of shape (404,) for Tensor 'Placeholder_3:0', which has shape '(?, 1)'

# reshapeをしない場合のデータ型
print(train_y[0:5])

> [12. 19.9 19.4 13.4 18.2]

# reshapeをした場合のデータ型
print(train_y[0:5])

> [[12. ] [19.9] [19.4] [13.4] [18.2]]

正則化を行わない場合

正則化を行わない場合は、以下のようにNaNが出力されます。これはハイパーパラメータの値を変更しても変わりませんでした。これは損失関数の値が発散した(∞になった)ためと思われます。

# 正則化を行わない場合の出力結果

train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
train_loss:  nan
実際の不動産価格:  23.6   予測した不動産価格:  nan
実際の不動産価格:  32.4   予測した不動産価格:  nan
実際の不動産価格:  13.6   予測した不動産価格:  nan
実際の不動産価格:  22.8   予測した不動産価格:  nan
実際の不動産価格:  16.1   予測した不動産価格:  nan
実際の不動産価格:  20.0   予測した不動産価格:  nan
実際の不動産価格:  17.8   予測した不動産価格:  nan
実際の不動産価格:  14.0   予測した不動産価格:  nan
実際の不動産価格:  19.6   予測した不動産価格:  nan

ですが、最適化関数を変更すると改善され、数値が出力されるようになります。ただtrain_lossの数値は正則化した場合と比べても高いため、やはり正則化した方が性能は向上するようです。

# Adagradを使った場合
optimizer = tf.train.AdagradOptimizer(learning_rate)

> 
train_loss:  71.82181
train_loss:  68.44326
train_loss:  65.977745
train_loss:  63.925743
train_loss:  62.17434
train_loss:  60.65328
train_loss:  59.314636
train_loss:  58.124134
train_loss:  57.05623
実際の不動産価格:  23.6   予測した不動産価格:  25.57679
実際の不動産価格:  32.4   予測した不動産価格:  28.002806
実際の不動産価格:  13.6   予測した不動産価格:  23.69553
実際の不動産価格:  22.8   予測した不動産価格:  20.848318
実際の不動産価格:  16.1   予測した不動産価格:  23.087069
実際の不動産価格:  20.0   予測した不動産価格:  22.962362
実際の不動産価格:  17.8   予測した不動産価格:  21.679335
実際の不動産価格:  14.0   予測した不動産価格:  20.155552
実際の不動産価格:  19.6   予測した不動産価格:  25.182972

# Adamを使った場合
optimizer = tf.train.AdamOptimizer(learning_rate)

> 
train_loss:  60.566013
train_loss:  49.024643
train_loss:  43.398365
train_loss:  40.164436
train_loss:  37.729042
train_loss:  35.65119
train_loss:  33.810036
train_loss:  32.166935
train_loss:  30.707003
実際の不動産価格:  23.6   予測した不動産価格:  26.805815
実際の不動産価格:  32.4   予測した不動産価格:  35.081577
実際の不動産価格:  13.6   予測した不動産価格:  17.97925
実際の不動産価格:  22.8   予測した不動産価格:  23.187765
実際の不動産価格:  16.1   予測した不動産価格:  20.638412
実際の不動産価格:  20.0   予測した不動産価格:  22.202286
実際の不動産価格:  17.8   予測した不動産価格:  18.063892
実際の不動産価格:  14.0   予測した不動産価格:  15.885112
実際の不動産価格:  19.6   予測した不動産価格:  23.200396

まとめ

TensorFlowで線形回帰を実施しました。TensorFlowは大規模なNN向けのツールのため、線形回帰をするのは不適ですが、学習のステップとしてやっておくのは良いかと思います。なお、線形回帰を実施したい場合はScikit-Learn等のライブラリを利用するべきです。

次回からNeural Networkに着手しようと思います。

参考リンク

TensorFlowとは?不動産の価格をTensorFlowを使って予測してみよう(入門編)

学習の種類と誤差関数

[TensorFlow] APIドキュメントを眺める -Math編-

PrometheusとDatadogをRancher Catalogでデプロイ

はじめに

Prometheusを検証するにはbinaryをインストールしたりdockerイメージを使ってデプロイする方法があります。しかし、Prometheusを検証する際にはPrometheus単体でやることは少なく、GrafanaやAlertManager、各種Exporterと合わせて使うことが多いです。それらを一発でデプロイできるようyamlファイルやhelmチャートを書くことはなかなか大変であり、Prometheusの検証をするという目的以前の段階で苦労が多いと思います。

この記事では、RancherのCatalog機能を使うことでPrometheusとその周辺を簡単に利用できることを紹介したいと思います。また、Prometheusと並んで有名なモニタリングツールであるDatadogのAgentもCatalogからデプロイできるので、合わせて紹介します。

Rancherとは

RancherはRancher社が開発を進めるOSSであり、コンテナオーケストレーションの一つです。Kubernetesクラスターを簡単に構築できるだけでなく、複数環境にあるKubernetesクラスターを統合できることが特徴的なツールになります。

またRancherではHelmと連携してCatalogというアプリケーションパッケージを提供しており、今回はこのCatalog機能を利用してPrometheus、Datadogをデプロイします。

Rancherの環境構築

Rancherのインストール方法は公式ページで紹介されています。またこちらの記事でも構築方法を紹介しております

構築環境

CatalogからPrometheusをデプロイ

早速Prometheusをデプロイします。Rancherにログインしたのち、画面上部から「Default」を選択します。

f:id:FY0323:20190224204957j:plain

画面遷移後、画面上部の「Catalog Apps」を選択し、さらにその先の画面から「Launch」をクリックします。

f:id:FY0323:20190224181616j:plain

Catalog一覧が表示されるので、その中からPrometheusを選択します。

f:id:FY0323:20190224205203j:plain

f:id:FY0323:20190224222525j:plain

するとPrometheusの設定画面が表示されます。

f:id:FY0323:20190224205312j:plain

設定画面では入力する箇所はほとんどありません。唯一Grafanaのパスワードだけは入力しておきましたが、以前Grafanaを触った時Grafanaの画面に初回アクセスした際パスワード入力を求められたため、ここで入力しなくても問題はないと思います。

f:id:FY0323:20190224205855j:plain

画面の一番下までスクロールし、「Launch」をクリックします。

f:id:FY0323:20190224214148j:plain

クリックすると以下のような画面が表示されます。

f:id:FY0323:20190224205936j:plain

しばらく待つと以下のようにステータスが緑色になりデプロイが完了します。

f:id:FY0323:20190224205946j:plain

画面上に幾つかのリンクが表示されています。これらは作成されたカタログの各アプリケーションへのリンクになります。なお、ポート番号はデプロイの度にランダムに割り振られるようです。

それではそれぞれのアプリケーションにアクセスします。ここでは<workerノードのグローバルIPアドレス:各ポート番号>をURL欄に入力します。

※ここでの表示先のリンクはプライベートIPアドレス宛になっており、今回構築した環境では直接アクセスできないため、グローバルIPを指定してアクセスしています。

Prometheus

f:id:FY0323:20190224210231j:plain

Grafana

f:id:FY0323:20190224210247j:plain

Alertmanager

f:id:FY0323:20190224210257j:plain

Node Exporter

f:id:FY0323:20190224210305j:plain

無事に各アプリケーションにアクセスできました。このようにRancherの環境さえあれば、数クリックでPrometheus + Grafana + Alertmanagerの環境が構築されるのです。とても簡単ですね。

ちなみに、Catalog Apps画面からデプロイされたPrometheusカタログの中身を見ると、Configmapが利用されていること、GrafanaのパスワードはSecretsで管理されていることなどがわかります。

f:id:FY0323:20190224212445j:plain

CatalogからDatadog Agentをデプロイ

続いてDatadogをデプロイします。Datadog Catalogでは、Datadog AgentをRancherの管理下にあるノードにデプロイします。そのため、Datadogにユーザ登録をしていない場合は、まずユーザ登録をする必要があります。

Datadogを利用するにはDatadogの公式ページから名前やメールアドレスを登録する必要があります。はじめは14日間の無料お試し期間になりますので、ひとまずお金はかかりません。

Datadogの登録が完了したら、Rancherの管理画面からDatadog Agentをデプロイします。

Prometheusをデプロイした時と同様、Catalog Apps画面に行き、Datadogを選択します。

f:id:FY0323:20190224205203j:plain

こちらも同様にDatadogの設定画面に遷移します。

f:id:FY0323:20190224211534j:plain

Datadogの設定画面で入力するのは、Datadog API Keyになります。まずDatadogの管理画面から「Integrations」→「APIs」にアクセスし、API Keyを取得します。

f:id:FY0323:20190224224901j:plain

続いて取得したAPI Keyを、Rancher CatalogのDatadog設定画面より「Agent Configuration」の項目に入力します。

f:id:FY0323:20190224213657j:plain

また今回はAWSインスタンス上にRancherを立てているため、「Daemonset Configuration」にある「Run datadog pod on RKE Control Plane Nodes」をFalseにします。

f:id:FY0323:20190224215116j:plain

こちらも画面下の「Launch」をクリックし、しばらくするとステータスが緑色になり、デプロイが完了します。

f:id:FY0323:20190224203706j:plain

Datadog管理画面の「Infrastructure」画面から見ると、確かにRancherのマネジメント配下の2ノードが確認できます(master-1, worker-1)。

f:id:FY0323:20190224215516j:plain

f:id:FY0323:20190224215530j:plain

まとめ

RancherのCatalogを利用することで、簡単にPrometheusやDatadogの環境を構築できました。Rancherではデフォルトで利用できるCatalogが数多く用意されており、様々なアプリケーションを簡単に利用できるかもしれません。検証環境等を用意する際には、選択肢の一つとしてRancherを考えておきべきかもしれません。

補足

Multi-Tenant Prometheus機能

Rancher 2.2からの新機能でMulti-Tenant Prometheus機能がサポートされました。今回紹介したCatalogよりも簡単にPrometheus + Grafana環境がデプロイできるようです。こちらの機能に関してはこちらの記事を参照ください

Helm Chartからデプロイ

今回はCatalog Appsの「Library」からデプロイしましたが、画面上部から「Global」→「Catalogs」を選択し、「Helm Stable」を有効にすることで、先ほどの「Catalog Apps」画面から「Helm」バージョンを選択することができるようになります。

f:id:FY0323:20190224221554j:plain

f:id:FY0323:20190224221611j:plain

f:id:FY0323:20190224221624j:plain

ただHelm Chartを利用するにはPersistentVolumeを用意する必要があります。

Catalog Categories

CatalogのCategoriesを「Monitoring」にすると、なぜかPrometheusだけが表示されます。。。

f:id:FY0323:20190224203621j:plain

TensorFlow再入門 ~基本の使い方をおさらいする~

はじめに

機械学習で有名なライブラリの一つとしてTensorFlowが挙げられます。昨年は社内勉強会で発表するなどしていたので少しだけ触っていたのですが、なかなか使い方を習得できず苦労していました。 そしてしばらく触っていなかった結果、すっかり使い方を忘れてしまったので、改めて基本的な使い方から復習しています。

注意点

  • 利用するバージョン

今回触っているのはTensorFlow ver1.12.0です。つい最近TensorFlow 2.0のPreview版が発表されましたが、大幅に仕様が変更されており、本記事での書き方の多くの部分は適用できなくなっています。なので使うバージョンには注意しておきましょう。

  • 実行環境

今回はPythonでTensorFlowを動かします。 現在はJavascriptやGoなど様々な環境で利用できるようになっていますが、そちらの紹介はしませんのでご注意ください。

TensorFlowとは

TensorFlowは2015年にGoogleが発表したオープンソース機械学習フレームです。Googleの提供するサービスはもちろんのこと、世界中の企業や研究機関で広く利用されています。TensorFlowの論文によると、もともとはDistBeliefと呼ばれるライブラリから改良されて誕生したようです。特徴として、 大規模な計算処理を行うこと、幅広い環境で実行することを念頭に設計されています。

f:id:FY0323:20190211221330p:plain

TensorFlowを動かす上で必要な要素

それではTensorFlowの使い方を見てみます。 TensorFlowを利用して機械学習を実行する場合の流れは、大まかに言えば以下のようになります。

  1. ライブラリのインポート
  2. データセットの読み込み
  3. データフローグラフの構築
  4. 各変数の初期化
  5. 計算処理の実行
  6. 計算結果の評価

1. ライブラリのインポート

まずは必要なライブラリをインポートします。TensorFlowをインポートして利用する際は「tf」という名前で扱うことが多いです。

2. データセットの読み込み

TensorFlowに限らず機械学習を行うにはデータが必須です。そのため、TensorFlowを実行するにはまずデータを読み込むことが必要です。

データを読み込む際は、Pandasなどを利用することができます。CSVファイルのデータなどをPandasを利用して読み込み、それをそのままTensorFlowで利用できます。

3. データフローグラフの構築

TensorFlowではデータフローグラフと呼ばれる「計算の過程をグラフにして表したもの」を利用します。データフローグラフは定数や変数、そして関数で構成されます。まずデータフローグラフを定義し、それをTensorFlowに取り込み、実際の計算処理は最適化されたC++コードで実行します。

データフローグラフを用いる利点として、「計算処理の高速化」があります。データフローグラフを使うことで局所的な計算を可能にし、無駄なリソース消費を抑えることができます。

またデータフローグラフを使うことでグラフを複数のグループに分割し、そのグループを複数のCPU/GPUで並列に処理することができます。TensorFlowは分散処理もサポートしているため、複数台のサーバを利用して大規模な計算処理も短時間で完了することができます。

また、この段階ではTensorオブジェクトを作成しているだけであり、実際の計算はまだ実行されていません。計算を実行するにはSessionオブジェクトを作成してrunを実行する必要があります。

4. 各変数の初期化

データフローグラフを作成したらそれを実行しますが、その前に各変数を初期化する必要があります。

なぜ初期化が必要かについてははっきりとした理由がわかりませんでしたが、こちらの記事でその理由が推察されていました。

TensorFlowでは、初期化を明示的に実行しないと変数が確保されません。 これは、GPUへのメモリ転送などのオーバーヘッドを避けるためだと思われます。

5. 各関数の実行(実行フェーズ)

変数の初期化を完了したら、作成したデータフローグラフを用いて計算処理を実行します。実際の計算ではまずSessionオブジェクトを作成して、TensorFlow Runtimeと接続する必要があります。

ここを実行することで初めて実際の計算が実行され、スカラー値が返されます。

6. 計算結果の確認・評価

計算処理が完了後、結果を確認します。結果がよくなければ条件を変えて再実行し、望ましい結果が得られるまで繰り返します。

実装

それでは実際の計算処理を実行します。ここでは単純な足し算を実行してみます。

実行環境

  • Google Colaboratory
  • 言語:Python 3.6.7(Colaboratoryの環境に依存)

※Colaboratoryでの実行結果は「>」で表しています。

実際のコード

前述した1から5までの流れに沿って、実際のコードを紹介します。

# ライブラリのインポート
import tensorflow as tf

# データフローグラフの設計
x = tf.constant(3)
y = tf.constant(4)
add = tf.add(x, y)

# 計算処理の実行
sess = tf.Session()
result = sess.run(add)

# 結果確認
print(result)
> 7
1. ライブラリのインポート ~ 3. データフローグラフの構築

ここでは単純な足し算を実行するので、あらかじめデータセットを読み込ませるような処理は行いません。なので、まずはデータフローグラフを定義するところから始めます。

まず定数や変数などを定義するには、tfモジュールに含まれる幾つかの関数を利用できます。主に使用するのは以下の3つです。

tf.constant
  • tf.constant: 定数を定義します。ここでの定数とは「計算の過程で値が更新されないもの」と言う意味です。
    • 使い方:
# デフォルト値
tf.constant(
    value,
    dtype=None,
    shape=None,
    name='Const',
    verify_shape=False
)

# 使用例
# 1次元テンソル
tensor = tf.constant([1, 2, 3, 4, 5, 6, 7]) => [1 2 3 4 5 6 7]

# 2次元テンソル
tensor = tf.constant(-1.0, shape=[2, 3]) => [[-1. -1. -1.]
                                             [-1. -1. -1.]]
tf.Variable
  • tf.Variable: 変数を定義します。定数とは異なり、変数は「計算の過程で値が更新されるもの」です。
    • 使い方:
# デフォルト値
tf.Variable(
    initial_value=None,
    trainable=True,
    collections=None,
    validate_shape=True,
    caching_device=None,
    name=None,
    variable_def=None,
    dtype=None,
    expected_shape=None,
    import_scope=None,
    constraint=None,
    use_resource=None,
    synchronization=tf.VariableSynchronization.AUTO,
    aggregation=tf.VariableAggregation.NONE
)

またtf.Variableとは別にtf.get_variableも変数の定義に利用できます。公式のドキュメントではtf.get_variableの利用を推奨しております

# デフォルト値
tf.get_variable(
    name,
    shape=None,
    dtype=None,
    initializer=None,
    regularizer=None,
    trainable=None,
    collections=None,
    caching_device=None,
    partitioner=None,
    validate_shape=True,
    use_resource=None,
    custom_getter=None,
    constraint=None,
    synchronization=tf.VariableSynchronization.AUTO,
    aggregation=tf.VariableAggregation.NONE
)

tf.Variableとtf.get_variableとでは、既存の変数に対する応答が違います。

  • tf.Variable: 既存の変数を指定した場合は、別名で新たな変数を作成する
  • tf.get_variable: 既存の変数を指定した場合は、その変数を取得する

上記の使い分けについてはこちらの記事が参考になりました。

tf.placeholder
  • tf.placeholder: データを格納します。実際の計算処理を行う際はfeed_dictオプションを利用し、辞書型で指定した値を格納します。
    • 使い方:
# デフォルト値
tf.placeholder(
    dtype,
    shape=None,
    name=None
)

# 使用例
x = tf.placeholder(tf.float32, shape=(1024, 1024))
y = tf.matmul(x, x)

with tf.Session() as sess:
  print(sess.run(y))  # xには何の値も入っていないのでエラーになる

  rand_array = np.random.rand(1024, 1024)
  print(sess.run(y, feed_dict={x: rand_array}))  # xにrand_arrayを挿入して計算を実行

またtf.placeholder_with_defaultを使うと、tf.placeholderに初期値を与えることができます。

# デフォルト値
tf.placeholder_with_default(
    input,
    shape,
    name=None
)
関数の定義

次にデータフローグラフ上の関数を定義するには、上述したtfモジュールにある幾つかの関数に加え、mathモジュールを利用できます。 mathモジュールには現時点で108種類の関数が用意されていますが、ここではごくごく一部のものだけ紹介します。

関数名 内容
tf.add 加算処理を定義する
tf.math.add 加算処理を定義する
tf.subtract 減算処理を定義する
tf.math.subtract 減算処理を定義する
tf.multiply 積算処理を定義する
tf.math.multiply 積算処理を定義する
tf.divide 除算処理を定義する
tf.math.divide 除算処理を定義する
tf.scalar_mul スカラー値の積算処理を定義する
tf.math.scalar_mul スカラー値の積算処理を定義する
tf.matmul 行列式の積算処理を定義する

※tf.addとtf.math.addのように対応する関数はエイリアスであり、使い方は一緒です。

ここでは「3+4=7」の加算処理を定義します。足し算の定義は複数の書き方があるので、例を書きます。

# 足し算の定義その1
add = x + y

# 足し算の定義その2
add = tf.math.add(x, y)

# 足し算の定義その3
add = tf.add(x, y)

ここまでの流れは以下の部分に該当します。

# ライブラリのインポート
import tensorflow as tf

# データフローグラフの設計
x = tf.constant(3)
y = tf.constant(4)
add = tf.add(x, y)
4. 各変数の初期化 ~ 6. 計算結果の確認・評価
tf.initializers

変数の初期化を実行するには、tfモジュールに加えて、initializersモジュールも利用できます。変数ごとに個別に初期化することも可能ですが、すべての変数をまとめて初期化することもできます。

関数名 内容
tf.variables_initializer 引数で与えられた変数を初期化する
tf.initializers.variables 引数で与えられた変数を初期化する
tf.global_variables_initializer グラフに定義されたグローバル変数を初期化する
tf.initializers.global_variables グラフに定義されたグローバル変数を初期化する

※こちらも対応する関数はエイリアスであり、使い方は一緒です。

# 特定の変数の初期化
tf.initializers.variables(
    var_list,
    name='init'
)

# グローバル変数の初期化
tf.initializers.global_variables()

今回は変数は定義していませんので、初期化を実行する必要はありません。

tf.Session

設定したデータフローグラフを使い、実際に計算処理を実行するには、Sessionモジュールを利用します。 runメソッドを実行することで計算処理を実行します。

# デフォルト値
tf.Session(
    target='',
    graph=None,
    config=None
)

tf.Session.run(
    fetches,
    feed_dict=None,
    options=None,
    run_metadata=None
)
tf.metrics

最後に処理結果を確認します。評価にはtf.metricsと言うモジュールも利用できます。

ここまでの流れは以下の部分に該当します。

# 計算処理の実行
sess = tf.Session()
result = sess.run(add)

# 結果確認
print(result)
> 7

まとめ

TensorFlowのごくごく簡単な使い方を書きました。特にデータフローグラフを定義する際に使うモジュール・関数は、調べてみるとたくさんの種類が存在することが分かりました。これを機にこれらの道具について、改めて種類と使いかたを整理しておくと良いかもしれません。

参考リンク

TensorFlowとは? データフローグラフを構築・実行してみよう

TensorFlowの基礎

TensorFlowを遊び倒す! 2-2. Variables: Creation, Initializing, Saving, and Restoring

Prometheusを利用する際のセキュリティモデル ~公式ドキュメントを読む~

はじめに

これまでPrometheusやExporterを設定し、監視対象のサーバからメトリクスが取得することができました。しかし、これまではセキュリティについて何も気にしておらず、あまり安全な監視体制とは言えない状態でした。

一方でPrometheusのコンフィグを眺めてみても、セキュリティに関わりそうな項目が存在せず、果たしてどうやってセキュアな通信を実現しているのだろう?と言うことが気になりました。

Prometheusの公式ドキュメントの中を見るとこちらのページにてPrometheusのSecurity Modelに関する紹介が載っていたので、ここに内容をまとめておきます。和訳+一部意訳+一部別記事から追加した内容です。

------記事内容はここから------

セキュリティモデル

Prometheusは多くのコンポーネントや他の多くのシステムと統合された洗練されたシステムであり、様々な環境にデプロイすることが可能です。

このページではPrometheusで一般的に想定されているセキュリティについて、そして幾つかの可能性のある攻撃因子について紹介します。

どんなシステムでも、バグが存在しないことを保証することはできません。もしもセキュリティ面のバグを発見した場合は、MAINTAINERS.mdにリストで書かれている管理者に報告してください。

Prometheus

想定されることとして、信頼できないユーザがPrometheusのHTTPエンドポイントやログにアクセスする状況があります。アクセスする情報には、データベースに収められているすべての時系列情報や、加えて様々な運用/デバッグの情報も含まれます。

また別の想定として、信頼できるユーザだけが、Prometheusや他のコンポーネントのランタイム環境に関する情報(コマンドライン・コンフィグファイル・ルールファイルなど)にアクセスできるような状況もあります。

どのターゲットから、どれくらいの頻度でPrometheusがスクレイプするか、あるいはそれ以外の設定は、すべてコンフィグファイルで決定されます。管理者はService Discoveryから得られた情報を使うことも出来ますが、Service Discoveryで得られた情報にはラベルを付け替えられたものも含まれることがあり、それらを参考とするかは考慮が必要です。

スクレイプされるターゲットは、信頼できないユーザによって起動されたものかもしれません。異なるターゲットのふりをするようなデータをさらすことは、ターゲットに対してデフォルトで許可されるべきではありません。honor_labelsオプションは、ラベルを再定義するよう設定するのと同様に、このような事態を防ぐことができます。

Prometheus 2.0から、--web.enable-admin-apiというフラグを使うことができます。これは、例えば時系列を削除するような役割を持つ、管理APIへのアクセスを制御することができます(デフォルトで無効化されています)。有効化すると、管理的・設定変更的な機能は/api/*/admin/パスからアクセスできるようになります。 また--web.enable-lifecycleフラグはPrometheusのHTTPリロードとシャットダウンをコントロールします。このフラグもデフォルトで無効化されており、有効化すると/-/reload/-/quitパスからアクセスできるようになります。

Prometheus 1.xでは、/-/reloadパスへのアクセスと/api/v1/series上でDELETEを実行することは、HTTP APIにアクセスすることで誰にでも可能です。/-/quitエンドポイントはデフォルトで無効化されていますが、-web.enable-remote-shutdownフラグとともに有効化することができます。

Remote Read機能は、HTTPにアクセスするすべてのユーザに対して、Readエンドポイントに向けてクエリを送ることを許可します。例えばPromQLクエリをrelational databaseに送り、起動終了することが可能であれば、クエリを送ることのできるユーザ全員が、好きなSQLをデータベースに対して起動することができることを意味します。

※Remote Read機能とは、Prometheusの持つRemote Storage機能(取得したメトリクスデータを別のマシン上のストレージに格納する)を実現するために用意されたHTTPエンドポイントのことです。

公式ページのこちらから

Alertmanager

AlertmanagerのHTTPエンドポイントにアクセスできるユーザは、Alertmanagerのデータにアクセスすることができます。そのようなユーザはアラートを作成・解除することもできるし、サイレンスを作成・削除することもできます。

※アラート、サイレンスはどちらもAlertmanagerでの設定項目です。

どこに通知を送るかはコンフィグファイルによって決定されます。特定のテンプレート設定をすると、アラート定義の宛先に通知することも可能です。例えばアラートラベルを宛先メールアドレスとして使えば、Alertmanagerにアラートを送ることのできるユーザは、メールアドレス宛に通知を送ることができます。またアラート定義の宛先がテンプレート化可能なsecret fieldであれば、PrometheusやAlertmanagerにアクセスできるすべてのユーザがsecretを閲覧することができます。

※secret fieldはAlertmanagerのコンフィグファイルで設定するsecret placeholderのことを指します。文字列を入力する領域であり、パスワードなどの情報を入力します。

公式ページのこちらから

上記のケースでは、テンプレート化可能なsecret fieldを使うことはrouteでの通知を想定しており、コンフィグファイルからsecretが取り出されるようなことは想定されていません。Alertmanagerのコンフィグファイルを編集し、通知の受け手を指定することのできるユーザは、テンプレートファイルに保存されたsecretを抽出することができます。 例えば大きなシステムの場合、各チームでフルアクセス権限を持ったAlertmanagerコンフィグファイルを別々に持っており、それらは最終的なコンフィグファイルに混在されることもあります。

※上記のrouteは、Alertmanagerのコンフィグファイルで設定できるブロックの一つ。

公式ページのこちらから

Exporters

Exporterは一般的には設定された対象と設定済みのコマンド/リクエストでしか会話しません。それはExporterのHTTPエンドポイントから情報が拡散されないことを意味します。

ExporterにはSNMP ExporterやBlackbox Exporterのような、URLパラメータからターゲットを取得するものもあります。そのため、そのようなExporterにアクセスするユーザは、好きなエンドポイントにリクエストを送ることができます。これらのEndpointはクライアントサイドの認証機能をサポートしており、これらはHTTP認証パスワードなどの機密情報をリークすることにつながる可能性があります。TLSのようなチャレンジレスポンス認証メカニズムはこれらの影響を受けません。

Authentication, Authorization, and Encryption

Prometheusとそのコンポーネントは、サーバーサイドでの認証・認可・暗号化機能は提供しません。もしそれを求めるなら、リバースproxyを利用することを推奨します

管理・変更エンドポイントではcURLのようなシンプルなツールでアクセスされることを想定しており、そのためCSRF (Cross-Site Request Forgery)に対する防御はされていません。したがって、あなたがリバースproxyを利用する際は、CSRFを防ぐためにそのようなパスをブロックしたいと望むでしょう。

変更なしのエンドポイントでは、XSS (Cross-Site Scripting)を防ぐため、リバースproxyにCORS headerをセットすることを望むでしょう。

もしあなたが信頼できないユーザからのインプットデータを含むPromQLクエリを構成している場合、injection攻撃を防ぐため、何らかの信頼できないインプットを適切に防ぐことができます。例えば<user_input>が"} or some_metric{zzz="の場合、up{job="<user_input>"}up{job=""} or some_metric{zzz=""}にすることができます。

Grafanaを利用する場合、dashboard permissionはdata source permissionではないので、proxyモードの際にクエリを投げるユーザの権限は制限しません。

Prometheusコンポーネントでは、クライアントサイドでの認証と暗号化の機能を提供します。TLSのクライアントサポートが求められた場合、insecure_skip_verifyと言うSSL verificationをスキップするようなフラグもよく利用されます。

------記事内容はここまで------

Prometheusでリバースproxyを利用する方法

上記記事の「Authentication, Authorization, and Encryption」にて、リバースproxyを利用してTKS機能を追加することを推奨しています。具体的な設定についても公式ページにて紹介されています

セキュリティを気にする必要はない?

上記公式ドキュメントを読むと、Prometheusではセキュリティに関する設計は、あまりスコープに含まれないように読めました。一方で、そもそもPrometheusを使う際、セキュリティはそこまで気にしなくても良いのでは、との意見もあります。 こちらのフォーラム(20171105の回答)を読んでみると、以下のように書かれています。

Prometheusを利用するのはプライベートネットワークの後ろであることが多いため、
セキュリティには通常関心はありません。
またExporterはシンプルなread-onlyターゲットのため、それほど情報を搾取されません。
ほとんどのメトリクスはセキュリティ的には関心のない項目ですが、例外もあります。

基本的に私たちは、パブリックインターネットからのアクセスを、シンプルなFWルールでブロックすることを推奨しますが、
TLSや認証機能を追加するため、リバースproxyを利用する方法もあります。
PrometheusサーバはSSL/x509証明書や基本的な認証機能をサポートしていますが、それがどう機能するかが
明らかなため、基本の認証機能を使うことは推奨しません。

まとめ

Prometheusでセキュリティを考慮した際には、リバース proxyを利用したTLS機能の追加が推奨されています。一方でセキュリティはそれほど気にしなくても良いのでは、という意見もあります。Prometheusのセキュリティについては、現状ではベストプラクティスのようなものはなく、利用ケースによって対策を考慮する必要がありそうです。

監視のこと全然知らない立場で「入門 監視」を読んだ感想

はじめに

以前Kubernetesで監視を行う際のポイントについて調べたりPrometheusを触ったりしていましたが、そもそも監視自体に対して理解できていないことが色々とありました。以下にその項目を書き出してみます。

  1. 監視のそもそもの目的。システムに異常がないことを確認することだとは思うけど、じゃあどんな場合は異常なのか?
  2. 優れた監視システムの定義。上記を踏まえて、ではどんな監視システムが優れていると言えるのか?
  3. 具体的な設計の指針。具体的にどんな設計をすれば優れた監視システムを構築できるのか?

私は監視システムの運用は経験がなく、また構築も一部お手伝いをした程度です。そのため、そもそも監視とは何か?に関しての理解が十分できていません

そんな中、先日オライリーから「入門 監視」の日本語版が出版されました。

f:id:FY0323:20190130100924j:plain

普段は何となく技術書を買ってそのまま積ん読してしまうことが多いのですが、これはちゃんと読まなければならない本だと思い、読み進めています。まだ完全に理解できたとは言えない状態ですが、上記の疑問に対しての回答を得られたと感じたため、ここに書き残しておきます。

本書の構成

本書は大きく2つのパートに分かれています。

  • 監視の原則:監視に対する考え方の紹介。アンチパターンから始まり、監視のデザインパターン、アラートを使う際の考え方などが紹介されています。
  • 監視戦略:具体的な戦略の紹介。ビジネス、フロントエンド、サーバなどの観点から、どのような設定をすれば良いかが紹介されています。

1. 監視のそもそもの目的

監視の定義

監視の定義は「はじめに」のパートの中で紹介されています。

監視とは、あるシステムやそのシステムのコンポーネントの振る舞いや出力を観察しチェックし続ける行為である。

※上記定義はGreg Poirier氏がMonitorama 2016というカンファレンスで発表したものです (こちらのリンクから動画が見れます)。

監視の目的

上記のように、監視とは対象のシステムやコンポーネントの振る舞いをチェックし続けることです。ではなぜそのようなことをするかといえば、システムに問題が発生して動かなくなると、システムの利用者・システムの管理者の双方にとってデメリットしかないからです。

それでは対象のシステムが正常に動いているとは、どのような状態のことを指すのでしょうか。また正常でないことが確認できたらどうすれば良いのでしょうか。

監視の目的については、第1章で幾つかヒントが書かれています。

「動いている」とはどういう意味か。「動いている」かどうかを監視しよう

システムが「動いている」状態については、具体例が紹介されています。

例えば、Webアプリケーションの場合、HTTPでGET / した結果を確認するといったことです。 HTTPレスポンスコードを記録し、HTTP 200 OK が返ってきているか、 ページに特定の文字列があるかどうか、 さらにリクエストのレイテンシが小さいかどうかを確認します。

このように「動いている」状態を定義することで、監視の設計に対する基準を設定することができます。上記の場合、例えばHTTP 200 OKが返ってきているけどレイテンシが増大していれば、何か重大な問題が発生していることがわかります。

システム管理者としてのキャリアを始めた頃、私はリードエンジニアのところへ行きました。 あるサーバのCPU使用率が高いことを伝え、どうしたらよいか聞きました。彼の答えは私を驚かせるものでした。 「そのサーバはやるべき処理はしてるんだろう?」そうだと私は答えました。「それなら、何も問題はないじゃないか」。

逆に言えば、「動いている」状態であれば、何か数値が異常なものがあったとしても、それは緊急性を要するものではないと判断することもできます (もちろん将来的に問題が表出する可能性もあるので、原因調査は必要です)。

この場合、「動いている」とはどういう状態なのでしょうか。サービスやアプリケーションのオーナーに聞いてみることから始めましょう。

では「動いている」状態をどうやって定義すれば良いのでしょうか。筆者はシステムの開発者や責任者と話して聞き出すことが有効だと述べています。

ここまでで、監視の目的についてまとめておきます。

監視の目的とは、システムやコンポーネントが「正常に」動いているかどうかをチェックすることである。正常に動いていない、何か問題がある場合にはその問題を修正する必要がある。またシステムに異常があると判断する基準は、そのシステムが「正常に動いている」という定義から外れている場合を指す。

2. 優れた監視システムの定義

監視の目的についてはわかってきました。ではそれを踏まえて、優れた監視システムとはどのようなシステムを指すのでしょうか。

監視システムのデザインパターンについては、第2章で幾つか紹介されています。

監視の目的を踏まえた上で特に参考になりそうなのが、2つめのデザインパターンである"ユーザ視点での監視"です。

まず監視を追加すべきなのは、ユーザがあなたのアプリケーションとやり取りをするところです。

Apacheのノードが何台動いているか、ジョブに対していくつのワーカが使用可能かといったアプリケーションの実装の詳細をユーザは気にしません。

ユーザが気にするのは、アプリケーションが動いているかどうかです。

監視の目的は、システムが「動いている」状態かどうかを見張ることです。そしてシステムが「動いている」状態というのは、やはりそのシステムを利用するユーザが利用できる状態にあることを指すと思います。それならば、ユーザ視点で監視システムを構築することは理にかなっていると感じます。

しかし一方で、優れた監視システムとはすぐに出来上がるものではない、とも述べています。なぜなら、監視対象のシステムは変化し続けるものであり、その都度監視方法や仕様も変化しなければならないからです。 世界的に有名な企業が自社開発・公開したツール群も、数年後には使われなくなり、また新しいツールが開発されるかもしれません。

世界レベルの監視システムは1週間でできるものではなく、数カ月あるいは数年間にわたる継続した注意深さとか以前から生まれるものです。あなたはこの長い道のりの途中にいるのです。

ここまでで、優れた監視システムについてまとめておきます。

優れた監視システムを一概に定義することは難しい。それは対象のシステムが常に変化を続けるからである。監視システムはその変化に対応するため、常に改善を続ける必要がある。その中でも、まず最初にユーザ視点での監視を考えることで、優れた監視システムを作り出す可能性を高めることができる。

3. 具体的な設計の指針

優れた監視システムを一言で定義することはできないようですが、それを目指し続けることは可能であることが見えてきました。それでは、具体的にどのような指針を元に、監視システムの設計を行うのが良いのでしょうか。

ここまでまとめてきて、個人的に重要だと感じたのが以下の2点です。

  • システムが「動いている」、つまりユーザが利用できる状態であることを念頭に置いた監視システムの設計
  • 監視対象のシステムやコンポーネントは常に変化するので、変化に対応できるよう柔軟性を持った監視システムの設計

1つめに対しては、第5章の「ビジネスを監視する」にて紹介されています。

ビジネスKPIを技術指標に結びつける

ビジネスKPIとは、ビジネスにおけるKPI (Key Performance Indicator)であり、会社のビジネスがどのような状態であるかを測る指標になります。また技術指標とは、システムと直接紐付けはできないような数値を推定するのに手助けとなるような、システム上で表示される指標のことを指していると思われます。

ビジネスはユーザの動向によって左右されます。そのためビジネスKPIにはユーザのアクション(ログイン、商品購入、コメントなど)を含みます。 これらを結びつける、つまりビジネスKPIを推定できるような監視メトリクスを設定することで、監視の目的と直結した監視システムを設計することができるようになります。具体的には以下のような感じです。

ビジネスKPI 技術指標
ユーザのログイン ユーザのログイン失敗、ログインのレイテンシ

ではそもそものビジネスKPIを知るにはどうするか、そしてどのような技術指標を定めればよいのでしょうか。

どのようにアプリケーションが動いていて、何を計測するのが重要かを理解できるよう、間違いない方法を教えます。 それは、人と話すことです。

プロダクトマネージャやエンジニアチームのリーダーと話すことで、測るべき技術指標を決定することができます。監視設定を定める上でのコミュニケーションの重要性については、本書で度々述べられています。

また2つ目に対しては、第2章で「組み合わせ可能な監視」として紹介されています。

専門化されたツールを複数使い、それらを疎に結合させて、監視「プラットフォーム」を作ることです。 このパターンは、多くの人が馴染みがあるであろうモノリシックなツール、代表的なもので言えばNagiosのようなツールとは対照的です。

これにより柔軟な監視システムを作ることができます。

あるツールがやり方に合わなくなった時、監視プラットフォーム全体を置き換えるのではなく、そのツールだけを削除して他のもので置き換えればよいのです。

ここまでで、具体的な設計指針についてまとめます。

優れた監視システムを作るため、まずはビジネスKPIと結びつくような技術指標を決定する。そして監視対象の変化に対応できるよう、複数の監視ツールを組み合わせた柔軟な監視プラットフォームを構築する。

まとめ

ここまで、監視に対する個人的な疑問に対する回答を探し、述べてきました。これまで私の中でイマイチまとまらなかった、監視の目的や具体的な設計指針までが一冊に書かれており、とても参考になりました。はじめに述べましたが、私はこれまで監視業務にはほとんど関わっておらず、監視業務とはどんなものか、何が目的か、といったところをイメージできませんでしたが、この書籍を通じて少しずつイメージができるようになったと思います。なので、監視のことを全然知らない、監視業務に就いたことがない方にもオススメしたい、素晴らしい一冊でした

またこの書籍には、より具体的な監視項目についてもたくさん述べられています。アラートはどのように設定するか、サーバやフロントエンドは何を監視すれば良いか、といったことが述べられていますので、より具体的な監視の設定項目については、これらの章を読んでみると参考になると思います。