ml-trust-model

git clone git://git.codymlewis.com/ml-trust-model.git
Log | Files | Refs | README

commit aebd00db97d66ef18570aca2a639b760b5f0b181
parent 5bf8b03f0031b6371c5cee5bacbfc104bc83fbe9
Author: Cody Lewis <codymlewis@protonmail.com>
Date:   Sat, 13 Apr 2019 23:43:28 +1000

Started on classifiers

Diffstat:
MBadMouther/__init__.py | 2++
MFunctions.py | 2+-
MNode/__init__.py | 15+++++++++------
MReport/__init__.py | 2+-
MTest.py | 8++++----
ATrustManager/ANN.py | 32++++++++++++++++++++++++++++++++
MTrustManager/SVM.py | 31+++++++------------------------
MTrustManager/__init__.py | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
MTrustModel.py | 32++++++++++++++++++++++++++------
9 files changed, 181 insertions(+), 89 deletions(-)

diff --git a/BadMouther/__init__.py b/BadMouther/__init__.py @@ -13,4 +13,6 @@ class BadMouther(Node.Node): A bad mouthing malicious node. ''' def take_note(self, proxy, service_target, capability_target): + if proxy.is_malicious(): # say that other bad mouthers are good + return 1 return -1 diff --git a/Functions.py b/Functions.py @@ -19,7 +19,7 @@ def print_progress(current_epoch, total_epochs, progress_len=20): ) progress_bar += "".join(["." for _ in range(unprogressed)]) progress_bar += "]" - print(f"{progress_bar} {progress}%") + print(f"\r{progress_bar} {progress}%", end="\r") def wrong_note(note): diff --git a/Node/__init__.py b/Node/__init__.py @@ -15,10 +15,10 @@ class Node: ''' A node in the trust managed network. ''' - def __init__(self, service=100, capability=100, note_acc=1.0): + def __init__(self, service=100, capability=100, is_malicious=False): self.__service = service self.__capability = capability - self.__note_taking_acc = note_acc + self.__is_malicious = is_malicious def get_service(self): return self.__service @@ -26,6 +26,9 @@ class Node: def get_capability(self): return self.__capability + def is_malicious(self): + return self.__is_malicious + def send_report(self, proxy, service_target, capability_target, time): ''' Create a report on a given proxy. @@ -35,7 +38,9 @@ class Node: return Report.Report(service_target, capability_target, note, time) def take_note(self, proxy, service_target, capability_target): - if proxy.get_service() >= service_target and \ + if proxy.is_malicious(): + note = -1 + elif proxy.get_service() >= service_target and \ proxy.get_capability() >= capability_target: note = 1 elif proxy.get_service() >= service_target or \ @@ -44,6 +49,4 @@ class Node: else: note = -1 - if np.random.rand() < self.__note_taking_acc: - return note - return Functions.wrong_note(note) + return note diff --git a/Report/__init__.py b/Report/__init__.py @@ -29,4 +29,4 @@ class Report: ''' Output contained data in a format suitable for a csv ''' - return f"{self.__service},{self.__capability},{self.__note},{self.__time}" + return f"{self.__service},{self.__capability},{self.__note}" diff --git a/Test.py b/Test.py @@ -37,7 +37,7 @@ class TestTrustModel(unittest.TestCase): self.assertEqual(report.get_note(), note) self.assertEqual(report.get_time(), time) self.assertEqual( - report.csv_output(), f"{service},{capability},{note},{time}" + report.csv_output(), f"{service},{capability},{note}" ) def test_wrong_note(self): @@ -75,7 +75,7 @@ class TestTrustModel(unittest.TestCase): num_malicious = 0 for node in trust_manager.get_network(): - if (node.get_capability() < 100) or (node.get_service() < 100): + if (node.get_capability() < TrustManager.CAP_MAX) or (node.get_service() < TrustManager.SERVICE_MAX): num_constrained += 1 if isinstance(node, BadMouther.BadMouther): num_malicious += 1 @@ -109,10 +109,10 @@ class TestTrustModel(unittest.TestCase): self.assertEqual(report.get_time(), time) def test_bootstrap(self): - trust_manager = TrustManager.TrustManager() + trust_manager = TrustManager.TrustManager(no_of_nodes=50) no_of_transactions = 5 - trust_manager.bootstrap(no_of_transactions) + trust_manager.bootstrap(no_of_transactions, False) self.assertEqual(np.shape(trust_manager.get_reports()), (50, 50)) diff --git a/TrustManager/ANN.py b/TrustManager/ANN.py @@ -0,0 +1,32 @@ +from tensorflow import keras +import numpy as np +import sklearn.preprocessing as skp + +''' +Use an artificial neural network for trust management. + +Author: Cody Lewis +Date: 2019-04-12 +''' + + +def create_and_train_ann(train_data, train_labels, test_data, test_labels): + model = keras.models.Sequential() + model.add(keras.layers.Dense(64, input_shape=(4,))) + model.add(keras.layers.Activation('relu')) + model.add(keras.layers.Dense(128)) + model.add(keras.layers.Activation('relu')) + model.add(keras.layers.Dense(128)) + model.add(keras.layers.Activation('relu')) + model.add(keras.layers.Dense(64)) + model.add(keras.layers.Activation('relu')) + model.add(keras.layers.Dropout(0.5)) + model.add(keras.layers.Dense(3)) + model.add(keras.layers.Activation('sigmoid')) + + adam = keras.optimizers.Adam(lr=0.0001) + model.compile(loss="mean_squared_error", optimizer=adam, metrics=['accuracy']) + model.summary() + data = np.array(train_data + test_data) + labels = skp.label_binarize(np.array(train_labels + test_labels), [-1, 0, 1]) + model.fit(x=data, y=labels, epochs=5, validation_split=0.5) diff --git a/TrustManager/SVM.py b/TrustManager/SVM.py @@ -35,7 +35,7 @@ def evolve(train_inputs, train_labels, test_inputs, test_labels): genome, acc = hill_climb( train_inputs, train_labels, test_inputs, test_labels ) - return f"C: {genome[0]}, gamma: {genome[1]}, accuracy: {acc}" + return f"{genome[0]},{genome[1]}" def normalise_genome(genome): @@ -49,7 +49,7 @@ def generate_genome(): ''' Generate a random starting genome. ''' - return [float(g) for g in list(np.random.normal(50, 20, 2))] + return [float(np.random.normal(50, 2)), float(np.random.normal(1, 0.5))] def mutate_genome(genome): @@ -60,38 +60,22 @@ def mutate_genome(genome): mutant_genome = genome.copy() if np.random.normal() < 0: - return generate_genome() # Introduce annealing + step_size = 3 * np.random.normal(0, 5) for index_mutant_genome in enumerate(mutant_genome): - mutant_genome[index_mutant_genome[0]] += \ - float(step_size * np.random.normal(0, 5)) + mutant_genome[index_mutant_genome[0]] += float(step_size * np.random.normal(0, 1)) return mutant_genome -def crossover(genome_a, genome_b): - ''' - Possibly crossover genome_b into genome_a and return the result. - ''' - crossover_genome = [] - crossed_over = False - - for gene_pair in zip(genome_a, genome_b): - if not crossed_over and np.random.normal() < 0: - crossover_genome.append(gene_pair[1]) - else: - crossover_genome.append(gene_pair[0]) - - return crossover_genome - - def hill_climb(train_inputs, train_labels, test_inputs, test_labels, acc_goal=99): ''' Evolutionary algorithm to find the optimal number of neurons for the ANN. ''' counter = 0 - n_epochs = 100_000 + n_epochs = 10_000 genome = generate_genome() + normalise_genome(genome) svm_champ = create_and_fit_svm( train_inputs, train_labels, genome[0], genome[1] ) @@ -99,8 +83,7 @@ def hill_climb(train_inputs, train_labels, test_inputs, test_labels, acc_goal=99 while (acc_champ < acc_goal) and (counter < n_epochs): mutant_genome = mutate_genome(genome) - mutant_genome = crossover(mutant_genome, genome) - print(f"Epoch: {counter}, Current mutant genome: {mutant_genome}, Current champion genome: {genome} at accuracy: {acc_champ}") + normalise_genome(mutant_genome) svm_mutant = create_and_fit_svm( train_inputs, train_labels, mutant_genome[0], mutant_genome[1] ) diff --git a/TrustManager/__init__.py b/TrustManager/__init__.py @@ -6,15 +6,18 @@ import Functions import Node import BadMouther import TrustManager.SVM as SVM +import TrustManager.ANN as ANN + +CAP_MAX = 10 +SERVICE_MAX = 6 class TrustManager: ''' Create and control the network. ''' - def __init__(self, no_of_nodes=50, constrained_nodes=0.5, - poor_witnesses=0.2, malicious_nodes=0.1, - train_filename="reports-train.csv", test_filename="reports-test_csv"): + def __init__(self, no_of_nodes=50, constrained_nodes=0.5, malicious_nodes=0.1, malicious_reporters=0.1, + train_filename="reports-train.csv", test_filename="reports-test.csv"): self.__network = [] self.__train_filename = train_filename self.__test_filename = test_filename @@ -23,27 +26,26 @@ class TrustManager: constrained_list = Functions.get_conditioned_ids( no_of_nodes, constrained_nodes ) - self.poor_witness_list = Functions.get_conditioned_ids( - no_of_nodes, poor_witnesses - ) - self.malicious_list = Functions.get_conditioned_ids( + malicious_node_list = Functions.get_conditioned_ids( no_of_nodes, malicious_nodes ) + malicious_reporter_list = Functions.get_conditioned_ids( + no_of_nodes, malicious_reporters + ) for i in range(no_of_nodes): if i in constrained_list: - service = np.round(np.random.rand()) - capability = np.round(np.random.rand()) + service = int(np.floor(np.random.rand() * SERVICE_MAX)) + capability = int(np.floor(np.random.rand() * CAP_MAX)) else: - service = 100 - capability = 100 - note_acc = np.random.rand() if i in self.poor_witness_list else 1.0 - if i in self.malicious_list: + service = SERVICE_MAX + capability = CAP_MAX + if i in malicious_reporter_list: self.__network.append( - BadMouther.BadMouther(service, capability, note_acc) + BadMouther.BadMouther(service, capability, i in malicious_node_list) ) else: - self.__network.append(Node.Node(service, capability, note_acc)) + self.__network.append(Node.Node(service, capability, i in malicious_node_list)) self.__reports = [ [None for _ in range(no_of_nodes)] for _ in range(no_of_nodes) @@ -59,7 +61,7 @@ class TrustManager: def get_reports(self): return self.__reports - def bootstrap(self, epochs=100): + def bootstrap(self, epochs=100, filewrite=True): ''' Go through the network and perform artificial transactions to develop reports. @@ -67,8 +69,10 @@ class TrustManager: print(f"\nBootstrapping network for {epochs} epochs:") Functions.print_progress(0, epochs) for i in range(1, epochs + 1): - self.__artificial_transactions(i) + self.__artificial_transactions(i, self.__train_filename if filewrite else None) + self.__artificial_transactions(i, self.__test_filename if filewrite else None) Functions.print_progress(i, epochs) + print() print("Done.") def __artificial_transactions(self, current_epoch, report_filename=None): @@ -79,8 +83,8 @@ class TrustManager: for i_node_i in enumerate(self.__network): for j_node_j in enumerate(self.__network): if i_node_i[0] != j_node_j[0]: - service_target = np.round(np.random.rand() * 100) - capability_target = np.round(np.random.rand() * 100) + service_target = int(np.floor(np.random.rand() * SERVICE_MAX)) + 1 + capability_target = int(np.floor(np.random.rand() * CAP_MAX)) + 1 self.__reports[i_node_i[0]][j_node_j[0]] = i_node_i[1].send_report( j_node_j[1], service_target, @@ -98,39 +102,87 @@ class TrustManager: for reports_from_node_i in enumerate(self.__reports): for reports_on_node_j in enumerate(reports_from_node_i[1]): if reports_from_node_i[0] != reports_on_node_j[0]: - if reports_from_node_i[0] in self.malicious_list: - observer_class = 2 - elif reports_from_node_i[0] in self.poor_witness_list: - observer_class = 1 - else: - observer_class = 0 report_csv.write( - f"{reports_from_node_i[0]},{reports_on_node_j[0]},{reports_on_node_j[1].csv_output()},{observer_class}\n" + f"{reports_from_node_i[0]},{reports_on_node_j[0]},{reports_on_node_j[1].csv_output()}\n" ) - def read_data(self, filename, delimiter=","): - train_data = [] - notes = [] - observer_class = [] - - with open(filename) as report_csv: - csv_reader = csv.reader(report_csv, delimiter=delimiter) - for row in csv_reader: - train_data.append(row[:4] + row[5:-1]) - notes.append(row[4]) - observer_class.append(row[-1]) - - return train_data, notes, observer_class - def train_svm(self): - print("Reading data...") - train_data, notes, observer_class = self.read_data() - print("Training SVMs...") + ''' + Fit a pair of SVMs to predict expected notes and observer class respectively. + ''' + train_data, notes = read_data(self.__train_filename) note_svm = SVM.create_and_fit_svm(train_data, notes, 5, 0.1) - # witness_svm = SVM.create_and_fit_svm(train_data, observer_class, 5, 0.1) - return note_svm # , witness_svm + return note_svm def evolve_svm(self): - data, notes, observer_class = self.read_data() - return SVM.evolve(data, notes, data, notes) + ''' + Perform an evolutionary algorithm to find the optimal values of C and gamma + for the respective SVMs. + ''' + train_data, train_notes = read_data(self.__train_filename) + test_data, test_notes = read_data(self.__test_filename) + + with open("svm_params.csv", "w") as param_file: + total_reporters = len(train_data.keys()) + progress = 0 + Functions.print_progress(progress, total_reporters) + for reporter_id in train_data.keys(): + svm_params = SVM.evolve( + train_data[reporter_id], + train_notes[reporter_id], + test_data[reporter_id], + test_notes[reporter_id] + ) + progress += 1 + Functions.print_progress(progress, total_reporters) + param_file.write(f"{reporter_id},{svm_params}\n") + print() + + def train_ann(self): + train_data, train_notes = read_data(self.__train_filename, dict_mode=False) + test_data, test_notes = read_data(self.__test_filename, dict_mode=False) + ANN.create_and_train_ann(train_data, train_notes, test_data, test_notes) + + def load_classifiers(self): + ''' + Load the classifiers for each node in the network. + ''' + svms = dict() + data, notes = read_data(self.__train_filename) + + with open("svm_params.csv") as param_file: + param_reader = csv.reader(param_file) + for row in param_reader: + svms[int(row[0])] = SVM.create_and_fit_svm(data, notes, int(row[1]), int(row[2])) + + return svms + + +def read_data(filename, delimiter=",", dict_mode=True): + ''' + Read data from a csv of reports. + ''' + if dict_mode: + train_data = dict() + notes = dict() + else: + train_data = [] + notes = [] + + with open(filename) as report_csv: + csv_reader = csv.reader(report_csv, delimiter=delimiter) + for row in csv_reader: + if dict_mode: + reporter_id = int(row[0]) + if train_data.get(reporter_id): + train_data[reporter_id].append(row[1:-1]) + notes[reporter_id].append(row[-1]) + else: + train_data[reporter_id] = [row[1:-1]] + notes[reporter_id] = [row[-1]] + else: + train_data.append(row[:-1]) + notes.append(row[-1]) + + return train_data, notes diff --git a/TrustModel.py b/TrustModel.py @@ -21,6 +21,9 @@ if __name__ == '__main__': PARSER.add_argument("--create-data", dest="is_creating", action="store_const", const=True, default=False, help="Create data and place it in the csv file.") + PARSER.add_argument("-e", "--epochs", dest="epochs", + action="store", default="200", + help="The number of epochs to bootstrap for.") PARSER.add_argument("-trf", "--train-file", dest="train_filename", action="store", default="reports-train.csv", help="Specify the training data file to read from or write to.") @@ -33,6 +36,9 @@ if __name__ == '__main__': PARSER.add_argument("--evolve-svm", dest="evolve_svm", action="store_const", const=True, default=False, help="Perform an evolutionary algorithm to find the optimal values for C and gamma") + PARSER.add_argument("--train-ann", dest="train_ann", action="store_const", + const=True, default=False, + help="Train an ANN on the previously generated data") ARGS = PARSER.parse_args() if len(sys.argv) == 1: @@ -40,24 +46,38 @@ if __name__ == '__main__': if ARGS.is_creating: # First blank out file - with open(ARGS.filename, "w") as FILE: + with open(ARGS.train_filename, "w") as FILE: + FILE.write("") + with open(ARGS.test_filename, "w") as FILE: FILE.write("") # Then create trust manager and bootstrap - TRUST_MANAGER = TrustManager.TrustManager(filename=ARGS.filename) - TRUST_MANAGER.bootstrap(1) + TRUST_MANAGER = TrustManager.TrustManager( + train_filename=ARGS.train_filename, test_filename=ARGS.test_filename + ) + TRUST_MANAGER.bootstrap(int(ARGS.epochs)) joblib.dump(TRUST_MANAGER, "trust_manager.pkl") if ARGS.evolve_svm: print("Evolving...") TRUST_MANAGER = joblib.load("trust_manager.pkl") - TRUST_MANAGER.set_filename(ARGS.filename) - print(TRUST_MANAGER.evolve_svm()) + TRUST_MANAGER.set_filenames(ARGS.train_filename, ARGS.test_filename) + TRUST_MANAGER.evolve_svm() + print("Done. Parameters written to svm_params.csv") if ARGS.fit_svm: - TRUST_MANAGER = TrustManager.TrustManager(filename=ARGS.filename) + TRUST_MANAGER = TrustManager.TrustManager( + train_filename=ARGS.train_filename, test_filename=ARGS.test_filename + ) print("Fitting SVM...") SVM = TRUST_MANAGER.train_svm() print("SVM fitted") joblib.dump(SVM, "svm.pkl") print("Done. SVM saved as svm.pkl") + + if ARGS.train_ann: + print("Training...") + TRUST_MANAGER = joblib.load("trust_manager.pkl") + TRUST_MANAGER.set_filenames(ARGS.train_filename, ARGS.test_filename) + TRUST_MANAGER.train_ann() + print("Done.")