TensorFlow の基礎

TensorFlow.org で表示 Google Colab で実行 GitHubでソースを表示 ノートブックをダウンロード

このガイドでは、TensorFlow basics の簡単な概要を説明します。このドキュメントの各セクションは、より大きなトピックの概要です。各セクションの末尾に、ガイド全文へのリンクを記載しています。

TensorFlow は機械学習用のエンドツーエンドプラットフォームで、以下の内容をサポートしています。

  • 多次元配列ベースの数値計算(NumPy に類似)
  • GPU および分散処理
  • 自動微分
  • モデルの構築、トレーニング、およびエクスポート
  • その他

テンソル

TensorFlow は、tf.Tensor として表現される多次元配列またはテンソルを操作します。以下は2次元テンソルです。

import tensorflow as tf

x = tf.constant([[1., 2., 3.],
                 [4., 5., 6.]])

print(x)
print(x.shape)
print(x.dtype)
2024-01-11 18:35:26.203069: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-01-11 18:35:26.203114: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-01-11 18:35:26.204654: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
tf.Tensor(
[[1. 2. 3.]
 [4. 5. 6.]], shape=(2, 3), dtype=float32)
(2, 3)
<dtype: 'float32'>

tf.Tensor の最も重要な属性は shapedtype です。

  • Tensor.shape: 各軸に沿ったテンソルのサイズを示します。
  • Tensor.dtype: テンソル内のすべての要素の型を示します。

TensorFlow はテンソルに標準の算術演算や機械学習に特化した多数の演算を実装します。

以下に例を示します。

x + x
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[ 2.,  4.,  6.],
       [ 8., 10., 12.]], dtype=float32)>
5 * x
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[ 5., 10., 15.],
       [20., 25., 30.]], dtype=float32)>
x @ tf.transpose(x)
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[14., 32.],
       [32., 77.]], dtype=float32)>
tf.concat([x, x, x], axis=0)
<tf.Tensor: shape=(6, 3), dtype=float32, numpy=
array([[1., 2., 3.],
       [4., 5., 6.],
       [1., 2., 3.],
       [4., 5., 6.],
       [1., 2., 3.],
       [4., 5., 6.]], dtype=float32)>
tf.nn.softmax(x, axis=-1)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0.09003057, 0.24472848, 0.6652409 ],
       [0.09003057, 0.24472848, 0.6652409 ]], dtype=float32)>
tf.reduce_sum(x)
<tf.Tensor: shape=(), dtype=float32, numpy=21.0>

注意: 通常、TensorFlow 関数が Tensor を入力として期待する場合、関数は tf.convert_to_tensor を使用して Tensor に変換できるものをすべて受け入れます。例については、以下を参照してください。

tf.convert_to_tensor([1,2,3])
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 2, 3], dtype=int32)>
tf.reduce_sum([1,2,3])
<tf.Tensor: shape=(), dtype=int32, numpy=6>

大規模な計算を CPU で実行すると低速化する可能性があります。適切に構成すれば、TensorFlow は GPU などのアクセラレータハードウェアを使用して演算を非常に素早く実行することが可能です。

if tf.config.list_physical_devices('GPU'):
  print("TensorFlow **IS** using the GPU")
else:
  print("TensorFlow **IS NOT** using the GPU")
TensorFlow **IS** using the GPU

詳細は、テンソルガイドをご覧ください。

変数

通常の tf.Tensor オブジェクトはイミュータブルです。TensorFlow にモデルの重み(またはその他のミュータブルな状態)を格納するには、tf.Variable を使用します。

var = tf.Variable([0.0, 0.0, 0.0])
var.assign([1, 2, 3])
<tf.Variable 'UnreadVariable' shape=(3,) dtype=float32, numpy=array([1., 2., 3.], dtype=float32)>
var.assign_add([1, 1, 1])
<tf.Variable 'UnreadVariable' shape=(3,) dtype=float32, numpy=array([2., 3., 4.], dtype=float32)>

詳細は、変数ガイドをご覧ください。

自動微分

最新の機械学習の基礎は、勾配降下と関連アルゴリズムです。

これを可能にするために、TensorFlow は自動微分(autodiff)を実装しており、微積分を使用して勾配を計算します。通常、モデルの重みに関する誤差または損失の勾配を計算する際にこれを使用します。

x = tf.Variable(1.0)

def f(x):
  y = x**2 + 2*x - 5
  return y
f(x)
<tf.Tensor: shape=(), dtype=float32, numpy=-2.0>

x = 1.0 の場合、y = f(x) = (1**2 + 2*1 - 5) = -2 となります。

y の導関数は y' = f'(x) = (2*x + 2) = 4 です。TensorFlow はこれを自動的に計算できます。

with tf.GradientTape() as tape:
  y = f(x)

g_x = tape.gradient(y, x)  # g(x) = dy/dx

g_x
<tf.Tensor: shape=(), dtype=float32, numpy=4.0>

この単純な例は、単一のスカラー(x)に関する導関数のみを取っていますが、TensorFlow はスカラーでない任意の数のテンソルに関する勾配を同時に計算できます。

詳細は、自動微分ガイドをご覧ください。

グラフと tf.function

TensorFlow は Python ライブラリのように対話型で使用できますが、以下を行うためのツールも提供しています。

  • パフォーマンス最適化: トレーニングと推論を高速化します。
  • エクスポート: モデルのトレーニングが完了したら、そのモデルを保存できます。

これらを行うには、tf.function を使用して、純粋な TensorFlow コードと Python を分離する必要があります。

@tf.function
def my_func(x):
  print('Tracing.\n')
  return tf.reduce_sum(x)

tf.function を初めて実行すると、Python として実行されますが、関数内で行われた TensorFlow 計算を表現する最適化された完全なグラフがキャプチャされます。

x = tf.constant([1, 2, 3])
my_func(x)
Tracing.
<tf.Tensor: shape=(), dtype=int32, numpy=6>

後続の呼び出しでは、TensorFlow は最適化グラフのみを実行し、TensorFlow でないステップが省略されます。以下では、my_funcトレーシングが出力されないところに注意してください。print は Python 関数であり、TensorFlow 関数ではないためです。

x = tf.constant([10, 9, 8])
my_func(x)
<tf.Tensor: shape=(), dtype=int32, numpy=27>

シグネチャshapedtype)が異なる入力ではグラフを再利用できない可能性があるため、代わりに新しいグラフが生成されます。

x = tf.constant([10.0, 9.1, 8.2], dtype=tf.float32)
my_func(x)
Tracing.
<tf.Tensor: shape=(), dtype=float32, numpy=27.3>

キャプチャされたこれらのグラフには、以下の 2 つのメリットがあります。

  • 多くの場合、実行速度が大幅に高速化します(このような単純な例では高速化されません)。
  • tf.saved_model を使ってこれらのグラフをエクスポートすると、Python がインストールされていないサーバーモバイルデバイスなどの他のシステムで実行することができます。

詳細は、グラフの導入をご覧ください。

モジュール、レイヤー、モデル

tf.Module は、tf.Variable オブジェクトと、それを操作する tf.function オブジェクトを管理するためのクラスです。tf.Module は、以下の重要な 2 つの機能をサポートするために必要なクラスです。

  1. tf.train.Checkpoint を使用すると、変数の値を保存し、復元することができます。モデルの状態を素早く保存して復元できるため、トレーニング中に役立ちます。
  2. tf.saved_model を使用すると、tf.Variable の値 tf.function グラフのインポートとエクスポートを行えます。そのため、モデルを作成した Python プログラムに頼らずに、モデルを実行することができます。

単純な tf.Module オブジェクトの完全なエクスポート例を以下に示します。

class MyModule(tf.Module):
  def __init__(self, value):
    self.weight = tf.Variable(value)

  @tf.function
  def multiply(self, x):
    return x * self.weight
mod = MyModule(3)
mod.multiply(tf.constant([1, 2, 3]))
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([3, 6, 9], dtype=int32)>

Module を保存します。

save_path = './saved'
tf.saved_model.save(mod, save_path)
INFO:tensorflow:Assets written to: ./saved/assets

結果で得られる SavedModel は、それを作成したコードから独立しています。この SavedModel は、Python、その他の言語バインディング、または TensorFlow Serving から読み込むことができアンス。また、TensorFlow Lite または TensorFlow JS で実行できるように変換することも可能です。

reloaded = tf.saved_model.load(save_path)
reloaded.multiply(tf.constant([1, 2, 3]))
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([3, 6, 9], dtype=int32)>

tf.keras.layers.Layer クラスと tf.keras.Model クラスは tf.Module を基礎とし、モデルを構築、トレーニング、および保存するための追加機能と便利な方法を提供します。一部の機能については、次のセクションで説明しています。

詳細は、モジュールの導入をご覧ください。

トレーニングループ

では、これらをゼロから合わせて基本的なモデルを構築し、トレーニングします。

まず、サンプルデータを作成します。これは、二次曲線に緩やかに沿ったポイントクラウドを生成します。

import matplotlib
from matplotlib import pyplot as plt

matplotlib.rcParams['figure.figsize'] = [9, 6]
x = tf.linspace(-2, 2, 201)
x = tf.cast(x, tf.float32)

def f(x):
  y = x**2 + 2*x - 5
  return y

y = f(x) + tf.random.normal(shape=[201])

plt.plot(x.numpy(), y.numpy(), '.', label='Data')
plt.plot(x, f(x), label='Ground truth')
plt.legend();

png

ランダムに初期化した重みとバイアスを使って、二次モデルを作成します。

class Model(tf.Module):

  def __init__(self):
    # Randomly generate weight and bias terms
    rand_init = tf.random.uniform(shape=[3], minval=0., maxval=5., seed=22)
    # Initialize model parameters
    self.w_q = tf.Variable(rand_init[0])
    self.w_l = tf.Variable(rand_init[1])
    self.b = tf.Variable(rand_init[2])

  @tf.function
  def __call__(self, x):
    # Quadratic Model : quadratic_weight * x^2 + linear_weight * x + bias
    return self.w_q * (x**2) + self.w_l * x + self.b

トレーニングする前に、モデルのパフォーマンスを観察します。

quad_model = Model()
def plot_preds(x, y, f, model, title):
  plt.figure()
  plt.plot(x, y, '.', label='Data')
  plt.plot(x, f(x), label='Ground truth')
  plt.plot(x, model(x), label='Predictions')
  plt.title(title)
  plt.legend()
plot_preds(x, y, f, quad_model, 'Before training')

png

次に、モデルtの損失を定義します。

このモデルは連続する値を予測するものであるため、損失関数には平均二乗誤差(MSE)が最適です。予測のベクトルを \(\hat{y}\) とすると、予測値とグラウンドトゥルースの二乗差の平均として定義されます。

\(MSE = \frac{1}{m}\sum_{i=1}^{m}(\hat{y}_i -y_i)^2\)

def mse_loss(y_pred, y):
  return tf.reduce_mean(tf.square(y_pred - y))

モデルの基本的なトレーニングループを記述します。このループは、モデルのパラメータを反復的に更新するために、MSE 損失関数と入力に対する勾配を利用します。トレーニングのミニバッチを使用すると、メモリ効率とより高速な収束を得られます。tf.data.Dataset API には、バッチ処理とシャッフル用の便利な関数が備わっています。

batch_size = 32
dataset = tf.data.Dataset.from_tensor_slices((x, y))
dataset = dataset.shuffle(buffer_size=x.shape[0]).batch(batch_size)
# Set training parameters
epochs = 100
learning_rate = 0.01
losses = []

# Format training loop
for epoch in range(epochs):
  for x_batch, y_batch in dataset:
    with tf.GradientTape() as tape:
      batch_loss = mse_loss(quad_model(x_batch), y_batch)
    # Update parameters with respect to the gradient calculations
    grads = tape.gradient(batch_loss, quad_model.variables)
    for g,v in zip(grads, quad_model.variables):
        v.assign_sub(learning_rate*g)
  # Keep track of model loss per epoch
  loss = mse_loss(quad_model(x), y)
  losses.append(loss)
  if epoch % 10 == 0:
    print(f'Mean squared error for step {epoch}: {loss.numpy():0.3f}')

# Plot model results
print("\n")
plt.plot(range(epochs), losses)
plt.xlabel("Epoch")
plt.ylabel("Mean Squared Error (MSE)")
plt.title('MSE loss vs training iterations');
Mean squared error for step 0: 55.600
Mean squared error for step 10: 9.563
Mean squared error for step 20: 3.912
Mean squared error for step 30: 1.961
Mean squared error for step 40: 1.296
Mean squared error for step 50: 1.060
Mean squared error for step 60: 0.975
Mean squared error for step 70: 0.945
Mean squared error for step 80: 0.934
Mean squared error for step 90: 0.931

png

トレーニングする前に、モデルのパフォーマンスを観察しましょう。

plot_preds(x, y, f, quad_model, 'After training')

png

動作してはいますが、tf.keras モジュールには、共通のトレーニングユーティリティの実装が存在することを思い出してください。独自のユーティリティを記述する前に、それらを使用することを検討できます。まず、Model.compileModel.fit メソッドを使って、トレーニングループを実装できます。

tf.keras.Sequential を使って Keras で Sequential Model を作成することから始めます。最も単純な Keras レイヤーの 1 つは Dense レイヤーです。これは、tf.keras.layers.Dense を使ってインスタンス化できます。密なレイヤーは、\(\mathrm{Y} = \mathrm{W}\mathrm{X} + \vec{b}\) の形態の多次元線形リレーションを学習できます。\(w_1x^2 + w_2x + b\) の非線形式を学習するために、密なレイヤーの入力は \(x^2\) and \(x\) を特徴量とするデータ行列である必要があります。tf.keras.layers.Lambda ラムダレイヤーは、このスタック変換を実行するために使用できます。

new_model = tf.keras.Sequential([
    tf.keras.layers.Lambda(lambda x: tf.stack([x, x**2], axis=1)),
    tf.keras.layers.Dense(units=1, kernel_initializer=tf.random.normal)])
new_model.compile(
    loss=tf.keras.losses.MSE,
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.01))

history = new_model.fit(x, y,
                        epochs=100,
                        batch_size=32,
                        verbose=0)

new_model.save('./my_new_model')
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1704998137.631935   85850 device_compiler.h:186] Compiled cluster using XLA!  This line is logged at most once for the lifetime of the process.
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(2, 1), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c6e00a0>, 140473019619168), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(2, 1), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c6e00a0>, 140473019619168), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(1,), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c60afd0>, 140473019125312), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(1,), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c60afd0>, 140473019125312), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(2, 1), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c6e00a0>, 140473019619168), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(2, 1), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c6e00a0>, 140473019619168), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(1,), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c60afd0>, 140473019125312), {}).
INFO:tensorflow:Unsupported signature for serialization: ((TensorSpec(shape=(1,), dtype=tf.float32, name='gradient'), <tensorflow.python.framework.func_graph.UnknownArgument object at 0x7fc26c60afd0>, 140473019125312), {}).
INFO:tensorflow:Assets written to: ./my_new_model/assets
INFO:tensorflow:Assets written to: ./my_new_model/assets
/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/keras/src/initializers/__init__.py:144: UserWarning: The `keras.initializers.serialize()` API should only be used for objects of type `keras.initializers.Initializer`. Found an instance of type <class 'function'>, which may lead to improper serialization.
  warnings.warn(

トレーニング後の Keras モデルのパフォーマンスを観察します。

plt.plot(history.history['loss'])
plt.xlabel('Epoch')
plt.ylim([0, max(plt.ylim())])
plt.ylabel('Loss [Mean Squared Error]')
plt.title('Keras training progress');

png

plot_preds(x, y, f, new_model, 'After Training: Keras')

png

詳細は、基本トレーニングループKeras ガイドをご覧ください。