Przegląd bibliotek

Zawartość

Przegląd bibliotek#

Autor sekcji: dr Filip Wójcik

Python jest jednym z najczęściej wybieranych języków programowania w zastosowaniach analitycznych. Swoją pozycję zawdzięcza między innymi:

  1. Relatywnej łatwości nauki;

  2. Dużej ilości dostępnych bibliotek;

  3. Aktywnej społeczności;

  4. Wykorzystaniu przez różne grupy zawodowe: od testerów, administratorów, po programistów i analityków danych.

Nic więc dziwnego, że jest uznawany za wiodące rozwiązanie w dziedzinie Data Science.

Istnieje cała masa bibliotek przeznaczonych do rozmaitych zastosowań, nierzadko dublujących się lub będących „nakładkami” na inne narzędzia. W tej sekcji zostaną przedstawione najważniejsze, naszym zdaniem, biblioteki, które warto znać i umieć wykorzystać w codziennej pracy.

Dla ułatwienia zadania podzieliliśmy je na kilka kategorii:

  1. Biblioteki do analizy danych;

  2. Biblioteki do operacji matematycznych i statystycznych;

  3. Biblioteki do wizualizacji danych;

  4. Biblioteki do uczenia maszynowego;

  5. Biblioteki do analizy szeregów czasowych;

  6. Biblioteki do automatyzacji i optymalizacji pracy;

  7. Inne.

Lista ta oczywiście nie jest w pełni kompletna i nie obejmuje m. in. narzędzi z zakresu np. MLOps, czyli zarządzania cyklem życia modeli uczenia maszynowego oraz całych projektów. Dodatkowo, niemal każda pod-dziedzina uczenia maszynowego posiada własne zestawy specyficznych bibliotek, których są dziesiątki. Przykładowo:

  1. Dla analizy grafów i grafowych sieci neuronowych:

    1. Pytorch Geometric

    2. NetworkX

    3. Deep Graph Library DGL

  2. Dla analizy języka naturalnego i przetwarzania tekstu:

    1. SpaCy

    2. NLTK

    3. Gensim

    4. Transformers od Hugginface - cały zestaw powiązanych narzędzi dla modeli LLM i szkolenia modeli językowych.

  3. Dla analizy obrazów:

    1. OpenCV

    2. Scikit-image

i tak dalej. Pracując z konkretną pod-dziedziną musimy zapoznać się z konkretnymi narzędziami w niej używanymi.

Lista zamieszczona w tej sekcji zawiera tylko absolutne minimum przydatne każdemu, rozpoczynającemu swoją przygodę w świecie DS.

Biblioteki do pracy z danymi#

Praca z danymi jest kluczowym elementem Data Science. Poniżej przedstawiamy dwie popularne biblioteki do manipulacji i analizy danych: pandas oraz polars.

Pandas#

Pandas to jedna z najbardziej wszechstronnych bibliotek Pythona, używana do manipulacji i analizy danych. Oferuje elastyczne struktury danych, takie jak Series (jednowymiarowe tablice) i DataFrame (dwuwymiarowe tablice/tabele bazodanowe/arkusze Excel - ogólniej: dane ustrukturyzowane).

Pandas jest nieodzownym elementem w zestawie narzędzi Data Science.

Zastosowania Pandas

Pandas jest używany do:

  1. Ładowania, czyszczenia i przekształcania danych.

  2. Analizy danych, takich jak grupowanie, agregacja, filtrowanie.

  3. Pracy z danymi z plików CSV, Excel, baz danych, oraz formatów takich jak JSON.

Dodatkowe materiały#

Przydatne linki - Pandas

  1. Pandas - przewodnik i oficjalna dokumentacja

  2. Książka - Data analytics with Python pokrywająca dużą część funkcjonalności biblioteki Pandas.

Przykład 1: Proste wczytywanie danych z pliku CSV#

import pandas as pd

# Wczytywanie danych z pliku CSV
url = "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/titanic.csv"
titanic_data = pd.read_csv(url)

# Przegląd pierwszych kilku wierszy
titanic_data.head()
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True
# Generowanie podsumowania danych
print(titanic_data.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   survived     891 non-null    int64  
 1   pclass       891 non-null    int64  
 2   sex          891 non-null    object 
 3   age          714 non-null    float64
 4   sibsp        891 non-null    int64  
 5   parch        891 non-null    int64  
 6   fare         891 non-null    float64
 7   embarked     889 non-null    object 
 8   class        891 non-null    object 
 9   who          891 non-null    object 
 10  adult_male   891 non-null    bool   
 11  deck         203 non-null    object 
 12  embark_town  889 non-null    object 
 13  alive        891 non-null    object 
 14  alone        891 non-null    bool   
dtypes: bool(2), float64(2), int64(4), object(7)
memory usage: 92.4+ KB
None

W powyższym przykładzie załadowano dane dotyczące pasażerów Titanica z pliku CSV dostępnego online, a następnie wyświetlono pierwsze kilka wierszy oraz podstawowe informacje o zbiorze danych, takie jak liczba wierszy i kolumn oraz typy danych.

Przykład 2: Filtrowanie i grupowanie#

# Filtrowanie danych - tylko kobiety, które przeżyły
surviving_females = titanic_data[(titanic_data['sex'] == 'female') & (titanic_data['survived'] == 1)]

print("Kobiety, które przetrwały katastrofę")
surviving_females.head()
Kobiety, które przetrwały katastrofę
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
8 1 3 female 27.0 0 2 11.1333 S Third woman False NaN Southampton yes False
9 1 2 female 14.0 1 0 30.0708 C Second child False NaN Cherbourg yes False
# Grupowanie danych - średni wiek i cena biletu dla różnych klas
average_stats = titanic_data.groupby('class').agg({'age': 'mean', 'fare': 'mean'})

print("\nŚredni wiek i cena biletu dla różnych klas:\n", average_stats)
Średni wiek i cena biletu dla różnych klas:
               age       fare
class                       
First   38.233441  84.154687
Second  29.877630  20.662183
Third   25.140620  13.675550

W tym przykładzie przefiltrowano dane, aby uzyskać informacje tylko o kobietach, które przeżyły katastrofę, a także dokonano grupowania danych według klasy pasażerów, obliczając średni wiek oraz średnią cenę biletu w każdej klasie.

Przykład 3: Wizualizacja danych przy pomocy Pandasa#

# Histogram wieku pasażerów
titanic_data['age'].hist(bins=30)

# Wykres pudełkowy (boxplot) dla cen biletów w różnych klasach
titanic_data.boxplot(column='fare', by='class', grid=False)
<Axes: title={'center': 'fare'}, xlabel='class'>
../_images/c5be411c1b67ac66851b30ffad053babcadc7cb33dc51b59675dad481f317c21.png ../_images/25df6a3c34a8cea4f9249d9170e1e9e4cd2695f70337fc7b6e392024c7dbba5d.png

Pandas oferuje również proste metody wizualizacji danych. Powyżej wygenerowano histogram wieku pasażerów oraz wykres pudełkowy (boxplot) dla cen biletów w różnych klasach, co pozwala na szybkie zwizualizowanie rozkładu danych.

Pandas posiada uproszczone funkcje do wizualizacji danych, które delegują wykonanie określonych funkcji do pakietów jak np. matplotlib.

Polars#

Polars to nowoczesna, szybka i wielowątkowa alternatywa dla Pandas, napisana w języku Rust. Jest zoptymalizowana pod kątem wydajności, zwłaszcza przy pracy z dużymi zbiorami danych. Polars oferuje podobne struktury danych jak Pandas, w tym DataFrame, ale dzięki swojej architekturze może działać szybciej i efektywniej na dużych danych.

Zastosowania Polars

Polars jest używany do:

  1. Szybkiej analizy dużych zbiorów danych.

  2. Przetwarzania danych na wielu rdzeniach procesora (wielowątkowość).

  3. Wykonywania operacji podobnych do Pandas, ale z lepszą wydajnością.

Dodatkowe materiały#

Przykład 1: Wczytaywanie i analiza dancyh#

import polars as pl

# Wczytywanie danych z pliku CSV
titanic_data = pl.read_csv(url)

# Przegląd pierwszych kilku wierszy
titanic_data.head()
shape: (5, 15)
survivedpclasssexagesibspparchfareembarkedclasswhoadult_maledeckembark_townalivealone
i64i64strf64i64i64f64strstrstrboolstrstrstrbool
03"male"22.0107.25"S""Third""man"truenull"Southampton""no"false
11"female"38.01071.2833"C""First""woman"false"C""Cherbourg""yes"false
13"female"26.0007.925"S""Third""woman"falsenull"Southampton""yes"true
11"female"35.01053.1"S""First""woman"false"C""Southampton""yes"false
03"male"35.0008.05"S""Third""man"truenull"Southampton""no"true
# Informacje o danych
print(titanic_data.describe())
shape: (9, 16)
┌────────────┬──────────┬──────────┬────────┬───┬──────┬─────────────┬───────┬──────────┐
│ statistic  ┆ survived ┆ pclass   ┆ sex    ┆ … ┆ deck ┆ embark_town ┆ alive ┆ alone    │
│ ---        ┆ ---      ┆ ---      ┆ ---    ┆   ┆ ---  ┆ ---         ┆ ---   ┆ ---      │
│ str        ┆ f64      ┆ f64      ┆ str    ┆   ┆ str  ┆ str         ┆ str   ┆ f64      │
╞════════════╪══════════╪══════════╪════════╪═══╪══════╪═════════════╪═══════╪══════════╡
│ count      ┆ 891.0    ┆ 891.0    ┆ 891    ┆ … ┆ 203  ┆ 889         ┆ 891   ┆ 891.0    │
│ null_count ┆ 0.0      ┆ 0.0      ┆ 0      ┆ … ┆ 688  ┆ 2           ┆ 0     ┆ 0.0      │
│ mean       ┆ 0.383838 ┆ 2.308642 ┆ null   ┆ … ┆ null ┆ null        ┆ null  ┆ 0.602694 │
│ std        ┆ 0.486592 ┆ 0.836071 ┆ null   ┆ … ┆ null ┆ null        ┆ null  ┆ null     │
│ min        ┆ 0.0      ┆ 1.0      ┆ female ┆ … ┆ A    ┆ Cherbourg   ┆ no    ┆ 0.0      │
│ 25%        ┆ 0.0      ┆ 2.0      ┆ null   ┆ … ┆ null ┆ null        ┆ null  ┆ null     │
│ 50%        ┆ 0.0      ┆ 3.0      ┆ null   ┆ … ┆ null ┆ null        ┆ null  ┆ null     │
│ 75%        ┆ 1.0      ┆ 3.0      ┆ null   ┆ … ┆ null ┆ null        ┆ null  ┆ null     │
│ max        ┆ 1.0      ┆ 3.0      ┆ male   ┆ … ┆ G    ┆ Southampton ┆ yes   ┆ 1.0      │
└────────────┴──────────┴──────────┴────────┴───┴──────┴─────────────┴───────┴──────────┘

W tym przykładzie załadowano dane o pasażerach Titanica za pomocą Polars i wyświetlono pierwsze kilka wierszy oraz podstawowe statystyki opisowe dla każdej kolumny. Operacje te będą bardzo szybkie w Polarsie, nawet przy dużych zbiorach danych.

Pozornie składnia Polarsa wydaje sie bardzo podobna do Pandasa, ale różni się w szczegółach, upodabniając go nieco do np. Sparka, używanego dla zastosowań Big Data.

Przykład 2: Filtrowanie i agregacja dnaych#

# Filtrowanie danych - tylko mężczyźni powyżej 30 lat
filtered_data = titanic_data.filter((pl.col('sex') == 'male') & (pl.col('age') > 30))

filtered_data
shape: (202, 15)
survivedpclasssexagesibspparchfareembarkedclasswhoadult_maledeckembark_townalivealone
i64i64strf64i64i64f64strstrstrboolstrstrstrbool
03"male"35.0008.05"S""Third""man"truenull"Southampton""no"true
01"male"54.00051.8625"S""First""man"true"E""Southampton""no"true
03"male"39.01531.275"S""Third""man"truenull"Southampton""no"false
02"male"35.00026.0"S""Second""man"truenull"Southampton""no"true
12"male"34.00013.0"S""Second""man"true"D""Southampton""yes"true
01"male"31.00050.4958"S""First""man"true"A""Southampton""no"true
01"male"33.0005.0"S""First""man"true"B""Southampton""no"true
03"male"47.0009.0"S""Third""man"truenull"Southampton""no"true
03"male"33.0007.8958"S""Third""man"truenull"Southampton""no"true
03"male"32.0007.75"Q""Third""man"truenull"Queenstown""no"true
# Grupowanie danych - suma biletów dla różnych klas
fare_sum = titanic_data.group_by('class').agg(pl.sum('fare').alias('total_fare'))
print("\nSuma biletów dla różnych klas:\n", fare_sum)
Suma biletów dla różnych klas:
 shape: (3, 2)
┌────────┬────────────┐
│ class  ┆ total_fare │
│ ---    ┆ ---        │
│ str    ┆ f64        │
╞════════╪════════════╡
│ Second ┆ 3801.8417  │
│ First  ┆ 18177.4125 │
│ Third  ┆ 6714.6951  │
└────────┴────────────┘

Podobnie jak w Pandas, Polars pozwala na filtrowanie i grupowanie danych. W powyższym kodzie przefiltrowano dane, aby wyświetlić tylko mężczyzn powyżej 30 roku życia, a następnie pogrupowano dane według klasy pasażerów, sumując ceny biletów.

Widzimy jednak, że zachodzą spore różnice w składni. Polars wykorzystuje funkcyjne podejście do wywoływania operacji, podobnie jak Apache Spark.

Biblioteki do operacji matematycznych i statystycznych#

NumPy#

NumPy jest fundamentalną biblioteką Pythona do obliczeń numerycznych. Oferuje wsparcie dla efektywnego przechowywania i manipulacji dużymi tablicami oraz macierzami wielowymiarowymi. Posiada również bogaty zbiór funkcji matematycznych umożliwiających wykonywanie operacji na tych danych, takich jak wszelkiego rodzaju arytmetyka, przekształcenia algebraiczne, badanie funkcji, i inne.

Znajomość NumPy, zwłaszcza w zakresie macierzy jest fundamentalną umiejętnością dla działań w zakresie uczenia maszynowego.

Zastosowania NumPy

NumPy jest często używany do:

  1. Tworzenia i manipulowania macierzami: dodawanie, mnożenie, transpozycja, itd.

  2. Wszelkich innych działań z zakresu algebry liniowej, jak np. rozwiazywanie układów równań.

Dodatkowe materiały#

Przykład 1: Tworzenie macierzy i podstawowe operacje na nich#

import numpy as np

# Tworzenie macierzy 3x3
A = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

# Transpozycja macierzy
A_transposed = A.T

# Mnożenie macierzy
B = np.array([[9, 8, 7],
              [6, 5, 4],
              [3, 2, 1]])
result = np.dot(A, B)

print("Macierz A:\n", A)
print("Macierz A po transpozycji:\n", A_transposed)
print("Wynik mnożenia A i B:\n", result)
Macierz A:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
Macierz A po transpozycji:
 [[1 4 7]
 [2 5 8]
 [3 6 9]]
Wynik mnożenia A i B:
 [[ 30  24  18]
 [ 84  69  54]
 [138 114  90]]

W powyższym kodzie utworzono macierz A o wymiarach 3x3, a następnie dokonano jej transpozycji. Następnie wykonano operację mnożenia macierzy A z macierzą B za pomocą funkcji np.dot.

Przykład 2: Rozwiązywanie układów równań liniowych#

W poniższym przykładzie wykorzystamy numpy do rozwiazania układu równań liniowych:

\[\begin{split}\begin{align} \begin{cases} x +2y &= 4 \\ 3x - 5y &= 1 \end{cases} \end{align}\end{split}\]
X = np.array([
    [1., 2.],
    [3, -5]
])
y = np.array([4, 1])

coef = np.linalg.solve(X, y)
print(f"Rozwiązanie układu to: [x,y] = {coef}")
Rozwiązanie układu to: [x,y] = [2. 1.]

SciPy#

SciPy to rozbudowana biblioteka dla zaawansowanych obliczeń naukowych. Oferuje funkcje do optymalizacji procesów, rozwiązywania równań różniczkowych, statystyki, przetwarzania sygnałów i wielu innych zadań. W tej sekcji skupimy się na module scipy.stats, który zawiera zestaw narzędzi statystycznych, w tym rozkłady prawdopodobieństwa, testy statystyczne, oraz funkcje estymacji.

Zastosowania SciPy

SciPy jest używany do:

  1. Przeprowadzania testów statystycznych (np. testu t-Studenta, testu chi-kwadrat, etc).

  2. Estymacji parametrów rozkładów.

  3. Analizy regresji i korelacji.

Dodatkowe materiały#

Przykład 1: test T-studenta dla dwóch próbek#

from scipy import stats

# Przykładowe dane: dwie próbki
sample1 = [5, 7, 8, 9, 10]
sample2 = [6, 9, 7, 12, 11]

# Test t-Studenta
t_stat, p_value = stats.ttest_ind(sample1, sample2)

print("t-statystyka:", t_stat)
print("p-wartość:", p_value)
t-statystyka: -0.8401680504168061
p-wartość: 0.4252097205513896

W powyższym kodzie przeprowadzono test t-Studenta dla dwóch niezależnych próbek danych. Wynikiem jest t-statystyka oraz p-wartość, która mówi nam o tym, czy możemy odrzucić hipotezę zerową o równości średnich w populacji.

Przykad 2: Estymacja parametrów rozkładu#

# Przykładowe dane - normalnei nie wiemy, z jakiego rozkładu by pochodziły :)
data = np.random.normal(loc=10, scale=3, size=1000)

# Estymacja parametrów "nieznanego" rozkładu na podstawie danych
mu, std = stats.norm.fit(data)

print("Estymowana średnia (mu):", mu)
print("Estymowana odchylenie standardowe (std):", std)
Estymowana średnia (mu): 10.264603450844067
Estymowana odchylenie standardowe (std): 2.9264260614737014

W tym przykładzie przeprowadzono estymację parametrów rozkładu normalnego (średniej oraz odchylenia standardowego) na podstawie wygenerowanej próbki danych.

Biblioteki do wizualizacji danych#

Wizualizacja danych to kluczowy element pracy z danymi, umożliwiający efektywne komunikowanie wyników analizy, odkrywanie wzorców i trendów, a także weryfikację hipotez. W tej sekcji omówimy trzy popularne biblioteki do wizualizacji danych: matplotlib, seaborn oraz plotly.

Matplotlib#

Matplotlib to podstawowa biblioteka do tworzenia wykresów w Pythonie. Jest niezwykle elastyczna i umożliwia tworzenie szerokiej gamy obrazów od prostych wykresów liniowych po skomplikowane wizualizacje 3D. Matplotlib jest często używany jako fundament dla innych bibliotek takich jak Seaborn. Oferuje kolosalną ilość funkcjonalności, których poznanie wymaga sporo czasu i wysiłku. W praktyce, zwykle uczymy się tych elementów, które najbardziej nam się przydadzą w prowadzonych analizach.

Zastosowania Matplolib

Matplotlib jest używany do:

  1. Tworzenia wykresów liniowych, słupkowych, kołowych, histogramów, i wielu innych.

  2. Dostosowywania elementów wykresu, takich jak osie, legendy, etykiety, kolory.

  3. Tworzenia interaktywnych wykresów i animacji.

Dodatkowe materiały#

Przykład 1: Prosty wykres liniowy#

import matplotlib.pyplot as plt

# Przykładowe dane
x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]

# Tworzenie wykresu liniowego
plt.plot(x, y, marker='o')

# Dodawanie tytułu i etykiet osi
plt.title('Prosty wykres liniowy')
plt.xlabel('X')
plt.ylabel('Y')

# Wyświetlanie wykresu
plt.show()
../_images/270e506ca4c36cac0da1ffeb2c7f52b759e8a0ec3ad9b5a94614181f5c57fa77.png

W powyższym przykładzie utworzono prosty wykres liniowy z pięcioma punktami. Dodano również tytuł wykresu oraz etykiety osi X i Y. Wykres został wygenerowany za pomocą funkcji plt.plot i wyświetlony z użyciem plt.show.

Przykład 2: Tworzenie histogramu i wykresu pudełkowego#

# Generowanie losowych danych
data = np.random.normal(0, 1, 1000)

# Tworzenie histogramu
plt.hist(data, bins=30, alpha=0.7, color='blue')
plt.title('Histogram rozkładu normalnego')
plt.xlabel('Wartość')
plt.ylabel('Częstość')
plt.show()

# Tworzenie wykresu pudełkowego
plt.boxplot(data)
plt.title('Wykres pudełkowy')
plt.show()
../_images/22057557adfbdc51bd18b29403cb30100691dd8bb3c221aa61ca77ae8f8cd912.png ../_images/11b2537d33d02aa0b4e68a0d53e09ced77356e607ac6701cc951c9b2d43c47c8.png

W powyższym kodzie najpierw wygenerowano losowy zbiór danych o rozkładzie normalnym. Następnie utworzono histogram dla tych danych, a także wykres pudełkowy, który pomaga wizualizować rozkład oraz identyfikować wartości odstające.

Seaborn#

Seaborn to biblioteka zbudowana na bazie Matplotlib, która upraszcza proces tworzenia atrakcyjnych wizualnie wykresów. Seaborn oferuje domyślne style i palety kolorów, które poprawiają estetykę wykresów, a także dodatkowe funkcje ułatwiające wizualizację złożonych relacji między zmiennymi, jak wykresy rozrzutu z liniami regresji, wykresy skrzynkowe, czy wykresy gęstości.

Zastosowania Seaborn

Seaborn jest używany do:

  1. Tworzenia bardziej zaawansowanych wykresów, jak wykresy korelacji, heatmapy.

  2. Wizualizacji rozkładów danych, takich jak wykresy gęstości, violin ploty.

  3. Łatwego łączenia danych kategorycznych z ilościowymi na wykresach.

Dodatkowe materiały#

Przykład 1: Tworzenie wykresu rozrzutu z linią regresji#

import seaborn as sns

# Wczytywanie danych z seaborn
tips = sns.load_dataset('tips')

# Tworzenie wykresu rozrzutu z linią regresji
sns.lmplot(x='total_bill', y='tip', data=tips)

# Dodawanie tytułu wykresu
plt.title('Relacja między rachunkiem a napiwkiem')
plt.show()
../_images/e62579667f7f1eaf6be5429450fc3a5b02759ab957bfb487e5333f9f29c7d63f.png

W powyższym przykładzie użyto wbudowanego w Seaborn zbioru danych tips i utworzono wykres rozrzutu, przedstawiający relację między wysokością rachunku a napiwkiem, z nałożoną linią regresji. Wykres jest automatycznie dostosowany do estetyki Seaborn.

Przykład 2: tworzenie wykresu pudełkowego (boxplot) i heatmapy#

# Wykres skrzynkowy dla napiwków w zależności od dnia tygodnia
sns.boxplot(x='day', y='tip', data=tips)
plt.title('Napiwki w zależności od dnia tygodnia')
plt.show()

# Tworzenie macierzy korelacji
corr = tips[['total_bill', 'tip', 'size']].corr()

# Heatmapa korelacji
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('Macierz korelacji')
plt.show()
../_images/df617fa024cbb4afb2c996431db5074a7bcd49168647e888f9713870b2bb5ad7.png ../_images/c6f7af11fb2266255e478f4ebcd4b459546da73b3829cdfc7c72b3b377ebc96f.png

Najpierw stworzono wykres skrzynkowy, który porównuje rozkład napiwków w zależności od dnia tygodnia. Następnie, na podstawie macierzy korelacji obliczonej dla danych z tips, wygenerowano heatmapę z zaznaczonymi wartościami korelacji pomiędzy różnymi zmiennymi.

Przykład 3: Tworzenie zaawansowanych wykresów wielofacetowych#

import seaborn as sns

# Load the Titanic dataset
titanic_data = sns.load_dataset('titanic')

# Create a FacetGrid with multiple facets
g = sns.FacetGrid(titanic_data, col='class', row='sex', hue='survived', height=4, sharey=False)

# Add a scatter plot to each facet
g.map(sns.scatterplot, 'age', 'fare')

# Add a regression line to each facet
g.map(sns.regplot, 'age', 'fare')

# Set titles for each facet
g.set_titles('Class: {col_name}, Sex: {row_name}')

# Add a legend
g.add_legend()

# Show the plot
plt.show()
../_images/8b7a72d7a227a552904664aba61cb54e653724f3b437a389f98ba8ee8195dfd9.png

W przykładzie powyżej wykonano tzw. wykres wielofasetowy z użyciem Seaborn, dzieląc zbiór danych na różne kategorie i przedstawiając na kilku planszach. Jednocześnie nałożono także linie regresji.

Plotly#

Plotly to biblioteka do tworzenia interaktywnych wykresów, która jest idealna do publikowania wyników analizy danych online. W przeciwieństwie do Matplotlib czy Seaborn, Plotly pozwala na tworzenie interaktywnych wizualizacji, które można łatwo integrować z aplikacjami internetowymi. Plotly obsługuje szeroki zakres wykresów, od podstawowych po zaawansowane, takie jak wykresy 3D, mapy geograficzne, wykresy sankey’ego i wiele innych.

Co ważne, Plotly można operować na kilku poziomach złożoności:

  1. Plotly Express - wysokopoziomowy interfejs do tworzenia wykresów, który pozwala na szybkie generowanie wykresów z minimalną ilością kodu.

  2. Plotly Graph Objects - niskopoziomowy interfejs, który umożliwia pełną kontrolę nad wyglądem i zachowaniem wykresu.

Zastosowania Plotly

Plotly jest używany do:

  1. Tworzenia interaktywnych wykresów, które można łatwo publikować online.

  2. Tworzenia zaawansowanych wykresów, takich jak wykresy 3D, mapy geograficzne, wykresy sankey’ego.

  3. Integracji z aplikacjami internetowymi.

Dodatkowe materiały#

Renderowanie wykresów w notebookach i książce

Wykresy tworzone w Plotly są pod spodem konwertowane do HTML. Z tego względu nie zawsze prawidłowo renderują się w postaci Jupyter Notebooka oraz w materiałach takich, jak ta książka. Warto zatem uruchomić kod w swoim środowisku, aby zobaczyć wykresy w pełnej krasie - np. w Google Colab albo lokalnie.

Przykład 1: Tworzenie interaktywnego wykresu liniowego#

import plotly.express as px

# Przykładowe dane
df = px.data.gapminder().query("country=='Canada'")

# Tworzenie interaktywnego wykresu liniowego
fig = px.line(df, x='year', y='gdpPercap', title='GDP per Capita in Canada')

# Wyświetlanie wykresu
fig.show()

W tym przykładzie użyto wbudowanego zbioru danych gapminder i utworzono interaktywny wykres liniowy przedstawiający wzrost PKB na mieszkańca w Kanadzie na przestrzeni lat. Wykres można eksplorować, np. przybliżając wybrane fragmenty lub klikając na punkty danych.

Przykład 2: Tworzenie wykresu 3D#

# Przykładowe dane
df = px.data.iris()

# Tworzenie wykresu 3D
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_length',
                    color='species', title='3D Scatter Plot - Iris Dataset')

# Wyświetlanie wykresu
fig.show()

W tym przykładzie stworzono trójwymiarowy wykres rozrzutu dla zbioru danych iris, który jest często używany do testowania algorytmów uczenia maszynowego. Kolory na wykresie reprezentują różne gatunki irysów, a wykres jest interaktywny, co umożliwia obracanie i przybliżanie przestrzeni 3D.

Biblioteki uczenia maszynowego#

W poniższej sekcji omówimy wyłącznie najważniejsze biblioteki przeznaczone do uczenia maszynowego. Warto zaznaczyć, że w tej dziedzinie istnieje wiele innych narzędzi, które są używane w zależności od konkretnego problemu, takich jak np. XGBoost, LightGBM, CatBoost do modeli gradient boosting, czy TensorFlow, PyTorch, Keras do sieci neuronowych. Nie sposób tutaj wymienić i przedstawić ich wszystkich.

Co więcej - nawet w obrębie omówionych tutaj bibliotek jak scikit-learn znajduja się setki (jeśli nie tysiące) różnych algorytmów, które można wykorzystać do rozwiązania konkretnego problemu. Warto zatem zapoznać się z oficjalną dokumentacją, aby dowiedzieć się, jakie funkcjonalności są dostępne.

W tym opracowaniu chcemy przedstawić tylko przykłady zastosowania, oraz podstawowe funkcjonalności, które są dostępne w wybranych bibliotekach, wskazując punkt startowy do dalszej eksploracji narzędzi.

Scikit-learn#

Scikit-learn to jedna z najbardziej wszechstronnych i najczęściej używanych bibliotek do uczenia maszynowego w Pythonie. Oferuje narzędzia do przetwarzania danych, uczenia nadzorowanego i nienadzorowanego, a także do oceny i wyboru modeli. Scikit-learn jest idealny do szybkiego prototypowania modeli oraz do realizacji klasycznych zadań ML, takich jak klasyfikacja, regresja, klasteryzacja czy redukcja wymiarów.

Ważne jest, że Scikit-learn udosstępnia spójne API, czyli interfejs programistyczny, który jest taki sam dla wszystkich algorytmów. Dzięki temu, nauka i stosowanie różnych modeli jest znacznie ułatwione!

Zastosowania Scikit-learn

Scikit-learn jest używany do:

  1. Wszelkich zadań z zakresu klasycznego uczenia maszynowego, (ang. classic ML), czyli klasyfikacji, regresji, klasteryzacji, redukcji wymiarów.

  2. Walidacji krzyżowej i projektowania eksperymentów.

  3. Wyliczania metryk i sprawdzania jakości modeli.

Dodatkowe materiały#

Przydatne linki - Scikit-learn

Oficjalny przewodnik i podręcznik Scikit-learn - to jest nieocenione źródło wiedzy, podzielone na rozdziały i sekcje. Można je traktować jak pełnoprawny podręcznik do uczenia maszynowego.

Przykład 1: Klasyfikacja za pomocą SVM i przegląd typowego API#

from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

# Wczytywanie wbudowanego zbioru danych (Iris)
iris = datasets.load_iris()
X = iris.data
y = iris.target

# Podział na zbiór treningowy i testowy
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Tworzenie modelu SVM i trenowanie
model = SVC(kernel='linear')
model.fit(X_train, y_train)

# Przewidywanie na zbiorze testowym
y_pred = model.predict(X_test)

# Ocena modelu
accuracy = accuracy_score(y_test, y_pred)
print(f"Dokładność modelu SVM: {accuracy:.2f}")
Dokładność modelu SVM: 1.00

W powyższym przykładzie użyto Scikit-learn do klasyfikacji gatunków irysów za pomocą maszyny wektorów nośnych (SVM) z liniowym kernelem. Model został przetrenowany na zbiorze treningowym, a następnie oceniony na zbiorze testowym, osiągając określoną dokładność.

Charakterystyczna jest obecność następujących elementów API:

  1. fit - trenowanie modelu na danych treningowych.

  2. predict - predykcja na danych testowych.

  3. Zastosowanie metryki, w tym przypadku accuracy_score, poprzez jej wywołanie z sygnaturą: METRYKA(y_true, y_pred).

Taki sposób działania będzie wspólny dla niemal wszystkich modeli w Scikit-learn.

Przykład 2: Klasteryzacja z pomocą algorytmu K-średnich#

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# Wygenerowanie losowych danych 2D
X, _ = datasets.make_blobs(n_samples=300, centers=4, random_state=42)

# Klasteryzacja przy użyciu k-means
kmeans = KMeans(n_clusters=4)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)

# Wizualizacja wyników
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], s=200, c='red', marker='X')
plt.title('Klasteryzacja k-means')
plt.show()
../_images/3bdf91400af3293c779feab68e72d3a5754969feac1607b30607a1aa0e012853.png

Powyższy kod pokazuje, jak użyć k-means do klasteryzacji danych 2D. Po wygenerowaniu losowych danych, zastosowano algorytm k-means, który podzielił dane na cztery klastry. Wynik został zwizualizowany na wykresie z zaznaczonymi centroidami klastrów.

PyTorch + Pytorch Lightning#

PyTorch to jedna z najpopularniejszych bibliotek do uczenia głębokiego (ang. deep learning) oraz innych zadań związanych z ML. PyTorch został opracowany przez MEta AI Research i jest znany ze swojej elastyczności i łatwości użycia, szczególnie w badaniach naukowych. Umożliwia definiowanie, trenowanie i testowanie sieci neuronowych, wspiera również dynamiczne przetwarzanie grafów obliczeniowych, co jest jego dużą zaletą w porównaniu do innych frameworków.

PyTorch Lightning to wysokopoziomowy framework zbudowany na bazie PyTorch, który ma na celu uproszczenie procesu trenowania sieci neuronowych. PyTorch jest bardzo elastyczny, ale ta elastyczność może prowadzić do skomplikowanego kodu, szczególnie gdy projekt staje się bardziej złożony. PyTorch Lightning pomaga w rozdzieleniu kodu związanego z logiką modelu od kodu związanego z infrastrukturą, taką jak trenowanie, walidacja, logowanie i zarządzanie eksperymentami. Dzięki temu kod staje się bardziej czytelny, łatwiejszy do debugowania i skalowania.

Zastosowania PyTorch

PyTorch jest używany do:

  1. Tworzenia i trenowania sieci neuronowych.

  2. Badań naukowych i tworzenia innowacyjnych modeli sieci neuronowych.

W czym pomaga PyTorch Lightning:

  1. Struktura kodu: Uporządkowanie kodu poprzez oddzielenie logiki modelu od logiki trenowania.

  2. Skalowanie: Umożliwia łatwe przejście od trenowania na pojedynczym GPU do trenowania na klastrach wielo-GPU czy TPU bez konieczności zmiany kodu.

  3. Zarządzanie eksperymentami: Automatyczne logowanie, punkty zapisu (ang. checkpointing), oraz wsparcie dla integracji z narzędziami do monitorowania eksperymentów (np. TensorBoard).

  4. Uproszczona walidacja i testowanie: PyTorch Lightning ułatwia implementację pętli walidacyjnych i testowych, co jest szczególnie przydatne w większych projektach.

Dodatkowe materiały#

Przydatne linki - PyTorch

  1. Oficjalna dokumentacja PyTorch

  2. Deep Learning Fundamendtals - kurs - fantastyczny (i darmowy) kurs poświęcony bibliotece Pytorch oraz Pytorch Lightning, prowadzony przez dr Sebastiana Raschkę, autora wielu narzędzi i publikacji z zakresu ML. Fantastyczne źródło wiedzy.

Przykład 1: Prosta sieć neuronowa do klasyfikacji danych tabelaryczncyh#

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from torch.utils.data import DataLoader, TensorDataset
from typing import Tuple

# Wczytywanie i przygotowanie danych
data = load_wine()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.3, random_state=42)

# Normalizacja danych
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# Konwersja do tensora
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.long)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.long)

# Tworzenie DataLoaderów
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

# Definiowanie modelu MLP
class MLP(nn.Module):
    def __init__(self, dims: Tuple[int, ...]):
        super().__init__()
        layers = []
        for dim_in, dim_out in zip(dims, dims[1:]):
            layer = nn.Linear(dim_in, dim_out)
            layers.append(layer)
            layers.append(nn.ReLU())
        self.net = nn.Sequential(*layers[:-1])

    def forward(self, x):
        out = self.net(x)
        return out

# Inicjalizacja modelu, funkcji kosztu i optymalizatora
torch.manual_seed(42)
model = MLP([X_train.shape[1], 32, 16, len(data.target_names)])
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Trenowanie modelu
for epoch in range(20):
    running_loss = 0.0
    for inputs, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()

    print(f"Epoch {epoch+1}, Loss: {running_loss/len(train_loader):.4f}")

# Ocena modelu
correct = 0
total = 0
with torch.no_grad():
    for inputs, labels in test_loader:
        outputs = model(inputs)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f"Dokładność na zbiorze testowym: {100 * correct / total:.2f}%")
Epoch 1, Loss: 1.0892
Epoch 2, Loss: 1.0654
Epoch 3, Loss: 1.0424
Epoch 4, Loss: 1.0159
Epoch 5, Loss: 0.9890
Epoch 6, Loss: 0.9614
Epoch 7, Loss: 0.9311
Epoch 8, Loss: 0.8964
Epoch 9, Loss: 0.8612
Epoch 10, Loss: 0.8256
Epoch 11, Loss: 0.7891
Epoch 12, Loss: 0.7513
Epoch 13, Loss: 0.7106
Epoch 14, Loss: 0.6666
Epoch 15, Loss: 0.6277
Epoch 16, Loss: 0.5841
Epoch 17, Loss: 0.5435
Epoch 18, Loss: 0.5027
Epoch 19, Loss: 0.4608
Epoch 20, Loss: 0.4207
Dokładność na zbiorze testowym: 92.59%

W powyższym przykładzie zdefiniowano model MLP, który składa się z dwóch ukrytych warstw (z 32 i 16 neuronami). Dane wejściowe zostały przeskalowane przy użyciu standardowej normalizacji. Model został przetrenowany przez 20 epok, a następnie oceniony na zbiorze testowym.

Przykład 2: MLP do regresji na danych tabelarycznych#

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from torch.utils.data import DataLoader, TensorDataset
from typing import Tuple

# Wczytywanie i przygotowanie danych
data = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.3, random_state=42)

# Normalizacja danych
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# Konwersja do tensora
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

# Tworzenie DataLoaderów
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

# Definiowanie modelu MLP do regresji
class MLPRegressor(nn.Module):
    def __init__(self, dims: Tuple[int, ...]):
        super().__init__()
        layers = []
        for dim_in, dim_out in zip(dims, dims[1:]):
            layer = nn.Linear(dim_in, dim_out)
            layers.append(layer)
            layers.append(nn.ReLU())
        self.net = nn.Sequential(*layers[:-1])

    def forward(self, x):
        out = self.net(x)
        return out

# Inicjalizacja modelu, funkcji kosztu i optymalizatora
torch.manual_seed(42)
model = MLPRegressor([X_train.shape[1], 32, 16, 1])
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Trenowanie modelu
for epoch in range(25):
    running_loss = 0.0
    for inputs, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()

    print(f"Epoch {epoch+1}, Loss: {running_loss/len(train_loader):.4f}")

# Ocena modelu
model.eval()
with torch.no_grad():
    test_predictions = model(X_test)
    mse = criterion(test_predictions, y_test)
    print(f"MSE na zbiorze testowym: {mse.item():.4f}")
Epoch 1, Loss: 1.4307
Epoch 2, Loss: 0.4944
Epoch 3, Loss: 0.4216
Epoch 4, Loss: 0.3952
Epoch 5, Loss: 0.3777
Epoch 6, Loss: 0.3673
Epoch 7, Loss: 0.3571
Epoch 8, Loss: 0.3505
Epoch 9, Loss: 0.3416
Epoch 10, Loss: 0.3340
Epoch 11, Loss: 0.3266
Epoch 12, Loss: 0.3242
Epoch 13, Loss: 0.3173
Epoch 14, Loss: 0.3122
Epoch 15, Loss: 0.3087
Epoch 16, Loss: 0.3120
Epoch 17, Loss: 0.3042
Epoch 18, Loss: 0.3021
Epoch 19, Loss: 0.2984
Epoch 20, Loss: 0.2955
Epoch 21, Loss: 0.2982
Epoch 22, Loss: 0.2937
Epoch 23, Loss: 0.2940
Epoch 24, Loss: 0.2911
Epoch 25, Loss: 0.2893
MSE na zbiorze testowym: 0.2996

W tym przykładzie zdefiniowano model MLP do zadania regresji. Model składa się z dwóch ukrytych warstw (32 i 16 neuronów) i jednej warstwy wyjściowej z jednym neuronem (do regresji). Model został przetrenowany przez 25 epok, a następnie oceniony na zbiorze testowym za pomocą miary błędu średniokwadratowego (MSE).

Przykład 3: Implementacja prostej sieci neuronowej w Pytorch Lightning#

import torch
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset, random_split
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from typing import Tuple
import pytorch_lightning as pl

# Wczytywanie i przygotowanie danych
data = load_iris()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.3, random_state=42)

# Normalizacja danych
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# Konwersja do tensora
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.long)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.long)

# Tworzenie DataLoaderów
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)

# Definiowanie modelu MLP za pomocą PyTorch Lightning
class IrisMLP(pl.LightningModule):
    def __init__(self, dims: Tuple[int, ...]):
        super().__init__()
        layers = []
        for dim_in, dim_out in zip(dims, dims[1:]):
            layer = torch.nn.Linear(dim_in, dim_out)
            layers.append(layer)
            layers.append(torch.nn.ReLU())
        self.network = torch.nn.Sequential(*layers[:-1])

    def forward(self, x):
        out = self.network(x)
        return out

    def training_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self.forward(x)
        loss = F.cross_entropy(y_hat, y)
        self.log('train_loss', loss)
        return loss

    def validation_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self.forward(x)
        val_loss = F.cross_entropy(y_hat, y)
        self.log('val_loss', val_loss)
        return val_loss

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=0.001)

# Trenowanie modelu za pomocą PyTorch Lightning
model = IrisMLP([X_train.shape[1], 32, 16, len(data.target_names)])
trainer = pl.Trainer(max_epochs=20)
trainer.fit(model, train_loader, test_loader)
GPU available: False, used: False
TPU available: False, using: 0 TPU cores
HPU available: False, using: 0 HPUs
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/logger_connector/logger_connector.py:75: Starting from v1.9.0, `tensorboardX` has been removed as a dependency of the `pytorch_lightning` package, due to potential conflicts with other packages in the ML ecosystem. For this reason, `logger=True` will use `CSVLogger` as the default logger, unless the `tensorboard` or `tensorboardX` packages are found. Please `pip install lightning[extra]` or one of them to enable TensorBoard support by default

  | Name    | Type       | Params | Mode 
-----------------------------------------------
0 | network | Sequential | 739    | train
-----------------------------------------------
739       Trainable params
0         Non-trainable params
739       Total params
0.003     Total estimated model params size (MB)
6         Modules in train mode
0         Modules in eval mode
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:424: The 'val_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=3` in the `DataLoader` to improve performance.
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/pytorch_lightning/trainer/connectors/data_connector.py:424: The 'train_dataloader' does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` to `num_workers=3` in the `DataLoader` to improve performance.
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/pytorch_lightning/loops/fit_loop.py:298: The number of training batches (7) is smaller than the logging interval Trainer(log_every_n_steps=50). Set a lower value for log_every_n_steps if you want to see logs for the training epoch.
`Trainer.fit` stopped: `max_epochs=20` reached.

W powyższym przykładzie:

  1. Model: Zdefiniowano model MLP będący modułem Pytorch Lighting obsługującym predefiniowane funkcje takie, jak forward, training_step, validation_step, configure_optimizers.

  2. Dane: Dane z zestawu Iris zostały przeskalowane, a następnie skonwertowane na tensory PyTorch.

  3. Trening: Model został przetrenowany przez 20 epok. PyTorch Lightning automatycznie zarządza procesem trenowania, w tym logowaniem strat/kosztów (train_loss, val_loss) i optymalizacją modelu.

MLXtend#

Mlxtend (Machine Learning Extensions) to biblioteka, która rozszerza możliwości Scikit-learn, oferując dodatkowe narzędzia, takie jak metody wyboru cech, łączenie klasyfikatorów, różnorodne algorytmy do klasyfikacji i regresji, a także funkcje do wizualizacji wyników. Mlxtend jest idealny dla osób, które chcą szybko przetestować różne techniki ML, które nie są wbudowane w Scikit-learn.

MLXtend pomaga też przy analizowaniu możliwości poszczególnych klas model, oraz ich własności. W tym podręczniku w rozdziale poświęconym obciążeniom indukcyjnym modeli ML, użyliśmy właśnie MLXtend.

Zastosowania MLXtend

Mlxtend jest używany do:

  1. Wyboru cech i redukcji wymiarów.

  2. Łączenia klasyfikatorów i regresorów.

  3. Wizualizacji wyników i analizy modeli.

Dodatkowe materiały#

Przydatne linki - MLXtend

Oficjalna dokumentacja MLXtend - dokumentacja jest też dodatkowo bardzo dobrym podręcznikiem.

Przykład 1: Łączenie modeli klasyfikacji#

from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from mlxtend.classifier import EnsembleVoteClassifier
from mlxtend.plotting import plot_decision_regions
import matplotlib.pyplot as plt

# Wczytywanie danych
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X[:, :2], y, test_size=0.3, random_state=42)

# Definiowanie modeli bazowych
clf1 = RandomForestClassifier(random_state=1)
clf2 = GradientBoostingClassifier(random_state=1)

# Łączenie modeli
eclf = EnsembleVoteClassifier(clfs=[clf1, clf2], voting='soft')

# Trenowanie i wizualizacja wyników

eclf.fit(X_train, y_train)
plot_decision_regions(X_train, y_train, clf=eclf),
plt.title('Łączenie modeli za pomocą Mlxtend')
plt.show()
../_images/03f80ae079e429622c575480aac3031473a03ac0ad3fd38e4da5f7b1c266f52c.png

W powyższym przykładzie wykorzystano Mlxtend do połączenia dwóch : lasu losowego i gradient boosting. Modele zostały połączone w celu stworzenia bardziej stabilnego i dokładnego modelu końcowego, a wynik został zwizualizowany za pomocą funkcji plot_decision_regions, ukazującą regiony działania/podejmowania decyzji przez modele.

Przykład 2: Wizualizacja macierzy pomyłek#

from mlxtend.plotting import plot_confusion_matrix
from sklearn.metrics import confusion_matrix

# Przewidywanie na zbiorze testowym
y_pred = eclf.predict(X_test)

# Tworzenie macierzy pomyłek
cm = confusion_matrix(y_test, y_pred)

# Wizualizacja macierzy pomyłek
fig, ax = plot_confusion_matrix(conf_mat=cm, figsize=(6, 6))
plt.title('Macierz pomyłek')
plt.show()
../_images/a4b532d9d241bc23391b8fa4ac85b2bc92ebd057bda15f9ad51da02bbc686946.png

Kod ten pokazuje, jak za pomocą Mlxtend można łatwo wizualizować macierz pomyłek. Macierz ta przedstawia liczbę poprawnych i błędnych klasyfikacji dokonanych przez model, co jest kluczowym narzędziem do oceny jego wydajności.

Optuna#

Optuna to biblioteka do automatycznej optymalizacji hiperparametrów, która pomaga w znalezieniu najlepszych ustawień parametrów modeli ML poprzez próbkowanie ich z różnych rozkładów. Optuna jest bardzo elastyczna i oferuje zaawansowane techniki optymalizacji, takie jak próbkowanie TPE (ang. Tree-structured Parzen Estimator) i optymalizacja oparta na modelu bayesowskim. Jest to szczególnie przydatne w automatyzacji procesu strojenia modeli, co jest jednym z bardziej czasochłonnych aspektów pracy nad ML.

Zastosowania Optuny

Optuna jest używana do:

  1. Automatycznego strojenia hiperparametrów modeli ML.

  2. Optymalizacji modeli ML w celu uzyskania najlepszych wyników.

Dodatkowe materiały#

Przydatne linki - Optuna

Oficjalny przewodnik Optuny

Przykład: Optymalizacja parmetrów klasyfikatora#

import optuna
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

optuna.logging.set_verbosity(optuna.logging.WARNING)

# Wczytywanie danych
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Funkcja celu
def objective(trial):
    C = trial.suggest_float('C', 1e-4, 1e2, log=True)
    gamma = trial.suggest_float('gamma', 1e-4, 1e1, log=True)
    model = SVC(C=C, gamma=gamma)
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    return accuracy_score(y_test, y_pred)

# Tworzenie study i optymalizacja
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)

print("Najlepsze parametry:", study.best_params)
print("Najlepsza dokładność:", study.best_value)
Najlepsze parametry: {'C': 24.956723007179015, 'gamma': 0.012497469517543196}
Najlepsza dokładność: 1.0

Biblioteki do pracy z szeregami czasowymi#

Analiza szeregów czasowych jest jedna z kluczowych dziedzin w Data Science, szczególnie w kontekście prognozowania danych finansowych, czy ekonomicznych. Python oferuje różnorodne narzędzia do pracy z szeregami czasowymi. W tej sekcji omówimy dwie biblioteki: statsmodels oraz statsforecast z Nixtlaverse.

Zwłaszcza ta ostatnia rodzina/grupa bibliotek zgrupowana pod wspólną nazwą jest bardzo interesująca - oferuje m. in. moduły prognozowania opartego o uczenie maszynowe (mlforecast), oraz sieci neuronowe (neuralforecast) w tym samym ekosystemie.

Statsmodels#

Statsmodels to wszechstronna biblioteka Pythona, która oferuje zaawansowane narzędzia do modelowania statystycznego. Biblioteka ta jest szczególnie użyteczna w analizie szeregów czasowych, oferując modele takie jak ARIMA, SARIMAX, modele stacjonarne oraz narzędzia do diagnostyki i oceny modeli.

Statsmodels - kłopoty z czytelnością

Wiele osób zauważa, że dokumentacja tego narzędzia pozostawia wiele do życzenia, podobnie jak czytelność i przejrzystość kodu. Jest to co prawda kwestia gustu, ale rzeczywiście, z punktu widzenia inżynierii oprogramowania, dobrych praktyk i przejrzystości kodu - statsmodels ma wiele do poprawy.

Ciężko jest znaleźć interesujące nas tematy w dokumentacji, a także zrozumieć, jakie są dostępne funkcjonalności. Warto zatem korzystać z dodatkowych materiałów, takich jak kursy online, czy przewodniki, które pomogą w zrozumieniu tej biblioteki. Niestety - nierzadko dokumentacja nijak się ma do tego, co aktualnie jest zaimplementowane w określonej wersji :/

Zastosowania Statsmodels

Statsmodels jest używany do:

  1. Modelowania szeregów czasowych, w tym ARIMA, SARIMAX.

  2. Diagnostyki modeli szeregów czasowych.

  3. Analizy statystycznej i modelowania danych.

Dodatkowe materiały#

Przydatne linki - Statsmodels

Oficjalny przewodnik po funkcjach Statsmodels

Przykład 1: Modelowanie ARIMA dla danych szeregów czasowych#

import pandas as pd
import numpy as np
import statsmodels.api as sm
from matplotlib import pyplot as plt

# Przykładowe dane: symulacja szeregu czasowego
np.random.seed(42)
data = np.cumsum(np.random.randn(100)) + 50
dates = pd.date_range('2023-01-01', periods=100, freq='D')
series = pd.Series(data, index=dates)

# Tworzenie i dopasowanie modelu ARIMA
model = sm.tsa.ARIMA(series, order=(1, 1, 1))
results = model.fit()

# Podsumowanie wyników modelu
print(results.summary())

# Prognozowanie przyszłych wartości
forecast = results.forecast(steps=10)
print("Prognoza na 10 dni:", forecast)

# Wizualizacja
series.plot(label='Dane historyczne')
plt.plot(forecast, label='Prognoza', linestyle='--')
plt.legend()
plt.show()
                               SARIMAX Results                                
==============================================================================
Dep. Variable:                      y   No. Observations:                  100
Model:                 ARIMA(1, 1, 1)   Log Likelihood                -131.425
Date:                Wed, 09 Oct 2024   AIC                            268.850
Time:                        14:36:46   BIC                            276.635
Sample:                    01-01-2023   HQIC                           272.000
                         - 04-10-2023                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
ar.L1          0.3996      5.649      0.071      0.944     -10.672      11.471
ma.L1         -0.4156      5.591     -0.074      0.941     -11.374      10.543
sigma2         0.8329      0.123      6.780      0.000       0.592       1.074
===================================================================================
Ljung-Box (L1) (Q):                   0.00   Jarque-Bera (JB):                 0.53
Prob(Q):                              0.95   Prob(JB):                         0.77
Heteroskedasticity (H):               0.92   Skew:                            -0.16
Prob(H) (two-sided):                  0.81   Kurtosis:                         2.82
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
Prognoza na 10 dni: 2023-04-11    39.618833
2023-04-12    39.620226
2023-04-13    39.620782
2023-04-14    39.621004
2023-04-15    39.621093
2023-04-16    39.621129
2023-04-17    39.621143
2023-04-18    39.621148
2023-04-19    39.621151
2023-04-20    39.621152
Freq: D, Name: predicted_mean, dtype: float64
../_images/797c8a10d77d9f691b676fb440938a51ea86abdb0a3bb155c968ac92747671e0.png

W tym przykładzie użyto Statsmodels do modelowania symulowanego szeregu czasowego za pomocą modelu ARIMA (ang. autoregressive integrated moving average). Model został dopasowany do danych, a następnie wykorzystany do prognozowania przyszłych wartości. Wyniki prognozy zostały zwizualizowane na wykresie, pokazując przewidywane wartości w stosunku do danych historycznych.

Przykład 2: Diagnostyka i wizualizacja reszt modelu ARIMA#

# Diagnostyka reszt modelu
residuals = results.resid

# Autokorelacja reszt
sm.graphics.tsa.plot_acf(residuals)
plt.show()

# Histogram reszt
residuals.plot(kind='hist', bins=30)
plt.title('Histogram reszt modelu')
plt.show()
../_images/9e62b5e1a944969e429446ecb8967046a3193fa29f67005b3f169045c3e71b36.png ../_images/9dca1b6c329219ba45c1384f637c3ca087617872a5958acbe6411a12f0ed4efb.png

Powyższy kod pokazuje, jak przeprowadzić diagnostykę reszt modelu ARIMA. Analiza autokorelacji reszt pomaga ocenić, czy model dobrze dopasowuje się do danych, podczas gdy histogram reszt pozwala sprawdzić ich rozkład, co jest istotne dla oceny jakości modelu.

Statsforecast (Nixtlaverse)#

Statsforecast to nowoczesna biblioteka zaprojektowana do szybkiego i skalowalnego prognozowania szeregów czasowych. Należy do ekosystemu Nixtlaverse, który skupia narzędzia optymalizujące prognozowanie za pomocą różnych technik - od klasycznej analizy szeregów czasowych po metody ML. Statsforecast oferuje implementacje popularnych modeli, takich jak ETS, ARIMA, i inne, zoptymalizowane pod kątem szybkości i wydajności. Dzięki wykorzystaniu wielowątkowości i innych technik optymalizacji, Statsforecast pozwala na szybkie prognozowanie setek tysięcy szeregów czasowych jednocześnie.

Co więcej - w porównaniu do Statsforecast - dokumentacja i przewodniki Nixlaverse są znakomicie i przejrzyście napisane!!! Razem z pokrewnymi bibliotekami mlforecast i neural tworzą spójny ekosystem.

Zastosowania Statsforecast

Statsforecast jest używany do:

  1. Prognozowania szeregów czasowych za pomocą modeli ARIMA, ETS, i innych.

  2. Szybkiego i skalowalnego prognozowania wielu szeregów czasowych.

Dodatkowe materiały#

Przydatne linki - Statsforecast

Oficjalny przewodnik po funkcjach Statsforecast

Przykład 1: Szybkie prognozowanie szeregu czasowego za pomocą metody Holta-Wintersa#

from statsforecast import StatsForecast
from statsforecast.models import HoltWinters
import pandas as pd

# Przykładowe dane: air passengers
df = pd.read_csv('https://datasets-nixtla.s3.amazonaws.com/air-passengers.csv', parse_dates=['ds'])

# Tworzenie modelu Holta-Wintersa i prognozowanie
sf = StatsForecast(models=[HoltWinters(season_length=12)], freq='D')

# Uczenie modelu na podstawie historycznych danych
sf.fit(df=df)

# Prognozowanie przyszłych wartości
forecast_df = sf.predict(h=10, level=[90])

# Wyświetlanie prognozy
print(forecast_df)

# Wizualizacja prognozy
sf.plot(df.tail(5), forecast_df, level=[90])
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/statsforecast/core.py:492: FutureWarning:

In a future version the predictions will have the id as a column. You can set the `NIXTLA_ID_AS_COL` environment variable to adopt the new behavior and to suppress this warning.

/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/statsforecast/core.py:1447: FutureWarning:

Passing the ids as the index is deprecated. Please provide them as a column instead.
                      ds  HoltWinters  HoltWinters-lo-90  HoltWinters-hi-90
unique_id                                                                  
AirPassengers 1960-12-02   453.088745         428.115234         478.062256
AirPassengers 1960-12-03   427.476532         402.366119         452.586975
AirPassengers 1960-12-04   460.631775         435.381927         485.881622
AirPassengers 1960-12-05   497.036804         471.645081         522.428528
AirPassengers 1960-12-06   507.507538         481.971436         533.043640
AirPassengers 1960-12-07   567.922485         542.239502         593.605408
AirPassengers 1960-12-08   650.842590         625.010376         676.674866
AirPassengers 1960-12-09   635.878601         609.894531         661.862610
AirPassengers 1960-12-10   537.471191         511.332947         563.609497
AirPassengers 1960-12-11   488.687286         462.392334         514.982239
../_images/66557325cf3952c7fc45f35cf6e6186c615bcfb742f890d79c47fcb202fb442d.png

W powyższym przykładzie użyto Statsforecast do prognozowania szeregów czasowych przy użyciu modelu Holta-Wintersa. Wyniki prognozy zostały wyświetlone i zwizualizowane, pokazując prognozowane wartości na kolejne 10 dni.

Przykład 2: Szkolenie wielu modeli jednocześnie#

from statsforecast import StatsForecast
from statsforecast.models import HoltWinters, AutoARIMA, AutoETS
import pandas as pd

# Przykładowe dane: air passengers
df = pd.read_csv('https://datasets-nixtla.s3.amazonaws.com/air-passengers.csv', parse_dates=['ds'])

# Szkolenie kilku modeli jednocześnie
sf = StatsForecast(models=[
    HoltWinters(season_length=12),
    AutoARIMA(season_length=12),
    AutoETS()], freq='D')

# Uczenie modelu na podstawie historycznych danych
sf.fit(df=df)

# Prognozowanie przyszłych wartości
forecast_df = sf.predict(h=12, level=[90])

# Wyświetlanie prognozy
print(forecast_df)

# Wizualizacja prognozy
sf.plot(df.tail(10), forecast_df, level=[90])
/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/statsforecast/core.py:492: FutureWarning:

In a future version the predictions will have the id as a column. You can set the `NIXTLA_ID_AS_COL` environment variable to adopt the new behavior and to suppress this warning.

/home/runner/.cache/pypoetry/virtualenvs/dsbook-K6kLFozp-py3.11/lib/python3.11/site-packages/statsforecast/core.py:1447: FutureWarning:

Passing the ids as the index is deprecated. Please provide them as a column instead.
                      ds  HoltWinters  HoltWinters-lo-90  HoltWinters-hi-90  \
unique_id                                                                     
AirPassengers 1960-12-02   453.088745         428.115234         478.062256   
AirPassengers 1960-12-03   427.476532         402.366119         452.586975   
AirPassengers 1960-12-04   460.631775         435.381927         485.881622   
AirPassengers 1960-12-05   497.036804         471.645081         522.428528   
AirPassengers 1960-12-06   507.507538         481.971436         533.043640   
AirPassengers 1960-12-07   567.922485         542.239502         593.605408   
AirPassengers 1960-12-08   650.842590         625.010376         676.674866   
AirPassengers 1960-12-09   635.878601         609.894531         661.862610   
AirPassengers 1960-12-10   537.471191         511.332947         563.609497   
AirPassengers 1960-12-11   488.687286         462.392334         514.982239   
AirPassengers 1960-12-12   420.511292         394.057220         446.965363   
AirPassengers 1960-12-13   463.407196         436.791565         490.022827   

                AutoARIMA  AutoARIMA-lo-90  AutoARIMA-hi-90     AutoETS  \
unique_id                                                                 
AirPassengers  444.300049       424.971436       463.628693  436.156677   
AirPassengers  418.210022       394.616974       441.803070  440.317139   
AirPassengers  446.237030       418.134003       474.340057  444.477600   
AirPassengers  488.228943       456.491608       519.966248  448.638092   
AirPassengers  499.231354       464.168854       534.293884  452.798553   
AirPassengers  562.230652       524.150452       600.310852  456.959015   
AirPassengers  649.230835       608.349976       690.111694  461.119476   
AirPassengers  633.230774       589.730652       676.730896  465.279938   
AirPassengers  535.230774       489.260010       581.201599  469.440399   
AirPassengers  488.230804       439.915619       536.545959  473.600891   
AirPassengers  417.230804       366.679810       467.781799  477.761353   
AirPassengers  459.230804       406.538788       511.922821  481.921814   

               AutoETS-lo-90  AutoETS-hi-90  
unique_id                                    
AirPassengers     359.774292     512.539062  
AirPassengers     331.475494     549.158813  
AirPassengers     310.160889     578.794312  
AirPassengers     292.366394     604.909790  
AirPassengers     276.760742     628.836365  
AirPassengers     262.666473     651.251587  
AirPassengers     249.683929     672.555054  
AirPassengers     237.554657     693.005249  
AirPassengers     226.100342     712.780457  
AirPassengers     215.192047     732.009705  
AirPassengers     204.733047     750.789612  
AirPassengers     194.648727     769.194885  
../_images/468bfbe4fd596469465176fe6b45f08701af10a6785e4a1cd6c3bd68f208dc7c.png