mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Beginning nn project
This commit is contained in:
parent
bb958b68d7
commit
9958a73059
19 changed files with 2560 additions and 74 deletions
|
@ -136,9 +136,9 @@ class ShowPassingFlash(ShowPartial):
|
|||
}
|
||||
def get_bounds(self, alpha):
|
||||
alpha *= (1+self.time_width)
|
||||
alpha -= self.time_width/2
|
||||
lower = max(0, alpha - self.time_width/2)
|
||||
upper = min(1, alpha + self.time_width/2)
|
||||
alpha -= self.time_width/2.0
|
||||
lower = max(0, alpha - self.time_width/2.0)
|
||||
upper = min(1, alpha + self.time_width/2.0)
|
||||
return (lower, upper)
|
||||
|
||||
def clean_up(self, *args, **kwargs):
|
||||
|
@ -146,6 +146,12 @@ class ShowPassingFlash(ShowPartial):
|
|||
for submob, start_submob in self.get_all_families_zipped():
|
||||
submob.pointwise_become_partial(start_submob, 0, 1)
|
||||
|
||||
class ShowCreationThenDestruction(ShowPassingFlash):
|
||||
CONFIG = {
|
||||
"time_width" : 2.0,
|
||||
"run_time" : 1,
|
||||
}
|
||||
|
||||
class MoveAlongPath(Animation):
|
||||
def __init__(self, mobject, vmobject, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
|
|
|
@ -100,7 +100,7 @@ class Swap(CyclicReplace):
|
|||
|
||||
class GrowFromPoint(Transform):
|
||||
def __init__(self, mobject, point, **kwargs):
|
||||
target = mobject.copy()
|
||||
target = mobject.deepcopy()
|
||||
point_mob = Point(point)
|
||||
mobject.replace(point_mob)
|
||||
mobject.highlight(point_mob.get_color())
|
||||
|
|
126
camera.py
126
camera.py
|
@ -7,7 +7,7 @@ from colour import Color
|
|||
import aggdraw
|
||||
|
||||
from helpers import *
|
||||
from mobject import PMobject, VMobject, ImageMobject
|
||||
from mobject import Mobject, PMobject, VMobject, ImageMobject, Group
|
||||
|
||||
class Camera(object):
|
||||
CONFIG = {
|
||||
|
@ -22,6 +22,8 @@ class Camera(object):
|
|||
"max_allowable_norm" : 2*SPACE_WIDTH,
|
||||
"image_mode" : "RGBA",
|
||||
"n_rgb_coords" : 4,
|
||||
"background_alpha" : 0, #Out of 255
|
||||
"pixel_array_dtype" : 'uint8'
|
||||
}
|
||||
|
||||
def __init__(self, background = None, **kwargs):
|
||||
|
@ -53,27 +55,34 @@ class Camera(object):
|
|||
#TODO, how to gracefully handle backgrounds
|
||||
#with different sizes?
|
||||
self.background = np.array(image)[:height, :width]
|
||||
self.background = self.background.astype(self.pixel_array_dtype)
|
||||
else:
|
||||
background_rgba = color_to_int_rgba(
|
||||
self.background_color, alpha = 0
|
||||
self.background_color, alpha = self.background_alpha
|
||||
)
|
||||
self.background = np.zeros(
|
||||
list(self.pixel_shape)+[self.n_rgb_coords],
|
||||
dtype = 'uint8'
|
||||
dtype = self.pixel_array_dtype
|
||||
)
|
||||
self.background[:,:] = background_rgba
|
||||
|
||||
def get_image(self):
|
||||
return np.array(self.pixel_array)
|
||||
return Image.fromarray(
|
||||
self.pixel_array,
|
||||
mode = self.image_mode
|
||||
)
|
||||
|
||||
def set_image(self, pixel_array):
|
||||
def get_pixel_array(self):
|
||||
return self.pixel_array
|
||||
|
||||
def set_pixel_array(self, pixel_array):
|
||||
self.pixel_array = np.array(pixel_array)
|
||||
|
||||
def set_background(self, pixel_array):
|
||||
self.background = np.array(pixel_array)
|
||||
|
||||
def reset(self):
|
||||
self.set_image(np.array(self.background))
|
||||
self.set_pixel_array(np.array(self.background))
|
||||
|
||||
def capture_mobject(self, mobject):
|
||||
return self.capture_mobjects([mobject])
|
||||
|
@ -97,8 +106,12 @@ class Camera(object):
|
|||
)
|
||||
elif isinstance(mobject, ImageMobject):
|
||||
self.display_image_mobject(mobject)
|
||||
elif isinstance(mobject, Mobject):
|
||||
pass #Remainder of loop will handle submobjects
|
||||
else:
|
||||
raise Exception("Unknown mobject type: " + type(mobject))
|
||||
raise Exception(
|
||||
"Unknown mobject type: " + mobject.__class__.__name__
|
||||
)
|
||||
#TODO, more? Call out if it's unknown?
|
||||
self.display_multiple_vectorized_mobjects(vmobjects)
|
||||
|
||||
|
@ -206,52 +219,73 @@ class Camera(object):
|
|||
ih, iw = impa.shape[:2] #inner with and height
|
||||
rgb_len = self.pixel_array.shape[2]
|
||||
|
||||
# List of all coordinates of pixels, given as (x, y),
|
||||
# which matches the return type of points_to_pixel_coords,
|
||||
# even though np.array indexing naturally happens as (y, x)
|
||||
all_pixel_coords = np.zeros((oh*ow, 2), dtype = 'int')
|
||||
a = np.arange(oh*ow, dtype = 'int')
|
||||
all_pixel_coords[:,0] = a%ow
|
||||
all_pixel_coords[:,1] = a/ow
|
||||
image = np.zeros((oh, ow, rgb_len), dtype = self.pixel_array_dtype)
|
||||
|
||||
recentered_coords = all_pixel_coords - ul_coords
|
||||
coord_norms = np.linalg.norm(recentered_coords, axis = 1)
|
||||
if right_vect[1] == 0 and down_vect[0] == 0:
|
||||
rv0 = right_vect[0]
|
||||
dv1 = down_vect[1]
|
||||
x_indices = np.arange(rv0, dtype = 'int')*iw/rv0
|
||||
y_indices = np.arange(dv1, dtype = 'int')*ih/dv1
|
||||
stretched_impa = impa[y_indices][:,x_indices]
|
||||
|
||||
with np.errstate(divide = 'ignore'):
|
||||
ix_coords, iy_coords = [
|
||||
np.divide(
|
||||
dim*np.dot(recentered_coords, vect),
|
||||
np.dot(vect, vect),
|
||||
)
|
||||
for vect, dim in (right_vect, iw), (down_vect, ih)
|
||||
]
|
||||
to_change = reduce(op.and_, [
|
||||
ix_coords >= 0, ix_coords < iw,
|
||||
iy_coords >= 0, iy_coords < ih,
|
||||
])
|
||||
n_to_change = np.sum(to_change)
|
||||
inner_flat_coords = iw*iy_coords[to_change] + ix_coords[to_change]
|
||||
flat_impa = impa.reshape((iw*ih, rgb_len))
|
||||
target_rgbas = flat_impa[inner_flat_coords, :]
|
||||
x0, x1 = ul_coords[0], ur_coords[0]
|
||||
y0, y1 = ul_coords[1], dl_coords[1]
|
||||
if x0 >= ow or x1 < 0 or y0 >= oh or y1 < 0:
|
||||
return
|
||||
siy0 = max(-y0, 0) #stretched_impa y0
|
||||
siy1 = dv1 - max(y1-oh, 0)
|
||||
six0 = max(-x0, 0)
|
||||
six1 = rv0 - max(x1-ow, 0)
|
||||
x0 = max(x0, 0)
|
||||
y0 = max(y0, 0)
|
||||
image[y0:y1, x0:x1] = stretched_impa[siy0:siy1, six0:six1]
|
||||
else:
|
||||
# Alternate (slower) tactice if image is tilted
|
||||
# List of all coordinates of pixels, given as (x, y),
|
||||
# which matches the return type of points_to_pixel_coords,
|
||||
# even though np.array indexing naturally happens as (y, x)
|
||||
all_pixel_coords = np.zeros((oh*ow, 2), dtype = 'int')
|
||||
a = np.arange(oh*ow, dtype = 'int')
|
||||
all_pixel_coords[:,0] = a%ow
|
||||
all_pixel_coords[:,1] = a/ow
|
||||
|
||||
image = np.zeros((ow*oh, rgb_len), dtype = 'uint8')
|
||||
image[to_change] = target_rgbas
|
||||
image = image.reshape((oh, ow, rgb_len))
|
||||
recentered_coords = all_pixel_coords - ul_coords
|
||||
coord_norms = np.linalg.norm(recentered_coords, axis = 1)
|
||||
|
||||
with np.errstate(divide = 'ignore'):
|
||||
ix_coords, iy_coords = [
|
||||
np.divide(
|
||||
dim*np.dot(recentered_coords, vect),
|
||||
np.dot(vect, vect),
|
||||
)
|
||||
for vect, dim in (right_vect, iw), (down_vect, ih)
|
||||
]
|
||||
to_change = reduce(op.and_, [
|
||||
ix_coords >= 0, ix_coords < iw,
|
||||
iy_coords >= 0, iy_coords < ih,
|
||||
])
|
||||
n_to_change = np.sum(to_change)
|
||||
inner_flat_coords = iw*iy_coords[to_change] + ix_coords[to_change]
|
||||
flat_impa = impa.reshape((iw*ih, rgb_len))
|
||||
target_rgbas = flat_impa[inner_flat_coords, :]
|
||||
|
||||
image = image.reshape((ow*oh, rgb_len))
|
||||
image[to_change] = target_rgbas
|
||||
image = image.reshape((oh, ow, rgb_len))
|
||||
self.overlay_rgba_array(image)
|
||||
|
||||
def overlay_rgba_array(self, arr):
|
||||
""" Overlays arr onto self.pixel_array with relevant alphas"""
|
||||
# """ Overlays arr onto self.pixel_array with relevant alphas"""
|
||||
bg, fg = self.pixel_array/255.0, arr/255.0
|
||||
A = 1 - (1 - bg[:,:,3])*(1 - fg[:,:,3])
|
||||
alpha_sum = bg[:,:,3] + fg[:,:,3]
|
||||
for i in range(3):
|
||||
with np.errstate(divide = 'ignore', invalid='ignore'):
|
||||
bg[:,:,i] = reduce(op.add, [
|
||||
np.divide(arr[:,:,i]*arr[:,:,3], alpha_sum)
|
||||
for arr in fg, bg
|
||||
])
|
||||
bg[:,:,3] = A
|
||||
self.pixel_array = (255*bg).astype('uint8')
|
||||
bga, fga = [arr[:,:,3:] for arr in bg, fg]
|
||||
alpha_sum = fga + (1-fga)*bga
|
||||
with np.errstate(divide = 'ignore', invalid='ignore'):
|
||||
bg[:,:,:3] = reduce(op.add, [
|
||||
np.divide(fg[:,:,:3]*fga, alpha_sum),
|
||||
np.divide(bg[:,:,:3]*bga*(1-fga), alpha_sum),
|
||||
])
|
||||
bg[:,:,3:] = 1 - (1 - bga)*(1 - fga)
|
||||
self.pixel_array = (255*bg).astype(self.pixel_array_dtype)
|
||||
|
||||
def align_points_to_camera(self, points):
|
||||
## This is where projection should live
|
||||
|
|
|
@ -6,7 +6,8 @@ DEFAULT_WIDTH = 1920
|
|||
|
||||
LOW_QUALITY_FRAME_DURATION = 1./15
|
||||
MEDIUM_QUALITY_FRAME_DURATION = 1./30
|
||||
PRODUCTION_QUALITY_FRAME_DURATION = 1./60
|
||||
# PRODUCTION_QUALITY_FRAME_DURATION = 1./60
|
||||
PRODUCTION_QUALITY_FRAME_DURATION = 1./30
|
||||
|
||||
#There might be other configuration than pixel_shape later...
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
||||
|
|
|
@ -42,7 +42,7 @@ NO_SCENE_MESSAGE = """
|
|||
|
||||
def get_configuration(sys_argv):
|
||||
try:
|
||||
opts, args = getopt.getopt(sys_argv[1:], 'hlmpwsqao:')
|
||||
opts, args = getopt.getopt(sys_argv[1:], 'hlmpwstqao:')
|
||||
except getopt.GetoptError as err:
|
||||
print str(err)
|
||||
sys.exit(2)
|
||||
|
@ -55,6 +55,8 @@ def get_configuration(sys_argv):
|
|||
"write_to_movie" : False,
|
||||
"save_frames" : False,
|
||||
"save_image" : False,
|
||||
#If -t is passed in (for transparent), this will be RGBA
|
||||
"saved_image_mode": "RGB",
|
||||
"quiet" : False,
|
||||
"write_all" : False,
|
||||
"output_name" : None,
|
||||
|
@ -76,6 +78,8 @@ def get_configuration(sys_argv):
|
|||
config["write_to_movie"] = True
|
||||
if opt == '-s':
|
||||
config["save_image"] = True
|
||||
if opt == '-t':
|
||||
config["saved_image_mode"] = "RGBA"
|
||||
if opt in ['-q', '-a']:
|
||||
config["quiet"] = True
|
||||
if opt == '-a':
|
||||
|
@ -106,7 +110,7 @@ def handle_scene(scene, **config):
|
|||
if config["save_image"]:
|
||||
if not config["write_all"]:
|
||||
scene.show_frame()
|
||||
scene.save_image()
|
||||
scene.save_image(mode = config["saved_image_mode"])
|
||||
|
||||
if config["quiet"]:
|
||||
sys.stdout.close()
|
||||
|
|
|
@ -17,7 +17,8 @@ class ImageMobject(Mobject):
|
|||
"invert" : False,
|
||||
# "use_cache" : True,
|
||||
"height": 2.0,
|
||||
"image_mode" : "RGBA"
|
||||
"image_mode" : "RGBA",
|
||||
"pixel_array_dtype" : "uint8",
|
||||
}
|
||||
def __init__(self, filename_or_array, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
|
@ -27,8 +28,29 @@ class ImageMobject(Mobject):
|
|||
self.pixel_array = np.array(image)
|
||||
else:
|
||||
self.pixel_array = np.array(filename_or_array)
|
||||
self.change_to_rgba_array()
|
||||
Mobject.__init__(self, **kwargs)
|
||||
|
||||
def change_to_rgba_array(self):
|
||||
pa = self.pixel_array
|
||||
if len(pa.shape) == 2:
|
||||
pa = pa.reshape(list(pa.shape) + [1])
|
||||
if pa.shape[2] == 1:
|
||||
pa = pa.repeat(3, axis = 2)
|
||||
if pa.shape[2] == 3:
|
||||
alphas = 255*np.ones(
|
||||
list(pa.shape[:2])+[1],
|
||||
dtype = self.pixel_array_dtype
|
||||
)
|
||||
pa = np.append(pa, alphas, axis = 2)
|
||||
self.pixel_array = pa
|
||||
|
||||
def highlight(self, color, alpha = 1, family = True):
|
||||
rgba = color_to_int_rgba(color, alpha = int(255*alpha))
|
||||
self.pixel_array[:,:] = rgba
|
||||
for submob in self.submobjects:
|
||||
submob.highlight(color, alpha, family)
|
||||
return self
|
||||
|
||||
def init_points(self):
|
||||
#Corresponding corners of image are fixed to these
|
||||
|
@ -52,4 +74,11 @@ class ImageMobject(Mobject):
|
|||
return self
|
||||
|
||||
|
||||
def interpolate_color(self, mobject1, mobject2, alpha):
|
||||
assert(mobject1.pixel_array.shape == mobject2.pixel_array.shape)
|
||||
self.pixel_array = interpolate(
|
||||
mobject1.pixel_array, mobject2.pixel_array, alpha
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class Mobject(object):
|
|||
from camera import Camera
|
||||
camera = Camera()
|
||||
camera.capture_mobject(self)
|
||||
return Image.fromarray(camera.get_image())
|
||||
return camera.get_image()
|
||||
|
||||
def show(self, camera = None):
|
||||
self.get_image(camera = camera).show()
|
||||
|
@ -611,11 +611,11 @@ class Mobject(object):
|
|||
if n_rows is not None:
|
||||
v1 = RIGHT
|
||||
v2 = DOWN
|
||||
n = n_rows
|
||||
n = len(submobs) / n_rows
|
||||
elif n_cols is not None:
|
||||
v1 = DOWN
|
||||
v2 = RIGHT
|
||||
n = n_cols
|
||||
n = len(submobs) / n_cols
|
||||
Group(*[
|
||||
Group(*submobs[i:i+n]).arrange_submobjects(v1, **kwargs)
|
||||
for i in range(0, len(submobs), n)
|
||||
|
|
0
nn/__init__.py
Normal file
0
nn/__init__.py
Normal file
1137
nn/image_map
Normal file
1137
nn/image_map
Normal file
File diff suppressed because one or more lines are too long
85
nn/mnist_loader.py
Normal file
85
nn/mnist_loader.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""
|
||||
mnist_loader
|
||||
~~~~~~~~~~~~
|
||||
|
||||
A library to load the MNIST image data. For details of the data
|
||||
structures that are returned, see the doc strings for ``load_data``
|
||||
and ``load_data_wrapper``. In practice, ``load_data_wrapper`` is the
|
||||
function usually called by our neural network code.
|
||||
"""
|
||||
|
||||
#### Libraries
|
||||
# Standard library
|
||||
import cPickle
|
||||
import gzip
|
||||
|
||||
# Third-party libraries
|
||||
import numpy as np
|
||||
|
||||
def load_data():
|
||||
"""Return the MNIST data as a tuple containing the training data,
|
||||
the validation data, and the test data.
|
||||
|
||||
The ``training_data`` is returned as a tuple with two entries.
|
||||
The first entry contains the actual training images. This is a
|
||||
numpy ndarray with 50,000 entries. Each entry is, in turn, a
|
||||
numpy ndarray with 784 values, representing the 28 * 28 = 784
|
||||
pixels in a single MNIST image.
|
||||
|
||||
The second entry in the ``training_data`` tuple is a numpy ndarray
|
||||
containing 50,000 entries. Those entries are just the digit
|
||||
values (0...9) for the corresponding images contained in the first
|
||||
entry of the tuple.
|
||||
|
||||
The ``validation_data`` and ``test_data`` are similar, except
|
||||
each contains only 10,000 images.
|
||||
|
||||
This is a nice data format, but for use in neural networks it's
|
||||
helpful to modify the format of the ``training_data`` a little.
|
||||
That's done in the wrapper function ``load_data_wrapper()``, see
|
||||
below.
|
||||
"""
|
||||
f = gzip.open('/Users/grant/cs/neural-networks-and-deep-learning/data/mnist.pkl.gz', 'rb')
|
||||
training_data, validation_data, test_data = cPickle.load(f)
|
||||
f.close()
|
||||
return (training_data, validation_data, test_data)
|
||||
|
||||
def load_data_wrapper():
|
||||
"""Return a tuple containing ``(training_data, validation_data,
|
||||
test_data)``. Based on ``load_data``, but the format is more
|
||||
convenient for use in our implementation of neural networks.
|
||||
|
||||
In particular, ``training_data`` is a list containing 50,000
|
||||
2-tuples ``(x, y)``. ``x`` is a 784-dimensional numpy.ndarray
|
||||
containing the input image. ``y`` is a 10-dimensional
|
||||
numpy.ndarray representing the unit vector corresponding to the
|
||||
correct digit for ``x``.
|
||||
|
||||
``validation_data`` and ``test_data`` are lists containing 10,000
|
||||
2-tuples ``(x, y)``. In each case, ``x`` is a 784-dimensional
|
||||
numpy.ndarry containing the input image, and ``y`` is the
|
||||
corresponding classification, i.e., the digit values (integers)
|
||||
corresponding to ``x``.
|
||||
|
||||
Obviously, this means we're using slightly different formats for
|
||||
the training data and the validation / test data. These formats
|
||||
turn out to be the most convenient for use in our neural network
|
||||
code."""
|
||||
tr_d, va_d, te_d = load_data()
|
||||
training_inputs = [np.reshape(x, (784, 1)) for x in tr_d[0]]
|
||||
training_results = [vectorized_result(y) for y in tr_d[1]]
|
||||
training_data = zip(training_inputs, training_results)
|
||||
validation_inputs = [np.reshape(x, (784, 1)) for x in va_d[0]]
|
||||
validation_data = zip(validation_inputs, va_d[1])
|
||||
test_inputs = [np.reshape(x, (784, 1)) for x in te_d[0]]
|
||||
test_data = zip(test_inputs, te_d[1])
|
||||
return (training_data, validation_data, test_data)
|
||||
|
||||
def vectorized_result(j):
|
||||
"""Return a 10-dimensional unit vector with a 1.0 in the jth
|
||||
position and zeroes elsewhere. This is used to convert a digit
|
||||
(0...9) into a corresponding desired output from the neural
|
||||
network."""
|
||||
e = np.zeros((10, 1))
|
||||
e[j] = 1.0
|
||||
return e
|
321
nn/network.py
Normal file
321
nn/network.py
Normal file
|
@ -0,0 +1,321 @@
|
|||
"""
|
||||
network.py
|
||||
~~~~~~~~~~
|
||||
|
||||
A module to implement the stochastic gradient descent learning
|
||||
algorithm for a feedforward neural network. Gradients are calculated
|
||||
using backpropagation. Note that I have focused on making the code
|
||||
simple, easily readable, and easily modifiable. It is not optimized,
|
||||
and omits many desirable features.
|
||||
"""
|
||||
|
||||
#### Libraries
|
||||
# Standard library
|
||||
import random
|
||||
|
||||
# Third-party libraries
|
||||
import numpy as np
|
||||
import os
|
||||
from PIL import Image
|
||||
import cPickle
|
||||
from nn.mnist_loader import load_data_wrapper
|
||||
|
||||
NN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
|
||||
PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases")
|
||||
IMAGE_MAP_DATA_FILE = os.path.join(NN_DIRECTORY, "image_map")
|
||||
# PRETRAINED_DATA_FILE = "/Users/grant/cs/manim/nn/pretrained_weights_and_biases_on_zero"
|
||||
DEFAULT_LAYER_SIZES = [28**2, 16, 16, 10]
|
||||
|
||||
class Network(object):
|
||||
|
||||
def __init__(self, sizes):
|
||||
"""The list ``sizes`` contains the number of neurons in the
|
||||
respective layers of the network. For example, if the list
|
||||
was [2, 3, 1] then it would be a three-layer network, with the
|
||||
first layer containing 2 neurons, the second layer 3 neurons,
|
||||
and the third layer 1 neuron. The biases and weights for the
|
||||
network are initialized randomly, using a Gaussian
|
||||
distribution with mean 0, and variance 1. Note that the first
|
||||
layer is assumed to be an input layer, and by convention we
|
||||
won't set any biases for those neurons, since biases are only
|
||||
ever used in computing the outputs from later layers."""
|
||||
self.num_layers = len(sizes)
|
||||
self.sizes = sizes
|
||||
self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
|
||||
self.weights = [np.random.randn(y, x)
|
||||
for x, y in zip(sizes[:-1], sizes[1:])]
|
||||
|
||||
def feedforward(self, a):
|
||||
"""Return the output of the network if ``a`` is input."""
|
||||
for b, w in zip(self.biases, self.weights):
|
||||
a = sigmoid(np.dot(w, a)+b)
|
||||
return a
|
||||
|
||||
def get_activation_of_all_layers(self, input_a, n_layers = None):
|
||||
if n_layers is None:
|
||||
n_layers = self.num_layers
|
||||
activations = [input_a.reshape((input_a.size, 1))]
|
||||
for bias, weight in zip(self.biases, self.weights)[:n_layers]:
|
||||
last_a = activations[-1]
|
||||
new_a = sigmoid(np.dot(weight, last_a) + bias)
|
||||
new_a = new_a.reshape((new_a.size, 1))
|
||||
activations.append(new_a)
|
||||
return activations
|
||||
|
||||
def SGD(self, training_data, epochs, mini_batch_size, eta,
|
||||
test_data=None):
|
||||
"""Train the neural network using mini-batch stochastic
|
||||
gradient descent. The ``training_data`` is a list of tuples
|
||||
``(x, y)`` representing the training inputs and the desired
|
||||
outputs. The other non-optional parameters are
|
||||
self-explanatory. If ``test_data`` is provided then the
|
||||
network will be evaluated against the test data after each
|
||||
epoch, and partial progress printed out. This is useful for
|
||||
tracking progress, but slows things down substantially."""
|
||||
if test_data: n_test = len(test_data)
|
||||
n = len(training_data)
|
||||
for j in xrange(epochs):
|
||||
random.shuffle(training_data)
|
||||
mini_batches = [
|
||||
training_data[k:k+mini_batch_size]
|
||||
for k in xrange(0, n, mini_batch_size)]
|
||||
for mini_batch in mini_batches:
|
||||
self.update_mini_batch(mini_batch, eta)
|
||||
if test_data:
|
||||
print "Epoch {0}: {1} / {2}".format(
|
||||
j, self.evaluate(test_data), n_test)
|
||||
else:
|
||||
print "Epoch {0} complete".format(j)
|
||||
|
||||
def update_mini_batch(self, mini_batch, eta):
|
||||
"""Update the network's weights and biases by applying
|
||||
gradient descent using backpropagation to a single mini batch.
|
||||
The ``mini_batch`` is a list of tuples ``(x, y)``, and ``eta``
|
||||
is the learning rate."""
|
||||
nabla_b = [np.zeros(b.shape) for b in self.biases]
|
||||
nabla_w = [np.zeros(w.shape) for w in self.weights]
|
||||
for x, y in mini_batch:
|
||||
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
|
||||
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
|
||||
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
|
||||
self.weights = [w-(eta/len(mini_batch))*nw
|
||||
for w, nw in zip(self.weights, nabla_w)]
|
||||
self.biases = [b-(eta/len(mini_batch))*nb
|
||||
for b, nb in zip(self.biases, nabla_b)]
|
||||
|
||||
def backprop(self, x, y):
|
||||
"""Return a tuple ``(nabla_b, nabla_w)`` representing the
|
||||
gradient for the cost function C_x. ``nabla_b`` and
|
||||
``nabla_w`` are layer-by-layer lists of numpy arrays, similar
|
||||
to ``self.biases`` and ``self.weights``."""
|
||||
nabla_b = [np.zeros(b.shape) for b in self.biases]
|
||||
nabla_w = [np.zeros(w.shape) for w in self.weights]
|
||||
# feedforward
|
||||
activation = x
|
||||
activations = [x] # list to store all the activations, layer by layer
|
||||
zs = [] # list to store all the z vectors, layer by layer
|
||||
for b, w in zip(self.biases, self.weights):
|
||||
z = np.dot(w, activation)+b
|
||||
zs.append(z)
|
||||
activation = sigmoid(z)
|
||||
activations.append(activation)
|
||||
# backward pass
|
||||
delta = self.cost_derivative(activations[-1], y) * \
|
||||
sigmoid_prime(zs[-1])
|
||||
nabla_b[-1] = delta
|
||||
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
|
||||
# Note that the variable l in the loop below is used a little
|
||||
# differently to the notation in Chapter 2 of the book. Here,
|
||||
# l = 1 means the last layer of neurons, l = 2 is the
|
||||
# second-last layer, and so on. It's a renumbering of the
|
||||
# scheme in the book, used here to take advantage of the fact
|
||||
# that Python can use negative indices in lists.
|
||||
for l in xrange(2, self.num_layers):
|
||||
z = zs[-l]
|
||||
sp = sigmoid_prime(z)
|
||||
delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
|
||||
nabla_b[-l] = delta
|
||||
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
|
||||
return (nabla_b, nabla_w)
|
||||
|
||||
def evaluate(self, test_data):
|
||||
"""Return the number of test inputs for which the neural
|
||||
network outputs the correct result. Note that the neural
|
||||
network's output is assumed to be the index of whichever
|
||||
neuron in the final layer has the highest activation."""
|
||||
test_results = [(np.argmax(self.feedforward(x)), y)
|
||||
for (x, y) in test_data]
|
||||
return sum(int(x == y) for (x, y) in test_results)
|
||||
|
||||
def cost_derivative(self, output_activations, y):
|
||||
"""Return the vector of partial derivatives \partial C_x /
|
||||
\partial a for the output activations."""
|
||||
return (output_activations-y)
|
||||
|
||||
#### Miscellaneous functions
|
||||
def sigmoid(z):
|
||||
"""The sigmoid function."""
|
||||
return 1.0/(1.0+np.exp(-z))
|
||||
|
||||
def sigmoid_prime(z):
|
||||
"""Derivative of the sigmoid function."""
|
||||
return sigmoid(z)*(1-sigmoid(z))
|
||||
|
||||
def sigmoid_inverse(z):
|
||||
# z = 0.998*z + 0.001
|
||||
assert(np.max(z) <= 1.0 and np.min(z) >= 0.0)
|
||||
z = 0.998*z + 0.001
|
||||
return np.log(np.true_divide(
|
||||
1.0, (np.true_divide(1.0, z) - 1)
|
||||
))
|
||||
|
||||
def get_pretrained_network():
|
||||
data_file = open(PRETRAINED_DATA_FILE)
|
||||
weights, biases = cPickle.load(data_file)
|
||||
sizes = [w.shape[1] for w in weights]
|
||||
sizes.append(weights[-1].shape[0])
|
||||
network = Network(sizes)
|
||||
network.weights = weights
|
||||
network.biases = biases
|
||||
return network
|
||||
|
||||
def save_pretrained_network(epochs = 30, mini_batch_size = 10, eta = 3.0):
|
||||
network = Network(sizes = DEFAULT_LAYER_SIZES)
|
||||
training_data, validation_data, test_data = load_data_wrapper()
|
||||
network.SGD(training_data, epochs, mini_batch_size, eta)
|
||||
weights_and_biases = (network.weights, network.biases)
|
||||
data_file = open(PRETRAINED_DATA_FILE, mode = 'w')
|
||||
cPickle.dump(weights_and_biases, data_file)
|
||||
data_file.close()
|
||||
|
||||
def test_network():
|
||||
network = get_pretrained_network()
|
||||
training_data, validation_data, test_data = load_data_wrapper()
|
||||
n_right, n_wrong = 0, 0
|
||||
for test_in, test_out in test_data[:30]:
|
||||
if np.argmax(network.feedforward(test_in)) == test_out:
|
||||
n_right += 1
|
||||
else:
|
||||
n_wrong += 1
|
||||
print float(n_right)/(n_right + n_wrong)
|
||||
|
||||
def layer_to_image_array(layer):
|
||||
w = int(np.ceil(np.sqrt(len(layer))))
|
||||
if len(layer) < w**2:
|
||||
layer = np.append(layer, np.zeros(w**2 - len(layer)))
|
||||
layer = layer.reshape((w, w))
|
||||
# return Image.fromarray((255*layer).astype('uint8'))
|
||||
return (255*layer).astype('int')
|
||||
|
||||
def maximizing_input(network, layer_index, layer_vect, n_steps = 100, seed_guess = None):
|
||||
pre_sig_layer_vect = sigmoid_inverse(layer_vect)
|
||||
weights, biases = network.weights, network.biases
|
||||
# guess = np.random.random(weights[0].shape[1])
|
||||
if seed_guess is not None:
|
||||
pre_sig_guess = sigmoid_inverse(seed_guess.flatten())
|
||||
else:
|
||||
pre_sig_guess = np.random.randn(weights[0].shape[1])
|
||||
norms = []
|
||||
for step in xrange(n_steps):
|
||||
activations = network.get_activation_of_all_layers(
|
||||
sigmoid(pre_sig_guess), layer_index
|
||||
)
|
||||
jacobian = np.diag(sigmoid_prime(pre_sig_guess).flatten())
|
||||
for W, a, b in zip(weights, activations, biases):
|
||||
jacobian = np.dot(W, jacobian)
|
||||
a = a.reshape((a.size, 1))
|
||||
sp = sigmoid_prime(np.dot(W, a) + b)
|
||||
jacobian = np.dot(np.diag(sp.flatten()), jacobian)
|
||||
gradient = np.dot(
|
||||
np.array(layer_vect).reshape((1, len(layer_vect))),
|
||||
jacobian
|
||||
).flatten()
|
||||
norm = np.linalg.norm(gradient)
|
||||
if norm == 0:
|
||||
break
|
||||
norms.append(norm)
|
||||
old_pre_sig_guess = np.array(pre_sig_guess)
|
||||
pre_sig_guess += 0.1*gradient
|
||||
print np.linalg.norm(old_pre_sig_guess - pre_sig_guess)
|
||||
print ""
|
||||
return sigmoid(pre_sig_guess)
|
||||
|
||||
def save_organized_images(n_images_per_number = 10):
|
||||
training_data, validation_data, test_data = load_data_wrapper()
|
||||
image_map = dict([(k, []) for k in range(10)])
|
||||
for im, output_arr in training_data:
|
||||
if min(map(len, image_map.values())) >= n_images_per_number:
|
||||
break
|
||||
value = int(np.argmax(output_arr))
|
||||
if len(image_map[value]) >= n_images_per_number:
|
||||
continue
|
||||
image_map[value].append(im)
|
||||
data_file = open(IMAGE_MAP_DATA_FILE, mode = 'w')
|
||||
cPickle.dump(image_map, data_file)
|
||||
data_file.close()
|
||||
|
||||
def get_organized_images():
|
||||
data_file = open(IMAGE_MAP_DATA_FILE, mode = 'r')
|
||||
image_map = cPickle.load(data_file)
|
||||
data_file.close()
|
||||
return image_map
|
||||
|
||||
# def maximizing_input(network, layer_index, layer_vect):
|
||||
# if layer_index == 0:
|
||||
# return layer_vect
|
||||
# W = network.weights[layer_index-1]
|
||||
# n = max(W.shape)
|
||||
# W_square = np.identity(n)
|
||||
# W_square[:W.shape[0], :W.shape[1]] = W
|
||||
# zeros = np.zeros((n - len(layer_vect), 1))
|
||||
# vect = layer_vect.reshape((layer_vect.shape[0], 1))
|
||||
# vect = np.append(vect, zeros, axis = 0)
|
||||
# b = np.append(network.biases[layer_index-1], zeros, axis = 0)
|
||||
# prev_vect = np.dot(
|
||||
# np.linalg.inv(W_square),
|
||||
# (sigmoid_inverse(vect) - b)
|
||||
# )
|
||||
# # print layer_vect, sigmoid(np.dot(W, prev_vect)+b)
|
||||
# print W.shape
|
||||
# prev_vect = prev_vect[:W.shape[1]]
|
||||
# prev_vect /= np.max(np.abs(prev_vect))
|
||||
# # prev_vect /= 1.1
|
||||
# return maximizing_input(network, layer_index - 1, prev_vect)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
85
nn/pretrained_weights_and_biases
Normal file
85
nn/pretrained_weights_and_biases
Normal file
File diff suppressed because one or more lines are too long
63
nn/pretrained_weights_and_biases_on_zero
Normal file
63
nn/pretrained_weights_and_biases_on_zero
Normal file
File diff suppressed because one or more lines are too long
710
nn/scenes.py
Normal file
710
nn/scenes.py
Normal file
|
@ -0,0 +1,710 @@
|
|||
import sys
|
||||
import os.path
|
||||
|
||||
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
|
||||
|
||||
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_fill_color" : GREEN,
|
||||
"edge_color" : LIGHT_GREY,
|
||||
"edge_stroke_width" : 2,
|
||||
"max_shown_neurons" : 16,
|
||||
"brace_for_large_layers" : 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,
|
||||
)
|
||||
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
|
||||
if len(av) > n_neurons:
|
||||
av = av[av > 0][:n_neurons]
|
||||
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.neuron_fill_color,
|
||||
width = 1.5*self.edge_stroke_width
|
||||
)
|
||||
return map(ShowCreationThenDestruction, 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.next_to(neuron, RIGHT, SMALL_BUFF)
|
||||
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):
|
||||
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)
|
||||
|
||||
def show_activation_of_layer(self, layer_index, activation_vector):
|
||||
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
|
||||
)
|
||||
self.play(*anims)
|
||||
|
||||
###############################
|
||||
|
||||
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"
|
||||
),
|
||||
run_time = 1
|
||||
)
|
||||
self.play(ShowCreation(
|
||||
network_mob.edge_groups,
|
||||
submobject_mode = "lagged_start",
|
||||
run_time = 2,
|
||||
lag_factor = 8,
|
||||
rate_func = None,
|
||||
))
|
||||
self.play(self.teacher.change, "plain")
|
||||
in_vect = np.random.random(self.network.sizes[0])
|
||||
self.feed_forward(in_vect)
|
||||
self.dither()
|
||||
|
||||
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()
|
||||
|
||||
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):
|
||||
rect = SurroundingRectangle(self.words[0][1], color = YELLOW)
|
||||
self.network_mob.neuron_fill_color = YELLOW
|
||||
|
||||
layer = self.network_mob.layers[-1]
|
||||
activation = np.zeros(len(layer))
|
||||
activation[1] = 1.0
|
||||
active_layer = self.network_mob.get_active_layer(
|
||||
-1, activation
|
||||
)
|
||||
|
||||
self.play(ShowCreation(rect))
|
||||
self.play(Transform(layer, active_layer))
|
||||
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):
|
||||
def construct(self):
|
||||
training_data, validation_data, test_data = load_data_wrapper()
|
||||
for data in test_data[:1]:
|
||||
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)
|
||||
rect = SurroundingRectangle(image, color = BLUE)
|
||||
|
||||
self.play(FadeIn(image), FadeIn(rect))
|
||||
print in_vect.shape
|
||||
# self.feed_forward(in_vect)
|
||||
# self.play(FadeOut(image))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###
|
||||
|
||||
def add_network(self):
|
||||
self.network_mob = MNistNetworkMobject()
|
||||
self.network = self.network_mob.neural_network
|
||||
self.add(self.network_mob)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -90,10 +90,13 @@ class Scene(object):
|
|||
self.camera = camera
|
||||
|
||||
def get_frame(self):
|
||||
return np.array(self.camera.get_pixel_array())
|
||||
|
||||
def get_image(self):
|
||||
return self.camera.get_image()
|
||||
|
||||
def set_camera_image(self, pixel_array):
|
||||
self.camera.set_image(pixel_array)
|
||||
def set_camera_pixel_array(self, pixel_array):
|
||||
self.camera.set_pixel_array(pixel_array)
|
||||
|
||||
def set_camera_background(self, background):
|
||||
self.camera.set_background(background)
|
||||
|
@ -113,7 +116,7 @@ class Scene(object):
|
|||
self.mobjects,
|
||||
)
|
||||
if background is not None:
|
||||
self.set_camera_image(background)
|
||||
self.set_camera_pixel_array(background)
|
||||
else:
|
||||
self.reset_camera()
|
||||
|
||||
|
@ -439,19 +442,21 @@ class Scene(object):
|
|||
|
||||
def show_frame(self):
|
||||
self.update_frame()
|
||||
Image.fromarray(self.get_frame()).show()
|
||||
self.get_image().show()
|
||||
|
||||
def preview(self):
|
||||
TkSceneRoot(self)
|
||||
|
||||
def save_image(self, name = None):
|
||||
def save_image(self, name = None, mode = "RGB"):
|
||||
path = os.path.join(self.output_directory, "images")
|
||||
file_name = (name or str(self)) + ".png"
|
||||
full_path = os.path.join(path, file_name)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
self.update_frame()
|
||||
Image.fromarray(self.get_frame()).save(full_path)
|
||||
image = self.get_image()
|
||||
image = image.convert(mode)
|
||||
image.save(full_path)
|
||||
|
||||
def get_movie_file_path(self, name, extension):
|
||||
file_path = os.path.join(self.output_directory, name)
|
||||
|
|
|
@ -34,7 +34,7 @@ class TkSceneRoot(Tkinter.Tk):
|
|||
self.mainloop()
|
||||
|
||||
def show_new_image(self, frame):
|
||||
image = Image.fromarray(frame).convert('RGB')
|
||||
image = Image.fromarray(frame.astype('uint8')).convert('RGB')
|
||||
photo = ImageTk.PhotoImage(image)
|
||||
self.canvas.delete(Tkinter.ALL)
|
||||
self.canvas.create_image(
|
||||
|
|
|
@ -99,10 +99,10 @@ class ZoomedScene(Scene):
|
|||
return frame
|
||||
|
||||
def set_camera_image(self, pixel_array):
|
||||
self.camera.set_image(pixel_array)
|
||||
self.camera.set_pixel_array(pixel_array)
|
||||
if self.zoom_activated:
|
||||
(up, left), (down, right) = self.zoomed_canvas_pixel_indices
|
||||
self.zoomed_camera.set_image(pixel_array[left:right, up:down])
|
||||
self.zoomed_camera.set_pixel_array(pixel_array[left:right, up:down])
|
||||
|
||||
def set_camera_background(self, background):
|
||||
self.set_camera_image(self, background)
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
\usepackage{wasysym}
|
||||
\usepackage{ragged2e}
|
||||
\usepackage{physics}
|
||||
\usepackage{xcolor}
|
||||
\usepackage[UTF8]{ctex}
|
||||
|
||||
|
||||
\begin{document}
|
||||
|
|
|
@ -648,7 +648,13 @@ class TeacherStudentsScene(PiCreatureScene):
|
|||
return self.pi_creature_thinks(student, *content, **kwargs)
|
||||
|
||||
def change_student_modes(self, *modes, **kwargs):
|
||||
added_anims = kwargs.get("added_anims", [])
|
||||
added_anims = kwargs.pop("added_anims", [])
|
||||
self.play(
|
||||
self.get_student_changes(*modes, **kwargs),
|
||||
*added_anims
|
||||
)
|
||||
|
||||
def get_student_changes(self, *modes, **kwargs):
|
||||
pairs = zip(self.get_students(), modes)
|
||||
pairs = [(s, m) for s, m in pairs if m is not None]
|
||||
start = VGroup(*[s for s, m in pairs])
|
||||
|
@ -656,13 +662,11 @@ class TeacherStudentsScene(PiCreatureScene):
|
|||
if "look_at_arg" in kwargs:
|
||||
for pi in target:
|
||||
pi.look_at(kwargs["look_at_arg"])
|
||||
self.play(
|
||||
Transform(
|
||||
start, target,
|
||||
submobject_mode = "lagged_start",
|
||||
run_time = 2
|
||||
),
|
||||
*added_anims
|
||||
submobject_mode = kwargs.get("submobject_mode", "lagged_start")
|
||||
return Transform(
|
||||
start, target,
|
||||
submobject_mode = submobject_mode,
|
||||
run_time = 2
|
||||
)
|
||||
|
||||
def zoom_in_on_thought_bubble(self, bubble = None, radius = SPACE_HEIGHT+SPACE_WIDTH):
|
||||
|
|
Loading…
Add table
Reference in a new issue