Contido da visión xeral
- Instalacións
- Introdución
- Formación, avaliación e inferencia
- Gardar e serializar
- Use o mesmo gráfico de capas para definir varios modelos
- Todos os modelos un callable, así como as capas
- Manipulación de topoloxías gráficas complexas
- Modelos con múltiples entradas e saídas
- Xoguete ResNet Modelo
Instalacións
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras import layers
Introdución
das KerasFuncións de APIé unha forma de crear modelos que sexan máis flexibles que okeras.Sequential
A API funcional pode manexar modelos con topoloxía non linear, capas compartidas e mesmo múltiples entradas ou saídas.
A idea principal é que un modelo de aprendizaxe profunda é xeralmente un gráfico acíclico dirixido (DAG) de capas.Gráficos de Layers.
Considere o seguinte modelo:
[Dense (64 unidades, activación relu)] [Dense (64 unidades, activación relu)] [Dense (10 unidades, activación softmax)] (saída: logits dunha distribución de probabilidade sobre 10 clases)
Este é un gráfico básico con tres capas. Para construír este modelo usando a API funcional, comeza creando un nodo de entrada:
inputs = keras.Input(shape=(784,))
O tamaño do lote sempre se omite xa que só se especifica a forma de cada mostra.
Se, por exemplo, ten unha entrada de imaxe cunha forma de(32, 32, 3)
E vostede usaría:
# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))
A súainputs
que se devolve contén información sobre a forma edtype
dos datos de entrada que alimenta ao seu modelo. Aquí está a forma:
inputs.shape
TensorShape([None, 784])
Esta é a dType:
inputs.dtype
tf.float32
Crea un novo nodo no gráfico de capas chamando unha capa nesteinputs
O obxecto:
dense = layers.Dense(64, activation="relu")
x = dense(inputs)
A acción de "chamada de capa" é como debuxar unha frecha desde "inputs" a esta capa que creou.dense
Lara, e que teñax
como a produción.
Engadimos algunhas capas máis ao gráfico de capas:
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)
Neste caso, pódese crear unhaModel
especificando as súas entradas e saídas no gráfico de capas:
model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")
Vexamos como é o resumo do modelo:
model.summary()
Model: "mnist_model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 784)] 0
dense (Dense) (None, 64) 50240
dense_1 (Dense) (None, 64) 4160
dense_2 (Dense) (None, 10) 650
=================================================================
Total params: 55050 (215.04 KB)
Trainable params: 55050 (215.04 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Tamén podes poñer o modelo como un gráfico:
keras.utils.plot_model(model, "my_first_model.png")
E, opcionalmente, amosar as formas de entrada e saída de cada capa no gráfico trazado:
keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)
Esta figura e o código son case idénticos.Na versión do código, as frechas de conexión son substituídas pola operación de chamada.
Un "gráfico de capas" é unha imaxe mental intuitiva para un modelo de aprendizaxe profunda, e a API funcional é unha forma de crear modelos que reflicten isto de cerca.
Formación, avaliación e inferencia
O adestramento, a avaliación e a inferencia funcionan exactamente do mesmo xeito para os modelos construídos utilizando a API funcional.Sequential
Os modelos.
A súaModel
A clase ofrece un circuíto de formación integrado (ofit()
metodoloxía) e un loop de avaliación integrado (oevaluate()
Observa que podes facelo con facilidadeCoñece estes loopspara implementar rutinas de formación máis aló da aprendizaxe supervisada (por exemplo,gansos) da
Aquí, cargar os datos de imaxe MNIST, reformulalo en vectores, encaixar o modelo nos datos (mentres monitorear o rendemento nunha división de validación), a continuación, avaliar o modelo nos datos de proba:
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255
model.compile(
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer=keras.optimizers.RMSprop(),
metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
history = model.fit(x_train, y_train, batch_size=64, epochs=2, validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=2)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])
Epoch 1/2
750/750 [==============================] - 4s 3ms/step - loss: 0.3556 - sparse_categorical_accuracy: 0.8971 - val_loss: 0.1962 - val_sparse_categorical_accuracy: 0.9422
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1612 - sparse_categorical_accuracy: 0.9527 - val_loss: 0.1461 - val_sparse_categorical_accuracy: 0.9592
313/313 - 0s - loss: 0.1492 - sparse_categorical_accuracy: 0.9556 - 463ms/epoch - 1ms/step
Test loss: 0.14915992319583893
Test accuracy: 0.9556000232696533
Para máis información, ver oFormación e avaliaciónUnha guía.
Gardar e serializar
O aforro do modelo e a serialización funcionan do mesmo xeito para os modelos construídos usando a API funcional que para eles.Sequential
O xeito estándar de gardar un modelo funcional é chamarmodel.save()
Pode posteriormente recrear o mesmo modelo a partir deste ficheiro, aínda que o código que construíu o modelo xa non estea dispoñible.
Este ficheiro gardado inclúe:
- Modelo Arquitectura
- valores de peso modelo (que foron aprendidos durante o adestramento)
- modelo de formación config, se hai (como pasou a compilar)
- optimizador e o seu estado, se hai (para reiniciar o adestramento onde deixaches de adestrar)
model.save("path_to_my_model.keras")
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model("path_to_my_model.keras")
Para máis información, lea o modeloSerialización e aforroUnha guía.
Use o mesmo gráfico de capas para definir varios modelos
Na API funcional, os modelos créanse especificando as súas entradas e saídas nun gráfico de capas, o que significa que un único gráfico de capas pode ser usado para xerar múltiples modelos.
No exemplo seguinte, usa a mesma pila de capas para instantizar dous modelos: unencoder
modelo que converte as entradas de imaxe en vectores de 16 dimensións, e unautoencoder
Modelo de adestramento.
encoder_input = keras.Input(shape=(28, 28, 1), name="img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
encoder = keras.Model(encoder_input, encoder_output, name="encoder")
encoder.summary()
x = layers.Reshape((4, 4, 1))(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
x = layers.Conv2DTranspose(32, 3, activation="relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu")(x)
autoencoder = keras.Model(encoder_input, decoder_output, name="autoencoder")
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
img (InputLayer) [(None, 28, 28, 1)] 0
conv2d (Conv2D) (None, 26, 26, 16) 160
conv2d_1 (Conv2D) (None, 24, 24, 32) 4640
max_pooling2d (MaxPooling2 (None, 8, 8, 32) 0
D)
conv2d_2 (Conv2D) (None, 6, 6, 32) 9248
conv2d_3 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d (Glob (None, 16) 0
alMaxPooling2D)
=================================================================
Total params: 18672 (72.94 KB)
Trainable params: 18672 (72.94 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
img (InputLayer) [(None, 28, 28, 1)] 0
conv2d (Conv2D) (None, 26, 26, 16) 160
conv2d_1 (Conv2D) (None, 24, 24, 32) 4640
max_pooling2d (MaxPooling2 (None, 8, 8, 32) 0
D)
conv2d_2 (Conv2D) (None, 6, 6, 32) 9248
conv2d_3 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d (Glob (None, 16) 0
alMaxPooling2D)
reshape (Reshape) (None, 4, 4, 1) 0
conv2d_transpose (Conv2DTr (None, 6, 6, 16) 160
anspose)
conv2d_transpose_1 (Conv2D (None, 8, 8, 32) 4640
Transpose)
up_sampling2d (UpSampling2 (None, 24, 24, 32) 0
D)
conv2d_transpose_2 (Conv2D (None, 26, 26, 16) 4624
Transpose)
conv2d_transpose_3 (Conv2D (None, 28, 28, 1) 145
Transpose)
=================================================================
Total params: 28241 (110.32 KB)
Trainable params: 28241 (110.32 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Aquí, a arquitectura de decodificación é estritamente simétrica á arquitectura de codificación, polo que a forma de saída é a mesma que a forma de entrada.(28, 28, 1)
.
O inverso dunhaConv2D
Laxe é unhaConv2DTranspose
layer, and the reverse of a MaxPooling2D
Layer é unhaUpSampling2D
de Layer.
Todos os modelos son chamables, como as capas
Podes tratar calquera modelo como se fose unha capa invocándoo nunhaInput
Ao chamar un modelo non está só reutilizando a arquitectura do modelo, está tamén reutilizando os seus pesos.
To see this in action, here's a different take on the autoencoder example that creates an encoder model, a decoder model, and chains them in two calls to obtain the autoencoder model:
encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")
x = layers.Conv2D(16, 3, activation="relu")(encoder_input)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.Conv2D(16, 3, activation="relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
encoder = keras.Model(encoder_input, encoder_output, name="encoder")
encoder.summary()
decoder_input = keras.Input(shape=(16,), name="encoded_img")
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
x = layers.Conv2DTranspose(32, 3, activation="relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu")(x)
decoder = keras.Model(decoder_input, decoder_output, name="decoder")
decoder.summary()
autoencoder_input = keras.Input(shape=(28, 28, 1), name="img")
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name="autoencoder")
autoencoder.summary()
Model: "encoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
original_img (InputLayer) [(None, 28, 28, 1)] 0
conv2d_4 (Conv2D) (None, 26, 26, 16) 160
conv2d_5 (Conv2D) (None, 24, 24, 32) 4640
max_pooling2d_1 (MaxPoolin (None, 8, 8, 32) 0
g2D)
conv2d_6 (Conv2D) (None, 6, 6, 32) 9248
conv2d_7 (Conv2D) (None, 4, 4, 16) 4624
global_max_pooling2d_1 (Gl (None, 16) 0
obalMaxPooling2D)
=================================================================
Total params: 18672 (72.94 KB)
Trainable params: 18672 (72.94 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Model: "decoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
encoded_img (InputLayer) [(None, 16)] 0
reshape_1 (Reshape) (None, 4, 4, 1) 0
conv2d_transpose_4 (Conv2D (None, 6, 6, 16) 160
Transpose)
conv2d_transpose_5 (Conv2D (None, 8, 8, 32) 4640
Transpose)
up_sampling2d_1 (UpSamplin (None, 24, 24, 32) 0
g2D)
conv2d_transpose_6 (Conv2D (None, 26, 26, 16) 4624
Transpose)
conv2d_transpose_7 (Conv2D (None, 28, 28, 1) 145
Transpose)
=================================================================
Total params: 9569 (37.38 KB)
Trainable params: 9569 (37.38 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Model: "autoencoder"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
img (InputLayer) [(None, 28, 28, 1)] 0
encoder (Functional) (None, 16) 18672
decoder (Functional) (None, 28, 28, 1) 9569
=================================================================
Total params: 28241 (110.32 KB)
Trainable params: 28241 (110.32 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
Como podes ver, o modelo pode ser anexado: un modelo pode conter submodelos (xa que un modelo é como unha capa).EnsamblaxePor exemplo, aquí está como unir un conxunto de modelos nun único modelo que media as súas previsións:
def get_model():
inputs = keras.Input(shape=(128,))
outputs = layers.Dense(1)(inputs)
return keras.Model(inputs, outputs)
model1 = get_model()
model2 = get_model()
model3 = get_model()
inputs = keras.Input(shape=(128,))
y1 = model1(inputs)
y2 = model2(inputs)
y3 = model3(inputs)
outputs = layers.average([y1, y2, y3])
ensemble_model = keras.Model(inputs=inputs, outputs=outputs)
Manipulación de topoloxías gráficas complexas
Modelos con múltiples entradas e saídas
A API funcional facilita a manipulación de múltiples entradas e saídas.Sequential
unha fogueira.
Por exemplo, se está a construír un sistema para clasificar os billetes de emisión de clientes por prioridade e dirixilos ao departamento correcto, entón o modelo terá tres entradas:
- o título do billete (entrada de texto),
- o corpo de texto do billete (entrada de texto), e
- Calquera etiqueta engadida polo usuario (entrada de categorías)
Este modelo terá dúas saídas:
- a puntuación de prioridade entre 0 e 1 (output sigmoide escalar), e
- o departamento que debe xestionar o billete (output softmax sobre o conxunto de departamentos).
Pode construír este modelo en poucas liñas coa API funcional:
num_tags = 12 # Number of unique issue tags
num_words = 10000 # Size of vocabulary obtained when preprocessing text data
num_departments = 4 # Number of departments for predictions
title_input = keras.Input(
shape=(None,), name="title"
) # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name="body") # Variable-length sequence of ints
tags_input = keras.Input(
shape=(num_tags,), name="tags"
) # Binary vectors of size `num_tags`
# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)
# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)
# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])
# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, name="priority")(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, name="department")(x)
# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(
inputs=[title_input, body_input, tags_input],
outputs=[priority_pred, department_pred],
)
Agora poñemos o modelo:
keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
Ao compilar este modelo, pode asignar diferentes perdas a cada saída. Pode ata asignar diferentes pesos a cada perda - para modular a súa contribución á perda total de adestramento.
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=[
keras.losses.BinaryCrossentropy(from_logits=True),
keras.losses.CategoricalCrossentropy(from_logits=True),
],
loss_weights=[1.0, 0.2],
)
Dado que as capas de saída teñen diferentes nomes, tamén podes especificar os pesos de perdas e perdas cos nomes de capas correspondentes:
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={
"priority": keras.losses.BinaryCrossentropy(from_logits=True),
"department": keras.losses.CategoricalCrossentropy(from_logits=True),
},
loss_weights={"priority": 1.0, "department": 0.2},
)
Adestrar o modelo ao pasar listas de matrizes NumPy de entradas e obxectivos:
# Dummy input data
title_data = np.random.randint(num_words, size=(1280, 10))
body_data = np.random.randint(num_words, size=(1280, 100))
tags_data = np.random.randint(2, size=(1280, num_tags)).astype("float32")
# Dummy target data
priority_targets = np.random.random(size=(1280, 1))
dept_targets = np.random.randint(2, size=(1280, num_departments))
model.fit(
{"title": title_data, "body": body_data, "tags": tags_data},
{"priority": priority_targets, "department": dept_targets},
epochs=2,
batch_size=32,
)
Epoch 1/2
40/40 [==============================] - 8s 112ms/step - loss: 1.2982 - priority_loss: 0.6991 - department_loss: 2.9958
Epoch 2/2
40/40 [==============================] - 3s 64ms/step - loss: 1.3110 - priority_loss: 0.6977 - department_loss: 3.0666
<keras.src.callbacks.History at 0x7f08d51fab80>
Ao chamarse a unhaDataset
Obxecto, debería dar lugar a un tuple de listas como([title_data, body_data, tags_data], [priority_targets, dept_targets])
ou un tuple de dicionarios como({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
.
Para unha explicación máis detallada, consulte oFormación e avaliaciónUnha guía.
Xoguete ResNet Modelo
Ademais de modelos con múltiples entradas e saídas, a API funcional facilita a manipulación de topoloxías de conectividade non lineares - estes son modelos con capas que non están conectadas secuencialmente.Sequential
O lume non pode manexar.
A common use case for this is residual connections. Let's build a toy ResNet model for CIFAR10 to demonstrate this:
inputs = keras.Input(shape=(32, 32, 3), name="img")
x = layers.Conv2D(32, 3, activation="relu")(inputs)
x = layers.Conv2D(64, 3, activation="relu")(x)
block_1_output = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_1_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_2_output = layers.add([x, block_1_output])
x = layers.Conv2D(64, 3, activation="relu", padding="same")(block_2_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
block_3_output = layers.add([x, block_2_output])
x = layers.Conv2D(64, 3, activation="relu")(block_3_output)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation="relu")(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10)(x)
model = keras.Model(inputs, outputs, name="toy_resnet")
model.summary()
Model: "toy_resnet"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
img (InputLayer) [(None, 32, 32, 3)] 0 []
conv2d_8 (Conv2D) (None, 30, 30, 32) 896 ['img[0][0]']
conv2d_9 (Conv2D) (None, 28, 28, 64) 18496 ['conv2d_8[0][0]']
max_pooling2d_2 (MaxPoolin (None, 9, 9, 64) 0 ['conv2d_9[0][0]']
g2D)
conv2d_10 (Conv2D) (None, 9, 9, 64) 36928 ['max_pooling2d_2[0][0]']
conv2d_11 (Conv2D) (None, 9, 9, 64) 36928 ['conv2d_10[0][0]']
add (Add) (None, 9, 9, 64) 0 ['conv2d_11[0][0]',
'max_pooling2d_2[0][0]']
conv2d_12 (Conv2D) (None, 9, 9, 64) 36928 ['add[0][0]']
conv2d_13 (Conv2D) (None, 9, 9, 64) 36928 ['conv2d_12[0][0]']
add_1 (Add) (None, 9, 9, 64) 0 ['conv2d_13[0][0]',
'add[0][0]']
conv2d_14 (Conv2D) (None, 7, 7, 64) 36928 ['add_1[0][0]']
global_average_pooling2d ( (None, 64) 0 ['conv2d_14[0][0]']
GlobalAveragePooling2D)
dense_6 (Dense) (None, 256) 16640 ['global_average_pooling2d[0][
0]']
dropout (Dropout) (None, 256) 0 ['dense_6[0][0]']
dense_7 (Dense) (None, 10) 2570 ['dropout[0][0]']
==================================================================================================
Total params: 223242 (872.04 KB)
Trainable params: 223242 (872.04 KB)
Non-trainable params: 0 (0.00 Byte)
__________________________________________________________________________________________________
Describe o modelo:
keras.utils.plot_model(model, "mini_resnet.png", show_shapes=True)
Agora imos poñer o modelo:
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=keras.losses.CategoricalCrossentropy(from_logits=True),
metrics=["acc"],
)
# We restrict the data to the first 1000 samples so as to limit execution time
# on Colab. Try to train on the entire dataset until convergence!
model.fit(x_train[:1000], y_train[:1000], batch_size=64, epochs=1, validation_split=0.2)
13/13 [==============================] - 4s 39ms/step - loss: 2.3086 - acc: 0.0988 - val_loss: 2.3020 - val_acc: 0.0850
<keras.src.callbacks.History at 0x7f078810c880>
Publicado orixinalmente no sitio web de TensorFlow, este artigo aparece aquí baixo un novo título e está licenciado baixo CC BY 4.0.
Publicado orixinalmente no sitio web de TensorFlow, este artigo aparece aquí baixo un novo título e está licenciado baixo CC BY 4.0.
TensorFlow