SzIProjekt/Aleksandra Werda - drzewa decyzyje.md

5.3 KiB

Sztuczna inteligencja - projekt zespołowy - Autonomiczny Traktor

autorzy: Aleksandra Werda, Natalia Wiśniewska, Kinga Jagodzińska, Aleksandra Jonas

Aleksandra Werda - podprojekt: Sprawdzanie stanu gleby przy użyciu drzewa decyzyjnego


Zbiór uczący:

Zbiorem uczącym jest podany odgórnie zestaw danych w tablicy tablic lista, w którym kolejno każda cyfra odpowiada za: liczbę chwastów, poziom podlania, ph oraz ogólny stan gleby.

lista = [[1, 6, 7, 'neutralny'],
         [5, 8, 6, 'neutralny'],
         [5, 1, 7, 'zły'],
         [5, 5, 7, 'neutralny'],
         [1, 6, 6, 'neutralny'],
         [2, 8, 7, 'dobry'], ... ]

Tworzenie drzewa:

Algorytm tworzymy w pętli while. Tworzymy listę open_set aby wiedzieć jakie wierzchołki diagramu mamy jeszcze odwiedzić, a także closed_set, aby łatwo rozpoznać te już odwiedzone. Definiujemy także ob1 i ob2, które są dwoma wierzchołkami do których przejdziemy po zadaniu pytania w wierzchołku x. Mamy też listę node która mówi nam w jakim wierzchołku obecnie znajduje się przykład z tablicy lista o danym indeksie. Tablica myset podaje nam indeksy przykładów, które obecnie znajdują się w wierzchołku x. Pętlą for szukamy wersji pytania, która podzieli myset na dwie części o jak najmniejszym zanieczyszczeniu.

for y in itertools.product(range(3), range(1, 11)):
    self.Questions(y[0], y[1])
    s1 = [0, 0, 0]
    s2 = [0, 0, 0]

W funkcji for korzystamy z funkcji Questions, używając jako danych wejściowych kombinacji dwóch liczb: pierwsza wskazuje na to na który parametr z listy bedziemy patrzeć; druga mówi nam o liczbie, która podzieli myset według tego czy dany przykład posiada parametr wynoszący mniej czy więcej niż ta liczba.

def Questions(self, column, number):
    for i in self.myset:
        if lista[i][column] <= number:
            self.node[i] = self.ob1 
        else:
            self.node[i] = self.ob2 

Następnie liczymy ile w każdej z tych grup jest przykładów, w których występuje jeden z konkretnych stanów gleby.

    for z in range(len(lista)):
        if self.node[z] == self.ob1:
            if lista[z][3] == "zły":
                s1[0] = s1[0] + 1
            elif lista[z][3] == "neutralny":
                s1[1] = s1[1] + 1
            elif lista[z][3] == "dobry":
                s1[2] = s1[2] + 1
        elif self.node[z] == self.ob2:
            if lista[z][3] == "zły":
                s2[0] = s2[0] + 1
            elif lista[z][3] == "neutralny":
                s2[1] = s2[1] + 1
            elif lista[z][3] == "dobry":
                s2[2] = s2[2] + 1
s1_suma = s1[0] + s1[1] + s1[2]
s2_suma = s2[0] + s2[1] + s2[2]

Szukamy kombinacji z najniższym zanieczyszczeniem, wywołując przy tym dla wierzchołków ob1 i ob2 funkcję Gini, która sprawdza "czystość", czyli jak często losowy element będzie źle zindentyfikowany.

if s1_suma > 0 and s2_suma > 0:
    impurity = s1_suma / (s1_suma + s2_suma) * self.Gini(s1, s1_suma) +     s2_suma / (s1_suma + s2_suma) * self.Gini(s2, s2_suma)
    if imp > impurity:
        imp = impurity # imp = najmniejsze imp jakie uzyskalismy
        opt = y  # y = optymalna kombincja (48)
        l_gini = self.Gini(s1, s1_suma)
        p_gini = self.Gini(s2, s2_suma)
        odp_s1 = s1
        odp_s2 = s2
def Gini(self, x, suma):
    return 1 - (x[0] / suma) ** 2 - (x[1] / suma) 
                ** 2 - (x[2] / suma) ** 2

Jeżeli nasz lewy bądź prawy wierzchołek jest równy 0, oznacza to, że jest "czysty", czyli został prawidłowo oznaczony(jednym z 3 możliwych stanów).

if l_gini != 0:
    open_node.append(self.ob1) 
else:
    for y in range(3):
        if odp_s1[y] != 0:
            odp = y
        self.odpowiedzi.append([self.ob1, odp]) 
if p_gini != 0:
        open_node.append(self.ob2)
else:
    for y in range(3):
        if odp_s2[y] != 0:
            odp = y
        self.odpowiedzi.append([self.ob2, odp])

W funkcji Solutions generujemy listę wszystkich wierzchołków, które znajdują się na liście odpowiedzi.

for i in range(len(self.odpowiedzi)):
    lista_wierzch_kończących.append(self.odpowiedzi[i][0])

Jeżeli wierzchołek w którym jesteśmy znajduje się w tablicy odpowiedzi, to zapamiętujemy go. Następnie porównujemy przypisane wartości z konkretnymi cyframi, które określają stan naszej gleby. W tym wypadku 0 - zły, 1 - neutralny, 2- dobry. W efekcie dostajemy informację zwrotną w jakim stanie jest nasza gleba.

if x in lista_wierzch_kończących:
    for i in range(len(self.odpowiedzi)):
        if self.odpowiedzi[i][0] == x:  
            self.ind = i 
            break

Jeśli natomiast nasz wiechołek nie jest liściem w zbudowanym przez nas w funkcji Algorithm drzewie, wtedy szukamy wierzchołka do którego przejdziemy na podstawie pytania wpisanego w wierzchołek x na podstawie tablicy pytania.

for i in range(len(self.pytania)):
        if self.pytania[i][0] == x:
            self.ind = i
            break
x = self.Answers(self.pytania[self.ind][1][0], 
self.pytania[self.ind][1][1], pole)

Używamy do tego funkcji Answers:

def Answers(self, a, b, pole):
    if pole[a] <= b:
        return self.pytania[self.ind][2][0]
    else:
        return self.pytania[self.ind][2][1]