Planning which question to ask#
The robot sees three objects (red cup, blue cup, red bowl) and hears “bring me that one”. It can ask yes/no questions (“is it red?”, “is it a cup?”, “is it on the left?”) before acting. Which question should it ask first, and when should it stop? The question planner computes the value of information of each question: the expected reduction in the cost of the final decision, minus the cost of asking.
The twist: people’s answers depend on what was asked before. A projective answer model captures this; an independent model (the classical default) ignores it. Both are compared on simulated people.
import numpy as np
import matplotlib.pyplot as plt
from quantum_mind.viz import use_mpl_style
from quantum_mind.envs.hri import default_domain
from quantum_mind.applications.questioning import QuestionPlanner
theme = use_mpl_style('dark')
person = default_domain() # a simulated person whose answers follow a projective model
print('hypotheses:', person.hypotheses, ' questions:', person.questions)
hypotheses: ['red cup', 'blue cup', 'red bowl'] questions: ['red?', 'cup?', 'left?']
1. How informative is each question?#
planner = QuestionPlanner(person, ask_cost=0.3, error_cost=5.0)
voi = {q: planner.value_of_information(q, []) for q in person.questions}
gain = {q: planner.information_gain(q, []) for q in person.questions}
for q in person.questions:
print('%-6s value of information %.3f information gain %.3f bits' % (q, voi[q], gain[q]))
print('decision now:', planner.decide([]))
red? value of information 0.476 information gain 0.123 bits
cup? value of information 0.781 information gain 0.169 bits
left? value of information 1.408 information gain 0.448 bits
decision now: ('ask', 'left?', 1.4076866816945368)
fig, ax = plt.subplots(figsize=(6.0, 2.8))
x = np.arange(len(voi))
ax.bar(x - 0.18, list(voi.values()), 0.36, label='value of information', color=theme['palette'][1])
ax.bar(x + 0.18, list(gain.values()), 0.36, label='information gain (bits)', color=theme['palette'][2])
ax.set_xticks(x, list(voi)); ax.set_title('Which question first?'); ax.legend()
plt.show()
2. Order dependence#
The probability of a “yes” to one question depends on the answer to an earlier one, and on the order. The independent model cannot represent this.
blind = person.order_free()
for h in person.hypotheses:
a = person.likelihood(h, [('red?', 1), ('cup?', 1)])
b = person.likelihood(h, [('cup?', 1), ('red?', 1)])
print('%-9s P(yes, yes): red first %.3f, cup first %.3f (independent model: %.3f both)'
% (h, a, b, blind.likelihood(h, [('red?', 1), ('cup?', 1)])))
red cup P(yes, yes): red first 0.097, cup first 0.019 (independent model: 0.017 both)
blue cup P(yes, yes): red first 0.010, cup first 0.174 (independent model: 0.015 both)
red bowl P(yes, yes): red first 0.003, cup first 0.162 (independent model: 0.004 both)
3. Does modelling the order help?#
Run both planners against the same simulated people, many times, for each target object.
rng = np.random.default_rng(0)
aware, naive = QuestionPlanner(person, ask_cost=0.3, error_cost=5.0), QuestionPlanner(blind, ask_cost=0.3, error_cost=5.0)
res = {'order-aware': [], 'independent': []}
for i in range(300):
target = person.hypotheses[i % 3]
for name, pl in (('order-aware', aware), ('independent', naive)):
out = pl.simulate(person, target, np.random.default_rng(i))
res[name].append((out['total_cost'], out['correct'], len(out['history'])))
for name, r in res.items():
r = np.array(r, float)
print('%-12s mean cost %.3f accuracy %.3f questions asked %.2f' % (name, r[:, 0].mean(), r[:, 1].mean(), r[:, 2].mean()))
order-aware mean cost 2.217 accuracy 0.617 questions asked 1.00
independent mean cost 2.390 accuracy 0.603 questions asked 1.36
fig, ax = plt.subplots(figsize=(6.0, 2.8))
for (name, r), c in zip(res.items(), theme['palette']):
ax.hist(np.array(r)[:, 0], bins=np.arange(0, 6, 0.3), alpha=0.6, color=c, label=name)
ax.set_xlabel('total cost of the episode'); ax.set_ylabel('episodes'); ax.legend()
ax.set_title('Episode costs on simulated people')
plt.show()
The statements below are checked by this cell every time the documentation is built:
mean_cost = {k: np.array(v, float)[:, 0].mean() for k, v in res.items()}
assert mean_cost['order-aware'] <= mean_cost['independent']
print('checked')
checked
Result. In this domain the order-aware planner has a lower average cost than the planner that treats answers as independent; the printed numbers give the size of the gain. The gain is not universal: across randomly drawn domains it depends on how strongly the questions interfere, and in some domains it is close to zero.
References: Howard (1966); Wang & Busemeyer (2013), Topics in Cognitive Science 5, 689-710.