Sentezlenmiş grafikler kullanarak duyarlılık sınıflandırması için grafik düzenleme

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin

Genel Bakış

Bu defter, inceleme metnini kullanarak film incelemelerini olumlu veya olumsuz olarak sınıflandırır. Bu, önemli ve yaygın olarak uygulanabilen bir makine öğrenimi problemi olan ikili sınıflandırma örneğidir.

Verilen girdiden bir grafik oluşturarak bu not defterinde grafik düzenleme kullanımını göstereceğiz. Giriş açık bir grafik içermediğinde, Sinirsel Yapılandırılmış Öğrenme (NSL) çerçevesini kullanarak grafiğe göre düzenlenmiş bir model oluşturmanın genel tarifi aşağıdaki gibidir:

  1. Girişteki her bir metin örneği için yerleştirmeler oluşturun. Bu, word2vec , Swivel , BERT vb. Gibi önceden eğitilmiş modeller kullanılarak yapılabilir.
  2. 'L2' mesafesi, 'kosinüs' mesafesi vb. Gibi bir benzerlik ölçüsü kullanarak bu yerleştirmelere dayalı bir grafik oluşturun. Grafikteki düğümler örneklere karşılık gelir ve grafikteki kenarlar, örnek çiftleri arasındaki benzerliğe karşılık gelir.
  3. Yukarıdaki sentezlenmiş grafikten ve örnek özelliklerden eğitim verileri oluşturun. Elde edilen eğitim verileri, orijinal düğüm özelliklerine ek olarak komşu özellikleri de içerecektir.
  4. Keras sıralı, işlevsel veya alt sınıf API'sini kullanarak temel model olarak bir sinir ağı oluşturun.
  5. Yeni bir grafik Keras modeli oluşturmak için temel modeli NSL çerçevesi tarafından sağlanan GraphRegularization sarmalayıcı sınıfıyla sarın. Bu yeni model, eğitim hedefinde düzenlilik terimi olarak bir grafik düzenleme kaybı içerecektir.
  6. Grafik Keras modelini eğitin ve değerlendirin.

Gereksinimler

  1. Sinirsel Yapılandırılmış Öğrenme paketini yükleyin.
  2. Tensorflow-hub'ı kurun.
pip install --quiet neural-structured-learning
pip install --quiet tensorflow-hub

Bağımlılıklar ve ithalatlar

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 veri kümesi

IMDB veri kümesi , İnternet Film Veritabanından 50.000 film incelemesinin metnini içerir. Bunlar, eğitim için 25.000 inceleme ve test için 25.000 incelemeye bölünmüştür. Eğitim ve test setleri dengelidir , yani eşit sayıda olumlu ve olumsuz inceleme içerirler.

Bu eğiticide, IMDB veri kümesinin önceden işlenmiş bir sürümünü kullanacağız.

Önceden işlenmiş IMDB veri kümesini indirin

IMDB veri kümesi, TensorFlow ile paketlenmiş olarak gelir. İncelemelerin (kelime dizileri), her bir tamsayının bir sözlükteki belirli bir sözcüğü temsil ettiği tamsayı dizilerine dönüştürüldüğü şekilde önceden işlenmiştir.

Aşağıdaki kod, IMDB veri kümesini indirir (veya önceden indirilmişse önbelleğe alınmış bir kopyayı kullanır):

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 argümanı eğitim verilerinde en sık görülen 10.000 kelimeyi tutar. Kelime dağarcığını yönetilebilir tutmak için nadir kelimeler atılır.

Verileri keşfedin

Verinin formatını anlamak için biraz zaman ayıralım. Veri kümesi önceden işlenmiş olarak gelir: her örnek, film incelemesinin kelimelerini temsil eden bir tamsayı dizisidir. Her etiket, 0 veya 1 tam sayı değeridir; burada 0, olumsuz bir incelemedir ve 1, olumlu bir incelemedir.

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

İnceleme metni, her bir tamsayının bir sözlükteki belirli bir kelimeyi temsil ettiği tam sayılara dönüştürüldü. İlk inceleme şöyle görünüyor:

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]

Film incelemeleri farklı uzunluklarda olabilir. Aşağıdaki kod, birinci ve ikinci incelemelerdeki kelime sayısını gösterir. Bir sinir ağına girişler aynı uzunlukta olması gerektiğinden, bunu daha sonra çözmemiz gerekecek.

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

Tam sayıları kelimelere geri dönüştürün

Tam sayıların karşılık gelen metne nasıl dönüştürüleceğini bilmek faydalı olabilir. Burada, tamsayı ile dizgi eşlemesini içeren bir sözlük nesnesini sorgulamak için bir yardımcı işlev oluşturacağız:

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

Şimdi, ilk incelemenin metnini görüntülemek için decode_review işlevini kullanabiliriz:

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"

Grafik yapısı

Grafik oluşturma, metin örnekleri için yerleştirmeler oluşturmayı ve ardından yerleştirmeleri karşılaştırmak için bir benzerlik işlevini kullanmayı içerir.

Daha fazla ilerlemeden önce, ilk olarak bu öğretici tarafından oluşturulan yapıları depolamak için bir dizin oluşturuyoruz.

mkdir -p /tmp/imdb

Örnek yerleştirmeler oluşturun

tf.train.Example her örnek için tf.train.Example biçiminde tf.train.Example oluşturmak için önceden eğitilmiş Döner yerleştirmeleri kullanacağız. Ortaya çıkan yerleştirmeleri, her numunenin kimliğini temsil eden ek bir özellik ile birlikte TFRecord formatında TFRecord . Bu önemlidir ve örnek yerleştirmeleri daha sonra grafikteki karşılık gelen düğümlerle eşleştirmemize olanak tanır.

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

Bir grafik oluşturun

Şimdi örnek yerleştirmelerimiz olduğuna göre, bunları bir benzerlik grafiği oluşturmak için kullanacağız, yani bu grafikteki düğümler örneklere karşılık gelecek ve bu grafikteki kenarlar düğüm çiftleri arasındaki benzerliğe karşılık gelecektir.

Sinirsel Yapılandırılmış Öğrenme, örnek gömmelere dayalı bir grafik oluşturmak için bir grafik oluşturma kitaplığı sağlar. Düğünleri karşılaştırmak ve aralarında kenarlar oluşturmak için benzerlik ölçüsü olarak kosinüs benzerliğini kullanır. Aynı zamanda, son grafikten farklı kenarları çıkarmak için kullanılabilecek bir benzerlik eşiği belirlememize olanak tanır. Bu örnekte, benzerlik eşiği olarak 0.99 ve rastgele çekirdek olarak 12345 kullanarak, 429.415 çift yönlü kenarı olan bir grafik elde ediyoruz. Burada, grafik oluşturmayı hızlandırmak için grafik oluşturucunun yerellik duyarlı hashing (LSH) desteğini kullanıyoruz. Grafik oluşturucunun LSH desteğini kullanmayla ilgili ayrıntılar için build_graph_from_config API belgelerine bakın.

graph_builder_config = nsl.configs.GraphBuilderConfig(
    similarity_threshold=0.99, lsh_splits=32, lsh_rounds=15, random_seed=12345)
nsl.tools.build_graph_from_config(['/tmp/imdb/embeddings.tfr'],
                                  '/tmp/imdb/graph_99.tsv',
                                  graph_builder_config)

Her iki yönlü kenar, çıktı TSV dosyasında iki yönlendirilmiş kenarla temsil edilir, böylece dosya 429.415 * 2 = 858.830 toplam satır içerir:

wc -l /tmp/imdb/graph_99.tsv
858830 /tmp/imdb/graph_99.tsv

Örnek özellikler

tf.train.Example formatını kullanarak problemimiz için örnek özellikler oluşturup bunları TFRecord formatında TFRecord . Her örnek aşağıdaki üç özelliği içerecektir:

  1. id : Örneğin düğüm kimliği.
  2. kelimeler : Kelime kimliklerini içeren bir int64 listesi.
  3. etiket : Gözden geçirmenin hedef sınıfını tanımlayan bir singleton 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

Grafik komşuları ile eğitim verilerini artırın

Örnek özelliklere ve sentezlenmiş grafiğe sahip olduğumuz için, Sinirsel Yapılandırılmış Öğrenme için artırılmış eğitim verilerini oluşturabiliriz. NSL çerçevesi, grafiğin düzenlenmesi için son eğitim verilerini üretmek üzere grafiği ve örnek özellikleri birleştiren bir kitaplık sağlar. Elde edilen eğitim verileri, orijinal örnek özelliklerin yanı sıra karşılık gelen komşularının özelliklerini içerecektir.

Bu eğiticide, yönsüz kenarları ele alıyoruz ve grafik komşularıyla eğitim verilerini artırmak için örnek başına maksimum 3 komşu kullanıyoruz.

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)

Temel model

Artık grafik düzenleme olmadan temel bir model oluşturmaya hazırız. Bu modeli inşa etmek için ya grafiğin oluşturulmasında kullanılan düğünleri kullanabiliriz ya da sınıflandırma görevi ile birlikte yeni yerleştirmeleri birlikte öğrenebiliriz. Bu defterin amacı için ikincisini yapacağız.

Global değişkenler

NBR_FEATURE_PREFIX = 'NL_nbr_'
NBR_WEIGHT_SUFFIX = '_weight'

Hiperparametreler

Biz bir örneğini kullanacak HParams eğitim ve değerlendirme için kullanılan çeşitli hyperparameters ve sabitleri içermek. Her birini aşağıda kısaca açıklıyoruz:

  • sınıf_sayısı : 2 sınıf vardır - pozitif ve negatif .

  • max_seq_length : Bu, bu örnekteki her film incelemesinde dikkate alınan maksimum kelime sayısıdır.

  • word_size : Bu, bu örnek için düşünülen kelime dağarcığının boyutudur.

  • mesafe_türü : Bu, numuneyi komşularıyla düzenlemek için kullanılan mesafe ölçüsüdür.

  • graph_regularization_multiplier : Bu, genel kayıp fonksiyonundaki grafik düzenleme teriminin göreceli ağırlığını kontrol eder.

  • num_neighbors : Grafik düzenlenmesi için kullanılan komşuların sayısı. Bu değer, max_nbrs çağrılırken yukarıda kullanılan max_nbrs bağımsız değişkeninden küçük veya ona eşitnsl.tools.pack_nbrs .

  • num_fc_units : Sinir ağının tam olarak bağlı katmanındaki birimlerin sayısı.

  • train_epochs : Eğitim dönemi sayısı.

  • batch_size : Eğitim ve değerlendirme için kullanılan parti boyutu.

  • eval_steps : Değerlendirmenin tamamlandığını düşünmeden önce işlenecek parti sayısı. None ayarlanırsa, test setindeki tüm örnekler değerlendirilir.

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()

Verileri hazırlayın

İncelemeler - tamsayı dizileri - sinir ağına beslenmeden önce tensörlere dönüştürülmelidir. Bu dönüştürme birkaç şekilde yapılabilir:

  • Dizileri, tek sıcak kodlamaya benzer şekilde, kelime oluşumunu gösteren 0 ve 1 s'lik vektörlere dönüştürün. Örneğin, [3, 5] dizisi, birler olan 3 ve 5 indisleri dışında tümü sıfır olan 10000 boyutlu bir vektör haline gelecektir. Ardından, bunu ağımızdaki kayan nokta vektör verilerini işleyebilen ilk katman ( Dense katman) yapın. Ancak bu yaklaşım, yoğun bellek gerektirir ve bir num_words * num_reviews boyut matrisi gerektirir.

  • Alternatif olarak, dizileri aynı uzunlukta olacak şekilde max_length * num_reviews , sonra da max_length * num_reviews şeklinde bir tamsayı tensörü oluşturabiliriz. Ağımızdaki ilk katman olarak bu şekli işleyebilen bir gömme katmanı kullanabiliriz.

Bu eğitimde ikinci yaklaşımı kullanacağız.

Film incelemelerinin aynı uzunlukta olması gerektiğinden, uzunlukları standartlaştırmak için aşağıda tanımlanan pad_sequence işlevini kullanacağız.

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')

Modeli oluşturun

Bir sinir ağı, katmanların üst üste dizilmesiyle oluşturulur - bu, iki ana mimari karar gerektirir:

  • Modelde kaç katman kullanılmalı?
  • Her katman için kaç tane gizli birim kullanılacak?

Bu örnekte, giriş verileri bir dizi kelime indeksinden oluşur. Tahmin edilecek etiketler 0 veya 1'dir.

Bu eğitimde temel modelimiz olarak çift yönlü bir LSTM kullanacağız.

# 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
_________________________________________________________________

Katmanlar, sınıflandırıcıyı oluşturmak için etkili bir şekilde sıralı olarak istiflenir:

  1. İlk katman, tamsayı ile kodlanmış kelimeleri alan bir Input katmanıdır.
  2. Bir sonraki katman, tamsayı ile kodlanmış kelimeleri alan ve her kelime dizini için gömme vektörünü arayan bir Embedding katmanıdır. Bu vektörler model trenler olarak öğrenilir. Vektörler, çıktı dizisine bir boyut ekler. Ortaya çıkan boyutlar: (batch, sequence, embedding) .
  3. Daha sonra, çift yönlü bir LSTM katmanı, her örnek için sabit uzunlukta bir çıktı vektörü döndürür.
  4. Bu sabit uzunluktaki çıkış vektörü, 64 gizli birim içeren tam bağlantılı ( Dense ) bir katman aracılığıyla Dense .
  5. Son katman, tek bir çıkış düğümü ile yoğun bir şekilde bağlanır. sigmoid aktivasyon fonksiyonunu kullanarak, bu değer, bir olasılığı veya güven seviyesini temsil eden, 0 ile 1 arasında bir kaymadır.

Gizli birimler

Yukarıdaki model, giriş ve çıkış arasında ve Embedding katmanı hariç olmak üzere iki ara veya "gizli" katmana sahiptir. Çıktıların sayısı (birimler, düğümler veya nöronlar), katman için temsili alanın boyutudur. Başka bir deyişle, dahili bir temsili öğrenirken ağa izin verilen özgürlük miktarı.

Bir modelin daha fazla gizli birimi (daha yüksek boyutlu bir temsil alanı) ve / veya daha fazla katmanı varsa, ağ daha karmaşık gösterimleri öğrenebilir. Ancak, ağı hesaplama açısından daha pahalı hale getirir ve istenmeyen kalıpların öğrenilmesine yol açabilir - eğitim verilerinde performansı artıran ancak test verilerinde olmayan örüntüler. Buna aşırı uyum denir.

Kayıp işlevi ve optimize edici

Bir modelin bir kayıp işlevine ve eğitim için bir optimize ediciye ihtiyacı vardır. Bu bir ikili sınıflandırma problemi olduğundan ve model bir olasılık çıkardığından (sigmoid aktivasyonlu tek birimlik bir katman), binary_crossentropy loss fonksiyonunu kullanacağız.

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

Doğrulama seti oluşturun

Eğitim yaparken, modelin doğruluğunu daha önce görmediği veriler üzerinde kontrol etmek istiyoruz. Orijinal eğitim verilerinin bir kısmını ayırarak bir doğrulama kümesi oluşturun. (Neden test setini şimdi kullanmıyoruz? Amacımız, modelimizi yalnızca eğitim verilerini kullanarak geliştirmek ve ayarlamak, ardından doğruluğumuzu değerlendirmek için test verilerini yalnızca bir kez kullanmaktır).

Bu eğiticide, başlangıç ​​eğitim örneklerinin yaklaşık% 10'unu (25000'in% 10'u) eğitim için etiketli veriler ve geri kalanını doğrulama verileri olarak alıyoruz. İlk eğitim / test bölümü 50/50 (her biri 25000 örnek) olduğundan, şu anda sahip olduğumuz etkili eğitim / doğrulama / test bölümü 5/45/50'dir.

'Train_dataset'in zaten toplu ve karıştırıldığını unutmayın.

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

Modeli eğitin

Modeli mini gruplar halinde eğitin. Eğitim sırasında modelin doğrulama setindeki kaybını ve doğruluğunu izleyin:

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 917ms/step - loss: 0.6930 - accuracy: 0.5081 - val_loss: 0.6924 - val_accuracy: 0.5518
Epoch 2/10
21/21 [==============================] - 18s 878ms/step - loss: 0.6902 - accuracy: 0.5319 - val_loss: 0.6587 - val_accuracy: 0.6465
Epoch 3/10
21/21 [==============================] - 18s 879ms/step - loss: 0.6338 - accuracy: 0.6731 - val_loss: 0.5882 - val_accuracy: 0.7310
Epoch 4/10
21/21 [==============================] - 18s 872ms/step - loss: 0.4889 - accuracy: 0.7854 - val_loss: 0.4445 - val_accuracy: 0.8047
Epoch 5/10
21/21 [==============================] - 18s 872ms/step - loss: 0.3911 - accuracy: 0.8369 - val_loss: 0.3870 - val_accuracy: 0.8352
Epoch 6/10
21/21 [==============================] - 18s 877ms/step - loss: 0.3544 - accuracy: 0.8542 - val_loss: 0.3420 - val_accuracy: 0.8571
Epoch 7/10
21/21 [==============================] - 19s 900ms/step - loss: 0.3262 - accuracy: 0.8700 - val_loss: 0.3135 - val_accuracy: 0.8762
Epoch 8/10
21/21 [==============================] - 18s 871ms/step - loss: 0.2770 - accuracy: 0.8977 - val_loss: 0.2739 - val_accuracy: 0.8923
Epoch 9/10
21/21 [==============================] - 18s 872ms/step - loss: 0.2863 - accuracy: 0.8958 - val_loss: 0.2703 - val_accuracy: 0.8942
Epoch 10/10
21/21 [==============================] - 18s 875ms/step - loss: 0.2232 - accuracy: 0.9150 - val_loss: 0.2543 - val_accuracy: 0.9037

Modeli değerlendirin

Şimdi modelin nasıl performans gösterdiğini görelim. İki değer döndürülecek. Kayıp (hatamızı temsil eden bir sayı, daha düşük değerler daha iyidir) ve doğruluk.

results = model.evaluate(test_dataset, steps=HPARAMS.eval_steps)
print(results)
196/196 [==============================] - 16s 82ms/step - loss: 0.3748 - accuracy: 0.8500
[0.37483155727386475, 0.8500000238418579]

Zaman içindeki doğruluk / kayıp grafiği oluşturun

model.fit() , eğitim sırasında olan her şeyi içeren bir sözlüğü içeren bir History nesnesi döndürür:

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

Dört giriş vardır: eğitim ve doğrulama sırasında izlenen her metrik için bir tane. Bunları, karşılaştırma için eğitim ve doğrulama kaybının yanı sıra eğitim ve doğrulama doğruluğunu planlamak için kullanabiliriz:

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

Her çağda eğitim kaybının azaldığına ve eğitim doğruluğunun her çağda arttığına dikkat edin. Bir gradyan iniş optimizasyonu kullanılırken bu beklenen bir durumdur - her yinelemede istenen miktarı en aza indirmelidir.

Grafik düzenlileştirme

Şimdi, yukarıda oluşturduğumuz temel modeli kullanarak grafik düzenlemeyi denemeye hazırız. Temel (bi-LSTM) modeli grafik düzenlileştirmeyi içerecek şekilde sarmak için Sinirsel Yapılandırılmış Öğrenme çerçevesi tarafından sağlanan GraphRegularization sarmalayıcı sınıfını kullanacağız. Grafiğe göre düzenlenmiş modeli eğitme ve değerlendirme adımlarının geri kalanı, temel modelinkine benzer.

Grafiğe göre düzenlenmiş model oluşturun

Grafik düzenlemenin artan faydasını değerlendirmek için yeni bir temel model örneği oluşturacağız. Bunun nedeni model zaten birkaç tekrarlamalar için eğitilmiş ve grafik-regularized model oluşturmak için bu eğitimli modeli yeniden kullanmak için adil bir karşılaştırma olmayacak 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'])

Modeli eğitin

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.6925 - accuracy: 0.5135 - scaled_graph_loss: 7.8682e-06 - val_loss: 0.6925 - val_accuracy: 0.5207
Epoch 2/10
21/21 [==============================] - 22s 1s/step - loss: 0.6902 - accuracy: 0.5373 - scaled_graph_loss: 2.3502e-05 - val_loss: 0.6591 - val_accuracy: 0.6627
Epoch 3/10
21/21 [==============================] - 21s 981ms/step - loss: 0.6376 - accuracy: 0.6942 - scaled_graph_loss: 0.0028 - val_loss: 0.6867 - val_accuracy: 0.5343
Epoch 4/10
21/21 [==============================] - 20s 975ms/step - loss: 0.6240 - accuracy: 0.7031 - scaled_graph_loss: 9.6606e-04 - val_loss: 0.5891 - val_accuracy: 0.7572
Epoch 5/10
21/21 [==============================] - 20s 973ms/step - loss: 0.5111 - accuracy: 0.7896 - scaled_graph_loss: 0.0059 - val_loss: 0.4260 - val_accuracy: 0.8207
Epoch 6/10
21/21 [==============================] - 21s 981ms/step - loss: 0.3816 - accuracy: 0.8508 - scaled_graph_loss: 0.0157 - val_loss: 0.3182 - val_accuracy: 0.8682
Epoch 7/10
21/21 [==============================] - 20s 976ms/step - loss: 0.3488 - accuracy: 0.8704 - scaled_graph_loss: 0.0202 - val_loss: 0.3156 - val_accuracy: 0.8749
Epoch 8/10
21/21 [==============================] - 20s 973ms/step - loss: 0.3227 - accuracy: 0.8815 - scaled_graph_loss: 0.0198 - val_loss: 0.2746 - val_accuracy: 0.8932
Epoch 9/10
21/21 [==============================] - 21s 1s/step - loss: 0.3058 - accuracy: 0.8958 - scaled_graph_loss: 0.0220 - val_loss: 0.2938 - val_accuracy: 0.8833
Epoch 10/10
21/21 [==============================] - 21s 979ms/step - loss: 0.2789 - accuracy: 0.9008 - scaled_graph_loss: 0.0233 - val_loss: 0.2622 - val_accuracy: 0.8981

Modeli değerlendirin

graph_reg_results = graph_reg_model.evaluate(test_dataset, steps=HPARAMS.eval_steps)
print(graph_reg_results)
196/196 [==============================] - 16s 82ms/step - loss: 0.3543 - accuracy: 0.8508
[0.354336142539978, 0.8507599830627441]

Zaman içindeki doğruluk / kayıp grafiği oluşturun

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

Sözlükte toplam beş giriş vardır: eğitim kaybı, eğitim doğruluğu, eğitim grafiği kaybı, doğrulama kaybı ve doğrulama doğruluğu. Karşılaştırma için hepsini birlikte planlayabiliriz. Grafik kaybının yalnızca eğitim sırasında hesaplandığını unutmayın.

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

Yarı denetimli öğrenmenin gücü

Yarı denetimli öğrenme ve daha spesifik olarak, bu eğitim bağlamında grafik düzenleme, eğitim verilerinin miktarı az olduğunda gerçekten güçlü olabilir. Eğitim verilerinin eksikliği, geleneksel denetimli öğrenmede mümkün olmayan eğitim örnekleri arasındaki benzerlikten yararlanılarak telafi edilir.

Denetim oranını , eğitim numunelerinin eğitim, doğrulama ve test numunelerini içeren toplam numune sayısına oranı olarak tanımlıyoruz. Bu defterde, hem temel modeli hem de grafiğe göre düzenlenmiş modeli eğitmek için 0,05'lik bir denetim oranı (yani, etiketli verilerin% 5'i) kullandık. Denetim oranının model doğruluğu üzerindeki etkisini aşağıdaki hücrede gösteriyoruz.

# 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

Görme oranı azaldıkça model doğruluğunun da azaldığı gözlemlenebilir. Bu, kullanılan model mimarisinden bağımsız olarak hem temel model hem de grafiğe göre düzenlenmiş model için geçerlidir. Ancak, grafiğe göre düzenlenmiş modelin her iki mimari için de temel modelden daha iyi performans gösterdiğine dikkat edin. Özellikle, Bi-LSTM modeli için, denetim oranı 0.01 olduğunda, grafiğe göre düzenlenmiş modelin doğruluğu, temel modelinkinden ~% 20 daha yüksektir. Bunun başlıca nedeni, eğitim örneklerinin kendilerine ek olarak eğitim örnekleri arasındaki yapısal benzerliğin kullanıldığı, grafiğe göre düzenlenmiş model için yarı denetimli öğrenmedir.

Sonuç

Girdi açık bir grafik içermese bile, Sinirsel Yapılandırılmış Öğrenme (NSL) çerçevesini kullanarak grafik düzenlemesinin kullanımını gösterdik. İnceleme yerleştirmelerine dayalı bir benzerlik grafiği sentezlediğimiz IMDB film incelemelerinin duyarlılık sınıflandırması görevini ele aldık. Kullanıcıları, değişen hiperparametreler, denetim miktarı ve farklı model mimarileri kullanarak daha fazla deneme yapmaya teşvik ediyoruz.