Dekodowanie API

Zobacz na TensorFlow.org Uruchom w Google Colab Zobacz na GitHub Pobierz notatnik

Przegląd

W niedalekiej przeszłości przeprowadzono wiele badań nad generowaniem języka za pomocą modeli autoregresyjnych. W pokoleniu auto-regresywny językowej, rozkład prawdopodobieństwa żeton w kroku czas K zależy od token-przewidywaniami modelu till Etap K-1. Dla tych modeli, takich jak dekodowanie strategie poszukiwania Belka, chciwy, Top-P i Top-K są krytyczne elementy modelu i w dużej mierze wpływają na styl / Natura wygenerowanego element wyjściowy w danym kroku k czasu.

Na przykład wyszukiwanie Beam zmniejsza ryzyko brakujących ukrytych tokenów wysokie prawdopodobieństwo poprzez utrzymywanie najprawdopodobniej num_beams hipotez na każdym kroku czasowego i ostatecznie wybiera hipotezę, że ma ogólnie najwyższe prawdopodobieństwo. Murray i in. (2018) oraz Yang i in. (2018) wskazują, że wyszukiwanie belka działa dobrze w zadaniach tłumaczenia maszynowego. Zarówno wyszukiwania Beam & Greedy strategie mają możliwość generowania powtarzających się znaków.

Wentylator i in. al (2018) wprowadził Najlepiej K pobierania próbek, w którym K najprawdopodobniej żetony przesączono i masy prawdopodobieństwa są rozdzielane między tylko tych znaczników K.

Ari Holtzman i in. al (2019) wprowadził próbkowanie Najlepiej P, które wybiera się od najmniejszej zestawu znaczników z łącznego prawdopodobieństwa dodaje września prawdopodobieństwa p. Masa prawdopodobieństwa jest następnie redystrybuowana w tym zbiorze. W ten sposób wielkość zestawu tokenów może się dynamicznie zwiększać i zmniejszać. Top-p, Top-k są powszechnie stosowane w zadaniach, takich jak historia generacji.

Interfejs API dekodowania zapewnia interfejs do eksperymentowania z różnymi strategiami dekodowania w modelach auto-regresywnych.

  1. W pliku sampling_module.py, który dziedziczy z podstawowej klasy Decoding, dostępne są następujące strategie próbkowania:

  2. Wyszukiwanie belek jest dostępne w beam_search.py. github

Ustawiać

pip install -q -U tensorflow-text
pip install -q tf-models-nightly
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf

from official import nlp
from official.nlp.modeling.ops import sampling_module
from official.nlp.modeling.ops import beam_search
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py:119: PkgResourcesDeprecationWarning: 0.18ubuntu0.18.04.1 is an invalid version and will not be supported in a future release
  PkgResourcesDeprecationWarning,

Zainicjuj moduł próbkowania w TF-NLP.

  • symbols_to_logits_fn: Użyj tego zamknięcie zadzwonić model do przewidywania logits dla index+1 kroku. Wejścia i wyjścia dla tego zamknięcia są następujące:
Args:
  1] ids : Current decoded sequences. int tensor with shape (batch_size, index + 1 or 1 if padded_decode is True)],
  2] index [scalar] : current decoded step,
  3] cache [nested dictionary of tensors] : Only used for faster decoding to store pre-computed attention hidden states for keys and values. More explanation in the cell below.
Returns:
  1] tensor for next-step logits [batch_size, vocab]
  2] the updated_cache [nested dictionary of tensors].

Pamięć podręczna służy do szybszego dekodowania. Oto referencyjna implementacja dla powyższego zamknięcia.

  • length_normalization_fn: Za pomocą tego parametru zamknięcie na powrocie długość normalizacji.
Args: 
  1] length : scalar for decoded step index.
  2] dtype : data-type of output tensor
Returns:
  1] value of length normalization factor.
  • vocab_size: Wyjście rozmiar słownictwo.

  • max_decode_length: skalarne dla ogółu dekodowania etapach.

  • eos_id: Dekodowanie zatrzyma jeśli wszystkie identyfikatory wyjściowe dekodowane w partii mają tę eos_id.

  • padded_decode: Ustaw na TRUE jeśli działa na TPU. Tensory są dopełniane do max_decoding_length, jeśli to prawda.

  • top_k: top_k jest włączona, jeśli ta wartość jest> 1.

  • top_p: top_p jest włączona, jeśli ta wartość jest> 0 i <1,0

  • sampling_temperature: Służy do ponownego oszacowania wyjście SoftMax. Temperatura odchyla rozkład w kierunku żetonów wysokiego prawdopodobieństwa i obniża masę w rozkładzie ogona. Wartość musi być dodatnia. Niska temperatura jest odpowiednikiem zachłanności i sprawia, że ​​rozkład jest ostrzejszy, podczas gdy wysoka temperatura sprawia, że ​​jest bardziej płaski.

  • enable_greedy: domyślnie jest to prawda i chciwy dekodowania jest włączony. Aby poeksperymentować z innymi strategiami, ustaw to na False.

Zainicjuj hiperparametry modelu

params = {}
params['num_heads'] = 2
params['num_layers'] = 2
params['batch_size'] = 2
params['n_dims'] = 256
params['max_decode_length'] = 4

W auto-regresywny architektur takich jak transformator oparte kodera-dekodera modeli Cache służy do szybkiego sekwencyjnego dekodowania. Jest to słownik zagnieżdżony przechowujący wstępnie obliczone stany ukryte (klucze i wartości w blokach samouwagi oraz w blokach wzajemnej uwagi) dla każdej warstwy.

Zainicjuj pamięć podręczną.

cache = {
    'layer_%d' % layer: {
        'k': tf.zeros([params['batch_size'], params['max_decode_length'], params['num_heads'], int(params['n_dims']/params['num_heads'])], dtype=tf.float32),
        'v': tf.zeros([params['batch_size'], params['max_decode_length'], params['num_heads'], int(params['n_dims']/params['num_heads'])], dtype=tf.float32)
        } for layer in range(params['num_layers'])
    }
print("cache key shape for layer 1 :", cache['layer_1']['k'].shape)
cache key shape for layer 1 : (2, 4, 2, 128)

W razie potrzeby zdefiniuj zamknięcie dla normalizacji długości.

Jest to używane do normalizacji końcowych wyników wygenerowanych sekwencji i jest opcjonalne

def length_norm(length, dtype):
  """Return length normalization factor."""
  return tf.pow(((5. + tf.cast(length, dtype)) / 6.), 0.0)

Utwórz model_fn

W praktyce ta zostanie zastąpiona przez rzeczywistej implementacji modelu, takiego jak tutaj

Args:
i : Step that is being decoded.
Returns:
  logit probabilities of size [batch_size, 1, vocab_size]
probabilities = tf.constant([[[0.3, 0.4, 0.3], [0.3, 0.3, 0.4],
                              [0.1, 0.1, 0.8], [0.1, 0.1, 0.8]],
                            [[0.2, 0.5, 0.3], [0.2, 0.7, 0.1],
                              [0.1, 0.1, 0.8], [0.1, 0.1, 0.8]]])
def model_fn(i):
  return probabilities[:, i, :]

Zainicjuj symbole_to_logits_fn

def _symbols_to_logits_fn():
  """Calculates logits of the next tokens."""
  def symbols_to_logits_fn(ids, i, temp_cache):
    del ids
    logits = tf.cast(tf.math.log(model_fn(i)), tf.float32)
    return logits, temp_cache
  return symbols_to_logits_fn

Chciwy

Chciwy dekodowania wybiera token identyfikator z dużym prawdopodobieństwem, jak następnym ID: \(id_t = argmax_{w}P(id | id_{1:t-1})\) przy każdym kroku to \(t\). Poniższy szkic pokazuje zachłanne dekodowanie.

greedy_obj = sampling_module.SamplingModule(
    length_normalization_fn=None,
    dtype=tf.float32,
    symbols_to_logits_fn=_symbols_to_logits_fn(),
    vocab_size=3,
    max_decode_length=params['max_decode_length'],
    eos_id=10,
    padded_decode=False)
ids, _ = greedy_obj.generate(
    initial_ids=tf.constant([9, 1]), initial_cache=cache)
print("Greedy Decoded Ids:", ids)
Greedy Decoded Ids: tf.Tensor(
[[9 1 2 2 2]
 [1 1 1 2 2]], shape=(2, 5), dtype=int32)

próbkowanie top_k

W próbkowania Top-K, K najprawdopodobniej obok symboliczne identyfikatory są filtrowane i masa prawdopodobieństwo jest rozpowszechniany wśród tylko tych identyfikatorów K.

top_k_obj = sampling_module.SamplingModule(
    length_normalization_fn=length_norm,
    dtype=tf.float32,
    symbols_to_logits_fn=_symbols_to_logits_fn(),
    vocab_size=3,
    max_decode_length=params['max_decode_length'],
    eos_id=10,
    sample_temperature=tf.constant(1.0),
    top_k=tf.constant(3),
    padded_decode=False,
    enable_greedy=False)
ids, _ = top_k_obj.generate(
    initial_ids=tf.constant([9, 1]), initial_cache=cache)
print("top-k sampled Ids:", ids)
top-k sampled Ids: tf.Tensor(
[[9 1 0 2 2]
 [1 0 1 2 2]], shape=(2, 5), dtype=int32)

próbkowanie top_p

Zamiast pobierania próbek tylko z najbardziej prawdopodobne K żeton identyfikatory, w Top-p próbkowania wybiera z najmniejszym możliwym zestaw identyfikatorów, których skumulowane prawdopodobieństwo przekracza prawdopodobieństwo p.

top_p_obj = sampling_module.SamplingModule(
    length_normalization_fn=length_norm,
    dtype=tf.float32,
    symbols_to_logits_fn=_symbols_to_logits_fn(),
    vocab_size=3,
    max_decode_length=params['max_decode_length'],
    eos_id=10,
    sample_temperature=tf.constant(1.0),
    top_p=tf.constant(0.9),
    padded_decode=False,
    enable_greedy=False)
ids, _ = top_p_obj.generate(
    initial_ids=tf.constant([9, 1]), initial_cache=cache)
print("top-p sampled Ids:", ids)
top-p sampled Ids: tf.Tensor(
[[9 1 1 2 2]
 [1 1 1 0 2]], shape=(2, 5), dtype=int32)

Dekodowanie wyszukiwania wiązki

Wyszukiwanie wiązek zmniejsza ryzyko pominięcia ukrytych identyfikatorów tokenów o wysokim prawdopodobieństwie, zachowując najbardziej prawdopodobną liczbę wiązek hipotez na każdym kroku czasowym i ostatecznie wybierając hipotezę, która ma ogólnie najwyższe prawdopodobieństwo.

beam_size = 2
params['batch_size'] = 1
beam_cache = {
    'layer_%d' % layer: {
        'k': tf.zeros([params['batch_size'], params['max_decode_length'], params['num_heads'], params['n_dims']], dtype=tf.float32),
        'v': tf.zeros([params['batch_size'], params['max_decode_length'], params['num_heads'], params['n_dims']], dtype=tf.float32)
        } for layer in range(params['num_layers'])
    }
print("cache key shape for layer 1 :", beam_cache['layer_1']['k'].shape)
ids, _ = beam_search.sequence_beam_search(
    symbols_to_logits_fn=_symbols_to_logits_fn(),
    initial_ids=tf.constant([9], tf.int32),
    initial_cache=beam_cache,
    vocab_size=3,
    beam_size=beam_size,
    alpha=0.6,
    max_decode_length=params['max_decode_length'],
    eos_id=10,
    padded_decode=False,
    dtype=tf.float32)
print("Beam search ids:", ids)
cache key shape for layer 1 : (1, 4, 2, 256)
Beam search ids: tf.Tensor(
[[[9 0 1 2 2]
  [9 1 2 2 2]]], shape=(1, 2, 5), dtype=int32)