From 99f136830018c5287252fa33bbe7e66cb14a08d5 Mon Sep 17 00:00:00 2001
From: dylan-fan <289765648@qq.com>
Date: Sat, 30 Dec 2023 12:16:15 +0800
Subject: [PATCH 01/12] fix nn tutorial bugs Signed-off-by: dylan-fan
<289765648@qq.com>
---
doc/2.0/fate/ml/hetero_nn_tutorial.md | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/doc/2.0/fate/ml/hetero_nn_tutorial.md b/doc/2.0/fate/ml/hetero_nn_tutorial.md
index f5409e2fd6..e072ac74b6 100644
--- a/doc/2.0/fate/ml/hetero_nn_tutorial.md
+++ b/doc/2.0/fate/ml/hetero_nn_tutorial.md
@@ -234,10 +234,13 @@ def run(ctx):
ds, model, optimizer, loss, args = get_setting(ctx)
trainer = train(ctx, ds, model, optimizer, loss, args)
pred = predict(trainer, ds)
- # compute auc here
- from sklearn.metrics import roc_auc_score
- print('auc is')
- print(roc_auc_score(pred.label_ids, pred.predictions))
+ if ctx.is_on_guest:
+ # print("pred:", pred)
+ # compute auc here
+ from sklearn.metrics import roc_auc_score
+ print('auc is')
+ print(roc_auc_score(pred.label_ids, pred.predictions))
+
if __name__ == '__main__':
from fate.arch.launchers.multiprocess_launcher import launch
From f690aa800115cc5e172c3e25d46e899aa3328d44 Mon Sep 17 00:00:00 2001
From: dylan-fan <289765648@qq.com>
Date: Sat, 30 Dec 2023 12:25:38 +0800
Subject: [PATCH 02/12] add launcher examples Signed-off-by: dylan-fan
<289765648@qq.com>
---
examples/launchers/fedpass_nn_launcher.py | 211 +++++++++++++++++++++
examples/launchers/secureboost_launcher.py | 62 ++++++
examples/launchers/sshe_nn_launcher.py | 112 +++++++++++
3 files changed, 385 insertions(+)
create mode 100644 examples/launchers/fedpass_nn_launcher.py
create mode 100644 examples/launchers/secureboost_launcher.py
create mode 100644 examples/launchers/sshe_nn_launcher.py
diff --git a/examples/launchers/fedpass_nn_launcher.py b/examples/launchers/fedpass_nn_launcher.py
new file mode 100644
index 0000000000..6a95c64c8b
--- /dev/null
+++ b/examples/launchers/fedpass_nn_launcher.py
@@ -0,0 +1,211 @@
+import torch as t
+
+from fate.arch import Context
+from fate.ml.nn.hetero.hetero_nn import HeteroNNTrainerGuest, HeteroNNTrainerHost, TrainingArguments
+from fate.ml.nn.model_zoo.hetero_nn_model import FedPassArgument, TopModelStrategyArguments
+from fate.ml.nn.model_zoo.hetero_nn_model import HeteroNNModelGuest, HeteroNNModelHost
+
+
+def train(ctx: Context,
+ dataset=None,
+ model=None,
+ optimizer=None,
+ loss_func=None,
+ args: TrainingArguments = None,
+ ):
+ if ctx.is_on_guest:
+ trainer = HeteroNNTrainerGuest(ctx=ctx,
+ model=model,
+ train_set=dataset,
+ optimizer=optimizer,
+ loss_fn=loss_func,
+ training_args=args
+ )
+ else:
+ trainer = HeteroNNTrainerHost(ctx=ctx,
+ model=model,
+ train_set=dataset,
+ optimizer=optimizer,
+ training_args=args
+ )
+
+ trainer.train()
+ return trainer
+
+
+def predict(trainer, dataset):
+ return trainer.predict(dataset)
+
+
+def get_setting(ctx):
+ import torchvision
+
+ # define model
+ from torch import nn
+ from torch.nn import init
+
+ class ConvBlock(nn.Module):
+ def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, bias=True, norm_type=None,
+ relu=False):
+ super().__init__()
+
+ self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, bias=bias)
+ self.norm_type = norm_type
+
+ if self.norm_type:
+ if self.norm_type == 'bn':
+ self.bn = nn.BatchNorm2d(out_channels)
+ elif self.norm_type == 'gn':
+ self.bn = nn.GroupNorm(out_channels // 16, out_channels)
+ elif self.norm_type == 'in':
+ self.bn = nn.InstanceNorm2d(out_channels)
+ else:
+ raise ValueError("Wrong norm_type")
+ else:
+ self.bn = None
+
+ if relu:
+ self.relu = nn.ReLU(inplace=True)
+ else:
+ self.relu = None
+
+ self.reset_parameters()
+
+ def reset_parameters(self):
+ init.kaiming_normal_(self.conv.weight, mode='fan_out', nonlinearity='relu')
+
+ def forward(self, x, scales=None, biases=None):
+ x = self.conv(x)
+ if self.norm_type is not None:
+ x = self.bn(x)
+ if scales is not None and biases is not None:
+ x = scales[-1] * x + biases[-1]
+
+ if self.relu is not None:
+ x = self.relu(x)
+ return x
+
+ # host top model
+ class LeNetBottom(nn.Module):
+ def __init__(self):
+ super(LeNetBottom, self).__init__()
+ self.layer0 = nn.Sequential(
+ ConvBlock(1, 8, kernel_size=5),
+ nn.ReLU(inplace=True),
+ nn.MaxPool2d(2, 2)
+ )
+
+ def forward(self, x):
+ x = self.layer0(x)
+ return x
+
+ # guest top model
+ class LeNetTop(nn.Module):
+
+ def __init__(self, out_feat=84):
+ super(LeNetTop, self).__init__()
+ self.pool = nn.MaxPool2d(2, 2)
+ self.fc1 = nn.Linear(16 * 4 * 4, 120)
+ self.fc1act = nn.ReLU(inplace=True)
+ self.fc2 = nn.Linear(120, 84)
+ self.fc2act = nn.ReLU(inplace=True)
+ self.fc3 = nn.Linear(84, out_feat)
+
+ def forward(self, x_a):
+ x = x_a
+ x = self.pool(x)
+ x = x.view(x.size(0), -1)
+ x = self.fc1(x)
+ x = self.fc1act(x)
+ x = self.fc2(x)
+ x = self.fc2act(x)
+ x = self.fc3(x)
+ return x
+
+ # fed simulate tool
+ from torch.utils.data import Dataset
+
+ class NoFeatureDataset(Dataset):
+ def __init__(self, ds):
+ self.ds = ds
+
+ def __len__(self):
+ return len(self.ds)
+
+ def __getitem__(self, item):
+ return [self.ds[item][1]]
+
+ class NoLabelDataset(Dataset):
+ def __init__(self, ds):
+ self.ds = ds
+
+ def __len__(self):
+ return len(self.ds)
+
+ def __getitem__(self, item):
+ return [self.ds[item][0]]
+
+ # prepare mnist data
+ train_data = torchvision.datasets.MNIST(root='./',
+ train=True, download=True, transform=torchvision.transforms.ToTensor())
+
+ if ctx.is_on_guest:
+
+ model = HeteroNNModelGuest(
+ top_model=LeNetTop(),
+ top_arg=TopModelStrategyArguments(
+ protect_strategy='fedpass',
+ fed_pass_arg=FedPassArgument(
+ layer_type='linear',
+ in_channels_or_features=84,
+ hidden_features=64,
+ out_channels_or_features=10,
+ passport_mode='multi',
+ activation='relu',
+ num_passport=1000,
+ low=-10
+ )
+ )
+ )
+ optimizer = t.optim.Adam(model.parameters(), lr=0.01)
+ loss = t.nn.CrossEntropyLoss()
+ ds = NoFeatureDataset(train_data)
+
+ else:
+
+ model = HeteroNNModelHost(
+ bottom_model=LeNetBottom(),
+ agglayer_arg=FedPassArgument(
+ layer_type='conv',
+ in_channels_or_features=8,
+ out_channels_or_features=16,
+ kernel_size=(5, 5),
+ stride=(1, 1),
+ passport_mode='multi',
+ activation='relu',
+ num_passport=1000
+ )
+ )
+ optimizer = t.optim.Adam(model.parameters(), lr=0.01)
+ loss = None
+ ds = NoLabelDataset(train_data)
+
+ args = TrainingArguments(
+ num_train_epochs=3,
+ per_device_train_batch_size=256,
+ disable_tqdm=False
+ )
+
+ return ds, model, optimizer, loss, args
+
+
+def run(ctx):
+ ds, model, optimizer, loss, args = get_setting(ctx)
+ trainer = train(ctx, ds, model, optimizer, loss, args)
+ pred = predict(trainer, ds)
+
+
+if __name__ == '__main__':
+ from fate.arch.launchers.multiprocess_launcher import launch
+
+ launch(run)
diff --git a/examples/launchers/secureboost_launcher.py b/examples/launchers/secureboost_launcher.py
new file mode 100644
index 0000000000..d68ddaf981
--- /dev/null
+++ b/examples/launchers/secureboost_launcher.py
@@ -0,0 +1,62 @@
+import pandas as pd
+
+from fate.arch import Context
+from fate.arch.dataframe import DataFrame
+from fate.arch.dataframe import PandasReader
+from fate.arch.launchers.multiprocess_launcher import launch
+from fate.ml.ensemble.algo.secureboost.hetero.guest import HeteroSecureBoostGuest
+from fate.ml.ensemble.algo.secureboost.hetero.host import HeteroSecureBoostHost
+
+
+def train(ctx: Context, data: DataFrame, num_trees: int = 3, objective: str = 'binary:bce', max_depth: int = 3,
+ learning_rate: float = 0.3):
+ if ctx.is_on_guest:
+ bst = HeteroSecureBoostGuest(num_trees=num_trees, objective=objective, \
+ max_depth=max_depth, learning_rate=learning_rate)
+ else:
+ bst = HeteroSecureBoostHost(num_trees=num_trees, max_depth=max_depth)
+
+ bst.fit(ctx, data)
+
+ return bst
+
+
+def predict(ctx: Context, data: DataFrame, model_dict: dict):
+ if ctx.is_on_guest:
+ bst = HeteroSecureBoostGuest()
+ else:
+ bst = HeteroSecureBoostHost()
+ bst.from_model(model_dict)
+ return bst.predict(ctx, data)
+
+
+def csv_to_df(ctx, file_path, has_label=True):
+ df = pd.read_csv(file_path)
+ df["sample_id"] = [i for i in range(len(df))]
+ if has_label:
+ reader = PandasReader(sample_id_name="sample_id", match_id_name="id", label_name="y", dtype="float32")
+ else:
+ reader = PandasReader(sample_id_name="sample_id", match_id_name="id", dtype="float32")
+
+ fate_df = reader.to_frame(ctx, df)
+ return fate_df
+
+
+def run(ctx):
+ num_tree = 3
+ max_depth = 3
+ if ctx.is_on_guest:
+ data = csv_to_df(ctx, '../data/breast_hetero_guest.csv')
+ bst = train(ctx, data, num_trees=num_tree, max_depth=max_depth)
+ model_dict = bst.get_model()
+ pred = predict(ctx, data, model_dict)
+ print(pred.as_pd_df())
+ else:
+ data = csv_to_df(ctx, '../data/breast_hetero_host.csv', has_label=False)
+ bst = train(ctx, data, num_trees=num_tree, max_depth=max_depth)
+ model_dict = bst.get_model()
+ predict(ctx, data, model_dict)
+
+
+if __name__ == '__main__':
+ launch(run)
diff --git a/examples/launchers/sshe_nn_launcher.py b/examples/launchers/sshe_nn_launcher.py
new file mode 100644
index 0000000000..da7402b6c1
--- /dev/null
+++ b/examples/launchers/sshe_nn_launcher.py
@@ -0,0 +1,112 @@
+import torch as t
+
+from fate.arch import Context
+from fate.ml.nn.hetero.hetero_nn import HeteroNNTrainerGuest, HeteroNNTrainerHost, TrainingArguments
+from fate.ml.nn.model_zoo.hetero_nn_model import HeteroNNModelGuest, HeteroNNModelHost
+from fate.ml.nn.model_zoo.hetero_nn_model import SSHEArgument
+
+
+def train(ctx: Context,
+ dataset=None,
+ model=None,
+ optimizer=None,
+ loss_func=None,
+ args: TrainingArguments = None,
+ ):
+ if ctx.is_on_guest:
+ trainer = HeteroNNTrainerGuest(ctx=ctx,
+ model=model,
+ train_set=dataset,
+ optimizer=optimizer,
+ loss_fn=loss_func,
+ training_args=args
+ )
+ else:
+ trainer = HeteroNNTrainerHost(ctx=ctx,
+ model=model,
+ train_set=dataset,
+ optimizer=optimizer,
+ training_args=args
+ )
+
+ trainer.train()
+ return trainer
+
+
+def predict(trainer, dataset):
+ return trainer.predict(dataset)
+
+
+def get_setting(ctx):
+ from fate.ml.nn.dataset.table import TableDataset
+ # prepare data
+ if ctx.is_on_guest:
+ ds = TableDataset(to_tensor=True)
+ ds.load("../data/breast_hetero_guest.csv")
+
+ bottom_model = t.nn.Sequential(
+ t.nn.Linear(10, 8),
+ t.nn.ReLU(),
+ )
+ top_model = t.nn.Sequential(
+ t.nn.Linear(8, 1),
+ t.nn.Sigmoid()
+ )
+ model = HeteroNNModelGuest(
+ top_model=top_model,
+ bottom_model=bottom_model,
+ agglayer_arg=SSHEArgument(
+ guest_in_features=8,
+ host_in_features=8,
+ out_features=8,
+ layer_lr=0.01
+ )
+ )
+
+ optimizer = t.optim.Adam(model.parameters(), lr=0.01)
+ loss = t.nn.BCELoss()
+
+ else:
+ ds = TableDataset(to_tensor=True)
+ ds.load("../data/breast_hetero_host.csv")
+ bottom_model = t.nn.Sequential(
+ t.nn.Linear(20, 8),
+ t.nn.ReLU(),
+ )
+
+ model = HeteroNNModelHost(
+ bottom_model=bottom_model,
+ agglayer_arg=SSHEArgument(
+ guest_in_features=8,
+ host_in_features=8,
+ out_features=8,
+ layer_lr=0.01
+ )
+ )
+ optimizer = t.optim.Adam(model.parameters(), lr=0.01)
+ loss = None
+
+ args = TrainingArguments(
+ num_train_epochs=3,
+ per_device_train_batch_size=256
+ )
+
+ return ds, model, optimizer, loss, args
+
+
+def run(ctx):
+ ds, model, optimizer, loss, args = get_setting(ctx)
+ trainer = train(ctx, ds, model, optimizer, loss, args)
+ pred = predict(trainer, ds)
+ if ctx.is_on_guest:
+ # print("pred:", pred)
+ # compute auc here
+ from sklearn.metrics import roc_auc_score
+ print('auc is')
+ print(roc_auc_score(pred.label_ids, pred.predictions))
+
+
+if __name__ == '__main__':
+ from fate.arch.launchers.multiprocess_launcher import launch
+
+ launch(run)
From 2973d345b97176c77770bce00aa667af72f86067 Mon Sep 17 00:00:00 2001
From: dylan-fan <289765648@qq.com>
Date: Sat, 30 Dec 2023 12:41:08 +0800
Subject: [PATCH 03/12] add launcher run.sh Signed-off-by: dylan-fan
<289765648@qq.com>
---
examples/launchers/run.sh | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 examples/launchers/run.sh
diff --git a/examples/launchers/run.sh b/examples/launchers/run.sh
new file mode 100644
index 0000000000..f8bf191c08
--- /dev/null
+++ b/examples/launchers/run.sh
@@ -0,0 +1,9 @@
+# examples; you can adjust it if you need.
+
+python sshe_lr_launcher.py --parties guest:9999 host:10000 --log_level INFO --guest_data ./breast_hetero_guest.csv --host_data ./breast_hetero_host.csv
+
+python secureboost_launcher.py --parties guest:9999 host:10000 --log_level INFO
+
+python sshe_nn_launcher.py --parties guest:9999 host:10000 --log_level INFO
+
+python fedpass_nn_launcher.py --parties guest:9999 host:10000 --log_level INFO
\ No newline at end of file
From e8f603c6c6df19b649eb33c32397b69a36e7e84a Mon Sep 17 00:00:00 2001
From: dylan-fan <289765648@qq.com>
Date: Sat, 30 Dec 2023 13:23:13 +0800
Subject: [PATCH 04/12] fix readme Signed-off-by: dylan-fan <289765648@qq.com>
---
README.md | 17 ++++-
README_zh.md | 69 ------------------
doc/architecture/README.md | 3 +
doc/images/fate_arch.png | Bin 0 -> 347002 bytes
...DPR_Data_Shortage_and_AI-AAAI_2019_PPT.pdf | Bin 0 -> 4335723 bytes
doc/resources/README.md | 25 +++++++
doc/resources/README.zh.md | 25 +++++++
.../SecureBoost-ijcai2019-workshop.pdf | Bin 0 -> 1845183 bytes
...0\347\240\224\350\256\250\344\274\232.pdf" | Bin 0 -> 9079725 bytes
9 files changed, 68 insertions(+), 71 deletions(-)
delete mode 100644 README_zh.md
create mode 100644 doc/architecture/README.md
create mode 100644 doc/images/fate_arch.png
create mode 100644 doc/resources/GDPR_Data_Shortage_and_AI-AAAI_2019_PPT.pdf
create mode 100644 doc/resources/README.md
create mode 100644 doc/resources/README.zh.md
create mode 100644 doc/resources/SecureBoost-ijcai2019-workshop.pdf
create mode 100644 "doc/resources/\346\235\250\345\274\272\346\225\231\346\216\210\357\274\2322021\350\201\224\351\202\246\345\255\246\344\271\240\344\270\223\351\242\230\347\240\224\350\256\250\344\274\232.pdf"
diff --git a/README.md b/README.md
index a5fe478c0e..cb981d86ee 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,6 @@
-[DOCS](./doc) | [中文](./README_zh.md)
FATE (Federated AI Technology Enabler) is the world's first industrial grade federated learning open source framework to enable enterprises and institutions to collaborate on data while protecting data security and privacy.
It implements secure computation protocols based on homomorphic encryption and multi-party computation (MPC).
@@ -36,8 +35,22 @@ Deploying FATE to multiple nodes to achieve scalability, reliability and managea
- [Cluster deployment by CLI](./deploy/cluster-deploy): Using CLI to deploy a FATE cluster.
### Quick Start
-- [Training Demo With Installing FATE AND FATE-Flow From Pypi](doc/2.0/fate/quick_start.md)
- [Training Demo With Installing FATE Only From Pypi](doc/2.0/fate/ml)
+- [Training Demo With Installing FATE AND FATE-Flow From Pypi](doc/2.0/fate/quick_start.md)
+
+### More examples
+- [ML examples](examples/launchers)
+- [PipeLine examples](examples/pipeline)
+
+## Documentation
+
+### FATE Design
+- [Architecture](./doc/architecture/README.md): Building Unified and Standardized API for Heterogeneous Computing Engines Interconnection
+- [FATE Algorithm Components](./doc/2.0/fate/components/README.md): Building Standardized Algorithm Components for different Scheduling Engines
+- [OSX (Open Site Exchange)](./doc/2.0/osx/osx.md): Building Open Platform for Cross-Site Communication Interconnection
+- [FATE-Flow](https://github.com/FederatedAI/FATE-Flow/blob/main/doc/fate_flow.md): Building Open and Standardized Scheduling Platform for Scheduling Interconnection
+- [PipeLine Design](https://github.com/FederatedAI/FATE-Client/blob/main/doc/pipeline.md): Building Scalable Federated DSL for Application Layer Interconnection And Providing Tools For Fast Federated Modeling
+- [Paper & Conference](./doc/resources/README.md)
## Related Repositories (Projects)
- [KubeFATE](https://github.com/FederatedAI/KubeFATE): An operational tool for the FATE platform using cloud native technologies such as containers and Kubernetes.
diff --git a/README_zh.md b/README_zh.md
deleted file mode 100644
index f632fae216..0000000000
--- a/README_zh.md
+++ /dev/null
@@ -1,69 +0,0 @@
-[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![CodeStyle](https://img.shields.io/badge/Check%20Style-Google-brightgreen)](https://checkstyle.sourceforge.io/google_style.html) [![Style](https://img.shields.io/badge/Check%20Style-Black-black)](https://checkstyle.sourceforge.io/google_style.html) [![Build Status](https://travis-ci.org/FederatedAI/FATE.svg?branch=master)](https://travis-ci.org/FederatedAI/FATE)
-[![codecov](https://codecov.io/gh/FederatedAI/FATE/branch/master/graph/badge.svg)](https://codecov.io/gh/FederatedAI/FATE)
-[![Documentation Status](https://readthedocs.org/projects/fate/badge/?version=latest)](https://fate.readthedocs.io/en/latest/?badge=latest)
-[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/from-referrer/)
-[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6308/badge)](https://bestpractices.coreinfrastructure.org/projects/6308)
-
-
-
-
-
-[DOC](./doc) | [Quick Start](doc/tutorial/pipeline/pipeline_tutorial_hetero_sbt.ipynb) | [English](./README.md)
-
-FATE (Federated AI Technology Enabler) 是全球首个联邦学习工业级开源框架,可以让企业和机构在保护数据安全和数据隐私的前提下进行数据协作。
-FATE项目使用多方安全计算 (MPC) 以及同态加密 (HE) 技术构建底层安全计算协议,以此支持不同种类的机器学习的安全计算,包括逻辑回归、基于树的算法、深度学习和迁移学习等。
-FATE于2019年2月首次对外开源,并成立
-[FATE TSC](https://github.com/FederatedAI/FATE-Community/blob/master/FATE_Project_Technical_Charter.pdf)
-对FATE社区进行开源治理,成员包含国内主要云计算和金融服务企业。
-
-
-
-## 教程
-
-### 2.0以前的版本
-FATE 2.0以前的版本在[发布页](https://github.com/FederatedAI/FATE/releases), 下载资源汇总页在[wiki](https://github.com/FederatedAI/FATE/wiki/Download)
-
-### 2.0.0 版本
-#### 单机版部署
-在单节点上部署FATE单机版,支持从 PyPI 直接安装,docker,主机安装包三种方式。
-- [单机版部署教程](./deploy/standalone-deploy)
-#### 集群
-- [原生集群安装](./deploy/cluster-deploy): Using CLI to deploy a FATE cluster.
-
-### 快速开始
-- [从 PyPI 下载安装 FATE 和 FATE-Flow 并启动训练任务示例](doc/2.0/fate/quick_start.md)
-- [从 PyPI 下载安装 FATE,并启动训练任务示例](doc/2.0/fate/ml)
-
-## 关联仓库
-- [KubeFATE](https://github.com/FederatedAI/KubeFATE)
-- [FATE-Flow](https://github.com/FederatedAI/FATE-Flow)
-- [FATE-Board](https://github.com/FederatedAI/FATE-Board)
-- [FATE-Serving](https://github.com/FederatedAI/FATE-Serving)
-- [FATE-Cloud](https://github.com/FederatedAI/FATE-Cloud)
-- [FedVision](https://github.com/FederatedAI/FedVision)
-- [EggRoll](https://github.com/WeBankFinTech/eggroll)
-- [AnsibleFATE](https://github.com/FederatedAI/AnsibleFATE)
-- [FATE-Builder](https://github.com/FederatedAI/FATE-Builder)
-- [FATE-Client](https://github.com/FederatedAI/FATE-Client)
-- [FATE-Test](https://github.com/FederatedAI/FATE-Test)
-
-## 社区治理
-
-[FATE-Community](https://github.com/FederatedAI/FATE-Community) 仓库包含历史社区合作,沟通,会议,章程等文档。
-
-- [GOVERNANCE.md](https://github.com/FederatedAI/FATE-Community/blob/master/GOVERNANCE.md)
-- [Minutes](https://github.com/FederatedAI/FATE-Community/blob/master/meeting-minutes)
-- [Development Process Guidelines](https://github.com/FederatedAI/FATE-Community/blob/master/FederatedAI_PROJECT_PROCESS_GUIDELINE.md)
-- [Security Release Process](https://github.com/FederatedAI/FATE-Community/blob/master/SECURITY.md)
-
-## 了解更多
-
-- [Fate-FedAI Group IO](https://groups.io/g/Fate-FedAI)
-- [FAQ](https://github.com/FederatedAI/FATE/wiki)
-- [issues](https://github.com/FederatedAI/FATE/issues)
-- [pull requests](https://github.com/FederatedAI/FATE/pulls)
-- [Twitter: @FATEFedAI](https://twitter.com/FateFedAI)
-
-
-## License
-[Apache License 2.0](LICENSE)
diff --git a/doc/architecture/README.md b/doc/architecture/README.md
new file mode 100644
index 0000000000..0ada72e7e2
--- /dev/null
+++ b/doc/architecture/README.md
@@ -0,0 +1,3 @@
+### FATE Overall Architecture
+
+![](../images/fate_arch.png)
\ No newline at end of file
diff --git a/doc/images/fate_arch.png b/doc/images/fate_arch.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c64976e8ff8cbca356d7e7304e8f2d68894ea09
GIT binary patch
literal 347002
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 {
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