Привет! Действительно интересная часть программирования, если вы знаете разговорный язык и язык программирования, — это обработка естественного языка. С помощью НЛП вы можете создавать программы, которые могут понимать людей, и выполнять действия взамен!

Тем не менее, если вы смотрите на это, скорее всего, вы хотите сделать это с помощью JavaScript. Есть некоторые преимущества НЛП с JavaScript по сравнению с другими языками, такими как Python. Например, мы можем использовать TypeScript для поддержки строгой типизации.

Есть несколько проблем с НЛП с помощью JavaScript, но об этом эта серия! Без лишних слов, давайте начнем.

Какую библиотеку НЛП следует использовать?

Первый шаг к созданию программы, способной понимать человеческий язык, — это найти библиотеку, анализирующую язык. Если вы раньше искали JavaScript NLP, вы наверняка сталкивались с полезными библиотеками, такими как compromise.js или wink.js. Эти библиотеки впечатляют на стороне клиента: Penn Treebank оценивает их как ~87% и ~95%.

В этой серии я буду использовать менее известную библиотеку НЛП для JavaScript под названием FinNLP. Это лучшая библиотека, которую я смог найти, с ее POS-теггером, дающим около 96,43% с включенным сглаживанием.

Во-первых, давайте создадим проект для использования FinNLP. FinNLP поддерживает как Node.js, так и клиентский JavaScript с примерно одинаковой производительностью в обоих случаях.

Я буду использовать клиентский JavaScript в CodeSandbox. Если вы используете клиентский JavaScript, убедитесь, что у вас есть инструмент сборки, такой как Parcel и npm. Если вы используете Node.js, все готово.

Во-первых, давайте добавим FinNLP, используя npm

npm i finnlp

Далее import FinNLP (require(), если вы используете Node.js)

import * as Fin from "finnlp"

Этот код импортирует весь FinNLP и помещает его в объект Fin. Теперь, чтобы на самом деле проанализировать текст:

import * as Fin from "finnlp";
var text = "Hey there, client-side JavaScript!";
var analyzedText = new Fin.Run(text);

Приведенный выше код создает новый объект Run, содержащий объект, который выглядит следующим образом:

{
  "raw": "Hey there, client-side JavaScript!",
  "intercepted": "Hey there, client-side JavaScript!",
  "sentences": [
    {
      "sentence": "Hey there, client-side JavaScript!",
      "confidence": -9.166666666666666,
      "deps": [
        {
          "label": "INTERJ",
          "type": "UH",
          "parent": 1
        },
        {
          "label": "ROOT",
          "type": "EX",
          "parent": -1
        },
        {
          "label": "PUNCT",
          "type": "PUNCT",
          "parent": 1
        },
        {
          "label": "EXT",
          "type": "NP",
          "parent": 4
        },
        {
          "label": "DEP",
          "type": "NP",
          "parent": 1
        },
        {
          "label": "PUNCT",
          "type": "PUNCT",
          "parent": 4
        }
      ],
      "tags": [
        "UH",
        "EX",
        ",",
        "NN",
        "NNP",
        "."
      ],
      "depsTree": {
        "left": [
          {
            "left": [],
            "right": [],
            "tokens": [
              "hey"
            ],
            "tags": [
              "UH"
            ],
            "index": [
              0,
              0
            ],
            "type": "UH",
            "label": "INTERJ"
          }
        ],
        "right": [
          {
            "left": [],
            "right": [],
            "tokens": [
              ","
            ],
            "tags": [
              ","
            ],
            "index": [
              2,
              2
            ],
            "type": "PUNCT",
            "label": "PUNCT"
          },
          {
            "left": [],
            "right": [
              {
                "left": [],
                "right": [],
                "tokens": [
                  "!"
                ],
                "tags": [
                  "."
                ],
                "index": [
                  5,
                  5
                ],
                "type": "PUNCT",
                "label": "PUNCT"
              }
            ],
            "tokens": [
              "client-side",
              "JavaScript"
            ],
            "tags": [
              "NN",
              "NNP"
            ],
            "index": [
              3,
              4
            ],
            "type": "NP",
            "label": "DEP"
          }
        ],
        "tokens": [
          "there"
        ],
        "tags": [
          "EX"
        ],
        "index": [
          1,
          1
        ],
        "type": "EX",
        "label": "ROOT"
      },
      "tokens": [
        "hey",
        "there",
        ",",
        "client-side",
        "JavaScript",
        "!"
      ],
      "lemmas": [
        "hey",
        "there",
        ",",
        "client-side",
        "JavaScript",
        "!"
      ]
    }
  ]
}

Вау! Это довольно большой объект. Что все это значит, и как мы даже используем это? Не волнуйтесь, мы все покроем.

Первые два свойства, raw и intercepted, довольно просты. raw — это строка, которую вы передали в Fin.Run, а intercepted — это то, что Fin получает после запуска всех препроцессоров (мы также рассмотрим это позже).

Свойство sentences содержит все предложения, которые нашел Fin. У каждого из них есть набор свойств, которые Fin создает для нас.

sentence - необработанный текст этого предложения

confidence - Уверенность Фина в анализе предложения (часто это абсурдное число, не беспокойтесь об этом прямо сейчас)

tags - Массив (по порядку) POS-тегов, соответствующих tokens (полный список можно посмотреть здесь).

tokens — Массив (по порядку) токенов (["How","are","you","?"]), соответствующих tags

lemmas — Массив (по порядку) стержневых (съел -> съел) токенов, соответствующих tokens

deps - Массив (не по порядку) зависимостей предложений

depsTree - Дерево зависимостей предложения, точно такое же, как deps, но в виде дерева

Ого, сколько информации! С чего начать?

Для нашей первой программы давайте создадим что-то, что может сказать, когда мы задаем вопрос «Wh-».

Во-первых, мы должны проверить, заканчивается ли первое предложение (вопрос) знаком «?»

import * as Fin from "finnlp";
var text = "Where is my phone?";
var analyzedText = new Fin.Run(text);
var sentence = analyzedText.sentences[0];
var endsWithQuestionMark = sentence.tokens[sentence.tokens.length - 1] === "?";

Если мы проверим Спецификации POS для FinNLP, мы увидим, что

WDT для which that whatever whichever

WP для who whoever whom what

WP$ для whose

WRBдля how where

На основе этого мы можем сделать детектор, который проверяет, соответствует ли первый тег в предложении одному из этих тегов.

var startsWithWH = ["WDT","WP","WP$","WRB"].includes(sentence.tags[0])
var isWHQuestion = endsWithQuestionMark && startsWithWH;
console.log("Result:",isWHQuestion);

Вуаля! Результат:

Result: true

Поздравляю! Вы создали свою первую программу обработки естественного языка на JavaScript!