Innehållsöversikt
- Setup
- Introduktion
- Utbildning, utvärdering och inferens
- Spara och serialisera
- Använd samma diagram av lager för att definiera flera modeller
- Alla modeller en callable, precis som lager
- Manipulera komplexa graftopologier
- Modeller med flera ingångar och utgångar
- En leksaksmodell ResNet
Setup
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras import layers
Introduktion
och KerasFunktionell APIär ett sätt att skapa modeller som är mer flexibla änkeras.Sequential
API. The functional API can handle models with non-linear topology, shared layers, and even multiple inputs or outputs.
Huvudidéen är att en djup inlärningsmodell vanligtvis är en riktad acyklisk graf (DAG) av lager.Grafer av Layers.
Tänk på följande modell:
[Dense (64 enheter, relu aktivering)] [Dense (64 enheter, relu aktivering)] [Dense (10 enheter, softmax aktivering)] (utgång: logiter av en sannolikhetsfördelning över 10 klasser)
Detta är ett grundläggande diagram med tre lager. För att bygga den här modellen med hjälp av det funktionella API-programmet, börja med att skapa en input-nod:
inputs = keras.Input(shape=(784,))
Formen på data är inställd som en 784-dimensionell vektor. Batchstorleken utelämnas alltid eftersom endast formen på varje prov anges.
Om du till exempel har en bildinmatning med en form av(32, 32, 3)
Du skulle använda:
# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))
deninputs
som returneras innehåller information om formen ochdtype
av de inmatningsdata som du matar till din modell. Här är formen:
inputs.shape
TensorShape([None, 784])
Här är dtypen:
inputs.dtype
tf.float32
Du skapar en ny nod i diagrammet av lager genom att ringa ett lager på dettainputs
och objekt:
dense = layers.Dense(64, activation="relu")
x = dense(inputs)
"Layer call" -åtgärden är som att dra en pil från "inmatningar" till det här lagret du har skapat.dense
layer, and you get x
Som med output.
Låt oss lägga till några fler lager i diagrammet av lager:
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)
Vid denna tidpunkt kan du skapa enModel
genom att specificera dess ingångar och utgångar i diagrammet av lager:
model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")
Låt oss kolla hur modellöversikten ser ut:
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)
_________________________________________________________________
Du kan också planera modellen som ett diagram:
keras.utils.plot_model(model, "my_first_model.png")
Och, alternativt, visa ingångs- och utgångsformerna för varje lager i det plottade diagrammet:
keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)
Denna siffra och koden är nästan identiska.I kodversionen ersätts anslutnings pilarna med samtalshandlingen.
En "graf av lager" är en intuitiv mental bild för en djup inlärningsmodell, och den funktionella API är ett sätt att skapa modeller som speglar detta.
Utbildning, utvärdering och inferens
Utbildning, utvärdering och inferens fungerar exakt på samma sätt för modeller som är byggda med den funktionella API:n som förSequential
och modeller.
denModel
klass erbjuder en inbyggd tränings loop (denfit()
metod) och en inbyggd utvärderings loop (denevaluate()
Observera att du enkelt kanAnpassa dessa looparatt genomföra träningsrutiner utöver övervakad inlärning (t.ex.Ganskaoch )
Här, ladda MNIST bilddata, omforma den i vektorer, passa modellen på data (under övervakning av prestanda på en validering split), sedan utvärdera modellen på testdata:
(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
För vidare läsning, seUtbildning och utvärderingoch guide.
Spara och serialisera
Spara modellen och serialisering fungerar på samma sätt för modeller byggda med den funktionella API som de gör förSequential
Standardmetoden för att spara en funktionell modell är att ringamodel.save()
Du kan senare återskapa samma modell från den här filen, även om koden som byggde modellen inte längre är tillgänglig.
Den här sparade filen innehåller:
- Modellarkitektur
- modellviktvärden (som lärt sig under träningen)
- modellutbildning config, om någon (som passerat att kompilera)
- optimerare och dess tillstånd, om något (för att starta om träningen där du slutade)
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")
För detaljer, läs modellenSerialisering och sparandeoch guide.
Använd samma diagram av lager för att definiera flera modeller
I det funktionella API skapas modeller genom att ange deras ingångar och utgångar i ett diagram med lager, vilket innebär att ett enda diagram med lager kan användas för att generera flera modeller.
I exemplet nedan använder du samma stack av lager för att instantiera två modeller: enencoder
model that turns image inputs into 16-dimensional vectors, and an end-to-end autoencoder
Modell för träning.
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)
_________________________________________________________________
Här är avkodningsarkitekturen strikt symmetrisk till kodningsarkitekturen, så utmatningsformen är densamma som inmatningsformen.(28, 28, 1)
.
Det omvända av aConv2D
Layer är enConv2DTranspose
A och omvänt av AMaxPooling2D
Layer är enUpSampling2D
och layer.
Alla modeller är kallbara, precis som lager
Du kan behandla varje modell som om det var ett lager genom att åberopa den på enInput
Genom att ringa en modell återanvänder du inte bara modellens arkitektur, du återanvänder också dess vikter.
För att se detta i handling, här är ett annat exempel på autoencoder som skapar en kodningsmodell, en decodermodell och kedjar dem i två samtal för att få autoencodermodellen:
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)
_________________________________________________________________
Som du kan se kan modellen förankras: en modell kan innehålla undermodeller (eftersom en modell är precis som ett lager).sammanställningTill exempel, här är hur man sammanställer en uppsättning modeller till en enda modell som genomsnitt deras förutsägelser:
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)
Manipulera komplexa graftopologier
Modeller med flera ingångar och utgångar
Den funktionella API:n gör det enkelt att manipulera flera ingångar och utgångar.Sequential
och eld.
Om du till exempel bygger ett system för att rangordna kundutgivningsbiljetter efter prioritet och omdirigera dem till rätt avdelning, kommer modellen att ha tre ingångar:
- titeln på biljetten (textinmatning)
- biljettens textkropp (textinmatning), och
- eventuella taggar som lagts till av användaren (kategorisk inmatning)
Denna modell kommer att ha två utgångar:
- Prioritetsresultatet är mellan 0 och 1 (scalar sigmoid output), och
- den avdelning som ska hantera biljetten (softmax-utgång över uppsättningen av avdelningar).
Du kan bygga den här modellen i några rader med det funktionella API:
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],
)
Nu är det dags för modellen:
keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
När du sammanställer denna modell kan du tilldela olika förluster till varje utgång. Du kan till och med tilldela olika vikter till varje förlust - för att modulera deras bidrag till den totala träningsförlusten.
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],
)
Eftersom utmatningsskikten har olika namn kan du också ange förluster och förlustvikter med motsvarande skiktnamn:
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},
)
Träna modellen genom att passera listor med NumPy-sorter av inmatningar och mål:
# 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>
När man kallar fit med enDataset
objekt, bör det ge antingen en tuple av listor som([title_data, body_data, tags_data], [priority_targets, dept_targets])
eller en tuple av ordböcker som({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
.
För mer detaljerad förklaring hänvisar vi tillUtbildning och utvärderingoch guide.
En leksaksmodell ResNet
Förutom modeller med flera ingångar och utgångar, gör den funktionella API det enkelt att manipulera icke-lineära konnektivitet topologier - dessa är modeller med lager som inte är anslutna sekventiellt, somSequential
Eld kan inte hantera.
Ett vanligt användningsfall för detta är resterande anslutningar. Låt oss bygga en ResNet-modell för CIFAR10 för att visa detta:
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)
__________________________________________________________________________________________________
Plocka upp modellen:
keras.utils.plot_model(model, "mini_resnet.png", show_shapes=True)
Nu kör vi modellen:
(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>
Ursprungligen publicerad på TensorFlow-webbplatsen, visas den här artikeln här under en ny rubrik och är licensierad under CC BY 4.0.
Ursprungligen publicerad på den