All streams
Search
Write a publication
Pull to refresh
55
0
Send message
Зачем вообще параллелить само вычисление каждого элемента? У нас же в матрицах миллионы элементов — и каждый из этих элементов вычисляется независимо от другого.

Вот потому, что каждый из этих элементов вычисляется независимо, мы можем (в теории) это неограниченно (в силу своих возможностей) параллелить, чтобы повысить производительность.
Что-то вы не туда залезли.

Я с инженерной точки зрения решил взглянуть на проблему: что получится в пределе, если мы можем позволить себе такую роскошь — как отдельный вычислительный блок на каждую элементарную операцию: умножение, сложение и функцию активации (этакий суперскалярный процессор) для каждого нейрона в каждом слое. В пределе маячит «проблема последовательных операций», которые в умножении матриц есть.
На мой взгляд, правильность организации кода тут не причем.

Выше в ответе BigBeaver я указал, что доля последовательных операций определяется как 1/N, где N — ширина слоя нейронной сети. Может, конечно, я ошибся в расчетах. Поправьте.

Свои 5% я взял из головы, просто чтобы показать, во сколько раз увеличится производительность алгоритма, если взять неограниченное число процессоров. Формула Амдала говорит нам, что в пределе в 20 раз. Для 0.01% — в 10000 раз. Вроде бы круто.
Вот только чтобы получить свои 0.01% = 1/10000 мы должны взять слой, в котором 10000 нейронов. Общая сложность вычислений такого слоя (если в нем нет нулей) составит (10^4)^3 = 10^12 операций (умножения и сложения). Идеального ускорения в 10000 раз удастся достигнуть, взяв 10^12(!) процессоров. Чтобы только перешагнуть планку ускорения в 9000 раз, потребуется взять свыше 89000(!) процессоров.

… операций, большая часть которых может выполняться параллельно

Так то да. Но рост сложности произведения матриц — операции, которая лежит в основе распространения сигналов по НС, — растет кубично (быстрые алгоритмы умножения матриц, снижающие сложность до O(N^2.17..), аппаратно реализуются крайне сложно — поэтому пока не используются). А вот производительность современных процессоров растет в лучшем случае линейно.
Кто кого обгонит?

Одно дело, если мы берем и тренируем относительно небольшую НС для решения конкретной задачи. Например, для распознавания автономеров.
Другое дело, если мы собираемся применить НС как базу для создания сильного ИИ. Мы не знаем какой глубины и ширины НС нам потребуется взять для этой задачи. Возможно многократно больше. Поэтому меня, как инженера, интересует — а потянут ли современные ЭВМ в будущем такие расчеты. Для этого я привлекаю математику. Математика говорит, что в теории можем обломиться.

Это как с пузырьковой сортировкой. Глупо надеяться, что увеличение производительности вашего компьютера существенно ускорит изначально ущербный алгоритм. Текущий математический аппарат НС — пузырьковая сортировка, вид сбоку.
В случае свёрточных нейросетей никто свёртки через произведения матриц не считает

Я не спорю. Просто изначально речь пошла про то, что последовательных операций:
В перемножении матриц таких нет.

Замечу, что даже если вы «не используете» в НС матричные операции, получение значения любого нейрона — это операция произведения двух векторов: вектора коэффициентов связи нейрона с вектором состояний входящих нейронов. А это значит, что распространение сигнала от одного слоя к другому с т.з. математики — это произведения матриц, которое вы, как инженер, просто оптимизируете там, где у вас нули. Но от этого матричные операции никуда не деваются.
Поправьте меня, если я не прав.
Давайте посчитаем
Пусть есть полно-связная глубокая нейронная сеть. Для упрощения расчетов возьмем её участок шириной N (кол-во нейронов в слое) и глубиной М (кол-во слоев).
Чтобы распространить сигнал от одного слоя к другому, необходимо для каждого из N нейронов выполнить следующие действия:
1. посчитать вес на входе (их у нас N штук): для чего необходимо состояние нейрона с предыдущего слоя, умножить на коэффициент его связи с данным;
2. сложить полученные веса (их у нас N штук), получив сумму входящих связей в данный нейрон;
3. активировать нейрон (применив любую удобную вам функцию)
Таким образом, чтобы распространить сигнал от одного слоя к другому, мы должны выполнить: N*N умножений (этап 1) и N*N сложений (этап 2) + выполнить активацию N раз.
Слоев у нас M. Отсюда нам потребуется (M-1) * (2*N*N + N) операций, которые могут быть выполнены параллельно (независимо). И ещё (M-1) * 2*N последовательных операций: передача данных между этапами.
Отсюда, доля последовательных операций, составит: 2*(M-1)*N / ((M-1)*(2*N*N + N)) = 1/(N + 0.5) ~ 1/N. Независимо от глубины.

Придется согласиться с Вами:
чем шире сеть (большие размеры слоёв) при той-же глубине, тем меньше процент нераспараллеливаемых операций при прямом проходе

А ведь хотел опровергнуть)
Так ньюанс не в том, что не сложение тут виновато. Виноват алгоритм)

Вычисление любого элемента матрицы (или значения входов нейрона), происходит в два этапа:
1. найти попарные произведения элементов матрицы (или весов входящих связей нейронов)
2. сложить полученные произведения (сложить веса входящих нейронов)
3. (для нейронов ещё и) применить функцию активации.
Этап 2 не может быть выполнен до выполнения этапа 1. И хотя каждый из этапов может исполняться параллельно, но сама результирующая операция потребует последовательного их выполнения. Передача данных с этапа 1 на этап 2 — и составляет тот самый % операций, не поддающийся распараллеливанию. А значит, на него действует закон Амдала. Об этом речь.

Это я к фразе:
В перемножении матриц таких нет.


Помимо Амдала
есть ещё и закон Густафсона-Барсиса. Он подходит для расчета производительности конвейеров.
Только до логарифма.

Вероятно вы говорите о каком-то более эффективном способе сложения? Об эффективном умножении наслышан. А вот о сложении, увы — нет. Буду очень признателен, за информацию.
Немного не в курсе таких алгоритмов. Можете сориентировать?
Например, можно поступить так
Пусть нужно сложить N чисел, которые есть результат поэлементного произведения строки матрицы А, на столбец матрицы В. Разобьем N слагаемых на пары, каждую пару посчитаем независимо (параллельно). Получим N/2 чисел, которые нам также нужно сложить. Применяя рекурсивно алгоритм к оставшимся числам, получим ряд: N/2 + N/4 + N/8… который в пределе сходится к N. Т.е. такой подход к сложению требует O(N) операций. Этот способ не изменил сложность.

Или вот так
Будем складывать не два числа одновременно, а тройки чисел. Для каждой комбинации триплета выберем из предрасcчитанной таблицы их итоговую сумму. Тогда для задачи сложения N чисел получим ряд: N/3 + N/9 + N/27 + ..., сходящийся к N/2. Т.е. сложность такого алгоритма O(N/2). Но это, если мы обладаем неограниченной памятью и сможем позволить себе складывать тройки 2^m-значных чисел.
Ну как же нет? Чтобы получить каждый элемент итоговой матрицы мы должны вначале попарно умножить элементы строки и столбца исходных матриц, а затем уже получившиеся произведения сложить.
Тут сложение зависит (по данным) от умножения. В терминах закона Амдала — это и есть, та самая доля операций, выполняемых последовательно.
Коварность закона состоит в том, что если у нас хотя бы 5% операций не могут выполняться параллельно, то максимальное ускорение алгоритма составит в пределе 20 раз, не зависимо от того, сколько процессоров вы возьмете. Хоть отдельный процессор для каждого произведения/сложения.

Это как ответ на заданный здесь вопрос:
это во сколько раз прирост скорости на 100 узлах по сравнению с одним?

Ситуация не меняется, если мы перемножаем вектора.

А вообще асимптотическая сложность произведения квадратных матриц размера N в зависимости от конкретного алгоритма колеблется в пределах от O(N^2) до O(N^3). Учитывая, что рост производительности современных компьютеров уже не то что не экспоненциальный, а даже уже и не линейный [2], глубокие нейронные сети, работа которых и базируется на перемножении векторов, смогут «ушатать» любой наперед заданный мощный суперкомпьютер (работающий на текущей элементной базе).
Меня одного смущает вот этот цикл в коде: for (k = 0; k < id; k++)?
Получается, чтобы вычислить цифру числа PI начиная с позиции id, нам придется выполнить id итераций этого цикла. Получается, алгоритм имеет сложность O(n) для вычисления n-й цифры числа.

Честно говоря, по заголовку подумал, что, найдена формула, которая имеет сложность O(1) для конкретной цифры числа. Формула, безусловно, красивая. Но думаю, что генератор случайных чисел создать на её базе, увы, не получится: накладно.
… что дало возможность построить точную модель астероида и создать его карты, включая цветные...

Хм… цветных снимков не нашел на www.asteroidmission.org и на www.nasa.gov/content/osiris-rex-images.
Или это у НАСА фишка такая: принципиально не публиковать цветные снимки?

Может есть ещё другой сайт? Кто в курсе — скиньте, плиз, ссылочки.
Вот кроме шуток. Мне однажды приснился участок кода программы, над которым днём работал. Так неприятно было. Мало того, что днём весь день за компом, так и ещё и во сне код прямо перед глазами. Да так близко — аж жуть!
И во сне я увидел в этом коде ошибку: забыл про обработку какого-то условия. Утром пришел на работу — и точно! Исправил потенциальный баг.
Так что во сне ещё как читаешь. И не приятная эта штука, я скажу. Мозг сильно напрягается.

На баше как-то шутка проскакивала. Сисадмина ночью разбудил телефон. Пришла SMS о том, что «упал сервер». Он встал, подключился из дома, починил. Утром пришел на работу. Увидел в логах, что сервер действительно падал, и он его поднимал. Проверил телефон: а ему никаких sms не приходило.
Это уже не юнит тест

Вы правы. И я писал, что «не все озвученные вопросы ниже относятся к unit-тестированию. Но, меня, как практика, интересует вопрос — как проверить, что написанный мною код работает „вообще“. А какой это тест: модульный, функциональный или интеграционный — дело десятое.

вы должны подготовить тестируемый ключ и тестируемый вектор инициализации

Есть ещё проблема в том, что библиотека (ГОСТ, например) в шифр вставляет иммитовставки, которые делают на его выходе уникальным. Т.е. для одних и тех же входных данных, вы всегда будете получать разные шифры.
вы должны подготовить тестируемый ключ и тестируемый вектор инициализации.
Думаю, это возможно, если вы — автор этой библиотеки и вам нужно её тестировать. Но, имхо, такая методика возможна в рамках шаблона visible_for_testing: вам придется делать бэкдоры для приведения конвейера шифра в нужное вам состояние. Публичных функций для реализации этого быть не должно — ибо вектор атаки.

Похоже, что методика:
assert decrypt(encrypt(data)) == data // проверяете на самых разных data

пока без вариантов
Спасибо, за ответы!

Про генератор случайных чисел: тут я, конечно, лишнее написал. Для них существуют специальные методики.
Видимо функция каким-то образом получает еще и текущую дату?

Да, так и есть:
private function getYearsOld( Date birthDate )
{
   Date currentDate = new Date(); // 
   ...
}

Наверное, Вы правы. Стоит передавать текущую дату в качестве второго параметра, превратив функцию getYearsOld в чистую (без явных и скрытых зависимостей). Тогда можно её вынести вообще в отдельный класс (сейчас она private), сделав static public.

Что касается тестирования reader. В принципе, я так и делал. Но не могу отделаться от ощущения, что это smell-test.

А кто, что посоветует для 8-го вопроса?
Я новичок в тестировании. Тема меня эта интересует: стараюсь изучать соответствующие материалы. Но пока мне не везет, и я не могу получить ответы на свои вопросы.

Я надеюсь, хабро-сообщество не будет против, если я озвучу здесь эти вопросы?
Всё-таки Хабр — это соц.сеть, где программисты делятся опытом.

Ремарка: понимаю, что не все тесты озвученные вопросы ниже относятся к unit-тестированию.
Ремарка2: некоторые вопросы я озвучивал на Тостере. Но качественных ответов так и не получил.

1. Нужно протестировать распределение hashCode у своего класса. Вам нужно сделать оценку кэш-промахов.

2. У вас функция шифрует данные (пусть алгоритмом AES).
Ваша задача убедиться, что вы правильно шифруете (правильно вызвали библиотечный код — там много подводных камней), и удаленный получатель сможет их расшифровать.
Проблема: для одной и той же входной последовательности шифр всегда разный.

3. Ваш класс генерирует параметризируемый mesh 3D-объекта. Пусть это будет куб с закругленными краями. Одна функция — генерирует массив точек, другая — массив индексов точек, связывающих их в полигоны, третья — нормали к вершинам, четвертая — текстурные координаты. Все функции — независимы друг от друга (вообще это чистые функции). Их можно хоть вынести в другие классы. Как протестировать их работу? А именно:
1) что куб — вообще на куб похож
2) что все полигоны обращены к наблюдателю, как ожидается
3) не перепутаны индексы полигонов или текстурных координат

4. Ваша функция увеличивает среднюю громкость аудиопотока. Как написать тест на это?

5. Вы реализовали генератор случайных чисел. Как его протестировать?

6. Функция получает на вход дату рождения человека и возвращает его возраст. Как написать тест на такую функцию?

7. Есть некий класс reader с двумя функциями: readChar(), isAvailable()
Первая функция — читает из буфера символ, вторая — проверяет, что буфер не исчерпан.
(Пусть буфер — это просто массив символов)
Как написать правильный тест на isAvailable? Функция readChar() влияет на результат работы isAvailable().

8. Функция получает сложный объект (например, большую json-структуру) и вытягивает нужные ей значения, например так: json.getDeclarant().getPersonalInfo().getIdentityDocument().getIssueDate().
Как правильно сделать mock такого объекта?

Благодарю всех, кто даст конструктивные советы.
От исключений можно и нужно уходить

Прошу прощения, а можно про это по-подробнее? Есть какая-то техника, или какие-то тренды в этом направлении?
На мой взгляд, очень не оптимально делаются расчеты. Вначале берется арктангенс atan(Y/X), чтобы вычислить угол. Затем вычисляется (дважды!) косинус и синус этого угла.
Но если вспомнить основы тригонометрии, то: D = sqrt(X*X + Y*Y), sin(angle) = Y/D, cos(angle) = X/D. При таком подходе можно не боятся ситуаций, при которых X = 0.
1. Скажите, сталкивались ли вы с автоколебаниями маятника? И если да, как боролись с этим в программе управления?
2. По вашему мнению, возможно ли здесь использовать вычислительную нейросеть (обучить и использовать) вместо фиксированного алгоритма управления? Интересно, что окажется эффективнее.
Вот у вас есть крутая идея минификатора CSS.

Есть такая идея. Прошу меня извинить за небольшой офтоп…

Вот честно говоря, сомнительно, что PostCSS «не приседая со штангой» сможет построить правильную синтаксическую модель CSS. Дело в том, что стандарт CSS 2/3/4 — это уже настолько сложный и запутанный документ, что обычными регулярками его не распарсишь.
Чего только стоят синтаксис селекторов, квалификаторов, выражений медиа-запросов (@media), и формат условных запросов (@conditional),… если внимательно почитать документацию.

Ну вот для примера.

Знаете ли вы, что вот эта конструкция является валидным именем класса:
.data\\\{5\} {… }

А как вам такой селектор по атрибуту:
[my|src^="/icon/"]

Или вот еще простенький квалификатор:
.colorized[ href
]:NOT(
.blue.dark.orange
)

А может быть такой селектор:
*|P * .test + P > span ~ em {...}

Или вот еще, финт со строками:
a::before
{
content: 'Этот текст слишком длинный\
так что я пожалуй перенесу \0000A \U+0003F #&567; его на следующую строку'
}

Ах, да, вот еще можно так:
.class { background-image: url(/my_url:;-.png) }

А еще добавьте к этому всему esape-последовательности, unicode-символы, инструкцию charset (которая прикажет нам сменить кодировку файла на лету), и т.п. и т.п. И все это парсер CSS должен поддерживать.

Кому интересно, вот несколько ссылок про CSS стандарт.
http://www.w3.org/TR/css-syntax-3/
http://www.w3.org/TR/css3-selectors/
http://www.w3.org/TR/css3-webfonts/
http://www.w3.org/TR/css-fonts-3/
http://www.w3.org/TR/css3-animations/
http://www.w3.org/TR/css3-values/
http://www.w3.org/TR/css3-color/
http://www.w3.org/TR/css3-namespace/
https://www.w3.org/TR/css3-images/
http://www.w3.org/TR/css3-mediaqueries/
http://www.w3.org/TR/css3-conditional/
http://www.w3.org/TR/css-ui-3/
http://www.w3.org/TR/css-counter-styles-3/
http://www.w3.org/TR/css-flexbox-1/
http://www.w3.org/TR/css-masking-1/
http://www.w3.org/TR/css-shapes-1/
http://www.w3.org/TR/css-writing-modes-3/

И это только 3-й.

Может быть поэтому PostCSS и уделывает других за счет того, что слишком упрощенно подходит к анализу файлов?

Information

Rating
Does not participate
Registered
Activity