def _create_2D_plots(self, normalizer): self._test_init(normalizer) self.logger.info("Create 2-dimensional plot") self.clf_orig = self.classifier.deepcopy() pois_clf = self._clf_poisoning()[0] fig = CFigure(height=4, width=10, title=self.clf_idx) n_rows = 1 n_cols = 2 box = self._create_box() fig.subplot(n_rows, n_cols, grid_slot=1) fig.sp.title('Attacker objective and gradients') self._plot_func(fig, self.poisoning.objective_function) self._plot_obj_grads(fig, self.poisoning.objective_function_gradient) fig.sp.plot_ds(self.tr) fig.sp.plot_decision_regions( self.clf_orig, plot_background=False, grid_limits=self.grid_limits, n_grid_points=10, ) fig.sp.plot_constraint(box, grid_limits=self.grid_limits, n_grid_points=10) fig.sp.plot_path(self.poisoning.x_seq, start_facecolor='r' if self.yc == 1 else 'b') fig.subplot(n_rows, n_cols, grid_slot=2) fig.sp.title('Classification error on val') self._plot_func(fig, self.poisoning.objective_function, acc=True) fig.sp.plot_ds(self.tr) fig.sp.plot_decision_regions( pois_clf, plot_background=False, grid_limits=self.grid_limits, n_grid_points=10, ) fig.sp.plot_constraint(box, grid_limits=self.grid_limits, n_grid_points=10) fig.sp.plot_path(self.poisoning.x_seq, start_facecolor='r' if self.yc == 1 else 'b') fig.tight_layout() exp_idx = "2d_pois_" exp_idx += self.clf_idx if self.classifier.class_type == 'svm': if self.classifier.kernel.preprocess is not None: exp_idx += "_norm" else: if self.classifier.preprocess is not None: exp_idx += "_norm" fig.savefig(exp_idx + '.pdf', file_format='pdf')
def _test_plot(self, evas): """Check if `stored_vars` is correctly populated. Parameters ---------- evas : CAttackEvasionCleverhans """ if self.make_figures is False: self.logger.debug("Skipping figures...") return fig = CFigure() fig.sp.plot_path(evas.x_seq) fig.sp.plot_fun(evas.objective_function, plot_levels=False, multipoint=True, n_grid_points=50) fig.sp.plot_decision_regions(self.clf, plot_background=False, n_grid_points=100) fig.title("ATTACK: {}, y_target: {}".format( evas._clvrh_attack_class.__name__, self.y_target)) name_file = '{}_evasion2D_target_{}.pdf'.format( evas._clvrh_attack_class.__name__, self.y_target) fig.savefig(fm.join(self.images_folder, name_file), file_format='pdf')
def _save_fig(self): """Visualizing the function being optimized with line search.""" x_range = CArray.arange(-5, 20, 0.5, ) score_range = x_range.T.apply_along_axis(self.fun.fun, axis=1) ref_line = CArray.zeros(x_range.size) fig = CFigure(height=6, width=12) fig.sp.plot(x_range, score_range, color='b') fig.sp.plot(x_range, ref_line, color='k') filename = fm.join(fm.abspath(__file__), 'test_line_search_bisect.pdf') fig.savefig(filename)
def _plot_2d_evasion(self, evas, ds, x0, filename, th=0, grid_limits=None): """Plot evasion attack results for 2D data. Parameters ---------- evas : CAttackEvasion ds : CDataset x0 : CArray Initial attack point. filename : str Name of the output pdf file. th : scalar, optional Scores threshold of the classifier. Default 0. grid_limits : list of tuple or None, optional If not specified, will be set as [(-1.5, 1.5), (-1.5, 1.5)]. """ if self.make_figures is False: self.logger.debug("Skipping figures...") return fig = CFigure(height=6, width=6) if grid_limits is None: grid_limits = [(-1.5, 1.5), (-1.5, 1.5)] fig.sp.plot_ds(ds) fig.sp.plot_fun(func=evas.objective_function, grid_limits=grid_limits, colorbar=False, n_grid_points=50, plot_levels=False) fig.sp.plot_decision_regions(clf=evas.classifier, plot_background=False, grid_limits=grid_limits, n_grid_points=50) fig.sp.plot_constraint(self._box(evas), n_grid_points=20, grid_limits=grid_limits) fig.sp.plot_fun(func=lambda z: self._constr(evas, x0).constraint(z), plot_background=False, n_grid_points=50, grid_limits=grid_limits, levels=[0], colorbar=False) fig.sp.plot_path(evas.x_seq) fig.savefig(fm.join(self.images_folder, filename), file_format='pdf')
def _create_params_grad_plot(self, normalizer): """ Show the gradient of the classifier parameters w.r.t the poisoning point """ self.logger.info("Create 2-dimensional plot of the poisoning " "gradient") self._test_init(normalizer) pois_clf = self._clf_poisoning()[0] if self.n_features == 2: debug_pois_obj = _CAttackPoisoningLinTest(self.poisoning) fig = CFigure(height=8, width=10) n_rows = 2 n_cols = 2 fig.title(self.clf_idx) fig.subplot(n_rows, n_cols, grid_slot=1) fig.sp.title('w1 wrt xc') self._plot_param_sub(fig, debug_pois_obj.w1, debug_pois_obj.gradient_w1_xc, pois_clf) fig.subplot(n_rows, n_cols, grid_slot=2) fig.sp.title('w2 wrt xc') self._plot_param_sub(fig, debug_pois_obj.w2, debug_pois_obj.gradient_w2_xc, pois_clf) fig.subplot(n_rows, n_cols, grid_slot=3) fig.sp.title('b wrt xc') self._plot_param_sub(fig, debug_pois_obj.b, debug_pois_obj.gradient_b_xc, pois_clf) fig.tight_layout() exp_idx = "2d_grad_pois_" exp_idx += self.clf_idx if self.classifier.preprocess is not None: exp_idx += "_norm" fig.savefig(exp_idx + '.pdf', file_format='pdf')
def test_margin(self): self.logger.info("Testing margin separation of SGD...") # we create 50 separable points dataset = CDLRandomBlobs(n_samples=50, centers=2, random_state=0, cluster_std=0.60).load() # fit the model clf = CClassifierSGD(loss=CLossHinge(), regularizer=CRegularizerL2(), alpha=0.01, max_iter=200, random_state=0) clf.fit(dataset.X, dataset.Y) # plot the line, the points, and the nearest vectors to the plane xx = CArray.linspace(-1, 5, 10) yy = CArray.linspace(-1, 5, 10) X1, X2 = np.meshgrid(xx.tondarray(), yy.tondarray()) Z = CArray.empty(X1.shape) for (i, j), val in np.ndenumerate(X1): x1 = val x2 = X2[i, j] Z[i, j] = clf.decision_function(CArray([x1, x2]), y=1) levels = [-1.0, 0.0, 1.0] linestyles = ['dashed', 'solid', 'dashed'] colors = 'k' fig = CFigure(linewidth=1) fig.sp.contour(X1, X2, Z, levels, colors=colors, linestyles=linestyles) fig.sp.scatter(dataset.X[:, 0].ravel(), dataset.X[:, 1].ravel(), c=dataset.Y, s=40) fig.savefig( fm.join(fm.abspath(__file__), 'figs', 'test_c_classifier_sgd2.pdf'))
def _show_adv(self, x0, y0, x_opt, y_pred, attack_idx, y_target): """Show the original and the modified sample. Parameters ---------- x0 : CArray Initial attack point. y0 : CArray Label of the initial attack point. x_opt : CArray Final optimal point. y_pred : CArray Predicted label of the final optimal point. attack_idx : str Identifier of the attack algorithm. y_target : int or None Attack target class. """ if self.make_figures is False: self.logger.debug("Skipping figures...") return added_noise = abs(x_opt - x0) # absolute value of noise image fig = CFigure(height=5.0, width=15.0) fig.subplot(1, 3, 1) fig.sp.title(self.digits[y0.item()]) fig.sp.imshow(x0.reshape((self.img_h, self.img_w)), cmap='gray') fig.subplot(1, 3, 2) fig.sp.imshow(added_noise.reshape((self.img_h, self.img_w)), cmap='gray') fig.subplot(1, 3, 3) fig.sp.title(self.digits[y_pred.item()]) fig.sp.imshow(x_opt.reshape((self.img_h, self.img_w)), cmap='gray') name_file = "{:}_MNIST_target-{:}.pdf".format(attack_idx, y_target) fig.savefig(fm.join(self.images_folder, name_file), file_format='pdf')
def test_margin(self): self.logger.info("Testing margin separation of SVM...") import numpy as np # we create 40 separable points rng = np.random.RandomState(0) n_samples_1 = 1000 n_samples_2 = 100 X = np.r_[1.5 * rng.randn(n_samples_1, 2), 0.5 * rng.randn(n_samples_2, 2) + [2, 2]] y = [0] * (n_samples_1) + [1] * (n_samples_2) dataset = CDataset(X, y) # fit the model clf = CClassifierSVM() clf.fit(dataset.X, dataset.Y) w = clf.w a = -w[0] / w[1] xx = CArray.linspace(-5, 5) yy = a * xx - clf.b / w[1] wclf = CClassifierSVM(class_weight={0: 1, 1: 10}) wclf.fit(dataset.X, dataset.Y) ww = wclf.w wa = -ww[0] / ww[1] wyy = wa * xx - wclf.b / ww[1] fig = CFigure(linewidth=1) fig.sp.plot(xx, yy.ravel(), 'k-', label='no weights') fig.sp.plot(xx, wyy.ravel(), 'k--', label='with weights') fig.sp.scatter(X[:, 0].ravel(), X[:, 1].ravel(), c=y) fig.sp.legend() fig.savefig( fm.join(fm.abspath(__file__), 'figs', 'test_c_classifier_svm.pdf'))
def _show_adv(self, x0, y0, x_opt, y_pred): """Show the original and the modified sample. Parameters ---------- x0 : CArray Initial attack point. y0 : CArray Label of the initial attack point. x_opt : CArray Final optimal point. y_pred : CArray Predicted label of the final optimal point. """ if self.make_figures is False: self.logger.debug("Skipping figures...") return added_noise = abs(x_opt - x0) # absolute value of noise image fig = CFigure(height=5.0, width=15.0) fig.subplot(1, 3, 1) fig.sp.title(self._digits[y0.item()]) fig.sp.imshow(x0.reshape( (self._tr.header.img_h, self._tr.header.img_w)), cmap='gray') fig.subplot(1, 3, 2) fig.sp.imshow(added_noise.reshape( (self._tr.header.img_h, self._tr.header.img_w)), cmap='gray') fig.subplot(1, 3, 3) fig.sp.title(self._digits[y_pred.item()]) fig.sp.imshow(x_opt.reshape( (self._tr.header.img_h, self._tr.header.img_w)), cmap='gray') fig.savefig(fm.join(self.images_folder, self.filename), file_format='pdf')
def test_draw(self): """ Compare the classifiers graphically""" self.logger.info("Testing classifiers graphically") # generate 2D synthetic data dataset = CDLRandom(n_features=2, n_redundant=1, n_informative=1, n_clusters_per_class=1).load() dataset.X = CNormalizerMinMax().fit_transform(dataset.X) self.sgds[0].fit(dataset.X, dataset.Y) svm = CClassifierSVM() svm.fit(dataset.X, dataset.Y) fig = CFigure(width=10, markersize=8) fig.subplot(2, 1, 1) # Plot dataset points fig.sp.plot_ds(dataset) # Plot objective function fig.sp.plot_fun(svm.decision_function, grid_limits=dataset.get_bounds(), y=1) fig.sp.title('SVM') fig.subplot(2, 1, 2) # Plot dataset points fig.sp.plot_ds(dataset) # Plot objective function fig.sp.plot_fun(self.sgds[0].decision_function, grid_limits=dataset.get_bounds(), y=1) fig.sp.title('SGD Classifier') fig.savefig( fm.join(fm.abspath(__file__), 'figs', 'test_c_classifier_sgd1.pdf'))
def _test_2D(self, fun, grid_limits=None, levels=None, vmin=None, vmax=None, fun_args=()): """2D plot of the function. Parameters ---------- fun : CFunction grid_limits : list of tuple or None, optional levels : list or None, optional vmin, vmax : scalar or None, optional fun_args : tuple """ fun_name = fun.__class__.__name__ self.logger.info("Plotting 2D of {:}".format(fun_name)) fig = CFigure(width=7) fig.sp.plot_fun(func=fun.fun, plot_levels=True, grid_limits=grid_limits, levels=levels, n_grid_points=50, n_colors=200, vmin=vmin, vmax=vmax, func_args=fun_args) fig.sp.title(fun_name) fig.savefig( fm.join(fm.abspath(__file__), 'test_function_{:}.pdf'.format(fun.class_type)))
def _test_plot(self, c, *points): """Visualize the constraint. Parameters ---------- c : CConstraint *points : CArray A series of point to plot. Each point will be plotted before and after cosntraint projection. """ self.logger.info("Plotting constrain {:}".format(c.class_type)) grid_limits = [(-1, 2.5), (-1, 2.5)] fig = CFigure(height=6, width=6) fig.sp.plot_fun(func=c.constraint, grid_limits=grid_limits, n_grid_points=40, levels=[0], levels_linewidth=1.5) colors = ['g', 'r', 'c', 'm', 'y', 'k', 'w'] for p_i, p in enumerate(points): self.logger.info("Plotting point (color {:}): {:}".format( colors[p_i], p)) fig.sp.scatter(*p, c=colors[p_i], zorder=10) p_proj = c.projection(p) self.logger.info("Plotting point (color {:}): {:}".format( colors[p_i], p_proj)) fig.sp.scatter(*p_proj, c=colors[p_i], zorder=10) filename = "test_constraint_{:}.pdf".format(c.class_type) fig.savefig(fm.join(fm.abspath(__file__), filename))
fig.sp.plot_ds(tr, markers=".") # fig.sp.plot_path(pois_attack.x_seq) fig.sp.set_axisbelow(True) fig.sp._sp.set_aspect("equal") fig.sp.title("Bi-level Objective") fig.subplot(1, 2, 2) fig.sp.plot_fun( loss.loss, plot_levels=False, colorbar=False, multipoint=False, n_grid_points=50, grid_limits=[(lb, ub), (lb, ub)], ) # fig.sp.plot_fgrads(pois_attack._objective_function_gradient, # n_grid_points=20, grid_limits=[(-20, 20), (-20, 20)]) fig.sp.plot_decision_regions(clf, plot_background=False, n_grid_points=500, grid_limits=[(lb, ub), (lb, ub)]) fig.sp.grid(grid_on=True, linestyle=":", linewidth=0.5) fig.sp.plot_ds(tr, markers=".") # fig.sp.plot_path(pois_attack.x_seq) fig.sp.set_axisbelow(True) fig.sp._sp.set_aspect("equal") fig.sp.title("Our Objective") fig.savefig("poison_objectives.png") fig.close()
n_grid_points=200, grid_limits=[(-20, 20), (-20, 20)]) # fig.sp.plot_fgrads(pois_attack._objective_function_gradient, # n_grid_points=20, grid_limits=[(-20, 20), (-20, 20)]) fig.sp.plot_decision_regions(clf, plot_background=False, n_grid_points=500, grid_limits=[(-20, 20), (-20, 20)]) fig.sp.grid(grid_on=True, linestyle=':', linewidth=0.5) plot_data(fig, training_set) fig.sp.plot_path(pois_attack.x_seq) fig.sp.set_axisbelow(True) fig.sp._sp.set_aspect('equal') fig.sp.title("Attacker's Loss") fig.sp.legend() fig.savefig("loss.pdf") fig.close() fig = CFigure(width=width, height=height) fig.sp.plot_fun(disp_impact.objective_function, plot_levels=False, multipoint=False, n_grid_points=200, cmap='jet', vmin=0.158, vmax=0.178, grid_limits=[(-20, 20), (-20, 20)], yc=1) fig.sp.plot_decision_regions(clf, plot_background=False, n_grid_points=500,
def _plot_optimization(self, solver, x_0, g_min, grid_limits, method=None, vmin=None, vmax=None, label=None): """Plots the optimization problem. Parameters ---------- solver : COptimizer x_0 : CArray Starting point. g_min : CArray Final point (after optimization). grid_limits : list of tuple vmin, vmax : int or None, optional label : str or None, optional """ fig = CFigure(markersize=12) # Plot objective function fig.sp.plot_fun(func=CArray.apply_along_axis, plot_background=True, n_grid_points=30, n_colors=25, grid_limits=grid_limits, levels=[0.5], levels_color='gray', levels_style='--', colorbar=True, func_args=( solver.f.fun, 1, ), vmin=vmin, vmax=vmax) if solver.bounds is not None: # Plot box constraint fig.sp.plot_fun(func=lambda x: solver.bounds.constraint(x), plot_background=False, n_grid_points=20, grid_limits=grid_limits, levels=[0], colorbar=False) if solver.constr is not None: # Plot distance constraint fig.sp.plot_fun(func=lambda x: solver.constr.constraint(x), plot_background=False, n_grid_points=20, grid_limits=grid_limits, levels=[0], colorbar=False) # Plot optimization trace if solver.x_seq is not None: fig.sp.plot_path(solver.x_seq) else: fig.sp.plot_path(x_0.append(g_min, axis=0)) fig.sp.title("{:}(fun={:}) - Glob Min @ {:}".format( solver.class_type, solver.f.class_type, solver.f.global_min_x().round(2).tolist())) test_img_fold_name = 'test_images' test_img_fold_path = fm.join(fm.abspath(__file__), test_img_fold_name) if not fm.folder_exist(test_img_fold_path): fm.make_folder(test_img_fold_path) if method is None: filename = fm.join(test_img_fold_path, solver.class_type + '-' + solver.f.class_type) else: filename = fm.join( test_img_fold_path, solver.class_type + '-' + method + '-' + solver.f.class_type) filename += '-' + label if label is not None else '' test_img_fold_name = 'test_images' if not fm.folder_exist(test_img_fold_name): fm.make_folder(test_img_fold_name) fig.savefig('{:}.pdf'.format(filename))
def _make_plots(self, x_seq, dmax, eva, x0, scores, f_seq): if self.make_figures is False: self.logger.debug("Skipping figures...") return fig = CFigure(height=9, width=10, markersize=6, fontsize=12) # Get plot bounds, taking into account ds and evaded point path bounds_x, bounds_y = self.ds.get_bounds() min_x, max_x = bounds_x min_y, max_y = bounds_y min_x = min(min_x, x_seq[:, 0].min()) max_x = max(max_x, x_seq[:, 0].max()) min_y = min(min_y, x_seq[:, 1].min()) max_y = max(max_y, x_seq[:, 1].max()) ds_bounds = [(min_x, max_x), (min_y, max_y)] # Plotting multiclass decision regions fig.subplot(2, 2, 1) fig = self._plot_decision_function(fig, plot_background=True) fig.sp.plot_path(x_seq, path_style='-', start_style='o', start_facecolor='w', start_edgewidth=2, final_style='o', final_facecolor='k', final_edgewidth=2) # plot distance constraint fig.sp.plot_fun(func=self._rescaled_distance, multipoint=True, plot_background=False, n_grid_points=20, levels_color='k', grid_limits=ds_bounds, levels=[0], colorbar=False, levels_linewidth=2.0, levels_style=':', alpha_levels=.4, c=x0, r=dmax) fig.sp.grid(linestyle='--', alpha=.5, zorder=0) # Plotting multiclass evasion objective function fig.subplot(2, 2, 2) fig = self._plot_decision_function(fig) fig.sp.plot_fgrads(eva._objective_function_gradient, grid_limits=ds_bounds, n_grid_points=20, color='k', alpha=.5) fig.sp.plot_path(x_seq, path_style='-', start_style='o', start_facecolor='w', start_edgewidth=2, final_style='o', final_facecolor='k', final_edgewidth=2) # plot distance constraint fig.sp.plot_fun(func=self._rescaled_distance, multipoint=True, plot_background=False, n_grid_points=20, levels_color='w', grid_limits=ds_bounds, levels=[0], colorbar=False, levels_style=':', levels_linewidth=2.0, alpha_levels=.5, c=x0, r=dmax) fig.sp.plot_fun(lambda z: eva._objective_function(z), multipoint=True, grid_limits=ds_bounds, colorbar=False, n_grid_points=20, plot_levels=False) fig.sp.grid(linestyle='--', alpha=.5, zorder=0) fig.subplot(2, 2, 3) if self.y_target is not None: fig.sp.title("Classifier Score for Target Class (Targ. Evasion)") else: fig.sp.title("Classifier Score for True Class (Indiscr. Evasion)") fig.sp.plot(scores) fig.sp.grid() fig.sp.xlim(0, dmax) fig.sp.xlabel("dmax") fig.subplot(2, 2, 4) fig.sp.title("Objective Function") fig.sp.plot(f_seq) fig.sp.grid() fig.sp.xlim(0, dmax) fig.sp.xlabel("dmax") fig.tight_layout() k_name = self.kernel.class_type if self.kernel is not None else 'lin' fig.savefig( fm.join( self.images_folder, "pgd_ls_multiclass_{:}c_kernel-{:}_target-{:}.pdf".format( self.ds.num_classes, k_name, self.y_target)))
def _make_plot(self, p_idx, eva, dmax): if self.make_figures is False: self.logger.debug("Skipping figures...") return x0 = self.ds.X[p_idx, :] y0 = self.ds.Y[p_idx].item() x_seq = CArray.empty((0, x0.shape[1])) scores = CArray([]) f_seq = CArray([]) x = x0 for d_idx, d in enumerate(range(0, dmax + 1)): self.logger.info("Evasion at dmax: " + str(d)) eva.dmax = d x, f_opt = eva._run(x0=x0, y0=y0, x_init=x) y_pred, score = self.multiclass.predict( x, return_decision_function=True) f_seq = f_seq.append(f_opt) # not considering all iterations, just values at dmax # for all iterations, you should bring eva.x_seq and eva.f_seq x_seq = x_seq.append(x, axis=0) s = score[:, y0 if self.y_target is None else self.y_target] scores = scores.append(s) self.logger.info("Predicted label after evasion: {:}".format(y_pred)) self.logger.info("Score after evasion: {:}".format(s)) self.logger.info("Objective function after evasion: {:}".format(f_opt)) fig = CFigure(height=9, width=10, markersize=6, fontsize=12) # Get plot bounds, taking into account ds and evaded point path bounds_x, bounds_y = self.ds.get_bounds() min_x, max_x = bounds_x min_y, max_y = bounds_y min_x = min(min_x, x_seq[:, 0].min()) max_x = max(max_x, x_seq[:, 0].max()) min_y = min(min_y, x_seq[:, 1].min()) max_y = max(max_y, x_seq[:, 1].max()) ds_bounds = [(min_x, max_x), (min_y, max_y)] # Plotting multiclass decision regions fig.subplot(2, 2, 1) fig = self._plot_decision_function(fig, plot_background=True) fig.sp.plot_path(x_seq, path_style='-', start_style='o', start_facecolor='w', start_edgewidth=2, final_style='o', final_facecolor='k', final_edgewidth=2) # plot distance constraint fig.sp.plot_fun(func=self._rescaled_distance, multipoint=True, plot_background=False, n_grid_points=20, levels_color='k', grid_limits=ds_bounds, levels=[0], colorbar=False, levels_linewidth=2.0, levels_style=':', alpha_levels=.4, c=x0, r=dmax) fig.sp.grid(linestyle='--', alpha=.5, zorder=0) # Plotting multiclass evasion objective function fig.subplot(2, 2, 2) fig = self._plot_decision_function(fig) fig.sp.plot_fgrads(eva.objective_function_gradient, grid_limits=ds_bounds, n_grid_points=20, color='k', alpha=.5) fig.sp.plot_path(x_seq, path_style='-', start_style='o', start_facecolor='w', start_edgewidth=2, final_style='o', final_facecolor='k', final_edgewidth=2) # plot distance constraint fig.sp.plot_fun(func=self._rescaled_distance, multipoint=True, plot_background=False, n_grid_points=20, levels_color='w', grid_limits=ds_bounds, levels=[0], colorbar=False, levels_style=':', levels_linewidth=2.0, alpha_levels=.5, c=x0, r=dmax) fig.sp.plot_fun(lambda z: eva.objective_function(z), multipoint=True, grid_limits=ds_bounds, colorbar=False, n_grid_points=20, plot_levels=False) fig.sp.grid(linestyle='--', alpha=.5, zorder=0) fig.subplot(2, 2, 3) if self.y_target is not None: fig.sp.title("Classifier Score for Target Class (Targ. Evasion)") else: fig.sp.title("Classifier Score for True Class (Indiscr. Evasion)") fig.sp.plot(scores) fig.sp.grid() fig.sp.xlim(0, dmax) fig.sp.xlabel("dmax") fig.subplot(2, 2, 4) fig.sp.title("Objective Function") fig.sp.plot(f_seq) fig.sp.grid() fig.sp.xlim(0, dmax) fig.sp.xlabel("dmax") fig.tight_layout() k_name = self.kernel.class_type if self.kernel is not None else 'lin' fig.savefig( fm.join( self.images_folder, "pgd_ls_reject_threshold_{:}c_kernel-{:}_target-{:}.pdf". format(self.ds.num_classes, k_name, self.y_target)))