Krótkie wprowadzenie do metryki odległości i skalowania wielokierunkowego

Krótkie wprowadzenie do metryki odległości i skalowania wielokierunkowego

Na początek załóżmy, że mamy bardzo prosty zestaw danych, w którym czterech klientów oceniło sześć produktów. Każdy klient został poproszony o nadanie każdemu produktowi kciuka w górę lub kciuka w dół, ale mogłaby pominąć produkt, gdyby nie miała o nim opinii. Istnieje wiele systemów oceny, które działają w ten sposób, w tym Pandora i YouTube. Korzystając z tych danych ocen, chcielibyśmy zmierzyć, jak podobni są klienci. W tym prostym przykładzie skonfigurujemy macierz 4 × 6, w której wiersze reprezentują klientów, a kolumny reprezentują produkty. Wypełnimy tę macierz symulowanymi ocenami, losowo wybierając kciuk w górę (1), kciuk w dół (–1) lub pomiń (0) dla każdej pary klientów / produktów. Aby to zrobić, użyjemy funkcji próbki, która pozwoli nam losowo wybrać wartości z wektora c (1, 0, -1) sześć razy z zamianą. Ponieważ będziemy uzyskiwać dostęp do generatora liczb losowych R w celu symulacji danych, ważne jest, aby ustawić ziarno dla generatora na tę samą wartość, której użyliśmy, aby Twoje wyniki były równe tym pokazanym w przykładach. Aby ustawić ziarno, wywołamy set.seed (), aby ustawić ziarno generatora na numer 851982.

set.seed(851982)

ex.matrix <- matrix(sample(c(-1,0,1), 24, replace=TRUE), nrow=4, ncol=6)

row.names(ex.matrix) <- c(‘A’,’B’,’C’,’D’)

colnames(ex.matrix) <- c(‘P1′,’P2′,’P3′,’P4′,’P5′,’P6’)

Ten kod utworzy macierz 4 × 6, w której każdy wpis odpowiada ocenie użytkowników wierszy produktu kolumny. Używamy funkcji row.names i colnames tylko po to, aby zachować przejrzystość w tym przykładzie: klienci A – D i produkty 1–6. Kiedy sprawdzamy matrycę, możemy dokładnie zobaczyć, jak wygląda nasz zestaw danych zabawek. Na przykład klient A dał kciuki w dół produktom 2 i 3 i nie ocenił żadnych innych produktów. Z drugiej strony klient B kciuk w dół do produktu 1, ale kciuk w górę do produktów 3, 4 i 5. Teraz musimy użyć tych różnic, aby wygenerować wskaźnik odległości między wszystkimi klientami.

ex.matrix

P1 P2 P3 P4 P5 P6

A 0 -1 0 -1 0 0

B -1 0 1 1 1 0

C 0 0 0 1 -1 1

D 1 0 1 -1 0 0

Pierwszym krokiem do zebrania tych danych i wygenerowania metryki odległości jest podsumowanie ocen klientów dla każdego produktu, ponieważ odnoszą się one do wszystkich innych klientów, to znaczy nie tylko do produktów w obecnej formie. Innym sposobem myślenia o tym jest to, że musimy przekonwertować macierz N-by-M na macierz N-by-N, w której elementy nowej macierzy zapewniają podsumowanie relacji między użytkownikami na podstawie oceny produktu. Jednym ze sposobów jest pomnożenie naszej macierzy przez jej transpozycję. Efektem tego mnożenia jest obliczenie korelacji między każdą parą kolumn w oryginalnej macierzy. Transponowanie i mnożenie macierzy jest czymś, czego można się nauczyć w pierwszych tygodniach zajęć z dyskretnej matematyki lub algebry liniowej. W zależności od temperamentu instruktora być może staranne wykonanie tych transformacji odbywało się ręcznie. Na szczęście R bardzo chętnie wykonuje te transformacje. Poniżej przedstawiamy krótkie wprowadzenie do operacji macierzowych i ich wykorzystania w konstruowaniu metryki odległości dla danych ocen. Jeśli znasz już operacje na macierzach, możesz pominąć ten rozdział. Transpozycja macierzy odwraca macierz, dzięki czemu wiersze stają się kolumnami, a kolumny – rzędami. Wizualnie transpozycja obraca matrycę o 90 stopni w prawo, a następnie odwraca ją w pionie. Na przykład w poprzednim bloku kodu widzimy naszą macierz oceny według klienta, np. Matrycę, ale w następnym bloku kodu używamy funkcji t do jej transpozycji. Mamy teraz macierz oceny według klienta:

t (ex.matrix)

A B C D

P1 0 -1 0 1

P2 -1 0 0 0

P3 0 1 0 1

P4 -1 1 1 -1

P5 0 1 -1 0

P6 0 0 1 0

Mnożenie macierzy jest nieco bardziej skomplikowane, ale wymaga jedynie podstawowej arytmetyki. Aby pomnożyć dwie macierze, zapętlamy rzędy pierwszej macierzy i kolumny drugiej macierzy. Dla każdej pary wiersz-kolumna mnożymy każdą parę wpisów i sumujemy je razem. Omówimy krótki przykład za sekundę, ale jest kilka ważnych rzeczy, o których należy pamiętać podczas mnożenia macierzy. Po pierwsze, ponieważ mnożymy elementy rzędu pierwszej macierzy przez elementy kolumny drugiej, wymiary te muszą się zgadzać w dwóch macierzach. W naszym przypadku my nie możemy po prostu pomnożyć np. macierzy przez macierz 2 × 2; jak zobaczymy, arytmetyka nie zadziałałaby. Jako taki, wynik mnożenia macierzy zawsze będzie miał taką samą liczbę wierszy jak pierwsza macierz i taką samą liczbę kolumn jak druga macierz.

Bezpośrednią konsekwencją tego jest to, że porządek ma znaczenie przy mnożeniu macierzy. W naszym przypadku chcemy macierzy, która podsumowuje różnice między klientami, dlatego mnożymy naszą macierz klient po przeglądzie przez jej transpozycję, aby uzyskać macierz klient po kliencie jako produkt. Gdybyśmy zrobili coś odwrotnego, tj. mnożąc macierz oceny według klienta, mielibyśmy różnice między recenzjami. Może to być interesujące w innej sytuacji, ale w tej chwili nie jest przydatne. Na rysunku 

omawiamy sposób mnożenia macierzy. W lewym górnym rogu znajduje się ex.matrix, a w prawym górnym rogu jest transpozycja. Są to dwie macierze, które zamierzamy pomnożyć i pomnożymy je w tej kolejności. Jako przykład procesu mnożenia wyróżniliśmy wiersz A w przykładowej macierzy i kolumnę B w jego transpozycji. Bezpośrednio pod macierzami pokazujemy dokładnie, jak przebiega arytmetyka mnożenia macierzy. Jest to tak proste, jak pobranie elementu wiersza pierwszej macierzy i pomnożenie go przez odpowiedni element kolumny z drugiej macierzy. Następnie bierzesz te produkty i sumujesz je. Jak widać, w wynikowej macierzy klient-klient element A, B wynosi –1, co jest wynikiem sumowania wiersz po kolumnie.

ex.mult <- ex.matrix %*% t(ex.matrix)

ex.mult

A B C D

A 2 -1 -1 1

B -1 4 0 -1

C -1 0 3 -1

D 1 -1 -1 3

Wprowadziliśmy również pewną notację R na powyższym rysunku, która jest powtarzana w poprzednim bloku kodu, który wytwarza mnożenie macierzy. W R używasz operatora% *% do mnożenia macierzy. Interpretacja nowej matrycy jest dość prosta. Ponieważ zastosowaliśmy schemat kodowania 1, -1 i 0, wartości nie przekątne podsumowują ich ogólną zgodność (wartość dodatnia) lub brak zgody (wartość ujemna) w odniesieniu do recenzji produktów, biorąc pod uwagę te produkty, które obaj ocenili, tj. Niezerowe wpisy . Im bardziej pozytywny jest element nie przekątny, tym więcej zgodności, i im bardziej negatywny, tym mniej zgodności. Ponieważ wpisy są losowe, rozbieżność wśród klientów jest bardzo niewielka, a żadna wartość nie przyjmuje wartości większej niż 1 lub mniejszej niż –1. Wartości ukośne przedstawiają po prostu liczbę produktów, które ocenił każdy użytkownik. Mamy teraz dość przydatne podsumowanie różnic między użytkownikami. Na przykład klienci A i D obrócili produkt 4 kciukiem w dół; jednak klient D lubił produkty 1 i 3, podczas gdy klient A ich nie sprawdzał. Tak więc dla produktów dla których mamy informacje, możemy powiedzieć, że są one podobne, a zatem mają wartość 1 odpowiadającą ich relacji. Niestety jest to bardzo ograniczające, ponieważ możemy jedynie powiedzieć coś o nakładaniu się. Wolelibyśmy metodę ekstrapolacji tych różnic na bogatszą reprezentację. W tym celu wykorzystamy koncepcję odległości euklidesowej w przestrzeni wielowymiarowej. W jednym, dwóch lub trzech wymiarach odległość euklidesowa jest formalizacją naszych intuicji dotyczących odległości. Aby obliczyć odległość euklidesową między dwoma punktami w przestrzeni, mierzymy najkrótszą bezpośrednią ścieżkę między nimi. W naszym przykładzie chcemy obliczyć odległość euklidesową wśród wszystkich klientów w oparciu o miary ogólnych podobieństw i różnic określonych przez mnożenie macierzy. Aby to zrobić, będziemy traktować oceny każdego klienta jako wektor. Aby porównać klienta A z klientem B, możemy odjąć wektory, wyrównać różnice, zsumować je, a następnie przyjąć pierwiastek kwadratowy. To daje nam odległość euklidesową między ocenami klienta A a ocenami klienta B.

Możemy to zrobić „ręcznie” w języku R, używając funkcji sum i sqrt. W poniższym bloku kodu pokazujemy to obliczenie dla klientów A i D, które wynosi około 2.236. Na szczęście obliczenie całej pary odległości między rzędami macierzy jest tak powszechną operacją, że R ma funkcję podstawową zwaną dist, która robi dokładnie to i zwraca macierz odległości, którą nazywamy macierzą odległości. Funkcja dist może wykorzystywać kilka różnych mierników odległości do utworzenia macierzy odległości, ale będziemy trzymać się odległości euklidesowej, która jest domyślna.

sqrt(sum((ex.mult[1,]-ex.mult[4,])^2))

[1] 2.236068

ex.dist <- dist(ex.mult)

ex.dist

A B C

B 6.244998

C 5.477226 5.000000

D 2.236068 6.782330 6.082763

Zmienna ex.dist przechowuje teraz naszą macierz odległości. Jak widać z tego bloku kodu, macierz ta jest w rzeczywistości tylko dolnym trójkątem całej macierzy odległości. Bardzo często pokazywany jest tylko dolny trójkąt dla macierzy odległości, ponieważ macierz odległości musi być symetryczna, ponieważ odległość między rzędem X i rzędem Y jest równa odległości między rzędem Y i rzędem X. Pokazanie górnego trójkąta macierzy odległości jest zatem zbędne i zasadniczo nie jest wykonywane. Możesz jednak zastąpić to ustawienie domyślne podczas wywoływania funkcji dist, ustawiając wartość górną = PRAWDA.

Jak widzimy na podstawie wartości w dolnym trójkącie np. odległości, klienci A i D są najbliżsi, a klienci D i B są najdalsi. Mamy teraz wyraźniejsze poczucie podobieństw wśród użytkowników na podstawie ich recenzji produktów; byłoby jednak lepiej, gdybyśmy mogli wizualnie wyczuć te różnice. W tym miejscu MDS można wykorzystać do stworzenia układu przestrzennego naszych klientów na podstawie właśnie obliczonych odległości. MDS to zestaw technik statystycznych używanych do wizualnego zobrazowania podobieństw i różnic z zestawu odległości. W przypadku klasycznego MDS, którego użyjemy w tym rozdziale, proces przyjmuje macierz odległości, która określa odległość między każdą parą punktów w naszym zbiorze danych i zwraca zestaw współrzędnych dla tych dwóch punktów, który aproksymuje te odległości. Powodem, dla którego musimy stworzyć przybliżenie, jest to, że znalezienie punktów w dwóch wymiarach, które są oddzielone określonym zestawem odległości, może nie być możliwe. Na przykład nie można znaleźć czterech punktów w dwóch wymiarach, wszystkie w odległości 1 od siebie. (Przekonać się o tej notatce, że trzy punkty, które wszystkie znajdują się w odległości 1 od siebie, są wierzchołkami trójkąta. Przekonaj się, że nie ma sposobu, aby znaleźć inny punkt, który jest w odległości 1 od wszystkich trzech wskazuje na ten trójkąt.) Klasyczny MDS wykorzystuje określone przybliżenie do macierzy odległości, a zatem jest kolejnym przykładem algorytmu optymalizacji wykorzystywanego do uczenia maszynowego. Oczywiście algorytm aproksymacji leżący u podstaw klasycznego MDS można zastosować w trzech lub czterech wymiarach, ale naszym celem jest uzyskanie reprezentacji naszych danych, która jest łatwa do wizualizacji.

We wszystkich przypadkach będziemy używać MDS do skalowania danych na dwa wymiary Jest to najczęstszy sposób korzystania z MDS, ponieważ to pozwala na bardzo proste wizualizacje danych na wykresie współrzędnych. Jednak użycie MDS do skalowania danych do wyższych wymiarów jest całkowicie uzasadnione. Na przykład trójwymiarowa wizualizacja może ujawnić różne poziomy grupowania obserwacji podczas przechodzenia punktów do trzeciego wymiaru.

Klasyczna procedura MDS jest częścią podstawowych funkcji R, takich jak cmdscale, a jej jedynym wymaganym wejściem jest macierz odległości, taka jak np. Odległość Domyślnie cmdscale obliczy MDS w dwóch wymiarach, ale można to ustawić za pomocą parametru k. Interesuje nas tylko skalowanie naszych danych odległości do dwóch wymiarów, więc w poniższym bloku kodu używamy ustawień domyślnych i wykreślamy wyniki przy użyciu podstawowej grafiki R.

ex.mds <- cmdscale(ex.dist)

plot(ex.mds, type=’n’)

text(ex.mds, c(‘A’,’B’,’C’,’D’))

Na rysunku 

widzimy, że klienci A i D grupują się razem w środku działki. Jednak klienci B i C wcale się nie klastrują. Na podstawie naszych danych widzimy, że klienci A i D mają nieco podobne upodobania, ale musielibyśmy uzyskać znacznie więcej danych i / lub klientów, zanim moglibyśmy mieć nadzieję na zrozumienie sposobu, w jaki klienci klastrują B i C. Ważne jest również, aby zauważyć, że chociaż możemy zobaczyć, jak skupiają się A i D, a także, jak B i C nie, nie możemy powiedzieć nic konkretnego o tym, jak interpretować te odległości. Oznacza to, że wiemy, że A i D są bardziej podobne ze względu na ich bliskość w płaszczyźnie współrzędnych, ale nie możemy używać liczb i odległości między nimi, aby zinterpretować, jak są podobni, ani jak różnią się od B lub C. Dokładna odległość numeryczna wytworzona przez MDS jest artefaktem algorytmu MDS i pozwala na bardzo ograniczoną interpretację merytoryczną. W poniższej sekcji omówimy studium przypadku podobne do przykładu z zabawką, którego właśnie użyliśmy, ale teraz wykorzystamy prawdziwe dane z głosowań imiennych od Senatu USA.

Dane te są znacznie większe niż zbiór danych o zabawkach i wykorzystamy je, aby pokazać, jak członkowie Senatu Stanów Zjednoczonych gromadzą się podczas chronologicznych kongresów. W takim przypadku wykorzystamy te zapisy głosowań imiennych w Senacie do wygenerowania naszego wskaźnika odległości. Następnie użyjemy MDS do skupienia senatorów w dwóch wymiarach

(I) : Funkcje ls() i rm()

Aby zobaczyć obiekty obecne w obszarze roboczym, użyj funkcji ls(). Wpisanie ls() w wierszu polecenia dla powyższego przykładu daje

> ls ()

[1] „abc” „bcd” „cde”

>,

które są trzema obiektami utworzonymi powyżej. Chociaż funkcje są szczegółowo omówione później, jedną z interesujących właściwości funkcji, o których należy tutaj wspomnieć, są argumenty wprowadzane przez użytkownika. Dwa z możliwych argumentów dla ls() to pattern i all.names. Pierwszy argument jest wprowadzany jako pattern = „ciąg”, gdzie „ciąg” jest dowolną częścią nazwy obiektu. Na przykład w powyższym obszarze roboczym wyszukiwanie obiektów zawierających bc w nazwie daje abc i bcd, czyli

> ls (pattern = “bc”)

[1] „abc” „bcd”

Argument pattern można zredukować do pat, jak w ls (pat = „bc”). Skrócenie argumentów funkcji jest własnością R. Wszystkie argumenty w R można sprowadzić do najkrótszej unikalnej postaci, ale zwykle są one podane w pełnej formie w instrukcjach. Drugi argument to all.names =, który może być równy TRUE lub FALS. Jeśli ustawiono na PRAWDA, argument all.names instruuje R, aby wyświetlić listę wszystkich plików w obszarze roboczym, w tym te, które zaczynają się kropką. FALS  jest wartością domyślną i nie trzeba jej wprowadzać. W powyższym przykładowym obszarze roboczym ustawienie all.names równe TRUE daje

> ls (all.n = T)

[1] „.commander.done” „.First” „.Random.seed” „.Traceback”

[5] „abc” „bcd” „cde”

.

[1] odnosi się do „.commander.done”, ponieważ „commander.done” jest pierwszym elementem wektora, a [5] odnosi się do „abc”, ponieważ „abc” jest piątym elementem wektora. W R, jeśli elementom wektora nie nadano nazwy, konwencją do wyświetlania elementów jest pokazanie indeksu pierwszego elementu w każdym wierszu linii wymienionych elementów. Funkcji rm () można użyć do usunięcia obiektów z obszaru roboczego. W przypadku rm () nazwy obiektów do usunięcia są umieszczane w nawiasach i oddzielane przecinkami. Na przykład,

rm (a, b, c)

usunie obiekty a, b i c. Aby usunąć wszystkie obiekty,

rm (list = ls ())

Działa

Aby uzyskać więcej informacji o ls () lub rm (), wpisz ?Ls lub ?Rm po znaku zachęty R.

MDS: Wizualne badanie podobieństwa amerykańskiego senatora

Grupowanie oparte na podobieństwie

Istnieje wiele sytuacji, w których możemy chcieć wiedzieć, jak podobni są członkowie grupy ludzi. Załóżmy na przykład, że byliśmy firmą zajmującą się marketingiem marek, która właśnie zakończyła badanie dotyczące potencjalnej nowej marki. W ankiecie pokazaliśmy grupie ludzi kilka cech marki i zapytaliśmy czy by mogli uszeregować markę według każdej z tych funkcji za pomocą pięciostopniowej skali. Zebraliśmy również wiele danych społeczno-ekonomicznych od badanych, takich jak wiek, płeć, rasa, kod pocztowy, w którym żyją, i ich przybliżony roczny dochód. Na podstawie tej ankiety chcemy zrozumieć, w jaki sposób marka przemawia do wszystkich zmiennych społeczno-ekonomicznych. Co najważniejsze, chcemy wiedzieć, czy marka ma szeroki oddźwięk. Alternatywnym sposobem myślenia o tym problemie jest sprawdzenie, czy osoby, które lubią większość cech marki, mają różnorodne cechy społeczno-ekonomiczne. Przydatnym sposobem na zrobienie tego byłoby zwizualizowanie sposobu grupowania respondentów ankiety. Następnie możemy użyć różnych wskazówek wizualnych, aby wskazać ich przynależność do różnych kategorii społeczno-ekonomicznych. Oznacza to, że chcielibyśmy zobaczyć dużą mieszankę między płcią, a także między rasami i rozwarstwieniem gospodarczym. Podobnie możemy wykorzystać tę wiedzę, aby zobaczyć, jak bliskie grupy grupują się w oparciu o atrakcyjność marki. Mogliśmy również zobaczyć, ile osób było w jednym klastrze w porównaniu do innych lub jak daleko były inne klastry. To może nam powiedzieć, jakie cechy marki kierować na różne kategorie społeczno-ekonomiczne. Podczas formułowania tych pytań używamy słów takich jak „blisko” i „daleko”, które mają nieodłączne pojęcie odległości. Dlatego, aby zwizualizować odległość między klastrami, potrzebujemy przestrzennej koncepcji gromadzenia się jednostek. Głównym celem tej części jest zrozumienie, jak używać pojęć odległości w zestawie obserwacji w celu zilustrowania ich podobieństw i odmienności. Będzie to wymagało zdefiniowania niektórych wskaźników odległości odpowiednich do rodzaju analizowanych danych. Na przykład w hipotetycznej sytuacji marketingu marki moglibyśmy wykorzystać porządkowy charakter skali ankiety, aby znaleźć odległości między respondentami w bardzo prosty sposób: po prostu obliczyć różnice bezwzględne. Nie wystarczy jednak obliczyć tylko te odległości. Wprowadzimy technikę zwaną skalowaniem wielowymiarowym (MDS) do grupowania obserwacji w oparciu o miarę odległości między obserwacjami. Zastosowanie MDS pozwoli nam zwizualizować nasze dane przy użyciu jedynie miary odległości między wszystkimi punktami. Najpierw wprowadzimy podstawowe zasady MDS, wykorzystując zabawkowy przykład ocen klientów dla produktów z symulowanymi danymi. Następnie przejdziemy do wykorzystywania rzeczywistych danych dotyczących głosowania imiennego w Senacie USA, aby pokazać, w jaki sposób jego członkowie grupują się na podstawie tych głosów.a

(I) : Przypisania i operatory

R działa z obiektami. Obiekty mogą obejmować wektory, macierze, funkcje, wyniki funkcji lub wiele innych rodzajów obiektów. Obiekty ułatwiają pracę z informacjami. Tu zajmiemy się przypisywaniem nazw do obiektów, wyświetlanie i usuwanie obiektów oraz operacjami na obiektach. Niektóre obiekty są dostarczane z pakietami w języku R. Inne obiekty są tworzone przez użytkownika. Obiekty tworzone przez użytkownika mają nazwy przypisywane przez użytkownika. Umiejętność tworzenia, wyświetlania i usuwania obiektów utworzonych przez użytkownika jest podstawowa dla R.

Rodzaje przypisania

Nazwy w R muszą zaczynać się od litery lub kropki, nie mogą zawierać przerw i mogą zawierać litery, cyfry, kropki i podkreślenia. Nazwy rozpoczynające się kropką są ukryte i są używane przez R do domyślnych ustawień uruchamiania, losowego materiału siewnego i innych podobnych rzeczy. Symbole indeksujące [], [[]], $ i @ mają specjalne znaczenie w odniesieniu do nazw R. R pierwotnie używał pięciu rodzajów przypisań, z których cztery są nadal aktualne. Cztery typy to

a <- b,

co przypisuje b do a,

a -> b,

która przypisuje a do b,

a << – b,

która przypisuje b do a i może być używana wewnątrz funkcji, aby doprowadzić przypisanie do poziomu obszaru roboczego, oraz

a – >> b,

która przypisuje a do b i przenosi przypisanie funkcji do poziomu obszaru roboczego.

Ostatnio programiści z R wprowadzili bardziej standardowy

a = b

który przypisuje b do a. Chociaż można użyć dowolnego rodzaju przypisania, użycie znaku równości jest najłatwiejsze do wpisania. Kiedy R wykonuje przypisanie, nazwa jest automatycznie zapisywana w obszarze roboczym. Pamiętaj, że żadne ostrzeżenie nie zostanie wyświetlone, jeśli przypisana nazwa już istnieje. Przypisanie zastąpi obiekt w obszarze roboczym przypisanym obiektem. R jest interesujące, ponieważ funkcję obiektu można przypisać do oryginalnego obiektu.

Na przykład,

a = 2 * a,

gdzie obiekt a jest zastępowany oryginałem razy dwa.

Przykład trzech rodzajów przypisania

Poniżej podano przykład niektórych rodzajów przypisań. Tworzone są trzy obiekty: abc, bcd i cde. Obiekty tworzy się, przypisując do nich sekwencje. Sekwencje są generowane po umieszczeniu dwukropka między dwiema liczbami całkowitymi, co tworzy sekwencję liczb całkowitych zaczynającą się od pierwszej liczby całkowitej i kończącą się na drugiej liczbie całkowitej. Aby pokazać, że obiekty faktycznie zawierają przypisaną sekwencję, zawartość trzech obiektów jest wyświetlona poniżej. Zauważ, że wprowadzenie nazwy obiektu po znaku zachęty R zawsze wyświetla zawartość obiektu. [1] odnosi się do pierwszego elementu obiektów.

> abc = 1:10

> abc

[1] 1 2 3 4 5 6 7 8 9 10

> bcd <- 11:20

> bcd

[1] 11 12 13 14 15 16 17 18 19 20

> 21:30 -> cde

> cde

[1] 21 22 23 24 25 26 27 28 29 30

Jak widać, operatory przypisania <- i = dają ten sam wynik. Operator przypisania -> działa w przeciwnym kierunku.

PCA: Budowanie indeksu rynku

Uczenie się bez nadzoru

Jak dotąd cała nasza praca z danymi opierała się na predykcjach: próbowaliśmy klasyfikować e-maile lub wyświetlenia stron internetowych, gdzie mieliśmy zestaw szkoleniowy przykładów, na które znaliśmy prawidłową odpowiedź. Jak wspomnieliśmy na początku , uczenie się na podstawie danych, gdy mamy próbkę szkoleniową z prawidłową odpowiedzią, nazywa się uczeniem nadzorowanym: znajdujemy strukturę w naszych danych za pomocą sygnału, który mówi nam, czy wykonujemy dobrą robotę odkrywanie prawdziwych wzorów. Ale często chcemy znaleźć strukturę bez odpowiedzi na temat tego, jak dobrze sobie radzimy; nazywamy to nauką bez nadzoru. Na przykład możemy chcieć wykonać redukcję wymiarów, co dzieje się, gdy zmniejszamy tabelę z dużą liczbą kolumn do tabeli z małą liczbą kolumn. Jeśli masz zbyt wiele kolumn, aby sobie z tym poradzić, to zmniejszenie wymiarów znacznie przyczynia się do zrozumienia zestawu danych. Chociaż wyraźnie tracisz informacje, gdy zamieniasz wiele kolumn na jedną kolumnę, zdobycie zrozumienia jest często cenne, szczególnie gdy eksplorujesz nowy zestaw danych. Jednym z miejsc, w których ten rodzaj zmniejszenia wymiarów jest szczególnie pomocny, jest przetwarzanie danych giełdowych. Na przykład możemy mieć dane, które wyglądają jak prawdziwe historyczne ceny pokazane poniżej dla 25 akcji w okresie od 2 stycznia 2002 r. Do 25 maja 2011 r.

Data : ADC : AFL:  … : UTR

2018-01-02 : 17,7 : 23,78 : … : 39,34

2018-01-03:  16,14 : 23,52 : …  : 39,49

… … … … …

2019-05-25 : 22,76 : 49,3 : … : 29,47

Chociaż pokazaliśmy tylko 3 kolumny, w rzeczywistości jest ich 25, co stanowi zdecydowanie zbyt wiele kolumn, aby można je było rozważnie rozważać. Chcemy stworzyć jedną kolumnę, która mówi nam, jak radzi sobie rynek każdego dnia, łącząc informacje w 25 kolumnach, do których mamy dostęp; nazwiemy tę pojedynczą kolumnę indeksem rynku. Jak więc możemy je zbudować? Najprostsze podejście nazywa się analizą głównych składników lub PCA. Główną ideą PCA jest stworzenie nowego zestawu 25 kolumn uporządkowanych na podstawie ilości surowych informacji w naszym zestawie danych. Pierwsza nowa kolumna, zwana pierwszym głównym składnikiem, lub po prostu głównym składnikiem w skrócie, często zawiera zdecydowaną większość struktury w całym zestawie danych. PCA jest szczególnie skuteczny, gdy wszystkie kolumny w naszym zestawie danych są silnie skorelowane. W takim przypadku można zastąpić skorelowane kolumny jedną kolumną, która pasuje do wzorca leżącego u podstaw korelacji między obiema kolumnami. Sprawdźmy więc, czy PCA będzie działać, sprawdzając, jak skorelowane są kolumny w naszym zestawie danych. Aby to zrobić, najpierw musimy załadować nasz nieprzetworzony zestaw danych do R:

prices <- read.csv(‘data/stock_prices.csv’)

prices[1,]

# Date Stock Close

#1 2011-05-25 DTE 51.12

Nasz nieprzetworzony zestaw danych nie jest w formacie, z którym chcielibyśmy pracować, dlatego musimy przeprowadzić wstępne przetwarzanie. Pierwszym krokiem jest przetłumaczenie wszystkich nieprzetworzonych znaczników danych w naszym zestawie danych na poprawnie zakodowane zmienne daty. Aby to zrobić, używamy pakietu lubridate z CRAN. Ten pakiet zawiera niezłą funkcję o nazwie ymd, która tłumaczy ciągi znaków w formacie rok-miesiąc-dzień na obiekty daty:

library(‘lubridate’)

prices <- transform(prices, Date = ymd(Date))

Po wykonaniu tej czynności możemy użyć funkcji rzutowania w bibliotece przekształcania, aby utworzyć macierz danych, taką jak tabela, którą widzieliśmy wcześniej w tym rozdziale. W tej tabeli wiersze będą dniami, a kolumny będą osobnymi zapasami. Robimy to w następujący sposób:

library(‘reshape’)

date.stock.matrix <- cast(prices, Date ~ Stock, value = ‘Close’)

Funkcja rzutowania pozwala określić, której kolumny należy użyć do zdefiniowania wierszy w macierzy wyjściowej po lewej stronie tyldy, a kolumny wyniku określa się po tyldie. Rzeczywiste wpisy w wyniku są określane za pomocą wartości. Po przeanalizowaniu wyników produkcji tej dużej matrycy zapasów daty zauważamy, że tam brakuje niektórych wpisów. Wracamy do zestawu danych cen, usuwamy brakujące wpisy, a następnie ponownie uruchamiamy przesyłanie:

prices <- subset(prices, Date != ymd(‘2002-02-01’))

prices <- subset(prices, Stock != ‘DDR’)

date.stock.matrix <- cast(prices, Date ~ Stock, value = ‘Close’)

Po usunięciu brakujących wpisów ponownie używamy rzutowania, aby utworzyć żądaną macierz. Po wykonaniu tej czynności możemy znaleźć korelacje między wszystkimi kolumnami liczbowymi w macierzy za pomocą funkcji cor. Po wykonaniu tej czynności przekształcamy macierz korelacji w pojedynczy wektor numeryczny i tworzymy wykres gęstości korelacji, aby uzyskać poczucie zarówno a) średniej korelacji, jak i b) częstotliwości, z jaką występują niskie korelacje:

cor.matrix <- cor(date.stock.matrix[,2:ncol(date.stock.matrix)])

correlations <- as.numeric(cor.matrix)

ggplot(data.frame(Correlation = correlations),

aes(x = Correlation, fill = 1)) +

geom_density() +

opts(legend.position = ‘none’)

Wynikowy wykres gęstości pokazano tu/

 Jak widzimy, zdecydowana większość korelacji jest dodatnia, więc PCA prawdopodobnie będzie dobrze działać na tym zestawie danych. Przekonawszy się, że możemy korzystać z PCA, jak to zrobić w R? Znowu jest to miejsce, w którym świeci R: całość PCA może być wykonana w jednym wierszu kodu. Używamy  funkcji princomp do uruchomienia PCA:

pca <- princomp(date.stock.matrix[,2:ncol(date.stock.matrix)])

Jeśli po prostu wpiszemy pca w R, zobaczymy krótkie podsumowanie głównych składników: Call:

princomp(x = date.stock.matrix[, 2:ncol(date.stock.matrix)])

Standard deviations:

Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 Comp.7

29.1001249 20.4403404 12.6726924 11.4636450 8.4963820 8.1969345 5.5438308

Comp.8 Comp.9 Comp.10 Comp.11 Comp.12 Comp.13 Comp.14

5.1300931 4.7786752 4.2575099 3.3050931 2.6197715 2.4986181 2.1746125

Comp.15 Comp.16 Comp.17 Comp.18 Comp.19 Comp.20 Comp.21

1.9469475 1.8706240 1.6984043 1.6344116 1.2327471 1.1280913 0.9877634

Comp.22 Comp.23 Comp.24

0.8583681 0.7390626 0.4347983

24 variables and 2366 observations.

W tym podsumowaniu odchylenia standardowe mówią nam, jak duża część wariancji w zestawie danych jest uwzględniana przez różne główne składniki. Pierwszy składnik, zwany Comp.1, stanowi 29% wariancji, podczas gdy następny składnik stanowi 20%. Do końca ostatni składnik, Comp.24, stanowi mniej niż 1% wariancji. Sugeruje to, że możemy się wiele dowiedzieć o naszych danych, patrząc tylko na pierwszy główny składnik.

Możemy zbadać pierwszy główny składnik bardziej szczegółowo, patrząc na jego obciążenia, które mówią nam, jak duży ciężar przypisuje każdej z kolumn. Otrzymujemy je poprzez wyodrębnienie elementu ładującego obiektu princomp przechowywanego w pca. Wyodrębnianie ładunków daje nam dużą macierz, która mówi nam, ile każda z 25 kolumn wkłada do każdego z głównych składników. Naprawdę interesuje nas tylko pierwszy główny składnik, więc wyciągamy pierwszą kolumnę ładunków pca:

principal.component <- ładunki pca $ [, 1]

Po wykonaniu tej czynności możemy zbadać wykres gęstości obciążeń, aby dowiedzieć się, jak powstaje pierwszy główny składnik:

loadings <- as.numeric(principal.component)

ggplot(data.frame(Loading = loadings),

aes(x = Loading, fill = 1)) +

geom_density() +

opts(legend.position = ‘none’)

Można to zobaczyć tu:

Wyniki są trochę podejrzane, ponieważ ładnie rozkładają się obciążenia, ale są w przeważającej mierze negatywne. Za chwilę przekonamy się, do czego to prowadzi; jest to naprawdę trywialna uciążliwość, którą naprawi pojedynczy wiersz kodu. Teraz, gdy mamy nasz główny składnik, możemy chcieć wygenerować nasze jednokolumnowe podsumowanie naszego zestawu danych. Możemy to zrobić za pomocą funkcji przewidywania:

market.index <- predict (pca) [, 1]

Jak możemy stwierdzić, czy te prognozy są dobre? Na szczęście jest to przypadek, w którym łatwo jest zdecydować, czy podoba nam się nasze wyniki, ponieważ istnieją znane indeksy rynkowe, z którymi możemy porównać nasze wyniki. W tym rozdziale wykorzystamy Indeks Dow Jones, który będziemy odnosić się do DJI. Ładujemy DJI do R w następujący sposób:

dji.prices <- read.csv (‘data / DJI.csv’)

dji.prices <- transform (dji.prices, Date = ymd (Date))

Ponieważ DJI działa o wiele dłużej niż chcemy, musimy wykonać pewne podzestawy, aby uzyskać tylko te daty, które nas interesują:

dji.prices <- subset (dji.prices, Date> ymd (‘2001-12-31’))

dji.prices <- subset (dji.prices, Date! = ymd (‘2002-02-01’))

Po wykonaniu tej czynności wyodrębniamy części DJI, którymi jesteśmy zainteresowani, czyli dzienne ceny zamknięcia i daty, w których zostały zarejestrowane. Ponieważ są one w odwrotnej kolejności niż nasz obecny zestaw danych, używamy funkcji rev, aby je odwrócić:

dji <- with (dji.prices, rev (Close))

daty <- with (prices dji, rev (data))

Teraz możemy wykonać proste wykresy graficzne, aby porównać nasz indeks rynku wygenerowany za pomocą PCA z DJI:

comparison <- data.frame (Data = daty, MarketIndex = market.index, DJI = dji)

ggplot (comparison, aes (x = MarketIndex, y = DJI)) +

geom_point () +

geom_smooth (method = ‘lm’, se = FALSE)

Ten pierwszy wykres pokazano tu

Jak widać, te negatywne ładunki, które wcześniej wydawały się podejrzane, okazały się prawdziwym źródłem problemów dla naszego zestawu danych: nasz indeks jest ujemnie skorelowany z DJI. Ale możemy to łatwo naprawić. Po prostu mnożymy nasz indeks przez -1, aby uzyskać indeks skorelowany we właściwym kierunku z DJI:

comparison <- transformation (comparison, MarketIndex = -1 * MarketIndex)

Teraz możemy ponownie wypróbować nasze porównanie:

ggplot (comparison, aes (x = MarketIndex, y = DJI)) +

geom_point () +

geom_smooth (method = ‘lm’, se = FALSE)

Jak widać na rysunku, naprawiliśmy kierunek naszego indeksu i wygląda na to, że bardzo dobrze pasuje do DJI.

Jedyne, czego brakuje, to dowiedzieć się, jak dobrze nasz indeks śledzi DJI w czasie. Możemy łatwo dokonać tego porównania. Po pierwsze, używamy funkcji stopu, aby uzyskać ramkę danych, z którą łatwo jest pracować w celu wizualizacji obu wskaźników jednocześnie. Następnie tworzymy wykres liniowy, w którym oś x jest datą, a oś y jest ceną każdego indeksu.

alt.comparison <- melt(comparison, id.vars = ‘Date’)

names(alt.comparison) <- c(‘Date’, ‘Index’, ‘Price’)

ggplot(alt.comparison, aes(x = Date, y = Price, group = Index, color = Index)) +

geom_point() +

geom_line()

Nasz pierwszy przebieg nie wygląda tak dobrze, ponieważ DJI przyjmuje bardzo wysokie wartości, podczas gdy nasz indeks przyjmuje bardzo małe wartości. Ale możemy to naprawić za pomocą skali, która ustawia oba wskaźniki na wspólnej skali:

comparison <- transform(comparisonMarketIndex = -scale(MarketIndex))

comparison <- transform(comparisonDJI = scale(DJI))

alt.comparison <- melt(comparison, id.vars = ‘Date’)

names(alt.comparison) <- c(‘Date’, ‘Index’, ‘Price’)

p <- ggplot(alt.comparison, aes(x = Date, y = Price, group = Index, color = Index)) +

geom_point() +

geom_line()

print(p)

Po wykonaniu tej czynności ponownie tworzymy wykres liniowy i sprawdzamy wyniki, które pokazano na rysunku.

Ta fabuła wydaje się, że nasz indeks rynku, który został stworzony w całości z PCA i nie wymagał żadnej wiedzy o rynku akcji, śledzi DJI niesamowicie dobrze.

Krótko mówiąc, PCA naprawdę stara się uzyskać obniżoną reprezentację cen akcji, która wygląda tak, jakbyśmy mogli stworzyć coś bardziej ostrożnego, zastanawiając się, jak wyrazić ogólne samopoczucie na giełdzie. Uważamy, że to niesamowite. Mam nadzieję, że ten przykład przekonuje cię, że PCA to potężne narzędzie do uproszczenia danych i że możesz naprawdę wiele zrobić, aby odkryć strukturę danych, nawet jeśli nie próbujesz czegoś przewidzieć. Jeśli interesuje Cię ten temat, zachęcamy do zapoznania się z niezależną analizą składników (ICA), która jest alternatywą dla PCA, która działa dobrze w niektórych okolicznościach, w których PCA się psuje.

(I) : Monit R

Ta część obejmuje monit R. Zaczyna się od opisów trzech części R: obiektów, operatorów i przypisań. Kontynuuje dyskusję na temat pracy z monitem R. Następnie podaje przykład wykonania obliczeń w wierszu polecenia R. W Windows i OS X, R działa w GUI: RGUI w Windows i R.app GUI w OS X. Zarówno RGUI, jak i R.app GUI otwierają konsolę R i uruchamiają z monitu R w Konsoli R. GUI są dostępne w systemie Linux, ale my zajmujemy się tylko uruchamianiem R z wiersza polecenia R w oknie terminala.

Trzy części R: Obiekty, operatory i przypisania

Istnieją w zasadzie trzy części R: obiekty, operatory i przypisania. Obiekty zawierają informacje i mogą być danymi, funkcjami lub wynikami funkcji. Obiekty zawsze mają nazwę. Użytkownicy tworzą niektóre obiekty, które są automatycznie zapisywane podczas tworzenia. Inne obiekty to funkcje i zestawy danych zawarte w pakietach R. Operatory manipulują obiektami, liczbami, łańcuchami i / lub zmiennymi logicznymi. Na przykład wpisanie a = 2 * b po znaku zachęty R spowoduje pomnożenie b przez dwa i przypisanie wyniku do a. Obiekty a i b są obiektami numerycznymi, a * jest operatorem mnożenia. Znak równości przypisuje dwa razy b do a. Przydziały przypisują wyrażenie do obiektu. Wyrażenia składają się z obiektów, liczb, zmiennych logicznych i / lub ciągów, które są obsługiwane przez operatory. Wyrażenia można oceniać z wiersza polecenia R bez przypisania. (Inne miejsca, w których występują zadania i operacje, mieszczą się w ramach funkcji i kontroli przepływu.)

Znak zachęty R.

Wszystkie w R wypływa z  monitu R. R jest zasadniczo działaniem funkcji i wykonywaniem obliczeń. Funkcje i obliczenia można uruchamiać po znaku zachęty R z przypisaniem do obiektu lub bez niego. Funkcje i obliczenia można również uruchamiać jako część funkcji, ale wszystko zaczyna się od znaku zachęty R. Używanie R z monitu R może początkowo wydawać się zniechęcające. R otwiera się za pomocą skryptu, a następnie samotny, nieco większy niż znak ( >) jest monitem R. Skrypt otwierający podaje numer wersji R i kilka innych informacji o programie, w tym fakt, że program działa bez gwarancji. R pamięta każdą linię wprowadzoną do programu, aż do określonej liczby linii. Bardzo przydatną stroną R jest to, że strzałki w górę i w dół na klawiaturze przechodzą przez linie. Musisz wprowadzić wyrażenie tylko raz. Korekty wyrażeń są łatwe do zrobienia bez ponownego wpisywania całego wyrażenia. Aby zamknąć R, wpisz q() po znaku zachęty R lub, w przypadku Windows i OS X, zamknij okno. R zamknie się z opcją zapisania obszaru roboczego. W systemie Linux, jeśli okno terminala zostanie zamknięte bez użycia q(), bieżący obszar roboczy zostanie utracony. Obszar roboczy składa się z dowolnych obiektów obecnych w R w momencie zamknięcia programu i bieżącej historii. Zamknięcie R bez zapisywania obszaru roboczego spowoduje powrót do obszaru roboczego obecnego w momencie rozpoczęcia sesji R.

Przykład obliczenia

Najprostsze użycie R to kalkulator. Poniższe obliczenia wykonano z monitu R. W obliczeniach nie ma przypisania, więc wynik jest zwracany na ekranie.

> (1 + 3 + 7) / 5

[1] 2.2

Pierwszy wiersz podaje wyrażenie do oceny, a drugi wiersz daje wynik. W drugim wierszu jest etykieta informująca użytkownika, że wynikiem jest pierwsza wartość zwrócona z wyrażenia. Wiele wyrażeń zwraca więcej niż jedną wartość. W trzecim wierszu pojawia się monit R, a R jest gotowy do kolejnego zadania.

Złamanie kodu jako optymalizacja

Wychodząc poza modele regresji, prawie każdy algorytm uczenia maszynowego może być postrzegany jako problem optymalizacji, w którym próbujemy zminimalizować pewną miarę błędu prognozowania. Ale czasami nasze parametry nie są zwykłymi liczbami, więc ocena funkcji błędu w jednym punkcie nie daje wystarczającej ilości informacji o pobliskich punktach, aby użyć optim. W przypadku tych problemów możemy użyć wyszukiwania siatki, ale istnieją inne podejścia, które działają lepiej niż wyszukiwanie siatki. Skoncentrujemy się na jednym podejściu, które jest dość intuicyjne i bardzo wydajne. Główną ideą tego nowego podejścia, które nazwiemy optymalizacją stochastyczną, jest nieznaczne losowe przejście przez zakres możliwych parametrów, ale upewnienie się, że pójdziemy w kierunkach, w których funkcja błędu raczej spada niż podnosi. To podejście wiąże się z wieloma popularnymi algorytmami optymalizacji, o których słyszałeś, w tym symulowanym wyżarzaniem, algorytmami genetycznymi i łańcuchem Markova Monte Carlo (MCMC). Konkretny algorytm, którego użyjemy, nazywa się metodą Metropolis; wersje metody Metropolis zasilają wiele nowoczesnych algorytmów uczenia maszynowego. Aby zilustrować metodę Metropolis, przejrzymy studium przypadku tej części: łamanie tajnych kodów. Algorytm, który zdefiniujemy, nie jest bardzo wydajnym systemem odszyfrowywania i nigdy nie byłby poważnie wykorzystywany w systemach produkcyjnych, ale jest to bardzo wyraźny przykład użycia metody Metropolis. Co ważne, jest to także przykład, w którym większość gotowych algorytmów optymalizacji, takich jak optim, nigdy nie mogłaby działać. Podajmy więc nasz problem: biorąc pod uwagę ciąg liter, które są szyfrowane przy użyciu szyfru zastępczego, w jaki sposób decydujemy o regule deszyfrującej, która daje nam oryginalny tekst? Jeśli nie znasz szyfrów zastępczych, są one najprostszym możliwym systemem szyfrowania, w którym zamieniasz każdą literę w niezaszyfrowanej wiadomości na stałą literę w zaszyfrowanej wiadomości. ROT132 jest prawdopodobnie najbardziej znanym przykładem, choć szyfr Cezara może być również znany. Szyfr Cezara to jeden bardzo prosty szyfr zastępczy, w którym każdą literę zamieniasz na alfabet: „a” staje się „b”, „b” zmienia się na „c”, a „c” na „d”. (Jeśli zastanawiasz się tutaj nad przypadkiem brzegowym: „z” staje się „a.”) Aby upewnić się, że jasne jest, jak pracować z szyframi w R, utwórzmy teraz szyfr Cezara, abyśmy mogli zobaczyć, jak go zaimplementować w R:

english.letters <- c(‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’,

‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’,

‘w’, ‘x’, ‘y’, ‘z’)

caesar.cipher <- list()

inverse.caesar.cipher <- list()

for (index in 1:length(english.letters))

{

caesar.cipher[[english.letters[index]]] <- english.letters[index %% 26 + 1]

inverse.caesar.cipher[[english.letters[index %% 26 + 1]]] <- english.letters[index]

}

print(caesar.cipher)

Teraz, gdy mamy zaimplementowany szyfr, zbudujmy niektóre funkcje, abyśmy mogli tłumaczyć ciąg znaków za pomocą szyfru:

apply.cipher.to.string <- function(string, cipher)

{

output <- ”

for (i in 1:nchar(string))

{

output <- paste(output, cipher[[substr(string, i, i)]], sep = ”)

}

return(output)

}

apply.cipher.to.text <- function(text, cipher)

{

  1. ROT13 zastępuje każdą literę literą 13 w dalszej części alfabetu. „A” zamienia się w „n”, „b” staje się „o” i tak dalej.

output <- c()

for (string in text)

{

output <- c(output, apply.cipher.to.string(string, cipher))

}

return(output)

}

apply.cipher.to.text(c(‘sample’, ‘text’), caesar.cipher)

Teraz mamy kilka podstawowych narzędzi do pracy z szyframi, więc zacznijmy myśleć o problemie z łamaniem kodów, które możemy napotkać. Podobnie jak w przypadku regresji liniowej rozwiążemy problem łamania szyfrów podstawienia w kilku częściach:

  1. Zdefiniuj miarę jakości proponowanej reguły deszyfrowania.
  2. Zdefiniuj algorytm proponowania nowych potencjalnych reguł deszyfrowania, który losowo modyfikuje wersje naszej obecnej najlepszej reguły.
  3. Zdefiniuj algorytm stopniowego przechodzenia w kierunku lepszych reguł deszyfrowania.

Aby zacząć myśleć o tym, jak zmierzyć regułę jakości odszyfrowywania, załóżmy, że otrzymałeś kawałek tekstu, o którym wiesz, że został zaszyfrowany przy użyciu szyfru zastępczego. Na przykład, jeśli Juliusz Cezar przesłał ci zaszyfrowaną wiadomość, może to wyglądać tak: „wfoj wjej wjdj”. Po odwróceniu szyfru Cezara tekst ten okaże się słynną frazą „veni vidi vici”. Teraz wyobraź sobie, że masz tylko zaszyfrowany tekst i gwarancję, że oryginalna wiadomość była w standardowym języku angielskim. Jak zabrałbyś się do dekodowania? Podejdziemy do rozwiązania tego problemu, mówiąc, że reguła jest dobra, jeśli zamienia zaszyfrowaną wiadomość w normalny angielski. Biorąc pod uwagę proponowaną regułę deszyfrowania, uruchomisz tę regułę na naszym zaszyfrowanym tekście i zobaczysz, czy wydruk jest realistyczny w języku angielskim. Wyobraź sobie na przykład, że zaproponowaliśmy dwie reguły deszyfrowania, A i B, których wyniki są następujące:

  • odszyfruj (T, A) = xgpk xkfk xkek
  • odszyfruj (T, B) = veni vidi vici

Po zapoznaniu się z wynikami dwóch proponowanych reguł wydaje Ci się całkiem jasne, że reguła B jest lepsza niż reguła A. W jaki sposób podejmujemy tę intuicyjną decyzję? Możemy powiedzieć, że B jest lepsze niż A, ponieważ tekst z reguły B wygląda bardziej jak prawdziwy język niż nonsens, podczas gdy tekst reguły A wygląda zupełnie bezsensownie. Przekształcić tego człowieka intuicyjnie w coś automatycznego, co możemy zaprogramować na komputerze, użyjemy leksykalnej bazy danych, która zapewnia prawdopodobieństwo każdego słowa, które zobaczymy. Prawdziwy język będzie wówczas równoważny tekstowi zbudowanemu ze słów o wysokim prawdopodobieństwie, podczas gdy fałszywy język będzie równoważny tekstowi ze słowami o niskim prawdopodobieństwie. Jedyną złożonością tego podejścia jest radzenie sobie ze słowami, które w ogóle nie istnieją. Ponieważ ich prawdopodobieństwo wynosi zero, a my oszacujemy prawdopodobieństwo fragmentu tekstu jako całości, mnożąc prawdopodobieństwo pojedynczych słów razem, musimy zamienić zero na coś naprawdę małego, na przykład najmniejszego możliwego do odróżnienia naszego urządzenia różnica zmiennoprzecinkowa, którą nazwiemy epsilon, aby użyć naszego algorytmu. Po zajęciu się tym przypadkiem krawędzi możemy użyć leksykalnej bazy danych, aby uszeregować jakość dwóch fragmentów przetłumaczonego tekstu, znajdując prawdopodobieństwo każdego słowa, a następnie mnożąc te prawdopodobieństwa razem, aby znaleźć oszacowanie prawdopodobieństwa tekstu jako cały.

Wykorzystanie bazy danych leksykalnych do obliczenia prawdopodobieństwa odszyfrowania tekstu da nam naszą miarę błędów w ocenie proponowanej reguły deszyfrowania. Teraz, gdy mamy funkcję błędu, nasz problem z łamaniem kodu przekształcił się całkowicie w problem optymalizacji, więc musimy tylko znaleźć reguły deszyfrowania, które generują tekst z dużym prawdopodobieństwem. Niestety, problem znalezienia reguły o najwyższym prawdopodobieństwie tekstu nie jest bliski temu, by działała optymalizacja. Reguł deszyfrowania nie można wykreślić i nie zapewniają one płynności, której potrzebuje optymaliacja, gdy próbuje wymyślić, w jaki sposób dążyć do lepszych reguł. Potrzebujemy więc zupełnie nowego algorytmu optymalizacji do rozwiązania naszego problemu odszyfrowywania. Algorytmem będzie metoda Metropolis, o której wspomnieliśmy już na początku. Algorytm Metropolis okazuje się działać stosunkowo dobrze w przypadku naszego problemu, choć jest bardzo, bardzo wolny dla każdej rozsądnej długości tekstu. Podstawową ideą metody Metropolis jest to, że zaczniemy od arbitralnej reguły deszyfrowania, a następnie iteracyjnie ją poprawimy wiele razy, aby stała się regułą, która może być właściwa. Na początku może się to wydawać magią, ale często działa w praktyce, ponieważ powinieneś przekonać się poprzez eksperymenty. A gdy już będziemy mieć do czynienia z potencjalną regułą deszyfrowania, możemy skorzystać z naszej ludzkiej intuicji opartej na semantycznej spójności i gramatyce, aby zdecydować, czy poprawnie odszyfrowaliśmy tekst. Aby wygenerować dobrą regułę, zaczynamy od całkowicie arbitralnej reguły, a następnie powtarzamy pojedynczą operację, która poprawia naszą regułę wiele razy – powiedzmy, 50 000 razy. Ponieważ każdy krok zmierza w kierunku lepszych zasad, powtarzanie tej operacji w kółko doprowadzi nas w końcu do rozsądnego celu, chociaż nie ma gwarancji, że nie będziemy potrzebować 50 000 000 kroków zamiast 50 000, aby dostać się tam, gdzie byśmy lubić być.

To jest powód, dla którego ten algorytm nie działa dobrze w przypadku poważnego systemu łamania kodu: nie masz gwarancji, że algorytm da ci rozwiązanie w rozsądnym czasie i bardzo trudno jest stwierdzić, czy się w ogóle wprowadzasz właściwy kierunek podczas oczekiwania. To studium przypadku jest tylko zabawkowym przykładem, który pokazuje, jak używać algorytmów optymalizacyjnych do rozwiązywania złożonych problemów, które w innym przypadku mogłyby wydawać się niemożliwe do rozwiązania. Sprecyzujmy, w jaki sposób zamierzamy zaproponować nową regułę deszyfrowania. Zrobimy to, losowo naruszając obecną regułę w jednym miejscu. To znaczy, będziemy przeszkadzać bieżącej regule, zmieniając efekt reguły na jedną literę alfabetu wejściowego. Jeśli „a” obecnie tłumaczy się na „b” zgodnie z naszą regułą, zaproponujemy modyfikację, która ma „a” przekłada się na „q”. Ze względu na sposób działania szyfrów substytucyjnych będzie to wymagało kolejnej modyfikacji części reguły, która pierwotnie wysłała kolejny list – na przykład „c” – na „q”. Aby nasz szyfr działał, „c” musi teraz zostać przetłumaczone na „b”. Zatem nasz algorytm proponowania nowych reguł sprowadza się do wykonania dwóch zamian w naszej istniejącej regule, jednej losowo wybranej, a drugiej wymuszonej przez definicję szyfru podstawienia. Gdybyśmy byli naiwni, zaakceptowalibyśmy tę nową proponowaną regułę tylko wtedy, gdyby zwiększała prawdopodobieństwo naszego odszyfrowanego tekstu. To się nazywa chciwa optymalizacja. Niestety, chciwa optymalizacja w tym przypadku spowoduje, że utkniemy w złych regułach, dlatego zamiast tej oryginalnej reguły A i nowej reguły B zastosujemy następującą regułę:

  1. Jeśli prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły B jest większe niż prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły A, wówczas zamieniamy A na B.
  2. Jeśli prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły B jest mniejsze niż prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły A, nadal będziemy zamieniać A na B czasami, ale nie za każdym razem. Mówiąc konkretnie, jeśli prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły B jest prawdopodobieństwem (T, B), a prawdopodobieństwo tekstu odszyfrowanego za pomocą reguły A to prawdopodobieństwo (T, A), przełączymy się na prawdopodobieństwo reguły B (T , B) / prawdopodobieństwo (T, A) procent czasu. Jeśli ten współczynnik wydaje się pochodzić z lewego pola, nie martw się. Dla intuicji, tak naprawdę liczy się nie konkretny stosunek, ale fakt, że akceptujemy regułę B częściej niż 0% czasu. To pomaga nam uniknąć pułapek, w które wpadłaby chciwa optymalizacja. Zanim będziemy mogli użyć metody Metropolis do sortowania różnych szyfrów, potrzebujemy narzędzi do tworzenia zaburzonych szyfrów, które właśnie opisaliśmy. Tutaj są:

generate.random.cipher <- function()

{

cipher <- list()

inputs <- english.letters

outputs <- english.letters[sample(1:length(english.letters),

length(english.letters))]

for (index in 1:length(english.letters))

{

cipher[[inputs[index]]] <- outputs[index]

}

return(cipher)

}

modify.cipher <- function(cipher, input, output)

{

new.cipher <- cipher

new.cipher[[input]] <- output

old.output <- cipher[[input]]

collateral.input <- names(which(sapply(names(cipher),

function (key) {cipher[[key]]}) == output))

new.cipher[[collateral.input]] <- old.output

return(new.cipher)

}

propose.modified.cipher <- function(cipher)

{

input <- sample(names(cipher), 1)

output <- sample(english.letters, 1)

return(modify.cipher(cipher, input, output))

}

Połączenie tego narzędzia do proponowania nowych reguł i procedury zamiany reguł zmiękcza chciwość naszego podejścia optymalizacyjnego, nie marnując zbyt wiele czasu na oczywiście złe reguły, które mają znacznie mniejsze prawdopodobieństwo niż nasza obecna reguła. Aby wykonać to zmiękczanie algorytmicznie, po prostu obliczamy prawdopodobieństwo (T, B) / prawdopodobieństwo (T, A) i porównujemy go z liczbą losową od 0 do 1. Jeśli wynikowa liczba losowa jest wyższa niż prawdopodobieństwo (T, B) / prawdopodobieństwo (T, A), zastępujemy naszą obecną zasadę. Jeśli nie, trzymamy się naszej obecnej zasady. Aby obliczyć prawdopodobieństwa, o których ciągle wspominamy, stworzyliśmy leksykalną bazę danych, która mówi, jak często każde słowo w / usr / share / dic / words występuje w tekście na Wikipedii. Aby załadować to do R, wykonaj następujące czynności:

load (‘data / lexical_database.Rdata’)

Możesz poczuć bazę danych, wyszukując proste słowa i je wyświetlając

częstotliwości w naszym przykładowym tekście:

lexical.database [[‘a’]]

lexical.database [[” ‘]]

lexical.database [[‘on’]]

lexical.database [[‘she’]]

lexical.database [[„data”]]

Word : Prawdopodobieństwo

a : 0,01617576

the : 0,05278924

he:  0,003205034

she :  0,0007412179

data :  0,0002168354

Teraz, gdy mamy naszą bazę leksykalną, potrzebujemy metod do obliczenia prawdopodobieństwa tekstu. Najpierw napiszemy funkcję, która zawinie ciągnienie prawdopodobieństwa z bazy danych. Napisanie funkcji ułatwia obsługę fałszywych słów, którym należy przypisać najniższe możliwe prawdopodobieństwo, którym będzie epsilon zmiennoprzecinkowy maszyny. Aby uzyskać dostęp do tej wartości w R, możesz użyć zmiennej .Machine

$double.eps.

one.gram.probability <- function(one.gram, lexical.database = list())

{

lexical.probability <- lexical.database[[one.gram]]

if (is.null(lexical.probability) || is.na(lexical.probability))

{

return(.Machine$double.eps)

}

else

{

return(lexical.probability)

}

}

Teraz, gdy mamy tę metodę określania prawdopodobieństwa wyodrębnienia słów, tworzymy metodę obliczania prawdopodobieństwa fragmentu tekstu, dzieląc tekst na osobne słowa, obliczając prawdopodobieństwa w izolacji i łącząc je ponownie, mnożąc je razem. Niestety okazuje się, że użycie surowych prawdopodobieństw jest niestabilne numerycznie z powodu arytmetyki skończonej precyzji, jaką zapewniają liczby zmiennoprzecinkowe podczas mnożenia. Z tego powodu obliczamy prawdopodobieństwo logarytmu tekstu, które jest sumą prawdopodobieństw logarytmicznych każdego słowa w tekście. Ta wartość okazuje się nie być niestabilna numerycznie.

log.probability.of.text <- function(text, cipher, lexical.database = list())

{

log.probability <- 0.0

for (string in text)

{

decrypted.string <- apply.cipher.to.string(string, cipher)

log.probability <- log.probability +

log(one.gram.probability(decrypted.string, lexical.database))

}

return(log.probability)

}

Teraz, gdy mamy już wszystkie potrzebne elementy administracyjne, możemy napisać jeden krok metody Metropolis w następujący sposób:

metropolis.step <- function(text, cipher, lexical.database = list())

{

proposed.cipher <- propose.modified.cipher(cipher)

lp1 <- log.probability.of.text(text, cipher, lexical.database)

lp2 <- log.probability.of.text(text, proposed.cipher, lexical.database)

if (lp2 > lp1)

{

return(proposed.cipher)

}

else

{

a <- exp(lp2 – lp1)

x <- runif(1)

if (x < a)

{

return(proposed.cipher)

}

else

{

return(cipher)

}

}

}

A teraz, gdy działają poszczególne etapy naszego algorytmu optymalizacji, połączmy je w jeden przykładowy program, który pokazuje, jak działają. Najpierw skonfigurujemy surowy tekst jako wektor znaków w R:

decrypted.text <- c(‘here’, ‘is’, ‘some’, ‘sample’, ‘text’)

Następnie zaszyfrujemy ten tekst za pomocą szyfru Cezar:

encrypted.text <- apply.cipher.to.text(decrypted.text, caesar.cipher)

Następnie utworzymy losowy szyfr deszyfrujący, uruchomimy 50 000 kroków Metropolis i zapiszemy wyniki w ramce data.frame zwanej wynikami. lub na każdym kroku, będziemy prowadzić rejestr prawdopodobieństwa dziennika odszyfrowanego tekstu, bieżącego odszyfrowania przykładowego tekstu oraz zmiennej zastępczej wskazującej, czy poprawnie odszyfrowaliśmy tekst wejściowy. Oczywiście, jeśli naprawdę próbujesz złamać tajny kod, nie będziesz w stanie powiedzieć, kiedy poprawnie odszyfrowałeś tekst, ale przydatne jest przechowywanie tego w celach demonstracyjnych.

set.seed(1)

cipher <- generate.random.cipher()

results <- data.frame()

number.of.iterations <- 50000

for (iteration in 1:number.of.iterations)

{

log.probability <- log.probability.of.text(encrypted.text, cipher, lexical.database)

current.decrypted.text <- paste(apply.cipher.to.text(encrypted.text, cipher),

collapse = ‘ ‘)

correct.text <- as.numeric(current.decrypted.text == paste(decrypted.text,

collapse = ‘ ‘))

results <- rbind(results,

data.frame(Iteration = iteration,

LogProbability = log.probability,

CurrentDecryptedText = current.decrypted.text,

CorrectText = correct.text))

cipher <- metropolis.step(encrypted.text, cipher, lexical.database)

}

write.table(results, file = ‘data/results.csv’, row.names = FALSE, sep = ‘\t’)

Uruchomienie tego kodu zajmuje trochę czasu, więc czekając, spójrzmy na próbkę wyników zawartych w tabeli

Iteracja : Prawdopodobieństwo dziennika :  Bieżący odszyfrowany tekst

1 :  -180.218266945586 : lsps bk kfvs kjvhys zsrz

5000 :  -67.6077693543898 : gen jest tym samym tekstem sfmpwe

10000  : -67,6077693543898 : gen jest tym samym tekstem spmzoe

15000 : -66,7799669880591 : gen  jest tekstem scmhbe

20000 : -70,8114316132189 : gen jako tekst scmire

25000 : -39,8590155606438 : gen jako prosty tekst

30000 : -39,8590155606438 : gen jako prosty tekst

35000 : -39,8590155606438 : gen jako prosty tekst

40000 :  -35,784429416419 : był jak jakiś prosty tekst

45000 : -37.0128944882928 : był to przykładowy tekst

50000:  -35,784429416419 : był jak jakiś prosty tekst

Jak widać, jesteśmy blisko poprawnego odszyfrowania po kroku 45 000, ale nie jesteśmy jeszcze całkiem gotowi. Jeśli przejrzysz bardziej szczegółowo wyniki, przekonasz się, że trafiliśmy poprawny tekst w wierszu 45 609, ale potem przeszliśmy obok właściwej reguły do ​​innej reguły. Jest to w rzeczywistości problem z naszą obiektywną funkcją: tak naprawdę nie ocenia ona, czy tłumaczenie jest angielskie, ale tylko czy poszczególne słowa są normalnymi angielskimi słowami. Jeśli zmiana reguły daje bardziej prawdopodobne słowo, będziesz skłonny iść w tym kierunku, nawet jeśli wynik jest zepsuty gramatycznie lub semantycznie niespójny. Aby obejść ten problem, możesz użyć więcej informacji o języku angielskim, takich jak prawdopodobieństwo sekwencji dwóch słów. Na razie uważamy, że podkreśla to złożoność stosowania metod optymalizacji z celowymi funkcjami ad hoc: czasami pożądane rozwiązanie nie jest rozwiązaniem, które według reguły będzie najlepsze. Ludzie muszą być na bieżąco, gdy używasz algorytmów optymalizacyjnych do rozwiązywania swoich problemów. W rzeczywistości sprawy są jeszcze bardziej skomplikowane niż problemy z naszą funkcją celu, która nie zawiera wystarczającej wiedzy o tym, jak działa angielski. Po pierwsze, metoda Metropolis jest zawsze metodą losowej optymalizacji. Na szczęście zaczęliśmy od dobrej wartości początkowej wynoszącej 1, ale zła wartość początkowa może oznaczać, że zajmie tryliony kroków, zanim osiągniemy prawidłową zasadę odszyfrowywania. Możesz się o tym przekonać, grając z nasionem, którego użyliśmy, i wykonując tylko 1000 iteracji dla każdego ziarna. Po drugie, metoda Metropolis zawsze będzie skłonna pozostawić dobre tłumaczenia. To sprawia, że ​​jest to algorytm niereagujący, więc często możesz obserwować, jak porzuca rozwiązanie, które chcesz znaleźć, jeśli wystarczająco długo śledzisz jego postęp. Poniżej pokazujemy prawdopodobieństwo dziennika odszyfrowanego tekstu na każdym kroku. Możesz zobaczyć, jak nieregularny jest ruch metody.

Istnieją popularne sposoby radzenia sobie z tym przypadkowym ruchem. Jednym z nich jest stopniowe zwiększanie chciwości metody w miarę upływu czasu poprzez rzadsze przyjmowanie proponowanych reguł. Nazywa się to symulowanym wyżarzaniem i jest to bardzo potężne podejście do optymalizacji, w którym możesz grać w naszym przykładzie, po prostu zmieniając sposób, w jaki akceptujesz nowe reguły deszyfrowania. Innym sposobem radzenia sobie z tą przypadkowością jest objęcie jej i użycie jej w celu uzyskania rozkładu możliwych odpowiedzi zamiast jednej właściwej odpowiedzi. W przypadku tego problemu, który nie jest zbyt pomocny, ale w przypadku problemów, w których poprawna odpowiedź jest liczbą, bardzo pomocne może być uzyskanie różnorodnych możliwych odpowiedzi. Na zakończenie mamy nadzieję, że zyskałeś wgląd w to, jak działa optymalizacja ogólna i jak można go używać do rozwiązywania skomplikowanych problemów w uczeniu maszynowym. Ponownie wykorzystamy niektóre z tych pomysłów w późniejszych częściach, zwłaszcza gdy mówimy o systemach rekomendacji.

(I) : Używanie R w osobnych folderach

Oddzielne obrazy obszaru roboczego dla R można przechowywać w osobnych folderach dla systemów Windows, OS X i Linux. Ta właściwość R jest bardzo przydatna do używania R w oddzielnych projektach. Podczas gdy proces otwierania R w danym folderze różni się w zależności od systemu operacyjnego, raz w folderze zapisywanie obrazu obszaru roboczego jest proste. Podczas zamykania sesji R program pyta, czy użytkownik chciałby zapisać obraz obszaru roboczego. Po wybraniu opcji Tak pliki .RData i .Rhistory (.Rapp.history dla OS X) są zapisywane w bieżącym katalogu. (W systemie OS X pliki są ukryte, ale pliki tam są.) Plik .RData zawiera obiekty, które znajdowały się w R na początku sesji oraz wszelkie obiekty dodane podczas sesji minus wszelkie obiekty, które zostały usunięte podczas sesja. Plik .Rhistory (.Rapp.history dla OS X) zawiera historię wierszy wprowadzanych na konsoli R. Domyślnie wszystkie wiersze do ostatnich 512 wierszy są zapisywane w systemie Windows. W systemach OS X i Linux wartością domyślną jest 250 linii. Dostęp do linii jest przenoszony z sesji na sesję, jeśli historia jest zapisana.

Windows

Aby wstępnie skonfigurować R w folderze, otwórz R na pulpicie. (Kliknij ikonę R na pulpicie lub kliknij R na liście programów lub, w Windows 10, na liście paneli.) Wybierz Plik na pasku menu u góry ekranu. Z menu rozwijanego wybierz Zmień katalog. . .. Otworzy się okno Przeglądaj do folderu. Przejdź do wybranego folderu. Wychodząc z R, zapisz obraz obszaru roboczego, a R utworzy pliki .RData i .Rhistory w folderze. Plik .RData będzie miał niebieską ikonę R powiązaną z plikiem. W przyszłości przejście do folderu i kliknięcie ikony R spowoduje otwarcie R, a historia i obiekty zapisane w folderze będą obecne. Uwaga: przy początkowej konfiguracji wszelkie obiekty na pulpicie R nadal będą znajdować się w R po zmianie folderu. Możesz łatwo usunąć obiekty. Wpisz rm (list = ls ()) w wierszu polecenia, aby usunąć wszystkie obiekty z folderu.

OS X

Praca w różnych folderach w OS X jest również łatwa. Istnieją dwa sposoby: przeciąganie i upuszczanie lub używanie terminala. Jeśli R znajduje się w doku, a R nie jest otwarte, przeciągnięcie folderu z Findera lub Dokumentów do ikony R w doku spowoduje otwarcie R w folderze przy użyciu .RData i .Rapp.history dla tego folderu. Aby otworzyć R za pomocą terminala, otwórz terminal (znajdujący się w obszarze Aplikacje / Narzędzia w Finderze) i wpisz open -a R folder, w którym folder jest lokalizacją folderu. R otworzy się w folderze przy użyciu plików .RData i .Rapp.history dla tego folderu.

Linux

Aby otworzyć R w danym folderze w systemie Linux, zmień katalog na folder i wpisz R w wierszu polecenia.

Regresja Ridge

Teraz, gdy wiemy trochę o tym, jak korzystać z optim, możemy zacząć korzystać z algorytmów optymalizacyjnych do implementacji naszej własnej wersji regresji grzbietu. Regresja grzbietowa jest specyficznym rodzajem regresji, który obejmuje regularyzację, o czym mówiliśmy wcześniej. Jedyną różnicą między zwykłą regresją najmniejszych kwadratów a regresją grzbietową jest funkcja błędu: regresja grzbietowa uznaje wielkość współczynników regresji za część składową błędu, co zachęca do niewielkich współczynników. W tym przykładzie popycha to nachylenie i przechwytuje w kierunku zera. Oprócz zmiany naszej funkcji błędu, jedyną dodatkową złożonością działania regresji grzbietu jest to, że musimy teraz dołączyć dodatkowy parametr lambda, który decyduje między znaczeniem minimalizacji błędu kwadratu naszych prognoz a minimalizacją wartości a i b, więc że nie przepełniamy naszych danych. Dodatkowy parametr dla tego znormalizowanego algorytmu nazywa się hiperparametrem i został omówiony bardziej szczegółowo wcześniej. Po wybraniu wartości lambda możemy napisać funkcję błędu grzbietu w następujący sposób:

ridge.error <- function(heights.weights, a, b, lambda)

{

predictions <- with(heights.weights, height.to.weight(Height, a, b))

errors <- with(heights.weights, Weight – predictions)

return(sum(errors ^ 2) + lambda * (a ^ 2 + b ^ 2))

}

Możemy wybrać wartość lambda za pomocą walidacji krzyżowej. W pozostałej części tego rozdziału po prostu założymy, że już to zrobiłeś i że poprawna wartość lambda wynosi 1. Po zdefiniowaniu funkcji błędu grbietu w R nowe wywołanie funkcji optim rozwiązuje problem regresji kalenicy tak łatwo, jak rozwiązano pierwotny problem zwykłych najmniejszych kwadratów:

lambda <- 1

optim(c(0, 0),

function (x)

{

ridge.error(heights.weights, x[1], x[2], lambda)

})

#$par

#[1] -340.434108 7.562524

#

#$value

#[1] 1612443

#

#$counts

#function gradient

# 115 NA

#

#$convergence

#[1] 0

#

#$message

#NULL

Patrząc na wynik optim, widzimy, że stworzyliśmy nieco mniejszy punkt przecięcia i nachylenie dla naszej linii niż podczas korzystania z lm, co dało punkt przecięcia -350 i nachylenie 7,7. W tym przykładzie zabawki, który nie jest tak naprawdę pomocny, ale w regresjach na dużą skalę przeprowadzona wcześniej, w tym kara za duże współczynniki była niezbędna do uzyskania znaczących wyników z naszego algorytmu. Oprócz spojrzenia na dopasowane współczynniki, możemy również powtórzyć wywołania funkcji krzywej, aby zobaczyć, dlaczego optym powinien być w stanie pracować z regresją grzbietu w taki sam sposób, jak działał dla zwykłej regresji liniowej. Wyniki pokazano poniżej

a.ridge.error <- function(a, lambda)

{

return(ridge.error(heights.weights, a, 0, lambda))

}

curve(sapply(x, function (a) {a.ridge.error(a, lambda)}), from = -1000, to = 1000)

b.ridge.error <- function(b, lambda)

{

return(ridge.error(heights.weights, 0, b, lambda))

}

curve(sapply(x, function (b) {b.ridge.error(b, lambda)}), from = -1000, to = 1000)

Mam nadzieję, że ten przykład przekonuje cię, że możesz wiele zrobić w uczeniu maszynowym, po prostu rozumiejąc, jak korzystać z funkcji takich jak optymalizacja, aby zminimalizować pewną miarę błędu prognozowania. Zalecamy samodzielne zapoznanie się z kilkoma przykładami, a następnie zapoznanie się z różnymi funkcjami błędów, które sam wymyśliłeś. Jest to szczególnie pomocne, gdy próbujesz użyć funkcji błędu, takiej jak pokazana tutaj funkcja błędu bezwzględnego:

absolute.error <- function

(heights.weights, a, b)

{

predictions <- with(heights.weights, height.to.weight(Height, a, b))

errors <- with(heights.weights, Weight – predictions)

return(sum(abs(errors)))

}

Ze względów technicznych związanych z rachunkiem różniczkowym ten termin błędu nie będzie działał dobrze z optymalizacją. Pełne wyjaśnienie, dlaczego wszystko się psuje, jest dość trudne bez rachunku różniczkowego, ale tak naprawdę można wizualnie przekazać wielki pomysł. Powtarzamy tylko wezwania do zakrzywienia, które wykonujemy:

a.absolute.error <- function(a)

{

return(absolute.error(heights.weights, a, 0))

}

curve(sapply(x, function (a) {a.absolute.error(a)}), from = -1000, to = 1000)

Jak widać 

krzywa błędu bezwzględnego jest znacznie ostrzejsza niż krzywa błędu kwadratu lub krzywa błędu grzbietu. Ponieważ kształt jest tak ostry, optim nie otrzymuje tylu informacji o kierunku, w którym powinien iść z jednego punktu i niekoniecznie osiąga globalne optimum, nawet jeśli możemy wyraźnie zobaczyć, że istnieje on na podstawie prostej kontroli wzrokowej . Ponieważ niektóre rodzaje metryk błędów łamią potężne algorytmy, umiejętność uczenia maszynowego polega na uczeniu się, gdy działają proste narzędzia, takie jak optim, i uczeniu się, gdy potrzebujesz czegoś mocniejszego. Istnieją algorytmy, które działają na rzecz absolutnej optymalizacji błędów, ale wykraczają one poza zakres . Jeśli chcesz dowiedzieć się więcej na ten temat, znajdź swojego lokalnego guru matematyki i poproś go o rozmowę na temat optymalizacji wypukłej.

(I) : Instalowanie i aktualizowanie pakietów

Po pierwszej instalacji R zawiera 30 pakietów. Często użytkownik będzie chciał wykorzystać moc wielu innych pakietów dostępnych w R. Instalowanie i aktualizowanie pakietu jest proste. W przypadku dowolnego systemu operacyjnego, jeśli nazwa pakietu jest znana, wpisz install.packages („nazwa pakietu”) w wierszu polecenia R, gdzie nazwa pakietu jest nazwą pakietu, zainstaluje pakiet. Aby zaktualizować pakiety, wpisując update.packages() w wierszu polecenia R, znajdziesz te pakiety z aktualizacjami i zaktualizuję pakiety. Aby zobaczyć, które pakiety są już zainstalowane na komputerze, wpisz install.packages () po znaku zachęty R. Jeśli nazwa pakietu nie jest znana (także w przypadku znanych nazw), skorzystaj z instalatora w systemach operacyjnych Windows i OS X są łatwe. W przypadku systemu Linux instrukcje można znaleźć na stronie internetowej CRAN http://cran.r-project.org. Tutaj znajdziesz instrukcje dla systemu Windows i OS X.

Windows

Aby zainstalować pakiet w systemie Windows bez użycia wiersza polecenia, zacznij od otwarcia R. Na pasku menu u góry ekranu wybierz Pakiety. Zostanie wyświetlone menu. Wybierz Zainstaluj pakiety. . . . Pojawi się okno lustrzane CRAN lub okno Pakiety. Jeśli pojawi się okno dublowania CRAN, wybierz zamknij dublowanie i kliknij OK, co spowoduje otwarcie okna Pakiety. Okno Pakiety składa się z listy wszystkich dostępnych pakietów. Przewiń listę w dół, aby znaleźć pakiety, które chcesz zainstalować, i wybierz pakiety. Kliknij OK, aby rozpocząć instalację. W trakcie instalacji kroki instalacji będą przewijane na konsoli R. Gdy monit R powróci do ekranu, instalacja jest zakończona. Aby zaktualizować pakiety nie korzystające z wiersza polecenia, wybierz Pakiety na pasku menu, a następnie wybierz Aktualizuj pakiety. . . . Otworzy się okno Pakiety do aktualizacji, i będziesz miał listę wszystkich zainstalowanych pakietów z aktualizacjami. Jeśli nie ma, okno będzie puste. Wybierz pakiety do aktualizacji i kliknij przycisk OK. Jeśli pojawi się pytanie dotyczące korzystania z biblioteki osobistej, wybierz opcję Tak. Pakiety zostaną zaktualizowane. Gdy monit R powróci do ekranu, aktualizacje są zakończone.

OS X

Aby zainstalować pakiety w OS X, zacznij od otwarcia R. Na pasku menu u góry ekranu wybierz Pakiety i dane. Z menu rozwijanego wybierz Instalator pakietów, który wyświetla Instalator pakietów R. Kliknij Pobierz listę, aby zobaczyć pełną listę pakietów lub użyj opcji Wyszukiwanie pakietów, aby wyszukać pakiet. W ramach dowolnej opcji wybierz z listy pakiety do zainstalowania. Poniżej listy paczek znajdują się opcje lokalizacji, w której mają zostać umieszczone paczki. Najedź kursorem na listę opcji lokalizacji, aby uzyskać więcej informacji. Zwykle jedna z dwóch pierwszych opcji będzie poprawna. Po prawej stronie opcji lokalizacji znajdują się przyciski Instaluj wybrane i Aktualizuj wszystko. Przed kliknięciem Instaluj wybrane zaznacz pole Instaluj zależności, aby upewnić się, że wszystkie niezbędne pakiety zostały zainstalowane. Kliknij Zainstaluj wybrane, aby rozpocząć proces instalacji. Wybrane pakiety zostaną zainstalowane. Aby zaktualizować pakiety, wybierz Pakiety i dane z paska menu u góry ekranu. Z menu rozwijanego wybierz Instalator pakietów, który otworzy Instalator pakietów R. W prawym dolnym rogu instalatora wybierz opcję Aktualizuj wszystko i postępuj zgodnie z instrukcjami.

Aktualizacja R

Ponieważ CRAN nie zapewnia automatycznych aktualizacji dla R, musisz zaktualizować go ręcznie. Procesy w systemie Windows i OS X są łatwe. W przypadku dystrybucji Linuksa Debian, Suse i Ubuntu instrukcje można znaleźć w plikach ReadMe pod adresem http: //cran.r-project/bin/ linux / Distribution, gdzie dystrybucją jest Debian, Suse lub Ubuntu. W przypadku systemu Red Hat Linux poszukaj gdzie indziej na stronie CRAN.

Windows

Pierwszym krokiem w aktualizacji R w Windows jest otwarcie R i zainstalowanie instalatora pakietu, jeśli pakiet nie został jeszcze zainstalowany. Następnie użyj funkcji library, aby zapewnić dostęp do installr. Wpisz

library (installr)

w wierszu polecenia i naciśnij enter. Następnie, aby zaktualizować R, wpisz

updateR()

w wierszu polecenia i naciśnij enter. R wykona aktualizację lub wyświetli komunikat, że program jest aktualny i zwróci Fałsz. Po zainstalowaniu instalatora nie trzeba go instalować ponownie. Dostęp do biblioteki należy uzyskać przy każdym uruchomieniu R.

OS X

Pierwszym krokiem w aktualizacji R w OS X jest otwarcie R i wybranie R z paska menu na górze strony. Aby uruchomić narzędzie do aktualizacji, wybierz polecenie Sprawdź aktualizacje R z menu rozwijanego pod R i postępuj zgodnie z instrukcjami.