DSZI_2020_Projekt/niestrawność_potraw_Marta_Roszak.md
2020-05-10 19:14:00 +02:00

5.7 KiB

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:

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):

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:

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:

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:

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:

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():

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:

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.