Trang này được dịch bởi Cloud Translation API.
Switch to English

API chức năng

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải vở

Thiết lập

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

Giới thiệu

API chức năng Keras là một cách để tạo các mô hình linh hoạt hơn so với API tf.keras.Sequential . API chức năng có thể xử lý các mô hình với cấu trúc liên kết phi tuyến tính, các mô hình với các lớp được chia sẻ và các mô hình có nhiều đầu vào hoặc đầu ra.

Ý tưởng chính rằng một mô hình học tập sâu thường là một biểu đồ chu kỳ có hướng (DAG) của các lớp. Vì vậy, API chức năng là một cách để xây dựng biểu đồ của các lớp .

Hãy xem xét mô hình sau:

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

Đây là một biểu đồ cơ bản với ba lớp. Để xây dựng mô hình này bằng API chức năng, hãy bắt đầu bằng cách tạo nút đầu vào:

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

Hình dạng của dữ liệu được đặt thành một vectơ 784 chiều. Kích thước lô luôn được bỏ qua vì chỉ hình dạng của từng mẫu được chỉ định.

Ví dụ: nếu bạn có đầu vào hình ảnh có hình dạng (32, 32, 3) , bạn sẽ sử dụng:

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

Các inputs được trả về chứa thông tin về hình dạng và dtype dữ liệu đầu vào mà bạn cung cấp cho mô hình của mình. Đây là hình dạng:

 inputs.shape
 
TensorShape([None, 784])

Đây là dtype:

 inputs.dtype
 
tf.float32

Bạn tạo một nút mới trong biểu đồ các lớp bằng cách gọi một lớp trên đối tượng inputs này:

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

Hành động "gọi lớp" giống như vẽ một mũi tên từ "đầu vào" sang lớp này mà bạn đã tạo. Bạn đang "chuyển" các đầu vào sang lớp dense và bạn nhận được x .

Chúng ta hãy thêm một vài lớp nữa vào biểu đồ của các lớp:

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

Tại thời điểm này, bạn có thể tạo Model bằng cách chỉ định đầu vào và đầu ra của nó trong biểu đồ các lớp:

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

Hãy xem tóm tắt mô hình trông như thế nào:

 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
_________________________________________________________________

Bạn cũng có thể vẽ mô hình dưới dạng biểu đồ:

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

png

Và, tùy ý, hiển thị hình dạng đầu vào và đầu ra của mỗi lớp trong biểu đồ được vẽ:

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

png

Con số này và mã gần như giống hệt nhau. Trong phiên bản mã, các mũi tên kết nối được thay thế bằng thao tác cuộc gọi.

"Biểu đồ các lớp" là một hình ảnh tinh thần trực quan cho một mô hình học tập sâu và API chức năng là một cách để tạo ra các mô hình phản ánh chặt chẽ điều này.

Đào tạo, đánh giá và suy luận

Đào tạo, đánh giá và suy luận hoạt động chính xác theo cùng một cách cho các mô hình được xây dựng bằng API chức năng như đối với các mô hình Sequential .

Tại đây, tải dữ liệu hình ảnh của MNIST, định hình lại nó thành các vectơ, điều chỉnh mô hình trên dữ liệu (trong khi giám sát hiệu suất khi phân tách xác thực), sau đó đánh giá mô hình trên dữ liệu thử nghiệm:

 (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 3ms/step - loss: 0.3458 - accuracy: 0.9013 - val_loss: 0.1860 - val_accuracy: 0.9463
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1588 - accuracy: 0.9524 - val_loss: 0.1242 - val_accuracy: 0.9645
313/313 - 1s - loss: 0.1297 - accuracy: 0.9604
Test loss: 0.12967276573181152
Test accuracy: 0.9603999853134155

Để đọc thêm, xem hướng dẫn đào tạo và đánh giá .

Lưu và tuần tự hóa

Việc lưu mô hình và tuần tự hóa hoạt động theo cùng một cách đối với các mô hình được xây dựng bằng API chức năng giống như các mô hình Sequential . Theo cách tiêu chuẩn để lưu mô hình chức năng là gọi model.save() để lưu toàn bộ mô hình dưới dạng một tệp. Sau này, bạn có thể tạo lại mô hình tương tự từ tệp này, ngay cả khi mã xây dựng mô hình không còn khả dụng.

Tệp đã lưu này bao gồm:

  • kiến trúc mô hình
  • mô hình giá trị trọng lượng (đã được học trong quá trình đào tạo)
  • mô hình đào tạo cấu hình, nếu có (như được chuyển sang compile )
  • trình tối ưu hóa và trạng thái của nó, nếu có (để khởi động lại đào tạo khi bạn rời đi)
 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/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
INFO:tensorflow:Assets written to: path_to_my_model/assets

Để biết chi tiết, hãy đọc hướng dẫn lưu trữ và tuần tự mô hình.

Sử dụng cùng một biểu đồ các lớp để xác định nhiều mô hình

Trong API chức năng, các mô hình được tạo bằng cách chỉ định đầu vào và đầu ra của chúng trong biểu đồ các lớp. Điều đó có nghĩa là một biểu đồ các lớp có thể được sử dụng để tạo nhiều mô hình.

Trong ví dụ dưới đây, bạn sử dụng cùng một ngăn xếp của các lớp để nhanh chóng hai mô hình: một encoder mô hình đầu vào lần lượt hình ảnh vào vectơ 16 chiều, và một end-to-end autoencoder mô hình đào tạo.

 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
_________________________________________________________________

Ở đây, kiến ​​trúc giải mã đối xứng hoàn toàn với kiến ​​trúc mã hóa, do đó hình dạng đầu ra giống với hình dạng đầu vào (28, 28, 1) .

Mặt trái của lớp Conv2D là lớp Conv2DTranspose và mặt trái của lớp MaxPooling2D là lớp UpSampling2D .

Tất cả các mô hình đều có thể gọi được, giống như các lớp

Bạn có thể coi bất kỳ mô hình nào như thể nó là một lớp bằng cách gọi nó trên Input hoặc trên đầu ra của một lớp khác. Bằng cách gọi một mô hình, bạn không chỉ sử dụng lại kiến ​​trúc của mô hình, bạn cũng đang sử dụng lại trọng số của nó.

Để thấy điều này trong thực tế, đây là một ví dụ khác về ví dụ bộ mã hóa tự động tạo ra một mô hình mã hóa, mô hình giải mã và xâu chuỗi chúng trong hai cuộc gọi để có được mô hình bộ mã hóa tự động:

 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 (Model)              (None, 16)                18672     
_________________________________________________________________
decoder (Model)              (None, 28, 28, 1)         9569      
=================================================================
Total params: 28,241
Trainable params: 28,241
Non-trainable params: 0
_________________________________________________________________

Như bạn có thể thấy, mô hình có thể được lồng nhau: một mô hình có thể chứa các mô hình con (vì một mô hình giống như một lớp). Một trường hợp sử dụng phổ biến cho lồng mô hình là tập hợp . Ví dụ: đây là cách tập hợp một nhóm mô hình thành một mô hình duy nhất tính trung bình dự đoán của chúng:

 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)
 

Thao tác cấu trúc liên kết đồ thị phức tạp

Các mô hình với nhiều đầu vào và đầu ra

API chức năng giúp dễ dàng thao tác nhiều đầu vào và đầu ra. Điều này không thể được xử lý với API Sequential .

Ví dụ: nếu bạn đang xây dựng một hệ thống để xếp hạng vé phát hành tùy chỉnh theo mức độ ưu tiên và định tuyến chúng đến đúng bộ phận, thì mô hình sẽ có ba đầu vào:

  • tiêu đề của vé (nhập văn bản),
  • nội dung văn bản của vé (nhập văn bản) và
  • bất kỳ thẻ nào được thêm bởi người dùng (đầu vào phân loại)

Mô hình này sẽ có hai đầu ra:

  • điểm ưu tiên giữa 0 và 1 (đầu ra sigmoid vô hướng) và
  • bộ phận nên xử lý vé (đầu ra softmax qua bộ các bộ phận).

Bạn có thể xây dựng mô hình này trong một vài dòng với API chức năng:

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

Bây giờ vẽ mô hình:

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

png

Khi biên dịch mô hình này, bạn có thể chỉ định các tổn thất khác nhau cho mỗi đầu ra. Bạn thậm chí có thể chỉ định các trọng số khác nhau cho mỗi mất mát - để điều chỉnh mức đóng góp của chúng vào tổng tổn thất đào tạo.

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

Vì các lớp đầu ra có các tên khác nhau, bạn cũng có thể chỉ định mất như thế này:

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

Huấn luyện mô hình bằng cách chuyển danh sách các mảng NumPy của đầu vào và mục tiêu:

 # 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 12ms/step - loss: 1.3372 - priority_loss: 0.7055 - department_loss: 3.1586
Epoch 2/2
40/40 [==============================] - 0s 11ms/step - loss: 1.3285 - priority_loss: 0.6998 - department_loss: 3.1438

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

Khi gọi phù hợp với một đối tượng Dataset , nó sẽ mang lại một bộ danh sách như ([title_data, body_data, tags_data], [priority_targets, dept_targets]) hoặc một bộ từ điển như ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets}) .

Để được giải thích chi tiết hơn, hãy tham khảo hướng dẫn đào tạo và đánh giá .

Mô hình ResNet đồ chơi

Ngoài các mô hình có nhiều đầu vào và đầu ra, API chức năng giúp dễ dàng thao tác các cấu trúc liên kết phi tuyến tính - đây là các mô hình có các lớp không được kết nối tuần tự. Một cái gì đó API Sequential không thể xử lý.

Một trường hợp sử dụng phổ biến cho điều này là kết nối còn lại. Hãy xây dựng mô hình ResNet đồ chơi cho CIFAR10 để chứng minh điều này:

 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
__________________________________________________________________________________________________

Vẽ mô hình:

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

png

Bây giờ đào tạo mô hình:

 (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)
 
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 6s 0us/step
13/13 [==============================] - 0s 25ms/step - loss: 2.3091 - acc: 0.1037 - val_loss: 2.2991 - val_acc: 0.1300

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

Các lớp dùng chung

Một cách sử dụng tốt khác cho API chức năng là dành cho các mô hình sử dụng các lớp dùng chung . Các lớp được chia sẻ là các thể hiện của lớp được sử dụng lại nhiều lần trong cùng một mô hình - chúng tìm hiểu các tính năng tương ứng với nhiều đường dẫn trong biểu đồ của các lớp.

Các lớp dùng chung thường được sử dụng để mã hóa các đầu vào từ các không gian tương tự (giả sử, hai đoạn văn bản khác nhau có tính năng từ vựng tương tự). Chúng cho phép chia sẻ thông tin qua các đầu vào khác nhau này và chúng cho phép đào tạo một mô hình như vậy trên ít dữ liệu hơn. Nếu một từ nhất định được nhìn thấy trong một trong các đầu vào, điều đó sẽ có lợi cho việc xử lý tất cả các đầu vào đi qua lớp chia sẻ.

Để chia sẻ một lớp trong API chức năng, hãy gọi cùng một thể hiện lớp nhiều lần. Chẳng hạn, đây là một lớp Embedding được chia sẻ trên hai kiểu nhập văn bản khác nhau:

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

Trích xuất và tái sử dụng các nút trong biểu đồ của các lớp

Bởi vì biểu đồ của các lớp bạn đang thao tác là một cấu trúc dữ liệu tĩnh, nên nó có thể được truy cập và kiểm tra. Và đây là cách bạn có thể vẽ các mô hình chức năng dưới dạng hình ảnh.

Điều này cũng có nghĩa là bạn có thể truy cập kích hoạt các lớp trung gian ("nút" trong biểu đồ) và sử dụng lại chúng ở nơi khác - rất hữu ích cho việc trích xuất tính năng.

Hãy xem xét một ví dụ. Đây là mô hình VGG19 với trọng lượng được xử lý trước trên ImageNet:

 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 [==============================] - 5s 0us/step

Và đây là các kích hoạt trung gian của mô hình, thu được bằng cách truy vấn cấu trúc dữ liệu đồ thị:

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

Sử dụng các tính năng này để tạo một mô hình trích xuất tính năng mới trả về các giá trị của kích hoạt lớp trung gian:

 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)
 

Điều này có ích cho các nhiệm vụ như chuyển kiểu thần kinh , trong số những thứ khác.

Mở rộng API bằng các lớp tùy chỉnh

tf.keras bao gồm một loạt các lớp dựng sẵn, ví dụ:

  • Các lớp kết hợp: Conv1D , Conv2D , Conv3D , Conv2DTranspose
  • Các lớp MaxPooling1D : MaxPooling1D , MaxPooling2D , MaxPooling3D , AveragePooling1D
  • Các lớp RNN: GRU , LSTM , ConvLSTM2D
  • BatchNormalization , Dropout , Embedding , v.v.

Nhưng nếu bạn không tìm thấy những gì bạn cần, thật dễ dàng để mở rộng API bằng cách tạo các lớp của riêng bạn. Tất cả các lớp con lớp Layer và thực hiện:

  • call phương thức, chỉ định tính toán được thực hiện bởi lớp.
  • phương thức build , tạo ra các trọng số của lớp (đây chỉ là một quy ước kiểu vì bạn cũng có thể tạo các trọng số trong __init__ ).

Để tìm hiểu thêm về cách tạo lớp từ đầu, hãy đọc hướng dẫn về lớp và mô hình tùy chỉnh .

Sau đây là cách triển khai cơ bản của 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)
 

Để hỗ trợ tuần tự hóa trong lớp tùy chỉnh của bạn, hãy xác định phương thức get_config trả về các đối số hàm tạo của thể hiện của lớp:

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

Tùy chọn, triển khai classmethod from_config(cls, config) được sử dụng khi tạo lại một thể hiện lớp cho từ điển cấu hình của nó. Việc triển khai mặc định của from_config là:

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

Khi nào nên sử dụng API chức năng

Khi nào bạn nên sử dụng API chức năng Keras để tạo một mô hình mới hoặc chỉ phân lớp trực tiếp lớp Model ? Nói chung, API chức năng ở cấp độ cao hơn, dễ dàng hơn và an toàn hơn và có một số tính năng mà các mô hình phân lớp không hỗ trợ.

Tuy nhiên, phân lớp mô hình cung cấp tính linh hoạt cao hơn khi xây dựng các mô hình không dễ biểu thị như các biểu đồ chu kỳ theo hướng của các lớp. Ví dụ: bạn không thể triển khai Tree-RNN với API chức năng và sẽ phải trực tiếp phân lớp Model .

Để tìm hiểu sâu về sự khác biệt giữa API chức năng và phân lớp mô hình, hãy đọc API tượng trưng và mệnh lệnh trong TensorFlow 2.0 là gì? .

Điểm mạnh API chức năng:

Các thuộc tính sau cũng đúng với các mô hình Tuần tự (cũng là các cấu trúc dữ liệu), nhưng không đúng với các mô hình được phân lớp (là mã byte Python, không phải cấu trúc dữ liệu).

Ít dài dòng

Không có super(MyClass, self).__init__(...) , không có def call(self, ...): , v.v.

Đối chiếu:

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

Với phiên bản được phân lớp:

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

Xác thực mô hình trong khi xác định biểu đồ kết nối của nó

Trong API chức năng, đặc tả đầu vào (hình dạng và kiểu chữ) được tạo trước (sử dụng Input ). Mỗi khi bạn gọi một lớp, lớp sẽ kiểm tra xem thông số kỹ thuật được truyền cho nó có khớp với các giả định của nó không và nó sẽ đưa ra một thông báo lỗi hữu ích nếu không.

Điều này đảm bảo rằng bất kỳ mô hình nào bạn có thể xây dựng với API chức năng sẽ chạy. Tất cả gỡ lỗi - ngoại trừ gỡ lỗi liên quan đến hội tụ - xảy ra tĩnh trong quá trình xây dựng mô hình và không tại thời điểm thực hiện. Điều này tương tự như kiểm tra kiểu trong trình biên dịch.

Một mô hình chức năng là plottable và kiểm tra

Bạn có thể vẽ mô hình dưới dạng biểu đồ và bạn có thể dễ dàng truy cập các nút trung gian trong biểu đồ này. Ví dụ, để trích xuất và sử dụng lại kích hoạt của các lớp trung gian (như đã thấy trong ví dụ trước):

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

Một mô hình chức năng có thể được tuần tự hóa hoặc nhân bản

Bởi vì một mô hình chức năng là một cấu trúc dữ liệu chứ không phải là một đoạn mã, nó có thể được tuần tự hóa một cách an toàn và có thể được lưu thành một tệp duy nhất cho phép bạn tạo lại chính xác mô hình mà không cần truy cập vào bất kỳ mã gốc nào. Xem hướng dẫn tuần tự và tiết kiệm .

Để tuần tự hóa một mô hình được phân lớp, người triển khai cần chỉ định phương thức get_config()from_config() ở cấp độ mô hình.

Điểm yếu API chức năng:

Nó không hỗ trợ kiến ​​trúc động

API chức năng coi các mô hình là DAG của các lớp. Điều này đúng với hầu hết các kiến ​​trúc học sâu, nhưng không phải tất cả - ví dụ, các mạng đệ quy hoặc Tree RNN không tuân theo giả định này và không thể được thực hiện trong API chức năng.

Các kiểu API kết hợp và khớp

Lựa chọn giữa API chức năng hoặc phân lớp mô hình không phải là quyết định nhị phân giới hạn bạn trong một loại mô hình. Tất cả các mô hình trong API tf.keras có thể tương tác với nhau, cho dù chúng là mô hình Sequential , mô hình chức năng hoặc mô hình phân lớp được viết từ đầu.

Bạn luôn có thể sử dụng mô hình chức năng hoặc mô hình Sequential như một phần của mô hình hoặc lớp được phân lớp:

 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)

Bạn có thể sử dụng bất kỳ lớp hoặc mô hình được phân lớp nào trong API chức năng miễn là nó thực hiện một phương thức call theo một trong các mẫu sau:

  • call(self, inputs, **kwargs) - Trong đó inputs là một cấu trúc tenxơ hoặc cấu trúc của các tenxơ (ví dụ: danh sách các tenxơ) và trong đó **kwargs là các đối số không căng (không phải đầu vào).
  • call(self, inputs, training=None, **kwargs) - Trường hợp training là một boolean cho biết liệu lớp có nên hoạt động trong chế độ đào tạo và chế độ suy luận hay không.
  • call(self, inputs, mask=None, **kwargs) - Trong đó mask là một tenxơ mặt nạ boolean (ví dụ hữu ích cho RNNs).
  • call(self, inputs, training=None, mask=None, **kwargs) - Tất nhiên, bạn có thể có cả hành vi che giấu và hành vi cụ thể đào tạo cùng một lúc.

Ngoài ra, nếu bạn triển khai phương thức get_config trên Lớp hoặc mô hình tùy chỉnh của mình, các mô hình chức năng bạn tạo sẽ vẫn có thể tuần tự hóa và có thể nhân bản.

Dưới đây là ví dụ nhanh về RNN tùy chỉnh, được viết từ đầu, được sử dụng trong mô hình chức năng:

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