import sys import os.path import cv2 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from big_ol_pile_of_manim_imports 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_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" : YELLOW, "edge_propogation_time" : 1, "max_shown_neurons" : 16, "brace_for_large_layers" : True, "average_shown_activation_of_large_layer" : True, "include_output_labels" : False, } 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(RIGHT, buff = self.layer_to_layer_buff) self.layers = layers self.add(self.layers) if self.include_output_labels: self.add_output_labels() 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, fill_color = self.neuron_fill_color, fill_opacity = 0, ) for x in range(n_neurons) ]) neurons.arrange( 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 = self.get_edge(n1, n2) 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_edge(self, neuron1, neuron2): return Line( neuron1.get_center(), neuron2.get_center(), buff = self.neuron_radius, stroke_color = self.edge_color, stroke_width = self.edge_stroke_width, ) def get_active_layer(self, layer_index, activation_vector): layer = self.layers[layer_index].deepcopy() self.activate_layer(layer, activation_vector) return layer def activate_layer(self, layer, activation_vector): 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 activate_layers(self, input_vector): activations = self.neural_network.get_activation_of_all_layers(input_vector) for activation, layer in zip(activations, self.layers): self.activate_layer(layer, activation) 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( edge_group_copy, run_time = self.edge_propogation_time, lag_ratio = 0.5 )] def add_output_labels(self): self.output_labels = VGroup() for n, neuron in enumerate(self.layers[-1].neurons): label = TexMobject(str(n)) label.set_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 MNistNetworkMobject(NetworkMobject): CONFIG = { "neuron_to_neuron_buff" : SMALL_BUFF, "layer_to_layer_buff" : 1.5, "edge_stroke_width" : 1, "include_output_labels" : True, } def __init__(self, **kwargs): network = get_pretrained_network() NetworkMobject.__init__(self, network, **kwargs) class NetworkScene(Scene): CONFIG = { "layer_sizes" : [8, 6, 6, 4], "network_mob_config" : {}, } def setup(self): self.add_network() def add_network(self): self.network = Network(sizes = self.layer_sizes) self.network_mob = NetworkMobject( self.network, **self.network_mob_config ) 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.set_color(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(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.set_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.set_height(0.8) real_three.next_to(arrow, RIGHT) self.play( FadeIn(three_mob[0]), OldLaggedStart(FadeIn, three_mob[1]) ) self.wait() self.play( OldLaggedStart( DrawBorderThenFill, three_mob_copy, run_time = 3, stroke_color = WHITE, remover = True, ), randy.change, "sassy", *it.chain( list(map(GrowFromCenter, braces)), list(map(FadeIn, brace_labels)) ) ) self.wait() self.play( ShowCreation(bubble), MoveToTarget(three_mob), FadeOut(braces), FadeOut(brace_labels), randy.change, "pondering" ) self.play( ShowCreation(arrow), Write(real_three) ) self.wait() 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(DOWN) alt_threes.set_height(FRAME_HEIGHT - 2) alt_threes.to_edge(RIGHT) for alt_three in alt_threes: self.add(alt_three) self.wait(0.5) self.play( randy.change, "plain", *list(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.wait() 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, lag_ratio = 0.5 )) self.play( Animation(randy), moving_three.replace, randy.eyes[1], moving_three.scale_in_place, 0.7, run_time = 2, lag_ratio = 0.5, ) 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.set_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.wait() 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.wait() for mob in alt_mobs[1:]: self.play(Transform(three, mob)) self.wait() ###### 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 BrainAndHow(Scene): def construct(self): brain = SVGMobject(file_name = "brain") brain.set_height(2) brain.set_fill(LIGHT_GREY) brain_outline = brain.copy() brain_outline.set_fill(opacity = 0) brain_outline.set_stroke(BLUE_B, 3) how = TextMobject("How?!?") how.scale(2) how.next_to(brain, UP) self.add(brain) self.play(Write(how)) for x in range(2): self.play( ShowPassingFlash( brain_outline, time_width = 0.5, run_time = 2 ) ) self.wait() 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(lambda p : np.dot(p, DOWN+RIGHT)) three.set_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_places = 1 ) num.set_stroke(width = 1) color = rgba_to_color(1 - (rgb + 0.2)/1.2) num.set_color(color) num.set_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(DOWN) choices.set_height(FRAME_HEIGHT - 1) choices.next_to(arrow, RIGHT) self.play( OldLaggedStart(DrawBorderThenFill, three), ShowCreation(three_rect) ) self.play(Write(numbers)) self.play( ShowCreation(arrow), OldLaggedStart(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.wait(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.wait(2) class LayOutPlan(TeacherStudentsScene, NetworkScene): def setup(self): TeacherStudentsScene.setup(self) NetworkScene.setup(self) self.remove(self.network_mob) def construct(self): self.force_skipping() self.show_words() self.show_network() self.show_math() self.ask_about_layers() self.show_learning() self.show_videos() def show_words(self): words = VGroup( TextMobject("Machine", "learning").set_color(GREEN), TextMobject("Neural network").set_color(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, lag_ratio = 0 ), self.teacher.change, "plain", run_time = 1 ) self.play(ShowCreation( network_mob.edge_groups, lag_ratio = 0.5, run_time = 2, rate_func=linear, )) 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.set_color_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.wait() self.equation = equation def ask_about_layers(self): self.student_says( "Why the layers?", student_index = 2, bubble_kwargs = {"direction" : LEFT} ) self.wait() 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].set_color(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.wait() self.learning_word = word def show_videos(self): network_mob = self.network_mob learning = self.learning_word structure = TextMobject("Structure") structure.set_color(YELLOW) videos = VGroup(*[ VideoIcon().set_fill(RED) for x in range(2) ]) videos.set_height(1.5) videos.arrange(RIGHT, buff = LARGE_BUFF) videos.next_to(self.students, UP, LARGE_BUFF) network_mob.generate_target() network_mob.target.set_height(0.8*videos[0].get_height()) network_mob.target.move_to(videos[0]) learning.generate_target() learning.target.next_to(videos[1], UP) structure.next_to(videos[0], UP) structure.shift(0.5*SMALL_BUFF*UP) self.revert_to_original_skipping_status() self.play( MoveToTarget(network_mob), MoveToTarget(learning) ) self.play( DrawBorderThenFill(videos[0]), FadeIn(structure), self.get_student_changes(*["pondering"]*3) ) self.wait() self.play(DrawBorderThenFill(videos[1])) self.wait() 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) image.shift_onto_screen() image_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(image_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.reset_display(rect, image, image_rect) def reset_display(self, answer_rect, image, image_rect): self.play(FadeOut(answer_rect)) self.play( FadeOut(image), FadeOut(image_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.set_width(FRAME_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.set_color(YELLOW) self.play( Write(examples[0], run_time = 2), morty.change, "raise_right_hand" ) self.wait() self.play( examples[0].shift, MED_LARGE_BUFF*UP, FadeIn(examples[1], lag_ratio = 0.5), ) self.wait() self.play( examples.shift, UP, FadeIn(maybe_words), morty.change, "maybe" ) self.wait(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.wait(2) self.play(Write(subtitle, run_time = 2)) self.wait(2) class NotPerfectAddOn(Scene): def construct(self): words = TextMobject("Not perfect!") words.scale(1.5) arrow = Arrow(UP+RIGHT, DOWN+LEFT, color = RED) words.set_color(RED) arrow.to_corner(DOWN+LEFT) words.next_to(arrow, UP+RIGHT) self.play( Write(words), ShowCreation(arrow), run_time = 1 ) self.wait(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.wait(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).set_color(BLUE) VGroup(q2, a2).set_color(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.set_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.wait() self.play( Write(q1, run_time = 1), ShowCreation(a1), name[0].set_color, q1.get_color(), ) self.play( Write(q2, run_time = 1), ShowCreation(a2), name[1].set_color, q2.get_color() ) self.wait(2) self.play(*list(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.set_color(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.set_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.wait() self.play( neuron.set_fill, None, 0.2, num.restore ) self.wait() 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.wait() 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.set_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.wait() self.play( ReplacementTransform(image_mob, neurons), FadeOut(rect), FadeIn(braces), FadeIn(labels), ) self.wait() self.play( ReplacementTransform(labels[0].copy(), equation[0]), Write(equation[1]), ReplacementTransform(labels[1].copy(), equation[2]), Write(equation[3]), Write(equation[4]), ) self.wait() 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_places = 1) num.set_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.set_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(OldLaggedStart(FadeIn, numbers)) self.play( MoveToTarget(example_neuron), MoveToTarget(example_num) ) self.wait() curr_opacity = example_neuron.get_fill_opacity() for num in 0.3, 0.01, 1.0, curr_opacity: change_activation(num) self.wait() rect = SurroundingRectangle(example_num, color = YELLOW) activation = TextMobject("``Activation''") activation.next_to(example_neuron, RIGHT) activation.set_color(rect.get_color()) self.play(ShowCreation(rect)) self.play(Write(activation, run_time = 1)) self.wait() change_activation(1.0) self.wait() change_activation(0.2) self.wait() 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] layer.save_state() layer.rotate(np.pi/2) layer.center() layer.brace_label.rotate_in_place(-np.pi/2) n = network_mob.max_shown_neurons/2 rows = VGroup(*[ VGroup(*neurons[28*i:28*(i+1)]) for i in range(28) ]) self.play( FadeOut(self.braces), FadeOut(self.brace_labels), FadeOut(VGroup(*self.num_pixels_equation[:-1])) ) self.play(rows.space_out_submobjects, 1.2) self.play( rows.arrange, RIGHT, buff = SMALL_BUFF, path_arc = np.pi/2, run_time = 2 ) 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:]), ), ) self.play( ReplacementTransform( self.num_pixels_equation[-1], layer.brace_label ), FadeIn(layer.brace), ) self.play(layer.restore, FadeIn(self.corner_image)) self.wait() for edge_group, layer in zip(network_mob.edge_groups, network_mob.layers[1:]): self.play( OldLaggedStart(FadeIn, layer, run_time = 1), ShowCreation(edge_group), ) self.wait() 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(OldLaggedStart(FadeIn, labels)) self.wait() self.play( MoveToTarget(neuron), MoveToTarget(label), ) self.play(FadeIn(activation)) for num in 0.5, 0.38, 0.97: change_activation(num) self.wait() self.play( neuron.restore, neuron.set_fill, None, 1, label.restore, FadeOut(activation), FadeOut(rect), ) self.wait() 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.set_color(YELLOW) q_marks = VGroup() for layer in hidden_layers: for neuron in layer.neurons: q_mark = TextMobject("?") q_mark.set_height(0.8*neuron.get_height()) q_mark.move_to(neuron) q_marks.add(q_mark) q_marks.set_color_by_gradient(BLUE, YELLOW) q_mark = TextMobject("?").scale(4) q_mark.move_to(hidden_layers) q_mark.set_color(YELLOW) q_marks.add(q_mark) self.play( ShowCreation(rect), Write(name) ) self.wait() self.play(Write(q_marks)) self.wait() 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 DiscussChoiceForHiddenLayers(TeacherStudentsScene): def construct(self): network_mob = MNistNetworkMobject( layer_to_layer_buff = 2.5, neuron_stroke_color = WHITE, ) network_mob.set_height(4) network_mob.to_edge(UP, buff = LARGE_BUFF) layers = VGroup(*network_mob.layers[1:3]) rects = VGroup(*list(map(SurroundingRectangle, layers))) self.add(network_mob) two_words = TextMobject("2 hidden layers") two_words.set_color(YELLOW) sixteen_words = TextMobject("16 neurons each") sixteen_words.set_color(MAROON_B) for words in two_words, sixteen_words: words.next_to(rects, UP) neurons_anim = OldLaggedStart( Indicate, VGroup(*it.chain(*[layer.neurons for layer in layers])), rate_func = there_and_back, scale_factor = 2, color = MAROON_B, ) self.play( ShowCreation(rects), Write(two_words, run_time = 1), self.teacher.change, "raise_right_hand", ) self.wait() self.play( FadeOut(rects), ReplacementTransform(two_words, sixteen_words), neurons_anim ) self.wait() self.play(self.teacher.change, "shruggie") self.change_student_modes("erm", "confused", "sassy") self.wait() self.student_says( "Why 2 \\\\ layers?", student_index = 1, bubble_kwargs = {"direction" : RIGHT}, run_time = 1, target_mode = "raise_left_hand", ) self.play(self.teacher.change, "happy") self.wait() self.student_says( "Why 16?", student_index = 0, run_time = 1, ) self.play(neurons_anim, run_time = 3) self.play( self.teacher.change, "shruggie", RemovePiCreatureBubble(self.students[0]), ) self.wait() 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, lag_ratio = 0.5, 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.wait() self.student_says( "How does \\\\ training work?", student_index = 2, run_time = 1 ) self.wait(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 = list(map(SurroundingRectangle, neuron_groups[1:3])) self.play( Write(question, run_time = 1), OldLaggedStart( GrowFromPoint, arrows, lambda a : (a, a.get_start()), run_time = 2 ) ) self.wait() self.play(*list(map(ShowCreation, rects))) self.wait() class BreakUpMacroPatterns(IntroduceEachLayer): CONFIG = { "camera_config" : {"background_opacity" : 1}, "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_raster_image_path("handwritten_" + p), ))[:,:,0].flatten()/255.0 for p in prefixes ] mobjects = list(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].set_color(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.set_color(color) mob.save_state() mob.move_to(nine) right_line[1].pixel_array[:14,:,3] = 0 self.play(FadeIn(nine)) self.wait() self.play(*list(map(FadeIn, parts))) self.wait() self.play( Write(equation[1]), upper_loop[1].restore, FadeIn(upper_loop[0]) ) self.wait() self.play( Write(equation[3]), right_line[1].restore, FadeIn(right_line[0]), ) self.wait() 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].set_color(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.wait() self.play(FadeIn(lower_loop[1])) self.play( Write(equation[3]), lower_loop[1].restore, FadeIn(lower_loop[0]), ) self.wait() self.eight_equation = equation def show_four(self): four = self.four upper_left_line = self.upper_left_line upper_left_line[1].set_color(BLUE) horizontal_line = self.horizontal_line horizontal_line[1].set_color(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(OldLaggedStart( FadeIn, VGroup(*equation[3:]) )) self.wait(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().set_color(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.set_height(neuron.get_height()) pattern.next_to(neuron, RIGHT, SMALL_BUFF) for pattern in patterns[5:]: pattern.fade(1) self.play(*list(map(FadeOut, everything))) self.play( FadeIn( network_mob, lag_ratio = 0.5, run_time = 3, ), MoveToTarget(patterns) ) self.wait(2) self.patterns = patterns def show_upper_loop_activation(self): neuron = self.network_mob.layers[-2].neurons[0] words = TextMobject("Upper loop neuron...maybe...") 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.play( ShowCreation(rect), Write(words) ) self.add_foreground_mobject(self.patterns) self.feed_forward(np.random.random(784)) self.wait(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(OldLaggedStart( ShowCreationThenDestruction, edge_group, run_time = 3 )) self.wait() ###### 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(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.set_color(WHITE) image_mob.pixel_array[:,:,3] = alpha_vect return image_mob class GenerallyLoopyPattern(Scene): def construct(self): image_map = get_organized_images() images = list(map(MNistMobject, it.chain( image_map[8], image_map[9], ))) random.shuffle(images) for image in images: image.to_corner(DOWN+RIGHT) self.add(image) self.wait(0.2) self.remove(image) 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.wait() 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].set_color(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.set_color(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.set_width(FRAME_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.wait() self.play(OldLaggedStart( ApplyMethod, edges, lambda e : (e.restore,), run_time = 4 )) self.wait() self.play( MoveToTarget(loop, run_time = 2), MoveToTarget(edges, run_time = 2), Write(symbols), randy.change, "happy", equation, ) self.wait() self.loop_equation = equation self.randy = randy def break_down_long_line(self): randy = self.randy line = self.right_line line[0].set_color(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.set_color(color) equation = self.get_equation(line, *edges) equation.set_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(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(OldLaggedStart(FadeIn, digits)) self.wait() self.play( OldLaggedStart(FadeIn, Group(*equation[1:])), randy.change, "pondering", equation ) self.wait(3) class SecondLayerIsLittleEdgeLayer(IntroduceEachLayer): CONFIG = { "camera_config" : { "background_opacity" : 1, }, "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_raster_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.set_height(1.5) self.nine[0].set_color(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(list(range(n)), list(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.set_color(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.set_color(YELLOW) self.play( ShowCreation(rect), Write(words, run_time = 2) ) self.wait() self.play(*list(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.set_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, lag_ratio = 0.5 ), FadeIn(active_layers[i]) ) self.play(FadeIn(nine)) self.play(ReplacementTransform(v_nine, neurons)) self.play(MoveToTarget( neurons, remover = True, lag_ratio = 0.5, run_time = 2 )) activate_layer(1) self.play(edge_colored_nine.restore) self.separate_parts(edge_colored_nine) self.wait() activate_layer(2) self.play(pattern_colored_nine.restore) self.separate_parts(pattern_colored_nine) activate_layer(3) self.wait(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.set_color(BLUE) arrow = Arrow(later.get_bottom(), question.get_top()) arrow.set_color(BLUE) self.play(Write(question, run_time = 2)) self.wait() self.play( FadeIn(later), GrowFromPoint(arrow, arrow.get_start()) ) self.wait() ### 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, lag_ratio = 0.5, run_time = 2, )) class EdgeDetection(Scene): CONFIG = { "camera_config" : {"background_opacity" : 1} } def construct(self): lion = ImageMobject("Lion") edges_array = get_edges(lion.pixel_array) edges = ImageMobject(edges_array) group = Group(lion, edges) group.set_height(4) group.arrange(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.wait(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].set_color(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(RIGHT) sequence.set_width(FRAME_WIDTH - 1) sequence.to_edge(UP) audio_label.next_to(audio, DOWN) VGroup(audio, audio_label).set_color(YELLOW) audio.save_state() self.teacher_says( "Many", "recognition", "tasks\\\\", "break down like this" ) self.change_student_modes(*["pondering"]*3) self.wait() 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.wait(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.wait() self.play( GrowFromPoint(arrows[1], arrows[1].get_start()), OldLaggedStart(FadeIn, syllables, run_time = 1) ) self.wait() self.play( GrowFromPoint(arrows[2], arrows[2].get_start()), OldLaggedStart(FadeIn, word, run_time = 1) ) self.wait() 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(RIGHT, buff = MED_SMALL_BUFF) result.set_height(1) return result class AskAboutWhatEdgesAreDoing(IntroduceEachLayer): CONFIG = { "network_mob_config" : { "layer_to_layer_buff" : 2, } } def construct(self): self.add_question() self.show_propogation() def add_question(self): self.network_mob.scale(0.8) self.network_mob.to_edge(DOWN) edge_groups = self.network_mob.edge_groups self.remove_random_edges(0.7) question = TextMobject( "What are these connections actually doing?" ) question.to_edge(UP) question.shift(RIGHT) arrows = VGroup(*[ Arrow( question.get_bottom(), edge_group.get_top() ) for edge_group in edge_groups ]) self.add(question, arrows) def show_propogation(self): in_vect = get_organized_images()[6][3] image = MNistMobject(in_vect) image.next_to(self.network_mob, LEFT, MED_SMALL_BUFF, UP) self.add(image) self.feed_forward(in_vect) self.wait() class IntroduceWeights(IntroduceEachLayer): CONFIG = { "weights_color" : GREEN, "negative_weights_color" : RED, } def construct(self): self.zoom_in_on_one_neuron() self.show_desired_pixel_region() self.ask_about_parameters() self.show_weights() self.show_weighted_sum() self.organize_weights_as_grid() self.make_most_weights_0() self.add_negative_weights_around_the_edge() def zoom_in_on_one_neuron(self): self.network_mob.to_edge(LEFT) layers = self.network_mob.layers edge_groups = self.network_mob.edge_groups neuron = layers[1].neurons[7].deepcopy() self.play( FadeOut(edge_groups), FadeOut(VGroup(*layers[1:])), FadeOut(self.network_mob.output_labels), Animation(neuron), neuron.edges_in.set_stroke, None, 2, lag_ratio = 0.5, run_time = 2 ) self.neuron = neuron def show_desired_pixel_region(self): neuron = self.neuron d = 28 pixels = PixelsAsSquares(ImageMobject( np.zeros((d, d, 4)) )) pixels.set_stroke(width = 0.5) pixels.set_fill(WHITE, 0) pixels.set_height(4) pixels.next_to(neuron, RIGHT, LARGE_BUFF) rect = SurroundingRectangle(pixels, color = BLUE) pixels_to_detect = self.get_pixels_to_detect(pixels) self.play( FadeIn(rect), ShowCreation( pixels, lag_ratio = 0.5, run_time = 2, ) ) self.play( pixels_to_detect.set_fill, WHITE, 1, lag_ratio = 0.5, run_time = 2 ) self.wait(2) self.pixels = pixels self.pixels_to_detect = pixels_to_detect self.pixels_group = VGroup(rect, pixels) def ask_about_parameters(self): pixels = self.pixels pixels_group = self.pixels_group neuron = self.neuron question = TextMobject("What", "parameters", "should exist?") parameter_word = question.get_part_by_tex("parameters") parameter_word.set_color(self.weights_color) question.move_to(neuron.edges_in.get_top(), LEFT) arrow = Arrow( parameter_word.get_bottom(), neuron.edges_in[0].get_center(), color = self.weights_color ) p_labels = VGroup(*[ TexMobject("p_%d\\!:"%(i+1)).set_color(self.weights_color) for i in range(8) ] + [TexMobject("\\vdots")]) p_labels.arrange(DOWN, aligned_edge = LEFT) p_labels.next_to(parameter_word, DOWN, LARGE_BUFF) p_labels[-1].shift(SMALL_BUFF*RIGHT) def get_alpha_func(i, start = 0): # m = int(5*np.sin(2*np.pi*i/128.)) m = random.randint(1, 10) return lambda a : start + (1-2*start)*np.sin(np.pi*a*m)**2 decimals = VGroup() changing_decimals = [] for i, p_label in enumerate(p_labels[:-1]): decimal = DecimalNumber(0) decimal.next_to(p_label, RIGHT, MED_SMALL_BUFF) decimals.add(decimal) changing_decimals.append(ChangingDecimal( decimal, get_alpha_func(i + 5) )) for i, pixel in enumerate(pixels): pixel.func = get_alpha_func(i, pixel.get_fill_opacity()) pixel_updates = [ UpdateFromAlphaFunc( pixel, lambda p, a : p.set_fill(opacity = p.func(a)) ) for pixel in pixels ] self.play( Write(question, run_time = 2), GrowFromPoint(arrow, arrow.get_start()), pixels_group.set_height, 3, pixels_group.to_edge, RIGHT, OldLaggedStart(FadeIn, p_labels), OldLaggedStart(FadeIn, decimals), ) self.wait() self.play( *changing_decimals + pixel_updates, run_time = 5, rate_func=linear ) self.question = question self.weight_arrow = arrow self.p_labels = p_labels self.decimals = decimals def show_weights(self): p_labels = self.p_labels decimals = self.decimals arrow = self.weight_arrow question = self.question neuron = self.neuron edges = neuron.edges_in parameter_word = question.get_part_by_tex("parameters") question.remove(parameter_word) weights_word = TextMobject("Weights", "")[0] weights_word.set_color(self.weights_color) weights_word.move_to(parameter_word) w_labels = VGroup() for p_label in p_labels: w_label = TexMobject( p_label.get_tex_string().replace("p", "w") ) w_label.set_color(self.weights_color) w_label.move_to(p_label) w_labels.add(w_label) edges.generate_target() random_numbers = 1.5*np.random.random(len(edges))-0.5 self.make_edges_weighted(edges.target, random_numbers) def get_alpha_func(r): return lambda a : (4*r)*a self.play( FadeOut(question), ReplacementTransform(parameter_word, weights_word), ReplacementTransform(p_labels, w_labels) ) self.play( MoveToTarget(edges), *[ ChangingDecimal( decimal, get_alpha_func(r) ) for decimal, r in zip(decimals, random_numbers) ] ) self.play(OldLaggedStart( ApplyMethod, edges, lambda m : (m.rotate_in_place, np.pi/24), rate_func = wiggle, run_time = 2 )) self.wait() self.w_labels = w_labels self.weights_word = weights_word self.random_numbers = random_numbers def show_weighted_sum(self): weights_word = self.weights_word weight_arrow = self.weight_arrow w_labels = VGroup(*[ VGroup(*label[:-1]).copy() for label in self.w_labels ]) layer = self.network_mob.layers[0] a_vect = np.random.random(16) active_layer = self.network_mob.get_active_layer(0, a_vect) a_labels = VGroup(*[ TexMobject("a_%d"%d) for d in range(1, 5) ]) weighted_sum = VGroup(*it.chain(*[ [w, a, TexMobject("+")] for w, a in zip(w_labels, a_labels) ])) weighted_sum.add( TexMobject("\\cdots"), TexMobject("+"), TexMobject("w_n").set_color(self.weights_color), TexMobject("a_n") ) weighted_sum.arrange(RIGHT, buff = SMALL_BUFF) weighted_sum.to_edge(UP) self.play(Transform(layer, active_layer)) self.play( FadeOut(weights_word), FadeOut(weight_arrow), *[ ReplacementTransform(n.copy(), a) for n, a in zip(layer.neurons, a_labels) ] + [ ReplacementTransform(n.copy(), weighted_sum[-4]) for n in layer.neurons[4:-1] ] + [ ReplacementTransform( layer.neurons[-1].copy(), weighted_sum[-1] ) ] + [ Write(weighted_sum[i]) for i in list(range(2, 12, 3)) + [-4, -3] ], run_time = 1.5 ) self.wait() self.play(*[ ReplacementTransform(w1.copy(), w2) for w1, w2 in zip(self.w_labels, w_labels)[:4] ]+[ ReplacementTransform(w.copy(), weighted_sum[-4]) for w in self.w_labels[4:-1] ]+[ ReplacementTransform( self.w_labels[-1].copy(), weighted_sum[-2] ) ], run_time = 2) self.wait(2) self.weighted_sum = weighted_sum def organize_weights_as_grid(self): pixels = self.pixels w_labels = self.w_labels decimals = self.decimals weights = 2*np.sqrt(np.random.random(784))-1 weights[:8] = self.random_numbers[:8] weights[-8:] = self.random_numbers[-8:] weight_grid = PixelsFromVect(np.abs(weights)) weight_grid.replace(pixels) weight_grid.next_to(pixels, LEFT) for weight, pixel in zip(weights, weight_grid): if weight >= 0: color = self.weights_color else: color = self.negative_weights_color pixel.set_fill(color, opacity = abs(weight)) self.play(FadeOut(w_labels)) self.play( FadeIn( VGroup(*weight_grid[len(decimals):]), lag_ratio = 0.5, run_time = 3 ), *[ ReplacementTransform(decimal, pixel) for decimal, pixel in zip(decimals, weight_grid) ] ) self.wait() self.weight_grid = weight_grid def make_most_weights_0(self): weight_grid = self.weight_grid pixels = self.pixels pixels_group = self.pixels_group weight_grid.generate_target() for w, p in zip(weight_grid.target, pixels): if p.get_fill_opacity() > 0.1: w.set_fill(GREEN, 0.5) else: w.set_fill(BLACK, 0.5) w.set_stroke(WHITE, 0.5) digit = self.get_digit() digit.replace(pixels) self.play(MoveToTarget( weight_grid, run_time = 2, lag_ratio = 0.5 )) self.wait() self.play(Transform( pixels, digit, run_time = 2, lag_ratio = 0.5 )) self.wait() self.play(weight_grid.move_to, pixels) self.wait() self.play( ReplacementTransform( self.pixels_to_detect.copy(), self.weighted_sum, run_time = 3, lag_ratio = 0.5 ), Animation(weight_grid), ) self.wait() def add_negative_weights_around_the_edge(self): weight_grid = self.weight_grid pixels = self.pixels self.play(weight_grid.next_to, pixels, LEFT) self.play(*[ ApplyMethod( weight_grid[28*y + x].set_fill, self.negative_weights_color, 0.5 ) for y in (6, 10) for x in range(14-4, 14+4) ]) self.wait(2) self.play(weight_grid.move_to, pixels) self.wait(2) #### def get_digit(self): digit_vect = get_organized_images()[7][4] digit = PixelsFromVect(digit_vect) digit.set_stroke(width = 0.5) return digit def get_pixels_to_detect(self, pixels): d = int(np.sqrt(len(pixels))) return VGroup(*it.chain(*[ pixels[d*n + d/2 - 4 : d*n + d/2 + 4] for n in range(7, 10) ])) def get_surrounding_pixels_for_edge(self, pixels): d = int(np.sqrt(len(pixels))) return VGroup(*it.chain(*[ pixels[d*n + d/2 - 4 : d*n + d/2 + 4] for n in (6, 10) ])) def make_edges_weighted(self, edges, weights): for edge, r in zip(edges, weights): if r > 0: color = self.weights_color else: color = self.negative_weights_color edge.set_stroke(color, 6*abs(r)) class MotivateSquishing(Scene): def construct(self): self.add_weighted_sum() self.show_real_number_line() self.show_interval() self.squish_into_interval() def add_weighted_sum(self): weighted_sum = TexMobject(*it.chain(*[ ["w_%d"%d, "a_%d"%d, "+"] for d in range(1, 5) ] + [ ["\\cdots", "+", "w_n", "a_n"] ])) weighted_sum.set_color_by_tex("w_", GREEN) weighted_sum.to_edge(UP) self.add(weighted_sum) self.weighted_sum = weighted_sum def show_real_number_line(self): weighted_sum = self.weighted_sum number_line = NumberLine(unit_size = 1.5) number_line.add_numbers() number_line.shift(UP) arrow1, arrow2 = [ Arrow( weighted_sum.get_bottom(), number_line.number_to_point(n), ) for n in (-3, 3) ] self.play(Write(number_line)) self.play(GrowFromPoint(arrow1, arrow1.get_start())) self.play(Transform( arrow1, arrow2, run_time = 5, rate_func = there_and_back )) self.play(FadeOut(arrow1)) self.number_line = number_line def show_interval(self): lower_number_line = self.number_line.copy() lower_number_line.shift(2*DOWN) lower_number_line.set_color(LIGHT_GREY) lower_number_line.numbers.set_color(WHITE) interval = Line( lower_number_line.number_to_point(0), lower_number_line.number_to_point(1), color = YELLOW, stroke_width = 5 ) brace = Brace(interval, DOWN, buff = 0.7) words = TextMobject("Activations should be in this range") words.next_to(brace, DOWN, SMALL_BUFF) self.play(ReplacementTransform( self.number_line.copy(), lower_number_line )) self.play( GrowFromCenter(brace), GrowFromCenter(interval), ) self.play(Write(words, run_time = 2)) self.wait() self.lower_number_line = lower_number_line def squish_into_interval(self): line = self.number_line line.remove(*line.numbers) ghost_line = line.copy() ghost_line.fade(0.5) ghost_line.set_color(BLUE_E) self.add(ghost_line, line) lower_line = self.lower_number_line line.generate_target() u = line.unit_size line.target.apply_function( lambda p : np.array([u*sigmoid(p[0])]+list(p[1:])) ) line.target.move_to(lower_line.number_to_point(0.5)) arrow = Arrow( line.numbers.get_bottom(), line.target.get_top(), color = YELLOW ) self.play( MoveToTarget(line), GrowFromPoint(arrow, arrow.get_start()) ) self.wait(2) class IntroduceSigmoid(GraphScene): CONFIG = { "x_min" : -5, "x_max" : 5, "x_axis_width" : 12, "y_min" : -1, "y_max" : 2, "y_axis_label" : "", "graph_origin" : DOWN, "x_labeled_nums" : list(range(-4, 5)), "y_labeled_nums" : list(range(-1, 3)), } def construct(self): self.setup_axes() self.add_title() self.add_graph() self.show_part(-5, -2, RED) self.show_part(2, 5, GREEN) self.show_part(-2, 2, BLUE) def add_title(self): name = TextMobject("Sigmoid") name.next_to(ORIGIN, RIGHT, LARGE_BUFF) name.to_edge(UP) char = self.x_axis_label.replace("$", "") equation = TexMobject( "\\sigma(%s) = \\frac{1}{1+e^{-%s}}"%(char, char) ) equation.next_to(name, DOWN) self.add(equation, name) self.equation = equation self.sigmoid_name = name def add_graph(self): graph = self.get_graph( lambda x : 1./(1+np.exp(-x)), color = YELLOW ) self.play(ShowCreation(graph)) self.wait() self.sigmoid_graph = graph ### def show_part(self, x_min, x_max, color): line, graph_part = [ self.get_graph( func, x_min = x_min, x_max = x_max, color = color, ).set_stroke(width = 4) for func in (lambda x : 0, sigmoid) ] self.play(ShowCreation(line)) self.wait() self.play(Transform(line, graph_part)) self.wait() class IncludeBias(IntroduceWeights): def construct(self): self.force_skipping() self.zoom_in_on_one_neuron() self.setup_start() self.revert_to_original_skipping_status() self.add_sigmoid_label() self.words_on_activation() self.comment_on_need_for_bias() self.add_bias() self.summarize_weights_and_biases() def setup_start(self): self.weighted_sum = self.get_weighted_sum() digit = self.get_digit() rect = SurroundingRectangle(digit) d_group = VGroup(digit, rect) d_group.set_height(3) d_group.to_edge(RIGHT) weight_grid = digit.copy() weight_grid.set_fill(BLACK, 0.5) self.get_pixels_to_detect(weight_grid).set_fill( GREEN, 0.5 ) self.get_surrounding_pixels_for_edge(weight_grid).set_fill( RED, 0.5 ) weight_grid.move_to(digit) edges = self.neuron.edges_in self.make_edges_weighted( edges, 1.5*np.random.random(len(edges)) - 0.5 ) Transform( self.network_mob.layers[0], self.network_mob.get_active_layer(0, np.random.random(16)) ).update(1) self.add(self.weighted_sum, digit, weight_grid) self.digit = digit self.weight_grid = weight_grid def add_sigmoid_label(self): name = TextMobject("Sigmoid") sigma = self.weighted_sum[0][0] name.next_to(sigma, UP) name.to_edge(UP, SMALL_BUFF) arrow = Arrow( name.get_bottom(), sigma.get_top(), buff = SMALL_BUFF, use_rectangular_stem = False, max_tip_length_to_length_ratio = 0.3 ) self.play( Write(name), ShowCreation(arrow), ) self.sigmoid_name = name self.sigmoid_arrow = arrow def words_on_activation(self): neuron = self.neuron weighted_sum = self.weighted_sum activation_word = TextMobject("Activation") activation_word.next_to(neuron, RIGHT) arrow = Arrow(neuron, weighted_sum.get_bottom()) arrow.set_color(WHITE) words = TextMobject("How positive is this?") words.next_to(self.weighted_sum, UP, SMALL_BUFF) self.play( FadeIn(activation_word), neuron.set_fill, WHITE, 0.8, ) self.wait() self.play( GrowArrow(arrow), ReplacementTransform(activation_word, words), ) self.wait(2) self.play(FadeOut(arrow)) self.how_positive_words = words def comment_on_need_for_bias(self): neuron = self.neuron weight_grid = self.weight_grid colored_pixels = VGroup( self.get_pixels_to_detect(weight_grid), self.get_surrounding_pixels_for_edge(weight_grid), ) words = TextMobject( "Only activate meaningfully \\\\ when", "weighted sum", "$> 10$" ) words.set_color_by_tex("weighted", GREEN) words.next_to(neuron, RIGHT) self.play(Write(words, run_time = 2)) self.play(ApplyMethod( colored_pixels.shift, MED_LARGE_BUFF*UP, rate_func = there_and_back, run_time = 2, lag_ratio = 0.5 )) self.wait() self.gt_ten = words[-1] def add_bias(self): bias = TexMobject("-10") wn, rp = self.weighted_sum[-2:] bias.next_to(wn, RIGHT, SMALL_BUFF) bias.shift(0.02*UP) rp.generate_target() rp.target.next_to(bias, RIGHT, SMALL_BUFF) rect = SurroundingRectangle(bias, buff = 0.5*SMALL_BUFF) name = TextMobject("``bias''") name.next_to(rect, DOWN) VGroup(rect, name).set_color(BLUE) self.play( ReplacementTransform( self.gt_ten.copy(), bias, run_time = 2 ), MoveToTarget(rp), ) self.wait(2) self.play( ShowCreation(rect), Write(name) ) self.wait(2) self.bias_name = name def summarize_weights_and_biases(self): weight_grid = self.weight_grid bias_name = self.bias_name self.play(OldLaggedStart( ApplyMethod, weight_grid, lambda p : (p.set_fill, random.choice([GREEN, GREEN, RED]), random.random() ), rate_func = there_and_back, lag_ratio = 0.4, run_time = 4 )) self.wait() self.play(Indicate(bias_name)) self.wait(2) ### def get_weighted_sum(self): args = ["\\sigma \\big("] for d in range(1, 4): args += ["w_%d"%d, "a_%d"%d, "+"] args += ["\\cdots", "+", "w_n", "a_n"] args += ["\\big)"] weighted_sum = TexMobject(*args) weighted_sum.set_color_by_tex("w_", GREEN) weighted_sum.set_color_by_tex("\\big", YELLOW) weighted_sum.to_edge(UP, LARGE_BUFF) weighted_sum.shift(RIGHT) return weighted_sum class BiasForInactiviyWords(Scene): def construct(self): words = TextMobject("Bias for inactivity") words.set_color(BLUE) words.set_width(FRAME_WIDTH - 1) words.to_edge(UP) self.play(Write(words)) self.wait(3) class ContinualEdgeUpdate(ContinualAnimation): CONFIG = { "max_stroke_width" : 3, "stroke_width_exp" : 7, "n_cycles" : 5, "colors" : [GREEN, GREEN, GREEN, RED], } def __init__(self, network_mob, **kwargs): digest_config(self, kwargs) n_cycles = self.n_cycles edges = VGroup(*it.chain(*network_mob.edge_groups)) self.move_to_targets = [] for edge in edges: edge.colors = [ random.choice(self.colors) for x in range(n_cycles) ] msw = self.max_stroke_width edge.widths = [ msw*random.random()**self.stroke_width_exp for x in range(n_cycles) ] edge.cycle_time = 1 + random.random() edge.generate_target() edge.target.set_stroke(edge.colors[0], edge.widths[0]) self.move_to_targets.append(MoveToTarget(edge)) self.edges = edges ContinualAnimation.__init__(self, edges, **kwargs) def update_mobject(self, dt): if self.internal_time < 1: alpha = smooth(self.internal_time) for move_to_target in self.move_to_targets: move_to_target.update(alpha) return for edge in self.edges: t = (self.internal_time-1)/edge.cycle_time alpha = ((self.internal_time-1)%edge.cycle_time)/edge.cycle_time low_n = int(t)%len(edge.colors) high_n = int(t+1)%len(edge.colors) color = interpolate_color(edge.colors[low_n], edge.colors[high_n], alpha) width = interpolate(edge.widths[low_n], edge.widths[high_n], alpha) edge.set_stroke(color, width) class ShowRemainingNetwork(IntroduceWeights): def construct(self): self.force_skipping() self.zoom_in_on_one_neuron() self.revert_to_original_skipping_status() self.show_all_of_second_layer() self.count_in_biases() self.compute_layer_two_of_weights_and_biases_count() self.show_remaining_layers() self.show_final_number() self.tweak_weights() def show_all_of_second_layer(self): example_neuron = self.neuron layer = self.network_mob.layers[1] neurons = VGroup(*layer.neurons) neurons.remove(example_neuron) words = TextMobject("784", "weights", "per neuron") words.next_to(layer.neurons[0], RIGHT) words.to_edge(UP) self.play(FadeIn(words)) last_edges = None for neuron in neurons[:7]: edges = neuron.edges_in added_anims = [] if last_edges is not None: added_anims += [ last_edges.set_stroke, None, 1 ] edges.set_stroke(width = 2) self.play( ShowCreation(edges, lag_ratio = 0.5), FadeIn(neuron), *added_anims, run_time = 1.5 ) last_edges = edges self.play( OldLaggedStart( ShowCreation, VGroup(*[ n.edges_in for n in neurons[7:] ]), run_time = 3, ), OldLaggedStart( FadeIn, VGroup(*neurons[7:]), run_time = 3, ), VGroup(*last_edges[1:]).set_stroke, None, 1 ) self.wait() self.weights_words = words def count_in_biases(self): neurons = self.network_mob.layers[1].neurons words = TextMobject("One", "bias","for each") words.next_to(neurons, RIGHT, buff = 2) arrows = VGroup(*[ Arrow( words.get_left(), neuron.get_center(), color = BLUE ) for neuron in neurons ]) self.play( FadeIn(words), OldLaggedStart( GrowArrow, arrows, run_time = 3, lag_ratio = 0.3, ) ) self.wait() self.bias_words = words self.bias_arrows = arrows def compute_layer_two_of_weights_and_biases_count(self): ww1, ww2, ww3 = weights_words = self.weights_words bb1, bb2, bb3 = bias_words = self.bias_words bias_arrows = self.bias_arrows times_16 = TexMobject("\\times 16") times_16.next_to(ww1, RIGHT, SMALL_BUFF) ww2.generate_target() ww2.target.next_to(times_16, RIGHT) bias_count = TextMobject("16", "biases") bias_count.next_to(ww2.target, RIGHT, LARGE_BUFF) self.play( Write(times_16), MoveToTarget(ww2), FadeOut(ww3) ) self.wait() self.play( ReplacementTransform(times_16.copy(), bias_count[0]), FadeOut(bb1), ReplacementTransform(bb2, bias_count[1]), FadeOut(bb3), OldLaggedStart(FadeOut, bias_arrows) ) self.wait() self.weights_count = VGroup(ww1, times_16, ww2) self.bias_count = bias_count def show_remaining_layers(self): weights_count = self.weights_count bias_count = self.bias_count for count in weights_count, bias_count: count.generate_target() count.prefix = VGroup(*count.target[:-1]) added_weights = TexMobject( "+16\\!\\times\\! 16 + 16 \\!\\times\\! 10" ) added_weights.to_corner(UP+RIGHT) weights_count.prefix.next_to(added_weights, LEFT, SMALL_BUFF) weights_count.target[-1].next_to( VGroup(weights_count.prefix, added_weights), DOWN ) added_biases = TexMobject("+ 16 + 10") group = VGroup(bias_count.prefix, added_biases) group.arrange(RIGHT, SMALL_BUFF) group.next_to(weights_count.target[-1], DOWN, LARGE_BUFF) bias_count.target[-1].next_to(group, DOWN) network_mob = self.network_mob edges = VGroup(*it.chain(*network_mob.edge_groups[1:])) neurons = VGroup(*it.chain(*[ layer.neurons for layer in network_mob.layers[2:] ])) self.play( MoveToTarget(weights_count), MoveToTarget(bias_count), Write(added_weights, run_time = 1), Write(added_biases, run_time = 1), OldLaggedStart( ShowCreation, edges, run_time = 4, lag_ratio = 0.3, ), OldLaggedStart( FadeIn, neurons, run_time = 4, lag_ratio = 0.3, ) ) self.wait(2) weights_count.add(added_weights) bias_count.add(added_biases) def show_final_number(self): group = VGroup( self.weights_count, self.bias_count, ) group.generate_target() group.target.scale_in_place(0.8) rect = SurroundingRectangle(group.target, buff = MED_SMALL_BUFF) num_mob = TexMobject("13{,}002") num_mob.scale(1.5) num_mob.next_to(rect, DOWN) self.play( ShowCreation(rect), MoveToTarget(group), ) self.play(Write(num_mob)) self.wait() self.final_number = num_mob def tweak_weights(self): learning = TextMobject("Learning $\\rightarrow$") finding_words = TextMobject( "Finding the right \\\\ weights and biases" ) group = VGroup(learning, finding_words) group.arrange(RIGHT) group.scale(0.8) group.next_to(self.final_number, DOWN, MED_LARGE_BUFF) self.add(ContinualEdgeUpdate(self.network_mob)) self.wait(5) self.play(Write(group)) self.wait(10) ### def get_edge_weight_wandering_anim(self, edges): for edge in edges: edge.generate_target() edge.target.set_stroke( color = random.choice([GREEN, GREEN, GREEN, RED]), width = 3*random.random()**7 ) self.play( OldLaggedStart( MoveToTarget, edges, lag_ratio = 0.6, run_time = 2, ), *added_anims ) class ImagineSettingByHand(Scene): def construct(self): randy = Randolph() randy.scale(0.7) randy.to_corner(DOWN+LEFT) bubble = randy.get_bubble() network_mob = NetworkMobject( Network(sizes = [8, 6, 6, 4]), neuron_stroke_color = WHITE ) network_mob.scale(0.7) network_mob.move_to(bubble.get_bubble_center()) network_mob.shift(MED_SMALL_BUFF*RIGHT + SMALL_BUFF*(UP+RIGHT)) self.add(randy, bubble, network_mob) self.add(ContinualEdgeUpdate(network_mob)) self.play(randy.change, "pondering") self.wait() self.play(Blink(randy)) self.wait() self.play(randy.change, "horrified", network_mob) self.play(Blink(randy)) self.wait(10) class WhenTheNetworkFails(MoreHonestMNistNetworkPreview): CONFIG = { "network_mob_config" : {"layer_to_layer_buff" : 2} } def construct(self): self.setup_network_mob() self.black_box() self.incorrect_classification() self.ask_about_weights() def setup_network_mob(self): self.network_mob.scale(0.8) self.network_mob.to_edge(DOWN) def black_box(self): network_mob = self.network_mob layers = VGroup(*network_mob.layers[1:3]) box = SurroundingRectangle( layers, stroke_color = WHITE, fill_color = BLACK, fill_opacity = 0.8, ) words = TextMobject("...rather than treating this as a black box") words.next_to(box, UP, LARGE_BUFF) self.play( Write(words, run_time = 2), DrawBorderThenFill(box) ) self.wait() self.play(*list(map(FadeOut, [words, box]))) def incorrect_classification(self): network = self.network training_data, validation_data, test_data = load_data_wrapper() for in_vect, result in test_data[20:]: network_answer = np.argmax(network.feedforward(in_vect)) if network_answer != result: break self.feed_in_image(in_vect) wrong = TextMobject("Wrong!") wrong.set_color(RED) wrong.next_to(self.network_mob.layers[-1], UP+RIGHT) self.play(Write(wrong, run_time = 1)) def ask_about_weights(self): question = TextMobject( "What weights are used here?\\\\", "What are they doing?" ) question.next_to(self.network_mob, UP) self.add(ContinualEdgeUpdate(self.network_mob)) self.play(Write(question)) self.wait(10) ### def reset_display(self, *args): pass class EvenWhenItWorks(TeacherStudentsScene): def construct(self): self.teacher_says( "Even when it works,\\\\", "dig into why." ) self.change_student_modes(*["pondering"]*3) self.wait(7) class IntroduceWeightMatrix(NetworkScene): CONFIG = { "network_mob_config" : { "neuron_stroke_color" : WHITE, "neuron_fill_color" : WHITE, "neuron_radius" : 0.35, "layer_to_layer_buff" : 2, }, "layer_sizes" : [8, 6], } def construct(self): self.setup_network_mob() self.show_weighted_sum() self.organize_activations_into_column() self.organize_weights_as_matrix() self.show_meaning_of_matrix_row() self.connect_weighted_sum_to_matrix_multiplication() self.add_bias_vector() self.apply_sigmoid() self.write_clean_final_expression() def setup_network_mob(self): self.network_mob.to_edge(LEFT, buff = LARGE_BUFF) self.network_mob.layers[1].neurons.shift(0.02*RIGHT) def show_weighted_sum(self): self.fade_many_neurons() self.activate_first_layer() self.show_first_neuron_weighted_sum() self.add_bias() self.add_sigmoid() ## def fade_many_neurons(self): anims = [] neurons = self.network_mob.layers[1].neurons for neuron in neurons[1:]: neuron.save_state() neuron.edges_in.save_state() anims += [ neuron.fade, 0.8, neuron.set_fill, None, 0, neuron.edges_in.fade, 0.8, ] anims += [ Animation(neurons[0]), Animation(neurons[0].edges_in), ] self.play(*anims) def activate_first_layer(self): layer = self.network_mob.layers[0] activations = 0.7*np.random.random(len(layer.neurons)) active_layer = self.network_mob.get_active_layer(0, activations) a_labels = VGroup(*[ TexMobject("a^{(0)}_%d"%d) for d in range(len(layer.neurons)) ]) for label, neuron in zip(a_labels, layer.neurons): label.scale(0.75) label.move_to(neuron) self.play( Transform(layer, active_layer), Write(a_labels, run_time = 2) ) self.a_labels = a_labels def show_first_neuron_weighted_sum(self): neuron = self.network_mob.layers[1].neurons[0] a_labels = VGroup(*self.a_labels[:2]).copy() a_labels.generate_target() w_labels = VGroup(*[ TexMobject("w_{0, %d}"%d) for d in range(len(a_labels)) ]) weighted_sum = VGroup() symbols = VGroup() for a_label, w_label in zip(a_labels.target, w_labels): a_label.scale(1./0.75) plus = TexMobject("+") weighted_sum.add(w_label, a_label, plus) symbols.add(plus) weighted_sum.add( TexMobject("\\cdots"), TexMobject("+"), TexMobject("w_{0, n}"), TexMobject("a^{(0)}_n"), ) weighted_sum.arrange(RIGHT) a1_label = TexMobject("a^{(1)}_0") a1_label.next_to(neuron, RIGHT) equals = TexMobject("=").next_to(a1_label, RIGHT) weighted_sum.next_to(equals, RIGHT) symbols.add(*weighted_sum[-4:-2]) w_labels.add(weighted_sum[-2]) a_labels.add(self.a_labels[-1].copy()) a_labels.target.add(weighted_sum[-1]) a_labels.add(VGroup(*self.a_labels[2:-1]).copy()) a_labels.target.add(VectorizedPoint(weighted_sum[-4].get_center())) VGroup(a1_label, equals, weighted_sum).scale( 0.75, about_point = a1_label.get_left() ) w_labels.set_color(GREEN) w_labels.shift(0.6*SMALL_BUFF*DOWN) a_labels.target.shift(0.5*SMALL_BUFF*UP) self.play( Write(a1_label), Write(equals), neuron.set_fill, None, 0.3, run_time = 1 ) self.play(MoveToTarget(a_labels, run_time = 1.5)) self.play( Write(w_labels), Write(symbols), ) self.a1_label = a1_label self.a1_equals = equals self.w_labels = w_labels self.a_labels_in_sum = a_labels self.symbols = symbols self.weighted_sum = VGroup(w_labels, a_labels, symbols) def add_bias(self): weighted_sum = self.weighted_sum bias = TexMobject("+\\,", "b_0") bias.scale(0.75) bias.next_to(weighted_sum, RIGHT, SMALL_BUFF) bias.shift(0.5*SMALL_BUFF*DOWN) name = TextMobject("Bias") name.scale(0.75) name.next_to(bias, DOWN, MED_LARGE_BUFF) arrow = Arrow(name, bias, buff = SMALL_BUFF) VGroup(name, arrow, bias).set_color(BLUE) self.play( FadeIn(name), FadeIn(bias), GrowArrow(arrow), ) self.weighted_sum.add(bias) self.bias = bias self.bias_name = VGroup(name, arrow) def add_sigmoid(self): weighted_sum = self.weighted_sum weighted_sum.generate_target() sigma, lp, rp = mob = TexMobject("\\sigma\\big(\\big)") # mob.scale(0.75) sigma.move_to(weighted_sum.get_left()) sigma.shift(0.5*SMALL_BUFF*(DOWN+RIGHT)) lp.next_to(sigma, RIGHT, SMALL_BUFF) weighted_sum.target.next_to(lp, RIGHT, SMALL_BUFF) rp.next_to(weighted_sum.target, RIGHT, SMALL_BUFF) name = TextMobject("Sigmoid") name.next_to(sigma, UP, MED_LARGE_BUFF) arrow = Arrow(name, sigma, buff = SMALL_BUFF) sigmoid_name = VGroup(name, arrow) VGroup(sigmoid_name, mob).set_color(YELLOW) self.play( FadeIn(mob), MoveToTarget(weighted_sum), MaintainPositionRelativeTo(self.bias_name, self.bias), ) self.play(FadeIn(sigmoid_name)) self.sigma = sigma self.sigma_parens = VGroup(lp, rp) self.sigmoid_name = sigmoid_name ## def organize_activations_into_column(self): a_labels = self.a_labels.copy() a_labels.generate_target() column = a_labels.target a_labels_in_sum = self.a_labels_in_sum dots = TexMobject("\\vdots") mid_as = VGroup(*column[2:-1]) Transform(mid_as, dots).update(1) last_a = column[-1] new_last_a = TexMobject( last_a.get_tex_string().replace("7", "n") ) new_last_a.replace(last_a) Transform(last_a, new_last_a).update(1) VGroup( *column[:2] + [mid_as] + [column[-1]] ).arrange(DOWN) column.shift(DOWN + 3.5*RIGHT) pre_brackets = self.get_brackets(a_labels) post_bracketes = self.get_brackets(column) pre_brackets.set_fill(opacity = 0) self.play(FocusOn(self.a_labels[0])) self.play(OldLaggedStart( Indicate, self.a_labels, rate_func = there_and_back, run_time = 1 )) self.play( MoveToTarget(a_labels), Transform(pre_brackets, post_bracketes), run_time = 2 ) self.wait() self.play(*[ OldLaggedStart(Indicate, mob, rate_func = there_and_back) for mob in (a_labels, a_labels_in_sum) ]) self.wait() self.a_column = a_labels self.a_column_brackets = pre_brackets def organize_weights_as_matrix(self): a_column = self.a_column a_column_brackets = self.a_column_brackets w_brackets = a_column_brackets.copy() w_brackets.next_to(a_column_brackets, LEFT, SMALL_BUFF) lwb, rwb = w_brackets w_labels = self.w_labels.copy() w_labels.submobjects.insert( 2, self.symbols[-2].copy() ) w_labels.generate_target() w_labels.target.arrange(RIGHT) w_labels.target.next_to(a_column[0], LEFT, buff = 0.8) lwb.next_to(w_labels.target, LEFT, SMALL_BUFF) lwb.align_to(rwb, UP) row_1, row_k = [ VGroup(*list(map(TexMobject, [ "w_{%s, 0}"%i, "w_{%s, 1}"%i, "\\cdots", "w_{%s, n}"%i, ]))) for i in ("1", "k") ] dots_row = VGroup(*list(map(TexMobject, [ "\\vdots", "\\vdots", "\\ddots", "\\vdots" ]))) lower_rows = VGroup(row_1, dots_row, row_k) lower_rows.scale(0.75) last_row = w_labels.target for row in lower_rows: for target, mover in zip(last_row, row): mover.move_to(target) if "w" in mover.get_tex_string(): mover.set_color(GREEN) row.next_to(last_row, DOWN, buff = 0.45) last_row = row self.play( MoveToTarget(w_labels), Write(w_brackets, run_time = 1) ) self.play(FadeIn( lower_rows, run_time = 3, lag_ratio = 0.5, )) self.wait() self.top_matrix_row = w_labels self.lower_matrix_rows = lower_rows self.matrix_brackets = w_brackets def show_meaning_of_matrix_row(self): row = self.top_matrix_row edges = self.network_mob.layers[1].neurons[0].edges_in.copy() edges.set_stroke(GREEN, 5) rect = SurroundingRectangle(row, color = GREEN_B) self.play(ShowCreation(rect)) for x in range(2): self.play(OldLaggedStart( ShowCreationThenDestruction, edges, lag_ratio = 0.8 )) self.wait() self.top_row_rect = rect def connect_weighted_sum_to_matrix_multiplication(self): a_column = self.a_column a_brackets = self.a_column_brackets top_row_rect = self.top_row_rect column_rect = SurroundingRectangle(a_column) equals = TexMobject("=") equals.next_to(a_brackets, RIGHT) result_brackets = a_brackets.copy() result_terms = VGroup() for i in 0, 1, 4, -1: a = a_column[i] if i == 4: mob = TexMobject("\\vdots") else: # mob = Circle(radius = 0.2, color = YELLOW) mob = TexMobject("?").scale(1.3).set_color(YELLOW) result_terms.add(mob.move_to(a)) VGroup(result_brackets, result_terms).next_to(equals, RIGHT) brace = Brace( VGroup(self.w_labels, self.a_labels_in_sum), DOWN ) arrow = Arrow( brace.get_bottom(), result_terms[0].get_top(), buff = SMALL_BUFF ) self.play( GrowArrow(arrow), GrowFromCenter(brace), ) self.play( Write(equals), FadeIn(result_brackets), ) self.play(ShowCreation(column_rect)) self.play(ReplacementTransform( VGroup(top_row_rect, column_rect).copy(), result_terms[0] )) self.play(OldLaggedStart( FadeIn, VGroup(*result_terms[1:]) )) self.wait(2) self.show_meaning_of_lower_rows( arrow, brace, top_row_rect, result_terms ) self.play(*list(map(FadeOut, [ result_terms, result_brackets, equals, column_rect ]))) def show_meaning_of_lower_rows(self, arrow, brace, row_rect, result_terms): n1, n2, nk = neurons = VGroup(*[ self.network_mob.layers[1].neurons[i] for i in (0, 1, -1) ]) for n in neurons: n.save_state() n.edges_in.save_state() rect2 = SurroundingRectangle(result_terms[1]) rectk = SurroundingRectangle(result_terms[-1]) VGroup(rect2, rectk).set_color(WHITE) row2 = self.lower_matrix_rows[0] rowk = self.lower_matrix_rows[-1] def show_edges(neuron): self.play(OldLaggedStart( ShowCreationThenDestruction, neuron.edges_in.copy().set_stroke(GREEN, 5), lag_ratio = 0.7, run_time = 1, )) self.play( row_rect.move_to, row2, n1.fade, n1.set_fill, None, 0, n1.edges_in.set_stroke, None, 1, n2.set_stroke, WHITE, 3, n2.edges_in.set_stroke, None, 3, ReplacementTransform(arrow, rect2), FadeOut(brace), ) show_edges(n2) self.play( row_rect.move_to, rowk, n2.restore, n2.edges_in.restore, nk.set_stroke, WHITE, 3, nk.edges_in.set_stroke, None, 3, ReplacementTransform(rect2, rectk), ) show_edges(nk) self.play( n1.restore, n1.edges_in.restore, nk.restore, nk.edges_in.restore, FadeOut(rectk), FadeOut(row_rect), ) def add_bias_vector(self): bias = self.bias bias_name = self.bias_name a_column_brackets = self.a_column_brackets a_column = self.a_column plus = TexMobject("+") b_brackets = a_column_brackets.copy() b_column = VGroup(*list(map(TexMobject, [ "b_0", "b_1", "\\vdots", "b_n", ]))) b_column.scale(0.85) b_column.arrange(DOWN, buff = 0.35) b_column.move_to(a_column) b_column.set_color(BLUE) plus.next_to(a_column_brackets, RIGHT) VGroup(b_brackets, b_column).next_to(plus, RIGHT) bias_rect = SurroundingRectangle(bias) self.play(ShowCreation(bias_rect)) self.play(FadeOut(bias_rect)) self.play( Write(plus), Write(b_brackets), Transform(self.bias[1].copy(), b_column[0]), run_time = 1 ) self.play(OldLaggedStart( FadeIn, VGroup(*b_column[1:]) )) self.wait() self.bias_plus = plus self.b_brackets = b_brackets self.b_column = b_column def apply_sigmoid(self): expression_bounds = VGroup( self.matrix_brackets[0], self.b_brackets[1] ) sigma = self.sigma.copy() slp, srp = self.sigma_parens.copy() big_lp, big_rp = parens = TexMobject("()") parens.scale(3) parens.stretch_to_fit_height(expression_bounds.get_height()) big_lp.next_to(expression_bounds, LEFT, SMALL_BUFF) big_rp.next_to(expression_bounds, RIGHT, SMALL_BUFF) parens.set_color(YELLOW) self.play( sigma.scale, 2, sigma.next_to, big_lp, LEFT, SMALL_BUFF, Transform(slp, big_lp), Transform(srp, big_rp), ) self.wait(2) self.big_sigma_group = VGroup(VGroup(sigma), slp, srp) def write_clean_final_expression(self): self.fade_weighted_sum() expression = TexMobject( "\\textbf{a}^{(1)}", "=", "\\sigma", "\\big(", "\\textbf{W}", "\\textbf{a}^{(0)}", "+", "\\textbf{b}", "\\big)", ) expression.set_color_by_tex_to_color_map({ "sigma" : YELLOW, "big" : YELLOW, "W" : GREEN, "\\textbf{b}" : BLUE }) expression.next_to(self.big_sigma_group, UP, LARGE_BUFF) a1, equals, sigma, lp, W, a0, plus, b, rp = expression neuron_anims = [] neurons = VGroup(*self.network_mob.layers[1].neurons[1:]) for neuron in neurons: neuron_anims += [ neuron.restore, neuron.set_fill, None, random.random() ] neuron_anims += [ neuron.edges_in.restore ] neurons.add_to_back(self.network_mob.layers[1].neurons[0]) self.play(ReplacementTransform( VGroup( self.top_matrix_row, self.lower_matrix_rows, self.matrix_brackets ).copy(), VGroup(W), )) self.play(ReplacementTransform( VGroup(self.a_column, self.a_column_brackets).copy(), VGroup(VGroup(a0)), )) self.play( ReplacementTransform( VGroup(self.b_column, self.b_brackets).copy(), VGroup(VGroup(b)) ), ReplacementTransform( self.bias_plus.copy(), plus ) ) self.play(ReplacementTransform( self.big_sigma_group.copy(), VGroup(sigma, lp, rp) )) self.wait() self.play(*neuron_anims, run_time = 2) self.play( ReplacementTransform(neurons.copy(), a1), FadeIn(equals) ) self.wait(2) def fade_weighted_sum(self): self.play(*list(map(FadeOut, [ self.a1_label, self.a1_equals, self.sigma, self.sigma_parens, self.weighted_sum, self.bias_name, self.sigmoid_name, ]))) ### def get_brackets(self, mob): lb, rb = both = TexMobject("\\big[\\big]") both.set_width(mob.get_width()) both.stretch_to_fit_height(1.2*mob.get_height()) lb.next_to(mob, LEFT, SMALL_BUFF) rb.next_to(mob, RIGHT, SMALL_BUFF) return both class HorrifiedMorty(Scene): def construct(self): morty = Mortimer() morty.flip() morty.scale(2) for mode in "horrified", "hesitant": self.play( morty.change, mode, morty.look, UP, ) self.play(Blink(morty)) self.wait(2) class SigmoidAppliedToVector(Scene): def construct(self): tex = TexMobject(""" \\sigma \\left( \\left[\\begin{array}{c} x \\\\ y \\\\ z \\end{array}\\right] \\right) = \\left[\\begin{array}{c} \\sigma(x) \\\\ \\sigma(y) \\\\ \\sigma(z) \\end{array}\\right] """) tex.set_width(FRAME_WIDTH - 1) tex.to_edge(DOWN) indices = it.chain( [0], list(range(1, 5)), list(range(16, 16+4)), list(range(25, 25+2)), [25+3], list(range(29, 29+2)), [29+3], list(range(33, 33+2)), [33+3], ) for i in indices: tex[i].set_color(YELLOW) self.add(tex) self.wait() class EoLA3Wrapper(PiCreatureScene): def construct(self): morty = self.pi_creature rect = ScreenRectangle(height = 5) rect.next_to(morty, UP+LEFT) rect.to_edge(UP, buff = LARGE_BUFF) title = TextMobject("Essence of linear algebra") title.next_to(rect, UP) self.play( ShowCreation(rect), FadeIn(title), morty.change, "raise_right_hand", rect ) self.wait(4) class FeedForwardCode(ExternallyAnimatedScene): pass class NeuronIsFunction(MoreHonestMNistNetworkPreview): CONFIG = { "network_mob_config" : { "layer_to_layer_buff" : 2 } } def construct(self): self.setup_network_mob() self.activate_network() self.write_neuron_holds_a_number() self.feed_in_new_image(8, 7) self.neuron_is_function() self.show_neuron_as_function() self.fade_network_back_in() self.network_is_a_function() self.feed_in_new_image(9, 4) self.wait(2) def setup_network_mob(self): self.network_mob.scale(0.7) self.network_mob.to_edge(DOWN) self.network_mob.shift(LEFT) def activate_network(self): network_mob = self.network_mob self.image_map = get_organized_images() in_vect = self.image_map[3][0] mnist_mob = MNistMobject(in_vect) mnist_mob.next_to(network_mob, LEFT, MED_LARGE_BUFF, UP) activations = self.network.get_activation_of_all_layers(in_vect) for i, activation in enumerate(activations): layer = self.network_mob.layers[i] Transform( layer, self.network_mob.get_active_layer(i, activation) ).update(1) self.add(mnist_mob) self.image_rect, self.curr_image = mnist_mob def write_neuron_holds_a_number(self): neuron_word = TextMobject("Neuron") arrow = Arrow(ORIGIN, DOWN, color = BLUE) thing_words = TextMobject("Thing that holds \\\\ a number") group = VGroup(neuron_word, arrow, thing_words) group.arrange(DOWN) group.to_corner(UP+RIGHT, buff = LARGE_BUFF) neuron = self.network_mob.layers[2].neurons[2] decimal = DecimalNumber(neuron.get_fill_opacity()) decimal.set_width(0.7*neuron.get_width()) decimal.move_to(neuron) neuron_group = VGroup(neuron, decimal) neuron_group.save_state() decimal.set_fill(opacity = 0) self.play( neuron_group.restore, neuron_group.scale, 3, neuron_group.next_to, neuron_word, LEFT, FadeIn(neuron_word), GrowArrow(arrow), FadeIn( thing_words, run_time = 2, rate_func = squish_rate_func(smooth, 0.3, 1) ) ) self.wait() self.play(neuron_group.restore) self.neuron_word = neuron_word self.neuron_word_arrow = arrow self.thing_words = thing_words self.neuron = neuron self.decimal = decimal def feed_in_new_image(self, digit, choice): in_vect = self.image_map[digit][choice] args = [] for s in "answer_rect", "curr_image", "image_rect": if hasattr(self, s): args.append(getattr(self, s)) else: args.append(VectorizedPoint()) MoreHonestMNistNetworkPreview.reset_display(self, *args) self.feed_in_image(in_vect) def neuron_is_function(self): thing_words = self.thing_words cross = Cross(thing_words) function_word = TextMobject("Function") function_word.move_to(thing_words, UP) self.play( thing_words.fade, ShowCreation(cross) ) self.play( FadeIn(function_word), VGroup(thing_words, cross).to_edge, DOWN, ) self.wait() self.function_word = function_word def show_neuron_as_function(self): neuron = self.neuron.copy() edges = neuron.edges_in.copy() prev_layer = self.network_mob.layers[1].copy() arrow = Arrow(ORIGIN, RIGHT, color = BLUE) arrow.next_to(neuron, RIGHT, SMALL_BUFF) decimal = DecimalNumber(neuron.get_fill_opacity()) decimal.next_to(arrow, RIGHT) self.play( FadeOut(self.network_mob), *list(map(Animation, [neuron, edges, prev_layer])) ) self.play(OldLaggedStart( ShowCreationThenDestruction, edges.copy().set_stroke(YELLOW, 4), )) self.play( GrowArrow(arrow), Transform(self.decimal, decimal) ) self.wait(2) self.non_faded_network_parts = VGroup( neuron, edges, prev_layer ) self.neuron_arrow = arrow def fade_network_back_in(self): anims = [ FadeIn( mob, run_time = 2, lag_ratio = 0.5 ) for mob in (self.network_mob.layers, self.network_mob.edge_groups) ] anims += [ FadeOut(self.neuron_arrow), FadeOut(self.decimal), ] anims.append(Animation(self.non_faded_network_parts)) self.play(*anims) self.remove(self.non_faded_network_parts) def network_is_a_function(self): neuron_word = self.neuron_word network_word = TextMobject("Network") network_word.set_color(YELLOW) network_word.move_to(neuron_word) func_tex = TexMobject( "f(a_0, \\dots, a_{783}) = ", """\\left[ \\begin{array}{c} y_0 \\\\ \\vdots \\\\ y_{9} \\end{array} \\right]""" ) func_tex.to_edge(UP) func_tex.shift(MED_SMALL_BUFF*LEFT) self.play( ReplacementTransform(neuron_word, network_word), FadeIn(func_tex) ) ### def reset_display(self, answer_rect, image, image_rect): #Don't do anything, just record these args self.answer_rect = answer_rect self.curr_image = image self.image_rect = image_rect return class ComplicationIsReassuring(TeacherStudentsScene): def construct(self): self.student_says( "It kind of has to \\\\ be complicated, right?", target_mode = "speaking", student_index = 0 ) self.play(self.teacher.change, "happy") self.wait(4) class NextVideo(MoreHonestMNistNetworkPreview, PiCreatureScene): CONFIG = { "network_mob_config" : { "neuron_stroke_color" : WHITE, "layer_to_layer_buff" : 2.5, "brace_for_large_layers" : False, } } def setup(self): MoreHonestMNistNetworkPreview.setup(self) PiCreatureScene.setup(self) def construct(self): self.network_and_data() self.show_next_video() self.talk_about_subscription() self.show_video_neural_network() def network_and_data(self): morty = self.pi_creature network_mob = self.network_mob network_mob.to_edge(LEFT) for obj in network_mob, self: obj.remove(network_mob.output_labels) network_mob.scale(0.7) network_mob.shift(RIGHT) edge_update = ContinualEdgeUpdate(network_mob) training_data, validation_data, test_data = load_data_wrapper() data_mobs = VGroup() for vect, num in test_data[:30]: image = MNistMobject(vect) image.set_height(0.7) arrow = Arrow(ORIGIN, RIGHT, color = BLUE) num_mob = TexMobject(str(num)) group = Group(image, arrow, num_mob) group.arrange(RIGHT, buff = SMALL_BUFF) group.next_to(ORIGIN, RIGHT) data_mobs.add(group) data_mobs.next_to(network_mob, UP) self.add(edge_update) self.play(morty.change, "confused", network_mob) self.wait(2) for data_mob in data_mobs: self.add(data_mob) self.wait(0.2) self.remove(data_mob) self.content = network_mob self.edge_update = edge_update def show_next_video(self): morty = self.pi_creature content = self.content video = VideoIcon() video.set_height(3) video.set_fill(RED, 0.8) video.next_to(morty, UP+LEFT) rect = SurroundingRectangle(video) rect.set_stroke(width = 0) rect.set_fill(BLACK, 0.5) words = TextMobject("On learning") words.next_to(video, UP) if self.edge_update.internal_time < 1: self.edge_update.internal_time = 2 self.play( content.set_height, 0.8*video.get_height(), content.move_to, video, morty.change, "raise_right_hand", FadeIn(rect), FadeIn(video), ) self.add_foreground_mobjects(rect, video) self.wait(2) self.play(Write(words)) self.wait(2) self.video = Group(content, rect, video, words) def talk_about_subscription(self): morty = self.pi_creature morty.generate_target() morty.target.change("hooray") morty.target.rotate( np.pi, axis = UP, about_point = morty.get_left() ) morty.target.shift(LEFT) video = self.video subscribe_word = TextMobject( "Subscribe", "!", arg_separator = "" ) bang = subscribe_word[1] subscribe_word.to_corner(DOWN+RIGHT) subscribe_word.shift(3*UP) q_mark = TextMobject("?") q_mark.move_to(bang, LEFT) arrow = Arrow(ORIGIN, DOWN, color = RED, buff = 0) arrow.next_to(subscribe_word, DOWN) arrow.shift(MED_LARGE_BUFF * RIGHT) self.play( Write(subscribe_word), self.video.shift, 3*LEFT, MoveToTarget(morty), ) self.play(GrowArrow(arrow)) self.wait(2) self.play(morty.change, "maybe", arrow) self.play(Transform(bang, q_mark)) self.wait(3) def show_video_neural_network(self): morty = self.pi_creature network_mob, rect, video, words = self.video network_mob.generate_target(use_deepcopy = True) network_mob.target.set_height(5) network_mob.target.to_corner(UP+LEFT) neurons = VGroup(*network_mob.target.layers[-1].neurons[:2]) neurons.set_stroke(width = 0) video.generate_target() video.target.set_fill(opacity = 1) video.target.set_height(neurons.get_height()) video.target.move_to(neurons, LEFT) self.play( MoveToTarget(network_mob), MoveToTarget(video), FadeOut(words), FadeOut(rect), morty.change, "raise_left_hand" ) neuron_pairs = VGroup(*[ VGroup(*network_mob.layers[-1].neurons[2*i:2*i+2]) for i in range(1, 5) ]) for pair in neuron_pairs: video = video.copy() video.move_to(pair, LEFT) pair.target = video self.play(OldLaggedStart( MoveToTarget, neuron_pairs, run_time = 3 )) self.play(morty.change, "shruggie") self.wait(10) ### class NNPatreonThanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Desmos", "Burt Humburg", "CrypticSwarm", "Juan Benet", "Ali Yahya", "William", "Mayank M. Mehrotra", "Lukas Biewald", "Samantha D. Suplee", "Yana Chernobilsky", "Kaustuv DeBiswas", "Kathryn Schmiedicke", "Yu Jun", "Dave Nicponski", "Damion Kistler", "Markus Persson", "Yoni Nazarathy", "Ed Kellett", "Joseph John Cox", "Luc Ritchie", "Andy Nichols", "Harsev Singh", "Mads Elvheim", "Erik Sundell", "Xueqi Li", "David G. Stork", "Tianyu Ge", "Ted Suzman", "Linh Tran", "Andrew Busey", "Michael McGuffin", "John Haley", "Ankalagon", "Eric Lavault", "Boris Veselinovich", "Julian Pulgarin", "Jeff Linse", "Cooper Jones", "Ryan Dahl", "Mark Govea", "Robert Teed", "Jason Hise", "Meshal Alshammari", "Bernd Sing", "James Thornton", "Mustafa Mahdi", "Mathew Bramson", "Jerry Ling", "Vecht", "Shimin Kuang", "Rish Kundalia", "Achille Brighton", "Ripta Pasay", ] } class PiCreatureGesture(PiCreatureScene): def construct(self): self.play(self.pi_creature.change, "raise_right_hand") self.wait(5) self.play(self.pi_creature.change, "happy") self.wait(4) class IntroduceReLU(IntroduceSigmoid): CONFIG = { "x_axis_label" : "$a$" } def construct(self): self.setup_axes() self.add_title() self.add_graph() self.old_school() self.show_ReLU() self.label_input_regions() def old_school(self): sigmoid_graph = self.sigmoid_graph sigmoid_title = VGroup( self.sigmoid_name, self.equation ) cross = Cross(sigmoid_title) old_school = TextMobject("Old school") old_school.to_corner(UP+RIGHT) old_school.set_color(RED) arrow = Arrow( old_school.get_bottom(), self.equation.get_right(), color = RED ) self.play(ShowCreation(cross)) self.play( Write(old_school, run_time = 1), GrowArrow(arrow) ) self.wait(2) self.play( ApplyMethod( VGroup(cross, sigmoid_title).shift, FRAME_X_RADIUS*RIGHT, rate_func = running_start ), FadeOut(old_school), FadeOut(arrow), ) self.play(ShowCreation( self.sigmoid_graph, rate_func = lambda t : smooth(1-t), remover = True )) def show_ReLU(self): graph = VGroup( Line( self.coords_to_point(-7, 0), self.coords_to_point(0, 0), ), Line( self.coords_to_point(0, 0), self.coords_to_point(4, 4), ), ) graph.set_color(YELLOW) char = self.x_axis_label.replace("$", "") equation = TextMobject("ReLU($%s$) = max$(0, %s)$"%(char, char)) equation.shift(FRAME_X_RADIUS*LEFT/2) equation.to_edge(UP) equation.add_background_rectangle() name = TextMobject("Rectified linear unit") name.move_to(equation) name.add_background_rectangle() self.play(Write(equation)) self.play(ShowCreation(graph), Animation(equation)) self.wait(2) self.play( Write(name), equation.shift, DOWN ) self.wait(2) self.ReLU_graph = graph def label_input_regions(self): l1, l2 = self.ReLU_graph neg_words = TextMobject("Inactive") neg_words.set_color(RED) neg_words.next_to(self.coords_to_point(-2, 0), UP) pos_words = TextMobject("Same as $f(a) = a$") pos_words.set_color(GREEN) pos_words.next_to( self.coords_to_point(1, 1), DOWN+RIGHT ) self.revert_to_original_skipping_status() self.play(ShowCreation(l1.copy().set_color(RED))) self.play(Write(neg_words)) self.wait() self.play(ShowCreation(l2.copy().set_color(GREEN))) self.play(Write(pos_words)) self.wait(2) class CompareSigmoidReLUOnDeepNetworks(PiCreatureScene): def construct(self): morty, lisha = self.morty, self.lisha sigmoid_graph = FunctionGraph( sigmoid, x_min = -5, x_max = 5, ) sigmoid_graph.stretch_to_fit_width(3) sigmoid_graph.set_color(YELLOW) sigmoid_graph.next_to(lisha, UP+LEFT) sigmoid_graph.shift_onto_screen() sigmoid_name = TextMobject("Sigmoid") sigmoid_name.next_to(sigmoid_graph, UP) sigmoid_graph.add(sigmoid_name) slow_learner = TextMobject("Slow learner") slow_learner.set_color(YELLOW) slow_learner.to_corner(UP+LEFT) slow_arrow = Arrow( slow_learner.get_bottom(), sigmoid_graph.get_top(), ) relu_graph = VGroup( Line(2*LEFT, ORIGIN), Line(ORIGIN, np.sqrt(2)*(RIGHT+UP)), ) relu_graph.set_color(BLUE) relu_graph.next_to(lisha, UP+RIGHT) relu_name = TextMobject("ReLU") relu_name.move_to(relu_graph, UP) relu_graph.add(relu_name) network_mob = NetworkMobject(Network( sizes = [6, 4, 5, 4, 3, 5, 2] )) network_mob.scale(0.8) network_mob.to_edge(UP, buff = MED_SMALL_BUFF) network_mob.shift(RIGHT) edge_update = ContinualEdgeUpdate( network_mob, stroke_width_exp = 1, ) self.play( FadeIn(sigmoid_name), ShowCreation(sigmoid_graph), lisha.change, "raise_left_hand", morty.change, "pondering" ) self.play( Write(slow_learner, run_time = 1), GrowArrow(slow_arrow) ) self.wait() self.play( FadeIn(relu_name), ShowCreation(relu_graph), lisha.change, "raise_right_hand", morty.change, "thinking" ) self.play(FadeIn(network_mob)) self.add(edge_update) self.wait(10) ### def create_pi_creatures(self): morty = Mortimer() morty.shift(FRAME_X_RADIUS*RIGHT/2).to_edge(DOWN) lisha = PiCreature(color = BLUE_C) lisha.shift(FRAME_X_RADIUS*LEFT/2).to_edge(DOWN) self.morty, self.lisha = morty, lisha return morty, lisha class ShowAmplify(PiCreatureScene): def construct(self): morty = self.pi_creature rect = ScreenRectangle(height = 5) rect.to_corner(UP+LEFT) rect.shift(DOWN) email = TextMobject("3blue1brown@amplifypartners.com") email.next_to(rect, UP) self.play( ShowCreation(rect), morty.change, "raise_right_hand" ) self.wait(2) self.play(Write(email)) self.play(morty.change, "happy", rect) self.wait(10) class Thumbnail(NetworkScene): CONFIG = { "network_mob_config" : { 'neuron_stroke_color' : WHITE } } def construct(self): network_mob = self.network_mob network_mob.set_height(FRAME_HEIGHT - 1) for layer in network_mob.layers: layer.neurons.set_stroke(width = 5) edge_update = ContinualEdgeUpdate( network_mob, max_stroke_width = 10, stroke_width_exp = 4, ) edge_update.internal_time = 3 edge_update.update(0) self.add(network_mob)