diff --git a/Justyna.md b/Justyna.md new file mode 100644 index 0000000..ab6520d --- /dev/null +++ b/Justyna.md @@ -0,0 +1,92 @@ +# Drzewa decyzyjne, algorytm ID3 + +### autor Justyna Zarzycka + +## Opis projektu +Projekt implementuje tworzenie drzewa decyzyjnego wykorzystującego algorytm ID3, ktióre pomaga określić chęci do pracy agenta na podstawie warunków panujących na planszy. + +### Tworzenie drzewa decyzyjnego + +Funkcja budująca drzewo za pomocą algorymu ID3: + +```py +def ID3(data, original_data, attributes, target, parent_node_class=None): + + if len(np.unique(data[target])) <= 1: + return np.unique(data[target])[0] + elif len(data) == 0: + return np.unique(original_data[target])[ + np.argmax(np.unique(original_data[target], return_counts=True)[1])] + elif len(attributes) == 0: + return parent_node_class + else: + parent_node_class = np.unique(data[target])[ + np.argmax(np.unique(data[target], return_counts=True)[1])] + + item_values = [info_gain(data, i, target) for i in + attributes] + + best_attribute_index = np.argmax(item_values) + best_attribute = attributes[best_attribute_index] + + tree = {best_attribute: {}} + + attributes = [i for i in attributes if i != best_attribute] + for value in np.unique(data[best_attribute]): + sub_data = data.where(data[best_attribute] == value).dropna() + subtree = ID3(sub_data, data, attributes, target, parent_node_class) + tree[best_attribute][value] = subtree + + return (tree) +``` +Cechą charakterystyczną algorytmu jest wybór atrybutów dla których kolejno przeprowadzane są testy taki, aby końcowe drzewo było jak najprostsze i jak najefektywniejsze. Wybór atrybutów opiera się na liczeniu entropii, co pozwala obliczyć, wybór którego z atrybutów da największy przyrost informacji. + +Obliczanie wartości przyrostu informacji: + +Funkcja oblicza który atrybut najlepiej rozdziela zbiór danych (dzieli zbiór przykładów na jak najbardziej równe podzbiory). + +```py +def info_gain(data, split_attribute, target): + + _entropy = entropy(data[target]) + vals, counts = np.unique(data[split_attribute], return_counts=True) + weighted_entropy = np.sum( + [(counts[i] / np.sum(counts)) * entropy(data.where(data[split_attribute] == vals[i]).dropna()[target]) + for i in range(len(vals))]) + information_gain = _entropy - weighted_entropy + + return information_gain +``` + +Entropia: + +Entropia jest miarą ilości informacji - im mniejsza entropia, tym więcej informacji. W przypadku problemu klasyfikacji przykładów do dwóch odrębnych klas, wzór na entropię przedstawia się następująco: + +Entropy(S) = - ∑ pᵢ * log₂(pᵢ) ; i = 1 to n +gdzie: +Z - źródło informacji +p - prawdopodobieństwo wystąpienia przykładu pozytywnego w zbiorze trenującym +(1-p) - prawdopodobieństwo wystąpienia przykładu negatywnego w zbiorze trenującym + +```py +def entropy(attribute): + values, counts = np.unique(target_col, return_counts=True) + entropy = np.sum( + [(-counts[i] / np.sum(counts)) * np.log2(counts[i] / np.sum(counts)) for i in range(len(values))]) + return entropy +``` + +### Zestaw uczący + +Zestaw budujący drzewo to lista zawierająca 24 przykładów waruków panujących na polu. Atrybyty zapisane są w formacie ['pogoda', 'ile_chwastow', 'ile_burakow', 'czy_chce_pracowac']. Przykłady ze zbioru: + +```py + ['slonecznie', 'duzo', 'bardzo_malo', 'srednio'], + ['deszcz', 'bardzo_duzo', 'malo', 'nie'], + ['grad', 'bardzo_duzo', 'bardzo_malo', 'nie'], + ['zachmurzenie', 'srednio', 'srednio', 'tak'] +``` + +### Implementacja w projekcie +Podprojet uruchamiany jest za pomocą klawisza *F5*. Pobierane są inforamcje o warunkach panujących na polu, na podstawie których oceniana jest chęć do pracy. + diff --git a/Justyna.py b/Justyna.py index 5cad369..003fd78 100644 --- a/Justyna.py +++ b/Justyna.py @@ -1,3 +1,120 @@ +import pandas as pd +import numpy as np +from pprint import pprint +import dataset +import random + +# obliczenie entropii dla wskazanej kolumny +def entropy(attribute): + values, counts = np.unique(attribute, return_counts=True) + entropy = np.sum( + [(-counts[i] / np.sum(counts)) * np.log2(counts[i] / np.sum(counts)) for i in range(len(values))]) + return entropy + +#obliczanie wartości przyrostu informacji +def info_gain(data, split_attribute, target): + + # Wartość entropii zbioru + _entropy = entropy(data[target]) + + # Wyodrębnienie poszczególnych podzbiorów + vals, counts = np.unique(data[split_attribute], return_counts=True) + + # Średnia ważona entropii każdego podzbioru + weighted_entropy = np.sum( + [(counts[i] / np.sum(counts)) * entropy(data.where(data[split_attribute] == vals[i]).dropna()[target]) + for i in range(len(vals))]) + + # Przyrost informacji + information_gain = _entropy - weighted_entropy + + return information_gain + + +def ID3(data, original_data, attributes, target, parent_node_class=None): + + + # Jeżeli wszystkie atrybuty są takie same, zwracamy liść z pierwszą napotkaną wartością + + if len(np.unique(data[target])) <= 1: + return np.unique(data[target])[0] + + elif len(data) == 0: + return np.unique(original_data[target])[ + np.argmax(np.unique(original_data[target], return_counts=True)[1])] + + elif len(attributes) == 0: + return parent_node_class + + else: + + # nadrzędna wartość + parent_node_class = np.unique(data[target])[ + np.argmax(np.unique(data[target], return_counts=True)[1])] + + # obliczenie przyrostu informacji dla każdego atrybutu + item_values = [info_gain(data, i, target) for i in + attributes] + + # Wybór najlepszego atrybutu + best_attribute_index = np.argmax(item_values) + best_attribute = attributes[best_attribute_index] + + # Struktura drzewa + tree = {best_attribute: {}} + + # Aktualizacja zbioru atrybutów + attributes = [i for i in attributes if i != best_attribute] + + # Budowa poddrzewa dla każdej wartości wybranego atrybutu + for value in np.unique(data[best_attribute]): + + sub_data = data.where(data[best_attribute] == value).dropna() + subtree = ID3(sub_data, data, attributes, target, parent_node_class) + + tree[best_attribute][value] = subtree + + return (tree) + +#tesownie drzewa +def test(data, tree): + queries = data.iloc[:, :-1].to_dict(orient="records") + + predicted = pd.DataFrame(columns=["predicted"]) + + for i in range(len(data)): + predicted.loc[i, "predicted"] = search(queries[i], tree, 'nie') + print('Precyzja przewidywań: ', (np.sum(predicted["predicted"] == data['czy_chce_pracowac']) / len(data)) * 100, '%') + +#dostowanie danych (lista na słownik) i wywolanie na nich funkcji serach +def data_to_dict(data, tree): + + queries = pd.DataFrame(data=data, columns=dataset.header) + predicted = pd.DataFrame(columns=["predicted"]) + dict = queries.iloc[:, :-1].to_dict(orient="records") + + for i in range(len(data)): + predicted.loc[i, "predicted"] = search(dict[i], tree, 'nie') + + predicted_list = predicted.values.tolist() + return predicted_list[0][0] + +#przeszukwianie drzewa +def search(query, tree, default='nie'): + + for key in list(query.keys()): + if key in list(tree.keys()): + try: + result = tree[key][query[key]] + except: + return default + result = tree[key][query[key]] + if isinstance(result, dict): + return search(query, result) + + else: + return result + class main(): def __init__(self,traktor,field,ui,path): self.traktor = traktor @@ -6,4 +123,75 @@ class main(): self.path = path def main(self): - pass \ No newline at end of file + training_data = pd.DataFrame(data=dataset.training_data, columns=dataset.header) + testing_data = pd.DataFrame(data=dataset.testing_data, columns=dataset.header) + + # Utworzenie drzewa + tree = ID3(training_data, training_data, training_data.columns[:-1], 'czy_chce_pracowac') + pprint(tree) + + # Testowanie drzewa + print(test(testing_data, tree)) + + # Uzyskanie danych od agenta + ocena_burakow = self.ocen_ile_burakow() + ocena_chwastow = self.ocen_ile_chwastow() + pogoda = self.pogoda() + print('chwasty: ' + ocena_chwastow) + print('buraki: ' + ocena_burakow) + print('pogoda: ' + pogoda) + data = [[pogoda, ocena_chwastow, ocena_burakow, '']] + + #podjecie decyzji + result = data_to_dict(data, tree) + print('czy traktor chce pracowac: ' + result) + + + def licz_chwasty_buraki(self): + chwasty = 0 + buraki = 0 + + for i in self.field.field_matrix: + for j in i: + if(j==8): + buraki = buraki + 1 + elif(j%2==1): + chwasty = chwasty + 1 + return chwasty, buraki + + def ocen_ile_burakow(self): + chwasty, buraki = self.licz_chwasty_buraki() + if buraki < 5: + return 'bardzo_malo' + elif buraki >= 5 and buraki<10: + return 'malo' + elif buraki >=10 and buraki<15: + return 'srednio' + elif buraki >=15 and buraki<20: + return 'duzo' + elif buraki >=20: + return 'bardzo_duzo' + + def ocen_ile_chwastow(self): + chwasty, buraki = self.licz_chwasty_buraki() + if chwasty < 40: + return 'bardzo_malo' + elif chwasty >= 40 and chwasty<42: + return 'malo' + elif chwasty >=42 and chwasty<45: + return 'srednio' + elif chwasty >=45 and chwasty<48: + return 'duzo' + elif chwasty >=48: + return 'bardzo_duzo' + + def pogoda(self): + number = random.randrange(0, 4) + if number==0: + return 'slonecznie' + elif number==1: + return 'deszcz' + elif number==2: + return 'grad' + elif number==3: + return 'zachmurzenie' \ No newline at end of file diff --git a/Marcin.py b/Marcin.py index eac09c7..4a5f415 100644 --- a/Marcin.py +++ b/Marcin.py @@ -74,3 +74,14 @@ class main(): return 3 elif self.mode_value in [6]: return 10 + + def main_collective(self,poz): + self.pole = self.ui.field_images[self.field.get_value(poz)] + self.img = pygame.surfarray.array3d(self.pole) + self.img = self.img.transpose([1, 0, 2]) + self.img = cv2.cvtColor(self.img, cv2.COLOR_RGB2BGR) + self.reco = self.mode(self.recognition(self.img)) + if self.reco == 10: + print("Nic nie trzeba robić") + else: + self.traktor.set_mode(self.reco) \ No newline at end of file diff --git a/collective.py b/collective.py new file mode 100644 index 0000000..a797c37 --- /dev/null +++ b/collective.py @@ -0,0 +1,15 @@ +class main(): + + def __init__(self,marcin, justyna, kamila, piotrek, traktor, pole, UI, path): + self.marcin = marcin + self.justyna = justyna + self.kamila = kamila + self.piotrek = piotrek + self.traktor = traktor + self.pole = pole + self.UI = UI + self.path = path + + def main(self): + # self.marcin.main_collective(pozycja) To podaje jakie jest pole jest na danym miejscu + pass \ No newline at end of file diff --git a/dataset.py b/dataset.py new file mode 100644 index 0000000..8f01ab4 --- /dev/null +++ b/dataset.py @@ -0,0 +1,39 @@ + +header = ['pogoda', 'ile_chwastow', 'ile_burakow', 'czy_chce_pracowac'] + +training_data = [ + ['slonecznie', 'duzo', 'malo', 'tak'], + ['slonecznie', 'srednio', 'srdenio', 'tak'], + ['slonecznie', 'bardzo_duzo', 'malo', 'srednio'], + ['slonecznie', 'malo', 'bardzo_duzo', 'tak'], + ['slonecznie', 'duzo', 'bardzo_malo', 'srednio'], + ['slonecznie', 'malo', 'bardzo_malo', 'srednio'], + ['deszcz', 'bardzo_duzo', 'malo', 'nie'], + ['deszcz', 'srednio', 'srednio', 'srednio'], + ['deszcz', 'malo', 'bardzo_duzo', 'tak'], + ['deszcz', 'duzo', 'duzo', 'srednio'], + ['deszcz', 'malo', 'malo', 'tak'], + ['deszcz', 'srednio', 'bardzo_duzo', 'tak'], + ['grad', 'srednio', 'malo', 'nie'], + ['grad', 'bradzo_malo', 'bardzo_duzo', 'srednio'], + ['grad', 'duzo', 'srednio', 'nie'], + ['grad', 'malo', 'bardzo_malo', 'srednio'], + ['grad', 'bardzo_duzo', 'bardzo_malo', 'nie'], + ['grad', 'srednio', 'bardzo_malo', 'nie'], + ['zachmurzenie', 'srednio', 'srednio', 'tak'], + ['zachmurzenie', 'bardzo_duzo', 'duzo', 'nie'], + ['zachmurzenie', 'malo', 'srednio', 'tak'], + ['zachmurzenie', 'duzo', 'malo', 'srednio'], + ['zachmurzenie', 'malo', 'bardzo_malo', 'tak'], + ['zachmurzenie', 'malo', 'srednio', 'tak'] +] + +testing_data = [ + ['slonecznie', 'srednio', 'srednio', 'tak'], + ['deszcz', 'duzo', 'malo', 'nie'], + ['deszcz', 'duzo', 'duzo', 'srednio'], + ['grad', 'srednio', 'duzo', 'nie'], + ['zachmurzenie', 'bardzo_duzo', 'malo', 'nie'], + ['zachmurzenie', 'bardzo_duzo', 'malo', 'nie'] +] + diff --git a/main.py b/main.py index 610c939..a0bda4d 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ import pygame, sys -import tractor, pathfinding, field, ui, Justyna, Kamila, Marcin, Piotrek, pathfinding_decision +import tractor, pathfinding, field, ui, Justyna, Kamila, Marcin, Piotrek, pathfinding_decision, collective from pygame.locals import * pole = field.field() @@ -10,6 +10,7 @@ j = Justyna.main(traktor,pole,UI,path) k = Kamila.main(traktor,pole,UI,pathfinding_decision.pathfinding_dec()) neuro = Marcin.main(traktor,pole,UI,path) p = Piotrek.main(traktor,pole,UI,path) +c = collective.main(neuro,j,k,p,traktor,pole,UI,path) pygame.init() UI.update() UI.update() @@ -41,12 +42,6 @@ while True: traktor.set_mode(3) if key[K_p]: path.pathfinding(traktor,pole,UI) - if key[K_F9]: - print(pole.if_value(traktor.get_modes_values())) - if key[K_F10]: - print(traktor.get_poz()) - if key[K_F11]: - print(traktor.get_field_value()) if key[K_F5]: #Dla projektu Justyny j.main() @@ -59,6 +54,15 @@ while True: if key[K_F8]: # Dla projektu Piotrka p.main() + if key[K_F9]: + print(pole.if_value(traktor.get_modes_values())) + if key[K_F10]: + print(traktor.get_poz()) + if key[K_F11]: + print(traktor.get_field_value()) + if key[K_12]: + c.main() + UI.update() UI.update() diff --git a/si.cfg b/si.cfg new file mode 100644 index 0000000..36a42d4 --- /dev/null +++ b/si.cfg @@ -0,0 +1,182 @@ +[net] +# Testing +batch=24 +subdivisions=8 +# Training +# batch=64 +# subdivisions=2 +width=416 +height=416 +channels=3 +momentum=0.9 +decay=0.0005 +angle=0 +saturation = 1.5 +exposure = 1.5 +hue=.1 + +learning_rate=0.001 +burn_in=1000 +max_batches = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=16 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=1 + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +########### + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=42 +activation=linear + + + +[yolo] +mask = 3,4,5 +anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 +classes=9 +num=6 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 8 + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=42 +activation=linear + +[yolo] +mask = 0,1,2 +anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 +classes=9 +num=6 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 diff --git a/si.names b/si.names new file mode 100644 index 0000000..1000f90 --- /dev/null +++ b/si.names @@ -0,0 +1,9 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 diff --git a/si_20000.weights b/si_20000.weights new file mode 100644 index 0000000..508285a Binary files /dev/null and b/si_20000.weights differ