Дипломная работа: Автоматизация обучения технологии MSF: разработка виртуального собеседника для сбора информации для развертывания программных продуктов

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Рисунок 3.4. Части речи, поддерживаемые языком LSPL

Словарь возвращает на запрос структуру, в которой указан синоним и его часть речи. Полученный синоним добавляется в базу данных в случае, если ранее его там не было. Код заполнения таблицы с синонимами представлен ниже.

private void GenerateWordsPool(object sender, RoutedEventArgs e)

{

var filePath = GetExcelFilePath();

if (filePath == null)

return;

Excel.Application xlApp = new Excel.Application();

Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filePath);

Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];

Excel.Range xlRange = xlWorksheet.UsedRange;

Dictionary<string, PhrasePattern> questionPattern = new Dictionary<string, PhrasePattern>();

using (var db = new ObaldeitChatBotEntities())

{

for (int i = 2; i < xlRange.Rows.Count; i++)

{

string question = xlRange.Cells[i, 3].Value2.ToString();

var words = PrepareInputString(question);

foreach (var word in words)

{

if (word.Trim() == string.Empty)

{

continue;

}

var lemmatizedWord = LemmatizerSingletone.GetInstance().LemmatizePhrase(word);

if (db.Word.Any(w => w.text == lemmatizedWord))

{

continue;

}

WordPattern wordPattern = new WordPattern(word);

if (wordPattern.Alternatives.Count == 0)

{

continue;

}

if (!db.PartOfSpeech.ToList().Any(p => p.name == wordPattern.Alternatives[0].pos))

{

continue;

}

Word dbWord = new Word() { text = lemmatizedWord, partOfSpeechId = db.PartOfSpeech.ToList().FirstOrDefault(pos => pos.name == wordPattern.Alternatives[0].pos).id, mainSymominId = -1};

db.Word.Add(dbWord);

db.SaveChanges();

foreach (var alt in wordPattern.Alternatives.Skip(1))

{

if (!db.PartOfSpeech.ToList().Any(p => p.name == alt.pos))

{

continue;

}

if (alt.pos != null)

{

db.Word.Add(new Word() { text = alt.text, partOfSpeechId = db.PartOfSpeech.ToList().FirstOrDefault(pos => pos.name == alt.pos).id, mainSymominId = dbWord.id });

}

}

}

}

db.SaveChanges();

}

xlWorkbook.Close();

xlApp.Quit();

}

После заполнения таблицы синонимов были сгенерированы шаблоны. Язык LSPL позволяет использовать альтернативы в шаблонах, что позволяет использовать в шаблонах ранее полученные синонимы. Помимо этого, LSPL дает возможность работать со словом как частью речи языка, используя такие его характеристики как число, падеж, время и т.д. Части речи в предложении играют разную роль и имеют разную значимость. Исходя из этого, была составлена маска частей речи, по которым будут составляться шаблоны. В маску вошли следующие части речи: существительное, наречие, глагол, числительное.

Далее кратко будет описан алгоритм составления шаблона. На вход подается ранее созданный файл с вопросами и ответами. Файл считывается построчно. Сначала определяется филиал, сотруднику, которого задается вопрос, затем определяется сам сотрудник из базы данных по его принадлежности к филиалу. Затем из заданного вопроса удаляются все слова, которые не соответствуют маске частей речи, которая была описана ранее. Затем для каждого из оставшихся слов генерируется лексико-синтаксический шаблон с его синонимами, после чего все полученные шаблоны синонимов объединяются в шаблон заданного вопроса в исходном порядке. Шаблоны синонимов и полученного вопроса по необходимости добавляются в базу данных. Код, добавляющий в базу данных шаблоны приведен ниже.

private void LoadPatterns_Click(object sender, RoutedEventArgs e)

{

var filePath = GetExcelFilePath();

if (filePath == null)

return;

Excel.Application xlApp = new Excel.Application();

Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filePath);

Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];

Excel.Range xlRange = xlWorksheet.UsedRange;

Dictionary<string, PhrasePattern> questionPattern = new Dictionary<string, PhrasePattern>();

using (var db = new ObaldeitChatBotEntities())

{

for (int i = 2; i < xlRange.Rows.Count; i++)

{

string subsidiaryName = xlRange.Cells[i, 1].Value2.ToString();

string employeeName = xlRange.Cells[i, 2].Value2.ToString();

string question = xlRange.Cells[i, 3].Value2.ToString().Replace('\n', ' ');

string answerValue = xlRange.Cells[i, 4].Value2.ToString().Replace('\n', ' ');

Employee employee = db.Employee.Include("Subsidiary").FirstOrDefault(emp => emp.name.ToLower() == employeeName.ToLower() && emp.Subsidiary.title.ToLower() == subsidiaryName.ToLower());

PhrasePattern pattern = new PhrasePattern();

PartOfSpeechMask mask = new PartOfSpeechMask(new List<PatternsGenerator.PartOfSpeech>() { }, new List<PatternsGenerator.PartOfSpeech> { PatternsGenerator.PartOfSpeech.Noun, PatternsGenerator.PartOfSpeech.Verb, PatternsGenerator.PartOfSpeech.Pronoun, PatternsGenerator.PartOfSpeech.Numeral });

var wordsStrings = PrepareInputString(question).Select(s => LemmatizerSingletone.GetInstance().LemmatizePhrase(s)).ToList();

for (int j = 0; j < wordsStrings.Count; j++)

{

var word = db.Word.Include("PartOfSpeech").ToList().FirstOrDefault(w => w.text == wordsStrings[j]);

if (word == null || !mask.NonRequired.Select(s => PartOfSpeechMask.PartOfSpeechDict[s].ToLower()).Contains(word.PartOfSpeech.name.ToLower()))

{

wordsStrings.RemoveAt(j);

j--;

}

}

if (wordsStrings.Count == 0) continue;

List<PhrasePattern> wordPatternsList = new List<PhrasePattern>();

for (int j = 0; j < wordsStrings.Count; j++)

{

var word = db.Word.Include("PartOfSpeech").ToList().First(w => w.text == wordsStrings[j]);

if (word.mainSymominId != -1)

{

word = db.Word.Include("PartOfSpeech").ToList().First(w => w.id == word.mainSymominId);

}

var wordPattern = db.PhrasePattern.ToList().FirstOrDefault(p => p.title.ToLower() == TranslitGenerator.GetTranslit(word.text).ToLower());

if (wordPattern == null)

{

var dbAlternates = db.Word.Include("PartOfSpeech").ToList().Where(w => w.mainSymominId == word.id).Take(10).Select(w => new ParserWord() { text = w.text, pos = w.PartOfSpeech.name}).ToList();

WordPattern parserWordPattern = new WordPattern(new ParserWord() { text = word.text, pos = word.PartOfSpeech.name }, dbAlternates);

var generatedPattern = parserWordPattern.GeneratePatternSeparately(mask);

if (!String.IsNullOrEmpty(generatedPattern[1]))

{

wordPattern = new PhrasePattern() { title = generatedPattern[0], body = generatedPattern[1] };

db.PhrasePattern.Add(wordPattern);

db.SaveChanges();

}

}

if (wordPattern != null)

{

wordPatternsList.Add(wordPattern);

}

}

var patternTitle = new string(wordPatternsList.Select(p => p.title[0]).ToArray());

pattern.title = Char.ToUpper(patternTitle[0]) + patternTitle.Substring(1);

pattern.body = wordPatternsList.ToList().Select(s => s.title).Aggregate((m, k) => m + " {W} " + k) + " =pattern> \'-\'";

db.PhrasePattern.Add(pattern);

Competence competence = new Competence() { employeeId = employee.id, patternId = pattern.id, answer = answerValue};

db.Competence.Add(competence);

db.SaveChanges();

}

}

xlWorkbook.Close();

xlApp.Quit();

}

После того, как все шаблоны были добавлены в базу данных, осталось только сгенерировать файл с паттернами, который будет подаваться на вход утилите.

private void GeneratePatternFile_Click(object sender, RoutedEventArgs e)

{

List<string> patterns = new List<string>();

using (var db = new ObaldeitChatBotEntities())

{

foreach (var ptrn in db.PhrasePattern.ToList())

{

patterns.Add(char.ToUpper(ptrn.title[0]) + ptrn.title.Substring(1) + " = " + ptrn.body);

}

}

patterns = patterns.Distinct().ToList();

foreach (var pattern in patterns)

{

File.AppendAllLines(@"C:\projects\ObaldeitChatBotV2\PatternsGenerator\lspl-tools-new\example\ptrns1.txt", new List<string> { pattern }, Encoding.GetEncoding("windows-1251"));

}

}

3.5 Использование лексико-синтаксических шаблонов

Разберем взаимодействие системы с лексико-синтаксическими шаблонами. Каждый ответ, который может давать система, связан с лексико-синтаксическим шаблоном в его перечне. Все лексико-синтаксические шаблоны находятся в файле, относительный путь которого ObaldeitChatBot\LsplUtils\lspl-tools-new\bin\patterns.txt. Сами шаблоны составлены так, что позволяют учитывать разные формулировки вопросов и должностей сотрудников. Достигнуто это за счет возможностей языка lspl. Рассмотрим некоторые шаблоны. Ниже представлен шаблон, определяющий должность Системного администратора:

Admin = N<администратор> =text> '-' | A<системный> N<администратор> =text> '-' | 'сисадмин' =text> '-'

Разберем подробнее его составляющие. В шаблоне представлены три альтернативы. Первая альтернатива «N<администратор> =text> '-'» предполагает, что исходный текст будет соотнесен с этим шаблоном, если он будет содержать существительное «администратор» в любом числе и падеже. Вторая альтернатива «A<системный> N<администратор> =text> '-'» предполагает, что исходный текст будет соотнесен с должностью, если в тексте встретится словосочетание, состоящее из прилагательного «системный» и существительного «администратор», которые могут быть не согласованы между собой. Так, такие варианты как «системный администратору» или «системному администратор» также будет распознаны этим шаблоном. Третья альтернатива не завязана на каких-либо частях речи и соотнесет с шаблоном любой текст, в котором встречается подстрока «сисадмин». Кроме альтернатив в простых лексико-синтаксических шаблонах, многовариантности можно достичь за счет использования составных шаблонов, которые состоят из других шаблонов. Разберем это на примере шаблона, который соотнесен с ответом на вопрос о том, сколько в филиале находится компьютеров. Этот шаблон представлен ниже:

QComp = Quantity {W} Computer {W} [Office] =pattern> '-'

Как видно, в этом шаблоне нет элементов, которые бы характеризовали слова на русском языке, однако он содержит название других трех шаблонов: «Quantity», «Computer» и «Office». Эти шаблоны представлены ниже:

Quantity = Num<сколько> =text> '-' | N<количество> =text> '-' | N<число> =text> '-' | N<величина> =text> '-'

Computer = N<компьютер> =text> '-' | N<эвм> =text> '-' | N<персоналка> =text> '-' | 'пк' =text> '-'

Office = N<офис> =text> '-' | N<отделение> =text> '-' | N<подразделение> =text> '-'| N<отдел>=text> '-'

Первый шаблон содержит пять альтернатив, второй четыре и третий пять альтернатив. Помимо этого, в шаблоне QComp шаблон Office указан в квадратных скобках, что означает, что он может не находиться в исходном тексте. Также между шаблонами указана буква W в фигурных скобках, что обозначает, что между шаблонами может находиться произвольное количество любых слов.

Разберем подробнее процесс запуска самой утилиты. Ее можно запустить командой из командной строки, указав относительный путь входного файла, лексико-синтаксических шаблонов, выходной файл и искомые шаблоны. Пример команды приведен ниже:

bin\lspl-find -i bin\text.txt -p bin\patterns.txt -o bin\output.txt QComp InternetSpeed AreUsable AreSuitable LevelEmployee Characteristic VersOS WillChange LanguageProgram Cabinets AreServersHere IsLocalNetworkHere ThemselfInstall

Однако, поскольку из приложения мы не имеем возможности воспользоваться командной строкой, команда выполняется через .bat файл. Всего по мере ответа на вопрос команда выполняется дважды: в первый раз устанавливается, к какому именно сотруднику задается вопрос. Далее, если такой сотрудник работает в указанном филиале, устанавливается, что за вопрос был задан. Разберем обработку результатов работы утилиты. Lspl-tools по результатам работы генерирует xml файл, в котором указано, какие именно шаблоны были встречены в тексте. Пример такого xml файла представлен ниже, где в качестве входного текста был указан вопрос «Сколько компьютеров есть в вашей фирме», а в качестве лексико-синтаксических шаблонов были указаны QComp, InternetSpeed, AreUsable, AreSuitable, LevelEmployee, Characteristic, VersOS, WillChange, Language Program, Cabinets, AreServersHere, IsLocalNetworkHere, ThemselfInstall:

<?xml version="1.0" encoding="windows-1251"?>

<texts>

<text>

<goal name="QComp">

<match startPos="0" endPos="19">

<fragment>Сколько компьютеров</fragment>

<result>-</result>

</match>

<match startPos="0" endPos="24">

<fragment>Сколько компьютеров есть</fragment>

<result>-</result>

</match>

<match startPos="0" endPos="26">

<fragment>Сколько компьютеров есть в</fragment>

<result>-</result>

</match>

<match startPos="0" endPos="32">

<fragment>Сколько компьютеров есть в вашей</fragment>

<result>-</result>

</match>

<match startPos="0" endPos="38">

<fragment>Сколько компьютеров есть в вашей фирме</fragment>

<result>-</result>

</match>

</goal>

<goal name="InternetSpeed">

</goal>

<goal name="AreUsable">

</goal>

И т.д.

</text>

</texts>

Как видно из кода файла, было найдено несколько соответствий с шаблоном «QComp». Поскольку в этом шаблоне есть возможность вставлять слова между частями шаблона, то было количество соответствий было увеличено. После завершения работы утилиты содержимое выходного файла обрабатывается системой, где проверяется, какие из поданных шаблонов были встречены в тексте. Результирующим шаблоном считается тот, фрагмент которого, встреченный в тексте, имеет максимальную длину. За обработку выходного файла отвечает класс QuestionsAnswerer.

Разберем, как работает этот класс. У него есть всего одна публичная функция, которая вызывается из внешнего кода. В нее передается текст выходного файла. Сначала функция проверяет, находится ли в коде файла подстрока «match»: эта подстрока находится в файле только в том случае, если было найдено соответствие между лексико-синтаксическими шаблонами и текстом на вход для утилиты. Если подстрока не была найдена, то функция ничего не вернет и это будет обрабатываться во внешнем коде, в ином случае выбирается то соответствие, в котором разница стартовой и конечной позиции максимальна, после чего возвращается название шаблона этого соответствия.

3.6 Разработка веб-приложения

После того, как была спроектирована и наполнена база данных, была начата разработка веб-приложения для взаимодействия с пользователем на платформе asp.net. Было разработано одностраничное приложение с использование технологий jquery и ajax с простым интерфейсом (Рис 3.5.), где пользователю предлагается выбрать филиал из списка доступных, ввести должность сотрудника и задать вопрос, после чего в графе ответ через некоторое время появится ответ, вычисленный системой. Алгоритм, по которому работает система, в точности повторяет спроектированный. Его реализация приведена ниже:

public JsonResult GetAnswer(QuestionAnswerModel model)

{

using (var db = new ObaldeitChatBotEntities())

{

var employeePatternName = GetPatternName(model.EmployeeText, db.Employee.ToList().Where(e => e.subsidiaryId == model.SelectedCityId).Select(e => e.EmployeePattern.title).ToList());

if (employeePatternName == null)

{

model.AnswerText = "Нет у нас такой должности!";

return Json(model);

}

var employee = db.Employee.ToList().First(e => e.subsidiaryId == model.SelectedCityId && e.EmployeePattern.title == employeePatternName);

var competencesPatterns = db.Competence.ToList().Where(c => c.employeeId == employee.id).Select(s => s.PhrasePattern.title).Distinct().ToList();

var answerPatternName = GetPatternName(model.Question, competencesPatterns);

if (answerPatternName == null)

{

model.AnswerText = "Сотрудник не знает ответа на заданный вопрос.";

return Json(model);

}

model.AnswerText = db.Competence.ToList().First(c => c.employeeId == employee.id && c.PhrasePattern.title == answerPatternName).answer;

Источник: https://otherreferats.allbest.ru/download/1021994/