By Qiyan, Mackenzie, Caitlyn
Why do we often choose to speak indirectly, especially when being direct would be more informative? If your friend baked a cake for you to try and you didn’t think it was very good, you might say, “It wasn’t terrible,” instead of the more honest “It was bad.” This kind of polite language is puzzling from the perspective of classical models of communication where the goal is to convey information efficiently and truthfully. Indirect or vague utterances seem suboptimal, yet we use them all the time.
Politeness is one of the clearest examples of how human communication goes beyond informativity. Speakers often balance competing goals: to tell the truth, to be kind, and to manage how they are perceived. The key question is: How do speakers decide what to say when these goals are in tension?
To address this, Yoon et al. (2017) and Yoon et al. (2018) extend the RSA framework to account for polite indirect speech. As we know, in the RSA framework, speakers and listeners are modeled as rational agents who recursively reason about each other. Speakers choose utterances based on how they expect listeners to interpret them, and listeners interpret utterances by inferring the speaker’s goals and beliefs.
If you recall, the early versions of the model proposed that polite speech arises from a tradeoff between two utilities:
This version captures phenomena like white lies, where speakers sacrifice some truth to be kind. But to fully explain indirectness a third dimension is needed. That’s where the self-presentational utility comes in. In the extended model, speakers don’t just want to be kind and informative, they want to appear that way. This higher-order goal accounts for situations where being fully honest would be rude, and being overly nice would feel dishonest. An utterance like “It wasn’t terrible” communicates that the speaker is trying to balance these goals, and importantly, that they want the listener to see them as someone who is trying to balance these goals.
Similar to the previous politeness model study we discussed in class, Yoon et al. (2018) constructs a scenario where the speaker gives feedback (0, 1, 2, or 3 stars) to the listener’s creative work (e.g. a poem). In the model, there are 4 states of the world, corresponding to the star rating, where 3 stars is the best and 0 stars is the worst.
A new addition to this model is the self-presentational utility – a way that speakers signal to the listeners their conversation goals. In other words, how do the speakers want to present their epistemic/informational and social goals, even if those are not their real intentions? This new utility allows the speaker to signal to the listener that they care about both epistemic and social goals though that’s not possible in reality.
Prioritizing the presentational utility leads speakers to use indirect speech, realized as negated adjectival phrases in this model. There are 8 possible utterances: 4 without negation (“terrible”, “bad”, “good”, “amazing”) and 4 with negation (“not terrible”, “not bad”, “not good”, “not amazing”). The negated utterances are more difficult to comprehend than the unnegated utterances, so they have a higher cost of 0.35 while the unnegated utterances have a cost of 0.
Yoon et al. (2018) obtained literal semantics of the utterances through an experiment probing participant judgments. They were presented with a state (0, 1, 2, 3) and an utterance (“Do you think Ann thought the presentation was [1 of 8 utterances]?”) and responded with “yes” or “no”.
Literal listener (L0) is the same as the vanilla RSA model. They only interpret the literal semantics of the utterances according to the meaning function. Running one of the new utterances “not bad” results in a probability distribution of states identical to “not bad” values in the literal semantics.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
///
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
viz(listener0("not_terrible"))
Pragmatic speaker 1 (S1) is the same as the previous politeness model we covered in class. The speaker considers their informational (being truthful) and social (being kind) goals, and φ determines which goal they prioritize. Running a speaker who wants to convey state 1 and 0.5 φ returns the utterances with the highest probabilities on “yes terrible” (satisfying the informational goal) and “not terrible” and “not bad” (satisfying the social goal). The reason why an utterance like “yes amazing” doesn’t have a high probability is because though it satisfies the social goal, it’s very unlikely to also be true for state 1.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
///
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
//return utterance
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
}, 10000);
viz(speaker1(1, 0.5))
Pragmatic listener 1 (L1) is also the same as the previous model. The listener takes in an utterance and returns a joint probability distribution over the most likely state and φ values. Running the listener on the utterance “not terrible” shows L1 believes that the speaker has a lower φ value (leaning social) and that the state is likely 1 or 0. L1 reasons that if the speaker is informational and the state is 2 or 3, then they could’ve used “yes amazing” or “yes good”, which also have less cost. Since they didn’t use those utterances, the speaker must be social, and the state is likely 0 or 1.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
return utterance
}})
}, 10000);
///
var listener1 = cache(function(utterance) {
Infer({model: function(){
var phi = categorical({vs: weightBins, ps: phiWeights})
var state = uniformDraw(states);
var S1 = speaker1(state, phi);
observe(S1, utterance)
return {
state: state,
phi: phi
}
}})
}, 10000);
var listenerPosterior = listener1("not_terrible")
display("expected state = " +
expectation(marginalize(listenerPosterior, "state")))
viz(marginalize(listenerPosterior, "state"))
display("expected phi = " +
expectation(marginalize(listenerPosterior, "phi")))
viz.density(marginalize(listenerPosterior, "phi"))
This is where the new parameter ω (omega) comes in. Unlike φ, which controls how much the speaker cares about being honest versus being nice, ω is a vector that contains the weights assigned to three utilities: epistemic (truth), social (kindness), and presentational (how S2 wants to be seen), showing how much the speaker values each when choosing what to say.
u is the utterance (what the speaker says); s is the state of the world (how good or bad things really are); φ is the goals (social vs informational) that the speaker wants the listener to believe, regardless of the speaker’s true beliefs; ω are the weights attributed to the three utilities.
ω_epistemic ⋅ U_epistemic(u; s) This part is how much the speaker wants to be seen as truthful (ω_epistemic) multiplied by how truthful the utterance actually is (U_epistemic)
ω_social ⋅ U_social(u) This is how much the speaker wants to be seen as kind (ω_social) multiplied by how kind the utterance actually is (U_social)
ω_presentational ⋅ U_presentational(u) This is how much the speaker cares about managing their image (ω_presentational) multiplied by how well the utterance helps them look like they balance both truth and kindness (U_presentational)
Here is what ω would look like in the model.
var speakerUtility = omega.epistemic * utilities.epistemic +
omega.social * utilities.social +
omega.presentational * utilities.presentational - cost(utterance)
In this updated model, function speaker2 takes in a state (0, 1, 2, 3), a φ value (0-1), and a vector which contains weights (ω) for the utilities.
var speaker2 = function(state, phi, weights){
display("state = " + state)
display("phi = " + phi)
display("social weight = " + weights.soc + ", presentational weight = " + weights.pres
+ ", informational weight = " + weights.inf)
}
//calling the function
speaker2(0, 0.5, {soc: 0.05, pres: 0.60, inf: 0.35})
Utilities calculations are based on how the listener interprets the utterance: S2 runs L1 and marginalizes over state (listener’s beliefs about the true state) and φ (listener’s beliefs about the speaker’s goals).
var utterance = uniformDraw(utterances);
var L1 = listener1(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
Calculating the informational and social utilities uses state marginals, and calculating the presentational utility uses φ marginals. Then, each utility is multiplied by its corresponding weight – determined by the input vector (ω). The total utility is then the sum of these weighted values minus the cost of the utterance – negated utterances are more costly. This total helps the speaker decide which utterance best suits their goals.
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state), // E [s]
pres: L1_phi.score(phi) // // log P(phi | u)
}
var totalUtility = weights.soc * utilities.soc +
weights.pres * utilities.pres +
weights.inf * utilities.inf - cost(utterance);
S2 returns a probability distribution of utterances which can best convey this information to the listener. This code splits the utterances into two parts at the underscore (“_”). It then returns an object where the first part is labeled as the “utterance particle” and the second part as the main “utterance.” This helps with visualizing the results by sorting the utterances into negated and not negated groups.
var utterance_split = function (utterance){
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}
utterance_split("not_bad")
Here’s S2 all together.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
return utterance;
}})
}, 10000);
var listener1 = cache(function(utterance) {
Infer({model: function(){
var phi = categorical({vs: weightBins, ps: phiWeights})
var state = uniformDraw(states);
var S1 = speaker1(state, phi);
observe(S1, utterance)
return {
state: state,
phi: phi
}
}})
}, 10000);
///
var speaker2 = function(state, phi, weights) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L1 = listener1(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state), // E [s]
pres: L1_phi.score(phi) // // log P(phi | u)
}
var totalUtility = weights.soc * utilities.soc +
weights.pres * utilities.pres +
weights.inf * utilities.inf - cost(utterance);
factor(speakerOptimality2 * totalUtility)
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
}
To see how ω works, let’s compare two equally balanced speakers. Here we have two speakers who care about being truthful and kind equally. The only difference between them is Speaker A does not care about presenting as polite while Speaker B wants to present as kind, honest, AND polite. Notice how the probability of negations increases as the desire to appear polite increases. This is because negation tends to be more indirect and often more polite.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
return utterance;
}})
}, 10000);
var listener1 = cache(function(utterance) {
Infer({model: function(){
var phi = categorical({vs: weightBins, ps: phiWeights})
var state = uniformDraw(states);
var S1 = speaker1(state, phi);
observe(S1, utterance)
return {
state: state,
phi: phi
}
}})
}, 10000);
var speaker2 = function(state, phi, weights) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L1 = listener1(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state), // E [s]
pres: L1_phi.score(phi) // // log P(phi | u)
}
var totalUtility = weights.soc * utilities.soc +
weights.pres * utilities.pres +
weights.inf * utilities.inf - cost(utterance);
factor(speakerOptimality2 * totalUtility)
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
}
///
// Comparing presentational weights
display("What happens as a speaker’s desire to save face increases?")
display("Speaker A:")
viz(speaker2(1, 0.50, {soc: 0.5, pres: 0.0, inf: 0.5}))
display("Speaker B:")
viz(speaker2(1, 0.50, {soc: 0.5, pres: 1, inf: 0.5}))
Imagine you are the baker now and you bring a cake to a social gathering. Also at the gathering is your neighbor who has a tendency to be condescending. They decide the cake is not very good and now have to decide how to communicate it to you. Their goal is to communicate the truth without regard to social etiquette, but they don’t want the other people to think they are being rude. We can model this “two-faced” approach below with a speaker who has a high informational value, low social value, but high presentational value. When describing your cake, they are most likely to say “it’s not terrible” in order to protect their image.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
return utterance;
}})
}, 10000);
var listener1 = cache(function(utterance) {
Infer({model: function(){
var phi = categorical({vs: weightBins, ps: phiWeights})
var state = uniformDraw(states);
var S1 = speaker1(state, phi);
observe(S1, utterance)
return {
state: state,
phi: phi
}
}})
}, 10000);
var speaker2 = function(state, phi, weights) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L1 = listener1(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state), // E [s]
pres: L1_phi.score(phi) // // log P(phi | u)
}
var totalUtility = weights.soc * utilities.soc +
weights.pres * utilities.pres +
weights.inf * utilities.inf - cost(utterance);
factor(speakerOptimality2 * totalUtility)
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
}
///
// Condescending - speaker wants to convey only truth but appear to be social
display("Speaker wants Listener to view them as nice while they are actually rude")
viz(speaker2(1, 0.40, {soc: 0.03, pres:.8, inf: 0.6}))
Next, we will look at how a friend might respond to the same situation. They taste your cake and rate it 1 star. When you ask what they think, they want to be as balanced and informative as possible while also not wanting to hurt your feelings. This would give them a higher social value while prioritizing presentational because they want you to think they are balanced and not being nice because they are your friend. You also know that because they are your friend, they are probably going to slightly favor social and so we set φ to 0.35. In this model your friend is most likely going to tell you “it’s not bad” or “it’s not terrible” because it communicates a truthful yet less hurtful opinion.
///fold:
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var states = [0,1,2,3];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
// in Yoon, Tessler, et al. (2020), the speaker optimality parameters were set to be the same value
var speakerOptimality = 4;
var speakerOptimality2 = 4;
var round = function(x){
return Math.round(x * 100) / 100
}
var weightBins = map(round, _.range(0,1, 0.05))
var phiWeights = repeat(weightBins.length, function(){1})
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
// var uttCosts = map(function(u) {
// return isNegation(u) ? Math.exp(-cost_neg) : Math.exp(-cost_yes)
// }, utterances)
//
// var utterancePrior = Infer({model: function(){
// return utterances[discrete(uttCosts)]
// }});
// Parameter values = Maximum A-Posteriori values from Yoon, Tessler et al., (2018)
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
var listener0 = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states);
var m = meaning(utterance, state);
condition(m);
return state;
}})
}, 10000);
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0 = listener0(utterance);
var utilities = {
inf: L0.score(state), // log P(s | u)
soc: expectation(L0) // E[s]
}
var speakerUtility = phi * utilities.inf +
(1-phi) * utilities.soc - cost(utterance);
factor(speakerOptimality * speakerUtility);
return utterance;
}})
}, 10000);
var listener1 = cache(function(utterance) {
Infer({model: function(){
var phi = categorical({vs: weightBins, ps: phiWeights})
var state = uniformDraw(states);
var S1 = speaker1(state, phi);
observe(S1, utterance)
return {
state: state,
phi: phi
}
}})
}, 10000);
var speaker2 = function(state, phi, weights) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L1 = listener1(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state), // E [s]
pres: L1_phi.score(phi) // // log P(phi | u)
}
var totalUtility = weights.soc * utilities.soc +
weights.pres * utilities.pres +
weights.inf * utilities.inf - cost(utterance);
factor(speakerOptimality2 * totalUtility)
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
}
///
// Friend - Your friend wants to be as balanced as possible, but internally does not want to hurt your feelings
display("Speaker wants to appear as balanced as possible but not hurt a friends feelings")
viz(speaker2(1, 0.45, {soc: 0.6, pres: 0.8, inf: 0.5}))
Can the effects of the presentational utility be achieved by adding negated utterances and layering another speaker model on top of the simpler politeness model? In this example, the same utterances are added to the simpler politeness model. However, instead of accounting for the presentational utility, S2, like S1, only considers the informational and social utilities.
This model has drastically different results. Though negated utterances are a possibility, S2 is unlikely to use them in any situation. Because S2 no longer has the goal to present a φ value to the listener, they have no incentive to use the negated utterances. Running a more informational speaker on state 1 shows that though an utterance like “not amazing” can also convey the state, “yes bad” is a better choice because the listener is more likely to infer the correct state and φ values at no utterance cost.
///fold:
var states = [0,1,2,3]
var utterances = [
"yes_terrible","yes_bad","yes_good","yes_amazing",
"not_terrible","not_bad","not_good","not_amazing"
];
var isNegation = function(utt){
return (utt.split("_")[0] == "not")
};
var cost_yes = 0;
var cost_neg = 0.35;
var cost = function(utterance){
return isNegation(utterance) ? cost_neg : cost_yes
}
var round = function(x){
return Math.round(x * 100) / 100
}
var literalSemantics = {
"state": [0, 1, 2, 3],
"not_amazing": [0.9652,0.9857,0.7873,0.0018],
"not_bad": [0.0967,0.365,0.7597,0.9174],
"not_good": [0.9909,0.736,0.2552,0.2228],
"not_terrible": [0.2749,0.5285,0.728,0.9203],
"yes_amazing": [4e-04,2e-04,0.1048,0.9788 ],
"yes_bad": [0.9999,0.8777,0.1759,0.005],
"yes_good": [0.0145,0.1126,0.9893,0.9999],
"yes_terrible": [0.9999,0.3142,0.0708,0.0198]
};
// determine whether the utterance describes the state
// by flipping a coin with the literalSemantics weight
// ... state - 1 because of 0-indexing
var meaning = function(words, state){
return flip(literalSemantics[words][state]);
};
// value function scales social utility by a parameter lambda
var lambda = 1.25 // value taken from MAP estimate from Yoon, Tessler, et al. 2016
var valueFunction = function(s){
return lambda * s
}
// literal listener
var literalListener = cache(function(utterance) {
Infer({model: function(){
var state = uniformDraw(states)
var m = meaning(utterance, state)
condition(m)
return state
}})
})
var alpha = 4; // same as the optimality parameter in the model with presentational utility
var speaker1 = cache(function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L0_posterior = literalListener(utterance);
var utility = {
epistemic: L0_posterior.score(state),
social: expectation(L0_posterior, valueFunction)
}
var speakerUtility = phi * utility.epistemic +
(1 - phi) * utility.social - cost(utterance)
factor(alpha * speakerUtility)
return utterance
}})
})
var pragmaticListener = function(utterance) {
Infer({model: function(){
var state = uniformDraw(states)
var phi = uniformDraw(map(round, _.range(0,1, 0.05)))
var S1 = speaker1(state, phi)
observe(S1, utterance)
return { state, phi }
}})
}
var speaker2 = function(state, phi) {
Infer({model: function(){
var utterance = uniformDraw(utterances);
var L1 = pragmaticListener(utterance);
var L1_state = marginalize(L1, "state");
var L1_phi = marginalize(L1, "phi");
var utilities = {
inf: L1_state.score(state), // log P(s | u)
soc: expectation(L1_state, valueFunction), // E [s]
// pres: L1_phi.score(phi) // // log P(phi | u)
}
// var totalUtility = weights.soc * utilities.soc +
// weights.pres * utilities.pres +
// weights.inf * utilities.inf - cost(utterance);
var totalUtility = phi * utilities.inf +
(1 - phi) * utilities.soc - cost(utterance)
factor(alpha * totalUtility)
var utt = utterance.split("_")
return {
"utterance particle": utt[0], utterance: utt[1]
}
}})
};
///
var listenerPosterior = pragmaticListener("not_amazing")
display("expected state = " +
expectation(marginalize(listenerPosterior, "state")))
viz(marginalize(listenerPosterior, "state"))
display("expected phi = " +
expectation(marginalize(listenerPosterior, "phi")))
viz.density(marginalize(listenerPosterior, "phi"))
var listenerPosterior = pragmaticListener("yes_bad")
display("expected state = " +
expectation(marginalize(listenerPosterior, "state")))
viz(marginalize(listenerPosterior, "state"))
display("expected phi = " +
expectation(marginalize(listenerPosterior, "phi")))
viz.density(marginalize(listenerPosterior, "phi"))
speaker2(1, 0.8)