├── DFA.py ├── Extraction.py ├── GRU.py ├── Helper_Functions.py ├── LSTM.py ├── LinearTransform.py ├── Lstar.py ├── ObservationTable.py ├── Quantisations.py ├── README.md ├── RNNClassifier.py ├── Specific_Language_Generation.py ├── Teacher.py ├── Tomita_Grammars.py ├── Training_Functions.py ├── WhiteboxRNNCounterexampleGenerator.py ├── dfa_from_rnn.ipynb ├── dfa_from_rnn_no_documentation.ipynb └── dfa_from_rnn_notebook_for_several_rnns.ipynb /DFA.py: -------------------------------------------------------------------------------- 1 | import graphviz as gv 2 | from IPython.display import Image 3 | from IPython.display import display 4 | import functools 5 | from copy import deepcopy, copy 6 | import itertools 7 | import Lstar 8 | from random import randint, shuffle 9 | import random 10 | from time import clock 11 | import string 12 | 13 | digraph = functools.partial(gv.Digraph, format='png') 14 | graph = functools.partial(gv.Graph, format='png') 15 | 16 | separator = "_" 17 | 18 | class DFA: 19 | def __init__(self,obs_table): 20 | self.alphabet = obs_table.A #alphabet 21 | self.Q = [s for s in obs_table.S if s==obs_table.minimum_matching_row(s)] #avoid duplicate states 22 | self.q0 = obs_table.minimum_matching_row("") 23 | self.F = [s for s in self.Q if obs_table.T[s]== 1] 24 | self._make_transition_function(obs_table) 25 | 26 | def _make_transition_function(self,obs_table): 27 | self.delta = {} 28 | for s in self.Q: 29 | self.delta[s] = {} 30 | for a in self.alphabet: 31 | self.delta[s][a] = obs_table.minimum_matching_row(s+a) 32 | 33 | def classify_word(self,word): 34 | #assumes word is string with only letters in alphabet 35 | q = self.q0 36 | for a in word: 37 | q = self.delta[q][a] 38 | return q in self.F 39 | 40 | def draw_nicely(self,force=False,maximum=60): #todo: if two edges are identical except for letter, merge them and note both the letters 41 | if (not force) and len(self.Q) > maximum: 42 | return 43 | 44 | #suspicion: graphviz may be upset by certain sequences, avoid them in nodes 45 | label_to_number_dict = {False:0} #false is never a label but gets us started 46 | def label_to_numberlabel(label): 47 | max_number = max(label_to_number_dict[l] for l in label_to_number_dict) 48 | if not label in label_to_number_dict: 49 | label_to_number_dict[label] = max_number + 1 50 | return str(label_to_number_dict[label]) 51 | 52 | def add_nodes(graph, nodes): #stolen from http://matthiaseisen.com/articles/graphviz/ 53 | for n in nodes: 54 | if isinstance(n, tuple): 55 | graph.node(n[0], **n[1]) 56 | else: 57 | graph.node(n) 58 | return graph 59 | 60 | def add_edges(graph, edges): #stolen from http://matthiaseisen.com/articles/graphviz/ 61 | for e in edges: 62 | if isinstance(e[0], tuple): 63 | graph.edge(*e[0], **e[1]) 64 | else: 65 | graph.edge(*e) 66 | return graph 67 | 68 | g = digraph() 69 | g = add_nodes(g, [(label_to_numberlabel(self.q0), {'color':'green' if self.q0 in self.F else 'black', 70 | 'shape': 'hexagon', 'label':'start'})]) 71 | states = list(set(self.Q)-{self.q0}) 72 | g = add_nodes(g, [(label_to_numberlabel(state),{'color': 'green' if state in self.F else 'black', 73 | 'label': str(i)}) 74 | for state,i in zip(states,range(1,len(states)+1))]) 75 | 76 | def group_edges(): 77 | def clean_line(line,group): 78 | line = line.split(separator) 79 | line = sorted(line) + ["END"] 80 | in_sequence= False 81 | last_a = "" 82 | clean = line[0] 83 | if line[0] in group: 84 | in_sequence = True 85 | first_a = line[0] 86 | last_a = line[0] 87 | for a in line[1:]: 88 | if in_sequence: 89 | if a in group and (ord(a)-ord(last_a))==1: #continue sequence 90 | last_a = a 91 | else: #break sequence 92 | #finish sequence that was 93 | if (ord(last_a)-ord(first_a))>1: 94 | clean += ("-" + last_a) 95 | elif not last_a == first_a: 96 | clean += (separator + last_a) 97 | #else: last_a==first_a -- nothing to add 98 | in_sequence = False 99 | #check if there is a new one 100 | if a in group: 101 | first_a = a 102 | last_a = a 103 | in_sequence = True 104 | if not a=="END": 105 | clean += (separator + a) 106 | else: 107 | if a in group: #start sequence 108 | first_a = a 109 | last_a = a 110 | in_sequence = True 111 | if not a=="END": 112 | clean += (separator+a) 113 | return clean 114 | 115 | 116 | edges_dict = {} 117 | for state in self.Q: 118 | for a in self.alphabet: 119 | edge_tuple = (label_to_numberlabel(state),label_to_numberlabel(self.delta[state][a])) 120 | # print(str(edge_tuple)+" "+a) 121 | if not edge_tuple in edges_dict: 122 | edges_dict[edge_tuple] = a 123 | else: 124 | edges_dict[edge_tuple] += separator+a 125 | # print(str(edge_tuple)+" = "+str(edges_dict[edge_tuple])) 126 | for et in edges_dict: 127 | edges_dict[et] = clean_line(edges_dict[et], string.ascii_lowercase) 128 | edges_dict[et] = clean_line(edges_dict[et], string.ascii_uppercase) 129 | edges_dict[et] = clean_line(edges_dict[et], "0123456789") 130 | edges_dict[et] = edges_dict[et].replace(separator,",") 131 | return edges_dict 132 | 133 | edges_dict = group_edges() 134 | g = add_edges(g,[(e,{'label':edges_dict[e]}) for e in edges_dict]) 135 | # print('\n'.join([str(((str(state),str(self.delta[state][a])),{'label':a})) for a in self.alphabet for state in 136 | # self.Q])) 137 | # g = add_edges(g,[((label_to_numberlabel(state),label_to_numberlabel(self.delta[state][a])),{'label':a}) 138 | # for a in self.alphabet for state in self.Q]) 139 | display(Image(filename=g.render(filename='img/automaton'))) 140 | 141 | def minimal_diverging_suffix(self,state1,state2): #gets series of letters showing the two states are different, 142 | # i.e., from which one state reaches accepting state and the other reaches rejecting state 143 | # assumes of course that the states are in the automaton and actually not equivalent 144 | res = None 145 | # just use BFS til you reach an accepting state 146 | # after experiments: attempting to use symmetric difference on copies with s1,s2 as the starting state, or even 147 | # just make and minimise copies of this automaton starting from s1 and s2 before starting the BFS, 148 | # is slower than this basic BFS, so don't 149 | seen_states = set() 150 | new_states = {("",(state1,state2))} 151 | while len(new_states) > 0: 152 | prefix,state_pair = new_states.pop() 153 | s1,s2 = state_pair 154 | if len([q for q in [s1,s2] if q in self.F])== 1: # intersection of self.F and [s1,s2] is exactly one state, 155 | # meaning s1 and s2 are classified differently 156 | res = prefix 157 | break 158 | seen_states.add(state_pair) 159 | for a in self.alphabet: 160 | next_state_pair = (self.delta[s1][a],self.delta[s2][a]) 161 | next_tuple = (prefix+a,next_state_pair) 162 | if not next_tuple in new_states and not next_state_pair in seen_states: 163 | new_states.add(next_tuple) 164 | return res -------------------------------------------------------------------------------- /Extraction.py: -------------------------------------------------------------------------------- 1 | from time import clock 2 | from ObservationTable import TableTimedOut 3 | from DFA import DFA 4 | from Teacher import Teacher 5 | from Lstar import run_lstar 6 | 7 | def extract(rnn,time_limit = 50,initial_split_depth = 10,starting_examples=None): 8 | print("provided counterexamples are:",starting_examples) 9 | guided_teacher = Teacher(rnn,num_dims_initial_split=initial_split_depth,starting_examples=starting_examples) 10 | start = clock() 11 | try: 12 | run_lstar(guided_teacher,time_limit) 13 | except KeyboardInterrupt: #you can press the stop button in the notebook to stop the extraction any time 14 | print("lstar extraction terminated by user") 15 | except TableTimedOut: 16 | print("observation table timed out during refinement") 17 | end = clock() 18 | extraction_time = end-start 19 | 20 | dfa = guided_teacher.dfas[-1] 21 | 22 | print("overall guided extraction time took: " + str(extraction_time)) 23 | 24 | print("generated counterexamples were: (format: (counterexample, counterexample generation time))") 25 | print('\n'.join([str(a) for a in guided_teacher.counterexamples_with_times])) 26 | return dfa -------------------------------------------------------------------------------- /GRU.py: -------------------------------------------------------------------------------- 1 | import dynet as dy 2 | from Helper_Functions import map_nested_dict 3 | 4 | class GRUCell: 5 | def __init__(self,input_dim,output_dim,pc): 6 | self.input_dim = input_dim 7 | self.output_dim = output_dim 8 | self.pc = pc 9 | self.gate_names = ["z","r","htilde"] 10 | self.gate_activations = [dy.logistic]*2+[dy.tanh] 11 | #todo: generalise to layers 12 | self.parameters = {"W":{"x":{},"h":{}},"b":{}} 13 | for gate in self.gate_names: 14 | self.parameters["W"]["x"][gate] = self.pc.add_parameters((self.output_dim,self.input_dim)) 15 | self.parameters["W"]["h"][gate] = self.pc.add_parameters((self.output_dim,self.output_dim)) #takes its own previous output 16 | self.parameters["b"][gate] = self.pc.add_parameters((self.output_dim)) 17 | self.parameters["h0"] = self.pc.add_parameters((self.output_dim)) 18 | self.parameters["h0"].clip_inplace(-1,1) 19 | 20 | self.store_expressions() 21 | 22 | 23 | def store_expressions(self): 24 | self.expressions = map_nested_dict(self.parameters,dy.parameter) 25 | self.parameters["h0"].clip_inplace(-1,1) 26 | self.initial_h = self.parameters["h0"].expr() 27 | 28 | 29 | 30 | def gate_vecs(self,ht1,xt): 31 | b = self.expressions["b"] 32 | W = self.expressions["W"] 33 | gate_vecs = {} 34 | for g,activation in zip(self.gate_names,self.gate_activations): 35 | hin = ht1 if not g=="htilde" else dy.cmult(gate_vecs["r"],ht1) 36 | gate_vecs[g] = activation(dy.affine_transform([b[g], 37 | W["x"][g],xt, 38 | W["h"][g],ht1])) 39 | return gate_vecs 40 | 41 | def gate_and_next_vecs(self,ht1,xt): 42 | v = self.gate_vecs(ht1,xt) 43 | h = dy.cmult(v["z"],ht1)+dy.cmult(1-v["z"],v["htilde"]) 44 | res = v 45 | res.update({"h":h}) 46 | return res 47 | 48 | from functools import reduce 49 | from operator import add 50 | class GRUNetworkState: 51 | def __init__(self,hs=None,full_vec=None,hidden_dim=None): 52 | if not None in [full_vec,hidden_dim]: 53 | hvec = full_vec 54 | self.hs = [dy.inputVector(hvec[i*hidden_dim:(i+1)*hidden_dim]) for i in range(int(len(hvec)/hidden_dim))] 55 | elif not None in [hs]: 56 | self.hs = hs #list of h expressions 57 | else: 58 | raise MissingInput() 59 | 60 | def output(self): 61 | return self.hs[-1] 62 | 63 | def as_vec(self): 64 | return reduce(add,[h.value() for h in self.hs]) 65 | # return np.concatenate([h.npvalue() for h in self.hs]).tolist() 66 | 67 | 68 | class GRUNetwork: 69 | def __init__(self,num_layers=None,input_dim=None,hidden_dim=None,pc=None,output_dim=None): 70 | if None in [num_layers,input_dim,hidden_dim,pc] or (num_layers <= 0): 71 | raise MissingInput() 72 | if None is output_dim: 73 | output_dim = hidden_dim 74 | 75 | self.num_layers = num_layers 76 | self.input_dim = input_dim 77 | self.hidden_dim = hidden_dim 78 | self.output_dim = output_dim 79 | self.pc = pc 80 | self.state_class = GRUNetworkState 81 | 82 | self.layers = [] 83 | if self.num_layers > 1: 84 | self.layers.append(GRUCell(self.input_dim,self.hidden_dim,self.pc)) 85 | for _ in range(num_layers-2): 86 | self.layers.append(GRUCell(self.hidden_dim,self.hidden_dim,self.pc)) 87 | self.layers.append(GRUCell(self.hidden_dim,self.output_dim,self.pc)) 88 | else: 89 | self.layers.append(GRUCell(self.input_dim,self.output_dim,self.pc)) 90 | 91 | def all_gate_and_next_vecs(self,state,input_vec): 92 | res = [] 93 | x = input_vec 94 | for layer,h in zip(self.layers,state.hs): 95 | res.append(layer.gate_and_next_vecs(h,x)) 96 | x = res[-1]["h"] #output of one layer is input to the next 97 | return res 98 | 99 | def next_state(self,state,input_vec): 100 | v = self.all_gate_and_next_vecs(state,input_vec) 101 | hs = [lvals["h"] for lvals in v] 102 | return GRUNetworkState(hs=hs) 103 | 104 | def store_expressions(self): 105 | for l in self.layers: 106 | l.store_expressions() 107 | self.initial_state = GRUNetworkState(hs=[l.initial_h for l in self.layers]) -------------------------------------------------------------------------------- /Helper_Functions.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import random 3 | 4 | def mean(num_list): 5 | return sum(num_list)*1.0/len(num_list) 6 | 7 | def n_words_of_length(n,length,alphabet): 8 | if 50*n >= pow(len(alphabet),length): 9 | res = all_words_of_length(length, alphabet) 10 | random.shuffle(res) 11 | return res[:n] 12 | #else if 50*n < total words to be found, i.e. looking for 1/50th of the words or less 13 | res = set() 14 | while len(res) 1: 87 | self.layers.append(LSTMCell(self.input_dim,self.hidden_dim,self.pc)) 88 | for _ in range(num_layers-2): 89 | self.layers.append(LSTMCell(self.hidden_dim,self.hidden_dim,self.pc)) 90 | self.layers.append(LSTMCell(self.hidden_dim,self.output_dim,self.pc)) 91 | else: 92 | self.layers.append(LSTMCell(self.input_dim,self.output_dim,self.pc)) 93 | 94 | def all_gate_and_next_vecs(self,state,input_vec): 95 | res = [] 96 | x = input_vec 97 | for layer,h,c in zip(self.layers,state.hs,state.cs): 98 | res.append(layer.gate_and_next_vecs(h,c,x)) 99 | x = res[-1]["h"] #output of one layer is input to the next 100 | return res 101 | 102 | def next_state(self,state,input_vec): 103 | v = self.all_gate_and_next_vecs(state,input_vec) 104 | hs = [lvals["h"] for lvals in v] 105 | cs = [lvals["c"] for lvals in v] 106 | return LSTMNetworkState(cs=cs,hs=hs) 107 | 108 | def store_expressions(self): 109 | for l in self.layers: 110 | l.store_expressions() 111 | self.initial_state = LSTMNetworkState(cs=[l.initial_c for l in self.layers], 112 | hs=[l.initial_h for l in self.layers]) 113 | -------------------------------------------------------------------------------- /LinearTransform.py: -------------------------------------------------------------------------------- 1 | import dynet as dy 2 | 3 | class LinearTransform: 4 | def __init__(self,input_dim,output_dim,pc): 5 | self.W_param = pc.add_parameters((output_dim, input_dim)) #parameter object 6 | self.b_param = pc.add_parameters((output_dim)) #parameter object 7 | self.store_expressions() 8 | 9 | def store_expressions(self): 10 | self.W = dy.parameter(self.W_param) 11 | self.bias = dy.parameter(self.b_param) 12 | 13 | def apply(self,input_vec_expression): 14 | return self.W*input_vec_expression + self.bias -------------------------------------------------------------------------------- /Lstar.py: -------------------------------------------------------------------------------- 1 | from ObservationTable import ObservationTable 2 | import DFA 3 | from time import clock 4 | 5 | def run_lstar(teacher,time_limit): 6 | table = ObservationTable(teacher.alphabet,teacher) 7 | start = clock() 8 | teacher.counterexample_generator.set_time_limit(time_limit,start) 9 | table.set_time_limit(time_limit,start) 10 | 11 | while True: 12 | while True: 13 | while table.find_and_handle_inconsistency(): 14 | pass 15 | if table.find_and_close_row(): 16 | continue 17 | else: 18 | break 19 | dfa = DFA.DFA(obs_table=table) 20 | print("obs table refinement took " + str(int(1000*(clock()-start))/1000.0) ) 21 | counterexample = teacher.equivalence_query(dfa) 22 | if None is counterexample: 23 | break 24 | start = clock() 25 | table.add_counterexample(counterexample,teacher.classify_word(counterexample)) 26 | return dfa -------------------------------------------------------------------------------- /ObservationTable.py: -------------------------------------------------------------------------------- 1 | from time import clock 2 | 3 | class TableTimedOut(Exception): 4 | pass 5 | 6 | 7 | class ObservationTable: 8 | def __init__(self,alphabet,interface,max_table_size=None): 9 | self.S = {""} #starts. invariant: prefix closed 10 | self.E = {""} #ends. invariant: suffix closed 11 | self.T = interface.recorded_words #{} #T: (S cup (S dot A)) dot E -> {True,False}, might also have more info if 12 | # interface remembers more, but this is not harmful so long as it contains what it needs 13 | self.A = alphabet #alphabet 14 | self.interface = interface 15 | self._fill_T() 16 | self._initiate_row_equivalence_cache() 17 | self.max_table_size = max_table_size 18 | self.time_limit = None 19 | 20 | def set_time_limit(self,time_limit,start): 21 | self.time_limit = time_limit 22 | self.start = start 23 | 24 | def _fill_T(self,new_e_list=None,new_s=None): #modifies, and involved in every kind of modification. modification: store more words 25 | self.interface.update_words(self._Trange(new_e_list,new_s)) 26 | 27 | def _Trange(self,new_e_list,new_s): # T: (S cup (S dot A)) dot E -> {True,False} #doesn't modify 28 | E = self.E if None is new_e_list else new_e_list 29 | starts = self.S | self._SdotA() if None is new_s else [new_s+a for a in (list(self.A)+[""])] 30 | return set([s+e for s in starts for e in E]) 31 | 32 | def _SdotA(self): #doesn't modify 33 | return set([s+a for s in self.S for a in self.A]) 34 | 35 | def _initiate_row_equivalence_cache(self): 36 | self.equal_cache = set() #subject to change 37 | for s1 in self.S: 38 | for s2 in self.S: 39 | for a in list(self.A)+[""]: 40 | if self._rows_are_same(s1+a,s2): 41 | self.equal_cache.add((s1+a,s2)) 42 | 43 | def _update_row_equivalence_cache(self,new_e=None,new_s=None): #just fixes cache. in case of new_e - only makes it smaller 44 | if not None is new_e: 45 | remove = [(s1,s2) for s1,s2 in self.equal_cache if not self.T[s1+new_e]==self.T[s2+new_e]] 46 | self.equal_cache = self.equal_cache.difference(remove) 47 | else: #new_s != None, or a bug! 48 | for s in self.S: 49 | for a in (list(self.A) + [""]): 50 | if self._rows_are_same(s+a,new_s): 51 | self.equal_cache.add((s+a,new_s)) 52 | if self._rows_are_same(new_s+a,s): 53 | self.equal_cache.add((new_s+a,s)) 54 | 55 | def _rows_are_same(self, s, t): #doesn't modify 56 | # row(s) = f:E->{0,1} where f(e)=T(se) 57 | return None is next((e for e in self.E if not self.T[s+e]==self.T[t+e]),None) 58 | 59 | def all_live_rows(self): 60 | return [s for s in self.S if s == self.minimum_matching_row(s)] 61 | 62 | def minimum_matching_row(self,t): #doesn't modify 63 | #to be used by automaton constructor once the table is closed 64 | #not actually minimum length but so long as we're all sorting them by something then whatever 65 | return next(s for s in self.S if (t,s) in self.equal_cache) 66 | 67 | def _assert_not_timed_out(self): 68 | if not None is self.time_limit: 69 | if clock()-self.start > self.time_limit: 70 | print("obs table timed out") 71 | raise TableTimedOut() # whatever, can't be bothered rn 72 | 73 | def find_and_handle_inconsistency(self): #modifies - and whenever it does, calls _fill_T 74 | #returns whether table was inconsistent 75 | maybe_inconsistent = [(s1,s2,a) for s1,s2 in self.equal_cache if s1 in self.S for a in self.A 76 | if not (s1+a,s2+a) in self.equal_cache] 77 | troublemakers = [a+e for s1,s2,a in maybe_inconsistent for e in 78 | [next((e for e in self.E if not self.T[s1+a+e]==self.T[s2+a+e]),None)] if not None is e] 79 | if len(troublemakers) == 0: 80 | return False 81 | self.E.add(troublemakers[0]) 82 | self._fill_T(new_e_list=troublemakers) # optimistic batching for queries - (hopefully) most of these will become relevant later 83 | self._update_row_equivalence_cache(troublemakers[0]) 84 | self._assert_not_timed_out() 85 | return True 86 | 87 | def find_and_close_row(self): #modifies - and whenever it does, calls _fill_T 88 | #returns whether table was unclosed 89 | s1a = next((s1+a for s1 in self.S for a in self.A if not [s for s in self.S if (s1+a,s) in self.equal_cache]),None) 90 | if None is s1a: 91 | return False 92 | self.S.add(s1a) 93 | self._fill_T(new_s=s1a) 94 | self._update_row_equivalence_cache(new_s=s1a) 95 | self._assert_not_timed_out() 96 | return True 97 | 98 | def add_counterexample(self,ce,label): #modifies - and definitely calls _fill_T 99 | if ce in self.S: 100 | print("bad counterexample - already saved and classified in table!") 101 | return 102 | 103 | new_states = [ce[0:i+1] for i in range(len(ce)) if not ce[0:i+1] in self.S] 104 | 105 | self.T[ce] = label 106 | self.S.update(new_states) 107 | 108 | self._fill_T() #has to be after adding the new states 109 | for s in new_states: #has to be after filling T 110 | self._update_row_equivalence_cache(new_s=s) 111 | self._assert_not_timed_out() 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Quantisations.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from Helper_Functions import mean 3 | from sklearn import svm 4 | from copy import deepcopy 5 | 6 | class SVMDecisionTreeNode: 7 | def __init__(self,id): 8 | self.id = id 9 | self.has_children = False 10 | 11 | def get_node(self,vector): 12 | if self.has_children: 13 | return self._choose_child(vector).get_node(vector) 14 | return self 15 | 16 | def _choose_child(self,vector): 17 | if self.is_dim_split: 18 | return self._dim_choose_child(vector) 19 | childnum = self.clf.predict([vector]).tolist()[0] 20 | if childnum == 0: 21 | return self.zero_child 22 | return self.one_child 23 | 24 | def _dim_choose_child(self,vector): 25 | if vector[self.split_dim] > self.split_val: 26 | return self.high 27 | return self.low 28 | 29 | def _dim_split_aux(self, split_tuples, split_vals, new_id, split_depth): 30 | if split_depth == 0 or len(split_tuples)==0: 31 | return new_id 32 | 33 | split_tuples = deepcopy(split_tuples) #else they're all popping from the same place and run out of dimensions to split, not to mention split asymmetrically! 34 | margin, self.split_dim = split_tuples.pop(0) 35 | 36 | self.split_val = split_vals[self.split_dim] 37 | 38 | self.high = SVMDecisionTreeNode(self.id) 39 | self.low = SVMDecisionTreeNode(new_id) 40 | new_id += 1 # next node will need to have the next id now that low has taken the one we had 41 | self.has_children = True 42 | self.is_dim_split = True 43 | 44 | new_id = self.high._dim_split_aux(split_tuples,split_vals,new_id,split_depth-1) 45 | new_id = self.low._dim_split_aux(split_tuples,split_vals,new_id,split_depth-1) 46 | return new_id 47 | 48 | # reminder: this function (dim_split) and the next (split) are called only by the overall SVMDecisionTree class, 49 | # and won't be called on internal nodes of the tree (only on leaves, which represent actual clusters in the partitioning) 50 | def dim_split(self,agreeing_continuous_visitors,conflicted_continuous_visitor,new_id,split_depth): 51 | # print("making initial split of depth " + str(split_depth)) 52 | mean_agreeing_vector = [] 53 | for i in range(len(conflicted_continuous_visitor)): 54 | mean_agreeing_vector.append(mean([visitor[i] for visitor in agreeing_continuous_visitors])) 55 | 56 | margins = [abs(m-v) for m,v in zip(mean_agreeing_vector,conflicted_continuous_visitor)] 57 | numbered_margins_by_largest = sorted([(margin,i) for i,margin in enumerate(margins)],reverse=True) 58 | split_vals = [(a+b)/2.0 for a,b in zip(mean_agreeing_vector,conflicted_continuous_visitor)] 59 | 60 | return self._dim_split_aux(numbered_margins_by_largest,split_vals,new_id,split_depth) 61 | 62 | def split(self,agreeing_continuous_visitors,conflicted_continuous_visitor,new_id): 63 | # print("trying regular svm split") 64 | x = agreeing_continuous_visitors + [conflicted_continuous_visitor] 65 | y = [0]*len(agreeing_continuous_visitors) + [1] 66 | self.clf = svm.SVC(C=10000) 67 | self.clf.fit(x,y) 68 | # print("clf used this many support vectors: " + str(self.clf.n_support_)) 69 | self.zero_child = SVMDecisionTreeNode(self.id) 70 | self.one_child = SVMDecisionTreeNode(new_id) 71 | new_id += 1 72 | self.has_children = True 73 | self.is_dim_split = False 74 | if not self.clf.predict(x).tolist() == y: 75 | print("svm classifier failed to obtain perfect split :(") 76 | return new_id 77 | 78 | 79 | class SVMDecisionTreeQuantisation: 80 | def __init__(self,num_dims_initial_split): 81 | self.num_dims_initial_split = num_dims_initial_split 82 | self.top_id = 1 #1-index so it's also a neat count of how many id's we have in general 83 | self.head = SVMDecisionTreeNode(self.top_id) 84 | self.had_initial_refine = False 85 | self.initiated_with_all_rnn_states_to_some_depth = False 86 | 87 | self.refinement_doesnt_hurt_other_clusters = True 88 | # this is a trait of Decision Tree refinements: they affect only the cluster being refined, 89 | # all the rest remain exactly the same (as opposed to, for instance, splitting a dimension 90 | # across the board). If you wish to implement a different quantisation, think about whether 91 | # yours satisfies this quality and fill this field appropriately) 92 | 93 | pass 94 | 95 | def _get_node(self,vector): 96 | if not self.had_initial_refine: 97 | return self.head 98 | if self.initiated_with_all_rnn_states_to_some_depth: 99 | return self.nodes[self.clf.predict([vector])[0]].get_node(vector) 100 | return self.head.get_node(vector) 101 | 102 | def get_partition(self, vector): 103 | return self._get_node(vector).id 104 | 105 | def refine(self,agreeing_continuous_visitors,conflicted_continuous_visitor): 106 | # print("refining, H size is " + str(len(agreeing_continuous_visitors))) 107 | relevant_node = self._get_node(conflicted_continuous_visitor) 108 | next_id = relevant_node.split(agreeing_continuous_visitors,conflicted_continuous_visitor,self.top_id+1) if \ 109 | self.had_initial_refine else relevant_node.dim_split(agreeing_continuous_visitors, 110 | conflicted_continuous_visitor, 111 | self.top_id+1, 112 | self.num_dims_initial_split) 113 | self.refined_something = True 114 | # print("refining - added "+str((next_id-1)-self.top_id)+" states") 115 | self.top_id = next_id-1 116 | self.had_initial_refine = True 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lstar Extraction 2 | Welcome to our public repository, implementing the extraction algorithm from our ICML 2018 paper, [Extracting Automata from Recurrent Neural Networks Using Queries and Counterexamples](https://arxiv.org/abs/1711.09576). 3 | 4 | ### Google Colaboratory 5 | To use the main notebook here without installing anything, you can go straight to google colaboratory: https://drive.google.com/file/d/1tkJK1rJVEg9e-QcWOxErDb3cQq9UR-yR/view?usp=sharing 6 | 7 | ### This Repository 8 | Open the `dfa_from_rnn` notebook for a full demonstration and run through of how to use it yourself (we have provided all of the Tomita grammars, but you can also define, train, and extract your own languages!). For the impatient, the `dfa_from_rnn_no_documentation` notebook is exactly like `dfa_from_rnn`, only without all the explanation blocks. And if you want to train and keep track of several RNNs, you can use `dfa_from_rnn_notebook_for_several_rnns`, which is like `dfa_from_rnn_no_documentation` only it keeps all of your RNNs in neat little wrappers with their target languages and then keeps all of those in a list. 9 | 10 | ### Package Requirements 11 | ##### Full Install 12 | Everything here is implemented in Python 3. To use these notebooks, you will also need to install: 13 | 14 | >1. [DyNet](http://dynet.readthedocs.io/en/latest/python.html) (for working with our LSTM and GRU networks, which are implemented in DyNet) 15 | >2. [Graphviz](http://graphviz.readthedocs.io/en/stable/manual.html#installation) (for drawing the extracted DFAs). 16 | >3. [NumPy and SciPy](https://scipy.org/install.html) (for Scikit-Learn) 17 | >4. [Scikit-Learn](http://scikit-learn.org/stable/install.html) (for the SVM classifier) 18 | >5. [Matplotlib](https://matplotlib.org/users/installing.html) (for plots of our networks' loss during training) 19 | >6. [Jupyter](http://jupyter.readthedocs.io/en/latest/install.html) (for the python notebooks themselves) 20 | 21 | If you are on a mac using Homebrew, then NumPy, SciPy, Scikit-Learn, Matplotlib, Graphviz and Jupyter should all hopefully work with `brew install numpy`, `brew install scipy`, etc. 22 | 23 | If you don't have Homebrew, or wherever `brew install` doesn't work, try `pip install` instead. 24 | 25 | For Graphviz you may first need to download and install the package yourself [Graphviz](https://www.graphviz.org/download/), after which you can run `pip install graphviz`. If you're lucky, `brew install graphviz` might take care of all of this for you by itself. On colab, we got Graphviz using `pip install graphviz` and then `apt-get install graphviz`. 26 | 27 | DyNet is installed by `pip install dynet` from the command line (for the basic CPU version. For the GPU version, check their [site](http://dynet.readthedocs.io/en/latest/python.html)). 28 | 29 | ### Extracting from Existing Networks 30 | You can also apply the code directly to your own networks without most of these packages. The main extraction function is in `Extraction.py` and called `extract`. You can run it on any network that implements the API described in our `dfa_from_rnn` notebook, which is viewable in-browser in git even if you don't have Jupyter, and reiterated here for completeness. 31 | ##### Network Extraction API 32 | >1. `classify_word(word)` returns a True or False classification for a word over the input alphabet 33 | >2. `get_first_RState()` returns a tuple (v,c) where v is a continuous vector representation of the network's initial state (an RState), and c is a boolean signifying whether it is an accepting state 34 | >3. `get_next_RState(state,char)` given an RState, returns the next RState the network goes to on input character `char`, in the same format as `get_first_RState` (i.e., a tuple (v,c) of vector + boolean) 35 | 36 | ##### Partial Install 37 | To run only the extraction code you will only need the NumPy, SciPy, Scikit-Learn, and Graphviz packages. If you want, you can also skip the Graphviz package, at the cost of the ability to visualise your DFAs. Remove the graphviz import from `DFA.py` and set the body of the `draw_nicely` function of the `DFA` class to `pass`. You only need the `DFA`, `Extraction`, `Lstar`, `Helper_Functions`, `Observation_Table`, `Quantisations`, `Teacher`, and `WhiteboxRNNCounterexampleGenerator` modules for extraction. 38 | 39 | 40 | 41 | 42 | ### Citation 43 | You can cite this work using: 44 | 45 | @InProceedings{weiss-goldberg-yahav, 46 | title = {Extracting Automata from Recurrent Neural Networks Using Queries and Counterexamples}, 47 | author = {Gail Weiss and Yoav Goldberg and Eran Yahav}, 48 | booktitle = {Proceedings of the 35th International Conference on Machine Learning}, 49 | year = {2018}, 50 | editor = {Jennifer Dy and Andreas Krause}, 51 | volume = {80}, 52 | series = {Proceedings of Machine Learning Research}, 53 | address = {Stockholmsm\\"{a}ssan, Stockholm, Sweden}, 54 | month = {10--15 Jul}, 55 | publisher = {PMLR} 56 | } 57 | 58 | -------------------------------------------------------------------------------- /RNNClassifier.py: -------------------------------------------------------------------------------- 1 | from LSTM import LSTMNetwork 2 | from GRU import GRUNetwork 3 | from LinearTransform import LinearTransform 4 | import dynet as dy 5 | from time import clock 6 | import random 7 | import matplotlib.pyplot as plt 8 | from math import ceil 9 | 10 | class RNNClassifier: 11 | def __init__(self,alphabet,num_layers=2,input_dim=3,hidden_dim=5,RNNClass=LSTMNetwork): 12 | 13 | self.alphabet = list(alphabet) 14 | self.int2char = self.alphabet 15 | self.char2int = {c:i for i,c in enumerate(self.int2char)} 16 | self.int2class = [True,False] # binary classifier for now 17 | self.class2int = {c:i for i,c in enumerate(self.int2class)} 18 | self.vocab_size = len(self.alphabet) 19 | 20 | self.pc = dy.ParameterCollection() 21 | self.lookup = self.pc.add_lookup_parameters((self.vocab_size, input_dim)) 22 | self.linear_transform = LinearTransform(hidden_dim,len(self.class2int),self.pc) 23 | self.rnn = RNNClass(num_layers=num_layers,input_dim=input_dim,hidden_dim=hidden_dim,pc=self.pc) 24 | self.store_expressions() 25 | self.all_losses = [] 26 | self.finish_signal = "Finished" 27 | self.keep_going = "Keep Going" 28 | 29 | 30 | def renew(self): 31 | dy.renew_cg() 32 | self.store_expressions() 33 | 34 | def store_expressions(self): 35 | self.rnn.store_expressions() 36 | self.linear_transform.store_expressions() 37 | 38 | def _char_to_input_vector(self,char): 39 | return self.lookup[self.char2int[char]] 40 | 41 | def _next_state(self,state,char): 42 | return self.rnn.next_state(state,self._char_to_input_vector(char)) 43 | 44 | def _state_probability_distribution(self,state): 45 | return dy.softmax(self.linear_transform.apply(state.output())) 46 | 47 | def get_first_RState(self): 48 | return self.rnn.initial_state.as_vec(), self._classify_state(self.rnn.initial_state) 49 | 50 | def get_next_RState(self,vec,char): 51 | #verification, could get rid of 52 | if not char in self.alphabet: 53 | print("char for next vector not from input alphabet") 54 | return None 55 | state = self.rnn.state_class(full_vec = vec, hidden_dim = self.rnn.hidden_dim) 56 | state = self._next_state(state,char) 57 | return state.as_vec(), self._classify_state(state) 58 | 59 | def _word_is_over_input_alphabet(self,word): 60 | return next((False for c in word if not c in self.alphabet),True) 61 | 62 | def _state_accept_probability(self,s): 63 | probabilities = self._state_probability_distribution(s) 64 | return probabilities[self.class2int[True]] 65 | 66 | def _classify_state(self,s): 67 | return self._state_accept_probability(s).value()>0.5 68 | 69 | def _probability_word_in_language(self,word): 70 | #verification, could get rid of 71 | if not self._word_is_over_input_alphabet(word): 72 | print("word is not over input alphabet") 73 | return False 74 | s = self.rnn.initial_state 75 | for c in word: 76 | s = self._next_state(s,c) 77 | return self._state_accept_probability(s) 78 | 79 | def classify_word(self,word): 80 | return self._probability_word_in_language(word).value()>0.5 81 | 82 | def loss_on_word(self, word, label): 83 | s = self.rnn.initial_state 84 | p = self._probability_word_in_language(word) 85 | p = p if label == True else (1-p) # now p = probability of correct label for word 86 | #dy.picklneglogsoftmax on self.linear_transform.apply(state.output()) should be numerically stable 87 | return -dy.log(p) # ideally p should be 1, in which case log(p)=0. the lower it gets: the greater -log(p) gets 88 | # loss = dy.esum(loss) 89 | 90 | def train_batch(self,word_dict,trainer): 91 | self.renew() 92 | loss = [self.loss_on_word(w,word_dict[w]) for w in word_dict] 93 | loss = dy.esum(loss) 94 | loss_value = loss.value()/len(word_dict) 95 | loss.backward() 96 | trainer.update() 97 | return loss_value 98 | 99 | def show_all_losses(self): 100 | plt.scatter(range(len(self.all_losses)),self.all_losses,label="classification loss since initiation") 101 | plt.legend() 102 | plt.show() 103 | 104 | def train_group(self,word_dict,iterations,trainer_class=dy.AdamTrainer,learning_rate=None,loss_every=100, 105 | batch_size=20,show=True,print_time=True,stop_threshold=0): 106 | if iterations == 0: 107 | return 108 | start = clock() 109 | trainer = trainer_class(self.pc) 110 | if not None is learning_rate: 111 | trainer.learning_rate = learning_rate 112 | loss_values = [] 113 | 114 | if None is batch_size: 115 | batch_size = len(word_dict) # leave None to define one huge batch 116 | 117 | words = list(word_dict.keys()) 118 | num_batches = int(ceil(len(words)/batch_size)) 119 | for i in range(iterations): 120 | random.shuffle(words) 121 | batches_loss = [] 122 | for j in range(num_batches): 123 | batch = words[j*batch_size:(j+1)*batch_size] 124 | batches_loss.append(self.train_batch({w:word_dict[w] for w in batch},trainer)) 125 | loss_values.append(sum(batches_loss)/num_batches) # its not perfect because the last batch might be a different size and they were training during, but whatever, its here to give a general idea of what's going on 126 | if loss_values[-1] 0: 29 | index = random.choice(range(len(new))) 30 | new = new[:index + 1] + new[index:] 31 | num_changes += 1 32 | # omissions 33 | while ((num_changes == 0) or random.choice(range(3)) == 0) and len(new) > 0: 34 | index = random.choice(range(len(new))) 35 | new = new[:index] + new[index + 1:] 36 | num_changes +=1 37 | return ''.join(new) 38 | 39 | def balanced_parantheses(w): 40 | open_counter = 0 41 | while len(w)>0: 42 | c = w[0] 43 | w = w[1:] 44 | if c == "(": 45 | open_counter += 1 46 | elif c==")": 47 | open_counter -= 1 48 | if open_counter <0: 49 | return False 50 | return open_counter == 0 51 | 52 | def random_balanced_word(start_closing): 53 | count = 0 54 | word = "" 55 | while len(word) self.time_limit: 98 | return True 99 | return False 100 | 101 | def _split_was_clean(self,old_cluster,split): 102 | new_states_given_to_agreeing = list(set([self.partitioning.get_partition(vec) for vec in split.agreeing_RStates])) 103 | return self.partitioning.refinement_doesnt_hurt_other_clusters \ 104 | and new_states_given_to_agreeing == [old_cluster] \ 105 | and not self.partitioning.get_partition(split.conflicted_RState) == old_cluster 106 | 107 | def counterexample(self,dfa): 108 | print("guided starting equivalence query for DFA of size " + str(len(dfa.Q))) 109 | dfa.draw_nicely(maximum=30) 110 | counterexample = self._cex_from_starting_dict(dfa) 111 | if not None is counterexample: 112 | return counterexample,counterexample_message(counterexample,self.whiteboxrnn) 113 | 114 | self.proposed_dfa = dfa 115 | while True: #main loop: restarts every time the partitioning is refined 116 | self._initialise_unrolling() # start BFS exploration of network abstraction with current partitioning 117 | while True: #inner loop: extracts according to the partitioning, comparing to the proposed dfa as it goes 118 | if self._out_of_time(): # note: putting this after all the next bits sometimes leaves the time limit unchecked for a very long time... 119 | return None, "lstar extraction not successful - ran out of time" 120 | if len(self.new_RStates) == 0: # seen everything there is to see here 121 | return None, "lstar successful: unrolling seems equivalent to proposed automaton" 122 | counterexample, split = self._process_top_pair() # always returns a cex, or a split, or neither - but never both 123 | if not None is counterexample: 124 | return counterexample,counterexample_message(counterexample,self.whiteboxrnn) 125 | elif split.has_info: 126 | cluster_being_split = self.partitioning.get_partition(split.agreeing_RStates[0]) 127 | self.partitioning.refine(split.agreeing_RStates,split.conflicted_RState) 128 | if self._split_was_clean(cluster_being_split,split): 129 | # the latest R-state got a new cluster of its own and absolutely nothing else changed, 130 | # so we can just reprocess this visitor and continue as if nothing happened 131 | self.new_RStates = [self.new_RStates_backup] + self.new_RStates 132 | else: 133 | print("split wasn't perfect: gotta start over") 134 | break # clustering has changed, have to restart unrolling from the top 135 | 136 | 137 | def counterexample_message(counterexample,rnn): 138 | return ("returning counterexample of length " + str(len(counterexample)) + ":\t\t" + counterexample + 139 | ", this counterexample is " + ("accepted" if rnn.classify_word(counterexample)==True else "rejected") + 140 | " by the given RNN.") 141 | 142 | class SplitInfo: #todo: move this to quantisations and just give the whole thing over to the relevant function there, instead of unpacking it to 3 parameters here 143 | def __init__(self,agreeing_RStates=None,conflicted_RState=None): 144 | self.agreeing_RStates = agreeing_RStates 145 | self.conflicted_RState = conflicted_RState 146 | self.has_info = not (None is conflicted_RState) 147 | 148 | class UnrollingInfo: 149 | def __init__(self,dfa_state,path,RState,accepting): 150 | self.explored = False 151 | self.dfa_state = dfa_state 152 | self.paths = [path] 153 | self.RStates = [RState] 154 | self.accepting = accepting 155 | 156 | def __add__(self, other): 157 | res = deepcopy(self) 158 | res.paths += other.paths 159 | res.RStates += other.RStates 160 | return res 161 | 162 | class NoCounterexampleFromClassificationConflict(Exception): 163 | pass 164 | -------------------------------------------------------------------------------- /dfa_from_rnn_no_documentation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Welcome!\n", 8 | "\n", 9 | "This is also a notebook to let you play around with the extraction method described in our ICML 2018 paper, [Extracting Automata from Recurrent Neural Networks Using Queries and Counterexamples](https://arxiv.org/abs/1711.09576), only without documentation." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "from LSTM import LSTMNetwork\n", 21 | "from GRU import GRUNetwork\n", 22 | "from RNNClassifier import RNNClassifier\n", 23 | "from Training_Functions import mixed_curriculum_train\n", 24 | "from Tomita_Grammars import tomita_1, tomita_2, tomita_3, tomita_4, tomita_5, tomita_6, tomita_7\n", 25 | "from Training_Functions import make_train_set_for_target\n", 26 | "from Extraction import extract" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 2, 32 | "metadata": {}, 33 | "outputs": [ 34 | { 35 | "name": "stdout", 36 | "output_type": "stream", 37 | "text": [ 38 | "made train set of size: 2753 , of which positive examples: 1324\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "target = tomita_3\n", 44 | "alphabet = \"01\"\n", 45 | "\n", 46 | "# alternative option (example):\n", 47 | "# def target(w):\n", 48 | "# if len(w)==0:\n", 49 | "# return True\n", 50 | "# return w[0]==w[-1]\n", 51 | "# alphabet = \"abc\"\n", 52 | "\n", 53 | "train_set = make_train_set_for_target(target,alphabet)\n", 54 | "rnn = RNNClassifier(alphabet,num_layers=1,hidden_dim=10,RNNClass = LSTMNetwork)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 3, 60 | "metadata": { 61 | "scrolled": false 62 | }, 63 | "outputs": [ 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | "current average loss is: 0.00303627456805213\n" 69 | ] 70 | }, 71 | { 72 | "data": { 73 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAH9xJREFUeJzt3Xt0VPXd7/H31xBqvCyxmiqQKOhBKJAEJMQiDwKCBKUC\n4qVQdQmr1lbBy7FNBa8caiuKpz61teXBHu9WFB5EzpGu2BYRtdomXJTbQiOiZNAS0FCRoEn4nj8y\nSYeQy04yITObz2utLGb/5jd7f7OZfPae376MuTsiIhIuR3V0ASIiEn8KdxGREFK4i4iEkMJdRCSE\nFO4iIiGkcBcRCSGFu4hICCncRURCSOEuIhJCnTpqwSeffLL36NGjoxYvIpKUVq9evcvd05vr12Hh\n3qNHD4qLiztq8SIiScnMPgrST8MyIiIhpHAXEQkhhbuISAh12Ji7hFtlZSWlpaXs37+/o0sRSUpH\nH300GRkZpKamtur1gcLdzMYCvwZSgD+4+9x6zz8EjIxOHgN8y927tKoiCYXS0lKOP/54evTogZl1\ndDkiScXd2b17N6WlpfTs2bNV82g23M0sBXgEuAAoBYrMbJm7b4op5H/G9L8RGNiqaiQ09u/fr2AX\naSUz46STTqKsrKzV8wgy5p4HlLj7Vnf/GlgITGii/xTguVZXJKGhYBdpvbb+/QQJ9+7A9pjp0mhb\nQ8WcDvQEVjTy/HVmVmxmxW3ZIomISNPifbbMZGCxu1c39KS7L3D3XHfPTU9v9gIrkbibPXs2Dz74\nYNzmd+6559Y9LigooF+/fhQUFDB//nyeeuqpFs+vvLyc3/3ud3XTO3bs4LLLLotLrSNGjGi3Cwen\nTJlCdnY2Dz30UKtev3LlSv72t7/VTU+dOpXFixfHq7wO1Z7rvSlBDqhGgMyY6YxoW0MmA9PbWlRj\nlq6NMK9wCzvKK+jWJY2C/N5MHNjghwiRwyI2kBYsWMBnn31GSkpKq+dXG+433HADAN26dUv4kPv0\n008pKiqipKQk8Guqqqro1Onf8bNy5UqOO+64gzaW0jZB9tyLgF5m1tPMOlMT4MvqdzKzPsCJwFvx\nLbHG0rURZi1ZT6S8Agci5RXMWrKepWsb285IMlm6NsLQuSvoOfNlhs5dEZf/16eeeors7GxycnK4\n+uqrD3n+0UcfZfDgweTk5HDppZeyb98+ABYtWkT//v3JycnhvPPOA2Djxo3k5eUxYMAAsrOzef/9\n9wE47rjjABg/fjx79+5l0KBBPP/88wd9QigpKWH06NHk5ORw9tln88EHH7B3715GjRrF2WefTVZW\nFi+99BIAM2fO5IMPPmDAgAEUFBSwbds2+vfvD9QcpJ42bRpZWVkMHDiQV199FYAnnniCSZMmMXbs\nWHr16sXPfvazZtfNc889R1ZWFv379+e2224DoLq6mqlTp9K/f3+ysrLq9sIffvhh+vbtS3Z2NpMn\nTz5kXmPGjCESiTBgwABef/111q1bx3e+8x2ys7O55JJL+Pzzz4GaPdhbbrmF3Nxcfv3rX9e9ftu2\nbcyfP5+HHnqobh4Aq1at4txzz+WMM844aAM3b948Bg8eTHZ2Nvfccw8AX375JePGjSMnJ4f+/fvz\n/PPPA7B69WqGDx/OoEGDyM/P55NPPjmk/rKyMi699FIGDx7M4MGDefPNN4GaT3lXX301Q4YMoVev\nXjz66KNAzZksBQUFdeupdlkA999/P1lZWeTk5DBz5sy69kWLFpGXl8dZZ51V9/s19p6KG3dv9ge4\nCHgP+AC4I9o2Bxgf02c2MDfI/NydQYMGeUuce99f/fTb/t8hP+fe99cWzUcOj02bNgXu++KaUu9z\n558O+n/tc+ef/MU1pa1e/oYNG7xXr15eVlbm7u67d+92d/d77rnH582b5+7uu3btqut/xx13+MMP\nP+zu7v379/fS0pplf/755+7uPmPGDH/mmWfc3f2rr77yffv2ubv7scceWzeP2Mexy8nLy/MlS5a4\nu3tFRYV/+eWXXllZ6Xv27HF397KyMj/zzDP9wIED/uGHH3q/fv3q5hM7/eCDD/q0adPc3X3z5s2e\nmZnpFRUV/vjjj3vPnj29vLzcKyoq/LTTTvOPP/74kHUyfPhwLyoq8kgk4pmZmb5z506vrKz0kSNH\n+osvvujFxcU+evTouv61v3vXrl19//79B7XFql9zVlaWr1y50t3d77rrLr/55pvrln/99dcf8vr6\n68vd/ZprrvHLLrvMq6urfePGjX7mmWe6u3thYaH/8Ic/9AMHDnh1dbWPGzfOX3vtNV+8eLFfe+21\nda8vLy/3r7/+2ocMGeI7d+50d/eFCxfWrb9YU6ZM8ddff93d3T/66CPv06dPXU3Z2dm+b98+Lysr\n84yMDI9EIr548WIfPXq0V1VV+aeffuqZmZm+Y8cOX758uQ8ZMsS//PJLd//3e2748OF+6623urv7\nyy+/7KNGjXL3xt9TsRr6OwKKPUDGBjrP3d2XA8vrtd1db3p2m7YyzdhRXtGidkke8wq3UFF58GGa\nispq5hVuafWw24oVK7j88ss5+eSTAfjmN795SJ8NGzZw5513Ul5ezt69e8nPzwdg6NChTJ06lSuu\nuIJJkyYBMGTIEH7xi19QWlrKpEmT6NWrV6A6vvjiCyKRCJdccglQc2EK1Fzkdfvtt7Nq1SqOOuoo\nIpEI//znP5uc1xtvvMGNN94IQJ8+fTj99NN57733ABg1ahQnnHACAH379uWjjz4iMzOzwfkUFRUx\nYsQIao97XXnllaxatYq77rqLrVu3cuONNzJu3DjGjBkDQHZ2NldeeSUTJ05k4sSJTda4Z88eysvL\nGT58OADXXHMNl19+ed3z3/ve95p8fayJEydy1FFH0bdv37p188orr/DKK68wcGDN2dZ79+7l/fff\nZ9iwYfzkJz/htttu47vf/S7Dhg1jw4YNbNiwgQsuuACo+WTStWvXQ5bzl7/8hU2b6s7s5l//+hd7\n9+4FYMKECaSlpZGWlsbIkSP5xz/+wRtvvMGUKVNISUnhlFNOYfjw4RQVFfHaa68xbdo0jjnmGODg\n91zt+2jQoEFs27YNaP17Kqikuf1Aty5pgdvb4yO+tJ+O2nBPnTqV3/72t6xfv5577rmn7mra+fPn\nc++997J9+3YGDRrE7t27+f73v8+yZctIS0vjoosuYsWKBk8IC+zZZ5+lrKyM1atXs27dOk455ZQ2\nXc37jW98o+5xSkoKVVVVLZ7HiSeeyDvvvMOIESOYP38+1157LQAvv/wy06dPZ82aNQwePLhV8651\n7LHHBu4b+zvV7LDW/Dtr1izWrVvHunXrKCkp4Qc/+AFnnXUWa9asISsrizvvvJM5c+bg7vTr16+u\n7/r163nllVcOWc6BAwd4++236/pFIpG64bb6pyO29vTE2t8l9v8m3u+p+pIm3Avye5OWevCBqrTU\nFAryex/UprH55NOSDXdQ559/PosWLWL37t0AfPbZZ4f0+eKLL+jatSuVlZU8++yzde0ffPAB55xz\nDnPmzCE9PZ3t27ezdetWzjjjDG666SYmTJjAu+++G6iO448/noyMDJYuXQrAV199xb59+9izZw/f\n+ta3SE1N5dVXX+Wjjz6q6//FF180OK9hw4bV1fnee+/x8ccf07t37wb7NiUvL4/XXnuNXbt2UV1d\n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\n", 74 | "text/plain": [ 75 | "" 76 | ] 77 | }, 78 | "metadata": {}, 79 | "output_type": "display_data" 80 | }, 81 | { 82 | "data": { 83 | "image/png": "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\nAABJRU5ErkJggg==\n", 84 | "text/plain": [ 85 | "" 86 | ] 87 | }, 88 | "metadata": {}, 89 | "output_type": "display_data" 90 | }, 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "classification loss on last batch was: 0.0004724000111183851\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "mixed_curriculum_train(rnn,train_set,stop_threshold = 0.0005)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 4, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "all_words = sorted(list(train_set.keys()),key=lambda x:len(x))\n", 112 | "pos = next((w for w in all_words if rnn.classify_word(w)==True),None)\n", 113 | "neg = next((w for w in all_words if rnn.classify_word(w)==False),None)\n", 114 | "starting_examples = [w for w in [pos,neg] if not None == w]" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 5, 120 | "metadata": { 121 | "collapsed": true 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "rnn.renew() \n", 126 | "# you only really need this if you start messing about and doing weird stuff. \n", 127 | "# It cleans the computation graph, but doesn't reset the weights so don't worry" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 6, 133 | "metadata": { 134 | "scrolled": false 135 | }, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "provided counterexamples are: ['', '10']\n", 142 | "obs table refinement took 0.0\n", 143 | "guided starting equivalence query for DFA of size 1\n" 144 | ] 145 | }, 146 | { 147 | "data": { 148 | "image/png": "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\nRU5ErkJggg==\n", 149 | "text/plain": [ 150 | "" 151 | ] 152 | }, 153 | "metadata": {}, 154 | "output_type": "display_data" 155 | }, 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "storing provided counterexample of length 2\n", 161 | "returning counterexample of length 2:\t\t10, this counterexample is rejected by the given RNN.\n", 162 | "equivalence checking took: 0.015492999999992207\n", 163 | "obs table refinement took 0.003\n", 164 | "guided starting equivalence query for DFA of size 4\n" 165 | ] 166 | }, 167 | { 168 | "data": { 169 | "image/png": "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\nwU70/wP2VLTHYT7M0QAAAABJRU5ErkJggg==\n", 170 | "text/plain": [ 171 | "" 172 | ] 173 | }, 174 | "metadata": {}, 175 | "output_type": "display_data" 176 | }, 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "split wasn't perfect: gotta start over\n", 182 | "returning counterexample of length 5:\t\t10010, this counterexample is rejected by the given RNN.\n", 183 | "equivalence checking took: 0.1335600000000028\n", 184 | "obs table refinement took 0.007\n", 185 | "guided starting equivalence query for DFA of size 5\n" 186 | ] 187 | }, 188 | { 189 | "data": { 190 | "image/png": "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\nA8rAPwz8f4/jmmW8Np5BAAAAAElFTkSuQmCC\n", 191 | "text/plain": [ 192 | "" 193 | ] 194 | }, 195 | "metadata": {}, 196 | "output_type": "display_data" 197 | }, 198 | { 199 | "name": "stdout", 200 | "output_type": "stream", 201 | "text": [ 202 | "lstar successful: unrolling seems equivalent to proposed automaton\n", 203 | "equivalence checking took: 0.02303599999999051\n", 204 | "overall guided extraction time took: 0.18628899999998794\n", 205 | "generated counterexamples were: (format: (counterexample, counterexample generation time))\n", 206 | "('10', 0.015492999999992207)\n", 207 | "('10010', 0.1335600000000028)\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "dfa = extract(rnn,time_limit = 50,initial_split_depth = 10,starting_examples=starting_examples)" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 7, 218 | "metadata": {}, 219 | "outputs": [ 220 | { 221 | "data": { 222 | "image/png": "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\nA8rAPwz8f4/jmmW8Np5BAAAAAElFTkSuQmCC\n", 223 | "text/plain": [ 224 | "" 225 | ] 226 | }, 227 | "metadata": {}, 228 | "output_type": "display_data" 229 | }, 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "testing on train set, i.e. test set is train set\n", 235 | "test set size: 2753\n", 236 | "of which positive: 1324 (48.09%)\n", 237 | "rnn score against target on test set: 2753 (100.0%)\n", 238 | "extracted dfa score against rnn on test set: 2753 (100.0%)\n", 239 | "extracted dfa score against target on rnn's test set: 2753 (100.0%)\n" 240 | ] 241 | } 242 | ], 243 | "source": [ 244 | "from math import pow\n", 245 | "def percent(num,digits=2):\n", 246 | " tens = pow(10,digits)\n", 247 | " return str(int(100*num*tens)/tens)+\"%\"\n", 248 | "\n", 249 | "dfa.draw_nicely(maximum=30) #max size willing to draw\n", 250 | "\n", 251 | "test_set = train_set \n", 252 | "print(\"testing on train set, i.e. test set is train set\")\n", 253 | "# we're printing stats on the train set for now, but you can define other test sets by using\n", 254 | "# make_train_set_for_target again\n", 255 | "\n", 256 | "n = len(test_set)\n", 257 | "print(\"test set size:\", n)\n", 258 | "pos = len([w for w in test_set if target(w)])\n", 259 | "print(\"of which positive:\",pos,\"(\"+percent(pos/n)+\")\")\n", 260 | "rnn_target = len([w for w in test_set if rnn.classify_word(w)==target(w)])\n", 261 | "print(\"rnn score against target on test set:\",rnn_target,\"(\"+percent(rnn_target/n)+\")\")\n", 262 | "dfa_rnn = len([w for w in test_set if rnn.classify_word(w)==dfa.classify_word(w)])\n", 263 | "print(\"extracted dfa score against rnn on test set:\",dfa_rnn,\"(\"+percent(dfa_rnn/n)+\")\")\n", 264 | "dfa_target = len([w for w in test_set if dfa.classify_word(w)==target(w)])\n", 265 | "print(\"extracted dfa score against target on rnn's test set:\",dfa_target,\"(\"+percent(dfa_target/n)+\")\")" 266 | ] 267 | } 268 | ], 269 | "metadata": { 270 | "kernelspec": { 271 | "display_name": "Python 3", 272 | "language": "python", 273 | "name": "python3" 274 | }, 275 | "language_info": { 276 | "codemirror_mode": { 277 | "name": "ipython", 278 | "version": 3 279 | }, 280 | "file_extension": ".py", 281 | "mimetype": "text/x-python", 282 | "name": "python", 283 | "nbconvert_exporter": "python", 284 | "pygments_lexer": "ipython3", 285 | "version": "3.6.3" 286 | } 287 | }, 288 | "nbformat": 4, 289 | "nbformat_minor": 2 290 | } 291 | --------------------------------------------------------------------------------