Вы наверняка когда-то испытывали трудности в понимании математических концепций алгоритмов машинного обучения и для лучшего понимания темы пользовались обучающим ресурсом 3Blue1Brown. 3Blue1Brown — известный математический YouTube-канал, который ведёт Грант Сандерсон. Многим нравится 3Blue1Brown за прекрасные объяснения Гранта и великолепные анимации.

21 мая стартует новый поток курса о математике для Data Science. Специально к его запуску мы делимся переводом, в котором автор решил рассказать, как делать анимации, подобные анимациям на канале 3Blue1Brown, чтобы вы могли иллюстрировать свои идеи и рассуждения о математике и не только.


Я решил рассказать о таких анимациях, чтобы вы сами могли создавать что-то подобное и объяснять различные научные рассуждения своим товарищам, менеджерам или просто интересующимся людям.

К счастью, Грант создал пакет manim на Python, этот пакет позволяет создавать математические анимации, или "живые" картинки. Из этой статьи вы узнаете, как с помощью пакета manim создавать математические анимации, подобные приведённым ниже.

Что такое Manim?

Manim — это средство для создания точных анимаций, с помощью которых поясняются разные математические операции. Существуют две версии manim. Одна была создана Грантом, а другая разработана сообществом Manim.

Поскольку версия, поддерживаемая сообществом Manim, обновляется чаще и протестирована лучше версии Гранта, мы будем работать с версией сообщества. В документации рассказывается, какие зависимости пакета нужно установить. После установки зависимостей введите команду:

pip install manim

Приступаем

Создадим увеличивающийся из центра синий квадрат

Код для создания анимации определяется внутри метода construct получаемого из Scene класса.

from manim import * 

class PointMovingOnShapes(Scene):
    def construct(self):
        square = Square(color=BLUE) # Create a square
        square.flip(RIGHT) # Flip the square to the right
        square.rotate(-3 * TAU / 8) # Rotate the square -3/8 * 2*PI 

         # Play the animation of a square growing from the center
        self.play(GrowFromCenter(square))

Сохраните этот скрипт с именем start.py. Запустите команду ниже, чтобы сгенерировать видео из скрипта.

manim -p -ql start.py PointMovingOnShapes

Видео с именем PointMovingOnShapes.mp4 будет сохранено в локальном каталоге. У вас должно получиться примерно следующее:

Пояснения к опциям:

  • -p: после генерации видео воспроизвести его один раз;

  • -ql: сгенерировать видео с низким качеством. 

Для генерирования видео с высоким качеством используется опция -qh.

Чтобы вместо видео сгенерировать GIF-анимацию, добавьте к команде опцию -i:

manim -p -ql -i start.py PointMovingOnShapes

Превращение квадрата в круг

Создать обычный квадрат — самая элементарная задача. Давайте её немного усложним. Превратим этот квадрат в круг.

Код для создания приведённой выше анимации:

from manim import * 

class PointMovingOnShapes(Scene):
    def construct(self):
        
        # Create a square
        square = Square(color=BLUE)
        square.flip(RIGHT)
        square.rotate(-3 * TAU / 8)
        
        # Create a circle
        circle = Circle()
        circle.set_fill(PINK, opacity=0.5) # set the color and transparency
      
        # Create animations
        self.play(GrowFromCenter(square))
        self.play(Transform(square, circle))  # turn the square into a circle
       
        self.wait() # wait for some seconds

Полный набор форм фигур можно найти здесь.

Настройка параметров Manim

Фон можно сделать не чёрным, а серым:

Для этого используется параметр config.background_color.

from manim import * 

config.background_color = DARK_GRAY

Здесь рассказывается о других способах кастомизации manim.

Что ещё можно сделать с помощью Manim?

Представление математических уравнений с подвижной рамкой

Также можно создавать анимации, выводящие математические уравнения с подвижной рамкой, например такие:

class MovingFrame(Scene):
     def construct(self):
        # Write equations
        equation = MathTex("2x^2-5x+2", "=", "(x-2)(2x-1)")

        # Create animation
        self.play(Write(equation))

        # Add moving frames
        framebox1 = SurroundingRectangle(equation[0], buff=.1)
        framebox2 = SurroundingRectangle(equation[2], buff=.1)

        # Create animations
        self.play(Create(framebox1))  # creating the frame

        self.wait()
        # replace frame 1 with frame 2
        self.play(ReplacementTransform(framebox1, framebox2))
    
        self.wait()

Или записывать шаги решения уравнения:

class MathematicalEquation(Scene):
    def construct(self):
    
        # Write equations
        equation1 = MathTex("2x^2-5x+2")
        eq_sign_1 = MathTex("=")
        equation2 = MathTex("2x^2-4x-x+2")
        eq_sign_2 = MathTex("=")
        equation3 = MathTex("(x-2)(2x-1)")

        # Put each equation or sign in the appropriate positions
        equation1.next_to(eq_sign_1, LEFT)
        equation2.next_to(eq_sign_1, RIGHT)
        
        eq_sign_2.shift(DOWN)
        equation3.shift(DOWN)
        
        # Align bottom equations with the top equations
        eq_sign_2.align_to(eq_sign_1, LEFT)
        equation3.align_to(equation2, LEFT)

        # Group equations and sign
        eq_group = VGroup(equation1, eq_sign_1, equation2, eq_sign_2, equation3)

        # Create animation
        self.play(Write(eq_group))
        
        self.wait()

Перемещение и панорамирование

Также можно направлять "камеру" на отдельные части уравнения и увеличивать масштабы отображения таких мест с помощью класса, унаследованного от объекта MovingCameraScene.

 class MovingAndZoomingCamera(MovingCameraScene):
    def construct(self):
        # Write equations
        equation = MathTex("2x^2-5x+2", "=", "(x-2)(2x-1)")

        self.add(equation)
        self.play(self.camera.frame.animate.move_to(equation[0]).set(width=equation[0].width*2))
        self.wait(0.3)
        self.play(self.camera.frame.animate.move_to(equation[2]).set(width=equation[2].width*2))

Построение графиков

Пакет manim также можно использовать для создания аннотированных графиков, например таких:

class Graph(GraphScene):
    def __init__(self, **kwargs):
        GraphScene.__init__(
            self,
            x_min=-3.5,
            x_max=3.5,
            y_min=-5,
            y_max=5,
            graph_origin=ORIGIN,
            axes_color=BLUE,
            x_labeled_nums=range(-4, 4, 2), # x tickers
            y_labeled_nums=range(-5, 5, 2), # y tickers
            **kwargs
        )
    
    def construct(self):
        self.setup_axes(animate=False)

        # Draw graphs
        func_graph_cube = self.get_graph(lambda x: x**3, RED)
        func_graph_ncube = self.get_graph(lambda x: -x**3, GREEN)

        # Create labels
        graph_lab = self.get_graph_label(func_graph_cube, label="x^3")
        graph_lab2 = self.get_graph_label(func_graph_ncube, label="-x^3", x_val=-3)

        # Create a vertical line
        vert_line = self.get_vertical_line_to_graph(1.5, func_graph_cube, color=YELLOW)
        label_coord = self.input_to_graph_point(1.5, func_graph_cube)
        text = MathTex(r"x=1.5")
        text.next_to(label_coord)
       
        self.add(func_graph_cube, func_graph_ncube, graph_lab, graph_lab2, vert_line, text)
        self.wait()

Если нужно получить изображение последнего кадра сцены, добавьте к команде опцию -s:

manim -p -qh -s more.py Graph

Также можно анимировать процесс добавления осей: для этого нужно установить параметр animate=True.

 def construct(self):
        self.setup_axes(animate=True)
        ################### The below is the same as above ################### 
manim -p -qh more.py Graph

Совместное перемещение объектов

Для группировки различных объектов Manim и их совместного перемещения можно воспользоваться VGroup:

class GroupCircles(Scene):
    def construct(self):

        # Create circles
        circle_green = Circle(color=GREEN)
        circle_blue = Circle(color=BLUE)
        circle_red = Circle(color=RED)
        
        # Set initial positions
        circle_green.shift(LEFT)
        circle_blue.shift(RIGHT)
        
        # Create 2 different groups
        gr = VGroup(circle_green, circle_red)
        gr2 = VGroup(circle_blue)
        self.add(gr, gr2) # add two groups to the scene
        self.wait()

        self.play((gr + gr2).animate.shift(DOWN)) # shift 2 groups down
        
        self.play(gr.animate.shift(RIGHT)) # move only 1 group
        self.play(gr.animate.shift(UP))

        self.play((gr + gr2).animate.shift(RIGHT)) # shift 2 groups to the right
        self.play(circle_red.animate.shift(RIGHT))
        self.wait()

Отслеживание перемещения

Для отображения следа движущегося объекта можно воспользоваться TracedPath:

 class TracedPathExample(Scene):
    def construct(self):
        # Create circle and dot
        circ = Circle(color=BLUE).shift(4*LEFT)
        dot = Dot(color=BLUE).move_to(circ.get_start())

        # Group dot and circle
        rolling_circle = VGroup(circ, dot)
        trace = TracedPath(circ.get_start)

        rolling_circle.add_updater(lambda m: m.rotate(-0.3))  # Rotate the circle

        self.add(trace, rolling_circle) # add trace and rolling circle to the scene

        # Shift the circle to 8*RIGHT
        self.play(rolling_circle.animate.shift(8*RIGHT), run_time=4, rate_func=linear)

Подводя итог

Пакет manim работает с тремя видами объектов:

  • Mobjects: объекты, которые могут выводиться на экран, например Circle (Окружность), Square (Квадрат), Matrix (Матрица), Angle (Угол) и пр.

  • Сцены: "холсты" для создания анимаций, например Scene, MovingCameraScene и пр.

  • Анимации: анимации, применяемые к объектам Mobjects, например Write (Записать), Create (Создать), GrowFromCenter (Увеличить от центра), Transform (Преобразовать) и пр.

Пакет manim имеет и другую функциональность, но это тема для отдельной статьи. Учиться лучше всего на практике, поэтому я рекомендую попрактиковаться на примерах из этой статьи, а также ознакомиться с руководством по использованию manim. Исходный код из этой статьи можно найти здесь.

При помощи manim красота математики приобретает вещественное выражение. Если хочется подтянуть математику — можете обратить внимание на курс о математике для Data Science. На нём мы рассматриваем применение математических и статистических закономерностей в машинном обучении и нейронных сетях, чтобы вы в дальнейшем могли работать не только с типовыми моделями и архитектурами.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы