Чому вираження обчислень як матричне множення робить їх швидшими?


18

У підручнику MNist від Google за допомогою TensorFlow викладається обчислення, в якому один крок еквівалентний множенню матриці на вектор. Google спочатку показує малюнок, на якому кожне числове множення та додавання, яке б вдалося виконати обчислення, виписується повністю. Далі вони показують малюнок, на якому він замість цього виражається як матричне множення, стверджуючи, що ця версія обчислення є або, принаймні, може бути швидшою:

Якщо записати це як рівняння, отримаємо:

скалярне рівняння

Ми можемо «векторизувати» цю процедуру, перетворивши її на матричне множення та векторне додавання. Це корисно для обчислювальної ефективності. (Це також корисний спосіб думати.)

векторне рівняння

Я знаю, що такі рівняння, як правило, записуються у форматі множення матриць практикуючими машинним навчанням, і, звичайно, можна побачити переваги в цьому з точки зору терміновості коду чи розуміння математики. Чого я не розумію, це твердження Google про те, що перетворення з довільної форми в матричну форму "корисно для обчислювальної ефективності"

Коли, чому і як можна було б покращити продуктивність програмного забезпечення, виражаючи обчислення як матричне множення? Якби я обчислював множення матриці у другому (на основі матриці) зображенні сам, як людина, я би робив це, послідовно виконуючи кожен із чітких обчислень, показаних на першому (скалярному) зображенні. Для мене вони - це не що інше, як два позначення для однієї і тієї ж послідовності обчислень. Чому для мого комп’ютера це відрізняється? Чому комп'ютер міг би виконати обчислення матриці швидше, ніж скалярний?

Відповіді:


19

Це може здатися очевидним, але комп'ютери не виконують формули , вони виконують код , а тривалість виконання якого залежить безпосередньо від коду, який вони виконують, і лише опосередковано від будь-якої концепції, що використовується в коді. Два логічно однакових фрагмента коду можуть мати дуже різні експлуатаційні характеристики. Деякі причини, які, ймовірно, спливають у матричному множенні конкретно:

  • Використання декількох ниток. Майже не існує сучасного процесора, який не має декількох ядер, у багатьох - до 8, а спеціалізовані машини для високоефективних обчислень можуть легко мати 64 в декількох розетках. Введення коду очевидним чином, у звичайній мові програмування, використовується лише одна з них. Іншими словами, він може використовувати менше 2% доступних обчислювальних ресурсів машини, на якій працює.
  • Використання інструкцій SIMD (оманливо це також називається "векторизація", але в іншому сенсі, ніж у текстових цитатах у запитанні). По суті, замість 4-х або 8-ти скалярних арифметичних вказівок, дайте ЦП однією інструкцією, яка виконує арифметику на 4-х або 8-ми реєстраціях паралельно. Це може буквально зробити деякі розрахунки (коли вони абсолютно незалежні і підходять для набору інструкцій) в 4 або 8 разів швидше.
  • Розумніше використання кешу . Доступ до пам’яті швидше, якщо вони тимчасово та просторово когерентні , тобто послідовний доступ до сусідніх адрес, а при доступі до адреси двічі ви отримуєте два рази швидше, а не з тривалою паузою.
  • Використання прискорювачів, таких як GPU. Ці пристрої є дуже різними звірами від процесорів, і програмування їх ефективно - це ціла форма мистецтва. Наприклад, у них сотні ядер, які згруповані в групи по кілька десятків ядер, і ці групи поділяють ресурси - вони ділять кілька кіБ пам'яті, що набагато швидше, ніж звичайна пам'ять, і коли будь-яке ядро ​​групи виконує ifзаява, всі інші в цій групі повинні дочекатися цього.
  • Розподіліть роботу на декількох машинах (що дуже важливо в суперкомп'ютерах!), Що вводить величезний набір нових головних болів, але, звичайно, може дати доступ до значно більших обчислювальних ресурсів.
  • Розумніші алгоритми. Для матричного множення простий алгоритм O (n ^ 3), належним чином оптимізований за допомогою наведених вище трюків, часто швидший, ніж субкубічні для розумних розмірів матриць, але іноді вони виграють. Для особливих випадків, таких як розріджені матриці, можна написати спеціалізовані алгоритми.

Багато розумних людей написали дуже ефективний код для звичайних операцій лінійної алгебри , використовуючи вищевказані трюки та багато іншого, як правило, навіть з дурними хитрощами, що стосуються платформи. Отже, перетворення вашої формули в матричне множення, а потім реалізація цього обчислення шляхом виклику в зрілу бібліотеку лінійної алгебри виграє від цього зусилля з оптимізації. На противагу цьому, якщо ви просто випишете формулу очевидним чином мовою високого рівня, машинний код, який в кінцевому підсумку генерується, не використовуватиме всі ці хитрощі і не буде настільки швидким. Це також справедливо, якщо ви берете формулу матриці та реалізуєте її, викликаючи рутину множення наївних матриць, яку ви написали самі (знову ж, очевидним чином).

Швидке виготовлення коду вимагає роботи , а часто і досить великої роботи, якщо ви хочете останню унцію продуктивності. Оскільки стільки важливих обчислень можна виразити комбінацією пари лінійних операцій алгебри, економічно створити високооптимізований код для цих операцій. Хоча ваш одноразовий спеціалізований варіант використання? Ніхто не хвилює цього, окрім вас, тому оптимізувати хек з нього не є економічним.


4

(розріджене) Множення матричного вектора є паралельним. Що дуже зручно, якщо ваші дані є великими та у вас в розпорядженні ферма серверів.

Це означає, що ви можете розділити матрицю і вектор на шматки і дозволити окремим машинам виконати певну роботу. Потім поділіться деякими результатами між собою, а потім отримайте кінцевий результат.

У вашому прикладі операції полягають у наступному

  1. встановіть сітку процесорів, кожен з яких містить Wx, y відповідно до їх координати в сітці

  2. трансляція вихідного вектора вздовж кожного стовпця (вартість O(log height))

  3. мати кожен процесор на множення локально (вартість O(width of submatrix * heightof submatrix))

  4. згорнути результат вздовж кожного ряду, використовуючи суму (вартість O(log width))

Ця остання операція є дійсною, оскільки сума асоціативна.

Це також дозволяє створити надмірність і дозволяє уникнути необхідності складати всю інформацію в одну машину.

Для малих матриць 4х4, як ви бачите на графіці, це тому, що процесор має спеціальну інструкцію та регістри для роботи з цими операціями.


-1

Найбільш повчальним буде порівняння продуктивності вашого коду з виконанням матричного множення, реалізованого альреді.

Завжди є якась оптимізація нижчого рівня, про яку ви не думали, тут ви можете знайти приклад:

https://simulationcorner.net/index.php?page=fastmatrixvector

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.