Introducere în Colab

Varianta rulabilă a fișierului curent poate fi accesată aici: Introducere în Colab

Ce este Colab?

Colaboratory este o instanță Jupyter notebook accesibilă full online. Asta înseamnă că nu necesită setup și le permite utilizatorilor să creeze și să colaboreze împreună cu alții pe documente live care conțin cod rulabil, vizualizări și text explicativ.

În general este util pentru a scrie prototipuri și a rezolva probleme scurte, ușor de urmărit secvențial.

Există mai multe variante pentru a rula o secvență de cod după ce ai dat click pe ea:

  • Apasă butonul de play din stânga acesteia
  • Rulează Shift + Enter pentru a trece la celula următoare
  • Rulează Cmd / Ctrl + Enter pentru a rămâne în celula curentă
print('Hello World!')

Output: Hello World

Sub celulă a apărut outputul acestuia. Outputul poate fi valoarea returnată de celulă, o afișare / serie de afișări (print) sau nimic. Obiectele apelate direct sau operațiile returnează o valoare, atribuirile nu apelează nimic:

'Hello World'

Output: Hello World

5 * 3

Output: 15

x = 6 * 4

Observi numărul care a apărut în stânga celulelor de până acum? Are forma: [1]. În colab putem rula celulele de mai multe ori, în orice ordine ne dorim. Acel număr ne indică ultimul indice de rulare corespunzător celulei - util pentru a ne da seama în proiecte mai mari care a fost ultima celulă rulată sau care a fost ordinea în care am rulat anumite celule.

Variabilele definite într-o celulă pot fi utilizate în orice celulă apelată ulterior, indiferent de poziția acesteia. Folosește săgeata în sus de pe celula de mai jos pentru a o muta deasupra atribuirii și observă rezultatul rulării acesteia:

x

Output: 24

Până acum am aflat că toate variabilele definite liber sunt globale. Rulări repetate neintenționate pot duce la confuzie în cod. Uneori aceasta poate fi rezolvată prin resetarea tuturor variabilelor globale:

%reset -f

Observi operatorul % pus la începutul liniei? Acesta anunță că vrem să folosim o funcție magică. Puteți afla mai multe detalii despre aceste funcții dacă apelați celula de mai jos:

%magic

O altă modalitate de a interacționa cu environmentul este cu ajutorul comenzii !. O putem folosi atât pentru a accesa poziția noastră în cloud cât și pentru a instala diverse pachete cu !pip install <librărie>.

!ls

Output: sample_data

Ce este sample_data?

În stânga paginii ai un buton de files care îți arată unde te afli. Atenție: Nu apăsa pe .. din directorul default, dar dacă o faci din greșeală îți poți accesa fișierele apăsând pe /content.

Directorul sample_data conține o serie de fișiere cu care te poți juca până te familiarizezi cu Colab. Citește fișierul README.md pentru mai multe detalii despre datele oferite.

Poți folosi drag & drop sau butonul de add files pentru a adăuga un fișier în sesiunea curentă. Dacă vrei să păstrezi fișierul pentru mai mult timp (sau să accesezi un fișier deja salvat) te poți conecta la contul tău Google Drive rulând codul de mai jos:

from google.colab import drive
drive.mount('/content/drive/')

Output: Mounted at /content/drive/

De aici poți citi / scrie fișiere în root-ul drive-ului tău personal (rădăcina arborelui de fișiere), într-o locație custom scriind calea până la aceasta de fiecare dată, sau poți seta noul root în locația dorită folosind librăria os:

import os
os.chdir("/content/drive/MyDrive/Cram School")

Notebook-urile Colab pot fi shared cu view access sau cu edit access. Cum ne afectează asta:

  1. Edit access: pe măsură ce modifici celule acestea se modifică automat pentru toate persoanele care au orice fel de drept la acest fișier
  2. View access: poți face modificări direct pe fișier, dar nu se salvează automat. Le poți salva cu Ctrl + S, conectându-te la drive-ul personal folosind codul de mai sus, sau copiind pe drive-ul personal cu ajutorul butonului din headerul paginii: Copy to Drive. În practică toate aceste metode fac exact același lucru.

Pentru a crea celule noi poți folosi butoanele + Code și + Text. Le poți accesa din headerul paginii sau după ce faci hover pe linia dintre 2 celule.

Mai multe resurse despre Colab Notebooks și cum poate fi folosit pentru Data Visualisation și Machine Learning aici: Welcome to Colaboratory

Cum Facem un Proiect?

Colab este un instrument util și ușor de urmărit pentru proiecte mici sau laboratoare, dar atunci când avem de făcut un proiect mai complex ne poate ajuta mai mult să ne mutăm pe un IDE local, caz în care e bine să respectăm anumite convenții.

1. Modularizare

Cel mai important lucru într-un proiect complex este modularizarea. Cu cât avem mai multe funcționalități cu atât devine mai greu de urmărit un proiect care se întinde mult în același fișier sau care nu are suficiente clase și funcții. Așadar ne vom folosi de următoarele elemente:

  • virtual environment — un loc magic unde se instalează toate librăriile necesare proiectului curent, fără a ne încărca memoria laptopului / calculatorului. Putem să ne creăm un virtual environment manual rulând în consolă:
pip install virtualenv  # instalăm virtualenv
python -m venv env      # creăm folderul în care vom salva datele
source env/bin/activate # activăm environmentul
  • clase — dacă vrem să repetăm o funcționalitate, ne e mult mai ușor să o declarăm direct într-o clasă, împreună cu funcțiile aferente. În general ne dorim să facem o clasă pentru fiecare componentă cu rol diferit (ex: embedding, antrenare).
  • fișiere — pentru a parcurge și mai ușor codul ne ajută să îl împărțim în multe fișiere în funcție de funcționalitate. O împărțire intuitivă ar fi să avem fișiere diferite pentru clase diferite plus un fișier main care le apelează.

2. Readability

Dacă plănuim să îi arătăm cuiva codul vreodată sau ne așteptăm să îl recitim în câteva luni / câțiva ani este destul de important să poată fi citit fără a necesita alte explicații. Din fericire nu ne trebuie o mașină a timpului dacă respectăm următoarele:

  • denumiri competente pentru clase, funcții, variabile etc. — ce trebuie să avem în vedere aici este "Cât i-ar lua unui coleg oarecare să înțeleagă pe cont propriu ce se întâmplă?". Putem merge chiar mai departe și să începem fiecare funcție cu o secțiune de comentarii unde explicăm ce face funcția în câteva cuvinte, ce fac parametrii și ce returnăm la final, de exemplu:
def maxim(a, b, c):
    """ Calculează maximul între 3 numere.
    
    :param a: o variabilă de tip int
    :param b: o variabilă de tip long
    :param c: o variabilă de tip long long
    :return: elementul cu valoarea cea mai mare
    """
    return max(a, b, c)
  • README.md — un fișier în care scriem un scurt overview al proiectului curent, ce clase apar în fiecare fișier și cum sunt legate între ele, ce structură are proiectul și ce comenzi trebuie rulate pentru a-l porni.
  • requirements.txt — un fișier care conține lista librăriilor utilizate în proiect împreună cu versiunea pe care am lucrat. Acesta poate fi extras automat folosind comanda:
py -m pip freeze > requirements.txt

3. Istoric

Doar pentru că ne-am mutat în local pentru un proiect mai complex nu înseamnă că trebuie să rămână local. În continuare putem lucra împreună cu mai mulți oameni pe același proiect online folosind git (Tutorial Git).

  • git — un instrument foarte puternic pentru a păstra istoricul tuturor modificărilor și pentru a crea un portofoliu online cu proiectele făcute până acum. Există 2 site-uri foarte cunoscute pe care le puteți utiliza: Tutorial GitHub și Tutorial GitLab. Ambele sunt la fel de bune, alegerea ține de preferința personală.
  • .gitignore — un fișier care conține lista folderelor și fișierelor pe care nu vrem să le salvăm în online. Acesta va include o serie de foldere cu configurări locale (__pycache__/, .idea/), variabile de sistem (env/) chei secrete (secret_key.txt) sau alte fișiere pe care nu vrem să le publicăm din diverse motive.

Model de cod complet

Python pe Scurt

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 1 - Python

Python este un limbaj de programare simplu. Nu folosim ";" la final de instrucțiune, ci terminăm instrucțiunile la final de rând. Nu folosim "{}" pentru a secvenția bucăți de cod, ci le scriem după ":". Ce este foarte important, în schimb, este indentarea. Pentru a declara că o bucată de cod se află în interiorul unei funcții, clase, instrucțiuni etc. aceasta trebuie să fie aliniată cu un tab în plus față de header:

x = 3
print(f"Valoarea variabilei x este: {x}")
if x > 4:
    print("Incorect")
else:
    print("x + 1 este: ", x + 1)

Output:

Valoarea variabilei X este: 3
x + 1 este:  4

Observați afișările. Folosind funcția print putem afișa orice tip de date pus între paranteze. Nu este obligatoriu să afișăm o singură variabilă, putem afișa oricât de multe dacă punem "," între ele. Ce altceva mai puteți observa din această secvență de cod?

...

Toate variabilele în Python sunt obiecte. Asta înseamnă că le puteți atribui orice valoare fără a preciza tipul de date, și chiar să actualizați valoarea unei variabile cu un tip de date diferit fără probleme:

x = 3
y = 2.15
print(x, y)

x = "abc"
print(x)

Output:

3 2.15
abc

Același principiu se aplică și la liste (vectori). Puteți crea o listă cu variabile de tipuri diferite:

l = ['z', 1, "mac-mac", 3.14, []]
l

Output: ['z', 1, 'mac-mac', 3.14, []]

Elementele pot fi accesate direct, sau putem secționa lista raportat la start_pos:end_pos:pas (slice-uri)

Pentru următoarele exemple incercati sa vă dați seama ce o să afișeze înainte de a le apela:

print(l[1:3])
print(l[3:])
print(l[:3])
print(l[1:9:2])
print(l[-2])
print(l[:-1])

Output:

[1, 'mac-mac']
[3.14, []]
['z', 1, 'mac-mac']
[1, 3.14]
3.14
['z', 1, 'mac-mac', 3.14]

EXERCIȚIU

Folosind doar slice-uri, afișează elementele listei date în ordine inversă.

l[::-1]

Output: [[], 3.14, 'mac-mac', 1, 'z']


Majoritatea tipurilor de date comune sunt preimplementate și ușor de folosit. De exemplu, mulțimile:

my_set = set()
my_set.add(3)
print("My first set: ", my_set)

my_second_set = set([2, 6, 3, 2])
print("My second set: ", my_second_set)

print("Set union: ", my_set.union(my_second_set))
print("Set intersection: ", my_set.intersection(my_second_set))

Output:

My first set:  {3}
My second set:  {2, 3, 6}
Set union:  {2, 3, 6}
Set intersection:  {3}

Mai multe funcții cu mulțimi găsiți aici: https://docs.python.org/3/tutorial/datastructures.html#sets

Sau dicționarele:

my_dict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
print("My first dictionary: ", my_dict)

my_second_dict = dict(name = "John", age = 36)
print("My second dictionary: ", my_second_dict)

print()

my_second_dict["country"] = "Norway"
print("We can easily add new fields: ", my_second_dict)

my_second_dict["country"] = "Italy"
print("Or modify them: ", my_second_dict)

Output:

My first dictionary:  {'brand': 'Ford', 'model': 'Mustang', 'year': 1964}
My second dictionary:  {'name': 'John', 'age': 36}

We can easily add new fields:  {'name': 'John', 'age': 36, 'country': 'Norway'}
Or modify them:  {'name': 'John', 'age': 36, 'country': 'Italy'}

Pentru mai multe funcții pe dicționare: https://docs.python.org/3/tutorial/datastructures.html#dictionaries

Funcții & Clase

Funcțiile se definesc cu ajutorul cuvântului def. Pot avea oricât de mulți parametri, și pot returna oricât de multe valori. Parametrii pot avea valori implicite, dar restricțiile de tip sunt mai mult pentru developer -- nu vor arunca erori dacă nu sunt respectate.

def f(age: int, name = "Bran") -> str:
  pass

f('Age')

Valorile implicite se află mereu la finalul listei de parametri.

Obervați cuvântul cheie pass folosit mai sus. Este o expresie vidă care poate fi utilizată în interiorul unei structuri pentru a nu arunca eroare până o completăm cu instrucțiunile dorite. O putem folosi atunci când vrem să scriem întâi headerele funcțiilor / claselor.

Clasele se definesc cu ajutorul cuvântului cheie class. Ca în orice limbaj, avem variabile statice pe care le putem apela fie la nivel de clasă, fie la nivel de instanță a clasei:

class Clasa:
  x = 10

Clasa.x

Output: 10

instanta = Clasa()
instanta.x

Output: 10

Cu ce seamănă clasele din ce ați mai făcut până acum?

...

Funcțiile din clase încep mereu cu operatorul self -- referință către clasa respectivă. Referința poate avea orice denumire, self este doar o convenție.

class Clasa:
  x = 3

  def f(self, x):
    return self.x, x

instanta = Clasa()
instanta.f(7)

Output: (3, 7)

Observă cum x și self.x sunt variabile complet separate. În Python, parametrii funcțiilor mereu sunt considerați independenți de mediul exterior, în timp ce elementele care încep cu self. sunt specifici instanței clasei respective.

Pe lângă funcțiile normale avem și o serie de funcții implicite:

  • __init__ -> inițializarea clasei (constructor)
  • __str__ -> transformarea clasei în instanță string
  • __lt__ -> definiția operatorului <
  • __le__ -> definiția operatorului <=
  • __gt__ -> definiția operatorului >

Funcțiile pot fi apelate cu denumirea lor, dar și direct, folosind simbolul respectiv.

class Clasa:
  def __init__(self, x):
    self.x = x

  def __str__(self):
    return "Valoarea clasei: " + str(self.x)

instanta = Clasa(5)
print(instanta)

Output: Valoarea clasei: 5

EXERCIȚIU

Creează o clasă cu minim 2 elemente. Creează 2 instanțe pentru noua clasă și afișeaz-o pe cea mai mare în urma comparației dintre ele.

class Clasa:
  def __init__(self, x, y):
    self.x = x
    self.y = y

  def __lt__(self, cls):
    return self.y < cls.y if self.x == cls.x else self.x < cls.x

  def __str__(self):
    return f"x: {self.x}\ny: {self.y}"

instanta1 = Clasa(3, 9)
instanta2 = Clasa(5, 6)

print(max(instanta1, instanta2))

Output:

x: 5
y: 6

Vom descoperi mai multe despre limbaj pe măsură ce lucrăm cu el.

Puteți găsi mai multe exemple aici: https://colab.research.google.com/drive/1Lxed6J79CsWwyrr8vpLugTmS6GMwMA-j?usp=drive_link

Sau în documentația limbajului: https://docs.python.org/3/

Regex

Un RegEx reprezintă o Expresie Regulată (codificarea unei secvențe de caractere). Poate fi folosit pentru a identifica secvențe de caractere într-un șir, pentru a înlocui secvențe sau pentru a separa un șir în funcție de diferite metrici. O secvență de căutare arată așa:

import re

txt = "The rain in Spain stays mainly in the plain"
x = re.search("Spain", txt)

if x:
  print("Cuvântul există în șir")
else:
  print("Cuvântul nu există în șir")

Output: Cuvântul există în șir

Există o serie de reguli și simboluri pe care le putem folosi pentru a descrie secvența de caractere căutată. O să modificăm exemplul original pentru a ne uita la câteva exemple.

Putem pune [.] în pozițiile în care poate fi orice caracter, [^] este caracterul început de șir și [$] este caracterul pentru final de șir.

print(re.findall("Sp.in", txt))
print(re.findall("^The rain in Spain stays mainly in the plain$", txt))

Output:

['Spain']
['The rain in Spain stays mainly in the plain']

Dacă punem [+] după un caracter acela trebuie să apară minim o dată. Dacă punem [*] după un caracter, acela poate să apară de oricâte ori, chiar și 0. Simbolurile pot fi combinate: [.+] înseamnă că putem avea o listă de caractere oricât de lungă.

x = re.findall(".*ai", txt)
print(x)

Output: ['The rain in Spain stays mainly in the plai']

În loc de a folosi [.+] putem folosi secvențe specifice:

  • \d identifică o listă de cifre
  • \D - caractere care nu sunt cifre
  • \s - caracterul spațiu
  • \w - litere mici și mari, cifre și caracterul "_"

Pentru a fi și mai specifici putem folosi seturi:

  • [ar] - cel puțin unul din caracterele "a" și "r" este prezent
  • [a-n] - orice caracter lowercase din intervalul dat
  • [0-9] - putem aplica intervalele și pentru cifre
  • [a-zA-Z] - sau le putem concatena pentru a accepta caractere mai variate
  • [1-3][7-9] - două seturi unul în continuarea celuilalt funcționează ca orice alte simboluri legate. Exemplul dat caută o secvență de 2 cifre, unde prima cifră este în intervalul 1-3, iar a doua în intervalul 7-9

Putem folosi secvențele de mai sus pentru a identifica toate cuvintele care conțin șirul "ai", de exemplu:

x = re.findall("\w*ai\w", txt)
print(x)

Output: ['rain', 'Spain', 'main', 'plain']

Lista completă de reguli și simboluri se află aici: https://docs.python.org/3/library/re.html

EXERCIȚIU

Creează o funcție de preprocesare a textului: funcția va primi un șir de caractere, îl va împărți în cuvinte și va elimina toate caracterele care nu sunt litere. Modifică funcția astfel încât să păstreze cratimele din cuvinte.

txt = "M-am dus la piata - asta voiam? Vom ajunge repede, cred, acasa!"

Lucru cu fișiere

O să ne conectăm întâi la contul de drive ca să accesăm mai ușor fișierele:

from google.colab import drive
drive.mount('/content/drive/')

Output: Mounted at /content/drive/

Stabilim locația "terminalului" în folderul acestui curs:

import os
os.chdir("/content/drive/MyDrive/Cram School")

Ați mai lucrat cu un terminal înainte? Putem apela anumite comenzi de linux dacă punem "!" în față. De exemplu, putem lista toate fișierele din folderul în care ne aflăm:

!ls

Output:

'Curs 1 - Python.ipynb'
'Curs 2 - Supervised Models.ipynb'
'Curs 3 - Preprocessing & Embeddings.ipynb'
'Curs 4 - Unsupervised Models & Ensembles.ipynb'
'Curs 5 - Scaling, Metrici & NLP.ipynb'

EXERCIȚIU

Identifică alte comenzi pe care le poți apela din terminalul colab, fie cu "!" fie cu ajutorul librăriei os:



Pentru a deschide un fișier folosim metoda open(cale_fisier, mod_deschidere).

f = open("input.txt", "r")

Există 4 tipuri de fișiere (mod_deschidere):

  • "r" - citește dintr-un fișier care deja există
  • "w" - scrie într-un fișier, creează fișierul dacă nu există deja
  • "a" - adaugă la informația deja scrisă dintr-un fișier, îl creează dacă nu există deja
  • "x" - creează un fișier, returnează eroare dacă deja există

Există mai multe metode de a citi sau scrie într-un fișier:

  • read() - citește fișierul complet
  • readlines() - citește pe rând câte o linie din fișier
  • write() - scrie în fișier
sir = f.read()
print(sir)

Nu uitați să închideți fișierele deschise!

f.close()

EXERCIȚIU

Creează un fișier cu extensia .csv sau .xls și adaugă în el 3 linii de text. Închide fișierul, deschide-l din nou, citește și afișează informația. Poți folosi orice metode vrei:



Analiză de Date

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 2 - Analiză de date

Introducere în Probabilități & Statistică

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 3 - Introducere în Probabilități & Statistică

Introducere în Machine Learning

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 4 - Introducere în Machine Learning

Introducere în Procesare de Text

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 5 - Introducere în Procesare de Text

Metrici & Scalări

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 6 - Metrici & Scalări

WordNet

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 7 - WordNet

K Nearest Neighbors

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 8 - K Nearest Neighbors

Unsupervised Models & Decision Trees

Varianta rulabilă a fișierului curent poate fi accesată aici: Ziua 9 - Unsupervised Models & Decision Trees

Proiect

Alegeți un set de date pe clasificare de text cu maxim 3 clase până pe 19.07.2023. Analizați setul de date, preprocesări, hiperparametri etc. (cum am făcut până acum) și pregătiți o prezentare powerpoint de 5 minute în care explicați ce ați încercat, ce ați observat, ce concluzii ați tras la final (ce funcționează cel mai bine și de ce?).

Puteți alege un set de date de aici: