KerasのCallbackをColaboratoryで試してみる

本日は、機械学習の学習中に操作をするCallbackについてです。

機械学習を勉強し初めの時に、混乱していたことの1つが、どんなデータのやり取りが行われているのか、分からない。と、言ったことでした。

特に、モデルの学習中には、一度学習をスタートすると、何だか分からないけれども指定したエポック数だけ処理されている。といった感覚でした。

そんな、中でモデルの学習中に、操作を行うことが出来る関数の集合がCallbackです。
詳細は、Kerasのオフィシャルで

■コールバック
https://keras.io/ja/callbacks/#modelcheckpoint

Callbackは、幾つかの目的の為に使われます。
・学習途中に、過学習を抑制するために、操作を停止したり、学習率を低下させる
・学習途中の内部の処理情報を可視化 or 保存させる

■KerasでCallbackをモデル学習にセットする方法

kerasでモデル学習する際には、
・model.fit()
・model.fit_generator()

2つのどちらかを利用すると思いまします。

2つの違いにはついては下記を参照

KerasでCallbackをモデル学習にセットさせる方法は、シンプルで
model.fit( callbacks= ) / model.fit_generator(callbacks=) の引数 callbacks= に対して、Keras.callbacks.Callback にあるインスタンスのリストを記載します。

例えば、よく使うのでいうと
ModelCheckpoint:学習中のモデルを保存することが出来る
EarlyStopping:学習中に値の変化が停止した時に訓練を終了出来る
TensorBoard:TensorBoardで可視化

■ColaboratoryでCallbackを用いて、学習中のモデルを保存

ModelCheckpointを使用して、学習中のモデルを保存してみましょう。

Colaboratoryで新規のフォルダを作成します。
フォルダ名称をlogとします。 パス : ‘/content/log’
このlogフォルダの中に格納していきます。

#保存するファイル名称の設定
ckpt_name = 'weights-{epoch:02d}-{loss:.2f}-{accuracy:.2f}-{val_loss:.2f}-{val_accuracy:.2f}-.hdf5'

log_dir='/content/log' # modelのデータ格納場所

#ModelCheckpointのインスタンス化
cbcheck = ModelCheckpoint(os.path.join(log_dir, ckpt_name),
                               monitor='val_acc', verbose=0,
                               save_best_only=False,
                               save_weights_only=True,
                               mode='auto', period=1)

この、ModelCheckpointをインスタンス化した、cbcheckを、fitの引数に設定します。

#色々と省略して
model.fit(callbacks= cbcheck)

そうすると、
log_dir=’/content/log’ # modelのデータ格納場所
の箇所にエポック毎に(period=1で設定)モデルが保存されております。

学習のモデルを操作できたり、可視化出来たりすることで
機械学習への距離感が少し縮まりました。
途中経過のモデルを活用して、predictしてみるのも面白いかと思います。

機械学習のデータセットを正規化する方法

本日は、機械学習におけるデータセット作成の注意点をお伝えします。

結論からいうと、正規化しましょう。ということです。
ありのままの数字では、学習する上で処理しづらいとうことです。

自分もまだまだ分かっていないところだらけですが
機械学習の根本の概念として、ベクトル距離というのがあります。

データセットを作る際には、あらゆるデータ(画像、動画、音、テキスト…)を数値化していきます。 ただ、これは実際の数値に意味があるのではなくて、数値感の距離を測っているのです。実際の数値は学習していく上では、重要な数値ではないのです。

なので、データ間での、距離を正しくしましょう。というのが、データセットの正規化です。極端にいうと、データを [ 0 ~ 1 ] のデータに変換しましょうというこです。

正規化のやり方は色々とあるようです。下記を参考にしました。

https://sinyblog.com/deaplearning/preprocessing_002/#i-2

After Effectsで、数値処理を行うプログラムを記載する方法

本日は動画制作のTIPSを記載していきます。

動画編集ソフト何を使ってますでしょうか。
私はAdobe国民なので、Premiere Pro・After Effects両方を使っております。

ソフトの細かい操作方法は置いておいて、
今回はAfter Effectsの中で様々なparameterをプログラムで処理していける機能について紹介します。
(簡単部分のみ)

それが、プロパティピックウィップという機能を使います。

After Effectsで、プロパティピックウィップを使って、カウントアップ機能を作る

それでは、実際に操作していきます。

まず、テキストで「1」と記載します。

そして、テキストに対して、プロパティピックウィップ機能を有効化します。

プロパティピックウィップ機能は、「渦巻」のような部分を押すことで有効化されます。

プロパティピックウィップ機能は様々なparameterに対して有効的に使うことができます。

今回は、テキストの値をカウントアップしていきます。
テキストの値に対して、プロパティピックウィップ機能を有効化します。
ソーステキストのプロパティピックウィップ機能を有効化して下さい。

すると、
エクスプレッション:ソーステキスト が新たに表示されます。
すると、プログラムを記載する場所が表示されるので、そこで設定していくことが可能です。

今回は、カウントアップを表示させるプログラムを記載します。
非常にシンプルなプログラムで終わります。

time.toFixed(1)

timeという、プロパティが存在していて、これはコンポジションの進捗時間です。
(細かい定義は、あるかもしれない。)

.toFixed(1)
timeに対して、少数の1桁まで残すメソッドです。

他にも、数値を四捨五入したり、切り捨てしたりと、メソッドがあるので見てみるといいと思います。

画像データを正規化。Numpyの高次元配列のデータ型を変更

画像読み込むと、0~255階調のデータになっているのを、0~1のデータへ正規化していきます。

まずは、Numpyのデータ型の確認方法です。
dtype属性で確認します。
trainingDataというndarray型の配列の場合

trainingData.dtype

unit8

など表示されます。

0~1のデータは、floatデータです。
floatデータ型に変換しつつ、0~1に変換します。

astype メソッドを使います。
配列の中のデータを丸っとキャストすることが出来ます。

trainingData.astype(np.float32) / 255

これで、0~255階調のデータになっているのを、0~1のデータへ正規化できます。

GANでデータセットを作るために、Numpyのreshapeを使う方法

画像pngデータを活用して、データセットを作るために必要だったreshapeの使い方を紹介したいと思います。(違うやり方もあると思います。)

以前、reshapeの基本的な使い方は下記で記載しました。

本記事の操作としては、下記記事で作成したデータセットを基に話を進めていきます。

画像データは、1つの画像に対して、( 350 × 350 ) の配列に格納された画像データが生成出来ました。

しかし、機械学習をしていく際に、
1つの画像が1つのndarray型に入っている必要があります。

(もしかしたら、行と列が別々の配列でも良い可能性がありますが、今のところ学習させる際には、1つの画像が、1つの配列に入っている方がススメやすいと思います。)

もう少し説明します。
上記で作ったデータを見ていきます。

training_data.shape

(3, 350, 350)

3枚の画像が、350×350のサイズデータとして存在している。ということですね。

機械学習していく際には、( 画像個数, X )といったデータ型にする必要があります。
ここで Numpy reshapeを使います。
※X = 1次元の画像データ

batchdata = training_data.reshape(3,122500)
#122500 = 350 × 350
batchdata.shape

(3, 122500)

こういったカタチで、機械学習する際に必要なデータ型になりました。

このデータって、変換されてしまったら画像データとして存在しないのでは?と思うかもしれません。確認してみましょう。

一度、reshapeして、変換したデータを、元に戻してみます。

batchReverse = batchdata.reshape(3,350, 350)

for p in batchReverse:
  plt.imshow(p, cmap="gray")
  plt.show()

実行してみると、元の画像が表示されます。

ここで、疑問なのは、データの変換される順番とかが決まっているのか?
ということです。
いま試してみたのは、同じ操作をもとに戻しただけですが、これが複雑な操作の後に、戻そうと思った際に、どうなるのか。疑問は深まるばかりです。

Google Colaboratory で 画像データを読み込んでデータセットを作る方法

Google Colaboratory便利ですね。
Google Colaboratoryで、データセットを作成したく、色々と試してみました。

正直、これで良いのかまだ、不明なところがあるのですが、ひとまず、紹介です。
やることをざっくりと分けると、下記フローでした。

(1):画像データ一覧をZipファイルに圧縮
(2):ZipファイルをGoogle Colaboratoryへアップロード
(3):Google Colaboratoryで、zipファイルを解凍
(4):Zipファイル内の画像データのファイルパス一覧を取得
(5):画像のFilePathを個別に取得
(6):画像データをNumPy配列ndarrayとして取得
(7):画像のリサイズ
(8):NumPy配列ndarrayとして、データセットとして保存

(1):画像データ一覧をZipファイルに圧縮

これは、普通にやってください。
※犬の画像を3枚取得して。ファイル名を(a.png, b.png, c.png)としました。
※Zipファイル名をdataset_vol2.zipとしました

(2):ZipファイルをGoogle Colaboratoryへアップロード

ZipファイルをGoogle Colaboratoryへアップロードします。
Colaboratoryファイルの、左部を開くとアップロードという箇所があるので、ここから、zipファイルをアップロードします。

(3):Google Colaboratoryで、zipファイルを解凍

Colaboratoryで、Zipファイルを解凍する際はZipファイルがある場所まで移動しましょう。
まずは、自分が何処の階層にいるのかを確認しましょう。

!ls

dataset_vol2.zip
sample_data

dataset_vol2.zip がある階層です。
それでは、解凍していきましょう。
解凍するコマンドは、!unzip です。

!unzip dataset_vol2.zip

Archive: dataset_vol2.zip
creating: dataset_vol2/
inflating: dataset_vol2/b.png
creating: __MACOSX/
creating: __MACOSX/dataset_vol2/
inflating: __MACOSX/dataset_vol2/._b.png
inflating: dataset_vol2/c.png
inflating: __MACOSX/dataset_vol2/._c.png
inflating: dataset_vol2/a.png
inflating: __MACOSX/dataset_vol2/._a.png

これで、Zipファイルが解凍されました。

(4):Zipファイル内の画像データのファイルパス一覧を取得

解凍したファイルの中へ移動していきましょう。

#ファイル移動
%cd dataset_vol2
!ls

a.png b.png c.png

(5):画像のFilePathを個別に取得

必要なライブラリをimportします。

import matplotlib.pyplot as plt
import os
import cv2
import glob
import numpy as np

フォルダの中のファイルPath一覧を取得する方法として、
Pythonのglobモジュールを活用しました。 詳しくは、下記を参考にしてます。
ワイルドカード(*)などを使って柔軟にFilePathを取得できるので、便利ですね。

Pythonで条件を満たすパスの一覧を再帰的に取得するglobの使い方
https://note.nkmk.me/python-glob-usage/

また、現在位置は、dataset_vol2の中にいるので、
このままでも問題ないですが、globを使う際には、取得したいファイルの中に移動してあげるのがいいでしょう。

そのために、コマンドじゃなく、ファイル階層を移動できる下記を使いました。
os.chdir(“Path名称”)

#階層移動
os.chdir("/content/dataset_vol2")
#階層内のPathを全取得
path = glob.glob("*")
print(path)

[‘a.png’, ‘c.png’, ‘b.png’]

もちろん、今回はファイル名を(a.png, b.png, c.png)にしたので、
当たり前の結果ですが、データ個数が多くなれば必要な作業です。

(6):画像データをNumPy配列ndarrayとして取得
(7):画像のリサイズ
(8):NumPy配列ndarrayとして、データセットとして保存

ここからは、一気にデータセット作成までいきましょう。

#データセットを格納する変数
training_data = []
#リサイズ後のサイズ
IMG_SIZE = 350

#個別のFilePathに対して処理していきます。
for p in path:
   pathEach = p
   print(pathEach)

  #NumPy配列ndarrayとして読み込まれ、ndarrayを画像として保存
   imageTTTT = cv2.imread(pathEach,cv2.IMREAD_GRAYSCALE)
  #画像のりサイズ
   img_resize_array = cv2.resize(imageTTTT, (IMG_SIZE, IMG_SIZE))
   print(imageTTTT)
   print(img_resize_array.dtype)
   training_data.append(img_resize_array)
   plt.imshow(img_resize_array, cmap="gray")
   plt.show()
   print("=============")

cv2.imreadを解説します。
OpenCVライブラリでは、様々なことができます。
imreadメソッドで、画像ファイルを読み込み、
NumPy配列ndarrayとして読み込まれ、ndarrayを画像として保存することができます。

ndarray型については、下記記事にも書きましたので参照下さい。
Numpyのarray・ndarrayって何。 簡単に説明します。
http://prglog.info/home/?p=29

imreadの詳細については、下記参考

▼Python, OpenCVで画像ファイルの読み込み、保存
https://note.nkmk.me/python-opencv-imread-imwrite/

print(training_data)

[array([[ 36, 36, 36, …, 191, 193, 193],
[ 36, 36, 36, …, 191, 192, 192],
[ 36, 36, 36, …, 190, 190, 190],
…,
[223, 221, 220, …, 225, 226, 228],
[225, 223, 222, …, 226, 228, 230],
[227, 225, 224, …, 228, 230, 232]], dtype=uint8), array([[ 84, 83, 82, …, 29, 27, 27],
[ 93, 92, 91, …, 34, 33, 32],
[102, 102, 101, …, 38, 37, 36],
…,
[126, 117, 110, …, 128, 133, 121],
[123, 117, 116, …, 134, 138, 127],
[119, 118, 122, …, 138, 142, 132]], dtype=uint8), array([[158, 158, 158, …, 142, 144, 147],
[158, 158, 158, …, 144, 146, 148],
[158, 158, 157, …, 146, 148, 149],
…,
[211, 208, 208, …, 212, 212, 211],
[219, 217, 217, …, 208, 207, 206],
[224, 223, 223, …, 205, 203, 203]], dtype=uint8)]

ここで、完了かなと思います。
ただ、他の記事を見てみると、ここからarrayメソッドを使って、ndarray型にデータを変換していたりします。

training_data = np.array(training_data)
print(training_data)

[[[ 36 36 36 … 191 193 193]
[ 36 36 36 … 191 192 192]
[ 36 36 36 … 190 190 190]

[223 221 220 … 225 226 228]
[225 223 222 … 226 228 230]
[227 225 224 … 228 230 232]]

[[ 84 83 82 … 29 27 27]
[ 93 92 91 … 34 33 32]
[102 102 101 … 38 37 36]

[126 117 110 … 128 133 121]
[123 117 116 … 134 138 127]
[119 118 122 … 138 142 132]]

[[158 158 158 … 142 144 147]
[158 158 158 … 144 146 148]
[158 158 157 … 146 148 149]

[211 208 208 … 212 212 211]
[219 217 217 … 208 207 206]
[224 223 223 … 205 203 203]]]

これで、ndarray型のデータが出来上がったということなのでしょうか。
ここは少し理解が浅いところですので、調べてみます。

PC購入時には、何を判断すればよいのか。

今日は、PC購入についてです。

正直、PCのことはよく分かっていないのですが、
WindowsのPCを購入予定でして、色々と調べていました

Macだと、ラインナップもそれ程多くないので分かりやすいかと思いますが、
Windowsは、メーカーから色々と違うのでよく分からない。という感じでした。

使用用途によって、全然違うと思います。
通常のデスクワークレベルですと、最安値のPCでも殆ど問題ないかと思います。

制作している人たち、特にCGグラフィックとか、機械学習のモデリングとか、ゲームPlayerとかは高スペックが求められ人たちかなと思います。

大きく下記4つの項目が必要なスペックかと思います。
(後は、費用対効果次第…)

・CPU
・メモリ
・GPU
・ハードディスク

CPUの考えるポイント

CPUを考えるポイントは、
・CPUのメーカー
・コア数
・スレッド数
かと思います。

コア数とスレッド数は高い方が処理スペックが高いです。
CPUのメーカーは複数あります。正直、何が一番良いのか分かっていない….

メモリの考えるポイント

処理時の容量みたいなものです。
メモリ数が大きいほど、多くの処理を一度にできます。
メモリは大きいほどよい。

■GPUの考えるポイント

GPUは、馴染みがない人も多いかもしれませんが
3D CG処理・ゲームPlayer・機械学習のモデリングとかには必須のスペックですね。
物凄い大量なデータを、並列処理できる機能です。
これも、幾つかのメーカーがあります。正直、何が一番良いのか分かっていない….

ハードディスクを考えるポイント

ハードディスクは、データを保存しておく容量部分です。
HDDとSSDの2つがあります。
いまは、SSDが主流になっている流れかと思います。

HDDの方が安価なハードディスクなのですが、衝撃に弱かったり、うるさかったりします。
SSDを購入する余裕があるならば、SSDの方をおすすめします。

というわけで、あまり良くわかっていない、WindowsPCを購入する際の検討ポイントをゆるく整理しました。
スペックと、カッコ良さと、値段を総合判断して購入したいと思います。

Numpyのarray・ndarrayって何。 簡単に説明します。

機械学習で必ず使う、Numpy。
ndarray, arrayなど混合してしまったり、Pythonのlist [a, b, c, d, e]とかと違うのか。
などなど、よく分からないことが多いと思います。

正確なことは、下記に詳細が記載されております。
分かりますでしょうか? 私は、全くわかりませんでした。

■numpy.ndarray
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html

■numpy.array
https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html

簡単に言いますと
ndarray:
Numpyの高次元配列を扱うことが可能なクラス

array:
ndarray のクラス型をしたオブジェクトをreturnする、関数

ということです。
全く、性質が異なるものですね。

詳しいことは、下記のサイトに色々と載ってます。
是非、一度見てみるといいと思います。

http://www.kamishima.net/mlmpyja/nbayes1/ndarray.html

arrayで生成するndarrayのオブジェクトと、Python配列のListは違うものなのでしょうか。実験してみましょう。

#listを準備
z = [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11]
#ndarray型のオブジェクト生成
nz = np.array(z)
print(nz)
print(z)

#print(nz)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
#print(z)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

#print(nz)で出力した値は、ndarray型のオブジェクトです。
#print(z)で出力した値は、Pythonの配列 list、そのままです。

arrayの引数にデータ(z)を入れたことで、ndarray型のオブジェクトが出来上がりました。

確認してみましょう。
ndarray型のオブジェクトは、shape属性を持ちます。(配列の型を示す)

print(nz.shape)
print(z.shape)

#print(nz.shape)
(12,)
#print(z.shape)
AttributeError: ‘list’ object has no attribute ‘shape’

あたりまえですね。
Pythonの配列 list は、shape属性を持ち合わせて無いということです。

Tensorflow 1.x の Session をゆるく理解

Tensorflow 難しいですね。
Tensorflow 2.x がリリースされています。(現在は、2020年3月)
チュートリアル、記事など参考にしたいものが、Tensorflow 1.x のときに作られたものも多く、しばらく混乱しておりました。

Tensorflow 2.x の使い方から学んでも良いのかもしれませんが、
Tensorflow 1.x の歴史を学ぶことで、Tensorflowの考え方も理解できるかもしれません。

と、言うことで
Tensorflow 2.x では廃止されたTensorflow 1.x で使われていた Session について残しておきたいと思います。

Tensorflow 1.x  → Tensorflow 2.x へのUPDATEの詳しい変更点は下記を参考に頂けたらと思います。

ここで、不必要になった、Session。
これは、Tensorflow 1.x の概念を象徴する機能だったかなと思います。

Tensorflowは、下記 2段階のプロセスを辿ることで、より高速で強力な処理を可能にしてます。
( 1 ) 計算グラフ(フロー)を作成
( 2 ) 計算グラフの実行

詳細については下記を参考。
第1回 TensorFlowとは? 入門連載始動! データフローグラフ、事例、学び方

https://www.atmarkit.co.jp/ait/articles/1804/18/news142.html

で、ここでSessionが果たしていた機能は、
( 1 ) 計算グラフ(フロー)を作成 → ( 2 ) 計算グラフの実行
という実行をしますよ。という合図です。
(細かいことは置いておいて….)
この Session()が無ければ、作成した計算グラフを実行に移すことができません。

それでは、実際に
Sessionの使い方を見ていきましょう。

定数の処理を、Sessionを使って見ていきます。

基本的な流れは
( 1 ) 計算グラフ(フロー)を作成
( 2 ) 計算グラフの実行

# 必要なライブラリをインストール
import numpy as np
import tensorflow as tf
 # 定数を定義
const1 = tf.constant(10) 
const2 = tf.constant(20) 
const3 = const1 + const2
print(const1)
print(const2)
print(const3)

下記の結果がprintされます。
Const_x, add_x の項目を確認してみると、どちらも、値が 0 となってます。
(xの部分は連番で数字が加算されていく)

Tensor(“Const_0:0”, shape=(), dtype=int32)
Tensor(“Const_1:0”, shape=(), dtype=int32)
Tensor(“add_0:0”, shape=(), dtype=int32)

これは、値が定数( const )に代入されていない状態を意味してます。
ここまでの処理は
( 1 ) 計算グラフ(フロー)を作成 したに留まります。

本記事のメインテーマである、Sessionを使って
( 2 ) 計算グラフの実行 を進めていきます。

sessionの使い方は
Sessionを定義して、run()で実行です。

with tf.Session() as sess:
    result1 = sess.run(const1)
    print(result1)

10

run()で実行したことによって、何が起きたのでしょうか。
result2も追加してみましょう。

with tf.Session() as sess:
    result1 = sess.run(const1)
    result2 = sess.run(const2)
    print(result1)
    print(result2)

10
20

( 1 ) 計算グラフ(フロー)で、作成した計算グラフの定数が代入されています。
Sessionを定義して、run()することによって
計算グラフで、定義したものが実行されました。
(それまでは、型があるのみという、感じ。)

with tf.Session() as sess:
     result1 = sess.run(const1)
     result2 = sess.run(const2)
     result3 = sess.run(const3)
     print(result1)
     print(result2)
     print(result3)

10
20
30

const3 = const1 + const2

で定義した、グラフも実行されました。
さらに、const1 , const2 が既に実行されているので、下記も可能です。

with tf.Session() as sess:
     result1 = sess.run(const1)
     result2 = sess.run(const2)
     result3 = sess.run(const3)
     result4 = sess.run(const1 + const2)
     print(result1)
     print(result2)
     print(result3)
     print(result4)

10
20
30
30

ちなみに、これまでの処理をリセットした状態で、下記を実行してみましょう。

with tf.Session() as sess:
    #runせずに、const2をprint
    print(const2)

    #runした後に、const2をprint
    result2 = sess.run(const2)
    print(const2)

    #runした後に、result2をprint
    print(result2)

#runせずに、const2をprint
Tensor(“Const_1:0”, shape=(), dtype=int32)

#runした後に、const2をprint
Tensor(“Const_1:0”, shape=(), dtype=int32)

#runした後に、result2をprint
20

という結果となりました。
runによって、session 外部の const2に代入されたのではなく、
sessionの中で、constに代入している流れなのでしょうか。
(若干怪しい….)

Google Collaboratory で tensorflowのバージョンを変更する方法

非常に簡単です。
基本的には、Tensorflowのサイトに記載している通りです。

https://www.tensorflow.org/install/gpu

!pip install tensorflow==1.15

一つだけ注意する点は、
Collaboratory上で、pip する際には、 !pip とする必要があります。

バージョンが変更されたかを確認しましょう。

import tensorflow as tf
print(tf.__version__)

1.15.0

このように指定した、バージョンに変更されたら完了です。

一度、既存のバージョンで、import tensorflow as tfしてしまった場合には、
新しいCollaboratoryファイルでやり直すか、下記のように、uninstallした後に、指定のバージョンをinstallしましょう。

!pip uninstall -y tensorflow
!pip install tensorflow==1.15

tensorflowはバージョン違いで、エラーが起きるので、柔軟に使用しましょう。