20201025のTensorFlowに関する記事は3件です。

tf.Keras @ Tensor Flow 2.xにおけるTFRecord活用の備忘録 2/2【学習・評価実行編】

前回の記事tf.Keras @ Tensor Flow 2.xにおけるTFRecord活用の備忘録 1/2【TFRecord作成編】で作成したTFRecordを用いて学習,評価を実行します。モデルはEfficientNetB7を用いました。

学習・評価実行用ソースコード

ソースコード例(JupyterNotebookでの実行を想定)
# 学習・評価用 Notebook

# データセット格納先パスのrootディレクトリを指定
dataset_root = ''
train_path = dataset_root + 'images/train/'
label_path = dataset_root + 'labels/'

import glob
import os
import pathlib
import json
import math
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import pickle
import cv2
import IPython.display as display
import tensorflow as tf
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.applications.inception_resnet_v2 import InceptionResNetV2
from tensorflow.keras.applications.efficientnet import EfficientNetB7
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Flatten, Dense, GlobalAveragePooling2D
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import to_categorical

# Model用パラメータ
img_size = 128
num_channels = 3
num_class = 2
loss='binary_crossentropy'
metrics = ['acc', tf.keras.metrics.AUC()]
batch_size = 64
epoch = 20

# ファイル一覧を確認
for d in glob.glob(dataset_root+'**', recursive=True):
    if pathlib.Path(d).is_dir():
        print(d)

train_tfrecord_path = dataset_root + 'tfrecord/train/'
test_tfrecord_path = dataset_root + 'tfrecord/test/'
print(train_tfrecord_path)
print(test_tfrecord_path)

files = sorted(os.listdir(train_tfrecord_path))
tfrecords = []
for f in files:
    _, ext = os.path.splitext(f)
    if ext == '.tfrecord':
        tfrecords.append(train_tfrecord_path + f)
print(tfrecords)

val_idx = len(tfrecords)-1 # 初期設定は最後のTFRecordを評価用とする
val_tfrecord = tfrecords[val_idx]
train_tfrecord = [tfrecords[i] for i in range(len(tfrecords)) if i != val_idx]

# データセット用パラメータ
image_feature_description = {
    'image': tf.io.FixedLenFeature([], tf.string),
    'image_name': tf.io.FixedLenFeature([], tf.string),
    'target': tf.io.FixedLenFeature([], tf.int64),    
}

def _parse_image_function(example_proto):
  # 入力の tf.Example のプロトコルバッファを上記のディクショナリを使って解釈
  return tf.io.parse_single_example(example_proto, image_feature_description)


def _parse_image_function_for_test(example_proto):
  # 入力の tf.Example のプロトコルバッファを上記のディクショナリを使って解釈
  return tf.io.parse_single_example(example_proto, test_image_feature_description)

def _resize_image_function(example_proto):
    image = tf.image.decode_jpeg(example_proto["image"],channels=num_channels)
    image = tf.image.resize(image,(img_size,img_size))
    image = tf.cast(image,tf.uint8)
    example_proto["image"] = tf.image.encode_jpeg(image)
    return example_proto

def _extract_xy_function(example_proto):
    label = tf.cast(example_proto["target"], tf.int32)
    image = tf.image.decode_jpeg(example_proto["image"],channels = num_channels)
    image = tf.cast(image,tf.float32)
    return image, label

def _extract_xy_function_for_test(example_proto):
    name = tf.cast(example_proto["image_name"],tf.string)
    image = tf.image.decode_jpeg(example_proto["image"],channels = num_channels)
    image = tf.cast(image,tf.float32)
    return image, name

def count_dataset_size(raw_dataset):
    dataset_size = 0
    for raw_record in raw_dataset:
        dataset_size += 1
    return dataset_size

def load_tfrecords(path, shuffle_op=False):
    raw_dataset = tf.data.TFRecordDataset(path)
    dataset_size = count_dataset_size(raw_dataset)
    parsed_image_dataset = raw_dataset.map(_parse_image_function)
    dataset = parsed_image_dataset.map(_resize_image_function)
    dataset = dataset.map(_extract_xy_function)
    if shuffle_op==True:
        dataset = dataset.shuffle(buffer_size=dataset_size)
    dataset = dataset.repeat().batch(batch_size)
    return dataset, dataset_size

def load_tfrecords_for_test(path):
    raw_dataset = tf.data.TFRecordDataset(path)
    dataset_size = count_dataset_size(raw_dataset)
    parsed_image_dataset = raw_dataset.map(_parse_image_function_for_test)
    dataset = parsed_image_dataset.map(_resize_image_function)
    dataset = dataset.map(_extract_xy_function_for_test)
    dataset = dataset.batch(batch_size)
    return dataset, dataset_size

def save_model(model_name, model):
    model_file_name = model_name + '.h5'
    json_file_name = model_name + '.json'
    weights_file_name = model_name + '_weights.h5'
    model.save(model_file_name)
    print('Saved: ' + model_file_name)
    json_string = model.to_json()
    with open(json_file_name, 'w') as f:
        json.dump(json_string, f)
    print('Saved: ' + json_file_name)
    model.save_weights(weights_file_name)
    print('Saved: ' + weights_file_name)

def check_dataset(path, op):
    raw_image_dataset = tf.data.TFRecordDataset(path)

    if op == "train":
        parsed_image_dataset = raw_image_dataset.map(_parse_image_function)
    else:
        parsed_image_dataset = raw_image_dataset.map(_parse_image_function_for_test)

    for image_features in parsed_image_dataset.take(1):
        image_raw = image_features['image'].numpy()
        display.display(display.Image(data=image_raw))

## データセットの点検
check_dataset(train_tfrecord, "train")

ds, ds_size = load_tfrecords(train_tfrecord)
print(ds_size)
val_ds, val_ds_size = load_tfrecords(val_tfrecord)
print(val_ds_size)

def calc_steps(ds_size, val_ds_size, batch_size):
    return math.ceil(ds_size / batch_size), math.ceil(val_ds_size / batch_size)

steps_per_ep, val_steps = calc_steps(ds_size, val_ds_size, batch_size)
print(steps_per_ep)
print(val_steps)

input_tensor = Input(shape=(img_size, img_size, num_channels))
# base_model = InceptionResNetV2(weights='imagenet', include_top=False, pooling='avg', input_tensor=input_tensor)
base_model = EfficientNetB7(weights='imagenet', include_top=False, pooling='avg', input_tensor=input_tensor)

print(base_model.summary())

x = base_model.output

if 0 < num_class <= 2:
    model_output = Dense(1, activation='sigmoid', name='output')(x)
else:
    model_output = Dense(num_class, activation='softmax', name='output')(x)

model = Model(inputs=base_model.input, outputs=model_output)
print(model.summary())
print(len(model.layers))

# 最終層を除いて学習させない
for layer in model.layers[:-1]:
    layer.trainable = False

# 全てのパラメータを学習させる場合
# for layer in model.layers:
#     layer.trainable = True

model.compile(loss=loss, optimizer='adam', metrics=metrics)
print(model.summary())

hist = model.fit(ds, validation_data=val_ds, validation_steps=val_steps, epochs=epoch, steps_per_epoch = steps_per_ep)

idx = list(hist.history.keys())

# 学習結果をグラフで表示
pd.DataFrame({'acc': hist.history[idx[1]], 'val_acc': hist.history[idx[4]]}).plot()
pd.DataFrame({'loss': hist.history[idx[0]], 'val_loss': hist.history[idx[3]]}).plot()
pd.DataFrame({'auc': hist.history[idx[2]], 'val_auc': hist.history[idx[5]]}).plot()
plt.show()

# 学習済モデルの保存
save_model('efficientnetb7_shuffle_128', model)

test_image_feature_description = {
    'image': tf.io.FixedLenFeature([], tf.string),
    'image_name': tf.io.FixedLenFeature([], tf.string),   
}

files = sorted(os.listdir(test_tfrecord_path))
test_tfrecords = []
for f in files:
    _, ext = os.path.splitext(f)
    if ext == '.tfrecord':
        test_tfrecords.append(test_tfrecord_path + f)
test_tfrecords

## データセットの点検
check_dataset(train_tfrecord, "test")

test_ds, test_ds_size = load_tfrecords_for_test(test_tfrecords)
print(test_ds_size)

test_image_ds = test_ds.map(lambda image, label: image)
test_label_ds = test_ds.map(lambda image, label: label)

result = model.predict(test_image_ds).flatten()

print(result.shape)

names = [str(name.numpy()).split("'")[1] for name in test_label_ds.unbatch().take(test_ds_size)]

# 結果の書き出し
csv_file_name = 'efficientnetb7_shuffle_128.csv'
output_ds = pd.DataFrame({'image_name': names, 'target': result}).sort_values(by='image_name', ascending=True)
output_ds.to_csv(csv_file_name, index=False)

今後の改善点

  1. スクリプトファイル化
  2. 冗長な部分を共通化するなどしてリファクタリングする

Reference

今後の研究用(TF2.xのドキュメント)


  1. このサイトのオーナーに実装のアドバイスを貰いました。 

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

tf.Keras @ Tensor Flow 2.xにおけるTFRecord活用の備忘録 1/2【TFRecord作成編】

本記事執筆の動機

ここ数年でDeep Learning用OSSは劇的な進化を遂げました。本記事で紹介するKerasも元々はTheanoやTensor Flowのラッパーに過ぎなかった物が今やTensor Flow(以下TF)の一部(th.Keras)となっています1。学習データは専用のファイル形式(TFRecord)に全て書き込んで,それを読み込むことで簡単にデータセットを共有したり、分割書き込みにも対応しているのでファイルサイズの圧縮が出来たりととても便利になりました。また最大の恩恵はバッチサイズ毎にデータを受け渡せるパイプラインを比較的少ないコードの実装で実現出来ることです。但し、TF2.xがリリースからまだ間もないことも有り、ネット上の情報はTF1.xの情報が大半です。TF2.xはTF1.xのコードと基本的に後方互換性が無い為、モデルの実装やTFRecord活用で大変苦労しました。故に今後の備忘録も兼ねてTFRecordの作成から学習への活用までを記事まとめておこうと思います。今回はTFRecord作成編をまとめたいと思います。

:warning: 作成したTFRecordを使った学習はこの記事の続編であるtf.Keras @ Tensor Flow 2.xにおけるTFRecord活用の備忘録 2/2【学習・評価実行編】を参照して下さい。
:warning: TF2.xからライブラリの構造などが抜本的に変更されました。今後も大幅な変更が更新の度に追加される可能性は十分に有ります。

TFRecord作成用ソースコード

以下にTFRecord作成用のPythonソースコードを示します。コードの実行はJupyter Notebookを前提としています。画像に前処理を実施し,3000枚毎にTFRecordに書き込みます。残った半端な枚数の画像が発生する場合が有りますが,最後に条件分岐を追加することで,余すこと無く書き込みが出来ます。

ソースコード例(JupyterNotebookでの実行を想定)
# データセットやNotebookの格納先ディレクトリのrootパスを記載
dataset_root = ''

import glob
import os
import re
import pathlib
import random
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf

# ファイル一覧の確認
for d in glob.glob(dataset_root+'**', recursive=True):
    if pathlib.Path(d).is_dir():
        print(d)

# ラベルデータ(Table)の格納先パス
tables_path = dataset_root + 'labels/'

# ラベルデータの読み込み
train = pd.read_csv(tables_path+'train.csv')
test = pd.read_csv(tables_path+'test.csv')

# ラベルデータの確認
print(train.head())
print(len(train))

# 水増し対象データセットの絞り込み(利用する場合)
target_argu = train[train['target']==1]
print(target_argu.head())
target_files = target_argu['image_name']
target_files = list(target_files)
len(target_files)

# 水増し後のデータセットの格納先パスを設定
img_root = dataset_root + 'images/train/'
new_dataset_path = dataset_root + 'images/train_arg/'
if not os.path.isdir(new_dataset_path):
        os.makedirs(new_dataset_path)

# 回転角の設定
angles = [i for i in range(0,361,45)]
angles = angles[:-1]

# 回転,切り出し及びリサイズ
for f in target_files:
    file_path = img_root + f + '.jpg'
    img = cv2.imread(file_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    size = (1024, 1024)
    img = cv2.resize(img, size)    
    center = tuple(np.array([img.shape[1]*0.5, img.shape[0]*0.5]))
    cutsize = (512, 512)
    scale = 1.0
    for angle in angles:
        rot_mat = cv2.getRotationMatrix2D(center, angle, scale)
        rotate_img = cv2.warpAffine(img, rot_mat, size, flags=cv2.INTER_LINEAR)
        img_name = '{}{}_{}degree.jpg'.format(new_dataset_path, f, angle)
        cut_width_start = int(cutsize[1]/2)
        cut_width_end = cut_width_start * 3
        cut_height_start = int(cutsize[0]/2)
        cut_height_end = cut_height_start * 3
        cut_img = rotate_img[cut_width_start:cut_width_end, cut_height_start:cut_height_end, :]
        cut_img = cv2.cvtColor(cut_img, cv2.COLOR_RGB2BGR)
        cv2.imwrite(img_name, cut_img)

# フリップ(左右反転)させる画像の選択(水増し後の画像)
flip_target_files = os.listdir(new_dataset_path)

# フリップ処理
for f in flip_target_files:
    base, ext = os.path.splitext(f)
    if ext == '.jpg':
        img = cv2.imread(new_dataset_path + f)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_name = new_dataset_path + base + '_flip' + ext
        flip_img = cv2.flip(img, 1)
        flip_img = cv2.cvtColor(flip_img, cv2.COLOR_RGB2BGR)
        cv2.imwrite(img_name, flip_img)

aug_image_nums = len(flip_target_files)*2
aug_image_nums

target_org = train[train['target']==0]
print(target_org.head())

range_num = len(target_org)
print(range_num)

index_list = random.sample(range(range_num), k=aug_image_nums)
print(index_list[:5])
print(len(index_list))
target_img_list = [target_org.iloc[i]['image_name'] for i in index_list]
print(target_img_list[:5])
print(len(target_img_list))
size = (512, 512)

# 処理結果の書き込み
for f in target_img_list:
    file_path = img_root + f + '.jpg'
    save_path = new_dataset_path + f + '.jpg'
    img = cv2.imread(file_path)
    img = cv2.resize(img, size) 
    cv2.imwrite(save_path, img)
file_list = sorted(os.listdir(new_dataset_path))
label_path = dataset_root + 'labels/new_train_labels.csv'

with open(label_path, 'w') as f:
    f.write('image_name, target\n')
    for fname in file_list:
        if re.search('degree', fname) == None:
            f.write(fname+', 0\n')
        else:
            f.write(fname+', 1\n')
new_label = pd.read_csv(label_path)
new_label.head()

# TFRecordの生成
## 学習用データの作成
def _bytes_feature(value):
    """Returns a bytes_list from a string / byte."""
    if isinstance(value, type(tf.constant(0))):
        value = value.numpy() # BytesList won't unpack a string from an EagerTensor.
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def _float_feature(value):
    """Returns a float_list from a float / double."""
    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))

def _int64_feature(value):
    """Returns an int64_list from a bool / enum / int / uint."""
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def serialize_example(image, image_name, target):
    feature = {
        'image': _bytes_feature(image),
        'image_name': _bytes_feature(image_name),
        'target': _int64_feature(target)
    }
    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))
    return example_proto.SerializeToString()

DEVIDE_SIZE = 3000                                                                                   # 3000枚毎にファイルを分ける
IMG_NUM = len(new_label)                                                                                  # 全画像枚数
loop_num = IMG_NUM // DEVIDE_SIZE + int(IMG_NUM%DEVIDE_SIZE!=0)  # ループ回数

# TFRecordファイルの保存先パス
tfrecord_root = dataset_root + 'tfrecord/train/'

if not os.path.isdir(tfrecord_root):
        os.makedirs(tfrecord_root)

for i in range(loop_num):
    tfrecord_path = tfrecord_root + 'train_argumentation_tfrecord_{}_of_{}.tfrecord'.format(i+1, loop_num)
    print('Writing: ' + tfrecord_path)
    with tf.io.TFRecordWriter(tfrecord_path) as writer:
        if(i < loop_num-1):
            for j in range(0+DEVIDE_SIZE*i, DEVIDE_SIZE+DEVIDE_SIZE*i):
                img_name = new_label.iat[j, 0]
                label = new_label.iat[j, 1]
                img_path = new_dataset_path + img_name
                img = cv2.imread(img_path)
                # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img = cv2.imencode('.jpg', img, (cv2.IMWRITE_JPEG_QUALITY, 94))[1].tostring()
                example = serialize_example(img, str.encode(img_name.split('.')[0]), label)
                writer.write(example)         
        else:
            for k in range(DEVIDE_SIZE*(loop_num-1), IMG_NUM):
                img_name = new_label.iat[k, 0]
                label = new_label.iat[k, 1]
                img_path = new_dataset_path + img_name
                img = cv2.imread(img_path)
                # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img = cv2.imencode('.jpg', img, (cv2.IMWRITE_JPEG_QUALITY, 94))[1].tostring()
                example = serialize_example(img, str.encode(img_name.split('.')[0]), label)
                writer.write(example)

## テスト用データの作成

def serialize_example_for_test(image, image_name):
    feature = {
        'image': _bytes_feature(image),
        'image_name': _bytes_feature(image_name)
    }
    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))
    return example_proto.SerializeToString()

DEVIDE_SIZE = 3000  # 3000枚毎にファイルを分ける
IMG_NUM = len(test) # 全画像枚数
loop_num = IMG_NUM // DEVIDE_SIZE + int(IMG_NUM%DEVIDE_SIZE!=0)  # ループ回数
size = (512, 512)

test_path = dataset_root + 'images/test/'
tfrecord_root = dataset_root + 'tfrecord/test/'

if not os.path.isdir(tfrecord_root):
        os.makedirs(tfrecord_root)

for i in range(loop_num):
    tfrecord_path = tfrecord_root + 'test_tfrecord_{}_of_{}.tfrecord'.format(i+1, loop_num)
    print('Writing: ' + tfrecord_path)
    with tf.io.TFRecordWriter(tfrecord_path) as writer:
        if(i < loop_num-1):
            for j in range(0+DEVIDE_SIZE*i, DEVIDE_SIZE+DEVIDE_SIZE*i):
                img_name = test.iat[j, 0] + '.jpg'
                img_path = test_path + img_name
                img = cv2.imread(img_path)
                # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img = cv2.resize(img, size)
                img = cv2.imencode('.jpg', img, (cv2.IMWRITE_JPEG_QUALITY, 94))[1].tostring()
                example = serialize_example_for_test(img, str.encode(img_name.split('.')[0]))
                writer.write(example)         
        else:
            for k in range(DEVIDE_SIZE*(loop_num-1), IMG_NUM):
                img_name = test.iat[k, 0] + '.jpg'
                img_path = test_path + img_name
                img = cv2.imread(img_path)
                # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img = cv2.resize(img, size)
                img = cv2.imencode('.jpg', img, (cv2.IMWRITE_JPEG_QUALITY, 94))[1].tostring()
                example = serialize_example_for_test(img, str.encode(img_name.split('.')[0]))
                writer.write(example)

今後の改善点

  1. 前処理部分も関数化して処理の実施の有無を選択可能にする。
  2. TFRecord書き出し部分が条件分岐内部の処理が粗同じなので共通部分は関数化してコード量を削減する。

Reference

今後の研究用(TF2.xのドキュメント)


  1. Kerasの開発者がGoogleに入社したことがKerasのTensor Flowへの同梱に繋がった様です。参考 

  2. このサイトのオーナーに実装のアドバイスを貰いました。 

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

Keras(Tensorflow)で用いられる様々な行列演算のイメージを実例で理解する

はじめに

Kerasのコードを読むと様々な行列演算に遭遇する。これらの演算の中身を知らないと読み進めることが非常に難しい。今回、私が読んだコードを中心に、Kerasによく出てくる行列演算を実例を元に確認したため共有する。

環境

Kerasといっても、今回確認した環境は、tensorflowの一部であった時代の古いバージョンである。ただ、行列演算は今とそれほど変わらないと思う。

  • tensoflow 1.14.0

サンプルを動作させる場合の注意点

本記事のソースコードは以下のインポートを前提としています。動作させる場合はコピペしておいてください。

import numpy as np
import warnings
import os
warnings.simplefilter('ignore')

import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense, Input, Dropout, BatchNormalization

確認方法

確認方法は、 Kerasでの処理をイメージできることを想定し、以下の通りとした。

  1. 演算を行うKerasのモデルを作成

  2. モデルのpredictメソッドに入力を与え、その結果を出力させることで、計算の内容を確認

やってみよう

concat

指定した軸に沿ってテンソルのリストを連結する。

ソース

    input1 = Input(shape=(2,))
    input2 = Input(shape=(2,))
    output = tf.concat(axis=1, values=[input1, input2])
    model = Model(inputs=[input1, input2], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[10, 20], [20, 30]]), np.array([[20, 5], [30, 2]])]))

結果

[[10. 20. 20.  5.]
 [20. 30. 30.  2.]]

stack

ランクRのテンソルのリストをランクR+1のテンソルに積み上げる。
concatとの大きな違いは、そのまま連結するのではなく、軸を1つ追加した上で連結する点であり、連結後も連結前の情報を取り出すことができるという点だろうか。

ソース

    # stack
    input1 = Input(shape=(2,))
    input2 = Input(shape=(2,))
    output = tf.stack(axis=1, values=[input1, input2])
    model = Model(inputs=[input1, input2], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[10, 20], [20, 30]]), np.array([[20, 5], [30, 2]])]))

結果

[[[10. 20.]
  [20.  5.]]
 [[20. 30.]
  [30.  2.]]]

expand_dims

添字"axis"でのサイズ1の次元を加える。

ソース

    input1 = Input(shape=(1,))
    output = tf.expand_dims(input1, axis=1)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[10], [20], [30]])]))

結果

[[[10.]]
 [[20.]]
 [[30.]]]

squeeze

expand_dimsの逆で、テンソルから添字"axis"での1次元を除く。

ソース

    input1 = Input(shape=(1,1,))
    output = tf.squeeze(input1, axis=1)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10]], [[20]], [[30]]])]))

結果

[[10.]
 [20.]
 [30.]]

reduce_max

テンソルの次元全体で要素の最大値を計算する。

ソース

以下は 3x2 の行列に対し、0次元、1次元、2次元で演算してみた。0次元だと次元はそのままで結果がブロードキャストされ、1次元だと結果が全く同じであり、2次元で次数が減るというところが興味深い。

    input1 = Input(shape=(1,2))
    output = tf.reduce_max(input1, axis=0)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

    input1 = Input(shape=(1,2))
    output = tf.reduce_max(input1, axis=1)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

    input1 = Input(shape=(1,2))
    output = tf.reduce_max(input1, axis=2)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

結果

[[30. 20.]
 [30. 20.]
 [30. 20.]]


[[10. 20.]
 [20.  5.]
 [30.  4.]]

[[20.]
 [20.]
 [30.]]

reduce_sum

テンソルの次元全体の要素の合計を計算する。ソースも同様に3x2 の行列に対し、0次元、1次元、2次元で演算してみた。reduce_maxと考え方は同じである。

ソース 

    input1 = Input(shape=(1,2))
    output = tf.reduce_sum(input1, axis=0)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

    input1 = Input(shape=(1,2))
    output = tf.reduce_sum(input1, axis=1)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

    input1 = Input(shape=(1,2))
    output = tf.reduce_sum(input1, axis=2)
    model = Model(inputs=[input1], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[[10, 20]], [[20, 5]], [[30, 4]]])]))

結果

[[60. 29.]
 [60. 29.]
 [60. 29.]]

[[10. 20.]
 [20.  5.]
 [30.  4.]]

[[30.]
 [25.]
 [34.]]

matmul

"Multiplies matrix"の略で、行列aに行列bを乗算して、a * bを生成する。全結合層等によく使われる。

ソース

以下は1x2と2x1の行列を演算した結果、1x1の行列が生成される例である。

    input1 = Input(shape=(2,))
    input2 = Input(shape=(2,1))
    output = tf.matmul(input1, input2)
    model = Model(inputs=[input1, input2], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[10, 20]]), np.array([[[1], [2]]])]))

結果

[[[50.]]]

slice

テンソルに対し、開始位置および抽出サイズを指定し、テンソルの一部を抽出する。

ソース

    input1 = Input(shape=(4,))
    input1_reshape = tf.reshape(input1, [4])
    input2 = Input(shape=(1), dtype=tf.int32)
    input2_reshape = tf.reshape(input2, [1])
    input3 = Input(shape=(1), dtype=tf.int32)
    input3_reshape = tf.reshape(input3, [1])
    output = tf.slice(input1_reshape, input2_reshape, input3_reshape)
    model = Model(inputs=[input1, input2, input3], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[1, 8, 3, 4]]), np.array([[1]]), np.array([[1]])]))

結果

[8.]

gather

テンソルに対し、インデックスを指定し、インデックスの要素を取得する。

ソース

[1,8,3,4]というリストに対し、0番目、3番目のものを取り出したリストと、1番目、2番目のものを取り出したリストを要素とした行列を返している。入力のshapeが確定していないとエラーとなるため、無理やりreshapeしている。

    input1 = Input(shape=(4,))
    input1_reshape = tf.reshape(input1, [4])
    input2 = Input(shape=(2,2), dtype=tf.int32)
    output = tf.gather(input1_reshape, input2)
    model = Model(inputs=[input1, input2], outputs=[output])
    print(model.summary())
    print(model.predict(x=[np.array([[1, 8, 3, 4]]), np.array([[[0, 3],[1, 2]]])]))

結果

[[[1. 4.]
  [8. 3.]]]

おわりに

  • 今回は一部の演算のみの掲載ではあるが、多少、読解力があがったのではないかと期待する。
  • Kerasのモデルの入力に与えて計算させるだけでも、行列の次元数が合わない等のエラーにより、中々すんなりとはいかずかなり苦労したが、行列の理解にはおおいに役立った。
  • 特にInputのshape引数の値と、predcitに与える行列の形状を合わせることが重要となる
  • 今回のサンプルは、Kerasにおいて複数のInputをとるModelの作成、予測する場合のミニマムコードともなるので、今後複雑なモデルを作成する場合にも役立つと考える。

参考

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