סיווג נתונים מובנים באמצעות עמודות תכונה

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

מדריך זה מדגים כיצד לסווג נתונים מובנים (למשל נתונים טבלאיים ב-CSV). נשתמש ב- Keras כדי להגדיר את המודל, וב- tf.feature_column כגשר למיפוי מעמודות ב-CSV לתכונות המשמשות לאימון המודל. מדריך זה מכיל קוד מלא ל:

  • טען קובץ CSV באמצעות Pandas .
  • בנו צינור קלט כדי לקבץ ולערבב את השורות באמצעות tf.data .
  • מפה מעמודות ב-CSV לתכונות המשמשות לאימון המודל באמצעות עמודות תכונה.
  • בנה, אימון והעריך מודל באמצעות Keras.

מערך הנתונים

אנו נשתמש בגרסה פשוטה של מערך הנתונים של PetFinder. יש כמה אלפי שורות ב-CSV. כל שורה מתארת ​​חיית מחמד, וכל עמודה מתארת ​​תכונה. אנו נשתמש במידע זה כדי לחזות את המהירות שבה חיית המחמד תאומץ.

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

טור תיאור סוג תכונה סוג מידע
סוּג סוג בעל חיים (כלב, חתול) קָטֵגוֹרִי חוּט
גיל גיל חיית המחמד מִספָּרִי מספר שלם
גזע 1 גזע ראשוני של חיית המחמד קָטֵגוֹרִי חוּט
צבע 1 צבע 1 של חיית המחמד קָטֵגוֹרִי חוּט
צבע 2 צבע 2 של חיית המחמד קָטֵגוֹרִי חוּט
גודל בגרות גודל בבגרות קָטֵגוֹרִי חוּט
FurLength אורך פרווה קָטֵגוֹרִי חוּט
מחוסן חיית המחמד חוסנה קָטֵגוֹרִי חוּט
מְעוּקָר חיית המחמד עברה עיקור קָטֵגוֹרִי חוּט
בְּרִיאוּת מצב בריאותי קָטֵגוֹרִי חוּט
תַשְׁלוּם דמי אימוץ מִספָּרִי מספר שלם
תיאור כתיבת פרופיל עבור חיית המחמד הזו טֶקסט חוּט
PhotoAmt סך התמונות שהועלו עבור חיית המחמד הזו מִספָּרִי מספר שלם
מהירות אימוץ מהירות האימוץ מִיוּן מספר שלם

ייבוא ​​TensorFlow וספריות אחרות

pip install sklearn
import numpy as np
import pandas as pd

import tensorflow as tf

from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split

השתמש ב-Pandas כדי ליצור מסגרת נתונים

Pandas היא ספריית Python עם הרבה כלי עזר מועילים לטעינה ועבודה עם נתונים מובנים. נשתמש ב-Pandas כדי להוריד את מערך הנתונים מכתובת URL, ולטעון אותו לתוך מסגרת נתונים.

import pathlib

dataset_url = 'http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip'
csv_file = 'datasets/petfinder-mini/petfinder-mini.csv'

tf.keras.utils.get_file('petfinder_mini.zip', dataset_url,
                        extract=True, cache_dir='.')
dataframe = pd.read_csv(csv_file)
Downloading data from http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip
1671168/1668792 [==============================] - 0s 0us/step
1679360/1668792 [==============================] - 0s 0us/step
dataframe.head()

צור משתנה יעד

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

לאחר שינוי עמודת התווית, 0 יציין שחיית המחמד לא אומצה, ו-1 יציין שכן.

# In the original dataset "4" indicates the pet was not adopted.
dataframe['target'] = np.where(dataframe['AdoptionSpeed']==4, 0, 1)

# Drop un-used columns.
dataframe = dataframe.drop(columns=['AdoptionSpeed', 'Description'])

פצל את מסגרת הנתונים לרכבה, אימות ובדיקה

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

train, test = train_test_split(dataframe, test_size=0.2)
train, val = train_test_split(train, test_size=0.2)
print(len(train), 'train examples')
print(len(val), 'validation examples')
print(len(test), 'test examples')
7383 train examples
1846 validation examples
2308 test examples

צור צינור קלט באמצעות tf.data

לאחר מכן, נעטוף את מסגרות הנתונים ב- tf.data . זה יאפשר לנו להשתמש בעמודות תכונות כגשר למיפוי מהעמודות במסגרת הנתונים של Pandas לתכונות המשמשות לאימון המודל. אם היינו עובדים עם קובץ CSV גדול מאוד (כל כך גדול שהוא לא נכנס לזיכרון), היינו משתמשים ב-tf.data כדי לקרוא אותו ישירות מהדיסק. זה לא מכוסה במדריך זה.

# A utility method to create a tf.data dataset from a Pandas Dataframe
def df_to_dataset(dataframe, shuffle=True, batch_size=32):
  dataframe = dataframe.copy()
  labels = dataframe.pop('target')
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  return ds
batch_size = 5 # A small batch sized is used for demonstration purposes
train_ds = df_to_dataset(train, batch_size=batch_size)
val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)

הבן את צינור הקלט

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

for feature_batch, label_batch in train_ds.take(1):
  print('Every feature:', list(feature_batch.keys()))
  print('A batch of ages:', feature_batch['Age'])
  print('A batch of targets:', label_batch )
Every feature: ['Type', 'Age', 'Breed1', 'Gender', 'Color1', 'Color2', 'MaturitySize', 'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Fee', 'PhotoAmt']
A batch of ages: tf.Tensor([ 6  2 36  2  2], shape=(5,), dtype=int64)
A batch of targets: tf.Tensor([1 1 1 1 1], shape=(5,), dtype=int64)

אנו יכולים לראות שמערך הנתונים מחזיר מילון של שמות עמודות (מתוך ה-dataframe) הממפים ערכי עמודות משורות ב-dataframe.

הצג כמה סוגים של עמודות תכונה

TensorFlow מספק סוגים רבים של עמודות תכונה. בסעיף זה, ניצור מספר סוגים של עמודות תכונה, ונדגים כיצד הן הופכות עמודה ממסגרת הנתונים.

# We will use this batch to demonstrate several types of feature columns
example_batch = next(iter(train_ds))[0]
# A utility method to create a feature column
# and to transform a batch of data
def demo(feature_column):
  feature_layer = layers.DenseFeatures(feature_column)
  print(feature_layer(example_batch).numpy())

עמודות מספריות

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

photo_count = feature_column.numeric_column('PhotoAmt')
demo(photo_count)
[[2.]
 [4.]
 [4.]
 [1.]
 [2.]]

במערך הנתונים של PetFinder, רוב העמודות ממסגרת הנתונים הן קטגוריות.

עמודים עם דליים

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

age = feature_column.numeric_column('Age')
age_buckets = feature_column.bucketized_column(age, boundaries=[1, 3, 5])
demo(age_buckets)
[[0. 0. 0. 1.]
 [0. 1. 0. 0.]
 [0. 0. 0. 1.]
 [0. 0. 1. 0.]
 [0. 1. 0. 0.]]

עמודות קטגוריות

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

animal_type = feature_column.categorical_column_with_vocabulary_list(
      'Type', ['Cat', 'Dog'])

animal_type_one_hot = feature_column.indicator_column(animal_type)
demo(animal_type_one_hot)
[[1. 0.]
 [1. 0.]
 [1. 0.]
 [1. 0.]
 [0. 1.]]

הטבעת עמודות

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

# Notice the input to the embedding column is the categorical column
# we previously created
breed1 = feature_column.categorical_column_with_vocabulary_list(
      'Breed1', dataframe.Breed1.unique())
breed1_embedding = feature_column.embedding_column(breed1, dimension=8)
demo(breed1_embedding)
[[-0.22380038 -0.09379731  0.21349265  0.33451992 -0.49730566  0.05174963
   0.2668497   0.27391028]
 [-0.5484653  -0.03492585  0.05648395 -0.09792244  0.02530896 -0.15477926
  -0.10695003 -0.45474145]
 [-0.22380038 -0.09379731  0.21349265  0.33451992 -0.49730566  0.05174963
   0.2668497   0.27391028]
 [ 0.10050306  0.43513173  0.375823    0.5652766   0.40925583 -0.03928828
   0.4901914   0.20637617]
 [-0.2319875  -0.21874283  0.12272807  0.33345345 -0.4563055   0.21609035
  -0.2410521   0.4736915 ]]

עמודות תכונה מגובבות

דרך נוספת לייצג עמודה קטגורית עם מספר רב של ערכים היא להשתמש בקטגורית_עמודה_עם_hash_bucket . עמודת תכונה זו מחשבת ערך hash של הקלט, ולאחר מכן בוחרת באחד hash_bucket_size כדי לקודד מחרוזת. בעת שימוש בעמודה זו, אינך צריך לספק את אוצר המילים, ותוכל לבחור להקטין את מספר ה-hash_buckets באופן משמעותי ממספר הקטגוריות בפועל כדי לחסוך במקום.

breed1_hashed = feature_column.categorical_column_with_hash_bucket(
      'Breed1', hash_bucket_size=10)
demo(feature_column.indicator_column(breed1_hashed))
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]]

עמודות תכונה מוצלבות

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

crossed_feature = feature_column.crossed_column([age_buckets, animal_type], hash_bucket_size=10)
demo(feature_column.indicator_column(crossed_feature))
[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
 [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]]

בחר באילו עמודות להשתמש

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

feature_columns = []

# numeric cols
for header in ['PhotoAmt', 'Fee', 'Age']:
  feature_columns.append(feature_column.numeric_column(header))
# bucketized cols
age = feature_column.numeric_column('Age')
age_buckets = feature_column.bucketized_column(age, boundaries=[1, 2, 3, 4, 5])
feature_columns.append(age_buckets)
# indicator_columns
indicator_column_names = ['Type', 'Color1', 'Color2', 'Gender', 'MaturitySize',
                          'FurLength', 'Vaccinated', 'Sterilized', 'Health']
for col_name in indicator_column_names:
  categorical_column = feature_column.categorical_column_with_vocabulary_list(
      col_name, dataframe[col_name].unique())
  indicator_column = feature_column.indicator_column(categorical_column)
  feature_columns.append(indicator_column)
# embedding columns
breed1 = feature_column.categorical_column_with_vocabulary_list(
      'Breed1', dataframe.Breed1.unique())
breed1_embedding = feature_column.embedding_column(breed1, dimension=8)
feature_columns.append(breed1_embedding)
# crossed columns
age_type_feature = feature_column.crossed_column([age_buckets, animal_type], hash_bucket_size=100)
feature_columns.append(feature_column.indicator_column(age_type_feature))

צור שכבת תכונה

כעת, לאחר שהגדרנו את עמודות התכונות שלנו, נשתמש בשכבת DenseFeatures כדי להזין אותן למודל Keras שלנו.

feature_layer = tf.keras.layers.DenseFeatures(feature_columns)

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

batch_size = 32
train_ds = df_to_dataset(train, batch_size=batch_size)
val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)

צור, הידור והכשרת המודל

model = tf.keras.Sequential([
  feature_layer,
  layers.Dense(128, activation='relu'),
  layers.Dense(128, activation='relu'),
  layers.Dropout(.1),
  layers.Dense(1)
])

model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

model.fit(train_ds,
          validation_data=val_ds,
          epochs=10)
Epoch 1/10
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'Type': <tf.Tensor 'IteratorGetNext:11' shape=(None,) dtype=string>, 'Age': <tf.Tensor 'IteratorGetNext:0' shape=(None,) dtype=int64>, 'Breed1': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=string>, 'Gender': <tf.Tensor 'IteratorGetNext:6' shape=(None,) dtype=string>, 'Color1': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'Color2': <tf.Tensor 'IteratorGetNext:3' shape=(None,) dtype=string>, 'MaturitySize': <tf.Tensor 'IteratorGetNext:8' shape=(None,) dtype=string>, 'FurLength': <tf.Tensor 'IteratorGetNext:5' shape=(None,) dtype=string>, 'Vaccinated': <tf.Tensor 'IteratorGetNext:12' shape=(None,) dtype=string>, 'Sterilized': <tf.Tensor 'IteratorGetNext:10' shape=(None,) dtype=string>, 'Health': <tf.Tensor 'IteratorGetNext:7' shape=(None,) dtype=string>, 'Fee': <tf.Tensor 'IteratorGetNext:4' shape=(None,) dtype=int64>, 'PhotoAmt': <tf.Tensor 'IteratorGetNext:9' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'Type': <tf.Tensor 'IteratorGetNext:11' shape=(None,) dtype=string>, 'Age': <tf.Tensor 'IteratorGetNext:0' shape=(None,) dtype=int64>, 'Breed1': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=string>, 'Gender': <tf.Tensor 'IteratorGetNext:6' shape=(None,) dtype=string>, 'Color1': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'Color2': <tf.Tensor 'IteratorGetNext:3' shape=(None,) dtype=string>, 'MaturitySize': <tf.Tensor 'IteratorGetNext:8' shape=(None,) dtype=string>, 'FurLength': <tf.Tensor 'IteratorGetNext:5' shape=(None,) dtype=string>, 'Vaccinated': <tf.Tensor 'IteratorGetNext:12' shape=(None,) dtype=string>, 'Sterilized': <tf.Tensor 'IteratorGetNext:10' shape=(None,) dtype=string>, 'Health': <tf.Tensor 'IteratorGetNext:7' shape=(None,) dtype=string>, 'Fee': <tf.Tensor 'IteratorGetNext:4' shape=(None,) dtype=int64>, 'PhotoAmt': <tf.Tensor 'IteratorGetNext:9' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
231/231 [==============================] - ETA: 0s - loss: 0.6759 - accuracy: 0.6802WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor. Received: inputs={'Type': <tf.Tensor 'IteratorGetNext:11' shape=(None,) dtype=string>, 'Age': <tf.Tensor 'IteratorGetNext:0' shape=(None,) dtype=int64>, 'Breed1': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=string>, 'Gender': <tf.Tensor 'IteratorGetNext:6' shape=(None,) dtype=string>, 'Color1': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'Color2': <tf.Tensor 'IteratorGetNext:3' shape=(None,) dtype=string>, 'MaturitySize': <tf.Tensor 'IteratorGetNext:8' shape=(None,) dtype=string>, 'FurLength': <tf.Tensor 'IteratorGetNext:5' shape=(None,) dtype=string>, 'Vaccinated': <tf.Tensor 'IteratorGetNext:12' shape=(None,) dtype=string>, 'Sterilized': <tf.Tensor 'IteratorGetNext:10' shape=(None,) dtype=string>, 'Health': <tf.Tensor 'IteratorGetNext:7' shape=(None,) dtype=string>, 'Fee': <tf.Tensor 'IteratorGetNext:4' shape=(None,) dtype=int64>, 'PhotoAmt': <tf.Tensor 'IteratorGetNext:9' shape=(None,) dtype=int64>}. Consider rewriting this model with the Functional API.
231/231 [==============================] - 4s 10ms/step - loss: 0.6759 - accuracy: 0.6802 - val_loss: 0.5361 - val_accuracy: 0.7351
Epoch 2/10
231/231 [==============================] - 2s 9ms/step - loss: 0.5742 - accuracy: 0.7054 - val_loss: 0.5178 - val_accuracy: 0.7411
Epoch 3/10
231/231 [==============================] - 2s 9ms/step - loss: 0.5369 - accuracy: 0.7231 - val_loss: 0.5031 - val_accuracy: 0.7438
Epoch 4/10
231/231 [==============================] - 2s 9ms/step - loss: 0.5161 - accuracy: 0.7214 - val_loss: 0.5115 - val_accuracy: 0.7259
Epoch 5/10
231/231 [==============================] - 2s 9ms/step - loss: 0.5034 - accuracy: 0.7296 - val_loss: 0.5173 - val_accuracy: 0.7237
Epoch 6/10
231/231 [==============================] - 2s 8ms/step - loss: 0.4983 - accuracy: 0.7301 - val_loss: 0.5153 - val_accuracy: 0.7254
Epoch 7/10
231/231 [==============================] - 2s 9ms/step - loss: 0.4912 - accuracy: 0.7412 - val_loss: 0.5258 - val_accuracy: 0.7010
Epoch 8/10
231/231 [==============================] - 2s 9ms/step - loss: 0.4890 - accuracy: 0.7360 - val_loss: 0.5066 - val_accuracy: 0.7221
Epoch 9/10
231/231 [==============================] - 2s 9ms/step - loss: 0.4824 - accuracy: 0.7443 - val_loss: 0.5091 - val_accuracy: 0.7481
Epoch 10/10
231/231 [==============================] - 2s 9ms/step - loss: 0.4758 - accuracy: 0.7466 - val_loss: 0.5159 - val_accuracy: 0.7492
<keras.callbacks.History at 0x7f06b52a1810>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
73/73 [==============================] - 0s 6ms/step - loss: 0.4812 - accuracy: 0.7543
Accuracy 0.7543327808380127

הצעדים הבאים

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