隐私计算实训营:SplitRec:当拆分学习遇上推荐系统

2024-09-07 21:36

本文主要是介绍隐私计算实训营:SplitRec:当拆分学习遇上推荐系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

拆分学习的概念

拆分学习的核心思想是拆分网络结构。每一个参与方拥有模型结构的一部分,所有参与方的模型合在一起形成一个完整的模型。训练过程中,不同参与方只对本地模型进行正向或者反向传播计算,并将计算结果传递给下一个参与方。多个参与方通过联合模型进行训练直至最终收敛。

一个典型的拆分学习例子:

Alice持有数据和基础模型。Bob只有数据、基础模型和fuse模型。

  1. Alice使用自己的数据和基础模型得到 hidden0,然后发送给Bob。
  2. Bob使用自己的数据和基础模型得到 hidden1
  3. Agg Layer使用 hidden_0 和 hidden_1 作为输入,并输出聚合后的隐层。
  4. Bob把聚合后的隐层作为fuse模型的输入,计算得到梯度。
  5. 梯度被拆分成两部分,分别返回给Alice和Bob。
  6. Alice和Bob使用各自收到的梯度更新基础模型。

SplitRec

SplitRec是隐语拆分学习针对跨域推荐场景中的模型训练所提供的一系列优化算法和策略。

在传统推荐场景中,用户的数据通常需要上传到中央服务器进行模型训练。而跨域推荐场景是指联合分布在不同域的数据进行分布式训练的推荐场景。例如一个用户在一个短视频平台看了很多短视频,在另一个电商平台被推荐相关的广告,电商平台除了自有数据外,也希望从短视频平台的数据中挖掘相关的信息。同时出于数据安全考虑,各平台数据不能被上传到中央服务器进行集中式的机器学习训练,这种联合分布在不同域的数据进行模型训练的场景很适合用联邦学习中的拆分学习。

跨域推荐模型将不同域的用户数据联合起来建模,相比传统推荐系统收集到的数据更多更丰富,同时由于数据分布在不同域,在精度、效率和安全性上都对模型的训练提出了很多挑战,主要有以下三点:

  • 模型效果上,例如DeepFM等复杂模型能否直接放到拆分框架中使用?
  • 训练效率上,模型训练中每个 batch 的前反向计算中的通信是否会严重降低训练效率?
  • 安全性上,通信的中间数据是否会造成信息泄露,引起安全性问题?

SplitRec 在效果、效率和安全方面对拆分模型训练做了很多优化。

  • 模型效果上,SplitRec 提供了拆分 DeepFM、BST、MMoe 等模型的封装。
  • 训练效率上,SplitRec 借由隐语拆分学习框架的能力,提供了压缩、流水并行等策略来提升训练效率。
  • 安全性上,SplitRec提供了安全聚合、差分隐私等安全策略。同时也提供了一些针对拆分学习的攻击方法,来验证不同攻击手段对拆分模型的影响,后续也会更新相关防御方法。

实践:在隐语中使用拆分 DeepFM 算法

DeepFM算法结合了FM和神经网络的长处,可以同时提升低维和高维特征,相比Wide&Deep模型还免去了特征工程的部分。

整体上来看。这个模型可以分成两个部分,分别是FM部分以及Deep部分。这两个部分的输入是一样的,并没有像Wide & Deep模型那样做区分。Deep的部分用来训练这些特征的高维的关联,而FM模型会通过隐藏向量V的形式来计算特征之间的二维交叉的信息。

隐语中的DeepFM

拆分的详细过程可以来看这里:

SplitRec:在隐语中使用拆分 DeepFM 算法(Tensorflow 后端) | SecretFlow v1.9.0b1 | 隐语 SecretFlow

环境设置

import secretflow as sf# Check the version of your SecretFlow
print('The version of SecretFlow: {}'.format(sf.__version__))# In case you have a running secretflow runtime already.
sf.shutdown()
sf.init(['alice', 'bob', 'charlie'], address="local", log_to_driver=False)
alice, bob, charlie = sf.PYU('alice'), sf.PYU('bob'), sf.PYU('charlie')

数据集介绍

我们这里将使用最经典的MovieLens数据集来进行演示。 MovieLens是一个开放式的推荐系统数据集,包含了电影评分和电影元数据信息。

我们对数据进行了切分:

- alice: “UserID”, “Gender”, “Age”, “Occupation”, “Zip-code”

- bob: “MovieID”, “Rating”, “Title”, “Genres”, “Timestamp”

下载并处理数据

数据拆分处理

%%capture
%%!
wget https://secretflow-data.oss-accelerate.aliyuncs.com/datasets/movielens/ml-1m.zip
unzip ./ml-1m.zip
# Read the data in dat format and convert it into a dictionary
def load_data(filename, columns):data = {}with open(filename, "r", encoding="unicode_escape") as f:for line in f:ls = line.strip("\n").split("::")data[ls[0]] = dict(zip(columns[1:], ls[1:]))return data
fed_csv = {alice: "alice_ml1m.csv", bob: "bob_ml1m.csv"}
csv_writer_container = {alice: open(fed_csv[alice], "w"), bob: open(fed_csv[bob], "w")}
part_columns = {alice: ["UserID", "Gender", "Age", "Occupation", "Zip-code"],bob: ["MovieID", "Rating", "Title", "Genres", "Timestamp"],
}
for device, writer in csv_writer_container.items():writer.write("ID," + ",".join(part_columns[device]) + "\n")
f = open("ml-1m/ratings.dat", "r", encoding="unicode_escape")users_data = load_data("./ml-1m/users.dat",columns=["UserID", "Gender", "Age", "Occupation", "Zip-code"],
)
movies_data = load_data("./ml-1m/movies.dat", columns=["MovieID", "Title", "Genres"])
ratings_columns = ["UserID", "MovieID", "Rating", "Timestamp"]rating_data = load_data("./ml-1m/ratings.dat", columns=ratings_columns)def _parse_example(feature, columns, index):if "Title" in feature.keys():feature["Title"] = feature["Title"].replace(",", "_")if "Genres" in feature.keys():feature["Genres"] = feature["Genres"].replace("|", " ")values = []values.append(str(index))for c in columns:values.append(feature[c])return ",".join(values)index = 0
num_sample = 1000
for line in f:ls = line.strip().split("::")rating = dict(zip(ratings_columns, ls))rating.update(users_data.get(ls[0]))rating.update(movies_data.get(ls[1]))for device, columns in part_columns.items():parse_f = _parse_example(rating, columns, index)csv_writer_container[device].write(parse_f + "\n")index += 1if num_sample > 0 and index >= num_sample:break
for w in csv_writer_container.values():w.close()

到此就完成了数据的处理和拆分

得到

alice: alice_ml1m.csv

bob: bob_ml1m.csv

! head alice_ml1m.csv
! head bob_ml1m.csv

构造data_builder_dict

# alice
def create_dataset_builder_alice(batch_size=128,repeat_count=5,
):def dataset_builder(x):import pandas as pdimport tensorflow as tfx = [dict(t) if isinstance(t, pd.DataFrame) else t for t in x]x = x[0] if len(x) == 1 else tuple(x)data_set = (tf.data.Dataset.from_tensor_slices(x).batch(batch_size).repeat(repeat_count))return data_setreturn dataset_builder# bob
def create_dataset_builder_bob(batch_size=128,repeat_count=5,
):def _parse_bob(row_sample, label):import tensorflow as tfy_t = label["Rating"]y = tf.expand_dims(tf.where(y_t > 3,tf.ones_like(y_t, dtype=tf.float32),tf.zeros_like(y_t, dtype=tf.float32),),axis=1,)return row_sample, ydef dataset_builder(x):import pandas as pdimport tensorflow as tfx = [dict(t) if isinstance(t, pd.DataFrame) else t for t in x]x = x[0] if len(x) == 1 else tuple(x)data_set = (tf.data.Dataset.from_tensor_slices(x).batch(batch_size).repeat(repeat_count))data_set = data_set.map(_parse_bob)return data_setreturn dataset_builderdata_builder_dict = {alice: create_dataset_builder_alice(batch_size=128,repeat_count=5,),bob: create_dataset_builder_bob(batch_size=128,repeat_count=5,),
}
from secretflow.ml.nn.applications.sl_deep_fm import DeepFMbase, DeepFMfuse
from secretflow.ml.nn import SLModelNUM_USERS = 6040
NUM_MOVIES = 3952
GENDER_VOCAB = ["F", "M"]
AGE_VOCAB = [1, 18, 25, 35, 45, 50, 56]
OCCUPATION_VOCAB = [i for i in range(21)]
GENRES_VOCAB = ["Action","Adventure","Animation","Children's","Comedy","Crime","Documentary","Drama","Fantasy","Film-Noir","Horror","Musical","Mystery","Romance","Sci-Fi","Thriller","War","Western",
]

DeepFMBase有4个参数:

-dnn_units_size: 这个参数需要提供一个list来对dnn部分进行定义,比如[256,32]意思是中间两个隐层分别是256,和32

-dnn_activation: dnn 的激活函数,eg:relu

-preprocess_layer: 需要对输入进行处理,传入一个定义好的keras.preprocesslayer

-fm_embedding_dim: fm vector的维度是多少

# Define alice's basenet
def create_base_model_alice():# Create modeldef create_model():import tensorflow as tfdef preprocess():inputs = {"UserID": tf.keras.Input(shape=(1,), dtype=tf.string),"Gender": tf.keras.Input(shape=(1,), dtype=tf.string),"Age": tf.keras.Input(shape=(1,), dtype=tf.int64),"Occupation": tf.keras.Input(shape=(1,), dtype=tf.int64),}user_id_output = tf.keras.layers.Hashing(num_bins=NUM_USERS, output_mode="one_hot")user_gender_output = tf.keras.layers.StringLookup(vocabulary=GENDER_VOCAB, output_mode="one_hot")user_age_out = tf.keras.layers.IntegerLookup(vocabulary=AGE_VOCAB, output_mode="one_hot")user_occupation_out = tf.keras.layers.IntegerLookup(vocabulary=OCCUPATION_VOCAB, output_mode="one_hot")outputs = {"UserID": user_id_output(inputs["UserID"]),"Gender": user_gender_output(inputs["Gender"]),"Age": user_age_out(inputs["Age"]),"Occupation": user_occupation_out(inputs["Occupation"]),}return tf.keras.Model(inputs=inputs, outputs=outputs)preprocess_layer = preprocess()model = DeepFMbase(dnn_units_size=[256, 32],preprocess_layer=preprocess_layer,)model.compile(loss=tf.keras.losses.binary_crossentropy,optimizer=tf.keras.optimizers.Adam(),metrics=[tf.keras.metrics.AUC(),tf.keras.metrics.Precision(),tf.keras.metrics.Recall(),],)return model  # need wrapreturn create_model
# Define bob's basenet
def create_base_model_bob():# Create modeldef create_model():import tensorflow as tf# define preprocess layerdef preprocess():inputs = {"MovieID": tf.keras.Input(shape=(1,), dtype=tf.string),"Genres": tf.keras.Input(shape=(1,), dtype=tf.string),}movie_id_out = tf.keras.layers.Hashing(num_bins=NUM_MOVIES, output_mode="one_hot")movie_genres_out = tf.keras.layers.TextVectorization(output_mode='multi_hot', split="whitespace", vocabulary=GENRES_VOCAB)outputs = {"MovieID": movie_id_out(inputs["MovieID"]),"Genres": movie_genres_out(inputs["Genres"]),}return tf.keras.Model(inputs=inputs, outputs=outputs)preprocess_layer = preprocess()model = DeepFMbase(dnn_units_size=[256, 32],preprocess_layer=preprocess_layer,)model.compile(loss=tf.keras.losses.binary_crossentropy,optimizer=tf.keras.optimizers.Adam(),metrics=[tf.keras.metrics.AUC(),tf.keras.metrics.Precision(),tf.keras.metrics.Recall(),],)return model  # need wrapreturn create_model

定义Fusenet

def create_fuse_model():# Create modeldef create_model():import tensorflow as tfmodel = DeepFMfuse(dnn_units_size=[256, 256, 32])model.compile(loss=tf.keras.losses.binary_crossentropy,optimizer=tf.keras.optimizers.Adam(),metrics=[tf.keras.metrics.AUC(),tf.keras.metrics.Precision(),tf.keras.metrics.Recall(),],)return modelreturn create_model
base_model_dict = {alice: create_base_model_alice(), bob: create_base_model_bob()}
model_fuse = create_fuse_model()
from secretflow.data.vertical import read_csv as v_read_csvvdf = v_read_csv({alice: "alice_ml1m.csv", bob: "bob_ml1m.csv"}, keys="ID", drop_keys="ID"
)
label = vdf["Rating"]data = vdf.drop(columns=["Rating", "Timestamp", "Title", "Zip-code"])
data["UserID"] = data["UserID"].astype("string")
data["MovieID"] = data["MovieID"].astype("string")sl_model = SLModel(base_model_dict=base_model_dict,device_y=bob,model_fuse=model_fuse,
)
history = sl_model.fit(data,label,epochs=5,batch_size=128,random_seed=1234,dataset_builder=data_builder_dict,
)

到这里,我们已经使用隐语提供的deepfm封装完成了movieLens数据集上的推荐任务训练。

总结

我们通过movieLens数据集上的推荐任务来演示了如何通过隐语来实现DeepFM。

1.下载并拆分数据集;

2.定义好数据处理的dataloader;

3.定义好数据预处理的preprocesslayer,定义好dnn结构,调用DeepFMBase,DeepFMFuse来进行模型定义;

4.使用SLModel进行训练,预测,评估即可。

这篇关于隐私计算实训营:SplitRec:当拆分学习遇上推荐系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1146224

相关文章

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

MySQL 存储引擎 MyISAM详解(最新推荐)

《MySQL存储引擎MyISAM详解(最新推荐)》使用MyISAM存储引擎的表占用空间很小,但是由于使用表级锁定,所以限制了读/写操作的性能,通常用于中小型的Web应用和数据仓库配置中的只读或主要... 目录mysql 5.5 之前默认的存储引擎️‍一、MyISAM 存储引擎的特性️‍二、MyISAM 的主

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

ubuntu20.0.4系统中安装Anaconda的超详细图文教程

《ubuntu20.0.4系统中安装Anaconda的超详细图文教程》:本文主要介绍了在Ubuntu系统中如何下载和安装Anaconda,提供了两种方法,详细内容请阅读本文,希望能对你有所帮助... 本文介绍了在Ubuntu系统中如何下载和安装Anaconda。提供了两种方法,包括通过网页手动下载和使用wg

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示