![]() | ![]() | ![]() | ![]() |
يثبت
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)))