HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Существует огромное количество задач,<strong>связанных с обработкой текстов</strong>: машинный перевод, фильтрация спама, data mining, спеллчекеры, научные задачи такие, как работа с последовательностями ДНК, и многие другие.</p>
1 <p>Существует огромное количество задач,<strong>связанных с обработкой текстов</strong>: машинный перевод, фильтрация спама, data mining, спеллчекеры, научные задачи такие, как работа с последовательностями ДНК, и многие другие.</p>
2 <p>Почти все эти задачи объединяет то, что тексты состоят из отдельных слов, а для эффективного хранения данных требуются<strong>специфические структуры данных</strong>. Почему? Что это за структуры данных, за счёт каких особенностей текстовых данных они эффективны? Давайте разбираться на примере<strong>DAWG</strong>.</p>
2 <p>Почти все эти задачи объединяет то, что тексты состоят из отдельных слов, а для эффективного хранения данных требуются<strong>специфические структуры данных</strong>. Почему? Что это за структуры данных, за счёт каких особенностей текстовых данных они эффективны? Давайте разбираться на примере<strong>DAWG</strong>.</p>
3 <h2>Что такое DAWG?</h2>
3 <h2>Что такое DAWG?</h2>
4 <p>DAWG (Deterministic Acyclic Word Graph, также DAFSA - Deterministic Acyclic Finite State Automaton) -<strong>структура данных</strong>, которая часто используется в ситуациях, когда нужна быстрая, компактная коллекция, в которой можно использовать слова в качестве ключей и связанную с ними дополнительную информацию (например, частоту использования, грамматические свойства и прочее). При этом количество слов для языка с богатой морфологией такого, как русский или финский, будет исчисляться миллионами за счёт разных<a>словоформ</a>одной и той же<a>лексемы</a>, например: - ворон; - ворона; - ворону; - и т.д.</p>
4 <p>DAWG (Deterministic Acyclic Word Graph, также DAFSA - Deterministic Acyclic Finite State Automaton) -<strong>структура данных</strong>, которая часто используется в ситуациях, когда нужна быстрая, компактная коллекция, в которой можно использовать слова в качестве ключей и связанную с ними дополнительную информацию (например, частоту использования, грамматические свойства и прочее). При этом количество слов для языка с богатой морфологией такого, как русский или финский, будет исчисляться миллионами за счёт разных<a>словоформ</a>одной и той же<a>лексемы</a>, например: - ворон; - ворона; - ворону; - и т.д.</p>
5 <p>Для создания словаря всегда можно прибегнуть к проверенному средству - хеш-таблице, однако в случае необходимости хранения нескольких миллионов слов размеры такого словаря будут весьма существенными - порядка десятков или даже сотен мегабайт.</p>
5 <p>Для создания словаря всегда можно прибегнуть к проверенному средству - хеш-таблице, однако в случае необходимости хранения нескольких миллионов слов размеры такого словаря будут весьма существенными - порядка десятков или даже сотен мегабайт.</p>
6 <p>Что, если для эффективного хранения слов использовать более "продвинутую" схему, которая использует то, что многие слова похожи или частично совпадают?</p>
6 <p>Что, если для эффективного хранения слов использовать более "продвинутую" схему, которая использует то, что многие слова похожи или частично совпадают?</p>
7 <p><strong>Схема простая</strong>: давайте "складывать" вместе слова, которые начинаются на одну и ту же букву, затем "складывать" вместе продолжения слов с одинаковой второй буквой и т.д.</p>
7 <p><strong>Схема простая</strong>: давайте "складывать" вместе слова, которые начинаются на одну и ту же букву, затем "складывать" вместе продолжения слов с одинаковой второй буквой и т.д.</p>
8 <p><em>Рис. 1."Связанные" вместе слофовормы лексемы "ворон". Цветами, кроме синего, выделены узлы, в которых хранятся конкретные лексемы, остальные узлы "пустые".</em></p>
8 <p><em>Рис. 1."Связанные" вместе слофовормы лексемы "ворон". Цветами, кроме синего, выделены узлы, в которых хранятся конкретные лексемы, остальные узлы "пустые".</em></p>
9 <p>При поиске или извлечении слова из реализованного таким образом словаря достаточно "спуститься" по рёбрам дерева, пока не закончатся буквы в слове-запросе или пока не будет достигнуто условие остановки: нет подходящего ребра для перехода - значит, нет и целевого слова. Время поиска слова в<strong>trie</strong>равно O(n), где n - количество символов в слове (как правило, для вычисления хеша от строки всё равно надо “обойти” все символы строки и выполнить над ними набор операций, что дает то же O(n) действий для хеш-таблиц).</p>
9 <p>При поиске или извлечении слова из реализованного таким образом словаря достаточно "спуститься" по рёбрам дерева, пока не закончатся буквы в слове-запросе или пока не будет достигнуто условие остановки: нет подходящего ребра для перехода - значит, нет и целевого слова. Время поиска слова в<strong>trie</strong>равно O(n), где n - количество символов в слове (как правило, для вычисления хеша от строки всё равно надо “обойти” все символы строки и выполнить над ними набор операций, что дает то же O(n) действий для хеш-таблиц).</p>
10 <p><em>Рис. 2. Trie из разных форм лексем "кот" и "ворон". В корне trie - пустая строка. Для наглядности буквы помещены на узлы, хотя они соответствуют правилам перехода между узлами, то есть рёбрам.</em></p>
10 <p><em>Рис. 2. Trie из разных форм лексем "кот" и "ворон". В корне trie - пустая строка. Для наглядности буквы помещены на узлы, хотя они соответствуют правилам перехода между узлами, то есть рёбрам.</em></p>
11 <p><em>Рис. 3. Поиск лексем "ворона" и "котёл" в trie.</em></p>
11 <p><em>Рис. 3. Поиск лексем "ворона" и "котёл" в trie.</em></p>
12 <p>Знакомые с графами могут заметить, что trie является<strong>направленным ациклическим графом</strong>(<a>DAG</a>), с рёбрами которого связаны символы, которые сопоставляются со словом-запросом при переходе между узлами.</p>
12 <p>Знакомые с графами могут заметить, что trie является<strong>направленным ациклическим графом</strong>(<a>DAG</a>), с рёбрами которого связаны символы, которые сопоставляются со словом-запросом при переходе между узлами.</p>
13 <p>Определение выше "не строго" описывает логику, которая используется при построении trie или<a>префиксного дерева</a>. Однако у trie есть недостаток: его "ветвистость" - повторяющиеся окончания слов встречаются во многих "ветвях". Вероятно,<strong>можно оптимизировать затраты памяти</strong>на хранение trie, не снижая при этом скорости поиска, если "связать" повторяющиеся участки вместе.</p>
13 <p>Определение выше "не строго" описывает логику, которая используется при построении trie или<a>префиксного дерева</a>. Однако у trie есть недостаток: его "ветвистость" - повторяющиеся окончания слов встречаются во многих "ветвях". Вероятно,<strong>можно оптимизировать затраты памяти</strong>на хранение trie, не снижая при этом скорости поиска, если "связать" повторяющиеся участки вместе.</p>
14 <p>Часто построение DAWG начинается с построения trie, а затем выполняется его "минимизация". Какие алгоритмы используются для минимизации trie? Задача выглядит нетривиальной.</p>
14 <p>Часто построение DAWG начинается с построения trie, а затем выполняется его "минимизация". Какие алгоритмы используются для минимизации trie? Задача выглядит нетривиальной.</p>
15 <p><em>Рис. 4. Трансформация trie в DAWG при помощи минимизации графа.</em></p>
15 <p><em>Рис. 4. Трансформация trie в DAWG при помощи минимизации графа.</em></p>
16 <p>К счастью, взгляд на DAWG под немного другим углом помогает нам увидеть в нём<strong><a>детерминированный конечный автомат</a></strong>. В этом случае trie - это детерминированный конечный автомат, который отклоняет либо принимает строки, поданные на вход, и который можно упростить. Для упрощения можно использовать один из алгоритмов<a>минимизации конечных автоматов</a>.</p>
16 <p>К счастью, взгляд на DAWG под немного другим углом помогает нам увидеть в нём<strong><a>детерминированный конечный автомат</a></strong>. В этом случае trie - это детерминированный конечный автомат, который отклоняет либо принимает строки, поданные на вход, и который можно упростить. Для упрощения можно использовать один из алгоритмов<a>минимизации конечных автоматов</a>.</p>
17 <p>Хотя, в принципе, строить DAWG можно и сразу,<a>минуя стадию Trie</a>.</p>
17 <p>Хотя, в принципе, строить DAWG можно и сразу,<a>минуя стадию Trie</a>.</p>
18 <p>Важно понимать, что при минимизации не все одинаковые участки объединяются: объединение одинаковых участков не должно приводить к "добавлению" новых слов в словарь и образованию циклов.</p>
18 <p>Важно понимать, что при минимизации не все одинаковые участки объединяются: объединение одинаковых участков не должно приводить к "добавлению" новых слов в словарь и образованию циклов.</p>
19 <p><em>Рис. 5. "Неправильная" минимизация добавила лишние слова: "вот", "воту", в "вотом", "корон", "корона", … (слово "корона" существует, но его не было в словаре).</em></p>
19 <p><em>Рис. 5. "Неправильная" минимизация добавила лишние слова: "вот", "воту", в "вотом", "корон", "корона", … (слово "корона" существует, но его не было в словаре).</em></p>
20 <p>После построения DAWG на заданном наборе строк мы получаем структуру данных-словарь, работающую "под капотом" как конечный автомат и способную проверить, есть ли целевое слово в словаре, и, при необходимости, извлечь данные о слове.</p>
20 <p>После построения DAWG на заданном наборе строк мы получаем структуру данных-словарь, работающую "под капотом" как конечный автомат и способную проверить, есть ли целевое слово в словаре, и, при необходимости, извлечь данные о слове.</p>
21 <p>Поскольку, в отличие от trie, в одном узле может "находиться" более одного слова, то данные об отдельных словах приходится хранить в дополнительной структуре данных (например, списке). Это усложнение, однако,<strong>компенсируется эффективностью DAWG</strong>.</p>
21 <p>Поскольку, в отличие от trie, в одном узле может "находиться" более одного слова, то данные об отдельных словах приходится хранить в дополнительной структуре данных (например, списке). Это усложнение, однако,<strong>компенсируется эффективностью DAWG</strong>.</p>
22 <p>Рассмотрим пример библиотеки<strong><a>pymorphy2</a></strong>(статья о реализации) для обработки естественного языка. Запись примерно трёх миллионов русских слов потребовала для хранения в зависимости от структуры данных при схожей скорости обработки (количество извлекаемых слов/сек): - словарь - около 600 Мб; - trie - 70 Мб; - DAWG - 7 Мб.</p>
22 <p>Рассмотрим пример библиотеки<strong><a>pymorphy2</a></strong>(статья о реализации) для обработки естественного языка. Запись примерно трёх миллионов русских слов потребовала для хранения в зависимости от структуры данных при схожей скорости обработки (количество извлекаемых слов/сек): - словарь - около 600 Мб; - trie - 70 Мб; - DAWG - 7 Мб.</p>
23 <h2>Делаем выводы</h2>
23 <h2>Делаем выводы</h2>
24 <p>DAWG - эффективная в плане потребления памяти и скорости извлечения слов<strong>структура данных</strong>, которая часто используется в области обработки естественных языков. Также DAWG можно воспринимать как<strong>детерминированный конечный автомат без циклов</strong>(<strong>DAFSA</strong>- Deterministic Acyclic Finite State Automaton). Время, затрачиваемое на поиск строки в DAWG, равно O(1) относительно строк и O(n) относительно количества символов в строке.</p>
24 <p>DAWG - эффективная в плане потребления памяти и скорости извлечения слов<strong>структура данных</strong>, которая часто используется в области обработки естественных языков. Также DAWG можно воспринимать как<strong>детерминированный конечный автомат без циклов</strong>(<strong>DAFSA</strong>- Deterministic Acyclic Finite State Automaton). Время, затрачиваемое на поиск строки в DAWG, равно O(1) относительно строк и O(n) относительно количества символов в строке.</p>
25 <p>Экономия памяти, достигаемая DAWG,<strong>зависит от конкретного набора строк</strong>, на котором строится автомат, но для естественных языков может снижать расход памяти на порядки.</p>
25 <p>Экономия памяти, достигаемая DAWG,<strong>зависит от конкретного набора строк</strong>, на котором строится автомат, но для естественных языков может снижать расход памяти на порядки.</p>
26  
26