Lưu ngày! Google I / O hoạt động trở lại từ ngày 18 đến 20 tháng 5 Đăng ký ngay
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 xuống sổ ghi chép

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 API tf.keras.Sequential . API chức năng có thể xử lý các mô hình có cấu trúc liên kết phi tuyến tính, các lớp chia sẻ và thậm chí nhiều đầu vào hoặc đầu ra.

Ý tưởng chính là mô hình học sâu thường là một đồ thị xoay chiều 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 đồ thị 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 đồ thị cơ bản có ba lớp. Để xây dựng mô hình này bằng cách sử dụng API chức năng, hãy bắt đầu bằng cách tạo một nút đầu vào:

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

Hình dạng của dữ liệu được đặt dưới dạng vectơ 784 chiều. Kích thước lô luôn bị 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 là (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à loại:

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" đến lớp này mà bạn đã tạo. Bạn đang "chuyển" các đầu vào cho lớp dense và bạn nhận được x là đầu ra.

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 các đầ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 cùng 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 đồ thị:

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

png

Và, theo tùy chọn, 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

Hình 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 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 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 đối với các mô hình được xây dựng bằng cách sử dụng API chức năng như đối với các mô hình Sequential .

Lớp Model cung cấp một vòng lặp huấn luyện cài sẵn fit() phương thức fit() ) và một vòng lặp evaluate() phương thức evaluate() ). Lưu ý rằng bạn có thể dễ dàng tùy chỉnh các vòng lặp này để triển khai các quy trình đào tạo ngoài việc học có giám sát (ví dụ: GAN ).

Tại đây, tải dữ liệu hình ảnh MNIST, định hình lại nó thành các vectơ, phù hợp với mô hình trên dữ liệu (trong khi theo dõi hiệu suất trên một 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 [==============================] - 3s 3ms/step - loss: 0.5848 - accuracy: 0.8332 - val_loss: 0.1880 - val_accuracy: 0.9480
Epoch 2/2
750/750 [==============================] - 2s 2ms/step - loss: 0.1699 - accuracy: 0.9503 - val_loss: 0.1490 - val_accuracy: 0.9563
313/313 - 0s - loss: 0.1463 - accuracy: 0.9563
Test loss: 0.14626088738441467
Test accuracy: 0.9563000202178955

Để đọc thêm, hãy 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ách tương tự đối với các mô hình được tạo bằng API chức năng như cách chúng làm đối với các mô hình Sequential . Cách tiêu chuẩn để lưu một 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 duy nhất. Sau đó, bạn có thể tạo lại cùng một mô hình từ tệp này, ngay cả khi mã đã tạo mô hình đó không còn nữa.

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

  • kiến trúc mô hình
  • giá trị trọng lượng mô hình (đã được học trong quá trình đào tạo)
  • cấu hình đào tạo mô 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ó (để bắt đầu lại quá trình đào tạo mà bạn đã dừng lạ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")
INFO:tensorflow:Assets written to: path_to_my_model/assets

Để biết chi tiết, hãy đọc hướng dẫn tuần tự hóa và lưu 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 đồ duy nhất của 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ã hoàn toàn đối xứng với kiến ​​trúc mã hóa, vì vậy 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 một 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 mà bạn còn đang sử dụng lại trọng lượng của nó.

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

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
_________________________________________________________________

Như bạn thấy, mô hình có thể được lồng vào nhau: một mô hình có thể chứa các mô hình con (vì một mô hình chỉ giống như một lớp). Một trường hợp sử dụng phổ biến cho lồng ghép mô hình là tập hợp . Ví dụ: đây là cách tập hợp một tập hợp các mô hình thành một mô hình duy nhất tính trung bình các 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ác cấu trúc liên kết đồ thị phức tạp

Mô hình có nhiều đầu vào và đầu ra

API chức năng giúp bạn dễ dàng thao tác nhiều đầu vào và đầu ra. Điều này không thể được xử lý bằng 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 của khách hàng 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 từ 0 đến 1 (đầu ra sigmoid vô hướng) và
  • bộ phận cần xử lý phiếu (đầu ra softmax trên bộ 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ể ấn đị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 lượng khác nhau cho mỗi lần giảm - để điều chỉnh mức đóng góp của chúng vào tổng số lần mất tập luyện.

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ó tên khác nhau, bạn cũng có thể chỉ định tổn thất như sau:

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 đầu vào và mục tiêu của mảng NumPy:

# 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 [==============================] - 4s 11ms/step - loss: 1.2978 - priority_loss: 0.7067 - department_loss: 2.9554
Epoch 2/2
40/40 [==============================] - 0s 11ms/step - loss: 1.2947 - priority_loss: 0.7023 - department_loss: 2.9621
<tensorflow.python.keras.callbacks.History at 0x7fe18923e6a0>

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

Để biết 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 với cấu trúc liên kết kết nối phi tuyến tính - đây là những mô hình có các lớp không được kết nối tuần tự, mà 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 dư. 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 [==============================] - 2s 35ms/step - loss: 2.2992 - acc: 0.1273 - val_loss: 2.2629 - val_acc: 0.1850
<tensorflow.python.keras.callbacks.History at 0x7fe210396ef0>

Các lớp được chia sẻ

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

Các lớp chia sẻ thường được sử dụng để mã hóa đầ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ừ vựng tương tự). Chúng cho phép chia sẻ thông tin trên các đầu vào khác nhau này và chúng giú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 được chia sẻ.

Để chia sẻ một lớp trong API chức năng, hãy gọi phiên bản lớp đó nhiều lần. Ví dụ: đây là một lớp Embedding được chia sẻ trên hai đầu vào 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à sử dụng lại các nút trong biểu đồ 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ó 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ủa các lớp trung gian ("nút" trong biểu đồ) và sử dụng lại chúng ở những nơi khác - điều này rất hữu ích cho những thứ như trích xuất tính năng.

Hãy xem một ví dụ. Đây là mô hình VGG19 với các trọng số đượ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 [==============================] - 7s 0us/step

Và đây là những 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 biểu đồ:

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

Sử dụng các tính năng này để tạo mô hình trích xuất tính năng mới trả về giá trị của các 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 rất hữu ích cho các tác vụ như chuyển kiểu thần kinh , trong số những thứ khác.

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

tf.keras bao gồm một loạt các lớp tích hợp, ví dụ:

  • Các lớp chuyển đổi: Conv1D , Conv2D , Conv3D , Conv2DTranspose
  • Nhóm các lớp: 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 thứ mình 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 phân lớp lớp Layer và triển khai:

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

Để tìm hiểu thêm về cách tạo các lớp từ đầu, hãy đọc hướng dẫn các 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ố phương thức khởi tạo của cá thể 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})

Theo tùy chọn, triển khai phương thức lớp from_config(cls, config) được sử dụng khi tạo lại một cá thể lớp với 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 sử dụng API chức năng

Bạn nên sử dụng API chức năng Keras để tạo một mô hình mới hay 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, đồng thời 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 diễn như đồ thị vòng có 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 .

Để có cái nhìn sâu hơn 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à bắt buộc trong TensorFlow 2.0 là gì? .

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

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

Ít dài dòng hơn

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

So sánh:

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 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 đồ thị 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) đượ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 chuyển cho nó có khớp với các giả định của nó hay 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 bằng API chức năng sẽ chạy. Tất cả các gỡ lỗ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 phải tại thời điểm thực thi. Đ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 có thể vẽ và kiểm tra được

Bạn có thể vẽ mô hình dưới dạng đồ thị và bạn có thể dễ dàng truy cập các nút trung gian trong đồ thị này. Ví dụ: để trích xuất và sử dụng lại các 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ô 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ể 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 cùng một 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ự hóa và lưu .

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

Điểm yếu của 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 như 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ụ: mạng đệ quy hoặc RNN dạng cây không tuân theo giả định này và không thể được triển khai trong API chức năng.

Kiểu API kết hợp và kết hợ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à một quyết định nhị phân hạn chế bạn vào một danh mục 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 hay 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 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 phân lớp con nào trong API chức năng miễn là nó triển khai một phương thức call tuân theo một trong các mẫu sau:

  • call(self, inputs, **kwargs) - Trong đó inputs là tensor hoặc cấu trúc lồng nhau của tensor (ví dụ: danh sách tensor) và trong đó **kwargs là đối số không phải tensor (không phải đầu vào).
  • call(self, inputs, training=None, **kwargs) - Trong đó training là một boolean cho biết liệu lớp có 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à tensor mặt nạ boolean (ví dụ: hữu ích cho RNN).
  • call(self, inputs, training=None, mask=None, **kwargs) - Tất nhiên, bạn có thể có cả hành vi tạo mặt nạ và đào tạo cụ thể 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ể sao chép.

Dưới đây là một ví dụ nhanh về RNN tùy chỉnh, được viết từ đầu, đang được sử dụng trong một 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)))