Зберігання n-грамових даних


12

Я сподівався трохи затримати мозковий штурм на предмет збереження n -грамних даних. У своєму проекті я намагаюся вирішити лінгвістичні проблеми, де я знаю всі ( n -1) елементи даних і хочу статистично відгадати свій n за допомогою лінійної інтерполяції над усіма застосовними n -грамами. (Так, є теггер, який присвоює теги відомим словам відповідно до його лексикону та дерева суфіксів, який намагається відгадати вигляд слів для невідомих слів; компонент n -грам, що обговорюється тут, матиме завдання вирішити амбугуальність.)

Моїм початковим підходом було б просто зберігати всі спостережувані n -грами (для n = 1..3, тобто монограму, біграму, триграму) даних у відповідних базах даних SQL і називати це щодня. Але вимоги мого проекту можуть змінитися, включаючи інші довжини вектора ( n ), і я хотів би, щоб моя програма адаптувалася до 4-грамової без великої роботи (оновлення схеми, оновлення коду програми тощо); в ідеалі я б просто сказав моєму додатку працювати з 4-грамовими граматами зараз, не потребуючи сильного зміни коду (або взагалі) та тренування його даних із заданого джерела даних.

Щоб підсумувати всі вимоги:

  • Можливість зберігання n -грамних даних (спочатку для n = {1, 2, 3}
  • Можливість змінити види n -грам, які слід використовувати (між запуском програми)
  • Можливість (повторного) навчання n -грамних даних (між запуском програми)
  • Можливість запиту сховища даних (наприклад, якщо я помітив A, B, C, я хотів би дізнатися, що найчастіше спостерігається, про що може піти за допомогою моїх навчених наборів даних 4-, 3-, 2-, 1 грам )

    Додаток, швидше за все, буде важким для читання, набори даних, швидше за все, не будуть перекваліфіковані так часто

  • У рішенні використовується .NET Framework (до 4.0)

Тепер який дизайн краще підійде для такого завдання?

  • Фіксована таблиця, керована сервером SQL (MSSQL, MySQL, ...) для кожного n (наприклад, виділені таблиці для бі-грамів, триграмів тощо)
  • Або рішення бази даних NoSQL, яке зберігає перший n -1 як ключ документа, а сам документ містить n -ве значення та спостережувані частоти?
  • Або щось інше?

3
Я думаю, що це краще підходить для Stack Overflow.
Конрад Рудольф

1
Можливо, структура даних трие (префіксного дерева) відповідала б Вашим вимогам?
Schedler

1
Я б запропонував переповнювати стек або навіть cstheory.stackexchange.com
Стів

Добре, дякую. Я спробую поставити питання там.
Менні

4
Це питання ідеально підходить для programmers.stackexchange.com і його не слід переносити на stackoverflow, IMO. Тут слід задати саме таке "ситуацію на дошці". Перевірте мета для деталей.
користувач281377

Відповіді:


8

Зважаючи на те, що ви не знаєте оптимального діапазону N, ви, безумовно, хочете мати можливість його змінити. Наприклад, якщо ваша програма передбачає ймовірність того, що певний текст є англійською мовою, ви, ймовірно, хочете використовувати символи N-грам для N 3..5. (Це ми знайшли експериментально.)

Ви не поділилися деталями своєї заявки, але проблема досить зрозуміла. Ви хочете представити N-грамові дані у реляційній базі даних (або на базі документа, що базується на документі). Перш ніж запропонувати власне рішення, ви можете поглянути на такі підходи:

  1. Як найкраще зберігати ngram Google в базі даних?
  2. Зберігання n-грамів у базі даних у <n кількості таблиць
  3. Керування 5-грамовою Google Web 1T за допомогою реляційної бази даних

Тепер, не прочитавши жодного з перерахованих вище посилань, пропоную простий, реляційний підхід до бази даних, використовуючи кілька таблиць, по одній для кожного розміру N-грам. Ви можете помістити всі дані в одну таблицю з максимально необхідними стовпцями (тобто зберігати біграми та триграми в ngram_4, залишаючи кінцеві стовпці нульовими), але я рекомендую розділити дані. Залежно від двигуна вашої бази даних, одна таблиця з великою кількістю рядків може негативно впливати на продуктивність.

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

Далі я надішлю вам запит, який поверне найімовірніше наступне слово з усіх ваших таблиць ngram. Але спочатку ось декілька зразкових даних, які слід вставити у вищезазначені таблиці:

  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)

Для запиту найбільш ймовірного наступного слова, ви б використовували такий запит.

  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

Якщо ви додасте більше таблиць ngram, вам потрібно буде додати ще один пункт UNION до вищезазначеного запиту. Ви можете помітити, що в першому запиті я використовував word1 = @ word3. А у другому запиті word1 = @ word2 AND word2 = @ word3. Це тому, що нам потрібно вирівняти три слова в запиті для даних ngram. Якщо ми хочемо, щоб, найімовірніше, було наступне слово для послідовності з трьох слів, нам потрібно перевірити перше слово в даних біграми проти останнього слова слів у послідовності.

Ви можете налаштувати вагові параметри за своїм бажанням. У цьому прикладі я припускав, що більш високі порядкові "n" грами будуть більш надійними.

PS Я би структурував програмний код для обробки будь-якої кількості таблиць ngram_N через конфігурацію. Після створення таблиць ngram_5 та ngram_6 ви можете декларативно змінити програму для використання діапазону N-грамів N (1..6).


За допомогою цього запиту я бачу лише показник частоти, який ви маєте тут. Як вибрати наступне передбачуване слово. Яке найбільше стосується речення?
TomSawyer

Хороший момент @TomSawyer. Я додав вибіркові дані до відповіді і дав зразок запиту, який повертає найбільш вірогідне наступне слово.
Матвій Родатус

Tks для вашого оновлення. Але як тут можна обчислити частоту? тобто: у ngram_2, словосполучення building withмає частоту 0,5. Те саме питання з тим @bigramWeight, що це ?. Я хоч freq - це поле буде оновлюватися щоразу, коли ми оновлюємо базу даних. Тобто якщо користувач введе більше рядка, частота для цього рядка буде перерахована? 0,5 - це 0,5 відсотка в загальній кількості використаних разів або показник появи кожної фрази?
TomSawyer

BigramWeight і trigramWeight (тощо) - це те, як зважувати різні n-грами в загальному розрахунку. Це спрощений спосіб сказати, що довші n-грами мають вищу ентропію, і ви, можливо, захочете, щоб вони "нараховували" більше, ніж коротші n-грами.
Матвій Родатус

Щодо оновлення бази даних, очевидно, я не висвітлював усіх деталей, і є багато можливостей для вдосконалення. Наприклад, замість того, щоб зберігати nvarchars у ngram-таблицях, ви, ймовірно, захочете перейти до таблиці слів (word_id INT, слово NVARCHAR), а потім звернутися до word_ids у ngram-таблицях. Щоб оновити таблиці про перепідготовку, це правильно - ви просто оновите поле частоти.
Матвій Родатус

3

На противагу тому, що пропонують інші, я б запропонував уникати будь-яких структур даних, складніших за хеш-карту чи сховище ключових значень.

Майте на увазі ваші вимоги доступу до даних: a) 99% запитів - запитуйте ngram "aaa-bbb-ccc" та отримайте значення (або 0) b) 1% запитів - вставлення / оновлення кількості конкретних ngram c) немає (в).

Найефективніший спосіб - отримати його за допомогою одного пошуку. Ви можете використовувати роз'єднувач поза межами (або уникнутий), щоб поєднати повний n-грам в одній рядку (наприклад, "альфа | бета | гамма" для 3грам, "альфа" для уніграма тощо) і просто отримати це ( хешом того). Ось як це робить чимало програмного забезпечення NLP.

Якщо ваші ngram-дані невеликі (скажімо, <1 gb) і вміщуються в пам'яті, я б запропонував використовувати ефективну структуру пам'яті програми (хеш-карти, дерева, спроби тощо), щоб уникнути накладних витрат; і просто серіалізувати / десеріалізувати до плоских файлів. Якщо ваші ngram-дані мають терабайт або більше, то ви можете вибрати сховища ключових значень NoSQL, розділені на декілька вузлів.

Для отримання додаткової продуктивності, ви, можливо, захочете замінити всі слова скрізь цілими ідентифікаторами, щоб ваш основний алгоритм взагалі не бачив (повільні) рядки; тоді дещо інакше реалізувати ту саму ідею.


1

Не найефективніший, але простий і пов'язаний з базою даних так, як вам потрібно:

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 повинні мати покажчики на документі та пос.

Біграми:

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

Тоді ви можете порахувати () та згрупувати свій шлях до частот та інших даних.

Щоб змінити на триграми, легко створити цей рядок, щоб включити слово3.

Я робив це раніше, ніж насправді (хоча SQL там, мабуть, трохи іржавий). Я влаштував набір плоских файлів, які можна було легко шукати, а потім виводити з диска. Як-небудь залежить від вашого обладнання, як зробити це краще.


1

Намагаючись покращити прості пошуки моїх програм до біграмів і триграмів з униграмів, я, по суті, побачив ваше запитання.

Якщо однією з вимог є можливість запиту на розподілену файлову систему або базу даних, то це може бути цікавим і для вас: стаття Pibiri та Venturini 2018 "Ефективне поводження з масивними наборами даних N-Gram" визначає ефективний спосіб зберігання n-грамових даних у умови виконання та простору. Вони запропонували свою реалізацію на https://github.com/jermp/tongrams

Кожне "n" n-грамів зберігається в окремій таблиці, до якої можна отримати мінімально ідеальну хеш-функцію з дуже швидкими можливостями вибору та запиту. Таблиці є статичними та побудовані за допомогою основного коду за допомогою введення у форматі текстових файлів Google n-грамів Google.

Я ще не використовував цей код, але є багато способів, коли ви могли б відкрити свої вимоги, звідки беруться ваші запити.

Один із способів: якщо еквівалент сервіту .NET використовується з базою даних або сховищем даних, і якщо вам потрібно зберегти простір пам’яті, то зберігання кожної таблиці ngram у двійковій формі в базі даних / сховищі даних як таблиця - це один варіант (одна база даних / таблиця зберігання даних для результуючого статичного файлу ефективного коду ngram для всіх 1 грамів, ще один для всіх 2-грамових тощо). Запити будуть виконуватися шляхом виклику ефективного n-грамового коду (обернутий для доступу до вашого сервлета). Це вирішення питання щодо створення розподіленої бази даних, яка використовує ефективний n-грамовий код для доступу до файлів у розподіленій файловій системі. Зауважте, що у кожній таблиці двійкових баз даних / сховища даних є обмеження розміру файлу базової файлової системи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.