![]() | ![]() | ![]() | ![]() |
Thành lập
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
Giới thiệu
Các Keras chức năng API là một cách để tạo ra các mô hình linh hoạt hơn so với tf.keras.Sequential
API. 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, các API chức năng là một cách để xây dựng đồ thị của 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.
Nếu, ví dụ, bạn có một đầu vào hình ảnh với một hình dạng của (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
của dữ liệu đầu vào mà bạn ăn vào mô hình của bạn. Đây là hình dạng:
inputs.shape
TensorShape([None, 784])
Đây là loại dtype:
inputs.dtype
tf.float32
Bạn tạo một nút mới trong đồ thị của các lớp bằng cách gọi một lớp về vấn đề này inputs
đối tượng:
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 "đi qua" các yếu tố đầu vào dense
lớp, và bạn sẽ có được x
như đầ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 một Model
bằng cách xác định đầu vào và đầu ra của nó trong đồ thị dưới 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")
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)
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ô 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à việc suy luận chính xác trong cùng một cách cho 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 Sequential
các mô hình.
Các Model
lớp Mời một built-in loop đào tạo (các fit()
phương pháp) và một built-in loop đánh giá (các evaluate()
phương pháp). Lưu ý rằng bạn có thể dễ dàng tùy chỉnh các vòng để thực hiện thói quen đào tạo ngoài học có giám sát (ví dụ Gans ).
Tại đây, tải dữ liệu hình ảnh MNIST, định hình lại thành 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.3430 - accuracy: 0.9035 - val_loss: 0.1851 - val_accuracy: 0.9463 Epoch 2/2 750/750 [==============================] - 2s 3ms/step - loss: 0.1585 - accuracy: 0.9527 - val_loss: 0.1366 - val_accuracy: 0.9597 313/313 - 0s - loss: 0.1341 - accuracy: 0.9592 Test loss: 0.13414572179317474 Test accuracy: 0.9592000246047974
Để đọc thêm, xem đào tạo và đánh giá hướng dẫn.
Lưu và tuần tự hóa
Lưu mô hình và serialization làm việc cùng một cách cho các mô hình được xây dựng bằng cách sử dụng API chức năng như họ làm cho Sequential
các mô hình. Cách thông thường để tiết kiệm một mô hình chức năng là để gọi model.save()
để lưu toàn bộ mô hình như một tập tin 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 (đã học được trong quá trình đào tạo)
- mô hình đào tạo config, nếu có (như thông qua với
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")
2021-08-25 17:50:55.989736: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them. INFO:tensorflow:Assets written to: path_to_my_model/assets
Để biết chi tiết, hãy đọc những mô hình serialization & tiết kiệm dẫn.
Sử dụng cùng một đồ thị của 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 ra 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ã là chọi nhau với kiến trúc mã hóa, vì vậy hình dạng đầu ra là giống như hình dạng đầu vào (28, 28, 1)
.
Điều ngược lại của một Conv2D
lớp là một Conv2DTranspose
lớp, và ngược lại của một MaxPooling2D
lớp là một UpSampling2D
lớp.
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ể đối xử với bất kỳ mô hình như thể nó là một lớp bằng cách gọi nó trên một Input
hoặc đầu ra của 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ô hình bộ giải mã và xâu 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 các mô hình làm tổ được ensembling. 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
Các 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 với nhiều đầu vào và đầu ra. Điều này không thể được xử lý với Sequential
API.
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)
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 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 và trọng số mất mát bằng các tên lớp tương ứng:
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={
"priority": keras.losses.BinaryCrossentropy(from_logits=True),
"department": keras.losses.CategoricalCrossentropy(from_logits=True),
},
loss_weights={"priority": 1.0, "department": 0.2},
)
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 [==============================] - 5s 9ms/step - loss: 1.2899 - priority_loss: 0.7186 - department_loss: 2.8564 Epoch 2/2 40/40 [==============================] - 0s 9ms/step - loss: 1.2668 - priority_loss: 0.6991 - department_loss: 2.8389 <keras.callbacks.History at 0x7fc1a66dc790>
Khi gọi phù hợp với một Dataset
đối tượng, nó nên năng suất hoặc là một tuple của danh sách như ([title_data, body_data, tags_data], [priority_targets, dept_targets])
hoặc một tuple của bộ từ điển như ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
.
Để xem giải thích chi tiết hơn, hãy tham khảo đào tạo và đánh giá hướng dẫn.
Mô hình ResNet đồ chơi
Ngoài các mô hình với nhiều đầu vào và đầu ra, API chức năng giúp bạn dễ dàng thao tác cấu trúc liên kết kết nối phi tuyến tính - đây là những mô hình với các lớp không được kết nối liên tục, mà Sequential
API 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ột 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)
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 [==============================] - 11s 0us/step 170508288/170498071 [==============================] - 11s 0us/step 13/13 [==============================] - 2s 29ms/step - loss: 2.3364 - acc: 0.1063 - val_loss: 2.2986 - val_acc: 0.0850 <keras.callbacks.History at 0x7fc19df22610>
Các lớp được chia sẻ
Một sử dụng tốt cho các API chức năng là mô hình sử dụng các lớp 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 dùng chung 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 ở 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 Embedding
lớp 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ì đồ thị 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 huấn luyện 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 [==============================] - 15s 0us/step 574726144/574710816 [==============================] - 15s 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 có ích cho công việc 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 built-in lớp, ví dụ:
- Lớp xoắn:
Conv1D
,Conv2D
,Conv3D
,Conv2DTranspose
- Lớp tổng hợp:
MaxPooling1D
,MaxPooling2D
,MaxPooling3D
,AveragePooling1D
- RNN lớp:
GRU
,LSTM
,ConvLSTM2D
-
BatchNormalization
,Dropout
,Embedding
vv
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 các Layer
lớp và thực hiện:
-
call
phương pháp, mà quy định cụ thể việc tính toán được thực hiện bởi lớp. -
build
phương pháp, tạo ra trọng lượng của lớp (đây chỉ là một quy ước phong cách kể từ khi bạn có thể tạo trọng trong__init__
, cũng).
Để tìm hiểu thêm về việc tạo lớp từ đầu, đọc lớp tùy chỉnh và các mô hình hướng dẫn.
Sau đây là một việc thực hiện 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)
Để được hỗ trợ serialization trong lớp tùy chỉnh của bạn, xác định một get_config
phương thức trả về các đối số constructor của lớp dụ:
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, thực hiện các phương thức lớp from_config(cls, config)
được sử dụng khi tái tạo một trường lớp cho từ điển cấu hình của nó. Việc thực hiện 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 các API chức năng Keras để tạo ra một mô hình mới, hoặc chỉ phân lớp các Model
lớp học trực tiếp? Nói chung, API chức năng ở mức 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ể thực hiện một Tree-RNN với các API chức năng và sẽ phải phân lớp Model
trực tiếp.
Đối với một cái nhìn sâu sắc vào sự khác biệt giữa các API chức năng và mô hình subclassing, đọ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 phân lớp (là mã byte 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, ...):
vv
Đố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 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, thông số kỹ thuật đầu vào (hình dạng và dtype) được tạo ra 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 - ngoài 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 serialization & tiết kiệm .
Để serialize một mô hình subclassed, nó là cần thiết cho đơn vị thực hiện để xác định một get_config()
và from_config()
phương pháp ở cấp 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 model trong tf.keras
API có thể tương tác với nhau, cho dù họ đang Sequential
các mô hình, mô hình chức năng, hoặc các mô hình subclassed được viết từ đầu.
Bạn luôn có thể sử dụng một mô hình chức năng hoặc Sequential
mô hình như là một phần của một mô hình subclassed hoặc 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 subclassed hoặc mô hình trong API chức năng miễn là nó thực hiện một call
phương pháp mà sau một trong những mô hình sau:
-
call(self, inputs, **kwargs)
- Trường hợpinputs
là một tensor hoặc một cấu trúc lồng nhau của tensors (ví dụ như một danh sách các tensors), và nơi**kwargs
là lập luận phi tensor (phi đầu vào). -
call(self, inputs, training=None, **kwargs)
- Nơitraining
là một boolean nêu rõ lớp nên cư xử trong chế độ đào tạo và phương thức suy luận. -
call(self, inputs, mask=None, **kwargs)
- Trường hợpmask
là một mặt nạ tensor boolean (hữu dụng cho RNNs, ví dụ). -
call(self, inputs, training=None, mask=None, **kwargs)
- Tất nhiên, bạn có thể có cả mặt nạ và hành vi đào tạo cụ thể cùng một lúc.
Ngoài ra, nếu bạn thực hiện get_config
phương pháp trên lớp tùy chỉnh của bạn hoặc mô hình, các mô hình chức năng bạn tạo vẫn sẽ serializable và cloneable.
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)))