raport
This commit is contained in:
parent
c9bbc11a79
commit
7e46213a57
129
Podprojekt_s444426/raport.md
Normal file
129
Podprojekt_s444426/raport.md
Normal file
@ -0,0 +1,129 @@
|
||||
# Agata Lenz - drzewa decyzyjne, algorytm ID3
|
||||
|
||||
## Wymagania
|
||||
Importowane biblioteki:
|
||||
* numpy
|
||||
* pandas
|
||||
|
||||
## Opis podprojektu
|
||||
Podprojekt implementuje algorytm ID3 pozwalający wyznaczyć drzewo decyzyjne, przy użyciu którego agent (traktor)podejmie decyzję co posadzić w danym miejscu, na podstawie:
|
||||
* *previous* - gatunku rośliny, która poprzednio rosła w danym miejscu
|
||||
* *pH* - pH gleby
|
||||
* *dry_level* - suchości gleby
|
||||
|
||||
## Uczenie modelu
|
||||
|
||||
### Dane
|
||||
Dane uczące jak i testowe znajdują się w pliku data.py. Dane uczące są zapisane w postaci listy, której elementy to przykładowe dane w formacie ['previous', 'pH', 'dry_level', 'label'], gdzie *label* oznacza posadzoną w wymienionych warunkach roślinę.
|
||||
Łącznie zestaw uczący zawiera 47 elementów, a zestaw testowy - 15.
|
||||
|
||||
Przykładowe elementy zestawu uczącego:
|
||||
```
|
||||
['pumpkin', 'neutral', 'dry', 'beetroot'],
|
||||
['none', 'neutral', 'wet', 'pumpkin'],
|
||||
['cabbage', 'alkaline', 'soaking wet', 'none']
|
||||
```
|
||||
|
||||
### Implementacja algorytmu ID3
|
||||
|
||||
Za budowę drzewa decyzyjnego odpowiada rekurencyjna funkcja *ID3(data, original_data, features, target_attribute_name="label", parent_node_class=None)*:
|
||||
|
||||
```
|
||||
def ID3(data, original_data, features, target_attribute_name="label", parent_node_class=None):
|
||||
"""
|
||||
Algorytm ID3
|
||||
|
||||
parametry:
|
||||
data zbiór danych, dla którego poszukujemy drzewa decyzyjnego
|
||||
original_data oryginalny zbiór danych (zwracany gdy data == None)
|
||||
features lista atrybutów wejściowego zbioru
|
||||
target_attribute_name docelowy atrybut, który chcemy przewidzieć
|
||||
parent_node_class nadrzędna wartość
|
||||
"""
|
||||
|
||||
# Jeżeli wszystkie atrybuty są takie same, zwracamy liść z pierwszą napotkaną wartością
|
||||
|
||||
if len(np.unique(data[target_attribute_name])) <= 1:
|
||||
return np.unique(data[target_attribute_name])[0]
|
||||
|
||||
elif len(data) == 0:
|
||||
return np.unique(original_data[target_attribute_name])[
|
||||
np.argmax(np.unique(original_data[target_attribute_name], return_counts=True)[1])]
|
||||
|
||||
elif len(features) == 0:
|
||||
return parent_node_class
|
||||
|
||||
else:
|
||||
|
||||
# Aktualizacja nadrzędnej wartości
|
||||
parent_node_class = np.unique(data[target_attribute_name])[
|
||||
np.argmax(np.unique(data[target_attribute_name], return_counts=True)[1])]
|
||||
|
||||
# Obliczenie przyrostu informacji dla każdego potencjalnego atrybutu,
|
||||
# według którego nastąpi podział zbioru
|
||||
item_values = [info_gain(data, feature, target_attribute_name) for feature in
|
||||
features]
|
||||
|
||||
# Najlepszym atrybutem jest ten o największym przyroście informacji
|
||||
best_feature_index = np.argmax(item_values)
|
||||
best_feature = features[best_feature_index]
|
||||
|
||||
# Struktura drzewa
|
||||
tree = {best_feature: {}}
|
||||
|
||||
# Aktualizacja zbioru atrybutów
|
||||
features = [i for i in features if i != best_feature]
|
||||
|
||||
# Dla każdej wartości wybranego atrybutu budujemy kolejne poddrzewo
|
||||
for value in np.unique(data[best_feature]):
|
||||
|
||||
sub_data = data.where(data[best_feature] == value).dropna()
|
||||
subtree = ID3(sub_data, data, features, target_attribute_name, parent_node_class)
|
||||
|
||||
tree[best_feature][value] = subtree
|
||||
|
||||
return (tree)
|
||||
```
|
||||
|
||||
Do obliczenia przyrostu informacji służy funkcja *info_gain(data, split_attribute_name, target_name="label")*, która dla wejściowego zestawu danych (*data*), oblicza jego entropię oraz średnią ważoną entropii każdego podzestawu (wyznaczanego przez unikalne wartości atrybutu *split_attribute_name*):
|
||||
|
||||
```
|
||||
def info_gain(data, split_attribute_name, target_name="label"):
|
||||
|
||||
total_entropy = entropy(data[target_name])
|
||||
vals, counts = np.unique(data[split_attribute_name], return_counts=True)
|
||||
|
||||
weighted_entropy = np.sum(
|
||||
[(counts[i] / np.sum(counts)) * entropy(data.where(data[split_attribute_name] == vals[i]).dropna()[target_name])
|
||||
for i in range(len(vals))])
|
||||
|
||||
information_gain = total_entropy - weighted_entropy
|
||||
|
||||
return information_gain
|
||||
```
|
||||
|
||||
## Implementacja w projekcie głównym
|
||||
|
||||
Funkcja, z której będzie korzystał traktor, aby podjąć decyzję o posadzeniu rośliny, to *def decide_to_plant(soil)*
|
||||
|
||||
```
|
||||
def decide_to_plant(soil):
|
||||
|
||||
if soil.have_plant():
|
||||
if soil.get_plant().get_collect():
|
||||
info = get_info(soil)
|
||||
plant.leave_soil()
|
||||
else:
|
||||
predicted = [['none']]
|
||||
else:
|
||||
info = get_info(soil)
|
||||
|
||||
data = []
|
||||
data.append(info)
|
||||
|
||||
predicted = predict_data(data)
|
||||
grow_a_plant(soil,predicted[0][0])
|
||||
|
||||
return predicted
|
||||
```
|
||||
Funkcja *get_info(soil)* zwraca listę parametrów obiektu Soil, potrzebną do poszukiwań w drzewie decyzyjnym.
|
Loading…
Reference in New Issue
Block a user