דף זה תורגם על ידי Cloud Translation API.
Switch to English

גרף להסדרה לסיווג סנטימנט באמצעות גרפים מסונתזים

צפה על TensorFlow.org הפעל ב Google Colab מקור צפה על GitHub

סקירה כללית

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

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

  1. צור שיבוצים עבור כל דגימה טקסט קלט. ניתן לעשות זאת באמצעות מודלים מאומן מראש כגון word2vec , סיבוב , ברט וכו '
  2. בנה גרף המבוסס על שיבוצים אלה באמצעות דמיון מטרי כגון מרחק "L2", "קוסינוס" המרחק, וכו 'צומת להתכתב גרף דגימות וקצוות של להתכתב גרף הדמיון בין הזוגות של דוגמה.
  3. להפיק נתונים כשרים מהגרף המסונתז מעל והתכונות מדגמות. נתון ההכשרה וכתוצאה יכילו תכונות שכן בנוסף לתכונות הצומת המקוריות.
  4. צור רשת עצבית כמודל בסיס באמצעות רציפי Keras, הפונקציונלי, או API תת.
  5. עוטף את דגם הבסיס עם מעמד מעטפת GraphRegularization, אשר מסופק על ידי מסגרת NSL, כדי ליצור מודל Keras גרף חדש. דגם חדש יכלול הפסד להסדרת גרף כמו המונח להסדרה ב אובייקטיבי האימונים שלה.
  6. רכבת ולהעריך את מודל Keras גרף.

דרישות

  1. התקן את החבילה למידה Structured עצבית.
  2. התקן-רכזת tensorflow.
pip install --quiet neural-structured-learning
pip install --quiet tensorflow-hub

תלוי ויבוא

 import matplotlib.pyplot as plt
import numpy as np

import neural_structured_learning as nsl

import tensorflow as tf
import tensorflow_hub as hub

# Resets notebook state
tf.keras.backend.clear_session()

print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub version: ", hub.__version__)
print(
    "GPU is",
    "available" if tf.config.list_physical_devices("GPU") else "NOT AVAILABLE")
 
Version:  2.3.0
Eager mode:  True
Hub version:  0.8.0
GPU is NOT AVAILABLE

הנתונים IMDB

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

במדריך זה, נשתמש בגרסה מעובדים של בסיס הנתונים IMDB.

נתון הורדת מעובדי IMDB

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

ההורדות הקוד הבא במערך IMDB (או שימושים בעותק מטמון אם זה כבר הורדו):

 imdb = tf.keras.datasets.imdb
(pp_train_data, pp_train_labels), (pp_test_data, pp_test_labels) = (
    imdb.load_data(num_words=10000))
 
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step

טענת num_words=10000 שומר את המילים הנפוצות ביותר 10,000 העליונה בנתון האימון. המילים הנדירות מבוטלות כדי לשמור את הגודל לניהול אוצר המילים.

חקור את הנתונים

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

 print('Training entries: {}, labels: {}'.format(
    len(pp_train_data), len(pp_train_labels)))
training_samples_count = len(pp_train_data)
 
Training entries: 25000, labels: 25000

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

 print(pp_train_data[0])
 
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

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

 len(pp_train_data[0]), len(pp_train_data[1])
 
(218, 189)

המר את המספרים השלמים חזרה מילים

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

 def build_reverse_word_index():
  # A dictionary mapping words to an integer index
  word_index = imdb.get_word_index()

  # The first indices are reserved
  word_index = {k: (v + 3) for k, v in word_index.items()}
  word_index['<PAD>'] = 0
  word_index['<START>'] = 1
  word_index['<UNK>'] = 2  # unknown
  word_index['<UNUSED>'] = 3
  return dict((value, key) for (key, value) in word_index.items())

reverse_word_index = build_reverse_word_index()

def decode_review(text):
  return ' '.join([reverse_word_index.get(i, '?') for i in text])
 
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb_word_index.json
1646592/1641221 [==============================] - 0s 0us/step

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

 decode_review(pp_train_data[0])
 
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"

בניית גרף

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

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

mkdir -p /tmp/imdb

צור שיבוצים מדגמים

נשתמש שיבוצי סיבוב pretrained ליצור שיבוצים של tf.train.Example הפורמט עבור כל דגימה בקלט. אנו מאחסנים את שיבוצים וכתוצאה מכך TFRecord בפורמט יחד עם תכונה נוספת אשר מייצג את הזהות של כל דגימה. זה חשוב ויאפשר לנו להתאים שיבוצים מדגמים עם מקבילי צומת בגרף מאוחרות.

 pretrained_embedding = 'https://  tfhub.dev  /google/tf2-preview/gnews-swivel-20dim/1'

hub_layer = hub.KerasLayer(
    pretrained_embedding, input_shape=[], dtype=tf.string, trainable=True)
 
 def _int64_feature(value):
  """Returns int64 tf.train.Feature."""
  return tf.train.Feature(int64_list=tf.train.Int64List(value=value.tolist()))


def _bytes_feature(value):
  """Returns bytes tf.train.Feature."""
  return tf.train.Feature(
      bytes_list=tf.train.BytesList(value=[value.encode('utf-8')]))


def _float_feature(value):
  """Returns float tf.train.Feature."""
  return tf.train.Feature(float_list=tf.train.FloatList(value=value.tolist()))


def create_embedding_example(word_vector, record_id):
  """Create tf.Example containing the sample's embedding and its ID."""

  text = decode_review(word_vector)

  # Shape = [batch_size,].
  sentence_embedding = hub_layer(tf.reshape(text, shape=[-1,]))

  # Flatten the sentence embedding back to 1-D.
  sentence_embedding = tf.reshape(sentence_embedding, shape=[-1])

  features = {
      'id': _bytes_feature(str(record_id)),
      'embedding': _float_feature(sentence_embedding.numpy())
  }
  return tf.train.Example(features=tf.train.Features(feature=features))


def create_embeddings(word_vectors, output_path, starting_record_id):
  record_id = int(starting_record_id)
  with tf.io.TFRecordWriter(output_path) as writer:
    for word_vector in word_vectors:
      example = create_embedding_example(word_vector, record_id)
      record_id = record_id + 1
      writer.write(example.SerializeToString())
  return record_id


# Persist TF.Example features containing embeddings for training data in
# TFRecord format.
create_embeddings(pp_train_data, '/tmp/imdb/embeddings.tfr', 0)
 
25000

בנה גרף

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

עצבי Structured למידה מספקת ספריית בניין גרף לבנות גרף המבוסס על שיבוצים מדגמים. היא משתמשת דמיון קוסינוס כמדד דמיון להשוות שיבוצים וקצוות לבנות ביניהם. זה גם מאפשר לנו להגדיר סף הדמיון, אשר ניתן להשתמש בהם כדי להשליך קצוות שונים מן הגרף הסופי. בדוגמה זו, באמצעות 0.99 כפי סף הדמיון, אנחנו בסופו של דבר עם גרף שיש לו 445,327 קצוות דו-כיוונית.

 nsl.tools.build_graph(['/tmp/imdb/embeddings.tfr'],
                      '/tmp/imdb/graph_99.tsv',
                      similarity_threshold=0.99)
 

תכונות לדוגמא

אנו יוצרים תכונות מדגמות לבעיה שלנו באמצעות tf.train.Example הפורמט להתמיד אותם TFRecord הפורמט. כל המדגם יכלול תכונות שלוש הבאות:

  1. id: הצומת מזהה של המדגם.
  2. מילים: An לרשימת Int64 המכיל מזהים מילה.
  3. תווית: סינגלטון Int64 זיהוי כיתת היעד של הסקירה.
 def create_example(word_vector, label, record_id):
  """Create tf.Example containing the sample's word vector, label, and ID."""
  features = {
      'id': _bytes_feature(str(record_id)),
      'words': _int64_feature(np.asarray(word_vector)),
      'label': _int64_feature(np.asarray([label])),
  }
  return tf.train.Example(features=tf.train.Features(feature=features))

def create_records(word_vectors, labels, record_path, starting_record_id):
  record_id = int(starting_record_id)
  with tf.io.TFRecordWriter(record_path) as writer:
    for word_vector, label in zip(word_vectors, labels):
      example = create_example(word_vector, label, record_id)
      record_id = record_id + 1
      writer.write(example.SerializeToString())
  return record_id

# Persist TF.Example features (word vectors and labels) for training and test
# data in TFRecord format.
next_record_id = create_records(pp_train_data, pp_train_labels,
                                '/tmp/imdb/train_data.tfr', 0)
create_records(pp_test_data, pp_test_labels, '/tmp/imdb/test_data.tfr',
               next_record_id)
 
50000

נתוני אימונים להגדיל עם שכני גרף

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

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

 nsl.tools.pack_nbrs(
    '/tmp/imdb/train_data.tfr',
    '',
    '/tmp/imdb/graph_99.tsv',
    '/tmp/imdb/nsl_train_data.tfr',
    add_undirected_edges=True,
    max_nbrs=3)
 

דגם הבסיס

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

משתנים גלובליים

 NBR_FEATURE_PREFIX = 'NL_nbr_'
NBR_WEIGHT_SUFFIX = '_weight'
 

Hyperparameters

נשתמש מופע של HParams כדי inclue hyperparameters וקבועים שונים שימשו לאימונים והערכה. נתאר בקצרה כל אחד מהם להלן:

  • num_classes: ישנן 2 כיתות - חיוביות ושליליות.

  • max_seq_length: זהו המספר המרבי של מילות נחשבות מכול ביקורת סרט בדוגמא זו.

  • vocab_size: זהו הגודל של אוצר המילים נחשב למשל זה.

  • DISTANCE_TYPE: זהו המרחק מטרי המשמש להסדרת המדגם עם שכנותיה.

  • graph_regularization_multiplier: זו קובעת את המשקל היחסי של המונח להסדרת גרף פונקצית ההפסד הכולל.

  • num_neighbors: מספר שכנים משמשים להסדרת גרף. יש ערך זה להיות קטן או שווה ל max_nbrs הטיעון בשימוש מעל כאשר פניית nsl.tools.pack_nbrs .

  • num_fc_units: מספר היחידות בשכבה המחוברת המלא של הרשת העצבית.

  • train_epochs: מספר תקופות האימונים.

  • גודל יצווה שמש לאימונים והערכה: batch_size.

  • eval_steps: מספר אצוות כדי תהליך לפני רואה סיום בדיקה. אם מוגדר None , כל המופעים בערכת הבדיקה מוערכים.

 class HParams(object):
  """Hyperparameters used for training."""
  def __init__(self):
    ### dataset parameters
    self.num_classes = 2
    self.max_seq_length = 256
    self.vocab_size = 10000
    ### neural graph learning parameters
    self.distance_type = nsl.configs.DistanceType.L2
    self.graph_regularization_multiplier = 0.1
    self.num_neighbors = 2
    ### model architecture
    self.num_embedding_dims = 16
    self.num_lstm_dims = 64
    self.num_fc_units = 64
    ### training parameters
    self.train_epochs = 10
    self.batch_size = 128
    ### eval parameters
    self.eval_steps = None  # All instances in the test set are evaluated.

HPARAMS = HParams()
 

הכן את הנתונים

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

  • המרת המערכים לתוך וקטורים של 0 ים ו 1 ים המציין התרחשות מילה, בדומה קידוד חד חם. לדוגמה, את הרצף [3, 5] יהפוך 10000 וקטור מימדי שכולו אפסים למעט מדדי 3 ו 5 , אשר הם אלה. ואז, להפוך את זה השכבה הראשונה שלנו ברשת-A Dense שכבה-שיכול לטפל צף נתוני וקטור נקודה. גישה זו צורכת זיכרון רב, אם כי, המחייב num_words * num_reviews מטריקס גודל.

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

במדריך זה, נשתמש בגישה השנייה.

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

 def make_dataset(file_path, training=False):
  """Creates a `tf.data.TFRecordDataset`.

  Args:
    file_path: Name of the file in the `.tfrecord` format containing
      `tf.train.Example` objects.
    training: Boolean indicating if we are in training mode.

  Returns:
    An instance of `tf.data.TFRecordDataset` containing the `tf.train.Example`
    objects.
  """

  def pad_sequence(sequence, max_seq_length):
    """Pads the input sequence (a `tf.SparseTensor`) to `max_seq_length`."""
    pad_size = tf.maximum([0], max_seq_length - tf.shape(sequence)[0])
    padded = tf.concat(
        [sequence.values,
         tf.fill((pad_size), tf.cast(0, sequence.dtype))],
        axis=0)
    # The input sequence may be larger than max_seq_length. Truncate down if
    # necessary.
    return tf.slice(padded, [0], [max_seq_length])

  def parse_example(example_proto):
    """Extracts relevant fields from the `example_proto`.

    Args:
      example_proto: An instance of `tf.train.Example`.

    Returns:
      A pair whose first value is a dictionary containing relevant features
      and whose second value contains the ground truth labels.
    """
    # The 'words' feature is a variable length word ID vector.
    feature_spec = {
        'words': tf.io.VarLenFeature(tf.int64),
        'label': tf.io.FixedLenFeature((), tf.int64, default_value=-1),
    }
    # We also extract corresponding neighbor features in a similar manner to
    # the features above during training.
    if training:
      for i in range(HPARAMS.num_neighbors):
        nbr_feature_key = '{}{}_{}'.format(NBR_FEATURE_PREFIX, i, 'words')
        nbr_weight_key = '{}{}{}'.format(NBR_FEATURE_PREFIX, i,
                                         NBR_WEIGHT_SUFFIX)
        feature_spec[nbr_feature_key] = tf.io.VarLenFeature(tf.int64)

        # We assign a default value of 0.0 for the neighbor weight so that
        # graph regularization is done on samples based on their exact number
        # of neighbors. In other words, non-existent neighbors are discounted.
        feature_spec[nbr_weight_key] = tf.io.FixedLenFeature(
            [1], tf.float32, default_value=tf.constant([0.0]))

    features = tf.io.parse_single_example(example_proto, feature_spec)

    # Since the 'words' feature is a variable length word vector, we pad it to a
    # constant maximum length based on HPARAMS.max_seq_length
    features['words'] = pad_sequence(features['words'], HPARAMS.max_seq_length)
    if training:
      for i in range(HPARAMS.num_neighbors):
        nbr_feature_key = '{}{}_{}'.format(NBR_FEATURE_PREFIX, i, 'words')
        features[nbr_feature_key] = pad_sequence(features[nbr_feature_key],
                                                 HPARAMS.max_seq_length)

    labels = features.pop('label')
    return features, labels

  dataset = tf.data.TFRecordDataset([file_path])
  if training:
    dataset = dataset.shuffle(10000)
  dataset = dataset.map(parse_example)
  dataset = dataset.batch(HPARAMS.batch_size)
  return dataset


train_dataset = make_dataset('/tmp/imdb/nsl_train_data.tfr', True)
test_dataset = make_dataset('/tmp/imdb/test_data.tfr')
 

לבנות מודל

רשת עצבית נוצרה על ידי ערמת שכבות-זה דורש שתי החלטות אדריכליות עיקריות:

  • כמה שכבות להשתמש במודל?
  • כמה יחידות נסתרות להשתמש עבור כל שכבה?

בדוגמה זו, נתוני הקלט מורכב מערך של מדדים המילה. התוויות לחזות הם או 0 או 1.

אנו נשתמש LSTM דו-כיוונית כמודל הבסיס שלנו במדריך זה.

 # This function exists as an alternative to the bi-LSTM model used in this
# notebook.
def make_feed_forward_model():
  """Builds a simple 2 layer feed forward neural network."""
  inputs = tf.keras.Input(
      shape=(HPARAMS.max_seq_length,), dtype='int64', name='words')
  embedding_layer = tf.keras.layers.Embedding(HPARAMS.vocab_size, 16)(inputs)
  pooling_layer = tf.keras.layers.GlobalAveragePooling1D()(embedding_layer)
  dense_layer = tf.keras.layers.Dense(16, activation='relu')(pooling_layer)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(dense_layer)
  return tf.keras.Model(inputs=inputs, outputs=outputs)


def make_bilstm_model():
  """Builds a bi-directional LSTM model."""
  inputs = tf.keras.Input(
      shape=(HPARAMS.max_seq_length,), dtype='int64', name='words')
  embedding_layer = tf.keras.layers.Embedding(HPARAMS.vocab_size,
                                              HPARAMS.num_embedding_dims)(
                                                  inputs)
  lstm_layer = tf.keras.layers.Bidirectional(
      tf.keras.layers.LSTM(HPARAMS.num_lstm_dims))(
          embedding_layer)
  dense_layer = tf.keras.layers.Dense(
      HPARAMS.num_fc_units, activation='relu')(
          lstm_layer)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(dense_layer)
  return tf.keras.Model(inputs=inputs, outputs=outputs)


# Feel free to use an architecture of your choice.
model = make_bilstm_model()
model.summary()
 
Model: "functional_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
words (InputLayer)           [(None, 256)]             0         
_________________________________________________________________
embedding (Embedding)        (None, 256, 16)           160000    
_________________________________________________________________
bidirectional (Bidirectional (None, 128)               41472     
_________________________________________________________________
dense (Dense)                (None, 64)                8256      
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 65        
=================================================================
Total params: 209,793
Trainable params: 209,793
Non-trainable params: 0
_________________________________________________________________

השכבות נערמות ביעילות ברצף לבנות המסווג:

  1. הרובד הראשון הוא Input שכבה אשר לוקחת את אוצר המילים מקודדים-השלם.
  2. השכבה הבאה היא Embedding שכבה, אשר לוקחת את אוצר המילים מקודדים-שלמות ונראה את וקטור ההטבעה לכול מילה-מדד. וקטורים אלו נלמדים כמו דגמי רכבות. הווקטורים להוסיף מימד למערך פלט. הממדים וכתוצאה מכך הם: (batch, sequence, embedding) .
  3. הבא, שכבה LSTM כיוונית מחזירה וקטור פלט קבוע באורך עבור כל דוגמה.
  4. וקטור פלט קבוע באורך זה מוזרם דרך מלאה מחוברת ( Dense שכבה) עם 64 יחידות נסתרות.
  5. השכבה האחרונה מחוברת בצפיפות עם צומת פלט יחיד. שימוש sigmoid פונקצית ההפעלה, ערך זה הוא לצוף בין 0 לבין 1, מייצג הסתברות, או ברמת ביטחון.

יחידות נסתרות

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

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

תפקוד האופטימיזציה הפסד

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

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

צור ערכת אימות

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

במדריך זה, אנו לוקחים בערך 10% מדגימות ההכשרה הראשוניות (10% מכלל 25,000) כנתוני שכותרתו לאימונים ואת הנותרים כמו אימות נתונים. מאז פיצול רכבת / בדיקה הראשונית היה 50/50 (25,000 דגימות כל אחד), הרכבת היעילה / אימות / המבחן לפצל עכשיו יש לנו הוא 5/45/50.

הערה כי "train_dataset" כבר לכלך וטרפה.

 validation_fraction = 0.9
validation_size = int(validation_fraction *
                      int(training_samples_count / HPARAMS.batch_size))
print(validation_size)
validation_dataset = train_dataset.take(validation_size)
train_dataset = train_dataset.skip(validation_size)
 
175

רכבת המודל

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

 history = model.fit(
    train_dataset,
    validation_data=validation_dataset,
    epochs=HPARAMS.train_epochs,
    verbose=1)
 
Epoch 1/10

/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/functional.py:543: UserWarning: Input dict contained keys ['NL_nbr_0_words', 'NL_nbr_1_words', 'NL_nbr_0_weight', 'NL_nbr_1_weight'] which did not match any model input. They will be ignored by the model.
  [n for n in tensors.keys() if n not in ref_input_names])

21/21 [==============================] - 19s 925ms/step - loss: 0.6930 - accuracy: 0.5092 - val_loss: 0.6924 - val_accuracy: 0.5006
Epoch 2/10
21/21 [==============================] - 19s 894ms/step - loss: 0.6890 - accuracy: 0.5465 - val_loss: 0.7294 - val_accuracy: 0.5698
Epoch 3/10
21/21 [==============================] - 19s 883ms/step - loss: 0.6785 - accuracy: 0.6208 - val_loss: 0.6489 - val_accuracy: 0.7043
Epoch 4/10
21/21 [==============================] - 19s 890ms/step - loss: 0.6592 - accuracy: 0.6400 - val_loss: 0.6523 - val_accuracy: 0.6866
Epoch 5/10
21/21 [==============================] - 19s 883ms/step - loss: 0.6413 - accuracy: 0.6923 - val_loss: 0.6335 - val_accuracy: 0.7004
Epoch 6/10
21/21 [==============================] - 21s 982ms/step - loss: 0.6053 - accuracy: 0.7188 - val_loss: 0.5716 - val_accuracy: 0.7183
Epoch 7/10
21/21 [==============================] - 18s 879ms/step - loss: 0.5204 - accuracy: 0.7619 - val_loss: 0.4511 - val_accuracy: 0.7930
Epoch 8/10
21/21 [==============================] - 19s 882ms/step - loss: 0.4719 - accuracy: 0.7758 - val_loss: 0.4244 - val_accuracy: 0.8094
Epoch 9/10
21/21 [==============================] - 18s 880ms/step - loss: 0.3695 - accuracy: 0.8431 - val_loss: 0.3567 - val_accuracy: 0.8487
Epoch 10/10
21/21 [==============================] - 19s 891ms/step - loss: 0.3504 - accuracy: 0.8500 - val_loss: 0.3219 - val_accuracy: 0.8652

הערכת המודל

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

 results = model.evaluate(test_dataset, steps=HPARAMS.eval_steps)
print(results)
 
196/196 [==============================] - 17s 85ms/step - loss: 0.4116 - accuracy: 0.8221
[0.4116455018520355, 0.8221200108528137]

צייר גרף של / פסד דיוק לאורך זמן

model.fit() מחזירה History אובייקט המכיל מילון עם כל מה שקרה במהלך האימון:

 history_dict = history.history
history_dict.keys()
 
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

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

 acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

# "-r^" is for solid red line with triangle markers.
plt.plot(epochs, loss, '-r^', label='Training loss')
# "-b0" is for solid blue line with circle markers.
plt.plot(epochs, val_loss, '-bo', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend(loc='best')

plt.show()
 

png

 plt.clf()   # clear figure

plt.plot(epochs, acc, '-r^', label='Training acc')
plt.plot(epochs, val_acc, '-bo', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='best')

plt.show()
 

png

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

הסדרת גרף

עכשיו אנחנו מוכנים לנסות להסדרת גרפים באמצעות מודל הבסיס שבנינו לעיל. נשתמש GraphRegularization מעמד המעטפת שמספק מסגרת למידת Structured העצבית לעטוף את הדגם הבסיסי (דו-LSTM) לכלול להסדרת גרף. שאר הצעדים לאימוני הערכת המודל סדיר-הגרף הם דומה לזה של דגם הבסיס.

צור מודל הסדירה גרף

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

 # Build a new base LSTM model.
base_reg_model = make_bilstm_model()
 
 # Wrap the base model with graph regularization.
graph_reg_config = nsl.configs.make_graph_reg_config(
    max_neighbors=HPARAMS.num_neighbors,
    multiplier=HPARAMS.graph_regularization_multiplier,
    distance_type=HPARAMS.distance_type,
    sum_over_axis=-1)
graph_reg_model = nsl.keras.GraphRegularization(base_reg_model,
                                                graph_reg_config)
graph_reg_model.compile(
    optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
 

רכבת המודל

 graph_reg_history = graph_reg_model.fit(
    train_dataset,
    validation_data=validation_dataset,
    epochs=HPARAMS.train_epochs,
    verbose=1)
 
Epoch 1/10

/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/framework/indexed_slices.py:432: UserWarning: Converting sparse IndexedSlices to a dense Tensor of unknown shape. This may consume a large amount of memory.
  "Converting sparse IndexedSlices to a dense Tensor of unknown shape. "

21/21 [==============================] - 22s 1s/step - loss: 0.6930 - accuracy: 0.5246 - scaled_graph_loss: 2.9800e-06 - val_loss: 0.6929 - val_accuracy: 0.4998
Epoch 2/10
21/21 [==============================] - 21s 988ms/step - loss: 0.6909 - accuracy: 0.5200 - scaled_graph_loss: 7.8452e-06 - val_loss: 0.6838 - val_accuracy: 0.5917
Epoch 3/10
21/21 [==============================] - 21s 980ms/step - loss: 0.6656 - accuracy: 0.6277 - scaled_graph_loss: 6.1205e-04 - val_loss: 0.6591 - val_accuracy: 0.6905
Epoch 4/10
21/21 [==============================] - 21s 981ms/step - loss: 0.6395 - accuracy: 0.6846 - scaled_graph_loss: 0.0016 - val_loss: 0.5860 - val_accuracy: 0.7171
Epoch 5/10
21/21 [==============================] - 21s 980ms/step - loss: 0.5388 - accuracy: 0.7573 - scaled_graph_loss: 0.0043 - val_loss: 0.4910 - val_accuracy: 0.7844
Epoch 6/10
21/21 [==============================] - 21s 989ms/step - loss: 0.4105 - accuracy: 0.8281 - scaled_graph_loss: 0.0146 - val_loss: 0.3353 - val_accuracy: 0.8612
Epoch 7/10
21/21 [==============================] - 21s 986ms/step - loss: 0.3416 - accuracy: 0.8681 - scaled_graph_loss: 0.0203 - val_loss: 0.4134 - val_accuracy: 0.8209
Epoch 8/10
21/21 [==============================] - 21s 981ms/step - loss: 0.4230 - accuracy: 0.8273 - scaled_graph_loss: 0.0144 - val_loss: 0.4755 - val_accuracy: 0.7696
Epoch 9/10
21/21 [==============================] - 22s 1s/step - loss: 0.4905 - accuracy: 0.7950 - scaled_graph_loss: 0.0080 - val_loss: 0.3862 - val_accuracy: 0.8382
Epoch 10/10
21/21 [==============================] - 21s 978ms/step - loss: 0.3384 - accuracy: 0.8754 - scaled_graph_loss: 0.0215 - val_loss: 0.3002 - val_accuracy: 0.8811

הערכת המודל

 graph_reg_results = graph_reg_model.evaluate(test_dataset, steps=HPARAMS.eval_steps)
print(graph_reg_results)
 
196/196 [==============================] - 16s 84ms/step - loss: 0.3852 - accuracy: 0.8301
[0.385225385427475, 0.830079972743988]

צייר גרף של / פסד דיוק לאורך זמן

 graph_reg_history_dict = graph_reg_history.history
graph_reg_history_dict.keys()
 
dict_keys(['loss', 'accuracy', 'scaled_graph_loss', 'val_loss', 'val_accuracy'])

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

 acc = graph_reg_history_dict['accuracy']
val_acc = graph_reg_history_dict['val_accuracy']
loss = graph_reg_history_dict['loss']
graph_loss = graph_reg_history_dict['scaled_graph_loss']
val_loss = graph_reg_history_dict['val_loss']

epochs = range(1, len(acc) + 1)

plt.clf()   # clear figure

# "-r^" is for solid red line with triangle markers.
plt.plot(epochs, loss, '-r^', label='Training loss')
# "-gD" is for solid green line with diamond markers.
plt.plot(epochs, graph_loss, '-gD', label='Training graph loss')
# "-b0" is for solid blue line with circle markers.
plt.plot(epochs, val_loss, '-bo', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend(loc='best')

plt.show()
 

png

 plt.clf()   # clear figure

plt.plot(epochs, acc, '-r^', label='Training acc')
plt.plot(epochs, val_acc, '-bo', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='best')

plt.show()
 

png

כוחה של למידה למחצה בפיקוח

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

אנו מגדירים יחס פיקוח כיחס של אימון דגימות למספר הכולל של דגימות הכולל הכשרה, אימות, ולבדוק דגימות. במחברת זו, השתמשנו יחס פיקוח של 0.05 (כלומר, 5% של הנתונים שכותרתו) לאימונים הוא במודל הבסיס וכן את המודל סדיר-גרף. אנחנו מדגימים את ההשפעה של יחס הפיקוח על דיוק מודל בתא שמתחתיו.

 # Accuracy values for both the Bi-LSTM model and the feed forward NN model have
# been precomputed for the following supervision ratios.

supervision_ratios = [0.3, 0.15, 0.05, 0.03, 0.02, 0.01, 0.005]

model_tags = ['Bi-LSTM model', 'Feed Forward NN model']
base_model_accs = [[84, 84, 83, 80, 65, 52, 50], [87, 86, 76, 74, 67, 52, 51]]
graph_reg_model_accs = [[84, 84, 83, 83, 65, 63, 50],
                        [87, 86, 80, 75, 67, 52, 50]]

plt.clf()  # clear figure

fig, axes = plt.subplots(1, 2)
fig.set_size_inches((12, 5))

for ax, model_tag, base_model_acc, graph_reg_model_acc in zip(
    axes, model_tags, base_model_accs, graph_reg_model_accs):

  # "-r^" is for solid red line with triangle markers.
  ax.plot(base_model_acc, '-r^', label='Base model')
  # "-gD" is for solid green line with diamond markers.
  ax.plot(graph_reg_model_acc, '-gD', label='Graph-regularized model')
  ax.set_title(model_tag)
  ax.set_xlabel('Supervision ratio')
  ax.set_ylabel('Accuracy(%)')
  ax.set_ylim((25, 100))
  ax.set_xticks(range(len(supervision_ratios)))
  ax.set_xticklabels(supervision_ratios)
  ax.legend(loc='best')

plt.show()
 
<Figure size 432x288 with 0 Axes>

png

ניתן לראות כי כיחס superivision פוחת, מודל דיוק גם פוחת. הדבר נכון גם עבור דגם הבסיס עבור דגם-הסדירה הגרף, ללא קשר ארכיטקטורת המודל המשמש. עם זאת, בהודעה כי מבצע מודל סדיר הגרף יותר מ במודל הבסיס הוא עבור הארכיטקטורות. בפרט, עבור דגם Bi-LSTM, כאשר יחס הפיקוח הוא 0.01, את הדיוק של המודל-הסדירה הגרף הוא ~ 20% גבוהים יותר מזה של הדגם הבסיסי. זהו בעיקר בשל למידה למחצה בפיקוח על המודל-הסדירה הגרף, איפה דמיון מבני בין דגימות אימונים משמש בנוסף דגימות אימונים עצמם.

סיכום

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