Градиентный спуск, как обучаются нейронные сети | Глава 2. Глубокое обучение

Краткое содержание видео “Как обучаются нейронные сети”

Введение:

В этом видео рассказывается о структуре нейронной сети и о том, как она обучается.
Цель обучения - классифицировать рукописные цифры.
Сеть имеет 784 входных нейрона (по одному на каждый пиксель изображения), два скрытых слоя с 16 нейронами каждый и 10 выходных нейронов (по одному на каждую цифру).

Как работает сеть:

Каждый нейрон в сети вычисляет сумму взвешенных входных значений, добавляет к ней смещение и затем применяет функцию активации.
Функция активации определяет, насколько сильно нейрон "активируется".
Выходной слой сети интерпретируется как вероятность того, что входное изображение принадлежит каждой из 10 цифр.

Как обучается сеть:

Сеть обучается на наборе данных изображений рукописных цифр, каждое из которых помечено правильной цифрой.
Алгоритм обучения пытается найти набор весов и смещений, который минимизирует функцию стоимости.
Функция стоимости измеряет, насколько хорошо сеть предсказывает правильную цифру для каждого изображения в обучающем наборе.
Алгоритм обучения использует метод, называемый "градиентный спуск", для поиска минимума функции стоимости.
Градиентный спуск - это итеративный процесс, который постепенно подстраивает веса и смещения в направлении, приводящем к уменьшению функции стоимости.

Результаты:

Описанная сеть может правильно классифицировать около 96% изображений рукописных цифр, которые она никогда раньше не видела.
Это достигается без явного программирования сети для поиска каких-либо конкретных признаков в изображениях.

Ограничения:

Сеть не всегда может объяснить, почему она дает определенный ответ.
Сеть может быть обманута специально созданными изображениями.

Дальнейшие исследования:

Исследователи изучают, как улучшить интерпретируемость нейронных сетей.
Исследователи также изучают, как сделать нейронные сети более устойчивыми к обману.

Заключение:

Нейронные сети - это мощный инструмент для машинного обучения, который может быть использован для решения сложных задач.
Однако важно понимать ограничения нейронных сетей и использовать их ответственно.

Примечание:

В видео также обсуждается исследование, в котором нейронная сеть обучалась на наборе данных изображений рукописных цифр, которые были случайным образом помечены.
Сеть смогла достичь такой же точности, как и при обучении на правильно помеченных данных, просто запомнив случайные соответствия.
Это исследование поднимает вопрос о том, действительно ли нейронные сети, обученные с использованием функции потерь, обучаются осмысленным представлениям о мире.

https://gemini.google.com

Расшифровка видео

Introduction
0:04
В последнем видео я изложил структуру нейронной сети
0:07
Здесь я дам краткое описание структуры, чтобы освежить память
0:10
И тогда у меня есть две основные цели для этого видео. Первый – представить идею градиентного спуска,
0:15
которая лежит в основе не только того, как учатся нейронные сети,
0:18
но как работает еще много других машин.
0:20
Затем, после этого, мы собираемся еще немного разобраться в том, как эта конкретная сеть работает
0:24
И то, что скрывают эти скрытые слои нейронов, фактически ищет
0:28
Напоминаем, что наша цель – классический пример распознавания рукописного знака
Handwritten digit recognition
0:34
привет мир нейронных сетей
0:36
эти цифры отображаются на сетке размером 28 х 28 пикселей каждый пиксель с некоторым значением оттенков серого между 0 и 1
0:43
это то, что определяют активации
0:46
784 нейронов во входном слое сети и
0:50
Тогда активация для каждого нейрона в следующих слоях основана на взвешенной сумме
0:56
Всех активаций в предыдущем слое плюс некоторое специальное значение, называемое смещением
1:01
затем вы составляете эту сумму с помощью какой-либо другой функции, такой как сигмовидное уплотнение или
1:06
ReLu, описание которого я предоставил в последнем видео
1:09
В целом, учитывая мой произвольный выбор двух скрытых слоев с 16 нейронами, каждая из которых имеет около
1:16
13 000 весов и смещений, которые мы можем настроить, и именно эти значения определяют, что именно сеть, которую вы видите, действительно делает
1:24
Тогда что мы имеем в виду, когда говорим, что эта сеть классифицирует данную цифру
1:28
Самый яркий из этих 10 нейронов в конечном слое соответствует этой цифре
1:33
И запомните, что мотивация, которую мы имели в виду здесь для многоуровневой структуры, заключалась в том, что, возможно,
1:38
Второй слой может распознать кусочки, а третий слой может набирать рисунки, такие как петли и линии
1:44
И последний мог просто объединить эти шаблоны, чтобы распознавать цифры
1:49
Итак, здесь мы узнаем, как сеть обучается
How the network learns
1:52
Нам нужен алгоритм, в котором вы cможете показать этой сети целую кучу обучающих данных,
1:57
которая поставляется в виде кучи разных изображений рукописных цифр вместе с пометками, что это за цифры
2:03
Он будет корректировать эти
2:05
13000 весов и смещений для того, чтобы улучшить его производительность по данным обучения
2:10
Надеемся, что эта слоистая структура усвоит то,чему ее обучают
2:14
и обобщит изображения, выходящие за рамки обучающих данных
2:16
И мы это протестируем после обучения сети
2:20
Вы показываете алгоритму дополнительные изображения, которые он раньше не видел, и увидите, насколько точно он классифицирует эти новые изображения
2:31
К счастью для нас и что делает этот такой распространенный пример, чтобы начать с того, что хорошие люди, стоящие за базой MNIST,
2:37
собрал коллекцию из десятков тысяч рукописных цифровых изображений, каждый из которых помечен цифрами, которые они должны были и
2:44
Это провокационно, так как описывать машину как обучение, когда вы действительно видите, как это работает
Emne Stata
2:49
Он чувствует себя намного меньше, чем какая-то сумасшедшая научно-фантастическая предпосылка, а также гораздо больше,
2:55
Я имею в виду, что в основном это сводится к поиску минимума определенной функции
3:01
Помните концептуально, что мы думаем о каждом нейроне как о соединении
3:05
для всех нейронов в предыдущем слое, а веса в взвешенной сумме, определяющие ее активацию, выглядят как
3:12
сильные стороны этих связей
3:14
И предвзятость – это некоторый признак того, что этот нейрон имеет тенденцию быть активным или неактивным и начинать
3:20
Мы просто собираемся инициализировать все эти веса и предубеждения, совершенно бесполезно сказать, что эта сеть будет выполнять
3:26
довольно ужасно на данном примере обучения, поскольку он просто делает что-то случайное, например, вы кормите этим изображением 3 и
3:33
Выходной уровень – это просто беспорядок
3:36
Итак, что вы делаете, вы определяете функцию стоимости как способ сообщить компьютеру: «Нет плохого компьютера!
3:42
Этот вывод должен иметь активацию, которая равна нулю для большинства нейронов, но для этого нейрона то, что вы дали мне, – это полный мусор “
3:51
Сказать, что немного математически то, что вы делаете, это добавить квадраты различий между
3:56
каждой из этих активированных выходов мусора и значения, которое вы хотите, чтобы они имели и
4:01
Это то, что мы будем называть стоимостью одного примера обучения
4:05
Обратите внимание: эта сумма невелика, когда сеть уверенно классифицирует изображение правильно
4:12
Но это большое, когда сеть кажется, что она не знает, что она делает
4:18
Итак, что вы делаете, считайте среднюю стоимость по всем десяткам тысяч примеров обучения в вашем распоряжении
4:27
Эта средняя стоимость – это наша мера за то, насколько паршивая сеть и насколько плохо компьютер должен чувствовать, и это сложная вещь
4:34
Помните, как сама сеть была в основном функцией, которая
4:39
784 числа вводит значения пикселей и выплескивает десять чисел в качестве вывода и в некотором смысле
4:45
Он параметризуется всеми этими весами и смещениями
4:49
В то время как функция стоимости представляет собой слой сложности, поверх которого он принимает в качестве своего входного
4:54
те тринадцать тысяч или около того весов и предубеждений, и он выплескивает один номер, описывающий, насколько плохи эти веса и предубеждения и
5:02
Способ его определения зависит от поведения сети по всем десяткам тысяч учебных данных
5:09
Об этом много думать
Local minimums
5:12
Но просто рассказывая компьютеру, какая дрянная работа, это не очень полезно
5:15
Вы хотите сказать, как изменить эти веса и предубеждения, чтобы он стал лучше?
5:20
Сделать это проще, чем пытаться представить себе функцию с 13 000 входов
5:25
Представьте себе простую функцию, которая имеет один номер в качестве входного и одно число в качестве вывода
5:30
Как вы находите вход, который минимизирует значение этой функции?
5:36
Студенты исчисления будут знать, что иногда вы можете понять, что минимум явно
5:40
Но это не всегда возможно для действительно сложных функций
5:44
Конечно, не в тринадцати тысячной входной версии этой ситуации для нашей сумасшедшей сложной функции стоимости нейронной сети
5:52
Более гибкая тактика заключается в том, чтобы начать с любого старого ввода и выяснить, в каком направлении вы должны шагнуть, чтобы сделать этот вывод ниже
6:00
В частности, если вы можете определить наклон функции, в которой вы находитесь
6:04
Затем сдвиньтесь влево, если этот наклон положителен и сдвиньте вход вправо, если этот наклон отрицательный
6:12
Если вы делаете это повторно в каждой точке, проверяя новый наклон и делая соответствующий шаг
6:16
вы подходите к локальному минимуму функции и
6:20
изображение, которое вы, возможно, имеете в виду, это мяч, спускающийся с холма и
6:24
Обратите внимание даже на эту очень упрощенную единую функцию ввода, есть много возможных долин, которые вы можете приземлиться
6:31
В зависимости от того, какой случайный ввод вы начинаете, и нет гарантии, что местный минимум
6:36
Вы приземляетесь, это будет наименьшее возможное значение функции стоимости
6:39
Это также будет перенесено на наш случай с нейронной сетью, и я также хочу, чтобы вы заметили
6:44
Как сделать размеры шага пропорциональными наклону
6:47
Затем, когда наклон сглаживается к минимуму, ваши шаги становятся все меньше и меньше, и этот вид помогает вам избежать перерегулирования
Downhill direction
6:55
Вместо того, чтобы усложнять сложность, вместо этого вы можете использовать функцию с двумя входами и одним выходом
7:01
Вы можете представить входное пространство как плоскость XY, а функцию стоимости, как графику, как поверхность над ней
7:08
Теперь вместо того, чтобы спрашивать о наклоне функции, вы должны спросить, в каком направлении вы должны входить в это пространство ввода?
7:15
Чтобы быстрее уменьшить выход функции, другими словами. Каково направление спуска?
7:22
И снова полезно подумать о том, как мяч катится вниз по холму
7:26
Те из вас, кто знаком с многовариантным исчислением, будут знать, что градиент функции дает вам направление самого крутого подъема
7:34
В основном, в каком направлении вы должны быстро активировать функцию
7:39
естественно, принимая отрицательное значение этого градиента, вы получаете направление на шаг, который быстрее уменьшает функцию и
7:47
Даже более того, длина этого вектора градиента на самом деле является показателем того, насколько крутым является самый крутой наклон
7:54
Теперь, если вы не знакомы с многовариантным исчислением
7:56
И вы хотите узнать больше о том, какую часть работы я сделал для Академии Хан по этой теме
8:00
Честно говоря, хотя все, что имеет значение для вас и меня прямо сейчас
8:03
Это в принципе существует способ вычислить этот вектор. Этот вектор, который сообщает вам, что
8:09
Наклон вниз, и как круто это будет, вы будете в порядке, если это все, что вы знаете, и вы не прочны на деталях
8:16
потому что, если вы можете получить, что алгоритм минимизации функции состоит в том, чтобы вычислить это направление градиента, тогда сделайте небольшой шаг вниз и
8:24
Просто повторите это снова и снова
8:27
Это та же основная идея для функции, которая имеет 13 000 входов вместо двух входов, представляющих собой организацию всего
8:35
13 000 весов и смещения нашей сети в гигантский вектор столбца
8:39
Отрицательным градиентом функции стоимости является только вектор
8:43
Это какое-то направление в этом безумно огромном пространстве ввода, которое говорит вам, какие
8:49
подталкивание ко всем этим числам приведет к самому быстрому снижению функции затрат и
8:55
конечно, с нашей специально разработанной функцией стоимости
8:58
Изменение весов и смещений для его уменьшения означает создание выходных данных сети на каждой части данных обучения
9:05
Похоже, что это случайный массив из десяти значений и больше похоже на фактическое решение, которое мы хотим сделать
9:11
Важно помнить, что эта функция стоимости включает в себя среднее значение по всем данным обучения
9:16
Поэтому, если вы минимизируете это, это означает, что это лучшая производительность для всех этих образцов
9:23
Алгоритм для эффективного вычисления этого градиента, который эффективно является сердцем того, как учится нейронная сеть, называется обратным распространением
Back propagation
9:31
И это то, о чем я буду говорить о следующем видео
9:34
Там я действительно хочу потратить время, чтобы пройти
9:36
Что именно происходит с каждым весом и каждым смещением для данной части данных обучения?
9:41
Пытаясь дать интуитивное чувство того, что происходит за кучей соответствующих исчислений и формул
9:47
Прямо здесь прямо сейчас главное. Я хочу, чтобы вы знали независимо от деталей реализации
9:52
это то, что мы имеем в виду, когда говорим о сетевом обучении, состоит в том, что это просто минимизирует функцию затрат и
9:58
Обратите внимание, что одним из следствий этого является то, что для этой функции затрат важно иметь приятный плавный выход
10:04
Чтобы мы могли найти локальный минимум, выполняя небольшие шаги вниз
10:08
Вот почему, кстати
10:10
Искусственные нейроны имеют непрерывную активацию, а не просто активную или неактивную в двоичном режиме
10:16
если способ биологических нейронов
10:19
Этот процесс многократного подталкивания ввода функции некоторым кратным отрицательного градиента называется градиентным спусканием
Gradient descent
10:26
Это способ сходиться к некоторому локальному минимуму функции стоимости, в основном долины на этом графике
10:32
Я все еще показываю изображение функции с двумя входами, конечно, потому что подталкивает тринадцать тысяч входных данных
10:38
Пространство немного сложно окутать вокруг, но на самом деле есть хороший не пространственный способ подумать об этом
10:44
Каждая компонента отрицательного градиента говорит нам две вещи: знак, конечно, говорит нам, соответствует ли соответствующая
10:51
Компонент входного вектора должен подталкиваться вверх или вниз, но важно относительные величины всех этих компонентов
10:59
Какие из вас рассказывают, какие изменения важны
11:05
Вы видите, что в нашей сети настройка на один из весов может быть намного больше
11:09
влияние на функцию стоимости, чем корректировка на какой-либо другой вес
11:14
Некоторые из этих соединений имеют большее значение для наших данных обучения
11:18
Таким образом, вы можете думать об этом градиентном векторе нашего разума
11:22
массивная функция затрат заключается в том, что она кодирует относительную важность каждого веса и смещения
11:28
То есть, какие из этих изменений будут нести наибольший удар для вашего доллара
11:33
Это действительно еще один способ задуматься о направлении
11:36
Чтобы взять более простой пример, если у вас есть функция с двумя переменными в качестве ввода, и вы
11:41
Вычислите, что его градиент в какой-то конкретной точке выражается как (3,1)
11:47
Затем, с одной стороны, вы можете интерпретировать это, говоря, что, когда вы стоите на этом входе
11:52
движение по этому направлению быстрее увеличивает функцию
11:55
То, что, когда вы рисуете функцию над плоскостью входных точек, этот вектор является тем, что дает вам прямое направление в гору
12:02
Но еще один способ прочитать это – сказать, что изменения этой первой переменной
12:06
В три раза важны изменения во второй переменной, по крайней мере, в окрестности соответствующего ввода
12:13
Подталкивание значения x несет намного больший удар для вашего доллара
Recap
12:19
Отлично
12:19
Давайте уменьшим масштаб и подведем итог, где мы находимся до сих пор, самой сетью является эта функция с
12:25
784 входа и 10 выходов, определенных в терминах всех этих взвешенных сумм
12:30
функция стоимости представляет собой слой сложности, поверх которого он принимает
12:35
13 000 весов и предубеждений в качестве исходных данных и выплескивают одну меру паршивости на основе примеров обучения и
12:42
Градиент функции стоимости – еще один уровень сложности, который все еще говорит нам
12:47
То, что подталкивает все эти веса и предубеждения, вызывают самое быстрое изменение стоимости функции затрат
12:53
Который вы могли бы интерпретировать, говорит, какие изменения, вес которых имеет наибольшее значение
13:02
Поэтому, когда вы инициализируете сеть со случайными весами и смещениями и настраиваете их много раз на основе этого процесса спуска градиента
13:09
Насколько хорошо он работает на изображениях, которых он никогда не видел?
13:13
Ну, тот, который я описал здесь, с двумя скрытыми слоями из шестнадцати нейронов, каждый из которых выбран в основном по эстетическим соображениям
13:20
ну, неплохо, что он классифицирует около 96 процентов новых изображений, которые он видит правильно, и
13:26
Честно говоря, если вы посмотрите на некоторые примеры, из-за которых вы чувствуете себя вынужденным немного порезать
13:35
Теперь, если вы играете со скрытой структурой слоя и делаете пару настроек
13:39
Вы можете получить это до 98%, и это очень хорошо. Это не лучший
13:43
Вы, безусловно, можете получить лучшую производительность, получив более сложную, чем эта простая ванильная сеть
13:48
Но учитывая, насколько сложной является первоначальная задача, я просто думаю, что есть что-то?
13:52
Невероятно, что любая сеть делает это хорошо на изображениях, которые никогда не видели раньше
13:57
Учитывая, что мы никогда конкретно не говорили, какие шаблоны искать
Motivation
14:02
Первоначально я мотивировал эту структуру, описывая надежду на то, что у нас могут быть
14:07
То, что второй слой может зацепиться за небольшие края
14:09
Чтобы третий слой скомпоновал эти края для распознавания петель и более длинных строк и чтобы их можно было собрать вместе, чтобы распознавать цифры
14:17
Так это то, что наша сеть на самом деле делает? Хорошо для этого, по крайней мере,
14:23
Не за что
14:24
помните, как в последнем видео мы смотрели, как весы
14:27
Соединения от всех нейронов в первом слое к данному нейрону во втором слое
14:31
Может быть визуализирован как заданный шаблон пикселя, который нейрон второго слоя набирает
14:37
Хорошо, когда мы на самом деле делаем это для весов, связанных с этими переходами от первого уровня к следующему
14:43
Вместо того, чтобы собираться на изолированных маленьких краях здесь и там. Они хорошо выглядят почти случайными
14:50
Просто положите некоторые очень свободные шаблоны в середине, казалось бы, что в необъяснимо больших
14:56
13 000-мерное пространство возможных весов и уклонов, наша сеть оказалась очень маленьким локальным минимумом,
15:02
несмотря на успешную классификацию большинства изображений, точно не подбирают шаблоны, на которые мы могли надеяться, и
15:09
Чтобы действительно управлять этой точкой дома, смотрите, что происходит, когда вы вводите случайное изображение
15:14
если бы система была умной, вы могли бы ожидать, что она либо почувствует неопределенность, может быть, не активирует ни один из этих 10 выходных нейронов или
15:21
Активация их всех равномерно
15:23
Но вместо этого
15:24
Уверенно дает вам какой-то бессмысленный ответ, как будто он чувствует себя уверенным, что этот случайный шум равен 5, так как он действительно
15:32
образ 5 – это 5
15:34
фразу по-разному, даже если эта сеть может распознавать цифры довольно хорошо, она не знает, как их привлечь
15:41
Это связано с тем, что это такая строго ограниченная тренировочная установка
15:45
Я имею в виду поставить себя в туфли сети здесь, с его точки зрения, вся вселенная состоит из ничего
15:51
Но четко определенные неподвижные цифры, сосредоточенные в крошечной сетке, и ее функция стоимости просто никогда не давали ей
15:57
Стимулирование быть чем угодно, но совершенно уверенно в своих решениях
16:01
Итак, если это изображение того, что действительно делают эти нейроны второго слоя
16:05
Вы можете задаться вопросом, почему я бы представил эту сеть с мотивацией собирать по краям и узорам
16:09
Я имею в виду, это совсем не то, что заканчивается
A starting point
16:13
Ну, это не значит быть нашей конечной целью, но вместо этого отправной точкой откровенно
16:17
Это старая технология
16:19
вид, исследованный в 80-х и 90-х годах и
16:21
Вам нужно понять это, прежде чем вы сможете понять более подробные современные варианты, и это, безусловно, способно решить некоторые интересные проблемы
16:29
Но чем больше вы вникаете в то, что эти скрытые слои действительно делают менее умным,
Engage with the material
16:38
Переход на какой-то момент с того, как сети узнают, как вы учитесь
16:42
Это произойдет только в том случае, если вы будете активно участвовать в этом материале здесь
16:46
Одна довольно простая вещь, которую я хочу, чтобы вы сделали, это просто приостановить прямо сейчас и задуматься на мгновение о том, что
16:53
Изменения, которые вы могли бы внести в эту систему
16:55
И как он воспринимает образы, если вы хотите, чтобы он лучше воспринимал такие вещи, как ребра и узоры?
17:01
Но лучше, чем фактически заниматься материалом
17:04
я
17:05
Очень рекомендую книгу Майкла Нильсена по глубокому обучению и нейронным сетям
17:09
В нем вы можете найти код и данные для загрузки и воспроизведения для этого точного примера
17:14
И книга проведет вас шаг за шагом, что делает этот код
17:18
Что удивительно в том, что эта книга является бесплатной и общедоступной
17:22
Поэтому, если вы что-то извлечете из этого, подумайте о том, чтобы присоединиться ко мне, чтобы пожертвовать на усилия Нильсена
17:27
Я также связал пару других ресурсов, которые мне очень нравятся в описании, включая
17:32
феноменальный и красивый пост в блоге Криса Олы и статьи в дистилляции
17:38
Чтобы закрыть все здесь в течение последних нескольких минут
17:40
Я хочу вернуться к фрагменту интервью, которое у меня было с Лейшей Ли
17:43
Вы можете вспомнить ее из последнего видео. Она занималась своей докторской работой в глубоком обучении и в этом маленьком фрагменте
Leisha Lee interview
17:49
Она рассказывает о двух последних документах, которые действительно копаются в том, как некоторые из более современных сетей распознавания образов фактически учатся
17:55
Просто чтобы настроить, где мы были в разговоре, первая работа взяла одну из этих особенно глубоких нейронных сетей
18:01
Это действительно хорошо для распознавания образов и вместо того, чтобы обучать его соответствующим образом помеченным данным
18:05
Установите его, чтобы перетасовать все ярлыки перед тренировкой
18:08
Очевидно, что точность тестирования здесь не лучше, чем случайная, поскольку все просто случайно помечено
18:14
Но он все еще мог достичь такой же точности обучения, как и для правильно маркированного набора данных
18:21
В основном миллионы весов для этой конкретной сети были достаточными для того, чтобы просто запомнить случайные данные
18:27
Какой вопрос поднимает вопрос о том, действительно ли минимизация этой функции стоимости соответствует какой-либо структуре изображения?
18:34
Или это просто ты знаешь?
18:36
запомнить все
18:37
Набор данных о том, какова правильная классификация, и поэтому пара из вас знает полгода спустя в ICML в этом году
18:44
Не было точно отформатированной бумажной бумаги, которая обращалась к некоторым из них:
18:49
Фактически, эти сети делают что-то немного умнее, если вы посмотрите на эту кривую точности
18:55
если вы просто тренировались на
18:58
Случайные данные показывают, что тип кривой спустился очень точно, вы знаете очень медленно почти линейным способом
19:05
Таким образом, вы действительно пытаетесь найти, что местные минимумы возможных
19:09
вы знаете правильные веса, которые доставят вам такую ​​точность, тогда как если вы действительно тренируетесь по структурированному набору данных, который имеет
19:15
Правые этикетки. Вы знаете, что вы немного заиграли в начале, но потом вы очень быстро упали, чтобы добраться до этого
19:22
Уровень точности и, следовательно, в каком-то смысле было легче найти, что
19:26
Местные максимумы, и поэтому было интересно также, что это поймано, приносит в свет другую бумагу от фактически пару лет назад
19:34
У чего намного больше
19:36
упрощения сетевых уровней
19:39
Но один из результатов заключался в том, что, если вы посмотрите на оптимизационный ландшафт, то локальные минимумы, которые эти сети имеют тенденцию изучать,
19:47
Фактически с равным качеством, поэтому в некотором смысле, если ваш набор данных является структурой, и вы должны быть в состоянии найти это намного проще
19:58
Моя благодарность, как всегда, тем, кто поддерживает вас на patreon
20:01
Я уже говорил, что это только что созданный игровой чейнджер, но эти видео действительно не будут возможны без тебя.
20:07
Также хочу дать специальный. Благодаря партнерам VC фирмы amplifi в поддержке этих первых видеороликов в серии

Поделиться: