def run_test(train_loader, test_loader, interim): class Net(nn.Module): def __init__(self): super(Net, self).__init__() # Testing several different activations self.conv_layers = nn.Sequential( nn.Conv2d(1, 10, kernel_size=5), nn.MaxPool2d(2), nn.Tanh(), nn.Conv2d(10, 20, kernel_size=5), nn.MaxPool2d(2), nn.Softplus(), ) self.fc_layers = nn.Sequential( nn.Linear(320, 50), nn.ReLU(), nn.Linear(50, 10), nn.ELU(), nn.Softmax(dim=1) ) def forward(self, x): x = self.conv_layers(x) x = x.view(-1, 320) x = self.fc_layers(x) return x model = Net() optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) def train(model, device, train_loader, optimizer, epoch, cutoff=2000): model.train() num_examples = 0 for batch_idx, (data, target) in enumerate(train_loader): num_examples += target.shape[0] data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = F.mse_loss(output, torch.eye(10)[target]) # loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % 10 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) if num_examples > cutoff: break device = torch.device('cpu') train(model, device, train_loader, optimizer, 1) next_x, next_y = next(iter(train_loader)) np.random.seed(0) inds = np.random.choice(next_x.shape[0], 20, replace=False) if interim: e = shap.DeepExplainer((model, model.conv_layers[0]), next_x[inds, :, :, :]) else: e = shap.DeepExplainer(model, next_x[inds, :, :, :]) test_x, test_y = next(iter(test_loader)) shap_values = e.shap_values(test_x[:1]) model.eval() model.zero_grad() with torch.no_grad(): diff = (model(test_x[:1]) - model(next_x[inds, :, :, :])).detach().numpy().mean(0) sums = np.array([shap_values[i].sum() for i in range(len(shap_values))]) d = np.abs(sums - diff).sum() assert d / np.abs(diff).sum() < 0.001, "Sum of SHAP values does not match difference! %f" % ( d / np.abs(diff).sum())
def test_shap(self): print(Colours.yellow("--- CNN shap---")) source_data = unserialize_pickle(self.data_source_dump) stat_dict = unserialize_json(self.stat_file) data_flow = unserialize_numpy(self.flow_file) t_s_dict = unserialize_json(self.t_s_dict_file) model_data = DataModel(source_data, data_flow, t_s_dict, stat_dict) model_dict = model_data.data_source.data_config.model_dict n_input = int(14) try_cnn = TryCnn() data, targets = routing_cnn.to_supervised( model_data.load_data(model_dict), n_input) try_cnn.load_state_dict( torch.load("F:\科研类\codes\hydro-routing-cnn\checkpoint.pt")) # x1 = data.reshape(715, 1, -1) # x = x1.reshape(-1, 1, 1, x1.shape[2]) x = data.reshape(-1, 1, data.shape[1], data.shape[2]) x = torch.from_numpy(x).float() try_cnn.eval() # x_pred = try_cnn(x[301:306]) # print(x[301:306]) # print(x_pred) print("======计算SHAP========") # 新建一个解释器(模型,数据) background = x[np.random.choice(x.shape[0], 100, replace=False)] e = shap.DeepExplainer(try_cnn, background) # e = shap.DeepExplainer(try_cnn, x) shap_values = e.shap_values(x) shap_values_stations_days = np.abs(shap_values).sum(axis=0).reshape( 14, data.shape[2]) shap_days = shap_values_stations_days.sum(axis=1) shap_stations = shap_values_stations_days.sum(axis=0) # 计算base_line # y_base = e.expected_value # print("y_base的值:", y_base) # print("y_base+shap值的和:",y_base+shap_values.sum()) shap_values_array = shap_values.reshape(-1, data.shape[1] * data.shape[2]) shap_arrays_values = [] for i in range(shap_values_array.shape[0]): new_array = np.zeros( (shap_values_array.shape[0] - 1) * data.shape[2]) if i == 0: ndarray = np.append(shap_values_array[i], new_array) elif i == shap_values_array.shape[0] - 1: ndarray = np.insert(shap_values_array[i], 0, new_array) else: ndarray = np.pad( shap_values_array[i], (i * data.shape[2], (shap_values_array.shape[0] - 1 - i) * data.shape[2]), 'constant') shap_arrays_values.append(ndarray) shap_arrays_values = np.array(shap_arrays_values) shap_arrays_values_abs = np.abs(shap_arrays_values).sum( axis=0).reshape(-1, data.shape[2]) print(shap_arrays_values_abs) shap_values_days_stations = [] for j in range(shap_arrays_values_abs.shape[0]): if j < 14: shap_values_day_state = shap_arrays_values_abs[j] / (j + 1) elif j >= shap_arrays_values_abs.shape[0] - 14: shap_values_day_state = shap_arrays_values_abs[j] / ( shap_arrays_values_abs.shape[0] - j) else: shap_values_day_state = shap_arrays_values_abs[j] / 14 shap_values_days_stations.append(shap_values_day_state) shap_values_days_stations = np.array(shap_values_days_stations) print(shap_values_days_stations) serialize_numpy(shap_values_days_stations, self.shap_values_days_states_file) serialize_numpy(shap_days, self.shap_days_file) serialize_numpy(shap_stations, self.shap_stations_file) serialize_numpy(shap_values_stations_days, self.shap_values_stations_days_file)
model.compile(optimizer=tf.keras.optimizers.Adam(),loss=tf.keras.losses.BinaryCrossentropy(from_logits=False)) ##################################################################### ##################################################################### ##################################################################### batch_size=64 model.load_weights("model/alpha_model_weights.hdf5") from get_spectrum_as_numpy import get_spectrum import shap spectrum = get_spectrum('example/example.mgf') background_spectra = np.zeros((1,3600,2)) e = shap.DeepExplainer(model, background_spectra) take_specific_spectrum=0 spectrum=spectrum[take_specific_spectrum,:,:] spectrum=np.expand_dims(spectrum,0) interpretation = np.squeeze(e.shap_values(spectrum)) spectrum = np.squeeze(spectrum) import matplotlib.pyplot as plt plt.title("Mirrorplot: demonstrating how SHAP values are used to interpret AHLF.") plt.stem(spectrum[:,0]/np.max(spectrum[:,0]),linefmt='C0-',markerfmt=' ',basefmt=' ',use_line_collection=True,label='acquired peak') plt.stem(- np.abs(interpretation[:,0])/np.max(np.abs(interpretation[:,0])),linefmt='C1-',markerfmt=' ',basefmt=' ',use_line_collection=True,label='abs. SHAP value') plt.xlabel('feature') plt.ylabel('Norm. abundance [a.u.]')
def deep_explain_model_summary_plot( model, datetime_start: datetime = datetime(2014, 6, 1, 0), test_csv_path: str = None, forecast_total: int = 336, dataset_params: Dict = {}, ): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") num_features = len(model.params["dataset_params"]["relevant_cols"]) # If the test dataframe is none use default one supplied in params if test_csv_path is None: csv_test_loader = model.test_data else: csv_test_loader = CSVTestLoader( test_csv_path, forecast_total, **dataset_params, interpolate=dataset_params["interpolate_param"], ) background_data = csv_test_loader.original_df background_batches = csv_test_loader.convert_real_batches( csv_test_loader.df.columns, background_data) background_tensor = (torch.stack( random.sample(background_batches, BACKGROUND_SAMPLE_SIZE)).float().to(device)) model.model.eval() # background shape (L, N, M) # L - batch size, N - history length, M - feature size deep_explainer = shap.DeepExplainer(model.model, background_tensor) shap_values = deep_explainer.shap_values(background_tensor) # summary plot shows overall feature ranking # by average absolute shap values mean_shap_values = np.concatenate(shap_values).mean(axis=0) plt.figure(figsize=(12, 8)) shap.summary_plot( mean_shap_values, feature_names=csv_test_loader.df.columns, plot_type="bar", show=False, max_display=10, plot_size=(10, num_features * 2), ) plt.savefig("overall_feature_ranking_by_shap_values.png") wandb.log({ "Overall feature ranking by shap values": wandb.Image("overall_feature_ranking_by_shap_values.png") }) plt.close() # summary plot for multi-step outputs multi_shap_values = list(np.stack(shap_values).mean(axis=1)) shap.summary_plot( multi_shap_values, feature_names=csv_test_loader.df.columns, class_names=[ f"time-step-{t}" for t in range(model.model.forecast_length) ], max_display=10, # max number of features to display show=False, plot_size=(10, num_features * 2), sort=False, ) plt.savefig("overall_feature_rank_per_time_step.png") wandb.log({ "Overall feature ranking per prediction time-step": wandb.Image("overall_feature_rank_per_time_step.png") }) plt.close() # summary plot for one prediction at datetime_start history, _, forecast_start_idx = csv_test_loader.get_from_start_date( datetime_start) to_explain = history.to(device).unsqueeze(0) shap_values = deep_explainer.shap_values(to_explain) mean_shap_values = np.concatenate(shap_values).mean(axis=0) shap.summary_plot( mean_shap_values, history.cpu().numpy(), feature_names=csv_test_loader.df.columns, plot_type="dot", max_display=10, plot_size=(9, num_features * 2), show=False, ) plt.savefig("feature_ranking_for_prediction_at_timestamp.png") wandb.log({ "Feature ranking for prediction at " f"{datetime_start.strftime('%Y-%m-%d')}": wandb.Image("feature_ranking_for_prediction_at_timestamp.png") }) plt.close()
def _run_pytorch_multiple_inputs_test(disconnected): """Testing multiple inputs """ import torch from torch import nn from torch.nn import functional as F from torch.utils.data import TensorDataset, DataLoader from sklearn.datasets import load_boston import shap X, y = load_boston(return_X_y=True) num_features = X.shape[1] x1 = X[:, num_features // 2:] x2 = X[:, :num_features // 2] data = TensorDataset(torch.tensor(x1).float(), torch.tensor(x2).float(), torch.tensor(y).float()) loader = DataLoader(data, batch_size=128) class Net(nn.Module): def __init__(self, num_features, disconnected): super(Net, self).__init__() self.disconnected = disconnected if disconnected: num_features = num_features // 2 + 1 self.linear = nn.Linear(num_features, 2) self.output = nn.Sequential( nn.MaxPool1d(2), nn.ReLU() ) def forward(self, x1, x2): if self.disconnected: x = self.linear(x1).unsqueeze(1) else: x = self.linear(torch.cat((x1, x2), dim=-1)).unsqueeze(1) return self.output(x).squeeze(1) model = Net(num_features, disconnected) optimizer = torch.optim.Adam(model.parameters()) def train(model, device, train_loader, optimizer, epoch): model.train() num_examples = 0 for batch_idx, (data1, data2, target) in enumerate(train_loader): num_examples += target.shape[0] data1, data2, target = data1.to(device), data2.to(device), target.to(device) optimizer.zero_grad() output = model(data1, data2) loss = F.mse_loss(output.squeeze(1), target) loss.backward() optimizer.step() if batch_idx % 2 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) device = torch.device('cpu') train(model, device, loader, optimizer, 1) next_x1, next_x2, next_y = next(iter(loader)) np.random.seed(0) inds = np.random.choice(next_x1.shape[0], 20, replace=False) background = [next_x1[inds, :], next_x2[inds, :]] e = shap.DeepExplainer(model, background) test_x1, test_x2, test_y = next(iter(loader)) shap_x1, shap_x2 = e.shap_values([test_x1[:1], test_x2[:1]]) model.eval() model.zero_grad() with torch.no_grad(): diff = (model(test_x1[:1], test_x2[:1]) - model(*background)).detach().numpy().mean(0) sums = np.array([shap_x1[i].sum() + shap_x2[i].sum() for i in range(len(shap_x1))]) d = np.abs(sums - diff).sum() assert d / np.abs(diff).sum() < 0.001, "Sum of SHAP values does not match difference! %f" % ( d / np.abs(diff).sum())
def __init__(self, model, initialization_examples, explain_subset=None, nclusters=10, features=None, classes=None, transformations=None, allow_all_transformations=False, model_task=ModelTask.Unknown, is_classifier=None, **kwargs): """Initialize the DeepExplainer. :param model: The DNN model to explain. :type model: PyTorch or TensorFlow model :param initialization_examples: A matrix of feature vector examples (# examples x # features) for initializing the explainer. :type initialization_examples: numpy.array or pandas.DataFrame or iml.datatypes.DenseData or scipy.sparse.csr_matrix :param explain_subset: List of feature indices. If specified, only selects a subset of the features in the evaluation dataset for explanation. The subset can be the top-k features from the model summary. :type explain_subset: list[int] :param nclusters: Number of means to use for approximation. A dataset is summarized with nclusters mean samples weighted by the number of data points they each represent. When the number of initialization examples is larger than (10 x nclusters), those examples will be summarized with k-means where k = nclusters. :type nclusters: int :param features: A list of feature names. :type features: list[str] :param classes: Class names as a list of strings. The order of the class names should match that of the model output. Only required if explaining classifier. :type classes: list[str] :param transformations: sklearn.compose.ColumnTransformer or a list of tuples describing the column name and transformer. When transformations are provided, explanations are of the features before the transformation. The format for a list of transformations is same as the one here: https://github.com/scikit-learn-contrib/sklearn-pandas. If you are using a transformation that is not in the list of sklearn.preprocessing transformations that are supported by the `interpret-community <https://github.com/interpretml/interpret-community>`_ package, then this parameter cannot take a list of more than one column as input for the transformation. You can use the following sklearn.preprocessing transformations with a list of columns since these are already one to many or one to one: Binarizer, KBinsDiscretizer, KernelCenterer, LabelEncoder, MaxAbsScaler, MinMaxScaler, Normalizer, OneHotEncoder, OrdinalEncoder, PowerTransformer, QuantileTransformer, RobustScaler, StandardScaler. Examples for transformations that work:: [ (["col1", "col2"], sklearn_one_hot_encoder), (["col3"], None) #col3 passes as is ] [ (["col1"], my_own_transformer), (["col2"], my_own_transformer), ] An example of a transformation that would raise an error since it cannot be interpreted as one to many:: [ (["col1", "col2"], my_own_transformer) ] The last example would not work since the interpret-community package can't determine whether my_own_transformer gives a many to many or one to many mapping when taking a sequence of columns. :type transformations: sklearn.compose.ColumnTransformer or list[tuple] :param allow_all_transformations: Allow many to many and many to one transformations :type allow_all_transformations: bool :param is_classifier: Optional parameter to specify whether the model is a classification or regression model. In most cases, the type of the model can be inferred based on the shape of the output, where a classifier has a predict_proba method and outputs a 2 dimensional array, while a regressor has a predict method and outputs a 1 dimensional array. :type is_classifier: bool :param model_task: Optional parameter to specify whether the model is a classification or regression model. :type model_task: str """ self._datamapper = None if transformations is not None: self._datamapper, initialization_examples = get_datamapper_and_transformed_data( examples=initialization_examples, transformations=transformations, allow_all_transformations=allow_all_transformations) super(DeepExplainer, self).__init__(model, initialization_examples, **kwargs) self._logger.debug('Initializing DeepExplainer') self._method = 'shap.deep' self.features = features self.classes = classes self.nclusters = nclusters self.explain_subset = explain_subset self.transformations = transformations self.model_task = model_task self.framework = _get_dnn_model_framework(self.model) summary = _get_summary_data(self.initialization_examples, nclusters, self.framework) # Suppress warning message from Keras with logger_redirector(self._logger): self.explainer = shap.DeepExplainer(self.model, summary)
agent = torch.load("agents/agent_zoo/dfq5_epsexp").to(DEVICE) player = PlayerQ(render=False) for episode_idx in range(n_ep): print("\r{}/{}".format(episode_idx, n_ep), end="") summary = player.play(agent) obs.append(summary["observations"]) _grid_size = config.N_LANES * config.LANE_LENGTH obs = np.concatenate(obs) obs = np.array([np.concatenate([state[:_grid_size], np.sum(state[_grid_size: 2 * _grid_size].reshape(-1, config.LANE_LENGTH), axis=1), state[2 * _grid_size:]]) for state in obs]) n_obs = len(obs) e = shap.DeepExplainer( agent.network, torch.from_numpy( obs[np.random.choice(np.arange(len(obs)), 100, replace=False)] ).type(torch.FloatTensor).to(DEVICE)) shap_values = e.shap_values( torch.from_numpy(obs[np.random.choice(np.arange(len(obs)), 30, replace=False)]).type(torch.FloatTensor).to(DEVICE) ) s = np.stack([np.sum(s, axis=0) for s in shap_values]) print(np.sum(s, axis=0)) shap.summary_plot(shap_values)
def deep_explain_model_heatmap( model, csv_test_loader: CSVTestLoader, datetime_start: Optional[datetime] = None) -> None: """Generate feature heatmap for prediction at a start time Args: model ([type]): trained model csv_test_loader ([CSVTestLoader]): test data loader datetime_start (Optional[datetime], optional): start date of the test prediction, Defaults to None, i.e. using model inference parameters. Returns: None """ if model.params["model_name"] == "SimpleTransformer": print("SimpleTransformer currently not supported.") return elif "probabilistic" in model.params: print("Probabilistic currently not supported.") return use_wandb = model.wandb device = torch.device("cuda" if torch.cuda.is_available() else "cpu") if model.params["model_name"] == "DARNN" and device.type == "cuda": print("Currently DARNN doesn't work with shap on CUDA") return if datetime_start is None: datetime_start = model.params["inference_params"]["datetime_start"] history, _, forecast_start_idx = csv_test_loader.get_from_start_date( datetime_start) background_tensor = _prepare_background_tensor(csv_test_loader) background_tensor = background_tensor.to(device) model.model.eval() # background shape (L, N, M) # L - batch size, N - history length, M - feature size # for each element in each N x M batch in L, # attribute to each prediction in forecast len deep_explainer = shap.DeepExplainer(model.model, background_tensor) shap_values = deep_explainer.shap_values( background_tensor) # forecast_len x N x L x M shap_values = np.stack(shap_values) if len(shap_values.shape) != 4: shap_values = np.expand_dims(shap_values, axis=0) shap_values = torch.tensor( shap_values, names=["preds", "batches", "observations", "features"]) figs = plot_shap_value_heatmaps(shap_values) if use_wandb: for fig, feature in zip(figs, csv_test_loader.df.columns): wandb.log({f"Average prediction heatmaps - {feature}": fig}) # heatmap one prediction sequence at datetime_start # (seq_len*forecast_len) per fop feature to_explain = history.to(device).unsqueeze(0) shap_values = deep_explainer.shap_values(to_explain) shap_values = np.stack(shap_values) if len(shap_values.shape) != 4: shap_values = np.expand_dims(shap_values, axis=0) shap_values = torch.tensor( shap_values, names=["preds", "batches", "observations", "features"]) figs = plot_shap_value_heatmaps(shap_values) if use_wandb: for fig, feature in zip(figs, csv_test_loader.df.columns): wandb.log({ "Heatmap for prediction " f"at {datetime_start} - {feature}": fig })
def deep_explain_model_summary_plot( model, csv_test_loader: CSVTestLoader, datetime_start: Optional[datetime] = None) -> None: """Generate feature summary plot for trained deep learning models Args: model (object): trained model csv_test_loader (CSVTestLoader): test data loader datetime_start (datetime, optional): start date of the test prediction, Defaults to None, i.e. using model inference parameters. """ if model.params["model_name"] == "SimpleTransformer": print("SimpleTransformer currently not supported.") return use_wandb = model.wandb device = torch.device("cuda" if torch.cuda.is_available() else "cpu") if model.params["model_name"] == "DARNN" and device.type == "cuda": print("DARNN does not work with shap on CUDA") return if datetime_start is None: datetime_start = model.params["inference_params"]["datetime_start"] history, _, forecast_start_idx = csv_test_loader.get_from_start_date( datetime_start) background_tensor = _prepare_background_tensor(csv_test_loader) background_tensor = background_tensor.to(device) model.model.eval() # background shape (L, N, M) # L - batch size, N - history length, M - feature size deep_explainer = shap.DeepExplainer(model.model, background_tensor) shap_values = deep_explainer.shap_values(background_tensor) shap_values = np.stack(shap_values) # shap_values needs to be 4-dimensional if len(shap_values.shape) != 4: shap_values = np.expand_dims(shap_values, axis=0) shap_values = torch.tensor( shap_values, names=["preds", "batches", "observations", "features"]) # summary plot shows overall feature ranking # by average absolute shap values fig = plot_summary_shap_values(shap_values, csv_test_loader.df.columns) if use_wandb: wandb.log({"Overall feature ranking by shap values": fig}) # summary plot for multi-step outputs # multi_shap_values = shap_values.apply_along_axis(np.mean, 'batches') fig = plot_summary_shap_values_over_time_series(shap_values, csv_test_loader.df.columns) if use_wandb: wandb.log({"Overall feature ranking per prediction time-step": fig}) # summary plot for one prediction at datetime_start history = history.to(device).unsqueeze(0) history_numpy = torch.tensor(history.cpu().numpy(), names=["batches", "observations", "features"]) shap_values = deep_explainer.shap_values(history) shap_values = np.stack(shap_values) if len(shap_values.shape) != 4: shap_values = np.expand_dims(shap_values, axis=0) shap_values = torch.tensor( shap_values, names=["preds", "batches", "observations", "features"]) figs = plot_shap_values_from_history(shap_values, history_numpy) if use_wandb: for fig, feature in zip(figs, csv_test_loader.df.columns.tolist()): wandb.log({ "Feature ranking for prediction" f" at {datetime_start} - {feature}": fig })
import shap # =============================================================== # === === # === !!! Before run this file, Add the code below to the === # === end of file: [shap]/explainers/_deep/deep_pytorch.py === # === [shap] means the root path of your python shap lib === # === such like "D:\anaconda3\Lib\site-packages\shap" === # === === # === code for supplement : === # === op_handler['Chomp1d'] = passthrough === # === === # =============================================================== model = torch.load('sepsis_predict.pt').cuda() data_raw = torch.load("dataTensor.pt").cuda() data_X = data_raw[:, :, :-1].transpose(1, 2) X_train = data_X[:100] X_test = data_X[-2000:-1995] e = shap.DeepExplainer(model, X_train) shap_values = e.shap_values(X_test) np.save('shap_values', shap_values[0, 0]) # shap.image_plot(shap_values, X_test) plt.imshow(shap_values[0, 0]) plt.colorbar() plt.show()
batch_size=50) test_loader = torch.utils.data.DataLoader(dataset=dataset, batch_size=5) with torch.no_grad(): # for mb_features in background_loader: # mb_features = {k: v.to(device) for k, v in mb_features.items()} # mb_alphas = torch.exp(model(**mb_features)[0]) background_iter = iter(background_loader) background_features = next( background_iter ) #model expects dict with keys input ids and attention mask, value shapes Nx512 background_features_list = [ background_features['input_ids'].to(device), background_features['attention_mask'].to(device) ] test_iter = iter(test_loader) test_features = next(test_iter) test_features_list = [ test_features['input_ids'].to(device), test_features['attention_mask'].to(device) ] explainershap = shap.DeepExplainer( model, background_features_list ) #shap requires background data to be one tensor only shap_values_matrix = explainershap.shap_values(test_features_list) shap.summary_plot(shap_values_matrix) #ä,feature_names=dataset.feature_names) print('done')
def get_instance_explanations(X, Y, subset = 1000, classifier_index = "gradient_boosting", explanation_method = "shap", shap_explainer = "kernel", text = False): """ A set of calls for obtaining aggregates of explanations. """ ## label encoding #lab_enc = preprocessing.LabelEncoder() #training_scores_encoded = lab_enc.fit_transform(Y) # TODO: zakaj je potreben label encoder? training_scores_encoded = Y if text: vectorizer = TfidfVectorizer(analyzer='word',stop_words= 'english') X_vectorized = vectorizer.fit_transform(X) #print(X_vectorized) X_vectorized = X_vectorized.todense() #print(X_vectorized) X = pd.DataFrame(X_vectorized) X.columns = vectorizer.get_feature_names() #X.columns = vectorizer.get_feature_names() logging.info("Feature pre-selection via Mutual Information ({}).".format(subset)) #X = X.iloc[:,1:100] minf = mutual_info_classif(X.values, training_scores_encoded) top_k = np.argsort(minf)[::-1][0:subset] attribute_vector = X.columns[top_k] X = X.astype(float).values[:,top_k] skf = StratifiedKFold(n_splits=10) performances = [] enx = 0 t_start = time.time() logging.info("Starting importance estimation .. shape: {}".format(X.shape)) per_class_explanations = defaultdict(list) classifier_mapping = ["gradient_boosting", "random_forest", "svm"] classifiers = [GradientBoostingClassifier(), RandomForestClassifier(n_estimators=10), svm.SVC(probability=True)] ## spyct.Model() model_dict = dict(zip(classifier_mapping, classifiers)) if explanation_method == "shap": logging.info("Shapley-based explanations.") ## for the correctly predicted instances, remember shap values and compute the expected value at the end. for train_index, test_index in skf.split(X, Y): enx+=1 clf = model_dict[classifier_index] x_train = X[train_index] x_test = X[test_index] y_train = Y[train_index] y_test = Y[test_index] ## perform simple feature ranking minf = mutual_info_classif(x_train, y_train) top_k = np.argsort(minf)[::-1][0:subset] x_train = x_train[:,top_k] x_test = x_test[:,top_k] x_train = x_train.astype('float') y_train = y_train.astype('float') x_test = x_test.astype('float') y_test = y_test.astype('float') model = clf.fit(x_train, y_train) preds = model.predict(x_test) if len(np.unique(y_train)) > 1: average = "micro" perf = f1_score(preds,y_test, average = average) performances.append(perf) logging.info("Performance in fold {}, {} (F1)".format(enx, perf)) ## different shap explainers if shap_explainer == "kernel": explainer = shap.KernelExplainer(model.predict_proba, x_train) if shap_explainer == "tree": explainer = shap.TreeExplainer(model.predict_proba, x_train) if shap_explainer == "gradient": explainer = shap.GradientExplainer(model.predict_proba, x_train) if shap_explainer == "deep": explainer = shap.DeepExplainer(model.predict_proba, x_train) if shap_explainer == "sampling": explainer = shap.SamplingExplainer(model.predict_proba, x_train) if shap_explainer == "partition": explainer = shap.PartitionExplainer(model.predict_proba, x_train) for unique_class in set(preds): cors_neg = np.array([enx for enx, pred_tuple in enumerate(zip(preds, y_test)) if pred_tuple[0] == pred_tuple[1] and pred_tuple[0] == unique_class]) if cors_neg.size != 0: shap_values = explainer.shap_values(x_test[cors_neg], nsamples = 10, verbose = False) stack = np.mean(np.vstack(shap_values),axis = 0) per_class_explanations[unique_class].append(stack) final_explanations = {} for class_name, explanation_set in per_class_explanations.items(): final_explanations[class_name] = np.mean(np.matrix(explanation_set),axis = 0) average_perf = (np.mean(performances), np.std(performances)) logging.info("Final performance: {}".format(average_perf)) elif explanation_method == "class-ranking": logging.info("Ranking-based explanations.") unique_scores = np.unique(training_scores_encoded) final_explanations = {} for label in unique_scores: inx = np.where(training_scores_encoded == label) tx = VarianceThreshold().fit(X[inx]).variances_ final_explanations[str(label)] = tx t_end = time.time() - t_start logging.info("Time spent on explanation estimation {}s.".format(t_end)) return (final_explanations, attribute_vector)
def train(num_clients, num_rounds, train_loader, test_loader, backdoor_test_loader, losses_train, losses_test, acc_train, acc_test, backdoor_acc_test, misclassification_rates, targeted_misclassification_rates, attack_success_rates, communication_rounds, clients_local_updates, global_update, source, target, euclid_dist_roundwise, autoencoder_test_data_roundwise, shap_data_roundwise, defense): # Initialize model and Optimizer # Initialize model global_model = Model_FashionMNIST() global_model_copy = copy.copy(global_model) # create K (num_clients) no. of client_models client_models = [Model_FashionMNIST() for _ in range(num_clients)] # synchronize with global_model for model in client_models: model.load_state_dict(global_model_copy.state_dict() ) # initial synchronizing with global model # create optimizers for client_models optimizer = [ optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5) for model in client_models ] # List containing info about learning # Runnining FL #attack_success_rate = 0 # since shuffle=True, this is a random sample of test data shap_tr_loader = torch.utils.data.DataLoader(shap_background, batch_size=128, shuffle=True) batch_shap = next(iter(shap_tr_loader)) images_shap, _ = batch_shap background = images_shap[:100] #n_test_images = 5 #test_images = images_shap[100:100+n_test_images] test_images = torch.zeros(1, 1, 28, 28) #images.size() for r in range(num_rounds): # client update loss = 0 for i in tqdm(range(num_clients)): loss += client_update(client_models[i], train_loader[i], optimizer[i], epoch=epochs) #comment out today if (defense): model_to_aggregate = [] threshold = 1.8 print('round : {}'.format(r)) id = 0 shap_data_temp = [] for model in client_models: e = shap.DeepExplainer(model, background) shap_values = e.shap_values(test_images) #print(shap_values) print('client id : {}'.format(id)) id += 1 shap_data_temp_model = [] #print('length : {}'.format(len(shap_values))) for i in range(10): #print(torch.sum(torch.tensor(shap_values[i]))) shap_data_temp_model.append( torch.sum(torch.tensor(shap_values[i]))) shap_data_temp.append(shap_data_temp_model) # rehspae the shap value array and test image array for visualization #shap_numpy2 = [np.swapaxes(np.swapaxes(s, 1, -1), 1, 2) for s in shap_values] #test_numpy2 = np.swapaxes(np.swapaxes(test_images.numpy(), 1, -1), 1, 2) # plot the feature attributions #shap.image_plot(shap_numpy2, -test_numpy2) temp = [] for shap_label in shap_data_temp_model: temp.append(shap_label.detach().item()) z_score = np.abs(stats.zscore(temp)) print(z_score) flag = True for j in range(len(z_score)): if z_score[j] > threshold: print('client {} not appended'.format(id)) flag = False break if flag == True: print('client {} is aggregated'.format(id)) model_to_aggregate.append(copy.copy(model)) shap_data_roundwise.append(shap_data_temp) temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_defense(global_model, client_models, model_to_aggregate) else: temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) #comment out today #x = 0 #for client_shap in shap_data_temp: # print('client {}'.format(x)) # x += 1 # csdata = copy.copy(client_shap) # med1 = torch.median(torch.tensor(csdata)) # for i in range(len(csdata)): # csdata[i] = abs(csdata[i] - med1) # md = torch.median(torch.tensor(csdata)) # mad = md*1.4826 # for i in range(len(csdata)): # csdata[i] = csdata[i]/mad # print(csdata) #print(shap_data_temp) ''' #check euclidean distance print("checking Euclidean distances") for i in range(num_clients-1): for j in range(i+1,num_clients): print('distance b/w client{} and client{} : {}'.format(i,j,euclidean_distance(client_models[i],client_models[j]))) ''' #for i in range(len(client_models)): # for j in range(i+1,len(client_models)): # temp = torch.norm(torch.cdist(client_models[i].fc1.weight,client_models[j].fc1.weight)) # print('dist b/w client{} and client {} is {}'.format(i+1,j+1,temp)) ''' euclid_dist_temp = [] for i in range(len(client_models)): total_dist = 0 count = 0 for j in range(len(client_models)): if i!= j: total_dist += torch.norm(torch.cdist(client_models[i].fc1.weight,client_models[j].fc1.weight)) count += 1 print('dist b/w client{} and other clients is {}'.format(i+1,total_dist/count)) euclid_dist_temp.append(total_dist/count) euclid_dist_roundwise.append(euclid_dist_temp) if r < 20: #calculate dataset for autoencoder for model in client_models: temp = [] for i in range(len(model.fc1.weight)): for j in range(32): temp.append(model.fc1.weight[i][j]) #print('vector size',len(temp)) dataAE.append(temp) elif r == 20: modelAE = AE() criterion_ae = nn.MSELoss() optimizer_ae = optim.Adam(modelAE.parameters(), lr=0.0001) tr_data = torch.Tensor(dataAE) trloader = torch.utils.data.DataLoader(tr_data, batch_size=32, shuffle=True) for epoch in range(20): ae_loss = 0 for batch_ae in trloader: optimizer_ae.zero_grad() outputs = modelAE(batch_ae) tr_loss = criterion_ae(outputs, batch_ae) tr_loss.backward() optimizer_ae.step() ae_loss += tr_loss.item() ae_loss = ae_loss / len(trloader) print("epoch : {}/{}, reconstruction loss = {:.8f}".format(epoch + 1, epochs, ae_loss)) else: ae_rounds_test_data = [] for model in client_models: input_test = [] for i in range(len(model.fc1.weight)): for j in range(32): input_test.append(model.fc1.weight[i][j]) output_test = modelAE(torch.Tensor(input_test)) te_loss = criterion_ae(output_test, torch.Tensor(input_test)) print('test_loss {:.8f}'.format(te_loss)) ae_rounds_test_data.append(te_loss) autoencoder_test_data_roundwise.append(ae_rounds_test_data) ''' #append clinet models and global models at the start of every round #temp_updates_clients = [] #for i in range(num_clients): # temp_updates_clients.append(copy.copy(client_models[i])) #clients_local_updates.append(temp_updates_clients) #global_update.append(global_model) #losses_train.append(loss) #communication_rounds.append(r+1) # server aggregate #server_aggregate(global_model, client_models) #defense #server_aggregate_defense(global_model, client_models, model_to_aggregate) # calculate test accuracy after the current round test_loss, acc, asr, mcr, tmcr = test(global_model, test_loader, source, target) backdoor_test_loss, back_acc = backdoor_test(global_model, backdoor_test_loader, 2) losses_test.append(test_loss) acc_test.append(acc) backdoor_acc_test.append(back_acc) misclassification_rates.append(mcr) targeted_misclassification_rates.append(tmcr) attack_success_rates.append(asr) print("attack success rate : ", asr) print("misclassification rate ", mcr) #attack_success_rate = asr print('%d-th round' % (r + 1)) print('average train loss %0.3g | test loss %0.3g | test acc: %0.3f' % (loss / num_clients, test_loss, acc)) print('backdoor accuracy {}'.format(back_acc))
# # TimeDistributed(Dense(out.shape[2]*20)), ## # TimeDistributed(Dense(32,)), # TimeDistributed(Dense(1), name='test'), Reshape((375,)), ]) # optimizer = optimizers.Adam(clipvalue=0.5) #optimizer = optimizers.Adam(clipnorm=1.) # model = multi_gpu_model(model, gpus=6) model_1.compile(loss=kl.mean_absolute_error, optimizer = 'adam',metrics=['accuracy']) model_1.summary() hist_1 = model_1.fit(input_bus, output_bus, epochs=30, batch_size=20, verbose=1, shuffle=False) with open('caseof.pickles','wb') as p: pickle.dump(model_1,p) pickle.dump(hist_1,p) # In[2] with open('caseof.pickles','rb') as p: model = pickle.load(p) hist = pickle.load(p) valo = sequence.pad_sequences(valo) explainer = shap.DeepExplainer(model, tf.convert_to_tensor(input_bus[:239,:,2:,:,:],dtype = 'float32'))
def test_pytorch_regression(): """Testing regressions (i.e. single outputs) """ try: import torch from torch import nn from torch.nn import functional as F from torch.utils.data import TensorDataset, ConcatDataset, DataLoader from sklearn.datasets import load_boston except Exception as e: print("Skipping test_pytorch_regression!") return import shap X, y = load_boston(return_X_y=True) num_features = X.shape[1] data = TensorDataset(torch.tensor(X).float(), torch.tensor(y).float()) loader = DataLoader(data, batch_size=128) class Net(nn.Module): def __init__(self, num_features): super(Net, self).__init__() self.linear = nn.Linear(num_features, 1) def forward(self, X): return self.linear(X) model = Net(num_features) optimizer = torch.optim.Adam(model.parameters()) def train(model, device, train_loader, optimizer, epoch): model.train() num_examples = 0 for batch_idx, (data, target) in enumerate(train_loader): num_examples += target.shape[0] data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = F.mse_loss(output.squeeze(1), target) loss.backward() optimizer.step() if batch_idx % 2 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) device = torch.device('cpu') train(model, device, loader, optimizer, 1) next_x, next_y = next(iter(loader)) np.random.seed(0) inds = np.random.choice(next_x.shape[0], 20, replace=False) e = shap.DeepExplainer(model, next_x[inds, :]) test_x, test_y = next(iter(loader)) shap_values = e.shap_values(test_x[:1]) model.eval() model.zero_grad() with torch.no_grad(): diff = (model(test_x[:1]) - model(next_x[inds, :])).detach().numpy().mean(0) sums = np.array([shap_values[i].sum() for i in range(len(shap_values))]) d = np.abs(sums - diff).sum() assert d / np.abs(diff).sum() < 0.001, "Sum of SHAP values does not match difference! %f" % ( d / np.abs(diff).sum())
def train(num_clients, num_rounds, train_loader, test_loader, backdoor_test_loader, losses_train, losses_test, acc_train, acc_test, backdoor_acc_test, misclassification_rates, targeted_misclassification_rates, attack_success_rates, communication_rounds, clients_local_updates, global_update, source, target, euclid_dist_roundwise, autoencoder_test_data_roundwise, shap_data_roundwise, defense, defense_type): # Initialize model and Optimizer # Initialize model global_model = Model_FashionMNIST() global_model_copy = copy.copy(global_model) # create K (num_clients) no. of client_models client_models = [Model_FashionMNIST() for _ in range(num_clients)] # synchronize with global_model for model in client_models: model.load_state_dict(global_model_copy.state_dict() ) # initial synchronizing with global model # create optimizers for client_models optimizer = [ optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5) for model in client_models ] shap_tr_loader = torch.utils.data.DataLoader(shap_background, batch_size=128, shuffle=True) batch_shap = next(iter(shap_tr_loader)) images_shap, _ = batch_shap background = images_shap[:100] test_images = torch.zeros(1, 1, 28, 28) for r in range(num_rounds): # client update loss = 0 for i in tqdm(range(num_clients)): loss += client_update(client_models[i], train_loader[i], optimizer[i], epoch=epochs) if (defense): if defense_type == 'geomed': temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_GeoMed(global_model, client_models) elif defense_type == 'trimmedmean': temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_TrimmedMean(global_model, client_models) elif defense_type == 'fedavg': temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) elif defense_type == 'krum': c = 6 # corrupted worker n = 30 # participants in a round euclid_dist_temp = [] lb = c // 2 + 1 ub = n - c // 2 - 1 for i in range(len(client_models)): client_i_dist = [] for j in range(len(client_models)): if i != j: dist = torch.norm( torch.cdist(client_models[i].fc1.weight, client_models[j].fc1.weight)) client_i_dist.append(dist) print(client_i_dist) sqdist_i = torch.sum( torch.sort(torch.tensor(client_i_dist)).values[lb:ub]) euclid_dist_temp.append(sqdist_i) print('dist of client{} and other clients is {}'.format( i + 1, sqdist_i)) mindist = euclid_dist_temp[0] next_client_index = 0 for i in range(len(euclid_dist_temp)): if euclid_dist_temp[i] < mindist: mindist = euclid_dist_temp[i] next_client_index = i print('min dist : {}'.format(mindist)) print('next index : {}'.format(next_client_index + 1)) euclid_dist_roundwise.append(euclid_dist_temp) temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) global_model.load_state_dict( client_models[next_client_index].state_dict()) for model in client_models: model.load_state_dict(global_model.state_dict()) #server_aggregate(global_model, client_models) elif defense_type == 'moat': model_to_aggregate = [] threshold = 1.8 print('round : {}'.format(r)) id = 0 shap_data_temp = [] for model in client_models: e = shap.DeepExplainer(model, background) shap_values = e.shap_values(test_images) print('client id : {}'.format(id)) id += 1 shap_data_temp_model = [] for i in range(10): shap_data_temp_model.append( torch.sum(torch.tensor(shap_values[i]))) shap_data_temp.append(shap_data_temp_model) temp = [] for shap_label in shap_data_temp_model: temp.append(shap_label.detach().item()) z_score = np.abs(stats.zscore(temp)) print(z_score) flag = True for j in range(len(z_score)): if z_score[j] > threshold: print('client {} not appended'.format(id)) flag = False break if flag == True: print('client {} is aggregated'.format(id)) model_to_aggregate.append(copy.copy(model)) shap_data_roundwise.append(shap_data_temp) temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_defense(global_model, client_models, model_to_aggregate) elif defense_type == 'ae': model_to_aggregate = [] if r < 20: for model in client_models: temp = [] for i in range(len(model.fc1.weight)): for j in range(32): temp.append(model.fc1.weight[i][j]) dataAE.append(temp) #aggregate temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy( client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) elif r == 20: modelAE = AE() criterion_ae = nn.MSELoss() optimizer_ae = optim.Adam(modelAE.parameters(), lr=0.0001) tr_data = torch.Tensor(dataAE) trloader = torch.utils.data.DataLoader(tr_data, batch_size=32, shuffle=True) for epoch in range(20): ae_loss = 0 for batch_ae in trloader: optimizer_ae.zero_grad() outputs = modelAE(batch_ae) tr_loss = criterion_ae(outputs, batch_ae) tr_loss.backward() optimizer_ae.step() ae_loss += tr_loss.item() ae_loss = ae_loss / len(trloader) print("epoch : {}/{}, reconstruction loss = {:.8f}". format(epoch + 1, epochs, ae_loss)) #aggregate temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy( client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) else: ae_rounds_test_data = [] for model in client_models: input_test = [] for i in range(len(model.fc1.weight)): for j in range(32): input_test.append(model.fc1.weight[i][j]) output_test = modelAE(torch.Tensor(input_test)) te_loss = criterion_ae(output_test, torch.Tensor(input_test)) print('test_loss {:.8f}'.format(te_loss)) ae_rounds_test_data.append(te_loss) ae_loss_round = [] for ae_loss in ae_rounds_test_data: ae_loss_round.append(ae_loss.detach().item()) sigma = min(ae_loss_round) anomaly_score = [] for el in ae_loss_round: x = 1 + el y = 1 + sigma anomaly_score.append(x / y) threshold = statistics.mean(anomaly_score) for i in range(len(anomaly_score)): if anomaly_score[i] < threshold: print('client {} is aggregated'.format(i + 1)) model_to_aggregate.append( copy.copy(client_models[i])) else: print('client {} not appended'.format(i + 1)) autoencoder_test_data_roundwise.append(ae_rounds_test_data) temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy( client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_defense(global_model, client_models, model_to_aggregate) else: temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) else: temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) # calculate test accuracy after the current round test_loss, acc, asr, mcr, tmcr = test(global_model, test_loader, source, target) backdoor_test_loss, back_acc = backdoor_test(global_model, backdoor_test_loader, 2) losses_test.append(test_loss) acc_test.append(acc) backdoor_acc_test.append(back_acc) misclassification_rates.append(mcr) targeted_misclassification_rates.append(tmcr) attack_success_rates.append(asr) print("attack success rate : ", asr) print("misclassification rate ", mcr) #attack_success_rate = asr print('%d-th round' % (r + 1)) print('average train loss %0.3g | test loss %0.3g | test acc: %0.3f' % (loss / num_clients, test_loss, acc)) print('backdoor accuracy {}'.format(back_acc))
def test_tf_keras_mnist_cnn(): """ This is the basic mnist cnn example from keras. """ try: from tensorflow import keras from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout, Flatten, Activation from tensorflow.keras.layers import Conv2D, MaxPooling2D from tensorflow.keras import backend as K import tensorflow as tf except Exception as e: print("Skipping test_tf_keras_mnist_cnn!") return import shap batch_size = 128 num_classes = 10 epochs = 1 # input image dimensions img_rows, img_cols = 28, 28 # the data, split between train and test sets (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() if K.image_data_format() == 'channels_first': x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols) x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols) input_shape = (1, img_rows, img_cols) else: x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1) input_shape = (img_rows, img_cols, 1) x_train = x_train.astype('float32') x_test = x_test.astype('float32') x_train /= 255 x_test /= 255 # convert class vectors to binary class matrices y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes) model = Sequential() model.add(Conv2D(8, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) model.add(Conv2D(16, (3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(32, activation='relu')) # 128 model.add(Dropout(0.5)) model.add(Dense(num_classes)) model.add(Activation('softmax')) model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy']) model.fit(x_train[:1000,:], y_train[:1000,:], batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(x_test[:1000,:], y_test[:1000,:])) # explain by passing the tensorflow inputs and outputs np.random.seed(0) inds = np.random.choice(x_train.shape[0], 10, replace=False) e = shap.DeepExplainer((model.layers[0].input, model.layers[-1].input), x_train[inds,:,:]) shap_values = e.shap_values(x_test[:1]) sess = tf.keras.backend.get_session() diff = sess.run(model.layers[-1].input, feed_dict={model.layers[0].input: x_test[:1]}) - \ sess.run(model.layers[-1].input, feed_dict={model.layers[0].input: x_train[inds,:,:]}).mean(0) sums = np.array([shap_values[i].sum() for i in range(len(shap_values))]) d = np.abs(sums - diff).sum() assert d / np.abs(diff).sum() < 0.001, "Sum of SHAP values does not match difference! %f" % d
x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(inputs) x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x) x = tf.keras.layers.Flatten()(x) x = tf.keras.layers.Dense(128, activation='relu')(x) out = tf.keras.layers.Dense(num_classes, activation='softmax')(x) model = tf.keras.Model(inputs=[inputs], outputs=out) model.compile(loss=tf.keras.losses.categorical_crossentropy, optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy']) model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(x_test, y_test)) # select a set of background examples to take an expectation over background = x_train[np.random.choice(x_train.shape[0], 100, replace=False)] # explain predictions of the model on four images e = shap.DeepExplainer(model, background) # ...or pass tensors directly # e = shap.DeepExplainer((model.layers[0].input, model.layers[-1].output), background) shap_values = e.shap_values(x_test[1:5]) # plot the feature attributions shap.image_plot(shap_values, -x_test[1:5])
def train(num_clients, num_rounds, train_loader, test_loader, backdoor_test_loader, losses_train, losses_test, acc_train, acc_test, backdoor_acc_test, misclassification_rates, attack_success_rates, communication_rounds, clients_local_updates, global_update, source, target, euclid_dist_roundwise, autoencoder_test_data_roundwise, shap_data_roundwise, defense): # Initialize model and Optimizer # Initialize model global_model = Model_FashionMNIST() global_model_copy = copy.copy(global_model) # create K (num_clients) no. of client_models client_models = [Model_FashionMNIST() for _ in range(num_clients)] # synchronize with global_model for model in client_models: model.load_state_dict(global_model_copy.state_dict() ) # initial synchronizing with global model # create optimizers for client_models optimizer = [ optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-5) for model in client_models ] # List containing info about learning # Runnining FL #attack_success_rate = 0 # since shuffle=True, this is a random sample of test data shap_tr_loader = torch.utils.data.DataLoader(shap_background, batch_size=128, shuffle=True) batch_shap = next(iter(shap_tr_loader)) images_shap, _ = batch_shap background = images_shap[:100] #n_test_images = 5 #test_images = images_shap[100:100+n_test_images] test_images = torch.zeros(1, 1, 28, 28) #images.size() for r in range(num_rounds): # client update loss = 0 for i in tqdm(range(num_clients)): loss += client_update(client_models[i], train_loader[i], optimizer[i], epoch=epochs) if (defense): model_to_aggregate = [] threshold = 2 print('round : {}'.format(r)) id = 0 shap_data_temp = [] for model in client_models: e = shap.DeepExplainer(model, background) shap_values = e.shap_values(test_images) #print(shap_values) print('client id : {}'.format(id)) id += 1 shap_data_temp_model = [] #print('length : {}'.format(len(shap_values))) for i in range(10): #print(torch.sum(torch.tensor(shap_values[i]))) shap_data_temp_model.append( torch.sum(torch.tensor(shap_values[i]))) shap_data_temp.append(shap_data_temp_model) # rehspae the shap value array and test image array for visualization #shap_numpy2 = [np.swapaxes(np.swapaxes(s, 1, -1), 1, 2) for s in shap_values] #test_numpy2 = np.swapaxes(np.swapaxes(test_images.numpy(), 1, -1), 1, 2) # plot the feature attributions #shap.image_plot(shap_numpy2, -test_numpy2) temp = [] for shap_label in shap_data_temp_model: temp.append(shap_label.detach().item()) z_score = np.abs(stats.zscore(temp)) print(z_score) flag = True for j in range(len(z_score)): if z_score[j] > threshold: print('client {} not appended'.format(id)) flag = False break if flag == True: print('client {} is aggregated'.format(id)) model_to_aggregate.append(copy.copy(model)) shap_data_roundwise.append(shap_data_temp) temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate_defense(global_model, client_models, model_to_aggregate) else: temp_updates_clients = [] for i in range(num_clients): temp_updates_clients.append(copy.copy(client_models[i])) clients_local_updates.append(temp_updates_clients) global_update.append(global_model) losses_train.append(loss) communication_rounds.append(r + 1) server_aggregate(global_model, client_models) test_loss, acc, asr, mcr = test(global_model, test_loader, source, target) backdoor_test_loss, back_acc = backdoor_test(global_model, backdoor_test_loader, 2) losses_test.append(test_loss) acc_test.append(acc) backdoor_acc_test.append(back_acc) misclassification_rates.append(mcr) attack_success_rates.append(asr) print("attack success rate : ", asr) print("misclassification rate ", mcr) #attack_success_rate = asr print('%d-th round' % (r + 1)) print('average train loss %0.3g | test loss %0.3g | test acc: %0.3f' % (loss / num_clients, test_loss, acc)) print('backdoor accuracy {}'.format(back_acc))
def explain_pic(shap_value_normal_path, shap_value_plug_path): # 构建SHAP解释器对测试数据进行解释 MODEL_PATH = 'model_nsh_sequence/Cnn.weights.005-0.9110.hdf5' train_100_path = 'data_sequence/train_data_100.json' x_bg = json.load(open(train_100_path)) bg_x = [ np.array(x_bg[0][:50]), np.array(x_bg[1][:50]), np.array(x_bg[2][:50]), np.array(x_bg[3][:50]), np.array(x_bg[4][:50]) ] model = load_model(MODEL_PATH, compile=False) explainer = shap.DeepExplainer(model, bg_x) print('build explainer') test_path = 'data_sequence/test_data.json' label_path = 'data_sequence/label.json' voc_path = 'data_sequence/logdisign_id_voc.json' x_test = json.load(open(test_path)) y = json.load(open(label_path)) voc = json.load(open(voc_path)) voc_re = {value: key for key, value in voc.items()} voc_re[0] = 'pad' train_end, valid_end = int(0.7 * len(y)), int(0.8 * len(y)) r = random.random random.seed(2) role_ids = list(y.keys()) role_ids.sort() random.shuffle(role_ids, random=r) train_id, valid_id, test_id = role_ids[:train_end], role_ids[ train_end + 1:valid_end], role_ids[valid_end:] plug_file_list = os.listdir(shap_value_plug_path) for file in plug_file_list: shap_value_np = np.load(shap_value_plug_path + file) role_id = file.split('.')[0] id_index = test_id.index(role_id) x = [ np.array(x_test[0][id_index]), np.array(x_test[1][id_index]), np.array(x_test[2][id_index]), np.array(x_test[3][id_index]), np.array(x_test[4][id_index]) ] voc_cnt = {} voc_shap_value = {} for i in range(5): for j in range(x[i].shape[0]): id = voc_re[x[i][j]] shap_value = shap_value_np[i][j] if id not in voc_cnt: voc_cnt[id] = 1 voc_shap_value[id] = shap_value else: voc_cnt[id] += 1 voc_shap_value[id] += shap_value id_name = [] data = [] shap_values = [] for key, value in voc_cnt.items(): id_name.append(key) data.append(value) shap_values.append(voc_shap_value[key]) shap.force_plot(explainer.expected_value, np.array(shap_values), np.array(data), feature_names=id_name, matplotlib=True, show=False) plt.savefig( 'data_sequence/cnn/balance_local_bot/{}.pdf'.format(role_id), dpi=100, bbox_inches='tight') plt.close() normal_file_list = os.listdir(shap_value_normal_path) for file in normal_file_list: shap_value_np = np.load(shap_value_normal_path + file) role_id = file.split('.')[0] id_index = test_id.index(role_id) x = [ np.array(x_test[0][id_index]), np.array(x_test[1][id_index]), np.array(x_test[2][id_index]), np.array(x_test[3][id_index]), np.array(x_test[4][id_index]) ] voc_cnt = {} voc_shap_value = {} for i in range(5): for j in range(x[i].shape[0]): id = voc_re[x[i][j]] shap_value = shap_value_np[i][j] if id not in voc_cnt: voc_cnt[id] = 1 voc_shap_value[id] = shap_value else: voc_cnt[id] += 1 voc_shap_value[id] += shap_value id_name = [] data = [] shap_values = [] for key, value in voc_cnt.items(): id_name.append(key) data.append(value) shap_values.append(voc_shap_value[key]) shap.force_plot(explainer.expected_value, np.array(shap_values), np.array(data), feature_names=id_name, matplotlib=True, show=False) plt.savefig( 'data_sequence/cnn/balance_local_normal/{}.pdf'.format(role_id), dpi=100, bbox_inches='tight') plt.close()