맞춤형 케라 레이어 및 모델을 구축하기 전에 이것을 읽으십시오
콘텐츠 개요
- 설정
- 레이어 클래스 : 상태 (가중치)와 일부 계산의 조합
- 레이어는 비전되지 않는 가중치를 가질 수 있습니다
- 모범 사례 : 입력의 모양이 알려질 때까지 체중 생성을 연기
- 레이어는 재귀 적으로 합성 가능합니다
- add_loss () 메소드
- 선택적으로 레이어에서 직렬화를 활성화 할 수 있습니다
- Call () 메소드의 특권 교육 인수
- Call () 메소드의 권한이있는 마스크 인수
- 모델 클래스
- 모든 것을 정리하십시오 : 엔드 투 엔드 예
\
설정
import tensorflow as tf
from tensorflow import keras
그만큼Layer
클래스 : 상태 (가중치)와 일부 계산의 조합
Keras의 중심 추상화 중 하나는입니다Layer
수업. 레이어는 상태 (레이어의 “가중치”)와 입력에서 출력 ( “호출”, 레이어의 순방향 패스)으로 변환을 모두 캡슐화합니다.
밀도가 높은 레이어는 다음과 같습니다. 상태가 있습니다 : 변수w
그리고b
.
\
class Linear(keras.layers.Layer):
def __init__(self, units=32, input_dim=32):
super().__init__()
self.w = self.add_weight(
shape=(input_dim, units), initializer="random_normal", trainable=True
)
self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
파이썬 함수와 마찬가지로 텐서 입력에서 호출하여 레이어를 사용합니다.
\
x = tf.ones((2, 2))
linear_layer = Linear(4, 2)
y = linear_layer(x)
print(y)
\
tf.Tensor(
[[-0.02419483 -0.06813122 0.00395634 -0.03124779]
[-0.02419483 -0.06813122 0.00395634 -0.03124779]], shape=(2, 4), dtype=float32)
가중치에 유의하십시오w
그리고b
레이어 속성으로 설정되면 레이어에 의해 자동으로 추적됩니다.
\
assert linear_layer.weights == [linear_layer.w, linear_layer.b]
레이어는 비전되지 않는 가중치를 가질 수 있습니다
훈련 가능한 무게 외에도 층에 비계 할 수없는 무게를 추가 할 수 있습니다. 이러한 가중치는 층을 훈련 할 때 역설 중에 고려되지 않아야합니다.
비전 할 수없는 무게를 추가하고 사용하는 방법은 다음과 같습니다.
\
class ComputeSum(keras.layers.Layer):
def __init__(self, input_dim):
super().__init__()
self.total = self.add_weight(
initializer="zeros", shape=(input_dim,), trainable=False
)
def call(self, inputs):
self.total.assign_add(tf.reduce_sum(inputs, axis=0))
return self.total
x = tf.ones((2, 2))
my_sum = ComputeSum(2)
y = my_sum(x)
print(y.numpy())
y = my_sum(x)
print(y.numpy())
\
[2. 2.]
[4. 4.]
그것은의 일부입니다layer.weights
그러나 비계 할 수없는 무게로 분류됩니다.
\
print("weights:", len(my_sum.weights))
print("non-trainable weights:", len(my_sum.non_trainable_weights))
# It's not included in the trainable weights:
print("trainable_weights:", my_sum.trainable_weights)
\
weights: 1
non-trainable weights: 1
trainable_weights: []
모범 사례 : 입력의 모양이 알려질 때까지 체중 생성을 연기
우리의Linear
위의 층이 가져 갔다input_dim
무게의 모양을 계산하는 데 사용 된 인수w
그리고b
~에__init__()
:
\
class Linear(keras.layers.Layer):
def __init__(self, units=32, input_dim=32):
super().__init__()
self.w = self.add_weight(
shape=(input_dim, units), initializer="random_normal", trainable=True
)
self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
대부분의 경우 입력의 크기를 미리 알지 못할 수도 있으며, 그 값이 알려지면 층을 인스턴스화 한 후에도 값을 느낄 때 가중치를 만들고 싶습니다.
Keras API에서는build(self, inputs_shape)
레이어의 방법. 이와 같이:
\
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
그만큼__call__()
레이어의 방법은 처음으로 호출 될 때 자동으로 빌드를 실행합니다. 이제 게으르고 사용하기 쉬운 레이어가 있습니다.
\
# At instantiation, we don't know on what inputs this is going to get called
linear_layer = Linear(32)
# The layer's weights are created dynamically the first time the layer is called
y = linear_layer(x)
구현build()
위에서 볼 수 있듯이 별도로는 모든 통화에서 가중치를 사용하여 한 번만 가중치 생성을 멋지게 분리합니다. 그러나 일부 고급 커스텀 레이어의 경우 상태 생성 및 계산을 분리하는 것이 비현실적 일 수 있습니다. 레이어 구현자는 첫 번째로 체중 생성을 연기 할 수 있습니다.__call__()
그러나 나중에 통화가 같은 가중치를 사용하도록 조심해야합니다. 또한 그 이후로__call__()
내부에서 처음으로 실행될 가능성이 높습니다.tf.function
발생하는 모든 가변 생성__call__()
atf.init_scope
.
레이어는 재귀 적으로 합성 가능합니다
레이어 인스턴스를 다른 레이어의 속성으로 할당하면 외부 레이어가 내부 레이어로 생성 된 가중치를 추적하기 시작합니다.
그러한 하위 계이를 만드는 것이 좋습니다__init__()
방법을 먼저 두십시오__call__()
무게를 건축하는 것을 유발합니다.
\
class MLPBlock(keras.layers.Layer):
def __init__(self):
super().__init__()
self.linear_1 = Linear(32)
self.linear_2 = Linear(32)
self.linear_3 = Linear(1)
def call(self, inputs):
x = self.linear_1(inputs)
x = tf.nn.relu(x)
x = self.linear_2(x)
x = tf.nn.relu(x)
return self.linear_3(x)
mlp = MLPBlock()
y = mlp(tf.ones(shape=(3, 64))) # The first call to the `mlp` will create the weights
print("weights:", len(mlp.weights))
print("trainable weights:", len(mlp.trainable_weights))
\
weights: 6
trainable weights: 6
그만큼add_loss()
방법
쓸 때call()
레이어 방법, 훈련 루프를 작성할 때 나중에 사용할 수있는 손실 텐서를 만들 수 있습니다. 이것은 전화로 가능합니다self.add_loss(value)
:
\
# A layer that creates an activity regularization loss
class ActivityRegularizationLayer(keras.layers.Layer):
def __init__(self, rate=1e-2):
super().__init__()
self.rate = rate
def call(self, inputs):
self.add_loss(self.rate * tf.reduce_mean(inputs))
return inputs
주목하십시오add_loss()
일반적인 텐서 플로 작동의 결과를 얻을 수 있습니다. a를 호출 할 필요가 없습니다Loss
여기에 물체.
이러한 손실 (내부 층에 의해 생성 된 손실 포함)은layer.losses
. 이 속성은 모든 시작시 재설정됩니다__call__()
최상위 레이어로layer.losses
항상 마지막 포워드 패스 중에 생성 된 손실 값이 포함됩니다.
\
class OuterLayer(keras.layers.Layer):
def __init__(self):
super().__init__()
self.activity_reg = ActivityRegularizationLayer(1e-2)
def call(self, inputs):
return self.activity_reg(inputs)
layer = OuterLayer()
assert len(layer.losses) == 0 # No losses yet since the layer has never been called
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1 # We created one loss value
# `layer.losses` gets reset at the start of each __call__
_ = layer(tf.zeros(1, 1))
assert len(layer.losses) == 1 # This is the loss created during the call above
또한loss
속성은 또한 모든 내부 층의 중량에 대해 생성 된 정규화 손실도 포함합니다.
\
class OuterLayerWithKernelRegularizer(keras.layers.Layer):
def __init__(self):
super().__init__()
self.dense = keras.layers.Dense(
32, kernel_regularizer=keras.regularizers.l2(1e-3)
)
def call(self, inputs):
return self.dense(inputs)
layer = OuterLayerWithKernelRegularizer()
_ = layer(tf.zeros((1, 1)))
# This is `1e-3 * sum(layer.dense.kernel ** 2)`,
# created by the `kernel_regularizer` above.
print(layer.losses)
\
[<tf.Tensor: shape=(), dtype=float32, numpy=0.0017542194>]
이러한 손실은 다음과 같이 훈련 루프를 작성할 때 고려됩니다.
\
# Instantiate an optimizer.
optimizer = keras.optimizers.SGD(learning_rate=1e-3)
loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
# Iterate over the batches of a dataset.
for x_batch_train, y_batch_train in train_dataset:
with tf.GradientTape() as tape:
logits = layer(x_batch_train) # Logits for this minibatch
# Loss value for this minibatch
loss_value = loss_fn(y_batch_train, logits)
# Add extra losses created during this forward pass:
loss_value += sum(model.losses)
grads = tape.gradient(loss_value, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
교육 루프 작성에 대한 자세한 안내서는 처음부터 교육 루프 작성에 대한 안내서를 참조하십시오.
이러한 손실도 완벽하게 작동합니다fit()
(자동으로 합산되어 주 손실에 추가됩니다.) :
\
import numpy as np
inputs = keras.Input(shape=(3,))
outputs = ActivityRegularizationLayer()(inputs)
model = keras.Model(inputs, outputs)
# If there is a loss passed in `compile`, the regularization
# losses get added to it
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))
# It's also possible not to pass any loss in `compile`,
# since the model already has a loss to minimize, via the `add_loss`
# call during the forward pass!
model.compile(optimizer="adam")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))
\
1/1 [==============================] - 0s 75ms/step - loss: 0.1081
1/1 [==============================] - 0s 31ms/step - loss: 0.0044
<keras.src.callbacks.History at 0x7fb23c0e3f40>
선택적으로 레이어에서 직렬화를 활성화 할 수 있습니다
기능 모델의 일부로 세 사용자 정의 레이어가 직렬화 할 수있는 경우 선택적으로 A를 구현할 수 있습니다.get_config()
방법:
\
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
def get_config(self):
return {"units": self.units}
# Now you can recreate the layer from its config:
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
\
{'units': 64}
주목하십시오__init__()
베이스의 방법Layer
클래스는 일부 키워드 인수, 특히 a를 취합니다name
그리고 adtype
. 이러한 주장을 부모 수업에 전달하는 것이 좋습니다.__init__()
그리고 레이어 구성에 포함시키기 위해 :
\
class Linear(keras.layers.Layer):
def __init__(self, units=32, **kwargs):
super().__init__(**kwargs)
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
def get_config(self):
config = super().get_config()
config.update({"units": self.units})
return config
layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)
\
{'name': 'linear_7', 'trainable': True, 'dtype': 'float32', 'units': 64}
구성에서 레이어를 실시 할 때 더 많은 유연성이 필요하면from_config()
수업 방법. 이것이 기본 구현입니다from_config()
:
\
def from_config(cls, config):
return cls(**config)
직렬화 및 저장에 대한 자세한 내용은 모델 저장 및 직렬화에 대한 완전한 안내서를 참조하십시오.
특권training
인수call()
방법
일부 층, 특히BatchNormalization
레이어와Dropout
층은 훈련 및 추론 중에 다른 행동을합니다. 그러한 층의 경우, 노출하는 것은 표준 관행입니다.training
(부울) 논쟁call()
방법.
이 주장을 노출시킴으로써call()
내장 교육 및 평가 루프 (예 :fit()
) 훈련 및 추론에 레이어를 올바르게 사용하려면.
\
class CustomDropout(keras.layers.Layer):
def __init__(self, rate, **kwargs):
super().__init__(**kwargs)
self.rate = rate
def call(self, inputs, training=False):
if training:
return tf.nn.dropout(inputs, rate=self.rate)
return inputs
특권mask
인수call()
방법
뒷받침되는 다른 특권 주장call()
입니다mask
논쟁.
모든 keras rnn 층에서 찾을 수 있습니다. 마스크는 타임 서리 데이터를 처리 할 때 특정 입력 타임 스텝을 건너 뛰는 데 사용되는 부울 텐서 (입력의 타임 스텝 당 1 개의 부울 값)입니다.
Keras는 자동으로 올바른 전달됩니다mask
논쟁__call__()
이를지지하는 층의 경우, 마스크가 이전 레이어에 의해 생성 될 때. 마스크 생성 레이어는 다음과 같습니다Embedding
구성된 레이어mask_zero=True
그리고Masking
층.
마스킹 및 마스킹 가능 레이어 작성 방법에 대한 자세한 내용은 “패딩 및 마스킹 이해”가이드를 확인하십시오.
그만큼Model
수업
일반적으로 사용합니다Layer
내부 계산 블록을 정의하는 클래스는Model
클래스 외부 모델 – 훈련 할 객체를 정의하는 클래스.
예를 들어 RESNET50 모델에는 여러 RESNET 블록 서브 클래싱이 있습니다.Layer
그리고 싱글Model
전체 RESNET50 네트워크를 포함합니다.
그만큼Model
클래스는 API와 동일합니다Layer
다음 차이점으로 :
- 내장 교육, 평가 및 예측 루프를 노출시킵니다 (
model.fit()
,,,model.evaluate()
,,,model.predict()
). - 그것은 내부 층의 목록을
model.layers
재산. - 저축 및 직렬화 API를 노출시킵니다 (
save()
,,,save_weights()
…)
효과적으로,Layer
클래스는 문헌에서 “컨퍼런스 레이어”또는 “재발 레이어”에서와 같이 “층”또는 “블록”( “Resnet Block”또는 “Inceple Block”에서와 같이)으로 언급 한 내용에 해당합니다.
한편,Model
클래스는 문헌에서 “모델”( “딥 러닝 모델”에서와 같이) 또는 “네트워크”( “Deep Neural Network”에서와 같이)로 언급 된 내용에 해당합니다.
그러니 궁금한 점이 있다면 “내가 사용해야합니다Layer
클래스 또는Model
클래스? “, 스스로에게 물어보세요 : 전화해야합니다fit()
그것에? 전화해야합니다save()
그것에? 그렇다면 함께 가십시오Model
. 그렇지 않은 경우 (클래스가 더 큰 시스템의 블록이거나 직접 교육 및 코드 저장을 작성하고 있기 때문에) 사용하십시오.Layer
.
예를 들어, 우리는 위의 미니 레즈넷 예제를 가져 와서 그것을 만들 수 있습니다.Model
우리가 훈련 할 수있는 것fit()
그리고 우리가 저축 할 수 있습니다save_weights()
:
\
class ResNet(keras.Model):
def __init__(self, num_classes=1000):
super().__init__()
self.block_1 = ResNetBlock()
self.block_2 = ResNetBlock()
self.global_pool = layers.GlobalAveragePooling2D()
self.classifier = Dense(num_classes)
def call(self, inputs):
x = self.block_1(inputs)
x = self.block_2(x)
x = self.global_pool(x)
return self.classifier(x)
resnet = ResNet()
dataset = ...
resnet.fit(dataset, epochs=10)
resnet.save(filepath.keras)
모든 것을 정리하십시오 : 엔드 투 엔드 예
지금까지 배운 내용은 다음과 같습니다.
- 에이
Layer
상태를 캡슐화하십시오 (생성__init__()
또는build()
) 및 일부 계산 (정의call()
). - 새롭고 더 큰 계산 블록을 생성하기 위해 레이어를 재귀 적으로 중첩 할 수 있습니다.
- 레이어는
add_loss()
. - 외부 용기, 훈련하고 싶은 것은
Model
. 에이Model
그냥 aLayer
교육 및 직렬화 유틸리티가 추가되었습니다.
이 모든 것들을 엔드 투 엔드 예제로합시다. 우리는 변형 자동 인코딩 (VAE)을 구현할 것입니다. 우리는 mnist 숫자로 훈련 할 것입니다.
우리 VAE는 서브 클래스가 될 것입니다Model
서브 클래스의 중첩 된 층 구성으로 제작되었습니다Layer
. 정규화 손실 (KL 발산)이 특징입니다.
\
from keras import layers
@keras.saving.register_keras_serializable()
class Sampling(layers.Layer):
"""Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""
def call(self, inputs):
z_mean, z_log_var = inputs
batch = tf.shape(z_mean)[0]
dim = tf.shape(z_mean)[1]
epsilon = keras.backend.random_normal(shape=(batch, dim))
return z_mean + tf.exp(0.5 * z_log_var) * epsilon
@keras.saving.register_keras_serializable()
class Encoder(layers.Layer):
"""Maps MNIST digits to a triplet (z_mean, z_log_var, z)."""
def __init__(self, latent_dim=32, intermediate_dim=64, name="encoder", **kwargs):
super().__init__(name=name, **kwargs)
self.dense_proj = layers.Dense(intermediate_dim, activation="relu")
self.dense_mean = layers.Dense(latent_dim)
self.dense_log_var = layers.Dense(latent_dim)
self.sampling = Sampling()
def call(self, inputs):
x = self.dense_proj(inputs)
z_mean = self.dense_mean(x)
z_log_var = self.dense_log_var(x)
z = self.sampling((z_mean, z_log_var))
return z_mean, z_log_var, z
@keras.saving.register_keras_serializable()
class Decoder(layers.Layer):
"""Converts z, the encoded digit vector, back into a readable digit."""
def __init__(self, original_dim, intermediate_dim=64, name="decoder", **kwargs):
super().__init__(name=name, **kwargs)
self.dense_proj = layers.Dense(intermediate_dim, activation="relu")
self.dense_output = layers.Dense(original_dim, activation="sigmoid")
def call(self, inputs):
x = self.dense_proj(inputs)
return self.dense_output(x)
@keras.saving.register_keras_serializable()
class VariationalAutoEncoder(keras.Model):
"""Combines the encoder and decoder into an end-to-end model for training."""
def __init__(
self,
original_dim,
intermediate_dim=64,
latent_dim=32,
name="autoencoder",
**kwargs
):
super().__init__(name=name, **kwargs)
self.original_dim = original_dim
self.encoder = Encoder(latent_dim=latent_dim, intermediate_dim=intermediate_dim)
self.decoder = Decoder(original_dim, intermediate_dim=intermediate_dim)
def call(self, inputs):
z_mean, z_log_var, z = self.encoder(inputs)
reconstructed = self.decoder(z)
# Add KL divergence regularization loss.
kl_loss = -0.5 * tf.reduce_mean(
z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1
)
self.add_loss(kl_loss)
return reconstructed
MNIST에 간단한 교육 루프를 작성해 봅시다 :
\
original_dim = 784
vae = VariationalAutoEncoder(original_dim, 64, 32)
optimizer = keras.optimizers.Adam(learning_rate=1e-3)
mse_loss_fn = keras.losses.MeanSquaredError()
loss_metric = keras.metrics.Mean()
(x_train, _), _ = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
train_dataset = tf.data.Dataset.from_tensor_slices(x_train)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
epochs = 2
# Iterate over epochs.
for epoch in range(epochs):
print("Start of epoch %d" % (epoch,))
# Iterate over the batches of the dataset.
for step, x_batch_train in enumerate(train_dataset):
with tf.GradientTape() as tape:
reconstructed = vae(x_batch_train)
# Compute reconstruction loss
loss = mse_loss_fn(x_batch_train, reconstructed)
loss += sum(vae.losses) # Add KLD regularization loss
grads = tape.gradient(loss, vae.trainable_weights)
optimizer.apply_gradients(zip(grads, vae.trainable_weights))
loss_metric(loss)
if step % 100 == 0:
print("step %d: mean loss = %.4f" % (step, loss_metric.result()))
\
Start of epoch 0
WARNING:tensorflow:5 out of the last 5 calls to <function _BaseOptimizer._update_step_xla at 0x7fb220066af0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to and for more details.
WARNING:tensorflow:6 out of the last 6 calls to <function _BaseOptimizer._update_step_xla at 0x7fb220066af0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to and for more details.
step 0: mean loss = 0.3433
step 100: mean loss = 0.1257
step 200: mean loss = 0.0994
step 300: mean loss = 0.0893
step 400: mean loss = 0.0844
step 500: mean loss = 0.0810
step 600: mean loss = 0.0788
step 700: mean loss = 0.0772
step 800: mean loss = 0.0760
step 900: mean loss = 0.0750
Start of epoch 1
step 0: mean loss = 0.0747
step 100: mean loss = 0.0741
step 200: mean loss = 0.0736
step 300: mean loss = 0.0731
step 400: mean loss = 0.0727
step 500: mean loss = 0.0723
step 600: mean loss = 0.0720
step 700: mean loss = 0.0717
step 800: mean loss = 0.0715
step 900: mean loss = 0.0712
VAE가 서브 클래싱이기 때문에 주목하십시오Model
내장 교육 루프가 있습니다. 그래서 당신은 다음과 같이 훈련했을 수도 있습니다.
\
vae = VariationalAutoEncoder(784, 64, 32)
optimizer = keras.optimizers.Adam(learning_rate=1e-3)
vae.compile(optimizer, loss=keras.losses.MeanSquaredError())
vae.fit(x_train, x_train, epochs=2, batch_size=64)
\
Epoch 1/2
938/938 [==============================] - 4s 3ms/step - loss: 0.0746
Epoch 2/2
938/938 [==============================] - 3s 3ms/step - loss: 0.0676
<keras.src.callbacks.History at 0x7fb1e0533580>
\ \
::: 정보
원래 게시텐서 플로웹 사이트,이 기사는 새 헤드 라인 아래에 표시되며 CC에 따라 4.0으로 라이센스가 부여됩니다. Apache 2.0 라이센스에 따라 공유 된 코드 샘플.
:::
\
Post Comment