Lưu ngày! Google I / O hoạt động trở lại từ ngày 18 đến 20 tháng 5 Đăng ký ngay
Trang này được dịch bởi Cloud Translation API.
Switch to English

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 dựa trên các khái niệm trong hướng dẫn Học liên kết cho Phân loại hình ảnh và trình bày một số cách tiếp cận hữu ích khác cho việc học liên kết.

Đặ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 nó 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 ngày càng nhiều các mô hình được đào tạo trước --- ví dụ, các mô hình ngôn ngữ đào tạo từ đầu hiếm khi cần thiết, vì nhiều mô hình được đào tạo trước hiện đã được phổ biến rộng rãi (xem, ví dụ: 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 một 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 tải một mô hình đã được đào tạo trước theo hướng dẫn TensorFlow Tạo văn bản bằng RNN với khả năng thực thi nhanh chóng . Tuy nhiên, thay vì đào tạo về Tác phẩm hoàn chỉnh của Shakespeare , chúng tôi đã đào tạo trước mô hình về văn bản từ Câu chuyện về hai thành phốA Christmas Carol của Charles Dickens.

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 bằng 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

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

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ố quá trình xử lý trước dữ liệu này được thực hiện bằng các công cụ từ dự án Leaf ( github ).

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

Các tập dữ liệu được cung cấp bởi shakespeare.load_data() bao gồm một chuỗi các Tensors chuỗi, một chuỗi cho mỗi dòng được nói bởi một nhân vật cụ thể trong vở kịch Shakespeare. Các phím khách bao gồm tên của vở kịch được kết hợp với tên của nhân vật, vì vậy, ví dụ MUCH_ADO_ABOUT_NOTHING_OTHELLO tương ứng với các dòng cho nhân vật Othello trong vở kịch Many 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 ứng dụng 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 tập 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 các phép biến đổitf.data.Dataset để chuẩn bị dữ liệu này cho việc huấn luyện char RNN được tải ở 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 trình tự ban đầu và hình thành các lô ở trên, chúng ta sử dụng drop_remainder=True để đơn giản hóa. Điều này có nghĩa là bất kỳ ký tự nào (máy khách) không có ít nhất (SEQ_LENGTH + 1) * BATCH_SIZE ký tự văn bản sẽ có tập dữ liệu trống. Một cách tiếp cận điển hình để giải quyết vấn đề này là bổ sung các lô bằng một mã thông báo đặc biệt, và sau đó che giấu tổn thất để 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ụ đôi chút, vì vậy đối với hướng dẫn này, chúng tôi chỉ sử dụng các 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ể xử lý trước raw_example_dataset của 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 đã tải một mô hình keras chưa được biên dịch, nhưng để chạy keras_model.evaluate , chúng tôi cần phải biên dịch nó với tổn thấ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 tôi cần xác định một lớp chỉ số mới cho điều này vì các dự đoán của chúng tôi có xếp hạng 3 (vectơ logits cho mỗi dự đoán BATCH_SIZE * SEQ_LENGTH ) và SparseCategoricalAccuracy chỉ dự đoán xếp hạng 2.

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 example_dataset của chúng tôi.

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 tính toán 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 ở chế độ háo hức, (TF 2.0), TFF hiện đang tuần tự hóa các phép tính TensorFlow bằng cách xây dựng các hoạt động cần thiết bên trong ngữ cảnh của câu lệnh " with tf.Graph.as_default() ". Do đó, chúng ta cần cung cấp một hàm mà TFF có thể sử dụng để đưa mô hình của chúng ta 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 tôi đã sẵn sàng xây dựng quy trình lặp lại Trung bình liên kết, quy trình mà chúng tôi sẽ sử dụng để cải thiện mô hình (để biết chi tiết về thuật toán Trung bình liên kết, hãy xem bài báo Học tập hiệu quả về mạng sâu từ dữ liệu phi tập trung ).

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 mục đích nghiên cứu khi thực hiện học tập 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 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)

Trạng thái ban đầu của mô hình do fed_avg.initialize() dựa trên các bộ khởi tạo ngẫu nhiên cho mô hình Keras, không phải các trọng số đã được tải, vì clone_model() không sao chép các trọng số. Để 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 trên 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 dữ liệu ứng dụng khách để thử nhiều kỷ nguyên đào tạo cục bộ (ví dụ: như trong McMahan và cộng sự ). Xem thêm Học liên kết để phân loại hình ảnh thực hiện điều này.
  • Thay đổi lệnh compile() để 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ủ.
  • Hãy thử đối số client_weight_fn để build_federated_averaging_process để thử các trọng số khác nhau của các khách hàng. Ứng dụng khách có trọng số mặc định cập nhật theo số lượng ví dụ trên ứng dụng khách, nhưng bạn có thể làm như client_weight_fn=lambda _: tf.constant(1.0) .