Obliczanie odległości Levenshteina szybko

24

Biorąc pod uwagę ogromną bazę dozwolonych słów (posortowane alfabetycznie) i słowo, znajdź słowo z bazy danych, która jest najbliższa podanemu słowu pod względem odległości Levenshteina.

Naiwnym podejściem jest oczywiście po prostu obliczenie odległości levenshteina między danym słowem a wszystkimi słowami w słowniku (możemy przeprowadzić wyszukiwanie binarne w bazie danych przed obliczeniem odległości).

Zastanawiam się, czy istnieje bardziej wydajne rozwiązanie tego problemu. Może jakaś heurystyka, która pozwala nam zmniejszyć liczbę słów do wyszukania lub optymalizację algorytmu odległości Levenshteina.

Linki do artykułów na ten temat są mile widziane.

Joshua Herman
źródło

Odpowiedzi:

16

To, o co pytasz, to problem wyszukiwania w sąsiedztwie pod odległością edycji. Nie wspomniałeś, czy interesują Cię wyniki teoretyczne, czy heurystyka, więc odpowiem na to pierwsze.

Odległość edycji jest nieco nieprzyjemna w przypadku budowania struktur wyszukiwania w sąsiedztwie. Główny problem polega na tym, że jako metryka zachowuje się (podobnie) jak inne dobrze znane złe metryki, takie jak w celu zmniejszenia wymiarów i przybliżenia. Jest dużo pracy do przeczytania na ten temat, a twoim najlepszym źródłem jest zestaw artykułów Alexa Andoniego : podążając za wskazówkami do tyłu (na przykład z jego artykułu z FOCS 2010) otrzymasz dobry zestaw źródeł.1

Suresh Venkat
źródło
1
Wszystko, co wiem o przestrzeniach metrycznych, pochodzi z semantyki, więc pytanie: czy są jakieś przyzwoite (dla dowolnej wartości przyzwoitej) wartości metrycznej Levenshteina w ultrametryce? Od razu może to spowodować powstanie algorytmu drzewa binarnego.
Neel Krishnaswami
Nie jestem do końca pewien. Podejrzewam, że odpowiedź jest przecząca, ale nie mam na co wskazywać.
Suresh Venkat
Drugi artykuł na boytsov.info/pubs jest dobrym przeglądem możliwych rozwiązań wyszukiwania sąsiadów w ramach odległości edytowania Levenshtein i Damereau-Levenshtein.
a3nm
@NeelKrishnaswami Osadzenie w ultrametrycznym miałoby zniekształcenie co najmniej gdzie d jest długością łańcucha. Wynika to z dolnej granicy zniekształceń dla osadzania w L 1 z powodu Krauthgamer i Rabani , ponieważ ultrametria osadza się izometrycznie w przestrzeni euklidesowej, która osadza się izometrycznie w L 1 . Ω(logd)dL1L1
Sasho Nikolov,
5

Jeśli masz niewielką liczbę błędnych edycji, które będziesz tolerować, możesz spróbować użyć drzewa kropek . Oświadczenie: Napisałem ten papier, ale rozwiązuje to, czego chcesz: ma wysoki koszt miejsca na dysku, ale zapytania są naprawdę szybkie.

Ogólnie rzecz biorąc, lepiej spojrzeć na to odwrotnie: masz indeks wszystkich słów w słowniku. Teraz, jeśli słowo wejściowe w znajduje się w słowniku, przestań. W przeciwnym razie wygeneruj wszystkie warianty w odległości 1 i poszukaj ich. Jeśli ich tam nie ma, poszukaj odmian w odległości 2 itd.

Istnieje kilka ulepszeń tego podstawowego pomysłu.

luispedro
źródło
1
Powinieneś dołączyć link do swojego odtwarzalnego archiwum badań dla tego artykułu .
Dan D.
4

O(mk+1σk)mσk

Jouni Sirén
źródło
4

Odpowiedziałem na bardzo podobne pytanie na cs.stackexchange.com ( /cs//a/2096/1490 ), a potem znalazłem to pytanie. Istnieje odpowiedź na przybliżone wyszukiwanie w sąsiedztwie w odległości edycji (tj. Algorytm generuje ciąg znaków, który jest w przybliżeniu tak blisko ciągu zapytania, jak najbliższy sąsiad ciągu zapytania). Piszę tutaj, ponieważ nie znajduję żadnego z odniesień, które tam podałem w podanych tutaj odpowiedziach.

Sasho Nikolov
źródło
3

Myślę, że to, czego chcesz, to algorytm Wagnera-Fischera: https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorytm Kluczową sprawą jest to, że skoro słownik, przez który przechodzisz, jest posortowany, dwa kolejne słowa bardzo często mają długi prefiks, więc nie trzeba aktualizować całej macierzy dla każdego obliczenia odległości.

Björn Lindqvist
źródło
2

Możesz użyć Czy miałeś na myśli?

Następnie znajdź odległość Levenshteina między odpowiedzią zwróconą przez „Czy miałeś na myśli” a ciągiem wejściowym za pomocą programowania dynamicznego.

Pratik Deoghare
źródło
Nie rozumiem tej odpowiedzi. Pytanie dotyczy tego, w jaki sposób można skutecznie znaleźć słowo w dużym słowniku z bliską odległością Levenshteina od podanych danych wejściowych, a nie o tym, jak obliczyć odległość Levenshteina lub o porównaniu do wyniku sprawdzania pisowni czarnej skrzynki ...
Huck Bennett
@Huck Bennett: Myślałem, że @Grigory Javadyan buduje Did you mean?funkcję. Poza tym Did you mean?zwraca słowo, które jest bardzo bliskie podanemu wejściowi i robi to całkiem skutecznie. :)
Pratik Deoghare
Myślę, że twoje pomysły są dobre, ale wydaje się, że Grigory prosi o coś głębszego i bardziej szczegółowego.
Huck Bennett
@Huck Bennett: Tak, masz rację! :)
Pratik Deoghare
-1

Jednym ze sposobów jest szkolenie modelu uczenia maszynowego do mapowania słów na wektory i mapowania odległości lewenshteina do odległości euklidesowej. Następnie możesz zbudować KDTree z wektorów dla słownika, którego chcesz użyć. Utworzyłem notatnik jupyter, który robi to tutaj: https://gist.github.com/MichaelSnowden/9b8b1e662c98c514d571f4d5c20c3a03

Zgodnie z komentarzami DW:

  1. procedura treningowa = opadanie gradientu stochastycznego z gradientami adaptacyjnymi
  2. funkcja straty = średni błąd kwadratu między prawdziwą odległością edycji a odległością euklidesową
  3. dane treningowe = losowe ciągi o długości od 1 do 32 znaków (można poprawić o dane, które pasują do rzeczywistego rozkładu typowych literówek)
  4. wyniki ilościowe: po treningu dla około 150 epok z rozmiarem partii 2048 (czas na ścianie = około jednej minuty), przy użyciu osadzania słów o 512 wymiarach, z jedną ukrytą warstwą, średni błąd bezwzględny między rzeczywistą odległością edycji a przewidywaną odległością edycji wynosi około 0,75, co oznacza, że ​​przewidywany dystans edycji wynosi mniej więcej jeden znak

Podsumowanie struktury modelu:

  1. Utwórz wyuczone osadzanie dla każdego znaku, w tym znaku zerowego (używanego później do wpisywania tekstu po prawej stronie poniżej limitu znaków)
  2. Wypełnij prawą stronę tekstu znakiem null, aż osiągnie limit znaków (32)
  3. Połącz te osadzenia
  4. Przeprowadź osadzanie przez sieć neuronową ze sprzężeniem zwrotnym, aby utworzyć osadzanie słów niższego wymiaru (512-wymiarowe)
  5. Zrób to dla obu słów
  6. Znajdź odległość euklidesową między wektorami
  7. Ustaw stratę jako średni błąd kwadratu między prawdziwą odległością Levenshteina a odległością euklidesową

Moje dane treningowe to tylko losowe ciągi, ale myślę, że wyniki mogłyby się naprawdę poprawić, gdyby dane treningowe były parami (literówka / poprawne słowo). Skończyło się na tym, że używałem, /usr/share/dict/wordsponieważ jest powszechnie dostępny.

michaelsnowden
źródło
2
Jak trenujesz model ML, aby słowa znajdujące się w pobliżu na mapie odległości Levenshteina przypominały wektory? Jakiej procedury treningowej i funkcji straty używasz do tego? Czy możesz streścić metodę w swojej odpowiedzi, aby odpowiedź była nadal przydatna, nawet jeśli link przestanie działać, i abyśmy nie musieli przekopać się przez Twój notatnik, aby zrozumieć metodę, której używasz? Czy możesz również ocenić, jak dobrze działa to w jakiś sposób ilościowy? Czy to jest lepsze niż alternatywy?
DW
W tej chwili jest to (jak sądzę) kiepskie dopasowanie do CSTheory. Oznacza to, że nie ma pojęcia, co jest konkretnie sugerowane, i nie ma teoretycznego uzasadnienia.
Klemens C.
@DW Przepraszam za to - dokonałem dość obszernej edycji, która powinna być wyczerpująca, w przypadku gdy link zejdzie (lub jeśli nie chcesz przeszukiwać notesu). Chociaż to nie jest tak naprawdę teoria CS, ponieważ nie są to badania, myślę, że jest to praktyczne podejście, ponieważ jest szybkie i łatwe zarówno do treningu, jak i wnioskowania.
michaelsnowden
1
Trenujesz na losowych strunach. Oczekiwana odległość Levenshteina między dwoma takimi strunami będzie w przybliżeniu długości dłuższej struny. Dlatego bardzo łatwo jest oszacować tę odległość na losowych ciągach, ale nie jest to przydatne w przypadku danych rzeczywistych. Podejrzewam, że twoje osadzanie może po prostu zakodować długość łańcucha, a zatem mógłbyś stworzyć wymyślny sposób na zrobienie czegoś trywialnego i bezużytecznego. Jest to problem z używaniem ML; jest bardzo wrażliwy na używaną funkcję utraty.
DW
@DW Jeśli spojrzysz na wyniki w notatniku, pobieranie zakończyło się powodzeniem przyzwoitymi wynikami - nie tylko ciągami o tej samej długości. Naprawdę zachęcam do przejrzenia go. Nie nazwałbym tego trywialnym i bezużytecznym.
michaelsnowden