Visão geral do conteúdo
- Camadas compartilhadas
- Extrair e reutilizar nós no gráfico de camadas
- Estender a API usando camadas personalizadas
- Quando usar a API funcional
- Forças funcionais da API
- Fraquezas funcionais da API
- Estilos de API de mistura e correspondência
Camadas compartilhadas
Outro bom uso para a API funcional são os modelos que usam camadas compartilhadas. Camadas compartilhadas são instâncias de camada que são reutilizadas várias vezes no mesmo modelo-eles aprendem recursos que correspondem a vários caminhos no gráfico de camadas.
Camadas compartilhadas são frequentemente usadas para codificar entradas de espaços semelhantes (digamos, duas peças de texto diferentes que apresentam vocabulário semelhante). Eles permitem o compartilhamento de informações nessas diferentes entradas e possibilitam treinar esse modelo com menos dados. Se uma determinada palavra for vista em uma das entradas, isso beneficiará o processamento de todas as entradas que passam pela camada compartilhada.
Para compartilhar uma camada na API funcional, chame a mesma instância de camada várias vezes. Por exemplo, aqui está um Embedding
Camada compartilhada em duas entradas de texto diferentes:
# Embedding for 1000 unique words mapped to 128-dimensional vectors
shared_embedding = layers.Embedding(1000, 128)
# Variable-length sequence of integers
text_input_a = keras.Input(shape=(None,), dtype="int32")
# Variable-length sequence of integers
text_input_b = keras.Input(shape=(None,), dtype="int32")
# Reuse the same layer to encode both inputs
encoded_input_a = shared_embedding(text_input_a)
encoded_input_b = shared_embedding(text_input_b)
Extrair e reutilizar nós no gráfico de camadas
Como o gráfico de camadas que você está manipulando é uma estrutura de dados estática, ele pode ser acessado e inspecionado. E é assim que você consegue plotar modelos funcionais como imagens.
Isso também significa que você pode acessar as ativações de camadas intermediárias (“nós” no gráfico) e reutilizá -las em outros lugares – o que é muito útil para algo como extração de recursos.
Vejamos um exemplo. Este é um modelo VGG19 com pesos pré -criados no ImageNet:
vgg19 = keras.applications.VGG19()
Downloading data from
574710816/574710816 [==============================] - 4s 0us/step
E essas são as ativações intermediárias do modelo, obtidas consultando a estrutura de dados do gráfico:
features_list = [layer.output for layer in vgg19.layers]
Use esses recursos para criar um novo modelo de extração de recursos que retorne os valores das ativações intermediárias da camada:
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
img = np.random.random((1, 224, 224, 3)).astype("float32")
extracted_features = feat_extraction_model(img)
Isso é útil para tarefas como transferência de estilo neural, entre outras coisas.
Estender a API usando camadas personalizadas
keras
Inclui uma ampla gama de camadas embutidas, por exemplo:
- Camadas convolucionais:
Conv1D
Assim,Conv2D
Assim,Conv3D
Assim,Conv2DTranspose
- Camadas de agrupamento:
MaxPooling1D
Assim,MaxPooling2D
Assim,MaxPooling3D
Assim,AveragePooling1D
- Camadas RNN:
GRU
Assim,LSTM
Assim,ConvLSTM2D
BatchNormalization
Assim,Dropout
Assim,Embedding
etc.
Mas se você não encontrar o que precisa, é fácil estender a API criando suas próprias camadas. Todas as camadas subclassem o Layer
classe e implemento:
call
Método, que especifica o cálculo feito pela camada.build
método, que cria os pesos da camada (esta é apenas uma convenção de estilo, pois você pode criar pesos em__init__
também).
Para saber mais sobre como criar camadas do zero, leia as camadas e os modelos personalizados.
A seguir é uma implementação básica de keras.layers.Dense
:
class CustomDense(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
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)
model = keras.Model(inputs, outputs)
Para suporte de serialização em sua camada personalizada, defina um get_config
Método que retorna os argumentos do construtor da instância da camada:
@keras.saving.register_keras_serializable()
class CustomDense(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}
inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)
model = keras.Model(inputs, outputs)
config = model.get_config()
new_model = keras.Model.from_config(config)
Opcionalmente, implemente o método de classe from_config(cls, config)
que é usado ao recriar uma instância de camada, dado seu dicionário de configuração. A implementação padrão de from_config
é:
def from_config(cls, config):
return cls(**config)
Quando usar a API funcional
Se você usar a API funcional do Keras para criar um novo modelo ou apenas subclasse o Model
classe diretamente? Em geral, a API funcional é de nível superior, mais fácil e segura e possui vários recursos que os modelos subclassem não suportam.
No entanto, a subclasse de modelo fornece maior flexibilidade ao criar modelos que não são facilmente expressíveis como gráficos acíclicos de camadas acíclicas. Por exemplo, você não poderia implementar uma árvore com a API funcional e teria que subclasse Model
diretamente.
Para uma análise aprofundada das diferenças entre a API funcional e a subclasse de modelo, leia o que são APIs simbólicas e imperativas no tensorflow 2.0?.
Forças funcionais da API:
As propriedades a seguir também são verdadeiras para modelos seqüenciais (que também são estruturas de dados), mas não são verdadeiros para modelos subclassificados (que são bytecode Python, não estruturas de dados).
Menos detalhado
Não há super().__init__(...)
não def call(self, ...):
etc.
Comparar:
inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
Com a versão subclassem:
class MLP(keras.Model):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.dense_1 = layers.Dense(64, activation='relu')
self.dense_2 = layers.Dense(10)
def call(self, inputs):
x = self.dense_1(inputs)
return self.dense_2(x)
# Instantiate the model.
mlp = MLP()
# Necessary to create the model's state.
# The model doesn't have a state until it's called at least once.
_ = mlp(tf.zeros((1, 32)))
Modelo Validação ao definir seu gráfico de conectividade
Na API funcional, a especificação de entrada (Shape e Dtype) é criada com antecedência (usando Input
). Toda vez que você chama uma camada, a camada verifica se a especificação passada corresponde a suas suposições e levantará uma mensagem de erro útil, se não.
Isso garante que qualquer modelo que você possa criar com a API funcional será executado. Toda a depuração-exceto a depuração relacionada à convergência-acontece estaticamente durante a construção do modelo e não no tempo de execução. Isso é semelhante à verificação do tipo em um compilador.
Um modelo funcional é plotável e inspecável
Você pode plotar o modelo como um gráfico e pode acessar facilmente nós intermediários neste gráfico. Por exemplo, para extrair e reutilizar as ativações de camadas intermediárias (como visto em um exemplo anterior):
features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
Um modelo funcional pode ser serializado ou clonado
Como um modelo funcional é uma estrutura de dados e não um código, ele é serializável com segurança e pode ser salvo como um único arquivo que permite recriar exatamente o mesmo modelo sem ter acesso a nenhum código original. Veja o guia de serialização e salvamento.
Para serializar um modelo subclassificado, é necessário para o implementador especificar um get_config()
e from_config()
método no nível do modelo.
Fraqueza funcional da API:
Não suporta arquiteturas dinâmicas
A API funcional trata os modelos como DAGs de camadas. Isso é verdade para a maioria das arquiteturas de aprendizado profundo, mas não todas – por exemplo, redes ou RNNs de árvores recursivas não seguem essa suposição e não podem ser implementadas na API funcional.
Estilos de API de mistura e correspondência
Escolher entre a API funcional ou a subclasse de modelo não é uma decisão binária que o restringe a uma categoria de modelos. Todos os modelos no keras
API pode interagir entre si, seja eles Sequential
Modelos, modelos funcionais ou modelos subclassificados que são escritos do zero.
Você sempre pode usar um modelo funcional ou Sequential
Modelo como parte de um modelo ou camada subclassificada:
units = 32
timesteps = 10
input_dim = 5
# Define a Functional model
inputs = keras.Input((None, units))
x = layers.GlobalAveragePooling1D()(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
@keras.saving.register_keras_serializable()
class CustomRNN(layers.Layer):
def __init__(self):
super().__init__()
self.units = units
self.projection_1 = layers.Dense(units=units, activation="tanh")
self.projection_2 = layers.Dense(units=units, activation="tanh")
# Our previously-defined Functional model
self.classifier = model
def call(self, inputs):
outputs = []
state = tf.zeros(shape=(inputs.shape[0], self.units))
for t in range(inputs.shape[1]):
x = inputs[:, t, :]
h = self.projection_1(x)
y = h + self.projection_2(state)
state = y
outputs.append(y)
features = tf.stack(outputs, axis=1)
print(features.shape)
return self.classifier(features)
rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, timesteps, input_dim)))
(1, 10, 32)
Você pode usar qualquer camada ou modelo subclassificado na API funcional, desde que implemente um call
Método que segue um dos seguintes padrões:
call(self, inputs, **kwargs)
— Ondeinputs
é um tensor ou uma estrutura aninhada de tensores (por exemplo, uma lista de tensores) e onde**kwargs
são argumentos sem tensor (não inputas).call(self, inputs, training=None, **kwargs)
— Ondetraining
é um booleano indicando se a camada deve se comportar no modo de treinamento e no modo de inferência.call(self, inputs, mask=None, **kwargs)
— Ondemask
é um tensor de máscara booleana (útil para RNNs, por exemplo).call(self, inputs, training=None, mask=None, **kwargs)
-Obviamente, você pode ter comportamento específico de mascaramento e treinamento ao mesmo tempo.
Além disso, se você implementar o get_config
Método em sua camada ou modelo personalizado, os modelos funcionais que você criar ainda serão serializáveis e clonáveis.
Aqui está um exemplo rápido de um RNN personalizado, escrito do zero, sendo usado em um modelo funcional:
units = 32
timesteps = 10
input_dim = 5
batch_size = 16
@keras.saving.register_keras_serializable()
class CustomRNN(layers.Layer):
def __init__(self):
super().__init__()
self.units = units
self.projection_1 = layers.Dense(units=units, activation="tanh")
self.projection_2 = layers.Dense(units=units, activation="tanh")
self.classifier = layers.Dense(1)
def call(self, inputs):
outputs = []
state = tf.zeros(shape=(inputs.shape[0], self.units))
for t in range(inputs.shape[1]):
x = inputs[:, t, :]
h = self.projection_1(x)
y = h + self.projection_2(state)
state = y
outputs.append(y)
features = tf.stack(outputs, axis=1)
return self.classifier(features)
# Note that you specify a static batch size for the inputs with the `batch_shape`
# arg, because the inner computation of `CustomRNN` requires a static batch size
# (when you create the `state` zeros tensor).
inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim))
x = layers.Conv1D(32, 3)(inputs)
outputs = CustomRNN()(x)
model = keras.Model(inputs, outputs)
rnn_model = CustomRNN()
_ = rnn_model(tf.zeros((1, 10, 5)))
Publicado originalmente no Tensorflow Site, este artigo aparece aqui sob uma nova manchete e é licenciado no CC por 4.0. Amostras de código compartilhadas sob a licença Apache 2.0.