Zusätzliche Details für die Implementierung von Federated Learning

Dieses Dokument enthält weitere Details zu Aspekten der Konfiguration von Federated Learning, die eine umfangreiche Codeumsetzung erfordern.

Anaconda-Konfiguration

Wenn Sie mit Federated Learning über den Python-Client arbeiten, müssen Sie in einigen Fällen eine neue conda-Umgebung erstellen. Nachfolgend finden Sie eine YLM-Beispieldatei mit den Befehlen für die Einrichtung einer unabhängigen conda-Umgebung.

# Umgebung erstellen
conda create -n fl_env python=3.7.9

# Jupyter in Umgebung erstellen
conda install -c conda-forge -n fl_env notebook

# Umgebung aktivieren
conda activate fl_env

# pip-Installation von SDK
pip install ibm-watson-machine-learning

# pip-Installation von Paketen, die von IBMFL benötigt werden
pip install environs parse websockets jsonpickle pandas pytest pyYAML requests pathlib2 psutil setproctitle tabulate lz4 opencv-python gym ray==0.8.0 cloudpickle==1.3.0 image

# pip-Installation von Frameworks, die von IBMFL verwendet werden
pip install tensorflow==2.1.0 scikit-learn==0.23.1 keras==2.2.4 numpy==1.17.4 scipy==1.4.1

# Juypyter-Notebooks starten
jupyter notebook  # <-- Sicherstellen, dass die Umgebung bereits aktiviert wurde: conda activate fl_env

Beispiele für Datenhandler

Datengenerator zurückgeben, der durch Keras oder TensorFlow 2 definiert ist

Im Folgenden sehen Sie ein Codebeispiel, das als Teil von get_data aufgenommen werden muss, um Daten in Form eines Datengenerators zurückzugeben, der von Keras oder TensorFlow 2 definiert wird:

train_gen = ImageDataGenerator(rotation_range=8,
                                width_shift_range=0.08,
                                shear_range=0.3,
                                height_shift_range=0.08,
                                zoom_range=0.08)

    train_datagenerator = train_gen.flow(
        x_train, y_train, batch_size=64)

    return train_datagenerator

Daten als NumPy-Arrays zurückgeben

Im Folgenden sehen Sie ein Codebeispiel für den MNIST-Datenhandler, der die Daten in der Form von NumPy-Arrays zurückgibt.

import numpy as np

# Import von ibmfl
from ibmfl.data.data_handler import DataHandler
from ibmfl.exceptions import FLException



class MnistKerasDataHandler(DataHandler):
    """
    Datenhandler für MNIST-Dataset.
    """

    def __init__(self, data_config=None, channels_first=False):
        super().__init__()
        self.file_name = None
        # `data_config` lädt alles im Teil `info` des Abschnitts `data`.
        if data_config is not None:
            # Bei diesem Beispiel wird angenommen, dass das lokale Dataset im .npz-Format vorliegt, also wird danach gesucht.
            if 'npz_file' in data_config: 
                self.file_name = data_config['npz_file']
        self.channels_first = channels_first
        
        if self.file_name is None:
            raise FLException('No data file name is provided to load the dataset.')
 else:
            try:
                data_train = np.load(self.file_name)
                self.x_train = data_train['x_train']
                self.y_train = data_train['y_train']
                self.x_test = data_train['x_test']
                self.y_test = data_train['y_test']
            except Exception:
                raise IOError('Unable to load training data from path '
                              'provided in config file: ' +
                              self.file_name)
            self.preprocess_data()

    def get_data(self):
        """
        Ruft vorverarbeitete MNIST-Trainings- und Testdaten ab.

        :return: training and testing data
        :rtype: `tuple`
        """
        return (self.x_train, self.y_train), (self.x_test, self.y_test)

    def preprocess_data(self):
        """
        Vorverarbeitung des Trainings- und Testdatasets.

        :return: None
        """
        num_classes = 10
        img_rows, img_cols = 28, 28
        if self.channels_first:
            self.x_train = self.x_train.reshape(self.x_train.shape[0], 1, img_rows, img_cols)
            self.x_test = self.x_test.reshape(self.x_test.shape[0], 1, img_rows, img_cols)
 else:
            self.x_train = self.x_train.reshape(self.x_train.shape[0], img_rows, img_cols, 1)
            self.x_test = self.x_test.reshape(self.x_test.shape[0], img_rows, img_cols, 1)

        print('x_train shape:', self.x_train.shape)
        print(self.x_train.shape[0], 'train samples')
        print(self.x_test.shape[0], 'test samples')

        # Konvertiert Klassenvektoren zu binären Klassenmatrizen
        self.y_train = np.eye(num_classes)[self.y_train]
        self.y_test = np.eye(num_classes)[self.y_test]

Hyperparameterkonfiguration

Basierend auf der Fusionsmethode und dem Framework, das Sie für das Federated Learning-Modell auswählen, unterscheiden sich die Hyperparameteroptionen.

In der folgenden Tabelle sind die Hyperparameteroptionen aufgeführt, die für den Scikit-learn-Framework und seine Fusionsmethoden verfügbar sind:

In der folgenden Tabelle sind die Hyperparameteroptionen aufgeführt, die für das Framework TensorFlow 2 verfügbar sind:

Framework Hyperparameter Definition Hinweise
TensorFlow 2 Epochen Die Gesamtanzahl von Durchläufen bei einem lokalen Trainingsdataset zum Trainieren eines TensorFlow-Modells.  
  Batchgröße Gibt bei der Batchverarbeitung an, wie viele Muster nacheinander verarbeitet werden können. Nützlich für die Verarbeitung großer Datasets.
  Schritte pro Epoche Schritte pro Epoche wird für große Datasets verwendet und legt die Batches fest, die in einem einzelnen Dataset für eine verbesserte Genauigkeit des Modells trainiert werden sollen. Der Parameter legt die Endbearbeitung einer Epoche und den Beginn der nächsten Epoche fest. Bei der Übergabe eines unbestimmt wiederkehrenden Datasets müssen Sie das Argument steps_per_epoch angeben.

In der folgenden Tabelle sind die für die Fusionsmethoden verfügbaren Hyperparameteroptionen aufgeführt:

Fusionsmethode Hyperparameter Definition Hinweise
Iterativer Durchschnitt,
FedAvg
Rounds (Runden) Die Anzahl der durchzuführenden Trainingsiterationen zwischen dem Aggregator und dem fernen System.  
Scikit-learn (XGBoost-Klassifikation) Lerning rate (Lernrate) Die Lernrate, die auch als Schrumpfung bezeichnet wird. Dies wird als multiplikativer Faktor für die Verzweigungswerte verwendet.  
  Loss (Verlust) Die Verlustfunktion, die beim Boosting-Prozess verwendet werden soll.
- binary_crossentropy (auch bekannt als logistischer Verlust) wird für die binäre Klassifikation verwendet.
- categorical_crossentropy wird für die Klassifikation mehrerer Klassen verwendet.
- auto wählt abhängig von der Art des Problems automatisch eine Verlustfunktion aus.
- least_squares wird für die Regression verwendet.
 
  Rounds (Runden) Die Anzahl der durchzuführenden Trainingsiterationen zwischen dem Aggregator und dem fernen System.  
  Number of classes (Anzahl der Klassen) Die Anzahl der Zielklassen für das Klassifikationsmodell. Erforderlich, wenn der Hyperparameter 'Loss' wie folgt lautet:
- auto
- binary_crossentropy
- categorical_crossentropy
Scikit-learn (XGBoost-Regression) Lerning rate (Lernrate) Die Lernrate, die auch als Schrumpfung bezeichnet wird. Dies wird als multiplikativer Faktor für die Verzweigungswerte verwendet.  
  Loss (Verlust) Die Verlustfunktion, die beim Boosting-Prozess verwendet werden soll.
- binary_crossentropy (auch bekannt als logistischer Verlust) wird für die binäre Klassifikation verwendet.
- categorical_crossentropy wird für die Klassifikation mehrerer Klassen verwendet.
- auto wählt abhängig von der Art des Problems automatisch eine Verlustfunktion aus.
- least_squares wird für die Regression verwendet.
 
  Rounds (Runden) Die Anzahl der durchzuführenden Trainingsiterationen zwischen dem Aggregator und dem fernen System.  
Scikit-learn (K-Means/SPAHM) Max iter (Max. Iterationen) Die Gesamtanzahl von Durchläufen bei einem lokalen Trainingsdataset zum Trainieren eines Scikit-learn-Modells.  
  N cluster (N Cluster) Die Anzahl der zu bildenden Cluster sowie die Anzahl der zu generierenden Zentroide.  

Voraussetzungen für den fernen Server

Federated Learning kann eine Verbindung zu verschiedenen fernen Servern herstellen, wie z. B. unter anderem zu Db2 oder anderen Cloud for Data-Clustern. Für die Verbindung mit Federated Learning muss Folgendes möglich sein:

Konfiguration eines Scikit-learn-Modells

Wenn Sie Scikit-learn (SKLearn) als Modellframework ausgewählt haben, müssen Sie Ihre Einstellungen so konfigurieren, dass das in Federated Learning trainierte Modell als Pickle-Datei gespeichert wird. Definieren Sie Ihr Modell anhand des jeweiligen Codebeispiels für die Methoden zur Implementierung Ihrer Modelldatei. Dies hängt von dem Modelltyp ab, den Sie für SKLearn auswählen.

XGBoost-Klassifikation

# XGBoost-Klassifikationsmodell
# Sie können Ihre eigene Verlustfunktion wählen, indem Sie den Inhalt für 'loss' ändern.
# Im folgenden Beispiel wird `binary_crossentropy` als Beispiel
# für eine binäre Klassifikation ausgewählt.
# Wenn Sie ein Problem für eine Klassifikation mehrerer Klassen trainieren möchten, müssen Sie
# 'categorical_crossentropy' auswählen.
# Sie können auch 'auto' auswählen, damit IBM FL den richtigen Verlust für Sie auswählt.

spec = {
	'global': {
            'learning_rate': 0.1,
            'loss': 'binary_crossentropy',
            'max_bins': 255,
            'max_depth': None,
            'max_iter': 100,
            'verbose': True,
            'num_classes': 2
        }
}

XGBoost-Regression

# XGBoost-Regressionsmodell
# Sie können Ihre eigene Verlustfunktion wählen, indem Sie den Inhalt für 'loss' ändern.
# Im folgenden Beispiel wird `binary_crossentropy` als Beispiel
# für eine binäre Klassifikation ausgewählt.
# Wenn Sie ein Problem für eine Klassifikation mehrerer Klassen trainieren möchten, müssen Sie
# 'categorical_crossentropy' auswählen.
# Sie können auch 'auto' auswählen, damit IBM FL den richtigen Verlust für Sie auswählt.

spec = {
	'global': {
            'learning_rate': 0.1,
            'loss': 'least_squares', 
            'max_bins': 255,
            'max_depth': None,
            'max_iter': 100,
            'verbose': True
        }
}

SKLearn-Klassifikation

# SKLearn -Klassifikation
# Geben Sie Ihr Modell an. Benutzer müssen die Klassen angeben, die bei Klassifikationsproblemen verwendet werden.
# In dem vorliegendenBeispiel gibt es 10 Klassen.

model = SGDClassifier(loss='log', penalty='l2')
model.classes_ = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])  

# Definieren Sie den Pfad und speichern Sie das Modell als eine Pickle-Datei

if not os.path.exists(folder_configs):
    os.makedirs(folder_configs)
fname = os.path.join(folder_configs, 'model_architecture.pickle')
with open(fname, 'wb') as f:
    joblib.dump(model, f)
    # Generate model spec:
spec = {'model_definition': fname}

SKLearn-Regression

# SKlearn-Regression
# Erstellen Sie ein SKlearn-Regressionsmodell

model = SGDRegressor(loss='huber', penalty='l2')

# Geben SieeinVerzeichnis an bzw. erstellen Sie eines, in dem die Modelldatei gespeichert werden soll .

if not os.path.exists(folder_configs):
    os.makedirs(folder_configs)
fname = os.path.join(folder_configs, 'model_architecture.pickle')

# Speichern Sie das Modell als Pickle-Datei

with open(fname, 'wb') as f:
    pickle.dump(model, f)

SKLearn K-Means

# SKLearn K-Means

def get_model_config(folder_configs, dataset, is_agg=False, party_id=0):

    model = KMeans()

    # Speichern Sie das Modell
    fname = os.path.join(folder_configs, 'kmeans-central-model.pickle')
    with open(fname, 'wb') as f:
        pickle.dump(model, f)
    # Generieren Sie eine Modellspezifikation:
    spec = {
        'model_name': 'sklearn-kmeans',
        'model_definition': fname
    }

    model = {
        'name': 'SklearnKMeansFLModel',
        'path': 'ibmfl.model.sklearn_kmeans_fl_model',
        'spec': spec
    }

    return model

Konfiguration eines TensorFlow 2-Modells

Im Folgenden sehen Sie ein Beispiel für eine TensorFlow 2-Modellkonfiguration.

img_rows, img_cols = 28, 28
    batch_size = 28
    input_shape = (batch_size, img_rows, img_cols, 1)
    sample_input = np.zeros(shape=input_shape)

    class MyModel(Model):
        def __init__(self):
            super(MyModel, self).__init__()
            self.conv1 = Conv2D(32, 3, activation='relu')
            self.flatten = Flatten()
            self.d1 = Dense(128, activation='relu')
            self.d2 = Dense(10)

        def call(self, x):
            x = self.conv1(x)
            x = self.flatten(x)
            x = self.d1(x)
            return self.d2(x)

    # Erstellen Sie eine Instanz des Modells
    model = MyModel()
    loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
        from_logits=True)
    optimizer = tf.keras.optimizers.Adam()
    acc = tf.keras.metrics.SparseCategoricalAccuracy(name='accuracy')
    model.compile(optimizer=optimizer, loss=loss_object, metrics=[acc])
    model._set_inputs(sample_input)

    if not os.path.exists(folder_configs):
        os.makedirs(folder_configs)

    model.save(folder_configs)

Um das Modell als HD5-Datei zu speichern, können Sie die folgende Konfiguration hinzufügen:

model = keras.Sequential([
   keras.layers.Dense(16, activation = 'relu', input_shape = (11,)),
   keras.layers.Dropout(0.5),
   keras.layers.Dense(1, activation = 'sigmoid')])
   model.compile(optimizer=keras.optimizers.Adam(lr=2e-2), loss=keras.losses.binary_crossentropy, metrics=metrics)
])