ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • โœฑ ํ•ธ์ฆˆ์˜จ ๋จธ์‹ ๋Ÿฌ๋‹ 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.float32

    tf.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 X

    12.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) # RuntimeError

    gradient() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ ํ›„์—๋Š” ์ž๋™์œผ๋กœ ํ…Œ์ดํ”„๊ฐ€ ์ฆ‰์‹œ ์ง€์›Œ์ง„๋‹ค. ๋”ฐ๋ผ์„œ 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 tape

    gradient() ๋ฉ”์„œ๋“œ๋ฅผ ํ•œ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ง€์† ๊ฐ€๋Šฅํ•œ ํ…Œ์ดํ”„๋ฅผ ๋งŒ๋“ค๊ณ  ์‚ฌ์šฉ์ด ๋๋‚œ ํ›„ ํ…Œ์ดํ”„๋ฅผ ์‚ญ์ œํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•ด์•ผ ํ•œ๋‹ค.

    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_gradients

    12.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 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

Designed by Tistory.