subclassing של שכבות ומודלים בספריית Keras

מחבר:
בתאריך:

אחרי שעבדנו עם ה-API הסדרתי sequential של Keras שהוא הכי ידידותי וגם הכי פחות גמיש ועם ה-API הפונקציונלי שהוא פחות ידידותי ויותר גמיש, במדריך זה נלמד לעשות subclassing של שכבות ומודלים.

הדוגמה במדריך מבוססת על מדריך קודם זיהוי ספרות שכתב אדם על ידי בינה מלאכותית שם למדנו להשתמש ברשת נוירונית מבוססת קונבולוציה CNN, Convolutional Neural Network לעבודה עם תמונות. רשת CNN בנויה מאחד או יותר בלוקים של קונבולוציה שכל אחד מהם מורכב משכבה אחת או יותר של קונבולוציה שאחריה שכבת pooling. במדריך הסתפקנו בשתי שכבות בגלל שהמשימה היתה פשוטה: סיווג תמונות של ספרות קטנות (28X28 פיקסלים) שאנשים כתבו בכתב יד לאחת מ-10 קטגוריות (0 - 9). במציאות, יכולים להיות הרבה יותר קטגוריות וגם התמונות יכולות להיות גדולות הרבה יותר מה שדורש יותר בלוקים של קונבולוציה וגם ארכיטקטורה מורכבת. אפשר לחסוך חלק מהמורכבות אם מתייחסים לבלוק של הקונבולוציה בתור מודול ואז משלבים כמה מודולים כאלה בבניית הרשת הנוירונית. כדי לעשות זאת נשתמש ב-API ה-subclassing של Keras. וזו רק דוגמה לשימוש ב- subclassing לצורך בניית מודלים מורכבים של למידת מכונה.

subclassing of keras models and layers for machine learning

למדריך זה שני חלקים. בחלק הראשון נלמד לעשות subclassing של שכבות. בשני subclassing של מודלים.

להורדת הקוד אותו נפתח במדריך

נייבא את הספריות הדרושות למדריך:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from tensorflow import keras
from keras import layers

נייבא את מסד הנתונים MNISTהכולל 70,000 תמונות של ספרות שכתבו אנשים שעברו סטנדרטיזציה לגודל של 28 על 28 פיקסלים בצבעים אפורים. 60,000 תמונות ישמשו לאימון והיתר למבחן:

from keras.datasets import mnist

# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()

את התמונות נעצב מחדש לתמונות שגודלם 28X28 פיקסלים עם ערוץ צבע 1. את התוצאה נחלק ב-225 כדי לנרמל:

# reshape to be [samples][pixels][width][height][channels_number]
# float32 is the data type that keras expects
img_width=28
img_height=28
X_train = X_train.reshape(X_train.shape[0], img_width, img_height, 1).astype('float32')
X_test = X_test.reshape(X_test.shape[0], img_width, img_height, 1).astype('float32')

# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255

את ערכי ה-y נקודד בשיטת one hot encode:

# one hot encode outputs
from keras.utils.np_utils import to_categorical

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

 

Subclassing של layers

ספריית Keras מצוידת במגוון של שכבות layers דוגמת: Dense ו-Conv2D, שנכתבו על ידי מיטב המומחים בתחום ועונים על מגוון רחב של צרכים בתחום למידת המכונה אבל כשרוצים שכבה שלא קיימת בספרייה פונים ל-subclassing.

רשת CNN בנויה מאחד או יותר בלוקים של קונבולוציה שכל אחד מהם מורכב משכבה אחת או יותר של קונבולוציה שאחריה שכבת pooling כשבסוף אפשר להוסיף שכבת dropout. במדריך השתמשנו בשני בלוקים של קונבולוציה כי המשימה פשוטה. במודלים מתוחכמים אנחנו יכולים למצוא 5 בלוקים של קונבולוציה (לדוגמה, VGG16), ואף יותר. במקום לכתוב את הבלוקים כל אחד בנפרד אנחנו יכולים ליצור subclass של layer שהוא בלוק קונבולוציה ואח"כ להשתמש בו כמה שצריך.

כדי להדגים subclassing של layers ניצור שכבה layer שתהיה בלוק קונבולוציה המורכב מ-3 שכבות:

  • Conv2D
  • Pooling
  • Dropout

שכבה שיוצרים באמצעות subclassing מיישמת את keras.layers.Layer והיא כוללת שתי מתודות הכרחיות: __init__() ו-call():

class ConvBlock(keras.layers.Layer):
   def __init__(self):
       super().__init__()
       pass

   def call(self, input_tensor):
       pass

ההסבר המדויק ביותר הוא שהמתודה __init__() מחזיקה את המשקלות, בעוד המתודה call() מבצעת את החישובים forward pass.

נכתוב את זה:

class ConvBlock(keras.layers.Layer):
   def __init__(self, out_channels, kernel_size=3):
       super().__init__()
       self.conv = layers.Conv2D(out_channels, kernel_size, activation="relu", padding="same")
       self.max_pooling = layers.MaxPooling2D(pool_size=(2, 2))
       self.dropout = layers.Dropout(0.2)

   def call(self, input_tensor):
       x = self.conv(input_tensor)
       x = self.max_pooling(x)
       x = self.dropout(x)
       return x
  • במתודה __init__() הגדרנו את השכבות: Conv2D, MaxPooling2D ,Dropout.
  • באמצעות המתודה call() נעביר את הטנסורים דרך forward pass העובר מחישוב לחישוב (אצלנו משכבה לשכבה) על פי הסדר הרצוי, כאשר בסוף המתודה מחזירה את הטנסור המתקבל בתהליך החישוב.

עכשיו שיש לנו שכבה שהיא מודול אנחנו יכולים להשתמש בה איך שנרצה. לדוגמה, לבניית מודל במסגרת API מסוג Sequential:

model = keras.Sequential(
   [
     ConvBlock(32),
     ConvBlock(64),
     layers.Flatten(),
     layers.Dense(128, activation="relu"),
     layers.Dense(10, activation="softmax")
   ]
)

השימוש ב-subclassing אפשר לנו לכתוב מודל קומפקטי וקריא יותר בהשוואה לכתיבת אותה רשת באמצעות API מסוג Sequential בלבד כפי שאפשר לראות במדריך "זיהוי ספרות שכתב אדם על ידי בינה מלאכותית".

נקמפל את המודל:

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

נריץ:

model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=3, batch_size=100)

נעריך את ביצועי המודל:

results = model.evaluate(X_test, y_test)
print("loss: {0:.3f}".format(results[0]))
print("accuracy: {0:.3f}".format(results[1]))
313/313 [==============================] - 1s 3ms/step - loss: 0.0357 - accuracy: 0.9890

loss: 0.036
accuracy: 0.989

 

Subclassing של מודלים

כפי שאנו יכולים לעשות subclassing עבור שכבות של מודל Keras אנחנו יכולים לעשות subclassing של מודלים.

הקלאס צריך ליישם את keras.Model ולכלול 2 מתודות: __init__() ו-call():

  • בתוך המתודה __init__() נגדיר את השכבות בהם המודל צריך להשתמש.
  • במתודה call() מגדירים את ה-forward pass של המודל במסגרתו הוא מעביר את הטנסורים דרך השכבות.

נכתוב subclass לסיווג תמונות:

class ImageClassificationModel(keras.Model):
   def __init__(self, num_classes):
       super().__init__()
       self.conv1 = ConvBlock(32)
       self.conv2 = ConvBlock(64)
       self.flatten = layers.Flatten()
       self.dense1 = layers.Dense(128, activation="relu")
       self.classifier = layers.Dense(num_classes, activation="softmax")
   def call(self, input_tensor):
       x = self.conv1(input_tensor)
       x = self.conv2(x)
       x = self.flatten(x)
       x = self.dense1(x)
       x = self.classifier(x)
       return x

יצירת subclass של model דומה מאוד ליצירת שכבה באמצעות subclassing. הבדל עקרוני הוא שספריית Keras מאפשרת לנו להפעיל את המתודות: fit(), evaluate() ו-predict() על מודלים ולא על שכבות.

נאתחל את המודל:

model = ImageClassificationModel(num_classes=10)

נקמפל ונריץ:

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=3, batch_size=100)

אחת המגבלות של subclassing היא שרק לאחר שמריצים את המודל אפשר להציג סיכום שלו באמצעות המתודה summary():

model.summary()

summary of a subclassed model

וגם אז התמונה שמקבלים אודות מבנה הרשת הוא חלקי בגלל שאין גרף. זו גם הסיבה שאי אפשר לגשת לשכבה מסוימת כדי לעשות feature extraction כפי שניתן לעשות עם ה-API הפונקציונלי.

נעריך את ביצועי המודל:

results = model.evaluate(X_test, y_test)
print("loss: {0:.3f}".format(results[0]))
print("accuracy: {0:.3f}".format(results[1]))
loss: 0.030
accuracy: 0.989

 

סיכום

subclassing של מודלים של Keras היא הגישה הגמישה ביותר. אחריה לפי סדר יורד ה-API הפונקציונלי והסדרתי sequential. אבל לצד הגמישות subclassing של מודלים סובל מחסרונות רציניים כדוגמת בעיה לדבג, קושי בתיאור הגרף ושימוש חוזר בשכבות. אם צריך להשתמש בגישה זו אז רק כשבאמת אין ברירה. לרוב השימושים, ה-API הפונקציונלי וה-sequential מספיקים בהחלט.

להורדת הקוד אותו פיתחנו במדריך

 

אולי גם זה יעניין אותך

שימוש ב-TensorBoard לניטור מודלים של למידת מכונה

שימוש ב-TensorBoard לניטור מודלים של למידת מכונה

מה רואות שכבות הביניים כשעושים למידת מכונה עם מודל מבוסס CNN

 

לכל המדריכים בנושא של למידת מכונה

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 5 + 2