Attualmente sto lavorando per implementare una ricerca fuzzy per un servizio web terminologico e sto cercando suggerimenti su come migliorare l'implementazione corrente. È troppo codice da condividere, ma penso che una spiegazione potrebbe essere sufficiente per suggerire suggerimenti ponderati. Capisco che c'è molto da leggere ma apprezzerei qualsiasi aiuto.Consigli su come migliorare un'implementazione di ricerca fuzzy attuale
In primo luogo, la terminologia è fondamentalmente solo un numero di nomi (o termini). Per ogni parola, la dividiamo in token per spazio e poi iteriamo attraverso ogni carattere per aggiungerlo al trie. Su un nodo terminale (come quando viene raggiunto il carattere y in fragola), archiviamo in un elenco un indice all'elenco dei termini principali. Quindi un nodo terminale può avere più indici (poiché il nodo terminale per fragola corrisponderà a "fragola" e "allergia a fragola").
Come per la ricerca effettiva, anche la query di ricerca viene suddivisa in token per spazio. L'algoritmo di ricerca viene eseguito per ogni token. Il primo carattere del token di ricerca deve essere una corrispondenza (quindi il traw non abbinerà mai la fragola). Dopodiché, passiamo attraverso i bambini di ciascun nodo successivo. Se c'è un bambino con un personaggio che corrisponde, continuiamo la ricerca con il prossimo carattere del token di ricerca. Se un bambino non corrisponde al carattere dato, guardiamo i bambini che usano il carattere corrente del token di ricerca (quindi non lo fanno avanzare). Questa è la parte fuzziness, quindi 'stwb' corrisponderà a 'fragola'.
Quando raggiungiamo la fine del token di ricerca, cercheremo attraverso il resto della struttura trie su quel nodo per ottenere tutte le potenziali corrispondenze (poiché gli indici all'elenco dei termini principali sono solo sui nodi terminali). Lo chiamiamo roll up. Archiviamo gli indici impostando il loro valore su un BitSet. Quindi, abbiamo semplicemente e i BitSet dai risultati di ogni risultato di token di ricerca. Quindi prendiamo, ad esempio, i primi 1000 o 5000 indici dai BitSet anded e troviamo i termini effettivi a cui corrispondono. Usiamo Levenshtein per segnare ogni termine e poi ordinare per punteggio per ottenere i nostri risultati finali.
Questo funziona abbastanza bene ed è piuttosto veloce. Ci sono oltre 390 nodi di nodi nell'albero e oltre 1,1 milioni di nomi di termini effettivi. Tuttavia, ci sono problemi con questo così com'è.
Ad esempio, la ricerca di "cat car" restituirà la cateterizzazione, quando non lo vogliamo (poiché la query di ricerca è composta da due parole, il risultato dovrebbe essere almeno due). Sarebbe abbastanza facile da controllare, ma non si cura di una situazione come la procedura di cateterizzazione, poiché si tratta di due parole. Idealmente, vorremmo che corrispondesse a qualcosa come la cateterizzazione cardiaca.
In base alla necessità di correggere ciò, abbiamo apportato alcune modifiche. Per uno, passiamo attraverso il trie in una ricerca di profondità/larghezza mista. Fondamentalmente andiamo in profondità per tutto il tempo in cui un personaggio corrisponde. Quei nodi secondari che non corrispondono vengono aggiunti a una coda di priorità. La coda di priorità è ordinata dalla distanza di modifica, che può essere calcolata durante la ricerca del trie (poiché se c'è una corrispondenza di carattere, la distanza rimane la stessa e in caso contrario aumenta di 1). In questo modo, otteniamo la distanza di modifica per ogni parola. Non usiamo più il BitSet. Invece, è una mappa dell'indice di un oggetto Terminfo. Questo oggetto memorizza l'indice della frase di interrogazione e il termine frase e il punteggio. Quindi, se la ricerca è "car cat" e un termine abbinato è "procedura di cateterizzazione", gli indici frase termine saranno 1 così come gli indici della frase di interrogazione. Per "cateterismo cardiaco" gli indici di frase termine saranno 1,2 come gli indici di frase di interrogazione. Come si può vedere, è molto semplice in seguito a guardare il conteggio degli indici frase termine e gli indici di query frase e se non sono almeno pari al conteggio ricerca di parola, si può essere scartata.
Dopo di che, si sommano le distanze di modifica delle parole, rimuovere le parole del termine che corrispondono l'indice frase termine, e contiamo le lettere rimanenti per ottenere la vera distanza di modifica.Ad esempio, se si abbinato il termine "allergia alle fragole" e la query di ricerca era "di paglia" si avrebbe un punteggio di 7 da fragole, allora devi usare l'indice frase termine di scartare le fragole dal termine, e basta contare "allergia a" (meno gli spazi) per ottenere il punteggio di 16.
Questo ci i risultati precisi ci aspettiamo ottiene. Tuttavia, è troppo lento. Dove prima potevamo ottenere 25-40 ms su una ricerca di parole, ora potrebbe essere anche mezzo secondo. È in gran parte da cose come l'istanziazione di oggetti TermInfo, usando operazioni .add(), operazioni .put() e il fatto che dobbiamo restituire un numero elevato di corrispondenze. Potremmo limitare ogni ricerca per restituire solo 1000 le partite, ma non c'è alcuna garanzia che i primi 1000 risultati per "auto" sarebbe partita qualsiasi dei primi 1000 risultati corrispondenti alla "gatto" (ricordate, ci sono più di 1.1. Milioni di termini).
Anche per una sola parola di query, come il gatto, abbiamo ancora bisogno di un gran numero di partite. Questo perché se cerchiamo "cat" la ricerca abbinerà auto e arrotolerà tutti i nodi terminali sotto di esso (che sarà molto). Tuttavia, se limitassimo il numero di risultati, darebbe troppo peso alle parole che iniziano con la query e non alla distanza di modifica. Quindi, le parole come la cateterizzazione sarebbero più probabili da includere rispetto a qualcosa come il cappotto.
Quindi, in sostanza, ci sono qualche idea su come potremmo gestire i problemi che la seconda implementazione fisso, ma senza tanto della velocità rallentano che ha introdotto? Posso includere del codice selezionato se potrebbe chiarire le cose ma non volevo pubblicare un gigantesco muro di codice.
Devo notare che i termini effettivi (per cui gli indici del nodo terminale vengono utilizzati per ottenere) contengono più di un nome, ma vogliamo solo cercare il nome. – AHungerArtist
Questo problema potrebbe essere rimappato in una serie storica; allora il tuo problema diventa come trovare le serie temporali corrispondenti. Uso FTSE algo per le serie temporali corrispondenti da http://www.eecs.umich.edu/db/files/sigmod07timeseries.pdf – Adrian