TensorFlow の定数・変数・プレースホルダの違い
TensorFlow でデータを扱う際、定数(Constant)、変数(Variable)、プレースホルダ(Placeholder)という3つの概念が登場します。TensorFlow 2.x では Eager モードがデフォルトになり、プレースホルダは非推奨となりましたが、レガシーコードを読む機会もあるため、それぞれの役割と違いを理解しておくことは大切です。
定数 - tf.constant
定数は一度作成すると値を変更できないテンソルです。学習中に変化しないデータ、たとえばハイパーパラメータや固定の入力データに使います。
import tensorflow as tf
# 定数の作成
learning_rate = tf.constant(0.01)
labels = tf.constant([0, 1, 1, 0, 1])
print(learning_rate) # tf.Tensor(0.01, shape=(), dtype=float32)
print(labels) # tf.Tensor([0 1 1 0 1], shape=(5,), dtype=int32)定数はグラフに値がそのまま埋め込まれるため、大きなデータを定数にするとメモリ効率が悪くなる場合があります。大量のデータを扱う場合は tf.data.Dataset などを検討してください。
変数 - tf.Variable
変数は値を更新できるテンソルです。ニューラルネットワークの重みやバイアスなど、訓練中に最適化されるパラメータに使います。
# 変数の作成
weights = tf.Variable(tf.random.normal([3, 2]))
bias = tf.Variable(tf.zeros([2]))
print(weights)
print(bias)変数の値を更新するには assign、assign_add、assign_sub メソッドを使います。直接代入はできない点に注意が必要です。
counter = tf.Variable(0)
# 値の代入
counter.assign(10)
print(counter) # <tf.Variable ... numpy=10>
# 加算
counter.assign_add(5)
print(counter) # <tf.Variable ... numpy=15>
# 減算
counter.assign_sub(3)
print(counter) # <tf.Variable ... numpy=12>変数と定数の決定的な違いは「値を変更できるかどうか」です。訓練ループの中でオプティマイザが自動的に変数を更新してくれるため、モデルのパラメータには必ず tf.Variable を使います。
値は不変。ハイパーパラメータや固定データに使う。グラフに値が埋め込まれるため、大きなデータには不向き。
値を更新できる。モデルの重みやバイアスに使う。オプティマイザによる自動更新の対象になる。
変数の訓練対象フラグ
tf.Variable には trainable パラメータがあります。デフォルトは True で、オプティマイザの更新対象になります。転移学習などで特定の層を凍結したい場合は False に設定します。
# 訓練対象の変数
trainable_var = tf.Variable(1.0, trainable=True)
# 凍結された変数(オプティマイザが更新しない)
frozen_var = tf.Variable(1.0, trainable=False)
# trainable な変数の一覧を取得
model_vars = [trainable_var, frozen_var]
trainable_only = [v for v in model_vars if v.trainable]
print(len(trainable_only)) # 1この仕組みにより、モデルの一部だけを学習させるファインチューニングが簡単に実現できます。
プレースホルダ - tf.placeholder(TensorFlow 1.x)
プレースホルダは TensorFlow 1.x で使われていた仕組みで、計算グラフの中に「後からデータを流し込む穴」を定義するものでした。
# TensorFlow 1.x のコード(2.x では非推奨)
import tensorflow.compat.v1 as tf1
tf1.disable_eager_execution()
x = tf1.placeholder(tf1.float32, shape=[None, 3])
y = x * 2
with tf1.Session() as sess:
result = sess.run(y, feed_dict={x: [[1, 2, 3]]})
print(result) # [[2. 4. 6.]]Session と feed_dict を使ってデータを供給する必要があり、デバッグが困難でした。TensorFlow 2.x では Eager モードにより、Python の関数をそのまま呼び出す形で計算できるため、プレースホルダは不要になっています。
TensorFlow 2.x での代替パターン
TensorFlow 2.x ではプレースホルダの代わりに、Python の関数引数やKeras の tf.keras.Input を使います。
# 方法1: 通常の Python 関数
def compute(x):
return x * 2
result = compute(tf.constant([[1, 2, 3]]))
print(result) # [[2, 4, 6]]# 方法2: Keras の Input レイヤー
inputs = tf.keras.Input(shape=(3,))
outputs = tf.keras.layers.Dense(1)(inputs)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model.summary()Keras の Input はモデルの入力形状を宣言する役割を持ちますが、プレースホルダとは異なり、Eager モードの恩恵を受けてデバッグしやすくなっています。
実践での使い分けまとめ
学習率、ラベルデータ、設定値など変化しないデータに使います。計算途中の中間結果も自動的に定数テンソルとして扱われます。
重み、バイアスなどオプティマイザが更新するパラメータに使います。Keras のレイヤーは内部で自動的に Variable を作成するため、手動で作る機会は限られます。
大量のデータは tf.data.Dataset パイプラインで供給し、小さなデータは関数の引数として渡すのが TensorFlow 2.x の標準的なパターンです。
TensorFlow 2.x を使っている限り、日常的に意識するのは tf.constant と tf.Variable の2つです。Keras を使えば変数の管理もフレームワークが自動で行ってくれるため、まずはこの2つの違いをしっかり理解しておきましょう。











