Obsah
1. Využití knihovny scikit-learn pro zpracování a analýzu přirozeného jazyka (NLP)
2. První kroky: stažení datové sady i seznamů stopslov
3. Načtení datové sady a zjištění základních informací o v ní uložených datech
4. Informace o sloupcích v datovém rámci
5. Statistické informace získané ze sloupců obsahujících numerická data
6. Zjištění počtu tweetů o jednotlivých dopravcích
7. Grafická vizualizace počtu tweetů o jednotlivých dopravcích
8. Zjištění celkové nálady tweetů
9. Vizualizace celkové nálady v koláčovém diagramu
10. Hodnocení rozdělené podle jednotlivých dopravců
11. Přečtení tweetů a jejich ohodnocení z datové sady
12. Předzpracování textových dat
13. Vektorizace textových dat založená na třídě CountVectorizer
14. Ukázka výsledků vektorizace a zpětného převodu na text
15. Vektorizace textových dat založená na třídě TfidfVectorizer
16. Ukázka výsledků vektorizace a zpětného převodu na text
17. Trénink a predikce modelu natrénovaného s využitím vektorizovaných dat
18. Výsledky získané natrénovaným modelem
19. Repositář s demonstračními příklady
1. Využití knihovny scikit-learn pro zpracování a analýzu přirozeného jazyka (NLP)
V seriálu o datové analýze s využitím programovacího jazyka Python jsme se již mnohokrát setkali s knihovnou scikit-learn. Prozatím jsme tuto knihovnu primárně používali pro zpracování (a popř. pro predikce) numerických dat. V praxi se dnes ovšem poměrně často setkáme i s nutností práce s texty psanými v nějakém „přirozeném“ jazyce, čímž jsou většinou myšleny neumělé jazyky, které se postupně vyvinuly pro potřebu komunikace mezi lidmi. A právě způsobem zpracování přirozeného jazyka (NLP – Natural Language Processing) se budeme zabývat dnes i v navazujících článcích. Dnes konkrétně si ukážeme, jakým způsobem můžeme natrénovat model knihovny scikit-learn tak, aby na základě zadaného tweetu o nějaké aerolince odhadl, jestli se jedná o pozitivní, negativní nebo neutrální hodnocení. Takovéto zpracování textů je velmi časté (ostatně každý e-shop, popř. další online služby, se ptají na zpětnou vazbu) a nazývá se sentiment analysis.
Mezi knihovny, které dnes použijeme, patří scikit-learn, Pandas, NumPy, Matplotlib a částečně též NLTK.
2. První kroky: stažení datové sady i seznamů stopslov
Nejprve musíme získat datovou sadu s tweety, které hodnotí jednotlivé aerolinky a kterou můžeme použít pro trénink modelu. Jedná se o známou datovou sadu, která je dostupná mj. i na Gitu. Stáhnout ji můžete interaktivně (přes browser), nástrojem wget nebo tímto skriptem napsaný v Pythonu:
# Stažení datové sady, kterou budeme používat v dalších demonstračních # příkladech. import requests url = "https://raw.githubusercontent.com/satyajeetkrjha/kaggle-Twitter-US-Airline-Sentiment-/refs/heads/master/Tweets.csv" response = requests.get(url) with open("Tweets_airlines.csv", "wb") as fout: fout.write(response.content)
Stažený soubor je v CSV formátu a jeho začátek vypadá následovně:
tweet_id,airline_sentiment,airline_sentiment_confidence,negativereason,negativereason_confidence,airline,airline_sentiment_gold,name,negativereason_gold,retweet_count,text,tweet_coord,tweet_created,tweet_location,user_timezone 570306133677760513,neutral,1.0,,,Virgin America,,cairdin,,0,@VirginAmerica What @dhepburn said.,,2015-02-24 11:35:52 -0800,,Eastern Time (US & Canada) 570301130888122368,positive,0.3486,,0.0,Virgin America,,jnardino,,0,@VirginAmerica plus you've added commercials to the experience... tacky.,,2015-02-24 11:15:59 -0800,,Pacific Time (US & Canada) 570301083672813571,neutral,0.6837,,,Virgin America,,yvonnalynn,,0,@VirginAmerica I didn't today... Must mean I need to take another trip!,,2015-02-24 11:15:48 -0800,Lets Play,Central Time (US & Canada) 570301031407624196,negative,1.0,Bad Flight,0.7033,Virgin America,,jnardino,,0,"@VirginAmerica it's really aggressive to blast obnoxious ""entertainment"" in your guests' faces & they have little recourse",,2015-02-24 11:15:36 -0800,,Pacific Time (US & Canada) 570300817074462722,negative,1.0,Can't Tell,1.0,Virgin America,,jnardino,,0,@VirginAmerica and it's a really big bad thing about it,,2015-02-24 11:14:45 -0800,,Pacific Time (US & Canada) 570300767074181121,negative,1.0,Can't Tell,0.6842,Virgin America,,jnardino,,0,"@VirginAmerica seriously would pay $30 a flight for seats that didn't have this playing.
Obrázek 1: Datová sada po otevření ve spreadsheetu.
Dále budeme v některých skriptech vyžadovat slovník s takzvanými stopslovy (stopwords). Jedná se o slova, která v textu nenesou žádnou skutečně užitečnou informaci a mohou být odfiltrována. Tento slovník stáhneme jednoduchým skriptem založeným na knihovně NLTK. Vše se stáhne do adresáře ntkl_data v domovském adresáři:
# Stažení slovníku, který bude použit pro předzpracování textu v dalších # demonstračních příkladech. import nltk # tento příkaz zajistí stažení příslušných datových souborů nltk.download("stopwords")
Příklad několika stopslov pro angličtinu (ale slovník obsahuje i další jazyky):
i me my myself we our ours ourselves you you're you've you'll you'd your yours yourself yourselves he ... ... ...
3. Načtení datové sady a zjištění základních informací o v ní uložených datech
Jak jsme viděli v předchozí kapitole, je datová sada, kterou máme k dispozici, uložena ve formátu CSV. Taková data je možné načíst různým způsobem, přičemž pravděpodobně nejjednodušší je použít knihovnu Pandas, popř. Polars. Celý CSV soubor tedy načteme do datového rámce (data frame) a necháme si zobrazit prvních pět a posledních pět záznamů. K tomu lze použít metody head a tail:
# Načtení datové sady a zjištění základních informací o v ní uložených datech. import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # zobrazení prvních deseti řádků tabulky print(airline_tweets.head()) print() # zobrazení posledních deseti řádků tabulky print(airline_tweets.tail())
Prvních pět záznamů (nezobrazí se všechny sloupce):
tweet_id ... user_timezone 0 570306133677760513 ... Eastern Time (US & Canada) 1 570301130888122368 ... Pacific Time (US & Canada) 2 570301083672813571 ... Central Time (US & Canada) 3 570301031407624196 ... Pacific Time (US & Canada) 4 570300817074462722 ... Pacific Time (US & Canada) [5 rows x 15 columns]
Posledních pět záznamů:
tweet_id ... user_timezone 14635 569587686496825344 ... NaN 14636 569587371693355008 ... NaN 14637 569587242672398336 ... NaN 14638 569587188687634433 ... Eastern Time (US & Canada) 14639 569587140490866689 ... NaN [5 rows x 15 columns]
4. Informace o sloupcích v datovém rámci
Při načítání datových sad uložených ve formátu CSV se knihovny Pandas i Polars snaží o detekci typů dat v jednotlivých sloupcích. Na základě zjištěného typu se následně nastaví typy celých sloupců. Tyto informace si můžeme velmi snadno vypsat, a to konkrétně zavoláním metody info. Ostatně ukažme si to prakticky:
# Načtení datové sady a zjištění základních informací o v ní uložených datech. import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # zobrazení základních informací o datovém rámci print(airline_tweets.info())
Z výpisu je patrné, že v datové sadě je 14640 záznamů a každý záznam má 15 sloupců. Mnoho sloupců obsahuje pouze neprázdné (nenullové) hodnoty, ovšem sloupce negativereason, tweet_coord atd. obsahují i prázdné hodnoty a sloupec airline_sentiment_gold je prakticky prázdný:
RangeIndex: 14640 entries, 0 to 14639 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 tweet_id 14640 non-null int64 1 airline_sentiment 14640 non-null object 2 airline_sentiment_confidence 14640 non-null float64 3 negativereason 9178 non-null object 4 negativereason_confidence 10522 non-null float64 5 airline 14640 non-null object 6 airline_sentiment_gold 40 non-null object 7 name 14640 non-null object 8 negativereason_gold 32 non-null object 9 retweet_count 14640 non-null int64 10 text 14640 non-null object 11 tweet_coord 1019 non-null object 12 tweet_created 14640 non-null object 13 tweet_location 9907 non-null object 14 user_timezone 9820 non-null object dtypes: float64(2), int64(2), object(11) memory usage: 1.7+ MB None
5. Statistické informace získané ze sloupců obsahujících numerická data
Taktéž je možné zjistit základní statistické informace o těch sloupcích (resp. přesněji řečeno o datech uložených v těchto sloupcích), které obsahují numerická data. Těchto sloupců v naší datové sadě existuje jen několik, což bylo ostatně patrné i z tabulky získané v rámci předchozí kapitoly – jedná se o sloupce s datovými typy int64 a float64 (většina sloupců obsahuje hodnoty typu object, což si můžeme přeložit jako „řetězec“):
Knihovna Pandas nám pro zjištění statistických informací nabízí užitečnou metodu describe, která se používá následovně:
# Načtení datové sady a zjištění základních informací o v ní uložených datech. import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # zobrazení základních statistických informací o datovém rámci print(airline_tweets.describe())
Výsledkem je nový datový rámec, kde každý sloupec obsahuje statistické informace o jednom sloupci z původního datového rámce:
tweet_id ... retweet_count count 1.464000e+04 ... 14640.000000 mean 5.692184e+17 ... 0.082650 std 7.791112e+14 ... 0.745778 min 5.675883e+17 ... 0.000000 25% 5.685592e+17 ... 0.000000 50% 5.694779e+17 ... 0.000000 75% 5.698905e+17 ... 0.000000 max 5.703106e+17 ... 44.000000 [8 rows x 4 columns]
6. Zjištění počtu tweetů o jednotlivých dopravcích
V čem nám naopak knihovna Pandas může pomoci, je automatické sjednocení shodných hodnot z nějakého sloupce, načež se můžeme dotázat, kolik shodných hodnot existuje. To je již informace, která se nám bude hodit, protože takto snadno zjistíme, kolik hodnocení (resp. tweetů) o jednotlivých dopravcích máme k dispozici. Celý postup je ve skutečnosti snadný, protože jen musíme vybrat vhodný sloupec (přes jeho selektor) a následně zavolat metodu value_counts. Ta vrátí nový datový rámec s požadovanými výsledky:
# Načtení datové sady a zjištění počtu tweetů o jednotlivých dopravcích import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # počet tweetů o jednotlivých dopravcích print(airline_tweets.airline.value_counts())
Nový datový rámec vypadá pro naši datovou sadu následovně (první řádek je nadpis indexu, poslední řádek obsahuje typ sloupce v novém datovém rámci):
airline United 3822 US Airways 2913 American 2759 Southwest 2420 Delta 2222 Virgin America 504 Name: count, dtype: int64
7. Grafická vizualizace počtu tweetů o jednotlivých dopravcích
Informace, které jsme získali metodou value_counts, je možné snadno vizualizovat. Pro tento účel zkombinujeme možnosti nabízené knihovnami Pandas a Matplotlib. Nejdříve si necháme zobrazit jednoduchý sloupcový diagram, kde výška sloupců (podle očekávání) odpovídá počtu tweetů o daném dopravci:
# Vykreslení sloupcového diagramu s počty tweetů o jednotlivých dopravcích import pandas as pd import matplotlib.pyplot as plt # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # specifikace plochy grafu fig = plt.figure(1, figsize=(6, 6), dpi=150) # vykreslení sloupcového diagramu s počty tweetů o jednotlivých dopravcích airline_tweets.airline.value_counts().plot( kind="bar", ) # zajištění místa pro popisky os fig.tight_layout() # uložení sloupcového diagramu do souboru plt.savefig("178.png") # zobrazení diagramu na obrazovce plt.show()
Výsledek by měl vypadat následovně:
Obrázek 2: Počty tweetů o jednotlivých dopravcích vyjádřené sloupcovým diagramem.
V případě, že nás nezajímají absolutní hodnoty, ale pouze porovnání počtu tweetů, je výhodnější použít koláčový (kruhový) diagram. Ten se vytvoří taktéž velmi snadno. Pouze jsem diagram otočil o 90° (lepší zarovnání jmen dopravců) a nastavil styl vykreslení okrajů jednotlivých kruhových výsečí, jinak jsou poměrně málo viditelné (ale tyto úpravy lze udělat později či vůbec):
# Vykreslení koláčového diagramu s počty tweetů o jednotlivých dopravcích import pandas as pd import matplotlib.pyplot as plt # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # specifikace plochy grafu fig = plt.figure(1, figsize=(6, 6), dpi=150) # vykreslení koláčového diagramu s počty tweetů o jednotlivých dopravcích airline_tweets.airline.value_counts().plot( kind="pie", autopct="%1.0f%%", startangle=90, wedgeprops={"edgecolor": "black", "linewidth": 2, "antialiased": True}, ) # uložení sloupcového diagramu do souboru plt.savefig("179.png") # zobrazení diagramu na obrazovce plt.show()
Nyní bude vizuální výsledek pochopitelně zcela odlišný od sloupcového diagramu:
Obrázek 3: Počty tweetů o jednotlivých dopravcích vyjádřené koláčovým (kruhovým) diagramem.
8. Zjištění celkové nálady tweetů
Námi používaná datová sada obsahuje i sloupec nazvaný sentiment. Tento sloupec je vyplněn příznakem, zda daný tweet obsahuje kladné, záporné nebo neutrální hodnocení. Právě díky existenci tohoto sloupce (ruční práce) budeme moci natrénovat náš model tak, aby tweety (resp. jejich hodnocení) ohodnocoval sám. Ovšem podívejme se nyní na obsah sloupce sentiment:
# Načtení datové sady a zjištění celkové nálady. import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # celková nálada: počty pozitivních, negativních a neutrálních reakcí print(airline_tweets.airline_sentiment.value_counts())
Podle očekávání je většina hodnocení spíše negativní, protože spokojený zákazník většinou necítí potřebu se ke službě nebo výrobku vyjadřovat (což je, zcela na okraj, jeden z největších problémů dnešního informačního prostoru):
airline_sentiment negative 9178 neutral 3099 positive 2363 Name: count, dtype: int64
9. Vizualizace celkové nálady v koláčovém diagramu
Nyní je již snadné zobrazit koláčový (kruhový) diagram s relativní mírou kladných, záporných a neutrálních hodnocení. Použijeme naprosto stejný postup jako u prvního koláčového diagramu, pouze vstupní datový rámec bude odlišný. Navíc explicitně nastavuji barvy jednotlivých výsečí, aby červená odpovídala negativnímu hodnocení a zelená hodnocení pozitivnímu:
# Načtení datové sady a vizualizace celkové nálady. import pandas as pd import matplotlib.pyplot as plt # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # plocha grafu fig = plt.figure(1, figsize=(6, 6), dpi=150) # vykreslení koláčového diagramu s hodnoceními airline_tweets.airline_sentiment.value_counts().plot( kind="pie", autopct="%1.0f%%", colors=["#ff8080", "yellow", "#80ff80"], wedgeprops={"edgecolor": "black", "linewidth": 2, "antialiased": True}, ) # uložení koláčového diagramu do souboru plt.savefig("181.png") # zobrazení koláčového diagramu na obrazovce plt.show()
Výsledný diagram by měl vypadat následovně:
Obrázek 4: Poměr kladných, záporných a neutrálních hodnocení.
10. Hodnocení rozdělené podle jednotlivých dopravců
Kromě celkové nálady (kladná/záporná hodnocení) nás může zajímat i hodnocení rozdělené podle jednotlivých dopravců. Na základě těchto hodnot by pak bylo možné rozhodnout, který dopravce je (stále z úzkého pohledu tweetů) nejpopulárnější a který naopak „nasbíral“ nejvíce negativních reakcí. Řešení by mohlo být založeno na velmi užitečné metodě groupby, kterou jsme si již popsali v seriálu o knihovně Pandas. Rozdělení na základě dvou sloupců s následným zjištěním počtu záznamů v jednotlivých skupinách může být realizováno následujícím způsobem:
# Výpočet hodnocení podle dopravce. import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # rozdělení hodnocení podle dopravce airline_sentiment = ( airline_tweets .groupby(["airline", "airline_sentiment"]) .airline_sentiment.count() ) # výsledný datový rámec (hierarchický) print(airline_sentiment) print() # pivot tabulka airline_sentiment = airline_sentiment.unstack() # výsledný datový rámec print(airline_sentiment)
Výsledná tabulka s počty sdružených hodnot i transformací zkonstruovaná pivot tabulka by měly vypadat následovně:
airline airline_sentiment American negative 1960 neutral 463 positive 336 Delta negative 955 neutral 723 positive 544 Southwest negative 1186 neutral 664 positive 570 US Airways negative 2263 neutral 381 positive 269 United negative 2633 neutral 697 positive 492 Virgin America negative 181 neutral 171 positive 152 Name: airline_sentiment, dtype: int64 airline_sentiment negative neutral positive airline American 1960 463 336 Delta 955 723 544 Southwest 1186 664 570 US Airways 2263 381 269 United 2633 697 492 Virgin America 181 171 152
Prakticky totožným způsobem je možné si nechat hodnocení vizualizovat. Pro jednoduchost zůstaneme u sloupcových diagramů, i když by pochopitelně bylo možné vytvořit graf, v němž bude umístěno několik koláčových diagramů:
# Vizualizace hodnocení podle dopravce. import pandas as pd import matplotlib.pyplot as plt # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # rozdělení hodnocení podle dopravce airline_sentiment = ( airline_tweets .groupby(["airline", "airline_sentiment"]) .airline_sentiment.count() .unstack() ) # zobrazení hodnocení podle dopravce airline_sentiment.plot( kind="bar", color=["#ff8080", "yellow", "#80ff80"], edgecolor="black", figsize=(6, 6), ).legend(loc='best') plt.tight_layout() # uložení koláčového diagramu do souboru plt.savefig("183.png") # zobrazení koláčového diagramu na obrazovce plt.show()
Výsledný graf:
Obrázek 5: Kladná, záporná a neutrální hodnocení rozdělená podle dopravce.
11. Přečtení tweetů a jejich ohodnocení z datové sady
V navazujících kapitolách budeme pracovat pouze se dvěma sloupci získanými z datové sady. Bude se jednat o sloupec pojmenovaný „text“ s vlastními tweety a taktéž o sloupec nazvaný „airline_sentiment“, který obsahuje pouze hodnoty „positive“, „negative“ a „neutral“. K přečtení hodnot z těchto sloupců použijeme standardní sadu nástrojů knihovny Pandas, zejména její podporu selektorů (vybrat sloupec lze mnoha různými způsoby):
# Načtení datové sady a získání vlastních tweetů a hodnocení import pandas as pd # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # hodnocení (pozitivní, negativní, neutrální) labels = airline_tweets["airline_sentiment"].values # vlastní text hodnocení features = airline_tweets["text"].values # hodnoty použité později pro trénink modelu print("Labels:") print(labels) print("Number of labels:", len(labels)) print() print("Features:") print(features) print("Number of features:", len(features))
Tento velmi jednoduchý skript po svém spuštění nejdříve zobrazí hodnocení (použijeme při tréninku modelu) i vlastní texty tweetů:
Labels: ['neutral' 'positive' 'neutral' ... 'neutral' 'negative' 'neutral'] Number of labels: 14640 Features: ['@VirginAmerica What @dhepburn said.' "@VirginAmerica plus you've added commercials to the experience... tacky." "@VirginAmerica I didn't today... Must mean I need to take another trip!" ... '@AmericanAir Please bring American Airlines to #BlackBerry10' "@AmericanAir you have my money, you change my flight, and don't answer your phones! Any other suggestions so I can make my commitment??" '@AmericanAir we have 8 ppl so we need 2 know how many seats are on the next flight. Plz put us on standby for 4 people on the next flight?'] Number of features: 14640
12. Předzpracování textových dat
V předchozích článcích o knihovně scikit-learn jsme používali datové sady, které byly očištěny od neúplných nebo zbytečných dat. V případě tweetů o dopravcích tomu však tak není, protože v tweetech mohou být neúplná slova, překlepy atd. Bude tedy vhodné ještě před tréninkem modelu data pročistit. V dalším skriptu je použit velmi přímočarý přístup, který sice není na sto procent účinný, ale odstraní poměrně velkou část „šumu“. Odstraňujeme v něm bílé znaky na začátku a konci vět, opakující se bílé znaky, slova tvořená jediným znakem atd.:
# Preprocesing textových dat import numpy as np import pandas as pd import re # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # hodnocení (pozitivní, negativní, neutrální) labels = airline_tweets["airline_sentiment"].values # vlastní text hodnocení features = airline_tweets["text"].values def process_feature(feature): """Preprocesing textových dat.""" # odstranění speciálních znaků a dalšího smetí processed_feature = re.sub(r"\W", " ", feature) # odstranění samostatných znaků (oddělených bílými znaky) processed_feature = re.sub(r"\s+[a-zA-Z]\s+", " ", processed_feature) # odstranění samostatných znaků na začátku vět processed_feature = re.sub(r"\^[a-zA-Z]\s+", " ", processed_feature) # náhrada více mezer (nebo jiných bílých znaků) za jedinou mezeru processed_feature = re.sub(r"\s+", " ", processed_feature, flags=re.I) # odstranění prefixů ^b processed_feature = re.sub(r"^b\s+", "", processed_feature) # konverze výsledku na malá písmena return processed_feature.lower() # preprocesing všech hodnocení processed_features = [process_feature(feature) for feature in features] # hodnoty použité později pro trénink modelu print("Labels:") print(labels) print("Number of labels:", len(labels)) print() print("Features:") print(processed_features) print("Number of features:", len(processed_features)) print() # porovnání výsledků preprocesingu for i in range(10): print(features[i], " | ", processed_features[i])
Tento skript nejdříve (opět) zobrazí základní informace o datové sadě:
Labels: ['neutral' 'positive' 'neutral' ... 'neutral' 'negative' 'neutral'] Number of labels: 14640 Features: Number of features: 14640
Posléze dojde k výpisu původních textů i textů po předzpracování. Tento výpis je ručně upraven do podoby tabulky:
Původní text | Text po předzpracování |
---|---|
@VirginAmerica What @dhepburn said. | virginamerica what dhepburn said |
@VirginAmerica plus you've added commercials to the experience… tacky. | virginamerica plus you ve added commercials to the experience tacky |
@VirginAmerica I didn't today… Must mean I need to take another trip! | virginamerica didn today must mean need to take another trip |
@VirginAmerica it's really aggressive to blast obnoxious „entertainment“ in your guests' faces & they have little recourse | virginamerica it really aggressive to blast obnoxious entertainment in your guests faces amp they have little recourse |
@VirginAmerica and it's a really big bad thing about it | virginamerica and it a really big bad thing about it |
@VirginAmerica seriously would pay $30 a flight for seats that didn't have this playing. it's really the only bad thing about flying VA | virginamerica seriously would pay 30 flight for seats that didn have this playing it really the only bad thing about flying va |
@VirginAmerica yes, nearly every time I fly VX this “ear worm” won’t go away :) | virginamerica yes nearly every time fly vx this ear worm won go away |
@VirginAmerica Really missed a prime opportunity for Men Without Hats parody, there. https://t.co/mWpG7grEZP | virginamerica really missed prime opportunity for men without hats parody there https co mwpg7grezp |
@virginamerica Well, I didn't…but NOW I DO! :-D | virginamerica well didn but now do d |
@VirginAmerica it was amazing, and arrived an hour early. You're too good to me. | virginamerica it was amazing and arrived an hour early you re too good to me |
13. Vektorizace textových dat založená na třídě CountVectorizer
Posledním krokem, který je nutné provést ještě před tréninkem modelu, je vektorizace textových dat do numerické podoby. Na Rootu už jsme se setkali s tokenizací, tedy náhradou celých slov nebo jejich částí za numerické tokeny získané z nějakého globálního slovníku. Ovšem my použijeme odlišný přístup, který je založen na tom, že si vytvoříme vlastní slovník, který obsahuje pouze slova použitá ve všech vstupních textech (a žádná jiná slova). Posléze vytvoříme matici s N řádky a M sloupci, kde N je počet textů (korpus) a M je počet slov ve slovníku. V každém řádku matice budou uloženy počty počty výskytů jednotlivých slov v jednom textu. Pokud například jeden řádek matice obsahuje hodnoty [1, 0, 0, 2, 0], znamená to, že příslušná vstupní věta (text) obsahovala jeden výskyt slova uloženého ve slovníku pod indexem 0 a dva výskyty slov, které jsou uloženy pod indexem 3. Další slova se ve větě nevyskytují. Typicky bývají takové matice řídké (mnoho prvků má nulovou hodnotu) a proto bývají uloženy speciálním způsobem.
Zkusme si takovou vektorizaci provést a následně dekódovat obsah spočtené matice:
# Vektorizace textových dat import numpy as np import pandas as pd import re import nltk from nltk.corpus import stopwords from sklearn.feature_extraction.text import CountVectorizer # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # hodnocení (pozitivní, negativní, neutrální) labels = airline_tweets["airline_sentiment"].values # vlastní text hodnocení features = airline_tweets["text"].values def process_feature(feature): """Preprocesing textových dat.""" # odstranění speciálních znaků a dalšího smetí processed_feature = re.sub(r"\W", " ", feature) # odstranění samostatných znaků (oddělených bílými znaky) processed_feature = re.sub(r"\s+[a-zA-Z]\s+", " ", processed_feature) # odstranění samostatných znaků na začátku vět processed_feature = re.sub(r"\^[a-zA-Z]\s+", " ", processed_feature) # náhrada více mezer (nebo jiných bílých znaků) za jedinou mezeru processed_feature = re.sub(r"\s+", " ", processed_feature, flags=re.I) # odstranění prefixů ^b processed_feature = re.sub(r"^b\s+", "", processed_feature) # konverze výsledku na malá písmena return processed_feature.lower() # preprocesing všech hodnocení processed_features = [process_feature(feature) for feature in features] # hodnoty použité později pro trénink modelu print("Labels:") print(labels) print("Number of labels:", len(labels)) print() print("Features:") #print(processed_features) print("Number of features:", len(processed_features)) print() # vektorizace textu vectorizer = CountVectorizer( max_features=2500, min_df=7, max_df=0.8, stop_words=stopwords.words("english") ) vectorized_features = vectorizer.fit_transform(processed_features).toarray() # slova pro dekódování vah feature_names = vectorizer.get_feature_names_out() print("Feature names count:", len(feature_names)) print("Feature names:") for feature_name in feature_names: print(feature_name) print() # vlastní výsledek vektorizace print("Sparse matrix of size", vectorized_features.shape, ":") print(vectorized_features) print() # ukázka způsobu zakódování print("Selected tweet:") print("Original: ", features[2]) print("Preprocessed: ", processed_features[2]) print("Vectorized: ", vectorized_features[2]) print() print("word# weight meaning") for i, f in enumerate(vectorized_features[2]): if f > 0: print(f"{i:4} {f:5} {feature_names[i]}")
14. Ukázka výsledků vektorizace a zpětného převodu na text
Zajímavé bude zjistit, jaké informace vlastně získáme po spuštění skriptu popsaného v předchozí kapitole.
Nejdříve se zobrazí nám již známé informace získané přímo ze vstupní datové sady:
Labels: ['neutral' 'positive' 'neutral' ... 'neutral' 'negative' 'neutral'] Number of labels: 14640 Features: Number of features: 14640 Feature names count: 2301 Feature names:
Dále se zobrazí slovník vybudovaný vektorizérem. Ten obsahuje 2301 slov, takže si ukážeme jen výběr z celého slovníku:
ah ahead ahold air airbus aircanada aircraft airfare airline airlines airplane airport airports airways alert alliance allow allowed allowing almost alone along
Nejdůležitější je vypočtená matice, která má 14640 řádků (= počet vstupních tweetů) a 2301 sloupců (= počet slov ve slovníku). Většina prvků je nulových, proto je tato matice uložena jako řídká matice:
Sparse matrix of size (14640, 2301) : [[0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] ... [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0]]
Skript dále zkusí zjistit, jakým způsobem je v matici uložen jeden tweet. Zobrazí původní tweet, tweed po svém předzpracování a taktéž příslušný řádek matice (vektor):
Selected tweet: Original: @VirginAmerica I didn't today... Must mean I need to take another trip! Preprocessed: virginamerica didn today must mean need to take another trip Vectorized: [0 0 0 ... 0 0 0]
Z vektoru příliš informací nevyčteme, protože většinou obsahuje samé nuly. Takže si programově vyfiltrujeme pouze ty prvky, které jsou nenulové a ze slovníku zjistíme původní slovo:
word# weight meaning 183 1 another 1300 1 mean 1379 1 must 1390 1 need 1995 1 take 2052 1 today 2087 1 trip 2176 1 virginamerica
Z původní věty nám tedy zbyl jen seznam slov – ztratili jsme jednopísmenná slova, ztratili jsme stopslova a hlavně jsme ztratili posloupnost slov! Nicméně vstup je dostatečně krátký pro další zpracování modelem.
15. Vektorizace textových dat založená na třídě TfidfVectorizer
S maticí obsahující počty slov vyskytujících se v jednotlivých textech korpusu se setkáme poměrně často. Neméně často má ovšem matice poněkud odlišný obsah, který je vypočten na základě metodiky Tf-idf neboli celým jménem term frequency–inverse document frequency. Jednotlivé hodnoty nyní neobsahují frekvenci daného slova v textu, ale relevanci nebo možná lépe řečeno důležitost takového slova. V textu se totiž vyskytují jak obecná slova, která text nijak necharakterizují (obecné názvy a vlastně i všechna stopslova – ty jsme však vyfiltrovali), tak i zcela charakteristická slova, jež je možné použít v případě klasifikace textů (charakteristickými slovy mohou být jména postav, měst, fiktivních planet atd. v knihách či filmech).
Touto tématikou se budeme podrobněji zabývat v navazujícím článku, který je celý věnován vektorizaci, ovšem již dnes si můžeme ukázat, jak tf-idf spočítat. Namísto třídy CountVectorizer použijeme třídu TfidfVectorizer:
# Vektorizace textových dat import numpy as np import pandas as pd import re import nltk from nltk.corpus import stopwords from sklearn.feature_extraction.text import TfidfVectorizer # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # hodnocení (pozitivní, negativní, neutrální) labels = airline_tweets["airline_sentiment"].values # vlastní text hodnocení features = airline_tweets["text"].values def process_feature(feature): """Preprocesing textových dat.""" # odstranění speciálních znaků a dalšího smetí processed_feature = re.sub(r"\W", " ", feature) # odstranění samostatných znaků (oddělených bílými znaky) processed_feature = re.sub(r"\s+[a-zA-Z]\s+", " ", processed_feature) # odstranění samostatných znaků na začátku vět processed_feature = re.sub(r"\^[a-zA-Z]\s+", " ", processed_feature) # náhrada více mezer (nebo jiných bílých znaků) za jedinou mezeru processed_feature = re.sub(r"\s+", " ", processed_feature, flags=re.I) # odstranění prefixů ^b processed_feature = re.sub(r"^b\s+", "", processed_feature) # konverze výsledku na malá písmena return processed_feature.lower() # preprocesing všech hodnocení processed_features = [process_feature(feature) for feature in features] # hodnoty použité později pro trénink modelu print("Labels:") print(labels) print("Number of labels:", len(labels)) print() print("Features:") print(processed_features) print("Number of features:", len(processed_features)) print() # vektorizace textu vectorizer = TfidfVectorizer( max_features=2500, min_df=7, max_df=0.8, stop_words=stopwords.words("english") ) vectorized_features = vectorizer.fit_transform(processed_features).toarray() # slova pro dekódování vah feature_names = vectorizer.get_feature_names_out() print("Feature names count:", len(feature_names)) print("Feature names:") for feature_name in feature_names: print(feature_name) print() # vlastní výsledek vektorizace print("Sparse matrix of size", vectorized_features.shape, ":") print(vectorized_features) print() # ukázka způsobu zakódování print("Selected tweet:") print("Original: ", features[2]) print("Preprocessed: ", processed_features[2]) print("Vectorized: ", vectorized_features[2]) print() print("word# weight meaning") for i, f in enumerate(vectorized_features[2]): if f > 0.0: print(f"{i:4} {f:5.3} {feature_names[i]}")
16. Ukázka výsledků vektorizace a zpětného převodu na text
Opět se podívejme na výsledky, které byly získány demonstračním příkladem z předchozí kapitoly. Začátek výstupu je shodný s předchozím příkladem – základní informace o vstupní datové sadě:
Labels: ['neutral' 'positive' 'neutral' ... 'neutral' 'negative' 'neutral'] Number of labels: 14640 Features: Number of features: 14640 Feature names count: 2301
Následuje výpis 2301 slov, které byly přidány do slovníku. Nyní pro zajímavost vypisuji slova začínající na „un“, které byly získány ze všech tweetů:
Feature names: unable unacceptable unavailable unbelievable uncomfortable understaffing understand understandable understood unfortunately unfriendly unfriendlyskies unhappy unhelpful united unitedairlines unitedfail unitedsucks unless unprofessional unreal unused
Matice vypočtená vektorizérem bude nyní odlišná, což je patrné už jen z toho, že obsahuje hodnoty s plovoucí řádovou čárkou a nikoli celá čísla:
Sparse matrix of size (14640, 2301) : [[0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] ... [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.]]
Skript na konci své činnosti opět provede zpětný převod vektoru na slova z původní věty. Povšimněte si, že nyní jednotlivá slova nemají v matici celočíselné hodnoty (počty výskytů ve větě), ale váhy představující jejich unikátnost. Vypsána jsou pouze slova s nenulovou váhou:
Selected tweet: Original: @VirginAmerica I didn't today... Must mean I need to take another trip! Preprocessed: virginamerica didn today must mean need to take another trip Vectorized: [0. 0. 0. ... 0. 0. 0.] word# weight meaning 183 0.332 another 1300 0.434 mean 1379 0.463 must 1390 0.286 need 1995 0.332 take 2052 0.302 today 2087 0.349 trip 2176 0.288 virginamerica
17. Trénink a predikce modelu natrénovaného s využitím vektorizovaných dat
V tomto okamžiku máme k dispozici matici obsahující relevanci jednotlivých slov a taktéž vektor obsahující hodnoty „positive“, „negative“ a „neutral“ (což nejsou numerické hodnoty, ale to nevadí, protože provádíme klasifikaci nikoli regresi). Můžeme se tedy pokusit natrénovat model, který by na základě neznámého tweetu měl zhodnotit, zda je hodnocení kladné, záporné nebo neutrální. Postup již velmi dobře známe.
Matici i známé hodnocení rozdělíme:
trainX, testX, trainY, testY = train_test_split( vectorized_features, labels, test_size=0.2, random_state=0 )
A provedeme trénink modelu následovaný jeho otestováním:
# konstrukce vybraného modelu s předáním hyperparametrů classifier = KNeighborsClassifier(n_neighbors=1) # trénink modelu classifier.fit(trainX, trainY) # predikce modelu pro testovací vstupy (ne pro trénovací data) predictions = classifier.predict(testX)
Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:
# Trénink a predikce modelu nad vektorizovanými daty import numpy as np import pandas as pd import re import nltk import matplotlib.pyplot as plt from nltk.corpus import stopwords from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import ConfusionMatrixDisplay from sklearn.metrics import classification_report, confusion_matrix, accuracy_score from sklearn.neighbors import KNeighborsClassifier # načtení tabulky do datového rámce s předzpracováním numerických dat airline_tweets = pd.read_csv("Tweets_airlines.csv") # hodnocení (pozitivní, negativní, neutrální) labels = airline_tweets["airline_sentiment"].values # vlastní text hodnocení features = airline_tweets["text"].values def process_feature(feature): """Preprocesing textových dat.""" # odstranění speciálních znaků a dalšího smetí processed_feature = re.sub(r"\W", " ", feature) # odstranění samostatných znaků (oddělených bílými znaky) processed_feature = re.sub(r"\s+[a-zA-Z]\s+", " ", processed_feature) # odstranění samostatných znaků na začátku vět processed_feature = re.sub(r"\^[a-zA-Z]\s+", " ", processed_feature) # náhrada více mezer (nebo jiných bílých znaků) za jedinou mezeru processed_feature = re.sub(r"\s+", " ", processed_feature, flags=re.I) # odstranění prefixů ^b processed_feature = re.sub(r"^b\s+", "", processed_feature) # konverze výsledku na malá písmena return processed_feature.lower() # preprocesing všech hodnocení processed_features = [process_feature(feature) for feature in features] # hodnoty použité později pro trénink modelu print("Labels:") print(labels) print("Number of labels:", len(labels)) print() print("Features:") print(processed_features) print("Number of features:", len(processed_features)) print() # vektorizace textu vectorizer = TfidfVectorizer( max_features=2500, min_df=7, max_df=0.8, stop_words=stopwords.words("english") ) vectorized_features = vectorizer.fit_transform(processed_features).toarray() # klasické rozdělení datové sady na trénovací a testovací část trainX, testX, trainY, testY = train_test_split( vectorized_features, labels, test_size=0.2, random_state=0 ) # konstrukce vybraného modelu s předáním hyperparametrů classifier = KNeighborsClassifier(n_neighbors=1) # trénink modelu classifier.fit(trainX, trainY) # predikce modelu pro testovací vstupy (ne pro trénovací data) predictions = classifier.predict(testX) # vyhodnocení kvality modelu print(classification_report(testY, predictions)) print(accuracy_score(testY, predictions)) # matice záměn - absolutní hodnoty disp = ConfusionMatrixDisplay.from_estimator( classifier, testX, testY, cmap=plt.cm.Blues, normalize=None, ) # zobrazení matice v textové podobě print(disp.confusion_matrix) # uložení výsledků ve formě rastrového obrázku plt.savefig("180_1.png") # vizualizace matice plt.show() # matice záměn - relativní hodnoty disp = ConfusionMatrixDisplay.from_estimator( classifier, testX, testY, cmap=plt.cm.Blues, normalize="true", ) # zobrazení matice v textové podobě print(disp.confusion_matrix) # uložení výsledků ve formě rastrového obrázku plt.savefig("180_2.png") # vizualizace matice plt.show()
18. Výsledky získané natrénovaným modelem
Model implementovaný v rámci dnešního posledního demonstračního příkladu je po svém natrénování otestován (testovací množina obsahuje 20% údajů z původní datové sady) a následně jsou zobrazeny údaje o úspěšnosti klasifikace. Nejsou příliš vysoké v porovnání s modely, které jsme si již ukázali u datové sady Iris:
precision recall f1-score support negative 0.83 0.65 0.73 1870 neutral 0.38 0.58 0.46 614 positive 0.45 0.53 0.48 444 accuracy 0.62 2928 macro avg 0.55 0.59 0.56 2928 weighted avg 0.68 0.62 0.63 2928
Tomu odpovídá i celková úspěšnost předpovědí modelu – cca 61%:
0.6154371584699454
Vypočteny jsou i matice záměn, a to v absolutním i relativním tvaru:
[[1210 457 203] [ 172 358 84] [ 82 128 234]]
[[0.64705882 0.24438503 0.10855615] [0.28013029 0.58306189 0.13680782] [0.18468468 0.28828829 0.52702703]]
Tyto matice jsou i vizualizovány:
Obrázek 6: Matice záměn s absolutními hodnotami.
Obrázek 7: Matice záměn s relativními hodnotami.
19. Repositář s demonstračními příklady
Všechny demonstrační příklady využívající knihovnu Scikit-learn lze nalézt v repositáři https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady i na (Jupyter) diáře s postupem výpočtů a analýz:
V repositáři nalezneme taktéž projektový soubor a Jupyter Notebook s vysvětlením, jak lze modely využít pro rozpoznávání obsahu rastrových obrázků:
# | Příklad | Stručný popis | Adresa příkladu |
---|---|---|---|
1 | pyproject.toml | projektový soubor (pro PDM) se všemi závislostmi | https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/pyproject.toml |
2 | pdm.lock | lock soubor s konkrétními verzemi všech přímých i tranzitivních závislostí | https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/pdm.lock |
3 | Rozpoznání_obrazu_scikit-learn.ipynb | Jupyter notebook s celým postupem | https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/Rozpoznání_obrazu_scikit-learn.ipynb |
4 | particle_life.py | emergence: příklad vzniku struktury | https://github.com/tisnik/most-popular-python-libs/blob/master/particles/particle_life.py |
20. Odkazy na Internetu
- Python for NLP: Sentiment Analysis with Scikit-Learn
https://stackabuse.com/python-for-nlp-sentiment-analysis-with-scikit-learn/ - Datová sada – hodnocení leteckých dopravců
https://raw.githubusercontent.com/satyajeetkrjha/kaggle-Twitter-US-Airline-Sentiment-/refs/heads/master/Tweets.csv - Twitter_US_Airline_Sentiment_Analysis
https://github.com/rustagijanvi/Twitter_US_Airline_Sentiment_Analysis/tree/main - Shluková analýza (clustering) a knihovna Scikit-learn
https://www.root.cz/clanky/shlukova-analyza-clustering-a-knihovna-scikit-learn/ - Shluková analýza (clustering) a knihovna Scikit-learn (2)
https://www.root.cz/clanky/shlukova-analyza-clustering-a-knihovna-scikit-learn-2/ - Shluková analýza (clustering) a knihovna Scikit-learn (z plochy do 3D prostoru)
https://www.root.cz/clanky/shlukova-analyza-clustering-a-knihovna-scikit-learn-z-plochy-do-3d-prostoru/ - Rozpoznávání obrázků knihovnou Scikit-learn: první kroky
https://www.root.cz/clanky/rozpoznavani-obrazku-knihovnou-scikit-learn-prvni-kroky/ - scikit-learn: Machine Learning in Python
https://scikit-learn.org/stable/index.html - Sklearn-pandas
https://github.com/scikit-learn-contrib/sklearn-pandas - sklearn-xarray
https://github.com/phausamann/sklearn-xarray/ - Clustering
https://scikit-learn.org/stable/modules/clustering.html - Cluster analysis (Wikipedia)
https://en.wikipedia.org/wiki/Cluster_analysis - Shluková analýza (Wikipedia)
https://cs.wikipedia.org/wiki/Shlukov%C3%A1_anal%C3%BDza - K-means
https://cs.wikipedia.org/wiki/K-means - k-means clustering
https://en.wikipedia.org/wiki/K-means_clustering - Spectral clustering
https://en.wikipedia.org/wiki/Spectral_clustering - Emergence
https://cs.wikipedia.org/wiki/Emergence - Particle Life: Vivid structures from rudimentary rules
https://particle-life.com/ - Hertzsprungův–Russellův diagram
https://cs.wikipedia.org/wiki/Hertzsprung%C5%AFv%E2%80%93Russell%C5%AFv_diagram - Using Machine Learning in an HR Diagram
https://cocalc.com/share/public_paths/08b6e03583cbdef3cdb9813a54ec68ff773c747f - Gaia H-R diagrams: Querying Gaia data for one million nearby stars
https://vlas.dev/post/gaia-dr2-hrd/ - The Hertzsprung–Russell diagram
https://scipython.com/book2/chapter-9-data-analysis-with-pandas/problems/p92/the-hertzsprung-russell-diagram/ - Animated Hertzsprung-Russell Diagram with 119,614 datapoints
https://github.com/zonination/h-r-diagram - Neuraxle Pipelines
https://github.com/Neuraxio/Neuraxle - scikit-learn: Getting Started
https://scikit-learn.org/stable/getting_started.html - Support Vector Machines
https://scikit-learn.org/stable/modules/svm.html - Use Deep Learning to Detect Programming Languages
http://searene.me/2017/11/26/use-neural-networks-to-detect-programming-languages/ - Natural-language processing
https://en.wikipedia.org/wiki/Natural-language_processing - THE MNIST DATABASE of handwritten digits
http://yann.lecun.com/exdb/mnist/ - MNIST database (Wikipedia)
https://en.wikipedia.org/wiki/MNIST_database - MNIST For ML Beginners
https://www.tensorflow.org/get_started/mnist/beginners - Stránka projektu Torch
http://torch.ch/ - Torch: Serialization
https://github.com/torch/torch7/blob/master/doc/serialization.md - Torch: modul image
https://github.com/torch/image/blob/master/README.md - Data pro neuronové sítě
http://archive.ics.uci.edu/ml/index.php - Torch na GitHubu (několik repositářů)
https://github.com/torch - Torch (machine learning), Wikipedia
https://en.wikipedia.org/wiki/Torch_%28machine_learning%29 - Torch Package Reference Manual
https://github.com/torch/torch7/blob/master/README.md - Torch Cheatsheet
https://github.com/torch/torch7/wiki/Cheatsheet - Neural network containres (Torch)
https://github.com/torch/nn/blob/master/doc/containers.md - Simple layers
https://github.com/torch/nn/blob/master/doc/simple.md#nn.Linear - Transfer Function Layers
https://github.com/torch/nn/blob/master/doc/transfer.md#nn.transfer.dok - Feedforward neural network
https://en.wikipedia.org/wiki/Feedforward_neural_network - Biologické algoritmy (4) – Neuronové sítě
https://www.root.cz/clanky/biologicke-algoritmy-4-neuronove-site/ - Biologické algoritmy (5) – Neuronové sítě
https://www.root.cz/clanky/biologicke-algoritmy-5-neuronove-site/ - Umělá neuronová síť (Wikipedia)
https://cs.wikipedia.org/wiki/Um%C4%9Bl%C3%A1_neuronov%C3%A1_s%C3%AD%C5%A5 - PyTorch
http://pytorch.org/ - JupyterLite na PyPi
https://pypi.org/project/jupyterlite/ - JupyterLite na GitHubu
https://github.com/jupyterlite/jupyterlite - Dokumentace k projektu JupyterLite
https://github.com/jupyterlite/jupyterlite - Matplotlib Home Page
http://matplotlib.org/ - Matplotlib (Wikipedia)
https://en.wikipedia.org/wiki/Matplotlib - Popis barvových map modulu matplotlib.cm
https://gist.github.com/endolith/2719900#id7 - Ukázky (palety) barvových map modulu matplotlib.cm
http://matplotlib.org/examples/color/colormaps_reference.html - Galerie grafů vytvořených v Matplotlibu
https://matplotlib.org/3.2.1/gallery/ - 3D rendering
https://en.wikipedia.org/wiki/3D_rendering - 3D computer graphics
https://en.wikipedia.org/wiki/3D_computer_graphics - Primary 3D view planes
https://matplotlib.org/stable/gallery/mplot3d/view_planes_3d.html - Getting started in scikit-learn with the famous iris dataset
https://www.youtube.com/watch?v=hd1W4CyPX58 - Training a machine learning model with scikit-learn
https://www.youtube.com/watch?v=RlQuVL6-qe8 - Iris (plant)
https://en.wikipedia.org/wiki/Iris_(plant) - Kosatec
https://cs.wikipedia.org/wiki/Kosatec - Iris setosa
https://en.wikipedia.org/wiki/Iris_setosa - Iris versicolor
https://en.wikipedia.org/wiki/Iris_versicolor - Iris virginica
https://en.wikipedia.org/wiki/Iris_virginica - Druh
https://cs.wikipedia.org/wiki/Druh - Iris subg. Limniris
https://en.wikipedia.org/wiki/Iris_subg._Limniris - Iris Dataset Classification with Python: A Tutorial
https://www.pycodemates.com/2022/05/iris-dataset-classification-with-python.html - Iris flower data set
https://en.wikipedia.org/wiki/Iris_flower_data_set - List of datasets for machine-learning research
https://en.wikipedia.org/wiki/List_of_datasets_for_machine-learning_research - Analýza hlavních komponent
https://cs.wikipedia.org/wiki/Anal%C3%BDza_hlavn%C3%ADch_komponent - Principal component analysis
https://en.wikipedia.org/wiki/Principal_component_analysis - Scikit-learn Crash Course – Machine Learning Library for Python
https://www.youtube.com/watch?v=0B5eIE_1vpU - calm-notebooks
https://github.com/koaning/calm-notebooks - Should you teach Python or R for data science?
https://www.dataschool.io/python-or-r-for-data-science/ - nbviewer: A simple way to share Jupyter Notebooks
https://nbviewer.org/ - AI vs Machine Learning (Youtube)
https://www.youtube.com/watch?v=4RixMPF4×is - Machine Learning | What Is Machine Learning? | Introduction To Machine Learning | 2024 | Simplilearn (Youtube)
https://www.youtube.com/watch?v=ukzFI9rgwfU - A Gentle Introduction to Machine Learning (Youtube)
https://www.youtube.com/watch?v=Gv9_4yMHFhI - Machine Learning vs Deep Learning
https://www.youtube.com/watch?v=q6kJ71tEYqM - Umělá inteligence (slajdy)
https://slideplayer.cz/slide/12119218/ - Úvod do umělé inteligence
https://slideplayer.cz/slide/2505525/ - Umělá inteligence I / Artificial Intelligence I
https://ktiml.mff.cuni.cz/~bartak/ui/ - Matplotlib vs. seaborn vs. Plotly vs. MATLAB vs. ggplot2 vs. pandas
https://ritza.co/articles/matplotlib-vs-seaborn-vs-plotly-vs-MATLAB-vs-ggplot2-vs-pandas/ - Matplotlib, Seaborn or Plotnine?
https://www.reddit.com/r/datascience/comments/jvrqxt/matplotlib_seaborn_or_plotnine/ - @Rabeez: Rabeez/plotting_comparison.ipynb
https://gist.github.com/Rabeez/ffc0b59d4a41e20fa8d944c44a96adbc - Matplotlib, Seaborn, Plotly and Plotnine Comparison
https://python.plainenglish.io/matplotlib-seaborn-plotly-and-plotnine-comparison-baf2db5a9c40 - Data Visualization 101: How to Choose a Python Plotting Library
https://towardsdatascience.com/data-visualization-101-how-to-choose-a-python-plotting-library-853460a08a8a - Data science in Python: pandas, seaborn, scikit-learn
https://www.youtube.com/watch?v=3ZWuPVWq7p4 - 7.2. Real world datasets
https://scikit-learn.org/stable/datasets/real_world.html#california-housing-dataset - 7.2.7. California Housing dataset
https://scikit-learn.org/stable/datasets/real_world.html#california-housing-dataset - Comprehensive Guide to Classification Models in Scikit-Learn
https://www.geeksforgeeks.org/comprehensive-guide-to-classification-models-in-scikit-learn/ - Tidy Data Visualization: ggplot2 vs seaborn
https://blog.tidy-intelligence.com/posts/ggplot2-vs-seaborn/ - seaborn: statistical data visualization
https://seaborn.pydata.org/ - Linear regression (Wikipedia)
https://en.wikipedia.org/wiki/Linear_regression - Lineární regrese (Wikipedia)
https://cs.wikipedia.org/wiki/Line%C3%A1rn%C3%AD_regrese - Iris Flower Classification with MLP Classifier
https://www.metriccoders.com/post/iris-flower-classification-with-mlp-classifier