機械学習のExampleから覚えるPython(class:__call__)

今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。

※基本的には Python3.x系のつもりで記載してます

などで、class について記載したのですが、
今回はその中でも独自モデル(Custom Model)を作る場合の記述について見てみます。

参考:https://www.tensorflow.org/guide/keras

Example

class MyModel(tf.keras.Model):

  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # Define your layers here.
    self.dense_1 = layers.Dense(32, activation='relu')
    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # Define your forward pass here,
    # using layers you previously defined (in `__init__`).
    x = self.dense_1(inputs)
    return self.dense_2(x)

  def compute_output_shape(self, input_shape):
    # You need to override this function if you want to use the subclassed model
    # as part of a functional-style model.
    # Otherwise, this method is optional.
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_classes
    return tf.TensorShape(shape)

解説

class MyModel(tf.keras.Model):

tf.keras.Modelを継承

  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # Define your layers here.
    self.dense_1 = layers.Dense(32, activation='relu')
    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

コンストラクタ(初期化:__init__)にて、レイヤをインスタンス

  def call(self, inputs):
    # Define your forward pass here,
    # using layers you previously defined (in `__init__`).
    x = self.dense_1(inputs)
    return self.dense_2(x)

call関数で、forwardの動作を記載します。
さて、なぜcall関数で?という疑問になりますよね?

tf.keras.Modelをおっていくとbase_layer.pyまでいきつきます。

base_layerにある __call__関数で、上記の callがよばれることになります。

__call__ は組込み関数でクラスをインスタンスして、関数のように呼ばれたときに動きます。

例でいうと、以下のようになります。

model = MyModel

model() # この時に __call__ →...→ call

今回、TensorFlow/Keras を例にしてますが、PyTorchやChainerでも__call__にて
Model or Layerを構築しています。

forwardbackward関数で実装というのもあるので、
フレームワークにしたがって書きましょう。

  def compute_output_shape(self, input_shape):
    # You need to override this function if you want to use the subclassed model
    # as part of a functional-style model.
    # Otherwise, this method is optional.
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_classes
    return tf.TensorShape(shape)

コメントにあるとおり、書いてもいいし。というやつです。

まとめ

今回、callからの__call__についてでした。

機械学習のExampleから覚えるPython(@デコレータ)

今までPythonを感覚的に使っていたので、改めて文法を知ろうかなと。
その際にいま流行りの機械学習(深層学習)のExampleを例にすると
わかりやすいのかなと思ったので書いてみる。

※基本的には Python3.x系のつもりで記載してます

の時に書いてた

@keras_export('keras.datasets.mnist.load_data')
def load_data(path='mnist.npz'):

の「@」デコレータについてです。

簡単にいうと

既存関数の処理の前後に自分自身で、処理を付け加える。
今回でいうと、load_data 関数の前後で何か処理やっているようです。

細かく見ていく

@keras_export('keras.datasets.mnist.load_data')

keras_export は 先頭で以下で宣言されています。

from tensorflow.python.util.tf_export import keras_export

では、tf_exportはというと、

tf_export にて定義されてます。
その api_export はなんでしょうか。

class api_export(object):  # pylint: disable=invalid-name
  """Provides ways to export symbols to the TensorFlow API."""

__init__ 関数に書いてあるのをみる。

  def __init__(self, *args, **kwargs):  # pylint: disable=g-doc-args
    """Export under the names *args (first one is considered canonical).

    Args:
      *args: API names in dot delimited format.
      **kwargs: Optional keyed arguments.
        v1: Names for the TensorFlow V1 API. If not set, we will use V2 API
          names both for TensorFlow V1 and V2 APIs.
        overrides: List of symbols that this is overriding
          (those overrided api exports will be removed). Note: passing overrides
          has no effect on exporting a constant.
        api_name: Name of the API you want to generate (e.g. `tensorflow` or
          `estimator`). Default is `tensorflow`.
        allow_multiple_exports: Allow symbol to be exported multiple time under
          different names.
    """

ってことらしい。
今回でいうと、特に引数はなく単純に load_dataを呼んでいるみたい。

参考

機械学習の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でも、代入記述先(関数の返り値)には型:データタイプがあります。

それぞれ見ていきたいと思います。

mnist = tf.keras.datasets.mnist
>>> type(mnist)
module

tf.keras.datasets.mnist は tensorflow/tensorflow/python/keras/datasets/mnist.pyのとおり importできるモジュール(パッケージ)になります。

(x_train, y_train),(x_test, y_test) = mnist.load_data()
>>> type(x_train)
numpy.ndarray
>>> x_train.dtype
dtype('uint8')

numpyモジュールの多次元配列型になります。
更にデータタイプを dtypeで確認することができます。
データロード時は uint8ですね。

x_train, x_test = x_train / 255.0, x_test / 255.0
>>> type(x_train)
numpy.ndarray
>>> x_train.dtype
dtype('float64')

先ほどと同じくnumpyモジュールの多次元配列型になります。
ただし、255.0という float型で除算しているためデータタイプは float64になってます。

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)
])
>>> type(model)
tensorflow.python.keras.engine.sequential.Sequential

こちらも、importできる Sequentialパッケージですね。

データタイプ

intfloatになります。

必要そうなものを列挙してみます。

項目 データタイプ
真理値判定 bool
数値型 int, float, complex
文字列 str
シーケンス型(配列) list, tuple, range
マッピング dict

データ型については、Pythonの公式ドキュメントも参考にしてください。

#ifや#defineの確認方法(プリプロセッサ)

昨日、#ifdef 追加の確認なんて目チェックで十分。なんてことを未来ある若者に適当な感じで言ってしまったを反省したのでメモとして書こうかなと。

今回は、プリプロセッサ処理後の結果で確認するという方法を書きます。
たぶん、今どきはIDE#define定義を指示すればコード上で色づけされるんだろうなと勝手に思ってます。

例えば、以下のmain.cppファイルがあった場合

int main(int argc, char const* argv[])
{
#ifdef TEST
  int value = 100;
#else
  int value = 0;
#endif
// 何かの処理が続く
  return 0;
}

あとは、gccのオプションに -Eをつけて標準出力に出す。

$ gcc -E main.cpp

実行結果は以下になります。

$ gcc -E main.cpp
# 1 "main.cpp"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 336 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.cpp" 2

int main(int argc, char const* argv[])
{



 int value = 0;


 return 0;
}

TEST が有効になった場合の時も確認したいので、-Dにて指定します。

$ gcc -E -DTEST main.cpp 
# 1 "main.cpp"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 336 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.cpp" 2

int main(int argc, char const* argv[])
{

 int value = 100;




 return 0;
}

とこんな感じで確認できます。
(色々読み込んでいると大量に出るけど...)

あと合わせて -Cとか -Pとかあるけど説明省略します。

参考URL

機械学習の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)

※本記事記載の時点のコードです。

数値演算子

x_train, x_test = x_train / 255.0, x_test / 255.0

x_train, x_testデータすべてに 255.0の除算を行ってます。
これは、元の値が0-255の範囲で定義されているため、0-1に変換しています。

除算するまえであれば、以下のコードを入力すると uint8と表示されると思います。

print(x_train.dtype)

では、除算後はというと float64になっていると思います。
これは、255.0という値が float64と認識されているからになります。

正確には uint8float64にキャストされて計算されています。

種類

演算子 説明
+ a + b 足し算
a – b 引き算
* a*b 掛け算
/ a/b 割り算
// a//b aをbで割った整数値(少数点以下削除
% a%b aをbで割った時の割り切れなかった余り
** a**n aをn回掛けた数(べき乗)

他にも ビット演算子, 比較演算子などありますが、
Exampleコードに出てきてから書こうと思います。