W uczeniu maszynowym często wykonujemy operacje na macierzach (wektor to też przykład macierzy, tylko że jeden z wymiarów wynosi 1). Cchcemy takie operacje wykonywać, gdzie tylko się da przy pomocy funkcji z modułu numpy, bo tak jest efektywniej!
Matematyka jest bezlitosna. Istnieją pewne określone reguły, określające warunki kiedy na macierzach można wykonywać operacje:
- aby dodać do siebie macierze/wektory, muszą one mieć takie same wymiary
- aby pomnożyć przez siebie dwie macierze, pierwsza z nich musi mieć tyle kolumn, co druga wierszy
Tymczasem u nas te warunki nie zawsze będą spełnione. Można się jednak „umówić”, co do tego jak wykonywać operacje, których, matematycznie rzecz ujmując, nie można wykonywać. Taki mechanizm w numpy nosi nazwę broadcasting, bo określa on, jak należy powiększyć mniejszą macierz do rozmiarów pasujących do większej macierzy, aby w intuicyjny sposób wykonać operację na macierzach.
Żeby uniknąć matematycznego hejtu, na usprawiedliwienie należy dodać, że broadcasting-u można by uniknąć. W takim przypadku programista musiałby po prostu samodzielnie dbać o to, żeby macierze miały właściwe rozmiary. Taka praca, nie dość że nudna, to jeszcze spowoduje znaczne użycie pamięci. Dlatego tak się palimy do zautomatyzowanego i zoptymalizowanego procesu broadcastu.
Zadanie:
Zacznijmy od czegoś prostego. Oto dwa wektory:
import numpy as np a = np.array([1, 2, 3]) print(a) b = np.array([10,20,30]) print(b)
Dodaj do siebie te dwa wektory. Nim to zrobisz, zastanów się:
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To jest poprawne działanie matematyczne.
Operacja dodawania może wyglądać tak:
c = a + b print(c)
Zadanie:
A co wydarzyłoby się, gdyby b był po prostu liczbą? Jak dodać do siebie wektor a i liczbę b?
a = np.array([1, 2, 3]) print(a) b = 100 print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To nie jest poprawne matematycznie… do wektora nie można dodawać liczby… ale na intuicję by się dało! Wystarczyło by powtórzyć wartość 100 trzy razy i zrobić z b wektor [100, 100, 100]
Operacja dodawania może wyglądać tak:
c = a + b print(c)
Tu właśnie po raz pierwszy skorzystaliśmy z broadcastingu. Jeśli masz do wykonania operację na dwóch tablicach (wektorach) i jeden z nich jest dłuższy od drugiego, to ten drugi „poszerzamy” powtarzając jego wartość.
Zadanie:
A co wydarzyłoby się, gdyby b był po prostu krótszym wektorem, ale nie liczbą? Jak dodać do siebie dwa wektory o różnej długości?
a = np.array([1, 2, 3]) print(a) b = [100, 200] print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To nie jest poprawne matematycznie… aby do wektora dodać wektor, oba wektory musza mieć ten sam wymiar. Chwyt z powieleniem jednej liczby tu się nie uda, bo wektor b składa się z 2 wartości…
Operacja dodawania w poniższej postaci skończy się błędem:
c = a + b # ValueError: operands could not be broadcast together with shapes (3,) (2,)
Broadcasting działa tylko w przypadku gdy podczas powiększania mniejszego wektora miał on wymiar 1 – czyli tu musiał być liczbą.
Zadanie:
No to przejdźmy to macierzy 2-wymiarowych. Poniżej definiujemy dwie macierze o takich samych wymiarach. Jak je do siebie dodać?
a = np.array([[1, 2, 3], [4, 5, 6]]) print(a) b = np.array([[101, 102, 103], [104, 105, 106]]) print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
Ponieważ wymiary macierzy są zgodne, to takie dodawanie jest poprawne matematycznie i najłatwiej zapisać je w postaci:
c = a + b
Zadanie:
A co wydarzyłoby się, gdyby b był po prostu liczbą? Jak dodać do siebie dwuwymiarową macierz a i liczbę b?
a = np.array([[1, 2, 3], [4, 5, 6]]) print(a) b = 100 print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To nie jest poprawne matematycznie… do macierzy nie można dodawać liczby… ale na intuicję by się dało! Wystarczyło by powtórzyć wartość 100 w pomocniczej macierzy, o takich samych wymiarach jak a
Operacja dodawania może wyglądać tak:
c = a + b print(c)
Tu znowu skorzystaliśmy z broadcastingu. Jeśli masz do wykonania operację na macierzy i liczbie to liczba została powielona tyle razy, aby utworzyć macierz pasującą wymiarami do oryginalnej macierzy, a następnie zostało wykonane dodawanie
Zadanie:
A co wydarzyłoby się, gdyby b był wektorem? Jak dodać do siebie dwuwymiarową macierz a i wektor b? Można tu rozważyć, co najmniej dwa wypadki. Raz b będzie „poziomym wektorem” o długości 3, co pasuje do 3 kolumn macierzy a, a raz b będzie „pionowym wektorem” o długości 2, co pasuje do liczby wierszy w macierzy a
# case 1 a = np.array([[1, 2, 3], [4, 5, 6]]) print(a) b = np.array([100, 200, 300]) print(b) # case 2 a = np.array([[1, 2, 3], [4, 5, 6]]) print(a) b = np.array([[100], [200]]) print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To nie jest poprawne matematycznie… można do siebie dodawać tylko macierze o takich samych wymiarach, … ale na intuicję by się dało! Wystarczyło by powtórzyć
- w pierwszym przypadku kilka razy wiersz b
- a w drugim przypadku kilka razy kolumnę b
i wykonać dodawanie takich poszerzonych macierzy!
Operacja dodawania może wyglądać tak:
c = a + b print(c)
Tu znowu skorzystaliśmy z broadcastingu. Jeśli masz do wykonania operację na macierzy i wektorze to ten wektor zostanie powielony tyle razy ile trzeba, aby wykonać obliczenia.
Zadanie:
A co wydarzyłoby się, gdyby b był macierzą 2×2? Jak dodać do siebie dwuwymiarową macierz a i wektor b o różnych wymiarach?
a = np.array([[1, 2, 3], [4, 5, 6]]) print(a) b = np.array([[100, 200],[300,400]]) print(b)
Zastanów się
- czy takie działanie jest poprawne matematycznie?
- jak najwygodniej zapisać dodawanie takich dwóch wektorów?
Rozwiązanie:
To nie jest poprawne matematycznie… można do siebie dodawać tylko macierze o takich samych wymiarach, … na dodatek, tutaj broadcasting nie zadziała, bo broadcasting może powielać tylko wektory jednowymiarowe
Poniższe polecenie zakończy się błędem
c = a + b print(c) # ValueError: operands could not be broadcast together with shapes (2,3) (2,2)
Podsumowanie:
W tym LAB poznaliśmy zasadę broadcastingu. Pozwala ona w wygodny sposób wykonywać obliczenia na macierzach i wektorach.
Więcej o broadcastingu można przeczytać w artykule dokumentacji modułu numpy:
https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
Komentarze:
Hej Rafał,
Bardzo fajny artykuł. Teraz uczę się trochę o macierzach z kursów eTrapez.pl i ten artykuł trochę mi pomógł w zrozumieniu zagadnienia.
Na razie jestem początkujący w programowaniu w Python i materiał tego artykułu wydaje mi się trochę za bardzo zaawansowany dla mnie, jednak i tak warto było przeczytać.
Dziękuję za dzielenie się swoją wiedzą i pozdrawiam :-).