| --- |
| active_crumb: Introduction To NLPCraft |
| layout: blog |
| blog_title: Quick Introduction to Apache NLPCraft |
| author_name: Nikita Ivanov |
| author_avatar: images/nivanov.png |
| author_twitter_id: c64hacker |
| publish_date: November 16, 2020 |
| --- |
| |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <section> |
| <div class="bq info"> |
| This blog is an English adaptation of Sergey Kamov's <a target=habr href="https://habr.com/ru/post/526950/">blog</a> written in Russian. |
| </div> |
| <p> |
| In this short article I would like to introduce Apache NLPCraft - an open source library for adding Natural |
| Language Interface to any application. It enables people to interact with your products using voice |
| or text. The goal of this project from its inception in 2017 was and still is |
| unambiguously straightforward - provide an efficient & highly productive API to develop advanced NLP-based |
| interfaces for modern applications. |
| </p> |
| {% include latest-ver-blog-warn.html %} |
| </section> |
| <section> |
| <h2 class="section-title">Overview <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| These are some of the key features that should give you a flavor of what NLPCraft is and what it can do: |
| </p> |
| <div class="container-fluid" style="padding: 0"> |
| <div class="row"> |
| <div class="col-6"> |
| <b>Intent Definition Language</b><br/> |
| <p> |
| Advanced <a href="/intent-matching.html">Intent Definition Language</a> (IDL) coupled with deterministic intent matching |
| provide ease of use and unprecedented expressiveness for designing real-life, non-trivial intents. |
| </p> |
| </div> |
| <div class="col-6"> |
| <b>Composable Named Entities</b><br/> |
| <p> |
| Easily compose, mix and match new named entities out of built-in or external ones, creating new |
| reusable named entity recognizers on the fly. |
| </p> |
| </div> |
| </div> |
| <div class="row"> |
| <div class="col-6"> |
| <b>Model-As-A-Code</b><br/> |
| <p> |
| Everything you do with NLPCraft is part of your source code. No more awkward web UIs |
| splitting your logic across different incompatible places. Model-as-a-code is built by |
| engineers, and it reflects how engineers work. |
| </p> |
| </div> |
| <div class="col-6"> |
| <b>Java First</b><br/> |
| <p> |
| REST API and Java-based implementation natively supports the world's largest ecosystem of development tools, |
| multiple programming languages, frameworks and services. |
| </p> |
| </div> |
| </div> |
| <div class="row"> |
| <div class="col-6"> |
| <b>Any Data Source</b><br/> |
| <p> |
| NLPCraft can work with any data source, device, or service - public or private. From databases and SaaS systems, to smart home devices, voice assistants and chatbots. |
| </p> |
| </div> |
| <div class="col-6"> |
| <b>Short-Term-Memory</b><br/> |
| <p> |
| Advanced out-of-the-box support for maintaining and managing conversational context that is fully integrated with intent matching. |
| </p> |
| </div> |
| </div> |
| <div class="row"> |
| <div class="col-6"> |
| <b>Out-Of-The-Box Integration</b><br/> |
| <p> |
| NLPCraft natively integrates with 3rd party libraries for basic NLP processing and named entity recognition: |
| </p> |
| <div style="display: inline-block; margin-bottom: 20px"> |
| <a style="margin-right: 10px" target=_ href="https://opennlp.apache.org"><img src="/images/opennlp-logo-h32.png" alt=""></a> |
| <a style="margin-right: 10px" target=_ href="https://cloud.google.com/natural-language/"><img src="/images/google-cloud-logo-small-h32.png" alt=""></a> |
| <a style="margin-right: 10px" target=_ href="https://stanfordnlp.github.io/CoreNLP"><img src="/images/corenlp-logo-h48.png" alt=""></a> |
| <a style="margin-right: 10px" target=_ href="https://spacy.io"><img src="/images/spacy-logo-h32.png" alt=""></a> |
| </div> |
| </div> |
| <div class="col-6"> |
| </div> |
| </div> |
| </div> |
| </section> |
| <section> |
| <h2 class="section-title">Terminology <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| There are two terms that are important to define before we proceed: |
| </p> |
| <ul> |
| <li> |
| <b>Named Entity (NE)</b> - typically a real world object or entity that can be recognized in the input |
| text (see <a target=wikipedia href="https://www.wikiwand.com/en/Named_entity">https://www.wikiwand.com/en/Named_entity</a> |
| for details). Note that NEs can be universal, like country to city name, or domain specific for each model. |
| </li> |
| <li> |
| <b>Intent</b> - a template made out of NEs and a callback function to call when such template is found in the input text. |
| </li> |
| </ul> |
| </section> |
| <section> |
| <h2 class="section-title">Runtime Components <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| When working with NLPCraft you will be dealing with these three main components: |
| </p> |
| <ul> |
| <li> |
| <b>Data Model</b><br/> |
| Data Model encapsulates the NLPCraft’s model-as-a-code approach where the data model is simply an |
| implementation of NCDataModel interface. Typically, static model configuration and NE definitions are |
| abstracted out to an external YAML or JSON file. |
| </li> |
| <li> |
| <b>Data Probe</b><br/> |
| Data probe is a light-weight container that securely deploys and manages data models. You can have |
| multiple data probes and each data probe can host multiple models. |
| </li> |
| <li> |
| <b>REST Server</b><br/> |
| REST server provides URL endpoint for user applications to securely query data sources using natural |
| language. REST server routes such requests to a data probe that hosts the requested data model. |
| </li> |
| </ul> |
| <figure> |
| <img class="img-fluid" src="/images/homepage-fig1.1.png" alt=""> |
| <figcaption><b>Fig 1.</b> NLPCraft Runtime Components</figcaption> |
| </figure> |
| </section> |
| <section> |
| <h2 class="section-title">Smart Home Example <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| Let’s see how NLPCraft works using a prototypical smart home NLP-based light switch as an example. We would |
| like to be able to say things like <code>"Turn off all the light in the house"</code> or |
| <code>"Light up the garage, please!"</code> to control our lights. Note that NLPCraft does not deal with |
| voice-to-text conversion - so this example deals only with the text regardless of its origin. |
| </p> |
| <p> |
| To accomplish that we need to first develop a data model that involves the following: |
| </p> |
| <ul> |
| <li>Determining which NEs we need and how to detect them in the text.</li> |
| <li>Use these NEs to define intents as well as code their callbacks for when such an intent is found in the input text.</li> |
| </ul> |
| </section> |
| <section> |
| <h2 class="section-title">Required Named Entities (NEs) <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| For our example we’ll need to define three NEs in our model: |
| </p> |
| <ul> |
| <li>A place where we need to turn on or off the lights like “kitchen” or “garage”.</li> |
| <li>Two actions: “on” and “off”.</li> |
| </ul> |
| <p> |
| If we can detect these NE in the input text we can perform the necessary operation. |
| </p> |
| </section> |
| <section> |
| <h2 class="section-title">Data Model <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| Once we have an idea of what NEs we’ll need - we can start building the data model that would define how |
| these NEs can be detected and ultimately what intents we are going to detecting using these NEs. |
| </p> |
| <p> |
| In NLPCraft the default built-in approach for detecting NEs is a synonym matching. For each NE you provide |
| a list of synonyms by which it will be found in the input text. To make this task really simple NLPCraft |
| comes with a set of tools including <a href="/data-model.html#macros">Macro DSL</a> and <a href="/intent-matching.html">Intent Definition Language</a> (IDL). |
| Here’s the static model configuration for our |
| example as <code>lightswitch_model.yaml</code> file that includes NE definitions and one intent: |
| </p> |
| <pre class="brush: js, highlight: [14, 21, 29, 38]"> |
| id: "nlpcraft.lightswitch.ex" |
| name: "Light Switch Example Model" |
| version: "1.0" |
| description: "NLI-powered light switch example model." |
| macros: |
| - name: "<ACTION>" |
| macro: "{turn|switch|dial|control|let|set|get|put}" |
| - name: "<ENTIRE_OPT>" |
| macro: "{entire|full|whole|total|_}" |
| - name: "<LIGHT>" |
| macro: "{all|_} {it|them|light|illumination|lamp|lamplight}" |
| enabledBuiltInTokens: [] # This example doesn't use any built-in tokens. |
| elements: |
| - id: "ls:loc" |
| description: "Location of lights." |
| synonyms: |
| - "<ENTIRE_OPT> {upstairs|downstairs|_} {kitchen|library|closet|garage|office|playroom|{dinning|laundry|play} room}" |
| - "<ENTIRE_OPT> {upstairs|downstairs|_} {master|kid|children|child|guest|_} {bedroom|bathroom|washroom|storage} {closet|_}" |
| - "<ENTIRE_OPT> {house|home|building|{1st|first} floor|{2nd|second} floor}" |
| |
| - id: "ls:on" |
| groups: |
| - "act" |
| description: "Light switch ON action." |
| synonyms: |
| - "<ACTION> {on|up|_} <LIGHT> {on|up|_}" |
| - "<LIGHT> {on|up}" |
| |
| - id: "ls:off" |
| groups: |
| - "act" |
| description: "Light switch OFF action." |
| synonyms: |
| - "<ACTION> <LIGHT> {off|out}" |
| - "{<ACTION>|shut|kill|stop|eliminate} {off|out} <LIGHT>" |
| - "no <LIGHT>" |
| intents: |
| - "intent=ls term(act)={has(tok_groups, 'act')} term(loc)={# == 'ls:loc'}*" |
| </pre> |
| <p><b>NOTES:</b></p> |
| <ul> |
| <li> |
| We define 3 model elements (corresponding to the NEs we’ve come up with earlier): |
| <ul> |
| <li><code>"ls:loc"</code> to define the location.</li> |
| <li><code>"ls:on"</code> to define ON action.</li> |
| <li><code>"ls:off"</code> to define OFF action.</li> |
| </ul> |
| </li> |
| <li> |
| We grouped <code>"ls:on"</code> and <code>"ls:off"</code> into group <code>"act"</code> for easier use in the intent. |
| </li> |
| <li> |
| Each model element is defined through synonyms using <a href="/data-model.html#macros">Macro DSL</a>. |
| </li> |
| <li> |
| Intent <code>"ls"</code> uses <a href="/intent-matching.html">Intent Definition Language</a>. In short - this intent |
| will match if we can detect one element |
| from the group <code>"act"</code> and zero or more <code>"ls:loc"</code> elements in any order. |
| </li> |
| </ul> |
| <p> |
| Notice that <a href="/data-model.html#macros">Macro DSL</a> allow you to define hundreds and often thousands of synonyms for each model |
| element with only a few lines of YAML (or JSON). In the above model, for example, the three elements have <b>over |
| 7,700 unique synonyms</b> after all <a href="/data-model.html#macros">Macro DSL</a> processing. |
| </p> |
| <p> |
| Now, dealing with synonyms may sound limiting at first. It is, however, a surprisingly powerful and flexible mechanism |
| for the <em>domain-specific</em> applications: |
| </p> |
| <ul> |
| <li> |
| Synonyms can be developed, extended and tested using many tools that NLPCraft provides like |
| <a href="/data-model.html#macros">Macro DSL</a>, <a href="/intent-matching.html">Intent Definition Language</a> and |
| <a href="/tools/syn_tool.html">Synonym Suggester</a> (that uses Google’ BERT and Facebook fastText models). |
| </li> |
| <li> |
| Classic NLP problems like word-sense disambiguation (“bass” the fish, and “bass” the sound) are |
| not a real concern with semantic modelling for domain specific models. |
| </li> |
| <li> |
| Unlike neural networks-based approaches, synonym based NEs and intents don’t require expensive |
| and time consuming model training and creation of specialized large text corpora. |
| </li> |
| <li> |
| Synonym-based NE detection and Intent matching is fully deterministic (i.e. predictable) which |
| is a stark contrast to deep learning approaches that allow only for probabilistic results. |
| </li> |
| </ul> |
| <p> |
| It’s also important to note that if synonym based approach is not enough NLPCraft, of course, supports custom |
| NE resolvers that can use any desired approach and technique. |
| </p> |
| </section> |
| <section> |
| <h2 class="section-title">Intent Matching <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| Although full explanation of the <a href="/intent-matching.html">intent matching</a> algorithm is outside the scope of this article, the basic workflow looks like this: |
| </p> |
| <ul> |
| <li> |
| User text is tokenized into individual words and tokens. |
| </li> |
| <li> |
| Each token gets lemmatized and stemmatized, and basic syntactic and grammatical information (such as POS tagging) being obtained. |
| </li> |
| <li> |
| Using these tokens the system tries to detect all NEs defined in the model. |
| </li> |
| <li> |
| Found NEs are matched against all model’s intents and the callback for the winning match, if any, is called. |
| </li> |
| </ul> |
| </section> |
| <section> |
| <h2 class="section-title">Model Implementation <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| Below is a full implementation of the data model in Scala (the same implementation in Java or Kotlin is practically identical): |
| </p> |
| <pre class="brush: scala"> |
| package org.apache.nlpcraft.examples.lightswitch |
| |
| import org.apache.nlpcraft.model.{NCIntentTerm, _} |
| |
| class LightSwitchModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/lightswitch/lightswitch_model.yaml") { |
| @NCIntentRef("ls") |
| def onMatch( |
| @NCIntentTerm("act") actTok: NCToken, |
| @NCIntentTerm("loc") locToks: List[NCToken] |
| ): NCResult = { |
| val status = if (actTok.getId == "ls:on") "on" else "off" |
| val locations = |
| if (locToks.isEmpty) |
| "entire house" |
| else |
| locToks.map(_.meta[String]("nlpcraft:nlp:origtext")).mkString(", ") |
| |
| // Add HomeKit, Arduino or other integration here. |
| |
| // By default - return a descriptive action string. |
| NCResult.text(s"Lights are [$status] in [${locations.toLowerCase}].") |
| } |
| } |
| </pre> |
| <p><b>NOTES:</b></p> |
| <ul> |
| <li> |
| We use an <a target="javadoc" href="/apis/latest/org/apache/nlpcraft/model/NCModelFileAdapter.html">NCModelFileAdapter</a> |
| adapter that allows us to load our static model configuration from a YAML file. |
| </li> |
| <li> |
| Method <code>onMatch(...)</code> is a callback function for our intent <code>"ls"</code> |
| (define above in the <code>lightswitch_model.yaml</code> file). |
| </li> |
| <li> |
| Method <code>onMatch(...)</code> has two input parameters: |
| <ul> |
| <li>A single token from the <code>"act"</code> term.</li> |
| <li>A list of tokens (zero or more) from the <code>"loc"</code> term.</li> |
| </ul> |
| </li> |
| <li> |
| The body of the <code>onMatch(...)</code> method is pretty straightforward: it detects the |
| desired action from <code>"actTok"</code> and the places to operate the lights on |
| from <code>"locToks"</code> parameters. |
| </li> |
| </ul> |
| <p> |
| We’ll leave outside of this article the details of the particular integration with HomeKit or Arduino devices. We’ll also defer |
| to the NLPCraft <a href="/docs.html">documentation</a> to learn about other topics such as |
| <a href="/short-term-memory.html">conversation management</a>, |
| details of <a href="/data-model.html#macros">Macro DSL</a> and <a href="/intent-matching.html">Intent Definition Language</a>, |
| built-in <a href="/tools/test_framework.html">testing tools</a>, |
| 3rd party NER <a href="/integrations.html">integrations</a>, etc. |
| </p> |
| </section> |
| <section> |
| <h2 class="section-title">Conclusion <a href="#"><i class="top-link fas fa-fw fa-angle-double-up"></i></a></h2> |
| <p> |
| In a couple dozen lines of code we’ve created a non-trivial application that understands free-speech |
| natural language interface to operate a simple lightswitch. You can ask it many things like: |
| </p> |
| <ul> |
| <li><code>"Turn the lights off in the entire house."</code></li> |
| <li><code>"Switch on the illumination in the master bedroom closet."</code></li> |
| <li><code>"Get the lights on."</code></li> |
| <li><code>"Lights up in the kitchen."</code></li> |
| <li><code>"Please, put the light out in the upstairs bedroom."</code></li> |
| <li><code>"Set the lights on in the entire house."</code></li> |
| <li><code>"Turn the lights off in the guest bedroom."</code></li> |
| <li><code>"Could you please switch off all the lights?"</code></li> |
| <li><code>"Dial off illumination on the 2nd floor."</code></li> |
| <li><code>"Please, no lights!"</code></li> |
| <li><code>"Kill off all the lights now!"</code></li> |
| <li><code>"No lights in the bedroom, please."</code></li> |
| </ul> |
| <p> |
| You can also extend this model and support different locations or actions like dimming or brightening |
| the lights, etc. Model-as-a-code approach enables you to iterate on this in the matter of minutes without |
| heavy re-training cycles. |
| </p> |
| <p> |
| Hopefully, this short introduction to Apache NLPCraft gave you a hint of what it is capable of and |
| what you can do with it. |
| </p> |
| </section> |