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