一、隐语开放标准
1.1 标准
- 隐语提出的适用于隐私计算应用的一系列协议的集合。
- 目前包括数据,组件,节点执行,运行报告等协议。
- 隐语生态各模块均遵守本标准。
- 链接:Github Repo: https://github.com/secretflow/spec Doc: https://www.secretflow.org.cn/docs/spec/latest/zh-Hans
隐语中各个模块的交互是依赖于开放标准的,通过标准化协议,提高系统兼容性和开发效率。
1.2 数据协议
隐语中数据部分包含两部分:Public Data和DataRef
- Public:包含数据名称、类型、元信息和系统信息,是完全公开的。
- DataRef:引用具体数据的位置和格式,其指向私有数据。
- 参考文档
比如在联邦学习中的联合数据表,每个Partition可以看作是一个DataRef
指向Remote Object
1.3 组件
-
ComponentDef:通过domain、name和version可以定位到唯一的组件,
-
域(domain):组件的命名空间。
-
名称(name):组件在命名空间内的唯一标识。
-
版本(version):组件的版本号。
-
属性(attributes):组件的属性定义。
-
输入/输出(inputs/outputs):组件的输入和输出要求,支持多种类型,并可以指定使用的列和参数。
如果把组件的属性当成一个问卷的话,全部展开这个问卷表是非常庞大的,那么可以先按照属性分类,然后再根据属性的具体值去分模块,比如问卷中问题会有姓名、职业、性别,这是一个模块,根据其不同的回答再去细分更进一步的模块,不同的职业里划分为不同的模块,通过这种方式来构造树结构的问卷表,直观上更有逻辑性些。
##### 1.4 组件I/O
-
- 指定输入输出的类型,可以是多种类型。
- 如果是表,可以进一步指定使用的列。
- 还可以进一步指定每一选中列需要填入的参数。
1.5 节点执行协议
- Node Evaluation:规范节点执行的过程,确保节点能够正确地处理输入数据并生成输出结果。
-
节点是组件的实例
-
StorageConfig
参与方的数据路径配置 -
NodeEvalParam
定义组件的相关参数Attribute和IO -
NodeEvalResult
是组件运行结果outputs
1.6 运行报告协议
Report:定义运行报告的结构,包括描述、表格、页面部分(Div)、选项卡(Tab)等,用于前端展示组件运行的结果和状态。
二、隐语组件列表
查看sf组件列表有三种方式:
- GitHub仓库:secretflow/comp_list.json
- SecretFlow文档: SecretFlow Component List
- Secretpad中也可以查看组件
三、调用隐语组件
3.1 调用方式
- SecretFlow CLI/Lib:无需其他依赖,通过命令行或库调用组件,调用比较复杂,一般用于内部测试。
- Kuscia:用于简化数据同步和调度操作,隐语推出的一个调度框架。
- SecretPad:通过用户界面调用组件,操作简便。
3.2 实操示例:
下面展示一个利用python API调用组件的例子,先写一个生成文本的脚本generate_csv.sh
#!/bin/bash
set -e
show_help() {
echo "Usage: bash generate_csv.sh -c {col_name} -p {file_name}"
echo " -c"
echo " the column name of id."
echo " -p"
echo " the path of output csv."
}
if [[ "$#" -lt 1 ]]; then
show_help
exit
fi
while getopts ":c:p:" OPTION; do
case $OPTION in
c)
COL_NAME=$OPTARG
;;
p)
FILE_PATH=$OPTARG
;;
*)
echo "Incorrect options provided"
exit 1
;;
esac
done
# header
echo $COL_NAME > $FILE_PATH
# generate 800 random int
for ((i=0; i<800; i++))
do
# from 0 to 1000
id=$(shuf -i 0-1000 -n 1)
# check duplicates
while grep -q "^$id$" $FILE_PATH
do
id=$(shuf -i 0-1000 -n 1)
done
# write
echo "$id" >> $FILE_PATH
done
echo "Generated csv file is $FILE_PATH."
传到Alice和Bob两个参与者,然后执行去产生数据
alice
:
mkdir -p /tmp/alice
sh generate_csv.sh -c id1 -p /tmp/alice/input.csv
Bob
:
mkdir -p /tmp/bob
sh generate_csv.sh -c id2 -p /tmp/bob/input.csv
然后是调用组件的脚本psi_demo.py
:
import json
from secretflow.component.entry import comp_eval
from secretflow.spec.extend.cluster_pb2 import (
SFClusterConfig,
SFClusterDesc,
)
from secretflow.spec.v1.component_pb2 import Attribute
from secretflow.spec.v1.data_pb2 import (
DistData,
TableSchema,
IndividualTable,
StorageConfig,
)
from secretflow.spec.v1.evaluation_pb2 import NodeEvalParam
import click
@click.command()
@click.argument("party", type=str)
def run(party: str):
desc = SFClusterDesc(
parties=["alice", "bob"],
devices=[
SFClusterDesc.DeviceDesc(
name="spu",
type="spu",
parties=["alice", "bob"],
config=json.dumps(
{
"runtime_config": {"protocol": "REF2K", "field": "FM64"},
"link_desc": {
"connect_retry_times": 60,
"connect_retry_interval_ms": 1000,
"brpc_channel_protocol": "http",
"brpc_channel_connection_type": "pooled",
"recv_timeout_ms": 1200 * 1000,
"http_timeout_ms": 1200 * 1000,
},
}
),
),
SFClusterDesc.DeviceDesc(
name="heu",
type="heu",
parties=[],
config=json.dumps(
{
"mode": "PHEU",
"schema": "paillier",
"key_size": 2048,
}
),
),
],
)
sf_cluster_config = SFClusterConfig(
desc=desc,
public_config=SFClusterConfig.PublicConfig(
ray_fed_config=SFClusterConfig.RayFedConfig(
parties=["alice", "bob"],
addresses=[
"127.0.0.1:61041",
"127.0.0.1:61042",
],
),
spu_configs=[
SFClusterConfig.SPUConfig(
name="spu",
parties=["alice", "bob"],
addresses=[
"127.0.0.1:61045",
"127.0.0.1:61046",
],
)
],
),
private_config=SFClusterConfig.PrivateConfig(
self_party=party,
ray_head_addr="local", # local means setup a Ray cluster instead connecting to an existed one.
),
)
# check https://www.secretflow.org.cn/docs/spec/latest/zh-Hans/intro#nodeevalparam for details.
sf_node_eval_param = NodeEvalParam(
domain="preprocessing",
name="psi",
version="0.0.1",
attr_paths=[
"protocol",
"sort",
"bucket_size",
"ecdh_curve_type",
"input/receiver_input/key",
"input/sender_input/key",
],
attrs=[
Attribute(s="ECDH_PSI_2PC"),
Attribute(b=True),
Attribute(i64=1048576),
Attribute(s="CURVE_FOURQ"),
Attribute(ss=["id1"]),
Attribute(ss=["id2"]),
],
inputs=[
DistData(
name="receiver_input",
type="sf.table.individual",
data_refs=[
DistData.DataRef(uri="input.csv", party="alice", format="csv"),
],
),
DistData(
name="sender_input",
type="sf.table.individual",
data_refs=[
DistData.DataRef(uri="input.csv", party="bob", format="csv"),
],
),
],
output_uris=[
"output.csv",
],
)
sf_node_eval_param.inputs[0].meta.Pack(
IndividualTable(
schema=TableSchema(
id_types=["str"],
ids=["id1"],
),
line_count=-1,
),
)
sf_node_eval_param.inputs[1].meta.Pack(
IndividualTable(
schema=TableSchema(
id_types=["str"],
ids=["id2"],
),
line_count=-1,
),
)
storage_config = StorageConfig(
type="local_fs",
local_fs=StorageConfig.LocalFSConfig(wd=f"/tmp/{party}"),
)
res = comp_eval(sf_node_eval_param, storage_config, sf_cluster_config)
print(f'Node eval res is \n{res}')
if __name__ == "__main__":
run()
在Alice和Bob中分别执行这个psi脚本,两个终端中能看到一下输出:
Node eval res is
outputs {
name: "output.csv"
type: "sf.table.vertical_table"
system_info {
}
meta {
type_url: "type.googleapis.com/secretflow.spec.v1.VerticalTable"
value: "\n\n\n\003id1\"\003str\n\n\n\003id2\"\003str\020\211\005"
}
data_refs {
uri: "output.csv"
party: "alice"
format: "csv"
}
data_refs {
uri: "output.csv"
party: "bob"
format: "csv"
}
}
在 /tmp/alice/output.csv
和/tmp/bob/output.csv
中检查结果。两个文件的内容除了表头应该是一致的。
四、创建组件
在隐语中新增组件,主要有以下步骤:
1、新建文件,在 secretflow/component/ 目录下创建一个新的文件。
2、声明组件,使用 secretflow.component.component 创建一个组件类。
3、定义组件参数、属性和输入输出。
4、定义组件执行内容。
5、注册组件、将新增组件加入到secretflow.component.entry 的 ALL_COMPONENTS 中。
6、打包隐语镜像(更新组件列表及翻译,打包镜像),注册隐语镜像(更新隐语SecretPad平台组件列表,在Kuscia中注册自定义算法镜像)。
7、在隐语SecretPad平台上使用新组件。
具体参考文档链接:secretpad新增组件:https://www.secretflow.org.cn/zh-CN/docs/secretpad-all-in-one/v1.6.1b0/more_tutorials/new_components