From c4c2562e8bfd2379b748da592e715213701542cb Mon Sep 17 00:00:00 2001 From: Wang Bochao Date: Thu, 24 Dec 2020 10:27:13 +0800 Subject: [PATCH] add ldp_linucb into model zoo --- model_zoo/research/rl/ldp_linucb/README.md | 132 ++++++++++++++++ model_zoo/research/rl/ldp_linucb/result1.png | Bin 0 -> 37204 bytes model_zoo/research/rl/ldp_linucb/result2.png | Bin 0 -> 37565 bytes .../rl/ldp_linucb/scripts/run_train_eval.sh | 17 +++ .../research/rl/ldp_linucb/src/dataset.py | 99 ++++++++++++ .../research/rl/ldp_linucb/src/linucb.py | 143 ++++++++++++++++++ .../research/rl/ldp_linucb/train_eval.py | 89 +++++++++++ 7 files changed, 480 insertions(+) create mode 100644 model_zoo/research/rl/ldp_linucb/README.md create mode 100644 model_zoo/research/rl/ldp_linucb/result1.png create mode 100644 model_zoo/research/rl/ldp_linucb/result2.png create mode 100644 model_zoo/research/rl/ldp_linucb/scripts/run_train_eval.sh create mode 100644 model_zoo/research/rl/ldp_linucb/src/dataset.py create mode 100644 model_zoo/research/rl/ldp_linucb/src/linucb.py create mode 100644 model_zoo/research/rl/ldp_linucb/train_eval.py diff --git a/model_zoo/research/rl/ldp_linucb/README.md b/model_zoo/research/rl/ldp_linucb/README.md new file mode 100644 index 00000000000..09150201e50 --- /dev/null +++ b/model_zoo/research/rl/ldp_linucb/README.md @@ -0,0 +1,132 @@ +# Contents + +- [LDP LinUCB Description](#ldp-linucb-description) +- [Model Architecture](#model-architecture) +- [Dataset](#dataset) +- [Environment Requirements](#environment-requirements) +- [Script Description](#script-description) + - [Script and Sample Code](#script-and-sample-code) + - [Script Parameters](#script-parameters) + - [Launch](#launch) +- [Model Description](#model-description) + - [Performance](#performance) +- [Description of Random Situation](#description-of-random-situation) +- [ModelZoo Homepage](#modelzoo-homepage) + +# [LDP LinUCB](#contents) + +Locally Differentially Private (LDP) LinUCB is a variant of LinUCB bandit algorithm with local differential privacy guarantee, which can preserve users' personal data with theoretical guarantee. + +[Paper](https://arxiv.org/abs/2006.00701): Kai Zheng, Tianle Cai, Weiran Huang, Zhenguo Li, Liwei Wang. "Locally Differentially Private (Contextual) Bandits Learning." *Advances in Neural Information Processing Systems*. 2020. + +# [Model Architecture](#contents) + +The server interacts with users in rounds. For a coming user, the server first transfers the current model parameters to the user. In the user side, the model chooses an action based on the user feature to play (e.g., choose a movie to recommend), and observes a reward (or loss) value from the user (e.g., rating of the movie). Then we perturb the data to be transfered by adding Gaussian noise. Finally, the server receives the perturbed data and updates the model. Details can be found in the [original paper](https://arxiv.org/abs/2006.00701). + +# [Dataset](#contents) + +Note that you can run the scripts based on the dataset mentioned in original paper. In the following sections, we will introduce how to run the scripts using the related dataset below. + +Dataset used: [MovieLens 100K](https://grouplens.org/datasets/movielens/100k/) + +- Dataset size:5MB, 100,000 ratings (1-5) from 943 users on 1682 movies. +- Data format:csv/txt files + +# [Environment Requirements](#contents) + +- Hardware (Ascend/GPU) + - Prepare hardware environment with Ascend or GPU processor. If you want to try Ascend, please send the[application form](https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/file/other/Ascend%20Model%20Zoo%E4%BD%93%E9%AA%8C%E8%B5%84%E6%BA%90%E7%94%B3%E8%AF%B7%E8%A1%A8.docx) to ascend@huawei.com. Once approved, you can get the resources. +- Framework + - [MindSpore](https://www.mindspore.cn/install/en) +- For more information, please check the resources below: + - [MindSpore Tutorials](https://www.mindspore.cn/tutorial/training/en/master/index.html) + - [MindSpore Python API](https://www.mindspore.cn/doc/api_python/en/master/index.html) + +# [Script Description](#contents) + +## [Script and Sample Code](#contents) + +```console +├── model_zoo + ├── README.md // descriptions about all the models + ├── research + ├── rl + ├── ldp_linucb + ├── README.md // descriptions about LDP LinUCB + ├── scripts + │ ├── run_train_eval.sh // shell script for runing on Ascend + ├── src + │ ├── dataset.py // dataset for movielens + │ ├── linucb.py // model + ├── train_eval.py // training script + ├── result1.png // experimental result + ├── result2.png // experimental result +``` + +## [Script Parameters](#contents) + +- Parameters for preparing MovieLens 100K dataset + + ```python + 'num_actions': 20 # number of candidate movies to be recommended + 'rank_k': 20 # rank of rating matrix completion + ``` + +- Parameters for LDP LinUCB, MovieLens 100K dataset + + ```python + 'epsilon': 8e5 # privacy parameter + 'delta': 0.1 # privacy parameter + 'alpha': 0.1 # failure probability + 'iter_num': 1e6 # number of iterations + ``` + +## [Launch](#contents) + +- running on Ascend + + ```shell + python train_eval.py > result.log 2>&1 & + ``` + +The python command above will run in the background, you can view the results through the file `result.log`. + +The regret value will be achieved as follows: + +```console +--> Step: 0, diff: 348.662, current_regret: 0.000, cumulative regret: 0.000 +--> Step: 1, diff: 338.457, current_regret: 0.000, cumulative regret: 0.000 +--> Step: 2, diff: 336.465, current_regret: 2.000, cumulative regret: 2.000 +--> Step: 3, diff: 327.337, current_regret: 0.000, cumulative regret: 2.000 +--> Step: 4, diff: 325.039, current_regret: 2.000, cumulative regret: 4.000 +... +``` + +# [Model Description](#contents) + +The [original paper](https://arxiv.org/abs/2006.00701) assumes that the norm of user features is bounded by 1 and the norm of rating scores is bounded by 2. For the MovieLens dataset, we normalize rating scores to [-1,1]. Thus, we set `sigma` in Algorithm 5 to be $$4/epsilon \* sqrt(2 \* ln(1.25/delta))$$. + +## [Performance](#contents) + +The performance for different privacy parameters: + +- x: number of iterations +- y: cumulative regret + +![Result1](result1.png) + +The performance compared with optimal non-private regret O(sqrt(T)): + +- x: number of iterations +- y: cumulative regret divided by sqrt(T) + +![Result2](result2.png) + +# [Description of Random Situation](#contents) + +In `train_eval.py`, we randomly sample a user at each round. We also add Gaussian noise to the date being transfered. + +# [ModelZoo Homepage](#contents) + +Please check the official +[homepage](https://gitee.com/mindspore/mindspore/tree/master/model_zoo). diff --git a/model_zoo/research/rl/ldp_linucb/result1.png b/model_zoo/research/rl/ldp_linucb/result1.png new file mode 100644 index 0000000000000000000000000000000000000000..3e6147468e49d1e11d4679fb7475ad8613a9aa2b GIT binary patch literal 37204 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 z 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 result.log 2>&1 & diff --git a/model_zoo/research/rl/ldp_linucb/src/dataset.py b/model_zoo/research/rl/ldp_linucb/src/dataset.py new file mode 100644 index 00000000000..02c04e1878e --- /dev/null +++ b/model_zoo/research/rl/ldp_linucb/src/dataset.py @@ -0,0 +1,99 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +MovieLens Environment. +""" + +import random +import numpy as np +from mindspore import Tensor + +_MAX_NUM_ACTIONS = 1682 +_NUM_USERS = 943 + + +def load_movielens_data(data_file): + """Loads the movielens data and returns the ratings matrix.""" + ratings_matrix = np.zeros([_NUM_USERS, _MAX_NUM_ACTIONS]) + with open(data_file, 'r') as f: + for line in f.readlines(): + row_infos = line.strip().split() + user_id = int(row_infos[0]) + item_id = int(row_infos[1]) + rating = float(row_infos[2]) + ratings_matrix[user_id - 1, item_id - 1] = rating + return ratings_matrix + + +class MovieLensEnv: + """ + MovieLens dataset environment for bandit algorithms. + + Args: + data_file(str): path of movielens file, e.g. 'ua.base'. + num_movies(int): number of movies for choices. + rank_k(int): the dim of feature. + + Returns: + Environment for bandit algorithms. + """ + def __init__(self, data_file, num_movies, rank_k): + # Initialization + self._num_actions = num_movies + self._context_dim = rank_k + + # Load Movielens dataset + self._data_matrix = load_movielens_data(data_file) + # Keep only the first items + self._data_matrix = self._data_matrix[:, :num_movies] + # Filter the users with at least one rating score + nonzero_users = list( + np.nonzero( + np.sum( + self._data_matrix, + axis=1) > 0.0)[0]) + self._data_matrix = self._data_matrix[nonzero_users, :] + # Normalize the data_matrix into -1~1 + self._data_matrix = 0.4 * (self._data_matrix - 2.5) + + # Compute the SVD # Only keep the largest rank_k singular values + u, s, vh = np.linalg.svd(self._data_matrix, full_matrices=False) + u_hat = u[:, :rank_k] * np.sqrt(s[:rank_k]) + v_hat = np.transpose(np.transpose( + vh[:rank_k, :]) * np.sqrt(s[:rank_k])) + self._approx_ratings_matrix = np.matmul( + u_hat, v_hat).astype(np.float32) + + # Prepare feature for user i and item j: u[i,:] * vh[:,j] + # (elementwise product of user feature and item feature) + self._ground_truth = s + self._current_user = 0 + self._feature = np.expand_dims(u[:, :rank_k], axis=1) * \ + np.expand_dims(np.transpose(vh[:rank_k, :]), axis=0) + self._feature = self._feature.astype(np.float32) + + @property + def ground_truth(self): + return self._ground_truth + + def observation(self): + """random select a user and return its feature.""" + sampled_user = random.randint(0, self._data_matrix.shape[0] - 1) + self._current_user = sampled_user + return Tensor(self._feature[sampled_user]) + + def current_rewards(self): + """rewards for current user.""" + return Tensor(self._approx_ratings_matrix[self._current_user]) diff --git a/model_zoo/research/rl/ldp_linucb/src/linucb.py b/model_zoo/research/rl/ldp_linucb/src/linucb.py new file mode 100644 index 00000000000..01bd805d869 --- /dev/null +++ b/model_zoo/research/rl/ldp_linucb/src/linucb.py @@ -0,0 +1,143 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +Linear UCB with locally differentially private. +""" + +import math +import numpy as np + +import mindspore +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.ops import operations as P + + +class LinUCB(nn.Cell): + """ + Linear UCB with locally differentially private bandits learning. + + Args: + context_dim(int): dim of input feature. + epsilon(float): epsilon for private parameter. + delta(float): delta for private parameter. + alpha(float): failure probability. + T(float/int): number of iterations. + + Returns: + Tuple of Tensors: gradients to update parameters and optimal action. + """ + def __init__(self, context_dim, epsilon=100, delta=0.1, alpha=0.1, T=1e5): + super(LinUCB, self).__init__() + self.matmul = P.MatMul() + self.expand_dims = P.ExpandDims() + self.transpose = P.Transpose() + self.reduce_sum = P.ReduceSum() + self.squeeze = P.Squeeze(1) + self.argmax = P.Argmax() + self.reduce_max = P.ReduceMax() + + # Basic variables + self._context_dim = context_dim + self._epsilon = epsilon + self._delta = delta + self._alpha = alpha + self._T = int(T) + + # Parameters + self._V = Tensor( + np.zeros( + (context_dim, + context_dim), + dtype=np.float32)) + self._u = Tensor(np.zeros((context_dim,), dtype=np.float32)) + self._theta = Tensor(np.zeros((context_dim,), dtype=np.float32)) + + # \sigma = 4*\sqrt{2*\ln{\farc{1.25}{\delta}}}/\epsilon + self._sigma = 4 * \ + math.sqrt(math.log(1.25 / self._delta)) / self._epsilon + self._c = 0.1 + self._step = 1 + self._regret = 0 + self._current_regret = 0 + self.inverse_matrix() + + @property + def theta(self): + return self._theta + + @property + def regret(self): + return self._regret + + @property + def current_regret(self): + return self._current_regret + + def inverse_matrix(self): + """compute the inverse matrix of parameter matrix.""" + Vc = self._V + Tensor(np.eye(self._context_dim, + dtype=np.float32)) * self._c + self._Vc_inv = Tensor(np.linalg.inv(Vc.asnumpy()), mindspore.float32) + + def update_status(self, step): + """update status variables.""" + t = max(step, 1) + T = self._T + d = self._context_dim + alpha = self._alpha + sigma = self._sigma + + gamma = sigma * \ + math.sqrt(t) * (4 * math.sqrt(d) + 2 * math.log(2 * T / alpha)) + self._c = 2 * gamma + self._beta = 2 * sigma * math.sqrt(d * math.log(T)) + (math.sqrt( + 3 * gamma) + sigma * math.sqrt(d * t / gamma)) * d * math.log(T) + + def construct(self, x, rewards): + """compute the perturbed gradients for parameters.""" + # Choose optimal action + x_transpose = self.transpose(x, (1, 0)) + scores_a = self.squeeze(self.matmul(x, self.expand_dims(self._theta, 1))) + scores_b = x_transpose * self.matmul(self._Vc_inv, x_transpose) + scores_b = self.reduce_sum(scores_b, 0) + scores = scores_a + self._beta * scores_b + max_a = self.argmax(scores) + xa = x[max_a] + xaxat = self.matmul(self.expand_dims(xa, -1), self.expand_dims(xa, 0)) + y = rewards[max_a] + y_max = self.reduce_max(rewards) + y_diff = y_max - y + self._current_regret = float(y_diff.asnumpy()) + self._regret += self._current_regret + + # Prepare noise + B = np.random.normal(0, self._sigma, size=xaxat.shape) + B = np.triu(B) + B += B.transpose() - np.diag(B.diagonal()) + B = Tensor(B.astype(np.float32)) + Xi = np.random.normal(0, self._sigma, size=xa.shape) + Xi = Tensor(Xi.astype(np.float32)) + + # Add noise and update parameters + return xaxat + B, xa * y + Xi, max_a + + def server_update(self, xaxat, xay): + """update parameters with perturbed gradients.""" + self._V += xaxat + self._u += xay + self.inverse_matrix() + theta = self.matmul(self._Vc_inv, self.expand_dims(self._u, 1)) + self._theta = self.squeeze(theta) diff --git a/model_zoo/research/rl/ldp_linucb/train_eval.py b/model_zoo/research/rl/ldp_linucb/train_eval.py new file mode 100644 index 00000000000..ba4aed99a45 --- /dev/null +++ b/model_zoo/research/rl/ldp_linucb/train_eval.py @@ -0,0 +1,89 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +train/eval. +""" + +import argparse +import time +import numpy as np +import matplotlib.pyplot as plt + +from src.dataset import MovieLensEnv +from src.linucb import LinUCB + + +def parse_args(): + """parse args""" + parser = argparse.ArgumentParser() + parser.add_argument('--data_file', type=str, default='ua.base', + help='data file for movielens') + parser.add_argument('--rank_k', type=int, default=20, + help='rank for data matrix') + parser.add_argument('--num_actions', type=int, default=20, + help='movie number for choices') + parser.add_argument('--epsilon', type=float, default=8e5, + help='epsilon for differentially private') + parser.add_argument('--delta', type=float, default=1e-1, + help='delta for differentially private') + parser.add_argument('--alpha', type=float, default=1e-1, + help='failure probability') + parser.add_argument('--iter_num', type=float, default=1e6, + help='iteration number for training') + + args_opt = parser.parse_args() + return args_opt + + +if __name__ == '__main__': + # build environment + args = parse_args() + env = MovieLensEnv(args.data_file, args.num_actions, args.rank_k) + + # Linear UCB + lin_ucb = LinUCB( + args.rank_k, + epsilon=args.epsilon, + delta=args.delta, + alpha=args.alpha, + T=args.iter_num) + + print('start') + start_time = time.time() + cumulative_regrets = [] + for i in range(int(args.iter_num)): + x = env.observation() + rewards = env.current_rewards() + lin_ucb.update_status(i + 1) + xaxat, xay, max_a = lin_ucb(x, rewards) + cumulative_regrets.append(float(lin_ucb.regret)) + lin_ucb.server_update(xaxat, xay) + diff = np.abs(lin_ucb.theta.asnumpy() - env.ground_truth).sum() + print( + f'--> Step: {i}, diff: {diff:.3f},' + f'current_regret: {lin_ucb.current_regret:.3f},' + f'cumulative regret: {lin_ucb.regret:.3f}') + end_time = time.time() + print(f'Regret: {lin_ucb.regret}, cost time: {end_time-start_time:.3f}s') + print(f'theta: {lin_ucb.theta.asnumpy()}') + print(f' gt: {env.ground_truth}') + + np.save(f'e_{args.epsilon:.1e}.npy', cumulative_regrets) + plt.plot( + range(len(cumulative_regrets)), + cumulative_regrets, + label=f'epsilon={args.epsilon:.1e}') + plt.legend() + plt.savefig(f'regret_{args.epsilon:.1e}.png')