Preliminary end to bayes animations

This commit is contained in:
Grant Sanderson
2017-06-09 16:10:34 -07:00
parent 5ceb0bd2a3
commit e9032a88df
3 changed files with 562 additions and 30 deletions

View File

@ -1091,10 +1091,59 @@ class UpdatePokerPrior(SampleSpaceScene):
new_label_kwargs = {"labels" : [label]},
)
class BayesRuleInMemory(Scene):
def construct(self):
randy = Randolph()
randy.to_corner(DOWN+LEFT)
bubble = ThoughtBubble(height = 4)
bubble.pin_to(randy)
B = "\\text{Belief}"
D = "\\text{Data}"
rule = TexMobject(
"P(", B, "|", D, ")", "=",
"P(", "B", ")",
"{P(", D, "|", B, ")", "\\over", "P(", D, ")}"
)
rule.highlight_by_tex(B, RED)
rule.highlight_by_tex(D, GREEN)
rule.next_to(randy, RIGHT, LARGE_BUFF, UP)
rule.generate_target()
bubble.add_content(rule.target)
screen_rect = ScreenRectangle()
screen_rect.next_to(randy, UP+RIGHT)
self.add(randy)
self.play(
LaggedStart(FadeIn, rule),
randy.change, "erm", rule
)
self.play(Blink(randy))
self.play(
ShowCreation(bubble),
MoveToTarget(rule),
randy.change, "pondering",
)
self.dither()
self.play(rule.fade, 0.7, run_time = 2)
self.play(randy.change, "confused", rule)
self.play(Blink(randy))
self.dither(2)
self.play(
FadeOut(VGroup(bubble, rule)),
randy.change, "pondering", screen_rect,
)
self.play(
randy.look_at, screen_rect.get_right(),
ShowCreation(screen_rect),
)
self.dither(4)
class NextVideoWrapper(TeacherStudentsScene):
CONFIG = {
"title" : "Next chapter: Bayesian networks"
}
def construct(self):
title = TextMobject("Next video: Bayesian networks")
title = TextMobject(self.title)
title.scale(0.8)
title.to_edge(UP, buff = SMALL_BUFF)
screen = ScreenRectangle(height = 4)
@ -1108,7 +1157,10 @@ class NextVideoWrapper(TeacherStudentsScene):
self.teacher.change, "raise_right_hand"
)
self.play(ShowCreation(screen))
self.change_student_modes(*["pondering"]*3)
self.change_student_modes(
*["pondering"]*3,
look_at_arg = screen
)
self.play(Animation(screen))
self.dither(5)
@ -1415,21 +1467,24 @@ class MoreExamples(TeacherStudentsScene):
class MusicExample(SampleSpaceScene, PiCreatureScene):
def construct(self):
self.force_skipping()
self.introduce_musician()
self.add_prior()
self.record_track()
self.add_bottom_conditionl()
self.friend_gives_compliment()
self.friends_dont_like()
self.false_compliment()
self.add_top_conditionl()
self.get_positive_review()
self.restrict_space()
self.show_posterior_rectangles()
self.show_prior_rectangle_areas()
self.show_posterior_probability()
self.intuition_of_positive_feedback()
self.make_friends_honest()
self.fade_out_post_rect()
self.get_negative_feedback()
self.show_negative_feedback_posterior()
self.compare_prior_to_post_given_negative()
self.intuition_of_negative_feedback()
def introduce_musician(self):
@ -1438,7 +1493,7 @@ class MusicExample(SampleSpaceScene, PiCreatureScene):
randy.arms = randy.get_arm_copies()
guitar = randy.guitar = Guitar()
guitar.move_to(randy)
guitar.shift(0.35*RIGHT + 0.6*UP)
guitar.shift(0.31*RIGHT + 0.6*UP)
randy.change_mode("plain")
self.play(
@ -1456,58 +1511,444 @@ class MusicExample(SampleSpaceScene, PiCreatureScene):
self.change_pi_creature_with_guitar("concerned_musician")
self.dither(2)
self.play(
randy.scale, 0.5,
randy.scale, 0.7,
randy.to_corner, UP+LEFT,
)
self.play_notes(guitar)
def add_prior(self):
pass
sample_space = SampleSpace()
sample_space.shift(DOWN)
sample_space.divide_horizontally(0.8, colors = [MAROON_D, BLUE_E])
labels = VGroup(
TexMobject("P(S) = ", "0.8"),
TexMobject("P(\\text{not } S) = ", "0.2"),
)
labels.scale(0.7)
braces, labels = sample_space.get_side_braces_and_labels(labels)
VGroup(sample_space, braces, labels).to_edge(LEFT)
words = map(TextMobject, [
"Blunt honesty", "Some confidence"
])
for word, part in zip(words, sample_space.horizontal_parts):
word.scale(0.6)
word.move_to(part)
self.play(LaggedStart(FadeIn, sample_space, run_time = 1))
self.play(*map(GrowFromCenter, braces))
for label in labels:
self.play(Write(label, run_time = 2))
self.dither()
for word, mode in zip(words, ["maybe", "soulful_musician"]):
self.play(LaggedStart(FadeIn, word, run_time = 1))
self.change_pi_creature_with_guitar(mode)
self.dither()
self.dither()
self.play(*map(FadeOut, words))
self.sample_space = sample_space
def record_track(self):
pass
randy = self.pi_creature
friends = VGroup(*[
PiCreature(mode = "happy", color = color).flip()
for color in BLUE_B, GREY_BROWN, MAROON_E
])
friends.scale(0.6)
friends.arrange_submobjects(RIGHT)
friends.next_to(randy, RIGHT, LARGE_BUFF, DOWN)
friends.to_edge(RIGHT)
for friend in friends:
friend.look_at(randy.eyes)
headphones = VGroup(*map(Headphones, friends))
self.play(FadeIn(friends))
self.pi_creatures.add(*friends)
self.play(
FadeIn(headphones),
Animation(friends)
)
self.play_notes(randy.guitar)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "hooray"),
run_time = 2,
))
self.friends = friends
self.headphones = headphones
def add_bottom_conditionl(self):
pass
p = 0.99
bottom_part = self.sample_space[1]
bottom_part.divide_vertically(p, colors = [GREEN_E, YELLOW])
label = self.get_conditional_label(p, False)
braces, labels = bottom_part.get_bottom_braces_and_labels([label])
brace = braces[0]
self.play(FadeIn(bottom_part.vertical_parts))
self.play(GrowFromCenter(brace))
self.play(Write(label))
self.dither()
def friend_gives_compliment(self):
friends = self.friends
bubble = SpeechBubble(
height = 1.25, width = 3, direction = RIGHT,
fill_opacity = 0,
)
content = TextMobject("Phenomenal!")
content.scale(0.75)
bubble.add_content(content)
VGroup(bubble, content).next_to(friends, LEFT, SMALL_BUFF)
VGroup(bubble, content).to_edge(UP, SMALL_BUFF)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change_mode, "conniving")
))
self.dither()
self.play(
ShowCreation(bubble),
Write(bubble.content, run_time = 1),
ApplyMethod(friends[0].change_mode, "hooray"),
LaggedStart(
ApplyMethod, VGroup(*friends[1:]),
lambda pi : (pi.change_mode, "happy")
),
)
self.dither(2)
self.play(*map(FadeOut, [bubble, content]))
def friends_dont_like(self):
friends = self.friends
pi1, pi2, pi3 = friends
for friend in friends:
friend.generate_target()
pi1.target.change("guilty", pi2.eyes)
pi2.target.change("hesitant", pi1.eyes)
pi3.target.change("pondering", pi2.eyes)
self.play(LaggedStart(
MoveToTarget, friends
))
self.change_pi_creature_with_guitar("concerned_musician")
self.dither()
def false_compliment(self):
friend = self.friends[0]
bubble = SpeechBubble(
height = 1.25, width = 4.5, direction = RIGHT,
fill_opacity = 0,
)
content = TextMobject("The beat was consistent.")
content.scale(0.75)
bubble.add_content(content)
VGroup(bubble, content).next_to(friend, LEFT, SMALL_BUFF)
VGroup(bubble, content).to_edge(UP, SMALL_BUFF)
self.play(
friend.change_mode, "maybe",
ShowCreation(bubble),
Write(content)
)
self.change_pi_creature_with_guitar("happy")
self.dither()
self.play(*map(FadeOut, [bubble, content]))
self.bubble = bubble
def add_top_conditionl(self):
pass
p = 0.9
top_part = self.sample_space[0]
top_part.divide_vertically(p, colors = [TEAL_E, RED_E])
label = self.get_conditional_label(p, True)
braces, labels = top_part.get_top_braces_and_labels([label])
brace = braces[0]
self.play(FadeIn(top_part.vertical_parts))
self.play(GrowFromCenter(brace))
self.play(Write(label, run_time = 2))
self.dither()
def get_positive_review(self):
pass
friends = self.friends
self.change_pi_creature_with_guitar(
"soulful_musician",
LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "happy"),
run_time = 1,
)
)
self.play_notes(self.pi_creature.guitar)
def restrict_space(self):
pass
positive_space, negative_space = [
VGroup(*[
self.sample_space[i][j]
for i in range(2)
])
for j in range(2)
]
negative_space.save_state()
self.play(negative_space.fade, 0.8)
self.play(LaggedStart(
ApplyMethod, positive_space,
lambda m : (m.highlight, YELLOW),
rate_func = there_and_back,
run_time = 2,
lag_ratio = 0.7,
))
self.dither()
self.negative_space = negative_space
def show_posterior_rectangles(self):
pass
prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
label = TexMobject("P(S | ", "\\checkmark", ")")
label.scale(0.7)
label.highlight_by_tex("\\checkmark", GREEN)
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [label]
)
brace = braces[0]
self.play(ReplacementTransform(
prior_rects.copy(), post_rects,
run_time = 2
))
self.play(GrowFromCenter(brace))
self.play(Write(label))
self.dither()
self.post_rects = post_rects
self.post_tex = label
def show_prior_rectangle_areas(self):
prior_rects = self.get_prior_rectangles()
products = VGroup(
TexMobject("(", "0.8", ")(", "0.9", ")"),
TexMobject("(", "0.2", ")(", "0.99", ")"),
)
top_product, bottom_product = products
for product, rect in zip(products, prior_rects):
product.scale(0.7)
product.move_to(rect)
side_labels = self.sample_space.horizontal_parts.labels
top_labels = self.sample_space[0].vertical_parts.labels
bottom_labels = self.sample_space[1].vertical_parts.labels
self.play(
ReplacementTransform(
side_labels[0][-1].copy(),
top_product[1],
),
ReplacementTransform(
top_labels[0][-1].copy(),
top_product[3],
),
Write(VGroup(*top_product[::2]))
)
self.dither(2)
self.play(
ReplacementTransform(
side_labels[1][-1].copy(),
bottom_product[1],
),
ReplacementTransform(
bottom_labels[0][-1].copy(),
bottom_product[3],
),
Write(VGroup(*bottom_product[::2]))
)
self.dither(2)
self.products = products
def show_posterior_probability(self):
pass
post_tex = self.post_tex
rhs = TexMobject("\\approx", "0.78")
rhs.scale(0.7)
rhs.next_to(post_tex, RIGHT)
ratio = TexMobject(
"{(0.8)(0.9)", "\\over",
"(0.8)(0.9)", "+", "(0.2)(0.99)}"
)
ratio.scale(0.6)
ratio.next_to(VGroup(post_tex, rhs), DOWN, LARGE_BUFF)
ratio.to_edge(RIGHT)
arrow_kwargs = {
"tip_length" : 0.15,
"color" : WHITE,
"buff" : 2*SMALL_BUFF,
}
to_ratio_arrow = Arrow(
post_tex.get_bottom(), ratio.get_top(), **arrow_kwargs
)
to_rhs_arrow = Arrow(
ratio.get_top(), rhs[1].get_bottom(), **arrow_kwargs
)
self.play(
ShowCreation(to_ratio_arrow),
FadeIn(ratio)
)
self.dither()
self.play(ShowCreation(to_rhs_arrow))
self.play(Write(rhs, run_time = 1))
self.dither(2)
self.post_rhs = rhs
self.ratio_group = VGroup(ratio, to_ratio_arrow, to_rhs_arrow)
def intuition_of_positive_feedback(self):
pass
friends = self.friends
prior_num = self.sample_space.horizontal_parts.labels[0][-1]
post_num = self.post_rhs[-1]
prior_rect = SurroundingRectangle(prior_num)
post_rect = SurroundingRectangle(post_num)
dot = Dot(prior_rect.get_center())
dot.set_fill(WHITE, 0.5)
self.play(ShowCreation(prior_rect))
self.play(
dot.move_to, post_rect,
dot.set_fill, None, 0,
path_arc = -np.pi/6,
run_time = 2,
)
self.play(ShowCreation(post_rect))
self.dither(2)
for mode, time in ("shruggie", 2), ("hesitant", 0):
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, mode),
run_time = 2,
))
self.dither(time)
self.play(*map(FadeOut, [
prior_rect, post_rect,
self.ratio_group, self.post_rhs
]))
self.prior_num_rect = prior_rect
def make_friends_honest(self):
pass
post_rects = self.post_rects
self.play(FadeOut(self.products))
for value in 0.5, 0.1, 0.9:
label = self.get_conditional_label(value)
self.play(*self.get_top_conditional_change_anims(
value, post_rects,
new_label_kwargs = {"labels" : [label]},
), run_time = 2)
self.dither(2)
def fade_out_post_rect(self):
self.play(*map(FadeOut, [
self.post_rects,
self.post_rects.braces,
self.post_rects.labels,
]))
self.play(self.negative_space.restore)
def get_negative_feedback(self):
pass
friends = self.friends
old_prior_rects = self.get_prior_rectangles()
for part in self.sample_space.horizontal_parts:
part.vertical_parts.submobjects.reverse()
new_prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
label = TexMobject(
"P(S | \\text{not } ", "\\checkmark", ")",
"\\approx", "0.98"
)
label.scale(0.7)
label.highlight_by_tex("\\checkmark", GREEN)
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [label]
)
brace = braces[0]
def show_negative_feedback_posterior(self):
pass
self.play(old_prior_rects.fade, 0.8)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "pondering", post_rects),
run_time = 1
))
self.dither()
self.play(ReplacementTransform(
new_prior_rects.copy(), post_rects,
run_time = 2
))
self.play(GrowFromCenter(brace))
self.dither(2)
self.play(Write(label))
self.dither(3)
self.post_rects = post_rects
def compare_prior_to_post_given_negative(self):
post_num = self.post_rects.labels[0][-1]
post_num_rect = SurroundingRectangle(post_num)
self.play(ShowCreation(self.prior_num_rect))
self.dither()
self.play(ShowCreation(post_num_rect))
self.dither()
self.post_num_rect = post_num_rect
def intuition_of_negative_feedback(self):
pass
friends = self.friends
randy = self.pi_creature
bubble = self.bubble
modes = ["sassy", "pleading", "horrified"]
for friend, mode in zip(friends, modes):
friend.generate_target()
friend.target.change(mode, randy.eyes)
content = TextMobject("Horrible. Just horrible.")
content.scale(0.6)
bubble.add_content(content)
self.play(*map(MoveToTarget, friends))
self.play(
ShowCreation(bubble),
Write(bubble.content)
)
self.change_pi_creature_with_guitar("sad")
self.dither()
self.change_pi_creature_with_guitar("concerned_musician")
self.dither(3)
######
def create_pi_creature(self):
randy = Randolph()
randy.left_arm_range = [.36, .45]
self.randy = randy
return randy
def change_pi_creature_with_guitar(self, target_mode):
def get_conditional_label(self, value, given_suck = True):
positive_str = "\\checkmark"
label = TexMobject(
"P(", positive_str, "|",
"" if given_suck else "\\text{not }",
"S", ")",
"=", str(value)
)
label.highlight_by_tex(positive_str, GREEN)
label.scale(0.7)
return label
def change_pi_creature_with_guitar(self, target_mode, *added_anims):
randy = self.pi_creature
randy.remove(randy.arms, randy.guitar)
target = randy.copy()
@ -1516,7 +1957,7 @@ class MusicExample(SampleSpaceScene, PiCreatureScene):
target.guitar = randy.guitar.copy()
for pi in randy, target:
pi.add(pi.guitar, pi.arms)
self.play(Transform(randy, target))
self.play(Transform(randy, target), *added_anims)
def play_notes(self, guitar):
note = SVGMobject(file_name = "8th_note")
@ -1524,6 +1965,7 @@ class MusicExample(SampleSpaceScene, PiCreatureScene):
note.set_stroke(width = 0)
note.set_fill(BLUE, 1)
note.move_to(guitar)
note.shift(MED_SMALL_BUFF*(DOWN+2*LEFT))
notes = VGroup(*[note.copy() for x in range(10)])
sine_wave = FunctionGraph(np.sin, x_min = -5, x_max = 5)
sine_wave.scale(0.75)
@ -1541,9 +1983,95 @@ class MusicExample(SampleSpaceScene, PiCreatureScene):
rate_func = lambda t : t,
))
class FinalWordsOnRule(SampleSpaceScene):
def construct(self):
self.add_sample_space()
self.add_uses()
self.tweak_values()
def add_sample_space(self):
sample_space = self.sample_space = SampleSpace()
prior = 0.2
top_conditional = 0.8
bottom_condional = 0.3
sample_space.divide_horizontally(prior)
sample_space[0].divide_vertically(
top_conditional, colors = [GREEN, RED]
)
sample_space[1].divide_vertically(
bottom_condional, colors = [GREEN_E, RED_E]
)
B = "\\text{Belief}"
D = "\\text{Data}"
P_B = TexMobject("P(", B, ")")
P_D_given_B = TexMobject("P(", D, "|", B, ")")
P_D_given_not_B = TexMobject(
"P(", D, "|", "\\text{not }", B, ")"
)
P_B_given_D = TexMobject("P(", B, "|", D, ")")
labels = VGroup(P_B, P_D_given_B, P_D_given_not_B, P_B_given_D)
for label in labels:
label.scale(0.7)
label.highlight_by_tex(B, BLUE)
label.highlight_by_tex(D, GREEN)
prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
for i in range(2):
sample_space[i][1].fade(0.7)
braces = VGroup()
bs, ls = sample_space.get_side_braces_and_labels([P_B])
braces.add(*bs)
bs, ls = sample_space[0].get_top_braces_and_labels([P_D_given_B])
braces.add(*bs)
bs, ls = sample_space[1].get_bottom_braces_and_labels([P_D_given_not_B])
braces.add(*bs)
bs, ls = self.get_posterior_rectangle_braces_and_labels(
post_rects, [P_B_given_D]
)
braces.add(*bs)
group = VGroup(sample_space, braces, labels, post_rects)
group.to_corner(DOWN + LEFT)
self.add(group)
self.post_rects = post_rects
def add_uses(self):
uses = TextMobject(
"Machine learning, ",
"scientific inference, $\\dots$",
)
uses.to_edge(UP)
for use in uses:
self.play(Write(use, run_time = 2))
self.dither()
def tweak_values(self):
post_rects = self.post_rects
new_value_lists = [
(0.85, 0.1, 0.11),
(0.3, 0.9, 0.4),
(0.97, 0.3, 1./22),
]
for new_values in new_value_lists:
for i, value in zip(range(2), new_values):
self.play(*self.get_conditional_change_anims(
i, value, post_rects
))
self.dither()
self.play(*it.chain(
self.get_horizontal_division_change_animations(new_values[-1]),
self.get_posterior_rectangle_change_anims(post_rects)
))
self.dither()
self.dither(2)
class FootnoteWrapper(NextVideoWrapper):
CONFIG = {
"title" : "Thoughts on the classic Bayes example"
}

View File

@ -38,7 +38,6 @@ class SunGlasses(SVGMobject):
)
self.move_to(pi_creature.eyes, UP)
class Speedometer(VMobject):
CONFIG = {
"arc_angle" : 4*np.pi/3,
@ -280,13 +279,18 @@ class Headphones(SVGMobject):
"y_stretch_factor" : 0.5,
"color" : GREY,
}
def __init__(self, **kwargs):
def __init__(self, pi_creature = None, **kwargs):
digest_config(self, kwargs)
SVGMobject.__init__(self, file_name = self.file_name, **kwargs)
self.stretch(self.y_stretch_factor, 1)
self.scale_to_fit_height(self.height)
self.set_stroke(width = 0)
self.set_fill(color = self.color)
if pi_creature is not None:
eyes = pi_creature.eyes
self.scale_to_fit_height(3*eyes.get_height())
self.move_to(eyes, DOWN)
self.shift(DOWN*eyes.get_height()/4)
class Clock(VGroup):
CONFIG = {

View File

@ -156,7 +156,7 @@ class SampleSpace(VGroup):
"height" : 3,
"width" : 3,
"fill_color" : DARK_GREY,
"fill_opacity" : 0.8,
"fill_opacity" : 1,
"stroke_width" : 0.5,
"stroke_color" : LIGHT_GREY,
},
@ -204,7 +204,7 @@ class SampleSpace(VGroup):
def get_horizontal_division(
self, p_list,
colors = [GREEN_E, BLUE],
colors = [GREEN_E, BLUE_E],
vect = DOWN
):
return self.get_division_along_dimension(p_list, 1, colors, vect)