機械学習のExampleから覚えるPython(class:継承)
今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。
※基本的には Python3.x系のつもりで記載してます
Example
https://www.tensorflow.org/tutorials/ に記載されている
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(), 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)
※本記事記載の時点のコードです。
class
model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(512, activation=tf.nn.relu), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation=tf.nn.softmax) ])
tf.keras.models.Sequential
は class
と呼ばれるもので定義されています。
実際のコードは こちらにあります。
@keras_export('keras.models.Sequential', 'keras.Sequential') class Sequential(Model):
継承
class Sequential(Model):
このSequential
クラスは Model
クラスを継承しています。
Model
を「基底クラス」、Sequential
を「派生クラス」と言ったりします。
やっていることは、シンプルで Sequential
= Model
+ Sequential独自 という形で
Model
の関数や変数を Sequential
でも使えます。
Exampleで記述している
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=5) model.evaluate(x_test, y_test)
こちらの compile
やfit
、evaluate
関数は Sequential
のクラスコード内には記述がありません。
実際に記述されているのは Model
にあります。
確認したい方は こちらを参照してください。
多重継承
基底クラスを複数宣言できるというお話です。
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
参考
さて、あとはSequential
クラスをインスタンスしている部分(初期化)と
演算子(Exampleでは x_train / 255.0 )さえ理解すれば題目の
Exampleコードはすべて理解できたことになりますね!
機械学習のExampleから覚えるPython(関数の引数)
今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。
※基本的には Python3.x系のつもりで記載してます
Example
https://www.tensorflow.org/tutorials/ に記載されている
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(), 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)
※本記事記載の時点のコードです。
関数の引数
前回 機械学習のExampleから覚えるPython(関数呼び出し)にて、load_data
関数の引数 path
が呼び出し時に省略されていることが確認できました。
関数の引数については3つの指定ができます。
- dafaultによる省略
- 引数を入力(引数名無し)
- 引数名を指定
defaultによる省略
関数定義側で、default値が設定している場合に
呼び出し側が default値で良い場合省略することができます。
- 関数定義側
def load_data(path='mnist.npz'):
- 呼び出し側
(x_train, y_train),(x_test, y_test) = mnist.load_data()
path
は mnist.npz
の値で処理されます。
引数を入力(引数名無し)
model.fit(x_train, y_train, epochs=5)
このコードを見てみます。
定義側はこのようになってます。
def fit(self, x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0., validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None, validation_freq=1, max_queue_size=10, workers=1, use_multiprocessing=False, **kwargs):
self
は一旦無視していただいて、x_train
, y_train
が fit
関数の
どの引数にあたるかというと、順番に設定しています。
つまり、以下のようになります。
# x=x_train, y=y_train model.fit(x_train, y_train, epochs=5)
引数名を指定
先ほどのコードの epochs
の設定が該当します。
model.fit(x_train, y_train, epochs=5)
fit
関数で定義されている epochs
を指定して 5
という値を入力しています。
可変長引数
関数の定義では、可変長引数 を定義することができます。
fit
関数でも最後の引数に出てきていた **kwargs
が該当します。
こちらのページが参考になると思いますので、リンク貼っときます。
機械学習のExampleから覚えるPython(関数呼び出し)
今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。
※基本的には Python3.x系のつもりで記載してます
Example
https://www.tensorflow.org/tutorials/ に記載されている
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(), 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)
※本記事記載の時点のコードです。
関数の呼び出し
mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data()
tf.keras.datasets.mnist
のload_data
関数を呼んでいます。
その返り値を x_train
,y_train
, x_test
,y_test
に代入しています。
load_data
関数はこちらで定義されています。
@keras_export('keras.datasets.mnist.load_data') def load_data(path='mnist.npz'):
「@」(デコレータ)は今後書こうと思います。
呼び出しの時に 引数path
は default : mnist.npz
のままで良いということで省略してますね。
return
としても以下で定義されているためそのまま格納ですね!
return (x_train, y_train), (x_test, y_test)
返り値の受け取りについては後々...
機械学習のExampleから覚えるPython(import)
今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。
※基本的には Python3.x系のつもりで記載してます
Example
https://www.tensorflow.org/tutorials/ に記載されている
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(), 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)
※本記事記載の時点のコードです。
import
本コードに適用するパッケージを記述します。
パッケージとは __init__.py
があるディレクトリ構想のものを示します。
Python 3.3以降では __init__.py
がなくても良いみたいです。
import module as identifier
import tensorflow as tf
tensorflow
パッケージを読みこみ、tf
と定義する。
もし、
import tensorflow
だけであれば、その後のコードは
mnist = tensorflow.keras.datasets.mnist
と書かないとエラーになります。
from relative_module import identifier
https://www.tensorflow.org/tutorials/keras/basic_classification より
# TensorFlow and tf.keras import tensorflow as tf from tensorflow import keras
from
を使った記述があります。
これは、tensorflow
パッケージにある keras
モジュールを呼び出してます。
その後のコードでは、keras
から記述することができます。
fashion_mnist = keras.datasets.fashion_mnist
最初の import - as で書くと
fashion_mnist = tf.keras.datasets.fashion_mnist
import時の検索範囲
その他
「,」区切り
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
とか書くことができます。
import xxx
でも書けますが、Pythonのスタイルガイドでは非推奨らしいです。
「*」
from tensorflow import *
ですべてのモジュールをインポートすることができます。
ただ、個人的には自分で書いたライブラリ以外は必要分しかimport
しないようにしてます。
追加(相対インポート)
コメントいただいたので、該当するリンクページ載せときます。
LazyAdamOptimizerを見てみた
見てみた
概要
Adam Optimizerの sparse更新をより効率化したもの。
既存のAdamアルゴリズムは2つの移動平均の各train variableに対して行います。 計算はすべてのStepで更新されます。 このクラスは sparse variableに対して勾配更新の緩やかな処理を行います。 すべてのインデックスのアキュムレータを更新するのではなく、 現在のバッチに含まれるスパース変数インデックスの移動平均アキュムレータのみを更新します。
オリジナルのAdamオプティマイザと比較すると、一部のアプリケーションではモデルトレーニングのスループットが大幅に向上しています。
ただし、元のAdamアルゴリズムとはわずかに異なるセマンティクスが提供されているため、異なる経験的結果が生じる可能性があります。
詳細
lazy_adam_optimizer.py
では、Adam Optimizerから2つの関数のみoverrideしている。_apple_sparse
※こちらを例に以後違いを説明します。- _resource_apply_sparse
_apple_sparse
Adam Optimizerでは以下のように関数を呼んでいる。
LazyAdam Optimizerでは*\_shared
関数は呼んでいない
def _apply_sparse(self, grad, var): beta1_power, beta2_power = self._get_beta_accumulators() beta1_power = math_ops.cast(beta1_power, var.dtype.base_dtype) beta2_power = math_ops.cast(beta2_power, var.dtype.base_dtype) lr_t = math_ops.cast(self._lr_t, var.dtype.base_dtype) beta1_t = math_ops.cast(self._beta1_t, var.dtype.base_dtype) beta2_t = math_ops.cast(self._beta2_t, var.dtype.base_dtype) epsilon_t = math_ops.cast(self._epsilon_t, var.dtype.base_dtype) lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power)) # \\(m := beta1 * m + (1 - beta1) * g_t\\) m = self.get_slot(var, "m") m_t = state_ops.scatter_update(m, grad.indices, beta1_t * array_ops.gather(m, grad.indices) + (1 - beta1_t) * grad.values, use_locking=self._use_locking) # \\(v := beta2 * v + (1 - beta2) * (g_t * g_t)\\) v = self.get_slot(var, "v") v_t = state_ops.scatter_update(v, grad.indices, beta2_t * array_ops.gather(v, grad.indices) + (1 - beta2_t) * math_ops.square(grad.values), use_locking=self._use_locking) # \\(variable -= learning_rate * m_t / (epsilon_t + sqrt(v_t))\\) m_t_slice = array_ops.gather(m_t, grad.indices) v_t_slice = array_ops.gather(v_t, grad.indices) denominator_slice = math_ops.sqrt(v_t_slice) + epsilon_t var_update = state_ops.scatter_sub(var, grad.indices, lr * m_t_slice / denominator_slice, use_locking=self._use_locking) return control_flow_ops.group(var_update, m_t, v_t)
の箇所だけを比較すると
- Adam Optimizer
# m_t = beta1 * m + (1 - beta1) * g_t m = self.get_slot(var, "m") m_scaled_g_values = grad * (1 - beta1_t) m_t = state_ops.assign(m, m * beta1_t, use_locking=self._use_locking) with ops.control_dependencies([m_t]): m_t = scatter_add(m, indices, m_scaled_g_values)
- LazyAdam Optimizer
# \\(m := beta1 * m + (1 - beta1) * g_t\\) m = self.get_slot(var, "m") m_t = state_ops.scatter_update(m, grad.indices, beta1_t * array_ops.gather(m, grad.indices) + (1 - beta1_t) * grad.values, use_locking=self._use_locking)
となっている。
LazyAdamで使っている scatter_update はindices
の部分(sparse)のみ更新する関数
そのあと v_t
も同じように計算し
m_t_slice = array_ops.gather(m_t, grad.indices) v_t_slice = array_ops.gather(v_t, grad.indices)
切り取って
denominator_slice = math_ops.sqrt(v_t_slice) + epsilon_t var_update = state_ops.scatter_sub(var, grad.indices, lr * m_t_slice / denominator_slice, use_locking=self._use_locking)
更新値を算出。
まとめ
Moving-Averageの計算において、部分な値にて計算しているだけみたい。
(一部 addから updateの変更も合っているけど)