Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Regulatorisasi grafik untuk klasifikasi sentimen menggunakan grafik yang disintesis

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub

Gambaran

Notebook ini mengklasifikasikan ulasan film sebagai positif atau negatif menggunakan teks ulasan. Ini adalah contoh klasifikasi biner , jenis masalah pembelajaran mesin yang penting dan dapat diterapkan secara luas.

Kami akan menunjukkan penggunaan regularisasi grafik di notebook ini dengan membuat grafik dari input yang diberikan. Resep umum untuk membangun model yang diatur grafik menggunakan kerangka Neural Structured Learning (NSL) ketika input tidak mengandung grafik eksplisit adalah sebagai berikut:

  1. Buat embeddings untuk setiap sampel teks dalam input. Ini dapat dilakukan dengan menggunakan model pra-terlatih seperti word2vec , Swivel , BERT dll.
  2. Bangun grafik berdasarkan embeddings ini dengan menggunakan metrik kesamaan seperti jarak 'L2', jarak 'cosinus', dll. Node dalam grafik berhubungan dengan sampel dan tepi pada grafik berhubungan dengan kesamaan antara pasangan sampel.
  3. Hasilkan data pelatihan dari grafik yang disintesis di atas dan fitur sampel. Data pelatihan yang dihasilkan akan berisi fitur tetangga selain fitur simpul asli.
  4. Buat jaringan saraf sebagai model dasar menggunakan API sekuensial, fungsional, atau subkelas Keras.
  5. Bungkus model dasar dengan kelas wrapper GraphRegularisasi, yang disediakan oleh kerangka kerja NSL, untuk membuat model grafik Keras baru. Model baru ini akan menyertakan grafik kerugian regularisasi sebagai istilah regularisasi dalam tujuan pelatihannya.
  6. Latih dan evaluasi grafik model Keras.

Persyaratan

  1. Instal paket Pembelajaran Terstruktur Saraf.
  2. Pasang hub tensorflow.
pip install --quiet neural-structured-learning
pip install --quiet tensorflow-hub

Ketergantungan dan impor

 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

Dataset IMDB

Dataset IMDB berisi teks 50.000 ulasan film dari Internet Movie Database . Ini dibagi menjadi 25.000 ulasan untuk pelatihan dan 25.000 ulasan untuk pengujian. Perangkat pelatihan dan pengujian seimbang , artinya berisi ulasan positif dan negatif dengan jumlah yang sama.

Dalam tutorial ini, kita akan menggunakan versi preproses data IMDB.

Unduh dataset IMDB yang telah diproses sebelumnya

Dataset IMDB dikemas dengan TensorFlow. Sudah diproses sebelumnya sehingga ulasan (urutan kata) telah dikonversi ke urutan bilangan bulat, di mana setiap bilangan bulat mewakili kata tertentu dalam kamus.

Kode berikut mengunduh dataset IMDB (atau menggunakan salinan cache jika sudah diunduh):

 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

Argumen num_words=10000 menyimpan num_words=10000 kata teratas yang paling sering muncul dalam data pelatihan. Kata-kata langka dibuang untuk menjaga agar ukuran kosakata dapat dikelola.

Jelajahi data

Mari luangkan waktu sejenak untuk memahami format data. Dataset datang diproses: setiap contoh adalah array bilangan bulat yang mewakili kata-kata dari tinjauan film. Setiap label adalah nilai integer 0 atau 1, di mana 0 adalah ulasan negatif, dan 1 adalah ulasan positif.

 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

Teks ulasan telah dikonversi menjadi bilangan bulat, di mana setiap bilangan bulat mewakili kata tertentu dalam kamus. Seperti apa ulasan pertama:

 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]

Ulasan film mungkin berbeda. Kode di bawah ini menunjukkan jumlah kata dalam ulasan pertama dan kedua. Karena input ke jaringan saraf harus sama panjang, kita harus menyelesaikannya nanti.

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

Ubah bilangan bulat kembali ke kata-kata

Mungkin bermanfaat untuk mengetahui cara mengubah bilangan bulat kembali ke teks yang sesuai. Di sini, kami akan membuat fungsi pembantu untuk menanyakan objek kamus yang berisi pemetaan integer ke string:

 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

Sekarang kita dapat menggunakan fungsi decode_review untuk menampilkan teks untuk ulasan pertama:

 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"

Konstruksi grafik

Konstruksi grafik melibatkan pembuatan embeddings untuk sampel teks dan kemudian menggunakan fungsi kesamaan untuk membandingkan embeddings.

Sebelum melangkah lebih jauh, kita terlebih dahulu membuat direktori untuk menyimpan artefak yang dibuat oleh tutorial ini.

mkdir -p /tmp/imdb

Buat contoh pernikahan

Kami akan menggunakan embri Putar putar yang sudah tf.train.Example untuk membuat embeddings dalam format tf.train.Example untuk setiap sampel dalam input. Kami akan menyimpan embeddings yang dihasilkan dalam format TFRecord bersama dengan fitur tambahan yang mewakili ID dari setiap sampel. Ini penting dan akan memungkinkan kami mencocokkan sampel pernikahan dengan simpul yang sesuai dalam grafik nanti.

 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

Buat grafik

Sekarang setelah kita memiliki embeddings sampel, kita akan menggunakannya untuk membangun grafik kesamaan, yaitu, node dalam grafik ini akan sesuai dengan sampel dan tepi dalam grafik ini akan sesuai dengan kesamaan antara pasangan node.

Neural Structured Learning menyediakan perpustakaan bangunan grafik untuk membangun grafik berdasarkan sampel pernikahan. Ia menggunakan cosine similarity sebagai ukuran kesamaan untuk membandingkan embeddings dan membangun edge di antara mereka. Ini juga memungkinkan kita untuk menentukan ambang kesamaan, yang dapat digunakan untuk membuang tepi yang berbeda dari grafik akhir. Dalam contoh ini, menggunakan 0,99 sebagai ambang kesamaan, kita berakhir dengan grafik yang memiliki 445.327 tepi dua arah.

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

Fitur sampel

Kami membuat fitur sampel untuk masalah kami menggunakan format tf.train.Example dan bertahan dalam format TFRecord . Setiap sampel akan mencakup tiga fitur berikut:

  1. id : ID simpul sampel.
  2. kata-kata : Daftar int64 yang berisi ID kata.
  3. label : A singleton int64 yang mengidentifikasi kelas target tinjauan.
 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

Menambah data pelatihan dengan tetangga grafik

Karena kami memiliki fitur sampel dan grafik yang disintesis, kami dapat menghasilkan data pelatihan yang diperbesar untuk Pembelajaran Terstruktur Neural. Kerangka kerja NSL menyediakan perpustakaan untuk menggabungkan grafik dan fitur sampel untuk menghasilkan data pelatihan akhir untuk regularisasi grafik. Data pelatihan yang dihasilkan akan mencakup fitur sampel asli serta fitur tetangga yang sesuai.

Dalam tutorial ini, kami mempertimbangkan tepi yang tidak diarahkan dan menggunakan maksimum 3 tetangga per sampel untuk menambah data pelatihan dengan tetangga grafik.

 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)
 

Model dasar

Kami sekarang siap untuk membangun model dasar tanpa regularisasi grafik. Untuk membangun model ini, kita dapat menggunakan embeddings yang digunakan dalam membangun grafik, atau kita dapat mempelajari embeddings baru bersama-sama bersama dengan tugas klasifikasi. Untuk keperluan notebook ini, kami akan melakukan yang terakhir.

Variabel global

 NBR_FEATURE_PREFIX = 'NL_nbr_'
NBR_WEIGHT_SUFFIX = '_weight'
 

Hyperparameter

Kami akan menggunakan instance HParams untuk HParams berbagai hiperparameter dan konstanta yang digunakan untuk pelatihan dan evaluasi. Kami jelaskan secara singkat masing-masing di bawah ini:

  • num_classes : Ada 2 kelas - positif dan negatif .

  • max_seq_length : Ini adalah jumlah kata maksimum yang dipertimbangkan dari setiap ulasan film dalam contoh ini.

  • vocab_size : Ini adalah ukuran kosakata yang dipertimbangkan untuk contoh ini.

  • distance_type : Ini adalah metrik jarak yang digunakan untuk mengatur sampel dengan tetangganya.

  • graph_regularization_multiplier : Ini mengontrol bobot relatif dari istilah regularisasi grafik dalam fungsi kerugian keseluruhan.

  • num_neighbors : Jumlah tetangga yang digunakan untuk regularisasi grafik. Nilai ini harus kurang dari atau sama dengan argumen max_nbrs digunakan di atas ketika memanggil nsl.tools.pack_nbrs .

  • num_fc_units : Jumlah unit dalam lapisan yang terhubung penuh dari jaringan saraf.

  • train_epochs : Jumlah zaman pelatihan.

  • batch_size : Ukuran batch yang digunakan untuk pelatihan dan evaluasi.

  • eval_steps : Jumlah batch untuk diproses sebelum menganggap evaluasi selesai. Jika diatur ke None , semua instance dalam set tes dievaluasi.

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

Siapkan data

Ulasan - array bilangan bulat - harus dikonversi ke tensor sebelum dimasukkan ke dalam jaringan saraf. Konversi ini dapat dilakukan dengan beberapa cara:

  • Ubah array menjadi vektor 0 s dan 1 s yang menunjukkan kemunculan kata, mirip dengan pengkodean satu-panas. Misalnya, urutan [3, 5] akan menjadi vektor 10000 dimensi yang semuanya nol kecuali untuk indeks 3 dan 5 , yang merupakan vektor. Lalu, buat ini layer pertama di jaringan kami — layer Dense — yang bisa menangani data vektor titik mengambang. Pendekatan ini bersifat intensif memori, membutuhkan matriks ukuran num_words * num_reviews .

  • Sebagai alternatif, kita dapat mengisi array sehingga semuanya memiliki panjang yang sama, lalu membuat bilangan bulat bentuk max_length * num_reviews . Kita dapat menggunakan lapisan embedding yang mampu menangani bentuk ini sebagai lapisan pertama di jaringan kami.

Dalam tutorial ini, kita akan menggunakan pendekatan kedua.

Karena ulasan film harus memiliki panjang yang sama, kami akan menggunakan fungsi pad_sequence didefinisikan di bawah ini untuk membuat standar panjang film.

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

Bangun model

Jaringan saraf dibuat dengan menumpuk lapisan — ini membutuhkan dua keputusan arsitektur utama:

  • Berapa banyak lapisan yang digunakan dalam model?
  • Berapa banyak unit tersembunyi yang digunakan untuk setiap lapisan?

Dalam contoh ini, data input terdiri dari array indeks kata. Label yang akan diprediksi adalah 0 atau 1.

Kami akan menggunakan LSTM dua arah sebagai model dasar kami dalam tutorial ini.

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

Lapisan-lapisan secara efektif ditumpuk secara berurutan untuk membangun classifier:

  1. Lapisan pertama adalah lapisan Input yang mengambil kosakata integer-encoded.
  2. Lapisan berikutnya adalah lapisan Embedding , yang mengambil kosa kata integer-encoded dan mencari vektor embedding untuk setiap indeks kata. Vektor ini dipelajari sebagai model kereta. Vektor menambahkan dimensi ke array output. Dimensi yang dihasilkan adalah: (batch, sequence, embedding) .
  3. Selanjutnya, layer LSTM dua arah mengembalikan vektor output dengan panjang tetap untuk setiap contoh.
  4. Vektor keluaran dengan panjang tetap ini disalurkan melalui lapisan ( Dense ) yang sepenuhnya terhubung dengan 64 unit tersembunyi.
  5. Lapisan terakhir terhubung dengan padat dengan satu simpul keluaran. Menggunakan fungsi aktivasi sigmoid , nilai ini mengambang antara 0 dan 1, mewakili probabilitas, atau tingkat kepercayaan.

Unit tersembunyi

Model di atas memiliki dua lapisan menengah atau "tersembunyi", antara input dan output, dan tidak termasuk lapisan Embedding . Jumlah output (unit, node, atau neuron) adalah dimensi ruang representasional untuk layer. Dengan kata lain, jumlah kebebasan yang diizinkan jaringan ketika mempelajari representasi internal.

Jika suatu model memiliki lebih banyak unit tersembunyi (ruang representasi berdimensi lebih tinggi), dan / atau lebih banyak lapisan, maka jaringan dapat mempelajari representasi yang lebih kompleks. Namun, itu membuat jaringan lebih mahal secara komputasi dan dapat mengarah pada pembelajaran pola yang tidak diinginkan — pola yang meningkatkan kinerja pada data pelatihan tetapi tidak pada data uji. Ini disebut overfitting .

Kehilangan fungsi dan pengoptimal

Model membutuhkan fungsi kehilangan dan pengoptimal untuk pelatihan. Karena ini adalah masalah klasifikasi biner dan model mengeluarkan probabilitas (lapisan unit-tunggal dengan aktivasi sigmoid), kami akan menggunakan fungsi kehilangan binary_crossentropy .

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

Buat set validasi

Saat pelatihan, kami ingin memeriksa keakuratan model pada data yang belum pernah dilihat sebelumnya. Buat set validasi dengan memisahkan sebagian dari data pelatihan asli. (Mengapa tidak menggunakan set pengujian sekarang? Tujuan kami adalah untuk mengembangkan dan menyetel model kami hanya menggunakan data pelatihan, lalu gunakan data uji sekali saja untuk mengevaluasi akurasi kami).

Dalam tutorial ini, kami mengambil sekitar 10% dari sampel pelatihan awal (10% dari 25.000) sebagai data berlabel untuk pelatihan dan sisanya sebagai data validasi. Karena kereta awal / uji split adalah 50/50 (masing-masing 25000 sampel), kereta efektif / validasi / uji split yang kita miliki sekarang adalah 5/45/50.

Perhatikan bahwa 'train_dataset' telah dikumpulkan dan dikocok.

 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

Latih modelnya

Latih model dalam batch-mini. Saat berlatih, pantau kehilangan dan akurasi model pada set validasi:

 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

Evaluasi model tersebut

Sekarang, mari kita lihat bagaimana kinerja model. Dua nilai akan dikembalikan. Kehilangan (angka yang mewakili kesalahan kami, nilai yang lebih rendah lebih baik), dan akurasi.

 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]

Buat grafik akurasi / kehilangan dari waktu ke waktu

model.fit() mengembalikan objek History yang berisi kamus dengan semua yang terjadi selama pelatihan:

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

Ada empat entri: satu untuk setiap metrik yang dipantau selama pelatihan dan validasi. Kita dapat menggunakan ini untuk merencanakan pelatihan dan kehilangan validasi untuk perbandingan, serta akurasi pelatihan dan validasi:

 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

Perhatikan kehilangan pelatihan berkurang dengan setiap zaman dan akurasi pelatihan meningkat dengan setiap zaman. Ini diharapkan ketika menggunakan optimasi gradient descent — itu harus meminimalkan kuantitas yang diinginkan pada setiap iterasi.

Regulatorisasi grafik

Kami sekarang siap untuk mencoba regularisasi grafik menggunakan model dasar yang kami buat di atas. Kami akan menggunakan kelas wrapper GraphRegularization disediakan oleh kerangka kerja Pembelajaran Terstruktur Neural untuk membungkus model dasar (bi-LSTM) untuk memasukkan regularisasi grafik. Langkah-langkah sisanya untuk pelatihan dan mengevaluasi model yang diatur grafik mirip dengan model dasar.

Buat model yang diatur grafik

Untuk menilai manfaat tambahan dari regularisasi grafik, kami akan membuat contoh model dasar baru. Ini karena model telah dilatih untuk beberapa iterasi, dan menggunakan kembali model yang terlatih ini untuk membuat model yang diatur grafik tidak akan menjadi perbandingan yang adil untuk 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'])
 

Latih modelnya

 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

Evaluasi model tersebut

 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]

Buat grafik akurasi / kehilangan dari waktu ke waktu

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

Ada lima entri dalam kamus: kehilangan pelatihan, akurasi pelatihan, kehilangan grafik pelatihan, kehilangan validasi, dan akurasi validasi. Kita bisa menyusun semuanya bersama untuk perbandingan. Perhatikan bahwa kehilangan grafik hanya dihitung selama pelatihan.

 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

Kekuatan pembelajaran semi-diawasi

Pembelajaran semi-terawasi dan lebih khusus lagi, grafik regularisasi dalam konteks tutorial ini, bisa sangat kuat ketika jumlah data pelatihan kecil. Kurangnya data pelatihan dikompensasi dengan memanfaatkan kesamaan di antara sampel pelatihan, yang tidak mungkin dalam pembelajaran yang diawasi secara tradisional.

Kami mendefinisikan rasio pengawasan sebagai rasio sampel pelatihan terhadap jumlah sampel yang mencakup pelatihan, validasi, dan sampel uji. Dalam buku catatan ini, kami telah menggunakan rasio pengawasan 0,05 (yaitu, 5% dari data yang diberi label) untuk melatih model dasar maupun model yang diatur grafik. Kami menggambarkan dampak rasio pengawasan terhadap akurasi model dalam sel di bawah ini.

 # 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

Dapat diamati bahwa ketika rasio superivision menurun, akurasi model juga menurun. Ini berlaku untuk model dasar dan untuk model yang diatur grafik, terlepas dari model arsitektur yang digunakan. Namun, perhatikan bahwa model yang diatur grafik berkinerja lebih baik daripada model dasar untuk kedua arsitektur. Khususnya, untuk model Bi-LSTM, ketika rasio pengawasan adalah 0,01, keakuratan model yang diatur grafik ~ 20% lebih tinggi dari model dasar. Ini terutama karena pembelajaran semi-diawasi untuk model grafik-diatur, di mana kesamaan struktural antara sampel pelatihan digunakan selain sampel pelatihan itu sendiri.

Kesimpulan

Kami telah menunjukkan penggunaan regularisasi grafik menggunakan kerangka Neural Structured Learning (NSL) bahkan ketika input tidak mengandung grafik eksplisit. Kami mempertimbangkan tugas klasifikasi sentimen ulasan film IMDB yang kami sintesis grafik kesamaan berdasarkan embeddings review. Kami mendorong pengguna untuk bereksperimen lebih lanjut dengan memvariasikan hyperparameter, jumlah pengawasan, dan dengan menggunakan arsitektur model yang berbeda.