Expert systems — the machine that reasoned
Fifteen if-then rules that chain into conclusions, work out which questions are worth asking, and can print exactly why they decided. That last part is the one everything after this lesson loses.
Search you can afford ended on a machine that explores. This lesson is about the other half of classical AI. It does not explore anything. It deduces.
Search felt impressive. This felt like talking to an expert.
Fifteen rules, and some of them use the answers of others
Here is the whole system. Fifteen sentences of the form if all of these, then that. That is what an expert says when you ask how they know something.
IF hair THEN mammal
IF gives milk THEN mammal
IF feathers THEN bird
IF lays eggs AND flies THEN bird
IF mammal AND eats meat THEN carnivore
IF mammal AND pointed teeth AND claws THEN carnivore
IF mammal AND hooves THEN ungulate
IF carnivore AND tawny AND dark spots THEN cheetah
IF carnivore AND tawny AND black stripes THEN tiger
IF ungulate AND black stripes THEN zebra
IF bird AND cannot fly AND swims THEN penguinThat is the difference between this and a lookup table. Nothing about an animal is visibly mammal. You observe hair. The system concludes mammal, and then uses its own conclusion as an input.
Four observations become a cheetah through a chain three steps deep
Tell it four things you can see and let it run.
Given hair, eats meat, tawny and dark spots, the engine fires 3 rules across 2 passes and concludes cheetah, through a chain 3 steps deep.
Nobody wrote a cheetah rule that mentions hair.
It asks 5.6 questions instead of 18
Now run it the other way. Give it a goal and let it work backwards. What would I need to know to prove this? Then ask only those things.
There are 18 different questions this system could ask you.
An average of 5.6 questions instead of 18. That is 69%% never asked, and every animal identified correctly. A cheetah takes 4.
You have seen this idea already. Search you can afford spent a lesson on not looking everywhere. Do not ask everything. Ask the thing that could settle it.
Ask it how it knows and it prints the chain
It does not produce a confidence score or a heat map.
cheetah
carnivore
mammal
hair <- you told me
eats meat <- you told me
tawny <- you told me
dark spots <- you told meEverything after this lesson loses that. A model later in this course will predict better than this system ever could. It will be unable to tell you why, beyond which inputs mattered most.
Systems built exactly this way ran real institutions
| System | What it did | How it ended |
|---|---|---|
| MYCIN | Diagnosed blood infections and recommended antibiotics, asking questions as it went. In a 1979 Stanford evaluation it prescribed correctly in 80% of test cases, against human specialists who scored between 42% and 65%. | Never used on a patient. The obstacles were not technical. Liability, and who is responsible when a program prescribes. |
| XCON | Configured VAX computer orders at Digital Equipment Corporation, checking that thousands of parts would work together. It launched on about 500 rules, passed 10,000 by the late 1980s, and saved DEC an estimated $40 million a year. | Drowned. Every new product meant new rules, the rules interacted, and eventually a team existed only to keep them consistent. |
MYCIN
- What it did
- Diagnosed blood infections and recommended antibiotics, asking questions as it went. In a 1979 Stanford evaluation it prescribed correctly in 80% of test cases, against human specialists who scored between 42% and 65%.
- How it ended
- Never used on a patient. The obstacles were not technical. Liability, and who is responsible when a program prescribes.
XCON
- What it did
- Configured VAX computer orders at Digital Equipment Corporation, checking that thousands of parts would work together. It launched on about 500 rules, passed 10,000 by the late 1980s, and saved DEC an estimated $40 million a year.
- How it ended
- Drowned. Every new product meant new rules, the rules interacted, and eventually a team existed only to keep them consistent.
XCON did not get slower or less accurate. It got unmaintainable. The knowledge kept changing, and every change had to be found, written and reconciled with ten thousand others.
Both machines were built by people writing down what they knew
Classical AI ends with two impressive machines. One explores futures faster than any human. One reasons from expertise and shows its working.
The next two lessons are about what happened when writing it down stopped working. First because the knowledge outgrew anybody's ability to keep writing it. Then because of something worse.
content/notebooks/introduction-to-ai/the-machine-that-reasoned.ipynb
Add an animal and the rules to identify it, or remove a rule and see what stops working. Worth trying: ask it to identify something the rules cannot reach and read what it says.
Show the code4 cells
RULES = [
(['hair'], 'mammal'),
(['gives milk'], 'mammal'),
(['feathers'], 'bird'),
(['lays eggs', 'flies'], 'bird'),
(['mammal', 'eats meat'], 'carnivore'),
(['mammal', 'pointed teeth', 'claws'], 'carnivore'),
(['mammal', 'hooves'], 'ungulate'),
(['mammal', 'chews cud'], 'ungulate'),
(['carnivore', 'tawny', 'dark spots'], 'cheetah'),
(['carnivore', 'tawny', 'black stripes'], 'tiger'),
(['ungulate', 'long neck', 'long legs', 'dark spots'], 'giraffe'),
(['ungulate', 'black stripes'], 'zebra'),
(['bird', 'cannot fly', 'long neck', 'long legs'], 'ostrich'),
(['bird', 'cannot fly', 'swims'], 'penguin'),
(['bird', 'good flyer'], 'albatross'),
]
ANIMALS = {'cheetah','tiger','giraffe','zebra','ostrich','penguin','albatross'}
DERIVED = {c for _, c in RULES}
ASKABLE = sorted({f for conds, _ in RULES for f in conds} - DERIVED)
record('n_rules', len(RULES))
record('n_animals', len(ANIMALS))
record('n_askable', len(ASKABLE))def forward(facts):
known = set(facts); why = {}; fired = 0; rounds = 0
changed = True
while changed:
changed = False; rounds += 1
for conds, concl in RULES:
if concl not in known and all(c in known for c in conds):
known.add(concl); why[concl] = conds; fired += 1; changed = True
return known, why, fired, rounds
obs = ['hair', 'eats meat', 'tawny', 'dark spots']
known, why, fired, rounds = forward(obs)
record('demo_observations', ', '.join(obs))
record('demo_conclusion', next(k for k in known if k in ANIMALS))
record('demo_fired', fired)
record('demo_rounds', rounds)
def explain(fact, why, depth=0):
lines = [' ' * depth + fact]
for c in why.get(fact, []):
lines += explain(c, why, depth + 1)
return lines
chain = explain('cheetah', why)
record('demo_chain_depth', max(l.count(' ') for l in chain))
print('\n'.join(chain))def backward(goal, oracle, asked):
if goal in DERIVED:
for conds, concl in RULES:
if concl != goal: continue
if all(backward(c, oracle, asked) for c in conds):
return True
return False
if goal not in asked:
asked[goal] = oracle(goal)
return asked[goal]
TRUTH = {
'cheetah': ['hair','eats meat','tawny','dark spots'],
'tiger': ['hair','eats meat','tawny','black stripes'],
'giraffe': ['hair','hooves','long neck','long legs','dark spots'],
'zebra': ['hair','hooves','black stripes'],
'ostrich': ['feathers','cannot fly','long neck','long legs'],
'penguin': ['feathers','cannot fly','swims'],
'albatross': ['feathers','good flyer'],
}
counts = {}
for animal, truth in TRUTH.items():
asked = {}
for candidate in ['cheetah','tiger','giraffe','zebra','ostrich','penguin','albatross']:
if backward(candidate, lambda f: f in truth, asked):
break
counts[animal] = (candidate, len(asked))
avg = sum(n for _, n in counts.values()) / len(counts)
record('avg_questions', f'{avg:.1f}')
record('questions_cheetah', counts['cheetah'][1])
record('questions_albatross', counts['albatross'][1])
record('all_correct', 'yes' if all(a == g for g, (a, _) in counts.items()) else 'NO')
record('ask_everything', len(ASKABLE))
record('question_saving', f'{100*(1-avg/len(ASKABLE)):.0f}%')
def plot(ax):
names = list(counts)
vals = [counts[n][1] for n in names]
ax.barh(names, vals)
for i, v in enumerate(vals):
ax.text(v + .25, i, str(v), va='center', fontsize=10)
ax.axvline(len(ASKABLE), ls='--', lw=1.2)
ax.text(len(ASKABLE) - .4, len(names) - 0.6,
f'asking everything: {len(ASKABLE)}', ha='right', fontsize=9)
ax.set_xlim(0, len(ASKABLE) + 1.5)
ax.set_xlabel('questions the system actually asked')
save_fig('questions-asked', plot, figsize=(7, 3.6))depths = {}
for animal, truth in TRUTH.items():
_, w, _, _ = forward(truth)
depths[animal] = max(l.count(' ') for l in explain(animal, w))
record('max_depth', max(depths.values()))
record('min_depth', min(depths.values()))
def plot(ax):
names = list(depths)
ax.bar(names, [depths[n] for n in names])
for i, n in enumerate(names):
ax.text(i, depths[n] + .06, str(depths[n]), ha='center', fontsize=10)
ax.set_ylabel('steps of reasoning')
ax.set_ylim(0, max(depths.values()) + .7)
ax.tick_params(axis='x', rotation=30)
save_fig('chain-depth', plot, figsize=(7, 3.4))Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
