Fuzzy matching umožňuje identifikovat nepřesné shody cílové položky. Je základním kamenem mnoha rámců vyhledávačů a jedním z hlavních důvodů, proč můžete získat relevantní výsledky vyhledávání, i když máte v dotazu překlep nebo jiný slovosled.

Jak se dalo očekávat, existuje mnoho algoritmů, které lze použít pro fuzzy vyhledávání v textu, ale prakticky všechny rámce vyhledávačů (včetně bleve) používají pro fuzzy porovnávání řetězců především Levenshteinovu vzdálenost:

Levenshteinova vzdálenost

Známá také jako editační vzdálenost, je to počet transformací (vymazání, vložení nebo nahrazení) potřebných k transformaci zdrojového řetězce na cílový. Pokud je například cílový výraz „book“ a zdrojový „back“, bude třeba změnit první „o“ na „a“ a druhé „o“ na „c“, čímž získáme Levenshteinovu vzdálenost 2. Edit Distance se velmi snadno implementuje a je oblíbenou výzvou při pohovorech o kódu (Implementace Levenshteinovy vzdálenosti v jazycích JavaScript, Kotlin, Java a mnoha dalších najdete zde).

Dále některé frameworky podporují také Damerauovu-Levenshteinovu vzdálenost:

Damerauova-Levenshteinova vzdálenost

Jedná se o rozšíření Levenshteinovy vzdálenosti, které umožňuje jednu operaci navíc:

Příklad: TSAR na STAR

Damerau-Levenshteinova vzdálenost = 1 (Prohození pozic S a T stojí jen jednu operaci)

Levenshteinova vzdálenost = 2 (Nahradit S za T a T za S)

Fuzzy porovnávání a relevance

Fuzzy porovnávání má jeden velký vedlejší efekt: zamotává se s relevancí. Přestože Damerau-Levenshteinův algoritmus zohledňuje většinu běžných uživatelských překlepů, může zahrnovat i značné množství falešně pozitivních výsledků, zejména pokud používáme jazyk s průměrem pouhých 5 písmen na slovo, jako je angličtina. Proto se většina rámců vyhledávačů raději drží Levenshteinovy vzdálenosti. Podívejme se na reálný příklad:

Nejprve použijeme tuto sadu dat z filmového katalogu. Vřele ho doporučuji, pokud si chcete pohrát s fulltextovým vyhledáváním. Poté vyhledáme filmy, které mají v názvu slovo „kniha“. Jednoduchý kód by vypadal následovně:

Java

1
2

.

3
4
5
6

String indexName = „movies_index“;
MatchQuery query = SearchQuery.match(„book“).field(„title“);
SearchQueryResult result = movieRepository.getCouchbaseOperations().getCouchbaseBucket().query(
new SearchQuery(indexName, query).highlight().limit(6));
printResults(movies);

Výše uvedený kód přinese následující výsledky:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

1) Zlodějka knih
skóre: 4.826942606027416
Místa zásahu:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
—————————–

Ve výchozím nastavení výsledky nerozlišují velká a malá písmena, ale toto chování můžete snadno změnit vytvořením nových indexů s různými analyzátory.

Nyní přidáme do našeho dotazu možnost fuzzy párování nastavením fuzzy na hodnotu 1 (Levenshteinova vzdálenost 1), což znamená, že „book“ a „look“ budou mít stejnou relevanci.

Java

1
2
3

.

4
5
6

String indexName = „movies_index“;
MatchQuery query = SearchQuery.match(„book“).field(„title“).fuzziness(1);
SearchQueryResult result = movieRepository.getCouchbaseOperations().getCouchbaseBucket().query(
new SearchQuery(indexName, query).highlight().limit(6));
printResults(movies);

A zde je výsledek fuzzy vyhledávání:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

1) Hák
skóre: 1.1012248063242538
Místa zásahu:
pole:název
termín:háček
fragment:
—————————–
2) Here Comes the Boom
skóre: 0.7786835148361213
místa zásahu:
místo zásahu:
místo zásahu
pole:název
termín:boom
fragment:
—————————–
3) Podívejte se, kdo taky mluví
skóre: 0.7047266634351538
Místa zásahu:
field:title
term:look
fragment:
—————————–
4) Podívejte se, kdo mluví
skóre: 0.7047266634351538
Místa zásahu:
field:title
term:look
fragment:
—————————–
5) Zlodějka knih
skóre: 0.52288117537184
Místa zásahu:
pole:název
termín:kniha
fragment:
—————————–
6) Kniha Eli
skóre: 0.52288117537184
Místa zásahu:
pole:název
termín:kniha
fragment:
—————————–

Nyní je film s názvem „Hook“ úplně prvním výsledkem hledání, což nemusí být přesně to, co uživatel při hledání „Book“ očekává.

Jak minimalizovat falešně pozitivní výsledky při fuzzy vyhledávání

V ideálním světě by uživatelé při hledání nikdy neudělali žádný překlep. V takovém světě však nežijeme, a pokud chcete, aby uživatelé měli příjemný zážitek, musíte zvládnout alespoň editační vzdálenost 1. Proto skutečná otázka zní: Jak můžeme provést fuzzy porovnávání řetězců a zároveň minimalizovat ztrátu relevance?

Můžeme využít jednu vlastnost většiny vyhledávacích rámců: Shoda s nižší editační vzdáleností obvykle získá vyšší skóre. Tato vlastnost nám umožňuje spojit tyto dva dotazy s různou úrovní fuzzy do jednoho:

Java

1
2
3
4
5
6
7
8

String indexName = „movies_index“;
String word = „Book“;
MatchQuery titleFuzzy = SearchQuery.match(word).fuzziness(1).field(„title“);
MatchQuery titleSimple = SearchQuery.match(word).field(„title“);
DisjunctionQuery ftsQuery = SearchQuery.disjuncts(titleSimple, titleFuzzy);
SearchQueryResult result = movieRepository.getCouchbaseOperations().getCouchbaseBucket().query(
new SearchQuery(indexName, ftsQuery).highlight().limit(20)); printResults(result);

Tady je výsledek výše uvedeného fuzzy dotazu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

1) Zlodějka knih
skóre: 2.398890092610849
Místa zásahu:
pole:název
termín:kniha
fragment:
—————————–
pole:název
termín:kniha
fragment:
—————————–
2) The Book of Eli
skóre: 2.398890092610849
místa zásahu:
pole:název
termín:kniha
fragment:
—————————–
pole:název
termín:kniha
fragment:
—————————–
3) Kniha života
skóre: 2.398890092610849
Místa zásahu:
pole:název
termín:kniha
fragment:
—————————–
pole:název
termín:kniha
fragment:
—————————–
4) Černá kniha
skóre: 2.398890092610849
místa zásahu:
field:title
term:book
fragment:
—————————–
field:title
term:book
fragment:
—————————–
5) Kniha džunglí
skóre: 2.398890092610849
místa zásahu:
field:title
term:book
fragment:
—————————–
field:title
term:book
fragment:
—————————–
6) Kniha džunglí 2
skóre: 1.958685557004688
místa zásahu:
field:title
term:book
fragment:
—————————–
field:title
term:book
fragment:
—————————–
7) National Treasure: Book of Secrets
score: 1.6962714808368062
Hit Locations:
field:title
term:book
fragment:
—————————–
field:title
term:book
fragment:
—————————–
8) American Pie Presents: The Book of Love
skóre: 1.517191317611584
Místa zásahu:
field:title
term:book
fragment:
—————————–
field:title
term:book
fragment:
—————————–
9) Háček
skóre: 0.5052232518679681
Místa zásahu:
pole:název
termín:háček
fragment:
—————————–
10) Here Comes the Boom
skóre: 0.357246781294941
Místa zásahu:
field:title
term:tree
fragment:
—————————–
11) Podívejte se, kdo taky mluví
skóre: 0.32331663301992025
Místa zásahu:
field:title
term:look
fragment:
—————————–
12) Podívejme se, kdo mluví
skóre: 0.32331663301992025
Místa zásahu:
field:title
term:look
fragment:
—————————–

Jak vidíte, tento výsledek je mnohem bližší tomu, co by uživatel mohl očekávat. Všimněte si, že nyní používáme třídu DisjunctionQuery, disjunkce jsou ekvivalentem operátoru „OR“ v SQL.

Co bychom ještě mohli zlepšit, abychom omezili negativní vedlejší efekt algoritmu fuzzy porovnávání? Znovu analyzujme náš problém, abychom pochopili, zda potřebuje další vylepšení:

Již víme, že fuzzy vyhledávání může přinést některé neočekávané výsledky (např. Book -> Look, Hook). Vyhledávání jediného výrazu je však obvykle hrozný dotaz, protože nám sotva napoví, čeho přesně se uživatel snaží dosáhnout.

Až Google, který má pravděpodobně jeden z nejpropracovanějších používaných fuzzy vyhledávacích algoritmů, neví přesně, co hledám, když hledám „tabulka“:

Jaká je tedy průměrná délka klíčových slov ve vyhledávacím dotazu? Pro zodpovězení této otázky uvedu graf z prezentace Randa Fishkina z roku 2016. (Je to jeden z nejznámějších guruů ve světě SEO)

Podle výše uvedeného grafu má ~80 % vyhledávacích dotazů 2 a více klíčových slov, zkusme tedy vyhledat film „Černá kniha“ pomocí fuzziness 1:

Java

1
2
3
4
5
6

String indexName = „movies_index“;
MatchQuery query = SearchQuery.match(„Black Book“).field(„title“).fuzziness(1);
SearchQueryResult result = movieRepository.getCouchbaseOperations().getCouchbaseBucket().query(
new SearchQuery(indexName, query).highlight().limit(12));
printResults(movies);

Result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

1) Černá kniha
skóre: 0.6946442424065404
Místa zásahu:
pole:název
termín:kniha
fragment:
—————————–
field:title
term:black
fragment:
—————————–
2) Háček
skóre: 0.40139742528039857
místa zásahu:
pole:název
termín:háček
fragment:
—————————–
3) Útok na blok
skóre: 0.2838308365090324
Místa zásahu:
pole:název
termín:blok
fragment:
—————————–
4) Here Comes the Boom
skóre: 0.2838308365090324
Místa zásahu:
pole:název
termín:boom
fragment:
—————————–
5) Podívejte se, kdo to taky mluví
skóre: 0.25687349813115684
Místa zásahu:
field:title
term:look
fragment:
—————————–
6) Podívejme se, kdo mluví
skóre: 0.25687349813115684
Místa zásahu:
Podívejme se, kdo mluví:
field:title
term:look
fragment:
pole:název
termín:prázdný
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:kniha
fragment:
pole:název
termín:zpět
fragment:
—————————–

Není špatné. Jako první výsledek se nám zobrazil hledaný film. Disjunkční dotaz by však stále přinesl lepší sadu výsledků.

Ale i tak to vypadá, že tu máme novou pěknou vlastnost; vedlejší efekt fuzziness matching mírně klesá s rostoucím počtem klíčových slov. Hledání „Black Book“ s fuzziness 1 může stále přinést výsledky jako back look nebo lack cook (některé kombinace s editační vzdáleností 1), ale ty pravděpodobně nebudou skutečnými názvy filmů.

Vyhledání výrazu „book eli“ s fuzziness 2 by stále přineslo tento výraz jako třetí výsledek:

Java

1
2
3
4
5
6

String indexName = „movies_index“;
MatchQuery query = SearchQuery.match(„book eli“).field(„title“).fuzziness(2);
SearchQueryResult result = movieRepository.getCouchbaseOperations().getCouchbaseBucket().query(
new SearchQuery(indexName, query).highlight().limit(12));
printResults(movies);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

1) Ed Wood
skóre: 0.030723793900031805
Místa zásahu:
pole:název
termín:wood
fragment:
—————————–
field:title
term:ed
fragment:
—————————–
2) Man of Tai Chi
skóre: 0.0271042761982626
místa zásahu:
pole:název
termín:chi
fragment:
—————————–
field:title
term:tai
fragment:
—————————–
3) Kniha Eli
skóre: 0.02608335441670756
místa zásahu:
pole:název
termín:eli
fragment:
—————————–
field:title
term:book
fragment:
—————————–
4) Dobrá lež
skóre: 0.02439822770591834
Místa zásahu:
pole:název
termín:lež
fragment:
—————————–
field:title
term:good
fragment:
—————————–

Jelikož však průměrné anglické slovo má 5 písmen, NEDOPORUČUJI používat editační vzdálenost větší než 2, pokud uživatel nehledá dlouhá slova, která se snadno překlepnou, jako například „Schwarzenegger“ (alespoň pro neněmecké nebo nerakouské uživatele).

Závěr

V tomto článku jsme se zabývali porovnáváním rozmazanosti a tím, jak překonat jeho hlavní vedlejší efekt, aniž bychom narušili jeho relevanci. Připomínáme, že fuzzy matching je jen jednou z mnoha funkcí, které byste měli využít při implementaci relevantního a uživatelsky přívětivého vyhledávání. Některé z nich probereme v průběhu tohoto seriálu: N-gramy, Stopwords, Steeming, Shingle, Elision. Atd.

Podívejte se také na 1. a 2. díl tohoto seriálu.

Pokud máte nějaké dotazy, napište mi na Twitter @deniswsrosa.

Autor

  • Denis Rosa, Developer Advocate, Couchbase –

.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.