This commit is contained in:
Agata Lenz 2020-05-26 18:50:34 +00:00
parent c9bbc11a79
commit 7e46213a57

View 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.