View on TensorFlow.org | Run in Google Colab | View source on GitHub |
これは、下記の手法を示す TensorFlow の入門チュートリアルです。
- 必要なパッケージのインポート
- テンソルの作成と使用
- GPUによる高速化の使用
tf.data.Dataset
のデモ
from __future__ import absolute_import, division, print_function, unicode_literals
!pip install -q tensorflow-gpu==2.0.0-beta1
TensorFlowのインポート
はじめに、tensorflow
モジュールをインポートします。TensorFlow 2.0 では、eager execution が既定でオンとなっています。
これにより、TensorFlow のフロントエンドがよりインタラクティブになります。詳細は後述します。
import tensorflow as tf
テンソル
テンソルは多次元配列です。NumPy の ndarray
オブジェクトと同様に、tf.Tensor
にはデータ型と形状があります。これに加えて、tf.Tensor
は( GPU のような)アクセラレータのメモリに置くことができます。TensorFlow には、tf.Tensor
を使用し生成するたくさんの演算(tf.add, tf.matmul, tf.linalg.inv など)のライブラリが存在します。これらの演算では、ネイティブな Python データ型が自動変換されます。例を示します。
print(tf.add(1, 2))
print(tf.add([1, 2], [3, 4]))
print(tf.square(5))
print(tf.reduce_sum([1, 2, 3]))
# Operator overloading is also supported
print(tf.square(2) + tf.square(3))
tf.Tensor(3, shape=(), dtype=int32) tf.Tensor([4 6], shape=(2,), dtype=int32) tf.Tensor(25, shape=(), dtype=int32) tf.Tensor(6, shape=(), dtype=int32) tf.Tensor(13, shape=(), dtype=int32)
それぞれのtf.Tensor
には、形状とデータ型があります。
x = tf.matmul([[1]], [[2, 3]])
print(x)
print(x.shape)
print(x.dtype)
tf.Tensor([[2 3]], shape=(1, 2), dtype=int32) (1, 2) <dtype: 'int32'>
NumPy 配列と tf.Tensor
の間のもっとも明確な違いは
- テンソルは( GPU や TPU などの)アクセラレータメモリを使用できる
- テンソルは変更不可
NumPy互換性
TensorFlow のtf.Tensor
と NumPy の ndarray
間の変換は簡単です。
- TensorFlow の演算により NumPy の ndarray は自動的にテンソルに変換される
- NumPy の演算によりテンソルは自動的に NumPy の ndarray に変換される
テンソルは .numpy()
メソッドを使って明示的に NumPy の ndarray に変換されます。NumPy のndarray と tf.Tensor
はその下敷きとなるメモリ上の表現が、できるかぎり共通化されているので、通常この変換のコストは小さいです。しかし、NumPy 配列はホスト側のメモリに置かれる一方、tf.Tensor
はGPU のメモリに置かれる可能性もあるため、下層の表現をいつも共通化できるとは限りません。また、変換にはGPU からホスト側メモリへのコピーも関わってきます。
import numpy as np
ndarray = np.ones([3, 3])
print("TensorFlow演算によりnumpy配列は自動的にテンソルに変換される")
tensor = tf.multiply(ndarray, 42)
print(tensor)
print("またNumPy演算によりテンソルは自動的にnumpy配列に変換される")
print(np.add(tensor, 1))
print(".numpy()メソッドによりテンソルは明示的にnumpy配列に変換される")
print(tensor.numpy())
TensorFlow演算によりnumpy配列は自動的にテンソルに変換される tf.Tensor( [[42. 42. 42.] [42. 42. 42.] [42. 42. 42.]], shape=(3, 3), dtype=float64) またNumPy演算によりテンソルは自動的にnumpy配列に変換される [[43. 43. 43.] [43. 43. 43.] [43. 43. 43.]] .numpy()メソッドによりテンソルは明示的にnumpy配列に変換される [[42. 42. 42.] [42. 42. 42.] [42. 42. 42.]]
GPU による高速化
TensorFlow の演算の多くは、GPU を計算に使用することで高速化されます。TensorFlow は演算に注釈をつけなくとも、自動的に GPU と CPU のどちらかを選択し、必要であればテンソルを GPU メモリと CPU メモリの間でコピーして実行します。演算で生成されたテンソルは通常演算を実行したデバイスのメモリに置かれます。例を見てみましょう。
x = tf.random.uniform([3, 3])
print("利用できるGPUはあるか: "),
print(tf.test.is_gpu_available())
print("テンソルはGPU #0にあるか: "),
print(x.device.endswith('GPU:0'))
利用できるGPUはあるか: True テンソルはGPU #0にあるか: True
デバイス名
Tensor.device
プロパティにより、そのテンソルの内容を保持しているデバイスの完全な名前文字列を得ることができます。この名前には、プログラムを実行中のホストのネットワークアドレスや、ホスト上のデバイスについての詳細がエンコードされています。この情報は、TensorFlow プログラムの分散実行に必要なものです。テンソルがホスト上の N
番目のGPUにある場合、文字列の最後は GPU:<N>
となります。
明示的デバイス配置
TensorFlowでいう配置は、個々の演算を実行するためにどのようにデバイスにアサイン(配置)されるかを指します。前述のとおり、明示的な示唆がなければ、TensorFlow は演算を実行するデバイスを自動的に決め、必要であればテンソルをそのデバイスにコピーします。しかし、tf.device
コンテキストマネジャーを使うことで、TensorFlow の演算を特定のデバイスに配置することができます。例を見てみましょう。
import time
def time_matmul(x):
start = time.time()
for loop in range(10):
tf.matmul(x, x)
result = time.time()-start
print("10 loops: {:0.2f}ms".format(1000*result))
# CPUでの実行を強制
print("On CPU:")
with tf.device("CPU:0"):
x = tf.random.uniform([1000, 1000])
assert x.device.endswith("CPU:0")
time_matmul(x)
# GPU #0があればその上での実行を強制
if tf.test.is_gpu_available():
print("On GPU:")
with tf.device("GPU:0"): # 2番めのGPUなら GPU:1, 3番目なら GPU:2 など
x = tf.random.uniform([1000, 1000])
assert x.device.endswith("GPU:0")
time_matmul(x)
On CPU: 10 loops: 108.40ms On GPU: 10 loops: 314.93ms
データセット
このセクションでは tf.data.Dataset
API を使って、モデルにデータを供給するためのパイプラインを構築します。tf.data.Dataset
APIは、単純で再利用可能な部品をもとに、モデルの訓練あるいは評価ループにデータを供給する高性能で複雑な入力パイプラインを構築するために使われます。
ソースDataset
の作成
Dataset.from_tensors やDataset.from_tensor_slices といったファクトリー関数または TextLineDataset あるいはTFRecordDataset のようなファイルを読み込むオブジェクトを使って、 元となるデータセットを作成しましょう。詳しくは、TensorFlow Dataset guide を参照してください。
ds_tensors = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6])
# CSVファイルを作成
import tempfile
_, filename = tempfile.mkstemp()
with open(filename, 'w') as f:
f.write("""Line 1
Line 2
Line 3
""")
ds_file = tf.data.TextLineDataset(filename)
変換の適用
map, batch, shuffle などの変換関数を使って、データセットレコードに変換を適用します。
ds_tensors = ds_tensors.map(tf.square).shuffle(2).batch(2)
ds_file = ds_file.batch(2)
イテレート
tf.data.Dataset
オブジェクトは、中のレコードを繰り返し利用するためのイテレーションをサポートします。
print('ds_tensors の要素:')
for x in ds_tensors:
print(x)
print('\nds_file の要素:')
for x in ds_file:
print(x)
ds_tensors の要素: tf.Tensor([4 9], shape=(2,), dtype=int32) tf.Tensor([16 25], shape=(2,), dtype=int32) tf.Tensor([ 1 36], shape=(2,), dtype=int32) ds_file の要素: tf.Tensor([b'Line 1' b'Line 2'], shape=(2,), dtype=string) tf.Tensor([b'Line 3' b' '], shape=(2,), dtype=string)