import sys import os.path import cv2 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from helpers import * from mobject.tex_mobject import TexMobject from mobject import Mobject, Group from mobject.image_mobject import ImageMobject from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * from animation.playground import * from animation.continual_animation import * from topics.geometry import * from topics.characters import * from topics.functions import * from topics.fractals import * from topics.number_line import * from topics.combinatorics import * from topics.numerals import * from topics.three_dimensions import * from topics.objects import * from topics.probability import * from topics.complex_numbers import * from scene import Scene from scene.reconfigurable_scene import ReconfigurableScene from scene.zoomed_scene import * from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * from nn.network import * #force_skipping #revert_to_original_skipping_status DEFAULT_GAUSS_BLUR_CONFIG = { "ksize" : (5, 5), "sigmaX" : 10, "sigmaY" : 10, } DEFAULT_CANNY_CONFIG = { "threshold1" : 100, "threshold2" : 200, } def get_edges(image_array): blurred = cv2.GaussianBlur( image_array, **DEFAULT_GAUSS_BLUR_CONFIG ) edges = cv2.Canny( blurred, **DEFAULT_CANNY_CONFIG ) return edges class WrappedImage(Group): CONFIG = { "rect_kwargs" : { "color" : BLUE, "buff" : SMALL_BUFF, } } def __init__(self, image_mobject, **kwargs): Group.__init__(self, **kwargs) rect = SurroundingRectangle( image_mobject, **self.rect_kwargs ) self.add(rect, image_mobject) class PixelsAsSquares(VGroup): CONFIG = { "height" : 2, } def __init__(self, image_mobject, **kwargs): VGroup.__init__(self, **kwargs) for row in image_mobject.pixel_array: for rgba in row: square = Square( stroke_width = 0, fill_opacity = rgba[3]/255.0, fill_color = rgba_to_color(rgba/255.0), ) self.add(square) self.arrange_submobjects_in_grid( *image_mobject.pixel_array.shape[:2], buff = 0 ) self.replace(image_mobject) class PixelsFromVect(PixelsAsSquares): def __init__(self, vect, **kwargs): PixelsAsSquares.__init__(self, ImageMobject(layer_to_image_array(vect)), **kwargs ) class MNistMobject(WrappedImage): def __init__(self, vect, **kwargs): WrappedImage.__init__(self, ImageMobject(layer_to_image_array(vect)), **kwargs ) class NetworkMobject(VGroup): CONFIG = { "neuron_radius" : 0.15, "neuron_to_neuron_buff" : MED_SMALL_BUFF, "layer_to_layer_buff" : LARGE_BUFF, "neuron_stroke_color" : BLUE, "neuron_stroke_width" : 3, "neuron_fill_color" : GREEN, "edge_color" : LIGHT_GREY, "edge_stroke_width" : 2, "edge_propogation_color" : GREEN, "edge_propogation_time" : 1, "max_shown_neurons" : 16, "brace_for_large_layers" : True, "average_shown_activation_of_large_layer" : True, } def __init__(self, neural_network, **kwargs): VGroup.__init__(self, **kwargs) self.neural_network = neural_network self.layer_sizes = neural_network.sizes self.add_neurons() self.add_edges() def add_neurons(self): layers = VGroup(*[ self.get_layer(size) for size in self.layer_sizes ]) layers.arrange_submobjects(RIGHT, buff = self.layer_to_layer_buff) self.layers = layers self.add(self.layers) def get_layer(self, size): layer = VGroup() n_neurons = size if n_neurons > self.max_shown_neurons: n_neurons = self.max_shown_neurons neurons = VGroup(*[ Circle( radius = self.neuron_radius, stroke_color = self.neuron_stroke_color, stroke_width = self.neuron_stroke_width, ) for x in range(n_neurons) ]) neurons.arrange_submobjects( DOWN, buff = self.neuron_to_neuron_buff ) for neuron in neurons: neuron.edges_in = VGroup() neuron.edges_out = VGroup() layer.neurons = neurons layer.add(neurons) if size > n_neurons: dots = TexMobject("\\vdots") dots.move_to(neurons) VGroup(*neurons[:len(neurons)/2]).next_to( dots, UP, MED_SMALL_BUFF ) VGroup(*neurons[len(neurons)/2:]).next_to( dots, DOWN, MED_SMALL_BUFF ) layer.dots = dots layer.add(dots) if self.brace_for_large_layers: brace = Brace(layer, LEFT) brace_label = brace.get_tex(str(size)) layer.brace = brace layer.brace_label = brace_label layer.add(brace, brace_label) return layer def add_edges(self): self.edge_groups = VGroup() for l1, l2 in zip(self.layers[:-1], self.layers[1:]): edge_group = VGroup() for n1, n2 in it.product(l1.neurons, l2.neurons): edge = Line( n1.get_center(), n2.get_center(), buff = self.neuron_radius, stroke_color = self.edge_color, stroke_width = self.edge_stroke_width, ) edge_group.add(edge) n1.edges_out.add(edge) n2.edges_in.add(edge) self.edge_groups.add(edge_group) self.add_to_back(self.edge_groups) def get_active_layer(self, layer_index, activation_vector): layer = self.layers[layer_index].deepcopy() n_neurons = len(layer.neurons) av = activation_vector def arr_to_num(arr): return (np.sum(arr > 0.1) / float(len(arr)))**(1./3) if len(av) > n_neurons: if self.average_shown_activation_of_large_layer: indices = np.arange(n_neurons) indices *= int(len(av)/n_neurons) indices = list(indices) indices.append(len(av)) av = np.array([ arr_to_num(av[i1:i2]) for i1, i2 in zip(indices[:-1], indices[1:]) ]) else: av = np.append( av[:n_neurons/2], av[-n_neurons/2:], ) for activation, neuron in zip(av, layer.neurons): neuron.set_fill( color = self.neuron_fill_color, opacity = activation ) return layer def deactivate_layers(self): all_neurons = VGroup(*it.chain(*[ layer.neurons for layer in self.layers ])) all_neurons.set_fill(opacity = 0) return self def get_edge_propogation_animations(self, index): edge_group_copy = self.edge_groups[index].copy() edge_group_copy.set_stroke( self.edge_propogation_color, width = 1.5*self.edge_stroke_width ) return [ ShowCreationThenDestruction( mob, run_time = self.edge_propogation_time ) for mob in edge_group_copy ] class MNistNetworkMobject(NetworkMobject): CONFIG = { "neuron_to_neuron_buff" : SMALL_BUFF, "layer_to_layer_buff" : 1.5, "edge_stroke_width" : 1, } def __init__(self, **kwargs): network = get_pretrained_network() NetworkMobject.__init__(self, network, **kwargs) self.add_output_labels() def add_output_labels(self): self.output_labels = VGroup() for n, neuron in enumerate(self.layers[-1].neurons): label = TexMobject(str(n)) label.scale_to_fit_height(0.75*neuron.get_height()) label.move_to(neuron) label.shift(neuron.get_width()*RIGHT) self.output_labels.add(label) self.add(self.output_labels) class NetworkScene(Scene): CONFIG = { "layer_sizes" : [8, 6, 6, 4], } def setup(self): self.add_network() def add_network(self): self.network = Network(sizes = self.layer_sizes) self.network_mob = NetworkMobject(self.network) self.add(self.network_mob) def feed_forward(self, input_vector, false_confidence = False, added_anims = None): if added_anims is None: added_anims = [] activations = self.network.get_activation_of_all_layers( input_vector ) if false_confidence: i = np.argmax(activations[-1]) activations[-1] *= 0 activations[-1][i] = 1.0 for i, activation in enumerate(activations): self.show_activation_of_layer(i, activation, added_anims) added_anims = [] def show_activation_of_layer(self, layer_index, activation_vector, added_anims = None): if added_anims is None: added_anims = [] layer = self.network_mob.layers[layer_index] active_layer = self.network_mob.get_active_layer( layer_index, activation_vector ) anims = [Transform(layer, active_layer)] if layer_index > 0: anims += self.network_mob.get_edge_propogation_animations( layer_index-1 ) anims += added_anims self.play(*anims) def remove_random_edges(self, prop = 0.9): for edge_group in self.network_mob.edge_groups: for edge in list(edge_group): if np.random.random() < prop: edge_group.remove(edge) def make_transparent(image_mob): alpha_vect = np.array( image_mob.pixel_array[:,:,0], dtype = 'uint8' ) image_mob.highlight(WHITE) image_mob.pixel_array[:,:,3] = alpha_vect return image_mob ############################### class ExampleThrees(PiCreatureScene): def construct(self): self.show_initial_three() self.show_alternate_threes() self.resolve_remaining_threes() self.show_alternate_digits() def show_initial_three(self): randy = self.pi_creature self.three_mobs = self.get_three_mobs() three_mob = self.three_mobs[0] three_mob_copy = three_mob[1].copy() three_mob_copy.sort_submobjects(lambda p : np.dot(p, DOWN+RIGHT)) braces = VGroup(*[Brace(three_mob, v) for v in LEFT, UP]) brace_labels = VGroup(*[ brace.get_text("28px") for brace in braces ]) bubble = randy.get_bubble(height = 4, width = 6) three_mob.generate_target() three_mob.target.scale_to_fit_height(1) three_mob.target.next_to(bubble[-1].get_left(), RIGHT, LARGE_BUFF) arrow = Arrow(LEFT, RIGHT, color = BLUE) arrow.next_to(three_mob.target, RIGHT) real_three = TexMobject("3") real_three.scale_to_fit_height(0.8) real_three.next_to(arrow, RIGHT) self.play( FadeIn(three_mob[0]), LaggedStart(FadeIn, three_mob[1]) ) self.dither() self.play( LaggedStart( DrawBorderThenFill, three_mob_copy, run_time = 3, stroke_color = WHITE, remover = True, ), randy.change, "sassy", *it.chain( map(GrowFromCenter, braces), map(FadeIn, brace_labels) ) ) self.dither() self.play( ShowCreation(bubble), MoveToTarget(three_mob), FadeOut(braces), FadeOut(brace_labels), randy.change, "pondering" ) self.play( ShowCreation(arrow), Write(real_three) ) self.dither() self.bubble = bubble self.arrow = arrow self.real_three = real_three def show_alternate_threes(self): randy = self.pi_creature three = self.three_mobs[0] three.generate_target() three.target[0].set_fill(opacity = 0, family = False) for square in three.target[1]: yellow_rgb = color_to_rgb(YELLOW) square_rgb = color_to_rgb(square.get_fill_color()) square.set_fill( rgba_to_color(yellow_rgb*square_rgb), opacity = 0.5 ) alt_threes = VGroup(*self.three_mobs[1:]) alt_threes.arrange_submobjects(DOWN) alt_threes.scale_to_fit_height(2*SPACE_HEIGHT - 2) alt_threes.to_edge(RIGHT) for alt_three in alt_threes: self.add(alt_three) self.dither(0.5) self.play( randy.change, "plain", *map(FadeOut, [ self.bubble, self.arrow, self.real_three ]) + [MoveToTarget(three)] ) for alt_three in alt_threes[:2]: self.play(three.replace, alt_three) self.dither() for moving_three in three, alt_threes[1]: moving_three.generate_target() moving_three.target.next_to(alt_threes, LEFT, LARGE_BUFF) moving_three.target[0].set_stroke(width = 0) moving_three.target[1].space_out_submobjects(1.5) self.play(MoveToTarget( moving_three, submobject_mode = "lagged_start" )) self.play( Animation(randy), moving_three.replace, randy.eyes[1], moving_three.scale_in_place, 0.7, run_time = 2, submobject_mode = "lagged_start", ) self.play( Animation(randy), FadeOut(moving_three) ) self.remaining_threes = [alt_threes[0], alt_threes[2]] def resolve_remaining_threes(self): randy = self.pi_creature left_three, right_three = self.remaining_threes equals = TexMobject("=") equals.move_to(self.arrow) for three, vect in (left_three, LEFT), (right_three, RIGHT): three.generate_target() three.target.scale_to_fit_height(1) three.target.next_to(equals, vect) self.play( randy.change, "thinking", ShowCreation(self.bubble), MoveToTarget(left_three), MoveToTarget(right_three), Write(equals), ) self.dither() self.equals = equals def show_alternate_digits(self): randy = self.pi_creature cross = Cross(self.equals) cross.stretch_to_fit_height(0.5) three = self.remaining_threes[1] image_map = get_organized_images() arrays = [image_map[k][0] for k in range(8, 4, -1)] alt_mobs = [ WrappedImage( PixelsAsSquares(ImageMobject(layer_to_image_array(arr))), color = LIGHT_GREY, buff = 0 ).replace(three) for arr in arrays ] self.play( randy.change, "sassy", Transform(three, alt_mobs[0]), ShowCreation(cross) ) self.dither() for mob in alt_mobs[1:]: self.play(Transform(three, mob)) self.dither() ###### def create_pi_creature(self): return Randolph().to_corner(DOWN+LEFT) def get_three_mobs(self): three_arrays = get_organized_images()[3][:4] three_mobs = VGroup() for three_array in three_arrays: im_mob = ImageMobject( layer_to_image_array(three_array), height = 4, ) pixel_mob = PixelsAsSquares(im_mob) three_mob = WrappedImage( pixel_mob, color = LIGHT_GREY, buff = 0 ) three_mobs.add(three_mob) return three_mobs class WriteAProgram(Scene): def construct(self): three_array = get_organized_images()[3][0] im_mob = ImageMobject(layer_to_image_array(three_array)) three = PixelsAsSquares(im_mob) three.sort_submobjects(lambda p : np.dot(p, DOWN+RIGHT)) three.scale_to_fit_height(6) three.next_to(ORIGIN, LEFT) three_rect = SurroundingRectangle( three, color = BLUE, buff = SMALL_BUFF ) numbers = VGroup() for square in three: rgb = square.fill_rgb num = DecimalNumber( square.fill_rgb[0], num_decimal_points = 1 ) num.set_stroke(width = 1) color = rgba_to_color(1 - (rgb + 0.2)/1.2) num.highlight(color) num.scale_to_fit_width(0.7*square.get_width()) num.move_to(square) numbers.add(num) arrow = Arrow(LEFT, RIGHT, color = BLUE) arrow.next_to(three, RIGHT) choices = VGroup(*[TexMobject(str(n)) for n in range(10)]) choices.arrange_submobjects(DOWN) choices.scale_to_fit_height(2*SPACE_HEIGHT - 1) choices.next_to(arrow, RIGHT) self.play( LaggedStart(DrawBorderThenFill, three), ShowCreation(three_rect) ) self.play(Write(numbers)) self.play( ShowCreation(arrow), LaggedStart(FadeIn, choices), ) rect = SurroundingRectangle(choices[0], buff = SMALL_BUFF) q_mark = TexMobject("?") q_mark.next_to(rect, RIGHT) self.play(ShowCreation(rect)) for n in 8, 1, 5, 3: self.play( rect.move_to, choices[n], MaintainPositionRelativeTo(q_mark, rect) ) self.dither(1) choice = choices[3] choices.remove(choice) choice.add(rect) self.play( choice.scale, 1.5, choice.next_to, arrow, RIGHT, FadeOut(choices), FadeOut(q_mark), ) self.dither(2) class LayOutPlan(TeacherStudentsScene, NetworkScene): def setup(self): TeacherStudentsScene.setup(self) NetworkScene.setup(self) self.remove(self.network_mob) def construct(self): self.show_words() self.show_network() self.show_math() self.ask_about_layers() self.show_learning() def show_words(self): words = VGroup( TextMobject("Machine", "learning").highlight(GREEN), TextMobject("Neural network").highlight(BLUE), ) words.next_to(self.teacher.get_corner(UP+LEFT), UP) words[0].save_state() words[0].shift(DOWN) words[0].fade(1) self.play( words[0].restore, self.teacher.change, "raise_right_hand", self.get_student_changes("pondering", "erm", "sassy") ) self.play( words[0].shift, MED_LARGE_BUFF*UP, FadeIn(words[1]), ) self.change_student_modes( *["pondering"]*3, look_at_arg = words ) self.play(words.to_corner, UP+RIGHT) self.words = words def show_network(self): network_mob = self.network_mob network_mob.next_to(self.students, UP) self.play( ReplacementTransform( VGroup(self.words[1].copy()), network_mob.layers ), self.get_student_changes( *["confused"]*3, submobject_mode = "all_at_once" ), self.teacher.change, "plain", run_time = 1 ) self.play(ShowCreation( network_mob.edge_groups, submobject_mode = "lagged_start", run_time = 2, lag_factor = 8, rate_func = None, )) in_vect = np.random.random(self.network.sizes[0]) self.feed_forward(in_vect) def show_math(self): equation = TexMobject( "\\textbf{a}_{l+1}", "=", "\\sigma(", "W_l", "\\textbf{a}_l", "+", "b_l", ")" ) equation.highlight_by_tex_to_color_map({ "\\textbf{a}" : GREEN, }) equation.move_to(self.network_mob.get_corner(UP+RIGHT)) equation.to_edge(UP) self.play(Write(equation, run_time = 2)) self.dither() self.equation = equation def ask_about_layers(self): self.student_says( "Why the layers?", student_index = 2, bubble_kwargs = {"direction" : LEFT} ) self.dither() self.play(RemovePiCreatureBubble(self.students[2])) def show_learning(self): word = self.words[0][1].copy() rect = SurroundingRectangle(word, color = YELLOW) self.network_mob.neuron_fill_color = YELLOW layer = self.network_mob.layers[-1] activation = np.zeros(len(layer.neurons)) activation[1] = 1.0 active_layer = self.network_mob.get_active_layer( -1, activation ) word_group = VGroup(word, rect) word_group.generate_target() word_group.target.move_to(self.equation, LEFT) word_group.target[0].highlight(YELLOW) word_group.target[1].set_stroke(width = 0) self.play(ShowCreation(rect)) self.play( Transform(layer, active_layer), FadeOut(self.equation), MoveToTarget(word_group), ) for edge_group in reversed(self.network_mob.edge_groups): edge_group.generate_target() for edge in edge_group.target: edge.set_stroke( YELLOW, width = 4*np.random.random()**2 ) self.play(MoveToTarget(edge_group)) self.dither() class PreviewMNistNetwork(NetworkScene): CONFIG = { "n_examples" : 15, "network_mob_config" : {}, } def construct(self): self.remove_random_edges(0.7) #Remove? training_data, validation_data, test_data = load_data_wrapper() for data in test_data[:self.n_examples]: self.feed_in_image(data[0]) def feed_in_image(self, in_vect): image = PixelsFromVect(in_vect) image.next_to(self.network_mob, LEFT, LARGE_BUFF, UP) big_rect = SurroundingRectangle(image, color = BLUE) start_neurons = self.network_mob.layers[0].neurons.copy() start_neurons.set_stroke(WHITE, width = 0) start_neurons.set_fill(WHITE, 0) self.play(FadeIn(image), FadeIn(big_rect)) self.feed_forward(in_vect, added_anims = [ self.get_image_to_layer_one_animation(image, start_neurons) ]) n = np.argmax([ neuron.get_fill_opacity() for neuron in self.network_mob.layers[-1].neurons ]) rect = SurroundingRectangle(VGroup( self.network_mob.layers[-1].neurons[n], self.network_mob.output_labels[n], )) self.play(ShowCreation(rect)) self.play(FadeOut(rect)) self.play( FadeOut(image), FadeOut(big_rect), self.network_mob.deactivate_layers, ) def get_image_to_layer_one_animation(self, image, start_neurons): image_mover = VGroup(*[ pixel.copy() for pixel in image if pixel.fill_rgb[0] > 0.1 ]) return Transform( image_mover, start_neurons, remover = True, run_time = 1, ) ### def add_network(self): self.network_mob = MNistNetworkMobject(**self.network_mob_config) self.network = self.network_mob.neural_network self.add(self.network_mob) class AlternateNeuralNetworks(PiCreatureScene): def construct(self): morty = self.pi_creature examples = VGroup( VGroup( TextMobject("Convolutional neural network"), TextMobject("Good for image recognition"), ), VGroup( TextMobject("Long short-term memory network"), TextMobject("Good for speech recognition"), ) ) for ex in examples: arrow = Arrow(LEFT, RIGHT, color = BLUE) ex[0].next_to(arrow, LEFT) ex[1].next_to(arrow, RIGHT) ex.submobjects.insert(1, arrow) examples.scale_to_fit_width(2*SPACE_WIDTH - 1) examples.next_to(morty, UP).to_edge(RIGHT) maybe_words = TextMobject("Maybe future videos?") maybe_words.scale(0.8) maybe_words.next_to(morty, UP) maybe_words.to_edge(RIGHT) maybe_words.highlight(YELLOW) self.play( Write(examples[0], run_time = 2), morty.change, "raise_right_hand" ) self.dither() self.play( examples[0].shift, MED_LARGE_BUFF*UP, FadeIn(examples[1], submobject_mode = "lagged_start"), ) self.dither() self.play( examples.shift, UP, FadeIn(maybe_words), morty.change, "maybe" ) self.dither(2) class PlainVanillaWrapper(Scene): def construct(self): title = TextMobject("Plain vanilla") subtitle = TextMobject("(aka ``multilayer perceptron'')") title.scale(1.5) title.to_edge(UP) subtitle.next_to(title, DOWN) self.add(title) self.dither(2) self.play(Write(subtitle, run_time = 2)) self.dither(2) class NotPerfectAddOn(Scene): def construct(self): words = TextMobject("Not perfect!") words.scale(1.5) arrow = Arrow(UP+RIGHT, DOWN+LEFT, color = RED) words.highlight(RED) arrow.to_corner(DOWN+LEFT) words.next_to(arrow, UP+RIGHT) self.play( Write(words), ShowCreation(arrow), run_time = 1 ) self.dither(2) class MoreAThanI(TeacherStudentsScene): def construct(self): self.teacher_says( "More \\\\ A than I", target_mode = "hesitant" ) self.change_student_modes("sad", "erm", "tired") self.dither(2) class BreakDownName(Scene): def construct(self): self.ask_questions() self.show_neuron() def ask_questions(self): name = TextMobject("Neural", "network") name.to_edge(UP) q1 = TextMobject( "What are \\\\ the ", "neuron", "s?", arg_separator = "" ) q2 = TextMobject("How are \\\\ they connected?") q1.next_to(name[0].get_bottom(), DOWN, buff = LARGE_BUFF) q2.next_to(name[1].get_bottom(), DOWN+RIGHT, buff = LARGE_BUFF) a1 = Arrow(q1.get_top(), name[0].get_bottom()) a2 = Arrow(q2.get_top(), name.get_corner(DOWN+RIGHT)) VGroup(q1, a1).highlight(BLUE) VGroup(q2, a2).highlight(YELLOW) randy = Randolph().to_corner(DOWN+LEFT) brain = SVGMobject(file_name = "brain") brain.set_fill(LIGHT_GREY, opacity = 0) brain.replace(randy.eyes, dim_to_match = 1) self.add(name) self.play(randy.change, "pondering") self.play( brain.scale_to_fit_height, 2, brain.shift, 2*UP, brain.set_fill, None, 1, randy.look, UP ) brain_outline = brain.copy() brain_outline.set_fill(opacity = 0) brain_outline.set_stroke(BLUE_B, 3) self.play( ShowPassingFlash( brain_outline, time_width = 0.5, run_time = 2 ) ) self.play(Blink(randy)) self.dither() self.play( Write(q1, run_time = 1), ShowCreation(a1), name[0].highlight, q1.get_color(), ) self.play( Write(q2, run_time = 1), ShowCreation(a2), name[1].highlight, q2.get_color() ) self.dither(2) self.play(*map(FadeOut, [ name, randy, brain, q2, a1, a2, q1[0], q1[2] ])) self.neuron_word = q1[1] def show_neuron(self): neuron_word = TextMobject("Neuron") arrow = TexMobject("\\rightarrow") arrow.shift(LEFT) description = TextMobject("Thing that holds a number") neuron_word.highlight(BLUE) neuron_word.next_to(arrow, LEFT) neuron_word.shift(0.5*SMALL_BUFF*UP) description.next_to(arrow, RIGHT) neuron = Circle(radius = 0.35, color = BLUE) neuron.next_to(neuron_word, UP, MED_LARGE_BUFF) num = TexMobject("0.2") num.scale_to_fit_width(0.7*neuron.get_width()) num.move_to(neuron) num.save_state() num.move_to(description.get_right()) num.set_fill(opacity = 1) self.play( ReplacementTransform(self.neuron_word, neuron_word), ShowCreation(neuron) ) self.play( ShowCreation(arrow), Write(description, run_time = 1) ) self.dither() self.play( neuron.set_fill, None, 0.2, num.restore ) self.dither() for value in 0.8, 0.4, 0.1, 0.5: mob = TexMobject(str(value)) mob.replace(num) self.play( neuron.set_fill, None, value, Transform(num, mob) ) self.dither() class IntroduceEachLayer(PreviewMNistNetwork): CONFIG = { "network_mob_config" : { "neuron_stroke_color" : WHITE, "neuron_stroke_width" : 2, "neuron_fill_color" : WHITE, "average_shown_activation_of_large_layer" : False, "edge_propogation_color" : YELLOW, "edge_propogation_time" : 2, } } def construct(self): self.setup_network_mob() self.break_up_image_as_neurons() self.show_activation_of_one_neuron() self.transform_into_full_network() self.show_output_layer() self.show_hidden_layers() self.show_propogation() def setup_network_mob(self): self.remove(self.network_mob) def break_up_image_as_neurons(self): self.image_map = get_organized_images() image = self.image_map[9][0] image_mob = PixelsFromVect(image) image_mob.scale_to_fit_height(4) image_mob.next_to(ORIGIN, LEFT) rect = SurroundingRectangle(image_mob, color = BLUE) neurons = VGroup() for pixel in image_mob: pixel.set_fill(WHITE, opacity = pixel.fill_rgb[0]) neuron = Circle( color = WHITE, stroke_width = 1, radius = pixel.get_width()/2 ) neuron.move_to(pixel) neuron.set_fill(WHITE, pixel.get_fill_opacity()) neurons.add(neuron) neurons.scale_in_place(1.2) neurons.space_out_submobjects(1.3) neurons.to_edge(DOWN) braces = VGroup(*[Brace(neurons, vect) for vect in LEFT, UP]) labels = VGroup(*[ brace.get_tex("28", buff = SMALL_BUFF) for brace in braces ]) equation = TexMobject("28", "\\times", "28", "=", "784") equation.next_to(neurons, RIGHT, LARGE_BUFF, UP) self.corner_image = MNistMobject(image) self.corner_image.to_corner(UP+LEFT) self.add(image_mob, rect) self.dither() self.play( ReplacementTransform(image_mob, neurons), FadeOut(rect), FadeIn(braces), FadeIn(labels), ) self.dither() self.play( ReplacementTransform(labels[0].copy(), equation[0]), Write(equation[1]), ReplacementTransform(labels[1].copy(), equation[2]), Write(equation[3]), Write(equation[4]), ) self.dither() self.neurons = neurons self.braces = braces self.brace_labels = labels self.num_pixels_equation = equation self.image_vect = image def show_activation_of_one_neuron(self): neurons = self.neurons numbers = VGroup() example_neuron = None example_num = None for neuron in neurons: o = neuron.get_fill_opacity() num = DecimalNumber(o, num_decimal_points = 1) num.scale_to_fit_width(0.7*neuron.get_width()) num.move_to(neuron) if o > 0.8: num.set_fill(BLACK) numbers.add(num) if o > 0.25 and o < 0.75 and example_neuron is None: example_neuron = neuron example_num = num example_neuron.save_state() example_num.save_state() example_neuron.generate_target() example_neuron.target.scale_to_fit_height(1.5) example_neuron.target.next_to(neurons, RIGHT) example_num.target = DecimalNumber( example_neuron.get_fill_opacity() ) example_num.target.move_to(example_neuron.target) def change_activation(num): self.play( example_neuron.set_fill, None, num, ChangingDecimal( example_num, lambda a : example_neuron.get_fill_opacity(), ), UpdateFromFunc( example_num, lambda m : m.set_fill( BLACK if example_neuron.get_fill_opacity() > 0.8 else WHITE ) ) ) self.play(LaggedStart(FadeIn, numbers)) self.play( MoveToTarget(example_neuron), MoveToTarget(example_num) ) self.dither() curr_opacity = example_neuron.get_fill_opacity() for num in 0.3, 0.01, 1.0, curr_opacity: change_activation(num) self.dither() rect = SurroundingRectangle(example_num, color = YELLOW) activation = TextMobject("``Activation''") activation.next_to(example_neuron, RIGHT) activation.highlight(rect.get_color()) self.play(ShowCreation(rect)) self.play(Write(activation, run_time = 1)) self.dither() change_activation(1.0) self.dither() change_activation(0.2) self.dither() self.play( example_neuron.restore, example_num.restore, FadeOut(activation), FadeOut(rect), ) self.play(FadeOut(numbers)) def transform_into_full_network(self): network_mob = self.network_mob neurons = self.neurons layer = network_mob.layers[0] n = network_mob.max_shown_neurons/2 self.play( FadeOut(self.braces), FadeOut(self.brace_labels), FadeOut(VGroup(*self.num_pixels_equation[:-1])) ) self.play( ReplacementTransform( VGroup(*neurons[:n]), VGroup(*layer.neurons[:n]), ), ReplacementTransform( VGroup(*neurons[n:-n]), layer.dots, ), ReplacementTransform( VGroup(*neurons[-n:]), VGroup(*layer.neurons[-n:]), ), FadeIn(self.corner_image) ) self.play( ReplacementTransform( self.num_pixels_equation[-1], layer.brace_label ), FadeIn(layer.brace) ) self.dither() for edge_group, layer in zip(network_mob.edge_groups, network_mob.layers[1:]): self.play( LaggedStart(FadeIn, layer, run_time = 1), ShowCreation(edge_group), ) self.dither() def show_output_layer(self): layer = self.network_mob.layers[-1] labels = self.network_mob.output_labels rect = SurroundingRectangle( VGroup(layer, labels) ) neuron = layer.neurons[-1] neuron.set_fill(WHITE, 0) label = labels[-1] for mob in neuron, label: mob.save_state() mob.generate_target() neuron.target.scale_in_place(4) neuron.target.shift(1.5*RIGHT) label.target.scale(1.5) label.target.next_to(neuron.target, RIGHT) activation = DecimalNumber(0) activation.move_to(neuron.target) def change_activation(num): self.play( neuron.set_fill, None, num, ChangingDecimal( activation, lambda a : neuron.get_fill_opacity(), ), UpdateFromFunc( activation, lambda m : m.set_fill( BLACK if neuron.get_fill_opacity() > 0.8 else WHITE ) ) ) self.play(ShowCreation(rect)) self.play(LaggedStart(FadeIn, labels)) self.dither() self.play( MoveToTarget(neuron), MoveToTarget(label), ) self.play(FadeIn(activation)) for num in 0.5, 0.38, 0.97: change_activation(num) self.dither() self.play( neuron.restore, neuron.set_fill, None, 1, label.restore, FadeOut(activation), FadeOut(rect), ) self.dither() def show_hidden_layers(self): hidden_layers = VGroup(*self.network_mob.layers[1:3]) rect = SurroundingRectangle(hidden_layers, color = YELLOW) name = TextMobject("``Hidden layers''") name.next_to(rect, UP, SMALL_BUFF) name.highlight(YELLOW) q_marks = VGroup() for layer in hidden_layers: for neuron in layer.neurons: q_mark = TextMobject("?") q_mark.scale_to_fit_height(0.8*neuron.get_height()) q_mark.move_to(neuron) q_marks.add(q_mark) q_marks.gradient_highlight(BLUE, YELLOW) q_mark = TextMobject("?").scale(4) q_mark.move_to(hidden_layers) q_mark.highlight(YELLOW) q_marks.add(q_mark) self.play( ShowCreation(rect), Write(name) ) self.dither() self.play(Write(q_marks)) self.dither() self.play( FadeOut(q_marks), Animation(q_marks[-1].copy()) ) def show_propogation(self): self.revert_to_original_skipping_status() self.remove_random_edges(0.7) self.feed_forward(self.image_vect) class MoreHonestMNistNetworkPreview(IntroduceEachLayer): CONFIG = { "network_mob_config" : { "edge_propogation_time" : 1.5, } } def construct(self): PreviewMNistNetwork.construct(self) def get_image_to_layer_one_animation(self, image, start_neurons): neurons = VGroup() for pixel in image: neuron = Circle( radius = pixel.get_width()/2, stroke_width = 1, stroke_color = WHITE, fill_color = WHITE, fill_opacity = pixel.fill_rgb[0] ) neuron.move_to(pixel) neurons.add(neuron) neurons.scale(1.2) neurons.next_to(image, DOWN) n = len(start_neurons) point = VectorizedPoint(start_neurons.get_center()) target = VGroup(*it.chain( start_neurons[:n/2], [point.copy() for x in range(len(neurons)-n)], start_neurons[n/2:], )) mover = image.copy() self.play(Transform(mover, neurons)) return Transform( mover, target, run_time = 2, submobject_mode = "lagged_start", remover = True ) class AskAboutPropogationAndTraining(TeacherStudentsScene): def construct(self): self.student_says( "How does one layer \\\\ influence the next?", student_index = 0, run_time = 1 ) self.dither() self.student_says( "How does \\\\ training work?", student_index = 2, run_time = 1 ) self.dither(3) class AskAboutLayers(PreviewMNistNetwork): def construct(self): self.play( self.network_mob.scale, 0.8, self.network_mob.to_edge, DOWN, ) question = TextMobject("Why the", "layers?") question.to_edge(UP) neuron_groups = [ layer.neurons for layer in self.network_mob.layers ] arrows = VGroup(*[ Arrow( question[1].get_bottom(), group.get_top() ) for group in neuron_groups ]) rects = map(SurroundingRectangle, neuron_groups[1:3]) self.play( Write(question, run_time = 1), LaggedStart( GrowFromPoint, arrows, lambda a : (a, a.get_start()), run_time = 2 ) ) self.dither() self.play(*map(ShowCreation, rects)) self.dither() class BreakUpMacroPatterns(IntroduceEachLayer): CONFIG = { "camera_config" : {"background_alpha" : 255}, "prefixes" : [ "nine", "eight", "four", "upper_loop", "right_line", "lower_loop", "horizontal_line", "upper_left_line" ] } def construct(self): self.setup_network_mob() self.setup_needed_patterns() self.setup_added_patterns() self.show_nine() self.show_eight() self.show_four() self.show_second_to_last_layer() self.show_upper_loop_activation() self.show_what_learning_is_required() def setup_needed_patterns(self): prefixes = self.prefixes vects = [ np.array(Image.open( get_full_image_path("handwritten_" + p), ))[:,:,0].flatten()/255.0 for p in prefixes ] mobjects = map(MNistMobject, vects) for mob in mobjects: image = mob[1] self.make_transparent(image) for prefix, mob in zip(prefixes, mobjects): setattr(self, prefix, mob) def setup_added_patterns(self): image_map = get_organized_images() two, three, five = mobs = [ MNistMobject(image_map[n][0]) for n in 2, 3, 5 ] self.added_patterns = VGroup() for mob in mobs: for i, j in it.product([0, 14], [0, 14]): pattern = mob.deepcopy() pa = pattern[1].pixel_array temp = np.array(pa[i:i+14,j:j+14,:], dtype = 'uint8') pa[:,:] = 0 pa[i:i+14,j:j+14,:] = temp self.make_transparent(pattern[1]) pattern[1].highlight(random_bright_color()) self.added_patterns.add(pattern) self.image_map = image_map def show_nine(self): nine = self.nine upper_loop = self.upper_loop right_line = self.right_line equation = self.get_equation(nine, upper_loop, right_line) equation.to_edge(UP) equation.shift(LEFT) parts = [upper_loop[1], right_line[1]] for mob, color in zip(parts, [YELLOW, RED]): mob.highlight(color) mob.save_state() mob.move_to(nine) right_line[1].pixel_array[:14,:,3] = 0 self.play(FadeIn(nine)) self.dither() self.play(*map(FadeIn, parts)) self.dither() self.play( Write(equation[1]), upper_loop[1].restore, FadeIn(upper_loop[0]) ) self.dither() self.play( Write(equation[3]), right_line[1].restore, FadeIn(right_line[0]), ) self.dither() self.nine_equation = equation def show_eight(self): eight = self.eight upper_loop = self.upper_loop.deepcopy() lower_loop = self.lower_loop lower_loop[1].highlight(GREEN) equation = self.get_equation(eight, upper_loop, lower_loop) equation.next_to(self.nine_equation, DOWN) lower_loop[1].save_state() lower_loop[1].move_to(eight[1]) self.play( FadeIn(eight), Write(equation[1]), ) self.play(ReplacementTransform( self.upper_loop.copy(), upper_loop )) self.dither() self.play(FadeIn(lower_loop[1])) self.play( Write(equation[3]), lower_loop[1].restore, FadeIn(lower_loop[0]), ) self.dither() self.eight_equation = equation def show_four(self): four = self.four upper_left_line = self.upper_left_line upper_left_line[1].highlight(BLUE) horizontal_line = self.horizontal_line horizontal_line[1].highlight(MAROON_B) right_line = self.right_line.deepcopy() equation = self.get_equation(four, right_line, upper_left_line, horizontal_line) equation.next_to( self.eight_equation, DOWN, aligned_edge = LEFT ) self.play( FadeIn(four), Write(equation[1]) ) self.play(ReplacementTransform( self.right_line.copy(), right_line )) self.play(LaggedStart( FadeIn, VGroup(*equation[3:]) )) self.dither(2) self.four_equation = equation def show_second_to_last_layer(self): everything = VGroup(*it.chain( self.nine_equation, self.eight_equation, self.four_equation, )) patterns = VGroup( self.upper_loop, self.lower_loop, self.right_line, self.upper_left_line, self.horizontal_line, *self.added_patterns[:11] ) for pattern in patterns: pattern.add_to_back( pattern[1].copy().highlight(BLACK, alpha = 1) ) everything.remove(*patterns) network_mob = self.network_mob layer = network_mob.layers[-2] patterns.generate_target() for pattern, neuron in zip(patterns.target, layer.neurons): pattern.scale_to_fit_height(neuron.get_height()) pattern.next_to(neuron, RIGHT, SMALL_BUFF) for pattern in patterns[5:]: pattern.fade(1) self.play(*map(FadeOut, everything)) self.play( FadeIn( network_mob, submobject_mode = "lagged_start", run_time = 3, ), MoveToTarget(patterns) ) self.dither(2) self.patterns = patterns def show_upper_loop_activation(self): neuron = self.network_mob.layers[-2].neurons[0] words = TextMobject("Upper loop neuron...mabye...") words.scale(0.8) words.next_to(neuron, UP) words.shift(RIGHT) rect = SurroundingRectangle(VGroup( neuron, self.patterns[0] )) nine = self.nine upper_loop = self.upper_loop.copy() upper_loop.remove(upper_loop[0]) upper_loop.replace(nine) nine.add(upper_loop) nine.to_corner(UP+LEFT) self.remove_random_edges(0.7) self.network.get_activation_of_all_layers = lambda v : [ np.zeros(784), sigmoid(6*(np.random.random(16)-0.5)), np.array([1, 0, 1] + 13*[0]), np.array(9*[0] + [1]) ] self.play(FadeIn(nine)) self.add_foreground_mobject(self.patterns) self.play( ShowCreation(rect), Write(words) ) self.feed_forward(np.random.random(784)) self.dither(2) def show_what_learning_is_required(self): edge_group = self.network_mob.edge_groups[-1].copy() edge_group.set_stroke(YELLOW, 4) for x in range(3): self.play(LaggedStart( ShowCreationThenDestruction, edge_group, run_time = 3 )) self.dither() ###### def get_equation(self, *mobs): equation = VGroup( mobs[0], TexMobject("=").scale(2), *list(it.chain(*[ [m, TexMobject("+").scale(2)] for m in mobs[1:-1] ])) + [mobs[-1]] ) equation.arrange_submobjects(RIGHT) return equation def make_transparent(self, image_mob): return make_transparent(image_mob) alpha_vect = np.array( image_mob.pixel_array[:,:,0], dtype = 'uint8' ) image_mob.highlight(WHITE) image_mob.pixel_array[:,:,3] = alpha_vect return image_mob class HowWouldYouRecognizeSubcomponent(TeacherStudentsScene): def construct(self): self.student_says( "Okay, but recognizing loops \\\\", "is just as hard!", target_mode = "sassy" ) self.play( self.teacher.change, "guilty" ) self.dither() class BreakUpMicroPatterns(BreakUpMacroPatterns): CONFIG = { "prefixes" : [ "loop", "loop_edge1", "loop_edge2", "loop_edge3", "loop_edge4", "loop_edge5", "right_line", "right_line_edge1", "right_line_edge2", "right_line_edge3", ] } def construct(self): self.setup_network_mob() self.setup_needed_patterns() self.break_down_loop() self.break_down_long_line() def break_down_loop(self): loop = self.loop loop[0].highlight(WHITE) edges = Group(*[ getattr(self, "loop_edge%d"%d) for d in range(1, 6) ]) colors = color_gradient([BLUE, YELLOW, RED], 5) for edge, color in zip(edges, colors): for mob in edge: mob.highlight(color) loop.generate_target() edges.generate_target() for edge in edges: edge[0].set_stroke(width = 0) edge.save_state() edge[1].set_opacity(0) equation = self.get_equation(loop.target, *edges.target) equation.scale_to_fit_width(2*SPACE_WIDTH - 1) equation.to_edge(UP) symbols = VGroup(*equation[1::2]) randy = Randolph() randy.to_corner(DOWN+LEFT) self.add(randy) self.play( FadeIn(loop), randy.change, "pondering", loop ) self.play(Blink(randy)) self.dither() self.play(LaggedStart( ApplyMethod, edges, lambda e : (e.restore,), run_time = 4 )) self.dither() self.play( MoveToTarget(loop, run_time = 2), MoveToTarget(edges, run_time = 2), Write(symbols), randy.change, "happy", equation, ) self.dither() self.loop_equation = equation self.randy = randy def break_down_long_line(self): randy = self.randy line = self.right_line line[0].highlight(WHITE) edges = Group(*[ getattr(self, "right_line_edge%d"%d) for d in range(1, 4) ]) colors = Color(MAROON_B).range_to(PURPLE, 3) for edge, color in zip(edges, colors): for mob in edge: mob.highlight(color) equation = self.get_equation(line, *edges) equation.scale_to_fit_height(self.loop_equation.get_height()) equation.next_to( self.loop_equation, DOWN, MED_LARGE_BUFF, LEFT ) image_map = get_organized_images() digits = VGroup(*[ MNistMobject(image_map[n][1]) for n in 1, 4, 7 ]) digits.arrange_submobjects(RIGHT) digits.next_to(randy, RIGHT) self.revert_to_original_skipping_status() self.play( FadeIn(line), randy.change, "hesitant", line ) self.play(Blink(randy)) self.play(LaggedStart(FadeIn, digits)) self.dither() self.play( LaggedStart(FadeIn, Group(*equation[1:])), randy.change, "pondering", equation ) self.dither(3) class SecondLayerIsLittleEdgeLayer(IntroduceEachLayer): CONFIG = { "camera_config" : { "background_alpha" : 255, }, "network_mob_config" : { "layer_to_layer_buff" : 2, "edge_propogation_color" : YELLOW, } } def construct(self): self.setup_network_mob() self.setup_activations_and_nines() self.describe_second_layer() self.show_propogation() self.ask_question() def setup_network_mob(self): self.network_mob.scale(0.7) self.network_mob.to_edge(DOWN) self.remove_random_edges(0.7) def setup_activations_and_nines(self): layers = self.network_mob.layers nine_im, loop_im, line_im = images = [ Image.open(get_full_image_path("handwritten_%s"%s)) for s in "nine", "upper_loop", "right_line" ] nine_array, loop_array, line_array = [ np.array(im)[:,:,0]/255.0 for im in images ] self.nine = MNistMobject(nine_array.flatten()) self.nine.scale_to_fit_height(1.5) self.nine[0].highlight(WHITE) make_transparent(self.nine[1]) self.nine.next_to(layers[0].neurons, UP) self.activations = self.network.get_activation_of_all_layers( nine_array.flatten() ) self.activations[-2] = np.array([1, 0, 1] + 13*[0]) self.edge_colored_nine = Group() nine_pa = self.nine[1].pixel_array n, k = 6, 4 colors = color_gradient([BLUE, YELLOW, RED, MAROON_B, GREEN], 10) for i, j in it.product(range(n), range(k)): mob = ImageMobject(np.zeros((28, 28, 4), dtype = 'uint8')) mob.replace(self.nine[1]) pa = mob.pixel_array color = colors[(k*i + j)%(len(colors))] rgb = (255*color_to_rgb(color)).astype('uint8') pa[:,:,:3] = rgb i0, i1 = 1+(28/n)*i, 1+(28/n)*(i+1) j0, j1 = (28/k)*j, (28/k)*(j+1) pa[i0:i1,j0:j1,3] = nine_pa[i0:i1,j0:j1,3] self.edge_colored_nine.add(mob) self.edge_colored_nine.next_to(layers[1], UP) loop, line = [ ImageMobject(layer_to_image_array(array.flatten())) for array in loop_array, line_array ] for mob, color in (loop, YELLOW), (line, RED): make_transparent(mob) mob.highlight(color) mob.replace(self.nine[1]) line.pixel_array[:14,:,:] = 0 self.pattern_colored_nine = Group(loop, line) self.pattern_colored_nine.next_to(layers[2], UP) for mob in self.edge_colored_nine, self.pattern_colored_nine: mob.align_to(self.nine[1], UP) def describe_second_layer(self): layer = self.network_mob.layers[1] rect = SurroundingRectangle(layer) words = TextMobject("``Little edge'' layer?") words.next_to(rect, UP, MED_LARGE_BUFF) words.highlight(YELLOW) self.play( ShowCreation(rect), Write(words, run_time = 2) ) self.dither() self.play(*map(FadeOut, [rect, words])) def show_propogation(self): nine = self.nine edge_colored_nine = self.edge_colored_nine pattern_colored_nine = self.pattern_colored_nine activations = self.activations network_mob = self.network_mob layers = network_mob.layers edge_groups = network_mob.edge_groups.copy() edge_groups.set_stroke(YELLOW, 4) v_nine = PixelsAsSquares(nine[1]) neurons = VGroup() for pixel in v_nine: neuron = Circle( radius = pixel.get_width()/2, stroke_color = WHITE, stroke_width = 1, fill_color = WHITE, fill_opacity = pixel.get_fill_opacity(), ) neuron.rotate(3*np.pi/4) neuron.move_to(pixel) neurons.add(neuron) neurons.scale_to_fit_height(2) neurons.space_out_submobjects(1.2) neurons.next_to(network_mob, LEFT) self.set_neurons_target(neurons, layers[0]) pattern_colored_nine.save_state() pattern_colored_nine.move_to(edge_colored_nine) edge_colored_nine.save_state() edge_colored_nine.move_to(nine[1]) for mob in edge_colored_nine, pattern_colored_nine: for submob in mob: submob.set_opacity(0) active_layers = [ network_mob.get_active_layer(i, a) for i, a in enumerate(activations) ] def activate_layer(i): self.play( ShowCreationThenDestruction( edge_groups[i-1], run_time = 2, submobject_mode = "lagged_start" ), FadeIn(active_layers[i]) ) self.play(FadeIn(nine)) self.play(ReplacementTransform(v_nine, neurons)) self.play(MoveToTarget( neurons, remover = True, submobject_mode = "lagged_start", run_time = 2 )) activate_layer(1) self.play(edge_colored_nine.restore) self.separate_parts(edge_colored_nine) self.dither() activate_layer(2) self.play(pattern_colored_nine.restore) self.separate_parts(pattern_colored_nine) activate_layer(3) self.dither(2) def ask_question(self): question = TextMobject( "Does the network \\\\ actually do this?" ) question.to_edge(LEFT) later = TextMobject("We'll get back \\\\ to this") later.to_corner(UP+LEFT) later.highlight(BLUE) arrow = Arrow(later.get_bottom(), question.get_top()) arrow.highlight(BLUE) self.play(Write(question, run_time = 2)) self.dither() self.play( FadeIn(later), GrowFromPoint(arrow, arrow.get_start()) ) self.dither() ### def set_neurons_target(self, neurons, layer): neurons.generate_target() n = len(layer.neurons)/2 Transform( VGroup(*neurons.target[:n]), VGroup(*layer.neurons[:n]), ).update(1) Transform( VGroup(*neurons.target[-n:]), VGroup(*layer.neurons[-n:]), ).update(1) Transform( VGroup(*neurons.target[n:-n]), VectorizedPoint(layer.get_center()) ).update(1) def separate_parts(self, image_group): vects = compass_directions(len(image_group), UP) image_group.generate_target() for im, vect in zip(image_group.target, vects): im.shift(MED_SMALL_BUFF*vect) self.play(MoveToTarget( image_group, rate_func = there_and_back, submobject_mode = "lagged_start", run_time = 2, )) class EdgeDetection(Scene): CONFIG = { "camera_config" : {"background_alpha" : 255} } def construct(self): lion = ImageMobject("Lion") edges_array = get_edges(lion.pixel_array) edges = ImageMobject(edges_array) group = Group(lion, edges) group.scale_to_fit_height(4) group.arrange_submobjects(RIGHT) lion_copy = lion.copy() self.play(FadeIn(lion)) self.play(lion_copy.move_to, edges) self.play(Transform(lion_copy, edges, run_time = 3)) self.dither(2) class ManyTasksBreakDownLikeThis(TeacherStudentsScene): def construct(self): audio = self.get_wave_form() audio_label = TextMobject("Raw audio") letters = TextMobject(" ".join("recognition")) syllables = TextMobject("$\\cdot$".join([ "re", "cog", "ni", "tion" ])) word = TextMobject( "re", "cognition", arg_separator = "" ) word[1].highlight(BLUE) arrows = VGroup() def get_arrow(): arrow = Arrow(ORIGIN, RIGHT, color = BLUE) arrows.add(arrow) return arrow sequence = VGroup( audio, get_arrow(), letters, get_arrow(), syllables, get_arrow(), word ) sequence.arrange_submobjects(RIGHT) sequence.scale_to_fit_width(2*SPACE_WIDTH - 1) sequence.to_edge(UP) audio_label.next_to(audio, DOWN) VGroup(audio, audio_label).highlight(YELLOW) audio.save_state() self.teacher_says( "Many", "recognition", "tasks\\\\", "break down like this" ) self.change_student_modes(*["pondering"]*3) self.dither() content = self.teacher.bubble.content pre_word = content[1] content.remove(pre_word) audio.move_to(pre_word) self.play( self.teacher.bubble.content.fade, 1, ShowCreation(audio), pre_word.shift, MED_SMALL_BUFF, DOWN ) self.dither(2) self.play( RemovePiCreatureBubble(self.teacher), audio.restore, FadeIn(audio_label), *[ ReplacementTransform( m1, m2 ) for m1, m2 in zip(pre_word, letters) ] ) self.play( GrowFromPoint(arrows[0], arrows[0].get_start()), ) self.dither() self.play( GrowFromPoint(arrows[1], arrows[1].get_start()), LaggedStart(FadeIn, syllables, run_time = 1) ) self.dither() self.play( GrowFromPoint(arrows[2], arrows[2].get_start()), LaggedStart(FadeIn, word, run_time = 1) ) self.dither() def get_wave_form(self): func = lambda x : abs(sum([ (1./n)*np.sin((n+3)*x) for n in range(1, 5) ])) result = VGroup(*[ Line(func(x)*DOWN, func(x)*UP) for x in np.arange(0, 4, 0.1) ]) result.set_stroke(width = 2) result.arrange_submobjects(RIGHT, buff = MED_SMALL_BUFF) result.scale_to_fit_height(1) return result