Almacenar datos de n-gram

12

Esperaba hacer una lluvia de ideas un poco sobre el tema del almacenamiento de datos de n- gramas. En mi proyecto, estoy tratando de resolver problemas lingüísticos en los que conozco todos los elementos de datos ( n -1) y quiero adivinar estadísticamente mi n usando la interpolación lineal sobre todos los n -gramas aplicables . (Sí, hay un etiquetador que asigna etiquetas a palabras conocidas de acuerdo con su léxico y un árbol de sufijos que intenta adivinar el tipo de palabra para palabras desconocidas; el componente n -gram discutido aquí tendrá la tarea de resolver la ambigüedad).

Mi enfoque inicial sería simplemente almacenar todos los n -grams observados (para n = 1..3, es decir, monograma, bigram, trigram) datos en las respectivas bases de datos SQL y llamarlo un día. Pero los requisitos de mi proyecto pueden cambiar para incluir otras longitudes de vectores ( n ), y me gustaría que mi aplicación se adapte a 4 gramos sin mucho trabajo (actualizar el esquema, actualizar el código de la aplicación, etc.); idealmente, simplemente le diría a mi aplicación que trabaje con 4 gramos ahora sin tener que cambiar mucho el código (o nada) y entrenar sus datos de una fuente de datos determinada.

Para resumir todos los requisitos:

  • Capacidad para almacenar datos de n- gramas (inicialmente para n = {1, 2, 3}
  • Capacidad para cambiar qué tipos de n -grams deberían usarse (entre ejecuciones de aplicaciones)
  • Capacidad para (re) entrenar datos de n- gramas (entre ejecuciones de aplicaciones)
  • Capacidad para consultar el almacén de datos (por ejemplo, si he observado A, B, C, me gustaría saber el elemento observado con mayor frecuencia para lo que podría seguir usando mis conjuntos de datos entrenados de 4, 3, 2 y 1 gramo )

    Es probable que la aplicación tenga mucha lectura, los conjuntos de datos probablemente no se vuelvan a entrenar con tanta frecuencia

  • La solución emplea .NET Framework (hasta 4.0)

Ahora, ¿qué diseño sería mejor para tal tarea?

  • Una tabla fija administrada por un servidor SQL (MSSQL, MySQL, ...) para cada n (por ejemplo, tablas dedicadas para bi-gramos, tri-gramos, etc.)
  • O una solución de base de datos NoSQL documento que almacena el primer n -1 como la clave del documento, y el propio documento contiene las n frecuencias de valores observados y -ésimos?
  • O algo diferente?
Manny
fuente
3
Creo que esto sería más adecuado en Stack Overflow.
Konrad Rudolph el
1
¿Quizás una estructura de datos trie (árbol de prefijos) se ajuste a sus requisitos?
Programado el
1
Sugeriría Stack Overflow o incluso cstheory.stackexchange.com
Steve
Bien gracias. Trataré de hacer la pregunta allí.
Manny
44
Esta pregunta es perfecta para programmers.stackexchange.com y no debe migrarse a stackoverflow, IMO. Es exactamente el tipo de pregunta de "situación de pizarra" que debería hacerse aquí. Verifique meta para más detalles.
user281377

Respuestas:

8

Dado que no conocerá el rango óptimo de N, definitivamente desea poder cambiarlo. Por ejemplo, si su aplicación predice la probabilidad de que cierto texto sea inglés, probablemente quiera usar caracteres N-gramos para N 3..5. (Eso es lo que encontramos experimentalmente).

No ha compartido detalles sobre su aplicación, pero el problema es lo suficientemente claro. Desea representar datos de N-gram en una base de datos relacional (o una solución basada en documentos NoSQL). Antes de sugerir una solución propia, es posible que desee echar un vistazo a los siguientes enfoques:

  1. ¿Cómo almacenar mejor los ngrams de Google en una base de datos?
  2. Almacenar n-gramos en la base de datos en <n número de tablas
  3. Administrar Google Web 1T de 5 gramos con base de datos relacional

Ahora, al no haber leído ninguno de los enlaces anteriores, sugiero un enfoque de base de datos simple y relacional utilizando múltiples tablas, una para cada tamaño de N-gram. Puede poner todos los datos en una sola tabla con las columnas máximas necesarias (es decir, almacenar bigrams y trigrams en ngram_4, dejando las columnas finales nulas), pero recomiendo particionar los datos. Dependiendo de su motor de base de datos, una sola tabla con una gran cantidad de filas puede afectar negativamente el rendimiento.

  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));

A continuación, le daré una consulta que devolverá la siguiente palabra más probable dada todas sus tablas de ngram. Pero primero, aquí hay algunos datos de muestra que debe insertar en las tablas anteriores:

  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)

Para consultar la siguiente palabra más probable, usaría una consulta como esta.

  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

Si agrega más tablas de ngram, deberá agregar otra cláusula UNION a la consulta anterior. Puede notar que en la primera consulta usé word1 = @ word3. Y en la segunda consulta, word1 = @ word2 AND word2 = @ word3. Eso es porque necesitamos alinear las tres palabras en la consulta para los datos de ngram. Si queremos la siguiente palabra más probable para una secuencia de tres palabras, necesitaremos verificar la primera palabra en los datos de bigramas contra la última palabra de las palabras en la secuencia.

Puede ajustar los parámetros de peso como lo desee. En este ejemplo, supuse que los "n" gramos ordinales más altos serán más confiables.

PD: Estructuraría el código del programa para manejar cualquier número de tablas ngram_N a través de la configuración. Podría cambiar declarativamente el programa para usar el rango de N-gramos N (1..6) después de crear las tablas ngram_5 y ngram_6.

Matthew Rodatus
fuente
Con esta consulta, solo veo la puntuación de frecuencia que tiene aquí. ¿Cómo selecciono la siguiente palabra predictiva? ¿Cuál es la más relevante para la oración?
TomSawyer
Buen punto @ TomSawyer. Agregué datos de muestra a la respuesta y di una consulta de muestra que devuelve la siguiente palabra más probable.
Matthew Rodatus
Gracias por tu actualización. Pero, ¿cómo podemos calcular la frecuencia aquí? es decir: en ngram_2, la frase building withtiene freq es 0.5. La misma pregunta con @bigramWeight, ¿qué es eso? Pensé que freq es que el campo se actualizará cada vez que actualicemos la base de datos. Es decir, si el usuario ingresa más cadena, ¿se volverá a calcular la frecuencia de esta cadena? 0.5 es 0.5 por ciento en tiempo total usado o tasa de apariencia de cada frase?
TomSawyer
El bigramWeight y el trigramWeight (etc.) son cómo ponderar los diferentes n-gramos en el cálculo general. Es una forma simplista de decir que los n-gramos más largos tienen una entropía más alta y es posible que desee que "cuenten" más que los n-gramos más cortos.
Matthew Rodatus
En términos de actualización de la base de datos, obviamente no he cubierto todos los detalles y hay mucho margen de mejora. Por ejemplo, en lugar de almacenar nvarchars en las tablas de ngram, es probable que desee convertirlo en una tabla de palabras (word_id INT, word NVARCHAR) y luego hacer referencia a word_ids en las tablas de ngram. Para actualizar las tablas en el reentrenamiento, es correcto: solo debe actualizar el campo de frecuencia.
Matthew Rodatus
3

Al contrario de lo que sugieren los demás, sugeriría evitar cualquier estructura de datos más compleja que un hashmap o un almacén de valores clave.

Tenga en cuenta sus requisitos de acceso a datos: a) 99% de solicitudes: consulte ngram "aaa-bbb-ccc" y recupere el valor (o 0) b) 1% de solicitudes: inserte / actualice un recuento de ngram c específico) (C).

La forma más efectiva es recuperarlo con una sola búsqueda. Puede usar un separador fuera de límites (o escapado) para combinar el n-gramo completo en una sola cadena (por ejemplo, "alpha | beta | gamma" para 3gram, "alpha" para unigram, etc.) y simplemente obtener eso ( por el hash de eso). Así es como lo hace una gran cantidad de software de PNL.

Si los datos de su ngram son pequeños (digamos, <1 gb) y se ajustan en la memoria, entonces sugeriría usar una estructura de memoria eficiente en el programa (hashmaps, árboles, intentos, etc.) para evitar sobrecarga; y solo serializar / deserializar a archivos planos. Si sus datos de ngram son terabytes o más, entonces puede elegir NoSQL key-value stores split en múltiples nodos.

Para un rendimiento adicional, es posible que desee reemplazar todas las palabras en todas partes con identificadores enteros para que su algoritmo central no vea ninguna cadena (lenta); entonces es ligeramente diferente implementar la misma idea.

Pedro es
fuente
1

No es el más eficiente, pero simple y está conectado a la base de datos como lo desea:

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 debe tener índices en el documento y pos.

bigrams son:

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

Luego puede contar () y agrupar su camino a frecuencias y otras cosas.

Para cambiar a trigramas, es fácil generar esta cadena para incluir una palabra3.

He hecho esto antes en realidad (aunque el SQL allí probablemente esté un poco oxidado). Me decidí por un conjunto de archivos planos que podían buscarse fácilmente y luego fluirse del disco. Depende un poco de tu hardware cómo hacerlo mejor.

JasonN
fuente
1

Al tratar de mejorar las búsquedas simples de mis aplicaciones para bigrams y trigrams de unigrams, esencialmente, vi su pregunta.

Si uno de los requisitos es la capacidad de consultar un sistema de archivos distribuido o una base de datos, entonces esto también puede ser interesante para usted: el documento Pibiri y Venturini 2018 "Manejo eficiente de conjuntos de datos masivos de N-Gram" describe una manera eficiente de almacenar datos de n-gram en términos de tiempo de ejecución y espacio. Ofrecieron su implementación en https://github.com/jermp/tongrams

Cada "n" de n-gramas se mantiene en una tabla separada a la que se accede mediante una función hash perfecta mínima con capacidades de selección y consulta muy rápidas. Las tablas son estáticas y están construidas por el código principal utilizando la entrada en formato de archivos de texto de n-gramas de Google.

Todavía no he usado el código, pero hay muchas maneras en que podría con sus requisitos abiertos de dónde provienen sus consultas.

Una forma: si el equivalente .NET de un servlet se usa con una base de datos o un almacén de datos, y si necesita conservar espacio de almacenamiento, almacenar cada tabla de ngram en forma binaria en la base de datos / almacén de datos como una tabla es una opción (una base de datos / tabla de almacenamiento de datos para el archivo estático resultante del código ngram eficiente para todos los 1 gramos, otro para los 2 gramos, etc. Las consultas se ejecutarían invocando el código eficiente de n-gram (envuelto para que su servlet pueda acceder a él). Es una solución para crear una base de datos distribuida que utiliza el código eficiente de n-gramas para acceder a los archivos en un sistema de archivos distribuido. Tenga en cuenta que las tablas de bases de datos / almacenes de datos binarios tienen la restricción de tamaño de archivo del sistema de archivos subyacente.

Nichole
fuente