Bu sayfa, Cloud Translation API ile çevrilmiştir.
Switch to English

Fonksiyonel API

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

Kurmak

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

Giriş

Keras işlevsel API'si , tf.keras.Sequential API'sinden daha esnek modeller oluşturmanın bir yoludur. İşlevsel API, doğrusal olmayan topolojiye, paylaşılan katmanlara ve hatta birden çok giriş veya çıkışa sahip modelleri işleyebilir.

Ana fikir, bir derin öğrenme modelinin genellikle katmanların yönlendirilmiş döngüsel olmayan grafiği (DAG) olmasıdır. Dolayısıyla işlevsel API, katman grafiklerini oluşturmanın bir yoludur.

Aşağıdaki modeli düşünün:

(input: 784-dimensional vectors)
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (10 units, softmax activation)]
       ↧
(output: logits of a probability distribution over 10 classes)

Bu, üç katmanlı temel bir grafiktir. Bu modeli işlevsel API kullanarak oluşturmak için bir giriş düğümü oluşturarak başlayın:

inputs = keras.Input(shape=(784,))

Verinin şekli 784 boyutlu bir vektör olarak belirlendi. Her numunenin yalnızca şekli belirlendiğinden parti boyutu her zaman ihmal edilir.

Örneğin, (32, 32, 3) şeklinde bir görüntü girişiniz varsa, şunu kullanırsınız:

# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))

Döndürülen inputs , modelinize beslediğiniz girdi verilerinin şekli ve dtype hakkında bilgi içerir. İşte şekil:

inputs.shape
TensorShape([None, 784])

İşte dtype:

inputs.dtype
tf.float32

Bu inputs nesnesinde bir katman çağırarak katman grafiğinde yeni bir düğüm oluşturursunuz:

dense = layers.Dense(64, activation="relu")
x = dense(inputs)

"Katman çağrısı" eylemi, oluşturduğunuz bu katmana "girdilerden" bir ok çizmeye benzer. Girdileri dense katmana "geçiriyorsunuz" ve çıktı olarak x elde ediyorsunuz.

Katman grafiğine birkaç katman daha ekleyelim:

x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)

Bu noktada, giriş ve çıkışlarını katman grafiğinde belirterek bir Model oluşturabilirsiniz:

model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")

Model özetinin neye benzediğine bakalım:

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: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________

Modeli grafik olarak da çizebilirsiniz:

keras.utils.plot_model(model, "my_first_model.png")

png

Ve isteğe bağlı olarak, çizilen grafikte her katmanın giriş ve çıkış şekillerini görüntüleyin:

keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)

png

Bu rakam ve kod hemen hemen aynı. Kod versiyonunda, bağlantı oklarının yerini arama işlemi almıştır.

Bir "katman grafiği", bir derin öğrenme modeli için sezgisel bir zihinsel görüntüdür ve işlevsel API, bunu yakından yansıtan modeller oluşturmanın bir yoludur.

Eğitim, değerlendirme ve çıkarım

Eğitim, değerlendirme ve çıkarım, Sequential modellerde olduğu gibi işlevsel API kullanılarak oluşturulan modeller için tam olarak aynı şekilde çalışır.

Burada, MNIST görüntü verilerini yükleyin, vektörler halinde yeniden şekillendirin, modeli verilere uydurun (bir doğrulama bölümünde performansı izlerken), ardından modeli test verileri üzerinde değerlendirin:

(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=["accuracy"],
)

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 [==============================] - 2s 2ms/step - loss: 0.3388 - accuracy: 0.9032 - val_loss: 0.1877 - val_accuracy: 0.9457
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1636 - accuracy: 0.9521 - val_loss: 0.1559 - val_accuracy: 0.9543
313/313 - 0s - loss: 0.1454 - accuracy: 0.9551
Test loss: 0.14535436034202576
Test accuracy: 0.9550999999046326

Daha fazla okuma için eğitim ve değerlendirme kılavuzuna bakın.

Kaydet ve serileştir

Modelin kaydedilmesi ve serileştirme, Sequential modellerde olduğu gibi işlevsel API kullanılarak oluşturulan modeller için de aynı şekilde çalışır. İşlevsel bir modeli kaydetmenin standart yolu, tüm modeli tek bir dosya olarak kaydetmek için model.save() yi çağırmaktır. Modeli oluşturan kod artık mevcut olmasa bile, aynı modeli daha sonra bu dosyadan yeniden oluşturabilirsiniz.

Kaydedilen bu dosya şunları içerir:

  • model mimarisi
  • model ağırlık değerleri (eğitim sırasında öğrenilenler)
  • varsa model eğitim yapılandırması ( compile geçildiği gibi)
  • optimizer ve varsa durumu (eğitimi kaldığınız yerden yeniden başlatmak için)
model.save("path_to_my_model")
del model
# Recreate the exact same model purely from the file:
model = keras.models.load_model("path_to_my_model")
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: path_to_my_model/assets

Ayrıntılar için model serileştirme ve kaydetme kılavuzunu okuyun.

Birden çok modeli tanımlamak için aynı katman grafiğini kullanın

İşlevsel API'de modeller, bir katman grafiğinde girdileri ve çıktıları belirtilerek oluşturulur. Bu, birden çok model oluşturmak için tek bir katman grafiğinin kullanılabileceği anlamına gelir.

Aşağıdaki örnekte, iki modeli somutlaştırmak için aynı katman yığınını kullanıyorsunuz: görüntü girişlerini 16 boyutlu vektörlere dönüştüren bir encoder modeli ve eğitim için uçtan uca bir autoencoder modeli.

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 (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
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 (MaxPooling2D) (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d (Global (None, 16)                0         
_________________________________________________________________
reshape (Reshape)            (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d (UpSampling2D) (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_3 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

Burada kod çözme mimarisi, kodlama mimarisine kesinlikle simetriktir, bu nedenle çıktı şekli giriş şekli ile aynıdır (28, 28, 1) .

Bir ters Conv2D tabakası a, Conv2DTranspose tabakası ve bir arka MaxPooling2D tabakası bir olduğunu UpSampling2D katmanı.

Katmanlar gibi tüm modeller çağrılabilir

Herhangi bir modeli bir Input veya başka bir katmanın çıktısında çağırarak bir katmanmış gibi ele alabilirsiniz. Bir modeli çağırarak, sadece modelin mimarisini değil, aynı zamanda ağırlıklarını da yeniden kullanmış olursunuz.

Bunu iş başında görmek için, bir kodlayıcı modeli, bir kod çözücü modeli oluşturan ve bunları otomatik kodlayıcı modelini elde etmek için iki çağrıda zincirleyen otomatik kodlayıcı örneğinin farklı bir yaklaşımını burada bulabilirsiniz:

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 (MaxPooling2 (None, 8, 8, 32)          0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 6, 6, 32)          9248      
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 4, 4, 16)          4624      
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 16)                0         
=================================================================
Total params: 18,672
Trainable params: 18,672
Non-trainable params: 0
_________________________________________________________________
Model: "decoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
encoded_img (InputLayer)     [(None, 16)]              0         
_________________________________________________________________
reshape_1 (Reshape)          (None, 4, 4, 1)           0         
_________________________________________________________________
conv2d_transpose_4 (Conv2DTr (None, 6, 6, 16)          160       
_________________________________________________________________
conv2d_transpose_5 (Conv2DTr (None, 8, 8, 32)          4640      
_________________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 24, 24, 32)        0         
_________________________________________________________________
conv2d_transpose_6 (Conv2DTr (None, 26, 26, 16)        4624      
_________________________________________________________________
conv2d_transpose_7 (Conv2DTr (None, 28, 28, 1)         145       
=================================================================
Total params: 9,569
Trainable params: 9,569
Non-trainable params: 0
_________________________________________________________________
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: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

Gördüğünüz gibi, model yuvalanabilir: bir model alt modeller içerebilir (çünkü bir model tıpkı bir katman gibidir). Model yerleştirme için yaygın bir kullanım durumu, bir araya getirmedir . Örneğin, bir dizi modeli, tahminlerinin ortalamasını alan tek bir modelde nasıl birleştireceğiniz aşağıda açıklanmıştır:

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)

Karmaşık grafik topolojilerini işleyin

Birden çok giriş ve çıkışa sahip modeller

İşlevsel API, birden çok giriş ve çıkışın değiştirilmesini kolaylaştırır. Bu, Sequential API ile ele alınamaz.

Örneğin, müşteri çıkış biletlerini önceliğe göre sıralamak ve bunları doğru departmana yönlendirmek için bir sistem oluşturuyorsanız, modelin üç girdisi olacaktır:

  • biletin başlığı (metin girişi),
  • biletin metin gövdesi (metin girişi) ve
  • kullanıcı tarafından eklenen herhangi bir etiket (kategorik girdi)

Bu modelin iki çıktısı olacaktır:

  • 0 ile 1 arasındaki öncelik puanı (skaler sigmoid çıktı) ve
  • bileti işlemesi gereken departman (departmanlar kümesi üzerinden softmax çıktı).

Bu modeli fonksiyonel API ile birkaç satırda oluşturabilirsiniz:

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],
)

Şimdi modeli çizin:

keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)

png

Bu modeli derlerken, her çıktıya farklı kayıplar atayabilirsiniz. Toplam eğitim kaybına katkılarını düzenlemek için her bir kayba farklı ağırlıklar bile atayabilirsiniz.

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],
)

Çıktı katmanlarının farklı adları olduğundan, kaybı şu şekilde de belirtebilirsiniz:

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=[1.0, 0.2],
)

NumPy giriş ve hedef dizilerinin listelerini ileterek modeli eğitin:

# 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 [==============================] - 0s 11ms/step - loss: 1.2819 - priority_loss: 0.7088 - department_loss: 2.8653
Epoch 2/2
40/40 [==============================] - 0s 10ms/step - loss: 1.2723 - priority_loss: 0.6992 - department_loss: 2.8655

<tensorflow.python.keras.callbacks.History at 0x7f57b19336d8>

Bir Dataset nesnesiyle fit ([title_data, body_data, tags_data], [priority_targets, dept_targets]) ya ([title_data, body_data, tags_data], [priority_targets, dept_targets]) gibi bir ([title_data, body_data, tags_data], [priority_targets, dept_targets]) demeti veya ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) .

Daha ayrıntılı açıklama için eğitim ve değerlendirme kılavuzuna bakın.

Bir oyuncak ResNet modeli

Birden fazla giriş ve çıkışa sahip modellere ek olarak, işlevsel API, doğrusal olmayan bağlantı topolojilerini yönetmeyi kolaylaştırır - bunlar, Sequential API'nin işleyemediği, Sequential bağlanmayan katmanlara sahip modellerdir.

Bunun için yaygın bir kullanım durumu, artık bağlantılardır. Bunu göstermek için CIFAR10 için bir oyuncak ResNet modeli oluşturalım:

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 (MaxPooling2D)  (None, 9, 9, 64)     0           conv2d_9[0][0]                   
__________________________________________________________________________________________________
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 (Globa (None, 64)           0           conv2d_14[0][0]                  
__________________________________________________________________________________________________
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: 223,242
Trainable params: 223,242
Non-trainable params: 0
__________________________________________________________________________________________________

Modeli çizin:

keras.utils.plot_model(model, "mini_resnet.png", show_shapes=True)

png

Şimdi modeli eğitin:

(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 [==============================] - 0s 21ms/step - loss: 2.3574 - acc: 0.1037 - val_loss: 2.3011 - val_acc: 0.1000

<tensorflow.python.keras.callbacks.History at 0x7f57b026cf98>

Paylaşılan katmanlar

İşlevsel API'nin bir başka iyi kullanımı, paylaşılan katmanları kullanan modellerdir. Paylaşılan katmanlar, aynı modelde birden çok kez yeniden kullanılan katman örnekleridir - katman grafiğindeki birden çok yola karşılık gelen özellikleri öğrenirler.

Paylaşılan katmanlar genellikle benzer alanlardan gelen girdileri kodlamak için kullanılır (örneğin, benzer kelime dağarcığı içeren iki farklı metin parçası). Bu farklı girdiler arasında bilgi paylaşımını sağlarlar ve böyle bir modeli daha az veriyle eğitmeyi mümkün kılarlar. Girişlerden birinde belirli bir kelime görülürse, bu, paylaşılan katmandan geçen tüm girişlerin işlenmesine fayda sağlayacaktır.

İşlevsel API'de bir katmanı paylaşmak için aynı katman örneğini birden çok kez çağırın. Örneğin, iki farklı metin girişi arasında paylaşılan bir Embedding katmanı aşağıda verilmiştir:

# 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)

Katman grafiğindeki düğümleri ayıklayın ve yeniden kullanın

İşlemekte olduğunuz katmanların grafiği statik bir veri yapısı olduğu için erişilebilir ve incelenebilir. Ve bu, işlevsel modelleri görüntü olarak çizebilmenizin yoludur.

Bu ayrıca, ara katmanların (grafikteki "düğümler") etkinleştirmelerine erişebileceğiniz ve bunları başka yerlerde yeniden kullanabileceğiniz anlamına gelir - bu, özellik çıkarma gibi bir şey için çok yararlıdır.

Bir örneğe bakalım. Bu, ağırlıkları ImageNet üzerinde önceden eğitilmiş bir VGG19 modelidir:

vgg19 = tf.keras.applications.VGG19()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574717952/574710816 [==============================] - 20s 0us/step

Ve bunlar, grafik veri yapısının sorgulanmasıyla elde edilen modelin ara etkinleştirmeleridir:

features_list = [layer.output for layer in vgg19.layers]

Ara katman etkinleştirmelerinin değerlerini döndüren yeni bir özellik çıkarma modeli oluşturmak için şu özellikleri kullanın:

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)

Bu, diğer şeylerin yanı sıra sinir stili aktarımı gibi görevler için kullanışlıdır.

API'yi özel katmanlar kullanarak genişletin

tf.keras , çok çeşitli yerleşik katmanlar içerir, örneğin:

  • Conv1D katmanlar: Conv1D , Conv2D , Conv3D , Conv2DTranspose
  • MaxPooling1D katmanları: MaxPooling1D , MaxPooling2D , MaxPooling3D , AveragePooling1D
  • RNN katmanları: GRU , LSTM , ConvLSTM2D
  • BatchNormalization , Dropout , Embedding vb

Ancak ihtiyacınız olanı bulamazsanız, kendi katmanlarınızı oluşturarak API'yi genişletmek kolaydır. Tüm katmanlar, Layer sınıfını alt sınıflandırır ve uygular:

  • katman tarafından yapılan hesaplamayı belirten call yöntemi.
  • katmanın ağırlıklarını oluşturan build yöntemi ( __init__ içinde de ağırlık oluşturabileceğiniz için bu sadece bir stil __init__ ).

Sıfırdan katman oluşturma hakkında daha fazla bilgi edinmek için özel katmanlar ve modeller kılavuzunu okuyun.

Aşağıda tf.keras.layers.Dense için temel bir uygulama yer tf.keras.layers.Dense :

class CustomDense(layers.Layer):
    def __init__(self, units=32):
        super(CustomDense, self).__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)

Özel katmanınızda serileştirme desteği için, katman örneğinin yapıcı argümanlarını döndüren bir get_config yöntemi tanımlayın:

class CustomDense(layers.Layer):
    def __init__(self, units=32):
        super(CustomDense, self).__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, custom_objects={"CustomDense": CustomDense})

İsteğe bağlı olarak, yapılandırma sözlüğü verilen bir katman örneğini yeniden oluştururken kullanılan from_config(cls, config) sınıf yöntemini from_config(cls, config) . from_config varsayılan uygulaması şudur:

def from_config(cls, config):
  return cls(**config)

İşlevsel API ne zaman kullanılır?

Yeni bir model oluşturmak için Keras işlevsel API'sini mi kullanmalısınız yoksa doğrudan Model sınıfını alt sınıflara mı ayırmalısınız? Genel olarak, işlevsel API daha yüksek seviyeli, daha kolay ve daha güvenlidir ve alt sınıflı modellerin desteklemediği bir dizi özelliğe sahiptir.

Bununla birlikte, model alt sınıflandırma, katmanların yönlendirilmiş döngüsel olmayan grafikleri olarak kolayca ifade edilemeyen modeller oluştururken daha fazla esneklik sağlar. Örneğin, işlevsel API ile bir Tree-RNN uygulayamazsınız ve Model doğrudan alt sınıflara ayırmanız gerekir.

İşlevsel API ile model alt sınıflandırma arasındaki farklara derinlemesine bir bakış için, TensorFlow 2.0'da Sembolik ve Zorunlu API'ler nedir? .

İşlevsel API güçleri:

Aşağıdaki özellikler Sıralı modeller için de geçerlidir (bunlar aynı zamanda veri yapılarıdır), ancak alt sınıflı modeller (veri yapıları değil Python bayt kodu olan) için doğru değildir.

Daha az ayrıntılı

super(MyClass, self).__init__(...) , def call(self, ...): vb yoktur.

Karşılaştırmak:

inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
mlp = keras.Model(inputs, outputs)

Alt sınıflı sürümle:

class MLP(keras.Model):

  def __init__(self, **kwargs):
    super(MLP, self).__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)))

Bağlantı grafiğini tanımlarken model doğrulama

İşlevsel API'de, giriş özelliği (şekil ve tip) önceden oluşturulur ( Input kullanılarak). Bir katmanı her çağırdığınızda, katman, kendisine iletilen spesifikasyonun varsayımlarıyla eşleşip eşleşmediğini kontrol eder ve değilse faydalı bir hata mesajı verir.

Bu, işlevsel API ile oluşturabileceğiniz herhangi bir modelin çalışacağını garanti eder. Yakınsama ile ilgili hata ayıklama dışındaki tüm hata ayıklama işlemleri, yürütme sırasında değil, model oluşturma sırasında statik olarak gerçekleşir. Bu, bir derleyicide tür denetlemeye benzer.

İşlevsel bir model çizilebilir ve incelenebilir

Modeli grafik olarak çizebilir, bu grafikte ara düğümlere kolayca ulaşabilirsiniz. Örneğin, ara katmanların etkinleştirmelerini ayıklamak ve yeniden kullanmak için (önceki bir örnekte görüldüğü gibi):

features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

İşlevsel bir model serileştirilebilir veya klonlanabilir

İşlevsel bir model, bir kod parçası olmaktan ziyade bir veri yapısı olduğundan, güvenli bir şekilde serileştirilebilir ve orijinal koda erişmeden tam olarak aynı modeli yeniden oluşturmanıza olanak tanıyan tek bir dosya olarak kaydedilebilir. Serileştirme ve kaydetme kılavuzuna bakın .

Alt get_config() bir modeli serileştirmek için, uygulayıcının model düzeyinde bir get_config() ve from_config() yöntemini belirtmesi get_config() .

İşlevsel API zayıflığı:

Dinamik mimarileri desteklemez

İşlevsel API, modelleri katmanların DAG'leri olarak ele alır. Bu, çoğu derin öğrenme mimarisi için geçerlidir, ancak hepsi için geçerli değildir - örneğin, özyinelemeli ağlar veya Ağaç RNN'leri bu varsayımı takip etmez ve işlevsel API'de uygulanamaz.

API stillerini karıştır ve eşleştir

İşlevsel API veya Model alt sınıflandırma arasında seçim yapmak, sizi tek bir model kategorisiyle sınırlayan ikili bir karar değildir. tf.keras API'sindeki tüm modeller, ister Sequential modeller, ister işlevsel modeller veya sıfırdan yazılan alt tf.keras modeller olsun, birbirleriyle etkileşim kurabilir.

Alt sınıflı bir modelin veya katmanın parçası olarak her zaman işlevsel bir model veya Sequential model kullanabilirsiniz:

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)


class CustomRNN(layers.Layer):
    def __init__(self):
        super(CustomRNN, self).__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)

Aşağıdaki modellerden birini izleyen bir call yöntemini uyguladığı sürece, işlevsel API'deki herhangi bir alt sınıf katmanı veya modeli kullanabilirsiniz:

  • call(self, inputs, **kwargs) - inputs bir tensör veya iç içe geçmiş bir tensör yapısı olduğu durumlarda (örneğin, tensörlerin bir listesi) ve burada **kwargs tensör olmayan argümanlardır (giriş olmayanlar).
  • call(self, inputs, training=None, **kwargs) - training , katmanın eğitim modunda ve çıkarım modunda davranması gerekip gerekmediğini belirten bir mantıksal call(self, inputs, training=None, **kwargs) .
  • call(self, inputs, mask=None, **kwargs) - mask boolean maske tensörü olduğu yerde (örneğin RNN'ler için kullanışlıdır).
  • call(self, inputs, training=None, mask=None, **kwargs) - Elbette, aynı anda hem maskeleme hem de eğitime özgü davranışa sahip olabilirsiniz.

Ek olarak, özel Katman veya modelinize get_config yöntemini get_config , oluşturduğunuz işlevsel modeller yine de serileştirilebilir ve klonlanabilir olacaktır.

İşte sıfırdan yazılmış, işlevsel bir modelde kullanılan özel bir RNN'nin hızlı bir örneği:

units = 32
timesteps = 10
input_dim = 5
batch_size = 16


class CustomRNN(layers.Layer):
    def __init__(self):
        super(CustomRNN, self).__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)))