しんさんのブログ

科学や技術のこと読書のことなど

TensorflowのTutorial "MNIST For ML Beginners"を試した時のメモ

Tensorflowを勉強するにはTensorflowのTutorialを自分で入力して実行しながら理解するのが一番手っ取り早いです。
特に初めてTensorflowを動かすときには、以下のリンク先のMNIST for ML BeginnersというTutorialがおすすめです。
MNIST For ML Beginners  |  TensorFlow

このチュートリアルは、単純な1層のネットワークで手書き文字を認識するというものです。
1層ですので、重みとバイアスも一つしかありませんが、それでもニューラルネットワークに必要な最小限の要素をすべて含んでいます。

TensorflowのtutorialにはMNISTの手書きデータを使うものはこのbiginner用とCNNを使ったものの2種類があります。
以下に、実際にコードを理解して実行したときの自分用のメモを残しておきます。

                  • -

mnist.train.images は [画像の数, 28x28=784] の shapeで値は0-1の画素の輝度
ラベルは 0~9 の数字でone-hot表現されています。
one-hot表現は例えば、
3 なら [0,0,0,1,0,0,0,0,0,0]
というふうに表現されます。
従ってラベルは
mnist.train.labels [画像の数, 10]というshapeで、値はfloatです。

> x = tf.placeholder(tf.float32, [None, 784])

xは学習データ(手書き数字の画像データを1次元にしたもの), Noneは画像の数は任意の長さを取りうることを意味する。
placeholderはこのように、学習データなどを入れる入れ物。

重みやバイアスは、実行時に随時更新されますので、tfのvariable変数として以下のように定義しています。
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

wは入力が784次元で出力が10次元なので[784, 10]となります。 
784列10行の行列と思えばいい。

ニューラルネットワークの計算のcoreの部分です。
> y = tf.nn.softmax(tf.matmul(x, W) + b)

tf.matmul(x, W)の部分ですが、データ数をNとすると
xは N x 784 の行列で, Wは 784 x 10の行列なので、結果のyはN行10列の行列です。
つまり各行にxの各行にたいする画像データを0から9のどの数値と予測したかの確率がyの各行に入ります。

この予想された出力yと正解とを使って表されたロス関数を最小化することで、適切なW,bを決めます。
従って正解も上記yと同じshapeである必要があるので、
> y_ = tf.placeholder(tf.float32, [None, 10])
というように、N行10列の行列としてplaceholderを用意しておきます。

多値分類問題なのでロス関数としてcross entropyを使用します。
> cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
tf.reduce_meanは指定されたshapeの成分についての和をとる関数で、今の場合は、reduction_indices=[1]となっているので
1番目の要素についての和をとります。
つまり、0から9の予想された数値の方向に和をとり、N個の要素をもつ1次元配列にします。
この1次元配列は、N個の学習データそれぞれに対するロス関数の値を格納しています。
最後に、tf.reduce_meanで、N個の学習データ(バッチ処理1回分)のそれぞれのロス関数すべての平均をとってこれを最適化関数として使用しています。

ここが少し混乱しやすいのですが、実際のtutorialのコードでは上記の部分が以下のようにsoftmax_cross_entropy_with_logitsという関数で書かれています。
>cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
解説を読むと、前者の書き方のほうが数値的に不安定するそうです。

実施のコードで試してみると、前者の場合の精度は私の実行環境では、約0.92、後者では、0.91でほぼ同じ結果になりました。
実行時間は前者では約0.77sec, 後者では0.90secでlogits関数の計算の分だけ速度が落ちているようです。
(実際のコードを一番最後に添付しておきます)

logits関数とは、L = log(p/(1-p)) のことで変形するとp= 1/(1+exp(-L))で、p=0.5の時0,P>0.5のときL>0, p<0.5のときL<0となる関数。
確率p [0, 1.0] -> [-inf, inf]にマップする。
softmaxで確率[0, 1.0]の出力を得て、それをlogitsに通すことで正解の確率が大きければ値はより大きくなる。マイナスが掛かっているのでloss関数としては値が小さくなるということです。
確率が0や1に近い時に勾配が数値的に消失したり不安定にあったりするのを防ぐ効果があります。
stackoverflow.com


最適化はGradientDescentで学習率は0.5に設定しています。
>train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

学習結果の評価
>correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
tf.argmaxは引数の配列の中で一番値の高いもののindexを返す。yは[教師データの数, 正解の確率]という配列になっているので、
1番目の要素(数値の0から9のそれぞれの予測確率)から一番高いもの一つが選ばれます。
tf.equalで教師データと一致しているときにTrue, 異なっているときにFalseをセットします。

# 精度の計算
>accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.castはTrue, Falseを以下のように数値に変換
[True, False, True, True] -> [1,0,1,1]
tf.reduce_meanでこの1,0,1,1,・・・の平均をとり結果を全体の精度とする。

今回試してみた実際のコードは以下のようになります。

import tensorflow as tf
import time

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)

start_time = time.time()

x = tf.placeholder(tf.float32, [None, 784])

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

y = tf.nn.softmax(tf.matmul(x, W) + b)

y_ = tf.placeholder(tf.float32, [None, 10])
#cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
# interactiveSessionでtfのsessionを作成
sess = tf.InteractiveSession()
# global変数の初期化
tf.global_variables_initializer().run()

print ("--- start training ---")
for _ in range(1000): # 1000ステップの学習を行う
    # 1 batch = 100個のデータでbatch学習を行う
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
print ("--- finish training ---")

# 予測yと正解ラベルy_が同じ値であればTrue、ことなればFalseがセットされる
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
# 精度の計算
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print("精度:")
print( sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

end_time = time.time()
print ("time: " + str(end_time - start_time) + "sec")

深層学習でよく使われる学習用データセットまとめ

3D系のデータセットは別の記事にまとめました
wshinya.hatenablog.com

arXivTimesに素晴らしいまとめがあります。
github.com

商用利用可能な6種類の最大4Kサイズの人物の肌テクスチャが無料配布されています

商用利用可能な6種類の最大4Kサイズの人物の肌テクスチャが無料配布されています • 3DCG最新情報サイト MODELING HAPPY

MNIST:

手書き文字認識学習用データ
28pixel x 28pixel x 1channel
60000枚:training用
10000枚:test用
クラス: 10個の数字

機械学習におけるHello World的なデータ
ちなみに、MNISTとはMixed National Institute of Standard Technologyの省略形で、日本語に訳せば"国立標準技術研究所"でしょうか。
日本では旧通産省工業技術院、今の産業技術研究所に相当するアメリカの研究機関です。

CIFAR-10:

一般物体認識のベンチマークとしてよく使われている写真データ
32pixel x 32pixel x 3channel
50000枚:training用
10000枚:test用

画像は10種類にラベル付けされている
クラス:airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck

AlexNet(SuperVision)のAlex Krizhevskyさんが整備
(AlexさんはILSVRC2012で優勝)
以下のサイトに詳しく解説されています
aidiary.hatenablog.com

CIFAR-100:
CIFAR-10の100種類のクラス分け版
qiita.com

Microsoft COCO ( MS COCO ):

画像と、画像の認識、セグメンテーション、画像を説明する短いキャプションがセットになったデータ
以下の解説を参照のこと
qiita.com

Imagenet:

ダウンロードできるのは学術関係者に限定される
ImageNetとはスタンフォード大学がインターネット上から画像を集め分類したデータセット。一般画像認識用に用いられる。ImageNetを利用して画像検出・識別精度を競うThe ImageNet Large Scale Visual Recognition Challenge(ILSVRC)などコンテストも開かれる
現在、1400万枚以上のデータが集まり、2万クラス以上あるらしい
ネット上の画像を集めてきているので結構リンク切れがあったりするらしい。

GoogleのOpen Image Dataset v4

Googleが用意した1,540万のバウンディングボックス含む190万画像600カテゴリのデータセット
Open Images V7
ai.googleblog.com
ダウンロードは以下のサイトから
github.com

Caltech101

101個にカテゴリー分類されたデータ.
画像の品質がいい。
http://www.vision.caltech.edu/Image_Datasets/Caltech101/

The PASCAL Visual Object Classes Homepage

Yoloの学習済みモデルが公開されている.
http://host.robots.ox.ac.uk/pascal/VOC/

YFCC100M

flickerから収集した画像、動画データ.
http://yfcc100m.appspot.com/?

OpenImageDataset v4

物体検出用のデータ。オブジェクトの位置・サイズ、名前がつけられている。
ライセンス:The annotations are licensed by Google Inc. under CC BY 4.0 license. The images are listed as having a CC BY 2.0 license.
https://storage.googleapis.com/openimages/web/index.html

顔画像のデータセット

約13000枚の顔画像で、人の名前がフルネームでつけられている。
人は5000人以上、1680人は2枚以上の画像あり。
人種、表情、顔の向き、撮影の状態はランダムで、多様性あり。
http://vis-www.cs.umass.edu/lfw/

Celebの顔写真のデータセット

20万枚の画像
http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html

102種類の花の画像データセット

データサイズは8189枚
Visual Geometry Group - University of Oxford

              • -

他のデータセットもKerasのドキュメントで解説されています
https://keras.io/ja/datasets/

Tensorflow2.0 CPU/GPU版 + Keras + OpenCV ver3.0の環境構築

2019年10月 更新

  • インストール環境

- Windows64Pro
- Nvidia Geforce2080Ti

Anacondaでよく使うコマンドは以下のサイトが便利です.
qiita.com

                          • -

Windows: tensorflow CPU版

1) anaconda の仮想環境の作成
まずは、pipのアップデート
python -m pip install --upgrade pip
そして、anacondaの仮想環境の作成
conda create -n tensorflowCPU200 python=3.5 anaconda
pythonのバージョンを3.5にしたのは、OpneCVを動かすため(追記にあるようにpython: ver.3.6.*でも動くかもしれません)

2) tensorflow2.0.0のインストール
> pip install tensorflow==2.0.0

> python -m pip install --upgrade pip
をやってpipもアップグレードした

これでようやくtensorflow2.0 CPU版のインストールが成功した。
> python
>> import tensorflow as tf
で正常にtensorflowがimportできるか確認した。
次のようにすれば、実際にtensorflowのバージョンを確認できます。
print(tf.__version__)
>>> print(tf.__version__)
2.0.0
となれば成功.

conda-build関連のwarningが出ることがあるが、放置したほうがいい。下手にconda-buildを
インストールするとcondaのバージョンが古いというエラーではまるので注意。

その他必要なモジュールをインストール。例えば以下のようなモジュール。
> conda install -c menpo opencv3
> conda install h5py
> conda install matplotlib
> conda install scikit-learn
> conda install pillow
> conda install pydot -> pydotは開発がおわっているらしい
pydotの代わりにpydotpluをインストール
> conda install pydotplus
> conda install seaborn


Windows: tensorflow GPU
1) anaconda の仮想環境の作成
conda create -n tensorflowGPU200 python=3.5 anaconda
>[WinError 193] %1 は有効な Win32 アプリケーションではありません。
というエラーがでてコマンドラインからは仮想環境が作れないときは、anaconda navigatorから作成する。

2) Nvidia GPUドライバーをアップデートする
ドライバーのバージョンは26.21.14.3160(NVIDIA 431.60)/Win10.64です.

3)CUDAとcuDNNのアップデート
現在のCUDAのバージョンがCUDA 9.xなので10.0にアップデートする必要がある。
(tensorflow 2.0 のインストールドキュメントにはCUDA10.0と書いてあるので、10.1はインストールしないように注意)

https://developer.nvidia.com/cuda-toolkit-archive
からCUDA 10.0をダウンロードしてインストール

nvcc -V
で見ると
release 10.0, V10.0.130
となり、CUDA10.0であることがわかる。

  • CUDNNのインストール

https://www.tensorflow.org/install/gpu
上記ページを改めてよく読むと、
cuDNN SDK (>= 7.4.1)
と書いてあるのでインストールする。
CUDA10.0に対応するバージョンを選ぶこと。
今回は、
cuDNN v7.6.3 (August 23, 2019), for CUDA 10.0
を選択しました。

4) tensorflow2.0.0 GPU版のインストール
> pip install tensorflow-gpu==2.0.0

後の作業はCPU版と同じです.
Ubuntuでもほぼ同じプロセスでCUP版,GPU版の構築が可能でした。

                                                          • -

おまけ:
仮想環境は新たにつくらず、tensorflowのバージョンのみアップデート
CPU版:
pip install -U tensorflow==2.0.0

                                                          • -

(2019/5/10追記)
Ubuntuで上記と同じようにしてpython+openCV環境を構築すると、WindowsではOKだった、
>cv2.namedWindow() 
を使用して新しいwindowを開こうとするとエラーになりました。openCVのビルド時にgtkがONになっていないためのようです。
以下の手順でpython+openCV環境を構築するとうまく動きました。
>conda create -n tensorflowCPUKerasOpenCV3 anaconda
>conda activate tensorflowCPUKerasOpenCV3
>conda install -c menpo opencv3
>conda install keras
これで、
python: ver.3.6.8
opencv: 3.1.0
tensorflow: 1.13.1
keras 2.2.4
がセットアップできます。

ひょっとしたら以下のライブラリのインストールも必要かもしれません。
GUI操作のためのGTKライブラリ:
sudo apt-get install libgtk-3-dev

JPEG, PNG, TIFFなどの画像処理に対応するためのライブラリ:
sudo apt-get install libjpeg-dev libpng-dev libtiff-dev

ムービー処理に対応するためのライブラリ:
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev

様々なOpenCVの関数を最適化するためのライブラリ:
sudo apt-get install libatlas-base-dev gfortran

デベロッパーツールのインストール:
sudo apt-get install build-essential cmake unzip pkg-config

上記は以下のサイトを参考にしました。
avinton.com


(参考)
OSError: Unable to open file (unable to open file: name =
というエラーが出るとき、
C:\Users\username\.keras\datasets に残っている以前ダウンロードしたデータをいったん消去すれば解決することがあるようです

proxyの設定は、
C:\Users\username\.condarc

proxy_servers:
http: http://proxyservername:portNum
https: https://proxyservername:portNum
を追加し、Windows環境変数
HTTP_PROXY = http://proxy-server:portNum
HTTPS_PROXY = https://proxy-server:portNum
を追加すればいいです。

NVIDIA GPUの歴史ざっくりまとめ

GPUアーキテクチャの変遷について自分用のメモです

2006年 CUDA発表
G80アーキテクチャ発表 初めてのUnfied shader architecture
GeForce8800

2007年 Tesla HPC用
TSUBAME1.2 Tesla S1070 2008年Top500で20位

2009年 Fermi世代
double precisionの大幅な性能向上
TSUBAME2.0 Tesla M2050 TOP500で4位

2012年 Keplar世代 28nm
電力性能比の向上
多くのスパコンに採用される
TSUBAME KFC Tesla K20x Green500で1位

2014年 Maxwell世代 28nm
single precisionがKeplarの1.4倍に
doubleはKeplarより劣る -> スパコンには採用されなかった 
ディープラーニングなどのワークロードに最適化

2016年 Pascal世代 16nm
HBM2の採用
NVLink 160GB/s GPU間インターコネクト
Unified Memory CPU-GPU memory
Tesla P100
FP32: 10.6TFlops
FP64: 5.3TFlops 倍精度の性能向上
FP16: 21.2TFlops
HPCも機械学習にも対応
TSUBAME 3.0 Green500で1位
GPU仮想化に対応

2017年 Volta世代
Tensor coreの追加: 4x4 half float matrix mul -> half or single float / one clock
120TFlops
Tesla V100
AWS TeslaV100x8のP3インスタンスの提供

機械学習基礎の基礎: 過学習を防ぐ正則化

前回のブログでは2次元平面上の8つの点を10次関数でfittingしました。
すると、fittingする関数の自由度が高いために、8つの点すべてを通るようにうまくパラメータを調節できてしまい、学習データに対しては100%近い正解率がでるのに、新たなデータに対する予言の精度が下がってしまうという現象をみました。
これが過学習というもので、別の言い方をすると汎化性が低いとも言えます。
この過学習を防ぐためには学習データを増やすか、fittingする関数の自由度を下げればいいということがわかります。
ここでは自由度を下げるために、L2正則化と呼ばれる手法を実装してみます。
実装はとても簡単で、最適化する目的関数に、(1/2) theta^2 を追加するだけです。
目的関数を最小化するようなthetaの組を求めるときに、この項の効果でthetaをあまり大きな値にできないという拘束が生まれます。
これにより、自由度が下がって過学習を防ぐことができるという仕組みです。
実際のコードは以下のようになります。

###############
# overfittingと正則化の実装
###############
import numpy as np
import matplotlib.pyplot as plt

# 3次関数を定義
def f(x):
	return 0.2*(x**3 + x **2 + x)

# ノイズを付与する
trainDataX = np.linspace(-1, 1, 8)
trainDataY = f(trainDataX) + 0.05 * np.random.randn(trainDataX.size)

# dataを平均ゼロ、標準偏差1に標準化する
mu = trainDataX.mean()
sigma = trainDataX.std()

def standardization(x):
	return (x - mu)/sigma

trainDataXSTD = standardization(trainDataX)

# 10次の多項式でfittingする
# data が8個しかないのに、自由度が10個ある関数でfitするのでoverfitする

def ToMatrix(x):
	return np.vstack([
		np.ones(x.size),
		x, x**2, x**3, x**4, x**5, x**6, x**7, x**8, x**9, x**10,
		]).T

X = ToMatrix(trainDataXSTD)

# パラメータの初期値設定
theta = np.random.randn(X.shape[1])

# fitting関数
def fitfunc(x):
	return np.dot(x, theta)

# loss 関数(最小2乗誤差)
def E(x, y):
	return 0.5 * np.sum((y - fitfunc(x))**2)

eta = 1e-4 #学習率
l = 5.0 #正則化係数

# loss 関数の変化量
diff = 1
### 学習 ###
error = E(X, trainDataY)
while diff > 1e-8:
	# 正則化項
	r = l * np.hstack([0, theta[1:]]) #バイアスには正則化を足さない
	theta = theta - eta * (np.dot(fitfunc(X) - trainDataY, X ) + r);
	current_error = E(X, trainDataY)
	diff = error - current_error
	error = current_error
# 結果を表示
x = np.linspace(-1, 1, 100)
xSTD = standardization(x)
plt.plot(trainDataXSTD, trainDataY, 'o') #3次関数に誤差を乗せた学習データ
plt.plot(xSTD, fitfunc(ToMatrix(xSTD))) # fittingした10次関数の曲線
#plt.show()
# データをplotする#plt.plot(trainDataX, trainDataY, 'o')
plt.plot(xSTD, f(x))
plt.show()

このコードを実行すると、次のようなグラフが描画されます。
正則化係数lを変更すると正則化項の強さが変わって、過学習の度合いが変化するのがわかります。
f:id:wshinya:20180401231849p:plain
グラフは、緑の線が学習データの基になった3次関数で、赤が正則化後のfittingカーブです。
前回の正則化なしの結果に比べて、データの基になった関数をより近似していることがわかります。

機械学習基礎の基礎: 過学習

今回は過学習についてみて見ます。
まずは訓練データを作成します。
訓練データは3次関数にノイズを加えた8つの点(x, y)構成することとします。
プログラムの初めに8点生成して、それらの点を10次関数でfittingすることにします。
データの個数がfitting関数の自由度よりも小さいのでoverfittingすることが予想できます。
最適化の目的関数は最小2乗誤差です。
損失関数の変化が小さくなるまで学習を繰り返し、fittingする10次関数の係数thetaを決めます。
コードにまとめると,

###############
# overfittingと正則化の実装
###############
import numpy as np
import matplotlib.pyplot as plt

# 3次関数を定義
def f(x):
	return 0.2*(x**3 + x **2 + x)

# ノイズを付与する
trainDataX = np.linspace(-1, 1, 8)
trainDataY = f(trainDataX) + 0.05 * np.random.randn(trainDataX.size)

# dataを平均ゼロ、標準偏差1に標準化する
mu = trainDataX.mean()
sigma = trainDataX.std()

def standardization(x):
	return (x - mu)/sigma

trainDataXSTD = standardization(trainDataX)

# 10次の多項式でfittingする
# data が8個しかないのに、自由度が10個ある関数でfitするのでoverfitする

def ToMatrix(x):
	return np.vstack([
		np.ones(x.size),
		x, x**2, x**3, x**4, x**5, x**6, x**7, x**8, x**9, x**10,
		]).T

X = ToMatrix(trainDataXSTD)

# パラメータの初期値設定
theta = np.random.randn(X.shape[1])

# fitting関数
def fitfunc(x):
	return np.dot(x, theta)

# loss 関数(最小2乗誤差)
def E(x, y):
	return 0.5 * np.sum((y - fitfunc(x))**2)

eta = 1e-4 #学習率

# loss 関数の変化量
diff = 1

### 学習 ###
error = E(X, trainDataY)
while diff > 1e-8:
	theta = theta - eta * np.dot(fitfunc(X) - trainDataY, X)
	current_error = E(X, trainDataY)
	diff = error - current_error
	error = current_error

# 結果を表示
x = np.linspace(-1, 1, 100)
xSTD = standardization(x)
plt.plot(trainDataXSTD, trainDataY, 'o') #3次関数に誤差を乗せた学習データ
plt.plot(xSTD, fitfunc(ToMatrix(xSTD))) # fittingした10次関数の曲線
#plt.show()
# データをplotする#plt.plot(trainDataX, trainDataY, 'o')
plt.plot(xSTD, f(x))
plt.show()

f:id:wshinya:20180327000706p:plain
緑の線は学習データを作成するために使った3次関数のグラフ。
点は3次関数にノイズを加えて作成した学習用データ。
さらにオレンジの線は10次元関数でfittingした結果のグラフ。
つまり学習結果です。
オレンジのグラフは元の3次関数のグラフとはかなりずれた結果になっていますので、この結果を使用して新しい入力xに対して出力yを求めると期待しているものとは異なった結果となりそうです。
与えられた8つの学習データに対してはそれなりにいい近似になっているように見えますが、未知の入力に対しての予想精度が悪くなっている、これが過学習です。