Học liên kết để tạo văn bản

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Hướng dẫn này được xây dựng trên các khái niệm trong học tập Federated cho hình ảnh Phân loại hướng dẫn, và cho thấy một số cách tiếp cận hữu ích khác cho học tập liên.

Đặc biệt, chúng tôi tải một mô hình Keras đã được đào tạo trước đó và tinh chỉnh mô hình này bằng cách sử dụng đào tạo liên kết trên một tập dữ liệu phi tập trung (được mô phỏng). Điều này thực tế quan trọng vì một số lý do. Khả năng sử dụng các mô hình tuần tự hóa giúp dễ dàng kết hợp việc học liên kết với các phương pháp tiếp cận ML khác. Hơn nữa, điều này cho phép sử dụng một phạm vi ngày càng tăng của mô hình pre-đào tạo --- ví dụ, mô hình ngôn ngữ đào tạo từ đầu là hiếm khi cần thiết, như rất nhiều mô hình pre-đào tạo hiện nay là phổ biến rộng rãi (xem, ví dụ như, TF Hub ). Thay vào đó, sẽ hợp lý hơn nếu bắt đầu từ một mô hình được đào tạo trước và tinh chỉnh nó bằng cách sử dụng Học liên kết, thích ứng với các đặc điểm cụ thể của dữ liệu phi tập trung cho một ứng dụng cụ thể.

Đối với hướng dẫn này, chúng tôi bắt đầu với RNN tạo các ký tự ASCII và tinh chỉnh nó thông qua học liên kết. Chúng tôi cũng chỉ ra cách các trọng số cuối cùng có thể được đưa trở lại mô hình Keras ban đầu, cho phép dễ dàng đánh giá và tạo văn bản bằng các công cụ tiêu chuẩn.

!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import collections
import functools
import os
import time

import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

np.random.seed(0)

# Test the TFF is working:
tff.federated_computation(lambda: 'Hello, World!')()
b'Hello, World!'

Tải một mô hình được đào tạo trước

Chúng tôi nạp một mô hình đã được pre-đào tạo sau TensorFlow hướng dẫn chữ thế hệ sử dụng một RNN với thực hiện háo hức . Tuy nhiên, chứ không phải là đào tạo về The Complete Works của Shakespeare , chúng tôi sẵn đào tạo các mô hình trên văn bản từ Charles Dickens' Chuyện hai thành phốA Christmas Carol .

Ngoài việc mở rộng vốn từ vựng, chúng tôi đã không sửa đổi hướng dẫn ban đầu, vì vậy mô hình ban đầu này không phải là hiện đại, nhưng nó tạo ra các dự đoán hợp lý và đủ cho mục đích hướng dẫn của chúng tôi. Mô hình cuối cùng được lưu với tf.keras.models.save_model(include_optimizer=False) .

Chúng tôi sẽ sử dụng phương pháp học liên kết để tinh chỉnh mô hình này cho Shakespeare trong hướng dẫn này, sử dụng phiên bản liên kết của dữ liệu do TFF cung cấp.

Tạo bảng tra cứu vocab

# A fixed vocabularly of ASCII chars that occur in the works of Shakespeare and Dickens:
vocab = list('dhlptx@DHLPTX $(,048cgkoswCGKOSW[_#\'/37;?bfjnrvzBFJNRVZ"&*.26:\naeimquyAEIMQUY]!%)-159\r')

# Creating a mapping from unique characters to indices
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

Tải mô hình được đào tạo trước và tạo một số văn bản

def load_model(batch_size):
  urls = {
      1: 'https://storage.googleapis.com/tff-models-public/dickens_rnn.batch1.kerasmodel',
      8: 'https://storage.googleapis.com/tff-models-public/dickens_rnn.batch8.kerasmodel'}
  assert batch_size in urls, 'batch_size must be in ' + str(urls.keys())
  url = urls[batch_size]
  local_file = tf.keras.utils.get_file(os.path.basename(url), origin=url)  
  return tf.keras.models.load_model(local_file, compile=False)
def generate_text(model, start_string):
  # From https://www.tensorflow.org/tutorials/sequences/text_generation
  num_generate = 200
  input_eval = [char2idx[s] for s in start_string]
  input_eval = tf.expand_dims(input_eval, 0)
  text_generated = []
  temperature = 1.0

  model.reset_states()
  for i in range(num_generate):
    predictions = model(input_eval)
    predictions = tf.squeeze(predictions, 0)
    predictions = predictions / temperature
    predicted_id = tf.random.categorical(
        predictions, num_samples=1)[-1, 0].numpy()
    input_eval = tf.expand_dims([predicted_id], 0)
    text_generated.append(idx2char[predicted_id])

  return (start_string + ''.join(text_generated))
# Text generation requires a batch_size=1 model.
keras_model_batch1 = load_model(batch_size=1)
print(generate_text(keras_model_batch1, 'What of TensorFlow Federated, you ask? '))
Downloading data from https://storage.googleapis.com/tff-models-public/dickens_rnn.batch1.kerasmodel
16195584/16193984 [==============================] - 0s 0us/step
16203776/16193984 [==============================] - 0s 0us/step
What of TensorFlow Federated, you ask? Sall
yesterday. Received the Bailey."

"Mr. Lorry, grimmering himself, or low varked thends the winter, and the eyes of Monsieur
Defarge. "Let his mind, hon in his
life and message; four declare

Tải và xử lý trước dữ liệu Shakespeare được liên kết

Các tff.simulation.datasets gói cung cấp nhiều bộ dữ liệu được chia thành "khách hàng", trong đó mỗi khách hàng tương ứng với một bộ dữ liệu trên một thiết bị cụ thể mà có thể tham gia học tập liên.

Các bộ dữ liệu này cung cấp các bản phân phối dữ liệu không phải IID thực tế, tái tạo trong mô phỏng những thách thức của việc đào tạo trên dữ liệu phi tập trung thực. Một số tiền xử lý các dữ liệu này đã được thực hiện bằng các công cụ từ các dự án Leaf ( github ).

train_data, test_data = tff.simulation.datasets.shakespeare.load_data()

Các bộ dữ liệu được cung cấp bởi shakespeare.load_data() bao gồm một chuỗi các chuỗi Tensors , một cho mỗi dòng được nói bởi một nhân vật đặc biệt trong một vở kịch của Shakespeare. Các phím khách hàng bao gồm tên của vở kịch cùng với tên của các nhân vật, ví dụ như vậy MUCH_ADO_ABOUT_NOTHING_OTHELLO tương ứng với dòng cho nhân vật trong vở kịch Othello Much Ado About Nothing. Lưu ý rằng trong một kịch bản học tập liên kết thực, các máy khách không bao giờ được xác định hoặc theo dõi bởi id, nhưng đối với mô phỏng, việc làm việc với các bộ dữ liệu được khóa sẽ rất hữu ích.

Ví dụ ở đây, chúng ta có thể xem một số dữ liệu từ King Lear:

# Here the play is "The Tragedy of King Lear" and the character is "King".
raw_example_dataset = train_data.create_tf_dataset_for_client(
    'THE_TRAGEDY_OF_KING_LEAR_KING')
# To allow for future extensions, each entry x
# is an OrderedDict with a single key 'snippets' which contains the text.
for x in raw_example_dataset.take(2):
  print(x['snippets'])
tf.Tensor(b'', shape=(), dtype=string)
tf.Tensor(b'What?', shape=(), dtype=string)

Bây giờ chúng ta sử dụng tf.data.Dataset biến đổi để chuẩn bị dữ liệu này cho đào tạo char RNN nạp trên.

# Input pre-processing parameters
SEQ_LENGTH = 100
BATCH_SIZE = 8
BUFFER_SIZE = 100  # For dataset shuffling
# Construct a lookup table to map string chars to indexes,
# using the vocab loaded above:
table = tf.lookup.StaticHashTable(
    tf.lookup.KeyValueTensorInitializer(
        keys=vocab, values=tf.constant(list(range(len(vocab))),
                                       dtype=tf.int64)),
    default_value=0)


def to_ids(x):
  s = tf.reshape(x['snippets'], shape=[1])
  chars = tf.strings.bytes_split(s).values
  ids = table.lookup(chars)
  return ids


def split_input_target(chunk):
  input_text = tf.map_fn(lambda x: x[:-1], chunk)
  target_text = tf.map_fn(lambda x: x[1:], chunk)
  return (input_text, target_text)


def preprocess(dataset):
  return (
      # Map ASCII chars to int64 indexes using the vocab
      dataset.map(to_ids)
      # Split into individual chars
      .unbatch()
      # Form example sequences of SEQ_LENGTH +1
      .batch(SEQ_LENGTH + 1, drop_remainder=True)
      # Shuffle and form minibatches
      .shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
      # And finally split into (input, target) tuples,
      # each of length SEQ_LENGTH.
      .map(split_input_target))

Lưu ý rằng trong việc hình thành các chuỗi gốc và sự hình thành của lô trên, chúng tôi sử dụng drop_remainder=True cho đơn giản. Điều này có nghĩa rằng bất kỳ ký tự (khách hàng) mà không có ít nhất (SEQ_LENGTH + 1) * BATCH_SIZE chars của văn bản sẽ có bộ dữ liệu rỗng. Một cách tiếp cận điển hình để giải quyết vấn đề này sẽ là bổ sung các lô bằng một mã thông báo đặc biệt, sau đó che giấu khoản lỗ để không tính đến các mã thông báo đệm.

Điều này sẽ làm phức tạp ví dụ hơi, vì vậy cho hướng dẫn này chúng tôi chỉ sử dụng lô đầy đủ, như trong hướng dẫn tiêu chuẩn . Tuy nhiên, trong cài đặt liên kết, vấn đề này quan trọng hơn, vì nhiều người dùng có thể có bộ dữ liệu nhỏ.

Bây giờ chúng ta có thể preprocess của chúng tôi raw_example_dataset , và kiểm tra các loại:

example_dataset = preprocess(raw_example_dataset)
print(example_dataset.element_spec)
(TensorSpec(shape=(8, 100), dtype=tf.int64, name=None), TensorSpec(shape=(8, 100), dtype=tf.int64, name=None))

Biên dịch mô hình và thử nghiệm trên dữ liệu được xử lý trước

Chúng tôi nạp một mô hình keras uncompiled, nhưng để chạy keras_model.evaluate , chúng ta cần phải biên dịch nó với một sự mất mát và số liệu. Chúng tôi cũng sẽ biên dịch trong một trình tối ưu hóa, sẽ được sử dụng làm trình tối ưu hóa trên thiết bị trong Học liên kết.

Hướng dẫn ban đầu không có độ chính xác cấp char (phần dự đoán trong đó xác suất cao nhất được đưa vào đúng char tiếp theo). Đây là một số liệu hữu ích, vì vậy chúng tôi thêm nó. Tuy nhiên, chúng ta cần phải xác định một lớp số liệu mới cho điều này bởi vì những dự đoán của chúng tôi có thứ hạng 3 (a vector của logits cho mỗi BATCH_SIZE * SEQ_LENGTH dự đoán), và SparseCategoricalAccuracy hy vọng duy nhất bậc 2 dự đoán.

class FlattenedCategoricalAccuracy(tf.keras.metrics.SparseCategoricalAccuracy):

  def __init__(self, name='accuracy', dtype=tf.float32):
    super().__init__(name, dtype=dtype)

  def update_state(self, y_true, y_pred, sample_weight=None):
    y_true = tf.reshape(y_true, [-1, 1])
    y_pred = tf.reshape(y_pred, [-1, len(vocab), 1])
    return super().update_state(y_true, y_pred, sample_weight)

Bây giờ chúng ta có thể biên dịch một mô hình, và đánh giá nó trên của chúng tôi example_dataset .

BATCH_SIZE = 8  # The training and eval batch size for the rest of this tutorial.
keras_model = load_model(batch_size=BATCH_SIZE)
keras_model.compile(
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[FlattenedCategoricalAccuracy()])

# Confirm that loss is much lower on Shakespeare than on random data
loss, accuracy = keras_model.evaluate(example_dataset.take(5), verbose=0)
print(
    'Evaluating on an example Shakespeare character: {a:3f}'.format(a=accuracy))

# As a sanity check, we can construct some completely random data, where we expect
# the accuracy to be essentially random:
random_guessed_accuracy = 1.0 / len(vocab)
print('Expected accuracy for random guessing: {a:.3f}'.format(
    a=random_guessed_accuracy))
random_indexes = np.random.randint(
    low=0, high=len(vocab), size=1 * BATCH_SIZE * (SEQ_LENGTH + 1))
data = collections.OrderedDict(
    snippets=tf.constant(
        ''.join(np.array(vocab)[random_indexes]), shape=[1, 1]))
random_dataset = preprocess(tf.data.Dataset.from_tensor_slices(data))
loss, accuracy = keras_model.evaluate(random_dataset, steps=10, verbose=0)
print('Evaluating on completely random data: {a:.3f}'.format(a=accuracy))
Downloading data from https://storage.googleapis.com/tff-models-public/dickens_rnn.batch8.kerasmodel
16195584/16193984 [==============================] - 0s 0us/step
16203776/16193984 [==============================] - 0s 0us/step
Evaluating on an example Shakespeare character: 0.402000
Expected accuracy for random guessing: 0.012
Evaluating on completely random data: 0.011

Tinh chỉnh mô hình với Học liên kết

TFF tuần tự hóa tất cả các phép tính TensorFlow để chúng có thể chạy trong môi trường không phải Python (mặc dù hiện tại, chỉ có một thời gian chạy mô phỏng được triển khai bằng Python). Mặc dù chúng tôi đang chạy trong chế độ háo hức, (TF 2.0), hiện TFF serializes TensorFlow tính toán bằng cách xây dựng các ops cần thiết trong bối cảnh của một " with tf.Graph.as_default() " tuyên bố. Do đó, chúng tôi cần cung cấp một chức năng mà TFF có thể sử dụng để đưa mô hình của chúng tôi vào biểu đồ mà nó kiểm soát. Chúng tôi làm điều này như sau:

# Clone the keras_model inside `create_tff_model()`, which TFF will
# call to produce a new copy of the model inside the graph that it will 
# serialize. Note: we want to construct all the necessary objects we'll need 
# _inside_ this method.
def create_tff_model():
  # TFF uses an `input_spec` so it knows the types and shapes
  # that your model expects.
  input_spec = example_dataset.element_spec
  keras_model_clone = tf.keras.models.clone_model(keras_model)
  return tff.learning.from_keras_model(
      keras_model_clone,
      input_spec=input_spec,
      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
      metrics=[FlattenedCategoricalAccuracy()])

Bây giờ chúng ta đã sẵn sàng để xây dựng một quá trình lặp Federated trung bình, mà chúng tôi sẽ sử dụng để cải thiện các mô hình (để biết chi tiết về thuật toán trung bình Federated, xem giấy Learning Truyền thông-hiệu quả của Deep Networks từ phân cấp dữ liệu ).

Chúng tôi sử dụng mô hình Keras đã biên soạn để thực hiện đánh giá tiêu chuẩn (không liên kết) sau mỗi vòng huấn luyện liên đoàn. Điều này rất hữu ích cho các mục đích nghiên cứu khi thực hiện học liên hợp mô phỏng và có một bộ dữ liệu kiểm tra tiêu chuẩn.

Trong bối cảnh sản xuất thực tế, kỹ thuật tương tự này có thể được sử dụng để lấy các mô hình được đào tạo với phương pháp học liên hợp và đánh giá chúng trên tập dữ liệu điểm chuẩn tập trung cho mục đích thử nghiệm hoặc đảm bảo chất lượng.

# This command builds all the TensorFlow graphs and serializes them: 
fed_avg = tff.learning.build_federated_averaging_process(
    model_fn=create_tff_model,
    client_optimizer_fn=lambda: tf.keras.optimizers.SGD(lr=0.5))

Đây là vòng lặp đơn giản nhất có thể, trong đó chúng tôi chạy tính trung bình được liên kết cho một vòng trên một ứng dụng khách trên một lô duy nhất:

state = fed_avg.initialize()
state, metrics = fed_avg.next(state, [example_dataset.take(5)])
train_metrics = metrics['train']
print('loss={l:.3f}, accuracy={a:.3f}'.format(
    l=train_metrics['loss'], a=train_metrics['accuracy']))
loss=4.403, accuracy=0.132

Bây giờ chúng ta hãy viết một vòng đào tạo và đánh giá thú vị hơn một chút.

Để mô phỏng này vẫn chạy tương đối nhanh, chúng tôi đào tạo ba ứng dụng khách giống nhau mỗi vòng, chỉ xem xét hai minibatch cho mỗi vòng.

def data(client, source=train_data):
  return preprocess(source.create_tf_dataset_for_client(client)).take(5)


clients = [
    'ALL_S_WELL_THAT_ENDS_WELL_CELIA', 'MUCH_ADO_ABOUT_NOTHING_OTHELLO',
]

train_datasets = [data(client) for client in clients]

# We concatenate the test datasets for evaluation with Keras by creating a 
# Dataset of Datasets, and then identity flat mapping across all the examples.
test_dataset = tf.data.Dataset.from_tensor_slices(
    [data(client, test_data) for client in clients]).flat_map(lambda x: x)

Tình trạng ban đầu của mô hình sản xuất bởi fed_avg.initialize() được dựa trên initializers ngẫu nhiên cho mô hình Keras, không phải là trọng lượng đã được nạp, vì clone_model() không bản sao các trọng. Để bắt đầu đào tạo từ một mô hình được đào tạo trước, chúng tôi đặt trọng số của mô hình ở trạng thái máy chủ trực tiếp từ mô hình được tải.

NUM_ROUNDS = 5

# The state of the FL server, containing the model and optimization state.
state = fed_avg.initialize()

# Load our pre-trained Keras model weights into the global model state.
state = tff.learning.state_with_new_model_weights(
    state,
    trainable_weights=[v.numpy() for v in keras_model.trainable_weights],
    non_trainable_weights=[
        v.numpy() for v in keras_model.non_trainable_weights
    ])


def keras_evaluate(state, round_num):
  # Take our global model weights and push them back into a Keras model to
  # use its standard `.evaluate()` method.
  keras_model = load_model(batch_size=BATCH_SIZE)
  keras_model.compile(
      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
      metrics=[FlattenedCategoricalAccuracy()])
  state.model.assign_weights_to(keras_model)
  loss, accuracy = keras_model.evaluate(example_dataset, steps=2, verbose=0)
  print('\tEval: loss={l:.3f}, accuracy={a:.3f}'.format(l=loss, a=accuracy))


for round_num in range(NUM_ROUNDS):
  print('Round {r}'.format(r=round_num))
  keras_evaluate(state, round_num)
  state, metrics = fed_avg.next(state, train_datasets)
  train_metrics = metrics['train']
  print('\tTrain: loss={l:.3f}, accuracy={a:.3f}'.format(
      l=train_metrics['loss'], a=train_metrics['accuracy']))

print('Final evaluation')
keras_evaluate(state, NUM_ROUNDS + 1)
Round 0
    Eval: loss=3.324, accuracy=0.401
    Train: loss=4.360, accuracy=0.155
Round 1
    Eval: loss=4.361, accuracy=0.049
    Train: loss=4.235, accuracy=0.164
Round 2
    Eval: loss=4.219, accuracy=0.177
    Train: loss=4.081, accuracy=0.221
Round 3
    Eval: loss=4.080, accuracy=0.174
    Train: loss=3.940, accuracy=0.226
Round 4
    Eval: loss=3.991, accuracy=0.176
    Train: loss=3.840, accuracy=0.226
Final evaluation
    Eval: loss=3.909, accuracy=0.171

Với các thay đổi mặc định, chúng tôi chưa thực hiện đủ đào tạo để tạo ra sự khác biệt lớn, nhưng nếu bạn đào tạo lâu hơn về nhiều dữ liệu Shakespeare hơn, bạn sẽ thấy sự khác biệt trong kiểu văn bản được tạo với mô hình cập nhật:

# Set our newly trained weights back in the originally created model.
keras_model_batch1.set_weights([v.numpy() for v in keras_model.weights])
# Text generation requires batch_size=1
print(generate_text(keras_model_batch1, 'What of TensorFlow Federated, you ask? '))
What of TensorFlow Federated, you ask? Shalways, I will call your
compet with any city brought their faces uncompany," besumed him. "When he
sticked Madame Defarge pushed the lamps.

"Have I often but no unison. She had probably come,

Phần mở rộng được đề xuất

Hướng dẫn này chỉ là bước đầu tiên! Dưới đây là một số ý tưởng về cách bạn có thể thử mở rộng sổ ghi chép này:

  • Viết một vòng đào tạo thực tế hơn trong đó bạn lấy mẫu khách hàng để đào tạo một cách ngẫu nhiên.
  • Sử dụng " .repeat(NUM_EPOCHS) " trên tập hợp dữ liệu khách hàng để thử nhiều kỷ nguyên của đào tạo địa phương (ví dụ, như trong McMahan et. Al. ). Xem thêm Federated Learning cho Phân loại ảnh mà thực hiện điều này.
  • Thay đổi compile() lệnh để thí nghiệm với việc sử dụng các thuật toán tối ưu hóa khác nhau trên máy khách.
  • Thử server_optimizer lập luận để build_federated_averaging_process thử thuật toán khác nhau để áp dụng các bản cập nhật mô hình trên máy chủ.
  • Thử client_weight_fn lập luận để đến build_federated_averaging_process thử trọng số khác nhau của khách hàng. Các cập nhật trọng lượng mặc định khách hàng bằng của số ví dụ về các khách hàng, nhưng bạn có thể làm ví dụ client_weight_fn=lambda _: tf.constant(1.0) .