TFF'de rastgele gürültü üretimi

Bu öğretici, TFF'de rastgele gürültü üretimi için önerilen en iyi uygulamaları tartışacaktır. Rastgele gürültü üretimi, federe öğrenme algoritmalarındaki birçok mahremiyet koruma tekniğinin önemli bir bileşenidir, örneğin, diferansiyel mahremiyet.

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

Başlamadan Önce

İlk olarak, dizüstü bilgisayarın ilgili bileşenlerin derlenmiş olduğu bir arka uca bağlı olduğundan emin olalım.

!pip install --quiet --upgrade tensorflow_federated_nightly
!pip install --quiet --upgrade nest_asyncio

import nest_asyncio
nest_asyncio.apply()
import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

TFF ortamının doğru şekilde kurulduğundan emin olmak için aşağıdaki "Merhaba Dünya" örneğini çalıştırın. İşe yaramazsa, bakınız Kurulum talimatları için rehber.

@tff.federated_computation
def hello_world():
  return 'Hello, World!'

hello_world()
b'Hello, World!'

İstemcilerde rastgele gürültü

İstemcilerde gürültü ihtiyacı genellikle iki duruma düşer: özdeş gürültü ve iid gürültüsü.

  • Özdeş gürültü için tavsiye edilen model istemcilere yayınlayabilir sunucuda bir tohum, korumak ve kullanmaktır tf.random.stateless gürültü üreten fonksiyonları.
  • iid gürültüsü için, TF'nin tf.random.<distribution> işlevlerinden kaçınma önerisine uygun olarak, istemcide from_non_deterministic_state ile başlatılan bir tf.random.Generator kullanın.

İstemci davranışı sunucudan farklıdır (daha sonra tartışılacak olan tuzaklardan etkilenmez), çünkü her istemci kendi hesaplama grafiğini oluşturacak ve kendi varsayılan tohumunu başlatacaktır.

İstemcilerde aynı gürültü

# Set to use 10 clients.
tff.backends.native.set_local_python_execution_context(num_clients=10)

@tff.tf_computation
def noise_from_seed(seed):
  return tf.random.stateless_normal((), seed=seed)

seed_type_at_server = tff.type_at_server(tff.to_type((tf.int64, [2])))

@tff.federated_computation(seed_type_at_server)
def get_random_min_and_max_deterministic(seed):
  # Broadcast seed to all clients.
  seed_on_clients = tff.federated_broadcast(seed)

  # Clients generate noise from seed deterministicly.
  noise_on_clients = tff.federated_map(noise_from_seed, seed_on_clients)

  # Aggregate and return the min and max of the values generated on clients.
  min = tff.aggregators.federated_min(noise_on_clients)
  max = tff.aggregators.federated_max(noise_on_clients)
  return min, max

seed = tf.constant([1, 1], dtype=tf.int64)
min, max = get_random_min_and_max_deterministic(seed)
assert min == max
print(f'Seed: {seed.numpy()}. All clients sampled value {min:8.3f}.')

seed += 1
min, max = get_random_min_and_max_deterministic(seed)
assert min == max
print(f'Seed: {seed.numpy()}. All clients sampled value {min:8.3f}.')
Seed: [1 1]. All clients sampled value    1.665.
Seed: [2 2]. All clients sampled value   -0.219.

İstemciler üzerinde bağımsız gürültü

@tff.tf_computation
def nondeterministic_noise():
  gen = tf.random.Generator.from_non_deterministic_state()
  return gen.normal(())

@tff.federated_computation(seed_type_at_server)
def get_random_min_and_max_nondeterministic(seed):
  noise_on_clients = tff.federated_eval(nondeterministic_noise, tff.CLIENTS)
  min = tff.aggregators.federated_min(noise_on_clients)
  max = tff.aggregators.federated_max(noise_on_clients)
  return min, max

min, max = get_random_min_and_max_nondeterministic(seed)
assert min != max
print(f'Values differ across clients. {min:8.3f},{max:8.3f}.')

new_min, new_max = get_random_min_and_max_nondeterministic(seed)
assert new_min != new_max
assert new_min != min and new_max != max
print(f'Values differ across rounds.  {new_min:8.3f},{new_max:8.3f}.')
Values differ across clients.   -1.810,   1.079.
Values differ across rounds.    -1.205,   0.851.

Sunucuda rastgele gürültü

Cesareti kullanımı: Doğrudan kullanılarak tf.random.normal

TF1.x API'ler gibi tf.random.normal rastgele gürültü nesil için şiddetle göre TF2 caydırılmıştır TF rastgele gürültü nesil öğretici . Bu API'ler ile birlikte kullanıldığında Şaşırtıcı davranış ortaya çıkabilir tf.function ve tf.random.set_seed . Örneğin, aşağıdaki kod her çağrıda aynı değeri üretecektir. Bu şaşırtıcı davranış TF için beklenen ve açıklama bulunabilir belgelenmesi tf.random.set_seed .

tf.random.set_seed(1)

@tf.function
def return_one_noise(_):
  return tf.random.normal([])

n1=return_one_noise(1)
n2=return_one_noise(2) 
assert n1 == n2
print(n1.numpy(), n2.numpy())
0.3052047 0.3052047

TFF'de işler biraz farklı. Biz gürültü oluşumunu sarın Eğer tff.tf_computation yerine tf.function , belirli olmayan rasgele gürültü oluşacaktır. Bu kodun parçacığı birden çok kez çalıştırmak Ancak, farklı seti (n1, n2) her zaman oluşturulur. TFF için küresel bir rastgele tohum belirlemenin kolay bir yolu yoktur.

tf.random.set_seed(1)

@tff.tf_computation
def return_one_noise(_):
  return tf.random.normal([])

n1=return_one_noise(1)
n2=return_one_noise(2) 
assert n1 != n2
print(n1, n2)
1.3283143 0.45740178

Ayrıca, açıkça bir tohum ayarlamadan TFF'de deterministik gürültü üretilebilir. Fonksiyon return_two_noise aşağıdaki kodda döndürür iki özdeş gürültü değerleri pasajı. Bu beklenen davranıştır çünkü TFF, yürütmeden önce hesaplama grafiğini oluşturacaktır. Ancak bu kullanıcıların kullanımına ilişkin ödeme dikkatine beslemesini önerdi tf.random.normal TFF'nin.

@tff.tf_computation
def tff_return_one_noise():
  return tf.random.normal([])

@tff.federated_computation
def return_two_noise():
  return (tff_return_one_noise(), tff_return_one_noise())

n1, n2=return_two_noise() 
assert n1 == n2
print(n1, n2)
-0.15665223 -0.15665223

Dikkatle Kullanımı: tf.random.Generator

Biz kullanabilirsiniz tf.random.Generator önerildiği şekilde TF öğretici .

@tff.tf_computation
def tff_return_one_noise(i):
  g=tf.random.Generator.from_seed(i)
  @tf.function
  def tf_return_one_noise():
    return g.normal([])
  return tf_return_one_noise()

@tff.federated_computation
def return_two_noise():
  return (tff_return_one_noise(1), tff_return_one_noise(2))

n1, n2 = return_two_noise() 
assert n1 != n2
print(n1, n2)
0.3052047 -0.38260338

Ancak, kullanıcıların kullanımı konusunda dikkatli olmaları gerekebilir.

Genel olarak TFF fonksiyonel operasyonlar tercih eder ve biz kullanımını sergileyecek tf.random.stateless_* aşağıdaki bölümlerde fonksiyonlar.

Federe öğrenme için TFF'de, genellikle skalerler yerine iç içe yapılarla çalışırız ve önceki kod parçacığı doğal olarak iç içe yapılara genişletilebilir.

@tff.tf_computation
def tff_return_one_noise(i):
  g=tf.random.Generator.from_seed(i)
  weights = [
         tf.ones([2, 2], dtype=tf.float32),
         tf.constant([2], dtype=tf.float32)
     ]
  @tf.function
  def tf_return_one_noise():
    return tf.nest.map_structure(lambda x: g.normal(tf.shape(x)), weights)
  return tf_return_one_noise()

@tff.federated_computation
def return_two_noise():
  return (tff_return_one_noise(1), tff_return_one_noise(2))

n1, n2 = return_two_noise() 
assert n1[1] != n2[1]
print('n1', n1)
print('n2', n2)
n1 [array([[0.3052047 , 0.5671378 ],
       [0.41852272, 0.2326421 ]], dtype=float32), array([1.1675092], dtype=float32)]
n2 [array([[-0.38260338, -0.47804865],
       [-0.5187485 , -1.8471988 ]], dtype=float32), array([-0.77835274], dtype=float32)]

TFF'nin genel öneri işlevsel kullanmaktır tf.random.stateless_* rastgele gürültü nesil için fonksiyonlar. Bu işlevler alır seed (şekilli bir Tensörü [2] veya tuple rastgele gürültü oluşturmak için açık bir giriş bağımsız değişken olarak iki sayısal tansörleri). Tohumu sözde durum olarak tutmak için önce bir yardımcı sınıf tanımlıyoruz. Yardımcı RandomSeedGenerator bir devlet içinde devlet-out moda fonksiyonel operatörler vardır. Bunun için sözde devlet olarak bir sayaç kullanmak mantıklı tf.random.stateless_* bu işlevler olarak karıştırmak istatistiksel ilintisiz ilişkili tohum ürettiği gürültü yapmak için kullanmadan önce tohum.

def timestamp_seed():
  # tf.timestamp returns microseconds as decimal places, thus scaling by 1e6.
  return tf.math.cast(tf.timestamp() * 1e6, tf.int64)

class RandomSeedGenerator():

  def initialize(self, seed=None):
    if seed is None:
      return tf.stack([timestamp_seed(), 0])
    else:
      return tf.constant(self.seed, dtype=tf.int64, shape=(2,))

  def next(self, state):
    return state + tf.constant([0, 1], tf.int64)

  def structure_next(self, state, nest_structure):
    "Returns seed in nested structure and the next state seed."
    flat_structure = tf.nest.flatten(nest_structure)
    flat_seeds = [state + tf.constant([0, i], tf.int64) for
                  i in range(len(flat_structure))]
    nest_seeds = tf.nest.pack_sequence_as(nest_structure, flat_seeds)
    return nest_seeds, flat_seeds[-1] + tf.constant([0, 1], tf.int64)

Şimdi bize yardımcı sınıf ve kullanmasına izin tf.random.stateless_normal (iç içe geçmiş yapısı) TFF rasgele gürültü üretecek. Aşağıdaki kod parçası bir TFF iteratif süreç gibi çok Görmekten görünüyor simple_fedavg TFF iteratif süreç olarak federe öğrenme algoritmasını ifade eden bir örnek olarak. Rastgele gürültü nesil için buraya sahte tohum durumudur tf.Tensor kolayca TFF ve TF işlevlerinde taşınabilmektedir.

@tff.tf_computation
def tff_return_one_noise(seed_state):
  g=RandomSeedGenerator()
  weights = [
         tf.ones([2, 2], dtype=tf.float32),
         tf.constant([2], dtype=tf.float32)
     ]
  @tf.function
  def tf_return_one_noise():
    nest_seeds, updated_state = g.structure_next(seed_state, weights)
    nest_noise = tf.nest.map_structure(lambda x,s: tf.random.stateless_normal(
        shape=tf.shape(x), seed=s), weights, nest_seeds)
    return nest_noise, updated_state
  return tf_return_one_noise()

@tff.tf_computation
def tff_init_state():
  g=RandomSeedGenerator()
  return g.initialize()

@tff.federated_computation
def return_two_noise():
  seed_state = tff_init_state()
  n1, seed_state = tff_return_one_noise(seed_state)
  n2, seed_state = tff_return_one_noise(seed_state)
  return (n1, n2)

n1, n2 = return_two_noise() 
assert n1[1] != n2[1]
print('n1', n1)
print('n2', n2)
n1 [array([[-0.21598858, -0.30700883],
       [ 0.7562299 , -0.21218438]], dtype=float32), array([-1.0359321], dtype=float32)]
n2 [array([[ 1.0722181 ,  0.81287116],
       [-0.7140338 ,  0.5896157 ]], dtype=float32), array([0.44190162], dtype=float32)]