This commit is contained in:
magdabiadala 2020-05-11 15:03:12 +02:00
commit 7532a0206f
4 changed files with 75 additions and 18 deletions

View File

@ -30,7 +30,7 @@ na przykład kładąc paczkę z lakierem/benzyną na regale lepiej mieć trochę
kładąc książkę - że nie zniszczeje od wilgoci. W związku z tym zamiast prostej odpowiedzi Tak/Nie na pytanie kładąc książkę - że nie zniszczeje od wilgoci. W związku z tym zamiast prostej odpowiedzi Tak/Nie na pytanie
czy dany obiekt można położyć na danym regale potrzebna była przewidywana wartość prawdopodobieństwa że w danym miejscu czy dany obiekt można położyć na danym regale potrzebna była przewidywana wartość prawdopodobieństwa że w danym miejscu
zachowa się on w dobrym stanie. Wszystkie te progi wynoszą odpowiednio: zachowa się on w dobrym stanie. Wszystkie te progi wynoszą odpowiednio:
`` ``` python
PACKAGE_PLACE_TRESHOLD = { PACKAGE_PLACE_TRESHOLD = {
"normal": 0.8, "normal": 0.8,
"freezed": 0.85, "freezed": 0.85,
@ -38,7 +38,7 @@ zachowa się on w dobrym stanie. Wszystkie te progi wynoszą odpowiednio:
"flammable": 0.9, "flammable": 0.9,
"keep_dry": 0.8 "keep_dry": 0.8
} }
`` ```
Zdecydowałem się więc na wybór drzewa regresyjnego. Zdecydowałem się więc na wybór drzewa regresyjnego.
Biblioteką której użyłem w celu implementacji drzewa jest scikit-learn. Biblioteką której użyłem w celu implementacji drzewa jest scikit-learn.
Najważniejszym problemem oprócz dokładności oszacowań dokonanych przy pomocy drzewa było uniknięcie overfittingu(przepasowania), Najważniejszym problemem oprócz dokładności oszacowań dokonanych przy pomocy drzewa było uniknięcie overfittingu(przepasowania),
@ -46,10 +46,13 @@ czyli sytuacji, w której drzewo perfekcyjnie dopasuje się do danych ze zbioru
z danymi spoza tego zbioru poradzi sobie już dużo gorzej. Oprócz błędnej oceny danych innych niż ze zbioru uczącego sygnałem wskazującym na overfitting drzewa z danymi spoza tego zbioru poradzi sobie już dużo gorzej. Oprócz błędnej oceny danych innych niż ze zbioru uczącego sygnałem wskazującym na overfitting drzewa
jest zbyt duża jego głębokość drzewa (odległość od korzenia do najdalszego liścia), oraz liście zawierające tylko 1 rekord. jest zbyt duża jego głębokość drzewa (odległość od korzenia do najdalszego liścia), oraz liście zawierające tylko 1 rekord.
W celu uniknięcia overfittingu zdecydowałem się na ograniczenie maksymalnej głębokości drzewa, oraz na ustawienie minimalnej W celu uniknięcia overfittingu zdecydowałem się na ograniczenie maksymalnej głębokości drzewa, oraz na ustawienie minimalnej
ilości rekordów w liściu. Drzewo wraz z odpowiednimi ograniczeniami zdefiniowane jest w następujący sposób \ ilości rekordów w liściu. Drzewo wraz z odpowiednimi ograniczeniami zdefiniowane jest w następujący sposób
``clf = DecisionTreeRegressor(ccp_alpha=0.02, min_samples_leaf=5, max_depth=5)`` ```python
clf = DecisionTreeRegressor(ccp_alpha=0.02, min_samples_leaf=5, max_depth=5)
```
gdzie argumenty min_samples_leaf, oraz max_depth oznaczają odpowiednio minimalną ilość rekordów(przykładów ze zbioru uczącego) w liściu, oraz maksymalną głębokość drzewa. gdzie argumenty min_samples_leaf, oraz max_depth oznaczają odpowiednio minimalną ilość rekordów(przykładów ze zbioru uczącego) w liściu, oraz maksymalną głębokość drzewa.
Kryterium według którego mierzona jest "jakość" rozgałęzienia jest tzw. MSE(Mean Squared Error), czyli kwadrat odchylenia standardowego wartości przewidywanej wobec faktycznej. Argument ccp_alpha oznacza parametr \alpha stosowany przy complexity-cost pruning. Pruning oznacza dalsze przycięcie drzewa, aby uniknąć overfittingu
Kryterium według którego mierzona jest "jakość" rozgałęzienia jest tzw. MSE(Mean Squared Error), czyli błąd średniokwadratowy(średnia kwadratów odchylenia wielkości oczekiwanej od rzeczywistej).
Dobierając te parametry wyszedłem z założenia że jeżeli 5 rekordów będzie w jednym liściu, to znaczy że najprawdopodbniej zachodzi Dobierając te parametry wyszedłem z założenia że jeżeli 5 rekordów będzie w jednym liściu, to znaczy że najprawdopodbniej zachodzi
już w ich przypadku pewna prawidłowość, i mają one jakieś wspólne cechy, które determinują taką, a nie inną wartość przewidywaną, już w ich przypadku pewna prawidłowość, i mają one jakieś wspólne cechy, które determinują taką, a nie inną wartość przewidywaną,
w odróżnieniu od sytuacji gdy liść zawierałby tylko 1-2 rekordy, co wskazywałoby na bardzo specyficzne parametry takiego/ich rekordu/ów, w odróżnieniu od sytuacji gdy liść zawierałby tylko 1-2 rekordy, co wskazywałoby na bardzo specyficzne parametry takiego/ich rekordu/ów,
@ -57,8 +60,9 @@ i prawdopodobnie oznaczało overfitting drzewa. W przypadku głębokości chodzi
Zastosowany zbiór uczący obejmuje 373 rekordy zapisane w formacie .csv, w którym poszczególne kolumny oznaczają odpowiednio: Zastosowany zbiór uczący obejmuje 373 rekordy zapisane w formacie .csv, w którym poszczególne kolumny oznaczają odpowiednio:
produkt, kategorię produktu, temperature na regale, wilgotność powietrza na danym regale, szansę że przedmiot po dłuższym czasie przechowywania będzie w dobrym stanie, oraz informację czy można bezpiecznie go tu położyć. produkt, kategorię produktu, temperature na regale, wilgotność powietrza na danym regale, szansę że przedmiot po dłuższym czasie przechowywania będzie w dobrym stanie, oraz informację czy można bezpiecznie go tu położyć.
Przykładowy rekord: ``frozen food,freezed,21, 0.5, 0.01, 0 `` . Zbiór testowy z kolei zawiera 26 rekordów w tym samym formacie. Przykładowy rekord: ``frozen food,freezed,21, 0.5, 0.01, 0 `` . Zbiór testowy z kolei zawiera 26 rekordów w tym samym formacie.
Przygotowanie zbioru uczącego i testowego dla drzewa: \ Zbiór uczący znajduje się w pliku package_location_classifier/trainset/trainset.csv, a testowy package_location/testset/testset.csv.
`` Przygotowanie zbioru uczącego i testowego dla drzewa:
``` python
products = pd.read_csv("package_location_classifier/trainset/trainset.csv", header=0, sep=",", names=cols_names) products = pd.read_csv("package_location_classifier/trainset/trainset.csv", header=0, sep=",", names=cols_names)
testset = pd.read_csv("package_location_classifier/testset/testset.csv", header=None, sep=",", names=cols_names) testset = pd.read_csv("package_location_classifier/testset/testset.csv", header=None, sep=",", names=cols_names)
products = products.round({"chance_of_survive": 1}) products = products.round({"chance_of_survive": 1})
@ -70,7 +74,63 @@ Przygotowanie zbioru uczącego i testowego dla drzewa: \
products = products.sample(frac=1) products = products.sample(frac=1)
X_train = pd.get_dummies(products[feature_cols]) X_train = pd.get_dummies(products[feature_cols])
y_train = products.chance_of_survive y_train = products.chance_of_survive
`` ```
Graficzna reprezentacja drzewa wygenerowanego dla tego zbioru uczącego: \ Uczenie drzewa i ewaluacja przy pomocy zbioru testowego:
[Przykładowe drzewo](Drzewo.png) ``` python
self.predictor = clf.fit(X_train, y_train)
y_pred = self.predictor.predict(test_X)
```
Graficzna reprezentacja drzewa wygenerowanego dla tego zbioru uczącego:
![Przykładowe drzewo](Drzewo.png)
Wyniki ewaluacji zestawu testowego, znajdujące się w pliku Test_results.xlsx:
![Wyniki testu](Test_Results.png)
Przewidywana wartość w zestawie testowym różni się od wartości faktycznej średnio o 0.87, jako że w raporcie wartości są pomnożone przez 10, daje to średnio
0.087 wartości różnicy w czasie działania drzewa.
##### Zastosowanie drzewa w części wspólnej projektu
Po podniesieniu paczki przez agenta odpalana jest funkcja szukająca najbliższego pasującego regału.
Przy poszukiwaniu takiego regału stosowana jest funkcja heurystyczna o następującym kodzie:
```python
def rack_heuristics(self, start, goal, can_place):
heur_can_place = not can_place
diff_x = pow(goal.x - start.x, 2)
diff_y = pow(goal.y - start.y, 2)
place_cost = 100 * float(heur_can_place)
return round(sqrt(diff_x + diff_y), 3) + float(place_cost)
```
Parametr can_place to wynik ewaluacji pola goal, przy pomocy drzewa:
```python
for rack in quarter_racks:
new_node = Node(rack.x_position, rack.y_position)
can_place = self.location_classifier.check_if_can_place(package, rack)
cost = self.rack_heuristics(start_node, new_node, can_place)
```
self.location_classifier, to obiekt klasy PackageLocationClassifier.
Klasa ta zawiera metodę check_if_can_place() :
```python
def check_if_can_place(self, package, tile):
category = package.category
cat_treshold = PACKAGE_PLACE_TRESHOLD[category]
fields = [[
tile.air_temperature,
tile.humidity,
category == "flammable",
category == "fragile",
category=="freezed" ,
category == "keep_dry",
category == "normal"
]]
quality_of_place = round(self.predictor.predict(fields)[0]/10, 2)
if quality_of_place > cat_treshold:
return True
return False
```

BIN
Test_Results.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
Test_results.xlsx Normal file

Binary file not shown.

View File

@ -42,17 +42,16 @@ class PackageLocationClassifier():
clf = DecisionTreeRegressor(ccp_alpha=0.02, min_samples_leaf=5, max_depth=5) clf = DecisionTreeRegressor(ccp_alpha=0.02, min_samples_leaf=5, max_depth=5)
self.predictor = clf.fit(X_train, y_train) self.predictor = clf.fit(X_train, y_train)
y_pred = self.predictor.predict(test_X) y_pred = self.predictor.predict(test_X)
evaluation = pd.DataFrame({'category': testset.category, 'temperature': testset.temperature , 'humid': testset.humidity ,'Actual': test_y, 'Predicted': y_pred}) evaluation = pd.DataFrame({'category': testset.category, 'temperature': testset.temperature , 'humid': testset.humidity ,'Actual': test_y, 'Predicted': y_pred})
evaluation = evaluation.round({'Actual': 3, 'Predicted': 3})
evaluation['Prediction_diff'] = abs(evaluation['Actual'] - evaluation['Predicted']) evaluation['Prediction_diff'] = abs(evaluation['Actual'] - evaluation['Predicted'])
print("Prediction differs from actual value by average {}".format(round(evaluation['Prediction_diff'].mean(), 2))) print("Prediction differs from actual value by average {}".format(round(evaluation['Prediction_diff'].mean(), 2)))
export_graphviz(clf, out_file=data, filled=True, rounded=True, special_characters=True, feature_names=dummies_names) # export_graphviz(clf, out_file=data, filled=True, rounded=True, special_characters=True, feature_names=dummies_names)
graph = pydotplus.graph_from_dot_data(data.getvalue()) # graph = pydotplus.graph_from_dot_data(data.getvalue())
graph.write_png('Drzewo.png') # graph.write_png('Drzewo.png')
Image(graph.create_png()) # Image(graph.create_png())
def check_if_can_place(self, package, tile): def check_if_can_place(self, package, tile):
category = package.category category = package.category
@ -68,8 +67,6 @@ class PackageLocationClassifier():
]] ]]
quality_of_place = round(self.predictor.predict(fields)[0]/10, 2) quality_of_place = round(self.predictor.predict(fields)[0]/10, 2)
# print("{} - dopasowanie {}".format(package,quality_of_place))
# pdb.set_trace()
if quality_of_place > cat_treshold: if quality_of_place > cat_treshold:
return True return True
return False return False