20200426のTensorFlowに関する記事は4件です。

【画像分類】犬の表情分析

1.はじめに

動物の表情分析に興味本位で手を出してみた。

2.参考文献

"Kerasで「笑っている犬」と「怒っている犬」を判別する機械学習モデルを作る"
https://qiita.com/ariera/items/545d48c961170a784075

3.内容

3-1.データ前処理

# 画像を読み込んで、行列に変換する関数を定義
from keras.preprocessing.image import load_img, img_to_array
def img_to_traindata(file, img_rows, img_cols, rgb):
    if rgb == 0:
        img = load_img(file, color_mode = "grayscale", target_size=(img_rows,img_cols)) # grayscaleで読み込み
    else:
        img = load_img(file, color_mode = "rgb", target_size=(img_rows,img_cols)) # RGBで読み込み
    x = img_to_array(img)
    x = x.astype('float32')
    x /= 255
    return x

# 学習データ、テストデータ生成
import glob, os

img_rows = 224 # 画像サイズはVGG16のデフォルトサイズとする
img_cols = 224
nb_classes = 2 # 怒っている、笑っているの2クラス
img_dirs = ["./dog_angry", "./dog_smile"] # 怒っている犬、笑っている犬の画像を格納したディレクトリ

X_train = []
Y_train = []
X_test = []
Y_test = []
for n, img_dir in enumerate(img_dirs):
    img_files = glob.glob(img_dir+"/*.jpg")   # ディレクトリ内の画像ファイルを全部読み込む
    for i, img_file in enumerate(img_files):  # ディレクトリ(文字種)内の全ファイルに対して
        x = img_to_traindata(img_file, img_rows, img_cols, 1) # 各画像ファイルをRGBで読み込んで行列に変換
        if i < 8: # 1~8枚目までを学習データ
            X_train.append(x) # 学習用データ(入力)に画像を変換した行列を追加
            Y_train.append(n) # 学習用データ(出力)にクラス(怒=0、笑=1)を追加
        else:       # 9枚目以降をテストデータ
            X_test.append(x) # テストデータ(入力)に画像を変換した行列を追加
            Y_test.append(n) # テストデータ(出力)にクラス(怒=0、笑=1)を追加

import numpy as np
# 学習、テストデータをlistからnumpy.ndarrayに変換
X_train = np.array(X_train, dtype='float') 
Y_train = np.array(Y_train, dtype='int')
X_test = np.array(X_test, dtype='float')
Y_test = np.array(Y_test, dtype='int')

# カテゴリカルデータ(ベクトル)に変換
from keras.utils import np_utils
Y_train = np_utils.to_categorical(Y_train, nb_classes)
Y_test = np_utils.to_categorical(Y_test, nb_classes)

# 作成した学習データ、テストデータをファイル保存
np.save('models/X_train_2class_120.npy', X_train)
np.save('models/X_test_2class_120.npy', X_test)
np.save('models/Y_train_2class_120.npy', Y_train)
np.save('models/Y_test_2class_120.npy', Y_test)

# 作成したデータの型を表示
print(X_train.shape)
print(Y_train.shape)
print(X_test.shape)


from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D

# 【パラメータ設定】
batch_size = 20
epochs = 30

input_shape = (img_rows, img_cols, 3)
nb_filters = 32
# size of pooling area for max pooling
pool_size = (2, 2)
# convolution kernel size
kernel_size = (3, 3)

# 【モデル定義】
model = Sequential()
model.add(Conv2D(nb_filters, kernel_size, # 畳み込み層
                        padding='valid',
                        activation='relu',
                        input_shape=input_shape))
model.add(Conv2D(nb_filters, kernel_size, activation='relu')) # 畳み込み層
model.add(MaxPooling2D(pool_size=pool_size)) # プーリング層
model.add(Conv2D(nb_filters, kernel_size, activation='relu')) # 畳み込み層
model.add(MaxPooling2D(pool_size=pool_size)) # プーリング層
model.add(Dropout(0.25)) # ドロップアウト(過学習防止のため、入力と出力の間をランダムに切断)

model.add(Flatten()) # 多次元配列を1次元配列に変換
model.add(Dense(128, activation='relu'))  # 全結合層
model.add(Dropout(0.2))  # ドロップアウト
model.add(Dense(nb_classes, activation='sigmoid'))  # 2クラスなので全結合層をsigmoid

# モデルのコンパイル
model.compile(loss='binary_crossentropy', # 2クラスなのでbinary_crossentropy
              optimizer='adam', # 最適化関数のパラメータはデフォルトを使う
              metrics=['accuracy'])

# 【各エポックごとの学習結果を生成するためのコールバックを定義(前回より精度が良い時だけ保存)】
from keras.callbacks import ModelCheckpoint
import os
model_checkpoint = ModelCheckpoint(
    filepath=os.path.join('models','model_2class120_{epoch:02d}.h5'),
    monitor='val_accuracy',
    mode='max',
    save_best_only=True,
    verbose=1)
print("filepath",os.path.join('models','model_.h5'))

# 【学習】
result = model.fit(X_train, Y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(X_test, Y_test),
                   callbacks=[model_checkpoint],validation_split=0.1)

3-2.学習

from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D

# 【パラメータ設定】
batch_size = 20
epochs = 30

input_shape = (img_rows, img_cols, 3)
nb_filters = 32
# size of pooling area for max pooling
pool_size = (2, 2)
# convolution kernel size
kernel_size = (3, 3)

# 【モデル定義】
model = Sequential()
model.add(Conv2D(nb_filters, kernel_size, # 畳み込み層
                        padding='valid',
                        activation='relu',
                        input_shape=input_shape))
model.add(Conv2D(nb_filters, kernel_size, activation='relu')) # 畳み込み層
model.add(MaxPooling2D(pool_size=pool_size)) # プーリング層
model.add(Conv2D(nb_filters, kernel_size, activation='relu')) # 畳み込み層
model.add(MaxPooling2D(pool_size=pool_size)) # プーリング層
model.add(Dropout(0.25)) # ドロップアウト(過学習防止のため、入力と出力の間をランダムに切断)

model.add(Flatten()) # 多次元配列を1次元配列に変換
model.add(Dense(128, activation='relu'))  # 全結合層
model.add(Dropout(0.2))  # ドロップアウト
model.add(Dense(nb_classes, activation='sigmoid'))  # 2クラスなので全結合層をsigmoid

# モデルのコンパイル
model.compile(loss='binary_crossentropy', # 2クラスなのでbinary_crossentropy
              optimizer='adam', # 最適化関数のパラメータはデフォルトを使う
              metrics=['accuracy'])

# 【各エポックごとの学習結果を生成するためのコールバックを定義(前回より精度が良い時だけ保存)】
from keras.callbacks import ModelCheckpoint
import os
model_checkpoint = ModelCheckpoint(
    filepath=os.path.join('models','model_2class120_{epoch:02d}_{val_acc:.3f}.h5'),
    monitor='val_acc',
    mode='max',
    save_best_only=True,
    verbose=1)

# 【学習】
result = model.fit(X_train, Y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(X_test, Y_test),
                   callbacks=[model_checkpoint])

3-3.分類

from keras.models import load_model
from keras.preprocessing.image import load_img, img_to_array
import matplotlib.pyplot as plt

model = load_model('models/model_2class120_04.h5')
model.summary()

def img_to_traindata(file, img_rows, img_cols, rgb):
    if rgb == 0:
        img = load_img(file, color_mode = "grayscale", target_size=(img_rows,img_cols)) # grayscaleで読み込み
    else:
        img = load_img(file, color_mode = "rgb", target_size=(img_rows,img_cols)) # RGBで読み込み
    x = img_to_array(img)
    x = x.astype('float32')
    x /= 255
    return x

import numpy as np
img_rows = 224 # 画像サイズはVGG16のデフォルトサイズとする
img_cols = 224

## 画像読み込み
filename = "dog_smile/n02085936_37.jpg"
x = img_to_traindata(filename, img_rows, img_cols, 1) # img_to_traindata関数は、学習データ生成のときに定義
x = np.expand_dims(x, axis=0)

## どのクラスかを判別する
preds = model.predict(x)
pred_class = np.argmax(preds[0])
print("識別結果:", pred_class)
print("確率:", preds[0])

from keras import backend as K
import cv2

# モデルの最終出力を取り出す
model_output = model.output[:, pred_class]

# 最後の畳込み層を取り出す
last_conv_output = model.get_layer('conv2d_3').output #'block5_conv3').output

# 最終畳込み層の出力の、モデル最終出力に関しての勾配
grads = K.gradients(model_output, last_conv_output)[0]
# model.inputを入力すると、last_conv_outputとgradsを出力する関数を定義
gradient_function = K.function([model.input], [last_conv_output, grads]) 

# 読み込んだ画像の勾配を求める
output, grads_val = gradient_function([x])
output, grads_val = output[0], grads_val[0]

# 重みを平均化して、レイヤーのアウトプットに乗じてヒートマップ作成
weights = np.mean(grads_val, axis=(0, 1))
heatmap = np.dot(output, weights)

heatmap = cv2.resize(heatmap, (img_rows, img_cols), cv2.INTER_LINEAR)
heatmap = np.maximum(heatmap, 0) 
heatmap = heatmap / heatmap.max()

heatmap = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET)  # ヒートマップに色をつける
heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB)  # 色をRGBに変換

img = plt.imread(filename, cv2.IMREAD_UNCHANGED)
print(img.shape)  # (330, 440, 4)

fig, ax = plt.subplots()
ax.imshow(img)

plt.show()

4.分類結果

2020-04-26 21:12:46.904489: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x7fded852e7f0 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2020-04-26 21:12:46.904528: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 222, 222, 32)      896       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 220, 220, 32)      9248      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 110, 110, 32)      0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 108, 108, 32)      9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 54, 54, 32)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 54, 54, 32)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 93312)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               11944064  
_________________________________________________________________
dropout_2 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 2)                 258       
=================================================================
Total params: 11,963,714
Trainable params: 11,963,714
Non-trainable params: 0
_________________________________________________________________
識別結果: 1
確率: [0.27252397 0.6845933 ]
(375, 500, 3)

5.今後の課題

精度をあげるための問題点を絞り込む必要がある。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10へTensorFlow2をインストールする

はじめに

4月になって、新しいPCを購入しました。
またゼロからTensorFlow環境を構築したので記録を残します。
ということで、何も入っていない状態からのインストールです。

環境

  • OS : Windows 10 64bit
  • Python : Python 3.7.7
  • TensorFlow : 2.1.0
  • jupyterlab : 2.1.1
  • CUDA : 10.1 update 2
  • cuDNN : 7.6.5
  • GPU NVIDIA GeForce MX250

TensorFlow2 の動作要件

TensorFlow2 の動作要件は、https://www.tensorflow.org/install?hl=jaから確認できます。

  • Windows 7 以上
    Windows10にインストールするので問題なし。
  • Python 3.5–3.7
    Python 3.7系の最新である、Python 3.7.7 をインストールします。

インストールするツールのバージョンはhttps://www.tensorflow.org/install/gpu#pip_packageで確認します。

TensorFlow2の場合は下記の要件になります。

  • TensorFlow 2.1.0 以降は CUDA 10.1 に対応しています。
    最新は10.2ですが、10.1にします。
  • CUDA 10.1の場合、VIDIA GPU ドライバ は 418.x 以降が必要です。
    これは、「NVIDIA コントロール パネル」で確認できます。低い場合はアップデートします。
  • cuDNN は7.6 以降が必要です。
    7.6.5をインストールします。

Python 3.7.7 のインストール

https://www.python.org/から、「Downloads」-「All releases」と選択します。
各バージョンのリストがあるので、Python 3.7.7 の Download をクリックします。
Files の一覧から「Windows x86-64 executable installer」をクリックしてダウンロードします。
ダウンロードしたファイルを実行して、インストールします。

インストールが完了したら、下記を実行してバージョンを確認します。

>python -V
Python 3.7.7

CPUの拡張命令セットの確認

こちらを参考に、CPUの拡張命令セットを確認します。

得られた結果は下記です。

Vendor ID         : GenuineIntel
CPU name          : Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
Microarchitecture : kabylake
Vector instructions supported:
SSE       : Yes
SSE2      : Yes
SSE3      : Yes
SSSE3     : Yes
SSE4.1    : Yes
SSE4.2    : Yes
SSE4a     : --
AVX       : Yes
AVX2      : Yes
BMI1      : Yes
BMI2      : Yes

AVX2に対応しているので、TensorFlowは1.6以上をインストール可能です。
現時点で最新のバージョンをインストールします。

GPUの確認

念のために、GPUを確認しておきます。
購入時点ですでに、「NVIDIA コントロール パネル」がインストールされていたので、これを使用します。

  1. デスクトップで右クリックして「NVIDIA コントロール パネル」を選択します。
  2. 「ヘルプ」メニューから「システム情報」を選択します。
  3. 「グラフィックス カード情」に「GeForce MX250」と書かれています。
    ※「グラフィックス カード情」は、「情報」の脱字ではないです。本当にこのように表示されています。

Visual Studio 2017 のインストール

https://docs.nvidia.com/cuda/archive/10.1/cuda-installation-guide-microsoft-windows/index.htmlに、CUDA 10.1で必要なものが書かれています。

Visual Studioが必要で、「Table 2. Windows Compiler Support in CUDA 10.1」の表にサポートされているVisual Studioのバージョンが書かれています。

Visual Studio 2019 は、Preview releasesなので、Visual Studio 2017をインストールします。

https://visualstudio.microsoft.com/ja/downloads/からダウンロードします。
「古いバージョンのダウンロード」をクリックします。
「2017」を展開して、右側の「ダウンロード」をクリックする。

Microsoft アカウントでログインして、ダウンロードします。

今回は、「Visual Studio Community 2017 (version 15.9)」ダウンロードしました。

ダウンロードした「vs_Community.exe」をダブルクリックしてVisual Studio Installer を起動します。
「C++によるデスクトップ開発」を選択して「インストール」をクリックします。

CUDA 10.1 update 2 のインストール

NVIDIA CUDA Toolkit をダウンロードします。
https://developer.nvidia.com/cuda-toolkit-archiveにアクセスします。
「CUDA Toolkit 10.1 update2 Archive」を選択して、ダウンロードします。

「Installer Type」は、ネットワークインストーラーを選択しました。(19.9MB)
ちなみにフルインストーラーだと2.5GBのファイルをダウンロードします。

ダウンロードした「cuda_10.1.243_win10_network.exe」をダブルクリックして、インストールします。

cuDNN 7.6.5 のインストール

cuDNNは、https://developer.nvidia.com/cudnnからダウンロードします。

「Download cuDNN v7.6.5 (November 5th, 2019), for CUDA 10.1」を展開して、
「cuDNN Library for Windows 10」をダウンロードします。

ダウンロードした「cudnn-10.1-windows10-x64-v7.6.5.32.zip」を解凍して、
中にあるCUDAディレクトリを任意のディレクトリに置きます。

CUDAを置いたディレクトリをシステム環境変数に設定します。

環境変数名 path
CUDNN_PATH c:\tf\CUDA
PATHの最後に追加 c:\tf\CUDA\bin

※CUDAディレクトリを「c:\tf\」に置いた場合。

pip のバージョンアップ

TensorFlow2 をインストールするためには、pip 19.0より新しいバージョンが必要です。
バージョンが低いようなら、バージョンアップします。

pip install -U pip

TensorFlow2 のインストール

下記コマンドだけでOKです。

>pip install tensorflow

動作確認を行います。
まずは、下記のファイルを作成します。

tensotflow_test.py
import tensorflow as tf
import os

print(tf.__version__)
print(tf.keras.__version__)

print(tf.random.normal([5, 5]))

実行します。
環境変数 TF_CPP_MIN_LOG_LEVEL は、余計なメッセージを表示しないためのものです。

>set TF_CPP_MIN_LOG_LEVEL=2
>python tensorflow_test.py
2.1.0
2.2.4-tf
tf.Tensor(
[[ 0.9073535  -0.30908114 -1.1792291  -0.03576818 -0.350677  ]
 [-0.30494577  0.0393485  -1.7498294   0.01919838 -0.881274  ]
 [-0.8610118  -1.6675215  -0.9653552   0.4389885  -0.06305566]
 [-0.6703128   0.85750896  1.0951182   0.029854    1.1836303 ]
 [ 0.5926097   0.5141883  -0.84746957  0.668688    0.30143082]], shape=(5, 5), dtype=float32)

TensorFlowとKerasのバージョンが表示され、5x5 の乱数も表示されました。
TensorFlowも2.1.0 がインストールされていることが確認できました。

jupyterlab のインストール

何かと便利なjupyterlab をインストールします。

>pip install jupyterlab

jupyterlab をバックグラウンドで起動します。

>start /b jupyter lab

起動すると、自動的にブラウザでjupyterlab へつながります。

自動的につながらない場合は、ブラウザでhttp://localhost:8888/labへアクセスます。
tokenは、jupyterlab 起動時のメッセージに書かれています。

最後に

TensorFlow2の実行環境ができました。
ゼロからのインストールだと、すんなり完了しました。
後にバージョンアップを行うときが怖いですが、今はこの環境で楽しみます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Tensorflow] tensorflowでarray_to_imgの使い方

tensorflowでarray_to_imgの使い方

自分用メモ

環境

  • Anaconda
  • python 3.5
  • Tensorlow 1.5.0
  • (jupyter notebook)

array_to_img

array_to_img が使えなかった。下記のモジュールインポートで解決した。

  from tensorflow.python.keras.preprocessing.image import array_to_img
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[TensorFlow 2] グラフモードでTensorの中身を確認する方法

はじめに

TensorFlow (2.x) で学習時の損失関数やデータセットの変換部分に自作関数を使うときに、「思ったとおりの値が入っているのかな?」と確認しようと思って print() を呼び出しても、値が出力されないときがあります。
tfdbg などのデバッガーを使えば良いのでしょうが、もっとシンプルに、いわゆる「printデバッグ」をする方法をご紹介します。

tfdbgについては→tfdbg を使って Keras の nan や inf を潰す - Qiita

検証環境

  • Ubuntu 18.04
  • Python 3.6.9
  • TensorFlow 2.1.0 (CPU)

以下のコードでは、↓が書かれているものとします。

import tensorflow as tf 

Tensor に対して print() した場合の挙動

TensorFlow 2.xでは Eager Execution がデフォルトになりましたので、インタプリタで Tensor を組み立てている限りは、普通に print() すれば Tensor の値が表示されますし、.numpy()ndarray として値を得ることもできます。

値を確認できる場合

x = tf.constant(10)
y = tf.constant(20)
z = x + y
print(z)         # tf.Tensor(30, shape=(), dtype=int32)
print(z.numpy()) # 30

値を確認できない場合 (1)

学習・評価時に逐次 Tensor の値を評価すると遅いので、@tf.function デコレータをつけることにより、グラフモードで処理する関数を定義することができます。グラフモードで定義した演算は計算グラフとしてまとめて(Pythonの外で)処理されるため、計算過程の値を見ることができません。

@tf.function
def dot(x, y):
    tmp = x * y
    print(tmp)
    return tf.reduce_sum(tmp)

x = tf.constant([1, 2])
y = tf.constant([3, 1])
w = tf.constant([0, 2])
print(dot(x, y))
print(dot(x, w))
# Tensor("mul:0", shape=(2,), dtype=int32)
# tf.Tensor(5, shape=(), dtype=int32)
# tf.Tensor(4, shape=(), dtype=int32)

dot() の外で計算結果を出力することはできますが、中では値を見ることができません。
しかも、dot() を複数回呼び出しても、中の print() は基本的にはグラフを解析するタイミングで1回しか実行されません。

もちろん、この場合は @tf.function() を取り除けば値が見えるようにはなります。

def dot(x, y):
    tmp = x * y
    print(tmp)
    return tf.reduce_sum(tmp)

x = tf.constant([1, 2])
y = tf.constant([3, 1])
w = tf.constant([0, 2])
print(dot(x, y))
print(dot(x, w))
# tf.Tensor([3 2], shape=(2,), dtype=int32)
# tf.Tensor(5, shape=(), dtype=int32)
# tf.Tensor([0 4], shape=(2,), dtype=int32)
# tf.Tensor(4, shape=(), dtype=int32)

値を確認できない場合 (2)

tf.data.Dataset に対する map() 処理や、Kerasで自作の損失関数を使用する場合など、暗黙のうちにグラフモードで実行される場合があります。この場合は @tf.function をつけていなくても print() で途中の値を見ることができません。

def fourth_power(x):
    z = x * x
    print(z)
    z = z * z
    return z

ds = tf.data.Dataset.range(10).map(fourth_power)
for i in ds:
    print(i)

# Tensor("mul:0", shape=(), dtype=int64)
# tf.Tensor(0, shape=(), dtype=int64)
# tf.Tensor(1, shape=(), dtype=int64)
# tf.Tensor(16, shape=(), dtype=int64)
# :

tf.print()

グラフモードで実行されている処理の中で Tensor の値を確認するには tf.print() を使用します。
tf.print | TensorFlow Core v2.1.0

以下のように、値を表示することもできますし、tf.shape() を使って Tensor の次元やサイズを表示することもできます。自作関数の中で、なぜか次元やサイズが合わないと怒られてしまうときのデバッグにもご利用ください。

@tf.function
def dot(x, y):
    tmp = x * y
    tf.print(tmp)
    tf.print(tf.shape(tmp))
    return tf.reduce_sum(tmp)

x = tf.constant([1, 2])
y = tf.constant([3, 1])
w = tf.constant([0, 2])
print(dot(x, y))
print(dot(x, w))
# [3 2] 
# [2]
# tf.Tensor(5, shape=(), dtype=int32)
# [0 4]
# [2]
# tf.Tensor(4, shape=(), dtype=int32)

Dataset.map() のデバッグをしたい時でも tf.print() を使えば大丈夫です。

def fourth_power(x):
    z = x * x
    tf.print(z)
    z = z * z
    return z

ds = tf.data.Dataset.range(10).map(fourth_power)
for i in ds:
    print(i)
# 0
# tf.Tensor(0, shape=(), dtype=int64)
# 1
# tf.Tensor(1, shape=(), dtype=int64)
# 4
# tf.Tensor(16, shape=(), dtype=int64)
# :

tf.print() がうまく動かない例

では何も考えず Tensor の中身は tf.print() で出力すればよいのかというと、実はそうでもないのです。
tf.print() が実行されるのは、実際に処理が計算グラフとして実行されるときです。
つまり、計算を実行するまでもなくグラフ解析時点で型や次元が一致しないことが分かってエラーが発生する場合、tf.print() の処理は実行されません。ややこしいですね…。

@tf.function
def add(x, y):
    z = x + y
    tf.print(z) # このprintは実行されない
    return z

x = tf.constant([1, 2])
y = tf.constant([3, 4, 5])
ret = add(x, y)
# ValueError: Dimensions must be equal, but are 2 and 3 for 'add' (op: 'AddV2') with input shapes: [2], [3].

このようなケースでは、逆に普通の print() を使って、目的の形状のデータが渡ってきているか確認するのがよいでしょう。

@tf.function
def add(x, y):
    print(x) # このprintは計算グラフ解析時に実行される
    print(y)
    z = x + y
    return z

x = tf.constant([1, 2])
y = tf.constant([3, 4, 5])
ret = add(x, y)
# Tensor("x:0", shape=(2,), dtype=int32)
# Tensor("y:0", shape=(3,), dtype=int32)
# ValueError: Dimensions must be equal, but are 2 and 3 for 'add' (op: 'AddV2') with input shapes: [2], [3].

自作関数の中で変数を更新する方法

例えば、グラフモードで自作関数が呼び出された回数をカウントし、何回目の呼び出しかを表示することを考えます。

ダメな例

count = 0

@tf.function
def dot(x, y):
    global count
    tmp = x * y
    count += 1
    tf.print(count, tmp)
    return tf.reduce_sum(tmp)

x = tf.constant([1, 2])
y = tf.constant([3, 1])
w = tf.constant([0, 2])
print(dot(x, y))
print(dot(x, w))
# 1 [3 2]
# tf.Tensor(5, shape=(), dtype=int32)
# 1 [0 4]
# tf.Tensor(4, shape=(), dtype=int32)

2回目にも「1」が表示されてしまいます。
Pythonコードとしての count += 1 がグラフ解析時に1回しか実行されないことによります。

うまく動く例

以下のように tf.Variable()assign_add() などを使うのが正解です。
tf.Variable | TensorFlow Core v2.1.0

count = tf.Variable(0)

@tf.function
def dot(x, y):
    tmp = x * y
    count.assign_add(1)
    tf.print(count, tmp)
    return tf.reduce_sum(tmp)

x = tf.constant([1, 2])
y = tf.constant([3, 1])
w = tf.constant([0, 2])
print(dot(x, y))
print(dot(x, w))
# 1 [3 2]
# tf.Tensor(5, shape=(), dtype=int32)
# 2 [0 4]
# tf.Tensor(4, shape=(), dtype=int32)

参考記事

tf.function で性能アップ | TensorFlow Core(公式ドキュメント)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む