צפה בהערות מרכזיות, הפעלות מוצר, סדנאות ועוד מ- Google I / O ראה רשימת השמעה

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

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

שכבות עיבוד מקדמות של קרס

ממשק ה- API של שכבות העיבוד המוקדם של Keras מאפשר למפתחים לבנות צינורות עיבוד קלט מקוריים של Keras. צינורות עיבוד קלט אלה יכולים לשמש כקוד עצמאי לעיבוד מקדים בתהליכי עבודה שאינם של Keras, בשילוב ישיר עם דגמי Keras, וייצואם כחלק ממודל Keras SavedModel.

בעזרת שכבות עיבוד מקדימות של Keras, ניתן לבנות ולייצא מודלים שהם ממש מקצה לקצה: מודלים המקבלים תמונות גולמיות או נתונים מובנים גולמיים כקלט; מודלים המטפלים בנורמליזציה של תכונות או אינדקס ערכי תכונות לבד.

שכבות עיבוד מקדימות זמינות

שכבות עיבוד מקדימות ליבה

  • שכבת TextVectorization : הופכת מחרוזות גולמיות לייצוג מקודד שניתן לקרוא על ידי שכבת Embedding או שכבה Dense .
  • שכבת Normalization : מבצעת נורמליזציה תכונתית של תכונות קלט.

שכבות עיבוד מקדים של נתונים מובנים

שכבות אלה מיועדות לקידוד נתונים מובנים ולהנדסת תכונות.

  • שכבת קידוד של CategoryEncoding : הופכת תכונות קטגוריות שלמות למספר ייצוגים צפופים אחד-חם, רב-חם או TF-IDF.
  • שכבת Hashing : מבצעת hashing תכונות קטגוריות, המכונה גם "טריק hashing".
  • שכבת Discretization : הופכת תכונות מספריות רציפות לתכונות קטגוריות שלמות.
  • שכבת StringLookup : הופכת ערכים קטגוריים של מחרוזות למדדי מספרים שלמים.
  • שכבת IntegerLookup : הופכת ערכים קטגוריים שלמים למדדי מספרים שלמים.
  • שכבת חוצה CategoryCrossing : משלב תכונות קטגוריות לתכונות התרחשות משותפת. למשל, אם יש לך ערכי תכונות "a" ו- "b", היא יכולה לספק את התכונה המשולבת "a ו- b קיימים בו זמנית".

שכבות עיבוד מקדים של תמונה

שכבות אלה מיועדות לתקינה של כניסות של מודל תמונה.

  • Resizing שכבה: Resizing גודל אצווה של תמונות לגודל יעד.
  • שכבת Rescaling : מגדילה מחדש ומקזזת את הערכים של קבוצת תמונות (למשל עוברים מקלטים בטווח [0, 255] לקלטים בטווח [0, 1] .
  • שכבת CenterCrop : מחזירה יבול מרכז של קבוצת תמונות.

שכבות הגדלת נתוני תמונה

שכבות אלה מיישמות טרנספורמציות הגדלה אקראיות למנת תמונות. הם פעילים רק במהלך האימון.

  • שכבת RandomCrop
  • שכבת RandomFlip
  • שכבת תרגום RandomTranslation
  • שכבת RandomRotation
  • שכבת RandomZoom
  • שכבת RandomHeight
  • שכבת RandomWidth

שיטת ה- adapt()

לחלק משכבות העיבוד המקדים יש מצב פנימי שיש לחשב אותו על סמך מדגם של נתוני האימון. רשימת השכבות המלאכותיות לעיבוד מקדים היא:

  • TextVectorization : מחזיק מיפוי בין אסימוני מחרוזות למדדי מספרים שלמים
  • Normalization : מחזיקה בסטיית הממוצע והסטנדרט של התכונות
  • StringLookup ו- IntegerLookup : החזק מיפוי בין ערכי קלט ומדדי פלט.
  • CategoryEncoding : מכיל אינדקס של ערכי קלט.
  • Discretization : מחזיק מידע על גבולות דלי ערך.

באופן מכריע, שכבות אלה אינן ניתנות להכשרה . מצבם אינו נקבע במהלך האימונים; זה צריך להיות מוגדר לפני האימון , צעד שנקרא "הסתגלות".

אתה מגדיר את המצב של שכבת עיבוד מקדימה על ידי חשיפתה לנתוני אימון, באמצעות שיטת adapt() :

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

data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],])
layer = preprocessing.Normalization()
layer.adapt(data)
normalized_data = layer(data)

print("Features mean: %.2f" % (normalized_data.numpy().mean()))
print("Features std: %.2f" % (normalized_data.numpy().std()))
Features mean: 0.00
Features std: 1.00

השיטה adapt adapt() לוקחת מערך Numpy או אובייקטtf.data.Dataset . במקרה של StringLookup ו- TextVectorization , תוכל גם להעביר רשימה של מחרוזות:

data = [
    "ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι",
    "γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.",
    "δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:",
    "αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:",
    "τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,",
    "οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:",
    "οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,",
    "οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.",
]
layer = preprocessing.TextVectorization()
layer.adapt(data)
vectorized_text = layer(data)
print(vectorized_text)
tf.Tensor(
[[37 12 25  5  9 20 21  0  0]
 [51 34 27 33 29 18  0  0  0]
 [49 52 30 31 19 46 10  0  0]
 [ 7  5 50 43 28  7 47 17  0]
 [24 35 39 40  3  6 32 16  0]
 [ 4  2 15 14 22 23  0  0  0]
 [36 48  6 38 42  3 45  0  0]
 [ 4  2 13 41 53  8 44 26 11]], shape=(8, 9), dtype=int64)

בנוסף, שכבות הניתנות להתאמה תמיד חושפות אפשרות להגדרת מצב ישירות באמצעות ארגומנטים של קונסטרוקטור או הקצאת משקל. אם ערכי המצב המיועדים ידועים בזמן בניית השכבה, או מחושבים מחוץ לשיחת adapt() , ניתן להגדיר אותם מבלי להסתמך על החישוב הפנימי של השכבה. למשל, אם כבר קיימים קבצי אוצר מילים חיצוניים TextVectorization StringLookup , StringLookup או IntegerLookup , ניתן לטעון אותם ישירות לטבלאות החיפוש על ידי העברת נתיב לקובץ אוצר המילים בטיעוני הבנאי של השכבה.

הנה דוגמה שבה אנו StringLookup שכבת StringLookup עם אוצר מילים StringLookup מראש:

vocab = ["a", "b", "c", "d"]
data = tf.constant([["a", "c", "d"], ["d", "z", "b"]])
layer = preprocessing.StringLookup(vocabulary=vocab)
vectorized_data = layer(data)
print(vectorized_data)
tf.Tensor(
[[2 4 5]
 [5 1 3]], shape=(2, 3), dtype=int64)

עיבוד מקדים של נתונים לפני המודל או בתוך המודל

ישנן שתי דרכים בהן אתה יכול להשתמש בשכבות לעיבוד מקדים:

אפשרות 1: הפוך אותם לחלק מהדגם, כך:

inputs = keras.Input(shape=input_shape)
x = preprocessing_layer(inputs)
outputs = rest_of_the_model(x)
model = keras.Model(inputs, outputs)

עם אפשרות זו, עיבוד מקדים יתרחש במכשיר, באופן סינכרוני עם שאר ביצועי הדגם, כלומר ייהנה מהאצת GPU. אם אתה מתאמן על GPU, זו האפשרות הטובה ביותר לשכבת Normalization , ולכל שכבות העיבוד המקדימות של התמונה והגדלת הנתונים.

אפשרות 2: החל אותה עלtf.data.Dataset שלך, כדי להשיג מערך נתונים שמניב קבוצות של נתוניםtf.data.Dataset מראש, כמו זה:

dataset = dataset.map(
  lambda x, y: (preprocessing_layer(x), y))

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

זוהי האפשרות הטובה ביותר עבור TextVectorization , וכל שכבות TextVectorization המוקדם של נתונים מובנים. זה יכול להיות גם אפשרות טובה אם אתה מתאמן על מעבד ומשתמש בשכבות עיבוד מקדימות של תמונות.

היתרונות של ביצוע עיבוד מקדים בתוך המודל בזמן ההסקה

גם אם תלך עם אפשרות 2, ייתכן שתרצה מאוחר יותר לייצא מודל מקצה לקצה מסקנה בלבד שיכלול את שכבות העיבוד המקדים. היתרון העיקרי לעשות זאת הוא בכך שהוא הופך את המודל שלך לנייד וזה עוזר להפחית את הטיית האימון / הגשה .

כאשר כל עיבוד מקדים של נתונים הוא חלק מהמודל, אנשים אחרים יכולים לטעון ולהשתמש במודל שלך מבלי להיות מודעים לאופן שבו כל תכונה צפויה להיות מקודדת ונורמלית. מודל ההיסק שלך יוכל לעבד תמונות גולמיות או נתונים מובנים גולמיים, ולא ידרוש ממשתמשי המודל להיות מודעים לפרטים של למשל ערכת האסימון המשמשת לטקסט, ערכת האינדקס המשמשת לתכונות קטגוריות, בין אם ערכי פיקסל תמונה. מנורמל ל- [-1, +1] או ל- [0, 1] וכו '. זה חזק במיוחד אם אתה מייצא את המודל שלך לזמן ריצה אחר, כגון TensorFlow.js: לא תצטרך ליישם מחדש את העיבוד המוקדם שלך צינור ב- JavaScript.

אם תחילה שמים את שכבות tf.data שלך בצינור tf.data שלך, באפשרותך לייצא מודל הסקה tf.data את tf.data המוקדם. כל שעליך לעשות הוא ליצור דגם חדש שמשרשר את שכבות העיבוד המוקדם שלך ואת מודל האימון שלך:

inputs = keras.Input(shape=input_shape)
x = preprocessing_layer(inputs)
outputs = training_model(x)
inference_model = keras.Model(inputs, outputs)

מתכונים מהירים

הגדלת נתוני תמונה (במכשיר)

שים לב ששכבות הגדלת נתוני תמונה פעילות רק במהלך האימון (בדומה לשכבת Dropout ).

from tensorflow import keras
from tensorflow.keras import layers

# Create a data augmentation stage with horizontal flipping, rotations, zooms
data_augmentation = keras.Sequential(
    [
        preprocessing.RandomFlip("horizontal"),
        preprocessing.RandomRotation(0.1),
        preprocessing.RandomZoom(0.1),
    ]
)

# Create a model that includes the augmentation stage
input_shape = (32, 32, 3)
classes = 10
inputs = keras.Input(shape=input_shape)
# Augment images
x = data_augmentation(inputs)
# Rescale image values to [0, 1]
x = preprocessing.Rescaling(1.0 / 255)(x)
# Add the rest of the model
outputs = keras.applications.ResNet50(
    weights=None, input_shape=input_shape, classes=classes
)(x)
model = keras.Model(inputs, outputs)

אתה יכול לראות התקנה דומה בפעולה בסיווג התמונות לדוגמה מאפס .

מנרמל תכונות מספריות

# Load some data
(x_train, y_train), _ = keras.datasets.cifar10.load_data()
x_train = x_train.reshape((len(x_train), -1))
input_shape = x_train.shape[1:]
classes = 10

# Create a Normalization layer and set its internal state using the training data
normalizer = preprocessing.Normalization()
normalizer.adapt(x_train)

# Create a model that include the normalization layer
inputs = keras.Input(shape=input_shape)
x = normalizer(inputs)
outputs = layers.Dense(classes, activation="softmax")(x)
model = keras.Model(inputs, outputs)

# Train the model
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
model.fit(x_train, y_train)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 11s 0us/step
1563/1563 [==============================] - 3s 1ms/step - loss: 2.1776
<tensorflow.python.keras.callbacks.History at 0x7f58c5f44208>

קידוד תכונות קטגוריות של מחרוזות באמצעות קידוד חם אחד

# Define some toy data
data = tf.constant(["a", "b", "c", "b", "c", "a"])

# Use StringLookup to build an index of the feature values
indexer = preprocessing.StringLookup()
indexer.adapt(data)

# Use CategoryEncoding to encode the integer indices to a one-hot vector
encoder = preprocessing.CategoryEncoding(output_mode="binary")
encoder.adapt(indexer(data))

# Convert new test data (which includes unknown feature values)
test_data = tf.constant(["a", "b", "c", "d", "e", ""])
encoded_data = encoder(indexer(test_data))
print(encoded_data)
tf.Tensor(
[[0. 0. 0. 0. 1.]
 [0. 0. 0. 1. 0.]
 [0. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [1. 0. 0. 0. 0.]], shape=(6, 5), dtype=float32)

שימו לב כי אינדקס 0 שמור לערכים חסרים (אותם עליכם לציין כמחרוזת הריקה "" ), ואינדקס 1 שמור לערכים מחוץ לאוצר המילים (ערכים שלא נראו במהלך adapt() ). באפשרותך להגדיר זאת באמצעות mask_token oov_token mask_token ו- oov_token של StringLookup .

אתה יכול לראות את השכבות StringLookup ו- CategoryEncoding בפעולה בסיווג הנתונים המובנים לדוגמה מאפס .

קידוד תכונות קטגוריות שלמות באמצעות קידוד חם אחד

# Define some toy data
data = tf.constant([10, 20, 20, 10, 30, 0])

# Use IntegerLookup to build an index of the feature values
indexer = preprocessing.IntegerLookup()
indexer.adapt(data)

# Use CategoryEncoding to encode the integer indices to a one-hot vector
encoder = preprocessing.CategoryEncoding(output_mode="binary")
encoder.adapt(indexer(data))

# Convert new test data (which includes unknown feature values)
test_data = tf.constant([10, 10, 20, 50, 60, 0])
encoded_data = encoder(indexer(test_data))
print(encoded_data)
tf.Tensor(
[[0. 0. 0. 1. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [1. 0. 0. 0. 0.]], shape=(6, 5), dtype=float32)

שימו לב כי אינדקס 0 שמור לערכים חסרים (אותם עליכם לציין כערך 0), ואינדקס 1 שמור לערכים מחוץ לאוצר המילים (ערכים שלא נראו במהלך adapt() ). באפשרותך להגדיר זאת באמצעות mask_value oov_value mask_value ו- oov_value של IntegerLookup .

אתה יכול לראות את שכבות IntegerLookup ו- CategoryEncoding בפעולה בסיווג הנתונים המובנים לדוגמא מאפס .

החלת טריק הגיבוב על תכונה קטגורית שלמה

אם יש לך תכונה קטגורית שיכולה לקחת ערכים רבים ושונים (בסדר גודל של 10e3 ומעלה), כאשר כל ערך מופיע רק כמה פעמים בנתונים, הוא הופך להיות לא מעשי ולא יעיל לאינדקס ולקודד את הערך התכונה. במקום זאת, זה יכול להיות רעיון טוב ליישם את "טריק hashing": חישוב הערכים על וקטור בגודל קבוע. זה שומר על ניהול שטח הגודל של התכונה, ומסיר את הצורך באינדקס מפורש.

# Sample data: 10,000 random integers with values between 0 and 100,000
data = np.random.randint(0, 100000, size=(10000, 1))

# Use the Hashing layer to hash the values to the range [0, 64]
hasher = preprocessing.Hashing(num_bins=64, salt=1337)

# Use the CategoryEncoding layer to one-hot encode the hashed values
encoder = preprocessing.CategoryEncoding(max_tokens=64, output_mode="binary")
encoded_data = encoder(hasher(data))
print(encoded_data.shape)
(10000, 64)

קידוד טקסט כרצף של מדדי אסימונים

כך עליכם לעבד מראש טקסט שיועבר לשכבת Embedding .

# Define some text data to adapt the layer
data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)
# Instantiate TextVectorization with "int" output_mode
text_vectorizer = preprocessing.TextVectorization(output_mode="int")
# Index the vocabulary via `adapt()`
text_vectorizer.adapt(data)

# You can retrieve the vocabulary we indexed via get_vocabulary()
vocab = text_vectorizer.get_vocabulary()
print("Vocabulary:", vocab)

# Create an Embedding + LSTM model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
x = layers.Embedding(input_dim=len(vocab), output_dim=64)(x)
outputs = layers.LSTM(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)
Vocabulary: ['', '[UNK]', 'the', 'side', 'you', 'with', 'will', 'wider', 'them', 'than', 'sky', 'put', 'other', 'one', 'is', 'for', 'ease', 'contain', 'by', 'brain', 'beside', 'and']

אתה יכול לראות את שכבת TextVectorization בפעולה, בשילוב עם מצב Embedding , בסיווג הטקסט לדוגמא מאפס .

שים לב שכאשר אתה מאמן מודל כזה, לביצועים הטובים ביותר, עליך להשתמש בשכבת TextVectorization כחלק מצינור הקלט (וזה מה שאנחנו עושים בדוגמה לסיווג הטקסט לעיל).

קידוד טקסט כמטריצה ​​צפופה של ngrams עם קידוד רב חם

כך עליך לעבד מראש טקסט שיועבר לשכבה Dense .

# Define some text data to adapt the layer
data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)
# Instantiate TextVectorization with "binary" output_mode (multi-hot)
# and ngrams=2 (index all bigrams)
text_vectorizer = preprocessing.TextVectorization(output_mode="binary", ngrams=2)
# Index the bigrams via `adapt()`
text_vectorizer.adapt(data)

print(
    "Encoded text:\n",
    text_vectorizer(["The Brain is deeper than the sea"]).numpy(),
    "\n",
)

# Create a Dense model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)

print("Model output:", test_output)
Encoded text:
 [[1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 0. 0. 0. 0. 0.

  0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0.]] 

Model output: tf.Tensor([[0.6381588]], shape=(1, 1), dtype=float32)

קידוד טקסט כמטריצה ​​צפופה של ngrams עם שקלול TF-IDF

זוהי דרך חלופית לעיבוד מקדים של טקסט לפני העברתו לשכבה Dense .

# Define some text data to adapt the layer
data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)
# Instantiate TextVectorization with "tf-idf" output_mode
# (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams)
text_vectorizer = preprocessing.TextVectorization(output_mode="tf-idf", ngrams=2)
# Index the bigrams and learn the TF-IDF weights via `adapt()`
text_vectorizer.adapt(data)

print(
    "Encoded text:\n",
    text_vectorizer(["The Brain is deeper than the sea"]).numpy(),
    "\n",
)

# Create a Dense model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)
print("Model output:", test_output)
Encoded text:
 [[8.04719   1.6945957 0.        0.        0.        0.        0.

  0.        0.        0.        0.        0.        0.        0.
  0.        0.        1.0986123 1.0986123 1.0986123 0.        0.
  0.        0.        0.        0.        0.        0.        0.
  1.0986123 0.        0.        0.        0.        0.        0.
  0.        1.0986123 1.0986123 0.        0.        0.       ]] 

Model output: tf.Tensor([[-1.2379041]], shape=(1, 1), dtype=float32)