واجهة برمجة التطبيقات الوظيفية

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يثبت

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

مقدمة

وAPI وظيفية Keras هو وسيلة لخلق نماذج التي هي أكثر مرونة من tf.keras.Sequential API. يمكن لواجهة برمجة التطبيقات الوظيفية التعامل مع النماذج ذات الهيكل غير الخطي والطبقات المشتركة وحتى المدخلات أو المخرجات المتعددة.

الفكرة الرئيسية هي أن نموذج التعلم العميق هو عادة رسم بياني لا دوري موجه (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)

هذا رسم بياني أساسي من ثلاث طبقات. لإنشاء هذا النموذج باستخدام واجهة برمجة التطبيقات الوظيفية ، ابدأ بإنشاء عقدة إدخال:

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 ظيفية ول 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 الأبعاد، ونهاية إلى نهاية 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)

معالجة طبولوجيا الرسم البياني المعقدة

النماذج ذات المدخلات والمخرجات المتعددة

تجعل واجهة برمجة التطبيقات الوظيفية من السهل التعامل مع المدخلات والمخرجات المتعددة. هذا لا يمكن أن يتم التعامل مع Sequential API.

على سبيل المثال ، إذا كنت تقوم ببناء نظام لترتيب تذاكر إصدار العملاء حسب الأولوية وتوجيهها إلى القسم الصحيح ، فسيكون للنموذج ثلاثة مدخلات:

  • عنوان التذكرة (إدخال نص) ،
  • نص التذكرة (إدخال نص) ، و
  • أي علامات أضافها المستخدم (إدخال فئوي)

سيكون لهذا النموذج ناتجين:

  • درجة الأولوية بين 0 و 1 (الناتج السيني القياسي) ، و
  • القسم الذي يجب أن يتعامل مع التذكرة (إخراج softmax عبر مجموعة الأقسام).

يمكنك بناء هذا النموذج في بضعة أسطر باستخدام واجهة برمجة التطبيقات الوظيفية:

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 ، الخ

ولكن إذا لم تجد ما تحتاج إليه ، فمن السهل توسيع واجهة برمجة التطبيقات عن طريق إنشاء طبقاتك الخاصة. جميع طبقات فرعية و 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 الوظيفية

يجب عليك استخدام API وظيفية Keras لخلق نموذج جديد، أو مجرد فئة فرعية Model الطبقة مباشرة؟ بشكل عام ، تعد واجهة برمجة التطبيقات الوظيفية عالية المستوى وأسهل وأكثر أمانًا ، وتحتوي على عدد من الميزات التي لا تدعمها النماذج المصنفة تحت الفئات الفرعية.

ومع ذلك ، فإن التصنيف الفرعي للنموذج يوفر قدرًا أكبر من المرونة عند بناء النماذج التي لا يمكن التعبير عنها بسهولة مثل الرسوم البيانية الحلقية الموجهة للطبقات. على سبيل المثال، لا يمكن تنفيذ شجرة-RNN مع API الوظيفي ويجب أن فئة فرعية Model مباشرة.

للحصول على نظرة معمقة على الاختلافات بين API الوظيفي ونموذج شاء subclasses ترث، وقراءة ما هي واجهات برمجة التطبيقات رمزي وحتميه في TensorFlow 2.0؟ .

نقاط القوة الوظيفية لواجهة برمجة التطبيقات:

الخصائص التالية صحيحة أيضًا للنماذج المتسلسلة (وهي أيضًا هياكل بيانات) ، ولكنها ليست صحيحة بالنسبة للنماذج المصنفة تحت فئة فرعية (وهي 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 ). في كل مرة تقوم فيها باستدعاء طبقة ، تتحقق الطبقة من أن المواصفات التي تم تمريرها إليها تطابق افتراضاتها ، وسوف تظهر رسالة خطأ مفيدة إذا لم تكن كذلك.

يضمن هذا تشغيل أي نموذج يمكنك إنشاؤه باستخدام واجهة برمجة التطبيقات الوظيفية. تحدث جميع عمليات التصحيح - بخلاف التصحيح المرتبط بالتقارب - بشكل ثابت أثناء إنشاء النموذج وليس في وقت التنفيذ. هذا مشابه لنوع التحقق في المترجم.

النموذج الوظيفي قابل للتخطيط والتفتيش

يمكنك رسم النموذج كرسم بياني ، ويمكنك الوصول بسهولة إلى العقد الوسيطة في هذا الرسم البياني. على سبيل المثال ، لاستخراج وإعادة تنشيط الطبقات الوسيطة (كما رأينا في المثال السابق):

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 الوظيفية النماذج على أنها DAGs من الطبقات. هذا صحيح بالنسبة لمعظم بنيات التعلم العميق ، ولكن ليس كلها - على سبيل المثال ، لا تتبع الشبكات العودية أو Tree RNNs هذا الافتراض ولا يمكن تنفيذها في واجهة برمجة التطبيقات الوظيفية.

مزيج وتطابق أنماط 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 الطريقة على طبقة مخصصة أو نموذج، والنماذج الوظيفية إنشاء سيظل للتسلسل و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)))