Logo kursu
Na licencji Pixabay

L4: Manipulacje zbiorami danych

Efekty kształcenia laboratorium


  • poznasz podstawowe bibliteki służące do przetwarzania różnego rodzaju danych

  • poznasz jakie są podstawowymi narzędziami do przetwarzania tekstu

  • poznasz jak obliczyć prostą reprezentację wektorową tekstu

  • poznasz jak interpretować predykcje modelu

Podstawowe narzędzia do przetwarzania danych

Typ danych

Biblioteka

Repozytorium

Zbiory danych

Przetwarzanie wstępne

Modele/Algorytmy

Ewaluacja

Tekst

nltk

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

spacy

LINK

\(\times\)

\(\surd\)

\(\surd\)

\(\surd\)

transformers

LINK

\(\times\)

\(\times\)

\(\surd\)

\(\times\)

datasets

LINK

\(\surd\)

\(\times\)

\(\times\)

\(\times\)

torchtext

LINK

\(\surd\)

\(\surd\)

\(\times\)

\(\times\)

Obrazy

torchvision

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\times\)

opencv

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

turicreate

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

Grafy

networkx

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

graphtool

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

ogb

LINK

\(\surd\)

\(\surd\)

\(\times\)

\(\surd\)

torch-geometric

LINK

\(\surd\)

\(\surd\)

\(\surd\)

\(\surd\)

Przetwarzanie tekstów z wykorzystaniem biblioteki spacy

Do przetwarzania tekstów wykorzystamy bibliotekę spacy. Spacy to darmowa biblioteka o otwartych źródłach dedykowana do pracy z tekstem. Może być wykorzystana zarówno do budowy systemów ekstrakcji informacji, jak i systemów do rozumienia języka naturalnego. Ponadto zawiera zestaw narzędzi do wstępnego przetwarzania tekstów. Spacy udostępnia wsparcie dla ponad 60 języków.

Moduł spacy udostępnia kilka wcześniej przeuczonych potoków przetwarzania dla języka angielskiego. Ich szczegółowy opis znajduje się pod adresem LINK. Do dalszego przetwarzania wykorzystamy najmniejszy potok en_core_web_sm.

Instalacja i pobieranie modułów

W ramach podstawowych operacji przetwarzania, będziemy wykorzystywać część biblioteki odpowiadającej za inferencję. Pozostałe komponety wymagają doinstalowania dodatkowych modułów.

Szczegółowy opis znajduję się pod adresem LINK

Do inferencji potrzebujemy wyłącznie paczki spacy, którą możemy zainstalować wykorzystając menadżera paczek pip.

pip install spacy

Teraz zostaje nam pobranie naszego potoku przetwarzania dedykowanego dla języka angielskiego.

python -m spacy download en_core_web_sm

Potok en_core_web_sm zawiera takie komponenty jak:

  • tok2vec - Transformacja tokenów w wektory

  • tagger - Tagowanie części mowy

  • parser - Parser zależnościowy

  • senter - Rozpoznawanie zdań

  • ner - Rozpoznawanie nazw własnych

  • attribute_ruler - Przypisywanie atrybutów do tokenów

  • lemmatizer - Sprowadzanie tokenów do nazw własnych

Wczytanie potoków

Podstawowym obiektem jest Language. Language zawiera m.in. takie elementy jak: Słownik (vocab), elementy Potoku przetwrzania np. tagger, dane specyficzne dla danego języka takie jak np. stop-słowa (ang. stopwords) czy reguły interpunkcji (ang. punctuation). Obiekt Language jest również tworzony podczas ładowania wcześniej przeuczonych potoków przetwrzania za pomocą spacy.load. Często definiuje się go pod nazwą nlp.

Pobrany moduł wczytujemy z wykorzystaniem wspomnianej funkcji spacy.load. Domyślnie wczytuje on wszystkie zdefiniowane komponenty, ale możemy go ograniczyć. Do dalszego przetwarzania wystarczą nam komponenty tagger, tok2vec, parser, lemmatizer i attribute_ruler. Pozostałe możemy wykluczyć za pomocą argumentu exclude.

import spacy
nlp = spacy.load("en_core_web_sm", exclude=["ner", "senter"])

Następnym kluczowym element jest obiekt Doc, który jest sekwencją Tokenów. Token może oznaczać słowo, znak interpukcyjny itp. Obiekt Doc ten jest często tworzony za pomocą wywołania funkcji __call__ obiektu nlp, który od razu wykonuje zdefiniowany potok przetwarzania.

doc = nlp("Please stop, don't parse me")

Aby dostać się do wyników przetwarzania odwołujemy się do każdego tokenu z osobna. W naszym potoku mieliśmy zdefiniowane komponenty jak części mowy, parser zależnościowy i lemmatyzację. Aby się do nich dostać odwołamy się kolejne do atrybutów pos_, dep_ i lemma_.

token = doc[0]
print(token.text, token.pos_, token.dep_, token.lemma_)
Please INTJ intj please

Wyświetlmy teraz wyniki przetwarzania dla całego tekstu.

for token in doc:
    print(token.text, token.pos_, token.dep_, token.lemma_)
Please INTJ intj please
stop VERB parataxis stop
, PUNCT punct ,
do AUX aux do
n't PART neg n't
parse VERB ROOT parse
me PRON dobj I

Wektoryzacja tekstów za pomocą modelu TF-IDF

Rozszerzenie modelu BoW o model TF-IDF

TF-IDF jest to skrót od ang. Term Frequency – Inverse Document Frequency. Pierwszą składową modelu jest Term Frequency (TF), drugim komponentem jest Inverse Document Frequency (odwrotna częstość dokumentów). Możemy zastosować różne metody ważenia dla obu komponentów, jednak w ramach tego zadania wykorzystamy jedynie proste składowe bez dodatkowego ważenia. Składowa TF jest częstością występowania wyrazów, co tak naprawdę jest wyjściem z modelu BoW. Odwrotna częstość dokumentów (IDF) jest obliczana z poniższego wzoru:

\[ idf(w) = \log{\frac{\text{liczba dokumentow}}{\text{liczba dokumentow zawierajaca slowo w}}} \]

TF-IDF jest iloczynem tych dwóch komponentów co daje finalną postać:

\[ tfidf(w) = tf(w) * idf(w) \]

Reprezentacja słów dla dokumentów D1i D2 ` z wykorzystaniem modelu BoW po zastosowaniu dodatkowego ważenia TF-IDF została przedstawiona w poniższej tabeli.

D1: Problemem metody tfidf nie jest czas obliczeń.
D2: Problem w tfidf leży w wymiarowości.

w

TF(w)

IDF(w)

TFIDF(w)

Problemem

1

0.69

0.69

metody

1

0.69

0.69

tfidf

2

0

0

nie

1

0.69

0.69

jest

1

0.69

0.69

czas

1

0.69

0.69

obliczeń

1

0.69

0.69

Problem

1

0.69

0.69

w

2

0.69

1.38

leży

1

0.69

0.69

wymiarowości

1

0.69

0.69

Tabela. Reprezentacja słów po zastosowaniu ważenia TF-IDF

W rezultacie stosując otrzymaną reprezentację słów wektory cech dokumentów D1,D2, będą miały następującą postać:
D1 = [0.69, 0.69, 0,   0.69,  0.69, 0.69, 0.69, 0,    0,    0,    0   ]
D2 = [0,    0,    0,   0,     0,    0,    0,    0.69, 1.38, 0.69, 0.69]

Kolumny w wektorach cech oznaczają reprezentacje poszczególnych wyrazów zgodnie ze słownikiem utworzonym wcześniej czyli: 'Problemem', 'metody', 'tfidf', 'nie', 'jest', 'czas', 'obliczeń', 'Problem', 'w', 'leży' 'wymiarowości'

Important

Reprezentację słów tworzymy tylko podczas fazy uczenia wykorzystując zbiór uczący. Podczas fazy testowania wykorzystujemy reprezentację obliczoną wcześniej przekształcając dokumenty ze zbioru testowego. Jeżeli zdarzy się taka sytuacja, że słowo ze zbioru testowego nie znajduje się w słowniku to w takim przypadku jest ono pomijane.

Hint

Do obliczenia reprezentacji TF-IDF można wykorzystać moduł sklearn.feature_extraction.text.TfidfVectorizer

Important

Formuła liczenia TF-DF z pakietu sklearn.feature_extraction.text.TfidfVectorizer jest inna niż, w przedstawionym wzorze. Szczegóły znajdują się w dokumentacji LINK.

Interpretacja modeli z wykorzystaniem biblioteki SHAP

SHAP (SHapley Additive exPlanations) jest podejściem opartym na teorii gier, służącym do interpretacji wyników dowolnego modelu uczenia maszynowego. Szczegóły dotyczące implementacji można znaleźć w oryginalnym artykule autorów LINK. Istotność danej cechy jest wyrażone w postaci metryki Shap Value.

Shap wspiera modele oparte o drzewa decyzyjne, sieci neuronowe oraz obsługuje też inne metody nie wpadające w te dwie grupy kosztem czasu obliczeń.

Dostępne obecnie moduły:

  • Drzewa decyzyjne - TreeExplainer

  • Sieci Neuronowe - DeepExplainer, GradientExplainer

  • Inne modele LinearExplainer, KernelExplainer (wolniejsze od innych podejść)

W ramach tego laboratorium skupimy się na wyjaśnianiu podejść bazujących o drzewa decyzyjnie, a konkretnie o algorytm XGBoost.

Instalacja

Zacznijmy od instalacji potrzebnych bibliotek

pip install shap
pip install xgboost==1.3.3

Następnym krokiem jest wyuczenie modelu XGBooost, do analizy wykorzystamy wcześniej poznany zbiór Boston House Pricing. Wykorzystamy loader dostarczony przez bibliotekę shap. Przypomnijmy najpierw co oznaczają dane cechy w tym zbiorze danych.

Atrybut

Opis

CRIM

wskaźnik przestępczości na jednego mieszkańca w poszczególnych miastach

ZN

udział terenów mieszkaniowych przeznaczonych pod działki powyżej 25.000 mkw.

INDUS

udział powierzchni pod działalność niedetaliczną w poszczególnych miastach

CHAS

cecha przyjmuje wartość 1, jeżeli obszar przecina rzekę Charlser, 0 w innym wypadku

NOX

stężenie tlenków azotu (części na 10 milionów)

RM

średnia liczba pokoi w mieszkaniu

AGE

odsetek jednostek okupowanych przez właścicieli zbudowanych przed 1940 rokiem

DIS

odległości ważone do pięciu ośrodków zatrudnienia w Bostonie

RAD

wskaźnik dostępności do autostrad prowadzących do centrum miasta

TAX

pełnowartościowa stawka podatku od nieruchomości na 10000 USD

PTRATIO

stosunek liczby uczniów do liczby nauczycieli w poszczególnych miastach

B

1000 (Bk - 0,63) ^ 2 gdzie Bk to odsetek Afroamerykanów w mieście

LSTAT

% niższy status populacji

Cecha wyjściowa (MEDVAL)

mediana wartości domów zamieszkanych przez właścicieli w tys. dolarów

import xgboost
import shap

X, y = shap.datasets.boston()
model = xgboost.XGBRegressor().fit(X, y)

Przeliczmy teraz wpływ cech na predykcję

shap.initjs()  # Inicjalizacja obsługi wizualizacji opartych na JavaScript w notebooku
explainer = shap.TreeExplainer(model)  # Inicjalizacja modułu dla konkretnego modelu
shap_values = explainer(X)  # Obliczenie wartości shap

Shap umożliwia analizę wyników per daną próbką, lub w podsumowaniu. Zacznijmy od przedstawienia analiz per próbka danych. Do tego wykorzystamy wykresy waterfall i force. Wykres waterfall jest renderowany za pomocą matplotliba, dzięki czemu możemy w łatwy sposób modyfikować wykres.

shap.plots.waterfall(shap_values[0], max_display=15, show=False)
fig = plt.gcf()
ax = fig.axes
ax[0].set_title(
    'Wyniki wpływu wartości poszczególnych cech dla pierwszej predykcji'
)
plt.show()
../_images/lab4-manipulacja-datasetem_40_0.png

Wartości zaznaczono na czerwono oznaczają cechy które podnosiły wartość predykcji, a na niebiesko te, które obniżały. W lewej kolumnie umieszczono cechy i ich wartości. Liczbę wyświetlanych cech regulujemy parametrem max_display. \(f(x)\) oznacza wartość predykcji, a \(E[f(X)]\) umieszczona pod osią X oznacza uśrednione wyjście modelu dla zadanego zbioru, które w tym przypadku wynosi 22.533.

Innym sposobem na przedstawieniem wyników z pojedynczego przypadku jest wykres force. Tu w przeciwieństwie do wykresów waterfall jest renderowany za pomocą JavaScriptu, ale również możemy wykorzystać interfejs matplotlib przekazując odpowiednią wartość dla flagi matplotlib.

shap.plots.force(shap_values[0], matplotlib=False)
Visualization omitted, Javascript library not loaded!
Have you run `initjs()` in this notebook? If this notebook was from another user you must also trust this notebook (File -> Trust notebook). If you are viewing this notebook on github the Javascript has been stripped for security. If you are using JupyterLab this error is because a JupyterLab extension has not yet been written.
shap.plots.force(shap_values[0], matplotlib=True)
../_images/lab4-manipulacja-datasetem_44_0.png

W przypadku wykresu force podawana wcześniej wartość $E[f(X)]$ jest oznaczona jako base value. Wartości cech są tutaj oznaczone jako poszczególne etykiety słupków. W porównaniu do poprzedniego wykresu tutaj nie widzimy dokładnych wartości, tylko określa je długość danego słupka.

W przypadku tego wykresu możemy również otrzymać objaśnienia dla wielu zmiennych, a można tego dokonać obracając wykres o 90 stopni. Taka funkcjonalność jest już dostępna w bibliotece wystarczy przekazać listę jako parametr. W celu poprawnego wyświetlenia wykresu oprócz przekazania wartości shap musimy podać wartości cech jak i ich nazwy.

shap.plots.force(
    base_value=explainer.expected_value, 
    shap_values=shap_values.values, 
    features=X, 
    feature_names=X.columns
)
Visualization omitted, Javascript library not loaded!
Have you run `initjs()` in this notebook? If this notebook was from another user you must also trust this notebook (File -> Trust notebook). If you are viewing this notebook on github the Javascript has been stripped for security. If you are using JupyterLab this error is because a JupyterLab extension has not yet been written.

Wykres jest w pełni renderowany przez JavaScript i daje on nam wiele nowych możliwości. Możemy również zmienić kolejność wyświetlania próbek, jak i zbadać zależności pomiędzy różnymi zmiennymi.

Jednak nie musimy renderować całego wykresu force, żeby zbadać te zależności, ponieważ przy dużej liczbie próbek proces jego renderowania będzie długi jak i również może dojść do ograniczenia responsywności. Aby tego dokonać wykorzystamy funkcję scatter.

shap.plots.scatter(shap_values[:,"LSTAT"])
../_images/lab4-manipulacja-datasetem_49_0.png

Ostatnią grupę wykresów pełnią wykresy podsumowujące wszystkie predykcje do tego służą wykresy beeswarm i bar

shap.plots.beeswarm(shap_values)
../_images/lab4-manipulacja-datasetem_51_0.png

Na podstawie analizy możemy zinterpretować zależności badanych cech na wysokość predykcji, dzięki czemu możemy wyróżnić dwie grupy:

  • Cechy wpływające w dużym stopniu na obniżenie wartości predykcji:

    • Wysoka procentowa wartość populacji o niższym statusie LSTAT

    • Niska średnia liczba pokoi RM

    • Wysoki współczynnik przestępstw CRIM

    • Wysokie stężenie tlenką azotów NOX

  • Cechy wpływające w dużym stopniu na podwyższenie wartości predykcji:

    • Niska procentowa wartość populacji o niższym statusie LSTAT

    • Niski stawka podatkowa TAX

    • Wysoka średnia liczba pokoi RM

Ostatnim wykresem jest wykres bar, który pokazuję średnią wartość zbadanej istotności SHAP value dla danej cechy.

shap.plots.bar(shap_values)
../_images/lab4-manipulacja-datasetem_54_0.png