Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00527 009165 11230519 na godz. na dobę w sumie
Spark. Zaawansowana analiza danych - ebook/pdf
Spark. Zaawansowana analiza danych - ebook/pdf
Autor: , , , Liczba stron: 240
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-1464-1 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> bazy danych >> inne
Porównaj ceny (książka, ebook (-20%), audiobook).

Analiza ogromnych zbiorów danych nie musi być wolna!

Apache Spark to darmowy, zaawansowany szkielet i silnik pozwalający na szybkie przetwarzanie oraz analizę ogromnych zbiorów danych. Prace nad tym projektem rozpoczęły się w 2009 roku, a już rok później Spark został udostępniony użytkownikom. Jeżeli potrzebujesz najwyższej wydajności w przetwarzaniu informacji, jeżeli chcesz uzyskiwać odpowiedź na trudne pytania niemalże w czasie rzeczywistym, Spark może być odpowiedzią na Twoje oczekiwania.

Sięgnij po tę książkę i przekonaj się, czy tak jest w rzeczywistości. Autor porusza tu zaawansowane kwestie związane z analizą statystyczną danych, wykrywaniem anomalii oraz analizą obrazów. Jednak zanim przejdziesz do tych tematów, zapoznasz się z podstawami — wprowadzeniem do analizy danych za pomocą języka Scala oraz Apache Spark. Nauczysz się też przeprowadzać analizę semantyczną i zobaczysz, jak w praktyce przeprowadzić analizę sieci współwystępowań za pomocą biblioteki GraphX. Na koniec dowiesz się, jak przetwarzać dane geoprzestrzenne i genomiczne, a także oszacujesz ryzyko metodą symulacji Monte Carlo. Książka ta pozwoli Ci na wykorzystanie potencjału Apache Spark i zaprzęgnięcie go do najtrudniejszych zadań!

Przykłady prezetnowane w książce obejmują:

Poznaj potencjał i wydajność Apache Spark!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Advanced Analytics with Spark Tłumaczenie: Andrzej Watrak ISBN: 978-83-283-1461-0 © 2016 Helion S.A. Authorized Polish translation of the English edition of Advanced Analytics with Spark, ISBN 9781491912768 © 2015 Sandy Ryza, Uri Laserson, Sean Owen, and Josh Wills. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/sparkz Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/sparkz.zip Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treści Przedmowa .............................................................................................................................9 S(cid:293)owo wst(cid:253)pne ......................................................................................................................11 1. Analiza wielkich zbiorów danych ............................................................................. 13 15 16 18 Wyzwania w nauce o danych Przedstawiamy Apache Spark O czym jest ta książka 2. Wprowadzenie do analizy danych za pomoc(cid:233) Scala i Spark ................................... 21 22 23 23 24 29 32 33 36 38 39 40 44 45 Scala dla badaczy danych Model programowania w Spark Wiązanie rekordów danych Pierwsze kroki — powłoka Spark i kontekst SparkContext Przesyłanie danych z klastra do klienta Wysyłanie kodu z klienta do klastra Tworzenie list danych i klas wyboru Agregowanie danych Tworzenie histogramów Statystyki sumaryzacyjne ciągłych wartości Tworzenie współdzielonego kodu wyliczającego statystyki sumaryczne Prosty wybór zmiennych i ocena zgodności rekordów Następny krok 3. Rekomendowanie muzyki i dane Audioscrobbler ...................................................47 48 Zbiór danych Algorytm rekomendacyjny wykorzystujący metodę naprzemiennych najmniejszych kwadratów Przygotowanie danych 49 51 3 Poleć książkęKup książkę Utworzenie pierwszego modelu Wyrywkowe sprawdzanie rekomendacji Ocena jakości rekomendacji Obliczenie metryki AUC Dobór wartości hiperparametrów Przygotowanie rekomendacji Dalsze kroki 54 56 57 59 60 62 63 4. Prognozowanie zalesienia za pomoc(cid:233) drzewa decyzyjnego ................................. 65 65 66 67 68 70 71 72 76 77 79 81 83 83 Szybkie przejście do regresji Wektory i cechy Przykłady treningowe Drzewa i lasy decyzyjne Dane Covtype Przygotowanie danych Pierwsze drzewo decyzyjne Hiperparametry drzewa decyzyjnego Regulacja drzewa decyzyjnego Weryfikacja cech kategorialnych Losowy las decyzyjny Prognozowanie Dalsze kroki 5. Wykrywanie anomalii w ruchu sieciowym metod(cid:233) grupowania wed(cid:293)ug k-(cid:316)rednich ................................................................................................... 85 Wykrywanie anomalii 86 86 Grupowanie według k-średnich 87 Włamania sieciowe 87 Dane KDD Cup 1999 Pierwsza próba grupowania 88 90 Dobór wartości k 93 Wizualizacja w środowisku R Normalizacja cech 94 96 Zmienne kategorialne 97 Wykorzystanie etykiet i wskaźnika entropii Grupowanie w akcji 98 100 Dalsze kroki 4 (cid:95) Spis treści Poleć książkęKup książkę 6. Wikipedia i ukryta analiza semantyczna ................................................................101 102 104 104 105 106 108 110 Macierz słowo – dokument Pobranie danych Analiza składni i przygotowanie danych Lematyzacja Wyliczenie metryk TF-IDF Rozkład według wartości osobliwych Wyszukiwanie ważnych pojęć Wyszukiwanie i ocenianie informacji za pomocą niskowymiarowej reprezentacji danych Związek dwóch słów Związek dwóch dokumentów Związek słowa i dokumentu Wyszukiwanie wielu słów Dalsze kroki 113 114 115 116 117 118 Katalog cytowań bazy MEDLINE — analiza sieci Pobranie danych Analiza dokumentów XML za pomocą biblioteki Scala Analiza głównych znaczników i ich współwystępowań Konstruowanie sieci współwystępowań za pomocą biblioteki GraphX Struktura sieci 7. Analiza sieci wspó(cid:293)wyst(cid:253)powa(cid:295) za pomoc(cid:233) biblioteki GraphX ............................121 122 123 125 126 128 131 131 133 135 136 138 139 139 141 145 Filtrowanie krawędzi zakłócających dane Przetwarzanie struktury EdgeTriplet Analiza przefiltrowanego grafu Połączone komponenty Rozkład stopni wierzchołków Sieci typu „mały świat” Kliki i współczynniki klastrowania Obliczenie średniej długości ścieżki za pomocą systemu Pregel Dalsze kroki 8. Geoprzestrzenna i temporalna analiza tras nowojorskich taksówek .................. 147 148 148 149 Pobranie danych Przetwarzanie danych temporalnych i geoprzestrzennych w systemie Spark Przetwarzanie danych temporalnych za pomocą bibliotek JodaTime i NScalaTime Spis treści (cid:95) 5 Poleć książkęKup książkę Przetwarzanie danych geoprzestrzennych za pomocą Esri Geometry API i Spray Użycie interfejsu API Esri Geometry Wprowadzenie do formatu GeoJSON Przygotowanie danych dotyczących kursów taksówek Obsługa dużej liczby błędnych rekordów danych Analiza danych geoprzestrzennych Sesjonowanie w systemie Spark Budowanie sesji — dodatkowe sortowanie danych w systemie Spark Dalsze kroki 150 151 152 154 155 158 161 162 165 Terminologia Metody obliczania wskaźnika VaR Wariancja-kowariancja Symulacja historyczna Symulacja Monte Carlo 9. Szacowanie ryzyka finansowego metod(cid:233) symulacji Monte Carlo ........................ 167 168 169 169 169 169 170 171 171 174 176 178 179 181 182 184 Nasz model Pobranie danych Wstępne przetworzenie danych Określenie wag czynników Losowanie prób Wykonanie testów Wizualizacja rozkładu zwrotów Ocena wyników Dalsze kroki Wielowymiarowy rozkład normalny 10. Analiza danych genomicznych i projekt BDG .........................................................187 188 190 195 Rozdzielenie sposobów zapisu i modelowania danych Przetwarzanie danych genomicznych za pomocą wiersza poleceń systemu ADAM Format Parquet i format kolumnowy Prognozowanie miejsc wiązania czynnika transkrypcyjnego na podstawie danych ENCODE Odczytywanie informacji o genotypach z danych 1000 Genomes Dalsze kroki 197 203 204 6 (cid:95) Spis treści Poleć książkęKup książkę Ogólne informacje o pakiecie PySpark 11. Analiza danych neuroobrazowych za pomoc(cid:233) pakietów PySpark i Thunder ......205 206 207 209 210 214 216 221 Ogólne informacje i instalacja biblioteki pakietu Thunder Ładowanie danych za pomocą pakietu Thunder Budowa pakietu PySpark Podstawowe typy danych w pakiecie Thunder Klasyfikowanie neuronów za pomocą pakietu Thunder Dalsze kroki Serializacja Akumulatory System Spark i metody pracy badacza danych Formaty plików Podprojekty Spark A Wi(cid:253)cej o systemie Spark ..........................................................................................223 224 225 226 228 229 229 230 230 230 MLlib Spark Streaming Spark SQL GraphX B Nowy interfejs MLlib Pipelines API ........................................................................ 231 231 232 233 Samo modelowanie to za mało Interfejs API Pipelines Przykład procesu klasyfikacji tekstu Skorowidz ...........................................................................................................................236 Spis treści (cid:95) 7 Poleć książkęKup książkę 8 (cid:95) Spis treści Poleć książkęKup książkę ROZDZIAŁ 3. Rekomendowanie muzyki i dane Audioscrobbler Sean Owen De gustibus non est disputandum. (O gustach się nie dyskutuje). Gdy ktoś pyta, czym się zajmuję, aby zarobić na życie, moja odpowiedź wprost: „Nauką o danych” czy „Uczeniem maszynowym” robi wrażenie, ale zazwyczaj wywołuje pełne zdumienia spojrzenia. Nic dziwnego, bo sami badacze danych mają kłopoty z określeniem, co te terminy oznaczają. Czy przechowywanie mnóstwa danych? Przetwarzanie ich? Prognozowanie wartości? Przejdę od razu do odpowiedniego przykładu: „OK, wiesz, że księgarnia Amazon rekomenduje książki podobne do tej, którą kupiłeś, prawda? Tak! To właśnie to!”. Z praktycznego punktu widzenia system rekomendacyjny jest przykładem zastosowania na sze- roką skalę algorytmu uczenia maszynowego, który każdy rozumie, a większość użytkowników księgarni Amazon go zna. Jest to powszechnie znana rzecz, ponieważ systemy rekomendacyjne są stosowane wszędzie, począwszy od serwisów społecznościowych, po strony z filmami i sklepy inter- netowe. Widzimy je również bezpośrednio w akcji. Wiemy, że komputer pobiera piosenki w serwisie Spotify, ale nie wiemy, jak serwer poczty Gmail decyduje, czy odebrana wiadomość jest spamem. Działanie systemu rekomendacyjnego jest bardziej intuicyjnie zrozumiałe niż funkcjonowanie innych algorytmów uczenia maszynowego. System taki jest wręcz fascynujący. Wszyscy wiemy, że gust muzyczny jest sprawą osobistą i niewytłumaczalną, ale systemy rekomendacyjne zaskakująco dobrze sobie radzą z wyszukiwaniem utworów, o których nawet nie wiemy, że będą nam się podobać. Ponadto, w branży muzycznej czy filmowej, gdzie systemy te są powszechnie stosowane, dość łatwo stwierdzić, dlaczego polecana pozycja pasuje do historii utworów wybieranych przez danego słuchacza. Jednak nie wszystkie algorytmy klasyfikujące odpowiadają temu opisowi. Na przykład maszyna wektorów nośnych jest zbiorem współczynników i nawet zawodowcom trudno określić, co oznaczają liczby wygenerowane podczas prognozowania wartości. Czas zatem przymierzyć się do trzech kolejnych rozdziałów, szczegółowo opisujących najważniejsze algorytmy uczenia maszynowego w systemie Spark. Niniejszy rozdział poświęcony jest systemom 47 Poleć książkęKup książkę rekomendacyjnym, a konkretnie rekomendowaniu muzyki. Stanowi on przystępne wprowadzenie do praktycznego zastosowania systemu Spark, biblioteki MLlib i kilku pojęć z dziedziny uczenia maszynowego, które będą opisane w następnych rozdziałach. Zbiór danych W tym rozdziale wykorzystane są dane udostępnione przez system Audioscrobbler. Jest to pierwszy system rekomendacyjny, wykorzystywany przez serwis last.fm, założony w roku 2002, będący jednym z pierwszych serwisów internetowych udostępniających strumieniową transmisję audycji radiowych. System Audioscrobbler oferował otwarty interfejs API do „scrobblowania” (wysyłania) listy utwo- rów wykonawcy wybranego przez użytkownika. Informacje te posłużyły do zbudowania skutecz- nego systemu rekomendującego muzykę. Strona przyciągnęła miliony użytkowników, ponieważ zewnętrzne aplikacje i strony internetowe wysyłały informacje o odsłuchiwanych utworach z powro- tem do systemu rekomendacyjnego. W tamtym czasie systemy rekomendacyjne w większości przypadków ograniczały swoje działanie do uczenia się ocen. Zazwyczaj były zatem postrzegane jako narzędzia wykorzystujące dane wejściowe typu „Robert dał Prince’owi ocenę 3,5 gwiazdki”. Dane udostępnione przez Audioscrobbler są interesujące, ponieważ rekordy zawierają jedynie informację typu „Robert słuchał utworu Prince’a”. Jest to informacja uboższa niż ocena. Jeżeli Robert słuchał jakiegoś utworu, nie oznacza to, że mu się on podobał. Ja czy Ty również możemy przypadkowo posłuchać utworu jakiegoś wykonawcy, który nie ma dla nas znaczenia, a nawet rozpocząć odtwarzanie całego albumu i wyjść z pokoju. Jednak użytkownicy znacznie rzadziej oceniają utwory, niż je odtwarzają. Zbiór tych ostatnich danych jest znacznie większy, obejmuje większą liczbę użytkowników i wykonawców, jak również zawiera w sumie więcej informacji niż zbiór danych z ocenami, mimo że pojedynczy rekord zawiera w sobie mniej danych. Tego typu dane są często nazywane niejawnymi informacjami zwrotnymi, ponieważ określenie związku pomiędzy użytkownikiem a wykonawcą jest efektem ubocznym innych czynności niż jawne wystawienie oceny czy kliknięcie symbolu kciuka w górę. Próbkę danych udostępnioną przez serwis last.fm w roku 2005 można pobrać w postaci spakowanego archiwum (http://www-etud.iro.umontreal.ca/~bergstrj/audioscrobbler_data.html). Wewnątrz niego znajduje się kilka plików. Główny zbiór danych znajduje się w pliku user_artist_data.txt. Zawiera on około 141 000 unikatowych identyfikatorów użytkowników i 1,6 miliona unikatowych identy- fikatorów wykonawców. Zarejestrowanych zostało około 24,2 miliona przypadków odsłuchania przez danego słuchacza utworu danego wykonawcy oraz liczby odsłuchań. Ponadto w pliku artist_data.txt znajdują się nazwy wykonawców odpowiadających każdemu identy- fikatorowi. Zwróć uwagę, że z chwilą rozpoczęcia odtwarzania utworu aplikacja wyświetla nazwę wykonawcy. Nazwa ta może być niestandardowa i zawierać błędy, ale takie przypadki można wykryć dopiero na późniejszym etapie analizy. Na przykład nazwy „Budka Sufler”, „Budka S” i „budka suflera” mogą mieć różne identyfikatory w pliku danych, choć oczywiście oznaczają tego samego wykonawcę. Dlatego wśród danych znajduje się również plik artist_alias.txt, w którym powiązane są identyfikatory błędnie zapisanych lub zmienionych nazw wykonawców z ich identyfikatorami podstawowymi. 48 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Algorytm rekomendacyjny wykorzystujący metodę naprzemiennych najmniejszych kwadratów Musimy wybrać algorytm rekomendacyjny odpowiedni dla danych z niejawnymi ocenami. Dane zawierają wyłącznie powiązania pomiędzy użytkownikami a wykonawcami. Nie ma w nich infor- macji o użytkownikach ani wykonawcach, z wyjątkiem nazw tych ostatnich. Potrzebny jest algorytm, który będzie uczył się preferencji użytkowników bez sięgania do ich atrybutów ani atrybutów wyko- nawców. Tego typu algorytmy nazywane są zazwyczaj filtrowaniem kolaboratywnym (http://en. wikipedia.org/wiki/Collaborative_filtering). Na przykład twierdzenie, że dwóch użytkowników ma podobne preferencje muzyczne, ponieważ są w tym samym wieku, nie jest przykładem filtrowania kolaboratywnego. Natomiast dobrym przykładem jest twierdzenie, że dwóch użytkowników lubi ten sam utwór, ponieważ obaj wielokrotnie słuchali podobnych. Przykładowy zbiór danych jest duży, obejmuje kilkadziesiąt milionów rekordów zawierających liczby odsłuchań utworów. Z drugiej strony jednak zbiór jest mały i ubogi, ponieważ zawiera skąpe infor- macje. Średnio każdy użytkownik odtwarzał piosenki 171 wykonawców spośród 1,6 miliona wszystkich. Niektórzy użytkownicy słuchali utworów tylko jednego wykonawcy. Nam potrzebny jest algorytm, który przygotuje rekomendacje nawet dla takich użytkowników. Przecież każdy użyt- kownik bez wyjątku musi zacząć od odsłuchania tylko jednego utworu! Ponadto potrzebny jest algorytm skalowalny, zarówno pod względem obsługi dużego modelu danych, jak i szybkiego przygotowywania rekomendacji. Zazwyczaj wymaga się, aby rekomendacje były przygotowywane niemal na bieżąco, tj. w ciągu kilku sekund, a nie na drugi dzień. W tym przykładzie zostanie wykorzystany jeden z algorytmów należących do tzw. modeli zmien- nych ukrytych (http://pl.wikipedia.org/wiki/Analiza_czynnikowa). Przeznaczeniem tych algorytmów jest wyjaśnienie obserwowanych interakcji pomiędzy dużą liczbą użytkowników i produktów na podstawie relatywnie małej liczby niezaobserwowanych, ukrytych przyczyn. Odpowiada to opisaniu preferencji użytkowników i twórczości wykonawców za pomocą kilkudziesięciu gatunków muzyki (choć preferencje nie są określone ani bezpośrednio dostępne) i wyjaśnieniu na tej podstawie, dla- czego miliony użytkowników kupują określone albumy spośród tysięcy dostępnych. Mówiąc dokładniej, w tym rozdziale wykorzystamy pewien rodzaj faktoryzacji macierzy (http://en. wikipedia.org/wiki/Non-negative_matrix_factorization). W algorytmach tego typu użytkownicy i wykonawcy są opisani za pomocą dużej macierzy A, która w wierszu i oraz kolumnie j zawiera określone dane, gdy użytkownik i słuchał utworu wykonawcy j. Macierz A jest niezapełniona — większość komórek zawiera zera, ponieważ dane są dostępne tylko dla nielicznych kombinacji użytkownik – wykonawca. Macierz A można zapisać jako iloczyn dwóch mniejszych macierzy, X i Y. Macierze te są bardzo wąskie — obie zawierają wprawdzie wiele wierszy, ponieważ macierz A ma wiele wierszy i kolumn, ale tylko kilka kolumn (k). Tych k kolumn odpowiada ukrytym zmiennym, które zostaną użyte do wyjaśnienia zależności między danymi. Faktoryzacja macierzy będzie przybliżona, ponieważ liczba k jest mała, jak pokazuje rysunek 3.1. Wspomniane algorytmy są niekiedy nazywane algorytmami uzupełniania macierzy, ponieważ oryginalna macierz A może być bardzo rzadko wypełniona, natomiast iloczyn XYT daje macierz gęsto wypełnioną. Bardzo mało elementów, o ile w ogóle jakiekolwiek, zawiera wartości 0, dlatego iloczyn Algorytm rekomendacyjny wykorzystujący metodę naprzemiennych najmniejszych kwadratów (cid:95) 49 Poleć książkęKup książkę Rysunek 3.1. Faktoryzacja macierzy ten jest jedynie przybliżeniem macierzy A. W tym modelu tworzone są („uzupełniane”) wartości również w tych elementach oryginalnej macierzy A, w których brakuje wartości (tj. zawierających wartość 0). Na szczęście, w naszym przypadku algebra liniowa w elegancki i bezpośredni sposób podąża za intuicją. Dwie macierze mają po jednym wierszu zawierającym informacje odpowiednio o użytkow- nikach i wykonawcach. Wiersze zawierają niewiele, tj. k wartości. Każda wartość odpowiada ukrytej zmiennej w modelu danych. Zatem wiersze opisują, jak ściśle użytkownicy i wykonawcy są skoja- rzeni za pomocą ukrytych cech, co przekłada się na preferencje użytkowników i gatunki muzyki wykonawców. Pełne oszacowanie gęsto wypełnionej macierzy interakcji użytkownik – wykonawca jest zatem po prostu iloczynem macierzy użytkownik – cecha oraz cecha – wykonawca. Jest jednak zła wiadomość: równanie A = XYT generalnie nie ma rozwiązania w ogóle, ponieważ macierze X i Y nie są na tyle duże (mówiąc językiem matematycznym, mają za mały rząd), aby dokładnie oddać macierz A. Jednak tak naprawdę jest to dobra wiadomość. Macierz A jest tylko małą próbką wszystkich możliwych interakcji. W pewien sposób zakładamy, że macierz ta jest niezwykle rzadko wypełniona, a więc jest trudnym w zrozumieniu obrazem prostszej rzeczywistości, którą można dobrze opisać za pomocą małej liczby (k) czynników. Wyobraź sobie puzzle z rysunkiem kota. Ostateczny obraz jest prosty do opisania: kot. Jeżeli jednak jest dostępnych tylko kilka elemen- tów układanki, trudno opisać obraz. Iloczyn XYT powinien jak najbliżej odpowiadać macierzy A, jest on przecież niezbędny do dalszej analizy. Wynik iloczynu nie odzwierciedla dokładnie oryginalnej macierzy i nie powinien tego robić. Kolejną złą wiadomością jest brak możliwości znalezienia najlepszych macierzy X i Y jedno- cześnie. Dobra wiadomość jest taka, że trywialnym zadaniem jest znalezienie najlepszej macierzy X, jeżeli znana jest macierz Y, i odwrotnie. Jednak na początku nie jest znana żadna z macierzy! Na szczęście, istnieją algorytmy umożliwiające wydostanie się z tego impasu i znalezienie satysfakcjo- nującego rozwiązania. Mówiąc dokładniej, w tym rozdziale do wyliczenia macierzy X i Y zostanie zastosowany algorytm naprzemiennych najmniejszych kwadratów (ang. Alternating Least Squares, ALS). Metoda ta została spopularyzowana w publikacjach Collaborative Filtering for Implicit Feedback Datasets (http://yifanhu.net/PUB/cf.pdf) i Large-scale Parallel Collaborative Filtering for the Netflix 50 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Prize (http://www.grappa.univ-lille3.fr/~mary/cours/stats/centrale/reco/paper/MatrixFactorizationALS. pdf), poświęconych konkursowi ogłoszonemu przez serwis Netflix (http://en.wikipedia.org/wiki/Netflix_ Prize). W bibliotece MLlib systemu Spark algorytm ALS jest oparty na pomysłach wziętych z obu publikacji. Macierz Y nie jest znana, ale można ją zainicjować wektorami wierszy zawierającymi wyłącznie losowe wartości. Następnie, wykorzystując prostą algebrę liniową, można dla danych macierzy A i Y znaleźć najlepszą macierz X. W rzeczywistości wyliczenie każdego wiersza i macierzy X jako funkcji macierzy Y i jednego wiersza tabeli A jest trywialnym zadaniem. Ponieważ obliczenia są od siebie niezależne, można je wykonywać równolegle, co doskonale usprawnia przetwarzanie danych na szeroką skalę: AiY(YTY)-1 = Xi Osiągnięcie pełnej zgodności obu stron równania nie jest możliwe, więc rzeczywistym celem jest zminimalizowanie wartości wyrażenia |AiY(YTY)-1 = Xi| albo sumy kwadratów różnic między wartościami z obu tabel. Stąd w nazwie algorytmu znalazły się słowa „najmniejsze kwadraty”. W praktyce równania nie da się rozwiązać przez odwrócenie macierzy, jednak szybką i bardziej bezpośrednią metodą jest na przykład dekompozycja QR (http://en.wikipedia.org/wiki/QR_ decomposition). Powyższe równanie opisuje teoretyczny sposób wyliczania wektora wierszy macierzy. Tę samą operację można wykonać w celu wyliczenia każdego wiersza Yj na podstawie macierzy X, następnie znów macierzy X na podstawie Y itd. Stąd w nazwie algorytmu pojawia się słowo „naprze- mienne”. Jest tylko mały problem: macierz Y jest tworzona z losowych wartości! Macierz X zostanie wyliczona optymalnie, ale na podstawie fikcyjnych danych w macierzy Y. Na szczęście, gdy proces będzie powtarzany, macierze X i Y ustabilizują się i ostatecznie będą zawierały akceptowalne wartości. Algorytm ALS nieco się komplikuje, gdy trzeba faktoryzować macierz zawierającą dane niejawne. Nie jest bezpośrednio faktoryzowana macierz A, ale macierz P, zawierająca wartości 0 i 1 w miej- scach, gdzie macierz A zawiera odpowiednio wartości zerowe i dodatnie. Wartości z macierzy A są wykorzystywane później jako wagi. Ten temat wykracza poza zakres niniejszej książki, a jego poznanie nie jest niezbędne do zrozumienia stosowania opisywanego algorytmu. Dodatkowo algorytm ALS wykorzystuje również nieliczność danych wejściowych. Dzięki zasto- sowaniu prostej, zoptymalizowanej algebry liniowej i jej natury równoległego przetwarzania danych algorytm działa bardzo sprawnie przy dużej ilości danych. Głównie z tego powodu stanowi on temat niniejszego rozdziału, a poza tym jest to jedyny algorytm rekomendacyjny obecnie zaimplemen- towany w bibliotece MLlib systemu Spark! Przygotowanie danych Skopiuj wszystkie trzy pliki danych do systemu HDFS. W tym rozdziale przyjęte jest założenie, że pliki będą zapisane w katalogu /user/ds/. Otwórz system Spark poleceniem spark-shell. Pamiętaj, że przetwarzanie danych będzie wymagało wyjątkowo dużej ilości pamięci. Jeżeli przetwarzasz dane lokalnie, a nie w klastrze, prawdopodobnie będziesz musiał użyć parametru --driver-memory 6g w celu skonfigurowania odpowiednio dużej ilości pamięci niezbędnej do przetworzenia danych. Przygotowanie danych (cid:95) 51 Poleć książkęKup książkę Pierwszym krokiem w procesie budowania modelu jest poznanie dostępnych danych, a następnie rozłożenie ich lub przetransformowanie na format umożliwiający analizę w systemie Spark. Jednym z małych ograniczeń implementacji algorytmu ALS w bibliotece MLlib jest konieczność określenia identyfikatorów użytkowników i elementów w postaci nieujemnych 32-bitowych liczb całkowitych. Oznacza to, że nie można używać identyfikatorów większych niż stała Integer.MAX_VALUE, czyli 2147483647. Czy dostępne dane spełniają ten warunek? Zamień plik na zbiór RDD danych typu String za pomocą metody textFile z kontekstu SparkContext: val rawUserArtistData = sc.textFile( hdfs:///user/ds/user_artist_data.txt ) Domyślnie zbiór RDD zawiera jedną partycję w każdym bloku HDFS. Ponieważ analizowany plik zajmuje ok. 400 MB miejsca w systemie HDFS, zostanie podzielony na trzy do sześciu partycji, zależnie od wielkości bloku HDFS. Zazwyczaj jest to wystarczający podział, ale zadania uczenia maszynowego, takie jak algorytm ALS, wymagają wykonania operacji bardziej intensywnych obliczeniowo niż zwykłe przetwarzanie pliku. Lepszym rozwiązaniem może być podzielenie danych na mniejsze części, tj. na większą liczbę partycji. Dzięki temu system Spark mógłby wykorzystać większą liczbę rdzeni procesorów do równoległej pracy nad problemem. W powyższej metodzie można określić dodatkowy argument wyznaczający inną, większą liczbę partycji. Argument ten może na przykład być równy liczbie rdzeni procesorów w Twoim klastrze. Każdy wiersz pliku zawiera identyfikator użytkownika, identyfikator wykonawcy i liczbę odtworzeń utworów. Dane rozdzielone są spacjami. W celu wyliczenia statystyk dla identyfikatorów użytkow- ników podzielimy wiersze zgodnie ze spacjami, a pierwszy element (indeksy elementów zaczynają się od 0) zamienimy na liczbę. Metoda stats() zwraca obiekt zawierający statystyki, takie jak wartości minimalna i maksymalna. Podobną operację wykonamy dla identyfikatorów wykonawców: rawUserArtistData.map(_.split( )(0).toDouble).stats() rawUserArtistData.map(_.split( )(1).toDouble).stats() Wyliczone statystyki pokazują, że maksymalne wartości identyfikatorów użytkowników i wyko- nawców są równe odpowiednio 2443548 i 10794401. Są to wartości znacznie mniejsze od 2147483647. W przypadku tych identyfikatorów nie są wymagane żadne dodatkowe transformacje. W tym przykładzie warto byłoby później poznać nazwiska wykonawców odpowiadające enigma- tycznym identyfikatorom. Potrzebna informacja jest zawarta w pliku artist_data.txt. Tym razem identyfikator wykonawcy i jego nazwa rozdzielone są znakiem tabulacji. Jednakże zwykły podział wiersza na pary (Int, String) nie powiedzie się: val rawArtistData = sc.textFile( hdfs:///user/ds/artist_data.txt ) val artistByID = rawArtistData.map { line = val (id, name) = line.span(_ != \t ) (id.toInt, name.trim) } W tym przypadku metoda span() dzieli wiersz w miejscu pierwszego wystąpienia znaku tabulacji. Następnie pierwsza część jest konwertowana na liczbę, ponieważ zawiera identyfikator wykonawcy, a druga jest traktowana jako nazwa wykonawcy (po usunięciu białych znaków, czyli znaku tabulacji). Kilka wierszy w pliku jest uszkodzonych. Nie zawierają one znaku tabulacji albo zawierają znak nowego wiersza. Wiersze te powodują zgłoszenie wyjątku NumberFormatException i w rzeczywistości nie zawierają żadnych powiązań. 52 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Jednakże funkcja map() musi zwracać dokładnie jedną wartość dla każdej danej wejściowej, więc w tym przypadku nie można jej użyć. Za pomocą metody filter() można byłoby usunąć wadliwe wiersze, ale w ten sposób algorytm podziału wiesza byłby stosowany dwukrotnie. Funkcję flatMap() można stosować w przypadku, gdy każdy element jest powiązany z jedną wartością, z kilkoma lub nie jest powiązany z żadną, ponieważ funkcja ta po prostu „spłaszcza” kolekcje zawierające kilka lub niezawierające żadnych wyników i tworzy jeden wielki zbiór RDD. Funkcja ta działa poprawnie z kolekcjami języka Scala i z klasą Option. Klasa ta reprezentuje wartość, która może istnieć tylko opcjonalnie. Przypomina prostą kolekcję zawierającą tylko wartości 1 i 0, odpowiadające podklasom Some i None. Skoro zatem funkcja flatMap w poniższym kodzie może równie dobrze zwrócić pustą listę typu List lub listę zawierającą jeden element, uzasadnione będzie użycie zamiast tych list prostszych i bardziej czytelnych klas Some i None: val artistByID = rawArtistData.flatMap { line = val (id, name) = line.span(_ != \t ) if (name.isEmpty) { None } else { try { Some((id.toInt, name.trim)) } catch { case e: NumberFormatException = None } } } Plik artist_alias.txt zawiera powiązane identyfikatory nazw wykonawców, które mogą być błędnie zapisane lub różnić się od nazwy skojarzonej z podstawowym identyfikatorem. Każdy wiersz zawiera dwa identyfikatory rozdzielone znakiem tabulacji. Plik ten jest dość mały, zawiera około 200 000 rekordów. Warto zamienić go na kolekcję Map, w której „złe” identyfikatory wykonawców są powią- zane z „dobrymi”, a nie na zbiór RDD, zawierający wyłącznie pary identyfikatorów. Podobnie jak poprzednio, z jakiegoś powodu w kilku wierszach brakuje pierwszego identyfikatora, więc wiersze te zostaną pominięte: val rawArtistAlias = sc.textFile( hdfs:///user/ds/artist_alias.txt ) val artistAlias = rawArtistAlias.flatMap { line = val tokens = line.split( \t ) if (tokens(0).isEmpty) { None } else { Some((tokens(0).toInt, tokens(1).toInt)) } }.collectAsMap() Na przykład w pierwszym rekordzie powiązane są identyfikatory 6803336 i 1000010. Można je wykorzystać do przeszukania zbioru RDD zawierającego nazwy wykonawców: artistByID.lookup(6803336).head artistByID.lookup(1000010).head Ten rekord ewidentnie wiąże nazwy „Aerosmith (unplugged)” i „Aerosmith”. Przygotowanie danych (cid:95) 53 Poleć książkęKup książkę Utworzenie pierwszego modelu Choć zbiór danych osiągnął niemal ostateczną formę umożliwiającą zastosowanie algorytmu ALS zaimplementowanego w bibliotece MLlib, wymaga wykonania jeszcze dwóch dodatkowych, niewiel- kich transformacji. Po pierwsze, trzeba wykorzystać zbiór danych z aliasami wykonawców do przekonwertowania wszystkich identyfikatorów dodatkowych (jeżeli istnieją) na identyfikatory podstawowe. Po drugie, dane muszą być przekształcone w obiekty typu Rating, implementujące rekordy zawierające dane użytkownik – produkt – wartość. Obiekt Rating, mimo swej nazwy, nadaje się do przetwarzania danych niejawnych. Zwróć również uwagę, że w interfejsie API biblioteki MLlib stosowane jest pojęcie „produkt”, które w naszym przypadku oznacza wykonawcę. Wyko- rzystywany model w żaden sposób nie jest związany z rekomendowaniem produktów czy też — jak w tym przypadku — rekomendowaniem produktów dla użytkowników: import org.apache.spark.mllib.recommendation._ val bArtistAlias = sc.broadcast(artistAlias) val trainData = rawUserArtistData.map { line = val Array(userID, artistID, count) = line.split( ).map(_.toInt) val finalArtistID = bArtistAlias.value.getOrElse(artistID, artistID) Rating(userID, finalArtistID, count) }.cache() Przyjmij alias wykonawcy, jeżeli jest dostępny. W przeciwnym wypadku przyjmij jego oryginalny identyfikator. Utworzony wcześniej obiekt artistAlias może być wykorzystywany bezpośrednio przez funkcję map(), mimo że jest to obiekt Map utworzony lokalnie na komputerze sterującym. Kod będzie działał poprawnie, ponieważ obiekt ten będzie automatycznie kopiowany przed wykonaniem każdego zadania. Jednak obiekt nie jest mały, zajmuje ok. 15 megabajtów pamięci, a w formie serializowanej co najmniej kilka megabajtów. Ponieważ na jednej maszynie JVM wykonywanych jest wiele zadań, przesyłanie i zapisywanie wielu kopii obiektu jest marnotrawstwem zasobów. Zamiast tego utworzymy zmienną rozgłaszaną (ang. broadcast variable — http://spark.apache. org/docs/latest/programming-guide.html#broadcast-variables) o nazwie bArtistAlias, skojarzoną z obiektem artistAlias. Dzięki niej system Spark będzie wysyłał i umieszczał w pamięci każdego komputera w klastrze tylko jedną kopię obiektu. W przypadku wykonywania tysięcy zadań, wielu równolegle na każdym komputerze, można w ten sposób znacznie zmniejszyć ruch sieciowy i oszczę- dzić pamięć. Wywołanie metody cache() stanowi wskazówkę dla systemu Spark, aby zbiór RDD po przetworzeniu został tymczasowo zapisany w pamięci klastra. Jest to przydatna opcja, ponieważ algorytm ALS jest iteracyjny, a dane są zazwyczaj odczytywane 10 lub więcej razy. Bez wykonania tej operacji zbiór RDD byłby wielokrotnie wyliczany na podstawie oryginalnych danych za każdym razem, kiedy byłby potrzebny! Zakładka Storage (zapisane dane) w interfejsie graficznym systemu Spark, pokazana na rysunku 3.2, pokazuje, jaka część zbioru RDD została zapisana i ile zajmuje pamięci. W tym przypadku zbiór zajmuje niemal 900 MB pamięci w całym klastrze. 54 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Zmienne rozg(cid:293)aszane Gdy system Spark wykonuje jakiś proces, tworzy binarne reprezentacje wszystkich informacji niezbęd- nych do wykonania zadania, czyli domknięcia (ang. closure) funkcji, które mają być wykonane. Domknięcie zawiera wszystkie struktury danych z procesu sterującego, do których odwołuje się dana funkcja. Spark wysyła domknięcie do wszystkich komputerów w klastrze. Zmienne rozgłaszane przydają się w sytuacjach, gdy wiele zadań musi mieć dostęp do tych samych (stałych) struktur danych. Zmienne te rozszerzają zwykłą obsługę domknięć przez zadania o następujące funkcjonalności: (cid:120) Zapisywanie danych w postaci podstawowych obiektów Java w pamięci każdego komputera, dzięki czemu dane nie muszą być deserializowane przez każde zadanie. (cid:120) Zapisywanie danych w pamięci, do wykorzystania przez różne zadania i procesy. Rozważmy dla przykładu aplikację do analizy języka mówionego, wykorzystującą duży słownik języka polskiego. Rozgłoszenie tego słownika umożliwia przesłanie go tylko raz do wszystkich komputerów: val dict = ... val bDict = sc.broadcast(dict) ... def query(path: String) = { sc.textFile(path).map(l = score(l, bDict.value)) ... } Rysunek 3.2. Zakładka Storage w graficznym interfejsie systemu Spark, zawierająca informacje o pamięci zajętej przez zbiory RDD Teraz możemy zbudować model: val model = ALS.trainImplicit(trainData, 10, 5, 0.01, 1.0) Powyższy kod tworzy zmienną model typu MatrixFactorizationModel. Operacja ta prawdopodobnie będzie wykonywana przez kilka minut lub dłużej, w zależności od zastosowanego klastra. W porów- naniu do kilku innych modeli uczenia maszynowego, które w ostatecznej formie składają się z zaledwie kilku parametrów czy współczynników, ten model jest ogromny. Zawiera wektor 10 wartości dla każdego użytkownika i produktu w modelu, których w naszym przypadku jest ponad 1,7 miliona. Model zawiera macierze użytkownik – cecha i produkt – cecha jako osobne zbiory RDD. Aby zobaczyć niektóre wektory cech, uruchom podany niżej kod. Zwróć uwagę, że wektor cech jest tabelą typu Array złożoną z 10 elementów, a tabele nie są standardowo wyświetlane w czytelnej postaci. W poniższym kodzie wektor jest zamieniany na czytelną postać za pomocą metody mkString(), powszechnie stosowanej w języku Scala do łączenia elementów kolekcji w rozdzielony separatorami ciąg znaków: model.userFeatures.mapValues(_.mkString( , )).first() ... (4293,-0.3233030601963864, 0.31964527593541325, Utworzenie pierwszego modelu (cid:95) 55 Poleć książkęKup książkę 0.49025505511361034, 0.09000932568001832, 0.4429537767744912, 0.4186675713407441, 0.8026858843673894, -0.4841300444834003, -0.12485901532338621, 0.19795451025931002) W Twoim przypadku wynikowe wartości będą inne. Ostateczny model zależy od losowo wygenerowanych wektorów cech. Pozostałe argumenty metody trainImplicit() są hiperparametrami, których wartości mają wpływ na jakość rekomendacji przedstawianych przez model. Hiperparametry zostaną opisane później. Pierwsze ważne pytanie brzmi: czy ten model jest dobry? Czy przygotowuje dobre rekomendacje? Wyrywkowe sprawdzanie rekomendacji Najpierw musimy sprawdzić, badając dane użytkownika i wybierane przez niego utwory, czy przygotowywane dla niego rekomendacje wykonawców są zgodnie z naszą intuicją. Weźmy dla przykładu użytkownika o identyfikatorze 2093760. Wyszukajmy identyfikatory wykonawców, których utworów słuchał ten użytkownik, i wyświetlmy odpowiadające im nazwy. Cała operacja będzie polegała na wyszukiwaniu identyfikatorów wykonawców na podstawie wejściowego identyfikatora użytkownika, następnie filtrowaniu danych według identyfikatorów wykonawców i na koniec zebraniu i wyświetleniu posortowanych nazw: val rawArtistsForUser = rawUserArtistData.map(_.split( )). filter { case Array(user,_,_) = user.toInt == 2093760 } val existingProducts = rawArtistsForUser.map { case Array(_,artist,_) = artist.toInt }. collect().toSet artistByID.filter { case (id, name) = existingProducts.contains(id) }.values.collect().foreach(println) ... David Gray Blackalicious Jurassic 5 The Saw Doctors Xzibit Wyszukanie wierszy danych z identyfikatorem użytkownika 2093760. Zebranie unikatowych identyfikatorów wykonawców. Odfiltrowanie danych o wykonawcach, wyszukanie oraz wyświetlenie ich nazw. Utwory wyszukanych wykonawców tworzą mieszankę gatunków mainstream pop i hip-hop. Jesteś fanem zespołu Jurassic 5? Pamiętaj, że dane pochodzą z roku 2005. Jeżeli Cię to interesuje, grupa Saw Doctors jest bardzo znanym zespołem rockowym z Irlandii. Teraz możemy wykonać coś w rodzaju zarekomendowania pięciu wykonawców wybranemu użytkownikowi: 56 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę val recommendations = model.recommendProducts(2093760, 5) recommendations.foreach(println) ... Rating(2093760,1300642,0.02833118412903932) Rating(2093760,2814,0.027832682960168387) Rating(2093760,1037970,0.02726611004625264) Rating(2093760,1001819,0.02716011293509426) Rating(2093760,4605,0.027118271894797333) Wynik składa się z obiektów Rating zawierających (taki sam) identyfikator użytkownika, identyfi- kator wykonawcy i liczbę. Liczba ta jest wartością pola rating, ale nie oznacza szacowanej oceny. W tego typu algorytmie ALS jest to enigmatyczna wartość z przedziału od 0 do 1. Im większa jest ta liczba, tym lepsza rekomendacja. Nie jest to współczynnik prawdopodobieństwa, ale w zależności od tego, jak bliska jest ta wartość liczbie 1 lub 0, można ocenić, czy dany użytkownik zainteresuje się danym wykonawcą, czy nie. Po wybraniu z rekomendacji identyfikatorów wykonawców możemy w podobny sposób wyszukać ich nazwy: val recommendedProductIDs = recommendations.map(_.product).toSet artistByID.filter { case (id, name) = recommendedProductIDs.contains(id) }.values.collect().foreach(println) ... Green Day Linkin Park Metallica My Chemical Romance System of a Down Wynik jest mieszanką gatunków pop punk i metal. Na pierwszy rzut oka rekomendacje te nie wyglą- dają zachęcająco. Są to wprawdzie popularni wykonawcy, jednak raczej nie odpowiadają prefe- rencjom wybranego użytkownika. Ocena jakości rekomendacji Oczywiście, opisana wyżej ocena wyników jest subiektywna. Trudno innej osobie ocenić, jak tra- fione są przygotowane rekomendacje. Co więcej, niemożliwe jest dokonanie przez człowieka oceny nawet niewielkiej próbki wyników. Rozsądne zatem jest przyjęcie założenia, że użytkownicy wybierają utwory popularnych wykonaw- ców, a unikają utworów wykonawców niepopularnych. Zatem utwory wybierane przez użytkow- nika dają częściową ocenę, jak bardzo „dobra” lub „zła” jest rekomendacja danego wykonawcy. Jest to problematyczne założenie, ale chyba najlepsze, jakie można przyjąć, nie dysponując innymi danymi. Na przykład, użytkownik o identyfikatorze 2093760 może lubić wielu innych wykonawców niż pięciu wyżej wymienionych, a spośród 1,7 miliona pozostałych wykonawców, których utworów dany użytkownik nie zna, kilku może być dla niego interesujących. Nie wszystkie rekomendacje są zatem „złe”. Ocena jakości rekomendacji (cid:95) 57 Poleć książkęKup książkę A gdyby tak oceniać algorytm rekomendacyjny według jego możliwości umieszczania dobrych wykonawców na wysokich pozycjach listy rekomendacji? Jest to jeden z podstawowych parame- trów, który można zastosować w systemach oceniających, takich jak systemy rekomendacyjne. Problem jednak polega na tym, że „dobry” wykonawca jest zdefiniowany jako „ten, którego utworów dany użytkownik słuchał”, a system rekomendacyjny już otrzymał te informacje. Może on zatem po prostu zwrócić listę wykonawców, których użytkownik już słuchał, i rekomendacje te będą idealne. Nie będą to jednak przydatne wyniki, przede wszystkim dlatego, że zadaniem systemu rekomenda- cyjnego jest wyszukiwanie wykonawców, których użytkownik jeszcze nie słuchał. Aby wyniki były użyteczne, niektóre dane o wybieranych wykonawcach muszą zostać wyodrębnione i ukryte przed algorytmem ALS tworzącym model. Później wyodrębnione dane można potraktować jako zbiór trafionych rekomendacji dla każdego użytkownika, ale nie mogą one być wcześniej prze- kazane systemowi rekomendacyjnemu. System rekomendacyjny powinien ocenić wszystkich wyko- nawców w modelu, po czym oceny te należy porównać z ocenami wyodrębnionych wykonawców. W idealnym przypadku system powinien wybrać wykonawców znajdujących się na początku listy. Następnie można ocenić system rekomendacyjny, porównując rankingi wszystkich wyodrębnio- nych wykonawców z pozostałymi. (W praktyce polega to na sprawdzaniu jedynie próbki takich par wartości, ponieważ potencjalnie może ich być bardzo dużo). Odsetek par, w których wyodrębnieni wykonawcy zajmują wysokie pozycje, stanowi ocenę rekomendacji. Wartość 1,0 oznacza najlepszą, a 0,0 najgorszą ocenę. Wartość 0,5 jest oczekiwaną średnią wartością ocen losowo wybranych wykonawców. Opisana metryka jest bezpośrednio związana z koncepcją pozyskiwania informacji, zwaną krzywą ROC (ang. Receiver Operating Characteristic, charakterystyka operacyjna odbiornika — http://en. wikipedia.org/wiki/Receiver_operating_characteristic). Metryka opisana w poprzednim akapicie odpowiada polu poniżej krzywej ROC. Jest to tzw. pole AUC (ang. Area Under the Curve, obszar pod krzywą), który można interpretować jako prawdopodobieństwo, że losowo wybrana dobra reko- mendacja będzie znajdowała się na wyższej pozycji niż losowo wybrana zła rekomendacja. Metryka AUC jest również wykorzystywana do oceny systemów klasyfikujących. Jest ona zaim- plementowana w bibliotece MLlib w klasie BinaryClassificationMetrics, zawierającej odpowiednie metody. Na potrzeby systemu rekomendacyjnego wyliczymy wartość AUC dla każdego użytkownika i uśrednimy wyniki. Wynikowa wartość będzie nieco inna. Nazwijmy ją „średnią wartością AUC”. Inne metryki, właściwe dla systemu pozycjonującego wartości, są zaimplementowane w klasie RankingMetrics. Są to: precyzja (ang. precision), powtórzenie (ang. recall) i średnia precyzja (ang. mean average precision, MAP). Metryka MAP jest często stosowana i lepiej opisuje jakość najlepszych rekomendacji. Jednak w naszym przypadku będziemy stosować metrykę AUC, ponieważ stanowi ona ogólną ocenę jakości wyników całego modelu. W rzeczywistości proces wyodrębniania pewnych danych w celu wybrania odpowiedniego modelu i oceny jego dokładności jest powszechnie stosowaną praktyką we wszystkich algorytmach uczenia maszynowego. Zazwyczaj dane są dzielone na trzy podzbiory: ćwiczebny, sprawdzający krzyżowy (ang. cross-validation, CV) i testowy. Dla uproszczenia, w naszym przykładzie zostaną wykorzystane tylko dwa podzbiory: ćwiczebny i sprawdzający. Wystarczą one do wybrania modelu. W rozdziale 5. metoda ta zostanie rozszerzona o wykorzystanie podzbioru testowego. 58 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Obliczenie metryki AUC Implementacja funkcji obliczającej metrykę AUC znajduje się w kodzie źródłowym dołączonym do książki. Kod jest skomplikowany i nie prezentujemy go tutaj, jednak niektóre jego szczegóły są opisane w komentarzach. Argumentami funkcji jest podzbiór sprawdzający „dobrych”, czyli „tra- fionych” wykonawców dla każdego użytkownika oraz funkcja prognozująca. Funkcja ta przekłada każdą parę wartości użytkownik – wykonawca na obiekt typu Rating zawierający informacje o użytkowniku, wykonawcy i prognozowaną ocenę, której wysoka wartość oznacza wyższą pozycję w rekomendowanej liście wykonawców. Aby użyć tej funkcji, należy dane wejściowe podzielić na podzbiory ćwiczebny i sprawdzający. Model ALS będzie tworzony tylko na podstawie podzbioru ćwiczebnego, natomiast podzbiór sprawdzający zostanie użyty do oceny modelu. W tym przypadku 90 danych będzie stanowiło podzbiór ćwiczebny, a pozostałe 10 podzbiór sprawdzający: import org.apache.spark.rdd._ def areaUnderCurve( positiveData: RDD[Rating], bAllItemIDs: Broadcast[Array[Int]], predictFunction: (RDD[(Int,Int)] = RDD[Rating])) = { ... } val allData = buildRatings(rawUserArtistData, bArtistAlias) val Array(trainData, cvData) = allData.randomSplit(Array(0.9, 0.1)) trainData.cache() cvData.cache() val allItemIDs = allData.map(_.product).distinct().collect() val bAllItemIDs = sc.broadcast(allItemIDs) val model = ALS.trainImplicit(trainData, 10, 5, 0.01, 1.0) val auc = areaUnderCurve(cvData, bAllItemIDs, model.predict) Funkcja zdefiniowana w załączonym kodzie źródłowym. Usunięcie duplikatów danych i zebranie ich na komputerze sterującym. Zwróć uwagę, że jednym z argumentów funkcji areaUnderCurve() jest inna funkcja. W tym przy- padku jest nią metoda predict() klasy MatrixFactorizationModel, ale za chwilę zostanie zamieniona na inną, alternatywną funkcję. Zwrócony wynik jest równy ok. 0,96. Czy to dobry rezultat? Oczywiście tak — jest to liczba większa niż 0,5, czyli oczekiwana średnia wartości losowych rekomendacji. Wynik jest bliski liczbie 1,0, czyli maksymalnej ocenie. Ogólnie przyjmuje się, że wartość AUC większa niż 0,9 jest wysoką oceną. Opisana ocena może być ponownie wyliczona z innym ćwiczebnym podzbiorem 90 danych. Wynikowa średnia wartość AUC może być lepszym oszacowaniem skuteczności algorytmu w przypadku użytych danych. W rzeczywistości powszechnie stosowaną praktyką jest dzielenie danych na k podzbiorów podobnej wielkości, wybranie k – 1 podzbiorów do utworzenia jednego podzbioru ćwiczebnego i weryfikacja wyniku na podstawie pozostałego podzbioru. Proces ten powtarza się k razy, każdorazowo wybierając inne podzbiory. Metoda ta jest nazywana k-krotnym Obliczenie metryki AUC (cid:95) 59 Poleć książkęKup książkę sprawdzianem krzyżowym (http//pl.wikipedia.org/wiki/Sprawdzian_krzyżowy). Dla uproszczenia nie jest ona zaimplementowana w prezentowanych przykładach, jednak niektóre jej elementy są dostępne w bibliotece MLlib w funkcji pomocniczej MLUtils.kFold(). Przydatne jest ocenienie skuteczności opisanego algorytmu z zastosowaniem prostszego podejścia. Rozważmy dla przykładu rekomendację wykonawców najczęściej słuchanych przez wszystkich użytkowników w ogóle. Nie jest to spersonalizowane podejście, ale jest proste i prawdopodobnie skuteczne. Zdefiniujmy prostą funkcję prognozującą i wyliczmy wartość AUC: def predictMostListened( sc: SparkContext, train: RDD[Rating])(allData: RDD[(Int,Int)]) = { val bListenCount = sc.broadcast( train.map(r = (r.product, r.rating)). reduceByKey(_ + _).collectAsMap() ) allData.map { case (user, product) = Rating( user, product, bListenCount.value.getOrElse(product, 0.0) ) } } val auc = areaUnderCurve( cvData, bAllItemIDs, predictMostListened(sc, trainData)) Powyższy kod jest kolejnym interesującym przykładem składni języka Scala, gdzie zdefiniowana jest funkcja przyjmująca dwie listy argumentów. Wywołanie tej funkcji i przekazanie jej pierwszych dwóch argumentów powoduje utworzenie częściowo stosowanej funkcji, która sama przyjmuje argu- ment (allData) w celu zwrócenia prognozowanych wyników. Wynikiem funkcji predictMostListened (cid:180)(sc,trainData) jest inna funkcja. W tym przypadku wynik jest równy 0,93. Oznacza to, że zgodnie z przyjętą metryką niespersona- lizowane rekomendacje są bardzo trafione. Miło jest widzieć, że zbudowany model jest lepszy niż użyty w tej prostej metodzie. Czy może być jeszcze lepszy? Dobór wartości hiperparametrów Do tej pory wartości hiperparametrów wykorzystanych do zbudowania modelu opartego na klasie MatrixFactorizationModel zostały dobrane bez żadnych wyjaśnień. Nie są one wyliczane w ramach algorytmu i muszą być podane w kodzie wywołującym klasę. Argumenty metody ALS.trainImplicit() były następujące: rank = 10 Liczba ukrytych zmiennych modelu, czyli liczba k oznaczająca liczbę kolumn w macierzach użytkownik – cecha i produkt – cecha. W nietrywialnych przypadkach jest to również rząd tych macierzy. 60 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę iterations = 5 Liczba iteracji wykonywanych podczas faktoryzacji macierzy. Wykonanie dużej liczby iteracji trwa dłużej, ale skutkuje lepszą faktoryzacją. lambda = 0,01 Parametr standardowego nadmiernego dopasowania. Duża wartość ogranicza dopasowanie, jednak może pogorszyć dokładność faktoryzacji. alpha = 1,0 Argument wpływający na względną wagę obserwowanych i nieobserwowanych interakcji użyt- kownik – produkt podczas faktoryzacji. Argumenty rank, lambda i alpha można uznać za hiperparametry modelu. (Argument iterations stanowi raczej ograniczenie ilości zasobów zajmowanych podczas faktoryzacji). Nie są to wartości, które są umieszczane w macierzach w klasie MatrixFactorizationModel — są to po prostu jej para- metry, określane w algorytmie. Te hiperparametry są stosowane zamiast parametrów procesu two- rzącego model. Wartości użyte w powyższym opisie niekoniecznie są optymalne. Dobór odpowiednich wartości hiperparametrów jest najczęściej spotykanym problemem w algorytmach uczenia maszynowego. Najbardziej podstawowym sposobem doboru wartości jest testowanie różnych ich kombinacji i ocena metryki uzyskiwanej dla każdej z nich, a na koniec wybranie kombinacji, która daje w wyniku najlepszą metrykę. W poniższym przykładzie sprawdzanych jest osiem możliwych kombinacji dla następujących wartości argumentów: rank = 10 i 50, lambda = 1,0 i 0,0001 oraz alpha = 1,0 i 40,0. Wartości te również zostały przyjęte odgórnie, ale dobrane tak, aby obejmowały szeroki zakres wartości para- metrów. Wyniki obliczeń zostały wyświetlone według malejącej wartości AUC: val evaluations = for (rank - Array(10, 50); lambda - Array(1.0, 0.0001); alpha - Array(1.0, 40.0)) yield { val model = ALS.trainImplicit(trainData, rank, 10, lambda, alpha) val auc = areaUnderCurve(cvData, bAllItemIDs, model.predict) ((rank, lambda, alpha), auc) } evaluations.sortBy(_._2).reverse.foreach(println) ... ((50,1.0,40.0),0.9776687571356233) ((50,1.0E-4,40.0),0.9767551668703566) ((10,1.0E-4,40.0),0.9761931539712336) ((10,1.0,40.0),0.976154587705189) ((10,1.0,1.0),0.9683921981896727) ((50,1.0,1.0),0.9670901331816745) ((10,1.0E-4,1.0),0.9637196892517722) ((50,1.0E-4,1.0),0.9543377999707536) Potrójnie zagnieżdżona pętla. Sortowanie wyników malejąco według drugiej wartości (AUC) i ich wyświetlenie. Dobór wartości hiperparametrów (cid:95) 61 Poleć książkęKup książkę Przedstawiona tu składnia jest przykładem tworzenia zagnieżdżonych pętli w języku Scala. Jest to pętla oparta na zmiennej alpha, zagnieżdżona w pętli opartej na zmiennej lambda, zagnieżdżonej w pętli opartej na zmiennej rank. Interesujące jest to, że wartość 40 parametru alpha wydaje się lepsza niż 1. (Co ciekawe, wartość 40 została zaproponowana jako domyślna w jednej z wspomnianych wcześniej publikacji poświę- conych algorytmowi ALS). Efekt ten można traktować jako wskazówkę, że model jest skuteczniejszy, gdy bardziej koncentruje się na utworach, które użytkownik już słyszał, niż na tych, których jeszcze nie zna. Nieco lepiej wyglądają również wyższe wartości argumentu lambda. Wydaje się, że model jest dość wrażliwy na nadmierne dopasowanie, więc potrzebuje wyższych wartości tego argumentu w celu ograniczenia zbyt dokładnego dopasowania na podstawie nielicznych danych dla każdego użyt- kownika. Nadmierne dopasowanie zostanie omówione dokładniej w rozdziale 4. Liczba cech raczej nie robi większej różnicy. Wartość 50 pojawia się w kombinacjach argumentów dających zarówno wysokie, jak i niskie oceny, aczkolwiek bezwzględne wartości tych ocen nie zmieniają się znacząco. Może to oznaczać, że właściwa liczba cech jest w rzeczywistości większa niż 50 i użyte tu wartości są raczej zbyt małe. Oczywiście, opisany proces można powtarzać dla innych zakresów i większej liczby wartości. Jest to sposób doboru wartości hiperparametrów metodą brutalnej siły. Jednak w praktyce, gdy klastry obejmujące terabajty pamięci i setki rdzeni procesorów nie są rzadkością, a platformy takie jak Spark mogą wykonywać zadania równolegle i wykorzystywać dostępną pamięć, sposób ten staje się całkiem realny do zastosowania. Zrozumienie znaczenia hiperparametrów nie jest bezwzględnie wymagane, aczkolwiek pomocna jest znajomość ich normalnych zakresów wartości, aby przeszukiwana przestrzeń parametrów nie była zbyt duża ani zbyt mała. Przygotowanie rekomendacji Jakie rekomendacje przygotuje nowy model dla użytkownika 2093760, gdy użyjemy najlepszej kombinacji hiperparametrów? 50 Cent Eminem Green Day U2 [unknown] Podobnie rekomendacja dwóch wykonawców gatunku hip-hop ma jakiś sens. Wynik [unknown] (nieznany) oczywiście nie oznacza wykonawcy. Po sprawdzeniu oryginalnych danych okazuje się, że wartość ta pojawia się 429 447 razy, co plasuje ją niemal w pierwszej setce wykonawców. Jest to domyślna wartość przyjmowana w przypadku, gdy nie ma danych o wykonawcy odsłuchanego utworu. Jest ona prawdopodobnie wysyłana przez jakąś aplikację serwisu. To nie jest przydatna informacja i należy ją usunąć z danych przed kolejną analizą. Jest to przykład, jak iteracyjna jest analiza danych w praktyce, gdy na każdym etapie odkrywane są nowe cechy danych. 62 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Opisany model może być użyty do przygotowywania rekomendacji dla wszystkich użytkowników. W zależności od ilości danych i wydajności klastra można utworzyć proces, który będzie przeliczał model i przygotowywał rekomendacje co godzinę lub częściej. Jednak obecnie implementacja algorytmu ALS w bibliotece MLlib systemu Spark nie umożliwia przygotowywania rekomendacji dla wszystkich użytkowników. Można to robić dla każdego użyt- kownika z osobna, ale wykonanie krótkotrwałego rozproszonego zadania trwa kilka sekund. Sposób ten może być odpowiedni w przypadku, gdy trzeba szybko przygotowywać rekomendacje dla nie- wielkiej grupy użytkowników. Poniższy kod przygotowuje i wyświetla rekomendacje dla 100 wybra- nych użytkowników: val someUsers = allData.map(_.user).distinct().take(100) val someRecommendations = someUsers.map(userID = model.recommendProducts(userID, 5)) someRecommendations.map( recs = recs.head.user + - + recs.map(_.product).mkString( , ) ).foreach(println) Skopiowanie danych 100 (różnych) użytkowników na komputer sterujący. Metoda map() jest tu wykonywana lokalnie. Metoda mkString() tworzy ciąg znaków złożony z elementów kolekcji rozdzielonych separatorami. W tym przypadku rekomendacje są po prostu wyświetlane. Równie łatwo mogą być zapisane w zewnętrznym systemie, na przykład bazie HBase, umożliwiającej szybkie przeszukiwanie danych. Co ciekawe, opisany proces można wykorzystać do rekomendowania użytkowników wykonawcom. Można w ten sposób uzyskać odpowiedź na pytanie: „Którzy ze 100 użytkowników mogą być najbardziej zainteresowani nowym albumem wykonawcy X?”. W tym celu podczas przygotowy- wania danych wejściowych należy jedynie zamienić miejscami pola z identyfikatorami użytkowni- ków i wykonawców: rawUserArtistData.map { line = ... val userID = tokens(1).toInt val artistID = tokens(0).toInt ... } Odczytanie identyfikatora wykonawcy jako „użytkownika”. Odczytanie identyfikatora użytkownika jako „wykonawcy”. Dalsze kroki Oczywiście, można spędzić jeszcze wiele czasu na regulacji parametrów modelu oraz wyszukiwaniu i poprawianiu anomalii w danych wejściowych, takich jak wykonawca [unknown]. Na przykład po krótkiej analizie liczby odtworzeń utworów okazuje się, że użytkownik 2064012 słuchał wykonawcy 4468 niesamowitą liczbę 439 771 razy! Wykonawca ten to nieszczególnie popu- larny zespół „System of a Down”, grający muzykę z gatunku metalu alternatywnego. Wykonawca ten pojawiał się we wcześniejszych rekomendacjach. Przy założeniu, że utwór trwa średnio 4 minuty, Dalsze kroki (cid:95) 63 Poleć książkęKup książkę oznacza to, że hity takie jak „Chop Suey!” czy „B.Y.O.B.” były w sumie odtwarzane przez ponad 33 lata! Ponieważ zespół ten zaczął nagrywać płyty w roku 1998, w celu osiągnięcia powyższego wyniku cztery lub pięć utworów musiałoby być jednocześnie odtwarzanych przez 7 lat. To widocz- nie jest spam albo błąd w danych. Jest to kolejny przykład problemów, jakie trzeba rozwiązywać w praktyce. Algorytm ALS nie jest jedynym algorytmem rekomendacyjnym. Obecnie jest to jedyny algorytm zaimplementowany w bibliotece MLlib systemu Spark. Jednak biblioteka ta obsługuje odmianę tego algorytmu, wykorzystującą jawne dane. Korzysta się z niej w bardzo podobny sposób, z tym wyjątkiem, że model tworzy się za pomocą metody ALS.train(). Algorytm ten jest odpowiedni dla danych opartych na rankingach, a nie na liczbach odsłuchań. Takimi danymi są na przykład oceny w skali od 1 do 5 wykonawców wystawiane przez użytkowników. Wynikowe pole rating obiektu Rating zwracane przez różne metody rekomendacyjne zawiera szacowaną ocenę. W miarę upływu czasu w bibliotece MLlib lub innych będą pojawiały się również inne algorytmy rekomendacyjne. W praktyce systemy rekomendacyjne często muszą przygotowywać dane w czasie rzeczywistym, ponieważ są stosowane w systemach sprzedaży internetowej, gdzie trzeba przygotowywać rekomen- dacje w czasie, gdy użytkownik przegląda strony z produktami. Jak wspomniałem wcześniej, wstępne przygotowanie i zapisanie rekomendacji w systemie NoSQL jest dobrym sposobem przygotowy- wania rekomendacji na szeroką skalę. Jednym z mankamentów tej metody jest konieczność wstępnego przygotowania rekomendacji dla wszystkich użytkowników, którzy będą ich wkrótce potrzebować, a może to być potencjalnie każdy użytkownik. Na przykład jeżeli stronę odwiedza dziennie tylko 10 000 spośród miliona użytkowników, wstępne przygotowywanie rekomendacji dla miliona użyt- kowników każdego dnia stanowi w 99 stracony czas. Lepiej byłoby przygotowywać rekomendacje na bieżąco, w miarę potrzeb. W tym przykładzie do przygotowania rekomendacji dla jednego użytkownika wykorzystywaliśmy klasę MatrixFactori (cid:180)zationModel i była to operacja wykonywana w sposób rozproszony. Trwała ona kilka sekund, ponieważ powyższa klasa jest wyjątkowo duża i w rzeczywistości stanowi rozproszony zbiór danych. Tak nie jest w przypadku innych modeli, które mają znacznie lepsze osiągi. W projektach takich jak Oryx 2 podejmowane są próby implementacji algorytmów przygotowujących rekomendacje na żądanie w czasie rzeczywistym z wykorzystaniem bibliotek takich jak MLlib, poprzez implementację efektywnego przetwarzania danych zapisanych w pamięci. 64 (cid:95) Rozdział 3. Rekomendowanie muzyki i dane Audioscrobbler Poleć książkęKup książkę Skorowidz C cecha, feature, 66, 152 cechy kategorialne, 96 centroid, 87 CVaR, conditional value at risk, 168 czynnik rynkowy, 168, 170 transkrypcyjny, 197 D dane 1000 Genomes, 203 Audioscrobbler, 47 Covtype, 70 dotyczące kursów taksówek, 154 ENCODE, 197 genomiczne, 190 GeoJSON, 152 geoprzestrzenne, 148, 158 KDD Cup, 87 RDD, 37, 39 temporalne, 148 DBSCAN, 100 dekompozycja QR, 51 dobór wartości k, 90 domknięcie, closure, 55 domniemanie typu, 27 drzewo decyzyjne, 65, 68, 72–78 A agregowanie danych, 36 akcja collect, 30 count, 30 countByValue, 39 saveAsTextFile, 30 textFile, 30 akumulatory, 225 algorytm k-średnie++, 92 LSA, 113 naprzemiennych najmniejszych kwadratów, 50 rekomendacyjny, 49 algorytmy uzupełniania macierzy, 49 w bibliotece MLlib, 229 alokacja ukrytej zmiennej Dirichleta, 119 analiza danych, 21 danych genomicznych, 187 danych geoprzestrzennych, 158 danych neuroobrazowych, 205 dokumentów XML, 125 głównych składowych, 93 głównych znaczników, 126 przefiltrowanego grafu, 138 semantyczna, 101 sieci, 122 sieci współwystępowań, 121 składni, 104 tras taksówek, 147 wielkich zbiorów, 13 anomalie, 86 w ruchu sieciowym, 85 Apache Avro, 188 Apache Spark, 16 API Esri Geometry, 151 API Pipelines, 231 aplikacja Spark, 223 architektura pakietu PySpark, 208 atrybut krawędzi, 129 wierzchołka, 129 Audioscrobbler, 47 Avro, 228 B badacze danych, 22 baza ENCODE, 197 MEDLINE, 122 biblioteka GraphX, 121, 128 JodaTime, 149 matplotlib, 206 MLlib, 18, 108, 229, 231 nltk, 206 NScalaTime, 149 pakietu Thunder, 209 pandas, 206 statsmodels, 206 Scala, 125 błędne rekordy danych, 155 BSP, bulk-synchronous parallel, 141 budowa pakietu PySpark, 207 budowanie sesji, 162 Poleć książkęKup książkę E entropia, 77, 97 Esri Geometry API, 150 etap zadania, 224 etykieta, 97 F faktoryzacja macierzy, 50 filtrowanie, 33 kolaboratywne, 49 krawędzi, 135 format GeoJSON, 152 kolumnowy, 195 Parquet, 195 formaty plików, 228 fragment, 28 funkcja gęstości prawdopodobieństwa, PDF, 169 isHeader, 32 parse, 155 G geometria, geometry, 152 graf, 230 współwystępowań, 129 grupowanie, 88, 98 według k-średnich, 85, 86 H hiperparametry, 60 drzewa decyzyjnego, 76 histogram, 38 HPC, high-performance computing, 14 I indeks giełdowy, 168 NASDAQ, 170 informacje o genotypach, 203 o pakiecie PySpark, 206 zwrotne, 48 instalacja biblioteki pakietu Thunder, 209 instrument, 168 interaktywna analiza, 154 interfejs API Pipelines, 232 MLlib Pipelines API, 231 interpreter REPL, 26, 29 IPython Notebook, 208 iteracje, 15 J jądrowe szacowanie gęstości, 176 język definicji interfejsów, 188 Python, 206 Scala, 30 K KDD Cup, 87 k-krotne sprawdzanie krzyżowe, 60 klasa Graph
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Spark. Zaawansowana analiza danych
Autor:
, , ,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: