EKSPLORACJA ZASOBÓW INTERNETU - MIŁOSZ KADZIŃSKI LABORATORIUM VI INDEKSOWANIE + LUCENE 1. Plan laboratorium VI 1.1. Ineks owrotny 1.2. w-shingling 1.3. Suffix tree algorytm naiwny oraz algorytm Ukkonena; zastosowanie struktur ineksujących 1.4. Suffix array algorytm qsufsort 1.5. Lucene 2. Inverte inex - ineks (plik) owrócony Struktura anych przechowująca poszczególne termy jako klucze, oraz ientyfikatory plików, w których te termy wystąpiły jako wartości (najczęściej implementowana jako tablica hashująca lub rzewo binarne). Cel: zwiększenie szybkości ziałania wyszukiwarki przy poniesieniu kosztu oania informacji o okumencie o ineksu W ineksie najczęściej przechowywane są ineksy okumentów (numery porząkowe). Doatkowo la każego termu często przechowywana jest ługość listy okumentów, które go zawierają. W wersji pełnej ineksu przechowuje się pary (DocID, TermPos), gzie TermPos jest pozycją termu w ramach okumentu o ientyfikatorze DocID. Zwykle okumenty la anego termu są uszeregowane zgonie z DocID. Inne pomysły polegają na wykorzystaniu tzw. statycznej jakości okumentu (np. miary PageRank) lub miary TF. 3. w-shingling zbiór ciagłych posekwencji tokenów w okumencie w oznacza licznę tokenów w każym pociągu wykorzystywany o baania poobieństwa okumentów (w tym przee wszystkim wykrywania plagiatów) po wzglęem wspólnych sekwencji o określonym rozmiarze sim( 1, 2 ) = S( S( 1 1 ) S( ) S( 2 2 ) ) 4. Drzewo sufiksów - suffix tree Prefiks określenie początkowych znaków ciągu (np. S la U jeśli U=SU ). Sufiks określenie końcowych znaków ciągu (np. S la U jeśli U=U S) Drzewo sufiksów - struktura anych reprezentująca zbiór sufiksów anego słowa lub tekstu. umożliwia efektywną obsługę zapytań, które wymagają wyszukania w tekstach ciągów słów lub ciągów znaków ineksowany tekst jest traktowany jako jeen ługi ciąg znaków; Suffix trie - struktura rzewiasta o przechowywania ciągów znaków, której każy możliwy sufiks można znaleźć na ścieżce o korzenia o któregoś liścia. Drzewo sufiksów (suffix tree) to barziej zwięzła reprezentacja struktury suffix trie (każy węzeł z pojeynczym potomkiem jest eliminowany (poprzez połączenie węzła z potomkiem w pojeynczy węzeł). - 1 -
Drzewo sufiksów T (suffix tree) la ciągu znaków S o ługości m to rzewo o następujących właściwościach: posiaa korzeń i jest skierowane m liści oznakowanych o 1 o m każa krawęź etykietowana pociągiem znaków S złączenie etykiet poszczególnych krawęzi na ścieżce o korzenia o liścia i aje sufiks i ciągu S (oznaczmy go jako S i m ) każy wewnętrzny wierzchołek ma co najmniej wójkę zieci krawęzie wychozące z korzenia muszą zaczynać się o różnych znaków Naive Suffix Tree Builing (złożoność O(m 2 )) Dla i=1 o m Doaj sufiks S i m o T, znajując najłuższy pasujący prefiks S i m, który jest już w T i rozgałęziając o tego miejsca Algorytm Ukkonena On-line zaczyna o pierwszego znaku, potem rugi, it. Wersja postawowa (złożoność O(m 3 )) Zbuuj I1 (oaj pierwszy znak o rzewa). Dla i=1 o m-1 Dla j = 1 o i+1 Znajź ścieźkę o korzenia zaetykietowaną S j i Doaj znak S i+1 na koniec ścieżki, jeśli to konieczne Trzy reguły oawania znaku na koniec ścieżki Reguła 1 Jeśli ścieżka S j i w rzewie kończy się liściem, oaj S i+1 na koniec etykiety Reguła 2 Jeśli w rzewie są ścieżki S j i, które nie kończą się w liściu i nie następuje po nich S i+1, utwórz nowy liść z etykietą S i+1 na końcu ścieżki S j i (tworząc nowy wierzchołek wewnętrzny, jeśli S j i kończy się w śroku etykiety) Reguła 3 Jeśli w rzewie są ścieżki S j i, które nie kończą się w liściu, ale po jenej z nich następuje S i+1, nie rób nic - 2 -
Zmniejszenie złożoności opiera się na wóch obserwacjach: I. Once a leaf always a leaf na krawęziach o liści można oawać znak bez zastanowienia. II. Jeśli w rzewie jest już jakiś sufiks, to wszystkie jego sufiksy też są już w rzewie, czyli jeśli w anej iteracji wykorzystano regułę 3 o rozszerzenia j, to można ją zastosować (czyli nic nie robić) la wszystkich alszych rozszerzeń w anej iteracji Złożoność O(m) Zbuuj I1. Dla i=1 o m-1 Dla j takiego, że jl < j < jr Znajź ścieźkę o korzenia zaetykietowaną S j i Doaj znak S i+1 na koniec ścieżki jl - w iteracji I to ineks ostatniego oanego liścia jr w iteracji I to pierwszy ineks j taki, że S j i+1 jest już w rzewie Wywołań pętli jest 2m; to, co jest wykonywane, a się zrobić w stałym czasie. - 3 -
Postawowe zastosowania: znalezienie pociągu znaków P w tekście S; najłuższa powtarzająca się sekwencja znaków w ramach jenego ciągu znaków; najłuższy wspólny pociąg kilku ciągów znaków (alibaba.taliban.) problem pociągów la kolekcji wzorców najłuższy palinrom (what o you think, o geese see Go? -> whatoyouthink ogeeseseego.ogeeseseegoknihtuoytahw) problem opasowania par suffix-prefix (la ciągów Si oraz Sj, znajź najłuższą parę suffix Si i prefix Sj, które o siebie pasują DNA sequence assembly, EST alignment) zanieczyszczenie DNA (DNA contamination) procesy laboratoryjne powouje wniknięcie niechcianego DNA o ciągu bęącego przemiotem baań; sekwencje DNA wielu możliwych zanieczyszczaczy są znane problem: mając ane S1 oraz zbiór ciągów S (potencjalne zanieczyszczenia), znajź pociągi S, które pojawiają się w S1 i są łuższe niż zaane r 5. Tablica sufiksów suffix array Każy sufiks może być jenoznacznie określony za pomocą ineksu swojego pierwszego znaku. Tablica sufiksów to tablica ineksów sufiksów posortowanych w porząku leksykograficznym. Tablica sufiksów jest zawsze permutacją ineksów sufiksów. Tablica sufiksów Suffix S(i) I Suffix S(i) I[i] banana$ 0 $ 6 anana$ 1 a $ 5 nana$ 2 -> sort -> a na $ 3 Ana$ 3 ana na$ 1 Na$ 4 banana$ 0 a$ 5 na $ 4 $ 6 na na$ 2 Główne zastosowania: Exact matching (wzór P[1 n] poszukiwany w T[1.m] P jest pociągiem T jeśli jest prefiksem jakiegoś sufiksu w T; pozycja początkowe sufiksow w T, które zaczynają się o tego samego prefiksu znajują się obok siebie w tablicy sufiksów; znajź najmniejszy i największy ineks, w którym zaczyna się any prefiks, wykorzystując np. wyszukiwanie binarne) Substring problem Tworzenie - algorytm qsufsort (Larsson, Saakane) I. W tablicy I umieść posortowane ineksy sufiksów 0,,n. Do sortowania wykorzystaj znak na pozycji i-tej. Ustaw h=1. II. Dla każego i 0 [0,n], oblicz V[i] - numer grupy sufiksu i jako najgorszą pozycję w I jaką może zajmować sufiks zaczynający się o tej samej litery co sufiks i-ty. III. Każą grupę, która nie jest jeszcze posortowana posortuj zgonie z ternary-split Quicksort, używając V[i+h] jako klucza la sufiksu i-tego. IV. Uzupełnij I[i]. Uzupełnij V[i]. Pomnóż h przez 2. V. Jeśli I skłaa się z jenej posortowanej grupy, stop. W przeciwnym razie, iź o punktu III. - 4 -
6. Ćwiczenia 1. Utwórz ineks owrócony la następujących okumentów: D1: new Home sales top forecasts Term/Doc D1 D2 D3 D4 D2: home sales rise in july forecasts 1 0 0 0 forecasts D3: increase in home sales in july home 1 1 1 1 home D4: july new home sales rise in 0 1 1 0 In increase 0 0 1 0 increase Jakie są wyniki wyszukiwania la july 0 1 1 1 july Zapytań: new 1 0 0 1 new sales AND rise rise 0 1 0 1 rise forecasts OR increase sales 1 1 1 1 sales top 1 0 0 0 top 2. Utwórz w-shingling la tekstu a banana is a banana is a banana la w=4. tokeny = (a, banana, is, a, banana, is, a, banana) zbiór wszystkich kolejnych sekwencji 4 tokenów (N-gramów, 4-gramów) to: {(a, banana, is, a), (banana, is, a, banana), (is, a, banana, a), (a, banana, is, a), (banana, is, a, banana)} 4-shingling = { } 3. Utwórz rzewo sufiksów la słowa banana$, wykorzystując algorytm naiwny. 0 1 0 1 0 2 oraz algorytm Ukonnena 4. Utwórz tablicę sufiksów la słowa banana$, korzystając z algorytmu qsufsort. 0 1 2 3 4 5 6 X = [ b a n a n a $ ] I[i] = [ ] V[I[i]] = [ ] V[I[i+(h=1)]] = [ ] I[i] = [ ] V[I[i]] = [ ] V[I[i+(h=2)]] = [ ] I[i] = [ ] - 5 -
7. Zaania o samozielnego wykonania 1. [1] Utwórz ineks owrócony la następujących okumentów: D1: breakthrough rug for schizophrenia D2: new schizphrenia rug D3: new approach for treatment of schizophrenia D4: new hopes for schizophrenia patients Jakie bęą wyniki wyszukiwania la zapytań: Q1: schizophrenia AND rug Q2: for AND NOT(rug OR approach) Dla ułatwienia w arkuszu poano macierz term-okument. 2. [1] Pokaż proces tworzenia tablicy sufiksów (suffix array) la frazy tobeornottobe$ z wykorzystaniem algorytmu qsufsort. 3. [3] Celem ćwiczenia jest zapoznanie się z Lucene Java API. Lucene to biblioteka uostępniająca funkcje niezbęne la zastosowań z zieziny Information Retrieval, np. ineksowanie okumentów czy tworzenie rankingu stron la zapytań. Uostępnia ona narzęzia takie jak parsery, systemy ineksujące i rangujące, pozwalając skupić się na projekcie architektury systemu. Co więcej, Lucene uostępnia proste algorytmy tokenizacji, stemmingu, it., które mogą być zastąpione przez barziej zaawansowane mouły w zależności o wymagań użytkowników. Opis zaania jest ługi w rzeczywistości zaanie jest proste i większość rozwiązania znajuje się bezpośrenio w poniższym opisie. Zaanie sprowaza się o opisania kilkunastu linii kou, z których większość jest poana w treści ćwiczenia. Zaanie polega na utworzeniu prostej wyszukiwarki, ziałającej na zaanej kolekcji tekstów. Szkielet rozwiązania znajuje się w klasie LuceneLab6.java. Klasę należy uzupełnić zgonie z poniższymi instrukcjami. Wywołanie programu wymaga poania wóch paramterów: args[0] - nazwa katalogu z kolekcją tekstów, args[1] nazwa katalogu, w którym zostanie utworzony ineks. Zapytania jenowyrazowe poawane są przez użytkownika z poziomu konsoli. Część 1 - ustawienia śroowiska Ściągnij bibliotekę Lucene z katalogu lab6. Upewnij się, że umieściłeś.jar w katalogu Twojego projektu. W LuceneLab6.java okonano już eklaracji importu wszystkich klas, których bęziesz potrzebował la celów zaania. Kolekcja tekstów na której bęziesz pracował znajuje się w katalogu lab6. Są to teksty Shakespeare pobrane ze strony projektu Gutenberg. Ścieżkę o kolekcji poajesz jako pierwszy parametr wywołania programu. Część 2 - utworzenie ineksu, przeczytanie okumentów, utworzenie reprezentacji okumentów, wypełnienie ineksu Napisz funkcję createinex, której celem jest utworzenie i wypełnienie ineksu la zaanej kolekcji tekstów. Parametrem funkcji jest ścieżka o kolekcji tesktów. Ineks w Lucene wygląa następująco: - 6 -
Inex Document 1 Fiel A (name/value) Fiel B (name/value) Document 2 Fiel A (name/value) Fiel B (name/value) Do utworzenia ineksu wykorzystaj klasę InexWriter : InexWriter inexwriter = new InexWriter("inexPath", new StanarAnalyzer(), true); gzie pierwszy parametr o ścieżka o katalogu, w którym ma być przechowywany ineks, rugi parametr to typ "analizatora" (parsera okumentu), który bęzie wykorzystywany, a trzeci parametr świaczy o tym, czy buowany jest nowy ineks (true) czy też rozwijany jest ineks już istniejący (false). StanarAnalyzer jest najczęściej wykorzystywanym analizotorem ogólnego użycia. Inne możliwe to: WhiteSpaceAnalyzer (prosty analizator, które separuje tokeny na spacjach), StopAnalyzer (usuwa słowa stopwors), SnowballAnalyzer (używa pewnej fermy stemmingu). Utwórz obiekt klasy File, korzystając z konstruktora, który jako parametr przyjmuje ścieżkę o kolekcji tekstów. Następnie wykorzystaj opowienią metoę klasy File tak, by nazwy okumentów w tym katalogu reprezentować jako tablicę String[] i następnie owołując się o tych nazwy, moć w pętli oawać okumenty o ineksu. Po utworzeniu ineksu, wypełnij go zawartością, oając poszczególne okumenty. Każy okument opowiaa obiektowi klasy Document. Dokument jest opisany za pomocą wielu własności w formie (nazwa, wartość). Każa własność opowiaa obiektowi klasy Fiel. Korzystając z tych obiektów, można oać informacje nt. okumentu, takie jak tytuł, ścieżka o miejsca przechowywania, treść, it. Na przykła: FileInputStream file = new FileInputStream(ocPath); Document oc = new Document(); oc.a(new Fiel("path", ocpath, Fiel.Store.YES, Fiel.Inex.TOKENIZED)); oc.a(new Fiel("content", (Reaer) new InputStreamReaer(file))); gzie ocpath zawiera ścieżkę o okumentu; Fiel.Store.YES określa, czy własność jest przechowywana w ramach ineksu (użyteczne, jeśli chce się ją wyświetlić w ostatecznych wynikach), Fiel.Inex.TOKENIZED wskazuje, czy ane muszą być poane tokenizacji czy też nie. Napisz osobną funkcję inexdoc la utworzenia obiektu klasy Document. Po zefiniowaniu obiektu klasy Document, oaje się go o ineksu za pomocą polecenia: inexwriter.adocument(oc); Następnie należy zakończyć tworzenie ineksu poleceniem: inexwriter.close(); Część 3 - przeszukiwanie kolekcji Jeśli utworzyłeś ineks, możesz przystąpić o napisania części przetwarzającej zapytania. Wykorzystaj klasy InexSearcher oraz QueryParser: InexSearcher isearch = new InexSearcher(inexPath); - 7 -
gzie inexpath to zmienna String, która zawiera ścieżkę o uprzenio utworzonego ineksu. Dla zefiniowania parsera zapytania, musisz wiezieć, które pola okumentu mają być analizowane po wzglęem zgoności z zapytaniem, a także jakiego typu parser zostanie użyty o parsowania zapytania (musi być on taki sam, jak parser okumentu), np.: Analyzer analyzer = new StanarAnalyzer(); QueryParser qparser = new QueryParser("content", analyzer); Parsowanie zapytania i wyszukiwanie okumentów obywa się za pomocą wykorzystania opowienich meto klasy QueryParser i InexSearcher: Query query = qparser.parse(querystring); Hits hits = isearch.search(query); Metoa search zwraca ranking aekwatnych okumentów (poprzez klasę Hits). Znalezienie wyników powinno być zaimplementowane w funkcji processquery(). Klasa Hits uostępnia iterator, z użyciem którego można przegląać aekwatne la zapytania okumenty i miary poobieństwa. Należy wypisać te wartości, jak w poniższym przykłazie. Przykłaowe wyjście progamu: Please enter your query: (lab6 to quit) queen 12 result(s) foun Shakespeare/0ws4210.txt : 0.09321891 Shakespeare/0ws1410.txt : 0.04346354 Shakespeare/0ws0110.txt : 0.038874973 Shakespeare/0ws0210.txt : 0.038874973 Shakespeare/0ws2610.txt : 0.038874973 Shakespeare/0ws0410.txt : 0.03366671 Shakespeare/0ws0310.txt : 0.027488753 Shakespeare/0ws1710.txt : 0.027488753 Shakespeare/0ws0910.txt : 0.019437486 Shakespeare/0ws1210.txt : 0.019437486 Shakespeare/0ws1910.txt : 0.019437486 Shakespeare/0ws3910.txt : 0.019437486 Please enter your query: (lab6 to quit) arthur 2 result(s) foun Shakespeare/0ws1410.txt : 0.16537577 Shakespeare/0ws2110.txt : 0.033757187 W razie potrzeby okumentacja Lucene API jest o znalezienia w sieci, ale o wykonania tego zaania wystarczy realizacja (ze zrozumieniem) poleceń z powyższego opisu. - 8 -
3. [4] Zaanie polega na wykorzystaniu Lucene o zaimplementowania meto ineksowania i wyszukiwania. Kolekcją okumentów jest RSS utworzony z BBC News bbc_rss_fee.xml. Dana jest klasa o parsowania RSS (RSSFeeParser), która zwraca okumenty jako obiekty Javy zawierające istotne z punktu naszego zastosowania pola: tytuł, opis oraz atę publikacji (klasa RssFeeDcoument). Klasą, którą należy uzupełnić to LuceneSearchApp (funkcje inex oraz search): Na etapie ineksowania okumentów trzy pola (tytuł, opis, ata) muszą być rozróżniane tak by zapytania mogły otyczyć zawartości poszczególnych pól. Metoa wyszukiwania powinna zwracać tytuły okumentów, które opowiaają zapytaniu. Szkielet klasy LuceneSearchApp zawiera funkcję main o testowania Twojej implementacji meto ineksowania oraz wyszukiwania. Wywoływana jest w niej kolejno: istniejąca funkcja parsowania RSS (ścieżka o pliku RSS jest poawana jako argument wywołania w linii komen), funkcja ineksowania okumentów oraz funkcje generujące kolejne zapytania testowe i wypisujące wynik. W pliku test-output.txt ane jest wzorcowe wyjście, o którego możesz przyrównywać swoje rozwiązanie. Implementacja wyszukiwania powinna uwzglęniać następujące parametry: słowa, które występują w tytule (operator logiczny AND); słowa, które nie występują w tytule (operator logiczny NOT); słowa,które występują w opisie (operator logiczny AND); słowa, które nie występują w opisie (operator logiczny NOT); zakres aty publikacji (operator logiczny AND la różnych at) aty w formacie RRRR-MM-DD zakresy powinny być typu [początek, koniec], by ata końcowa i początkowa były zawarte w przeziale (a nie typu (start, koniec)) jeżeli ata początkowa lub końcowa nie jest poana, przyjmuje się przeział otwarty z tej strony (nieograniczony) Funkcja search przyjumuje więc na wejście sześć parametrów: tablice stringów intitle, notintitle, indescription, notindescription oraz stringi startdate oraz endate. Jako rozwiązanie trzeba przesłać ko źrółowy klasy LuceneSearchApp. Do realizacji zaania stosowana była wersja Lucene 4.1.0 (klasy potrzebne w rozwiązaniu są zawarte w lucencore-4.1.0.jar oraz lucene-analyzers-common-4.1.0.jar). Można też wykorzystać na pewno Lucene 4.2.0. Doatkowe 2 punkty (bonusowe w stosunku o 4 przewizianych za to zaanie i nie zwiększające sumarycznej liczby punktów) można zobyć, implementując własną klasę EziScoreQuery (rozszerzającą CustomScoreQuery), która w metozie getcustomscoreprovier obliczałaby inną niż omyślna funkcja poobieństwa. Stanarowo przy utworzeniu nowego InexSearcher, Lucene wykorzystuje DefaultSimilarity, które jest miarą kosinusową opartą na tf-if. Dla uproszczenia możemy uznać, że funkcja bęzie wykorzystywana tylko la zapytań o słowa, które występują w tytule i/lub opisie i może polegać na prostym zliczaniu tych słów (okument tym barziej jest poobny, im częściej słowa z zapytania w nim występują). Dla przejrzystości i ułatwienie sprawzania, wersja stanarowa rozwiązania (bez nowej funkcji poobieństwa) niech zostanie w LuceneSearchApp, a la wersji rozszerzonej zróbcie nową klasę LuceneSearchSimApp. Czas przesłania zaań 24 listopaa lub 1 grunia (patrz następne zaanie). - 9 -