Uvod u Pajton i Jupyter okruženje

Digitalna obrada signala, Vladimir Petrović

Dobrodošli na kurs Digitalna obrada signala na Odseku za elektroniku na Elektrotehničkom fakultetu u Beogradu!

Ovaj notebook je uvod u osnovne programerske koncepte programskog jezika Pajton (Python) koji ćemo koristiti u okviru kursa. Videćemo kako se deklarišu promenljive, liste, nizovi, ali i novi interesantan tip podataka koji zovemo rečnik (dictionary). Zatim, naučićemo sintaksu za konstrukte kontrole toka programa (uslove, petlje) i kako se definišu funkcije. Na kraju ćemo prodiskutovati paradigmu pisanja programa korišćenjem vektorizacije koja je neophodna za efikasne algoritme digitalne obrade signala, digitalne obrade slike i mašinskog učenja. Mnoge stvari ćemo detaljno objasniti tek u toku kursa, na konkretnim primerima.


Jupyter okruženje

Okruženje koje vidite se zove Jupyter Lab. Fajlovi sa kojima radimo u ovom okruženju imaju ekstenziju .ipynb što je skraćeno od interactive Python notebook. Ovo okruženje i fajlovi mogućavaju da različiti delovi fajla budu tumačeni na različite načine, pa ćete tako videti delove kao što je ovaj koji su predstavljeni kao tekst i formatirani kao markdown kod ili delove kao što će uslediti koje vidimo kao Pajton kod.

Kreiranje novog fajla, otvaranje već postojećeg i veliki broj opcija vezanih za fajlove dostupan je u padajućem meniju File.

Fajl je izdeljen na ćelije (cells) koje dodajemo klikom na dugmence + u levom kraju tulbara. Ako želimo da ćelije budu namenjene za tekst iz padajućeg menija (jedinog u tulbaru) ćemo odabrati Markdown, a ako želimo da ćelija bude namenjena za programski kod, odabraćemo Code. Ćelija se može premestiti, obrisati, iskopirati kao i bilo koji drugi tekst desnim klikom na nju i odabirom odgovarajuće opcije. Slično kao i u uobičajenim razvojnim okruženjima, neke od opcija su podržane i u tulbaru.

Ćelija se pokreće sa Shift+Enter na tastaturi ili klikom na Play dugme u tulbaru. Tekstualna ćelija može biti u režimu prikaza teksta ili režimu ažuriranja teksta. Duplim klikom na tekst se otvara markdown kod koji se može menjati (slično kao u LaTeX-u). Dodatne opcije pokretanja ćelija su dostupne u padajućem meniju Run.

U pozadini ovog okruženja (backend) se izvršava Pajton kernel na kome se izvršava sav Pajton kod koji je napisan u Code ćelijama. Ukoliko se desi sintaksna greška ili greška u izvršavanju kernel će tu grešku prijaviti, a Jupyter će je samo prikazati. Jupyter se dakle ponaša kao frontend. Za potrebe ovog kursa koristićemo dva kernela: 1) standarndi Python 3 kernel i 2) XPython kernel koji ćemo koristiti samo u slučaju da nam je potrebno naprednije debagovanje koda koje uključuje izvršavanje korak po korak, praćenje vrednosti varijabli i postavljanje tačaka prekida programa (breakpoints). Kernel se bira klikom na opciju u gornjem desnom uglu tulbara. Kernel se može zaustaviti ako smo na primer ušli u neku beskonačnu petlju ili ako je program počeo neočekivano da se ponaša klikom na dugme Stop u tulbaru. U slučaju problema, kernel se može restartovati klikom na dugme Restart u tulbaru. Dodatne opcije vezane za ponašanje kernela su dostupne u padajućem meniju Kernel.

Ne zaboravite da čuvate svoje promene (Ctrl+S ili klikom na disketicu u gornjem levom uglu).

Padajući meni Edit sadrži dodatne komande ažuriranja notebook-a što je uobičajeno u većini razvojnih okruženja.

U padajućem meniju View možemo podesiti koje prozore želimo da vidimo u celom okruženju. Klikom na Show Left Sidebar levi prozor se otvara ili zatvara. On je pre svega značajan zbog navigacije u okviru fajl sistema i rada sa fajlovima. Desni prozor je uglavnom koristan za debagovanje. Statusna linija (Statusbar) je korisna kako bismo videli u kom je stanju kernel i da li se kod ispravno izvršava.

Ostale opcije koje nisu navedene isprobajte sami.


Osnove Pajton programskog jezika

Pajton je programski jezik visokog nivoa opšte namene koga je razvio Gvido van Rosum 1991. godine. Napravljen je sa ciljem da podstiče čitljivost programskog koda. Njegova sintaksa forsira pisanje preglednog koda sa upotrebom "uvlačenja" (identacija - identations). Jezik podržava i paradigmu proceduralnog programiranja (kakav je na primer C) i paradigmu objektno-orijentisanog programiranja (kakvi su na primer C++ ili Java), kao i paradigmu funkcionalnog programiranja (kakav je na primer Scala programski jezik). Na ovom kursu ćemo najčešće koristiti proceduralno programiranje.

Pajton je jezik čiji se programi najčešće interpretiraju, tj. izvršavaju se u programu koji se zove interpreter direktno, bez prethodnog kompajliranja programskog koda. Međutim, postoje ozbiljni sistemi koji su pisani u Pajtonu i kompajlirani za rad na nekom procesoru opšte ili specifične namene. Kod napisan u Pajtonu se za razliku od većine drugih interpreterskih jezika lako može kompajlirati u izvršni fajl i pokrenuti bez interpretera.

Gotovo cela zajednica koja se bavi mašinskim učenjem koristi Pajton kao osnovni alat što je verovatno najviše doprinelo njegovoj popularnosti. Postoji mnogo gotovih paketa za razne primene u inženjerstvu koji se besplatno mogu koristiti i ubrzati razvoj algoritma ili aplikacije. Neke od njih ćemo koristiti i na ovom kursu (na primer: numpy, scipy, matplotlib). Pajton je danas jedan od najpopularnijih i najkorišćenijih programskih jezika na svetu i ne čudi što se sve više zahteva njegovo znanje u industriji.

Ovde ćemo opisati osnovne koncepte programskog jezika Pajton potrebne za početak ovog kursa. Koristimo Pajton 3. Pajton 2 već neko vreme ostaje u senci i konačno ove godine gubi podršku - svi prelaze na pajton 3. Ovaj kurs koristi verziju 3.6.

Promenljive i tipovi podataka

Promenljive u pajtonu se ne deklarišu ni na koji poseban način, kako smo to navikli u C-u ili C++-u. Dovoljno je samo nekom literalu dodeliti vrednost.

Integer

Float

Complex

String

Logički tip podatka - Boolean

Liste

Liste ne moraju imati elemente koji su istog tipa.

Evo primera jedne liste i raznih manipulacija nad njom.

⚠️ Indeksiranje počinje od 0, kao u C-u, ne od 1 kao u Matlab-u! ⚠️

Može i unazad:

Ili u grupama, sintaksa je

imeListe[startIndeks:stopIndeks+1:korak]

Dodavanje elemenata u listu i uklanjanje postojećih

Rečnik (Dictionary)

Rečnik je vrlo korisna tabela koja se ponaša slično kao lista, ali u proizvoljnom rasporedu. U rečniku se vrednostima pristupa preko ključeva.

Možemo prikazati samo ključeve ili samo vrednosti.

Ili proveriti da li se neki ključ nalazi u rečniku.

Za dodatne informacije o tipovima i varijablama posetiti link.

Kontrola toka programa

if/elif/else

Nigde nema ključnih reči begin, end, then, vitičastih zagrada koje definišu delove koda. Uvlačenjem (identacijom) se piše pregledan kod, ali i definiše opseg vidljivosti.

for petlja

Iteriranje po redu korišćenjem range(N) funkcije.

Ili ovako:

Može i da se iterira po vrednostima:

Ili malo kompleksnije:

Ili po rečniku

while petlja

Može i break i continue{:.python}.

for i in range
    break

Za dodatne informacije o sintaksi i dodatnim opcijama kontrole toka posetiti link.

Funkcije

Funkciju najpre moramo definisati. Ona može imati više ulaznih i više izlaznih argumenata.

Možemo vratiti više parametara, ali da neke od njih ne koristimo, tj. povratnu vrednost ne moramo dodeliti nekoj promenljivoj.

Neki parametri fukcije mogu biti podrazumevani.

Takođe, parametri funkcije ne moraju da se prosleđuju po redosledu po kome su definisani.

Moguće su još neke opcije kao što je promenljiv broj argumenata funkcije, prosleđivanje argumenata po referenci, korišćenje globalnih promenljivih itd. Za dodatne informacije posetiti link.

Biblioteka NumPy i vektorizacija

NumPy je biblioteka koja predstavlja proširenje Pajton programskog jezika koja dodaje podršku za velike, višedimenzione nizove i matice, zajedno sa veoma opširnim matematičkim aparatom koji radi sa ovim nizovima. U Pajton program se ova biblioteka mora uključiti i to se uobičajno radi na sledeći način:

Zatim će se sve funkcije iz ovog paketa pozivati sa np.funkcija().

Glavni objekat ovog paketa je NumPy array, tj. NumPy niz kome se mogu definisati proizvoljne dimenzije.

Definisanje NumPy niza

Broj dimenzija niza.

Dužina niza i oblik (shape):

Višedimenzioni nizovi:

Funkcije za generisanje nizova

Retko se vrendosti upisuju jedna po jedna kao u prethodnim primerima. Zato postoje funkcije koje generišu mnoge često korišćene nizove.

arange
linspace
Nizovi nula
Nizovi jedinica
Jedinična matrica
Dijagonalna matrica
Nizovi slučajnih brojeva

Ako se želi da kod daje iste slučajne brojeve pri svakom pokretanju, onda se mora postaviti seed.

Indeksiranje

Indeksiranje je slično kao kod listi.

Višedimenzioni nizovi se indeksiraju uređenim parovima.

Logičko indeksiranje:

Dodatno o nizovima na linku.

Operacije sa nizovima

Operacije nad nizovima se mogu izvršavati element po element, ali je ovo izuzetno nepraktično jer uvek zahteva pisanje velikog broja petlji. Vrlo opširan matematički aparat je podržan u paketu NumPy. Neke od ovih opcija ćemo ovde nabrojati, ali ćemo nastaviti da ih koristimo u toku kursa i na njima ćemo insistirati.

Operacije na elementima niza

Primetimo da izvršavanje raznih operacija nad skalarom i vektorom ili matricom ne rezultira greškom, već se operacija izvršava nad svakim elementom matrice.

Množenje matrica nije množenje pojedinačnih elemenata! Evo primera:

⚠️ VAŽNO: Korišćenje ugrađenih operacija direktno nad vektorima utiče na preglednost koda. Međutim, mnogo je bitniji drugi efekat: kod se mnogo brže izvršava. U pozadini, ako procesor treba da izvrši mnogo istih operacija nad gomilom podataka, moguće je da će koristiti takozvane SIMD instrukcije. Naziv je skraćenica od __S__ingle __I__nstruction __M__ultiple __D__ata, što znači da procesor ne mora da gubi vreme na dovlačenje instrukcije iz memorije, njeno dekodovanje i pripremu za izvršavanje za svaki podatak, već to može da uradi jedanput i da iz memorije dovlači samo operande. Dodatno, ovako pisan kod se može kasnije i lakše paralelizovati na više jezgara.

Postupak pisanja koda izbegavanjem petlji i uz pomoć nizova (vektora i matrica) često zovemo vektorizacija (engl. vectorization).

Uporedimo sada vreme izvršavanja ugrađene operacije nad vektorima i korišćenjem petlje.

Primetite da je vreme izvršavanja za red veličine manji. Evo još par primera množenja vektora i matrica koji će nam dočarati važnost vektorizacije.

DEBUG

Da ne zaboravimo, sada smo već dovoljno naučili da otvorimo ugrađeni debager.

Broadcasting

Videli smo da sabiranje vektora i skalara daje vektor kod koga je svaki element sabran sa skalarom. Slično je i sa drugim operacijama. Međutim, šta se dešava ako matricu saberemo sa vektorom? Logičan odgovor je da će se javiti greška pošto su dimenzije matrice i vektora različite. Međutim, ovo ne mora uvek da bude slučaj i zapravo je vrlo korisno kad nije.

NumPy će u situacijama kada operandi nisu istih dimenzija transformisati manji tako da njegove dimenzije izjednači sa većim. Ova konverzija se na engleskom zove broadcasting. Najlakše je objasniti na primeru. Evo sličice:

*Sličica preuzeta sa linka

Treba voditi računa da oblik NumPy niza može biti definisan kao prost niz kome je podrzumevana druga dimenzija 1 ili kao niz kompleksnijeg oblika. Primer:

Konverziju iz jednog u drugi tip možemo uraditi ovako:

A sada će raditi i broadcasting.

Neke važne funkcije

Nabrojaćemo samo neke. Najčešće rade nad elementima matrice pojedinačno, osim gorenavedene koje su pravljene za matrične operacije.

Još mnogo detalja o NumPy paketu i manimulacijama sa vektorima i matricama možete pronaći na linku. Neke od tamo pomenutih stvari ćemo možda koristiti kasnije, ali ovo je uvod. Dosta za sada. :)

Plotovanje grafika - matplotlib

Složićemo se da je vizuelizacija vrednosti prostim ispisom na izlaz blago rečeno nepraktična. Zato ćemo često koristiti grafike. Za potrebe crtanja (plotovanja) grafika, koristićemo paket matplotlib i njegov deo pyplot.

Kao i NumPy, pyplot se mora uključiti u kod. Konvencija je da se to uradi ovako:

⚠️ VAŽNO: Biblioteka matplotlib neće prikazivati grafike ako se koristi kernel XPython koji je pogodan za debagovanje. Zbog toga XPython treba koristiti samo u slučaju potrebe naprednijeg debagovanja. U nekoj od narednih verzija ovog kernela se očekuje ispravka ovog problema.

Da vidimo kako će izgledati podaci sada:

Ovaj grafik je nejasan. Šta je na x-osi, a šta na y-osi? Može lepše.

Može i više gradika na jednoj slici:

Ili u dikretnim tačkama:

Još mnogo lepih primera možemo naći na linku.

Nekad je važno da različite deove grafika možemo da uvećamo. Za to nam je potreban interaktivni prozor. Za tu svrhu ćemo koristiti matplotlib-widgets ekstenziju. Da bi se ona aktivirala potrebno je paket matplotlib uključiti na sledeći način:

Važno je da se ovakav pristup koristi samo sa Python 3 kernelom, XPython će izbacivati greške. Dodatno, widget nije vidljiv u eksportovanim html ili pdf fajlovima, pa u tom slučaju ne treba koristiti interaktivne prozore. Poništavanje opcije za interaktivne prozore se postiže komandom %matplotlib inline.

To je to što se uvoda tiče. Mnoge stvari će postati jasnije na primerima. Do kuckanja!