1 Kwantyzacja wektorowa Kompresja danych Streszczenie Studia Dzienne Wykład 10, 28.04.2006 Kwantyzacja wektorowa: dane dzielone na bloki (wektory), każdy blok kwantyzowany jako jeden element danych. Ogólny schemat kwantyzacji wektorowej dla L-wymiarowych wektorów: ustalamy M wektorów L-wymiarowych jako wartości rekonstrukcji, nazywanych też wektorami kodowymi; każdemu z wektorów kodowych przyporządkowujemy indeks w tablicy tych wektorów, zwanej słownikiem; dane dzielimy na bloki o długości L; dla każdego bloku danych znajdujemy najbliższy mu wektor kodowy i on staje się skwantyzowaną wartością tego bloku. Miary jakości kwantyzatora wektorowego: (średniokwadratowy) błąd kwantyzacji σ 2 q, w którym odległość między wektorami X = (x 1... x L ) i Y = (y 1... y L ) to X Y ; gdzie X 2 = L i=1 x 2 i. (średnia) liczba bitów na próbkę: równa L rozmiar kwantyzowanych wektorów. log K, gdzie K to rozmiar słownika, a L Poglądowe przykłady przewagi kwantyzacji wektorowej nad skalarną: dane skorelowane (np. pary (wzrost,waga), bloki sąsiednich pikseli); dane nieskorelowane: wartości odwzorowane na konkretny wektor kodowy nie musza być zdefiniowane w postaci przedziałów, prostopadłościanów. (p. wektorowa interpretacja kwantyzacji skalarnej). Algorytm Lindego-Buzo-Graya (LBG), inaczej Generalized Lloyd Algorithm (GLA) Dane: zbiór wektorów uczących {X n } N n=1, próg błędu ε, M liczba wektorów kodowych takie, że N M. Cel: minimalizacja średniej odległości między wektorem uczącym a reprezentującym go wektorem kodowym. 1. Wybierz dowolnie zbiór wektorów kodowych {Y (0) i } M i=1. Niech k = 0, D (0) = 0. 1
2. Określ obszary kwantyzacji V 1,..., V M w następujący sposób: V (k) i Załóżmy, że V (k) i = {X n d(x n, Y (k) i ) < d(x n, Y (k) j ) dla każd. j i} dla każdego i [1, M]. 3. Oblicz średnią odległość między wektorami uczącymi a odpowiadającymi im wektorami kodowymi D (k) = 1 N M i=1 X j V (k) i d(x j, Y (k) i ). 4. Jeśli D(k) D (k 1) D (k) < ε, zakończ obliczenia. 5. niech nowe wektory kodowe to średnie wartości obszarów kwantyzacji: Y (k+1) j = 1 V (k) j 6. Niech k := k + 1, przejdź do kroku 2 Wynik algorytmu LBG: X i V (k) j X i dla j [1, M]. zmierza do minimum lokalnego (funkcji błędu, gdzie zmiennymi są wartości X 1,..., X M ; rozwiązanie optymalne - problem NP-trudny. Problemy techniczne w algorytmie LBG: Wybór poczatkowych wektorów kodowych. Technika podziałów: zaczynamy z jednym początkowym wektorem kodowym, po zastosowaniu algorytmu LBG dołączamy drugi wektor, uzyskany z pierwszego przez dodanie ustalonego wektora zaburzeń γ. Mając 2 i wektorów kodowych, stosujemy LBG i uzyskujemy 2 i+1 wektorów przez dodanie zaburzenia do każdego z wynikowych wektorów kodowych. Algorytm par najbliższych sasiadów (PNN): zaczynamy ze zbiorem wektorów kodowych równym zbiorowi uczącemu. W każdym kroku (aż do uzyskania M wektorów) wybieramy 2 najbliższe wektory kodowe i zastępujemy je ich średnią i stosujemy algorytm LBG. Dokładniej, w metodzie zaproponowanej przez Equitza (1989): 2
łączymy takie dwa wektory kodowe, które zminimalizują wzrost zniekształcenia (D). korzystamy tu z własności (Equitz), że połączenie obszarów reprezentowanych przez Y i i Y j daje następujący wzrost zniekształcenia: n i n j n i + n j Y i Y j 2, gdzie n i i n j to liczba elementów w obszarze V i i V j. Problem pustych obszarów kwantyzacji. Metoda: usuwamy wektor kodowy odpowiadający pustemu obszarowi kwantyzacji, zastępujemy go losowo wybranym wektorem uczącym z obszaru kwantyzacji, który zawiera najwięcej wektorów. Typowe zastosowanie: kompresja obrazów (wektory to bloki rozmiaru n m, co umożliwia wykorzystanie korelacji poziomych i pionowyc); ograniczeniem jest wzrost rozmiaru słownika i dobór słownika (statyczny czy projektowany dla każdego obrazka osobno, co wymaga dołączenia słownika do danych). Patrz: http://www.data-compression.com/vqanim.shtml 1.1 Kwantyzatory o strukturze drzewiastej Idea: chcemy zmniejszyć liczbę porównań potrzebną do ustalenia obszaru kwantyzacji, do którego należy dany wektor: w trakcie działania algorytmu LBG: standardowo konieczne policzenie N M odległości między punktami w każdej iteracji; w trakcie kodowania: standardowo, każdy kodowany wektor musi być porównany ze wszystkimi N wektorami kodowymi. Metoda: tworzymy zbalansowane drzewo binarne, w każdym węźle umieszczamy wektor, w liściach wektory kodowe; dla ustalenia obszaru kwantyzacji danego wektora Z, w każdym kroku przechodzimy do tego dziecka aktualnego wierzchołka w drzewie, który znajduje się bliżej Z (zaczynając od korzenia). Cechy: czas znalezienia obszaru kwantyzacji dla danego wektora redukuje się z M do 2 log M ; wzrost zniekształceń: podział na obszary kwantyzacji nie zawsze przyparządkowuje wektor do obszaru o najbliższym mu wektorze kodowym; 3
wzrost pamięci: oprócz wektorów kodowych, potrzeba M 1 wektorów w wierzchołkach wewnętrznych. Ogólna metoda tworzenia kwantyzatora drzewiastego o głębokości k dla zbioru wektorów X : jeśli k = 0: utwórz kwantyzator z jednym wektorem kodowym równym średniej z wektorów z X ; wybieramy dwa początkowe wektory kodowe: Y 1 : średnią S z wektorów ze zbioru X ; Y 2 : wektor otrzymany z S przez dodanie zaburzenia; tworzymy kwantyzator z dwoma wektorami kodowymi (stosując np. algorytm LBG) Y 1, Y 2 ; dzielimy X na X 1, X 2 takie, że X 1 składa się z wektorów uczących bliższych Y 1 a X 2 składa się z wektorów uczących bliższych Y 2 tworzymy (osobno!) kwantyzatory o głębokości k 1 dla zbiorów X 1 i X 2. Modyfikacje: przycinanie usuwanie obszarów kwantyzacji, do których należy najmniej wektorów uczących (zmniejszanie średniej długości słowa kodowego kosztem wzrostu zniekształceń). 1.2 k-d-drzewa Cel: implementacja oryginalnej metody LBG! przyspieszenie wyszukiwania najbliższego wektora kodowego (Y ) dla punktu treningowego X (NNS=nearest neighbor search); nie wymagamy zmniejszenia złożoności najgorszego przypadku, chcemy poprawić złożoność średniego przypadku. 4
Konstrukcja k-d-drzewa Dane Zbiór punktów S = {X 1,..., X n } w przestrzeni d wymiarowej. Algorytm: 1. Jeśli n = 1: utwórz liść zawierający ten wierzchołek, zakończ. 2. Podziel zbiór S na dwa równoliczne podzbiory S 1 i S 2 : wybierz jedną ze współrzędnych i oraz wartość p taką, że dla połowy punktów i-ta współrzędna jest mniejsza od p. 3. Utwórz k-d-drzewa dla S 1 i S 2. 4. utwórz wierzchołek symbolizująccy podział w punkcie p wg współrzędnej i; jego lewym i prawym poddrzewem są k-d-drzwa dla S 1 i S 2. Strategie wyboru współrzędnej do podziału wybierz współrzędną o największej rozpiętości (maksymalnej różnicy między największą i najmniejszą wartością w zbiorze S); round-robin: współrzędne 1, 2,..., d, 1, 2,..., d, 1, 2... Wynik: głębokość drzewa: log n; złożoność czasowa tworzenia drzewa: sortowanie po wszystkich współrzędnych (tylko raz): O(dn log n); wybór współrzędnej: O(d); podział wg wybranej współrzędnej O(dn); razem: O(dn log n). Struktura wierzchołka w k-d-drzewie (node): axis: współrzędna, wg której dzielimy punkty w tym wierzchołku; value: wartość graniczna (punkty o mniejszej wartości współrzędnej axis w lewym poddrzewie); left, right: lewe i prawe poddrzewo; point: punkt przechowywany w wierzchołku, gdy wierzchołek jest liściem. Szukanie najbliższego punktu Dane: k-d-drzewo dla zbioru punktów S = {X 1,..., X n }; punkt q. Wynik: element S położony najbliżej punktu q. Algorytm: NNS(q: point; n: node; var p: point; var w: real) Cel: 5
dla punktu q chcemy znaleźć najbliższy punkt w zbiorze opisanych w (pod)drzewie o korzeniu n, o ile taki punkt jest w odległości mniejszej niż w; jeśli najbliższy punkt jest w odległości większej niż w: p i w mają pozostać bez zmian; jeśli najbliższy punkt jest w odległości mniejszej niż w: p ma wskazywać na najbliższy punkt, w ma być równe odległości od q do tego punktu. wywołanie początkowe: NNS(q,root,?,infinity) 6
NNS(q: point; n: node; var p: point; var w: real) 1. Jeśli n jest liściem: Jeśli d(n.point, q) < w: w d(n.point, q), p n.point Zakończ. 2. Jeśli w = : Jeśli q(n.axis) n.value { przec. z lewym} NNS(q,n.left,p,w) Jeśli q(n.axis) + w > n.value: NNS(q,n.right,p,w). Jeśli q(n.axis) > n.value { przec. z prawym} 3. Jeśli w : UWAGI: NNS(q,n.right,p,w) Jeśli q(n.axis) w n.value: NNS(q,n.left,p,w). Jeśli q(n.axis) w n.value: NNS(q,n.left,p,w). Jeśli q(n.axis) + w > n.value: NNS(q,n.right,p,w). każde wywołanie rekurencyjne modyfikuje wartości p i w (jeśli znajdzie bliższy punkt niż dotychczas najbliższy); q(n.axis) oznacza tutaj współrzędna n.axis punktu q; a n.axix to współrzędna wg której dokonywany jest podział w wierzchołku n. WŁASNOŚĆ: NNS działa w czasie oczekiwanym log n. Zastosowanie w LBG: w każdej iteracji tworzymy kd-tree dla aktualnego zbioru wektorów kodowych Y 1,..., Y M : czas O(M log M). aby ustalić obszar kwantyzacji dla punktu X i, znajdujemy najbliższy mu punkt w kd-tree: czas O(log M). czas średni całej iteracji LBG: O(M log M + N log M) = O(N log M) (ponieważ N > M). 7