Removed Conv2D because it can be done using just Conv3D and renamed Conv3D to Conv2D to correspond to the spatial conv dimenson not the scene dimension, which is more inline with convention.

This commit is contained in:
Alec Helbling
2023-01-15 14:35:26 +09:00
parent ba63116b37
commit 42b6e37b16
23 changed files with 358 additions and 467 deletions

View File

@ -3,7 +3,7 @@ from pathlib import Path
from manim import *
from PIL import Image
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
@ -21,9 +21,9 @@ def make_code_snippet():
# Make nn
nn = NeuralNetwork([
ImageLayer(numpy_image, height=1.5),
Convolutional3DLayer(1, 7, 7, 3, 3),
Convolutional3DLayer(3, 5, 5, 3, 3),
Convolutional3DLayer(5, 3, 3, 1, 1),
Convolutional2DLayer(1, 7, 7, 3, 3),
Convolutional2DLayer(3, 5, 5, 3, 3),
Convolutional2DLayer(5, 3, 3, 1, 1),
FeedForwardLayer(3),
FeedForwardLayer(3),
])
@ -54,9 +54,9 @@ class CombinedScene(ThreeDScene):
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.5),
Convolutional3DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(5, 3, 3, 1, 1, filter_spacing=0.18),
Convolutional2DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(5, 3, 3, 1, 1, filter_spacing=0.18),
FeedForwardLayer(3),
FeedForwardLayer(3),
],

View File

@ -0,0 +1,77 @@
from pathlib import Path
from manim import *
from PIL import Image
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
# Make the specific scene
config.pixel_height = 1200
config.pixel_width = 1900
config.frame_height = 7.0
config.frame_width = 7.0
ROOT_DIR = Path(__file__).parents[2]
def make_code_snippet():
code_str = """
# Make nn
nn = NeuralNetwork([
ImageLayer(numpy_image, height=1.5),
Convolutional2DLayer(1, 5, 5, 1, 1),
Convolutional2DLayer(4, 5, 5, 1, 1),
Convolutional2DLayer(2, 5, 5),
])
# Play animation
self.play(nn.make_forward_pass_animation())
"""
code = Code(
code=code_str,
tab_width=4,
background_stroke_width=1,
background_stroke_color=WHITE,
insert_line_no=False,
style="monokai",
# background="window",
language="py",
)
code.scale(0.50)
return code
class CombinedScene(ThreeDScene):
def construct(self):
image = Image.open(ROOT_DIR / "assets/mnist/digit.jpeg")
numpy_image = np.asarray(image)
# Make nn
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.5),
Convolutional2DLayer(1, 5, 5, 1, 1, filter_spacing=0.32),
Convolutional2DLayer(4, 5, 5, 1, 1, filter_spacing=0.32),
Convolutional2DLayer(2, 5, 5, filter_spacing=0.32),
],
layer_spacing=0.4,
)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Make code snippet
code = make_code_snippet()
code.next_to(nn, DOWN)
self.add(code)
# Group it all
group = Group(nn, code)
group.move_to(ORIGIN)
# Play animation
forward_pass = nn.make_forward_pass_animation(
corner_pulses=False,
all_filters_at_once=False,
)
self.wait(1)
self.play(forward_pass)

View File

@ -1,7 +1,7 @@
from manim import *
from PIL import Image
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.layers.parent_layers import ThreeDLayer
@ -25,8 +25,8 @@ class CombinedScene(ThreeDScene):
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.5),
Convolutional3DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(3, 5, 5, 1, 1, filter_spacing=0.18),
Convolutional2DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(3, 5, 5, 1, 1, filter_spacing=0.18),
],
layer_spacing=0.25,
layout_direction="top_to_bottom",

View File

@ -1,13 +1,11 @@
from manim_ml.neural_network.layers.convolutional_3d_to_feed_forward import (
Convolutional3DToFeedForward,
from manim_ml.neural_network.layers.convolutional_2d_to_feed_forward import (
Convolutional2DToFeedForward,
)
from manim_ml.neural_network.layers.image_to_convolutional3d import (
ImageToConvolutional3DLayer,
from manim_ml.neural_network.layers.image_to_convolutional_2d import (
ImageToConvolutional2DLayer,
)
from .convolutional3d_to_convolutional3d import Convolutional3DToConvolutional3D
from .convolutional2d_to_convolutional2d import Convolutional2DToConvolutional2D
from .convolutional3d import Convolutional3DLayer
from .convolutional2d import Convolutional2DLayer
from .convolutional_2d_to_convolutional_2d import Convolutional2DToConvolutional2D
from .convolutional_2d import Convolutional2DLayer
from .feed_forward_to_vector import FeedForwardToVector
from .paired_query_to_feed_forward import PairedQueryToFeedForward
from .embedding_to_feed_forward import EmbeddingToFeedForward
@ -23,6 +21,7 @@ from .triplet import TripletLayer
from .triplet_to_feed_forward import TripletToFeedForward
from .paired_query import PairedQueryLayer
from .paired_query_to_feed_forward import PairedQueryToFeedForward
from .max_pooling_2d import MaxPooling2DLayer
connective_layers_list = (
EmbeddingToFeedForward,
@ -34,8 +33,8 @@ connective_layers_list = (
TripletToFeedForward,
PairedQueryToFeedForward,
FeedForwardToVector,
Convolutional3DToConvolutional3D,
Convolutional2DToConvolutional2D,
ImageToConvolutional3DLayer,
Convolutional3DToFeedForward,
Convolutional2DToConvolutional2D,
ImageToConvolutional2DLayer,
Convolutional2DToFeedForward,
)

View File

@ -1,50 +0,0 @@
from manim import *
from matplotlib import animation
from manim_ml.neural_network.layers.parent_layers import VGroupNeuralNetworkLayer
class Convolutional2DLayer(VGroupNeuralNetworkLayer):
def __init__(
self,
feature_map_height,
feature_map_width,
filter_width,
filter_height,
stride=1,
cell_width=0.5,
feature_map_color=BLUE,
filter_color=ORANGE,
**kwargs
):
super(VGroupNeuralNetworkLayer, self).__init__(**kwargs)
self.feature_map_height = feature_map_height
self.feature_map_width = feature_map_width
self.filter_width = filter_width
self.filter_height = filter_height
self.feature_map_color = feature_map_color
self.filter_color = filter_color
self.stride = stride
self.cell_width = cell_width
# Construct the input
self.construct_feature_map()
def construct_feature_map(self):
"""Makes feature map"""
# Make feature map rectangle
self.feature_map = Rectangle(
width=self.feature_map_width * self.cell_width,
height=self.feature_map_height * self.cell_width,
color=self.feature_map_color,
grid_xstep=self.cell_width,
grid_ystep=self.cell_width,
)
self.add(self.feature_map)
@override_animation(Create)
def _create_override(self, **kwargs):
return FadeIn(self.feature_map)
def make_forward_pass_animation(self, **kwargs):
"""Make feed forward animation"""
return AnimationGroup()

View File

@ -1,242 +0,0 @@
from cv2 import line
from manim import *
from manim_ml.neural_network.layers.convolutional2d import Convolutional2DLayer
from manim_ml.neural_network.layers.parent_layers import ConnectiveLayer
class Convolutional2DToConvolutional2D(ConnectiveLayer):
"""2D Conv to 2d Conv"""
input_class = Convolutional2DLayer
output_class = Convolutional2DLayer
def __init__(
self,
input_layer,
output_layer,
color=WHITE,
filter_opacity=0.3,
line_color=WHITE,
pulse_color=ORANGE,
**kwargs
):
super().__init__(
input_layer,
output_layer,
input_class=Convolutional2DLayer,
output_class=Convolutional2DLayer,
**kwargs
)
self.color = color
self.filter_color = self.input_layer.filter_color
self.filter_width = self.input_layer.filter_width
self.filter_height = self.input_layer.filter_height
self.feature_map_width = self.input_layer.feature_map_width
self.feature_map_height = self.input_layer.feature_map_height
self.cell_width = self.input_layer.cell_width
self.stride = self.input_layer.stride
self.filter_opacity = filter_opacity
self.line_color = line_color
self.pulse_color = pulse_color
@override_animation(Create)
def _create_override(self, **kwargs):
return AnimationGroup()
def make_filter(self):
"""Make filter object"""
# Make opaque rectangle
filter = Rectangle(
color=self.filter_color,
fill_color=self.filter_color,
width=self.cell_width * self.filter_width,
height=self.cell_width * self.filter_height,
grid_xstep=self.cell_width,
grid_ystep=self.cell_width,
fill_opacity=self.filter_opacity,
)
# Move filter to top left of feature map
filter.move_to(
self.input_layer.feature_map.get_corner(LEFT + UP), aligned_edge=LEFT + UP
)
return filter
def make_output_node(self):
"""Put output node in top left corner of output feature map"""
# Make opaque rectangle
filter = Rectangle(
color=self.filter_color,
fill_color=self.filter_color,
width=self.cell_width,
height=self.cell_width,
fill_opacity=self.filter_opacity,
)
# Move filter to top left of feature map
filter.move_to(
self.output_layer.feature_map.get_corner(LEFT + UP), aligned_edge=LEFT + UP
)
return filter
def make_filter_propagation_animation(self):
"""Make filter propagation animation"""
lines_copy = self.filter_lines.copy().set_color(ORANGE)
animation_group = AnimationGroup(
Create(lines_copy, lag_ratio=0.0),
# FadeOut(self.filter_lines),
FadeOut(lines_copy),
lag_ratio=1.0,
)
return animation_group
def make_filter_lines(self):
"""Lines connecting input filter with output node"""
filter_lines = []
corner_directions = [LEFT + UP, RIGHT + UP, RIGHT + DOWN, LEFT + DOWN]
for corner_direction in corner_directions:
filter_corner = self.filter.get_corner(corner_direction)
output_corner = self.output_node.get_corner(corner_direction)
line = Line(filter_corner, output_corner, stroke_color=self.line_color)
filter_lines.append(line)
filter_lines = VGroup(*filter_lines)
filter_lines.set_z_index(5)
# Make updater that links the lines to the filter and output node
def filter_updater(filter_lines):
for corner_index, corner_direction in enumerate(corner_directions):
line = filter_lines[corner_index]
filter_corner = self.filter.get_corner(corner_direction)
output_corner = self.output_node.get_corner(corner_direction)
# line._set_start_and_end_attrs(filter_corner, output_corner)
# line.put_start_and_end_on(filter_corner, output_corner)
line.set_points_by_ends(filter_corner, output_corner)
# line._set_start_and_end_attrs(filter_corner, output_corner)
# line.set_points([filter_corner, output_corner])
filter_lines.add_updater(filter_updater)
return filter_lines
def make_assets(self):
"""Make all of the assets"""
# Make the filter
self.filter = self.make_filter()
self.add(self.filter)
# Make output node
self.output_node = self.make_output_node()
self.add(self.output_node)
# Make filter lines
self.filter_lines = self.make_filter_lines()
self.add(self.filter_lines)
super().set_z_index(5)
def make_forward_pass_animation(self, layer_args={}, run_time=1.5, **kwargs):
"""Forward pass animation from conv2d to conv2d"""
# Make assets
self.make_assets()
self.lines_copies = VGroup()
self.add(self.lines_copies)
# Make the animations
animations = []
# Create filter animation
animations.append(
AnimationGroup(
Create(self.filter),
Create(self.output_node),
# Create(self.filter_lines)
)
)
# Make scan filter animation
num_y_moves = (
int((self.feature_map_height - self.filter_height) / self.stride) + 1
)
num_x_moves = int((self.feature_map_width - self.filter_width) / self.stride)
for y_location in range(num_y_moves):
if y_location > 0:
# Shift filter back to start and down
shift_animation = ApplyMethod(
self.filter.shift,
np.array(
[
-self.cell_width
* (self.feature_map_width - self.filter_width),
-self.stride * self.cell_width,
0,
]
),
)
# Shift output node
shift_output_node = ApplyMethod(
self.output_node.shift,
np.array(
[
-(self.output_layer.feature_map_width - 1)
* self.cell_width,
-self.cell_width,
0,
]
),
)
# Make animation group
animation_group = AnimationGroup(
shift_animation,
shift_output_node,
)
animations.append(animation_group)
# Make filter passing flash
# animation = self.make_filter_propagation_animation()
animations.append(Create(self.filter_lines, lag_ratio=0.0))
# animations.append(animation)
for x_location in range(num_x_moves):
# Shift filter right
shift_animation = ApplyMethod(
self.filter.shift, np.array([self.stride * self.cell_width, 0, 0])
)
# Shift output node
shift_output_node = ApplyMethod(
self.output_node.shift, np.array([self.cell_width, 0, 0])
)
# Make animation group
animation_group = AnimationGroup(
shift_animation,
shift_output_node,
)
animations.append(animation_group)
# Make filter passing flash
old_z_index = self.filter_lines.z_index
lines_copy = (
self.filter_lines.copy()
.set_color(ORANGE)
.set_z_index(old_z_index + 1)
)
# self.add(lines_copy)
# self.lines_copies.add(lines_copy)
animations.append(Create(self.filter_lines, lag_ratio=0.0))
# animations.append(FadeOut(self.filter_lines))
# animation = self.make_filter_propagation_animation()
# animations.append(animation)
# animations.append(Create(self.filter_lines, lag_ratio=1.0))
# animations.append(FadeOut(self.filter_lines))
# Fade out
animations.append(
AnimationGroup(
FadeOut(self.filter),
FadeOut(self.output_node),
FadeOut(self.filter_lines),
)
)
# Make animation group
animation_group = Succession(*animations, lag_ratio=1.0)
return animation_group
def set_z_index(self, z_index, family=False):
"""Override set_z_index"""
super().set_z_index(4)
def scale(self, scale_factor, **kwargs):
self.cell_width *= scale_factor
super().scale(scale_factor, **kwargs)

View File

@ -7,7 +7,7 @@ from manim_ml.gridded_rectangle import GriddedRectangle
import numpy as np
class Convolutional3DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
class Convolutional2DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
"""Handles rendering a convolutional layer for a nn"""
def __init__(
@ -41,6 +41,8 @@ class Convolutional3DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
self.stride = stride
self.stroke_width = stroke_width
self.show_grid_lines = show_grid_lines
def construct_layer(self, input_layer: 'NeuralNetworkLayer', output_layer: 'NeuralNetworkLayer', **kwargs):
# Make the feature maps
self.feature_maps = self.construct_feature_maps()
self.add(self.feature_maps)

View File

@ -1,11 +1,10 @@
from manim import *
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.parent_layers import ConnectiveLayer, ThreeDLayer
from manim_ml.gridded_rectangle import GriddedRectangle
from manim.utils.space_ops import rotation_matrix
class Filters(VGroup):
"""Group for showing a collection of filters connecting two layers"""
@ -244,16 +243,16 @@ class Filters(VGroup):
return passing_flash
class Convolutional3DToConvolutional3D(ConnectiveLayer, ThreeDLayer):
class Convolutional2DToConvolutional2D(ConnectiveLayer, ThreeDLayer):
"""Feed Forward to Embedding Layer"""
input_class = Convolutional3DLayer
output_class = Convolutional3DLayer
input_class = Convolutional2DLayer
output_class = Convolutional2DLayer
def __init__(
self,
input_layer: Convolutional3DLayer,
output_layer: Convolutional3DLayer,
input_layer: Convolutional2DLayer,
output_layer: Convolutional2DLayer,
color=ORANGE,
filter_opacity=0.3,
line_color=ORANGE,
@ -266,8 +265,8 @@ class Convolutional3DToConvolutional3D(ConnectiveLayer, ThreeDLayer):
super().__init__(
input_layer,
output_layer,
input_class=Convolutional3DLayer,
output_class=Convolutional3DLayer,
input_class=Convolutional2DLayer,
output_class=Convolutional2DLayer,
**kwargs,
)
self.color = color

View File

@ -1,18 +1,18 @@
from manim import *
from manim_ml.neural_network.layers.parent_layers import ConnectiveLayer, ThreeDLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
class Convolutional3DToFeedForward(ConnectiveLayer, ThreeDLayer):
class Convolutional2DToFeedForward(ConnectiveLayer, ThreeDLayer):
"""Feed Forward to Embedding Layer"""
input_class = Convolutional3DLayer
input_class = Convolutional2DLayer
output_class = FeedForwardLayer
def __init__(
self,
input_layer: Convolutional3DLayer,
input_layer: Convolutional2DLayer,
output_layer: FeedForwardLayer,
passing_flash_color=ORANGE,
**kwargs
@ -20,8 +20,8 @@ class Convolutional3DToFeedForward(ConnectiveLayer, ThreeDLayer):
super().__init__(
input_layer,
output_layer,
input_class=Convolutional3DLayer,
output_class=Convolutional3DLayer,
input_class=Convolutional2DLayer,
output_class=Convolutional2DLayer,
**kwargs
)
self.passing_flash_color = passing_flash_color

View File

@ -35,9 +35,7 @@ class FeedForwardLayer(VGroupNeuralNetworkLayer):
self.node_group = VGroup()
self._construct_neural_network_layer()
def _construct_neural_network_layer(self):
def construct_layer(self, input_layer: 'NeuralNetworkLayer', output_layer: 'NeuralNetworkLayer', **kwargs):
"""Creates the neural network layer"""
# Add Nodes
for node_number in range(self.num_nodes):

View File

@ -1,10 +1,10 @@
from manim import *
import numpy as np
from manim_ml.image import GrayscaleImageMobject
from manim_ml.neural_network.layers.parent_layers import NeuralNetworkLayer
from PIL import Image
class ImageLayer(NeuralNetworkLayer):
"""Single Image Layer for Neural Network"""
@ -12,6 +12,17 @@ class ImageLayer(NeuralNetworkLayer):
super().__init__(**kwargs)
self.numpy_image = numpy_image
self.show_image_on_create = show_image_on_create
def construct_layer(self, input_layer, output_layer):
"""Construct layer method
Parameters
----------
input_layer :
Input layer
output_layer :
Output layer
"""
if len(np.shape(self.numpy_image)) == 2:
# Assumed Grayscale
self.num_channels = 1

View File

@ -1,7 +1,7 @@
import numpy as np
from manim import *
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.layers.parent_layers import (
ThreeDLayer,
@ -10,14 +10,14 @@ from manim_ml.neural_network.layers.parent_layers import (
from manim_ml.gridded_rectangle import GriddedRectangle
class ImageToConvolutional3DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
class ImageToConvolutional2DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
"""Handles rendering a convolutional layer for a nn"""
input_class = ImageLayer
output_class = Convolutional3DLayer
output_class = Convolutional2DLayer
def __init__(
self, input_layer: ImageLayer, output_layer: Convolutional3DLayer, **kwargs
self, input_layer: ImageLayer, output_layer: Convolutional2DLayer, **kwargs
):
super().__init__(input_layer, output_layer, **kwargs)
self.input_layer = input_layer

View File

@ -0,0 +1,61 @@
from manim import *
from manim_ml.neural_network.layers.parent_layers import ThreeDLayer, VGroupNeuralNetworkLayer
class MaxPooling2DLayer(VGroupNeuralNetworkLayer, ThreeDLayer):
"""Max pooling layer for Convolutional2DLayer
Note: This is for a Convolutional2DLayer even though
it is called MaxPooling2DLayer because the 2D corresponds
to the 2 spatial dimensions of the convolution.
"""
def __init__(self, output_feature_map_size=(4, 4), kernel_size=2, stride=1,
cell_highlight_color=ORANGE, **kwargs):
"""Layer object for animating 2D Convolution Max Pooling
Parameters
----------
kernel_size : int or tuple, optional
Width/Height of max pooling kernel, by default 2
stride : int, optional
Stride of the max pooling operation, by default 1
"""
super().__init__(**kwargs)
self.output_feature_map_size = output_feature_map_size
self.kernel_size = kernel_size
self.stride = stride
self.cell_highlight_color = cell_highlight_color
# Make the output feature maps
feature_maps = self._make_output_feature_maps()
self.add(feature_maps)
def construct_layer(self, input_layer, output_layer):
"""Constructs the layer in the context of adjacent layers"""
pass
def _make_output_feature_maps(self):
"""Makes a set of output feature maps"""
# Compute the size of the feature maps
pass
def make_forward_pass_animation(self, layer_args={}, **kwargs):
"""Makes forward pass of Max Pooling Layer.
Parameters
----------
layer_args : dict, optional
_description_, by default {}
"""
# 1. Draw gridded rectangle with kernel_size x kernel_size
# box regions over the input feature map.
# 2. Randomly highlight one of the cells in the kernel.
# 3. Move and resize the gridded rectangle to the output
# feature maps.
# 4. Make the gridded feature map(s) disappear.
pass
@override_animation(Create)
def _create_override(self, **kwargs):
"""Create animation for the MaxPooling operation"""
pass

View File

@ -1,7 +1,6 @@
from manim import *
from abc import ABC, abstractmethod
class NeuralNetworkLayer(ABC, Group):
"""Abstract Neural Network Layer class"""
@ -12,6 +11,20 @@ class NeuralNetworkLayer(ABC, Group):
self.title.next_to(self, UP, 1.2)
# self.add(self.title)
@abstractmethod
def construct_layer(self, input_layer: 'NeuralNetworkLayer',
output_layer: 'NeuralNetworkLayer', **kwargs):
"""Constructs the layer at network construction time
Parameters
----------
input_layer : NeuralNetworkLayer
preceding layer
output_layer : NeuralNetworkLayer
following layer
"""
pass
@abstractmethod
def make_forward_pass_animation(self, layer_args={}, **kwargs):
pass
@ -23,7 +36,6 @@ class NeuralNetworkLayer(ABC, Group):
def __repr__(self):
return f"{type(self).__name__}"
class VGroupNeuralNetworkLayer(NeuralNetworkLayer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -37,7 +49,6 @@ class VGroupNeuralNetworkLayer(NeuralNetworkLayer):
def _create_override(self):
return super()._create_override()
class ThreeDLayer(ABC):
"""Abstract class for 3D layers"""
@ -47,7 +58,6 @@ class ThreeDLayer(ABC):
rotation_angle = 60 * DEGREES
rotation_axis = [0.0, 0.9, 0.0]
class ConnectiveLayer(VGroupNeuralNetworkLayer):
"""Forward pass animation for a given pair of layers"""
@ -76,7 +86,6 @@ class ConnectiveLayer(VGroupNeuralNetworkLayer):
+ ")"
)
class BlankConnective(ConnectiveLayer):
"""Connective layer to be used when the given pair of layers is undefined"""

View File

@ -51,11 +51,20 @@ class NeuralNetwork(Group):
self.layout_direction = layout_direction
# TODO take layer_node_count [0, (1, 2), 0]
# and make it have explicit distinct subspaces
# Construct all of the layers
self._construct_input_layers()
# Place the layers
self._place_layers(layout=layout, layout_direction=layout_direction)
self._place_layers(
layout=layout,
layout_direction=layout_direction
)
# Make the connective layers
self.connective_layers, self.all_layers = self._construct_connective_layers()
# Make overhead title
self.title = Text(self.title_text, font_size=DEFAULT_FONT_SIZE / 2)
self.title = Text(
self.title_text,
font_size=DEFAULT_FONT_SIZE / 2
)
self.title.next_to(self, UP, 1.0)
self.add(self.title)
# Place layers at correct z index
@ -67,6 +76,21 @@ class NeuralNetwork(Group):
# Print neural network
print(repr(self))
def _construct_input_layers(self):
"""Constructs each of the input layers in context
of their adjacent layers"""
prev_layer = None
next_layer = None
# Go through all the input layers and run their construct method
for layer_index in range(len(self.input_layers)):
current_layer = self.input_layers[layer_index]
if layer_index < len(self.input_layers) - 1:
next_layer = self.input_layers[layer_index + 1]
if layer_index > 0:
prev_layer = self.input_layers[layer_index - 1]
# Run the construct layer method for each
current_layer.construct_layer(prev_layer, next_layer)
def _place_layers(self, layout="linear", layout_direction="top_to_bottom"):
"""Creates the neural network"""
# TODO implement more sophisticated custom layouts
@ -300,7 +324,6 @@ class NeuralNetwork(Group):
string_repr = "NeuralNetwork([\n" + inner_string + "])"
return string_repr
class FeedForwardNeuralNetwork(NeuralNetwork):
"""NeuralNetwork with just feed forward layers"""

View File

@ -1,30 +1,83 @@
from manim import *
from manim_ml.neural_network.layers import Convolutional2DLayer
from PIL import Image
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
class SingleConvolutionalLayerScene(ThreeDScene):
def construct(self):
# Make nn
layers = [Convolutional2DLayer(3, 4)]
nn = NeuralNetwork(layers)
nn.scale(1.3)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Play animation
self.set_camera_orientation(
phi=280 * DEGREES, theta=-10 * DEGREES, gamma=90 * DEGREES
)
# self.play(nn.make_forward_pass_animation(run_time=5))
class Simple3DConvScene(ThreeDScene):
def construct(self):
"""
TODO
- [X] Make grid lines for the CNN filters
- [ ] Make Scanning filter effect
- [ ] Have filter box go accross each input feature map
- [ ] Make filter lines effect
- [ ] Make flowing animation down filter lines
"""
# Make nn
layers = [
Convolutional2DLayer(
1, 5, 5, 5, 5, feature_map_height=3, filter_width=3, filter_height=3
),
Convolutional2DLayer(
1, 3, 3, 1, 1, feature_map_width=3, filter_width=3, filter_height=3
),
]
nn = NeuralNetwork(layers)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Play animation
# self.set_camera_orientation(phi=280*DEGREES, theta=-10*DEGREES, gamma=90*DEGREES)
self.play(nn.make_forward_pass_animation(run_time=30))
# Make the specific scene
config.pixel_height = 1200
config.pixel_width = 1900
config.frame_height = 12.0
config.frame_width = 12.0
config.frame_height = 6.0
config.frame_width = 6.0
class TestConv2d(Scene):
class CombinedScene(ThreeDScene):
def construct(self):
image = Image.open("../assets/mnist/digit.jpeg")
numpy_image = np.asarray(image)
# Make nn
nn = NeuralNetwork(
[
Convolutional2DLayer(5, 5, 3, 3, cell_width=0.5, stride=1),
Convolutional2DLayer(3, 3, 2, 2, cell_width=0.5, stride=1),
ImageLayer(numpy_image, height=1.5),
Convolutional2DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(5, 3, 3, 1, 1, filter_spacing=0.18),
FeedForwardLayer(3),
FeedForwardLayer(3),
],
layer_spacing=1.5,
camera=self.camera,
layer_spacing=0.25,
)
# Center the nn
nn.scale(1.3)
nn.move_to(ORIGIN)
self.play(Create(nn), run_time=2)
self.add(nn)
# Play animation
forward_pass = nn.make_forward_pass_animation(run_time=19)
self.play(
forward_pass,
forward_pass = nn.make_forward_pass_animation(
corner_pulses=False, all_filters_at_once=False
)
self.wait(1)
self.play(forward_pass)

View File

@ -1,87 +0,0 @@
from manim import *
from PIL import Image
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
class SingleConvolutionalLayerScene(ThreeDScene):
def construct(self):
# Make nn
layers = [Convolutional3DLayer(3, 4)]
nn = NeuralNetwork(layers)
nn.scale(1.3)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Play animation
self.set_camera_orientation(
phi=280 * DEGREES, theta=-10 * DEGREES, gamma=90 * DEGREES
)
# self.play(nn.make_forward_pass_animation(run_time=5))
class Simple3DConvScene(ThreeDScene):
def construct(self):
"""
TODO
- [X] Make grid lines for the CNN filters
- [ ] Make Scanning filter effect
- [ ] Have filter box go accross each input feature map
- [ ] Make filter lines effect
- [ ] Make flowing animation down filter lines
"""
# Make nn
layers = [
Convolutional3DLayer(
1, 5, 5, 5, 5, feature_map_height=3, filter_width=3, filter_height=3
),
Convolutional3DLayer(
1, 3, 3, 1, 1, feature_map_width=3, filter_width=3, filter_height=3
),
]
nn = NeuralNetwork(layers)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Play animation
# self.set_camera_orientation(phi=280*DEGREES, theta=-10*DEGREES, gamma=90*DEGREES)
self.play(nn.make_forward_pass_animation(run_time=30))
# Make the specific scene
config.pixel_height = 1200
config.pixel_width = 1900
config.frame_height = 6.0
config.frame_width = 6.0
class CombinedScene(ThreeDScene):
def construct(self):
image = Image.open("../assets/mnist/digit.jpeg")
numpy_image = np.asarray(image)
# Make nn
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.4),
Convolutional3DLayer(1, 5, 5, 3, 3, filter_spacing=0.2),
Convolutional3DLayer(2, 3, 3, 1, 1, filter_spacing=0.2),
FeedForwardLayer(3, rectangle_stroke_width=4, node_stroke_width=4),
FeedForwardLayer(1, rectangle_stroke_width=4, node_stroke_width=4),
],
layer_spacing=0.5,
camera=self.camera,
)
nn.scale(1.3)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
# Play animation
# self.set_camera_orientation(phi=280* DEGREES, theta=-20*DEGREES, gamma=90 * DEGREES)
# self.begin_ambient_camera_rotation()
forward_pass = nn.make_forward_pass_animation(run_time=10, corner_pulses=False)
print(forward_pass)
self.play(forward_pass)

View File

@ -9,7 +9,6 @@ from sklearn import datasets
import sklearn
import matplotlib.pyplot as plt
def learn_iris_decision_tree(iris):
decision_tree = DecisionTreeClassifier(
random_state=1, max_depth=3, max_leaf_nodes=6
@ -18,13 +17,11 @@ def learn_iris_decision_tree(iris):
# output the decisioin tree in some format
return decision_tree
def make_sklearn_tree(dataset, max_tree_depth=3):
tree = learn_iris_decision_tree(dataset)
feature_names = dataset.feature_names[0:2]
return tree, tree.tree_
class DecisionTreeScene(Scene):
def construct(self):
"""Makes a decision tree object"""
@ -32,7 +29,6 @@ class DecisionTreeScene(Scene):
clf, sklearn_tree = make_sklearn_tree(iris_dataset)
# sklearn.tree.plot_tree(clf, node_ids=True)
# plt.show()
decision_tree = DecisionTreeDiagram(
sklearn_tree,
class_images_paths=[
@ -48,7 +44,6 @@ class DecisionTreeScene(Scene):
self.play(create_decision_tree)
# self.play(create_decision_tree)
class SurfacePlot(Scene):
def construct(self):
iris_dataset = datasets.load_iris()

View File

@ -1,9 +1,11 @@
from manim import *
from PIL import Image
import numpy as np
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.layers.max_pooling_2d import MaxPooling2DLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
# Make the specific scene
@ -12,7 +14,6 @@ config.pixel_width = 1900
config.frame_height = 6.0
config.frame_width = 6.0
class CombinedScene(ThreeDScene):
def construct(self):
image = Image.open("../assets/mnist/digit.jpeg")
@ -21,22 +22,15 @@ class CombinedScene(ThreeDScene):
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.5),
Convolutional3DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(5, 3, 3, 1, 1, filter_spacing=0.18),
FeedForwardLayer(3),
FeedForwardLayer(3),
Convolutional2DLayer(1, 8, 8, 3, 3, filter_spacing=0.32),
MaxPooling2DLayer(kernel_size=2),
Convolutional2DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
],
layer_spacing=0.25,
)
# Center the nn
nn.move_to(ORIGIN)
self.add(nn)
"""
self.play(
FadeIn(nn)
)
"""
# Play animation
forward_pass = nn.make_forward_pass_animation(
corner_pulses=False, all_filters_at_once=False

31
tests/test_mcmc.py Normal file
View File

@ -0,0 +1,31 @@
from manim import *
from manim_ml.diffusion.mcmc import MCMCAxes, MultidimensionalGaussianPosterior, metropolis_hastings_sampler
# Make the specific scene
config.pixel_height = 1200
config.pixel_width = 1200
config.frame_height = 12.0
config.frame_width = 12.0
def test_metropolis_hastings_sampler(iterations=100):
samples, _, candidates = metropolis_hastings_sampler(iterations=iterations)
assert samples.shape == (iterations, 2)
class MCMCTest(Scene):
def construct(self):
axes = MCMCAxes()
self.play(Create(axes))
gaussian_posterior = MultidimensionalGaussianPosterior(
mu=np.array([0.0, 0.0]),
var=np.array([4.0, 2.0])
)
show_gaussian_animation = axes.show_ground_truth_gaussian(
gaussian_posterior
)
self.play(show_gaussian_animation)
chain_sampling_animation = axes.visualize_metropolis_hastings_chain_sampling(
log_prob_fn=gaussian_posterior,
sampling_kwargs={"iterations": 1000}
)
self.play(chain_sampling_animation)

View File

@ -0,0 +1,4 @@
"""
The purpose of this test is to ensure that it is possible
to have nested neural network layers.
"""

View File

@ -1,7 +1,7 @@
from manim import *
from PIL import Image
from manim_ml.neural_network.layers.convolutional3d import Convolutional3DLayer
from manim_ml.neural_network.layers.convolutional_2d import Convolutional2DLayer
from manim_ml.neural_network.layers.feed_forward import FeedForwardLayer
from manim_ml.neural_network.layers.image import ImageLayer
from manim_ml.neural_network.neural_network import NeuralNetwork
@ -21,8 +21,8 @@ class CombinedScene(ThreeDScene):
nn = NeuralNetwork(
[
ImageLayer(numpy_image, height=1.5),
Convolutional3DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional3DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(1, 7, 7, 3, 3, filter_spacing=0.32),
Convolutional2DLayer(3, 5, 5, 3, 3, filter_spacing=0.32),
FeedForwardLayer(3),
],
layer_spacing=0.25,

View File

@ -0,0 +1,14 @@
from manim import *
from manim_ml.probability import GaussianDistribution
class TestShowGaussian(Scene):
def construct(self):
axes = Axes()
self.add(axes)
gaussian = GaussianDistribution(
axes,
mean=np.array([0.0, 0.0]),
cov=np.array([[2.0, 0.0], [0.0, 1.0]])
)
self.add(gaussian)