- Основной класс с функционалом системы:
- Engine.
- Основные модули:
- NLDB - программа (в перспективе библиотека класов) с основным функционалом (классы Language, DataContainer, Word, Term, Grammar);
- NLCLI - интерпретатор командной строки для создания, наполнения и управления хранилищем данных (классы Shell, Commands);
- NLDBTests - тесты. Многие тесты быстро устаревают, опаздывая за изменением функционала, так что большинство пока закомментировано.
- Платформа:
- .NET Core 2.2, использется SQLite 1.0.109
Построение иерархической структуры текста для анализа.
Поиск нужного блока текста с использованием различных метрик схожести.
Поиск связанных блоков текста, где связь определяется различными отношениями: следования, предшествования, включения и т.п.
- Построение иерархической многоуровневой структуры текста путем создания Словаря (метод Language.BuildLexicon), состоящего из ранжированных Слов (класс Word).
- Исходный текст является линейной последовательностью символов и подается на вход как текстовый файл (UTF-8) или строка System.String. При помощи простого лексического анализа текст разбивается на слова ранга n (свойство Word.rank), где n - это максимальный ранг слов, заданный в настройках системы.
Пример. Предположим, текст является файлом с набором статей Wikipedia. Лексический анализ разбивает текст на статьи - Слова ранга 3, которые, в свою очередь разбиваются на Слова ранга 2 - предложения, которые разбиваются на Слова ранга 1 - Слова, которые состоят из букв - Слов ранга 0.
- На данный момент (8.10.2018) лексический анализатор (класс Parser) приводит текст весь текст к нижнему регистру, удаляет незначимые символы, разделяет текст на фрагменты в соответствии с символами-разделителями, заданными регулярными выражениями (метод Parser.Split).
-
Каждое Слово ранга n имеет следующие представления:
- Блок текста, ограниченный символами разделителями;
- Дерево Слов. Высота дерева - n, потомки корня дерева - Слова ранга n-1;
- Словарь объединяет в себе слова различных рангов. Для достижения эффективности обработки данных возможно разбиение Словаря на несколько, содержащих Слова только одного ранга.
-
Поиск блока текста (Слова) с использованием различных метрик схожести.
- Поиск Слова из Словаря по введенной строке (метод Language.Identify) производится как поиск Слова, для которого оценка уверенности (confidence) в похожести на введенный текст максимальна.
- По введенной строке строится Слово, начиная с составляющих минимального 0-го ранга (например, букв). Построенные Слова сравнивается со Словами из Словаря, для каждого из которых вычисляется оценка схожести. Ближайшим считается Слово с максимальной оценкой схожести.
- После определения дочерних Слов n-1 ранга определяется родительское Слово ранга n, с учетом вычисленных ранее оценок схожести дочерних слов.
- Математически операция вычисления родительского Слова, соответствующего дочерним Словам ранга n-1 выглядит как умножение вектора дочерних Слов w на матрицу принадлежности P размерности (n-1)xn. При этом матрица принадлежности бинарная, со строками - id слов ранга n-1, столбцами - id слов ранга n. Распознаваемое Слово - вектор-строка v, с элементами из интервала [0,1], - оценками схожести элементов с соответствующими Словами из Словаря. Вместо суммирования в этой векторно-матричной операции применяется операция max. Вместо произведения - вычисление функции схожести элемента w[i] и элемента P[i,k]. Слово-победитель (то, которому принадлежат дочерние Слова) выясняется через argmax(v). Т.о. полная формула вычисления слова по составляющим элементам выгляди как id_parent = argmax(w*P). Если речь идет об определении слова ранга n по потомкам ранга 0, то вычисления рекурсивно повторяется для всех рангов от 0 до n-1.
- Метрики для вычисления схожести могут различаться для разных рангов. На данный момент используются метрики: Confidence.Equality, Confidence.Cosine, Confidence.SoftInclusive (пояснения и формулы в тексте класса Confidence).
- Уверенность в схожести Слова определяется по формуле, аналогичной формуле полной вероятности , исходя из следующих предпосылок: Если Слово ранга n состоит из некоторых Слов ранга n-1, то полная апостериорная уверенность того что строка является Словом ранга n, является суммой произведений уверенностей в схожести соответстветствующих составляющих термов ранга n-1. Вместо суммы max
- Добавлен метод для поиска набора похожих на введенный текст Слов (метод Language.Similars). Алгоритм аналогичен описанному выше, только выбирается не одно Слово-родитель с максимальной оценкой, а несколько лучших.
- Добавлен метод для получения следующего за найденным "похожим" на введенный текст Словом (метод Language.Next). В методе используются описанные выше методы поиска родительских слов (контекст слова) и вычисляется предполагаемое продолжение с использованием взвешенного префиксного дерева Слов. Предполагается использование в диалоговых системах машинного интерфейса.
- Добавлен метод рекуррентной последовательности слов, следующих за "похожим" на введенный текст (метод Language.PredictRecurrent). Использует алгорим учитывающий частоты n-грамм слов. На данный момент (01.09.2018) закомментирован, так как выводит связный бред и не представляет интереса, кроме развлекательных целей
- Добавлен метод для выделения синтаксического ядра Слов высоких рангов (например, суть статьи в нескольких предложениях) (метод Language.GetCore). Алгоритм использует матрицу схожестви между Словами, минимаксные стратегии оптимизации и методы перечисленные выше. Требует напильника, а может и топора.
private static void Main(string[] args) { //Имя Словаря(а также базы данных). При отсутствии класс Language его создаст автоматически. string dbname = "wikiru5mb.db"; //Укажем файл с текстом, который будем использовать для обучения. Должен присутствовать по указанному пути string trainfile = @"D:\Data\Wiki\ru\5mb.txt"; //Массив разделителей текста на Слова. Разделители задаются регулярными выражениями, //применяемыми к нормализованному тексту. string[] splitters = new string[] { "", //0-й ранг - символы, поэтому используется пустая строка @"[^а-яё\d\{\}]+", //1-ранг - любой символ не являющийся буквой русского алфавита или цифрой разделяет слова @"[\n\r]+", //2-й ранг - символы перевода строки разделяет предложения @"\[\[{число}\]\]" //3-й ранг - текст вида [[343467]] разделяет статьи }; //Создаем Словарь Language l = new Language(dbname, splitters); //После создания объекта создаем хранилище. Это нужно так как к созданному ранее хранилищу можно сразу подключиться l.Create(); //Подключимся к хранилищу l.Connect(); Console.WriteLine($"Начало обучения на файле {trainfile}"); //Запускаем процесс построения структуры текста l.BuildLexicon(trainfile); //Теперь будем использовать полученные данные Console.Write($"\n\nВведите фразу:"); string line = Console.ReadLine(); //Найдем 8 лучших совпадений с текстом line. rank=2 означает, что нас интересуют совпадения предложений List similars = l.Similars(text: line, rank: 2, count: 8); Console.WriteLine("\n\nПохожие предложения:\n" + similars.Aggregate("", (c, n) => c + $"\n" + n.ToString())); //Получим предположение о предложении, следующем за line List next = l.Next(text: line, rank: 2); Console.WriteLine("\n\nОтветные предложения (продолжение):\n" + next.Aggregate("", (c, n) => c + $"\n" + n.ToString())); //Получим предоположение о сути статьи, в котором есть предложение, наиболее похожее на line IEnumerable core = l.GetCore(text: line, rank: 2); Console.WriteLine("\n\nЯдро текста статьи:\n" + core.Aggregate("", (c, n) => c + $"\n" + n.ToString())); Console.ReadKey(); //Отключаемся от хранилища l.Disconnect(); }