ช่วยปกป้อง Great Barrier Reef กับ TensorFlow บน Kaggle เข้าร่วมท้าทาย

API การทำงาน

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

ติดตั้ง

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

png

และแสดงรูปร่างอินพุตและเอาต์พุตของแต่ละเลเยอร์ในกราฟที่ลงจุด (ไม่บังคับ):

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

png

ตัวเลขนี้และรหัสเกือบจะเหมือนกัน ในเวอร์ชันโค้ด ลูกศรเชื่อมต่อจะถูกแทนที่ด้วยการดำเนินการโทร

"กราฟของเลเยอร์" เป็นภาพจิตที่เข้าใจได้ง่ายสำหรับโมเดลการเรียนรู้เชิงลึก และ 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)

png

เมื่อรวบรวมโมเดลนี้ คุณสามารถกำหนดความสูญเสียที่แตกต่างกันให้กับแต่ละเอาต์พุตได้ คุณยังสามารถกำหนดน้ำหนักที่แตกต่างกันให้กับการสูญเสียแต่ละครั้ง เพื่อปรับการมีส่วนสนับสนุนของการสูญเสียการฝึกทั้งหมด

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)

png

ตอนนี้ฝึกโมเดล:

(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 แบบผสมผสานและจับคู่

การเลือกระหว่าง API การทำงานหรือคลาสย่อยของโมเดลไม่ใช่การตัดสินใจแบบไบนารีที่จำกัดคุณให้อยู่ในโมเดลประเภทเดียว ทุกรุ่นใน 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)))