![]() | ![]() | ![]() | ![]() |
ติดตั้ง
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
บทนำ
Keras ทำงาน API เป็นวิธีในการสร้างแบบจำลองที่มีความยืดหยุ่นมากขึ้นกว่าที่ tf.keras.Sequential
API API ที่ใช้งานได้สามารถจัดการโมเดลที่มีโทโพโลยีที่ไม่ใช่เชิงเส้น เลเยอร์ที่ใช้ร่วมกัน และแม้กระทั่งอินพุตหรือเอาต์พุตหลายรายการ
แนวคิดหลักคือโมเดลการเรียนรู้เชิงลึกมักจะเป็นกราฟ acyclic แบบกำกับ (DAG) ของเลเยอร์ ดังนั้นการทำงานของ API เป็นวิธีที่จะสร้างกราฟของชั้น
พิจารณารูปแบบต่อไปนี้:
(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)
นี่คือกราฟพื้นฐานที่มีสามชั้น ในการสร้างโมเดลนี้โดยใช้ API การทำงาน ให้เริ่มต้นด้วยการสร้างโหนดอินพุต:
inputs = keras.Input(shape=(784,))
รูปร่างของข้อมูลถูกกำหนดเป็นเวกเตอร์ 784 มิติ ขนาดแบทช์จะถูกละเว้นเสมอเนื่องจากระบุเฉพาะรูปร่างของแต่ละตัวอย่างเท่านั้น
ตัวอย่างเช่นหากคุณมีการป้อนข้อมูลภาพที่มีรูปทรงของ (32, 32, 3)
คุณจะใช้:
# Just for demonstration purposes.
img_inputs = keras.Input(shape=(32, 32, 3))
inputs
ที่ถูกส่งกลับมีข้อมูลเกี่ยวกับรูปร่างและ dtype
ของการป้อนข้อมูลที่คุณกินกับรูปแบบของคุณ นี่คือรูปร่าง:
inputs.shape
TensorShape([None, 784])
นี่คือ dtype:
inputs.dtype
tf.float32
คุณสามารถสร้างโหนดใหม่ในกราฟของชั้นโดยการเรียกชั้นบนนี้ inputs
วัตถุ:
dense = layers.Dense(64, activation="relu")
x = dense(inputs)
การกระทำ "การเรียกเลเยอร์" เหมือนกับการวาดลูกศรจาก "อินพุต" ไปยังเลเยอร์นี้ที่คุณสร้างขึ้น คุณกำลัง "ผ่าน" ปัจจัยการผลิตไปยัง dense
ชั้นและคุณได้รับ x
เป็นเอาท์พุท
มาเพิ่มอีกสองสามเลเยอร์ในกราฟของเลเยอร์:
x = layers.Dense(64, activation="relu")(x)
outputs = layers.Dense(10)(x)
ณ จุดนี้คุณสามารถสร้าง Model
โดยระบุปัจจัยการผลิตและผลในกราฟของชั้น:
model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")
ลองดูว่าสรุปแบบจำลองมีลักษณะอย่างไร:
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 _________________________________________________________________
คุณยังสามารถพล็อตโมเดลเป็นกราฟได้:
keras.utils.plot_model(model, "my_first_model.png")
และแสดงรูปร่างอินพุตและเอาต์พุตของแต่ละเลเยอร์ในกราฟที่ลงจุด (ไม่บังคับ):
keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes=True)
ตัวเลขนี้และรหัสเกือบจะเหมือนกัน ในเวอร์ชันโค้ด ลูกศรเชื่อมต่อจะถูกแทนที่ด้วยการดำเนินการโทร
"กราฟของเลเยอร์" เป็นภาพจิตที่เข้าใจได้ง่ายสำหรับโมเดลการเรียนรู้เชิงลึก และ API ที่ใช้งานได้คือวิธีสร้างแบบจำลองที่สะท้อนสิ่งนี้อย่างใกล้ชิด
การฝึกอบรม การประเมิน และการอนุมาน
การฝึกอบรมและการประเมินผลและการทำงานอนุมานว่าในทางเดียวกันสำหรับรุ่นที่สร้างขึ้นโดยใช้ API การทำงานกับการ Sequential
รุ่น
Model
คลาสเสนอตัวในวงการฝึกอบรม (คน fit()
วิธี) และในตัวห่วงการประเมินผล (การ evaluate()
วิธีการ) โปรดทราบว่าคุณสามารถ ปรับแต่งลูปเหล่านี้ ในการดำเนินการขั้นตอนการฝึกอบรมเกินกว่าการเรียนรู้ภายใต้การดูแล (เช่น Gans )
ที่นี่ โหลดข้อมูลภาพ MNIST ปรับรูปร่างเป็นเวกเตอร์ ปรับโมเดลให้พอดีกับข้อมูล (ขณะตรวจสอบประสิทธิภาพบนการแยกการตรวจสอบ) จากนั้นประเมินแบบจำลองจากข้อมูลการทดสอบ:
(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
สำหรับการอ่านเพิ่มเติมโปรดดูที่ การฝึกอบรมและการประเมินผล คู่มือ
บันทึกและจัดลำดับ
รุ่นประหยัดและเป็นอันดับที่ทำงานในลักษณะเดียวกันสำหรับรุ่นที่สร้างขึ้นโดยใช้ API หน้าที่ที่พวกเขาทำเพื่อ Sequential
รุ่น วิธีมาตรฐานในการบันทึกรูปแบบการทำงานคือการเรียก model.save()
เพื่อบันทึกรูปแบบทั้งหมดเป็นไฟล์เดียว คุณสามารถสร้างโมเดลเดียวกันจากไฟล์นี้ได้ในภายหลัง แม้ว่าโค้ดที่สร้างโมเดลจะไม่สามารถใช้ได้อีกต่อไป
ไฟล์ที่บันทึกไว้นี้รวมถึง:
- สถาปัตยกรรมจำลอง
- ค่าน้ำหนักแบบจำลอง (ที่เรียนรู้ระหว่างการฝึก)
- การตั้งค่ารูปแบบการฝึกอบรมถ้าใด ๆ (รวมทั้งที่ผ่านไป
compile
) - เครื่องมือเพิ่มประสิทธิภาพและสถานะ หากมี (เพื่อเริ่มการฝึกใหม่จากจุดที่คุณค้างไว้)
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
สำหรับรายละเอียดอ่านรูปแบบ อนุกรมและประหยัด คู่มือ
ใช้กราฟชั้นเดียวกันเพื่อกำหนดหลายรุ่น
ใน API การทำงาน โมเดลจะถูกสร้างขึ้นโดยการระบุอินพุตและเอาต์พุตในกราฟของเลเยอร์ ซึ่งหมายความว่าสามารถใช้กราฟชั้นเดียวเพื่อสร้างแบบจำลองได้หลายแบบ
ในตัวอย่างด้านล่างคุณจะใช้กองเดียวกันของชั้นเพื่อยกตัวอย่างสองรุ่น: การ encoder
รูปแบบที่ปัจจัยการผลิตเปลี่ยนภาพเป็นพาหะ 16 มิติและแบบ end-to-end autoencoder
รูปแบบสำหรับการฝึกอบรม
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 _________________________________________________________________
นี่สถาปัตยกรรมถอดรหัสเป็นอย่างเคร่งครัดสมมาตรสถาปัตยกรรมการเข้ารหัสเพื่อให้รูปร่างออกเป็นเช่นเดียวกับการป้อนข้อมูลรูปร่าง (28, 28, 1)
ย้อนกลับของ Conv2D
ชั้นเป็น Conv2DTranspose
ชั้นและย้อนกลับของการ MaxPooling2D
ชั้นเป็น UpSampling2D
ชั้น
ทุกรุ่นสามารถเรียกได้เช่นเดียวกับเลเยอร์
คุณสามารถรักษารูปแบบใด ๆ ราวกับว่ามันเป็นชั้นโดยเรียกมันใน Input
หรือการส่งออกของชั้นอื่น การเรียกแบบจำลองนั้นไม่ได้เป็นเพียงการนำสถาปัตยกรรมของแบบจำลองมาใช้ซ้ำ แต่ยังเป็นการนำน้ำหนักของแบบจำลองมาใช้ซ้ำอีกด้วย
ในการดูการทำงานจริง ต่อไปนี้คือแนวทางที่แตกต่างในตัวอย่างตัวเข้ารหัสอัตโนมัติที่สร้างโมเดลตัวเข้ารหัส โมเดลตัวถอดรหัส และเชื่อมโยงกันในการเรียกสองครั้งเพื่อรับโมเดลตัวเข้ารหัสอัตโนมัติ:
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 _________________________________________________________________
อย่างที่คุณเห็น โมเดลสามารถซ้อนกันได้: โมเดลสามารถมีโมเดลย่อยได้ (เนื่องจากโมเดลนั้นเหมือนกับเลเยอร์) กรณีการใช้งานทั่วไปสำหรับรูปแบบการทำรังอยู่ใน ensembling ตัวอย่างเช่น ต่อไปนี้คือวิธีการรวมชุดของแบบจำลองเป็นแบบจำลองเดียวที่หาค่าเฉลี่ยของการคาดการณ์:
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)
จัดการโทโพโลยีกราฟที่ซับซ้อน
โมเดลที่มีอินพุตและเอาต์พุตหลายตัว
API การทำงานทำให้ง่ายต่อการจัดการอินพุตและเอาต์พุตหลายรายการ นี้ไม่สามารถจัดการกับ Sequential
API
ตัวอย่างเช่น หากคุณกำลังสร้างระบบสำหรับการจัดอันดับตั๋วการออกตั๋วของลูกค้าตามลำดับความสำคัญและกำหนดเส้นทางไปยังแผนกที่ถูกต้อง โมเดลจะมีสามอินพุต:
- ชื่อของตั๋ว (ป้อนข้อความ)
- เนื้อความของตั๋ว (การป้อนข้อความ) และ
- แท็กใด ๆ ที่เพิ่มโดยผู้ใช้ (อินพุตตามหมวดหมู่)
โมเดลนี้จะมี 2 เอาต์พุต:
- คะแนนลำดับความสำคัญระหว่าง 0 ถึง 1 (เอาต์พุตสเกลาร์ sigmoid) และ
- แผนกที่ควรจัดการตั๋ว (เอาต์พุต softmax เหนือชุดของแผนก)
คุณสามารถสร้างโมเดลนี้ในสองสามบรรทัดด้วย API ที่ใช้งานได้:
num_tags = 12 # Number of unique issue tags
num_words = 10000 # Size of vocabulary obtained when preprocessing text data
num_departments = 4 # Number of departments for predictions
title_input = keras.Input(
shape=(None,), name="title"
) # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name="body") # Variable-length sequence of ints
tags_input = keras.Input(
shape=(num_tags,), name="tags"
) # Binary vectors of size `num_tags`
# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)
# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)
# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])
# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, name="priority")(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, name="department")(x)
# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(
inputs=[title_input, body_input, tags_input],
outputs=[priority_pred, department_pred],
)
ตอนนี้พล็อตโมเดล:
keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
เมื่อรวบรวมโมเดลนี้ คุณสามารถกำหนดความสูญเสียที่แตกต่างกันให้กับแต่ละเอาต์พุตได้ คุณยังสามารถกำหนดน้ำหนักที่แตกต่างกันให้กับการสูญเสียแต่ละครั้ง เพื่อปรับการมีส่วนสนับสนุนของการสูญเสียการฝึกทั้งหมด
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],
)
เนื่องจากเลเยอร์เอาต์พุตมีชื่อต่างกัน คุณจึงสามารถระบุการสูญเสียและน้ำหนักที่สูญเสียด้วยชื่อเลเยอร์ที่เกี่ยวข้องได้:
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},
)
ฝึกโมเดลโดยส่งรายการอาร์เรย์ 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>
เมื่อโทรพอดีกับ Dataset
วัตถุก็ควรให้ผลผลิตทั้ง tuple ของรายการเช่น ([title_data, body_data, tags_data], [priority_targets, dept_targets])
หรือ tuple ของพจนานุกรมเหมือน ({'title': title_data, 'body': body_data, 'tags': tags_data}, {'priority': priority_targets, 'department': dept_targets})
สำหรับคำอธิบายรายละเอียดเพิ่มเติมโปรดดูที่ การฝึกอบรมและการประเมินผล คู่มือ
โมเดลของเล่น ResNet
นอกเหนือไปจากรุ่นที่มีหลายปัจจัยการผลิตและผลผลิต API ของการทำงานที่ทำให้มันง่ายต่อการจัดการโครงสร้างการเชื่อมต่อที่ไม่ใช่เชิงเส้น - เหล่านี้เป็นรุ่นที่มีเลเยอร์ที่ไม่ได้เชื่อมต่อตามลำดับซึ่ง Sequential
API ไม่สามารถจัดการ
กรณีใช้งานทั่วไปสำหรับสิ่งนี้คือการเชื่อมต่อที่เหลือ มาสร้างโมเดลของเล่น ResNet สำหรับ CIFAR10 เพื่อสาธิตสิ่งนี้:
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 __________________________________________________________________________________________________
พล็อตโมเดล:
keras.utils.plot_model(model, "mini_resnet.png", show_shapes=True)
ตอนนี้ฝึกโมเดล:
(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>
เลเยอร์ที่ใช้ร่วมกัน
ควบคุมการใช้งานที่ดีสำหรับ API การทำงานเป็นรูปแบบที่ใช้เลเยอร์ที่ใช้ร่วมกัน เลเยอร์ที่ใช้ร่วมกันคืออินสแตนซ์ของเลเยอร์ที่ใช้ซ้ำหลายครั้งในโมเดลเดียวกัน โดยจะเรียนรู้คุณลักษณะที่สอดคล้องกับหลายเส้นทางในกราฟของเลเยอร์
เลเยอร์ที่ใช้ร่วมกันมักใช้เพื่อเข้ารหัสอินพุตจากช่องว่างที่คล้ายคลึงกัน (เช่น ข้อความสองส่วนที่มีคำศัพท์คล้ายกัน) พวกเขาเปิดใช้งานการแบ่งปันข้อมูลผ่านอินพุตต่างๆ เหล่านี้ และทำให้สามารถฝึกโมเดลดังกล่าวโดยใช้ข้อมูลน้อยลง หากพบคำที่กำหนดในอินพุตตัวใดตัวหนึ่ง ก็จะเป็นประโยชน์ต่อการประมวลผลอินพุตทั้งหมดที่ผ่านเลเยอร์ที่ใช้ร่วมกัน
หากต้องการแชร์เลเยอร์ใน API การทำงาน ให้เรียกใช้อินสแตนซ์เลเยอร์เดียวกันหลายครั้ง ยกตัวอย่างเช่นที่นี่เป็น Embedding
ชั้นที่ใช้ร่วมกันทั่วทั้งสองปัจจัยข้อความที่แตกต่างกัน
# 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)
แยกและนำโหนดมาใช้ซ้ำในกราฟของเลเยอร์
เนื่องจากกราฟของเลเยอร์ที่คุณกำลังจัดการนั้นเป็นโครงสร้างข้อมูลแบบคงที่ คุณจึงสามารถเข้าถึงและตรวจสอบได้ และนี่คือวิธีที่คุณสามารถพล็อตโมเดลการทำงานเป็นรูปภาพได้
นอกจากนี้ยังหมายความว่าคุณสามารถเข้าถึงการเปิดใช้งานของเลเยอร์ระดับกลาง ("โหนด" ในกราฟ) และนำกลับมาใช้ใหม่ในที่อื่นได้ ซึ่งมีประโยชน์มากสำหรับบางอย่าง เช่น การดึงข้อมูลคุณลักษณะ
มาดูตัวอย่างกัน นี่คือรุ่น VGG19 พร้อมน้ำหนักที่ได้รับการฝึกฝนบน 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
และนี่คือการเปิดใช้งานระดับกลางของโมเดล ซึ่งได้จากการสืบค้นโครงสร้างข้อมูลกราฟ:
features_list = [layer.output for layer in vgg19.layers]
ใช้คุณสมบัติเหล่านี้เพื่อสร้างโมเดลการแยกคุณลักษณะใหม่ที่คืนค่าของการเปิดใช้งานเลเยอร์กลาง:
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)
นี้มาในที่มีประโยชน์สำหรับงานต่างๆเช่นการ ถ่ายโอนสไตล์ประสาท เหนือสิ่งอื่นใด
ขยาย API โดยใช้เลเยอร์ที่กำหนดเอง
tf.keras
รวมถึงความหลากหลายในตัวชั้นตัวอย่างเช่น:
- ชั้นสับสน:
Conv1D
,Conv2D
,Conv3D
,Conv2DTranspose
- ชั้นการรวม:
MaxPooling1D
,MaxPooling2D
,MaxPooling3D
,AveragePooling1D
- ชั้น RNN:
GRU
,LSTM
,ConvLSTM2D
-
BatchNormalization
,Dropout
,Embedding
, ฯลฯ
แต่ถ้าคุณไม่พบสิ่งที่คุณต้องการ การขยาย API นั้นง่ายด้วยการสร้างเลเยอร์ของคุณเอง ทุกชั้นซับคลาสตัว Layer
เรียนและการดำเนินการ:
-
call
วิธีการระบุว่าการคำนวณที่ทำโดยชั้น -
build
วิธีการที่สร้างน้ำหนักของชั้น (นี้เป็นเพียงการประชุมสไตล์ตั้งแต่คุณสามารถสร้างน้ำหนักใน__init__
เช่นเดียว)
ต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการสร้างชั้นจากรอยขีดข่วนอ่าน ชั้นที่กำหนดเองและรูปแบบ คู่มือ
ต่อไปนี้คือการดำเนินการขั้นพื้นฐานของ 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)
สำหรับการสนับสนุนเป็นอันดับในชั้นที่กำหนดเองของคุณกำหนด get_config
วิธีการที่จะส่งกลับข้อโต้แย้งสร้างของอินสแตนซ์ชั้น:
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})
เลือกใช้วิธีการเรียน from_config(cls, config)
ซึ่งจะใช้เมื่อมีการสร้างอินสแตนซ์ชั้นที่ได้รับการตั้งค่าของพจนานุกรม เริ่มต้นใช้งาน from_config
คือ:
def from_config(cls, config):
return cls(**config)
เมื่อใดควรใช้ API ที่ใช้งานได้
คุณควรใช้ Keras ทำงาน API เพื่อสร้างรูปแบบใหม่หรือเพียงแค่ซับคลาสตัว Model
คลาสโดยตรง? โดยทั่วไป API ที่ใช้งานได้จะมีระดับที่สูงกว่า ง่ายขึ้น และปลอดภัยกว่า และมีคุณสมบัติหลายอย่างที่โมเดลย่อยไม่รองรับ
อย่างไรก็ตาม การจัดคลาสย่อยของแบบจำลองนั้นให้ความยืดหยุ่นมากขึ้นเมื่อสร้างแบบจำลองที่ไม่สามารถอธิบายได้ง่ายเป็นกราฟชั้นแบบวนซ้ำที่กำหนดทิศทาง ตัวอย่างเช่นคุณไม่สามารถใช้ต้นไม้ RNN กับ API การทำงานและจะต้องซับคลาส Model
โดยตรง
สำหรับการมองในเชิงลึกที่แตกต่างระหว่างการทำงานและ API รุ่น subclassing อ่าน สิ่งที่เป็นสัญลักษณ์ APIs และความจำเป็นใน TensorFlow 2.0? .
จุดแข็งของ API การทำงาน:
คุณสมบัติต่อไปนี้เป็นจริงสำหรับโมเดลตามลำดับ (ซึ่งเป็นโครงสร้างข้อมูลด้วย) แต่ไม่เป็นจริงสำหรับโมเดลย่อย (ซึ่งเป็น Python bytecode ไม่ใช่โครงสร้างข้อมูล)
ละเอียดน้อยลง
ไม่มีเป็น super(MyClass, self).__init__(...)
ไม่มี def call(self, ...):
ฯลฯ
เปรียบเทียบ:
inputs = keras.Input(shape=(32,))
x = layers.Dense(64, activation='relu')(inputs)
outputs = layers.Dense(10)(x)
mlp = keras.Model(inputs, outputs)
ด้วยรุ่นย่อย:
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)))
การตรวจสอบแบบจำลองขณะกำหนดกราฟการเชื่อมต่อ
ใน API ทำงานเปคอินพุท (รูปร่างและ dtype) ถูกสร้างขึ้นในล่วงหน้า (โดยใช้ Input
) ทุกครั้งที่คุณเรียกใช้เลเยอร์ เลเยอร์จะตรวจสอบว่าข้อกำหนดที่ส่งไปยังเลเยอร์นั้นตรงกับข้อสันนิษฐานหรือไม่ และจะแจ้งข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์หากไม่เป็นเช่นนั้น
สิ่งนี้รับประกันได้ว่าแบบจำลองใดๆ ที่คุณสามารถสร้างด้วย API การทำงานจะทำงานได้ การดีบักทั้งหมด - นอกเหนือจากการดีบักที่เกี่ยวข้องกับคอนเวอร์เจนซ์ - เกิดขึ้นแบบคงที่ระหว่างการสร้างโมเดลและไม่ใช่ในเวลาดำเนินการ คล้ายกับการตรวจสอบประเภทในคอมไพเลอร์
โมเดลการทำงานสามารถวางแผนและตรวจสอบได้
คุณสามารถพล็อตโมเดลเป็นกราฟ และคุณสามารถเข้าถึงโหนดระดับกลางในกราฟนี้ได้อย่างง่ายดาย ตัวอย่างเช่น เพื่อแยกและนำการเปิดใช้งานของเลเยอร์กลางมาใช้ซ้ำ (ดังที่เห็นในตัวอย่างก่อนหน้านี้):
features_list = [layer.output for layer in vgg19.layers]
feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)
โมเดลการทำงานสามารถทำให้เป็นอนุกรมหรือโคลนได้
เนื่องจากโมเดลการทำงานเป็นโครงสร้างข้อมูลแทนที่จะเป็นชิ้นส่วนของโค้ด จึงทำให้สามารถซีเรียลไลซ์ได้อย่างปลอดภัยและสามารถบันทึกเป็นไฟล์เดียวที่ช่วยให้คุณสร้างโมเดลเดียวกันได้ใหม่ทุกประการโดยไม่ต้องเข้าถึงโค้ดต้นฉบับใดๆ ดู อนุกรมและประหยัดคู่มือ
เป็นอันดับรูปแบบ subclassed ก็เป็นสิ่งจำเป็นสำหรับผู้ดำเนินเพื่อระบุ get_config()
และ from_config()
วิธีการในระดับรูปแบบ
จุดอ่อนของ API การทำงาน:
ไม่รองรับสถาปัตยกรรมแบบไดนามิก
API การทำงานจะถือว่าโมเดลเป็น DAG ของเลเยอร์ สิ่งนี้เป็นจริงสำหรับสถาปัตยกรรมการเรียนรู้เชิงลึกส่วนใหญ่ แต่ไม่ใช่ทั้งหมด ตัวอย่างเช่น เครือข่ายแบบเรียกซ้ำหรือ Tree RNN ไม่เป็นไปตามสมมติฐานนี้และไม่สามารถนำไปใช้ใน API การทำงานได้
รูปแบบ API แบบผสมผสานและจับคู่
การเลือกระหว่าง Functional API หรือ Model subclassing ไม่ใช่การตัดสินใจแบบไบนารีที่จำกัดคุณให้อยู่ในหมวดหมู่หนึ่งของโมเดล ทุกรุ่นใน tf.keras
API สามารถโต้ตอบกับแต่ละอื่น ๆ ไม่ว่าจะเป็น Sequential
รูปแบบรูปแบบการทำงานหรือรุ่น subclassed ที่เขียนขึ้นจากรอยขีดข่วน
คุณสามารถใช้รูปแบบการทำงานหรือ Sequential
รุ่นเป็นส่วนหนึ่งของรูปแบบ subclassed หรือชั้น:
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)
คุณสามารถใช้ชั้นใด subclassed หรือรุ่นใน API ทำงานตราบเท่าที่มันดำเนิน call
วิธีการที่เป็นไปตามรูปแบบหนึ่งต่อไปนี้:
-
call(self, inputs, **kwargs)
- ที่inputs
เป็นเมตริกซ์หรือโครงสร้างที่ซ้อนกันของเทนเซอร์ (เช่นรายการเทนเซอร์) และที่**kwargs
ข้อโต้แย้งที่ไม่เมตริกซ์ (ไม่ใช่ปัจจัยการผลิต) -
call(self, inputs, training=None, **kwargs)
- ที่training
เป็นแบบบูลระบุว่าชั้นควรประพฤติตนอยู่ในโหมดการฝึกอบรมและโหมดการอนุมาน -
call(self, inputs, mask=None, **kwargs)
- ที่ไหนmask
เป็นหน้ากากเมตริกซ์บูล (มีประโยชน์สำหรับ RNNs เป็นต้น) -
call(self, inputs, training=None, mask=None, **kwargs)
- แน่นอนคุณสามารถมีทั้งกาวและพฤติกรรมการฝึกอบรมเฉพาะในเวลาเดียวกัน
นอกจากนี้หากคุณใช้ get_config
วิธีการในชั้นที่กำหนดเองหรือรูปแบบของคุณในรูปแบบการทำงานที่คุณสร้างจะยังคงเป็น serializable และ cloneable
ต่อไปนี้คือตัวอย่างสั้นๆ ของ RNN ที่กำหนดเอง ซึ่งเขียนขึ้นใหม่ทั้งหมด ซึ่งใช้ในแบบจำลองการทำงาน:
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)))