Zrozumieć głębokie uczenie - ebook
Zrozumieć głębokie uczenie - ebook
Książka Zrozumieć głębokie uczenie pokazuje, jak od zera budować sieci neuronowe głębokiego uczenia. Andrew Trask - doświadczony ekspert w tej dziedzinie, w swobodnym i przejrzystym stylu prezentuje leżącą w tle naukę, dzięki czemu możesz samodzielnie zrozumieć każdy szczegół nauczania sieci neuronowych. Jedynie przy użyciu Pythona i jego biblioteki matematycznej NumPy będziesz mógł uczyć swoje własne sieci neuronowe, aby samodzielnie zobaczyć i zrozumieć jak działa rozpoznawanie obrazów, tłumaczenie tekstów na różne języki, a nawet w jaki sposób pisać jak Szekspir! Po ukończeniu lektury będziesz gotów do poznawania platform głębokiego uczenia.
Zawartość książki:
Wiedza, na której opiera się głębokie uczenie Budowanie i uczenie swoich własnych sieci neuronowych Koncepcje ochrony prywatności, w tym uczenie sfederowane Wskazówki na temat dalszego studiowania głębokiego uczenia Książka przeznaczona jest dla czytelników dysponujących wiedzą matematyczną na poziomie szkoły średniej i średnimi umiejętnościami programistycznymi.
Kategoria: | Programowanie |
Zabezpieczenie: |
Watermark
|
ISBN: | 978-83-01-20782-3 |
Rozmiar pliku: | 17 MB |
FRAGMENT KSIĄŻKI
Zrozumieć głębokie uczenie jest owocem trzech lat wysiłków. Aby powstała książka, którą masz w ręku, napisałem co najmniej dwa razy więcej stron niż tu widoczne. Kilka rozdziałów zostało trzy lub cztery razy ponownie napisanych od nowa, zanim nadawały się do publikacji, po drodze zaś dodałem ważne rozdziały, które nie były częścią oryginalnego planu.
Co ważniejsze, wcześniej podjąłem dwie decyzje, które powinny zapewnić unikatową wartość książki: nie jest wymagane przygotowanie matematyczne wykraczające poza podstawową arytmetyką i nie opieram się na bibliotekach wysokiego poziomu, które mogłyby ukryć to, co naprawdę się dzieje. Innymi słowy, każdy może przeczytać tę książkę i zrozumieć, jak naprawdę działa głębokie uczenie. Aby to osiągnąć, musiałem wynaleźć nowe sposoby opisywania i wykładania podstawowych idei i technik bez zwracania się do zaawansowanej matematyki lub wyrafinowanego kodu napisanego przez kogoś innego.
Moim celem przy pisaniu Zrozumieć głębokie uczenie było ustawienie jak najniżej progu prowadzącego do praktykowania głębokiego uczenia. Nie będziesz po prostu poznawał teorii; będziesz odkrywać ją samodzielnie. Aby w tym pomóc, napisałem mnóstwo kodu i zrobiłem co w mojej mocy, aby wyjaśniać go we właściwej kolejności, tak by każdy fragment kodu potrzebnego dla działających demonstracji miał sens.
Ta wiedza, połączona z całą teorią, kodem i przykładami, które będziesz poznawać w tej książce, sprawi, że znacznie szybciej będziesz mógł przechodzić przez kolejne eksperymenty. Szybciej będziesz osiągać sukcesy i uzyskasz większe możliwości pracy, i w lepszym tempie poznasz nawet najbardziej zaawansowane koncepcje głębokiego uczenia.
W ciągu tych trzech lat nie tylko napisałem tę książkę, lecz także rozpocząłem studia doktoranckie na Oxfordzie, dołączyłem do zespołu Google’a i pomagałem szlifować narzędzia OpenMined, zdecentralizowanej platformy sztucznej inteligencji. Książka ta jest kulminacją trzech lat przemyśleń, uczenia się i wykładania.
Istnieje wiele innych źródeł, z których można uczyć się głębokiego uczenia. Cieszę się, że sięgnąłeś właśnie po to.O tej książce
Napisałem Zrozumieć głębokie uczenie, aby dać Czytelnikowi podstawy wiedzy pozwalającej na korzystanie z głównych platform głębokiego uczenia. Rozpoczniemy od podstaw sieci neuronowych, po czym przejdziemy do poznawania zaawansowanych warstw i architektur.
Kto powinien przeczytać tę książkę
Celowo napisałem ją w taki sposób, aby możliwie nisko ustawić próg wejściowy. Nie zakładam żadnej wiedzy na temat algebry liniowej, rachunku różniczkowego, optymalizacji funkcji wypukłych ani nawet samego uczenia się maszyn¹. Wszystkie należące do tych tematów zagadnienia, które są niezbędne do zrozumienia głębokiego uczenia, zostaną wyjaśnione w trakcie wykładu. Jeśli ktoś opanował matematykę na poziomie licealnym i nieco kodował w Pythonie, jest gotowy do lektury tej książki.
Plan pracy
Książka zawiera 16 rozdziałów:
- Rozdział 1 skupia się na tym, dlaczego warto poznać głębokie uczenie i co jest potrzebne, aby zacząć.
- Rozdział 2 zaczyna się od przeglądu fundamentalnych idei, takich jak uczenie się maszyn, modele parametryczne i nieparametryczne oraz nadzorowane i nienadzorowane uczenie się. Wprowadzimy w nim również paradygmat „przewidywanie, porównanie, nauka”, który będzie stale wykorzystywany w kolejnych rozdziałach.
- Rozdział 3 poprowadzi Czytelnika przez wykorzystanie prostych sieci do tworzenia przewidywań, a także zapewni pierwszy wgląd w sieć neuronową.
- W rozdziale 4 nauczymy się, jak oceniać przewidywania wykonywane w rozdziale 3 i identyfikować błędy, pomagające w uczeniu modeli w kolejnym kroku.
- Rozdział 5 koncentruje się na części nauki paradygmatu „przewidywanie, porównanie, nauka”. Rozdział ten pokazuje proces uczenia się na pogłębionym przykładzie.
- W rozdziale 6 zbudujemy naszą pierwszą „głęboką” sieć neuronową, jej kod i wszystko poza tym.
- Rozdział 7 przedstawia ogólny widok sieci neuronowej i jej działania w celu uproszczenia wyobrażeń.
- Rozdział 8 wprowadza pojęcia przeuczenia, wykluczania oraz miniwsadowej metody gradientowej i pokazuje, jak klasyfikować zbiór danych w nowej, dopiero co zbudowanej sieci.
- Rozdział 9 przedstawia funkcje aktywacji i ich stosowanie przy modelowaniu prawdopodobieństw.
- W rozdziale 10 wprowadzimy konwolucyjne sieci neuronowe, podkreślając użyteczność struktury do przeciwdziałania przeuczeniu.
- Rozdział 11 stanowi wprowadzenie do przetwarzania języka naturalnego (natural language processing – NLP) i przedstawia podstawowe pojęcia oraz koncepcje z dziedziny głębokiego uczenia.
- W rozdziale 12 omówimy rekurencyjne sieci neuronowe, nowoczesne podejście stosowane w niemal każdym obszarze modelowania sekwencji, będące jednym z najbardziej popularnych narzędzi używanych w branży.
- Rozdział 13 jest szybkim wprowadzeniem do budowania od zera platformy głębokiego uczenia się, aby stać się zaawansowanym użytkownikiem platform głębokiego uczenia.
- W rozdziale 14 wykorzystamy naszą rekurencyjną sieć neuronową, aby zmierzyć się z bardziej wymagającym zadaniem: modelowaniem języka.
- Rozdział 15 skupia się na zagadnieniu prywatności w danych, wprowadzając podstawowe koncepcje zachowania prywatności, takie jak sfederowane uczenie się, szyfrowanie homomorficzne i idee powiązane z prywatnością różnicową i bezpiecznym przetwarzaniem wielopodmiotowym.
- Rozdział 16 przedstawia narzędzia i zasoby, które są potrzebne na dalszej drodze nauki głębokiego uczenia.
Konwencje kodu i pobieranie
Wszystkie przykłady kodu w książce zostały złożone czcionką stałopozycyjną, taką jak tutaj, aby oddzielić je od zwykłego tekstu. Niektóre listingi są opatrzone komentarzami, wyróżniającymi ważne koncepcje.
Całość kodu przykładów z tej książki można pobrać z witryny internetowej wydawcy pod adresem www.manning.com/books/grokking-deep-learning albo z repozytorium GitHub: https://github.com/iamtrask/grokking-deep-learning.O autorze
Andrew Trask jest jednym z założycieli pracowni głębokiego uczenia w Digital Reasoning, w której są rozwijane podejścia głębokiego uczenia do zagadnień przetwarzania języka naturalnego, rozpoznawania obrazów i transkrypcji audio. W ciągu kilku miesięcy Andrew i jego partner badawczy przewyższyli najlepsze publikowane wyniki w dziedzinie klasyfikacji nastrojów i oznakowywania części mowy. Trenował największą w świecie sztuczną sieć neuronową o ponad 160 miliardach parametrów, czego wyniki zaprezentował wraz z współautorem na International Conference on Machine Learning. Wyniki te zostały opublikowane w Journal of Machine Learning. Obecnie jest menedżerem produktu w dziedzinie analiz tekstu i audio w Digital Reasoning, odpowiedzialnym za kierowanie planami analitycznymi w platformie przetwarzania poznawczego Synthesys, dla której głębokie uczenie jest podstawową umiejętnością.2. Podstawowe koncepcje: jak maszyny się uczą?
W tym rozdziale
- Czym jest głębokie uczenie, uczenie się maszyn i sztuczna inteligencja?
- Czym są modele parametryczne i nieparametryczne?
- Czym jest nadzorowane i nienadzorowane uczenie się maszyn?
- Jak maszyny mogą się uczyć?
Uczenie się maszyn doprowadzi do bardzo udanych debiutów giełdowych w ciągu pięciu lat.
Eric Schmidt, dyrektor wykonawczy Google,
przemówienie programowe na konferencji
Cloud Computing Platform, 2016Czym jest głębokie uczenie?
Głębokie uczenie jest podzbiorem metod uczenia się maszyn.
Głębokie uczenie (deep learning, DL) to podzbiór uczenia się maszyn (machine learning, ML), czyli dziedziny dedykowanej badaniom i rozwijaniem maszyn, które mogą się uczyć, niekiedy z ostatecznym celem zbudowania ogólnej sztucznej inteligencji (artificial intelligence, AI).
W zastosowaniach branżowych głębokie uczenie używane jest do rozwiązywania zadań praktycznych w najrozmaitszych obszarach, takich jak widzenie komputerowe (rozpoznawanie obrazów), przetwarzanie języka naturalnego (tekst) lub automatyczne rozpoznawanie mowy (audio). Mówiąc w skrócie, głębokie uczenie jest podzbiorem metod należących do zestawu narzędzi uczenia się maszyn, głównie wykorzystujących sztuczne sieci neuronowe, które stanowią klasę algorytmów luźno inspirowanych działaniem ludzkiego mózgu.
Na rysunku tym można zauważyć, że nie cały zakres głębokiego uczenia dotyczy prób zbudowania ogólnej sztucznej inteligencji (myślących maszyn, jak w filmach). Wiele zastosowań tej technologii służy do rozwiązywania szerokiego zakresu problemów występujących w branży informatycznej. W tej książce staram się skupić na wykładaniu podstaw głębokiego uczenia zarówno dla potrzeb nowatorskich badań, jak i bieżących potrzeb branżowych, przygotowując Czytelnika do obu zakresów.
Czym jest uczenie się maszyn?
Obszar badań, które mają dać komputerom zdolność do uczenia się bez jawnego zaprogramowania.
przypisywane Arthurowi Samuelowi
Skoro głębokie uczenie jest podzbiorem uczenia się maszyn, czym jest to ostatnie? Mówiąc najogólniej, dokładnie tym, co głosi nazwa. Uczenie się maszyn jest dziedziną informatyki, w której maszyny uczą się wykonywać zadania, które nie zostały jawnie zaprogramowane. Upraszczając, maszyny postrzegają pewien wzorzec i podejmują próbę naśladowania go w pewien sposób, bezpośrednio albo pośrednio.
Wymieniłem bezpośrednie i pośrednie naśladownictwo jako paralelę do dwóch głównych typów uczenia się maszyn: nadzorowanego i nienadzorowanego. Nadzorowane uczenie się maszyn jest bezpośrednim naśladowaniem wzorca występującego między dwoma zbiorami danych. Zawsze następuje próba wzięcia zbioru wejściowego i przekształcenia go na zbiór wyjściowy. Może to być niezwykle skuteczna i użyteczna umiejętność. Rozważmy następujące przykłady (wejściowe zbiory danych zostały wytłuszczone, wyjściowe zaś są wyróżnione kursywą):
- Użycie pikseli obrazu do wykrycia obecności lub nieobecności kota
- Użycie filmów, które lubimy, do przewidzenia innych filmów, które możemy lubić
- Użycie czyichś słów do rozpoznania, czy jest on szczęśliwy, czy smutny
- Użycie danych czujników pogodowych do przewidzenia prawdopodobieństwa deszczu
- Użycie czujników silnika samochodu do przewidzenia optymalnych ustawień tuningowania
- Użycie danych wiadomości prasowych do przewidzenia jutrzejszych cen akcji
- Użycie liczby na wejściu do przewidzenia liczby o dwukrotnej wartości
- Użycie surowego nagrania audio do przewidzenia treści transkryptu tego nagrania
Wszystko to są zadania nadzorowanego uczenia się maszyn. We wszystkich przypadkach algorytm uczenia się maszyny próbuje tak naśladować wzorzec występujący między dwoma zbiorami danych, aby użyć jednego zbioru do przewidzenia drugiego. Wyobraźmy sobie, że dla dowolnego z tych przykładów mamy moc pozwalającą przewidzieć wyjściowy zbiór danych, mając jedynie zbiór wejściowy. Taka możliwość byłaby czymś bardzo istotnym.
Nadzorowane uczenie się maszyn
Nadzorowane uczenie przekształca zbiory danych.
Nadzorowane uczenie jest metodą przekształcania jednego zbioru danych w inny. Jeśli na przykład mamy zbiór danych o nazwie poniedziałkowe ceny akcji, zawierający ceny każdej akcji dla każdego poniedziałku z minionych 10 lat, oraz drugi zbiór wtorkowych cen zarejestrowanych w tym samym okresie, algorytm uczenia nadzorowanego może próbować użyć jednego zbioru do przewidzenia wartości w drugim zbiorze.
Jeśli uda nam się wytrenować algorytm nadzorowanego uczenia się maszyny dla danych z poniedziałków i wtorków z 10 lat, może on następnie przewidzieć ceny akcji dla dowolnego wtorku w przyszłości na podstawie cen z bezpośrednio poprzedzającego poniedziałku. Proponuję, abyśmy zatrzymali się teraz i rozważyli to przez chwilę.
Nadzorowane uczenie się maszyn to chleb powszedni stosowanej sztucznej inteligencji (nazywanej też słabą albo wąską AI). Jest użyteczne, gdy chcemy wziąć to, co wiemy jako wejście i szybko przekształcić to w to, co chcemy wiedzieć. Pozwala to algorytmom nadzorowanego uczenia się maszyn rozszerzyć ludzką inteligencję i możliwości na pozornie nieograniczoną liczbę sposobów.
Większość pracy wykorzystującej uczenie się maszyny prowadzi do uczenia nadzorowanego klasyfikatora jakiegoś rodzaju. Nawet nienadzorowane uczenie się maszyn (o którym więcej powiemy za chwilę) zazwyczaj służy temu, aby wspomóc projektowanie dokładnego algorytmu nadzorowanego uczenia się maszyny.
W pozostałej części tej książki będziemy tworzyć algorytmy, przyjmujące jako wejście dane, które są obserwowalne, rejestrowalne, a tym samym możliwe do poznania, i transformujące je w wartościowe dane wyjściowe, które wymagają analizy logicznej. Na tym właśnie polega siła nadzorowanego uczenia się maszyn.
Nienadzorowane uczenie się maszyn
Uczenie nienadzorowane grupuje nasze dane.
Uczenie nienadzorowane ma pewną wspólną cechę z uczeniem nadzorowanym: przekształca jeden zbiór danych w drugi. Jednak zbiór, do którego przekształcamy dane wejściowe, nie jest wcześniej znany ani zrozumiany. W przeciwieństwie do uczenia nadzorowanego, nie ma tu „poprawnej odpowiedzi”, dla której próbujemy uzyskać model, który ją duplikuje. Nakazujemy algorytmowi nienadzorowanemu jedynie „znajdź wzorce w tych danych i opowiedz mi o nich”.
Przykładem uczenia nienadzorowanego jest analiza skupień (grupowanie) zbioru danych. Analiza skupień przekształca sekwencję obserwacji w sekwencję etykiet grup. Jeśli zostanie odkrytych 10 skupień, etykiety te typowo będą liczbami od 1 do 10. Każdej obserwacji zostanie przypisany numer odpowiadający grupie, w której się znalazł. Tym samym nasz zbiór danych zmienia się z mnóstwa obserwacji w zbiór etykiet. Dlaczego etykiety są liczbami? Algorytm nie jest w stanie powiedzieć nam, czym są te grupy. Skąd mógłby to wiedzieć? Mówi jedynie „Hej, człowieku! Znalazłem jakąś strukturę. Wygląda na to, że w twoich danych są jakieś skupienia. Oto one!”
I tu mam dobrą wiadomość! Ta idea grupowania jest czymś, co możemy utrwalić sobie w pamięci jako definicję uczenia nienadzorowanego. Choć istnieje bardzo wiele form uczenia nienadzorowanego, każdą postać uczenia nienadzorowanego można postrzegać jako formę pewnego grupowania. W dalszej części książki poznamy więcej związanych z tym pojęć.
Przyjrzyjmy się temu przykładowi. Choć algorytm nie wyjaśnia, czym są nazwane grupy, czy możemy ustalić, jak pogrupował słowa? (odpowiedź: 1 == miłe oraz 2 == smaczne). Później wyjaśnimy, że inne formy nienadzorowanego uczenia są również po prostu formą grupowania i dlaczego te grupy (skupienia) są przydatne dla uczenia nadzorowanego.
Uczenie parametryczne kontra nieparametryczne
W uproszczeniu: uczenie się metodą prób i błędów kontra zliczanie i prawdopodobieństwo.
Na poprzednich stronach podzieliłem wszystkie algorytmy uczenia się maszyn na dwie grupy: nadzorowane i nienadzorowane. Teraz omówimy inny sposób podziału tych samych algorytmów uczenia się na dwie grupy: parametryczne i nieparametryczne. Jeśli więc zastanowimy się nad naszą małą chmurą uczenia się maszyn, ma ona dwa ustawienia:
Jak widać, w rzeczywistości mamy do wyboru cztery różne typy algorytmów. Algorytm jest albo nadzorowany, albo nienadzorowany oraz albo parametryczny, albo nieparametryczny. Podczas gdy poprzedni punkt o nadzorowaniu dotyczył rodzaju nauczanego wzorca, parametryczność dotyczy sposobu przechowywania tej nauki i często, przez rozszerzenie, metody uczenia się. Na początek przyjrzyjmy się formalnym definicjom parametryczności i nieparametryczności. Gwoli ścisłości, nadal toczą się dyskusje na temat ścisłego rozróżnienia tych pojęć.
Model parametryczny charakteryzuje się posiadaniem stałej (ustalonej) liczby parametrów, podczas gdy w modelu nieparametrycznym liczba parametrów jest nieskończona (zdeterminowana przez dane).
Dla przykładu przyjmijmy, że problem polega na dopasowaniu kwadratowego kołka do odpowiedniego (kwadratowego) otworu. Niektórzy (małe dzieci) będą po prostu wciskać kołek we wszystkie otwory, aż będzie gdzieś pasował (parametryczne). Nastolatek jednak może policzyć liczbę boków (cztery) i wyszukać otwór z taką samą liczbą brzegów (nieparametryczne). Modele parametryczne mają skłonność do stosowania metody prób i błędów, podczas gdy modele nieparametryczne skłaniają się w stronę liczenia. Przyjrzyjmy się temu bliżej.
Nadzorowane uczenie parametryczne
W uproszczeniu: Uczenie metodą prób i błędów przy użyciu pokręteł.
Nadzorowane parametryczne maszyny uczące się to maszyny wyposażone w skończoną liczbę pokręteł (to część parametryczna), przy czym uczenie się następuje poprzez obracanie tych pokręteł (regulowanie). Dane wejściowe są wprowadzane, następnie przetwarzane na podstawie położenia gałek, po czym transformowane w prognozę.
Uczenie się uzyskujemy przez przestawianie gałek na różne położenia. Jeśli próbujemy przewidzieć prawdopodobieństwo tego, że Red Sox wygrają rozgrywki World Series, model ten najpierw przyjmie dane wejściowe (takie jak statystyki sportowe z liczbą zwycięstw i porażek lub średnią liczbą gotowości do rzutów (toes) dla gracza) i wyliczy prognozę (taką jak prawdopodobieństwo 98%). Następnie model sprawdzi, czy Red Sox rzeczywiście wygrali, czy nie. Wiedząc już, czy wygrali, algorytm uczenia się może skorygować położenie gałek, aby uzyskać dokładniejszą prognozę następnym razem, gdy zobaczy te same lub podobne dane wejściowe.
Być może „podkręci” gałkę „rejestr zwycięstw/porażek”, jeśli wskaźnik wygranych i porażek zespołu był dobrym prognostykiem. I odwrotnie, może „przykręcić w dół” gałkę dla „średniej liczby rzutów”, jeśli ta obserwacja nie była. Tak właśnie uczy się model parametryczny!
Zauważmy, że wszystko, czego model się nauczył, można przechwycić jako pozycje gałek w dowolnym wybranym momencie. Można myśleć o tego rodzaju modelu uczenia się jako o algorytmie wyszukiwania. Po prostu „szukamy” odpowiedniej konfiguracji gałek, próbując różnych ustawień, korygując je i próbując ponownie.
Możemy też zauważyć, że uwaga o próbach i błędach nie jest definicją formalną, ale jest powszechną (z wyjątkami) cechą modeli parametrycznych. Gdy mamy arbitralnie wybraną (ale ustaloną) liczbę gałek, którymi możemy kręcić, potrzebny jest jakiś poziom poszukiwań, aby znaleźć optymalną konfigurację. Różni się to zasadniczo od uczenia nieparametrycznego, które często bazuje na zliczaniu i (w większym lub mniejszym stopniu) dodaje nowe gałki, gdy znajdzie coś nowego, co można policzyć. Spróbujmy podzielić parametryczne nadzorowane uczenie na trzy fazy.
Krok 1: Przewidywanie
Aby zilustrować przebieg parametrycznego nadzorowanego uczenia, będziemy kontynuować analogię sportową i próbowali przewidzieć, czy Red Rox wygrają rozgrywki World Series. Pierwszym krokiem, jak wspomniałem, jest zebranie statystyk sportowych, przepuszczenie ich przez maszynę i wyliczenie prognozy prawdopodobieństwa, że Red Sox zwyciężą.
Krok 2: Porównanie ze wzorcem (prawdą)
Drugim krokiem jest porównanie prognozy (98%) ze wzorcem, o który nam chodzi (czyli czy Red Sox wygrali). Nieszczęśliwie przegrali, zatem porównanie to
Prognoza: 98% > Prawda: 0%
W tej fazie stwierdzamy, że gdyby model przewidział 0%, doskonale przewidziałby nadchodzącą przegraną drużyny. Chcemy, aby maszyna była dokładna, co prowadzi nas do kroku 3.
Krok 3: Uczenie się wzorca
W tym kroku korygujemy ustawienia gałek, analizując zarówno to, jak bardzo model się pomylił (98%), jak i to, czym były dane wejściowe (statystyki sportowe) w momencie prognozowania. Krok ten następnie zmienia ustawienia gałek, aby uzyskać bardziej dokładne przewidywanie dla posiadanych danych wejściowych.
W teorii gdy w tym kroku pojawią się te same statystyki, nowa prognoza powinna być niższa niż 98%. Zauważmy, że każda gałka reprezentuje wrażliwość prognozy na różnego typu dane wejściowe. To właśnie zmieniamy, gdy się „uczymy”.
Parametryczne uczenie nienadzorowane
Nienadzorowane uczenie parametryczne wykorzystuje bardzo podobne podejście. Przyjrzyjmy się poszczególnym fazom na wysokim poziomie. Pamiętajmy, że uczenie nienadzorowane sprowadza się do grupowania danych. Nienadzorowane uczenie parametryczne używa gałek do grupowania danych. Jednak w tym przypadku zazwyczaj mamy wiele gałek dla każdej grupy, z których każda odzwierciedla koligację danych wejściowych z tą konkretną grupą (z wyjątkami i niuansami – na razie zajmujemy się ogólnikowym opisem). Przyjrzyjmy się przykładowi, w którym zakładamy, że chcemy podzielić dane na trzy grupy.
W naszym zbiorze danych zidentyfikowałem trzy skupienia danych, które nasz model parametryczny powinien znaleźć. Zostały one oznaczone przez sformatowanie jako grupa 1, grupa 2 i grupa 3. Spróbujmy przepuścić pierwszą obserwację przez wytrenowany model nienadzorowany, pokazany poniżej. Zauważmy, że zostanie on najsilniej odwzorowany do grupy 1.
Dla każdej grupy maszyna próbuje przekształcić dane wejściowe na liczbę z przedziału od 0 do 1, informując o prawdopodobieństwie, z jakim konkretna obserwacja jest członkiem określonej grupy. Istniej wielka różnorodność technik uczenia takich modeli oraz ich wynikowych cech, ale na wysokim poziomie wykonują one dopasowywanie parametrów, aby przekształcić dane wejściowe do odpowiednich grup.
Uczenie nieparametryczne
W uproszczeniu: Metody oparte na zliczaniu.
Uczenie nieparametryczne jest klasą algorytmów, w której liczba parametrów jest oparta na samych danych (a nie wstępnie zdefiniowana). Prowadzi to do metod, które w ogólności zliczają coś w ten czy inny sposób, tym samym zwiększając liczbę parametrów na podstawie liczby elementów w danych. W wariancie nadzorowanym model nieparametryczny mógłby na przykład liczyć przypadki, gdy określony kolor świateł na skrzyżowaniu powoduje, że samochody „jadą”. Po zliczeniu tylko kilku przykładów model ten będzie mógł przewidzieć, że środkowe światło zawsze (100%) powoduje, że samochody jadą, podczas gdy prawe światła powodują to tylko czasami (50%).
Zauważmy, że ten model będzie używać trzech parametrów: trzy liczniki wskazujące liczbę zapaleń każdego koloru, gdy samochody ruszyły (być może podzieloną przez całkowitą liczbę obserwacji). Gdyby świateł byłoby pięć, potrzebowalibyśmy pięciu liczników (pięciu parametrów). Tym, co sprawia, że ten prosty model jest nieparametryczny, jest jego cecha uzależniająca liczbę parametrów od samych danych (w tym przypadku liczby kolorów świateł). Różni się to zasadniczo od modeli parametrycznych, które zaczynają od ustalonej liczby parametrów, a co ważniejsze, mogą mieć więcej lub mniej parametrów wyłącznie na podstawie decyzji naukowca trenującego model (niezależnie od danych).
Uważne przyjrzenie się może podawać tę koncepcję w wątpliwość. Model parametryczny pokazany wcześniej wydawał się mieć „gałkę” dla każdej obserwacji. Większość modeli parametrycznych nadal musi mieć jakiś rodzaj wejścia zależnego od liczby klas w danych. Możemy zatem zauważyć, że między algorytmami parametrycznymi i nieparametrycznymi jest jakiś nieokreślony obszar. Nawet algorytmy parametryczne w jakiś sposób ulegają wpływowi liczby klas w danych, nawet jeśli nie są to jawnie wzorce zliczające.
Podkreśla to też fakt, że parametr jest terminem ogólnym, odnoszącym się tylko do zbioru liczb używanego w celu modelowania wzorca (bez żadnych ograniczeń pod względem sposobu użycia tych liczb). Liczniki są parametrami. Wagi są parametrami. Znormalizowane warianty liczników lub wag są parametrami. Współczynniki korelacji mogą być parametrami. Termin odnosi się do zbioru liczb używanych do modelowania wzorca. Jak się okazuje, głębokie uczenie jest klasą modeli parametrycznych. W dalszej części książki nie będziemy więcej omawiać modeli nieparametrycznych, ale jest to bardzo interesująca i skuteczna klasa algorytmów.
Podsumowanie
W tym rozdziale zeszliśmy jeden poziom głębiej w rozmaite odmiany uczenia się maszyn. Dowiedzieliśmy się, że algorytm uczenia się może być albo nadzorowany, albo nienadzorowany, a także parametryczny albo nieparametryczny. Co więcej, pokazaliśmy, co odróżnia od siebie te cztery grupy algorytmów. Nauczyliśmy się, że nadzorowane uczenie się maszyn jest klasą algorytmów, w których uczymy się przewidywać zawartość pewnego zbioru danych na podstawie innego, uczenie zaś nienadzorowane w ogólności polega na grupowaniu pojedynczego zbioru danych na różnego rodzaju skupienia (grupy). Dowiedzieliśmy się też, że algorytmy parametryczne mają ustaloną liczbę parametrów, algorytmy zaś nieparametryczne dopasowują liczbę używanych parametrów w zależności od zawartości zbioru danych.
Głębokie uczenie wykorzystuje sieci neuronowe do realizowania zarówno nadzorowanych, jak i nienadzorowanych przewidywań. Jak dotąd, pozostawaliśmy na poziomie koncepcyjnym, aby Czytelnik uzyskał orientację w całości rozważanej dziedziny i swojego miejsca w niej. W kolejnym rozdziale zbudujemy naszą pierwszą sieć neuronową i wszystkie kolejne rozdziały będą opierać się na projektach. Zatem wyciągamy swoje notatniki Jupyter i do przodu!3. Wprowadzenie do prognozowania neuronowego: propagacja w przód
W tym rozdziale
- Prosta sieć wykonująca prognozę
- Czym jest sieć neuronowa i co robi?
- Wykonywanie prognozy z wieloma danymi wejściowymi
- Wykonywanie prognozy z wieloma danymi wyjściowymi
- Wykonywanie prognozy z wieloma danymi wejściowymi i wyjściowymi
- Prognozowanie na podstawie prognoz
Staram się nie angażować w przewidywanie przyszłości. To szybki sposób, aby wyjść na idiotę.
Warren Ellis, autor komiksów,
powieściopisarz i scenarzystaKrok 1: Prognoza
Ten rozdział dotyczy prognozowania.
W poprzednim rozdziale poznaliśmy paradygmat przewidywanie, porównywanie, nauka. W tym rozdziale zagłębimy się w jego pierwszy krok: przewidywanie. Przypomnijmy, że krok ten wygląda następująco:
W tym rozdziale dowiemy się więcej o tym, jak te trzy części prognozowania wykonywanego przez sieć neuronową wyglądają od środka. Zaczniemy od pierwszej: danych. W naszej pierwszej sieci neuronowej będziemy chcieli przewidzieć po jednym punkcie danych na raz, jak poniżej:
Później zobaczymy, że liczba obserwacji przetwarzanych jednocześnie ma znaczący wpływ na to, jak wygląda i zachowuje się nasza sieć. Ktoś mógłby postawić pytanie: „Jak mam wybrać, ile obserwacji ma być propagowanych jednocześnie?”. Odpowiedź zależy od tego, czy (w naszej opinii) sieć neuronowa może być dokładna, mając te dane, które jej dostarczamy.
Dla przykładu jeśli próbujemy rozpoznać (przewidzieć), czy na zdjęciu jest widoczny kot, zdecydowanie muszę pokazać mojej sieci wszystkie piksele obrazu jednocześnie. Dlaczego? No cóż, gdybym przesyłał po jednym pikselu na raz, czy ktokolwiek byłby w stanie stwierdzić, czy na obrazku jest kot? Ja też bym nie umiał! (Nawiasem mówiąc, można to zapamiętać jako ogólną regułę: zawsze należy przekazać sieci wystarczające informacje, przy czym „wystarczające” można z grubsza zdefiniować jako informacje potrzebne, by człowiek był w stanie wykonać takie samo przewidywanie).
Na razie pominiemy samą sieć. Jak się okazuje, będziemy mogli utworzyć sieć dopiero wtedy, gdy zrozumiemy kształt wejściowych i wyjściowych zbiorów danych (na razie kształt będziemy rozumieć jak „liczbę kolumn” albo „liczbę obserwacji przetwarzanych jednocześnie”). Skupimy się na pojedynczej prognozie prawdopodobieństwa tego, że konkretna drużyna baseballowa zwycięży:
Teraz, gdy wiemy już, że zamierzamy przyjąć tylko jedną obserwację i uzyskać na wyjściu jedną prognozę, możemy utworzyć sieć neuronową. Ponieważ mamy tylko jedną daną wejściową i jedną wyjściową, będziemy budować sieć z jedną gałką, przekształcającą wartość wejściową na wyjściową. (Na poziomie abstrakcji te „gałki” są w rzeczywistości nazywane wagami i tak się będę do nich odnosił, poczynając od tego miejsca). Tak więc, bez dalszych ceregieli, oto nasza pierwsza sieć neuronowa z pojedynczą wagą mapującą daną wejściową „liczba rzutów” (# toes) na daną wyjściową „zwyciężył?” (win):
Jak można zauważyć, przy jednej wadze sieć ta przyjmuje po jednym punkcie danych na raz (średnią liczbę rzutów na gracza w drużynie baseballowej) i zwraca pojedynczą prognozę (to, czy jej zdaniem drużyna wygra).
Wykonywanie prognozy przez prostą sieć neuronową
Zacznijmy od najprostszej możliwej sieci neuronowej.
Czym jest sieć neuronowa?
Oto nasza pierwsza sieć neuronowa.
Aby uruchomić sieć neuronową, otwieramy notatnik Jupyter i uruchamiamy poniższy kod:
Następnie uruchamiamy poniższy fragment:
Właśnie zbudowaliśmy naszą pierwszą sieć neuronową i użyliśmy jej do przewidywań! Gratulacje! Ostatni wiersz powoduje wypisanie prognozy (pred). Powinna ona wynosić 0.85. Zatem czym jest sieć neuronowa? Na razie jest to jedna lub więcej wag, przez które możemy mnożyć dane wejściowe, aby uzyskać prognozę.
Czym są dane wejściowe?
Jest to liczba, którą zarejestrowaliśmy gdzieś w rzeczywistym świecie. Zazwyczaj jest to coś łatwo dostępnego, jak dzisiejsza temperatura, średnia uderzeń baseballisty (BA) czy wczorajsza cena jakiejś akcji.
Czym jest prognoza?
Prognoza jest tym, co sieć neuronowa może nam powiedzieć na podstawie otrzymanych danych wejściowych, na przykład „dla podanej temperatury istnieje 0% prawdopodobieństwa, że ludzie będą dziś nosić dresy” albo „dla podanego BA danego gracza istnieje 30% prawdopodobieństwa, że uderzenie doprowadzi do home run”, albo „na podstawie wczorajszej ceny akcji dzisiejsza cena będzie wynosić 101,52”.
Czy te prognozy są zawsze poprawne?
Nie. Sieci neuronowe będą popełniać błędy, ale mogą się na nich uczyć. Dla przykładu jeśli uzyskana prognoza będzie zbyt wysoka, sieć skoryguje swoją wagę, aby następnym razem podać niższą prognozę i vice versa.
Jak sieć się uczy?
Metodą prób i błędów! Najpierw próbuje dokonać jakiejś prognozy. Następnie może stwierdzić, czy prognoza ta była zbyt wysoka, czy zbyt niska. Następnie zmienia wagę (w górę lub w dół), aby przewidywać bardziej dokładnie następnym razem, gdy otrzyma te same dane wejściowe.
Co robi ta sieć neuronowa?
Mnoży dane wejściowe przez wagę. „Skaluje” dane wejściowe o określony czynnik.
W poprzednim podpunkcie wykonaliśmy pierwszą prognozę przy użyciu sieci neuronowej. W swej najprostszej postaci sieć neuronowa wykorzystuje siłę mnożenia. Przyjmuje wejściową obserwację (w tym przypadku liczbę 8,5) i mnoży ją przez wagę. Gdyby waga była równa 2, wówczas sieć neuronowa podwoiłaby dane wejściowe. Jeśli waga to 0,01, sieć podzieli dane wejściowe przez 100. Jak można zauważyć, niektóre wartości wagi powiększają, a inne zmniejszają dane wejściowe.
Interfejs sieci neuronowej jest prosty. Akceptuje ona zmienną input jako informację oraz zmienną weight (waga) jako wiedzę, po czym zwraca prediction (prognozę). Każda sieć neuronowa, jaką kiedykolwiek spotkamy, będzie działać w ten sposób. Wykorzystuje wiedzę zawartą w wagach do interpretowania informacji w danych wejściowych. Później zobaczymy sieci neuronowe akceptujące większe, bardziej złożone wartości zmiennych input i weight, ale w tle zawsze będą te same przesłanki.
W tym przypadku informacją jest średnią liczbą rzutów dla drużyny przed rozpoczęciem meczu. Możemy tu zauważyć kilka rzeczy. Po pierwsze, sieć neuronowa nie ma dostępu do żadnej informacji poza jednym wystąpieniem. Jeśli po tej prognozie przekażemy do niej number_of_toes, sieć nie będzie pamiętać prognozy wykonanej w ostatnim kroku. Sieć neuronowa wie tylko to, co przekażemy do niej jako wejście. Zapomina wszystko inne. Później pokażę, jak można dać sieci neuronowej „pamięć krótkoterminową”, dostarczając jej jednocześnie wiele danych wejściowych.
Innym sposobem myślenia o wartościach wag w sieciach neuronowych jest traktowanie ich jako miary czułości między danymi wejściowymi sieci a jej prognozami. Jeśli waga jest bardzo duża, wówczas nawet najmniejsze wejście może utworzyć bardzo wielką wartość prognozy! Jeśli waga jest bardzo mała, wówczas nawet wielkie wartości wejściowe stworzą małe prognozy. Tę czułość można porównać z głośnością. „Podkręcenie wagi” wzmacnia prognozę względem danych wejściowych: waga jest regulatorem głośności!
W naszym przypadku to, co sieć neuronowa faktycznie robi, jest zastosowanie regulatora głośności do zmiennej number_of_toes. W teorii ten regulator potrafi nam podać prawdopodobieństwo zwycięstwa drużyny na podstawie średniej liczby udanych rzutów na gracza w drużynie. To może działać, choć nie musi. Prawdę mówiąc, gdyby członkowie drużyny mieli średnią 0 rzutów, zapewne graliby fatalnie. Jednak baseball jest znacznie bardziej złożony. W kolejny punkcie przekażemy wiele elementów informacji jednocześnie, tak by sieć neuronowa mogła podejmować lepsze decyzje.
Zauważmy, że sieci neuronowe nie muszą przewidywać jedynie dodatnich liczb – prognozy mogą być też liczbami ujemnymi, a także można przekazywać liczby ujemne jako wejście. Załóżmy, że chcemy przewidzieć prawdopodobieństwo, że ludzie będą dziś chodzić w płaszczach. Jeśli temperatura to –10 stopni Celsjusza, wówczas ujemna waga pozwoli przewidzieć wysokie prawdopodobieństwo tego, że ludzie założą płaszcze.
Wykonywanie prognozy dla wielu danych wejściowych
Sieci neuronowe mogą łączyć spostrzeżenia dla wielu obserwacji.
Poprzednia sieć neuronowa była w stanie przyjąć jedną obserwację jako wejście i wykonać jedną prognozę na tej podstawie. Mógłbyś jednak spytać, „Czy średnia liczba rzutów, sama jedna, w ogóle jest dobrym prognostykiem?”. Jeśli tak, to słusznie się zastanawiasz. Co by było, gdybyśmy mogli przekazać sieci więcej informacji (jednocześnie), niż jedynie średnią liczbę rzutów na gracza? W takim przypadku sieć powinna, przynajmniej teoretycznie, być w stanie wykonać bardziej dokładne prognozy. I jak się okazuje, sieć może przyjmować wiele wejściowych obserwacji jednocześnie. Przyjrzyjmy się kolejnemu przykładowi:
Wiele danych wejściowych: Co robi ta sieć neuronowa?
Mnoży trzy dane wejściowe przez trzy wagi i sumuje je. Jest to suma ważona.
Pod koniec poprzedniego podpunktu doszliśmy do zrozumienia ograniczającego czynnika naszej prostej sieci neuronowej: był tylko jeden regulator głośności dla jednej obserwacji. W przykładzie tą obserwacją była średnia liczba rzutów na gracza danej drużyny baseballowej. Doszliśmy do tego, że aby przewidywać dokładniej, potrzebujemy sieci neuronowej łączącej wiele danych wejściowych w tym samym czasie. Szczęśliwie sieci neuronowe zdolne są działać w ten sposób.
Ta nowa sieć neuronowa może przyjmować wiele danych wejściowych jednocześnie dla każdej prognozy. Pozwala to sieci łączyć różnego typu informacje, aby podejmować decyzje na podstawie najlepszych informacji. Jednak fundamentalny mechanizm użycia wag nie uległ zmianie. Nadal pobieramy poszczególne dane wejściowe i przepuszczamy je przez ich własny regulator głośności. Inaczej mówiąc, każdą daną wejściową mnożymy przez jej własną wagę.
Nową cechą, którą możemy tu zauważyć, jest to, że ponieważ mamy wiele danych wejściowych, musimy zsumować odpowiadające im prognozy. Tak więc mnożymy każdą daną wejściową przez odpowiadającą jej wagę i sumujemy wyniki (lokalne prognozy). Nazywamy to sumą ważoną danych wejściowych lub w skrócie sumą ważoną. Niektórzy autorzy odnoszą się do sumy ważonej jako do iloczynu skalarnego (dot product).
Istotne przypomnienie
Interfejs sieci neuronowej jest prosty. Akceptuje ona zmienną input jako informację oraz zmienną weights jako wiedzę, po czym zwraca prognozę.
Ta nowa potrzeba przetwarzania wielu danych wejściowych jednocześnie usprawiedliwia użycie nowego narzędzia. Nazywamy je wektorem i jeśli podążasz za mną w swoim notatniku Jupyter, już go używałeś. Wektor nie jest niczym innym niż listą liczb. W aktualnym przykładzie zmienna input jest wektorem i zmienna weights jest wektorem. Czy zauważasz jakieś inne wektory w pokazanym kodzie? (Są tam jeszcze trzy).
Jak się okazuje, wektory są niebywale użyteczne, ilekroć potrzebujemy wykonać operacje wykorzystujące grupy liczb. W tym przypadku wykonujemy sumę ważoną między dwoma wektorami (iloczyn skalarny). Bierzemy dwa wektory o równej długości (input oraz weights), mnożymy przez siebie poszczególne liczby, bazując na ich pozycji (pierwsza pozycja w input jest mnożona przez pierwszą pozycję w weights i tak dalej), po czym sumujemy uzyskane wyniki.
Ilekroć wykonujemy operację matematyczną na dwóch wektorach, w której parujemy wartości zgodnie z ich pozycją w wektorach (przypomnijmy: pozycję 0 z 0, 1 z 1 i tak dalej), nazywamy to operacją według elementów (albo element po elemencie, ang. elementwise). Tak więc dodawanie według elementów sumuje, mnożenie zaś według elementów mnoży odpowiednie składowe dwóch wektorów.
Wyzwanie: Arytmetyka wektorów
Umiejętność manipulowania wektorami jest kamieniem węgielnym głębokiego uczenia. Sprawdź, czy potrafisz napisać funkcje, które będą wykonywać następujące operacje:
- def elementwise_multiplication(vec_a, vec_b)
- def elementwise_addition(vec_a, vec_b)
- def vector_sum(vec_a)
- def vector_average(vec_a)
Następnie przekonaj się, czy potrafisz wykorzystać dwie z powyższych metod,
aby zrealizować iloczyn skalarny!
Intuicyjne rozumienie tego, jak i dlaczego działa iloczyn skalarny (suma ważona) jest zdecydowanie jedną z najważniejszych części prawdziwego przyswojenia sobie tego, jak sieci neuronowe wykonują prognozy. Mówiąc w uproszczeniu, iloczyn skalarny daje nam pojęcie o podobieństwie dwóch wektorów. Rozważmy poniższe przykłady³:
+-------------------+-----------------+
| a = | w_sum(a,b) = 0 |
| | |
| b = | w_sum(b,c) = 1 |
| | |
| c = | w_sum(b,d) = 1 |
| | |
| d = | w_sum(c,c) = 2 |
| | |
| e = | w_sum(d,d) = .5 |
| | |
| | w_sum(c,e) = 0 |
+-------------------+-----------------+
Największa suma ważona (w_sum(c,c)) występuje dla pary wektorów, które są dokładnie takie same. Dla kontrastu, ponieważ a i b nie mają nakładających się wag, ich iloczyn skalarny jest równy zero. Być może najbardziej interesująca jest suma ważona wektorów c i e, gdyż jedna z wag e jest ujemna. Ta ujemna waga zneutralizowała dodatnie podobieństwo występujące między tymi wektorami. Jednak iloczyn skalarny e z sobą samym zwróci 2 mimo ujemnej wagi (iloczyn liczb ujemnych jest dodatni). Poznajmy bliżej różne właściwości operacji iloczynu skalarnego.
Niekiedy można przyrównać właściwości iloczynu skalarnego i logicznego AND (koniunkcji logicznej). Rozważmy następujące wektory a i b:
a =
b =
Jeśli zapytamy, czy a AND b (czyli czy a oraz b mają jednocześnie wartość), odpowiedź brzmi nie (czyli fałsz, używając terminologii logicznej). Jeśli sprawdzimy a AND b, odpowiedzią ponownie jest nie. Ponieważ jest to zawsze prawdą dla wszystkich czterech składowych, finalny wynik wynosi 0. Dla każdej pary wartości logiczne AND zwraca fałsz.
b =
c =
Natomiast wektory b oraz c mają wspólną wartość dla jednej kolumny (składowej). W tym przypadku logiczne AND zwróci prawdę, gdyż b oraz c mają niezerowe wagi. Ta kolumna (i tylko ta) powoduje, że wynik jest zwiększany do 1.
c =
d =
Szczęśliwie sieci neuronowe są w stanie modelować częściową koniunkcję logiczną. W tym przypadku c oraz d mają tę samą niezerową kolumnę, co b i c, ale ponieważ w wektorze d waga wynosi 0.5, finalny wynik jest równy tylko 0.5. Własność tę wykorzystamy przy modelowaniu prawdopodobieństw w sieciach neuronowych.
d =
e =
W tej analogii ujemna waga odpowiada zastosowaniu logicznego operatora NOT (negacji), biorąc pod uwagę, że dowolna dodatnia waga sparowana z ujemną powoduje zmniejszanie ogólnego wyniku. Co więcej, jeśli obydwa wektory mają wagi ujemne (tak jak w w_sum(e,e)), sieć neuronowa wykona podwójną negację i doda wartości bezwzględne tych wag. Dodatkowo ktoś mógłby powiedzieć, że jest to alternatywa (OR) po koniunkcji (AND), gdyż jeśli którykolwiek wiersz zawiera jakąś wagę, ma to wpływ na wynik. Tak więc dla w_sum(a,b), jeżeli (a AND b) OR (a AND b) i tak dalej, wówczas w_sum(a,b) zwraca dodatni wynik. Co więcej, jeśli jedna wartość jest ujemna, wówczas ta kolumna zostaje zanegowana.
Co zabawne, daje to nam pewnego rodzaju „zgrubny” język odczytywania wag. Spróbujmy przeczytać kilka przykładów? Zakładają one, że wykonujemy w_sum(input, weights) i pominięte „then” w tych instrukcjach if są abstrakcjami dla „zwraca największy wynik”:
weights = => if input OR input
weights = => if input
weights = => if input OR NOT input
weights = => if NOT input OR NOT input
weights = => if BIG input or input
Zauważmy w ostatnim wierszu, że weight = 0.5 oznacza, że odpowiadająca mu wartość input musi być większa, aby skompensować mniejszą wagę. Jak wspomniałem, jest to bardzo zgrubnie przybliżający język. Wydaje się jednak niezmiernie przydany, gdy próbuję sobie zwizualizować, co się naprawdę dzieje. To może być bardzo pomocne w przyszłości, szczególnie gdy sieci neuronowe będziemy łączyć ze sobą na coraz bardziej złożone sposoby.
Opierając się na tych intuicjach, co to może znaczyć, gdy sieć neuronowa wykonuje prognozę? Mówiąc w uproszczeniu, oznacza to, że sieć zwraca największy wynik dla danych wejściowych, opierając się na tym, jak bardzo są one podobne do wag. W kolejnym przykładzie możemy zauważyć, że zmienna nfans jest całkowicie ignorowana w prognozowaniu, gdyż powiązana z nią waga wynosi 0. Najbardziej wrażliwą zmienną predykcyjną jest wlrec, gdyż odpowiada jej waga 0.2. Jednak dominujący wpływ na największy wynik ma liczba rzutów (ntoes), nie z powodu największej wagi, ale dlatego że dane wejściowe w powiązaniu z wagą dają zdecydowanie największy wynik.
Przypisy
1 Termin „machine learning” jest często tłumaczony jako „uczenie maszynowe”. Jednak w moim przekonaniu ten polski termin jest mylący – sugeruje „uczenie się przy użyciu maszyny”, podczas gdy w istocie chodzi o uczenie się samych maszyn. Z tego względu zdecydowałem się na używanie nieco mniej popularnego terminu „uczenie się maszyn” (przyp. tłum.).
2 Dla lepszej czytelności będziemy mówić po prostu o notatnikach Jupyter.
3 W Pythonie, podobnie jak w większości innych języków programowania, znakiem dziesiętnym zawsze jest kropka i tak też jest w tej książce (przyp. tłum.).