def __init__(self, smtlib_property: SMTLIBProperty, x_name: str, y_name: str): self.smtlib_property = smtlib_property self.x_name = x_name self.y_name = y_name self.x = self.get_components_of(self.x_name) self.y = self.get_components_of(self.y_name) self.in_coef_mat = Tensor([]) self.in_bias_mat = Tensor([]) self.out_coef_mat = [] self.out_bias_mat = []
def text_to_tensor_set(text: str) -> tuple: """ This method takes a string in format "(n,m,l), (n,m,l), ..." where n, m, l are integers and converts it into a tuple containing the set of Tensors with the given dimensions. Parameters ---------- text: str Input string to convert. Returns ---------- tuple The converted tensor set. """ tensors = tuple() temp = tuple() for token in text.split(", "): num = int(token.replace("(", "").replace(")", "")) temp += (num, ) if ")" in token: tensors += (Tensor(shape=temp, buffer=np.random.normal(size=temp)), ) return tensors
def text_to_tensor(text: str) -> Tensor: """ This method takes a string in format "n,m,l" with n, m, l are integers and converts it into a Tensor with the given dimensions. Parameters ---------- text : str Input string to convert. Returns ---------- Tensor The converted tensor. """ dims = () num = 0 for c in text: if '0' <= c <= '9': num = (num * 10) + int(c) else: dims += (num, ) num = 0 dims += (num, ) return Tensor(shape=dims, buffer=np.random.normal(size=dims))
def compute_saliency(net: networks.NeuralNetwork, ref_input: Tensor): class BackHook: def __init__(self, module: torch.nn.Module, backward=True): if backward: self.hook = module.register_backward_hook(self.hook_fn) else: self.hook = module.register_forward_hook(self.hook_fn) self.m_input = None self.m_output = None def hook_fn(self, module, m_input, m_output): self.m_input = m_input self.m_output = m_output def close(self): self.hook.remove() py_net = cv.PyTorchConverter().from_neural_network(net).pytorch_network # We register the hooks on the modules of the networks backward_hooks = [BackHook(layer) for layer in py_net.modules()] forward_hooks = [BackHook(layer, False) for layer in py_net.modules()] ref_input = torch.from_numpy(ref_input) ref_input.requires_grad = True out = py_net(ref_input) i = 0 print("FORWARD HOOKS") for m in py_net.modules(): hook = forward_hooks[i] print(m) print("INPUT") print(hook.m_input) print("OUTPUT") print(hook.m_output) i = i + 1 for k in range(len(out)): print(f"Variable {k} of output") out = py_net(ref_input) out[k].backward(retain_graph=True) print("INPUT GRAD:" + f"{ref_input.grad}") i = 0 for m in py_net.modules(): hook = backward_hooks[i] print(m) print("INPUT") print(hook.m_input[0]) print("OUTPUT") print(hook.m_output[0]) i = i + 1 print(out) ref_input[0] = ref_input[0] + 10 out = py_net(ref_input) print(out)
def generate_untargeted_linf_robustness_query(data: Tensor, target: int, bounds: tuple, num_classes: int, epsilon: float, filepath: str): """ Function to generate an untargeted Robustness SMTLIB query and to save it to a SMTLIB file. The robustness query is of the kind based on the infinity norm. It assumes that the data and target are from a classification task. Parameters ---------- data : Tensor Input data of interest. adv_target : int Desired adversarial target for the input data. bounds : (int, int) Bounds for the input data (lower_bound, upper_bound). num_classes : int Number of possible classes. epsilon : float Perturbation with respect to the infinity norm. filepath : str Filepath for the resulting SMTLIB file. """ with open(filepath, "w") as f: flattened_data = data.flatten() for i in range(len(flattened_data)): f.write(f"(declare-const X_{i} Real)\n") for i in range(num_classes): f.write(f"(declare-const Y_{i} Real)\n") for i in range(len(flattened_data)): if flattened_data[i] - epsilon < bounds[0]: f.write(f"(assert (>= X_{i} {bounds[0]}))\n") else: f.write(f"(assert (>= X_{i} {flattened_data[i] - epsilon}))\n") if flattened_data[i] + epsilon > bounds[1]: f.write(f"(assert (<= X_{i} {bounds[1]}))\n") else: f.write(f"(assert (<= X_{i} {flattened_data[i] + epsilon}))\n") output_query = "(assert (or" for i in range(num_classes): if i != target: output_query += f" (<= (- Y_{target} Y_{i}) 0)" output_query += "))" f.write(output_query)
def edit_node(self, block: NodeBlock): """ This method propagates the changes stored in the block.edits attribute. Parameters ---------- block : NodeBlock The block to update. """ edits = block.edits if edits is not None and block.block_id in self.renderer.disconnected_network.keys( ): edit_node_id = edits[0] edit_data = edits[1] new_in_dim = None if "in_dim" in edit_data.keys(): new_in_dim = edit_data["in_dim"] # If in_dim changes in FC, update in_features if block.node.name == 'Fully Connected' and new_in_dim is not None: edit_data["in_features"] = new_in_dim[-1] # If out_features changes in FC, update out_dim if block.node.name == 'Fully Connected' and 'out_features' in edit_data.keys( ): edit_data["out_dim"] = block.out_dim[:-1] + ( edit_data["out_features"], ) for block_par, info in block.node.param.items(): if "shape" in info: str_shape = tuple(map(str, info["shape"].split(', '))) new_shape = tuple() # Confront for dim in str_shape: new_dim = block.block_data[dim] if dim in edit_data: new_dim = edit_data[dim] if isinstance(new_dim, tuple): new_shape += new_dim else: new_shape += (new_dim, ) # Add new Tensor value to edit_data edit_data[block_par] = Tensor( shape=new_shape, buffer=np.random.normal(size=new_shape)) try: # Check if the network has changed self.renderer.edit_node(edit_node_id, edit_data) except Exception as e: dialog = MessageDialog( str(e) + "\nImpossible to propagate changes.", MessageType.ERROR) dialog.exec() # Update the graphic block self.renderer.disconnected_network[edit_node_id].update_labels() # Update dimensions in edges & nodes for line in self.scene.edges: if isinstance(self.scene.blocks[line.origin], NodeBlock): origin_id = self.scene.blocks[line.origin].block_id new_dim = self.renderer.NN.nodes[origin_id].out_dim line.update_dims(new_dim) self.scene.blocks[line.origin].out_dim = new_dim self.scene.blocks[line.destination].in_dim = new_dim # Empty changes buffer block.edits = None
def init_layout(self) -> None: """ This method sets up the the node block main_layout with attributes and values. """ self.init_grid() # Iterate and display parameters, count rows par_labels = dict() count = 1 for name, param in self.node.param.items(): # Set the label par_labels[name] = CustomLabel(name) par_labels[name].setAlignment(Qt.AlignLeft) par_labels[name].setStyleSheet(style.PAR_NODE_STYLE) self.dim_labels[name] = CustomLabel() self.dim_labels[name].setAlignment(Qt.AlignCenter) self.dim_labels[name].setStyleSheet(style.DIM_NODE_STYLE) self.content_layout.addWidget(par_labels[name], count, 0) self.content_layout.addWidget(self.dim_labels[name], count, 1) count += 1 # Init block data with default values if "default" in param.keys() and param["default"] == "None": self.block_data[name] = None elif param["type"] == "Tensor": if "shape" in param.keys(): shape = self.text_to_tuple(param["shape"]) self.block_data[name] = Tensor( shape=shape, buffer=np.random.normal(size=shape)) else: self.block_data[name] = Tensor( shape=(1, 1), buffer=np.random.normal(size=(1, 1))) elif param["type"] == "int": if "default" in param.keys(): if param["default"] == "rand": self.block_data[name] = 1 else: self.block_data[name] = int(param["default"]) else: self.block_data[name] = 1 elif param["type"] == "list of ints": if "default" in param.keys(): self.block_data[name] = tuple( map(int, param["default"].split(', '))) else: self.block_data[name] = (1, 1) elif param["type"] == "float": if "default" in param.keys(): if param["default"] == "rand": self.block_data[name] = 1 else: self.block_data[name] = float(param["default"]) else: self.block_data[name] = 0.1 elif param["type"] == "boolean": if "default" in param.keys(): self.block_data[name] = bool(param["default"]) else: self.block_data[name] = False self.update_labels() self.edited.connect(lambda: self.update_labels())