Memorizzazione di dati n-gram


12

Speravo di fare un po 'di brainstorming sul tema della memorizzazione di dati n -gram. Nel mio progetto, sto cercando di risolvere problemi linguistici in cui conosco tutti ( n -1) elementi di dati e voglio indovinare statisticamente il mio n usando l'interpolazione lineare su tutti gli n -grammi applicabili . (Sì, c'è un tagger che assegna tag a parole conosciute in base al suo lessico e un albero di suffisso che cerca di indovinare il tipo di parola per parole sconosciute; la componente n -gram discussa qui sarà incaricata di risolvere l'ambuguità.)

Il mio approccio iniziale sarebbe semplicemente quello di memorizzare tutti i dati n -gram osservati (per n = 1..3, cioè monogramma, bigram, trigramma) nei rispettivi database SQL e chiamarli un giorno. Ma i requisiti del mio progetto possono cambiare per includere altre lunghezze vettoriali ( n ), e vorrei che la mia applicazione si adattasse a 4 grammi senza molto lavoro (aggiornamento dello schema, aggiornamento del codice dell'applicazione, ecc.); idealmente, direi semplicemente alla mia applicazione di lavorare con 4 grammi ora senza dover cambiare molto il codice (o affatto) e addestrare i suoi dati da una determinata fonte di dati.

Per riassumere tutti i requisiti:

  • Possibilità di memorizzare dati n -gram (inizialmente per n = {1, 2, 3}
  • Possibilità di modificare i tipi di n -grammi da utilizzare (tra le esecuzioni dell'applicazione)
  • Capacità di (ri) formare dati n -gram (tra le esecuzioni dell'applicazione)
  • Possibilità di interrogare l'archivio dati (ad es. Se ho osservato A, B, C, vorrei conoscere l'elemento osservato più frequentemente per ciò che potrebbe seguire usando i miei set di dati addestrati da 4, 3, 2, 1 grammo )

    Molto probabilmente l'applicazione sarà pesante, i set di dati molto probabilmente non verranno riqualificati così spesso

  • La soluzione utilizza .NET Framework (fino a 4.0)

Ora, quale design sarebbe più adatto per tale compito?

  • Una tabella fissa gestita da un server SQL (MSSQL, MySQL, ...) per ogni n (es. Tabelle dedicate per bi-grammi, tri-grammi, ecc.)
  • O una soluzione di database di documenti NoSQL che memorizza il primo n -1 come chiave del documento e il documento stesso contiene l' n -esimo valore e le frequenze osservate?
  • O qualcosa di diverso?

3
Penso che questo sarebbe più adatto su Stack Overflow.
Konrad Rudolph,

1
Forse una struttura dati trie (albero dei prefissi) si adatterebbe alle tue esigenze?
Programmatore

1
Suggerirei Stack Overflow o persino cstheory.stackexchange.com
Steve

Va bene, grazie. Proverò a porre la domanda laggiù.
Manny,

4
Questa domanda è perfettamente adatta per programmers.stackexchange.com e non deve essere migrata su StackOverflow, IMO. È esattamente il tipo di domanda sulla "situazione della lavagna" che dovrebbe essere posta qui. Controlla la meta per i dettagli.
user281377

Risposte:


8

Dato che non conoscerai l'intervallo ottimale di N, vorrai sicuramente essere in grado di cambiarlo. Ad esempio, se la tua applicazione prevede la probabilità che un determinato testo sia inglese, probabilmente vorrai usare il carattere N-grammi per N 3..5. (Questo è quello che abbiamo trovato sperimentalmente.)

Non hai condiviso i dettagli sulla tua applicazione, ma il problema è abbastanza chiaro. Si desidera rappresentare i dati N-gram in un database relazionale (o soluzione basata su documenti NoSQL). Prima di suggerire una mia soluzione, potresti voler dare un'occhiata ai seguenti approcci:

  1. Come archiviare al meglio gli ngram di Google in un database?
  2. Memorizzare n-grammi nel database in <n numero di tabelle
  3. Gestione di Google Web 1T da 5 grammi con Database relazionale

Ora, non avendo letto nessuno dei link sopra, suggerisco un approccio di database relazionale semplice usando più tabelle, una per ogni dimensione di N-grammo. Potresti mettere tutti i dati in una singola tabella con le colonne massime necessarie (ad es. Memorizzare i bigrammi e i trigrammi in ngram_4, lasciando nulle le colonne finali), ma ti consiglio di partizionare i dati. A seconda del motore del database, una singola tabella con un numero elevato di righe può influire negativamente sulle prestazioni.

  create table ngram_1 (
      word1 nvarchar(50),
      frequency FLOAT,
   primary key (word1));

  create table ngram_2 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2));

  create table ngram_3 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      word3 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2, word3));

  create table ngram_4 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      word3 nvarchar(50),
      word4 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2, word3, word4));

Successivamente, ti darò una query che restituirà la parola successiva più probabile date tutte le tue tabelle ngram. Ma prima, ecco alcuni dati di esempio che dovresti inserire nelle tabelle sopra:

  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'building', N'with', 0.5)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'hit', N'the', 0.1)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'man', N'hit', 0.2)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'bat', 0.7)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'building', 0.3)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'man', 0.4)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'with', N'the', 0.6)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'building', N'with', N'the', 0.5)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'hit', N'the', N'building', 0.3)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'man', N'hit', N'the', 0.2)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'the', N'building', N'with', 0.4)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'the', N'man', N'hit', 0.1)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'with', N'the', N'bat', 0.6)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'building', N'with', N'the', N'bat', 0.5)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'hit', N'the', N'building', N'with', 0.3)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'man', N'hit', N'the', N'building', 0.2)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'the', N'building', N'with', N'the', 0.4)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'the', N'man', N'hit', N'the', 0.1)

Per eseguire una query sulla parola successiva più probabile, utilizzare una query come questa.

  DECLARE @word1 NVARCHAR(50) = 'the'
  DECLARE @word2 NVARCHAR(50) = 'man'
  DECLARE @word3 NVARCHAR(50) = 'hit'
  DECLARE @bigramWeight FLOAT = 0.2;
  DECLARE @trigramWeight FLOAT = 0.3
  DECLARE @fourgramWeight FLOAT = 0.5

  SELECT next_word, SUM(frequency) AS frequency
  FROM (
    SELECT word2 AS next_word, frequency * @bigramWeight AS frequency
    FROM ngram_2
    WHERE word1 = @word3
    UNION
    SELECT word3 AS next_word, frequency * @trigramWeight AS frequency
    FROM ngram_3
    WHERE word1 = @word2
      AND word2 = @word3
    UNION
    SELECT word4 AS next_word, frequency * @fourgramWeight AS frequency
    FROM ngram_4
    WHERE word1 = @word1
      AND word2 = @word2
      AND word3 = @word3
    ) next_words
  GROUP BY next_word
  ORDER BY SUM(frequency) DESC

Se si aggiungono più tabelle ngram, sarà necessario aggiungere un'altra clausola UNION alla query sopra. Potresti notare che nella prima query ho usato word1 = @ word3. E nella seconda query, word1 = @ word2 AND word2 = @ word3. Questo perché dobbiamo allineare le tre parole nella query per i dati ngram. Se vogliamo la parola successiva più probabile per una sequenza di tre parole, dovremo controllare la prima parola nei dati bigram rispetto all'ultima parola delle parole nella sequenza.

Puoi modificare i parametri del peso come desideri. In questo esempio, ho assunto che "n" grammi ordinali più alti saranno più affidabili.

PS Strutturerei il codice del programma per gestire qualsiasi numero di tabelle ngram_N tramite la configurazione. È possibile modificare in modo dichiarativo il programma per utilizzare l'intervallo N-grammo N (1..6) dopo aver creato le tabelle ngram_5 e ngram_6.


Con questa query, vedo solo il punteggio di frequenza che hai qui. Come seleziono la parola predittiva successiva. Qual è la più pertinente per la frase?
TomSawyer il

Buon punto @ TomSawyer. Ho aggiunto dati di esempio alla risposta e ho fornito una query di esempio che restituisce la parola successiva più probabile.
Matthew Rodatus,

Tks per il tuo aggiornamento. Ma come possiamo calcolare la frequenza qui? vale a dire: in ngram_2, la frase building withha freq è 0,5. Stessa domanda con @bigramWeight, cos'è? Anche se freq è il campo verrà aggiornato ogni volta che aggiorniamo il database. Vale a dire se l'utente immette più stringhe, la frequenza per questa stringa verrà ricalcolata? 0,5 è lo 0,5 percento dei tempi totali utilizzati o il tasso di comparsa di ogni frase?
TomSawyer il

Il bigramWeight e il trigramWeight (ecc.) Sono come ponderare i diversi n-grammi nel calcolo complessivo. È un modo semplicistico per dire che n-grammi più lunghi hanno un'entropia più elevata e potresti volerli "contare" più di n-grammi più corti.
Matthew Rodatus,

In termini di aggiornamento del database, ovviamente non ho coperto tutti i dettagli e c'è molto margine di miglioramento. Ad esempio, anziché archiviare nvarchars nelle tabelle ngram, probabilmente vorrai tokenizzare in una tabella di parole (word_id INT, word NVARCHAR) e quindi fare riferimento a word_ids nelle tabelle di ngram. Per aggiornare le tabelle sulla riqualificazione, esatto, aggiorneresti semplicemente il campo di frequenza.
Matthew Rodatus,

3

Contrariamente a quanto suggeriscono gli altri, suggerirei di evitare qualsiasi struttura di dati più complessa di una hashmap o di un archivio di valori-chiave.

Tenere presente i requisiti di accesso ai dati: a) richieste al 99% - interrogare ngram "aaa-bbb-ccc" e recuperare il valore (o 0) b) richieste all'1% - inserire / aggiornare un conteggio di ngram specifici c) non esiste (c).

Il modo più efficace è recuperarlo con una sola ricerca. Puoi usare un separatore fuori limite (o con escape) per combinare l'intero n-grammo in una singola stringa (ad es. "Alpha | beta | gamma" per 3gram, "alpha" per unigram, ecc.) E recuperarlo ( per l'hash). Ecco come funziona un bel po 'di software NLP.

Se i tuoi dati ngram sono piccoli (diciamo, <1 gb) e si adattano alla memoria, allora suggerirei di usare una struttura di memoria in programma efficiente (hashmap, alberi, tentativi, ecc.) Per evitare sovraccarico; e solo serializzare / deserializzare in file flat. Se i tuoi dati ngram sono pari o superiori a terabyte, puoi scegliere gli archivi di valori-chiave NoSQL suddivisi su più nodi.

Per prestazioni extra, potresti voler sostituire tutte le parole ovunque con ID interi in modo che l'algoritmo core non veda affatto stringhe (lente); quindi è leggermente diverso implementare la stessa idea.


1

Non il più efficiente, ma semplice e integrato nel database come desideri:

Table: word
Colums:
word (int, primary key) - a unique identifier for each word
text (varchar) - the actual word

Table: wordpos
Columns:
document (int) - a unique identified for the document of this word
word (int, foreign key to word.word) - the word in this position
pos (int) - the position of this word (e.g., first word is 1, next is 2, ...)

wordpos dovrebbe avere indici sul documento e pos.

i bigrammi sono:

select word1.text as word1, word2.text as word2
from wordpos as pos1, wordpos as pos2, word as word1, word as word2
where pos1.document = pos2.document
      and pos1.pos = pos2.pos - 1
      and word1.word = pos1.word
      and word2.word = pos2.word

Quindi puoi contare () e raggruppare la tua strada verso frequenze e cose.

Per passare a trigrammi, è facile generare questa stringa per includere una parola3.

L'ho già fatto prima (anche se l'SQL lassù è probabilmente un po 'arrugginito). Ho optato per un set di file flat che potevano essere cercati facilmente e poi inviati in streaming dal disco. Kinda dipende dal tuo hardware come farlo meglio.


1

Mentre cercavo di migliorare le semplici ricerche delle mie applicazioni su bigram e trigrammi da unigrammi, in sostanza, ho visto la tua domanda.

Se uno dei requisiti è la capacità di eseguire una query su un file system o database distribuito, questo potrebbe essere interessante anche per te: il documento Pibiri e Venturini 2018 "Gestire in modo efficiente set di dati N-Gram" delinea un modo efficiente per archiviare i dati di n-gram termini di runtime e spazio. Hanno offerto la loro implementazione su https://github.com/jermp/tongrams

Ogni "n" di n-grammi è contenuta in una tabella separata a cui si accede da una funzione hash minima perfetta con capacità di selezione e query molto veloci. Le tabelle sono statiche e costruite dal codice principale usando l'input nel formato dei file di testo di Google n-grammi.

Non ho ancora usato il codice, ma ci sono molti modi in cui potresti soddisfare i tuoi requisiti aperti di provenienza delle tue domande.

Un modo: se l'equivalente .NET di un servlet viene utilizzato con un database o un archivio dati e se è necessario conservare lo spazio di archiviazione, l'archiviazione di ogni tabella di ngram in forma binaria nel database / archivio dati come tabella è un'opzione (un database / tabella datastore per il file statico risultante del codice ngram efficiente per tutti i 1 grammi, un altro per tutti i 2 grammi, ecc.). Le query verrebbero eseguite invocando il codice n-gram efficiente (racchiuso per essere accessibile dal servlet). È una soluzione per creare un database distribuito che utilizza l'efficiente codice n-gram per accedere ai file su un file system distribuito. Si noti che le tabelle del database binario / archivio dati hanno ciascuna la limitazione delle dimensioni del file system sottostante.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.