##### Raport przygotowała: Marta Roszak ##### Raportowany okres: 4 maja - 10 maja 2020 ##### Niniejszy raport poświęcony jest przekazaniu informacji na temat stanu mini-projektu indywidualnego w ramach projektu grupowego realizowanego na przedmiot Sztuczna Inteligencja w roku akademickim 2019/2020. Tematem realizowanego projektu indywidualnego jest stworzenie sztucznej inteligencji rozpoznającej z podanych parametrów czy danie jest ciężkostrawne czy nie. Do tego celu wykorzystane zostało drzewo decyzyjne oraz biblioteki: - scikit - learn - pydotplus - joblib - IPython - pandas ## Uczenie modelu ## #### Dane wejściowe: #### Do utworzenia modelu przygotowałam zestaw danych składający się z 60 krotek, każda składająca się z 6 liczb oznaczających odpowiednio: - wiek osoby zamawiającej danie (z przedziału 10 do 60); - zawartość tłuszczu w daniu (z przedziału 0 do 16); - zawartość błonnika w daniu (z przedziału 0 do 16); - płeć osoby zamawiającej (0 - mężczyzna lub 1 - kobieta); - wskazanie czy danie jest ostre czy nie (0 - nieostre lub 1 - ostre); - wskazanie czy danie jest ciężkostrawne czy nie (0 - nie lub 1 - tak); Liczby oddzielone są przecinkami i zapisane w pliku z rozszerzeniem .csv. #### Proces uczenia: #### Na początku dane są importowane do programu: ```python def dataImport(): dataset = pd.read_csv('learnData4.csv', sep=',', header=None) return dataset ``` Następnie dane są dzielone odpowiednio na zestaw cech (*X*) i zestaw klas - "wyników" (*Y*). Zbiory te są jeszcze, przy pomocy funkcji **train_test_split** (z biblioteki scikit - learn) dodatkowo dzielone na zestawy do uczenia i zestawy do testowania (*x_train*, *x_test*, *y_train*, *y_test*): ```python def splitDataSet(dataset): X = dataset.values[:, 0:5] Y = dataset.values[:, 5] x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=100) return X, Y, x_train, x_test, y_train, y_test ``` Zbiór danych testowych składa się z 0.3 krotek zestawu początkowego. Korzystając z funkcji **DecisionTreeClassifier** utworzony zostaje model w postaci drzewa, do którego następnie zostają załadowane uprzednio przygotowane zbiory danych: ```python model = tree.DecisionTreeClassifier() model2 = tree.DecisionTreeClassifier(criterion="entropy") model.fit(x_train, y_train) model2.fit(x_train, y_train) ``` Funkcja **DecisionTreeClassifier** domyślnie wykorzystuje indeks Gini jako kryterium podziału. Ja jednakże, wygenerowałam dodatkowo model, gdzie jako kryterium podziału została przyjęta entropia. Następnie modele zostają poddane testowi i obliczony zostaje wskaźnik trafności wygenerowanych, na zbiorze testowym, wyników: ```python pred = model.predict(x_test) pred2 = model2.predict(x_test) acc = accuracy_score(y_test, pred) * 100 acc2 = accuracy_score(y_test, pred2) * 100 print("akuratnosc dla modelu Gini: " + str(acc)) # aprox. 77.78% print("akuratnosc dla modelu Entropy: " + str(acc2)) # aprox. 83.33% ``` #### Operacje na wygenerowanym modelu: #### W tym przypadku, model z entropią daje nam większą trafność. Zatem to on zostanie wykorzystany w głównym programie. Model drzewa zostaje zapisany w pliku .sav: ```python filename = 'finalized_model.sav' joblib.dump(model2, filename) ``` Dodatkowo zostaje wygenerowane (przy pomocy biblioteki Graphviz i IPython) graficzne przedstawienie drzewa i zapisane w pliku .png: ```python dot_data2 = tree.export_graphviz(model2, out_file=None, feature_names=["age", "fat", "fiber", "sex", "spicy"], class_names=["easty to digest", "hard to digest"], filled=True, rounded=True, special_characters=True) graph2 pydotplus.graph_from_dot_data(dot_data2) Image(graph2.create_png()) graph2.write_png("digest_entropy.png") ``` ## Integracja z projektem ## Po uruchomieniu programu i wybraniu na ekranie głównym opcji *Ciężkostrawność dań*, uruchomiona zostaje funkcja *Evaluate()*, która ładuje z pliku model drzewa. Zainicjowany zostaje również przykładowy stan restauracji (dodanie kilku klientów, przypisanie im stołów i talerzy). Wywoływany jest też przykładowy ruch kelnera, który podchodzi do kilku stolików i pomaga w ocenie strawności dania, przy pomocy funkcji *predictDigest()*: ```python def predictDigest(dish, client, model): data = [] data.append(client.age) data.append(dish.fatContent) data.append(dish.fiberContent) data.append(client.sex) data.append(dish.spicy) prediction = model.predict([data]) if prediction == 1: messagebox.showinfo("opinia", "Z tym może być ciężko. " + str(data)) return prediction else: messagebox.showinfo("opinia", "Z tym nie będzie tak źle! " + str(data)) return prediction ``` Funkcja jako parametry przyjmuje obiekty danie, klient i załadowany model. Parametry potrzebne modelowi do wyznaczenia wyniku pobierane są z odpowiednich obiektów i jako tablica przekazywane do funkcji *predict()*. Następnie, w zależności od otrzymanego wyniku wyświetlany jest odpowiedni komunikat i dane jakie podlegały ocenie. Pola *fatContent*, *fiberContent*, *spicy* klasy *Dish* w momencie tworzenia instancji klasy są ustawiane na losowo wygenerowaną liczbę z odpowiednich przedziałów: ```python self.fatContent = random.randint(0, 16) self.fiberContent = random.randint(0, 16) self.spicy = random.randint(0, 1) ``` Po zakończeniu trasy wyświetlany jest stosowny komunikat, a aplikacja zostaje wyłączona.