-
โฑ ํธ์ฆ์จ ๋จธ์ ๋ฌ๋ 12์ฅ ํ ์ํ๋ก๋ฅผ ์ฌ์ฉํ ์ฌ์ฉ์ ์ ์ ๋ชจ๋ธ๊ณผ ํ๋ จ โฑ๐ฎ ์ด๊ฒ์ ๊ฒ ๊ณต๋ถ/โฉ ํธ์ฆ์จ ๋จธ์ ๋ฌ๋ 2026. 1. 27. 21:11
12.1 ๋ํ์ด์ฒ๋ผ ํ ์ํ๋ก ์ฌ์ฉํ๊ธฐ
ํ ์ํ๋ก๋ ๊ฐ๋ ฅํ ์์น ๊ณ์ฐ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋๊ท๋ชจ ๋จธ์ ๋ฌ๋์ ์ ๋ง๋๋ก ํ๋๋์ด ์๋ค.
ํ ์ํ๋ก API๋ ํ ์๋ฅผ ์ํ์ํจ๋ค. ํ ์๋ ํ ์ฐ์ฐ์์ ๋ค๋ฅธ ์ฐ์ฐ์ผ๋ก ํ๋ฅด๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ๋ค์ฐจ์ ๋ฐฐ์ด์ด๋ค.
์ฌ์ฉ์ ์ ์ ์์ค ํจ์, ์ฌ์ฉ์ ์ ์ ์งํ, ์ฌ์ฉ์ ์ ์ ์ธต ๋ฑ์ ๋ง๋ค ๋ ํ ์๊ฐ ์ค์ํ๋ค.
12.1.1 ํ ์์ ์ฐ์ฐ
tf.constant() ํจ์๋ก ํ ์๋ฅผ ๋ง๋ค ์ ์๋ค.
import tensorflow as tf t = tf.constant([[1., 2., 3.], [4., 5., 6.]]) # ํ๋ ฌ t # <tf.Tensor: shape=(2, 3), dtype=float32, numpy= # array([[1., 2., 3.], [4., 5., 6.]]), dtype=float32)>t.shape # TensorShape([2, 3]) t.dtype # tf.float32tf.Tensor๋ ํฌ๊ธฐ(shape)์ ๋ฐ์ดํฐ ํ์ (dtype)์ ๊ฐ์ง๋ค.
t[:, 1:] # <tf.Tensor: shape=(2, 2), dtype=float32, numpy= # array([[2., 3.], # [5., 6.]], dtype=float32)> t[..., 1, tf.nexaxis] # <tf.tensor: shape=(2, 1), dtype=float32, numpy= # array([[2.]. [5.]], dtype=float32)>์ธ๋ฑ์ค ์ฐธ์กฐ๋ ๋ํ์ด์ ๋น์ทํ๊ฒ ์๋ํ๋ค.
t + 10 # <tf.Tensor: shape=(2, 3), dtype=float32, numpy= # array([[11., 12., 13.], [14., 15., 16.]], dtype=float32)> tf.square(t) # <tf.Tensor: shape=(2, 3), dtype=float32, numpy= # array([[ 1., 4., 9.], # [16., 25., 36.]], dtype=float32)> t @ tf.transpose(t) # <tf.Tensor: shape=(2, 2), dtype=float32, numpy= # array([[14., 32.], [32., 77.]], dtype=float32)>๋ชจ๋ ์ข ๋ฅ์ ํ ์ ์ฐ์ฐ์ด ๊ฐ๋ฅํ๋ค.
t + 10์ tf.add(t, 10)์ ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ๋ค. @ ์ฐ์ฐ์ tf.matmul() ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
tf.constant(42) # <tf.Tensor: shape(), dtype=int32, numpy=42>ํ ์๋ ์ค์นผ๋ผ๊ฐ๋ ๊ฐ์ง ์ ์์ผ๋ฉฐ ํฌ๊ธฐ๋ ๋น์ด ์๋ค.
12.1.2 ํ ์์ ๋ํ์ด
ํ ์๋ ๋ํ์ด์ ํจ๊ป ์ฌ์ฉํ๊ธฐ ํธํ๋ค. ๋ํ์ด ๋ฐฐ์ด๋ก ํ ์๋ฅผ ๋ง๋ค ์ ์๊ณ ๊ทธ ๋ฐ๋๋ ๊ฐ๋ฅํ๋ค.
๋ํ์ด ๋ฐฐ์ด์ ํ ์ํ๋ก ์ฐ์ฐ์ ์ ์ฉํ ์ ์๊ณ ํ ์์ ๋ํ์ด ์ฐ์ฐ์ ์ ์ฉํ ์ ์๋ค.
import numpy as np a = np.array([2., 4., 5.]) tf.constant # ‹tf. Tensor: id=111, shape=(3,), dtype=float64, numpy=array([2., 4., 5.])> t.numpy() # array([[1., 2., 3.1, # [4., 5., 6.]1, dtype=float32) tf.square(a) # ‹tf. Tensor: id=116, shape=(3,), dtype=float64, numpy=array([4., 16., 25.])> np.square(t) # array([[ 1., 4., 9.], # [16., 25., 36.]], dtype=float32)12.1.3 ํ์ ๋ณํ
ํ ์ํ๋ก๋ ์ด๋ค ํ์ ๋ณํ๋ ์๋์ผ๋ก ์ํํ์ง ์๋๋ค.
ํธํ๋์ง ์๋ ํ์ ์ ํ ์๋ก ์ฐ์ฐ์ ์คํํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
12.1.4 ๋ณ์
tf.Tensor๋ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ ๊ฐ์ฒด์ด๋ฏ๋ก ํ ์์ ๋ด์ฉ์ ๋ฐ๊ฟ ์ ์๋ค.
๋ฐ๋ผ์ ์ผ๋ฐ์ ์ธ ํ ์๋ก๋ ์ญ์ ํ๋ก ๋ณ๊ฒฝ๋์ด์ผ ํ๋ ์ ๊ฒฝ๋ง์ ๊ฐ์ค์น๋ฅผ ๊ตฌํํ ์ ์๋ค.
์๊ฐ์ ๋ฐ๋ผ ๋ณ๊ฒฝ๋์ด์ผ ํ๋ ํ๋ผ๋ฏธํฐ๋ ์๋ค.
์ด๊ฒ์ด tf.Variable์ด ํ์ํ ์ด์ ์ด๋ค.
v = tf.Variable([[1., 2., 3.], [4., 5., 6.]]) v # <tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy= # array([[1., 2., 3.], # [4., 5., 6.]1, dtype=float32)>v.assign(2 * v) # v๋ ์ด์ [[2., 4., 6.], [8., 10., 12.]] v[0, 1]- assign(42) # v๋ ์ด์ [[2., 42., 6.], [8., 10., 12.]] v[:, 2].assign([0., 1.] # v๋ ์ด์ [[2., 42., 0.], [8., 10., 1.]] v.scatter_nd_update(indices=[[0, 0], [1, 2]], updates=[100., 200.]) # v๋ ์ด์ [[100., 42., 0.], [8., 10., 200.]]tf.Variable์ tf.Tensor์ ๋น์ทํ๊ฒ ์๋ํ๋ค. ๋์ผํ ์ฐ์ฐ์ ์ํํ ์ ์๊ณ ๋ํ์ด์๋ ์ ํธํ๋๋ค.
assign() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณ์ซ๊ฐ์ ๋ฐ๊ฟ ์ ์๋ค.
assign_add()๋ assign_sub() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ฃผ์ด์ง ๊ฐ๋งํผ ๋ณ์๋ฅผ ์ฆ๊ฐ ํน์ ๊ฐ์์ํฌ ์ ์๋ค.
๋๋ ์์์ assign() ๋ฉ์๋๋ scatter_update(), scatter_nd_update() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋ณ ์์๋ฅผ ์์ ํ ์๋ ์๋ค.
์ง์ ์์ ์ ์ ๋๋ค.
12.2 ์ฌ์ฉ์ ์ ์ ๋ชจ๋ธ๊ณผ ํ๋ จ ์๊ณ ๋ฆฌ์ฆ
12.2.1 ์ฌ์ฉ์ ์ ์ ์์ค ํจ์
ํ๊ท ๋ชจ๋ธ์ ํ๋ จํ๋ ๋ฐ ํ๋ จ ๋ฐ์ดํฐ์ ์ก์ ๋ฐ์ดํฐ๊ฐ ์กฐ๊ธ ์๋ค๋ฉด ์ด์์น๋ฅผ ์ ๊ฑฐํ๊ฑฐ๋ ๊ณ ์ณ์ ๋ฐ์ดํฐ์ ์ ์์ ํ ์ ์์ง๋ง, ์ก์ ๋ฐ์ดํฐ๋ ์ฌ์ ํ ๋จ์ ์์ ๊ฒ์ด๋ค. ์ด๋ด ๋ ํ๋ฒ ์์ค์ ์ฌ์ฉํ๋ฉด ์ข๋ค.
def hubor_fn(y_true, y_pred): error = y_true - y_pred is_small_error = tf.ads(error) < 1 squared_loss = tf.square(error) / 2 linear_loss = tf.ads(error) - 0.5 return tf.where(is_small_error, squared_loss, linear_loss)๋ ์ด๋ธ๊ณผ ๋ชจ๋ธ์ ์์ธก์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋ ํจ์๋ฅผ ๋ง๋ค๊ณ ํ ์ํ๋ก ์ฐ์ฐ์ ์ฌ์ฉํด ๊ฐ ์ํ์ ์์ค์ ๋ชจ๋ ๋ด์ ํ ์๋ฅผ ๊ณ์ฐํ๋ฉด ๋๋ค.
model.compile(loss=huber_fn, optimizer="nadam") model.fit(X_train, y_train, [...])ํ๋ฒ ์์ค์ ์ฌ์ฉํด ์ผ๋ผ์ค ๋ชจ๋ธ์ ์ปดํ์ผ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ๋ชจ๋ธ์ ํ๋ จํ ์ ์๋ค.
ํ๋ จํ๋ ๋์ ๋ฐฐ์น๋ง๋ค ์ผ๋ผ์ค๋ huber_fn() ํจ์๋ฅผ ํธ์ถํ์ฌ ์์ค์ ๊ณ์ฐํ๊ณ ํ์ง ๋ชจ๋ ์๋ ๋ฏธ๋ถ์ ์ฌ์ฉํด ๋ชจ๋ธ ํ๋ผ๋ฏธํฐ์ ๋ํ ์์ค์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ค.
๊ทธ๋ค์ ๊ฒฝ์ฌ ํ๊ฐ๋ฒ ๋จ๊ณ๋ฅผ ์ํํ๊ณ ์ํฌํฌ ์์๋ถํฐ ์ ์ฒด ์์ค์ ๊ธฐ๋กํ์ฌ ํ๊ท ์์ค์ ์ถ๋ ฅํ๋ค.
12.2.2 ์ฌ์ฉ์ ์ ์ ์์๋ฅผ ๊ฐ์ง ๋ชจ๋ธ์ ์ ์ฅํ๊ณ ๋ก๋ํ๊ธฐ
์ฌ์ฉ์ ์ ์ ์์ค ํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ธ์ ์ ์ฅํ๋ ๋ฐ๋ ๋ฌธ์ ๊ฐ ์์ง๋ง, ๋ชจ๋ธ์ ๋ก๋ํ ๋๋ ํจ์ ์ด๋ฆ๊ณผ ์ค์ ํจ์๋ฅผ ๋งคํํ ๋์ ๋๋ฆฌ๋ฅผ ์ ๋ฌํด์ผ ํ๋ค.
์ฌ์ฉ์ ์ ์ ๊ฐ์ฒด๋ฅผ ํฌํจํ ๋ชจ๋ธ์ ๋ก๋ํ ๋๋ ๊ทธ ์ด๋ฆ๊ณผ ๊ฐ์ฒด๋ฅผ ๋งคํํด์ผ ํ๋ค.
model = tf.keras.models.load_model("my_model_with_a_custom_loss", custom_objects={"huber_fn":huber_fn})def create_huber(threshold=1.0): def huber_fn(y_true, y_pred): error = y_true - y_pred is_small_error = tf.abs(error) < threshold squared_loss = tf.square(error) / 2 linear_loss = threshold * tf.abs(error) - threshold ** 2 / 2 return tf.where(is_small_eror, squared_loss, linear_loss) return huber_fn model.compile(loss=create_huber(2.0), optimizer="nadam")์ค์ฐจ๋ฅผ ์์ ๊ฒ์ผ๋ก ํ๋จํ ๊ธฐ์ค์ด ํ์ํ ๋๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ ์ ์๋ ํจ์๋ฅผ ๋ง๋ค๋ฉด ๋๋ค.
model = tf.keras.models.load_model( "my_model_with_a_custom_loss_threshold_2", custom_objects={"huber_fn": create_huber(2.0)] )๋ชจ๋ธ์ ์ ์ฅํ ๋ ์ด threshold ๊ฐ์ ์ ์ฅ๋์ง ์์ผ๋ฏ๋ก ๋ชจ๋ธ์ ๋ก๋ํ ๋ threshold ๊ฐ์ ์ง์ ํด์ผ ํ๋ค.
tf.keras.losses.Loss ํด๋์ค๋ฅผ ์์ํ๊ณ get_config() ๋ฉ์๋๋ฅผ ๊ตฌํํ์ฌ ํด๊ฒฐํ ์ ์๋ค.
class HuberLoss(tf.keras.losses.Loss): def __init__(self, threshold=1.0, **kwargs): self.threshold = threshold super().__init__(**kwargs) def call(self, y_true, y_pred): error = y_true - y_pred is_small_error = tf.abs(error) < self.threshold squared_loss = tf.square(error) / 2 linear_loss = self.threshold * tf.abs(error) - self.threshold**2 / 2 return tf.where(is_small_error, squared_loss, linear_loss) def get_config(self): base_config = super().get_config() return {**base_config, "threshold": self.threshold)์์ฑ์๋ ๊ธฐ๋ณธ์ ์ธ ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ **kwargs๋ก ๋ฐ์ ๋งค๊ฐ๋ณ์ ๊ฐ์ ๋ถ๋ชจ ํด๋์ค์ ์์ฑ์์๊ฒ ์ ๋ฌํ๋ค.
call() ๋ฉ์๋๋ ๋ ์ด๋ธ๊ณผ ์์ธก์ ๋ฐ๊ณ ๋ชจ๋ ์ํ์ ์์ค์ ๊ณ์ฐํ์ฌ ๋ฐํํ๋ค.
get_config() ๋ฉ์๋๋ ํ์ดํผํ๋ผ๋ฏธํฐ ์ด๋ฆ๊ณผ ๊ฐ์ด ๋งคํ๋ ๋์ ๋๋ฆฌ๋ฅผ ๋ฐํํ๋ค. ๋ถ๋ชจ ํด๋์ค์ get_config() ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ๊ทธ๋ค์ ๋ฐํธ๋๋ ๋์ ๋๋ฆฌ์ ์๋ก์ด ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ๋ค.
model.compile(loss=HuberLoss(2.), optimizer="nadam")๋ชจ๋ธ์ ์ปดํ์ผํ ๋ ์ด ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
model = tf.keras.models.load_model("my_model_with_a_custom_loss_class", custom_objects={"HuberLoss": HuberLoss})๋ชจ๋ธ์ ์ ์ฅํ ๋ ์๊ณ๊ฐ๋ ํจ๊ป ์ ์ฅ๋๋ค. ๋ชจ๋ธ์ ๋ก๋ํ ๋ ํด๋์ค ์ด๋ฆ๊ณผ ํด๋์ค ์์ฒด๋ฅผ ๋งคํํด์ค์ผ ํ๋ค.
๋ชจ๋ธ์ ์ ์ฅํ ๋ ์ผ๋ผ์ค๋ ์์ค ๊ฐ์ฒด์ get_config() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋ฐํ๋ ์ค์ ์ HDF5 ํ์ผ์ JSON ํํ๋ก ์ ์ฅํ๋ค.
๋ชจ๋ธ์ ๋ก๋ํ๋ฉด HuberLoss ํด๋์ค์ from_config() ํด๋์ค ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
์ด ๋ฉ์๋๋ ๊ธฐ๋ณธ ์์ค ํด๋์ค์ ๊ตฌํ๋์ด ์๊ณ ์์ฑ์์๊ฒ **config ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํด ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋ ๋ค.
12.2.3 ํ์ฑํ ํจ์, ์ด๊ธฐํ, ๊ท์ , ์ ํ์ ์ปค์คํฐ๋ง์ด์งํ๊ธฐ
def my_softplus(z): return tf.math.log(1.0 + tf.exp(z)) def my_glorot_initializer(shape, dtype=tf.float32): stddev = tf.sqrt(2. / (shape[0] + shape[1])) return tf.random.normal(shape, stddev=stddev, dtype=dtype) def my_l1_regularizer(weights): return tf.reduce_sum(tf.abs(0.01 * weights)) def my_positive_weights(weights): return tf.where(weights < 0., tf.zeros_like(weights), weights)๋งค๊ฐ๋ณ์๋ ์ฌ์ฉ์ ์ ์ํ๋ จ๋ ํจ์์ ์ข ๋ฅ์ ๋ฐ๋ผ ๋ค๋ฅด๋ค.
layer = tf.keras.layers.Dense(1, activation=my_softplus, kernel_initializer=my_glorot_initializer, kernel_regularizer=my_l1_regularizer, kernel_constraint=my_positive_weights)์ด ํ์ฑํ ํจ์๋ Dense ์ธต์ ์ถ๋ ฅ์ ์ ์ฉ๋๊ณ ๋ค์ ์ธต์ ๊ทธ ๊ฒฐ๊ณผ๊ฐ ์ ๋ฌ๋๋ค.
์ธต์ ๊ฐ์ฐ์น๋ ์ด๊ธฐํ ํจ์์์ ๋ฐํ๋ ๊ฐ์ผ๋ก ์ด๊ธฐํ๋๋ค.
ํ๋ จ ์คํ ๋ง๋ค ๊ฐ์ค์น๊ฐ ๊ท์ ํจ์์ ์ ๋ฌ๋์ด ๊ท์ ์์ค์ธ ๊ณ์ฐํ๊ณ ์ ์ฒด ์์ค์ ์ถ๊ฐ๋์ด ํ๋ จ์ ์ํ ์ต์ข ์์ค์ ๋ง๋ค์ด๋ธ๋ค.
๋ง์ง๋ง์ผ๋ก ์ ํ ํจ์๊ฐ ํ๋ จ ์คํ ๋ง๋ค ํธ์ถ๋์ด ์ธต์ ๊ฐ์ค์น๋ฅผ ์ ํํ ๊ฐ์ค์น ๊ฐ์ผ๋ก ๋ฐ๋๋ค.
ํจ์๊ฐ ๋ชจ๋ธ๊ณผ ํจ๊ป ์ ์ฅ๋์ผ ํ ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด tf.keras.regularizers.Regularizer, tf.keras.constraints.Contraint, tf.keras.initializers.Initializer, tf.keras.layers.Layer์ ๊ฐ์ด ์ ์ ํ ํด๋์ค๋ฅผ ์์ํ๋ค.
[factor ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ฅํ๋ l1 ๊ท์ ๋ฅผ ์ํ ํด๋์ค์ ์]
class MyL1Regularizer(tf.keras.regularizers.Regularizer): def __init__(self, factor): self.factor = factor def __cal__(self, weights): return tf.reduce_sum(tf.abs(self.factor * weights)) def get_config(self): return {"factor": self.factor}12.2.4 ์ฌ์ฉ์ ์ ์ ์งํ
์์ค์ ๋ชจ๋ธ์ ํ๋ จํ๊ธฐ ์ํด ๊ฒฝ์ฌ ํ๊ฐ๋ฒ์์ ์ฌ์ฉ๋๋ฏ๋ก (์ ์ด๋ ํ๊ฐํ ์ง์ ์์๋) ๋ฏธ๋ถ ๊ฐ๋ฅํด์ผ ํ๊ณ ๊ทธ๋ ์ด๋์ธํธ๊ฐ ๋ชจ๋ ๊ณณ์์ 0์ด ์๋์ด์ผ ํ๋ค.
์งํ๋ ๋ชจ๋ธ์ ํ๊ฐํ ๋ ์ฌ์ฉ๋๋ฉฐ ํจ์ฌ ์ดํด๋๊ธฐ ์ฌ์์ผ ํ๋ค. ๋ฏธ๋ถ์ด ๊ฐ๋ฅํ์ง ์๊ฑฐ๋ ๋ชจ๋ ๊ณณ์์ ๊ทธ๋ ์ด๋์ธํธ๊ฐ 0์ด์ด๋ ๊ด์ฐฎ๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ฌ์ฉ์ ์งํ ํจ์๋ฅผ ๋ง๋๋ ๊ฒ์ ์ฌ์ฉ์ ์์ค ํจ์๋ฅผ ๋ง๋๋ ๊ฒ๊ณผ ๋์ผํ๋ค.
ํ์ง๋ง ์ง์ง ์์ฑ ๊ฐ์์ ๊ฑฐ์ง ์์ฑ ๊ฐ์๋ฅผ ๊ธฐ๋กํ๊ณ ํ์ํ ๋ ์ ๋ฐ๋๋ฅผ ๊ณ์ฐํ ์ ์๋ ๊ฐ์ฒด๊ฐ ํ์ํ๋ค.
precision = tf.keras.metrics.Precision() pricision([0, 1, 1, 1, 0, 1, 0, 1], [1, 1, 0, 1, 0, 1, 0, 1]) # <tf.Tensor: shape(), dtype=float32, numpy=0.8> precision([0, 1, 0, 0, 1, 0, 1, 1], [1, 0, 1, 1, 0, 0, 0, 0]) # <tf.Tensor: shape(), dtype=float32, numpy=0.5>์ด๋ ์คํธ๋ฆฌ๋ฐ ์งํ์ด๋ค.
precision.result() # <tf.Tensor: shape(), dtype=float32, nupy=0.5> precision.variables # [<tf. Variable 'true_positives:0' [...], numpy=array([4.], dtype=float32)>, # ‹tf.Variable 'false_positives:0' [...], numpy=array([4.], dtype=float32)>] precision.reset_states() # ๋ ๋ณ์๊ฐ 0.0์ผ๋ก ์ด๊ธฐํ๋๋ค.result() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ํ์ฌ ์งํ ๊ฐ์ ์ป์ ์ ์๋ค.
variables ์์ฑ์ ์ฌ์ฉํ์ฌ ์ง์ง ์์ฑ๊ณผ ๊ฑฐ์ง ์์ฑ์ ๊ธฐ๋กํ ๋ณ์๋ฅผ ํ์ธํ ์ ์๋ค.
reset_states() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ด ๋ณ์๋ฅผ ์ด๊ธฐํํ ์ ์๋ค.
์ฌ์ฉ์ ์ ์ ์คํธ๋ฆฌ๋ฐ ์งํ๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด tf.keras.metrics.Metric ํด๋์ค๋ฅผ ์์ํ๋ค.
class HuberMetric(tf.keras.metrics.Metric): def __init__(self, threshold=1.0, **kwargs): super().__init__(**kwargs) # ๊ธฐ๋ณธ ๋งค๊ฐ๋ณ์ ์ฒ๋ฆฌ self.threshold = threshold self.huber_fn = create_huber(threshold) self.total = self.add_weights("total", initializer="zeros") self.count = self.add_weights("count", initializer="zeros") def update_state(self, y_true, y_pred, sample_weight=None): sample_metrics = self.huber_fn(y_true, y_pred) self.total.assign_add(tf.reduce_sum(sample_matrics)) self.count.assign_add(tf.cast(tf.cast(tf.size(Y_true), tf.float32)) def result(self): return self.total / self.count def get_config(self): base_config = super().get_config() return (**base_config, "threshold": self.threshold)์์ฑ์๋ add_weight() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ฌ๋ฌ ๋ฐฐ์น์ ๊ฑธ์ณ ์งํ์ ์ํ๋ฅผ ๊ธฐ๋กํ๊ธฐ ์ํ ๋ณ์๋ฅผ ๋ง๋ ๋ค.
์ด ์์์๋ ํ๋ฒ ์์ค์ ํฉ(total)๊ณผ ์ง๊ธ๊น์ง ์ฒ๋ฆฌํ ์ํ ์(count)๋ฅผ ๊ธฐ๋กํ๋ค. ์๋์ผ๋ก ๋ณ์๋ฅผ ๋ง๋ค ์ ์๋ค.
์ผ๋ผ์ค๋ ์์ฑ์ผ๋ก ๋ง๋ค์ด์ง ๋ชจ๋ tf.Variable์ ๊ด๋ฆฌํ๋ค.
update_state() ๋ฉ์๋๋ ์ด ํด๋์ค๋ฅผ ํจ์์ฒ๋ผ ์ฌ์ฉํ ๋ ํธ์ถ๋๋ค.
๋ฐฐ์น์ ๋ ์ด๋ธ๊ณผ ์์ธก์ ๋ฐํ์ผ๋ก ๋ณ์๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
result() ๋ฉ์๋๋ ์ต์ข ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ๊ณ ๋ฐํํ๋ค. ์ด ์งํ ํด๋์ค๋ฅผ ํจ์์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋จผ์ update_state() ๋ฉ์๋๊ฐ ํธ์ถ๋๊ณ ๊ทธ๋ค์ result() ๋ฉ์๋๊ฐ ํธ์ถ๋์ด ์ถ๋ ฅ์ด ๋ฐํ๋๋ค.
get_config() ๋ฉ์๋๋ฅผ ๊ตฌํํ์ฌ threshold ๋ณ์๋ฅผ ๋ชจ๋ธ๊ณผ ํจ๊ป ์ ์ฅํ๋ค.
์งํ๋ฅผ ๊ฐ๋จํ ํจ์๋ก ์ ์ํ๋ฉด ์ผ๋ผ์ค๊ฐ ๋ฐฐ์น๋ง๋ค ์๋์ผ๋ก ์ด ํจ์๋ฅผ ํธ์ถํ๊ณ ์ํฌํฌ ๋์ ํ๊ท ์ ๊ธฐ๋กํ๋ค.
HuberMetric ํด๋์ค๋ฅผ ์ ์ํ๋ ๊ฒ์ ์ ์ผํ ์ด์ ์ threshold๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด๋ค.
12.2.5 ์ฌ์ฉ์ ์ ์ ์ธต
ํ ์ํ๋ก์๋ ์๋ ํน์ดํ ์ธต์ ๊ฐ์ง ๋คํธ์ํฌ๋ฅผ ๋ง๋ค์ด์ผ ํ ๋๊ฐ ์๋ค.
๋๋ ๋์ผํ ์ธต ๋ธ๋ก์ด ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณต๋๋ ๋คํธ์ํฌ๋ฅผ ๋ง๋ค ๊ฒฝ์ฐ ๊ฐ๊ฐ์ ๋ธ๋ก์ ํ๋์ ์ธต์ผ๋ก ๋ค๋ฃจ๋ ๊ฒ ํธํ๋ค.
์ด๋ฐ ๊ฒฝ์ฐ ์ฌ์ฉ์ ์ ์ ์ธต์ ๋ง๋ ๋ค.
tf.keras.layers.Flatten์ด๋ tf.keras.layers.ReLU์ ๊ฐ์ด ๊ฐ์ค์น๊ฐ ์๋ ์ธต์ด ์๋ค.
๊ฐ์ค์น๊ฐ ํ์ ์๋ ์ฌ์ฉ์ ์ ์ ์ธต์ ๋ง๋๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ํ์ด์ฌ ํจ์๋ฅผ ๋ง๋ ํ tf.keras.layers.Lambda ์ธต์ผ๋ก ๊ฐ์ธ๋ ๊ฒ์ด๋ค.
exponential_layer = tf.keras.layers.Lambda(lambda x: tf.exp(x))์ด ์ฌ์ฉ์ ์ ์ ์ธต์ ์ํ์ API๋ ํจ์ํ API, ์๋ธํด๋์ฑ API์์ ๋ณดํต์ ์ธต๊ณผ ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
๋๋ ํ์ฑํ ํจ์๋ก ์ฌ์ฉํ๊ฑฐ๋ activation=tf.exp์ ๊ฐ์ด ์ง์ ํ ์๋ ์๋ค.
์ง์ ํจ์๋ ์ด๋ฐ๊ธ ํ๊ท ๋ชจ๋ธ์์ ์์ธก๊ฐ์ ์ค์ผ์ผ์ด ๋งค์ฐ ๋ค๋ฅผ ๋ ์ถ๋ ฅ ์ธต์ ์ฌ์ฉ๋๋ค.
์ํ๊ฐ ์๋ ์ธต(๊ฐ์ค์น๋ฅผ ๊ฐ์ง ์ธต)์ ๋ง๋๋ ค๋ฉด tf.keras.layers.Layer๋ฅผ ์์ํด์ผ ํ๋ค.
class MyDense(tf.keras.layers.Layer): def __init__(self, units, activation=None, **kwargs): super().__init__(**kwargs) self.units = units self.activation = tf.keras.activations.get(activation) def build(self, batch_input_shape): se;f.kernel = self.add_weight( name="kernel", shape=[batch_input_shape[-1], self.units], initializer="glorot_normal") self.bias = self.add_weight( name="bias", shape=[self.units], initializer="zeros") def call(self, X): return self.activation(X @ self.kernel + self.bias) def get_config(self): base_config = super().get_config() return {**base__config, "units": self.units, "activation": tf.keras.activations.serialize(self.activation)}์์ฑ์๋ ๋ชจ๋ ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค.
build() ๋ฉ์๋์ ์ญํ ์ ๊ฐ์ค์น๋ง๋ค add_weight() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ธต์ ๋ณ์๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค. ์ธต์ด ์ฒ์ ์ฌ์ฉ๋ ๋ ํธ์ถ๋๋ค. ์ด ์์ ์ด ๋๋ฉด ์ผ๋ผ์ค๊ฐ ์ธต์ ์ ๋ ฅ ํฌ๊ธฐ๋ฅผ ์๊ณ ์์ ๊ฒ์ด๋ฏ๋ก build() ๋ฉ์๋์ ์ ๋ ฅ์ผ๋ก ํฌ๊ธฐ๋ฅผ ์ ๋ฌํ๋ค.
call() ๋ฉ์๋๋ ์ด ์ธต์ ํ์ํ ์ฐ์ฐ์ ์ํํ๋ค. ์ ๋ ฅ X์ ์ธต์ ์ปค๋์ ํ๋ ฌ ๊ณฑ์ ํ๊ณ ํธํฅ์ ๋ํ๋ค. ๊ฒฐ๊ณผ์ ํ์ฑํ ํจ์๋ฅผ ์ ์ฉํ๋ค. ์ด ๊ฐ์ด ์ธต์ ์ถ๋ ฅ์ด๋ค.
get_config() ๋ฉ์๋๋ ์์ ์ฝ๋์ ๊ฐ๋ค. tf.keras.activations.serialize()๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฑํ ํจ์์ ์ ์ฒด ์ค์ ์ ์ ์ฅํ๋ค.
์ฌ๋ฌ ๊ฐ์ง ์ ๋ ฅ์ ๋ฐ๋ ์ธต์ ๋ง๋ค๋ ค๋ฉด call() ๋ฉ์๋์ ๋ชจ๋ ์ ๋ ฅ์ด ํฌํจ๋ ํํ์ ๋งค๊ฐ๋ณ์ ๊ฐ์ผ๋ก ์ ๋ฌํด์ผ ํ๋ค.
์ฌ๋ฌ ์ถ๋ ฅ์ ๊ฐ์ง ์ธต์ ๋ง๋๋ ค๋ฉด call() ๋ฉ์๋๊ฐ ์ถ๋ ฅ์ ๋ฆฌ์คํธ๋ฅผ ๋ฐํํด์ผ ํ๋ค.
class MyMultiLayer(tf.keras.layers.Layer): def call(self, X): X1, X2 = X return X1 + X2, X1 * X2, X1 / X2ํจ์ํ API์ ์๋ธํด๋์ฑ API์๋ง ์ฌ์ฉํ ์ ์๋ค. ํ๋์ ์ ๋ ฅ๊ณผ ํ๋์ ์ถ๋ ฅ์ ๊ฐ์ง ์ธต๋ง ์ฌ์ฉํ๋ ์ํ์ API์๋ ์ฌ์ฉํ ์ ์๋ค.
ํ๋ จ๊ณผ ํ ์คํธ์์ ๋ค๋ฅด๊ฒ ์๋ํ๋ ์ธต์ด ํ์ํ๋ค๋ฉด call() ๋ฉ์๋์ training ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ ํ๋ จ์ธ์ง ํ ์คํธ์ธ์ง๋ฅผ ๊ฒฐ์ ํด์ผ ํ๋ค.
ํ๋ จํ๋ ๋์ ๊ฐ์ฐ์ค ์ก์์ ์ถ๊ฐํ๊ณ ํ ์คํธ ์์๋ ์๋ฌด๊ฒ๋ ํ์ง ์๋ ์ธต์ ๋ง๋ค ์ ์๋ค.
class MyGaussianNoise(tf.keras.layers.Layer): def __init__(self, stddev, **kwargs): super().__init__(**kwargs) self.stddev = stddev def call(self, X, training=False): if training: noise = tf.random.normal(tf.shape(X), stddev=self.stddev) return X + noise else: return X12.2.6 ์ฌ์ฉ์ ์ ์ ๋ชจ๋ธ
tf.keras.Model ํด๋์ค๋ฅผ ์์ํ์ฌ ์์ฑ์์์ ์ธต๊ณผ ๋ณ์๋ฅผ ๋ง๋ค๊ณ , ๋ชจ๋ธ์ด ํด์ผ ํ ์์ ์ call() ๋ฉ์๋์ ๊ตฌํํ๋ค.

์ ๋ ฅ์ด ์ฒซ ๋ฒ์งธ ์์ ์ฐ๊ฒฐ ์ธต์ ํต๊ณผํ์ฌ ๋ ๊ฐ์ ์์ ์ฐ๊ฒฐ ์ธต๊ณผ ์คํต ์ฐ๊ฒฐ๋ก ๊ตฌ์ฑ๋ ์์ฐจ ๋ธ๋ก์ผ๋ก ์ ๋ฌ๋๋ค.
๊ทธ๋ค์ ๋์ผํ ์์ฐจ ๋ธ๋ก์ ์ธ ๋ฒ ๋ ํต๊ณผ์ํจ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ ๋ฒ์จฐ ์์ฐจ ๋ธ๋ก์ ์ง๋ ๋ง์ง๋ง ์ถ๋ ฅ์ด ์์ ์ฐ๊ฒฐ๋ ์ถ๋ ฅ ์ธต์ ์ ๋ฌ๋๋ค.
class ResidualBlock(tf.keras.layers.Layer): def__init__(self, n_layers, n_neurons, **kwargs): super ().__init__(**kwargs) self.hidden = [tf.keras. layers.Dense(n_neurons, activation="relu", kernel_initializer="he_normal") for _ in range(n_layers)] def call(self, inputs): Z = inputs for layer in self.hidden: Z = layer (Z) return inputs + Z์ผ๋ผ์ค๊ฐ ์์์ ์ถ์ ํด์ผ ํ ๊ฐ์ฒด๊ฐ ๋ด๊ธด hidden ์์ฑ์ ๊ฐ์งํ๊ณ ํ์ํ ๋ณ์๋ฅผ ์๋์ผ๋ก ์ด ์ธต์ ๋ณ์ ๋ฆฌ์คํธ์ ์ถ๊ฐํ๋ค.
class ResidualRegressor(tf.keras.Model): def __init_(self, output_dim, **kwargs): super()._init__(**kwargs) self.hidden1 = tf.keras. layers.Dense(30, activation="relu", kernel_initializer="he_normal") self.block1 = ResidualBlock(2, 30) self.block2 = ResidualBlock(2, 30) self.out = tf.keras. layers. Dense(output_dim) def call(self, inputs): Z = self.hidden1(inputs) for _ in range(1 + 3): Z = self.block1(Z) Z = self.block2(Z) return self.out(Z)์์ฑ์์์ ์ธต์ ๋ง๋ค๊ณ call() ๋ฉ์๋์์ ์ด๋ฅผ ์ฌ์ฉํ๋ค.
save() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ชจ๋ธ์ ์ ์ฅํ๊ณ tf.keras.models.load_model() ํจ์๋ฅผ ์ฌ์ฉํด ์ ์ฅ๋ ๋ชจ๋ธ์ ๋ก๋ํ๊ณ ์ถ๋ค๋ฉด ResidualBlock ํด๋์ค์ ResidualRegressor ํด๋์ค์ ๋ชจ๋ get_config() ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
๋ํ save_weights()์ load_weights() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๊ฐ์ค์น๋ฅผ ์ ์ฅํ๊ณ ๋ก๋ํ ์ ์๋ค.
Model ํด๋์ค๋ Layer ํด๋์ค์ ์๋ธํด๋์ค์ด๋ฏ๋ก ๋ชจ๋ธ์ ์ธต์ฒ๋ผ ์ ์ํ ์ ์๋ค.
ํ์ง๋ง ๋ชจ๋ธ์ compile(), fit(), evaluate(), predict() ๋ฉ์๋์ ๊ฐ์ ์ถ๊ฐ์ ใ ๋ ๋ใ กใ ์ด ์๋ค.
๋ํ get_layers() ๋ฉ์๋์ save() ๋ฉ์๋๊ฐ ์๋ค.
12.2.7 ๋ชจ๋ธ ๊ตฌ์ฑ ์์์ ๊ธฐ๋ฐํ ์์ค๊ณผ ์งํ
์ฌ์ฉ์ ์์ค๊ณผ ์งํ๋ ๋ชจ๋ ๋ ์ด๋ธ๊ณผ ์์ธก, ๊ทธ๋ฆฌ๊ณ ์ ํ์ ์ผ๋ก ์ํ ๊ฐ์ค์น๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค.
ํ์ง๋ง ์๋ ์ธต์ ๊ฐ์ค์น๋ ํ์ฑํ ํจ์ ๋ฑ๊ณผ ๊ฐ์ด ๋ชจ๋ธ์ ๊ตฌ์ฑ ์์์ ๊ธฐ๋ฐํ ์์ค์ ์ ์ํด์ผ ํ ๋๋ ์๋ค.
์ด๋ฐ ์์ค์ ๊ท์ ๋ ๋ชจ๋ธ์ ๋ด๋ถ ์ํฉ์ ๋ชจ๋ํฐ๋งํ ๋ ์ ์ฉํ๋ค.
๋ชจ๋ธ ๊ตฌ์ฑ ์์์ ๊ธฐ๋ฐํ ์์ค์ ์ ์ํ๊ณ ๊ณ์ฐํ์ฌ add_loss() ๋ฉ์๋์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฌํ๋ค.
์ฌ๊ตฌ์ฑ ์์ค(๋ณด์กฐ ์ถ๋ ฅ์ ์ฐ๊ฒฐ๋ ์์ค)์ ์ฃผ ์์ค์ ๋ํ์ฌ ํ๊ท ์์ ์ ์ง์ ์ ์ผ๋ก ๋์์ด ๋์ง ์์ ์ ๋ณด์ผ์ง๋ผ๋ ๋ชจ๋ธ์ด ์๋ ์ธต์ ํต๊ณผํ๋ฉด์ ๊ฐ๋ฅํ ํ ๋ง์ ์ ๋ณด๋ฅผ ์ ์งํ๋๋ก ์ ๋ํ๋ค.
์ด๋ฐ ์์ค์ด ์ด๋ฐ๊ธ ์ผ๋ฐํ ์ฑ๋ฅ์ ํฅ์์ํจ๋ค.
๋ชจ๋ธ์ add_metric() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ฌ์ฉ์ ์ ์ ์งํ๋ฅผ ์ถ๊ฐํ ์๋ ์๋ค.
class ReconstructingRegressor(tf.keras.Model): def__init__(self, output_dim, **kwargs): super()._init__(**kwargs) self. hidden = [tf.keras.layers.Dense(30, activation="relu", kernel_initializer="he_normal") for _ in range(5)] self.out = tf.keras.layers.Dense(output_dim) self.reconstruction_mean = tf.keras.metrics.Mean( name="reconstruction _error") def build(self, batch_input_shape): n_inputs = batch_input_shape[-1] self. reconstruct = tf.keras.layers.Dense(n_inputs) def call(self, inputs, training=False): Z = inputs for layer in self.hidden: Z = layer(Z)| reconstruction = self.reconstruct(Z) recon_loss = tf. reduce_mean(tf.square(reconstruction - inputs)) self.add_loss(0.05 * recon_loss) if training: result = self.reconstruction_mean(recon_loss) self.add _metricresult) return self.out (Z)์์ฑ์๊ฐ ๋ค์ฏ ๊ฐ์ ์๋ ์ธต๊ณผ ํ๋์ ์ถ๋ ฅ ์ธต์ผ๋ก ๊ตฌ์ฑ๋ ์ฌ์ธต ์ ๊ฒฝ๋ง์ ๋ง๋ ๋ค. ํ๋ จํ๋ ๋์ ์ฌ๊ตฌ์ฑ ์ค์ฐจ๋ฅผ ์ถ์ ํ๊ธฐ ์ํด Mean ์คํธ๋ฆฌ๋ฐ ์งํ๋ ๋ง๋ ๋ค.
build() ๋ฉ์๋์์ ์์ ์ฐ๊ฒฐ ์ธต์ ํ๋ ๋ ์ถ๊ฐํ์ฌ ๋ชจ๋ธ์ ์ ๋ ฅ์ ์ฌ๊ตฌ์ฑํ๋ ๋ฐ ์ฌ์ฉํ๋ค. ์ด ์์ ์ฐ๊ฒฐ ์ธต์ ์ ๋ ๊ฐ์๋ ์ ๋ ฅ ๊ฐ์์ ๊ฐ์์ผ ํ๋ค. ์ด๋ฐ ์ฌ๊ตฌ์ฑ ์ธต์ build() ๋ฉ์๋์์ ๋ง๋๋ ์ด์ ๋ ์ด ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ์ ๊น์ง๋ ์ ๋ ฅ ๊ฐ์๋ฅผ ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
call() ๋ฉ์๋์์ ์ ๋ ฅ์ด ๋ค์ฏ ๊ฐ์ ์๋ ์ธต์ ๋ชจ๋ ํต๊ณผํ๋ค. ๊ทธ๋ค์ ๊ฒฐ๊ด๊ฐ์ ์ฌ๊ตฌ์ฑ ์ธต์ ์ ๋ฌํ์ฌ ์ฌ๊ตฌ์ฑ์ ๋ง๋ ๋ค. ์ฌ๊ตฌ์ฑ ์์ค์ ๊ณ์ฐํ๊ณ add_loss() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ชจ๋ธ์ ์์ค ๋ฆฌ์คํธ์ ์ถ๊ฐํ๋ค. ํ๋ จ ์ค์๋ง call() ๋ฉ์๋๊ฐ ์ฌ๊ตฌ์ฑ ์งํ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ํ๋ฉด์ ์ถ๋ ฅ๋๋๋ ๋ชจ๋ธ์ ์ถ๊ฐํ๋ค. ์ผ๋ผ์ค๊ฐ ์๋์ผ๋ก ํ๊ท ์ ์ถ์ ํ๋ค. call() ๋ฉ์๋ ๋ง์ง๋ง์์ ์๋ ์ธต์ ์ถ๋ ฅ์ ์ถ๋ ฅ ์ธต์ ์ ๋ฌํ์ฌ ์ป์ ์ถ๋ ฅ๊ฐ์ ๋ฐํํ๋ค.
ํ๋ จํ๋ ๋์ ์ด ์์ค๊ณผ ์ฌ๊ตฌ์ฑ ์์ค์ด ํจ๊ป ๊ณ์ฐ๋๋ค.
Epoch 1/5 363/363 [========] - 1s 820us/step - loss: 0.7640 - reconstruction error: 1.2728 Epoch 2/5 363/363 [========] - Os 809us/step - loss: 0.4584 - reconstruction_error: 0.6340 [ . . . ]12.2.8 ์๋ ๋ฏธ๋ถ์ผ๋ก ๊ทธ๋ ์ด๋์ธํธ ๊ณ์ฐํ๊ธฐ
def f(w1, w2): return 3 * w1 ** 2 + 2 * w1 * w2 w1, w2 = tf.Variable(5.), tf.Variable(3.) with tf.GradientTape() as tape: z = f(w1, w2) gradients = tape.gradient(z, [w1, w2])๋ ๋ณ์ w1๊ณผ w2๋ฅผ ์ ์ํ๊ณ tf.GradientTape ๋ธ๋ก์ ๋ง๋ค์ด ์ด ๋ณ์์ ๊ดํ๋ ๋ชจ๋ ์ฐ์ฐ์ ์๋์ผ๋ก ๊ธฐ๋กํ๋ค.
๋ง์ง๋ง์ผ๋ก ์ด ํ ์ดํ์ ๋ ๋ณ์ [w1, w2]์ ๋ํ z์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ์์ฒญํ๋ค.
๋ณ์์ ๊ฐ์์ ์๊ด์์ด gradient() ๋ฉ์๋๋ ๊ธฐ๋ก๋ ๊ณ์ฐ์ ํ ๋ฒ๋ง์ ๊ฑฐ๊พธ๋ก ํต๊ณผํ๋ค.
with tf.GradientTape() as tape: z = f(w1, w2) dz_dw1 = tape.gradient(z, w1) dz_dw2 = tape.gradient(z, w2) # RuntimeErrorgradient() ๋ฉ์๋๊ฐ ํธ์ถ๋ ํ์๋ ์๋์ผ๋ก ํ ์ดํ๊ฐ ์ฆ์ ์ง์์ง๋ค. ๋ฐ๋ผ์ gradient() ๋ฉ์๋๋ฅผ ๋ ๋ฒ ํธ์ถํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
with tf.GradientTape(persistent=True) as tape: z = f(w1, w2) dz_dw1 = tape.gradient(z, w1) dz_dw2 = tape.gradient(z, w2) del tapegradient() ๋ฉ์๋๋ฅผ ํ ๋ฒ ์ด์ ํธ์ถํด์ผ ํ๋ค๋ฉด ์ง์ ๊ฐ๋ฅํ ํ ์ดํ๋ฅผ ๋ง๋ค๊ณ ์ฌ์ฉ์ด ๋๋ ํ ํ ์ดํ๋ฅผ ์ญ์ ํ์ฌ ๋ฆฌ์์ค๋ฅผ ํด์ ํด์ผ ํ๋ค.
c1, c2 = tf.constant(5.), tf.constant (3.) with tf.GradientTape() as tape: z = f(c1, c2) gradients = tape.gradient(z, [c1, c2]) # [None, None] ๋ฐํ๊ธฐ๋ณธ์ ์ผ๋ก ํ ์ดํ๋ ๋ณ์๊ฐ ํฌํจ๋ ์ฐ์ฐ๋ง์ ๊ธฐ๋กํ๋ค. ๋ณ์๊ฐ ์๋ ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ํ z์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ฉด None์ด ๋ฐํ๋๋ค.
with tf.GradientTape() as tape: tape.watch(c1) tape.watch(c2) z = f(c1, c2) gradinets = tape.gradient(z, [c1, c2]) # [36., 10. ํ ์] ๋ฐํํ์ํ ๊ฒฝ์ฐ ์ด๋ค ํ ์๋ ๊ฐ์ํ์ฌ ๊ด๋ จ๋ ๋ชจ๋ ์ฐ์ฐ์ ๊ธฐ๋กํ๋๋ก ๊ฐ์ ํ ์ ์์ผ๋ฉฐ ๋ณ์์ฒ๋ผ ์ด๋ฐ ํ ์์ ๋ํด ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
์ ๋ ฅ์ด ์์ ๋ ๋ณ๋ ํญ์ด ํฐ ํ์ฑํ ํจ์์ ๋ํ ๊ท์ ์์ค์ ๊ตฌํํ๋ ๊ฒฝ์ฐ์ ์ ์ฉํ๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๊ทธ๋ ์ด๋์ธํธ ํ ์ดํ๋ ์ฌ๋ฌ ๊ฐ์ ๋ํ ํ ๊ฐ์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ ํ์ง ๋ชจ๋ ์๋ ๋ฏธ๋ถ์ด ์ ํฉํ๋ฉฐ ํ ๋ฒ์ ์ ๋ฐฉํฅ ๊ณ์ฐ๊ณผ ์ญ๋ฐฉํฅ ๊ณ์ฐ์ผ๋ก ๋ชจ๋ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๋์์ ๊ณ์ฐํ ์ ์๋ค.
์ฌ๋ฌ ์์ค์ด ํฌํจ๋ ๋ฒกํฐ์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ฉด ํ ์ํ๋ก๋ ๋ฒกํฐ์ ํฉ์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ ๊ฒ์ด๋ค.
๊ฐ๋ณ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๊ณ ์ถ๋ค๋ฉด ํ ์ดํ์ jacobian() ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํ๋ค.
๋ฒกํฐ์ ์๋ ๊ฐ ์์ค๋ง๋ค ํ์ง ์๋ ๋ฏธ๋ถ์ ์ํํ๋ฉฐ ์ด๊ณ๋ํจ์๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
์ด๋ค ๊ฒฝ์ฐ์๋ ์ ๊ฒฝ๋ง์ ์ผ๋ถ๋ถ์ ๊ทธ๋ ์ด๋์ธํธ๊ฐ ์ญ์ ํ๋์ง ์๋๋ก ๋ง์ ํ์๊ฐ ์๋ค.
tf.stop_gradient() ํจ์๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
์ด ํจ์๋ ์ ๋ฐฉํฅ ๊ณ์ฐ์ ํ ๋ ์ ๋ ฅ์ ๋ฐํํ์ง๋ง, ์ญ์ ํ ์์๋ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ์ ํํ์ง ์์ ์์์ฒ๋ผ ์๋ํ๋ค.
def f(w1, w2): return 3 * w1 ** 2 + tf.stop_gradient(2 * w1 * w2) with tf.gradientTape() as tape: z = f(w1, w2) # ์ ๋ฐฉํฅ ๊ณ์ฐ์ stop_gradient()์ ์ํฅ์ ๋ฐ์ง ์๋๋ค. gradient = tape.gradient(z, [w1, w2]) # [30. ํ ์, None] ๋ฐํ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ ๋ 32๋นํธ ๋ถ๋์์์ ์ผ๋ก ๋ค๋ฃฐ ์ ์๋ ์์น์ ์ธ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ๋ ํ๋ค.
x = tf.Variable(1e-50) with tf.GradientTape() as tape: ... z = tf.sqrt(x) ... tape.gradient(z, [x]) # [<tf.Tensor: shape=(), dtype=float32, numpy=inf>]์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ ๊ณฑ๊ทผ์ ๊ณ์ฐํ ๋ ์์ ๊ฐ์ ์ถ๊ฐํ๋ค.
์ง์ ํจ์๋ ๋งค์ฐ ๋น ๋ฅด๊ฒ ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ ์ง์ ํญ์ด ํญ๋ฐํ์ง ์๋๋ก my_softplus() ํจ์๋ฅผ ์์น์ ์ผ๋ก ์์ ์ ์ด๊ฒ ๊ตฌํํ ์ ์๋ค.
def my_softplus(z): return tf.math.log(1 + tf.exp(-tf.abs(z))) + tf.maximum(0., z)์์น์ ์ผ๋ก ์์ ์ ์ธ ํจ์์๋ ์์น์ ์ผ๋ก ๋ถ์์ ํ ๊ทธ๋ ์ด๋์ธํธ๊ฐ ์์ ์ ์๋ค.
์ด๋ฌํ ๊ฒฝ์ฐ ์๋ ๋ฏธ๋ถ์ ์ฌ์ฉํ์ง ์๊ณ ๊ทธ๋ ์ด๋์ธํธ ๊ณ์ฐ์ ์ํด ์ฌ์ฉํ ์์ ํ ์ํ๋ก์ ์๋ ค์ฃผ์ด์ผ ํ๋ค.
ํจ์๋ฅผ ์ ์ํ ๋ @tf.custom_gradient ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ ์ผ๋ฐ์ ์ธ ํจ์ ๊ฒฐ๊ณผ์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ ํจ์๋ฅผ ๋ชจ๋ ๋ฐํํด์ผ ํ๋ค.
@tf.custom_gradient def my_softplus(z): def my_softplus_gradients(grads): # grads = ์์ ์ธต์์ ์ญ์ ํ๋ ๊ทธ๋ ์ด๋์ธํธ return grads * (1 - 1 / (1 + tf.exp(z))) # ์์ ์ ์ธ ์ํํธํ๋ฌ์ค์ ๊ทธ๋ ์ด๋์ธํธ return = tf.math.log(1 + tf.exp(-tf.abs(z))) + tf.maximum(0., z) return result. my_softplus_gradients12.2.9 ์ฌ์ฉ์ ์ ์ ํ๋ จ ๋ฐ๋ณต
l2_reg = tf.keras.regularizers.l2(0.05) model = tf.keras.models.Sequential([ tf.keras.layers.Dense(30, activation="relu", kernel_initializer="he_normal", kernel_regularizer=l2_reg), tf.keras.layers.Dense(1, kernel_regularizer=l2_reg) ])๊ฐ๋จํ ๋ชจ๋ธ์ ๋ง๋ ๋ค.
def random_batch(X, y, batch_size=32): idx = np.random.randint(len(X), size=batch_size) return X[idx], y[idx]ํ๋ จ ์ธํธ์์ ์ํ ๋ฐฐ์น๋ฅผ ๋๋คํ๊ฒ ์ถ์ถํ๋ ์์ ํจ์๋ฅผ ๋ง๋ ๋ค.
def print_status_bar(step, total, loss, metrics=None): metrics = " - ".join([f"{m.name}: {m.result():.4f}" for m in [loss] + (metrics or [])]) end = "" if step < total else "\n" print(f"\r{step}/{total} - " + metrics, end=end)ํ์ฌ ์คํ ์, ์ ์ฒด ์คํ ์, ์ํฌํฌ ์์๋ถํฐ ํ๊ท ์์ค, ๊ทธ ์ธ ๋ค๋ฅธ ์งํ๋ฅผ ํฌํจํ์ฌ ํ๋ จ ์ํ๋ฅผ ์ถ๋ ฅํ๋ ํจ์๋ฅผ ๋ง๋ ๋ค.
n_epochs = 5 batch_size = 32 n_steps = len(X_train) // batch_size optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) loss_fn = tf.keras. losses.mean_squared_error mean_loss = tf.keras.metrics.Mean(name="mean_loss") metrics = [tf.keras.metrics.MeanAbsoluteError]๋ช ๊ฐ์ ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ํ๊ณ ์ตํฐ๋ง์ด์ , ์์ค ํจ์, ์งํ๋ฅผ ์ ํํด์ผ ํ๋ค.
for epoch in range(1, n_epochs + 1): print("Epoch {}/{}".format(epoch, n_epochs)) for step in range(1, n_steps + 1): X_batch, y_batch = random_batch(X_train_scaled, y_train) with tf.GradientTape() as tape: y_pred = model(X_batch, training=True) main_loss = tf.reduce_mean(loss_fn(y_batch, y_pred)) loss = tf.add_n([main_loss] + model.losses) gradients = tape.gradient(loss, model.trainable_variables) optimizer.appy_gradient(zip(gradients, model.trainable_variables)) mean_loss(loss) for metric in metrics: metirc(y_batch, y_pred) print_status_bar(step, n_steps, mean_loss, metrics) for metric in [mean_loss] + metrics: metric.reset_status()ํ๋ จ ์ธํธ์์ ๋ฐฐ์น๋ฅผ ๋๋คํ๊ฒ ์ํ๋งํ๋ค.
tf.GradientTape() ๋ธ๋ก ์์์ ๋ชจ๋ธ์ ํจ์์ฒ๋ผ ์ฌ์ฉํ์ฌ ๋ฐฐ์น ํ๋๋ฅผ ์ํ ์์ธก์ ๋ง๋ค๊ณ ์์ค์ ๊ณ์ฐํ๋ค. ์ด ์์ค์ ์ฃผ ์์ค์ ๋ค๋ฅธ ์์ค์ ๋ํ ๊ฒ์ด๋ค. mean_squred_error() ํจ์๊ฐ ์ํ๋ง๋ค ํ๋์ ์์ค์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ tf.reduce_mean() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฐ์น์ ๋ํ ํ๊ท ์ ๊ณ์ฐํ๋ค. ๊ท์ ์์ค์ ํ๋์ ์ค์นผ๋ผ๊ฐ์ด๋ฏ๋ก ์์ค์ ๋ชจ๋ ๋ํ๋ค.
ํ ์ดํ๋ฅผ ์ฌ์ฉํด ํ๋ จ ๊ฐ๋ฅํ ๊ฐ ๋ณ์์ ๋ํ ์์ค์ ๊ทธ๋ ์ด๋์ธํธ๋ฅผ ๊ณ์ฐํ๋ค. ์ด๋ฅผ ์ตํฐ๋ง์ด์ ์ ์ ์ฉํ์ฌ ๊ฒฝ์ฌ ํ๊ฐ๋ฒ์ ์ํํ๋ค.
ํ์ฌ ์ํฌํฌ์ ๋ํ ํ๊ท ์์ค๊ณผ ์งํ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ ์ํ ๋ง๋๋ฅผ ์ถ๋ ฅํ๋ค.
๋งค ์ํฌํฌ ๋์์ ํ๊ท ์์ค๊ณผ ์งํ ๊ฐ์ ์ด๊ธฐํํ๋ค.
for variable in model.variables: if variable.constraint is not None: variable.assign(variable.consraint(variable))๋ง์ฝ ๊ทธ๋ ์ด๋์ธํธ ํด๋ฆฌํ์ ํ๊ณ ์ถ๋ค๋ฉด clipnorm์ด๋ clipvalue ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ํ๋ฉด ๋๋ค.
๊ฐ์ค์น์ ๋ค๋ฅธ ๋ณํ์ ์ ์ฉํ๋ ค๋ฉด apply_gradients() ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ์ํํด์ผ ํ๋ค.
๋ชจ๋ธ์ ๊ฐ์ค์น ์ ํ์ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด apply_gradients() ๋ค์์ ์ด ์ ํ์ ์ ์ฉํ๋๋ก ํ๋ จ ๋ฐ๋ณต์ ์์ ํด์ผ ํ๋ค.
12.3 ํ ์ํ๋ก ํจ์์ ๊ทธ๋ํ
def cube(x): return x ** 3 tf_cube = tf.function(cube) tf_cube # <tensorflow.python.eager.def_function at 0x1546fc080>tf.function()์ ์ฌ์ฉํ์ฌ ํ์ด์ฌ ํจ์๋ฅผ ํ ์ํ๋ก ํจ์๋ก ๋ฐ๊ฟ ์ ์๋ค.
tf_cube(2) # <tf.Tensor: shape=(), dtype=int32, numpy=8> tf_cube(tf.constant(2.0)) # <tf.Tensor: shape=().dtype=float32, numpy=8.0>ํ์ด์ฌ ํจ์๋ก ์ฌ์ฉํ ์ ์๊ณ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
@tf.function def tf_cube(x): return x ** 3๋ด๋ถ์ ์ผ๋ก tf.function()์ cube() ํจ์์์ ์ํ๋๋ ๊ณ์ฐ์ ๋ถ์ํ๊ณ ๋์ผํ ์์ ์ ์ํํ๋ ๊ณ์ฐ ๊ทธ๋ํ๋ฅผ ์์ฑํ๋ค.
tf.function ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
tf_cube.python_function(2) # 8์๋ณธ ํ์ด์ฌ ํจ์๋ ํ์ํ ๋ ํ ์ํ๋ก ํจ์์ python_function ์์ฑ์ผ๋ก ์ฐธ์กฐํ ์ ์๋ค.
ํ ์ํ๋ก๋ ์ฌ์ฉํ์ง ์๋ ๋ ธ๋๋ฅผ ์ ๊ฑฐํ๊ณ ํํ์ ๋จ์ํํ๋ ๋ฑ ๊ณ์ฐ ๊ทธ๋ํ๋ฅผ ์ต์ ํํ๋ค.
์ต์ ํ๋ ๊ทธ๋ํ๊ฐ ์ค๋น๋๋ฉด ํ ์ํ๋ก ํจ์๋ ์ ์ ํ ์์์ ๋ง์ถฐ ๊ทธ๋ํ ๋ด์ ์ฐ์ฐ์ ํจ์จ์ ์ผ๋ก ์คํใ ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ํ ์ํ๋ก ํจ์๋ ์๋ณธ ํ์ด์ฌ ํจ์๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๊ฒ ์คํ๋๋ค.
tf.function()์ ํธ์ถํ ๋ jit_compile=True๋ก ์ค์ ํ๋ฉด ํ ์ํ๋ก๋ XLA๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ๊ทธ๋ํ๋ฅผ ์ํ ์ ์ฉ ์ปค๋์ ์ปดํ์ผํ๋ฉฐ, ์ข ์ข ์ฌ๋ฌ ์ฐ์ฐ์ ์ตํฉํ๋ค.
์ฌ์ฉ์ ์ ์ ์์ค ํจ์, ์ฌ์ฉ์ ์ ์ ์งํ, ์ฌ์ฉ์ ์ ์ ์ธต ๋๋ ๋ค๋ฅธ ์ฌ์ฉ์ ์ ์ ํจ์๋ฅผ ์์ฑํ๊ณ ์ด๋ฅผ ์ผ๋ผ์ค ๋ชจ๋ธ์ ์ฌ์ฉํ ๋, ์ผ๋ผ์ค๋ ์๋์ผ๋ก ์ด ํจ์๋ฅผ ํ ์ํ๋ก ํจ์๋ก ๋ณํํ๋ฏ๋ก tf.function()์ ์ฌ์ฉํ ํ์๊ฐ ์๋ค.
์ผ๋ผ์ค๊ฐ XLA๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋ ค๋ฉด compile() ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ jit_compile=True๋ก ์ค์ ํ๋ฉด ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ํ ์ํ๋ก ํจ์๋ ํธ์ถ์ ์ฌ์ฉ๋๋ ์ ๋ ฅ ํฌ๊ธฐ์ ๋ฐ์ดํฐ ํ์ ์ ๋ง์ถฐ ๋งค๋ฒ ์๋ก์ด ๊ทธ๋ํ๋ฅผ ์์ฑํ๋ค.
12.3.1 ์คํ ๊ทธ๋ํ์ ํธ๋ ์ด์ฑ
์คํ ๊ทธ๋ํ : ํ ์ํ๋ก๊ฐ ๊ทธ๋ํ๋ฅผ ์์ฑํ๊ธฐ ์ํด ๋จผ์ ํ์ด์ฌ ํจ์์ ์์ค ์ฝ๋๋ฅผ ๋ถ์ํ์ฌ for๋ฌธ, while๋ฌธ, if๋ฌธ์ ๋ฌผ๋ก break, continue, return ๊ฐ์ ์ ์ด๋ฌธ์ ๋ชจ๋ ์ฐพ๋๋ค.
ํ ์ํ๋ก๊ฐ ์์ค ์ฝ๋๋ฅผ ๋ถ์ํ๋ ์ด์ ๋ ํ์ด์ฌ์ด ์ ์ด๋ฌธ์ ์ฐพ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
ํจ์์ ์ฝ๋๋ฅผ ๋ถ์ํ ํ ์คํ ๊ทธ๋ํ๋ ์ด ํจ์์ ๋ชจ๋ ์ ์ด๋ฌธ์ ํ ์ํ๋ก ์ฐ์ฐ์ผ๋ก ๋ฐ๊พธ์ด ์ ๊ทธ๋ ์ด๋๋ ๋ฒ์ ์ ๋ง๋ ๋ค.

๊ทธ๋ค์ ํ ์ํ๋ก๊ฐ ์ด ์ ๋ฐ์ดํธ๋ ํจ์๋ฅผ ํธ์ถํ๋ค.
ํ์ง๋ง ๋งค๊ฐ๋ณ์ ๊ฐ์ ์ ๋ฌํ๋ ๋์ ์ฌ๋ณผ๋ฆญ ํ ์๋ฅผ ์ ๋ฌํ๋ค. ์ด ํ ์๋ ์ค์ ํ๋ ๊ฐ์ด ์๊ณ ์ด๋ฆ, ๋ฐ์ดํฐ ํ์ , ํฌ๊ธฐ๋ง ๊ฐ์ง๋ค.
์ด ํจ์๋ ๊ทธ๋ํ ๋ชจ๋๋ก ์คํ๋ ๊ฒ์ด๋ค. ๊ฐ ํ ์ํ๋ก ์ฐ์ฐ์ด ํด๋น ์ฐ์ฐ์ ๋ํ๋ด๊ณ ํ ์๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด ๊ทธ๋ํ์ ๋ ธ๋๋ฅผ ์ถ๊ฐํ๋ค๋ ์๋ฏธ์ด๋ค.
ํ ์ํ๋ก ์ฐ์ฐ์ ์ด๋ค ๊ณ์ฐ๋ ์ํํ์ง ์๋๋.
์ต์ข ๊ทธ๋ํ๋ ํธ๋ ์ด์ฑ ๊ณผ์ ์ ํตํด ์์ฑ๋๋ค. ๋ ธ๋๋ ์ฐ์ฐ์ ๋ํ๋ด๊ณ ํ์ดํ๋ ํ ์๋ฅผ ๋ํ๋ธ๋ค.
12.3.2 ํ ์ํ๋ก ํจ์ ์ฌ์ฉ๋ฒ
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํ ์ํ๋ก ์ฐ์ฐ์ ์ํํ๋ ํ์ด์ฌ ํจ์๋ฅผ ํ ์ํ๋ก ํจ์๋ก ๋ฐ๊พธ๊ธฐ ์ํด์๋ @tf.function ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
'๐ฎ ์ด๊ฒ์ ๊ฒ ๊ณต๋ถ > โฉ ํธ์ฆ์จ ๋จธ์ ๋ฌ๋' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ