From 010814eccc6d078d5bd3681d5e116d063945982b Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:36:44 +0300 Subject: [PATCH 1/9] Create knowledge.py --- knowledge.py | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 knowledge.py diff --git a/knowledge.py b/knowledge.py new file mode 100644 index 000000000..58ca29332 --- /dev/null +++ b/knowledge.py @@ -0,0 +1,184 @@ +from random import shuffle +from utils import powerset + + +def current_best_learning(examples, h, examples_so_far=[]): + """ [Figure 19.2] + The hypothesis is a dictionary in the following form: + * The keys are the attribute names. + * The NOT operation is denoted by an exclamation mark (!) in front of a value. + * A special key (GOAL) for the goal/target attribute. Its value is boolean. + * A special key (|) for the OR operation. The value for that key is another + dictionary/hypothesis, in a recursive structure.""" + if not examples: + return h + + e = examples[0] + if is_consistent(e, h): + return current_best_learning(examples[1:], h, examples_so_far + [e]) + elif false_positive(e, h): + for h2 in specializations(examples_so_far + [e], h): + h3 = current_best_learning(examples[1:], h2, examples_so_far + [e]) + if h3 != 'FAIL': + return h3 + elif false_negative(e, h): + for h2 in generalizations(examples_so_far + [e], h): + h3 = current_best_learning(examples[1:], h2, examples_so_far + [e]) + if h3 != 'FAIL': + return h3 + + return 'FAIL' + + +def specializations(examples_so_far, h): + """Specialize the hypothesis. First add AND operations, then + recursively specialize in the OR part of the hypothesis.""" + hypotheses = [] + + # Add AND operations + for e in examples_so_far: + mod = '' + if not e['GOAL']: + mod = '!' + + for k, v in e.items(): + if k in h or k == 'GOAL' or k == '|': + continue + + h2 = h.copy() + h2[k] = mod + v + if check_all_consistency(examples_so_far, h2): + hypotheses += [h2] + + + # Specialize in OR + if '|' in h.keys(): + for h_or in specializations(examples_so_far, h['|']): + h2 = h.copy() + h2['|'] = h_or + hypotheses += [h2] + + shuffle(hypotheses) + return hypotheses + + +def generalizations(examples_so_far, h): + """Generalize the hypothesis. First delete operations (including OR) from + the hypothesis. Then, add OR operations if one doesn't already exist. + If an OR operation exists, recursively generalize in that.""" + hypotheses = [] + h_powerset = powerset(h.keys()) + + # Delete operations + for deletions in h_powerset: + h2 = h.copy() + for d in deletions: + del h2[d] + + if check_all_consistency(examples_so_far, h2): + hypotheses += [h2] + + if '|' not in h: + # Add OR + hypotheses.extend(add_or(examples_so_far, h)) + else: + # Generalize in OR + for h_or in generalizations(examples_so_far, h['|']): + if check_negative_consistency(examples_so_far, h2): + h2 = h.copy() + h2['|'] = h_or + hypotheses += [h2] + + shuffle(hypotheses) + return hypotheses + + +def add_or(examples_so_far, h): + """Adds an OR operation to the hypothesis. The AND operations inside the OR + are generated by the last example (which is the problematic one). Note that + the function adds just one OR operation.""" + hypotheses = [] + e = examples_so_far[-1] + mod = '!' + if e['GOAL']: + mod = '' + + attrs = {k: mod + v for k, v in e.items() if k != 'GOAL'} + a_powerset = powerset(attrs.keys()) + + for c in a_powerset: + h2 = {} + for k in c: + h2[k] = attrs[k] + + if check_negative_consistency(examples_so_far, h2): + h3 = h.copy() + h3['|'] = h2 + hypotheses += [h3] + + return hypotheses + + +def check_all_consistency(examples, h): + for e in examples: + if not is_consistent(e, h): + return False + + return True + + +def check_negative_consistency(examples, h): + """Check if the negative examples are consistent under h.""" + for e in examples: + if e['GOAL']: + continue + + if not is_consistent(e, h): + return False + + return True + + +def guess_value(e, h): + """Guess value of example e under hypothesis h.""" + for k, v in h.items(): + if k == '|': + if guess_value(e, v): + return True + continue + + if v[0] == '!': + # v is a NOT expression + # e[k], thus, should not be equal to v + if e[k] == v[1:]: + if '|' not in h: + return False + else: + return guess_value(e, h['|']) + elif e[k] != v: + if '|' not in h: + return False + else: + return guess_value(e, h['|']) + + return True + + +def is_consistent(e, h): + return e["GOAL"] == guess_value(e, h) + + +def false_positive(e, h): + if e["GOAL"] == False: + if guess_value(e, h): + return True + + return False + + +def false_negative(e, h): + if e["GOAL"] == True: + if not guess_value(e, h): + return True + + return False From e8471eb6b0710e013358c455fe05fa4dec61bf14 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:38:00 +0300 Subject: [PATCH 2/9] Create test_knowledge.py --- tests/test_knowledge.py | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/test_knowledge.py diff --git a/tests/test_knowledge.py b/tests/test_knowledge.py new file mode 100644 index 000000000..9d757be72 --- /dev/null +++ b/tests/test_knowledge.py @@ -0,0 +1,78 @@ +from knowledge import * +import random + +random.seed("aima-python") + + +# def test_current_best_learning_full(): +# """ !!! This takes too long !!! """ +# examples = restaurant +# hypothesis = {'Alt': 'Yes'} +# h = current_best_learning(examples, hypothesis) +# values = [] +# for e in examples: +# values.append(guess_value(e, h)) +# +# assert values == [True, False, True, True, False, True, False, True, False, False, False, True] + + +def test_current_best_learning(): + examples = restaurant[:3] + hypothesis = {'Alt': 'Yes'} + h = current_best_learning(examples, hypothesis) + values = [] + for e in examples: + values.append(guess_value(e, h)) + + assert values == [True, False, True] + + +restaurant = [ + {'Alt': 'Yes', 'Bar': 'No', 'Fri': 'No', 'Hun': 'Yes', 'Pat': 'Some', + 'Price': '$$$', 'Rain': 'No', 'Res': 'Yes', 'Type': 'French', 'Est': '0-10', + 'GOAL': True}, + + {'Alt': 'Yes', 'Bar': 'No', 'Fri': 'No', 'Hun': 'Yes', 'Pat': 'Full', + 'Price': '$', 'Rain': 'No', 'Res': 'No', 'Type': 'Thai', 'Est': '30-60', + 'GOAL': False}, + + {'Alt': 'No', 'Bar': 'Yes', 'Fri': 'No', 'Hun': 'No', 'Pat': 'Some', + 'Price': '$', 'Rain': 'No', 'Res': 'No', 'Type': 'Burger', 'Est': '0-10', + 'GOAL': True}, + + {'Alt': 'Yes', 'Bar': 'No', 'Fri': 'Yes', 'Hun': 'Yes', 'Pat': 'Full', + 'Price': '$', 'Rain': 'Yes', 'Res': 'No', 'Type': 'Thai', 'Est': '10-30', + 'GOAL': True}, + + {'Alt': 'Yes', 'Bar': 'No', 'Fri': 'Yes', 'Hun': 'No', 'Pat': 'Full', + 'Price': '$$$', 'Rain': 'No', 'Res': 'Yes', 'Type': 'French', 'Est': '>60', + 'GOAL': False}, + + {'Alt': 'No', 'Bar': 'Yes', 'Fri': 'No', 'Hun': 'Yes', 'Pat': 'Some', + 'Price': '$$', 'Rain': 'Yes', 'Res': 'Yes', 'Type': 'Italian', 'Est': '0-10', + 'GOAL': True}, + + {'Alt': 'No', 'Bar': 'Yes', 'Fri': 'No', 'Hun': 'No', 'Pat': 'None', + 'Price': '$', 'Rain': 'Yes', 'Res': 'No', 'Type': 'Burger', 'Est': '0-10', + 'GOAL': False}, + + {'Alt': 'No', 'Bar': 'No', 'Fri': 'No', 'Hun': 'Yes', 'Pat': 'Some', + 'Price': '$$', 'Rain': 'Yes', 'Res': 'Yes', 'Type': 'Thai', 'Est': '0-10', + 'GOAL': True}, + + {'Alt': 'No', 'Bar': 'Yes', 'Fri': 'Yes', 'Hun': 'No', 'Pat': 'Full', + 'Price': '$', 'Rain': 'Yes', 'Res': 'No', 'Type': 'Burger', 'Est': '>60', + 'GOAL': False}, + + {'Alt': 'Yes', 'Bar': 'Yes', 'Fri': 'Yes', 'Hun': 'Yes', 'Pat': 'Full', + 'Price': '$$$', 'Rain': 'No', 'Res': 'Yes', 'Type': 'Italian', 'Est': '10-30', + 'GOAL': False}, + + {'Alt': 'No', 'Bar': 'No', 'Fri': 'No', 'Hun': 'No', 'Pat': 'None', + 'Price': '$', 'Rain': 'No', 'Res': 'No', 'Type': 'Thai', 'Est': '0-10', + 'GOAL': False}, + + {'Alt': 'Yes', 'Bar': 'Yes', 'Fri': 'Yes', 'Hun': 'Yes', 'Pat': 'Full', + 'Price': '$', 'Rain': 'No', 'Res': 'No', 'Type': 'Burger', 'Est': '30-60', + 'GOAL': True} +] From 956a36b26fe207128b4058b1471e2233c2fbd6d7 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:38:57 +0300 Subject: [PATCH 3/9] add powerset to utils --- utils.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils.py b/utils.py index 3ba0b202b..0fece4ed4 100644 --- a/utils.py +++ b/utils.py @@ -8,6 +8,7 @@ import random import math import functools +from itertools import chain, combinations # ______________________________________________________________________________ # Functions on Sequences and Iterables @@ -66,6 +67,12 @@ def mode(data): return item +def powerset(iterable): + """powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)""" + s = list(iterable) + return list(chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))[1:] + + # ______________________________________________________________________________ # argmin and argmax From 8f06c0ef7c4049055545cde40ac95afd39f922ab Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:40:58 +0300 Subject: [PATCH 4/9] utils docstring fix --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 0fece4ed4..86617100c 100644 --- a/utils.py +++ b/utils.py @@ -68,7 +68,7 @@ def mode(data): def powerset(iterable): - """powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)""" + """powerset([1,2,3]) --> (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)""" s = list(iterable) return list(chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))[1:] From 58501dbe33ea5d49c9760691f3691ca3a57010ae Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:41:27 +0300 Subject: [PATCH 5/9] add test for powerset --- tests/test_utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3785b762b..47d65220a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -51,6 +51,10 @@ def test_mode(): assert mode("absndkwoajfkalwpdlsdlfllalsflfdslgflal") == 'l' +def test_powerset(): + assert powerset([1, 2, 3]) == [(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + def test_argminmax(): assert argmin([-2, 1], key=abs) == 1 assert argmax([-2, 1], key=abs) == -2 From 3f61041d5e5f1f7f38bfa1a3db2235186d4a6e50 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Fri, 21 Jul 2017 18:44:12 +0300 Subject: [PATCH 6/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b1d8650a..a48b0453d 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Here is a table of algorithms, the figure, name of the code in the book and in t | 18.11 | Decision-List-Learning | `DecisionListLearner` | [`learning.py`][learning] | | 18.24 | Back-Prop-Learning | `BackPropagationLearner` | [`learning.py`][learning] | | 18.34 | AdaBoost | `AdaBoost` | [`learning.py`][learning] | -| 19.2 | Current-Best-Learning | | +| 19.2 | Current-Best-Learning | `current_best_learning` | [`knowledge.py`](knowledge.py) | | 19.3 | Version-Space-Learning | | | 19.8 | Minimal-Consistent-Det | | | 19.12 | FOIL | | From 0114d189f1d775a771f32668bc008dbde5a1f214 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Sat, 22 Jul 2017 00:06:11 +0300 Subject: [PATCH 7/9] update learning.py docstring --- learning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learning.py b/learning.py index 11abaf420..7d5aef302 100644 --- a/learning.py +++ b/learning.py @@ -1,4 +1,4 @@ -"""Learn to estimate functions from examples. (Chapters 18-20)""" +"""Learn to estimate functions from examples. (Chapters 18, 20)""" from utils import ( removeall, unique, product, mode, argmax, argmax_random_tie, isclose, gaussian, From fc35f1a3eacb83a5c675a2420733c546d5b20449 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Sat, 22 Jul 2017 00:06:51 +0300 Subject: [PATCH 8/9] add header docstring to knowledge.py --- knowledge.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/knowledge.py b/knowledge.py index 58ca29332..b0d5cf0a2 100644 --- a/knowledge.py +++ b/knowledge.py @@ -1,3 +1,5 @@ +"""Knowledge in learning, Chapter 19""" + from random import shuffle from utils import powerset From 9bfdc8b1d0891d2baf4c93749f1a12c1b0166b72 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Sat, 22 Jul 2017 21:52:19 +0300 Subject: [PATCH 9/9] add smaller examples set --- tests/test_knowledge.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_knowledge.py b/tests/test_knowledge.py index 9d757be72..bec27ba3a 100644 --- a/tests/test_knowledge.py +++ b/tests/test_knowledge.py @@ -18,14 +18,33 @@ def test_current_best_learning(): examples = restaurant[:3] - hypothesis = {'Alt': 'Yes'} - h = current_best_learning(examples, hypothesis) + initial_h = {'Alt': 'Yes'} + h = current_best_learning(examples, initial_h) values = [] for e in examples: values.append(guess_value(e, h)) assert values == [True, False, True] + examples = animals_umbrellas + initial_h = {'Species': 'Cat'} + h = current_best_learning(examples, initial_h) + values = [] + for e in examples: + values.append(guess_value(e, h)) + + assert values == [True, True, True, False, False, False, True] + + +animals_umbrellas = [ + {'Species': 'Cat', 'Rain': 'Yes', 'Coat': 'No', 'GOAL': True}, + {'Species': 'Cat', 'Rain': 'Yes', 'Coat': 'Yes', 'GOAL': True}, + {'Species': 'Dog', 'Rain': 'Yes', 'Coat': 'Yes', 'GOAL': True}, + {'Species': 'Dog', 'Rain': 'Yes', 'Coat': 'No', 'GOAL': False}, + {'Species': 'Dog', 'Rain': 'No', 'Coat': 'No', 'GOAL': False}, + {'Species': 'Cat', 'Rain': 'No', 'Coat': 'No', 'GOAL': False}, + {'Species': 'Cat', 'Rain': 'No', 'Coat': 'Yes', 'GOAL': True} +] restaurant = [ {'Alt': 'Yes', 'Bar': 'No', 'Fri': 'No', 'Hun': 'Yes', 'Pat': 'Some', pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy