FATE系统主要支持表格数据作为其标准数据格式。然而,通过使用NN模块的数据集特性,可以在神经网络中使用非表格数据,例如图像、文本、混合数据或关系数据。NN模块中的数据集模块允许自定义数据集,以便用户可以在更复杂的数据场景中使用它们。本教程将介绍Hetero NN中数据集功能的使用。为了便于演示,我们将使用MNIST手写识别数据集作为示例来模拟Hetero Federation任务以说明这些概念。
请从以下链接下载来宾/主机MNIST数据集,并将其放在项目示例/数据文件夹中:
gust数据:guest地址
host数据:host地址
mnist_guest是mnist数据集的简化版本,共有十个类别,根据标签分为0-9个10个文件夹。mnist_host具有与mnist_guest相同的图像,但未标记。
! ls /mnt/hgfs/Hetero/mnist_guest # 根据自己的文件位置进行调整
0 1 2 3 4 5 6 7 8 9
! ls /mnt/hgfs/Hetero/mnist_host
not_labeled
在FATE-1.10版本中,FATE为数据集引入了一个新的基类,称为Dataset,它基于PyTorch的Dataset类。此类允许用户根据其特定需求创建自定义数据集。其用法与PyTorch的Dataset类类似,在使用FATE-NN进行数据读取和训练时,需要实现两个额外的接口:load()和get_sample_ids()。
要在Hetero NN中创建自定义数据集,用户需要:
实现__len__()和__getitem__()方法,它们与PyTorch的数据集用法一致。__len__()方法应返回数据集的长度,而__getitem_()方法则应返回指定索引处的相应数据。但是,请注意,不同的__getitem_()方法在不同方之间可能有不同的行为。在来宾方(带有标签的方)中,_getitem\_()方法返回功能和标签,而在宿主方(没有标签的方,_getiitem\_(()方法仅返回功能。
对于不熟悉PyTorch的数据集类的人,可以在PyTorch文档中找到更多信息:PyTorch数据集文档
所需的第一个附加接口是load()。此接口接收文件路径,并允许用户直接从本地文件系统读取数据。提交任务时,可以通过读取器组件指定数据路径。Hetero NN将使用用户指定的Dataset类,利用load()接口从指定路径读取数据,并完成数据集的加载以进行训练。有关更多信息,请参阅base.py中的源代码。
第二个附加接口是get_sample_ids()。此接口应返回一个样本ID列表,该列表可以是整数或字符串,并且长度应与数据集相同。该功能在Hetero NN中很重要,您需要了解以下几点:
在Hetero NN中使用自定义数据集时,确保您的样本ID与其他方的样本ID一致非常重要。您可以通过使用交集组件并提取结果,或者通过与其他方商定要使用的样本ID来实现这一点。
你不必把你的id按顺序排列,Hetero-NN组件会对它们进行排序。
第三个函数返回所有唯一标签的列表。这将在宾客聚会上进行。如果不是分类任务,只需返回一个空列表。
为了更好地理解数据集的定制,这里我们实现了一个简单的图像数据集来读取MNIST图像,然后在垂直场景中完成联合图像分类任务。为了方便起见,我们使用save_to_rate的jupyter接口将代码更新为federatedml.nn.dataset(名为mnist_dataset.py),当然,您可以手动将代码文件复制到目录中。
此数据集有一个参数“return_label”,当来宾方(带标签的方)使用它时,我们将return_Label=True,否则return_lappel=False
它是基于ImageFolder开发的,我们将图像名称作为示例id。
from pipeline.component.nn import save_to_fate
%%save_to_fate dataset mnist_dataset.py
import numpy as np
from federatedml.nn.dataset.base import Dataset
from torchvision.datasets import ImageFolder
from torchvision import transforms
class MNISTDataset(Dataset):
def __init__(self, return_label=True):
super(MNISTDataset, self).__init__()
self.return_label = return_label
self.image_folder = None
self.ids = None
def load(self, path):
self.image_folder = ImageFolder(root=path, transform=transforms.Compose([transforms.ToTensor()]))
ids = []
for image_name in self.image_folder.imgs:
ids.append(image_name[0].split('/')[-1].replace('.jpg', ''))
self.ids = ids
return self
def get_sample_ids(self, ):
return self.ids
def get_classes(self, ):
return np.unique(self.image_folder.targets).tolist()
def __len__(self,):
return len(self.image_folder)
def __getitem__(self, idx): # get item
ret = self.image_folder[idx]
img = ret[0][0].flatten() # flatten tensor 784 dims
if self.return_label:
return img, ret[1] # img & label
else:
return img # no label, for host
现在我们测试数据集类:
# guest party
! ls /mnt/hgfs/Hetero/mnist_guest
ds = MNISTDataset().load('/mnt/hgfs/Hetero/mnist_guest')
print(len(ds))
print(ds[0][0])
print(ds.get_classes())
print(ds.get_sample_ids()[0: 10])
# host party
! ls /mnt/hgfs/Hetero/mnist_host # no label
ds = MNISTDataset(return_label=False).load('/mnt/hgfs/Hetero/mnist_host')
print(len(ds))
print(ds[0]) # no label
好的它已经准备好使用了,所以让我们使用这个开发的数据集来运行一个Hetero-NN模型,并且双方都使用两个数据集mnist_guest和mnist_host来执行一个异类联合训练
与Homo NN(请参阅自定义数据集)相同,这里我们不会遵循传统FATE组件的用法,而是将数据路径直接绑定到FATE名称和命名空间,并通过读取器将其传递给Hemo NN组件,Hemo NN通过您设置的DatasetParam导入自定义数据集类,然后从路径读取数据。
在这里,我们定义了运行异类任务的pipeline
import os
import torch as t
from torch import nn
from pipeline import fate_torch_hook
from pipeline.component import HeteroNN
from pipeline.component.hetero_nn import DatasetParam
from pipeline.backend.pipeline import PipeLine
from pipeline.component import Reader, Evaluation, DataTransform
from pipeline.interface import Data, Model
from pipeline.component.nn import save_to_fate
fate_torch_hook(t)
# bind path to fate name&namespace
# fate_project_path = os.path.abspath('/mnt/hgfs/Hetero/') # 自定义文件位置
guest = 10000
host = 9999
pipeline_img = PipeLine().set_initiator(role='guest', party_id=guest).set_roles(guest=guest, host=host)
guest_data = {"name": "mnist_guest", "namespace": "experiment"}
host_data = {"name": "mnist_host", "namespace": "experiment"}
# 自定义文件位置
guest_data_path = '/mnt/hgfs/Hetero/mnist_guest/'
host_data_path = '/mnt/hgfs/Hetero/mnist_host/'
pipeline_img.bind_table(name='mnist_guest', namespace='experiment', path=guest_data_path)
pipeline_img.bind_table(name='mnist_host', namespace='experiment', path=host_data_path)
{'namespace': 'experiment', 'table_name': 'mnist_host'}
guest_data = {"name": "mnist_guest", "namespace": "experiment"}
host_data = {"name": "mnist_host", "namespace": "experiment"}
reader_0 = Reader(name="reader_0")
reader_0.get_party_instance(role='guest', party_id=guest).component_param(table=guest_data)
reader_0.get_party_instance(role='host', party_id=host).component_param(table=host_data)
hetero_nn_0 = HeteroNN(name="hetero_nn_0", epochs=3,
interactive_layer_lr=0.01, batch_size=512, task_type='classification', seed=100
)
guest_nn_0 = hetero_nn_0.get_party_instance(role='guest', party_id=guest)
host_nn_0 = hetero_nn_0.get_party_instance(role='host', party_id=host)
# define model
# image features 784, guest bottom model
# our simple classification model:
guest_bottom = t.nn.Sequential(
t.nn.Linear(784, 8),
t.nn.ReLU()
)
# image features 784, host bottom model
host_bottom = t.nn.Sequential(
t.nn.Linear(784, 8),
t.nn.ReLU()
)
# Top Model, a classifier
guest_top = t.nn.Sequential(
nn.Linear(8, 10),
nn.Softmax(dim=1)
)
# interactive layer define
interactive_layer = t.nn.InteractiveLayer(out_dim=8, guest_dim=8, host_dim=8)
# add models
guest_nn_0.add_top_model(guest_top)
guest_nn_0.add_bottom_model(guest_bottom)
host_nn_0.add_bottom_model(host_bottom)
# opt, loss
optimizer = t.optim.Adam(lr=0.01)
loss = t.nn.CrossEntropyLoss()
# use DatasetParam to specify dataset and pass parameters
guest_nn_0.add_dataset(DatasetParam(dataset_name='mnist_dataset', return_label=True))
host_nn_0.add_dataset(DatasetParam(dataset_name='mnist_dataset', return_label=False))
hetero_nn_0.set_interactive_layer(interactive_layer)
hetero_nn_0.compile(optimizer=optimizer, loss=loss)
pipeline_img.add_component(reader_0)
pipeline_img.add_component(hetero_nn_0, data=Data(train_data=reader_0.output.data))
pipeline_img.add_component(Evaluation(name='eval_0', eval_type='multi'), data=Data(data=hetero_nn_0.output.data))
pipeline_img.compile()
pipeline_img.fit()
pipeline_img.get_component('hetero_nn_0').get_output_data() # get result