How to use NLP in python to analyze questions from a chat conversation

Viewed 3065

I've written a chatbot in python which connects to discord, and is able to fulfil some tasks. One of the tasks is to query a list of resources of a specific computer game, and return the detailed location of the queried resource. Now I want to integrate the functionality into the chat, as seemlessly as possible. So I thought I could use NLP techniques for it.

To give an example: User 1 wants to know where he/she can find the resource "wood". So he/she asks in the discord chat: "Where can I find wood?"

My program shall now be able to identify this question as a valid query for a resource location, and respond with the location for resource "wood".

This might involve several steps:

  • Determine that in fact a question is asked
  • Determine the name of the resource which was asked for
  • ???

I am not new to programming, however I am new to NLP. Also I'm a beginner in deep learning / already developed RNN models using tensorflow/keras.

For this project, I found nltk and spaCy, both of which are python modules used for NLP. I've learned already that text analysis consists of several distinct jobs, and not all of them might be of interest for my project. But it seems that both tokenization and pos tagging might be of interest. But somehow I am struggling to find a viable approach for the task. It already starts with how to identify if a text message is actually a question. My research indicates this is no functionality which is provided by NLP libraries out of the box, and pre-trained deep learning models are usually used to categorize sentences like that.

Ideas I've had so far:

1) Analyze every chat message sentence by sentence
Tokenize the sentence, use stemming, then pos tagging, then iterate all tokens to find out if:

  • The verbs "find" (Where can I find ...) or "get" (Where can I get ...)" or "is" (Where is ...) are contained
  • Check if a noun is contained, and if so, if this noun is a valid resource name (a better approach would probably to find out of the noun is actually the object related to the verb. is this possible even?)
  • Check if the sentence if a question by checking if the last token is a ?

2) Use some kind of matching, like the spaCy's rule based matching

  • Build several patterns which can identify the desired question/question types
  • Match the patterns on every chat message
  • If matched, extract the resource name

3) Use non-NLP techniques If everything else should be unviable/too complicated, I can still come up with a hardcoded approach where I would just pre-define a couple of question types, and string-search their occurence within chat messages, and try to manually extract the resource names by using string operations.
This will probably be the most error prone and unflexible solution, but I'll keep it as a fallback.

Of course, I do want to implement a solution which is working as flexible as possible, so it can detect various forms and types of questions, without hardcoding all possible types of questions beforehand. It should be as close to "the bot just understands the chat and answers the question" as possible.

Could someone guide me towards a good solution? (not asking for complete code, but rather the techniques/steps/libraries I shall use)

Maybe as a sidenote: In a later version I want to extend the functionality. Then, it shall be possible that other users name the location of a resource in the discord chat, and the bot shall add this location to its database, if its not already contained. So the chat conversation might look like:

User 1: Where can I find cryptonite?
User 2: It can be found in lex luthors lab
Bot: Shall I add "lex luthors lab" as location for resource "cryptonite"?
User 2: @bot: yes
Bot: Done.  
1 Answers

tl:dr

It seems like you basically have an intent/entity problem.

1) Analyze every chat message sentence by sentence. This can be solved with intent classification.

2) Use some kind of matching, like the spaCy's rule based matching This can be solved with entity extraction.


Intent

An intent is a classification of the whole sentence.

For example, you can have an intent: find_resource. Then, you will need example sentences that should be classified as find_resource. For example:

X = [
  'Where can I find wood?',
  'What is the location of wood?',
  'Where do I find fire?',
  'Give me the coordinates of lemons.',
  'What is the best place to gather coal?',
  'Do you know where I can find tomatoes?',
  'Tell me a spot to collect wood.'
]

You could train a neural network to perform this classification task, but there are much simpler models that you could try first. A good machine learning library is scikit-learn which provides out-of-the-box traditional machine learning classification methods. It also has a feature_extraction.text sub-package for dealing with text.

Example

# Training data

## X is the sample sentences
X = [
    'Where can I find wood?',
    'What is the location of wood?',
    'Where do I find fire?',
    'Give me the coordinates of lemons.',
    'What is the best place to gather coal?',
    'Do you know where I can find tomatoes?',
    'Tell me a spot to collect wood.',
    'How can I level up strength?',
    'How do I train woodcutting?',
    'Where can I practice my swimming skill?',
    'Can I become better in running?',
    'Where can I train my woodcutting skill?'
]

## y is the intent class corresponding to sentences in X
y = [
    'find_resource',
    'find_resource',
    'find_resource',
    'find_resource',
    'find_resource',
    'find_resource',
    'find_resource',
    'improve_skill',
    'improve_skill',
    'improve_skill',
    'improve_skill',
    'improve_skill'
]

# Define the classifier

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline

clf = Pipeline(
    [
        ('tfidf', TfidfVectorizer()),
        ('sgd', SGDClassifier())
    ]
)

## Train the classifier

clf.fit(X, y)

# Test your classifier

## New sentences (that weren't in X and your model never seen before)

new_sentences = [
    'What are the coordinates of wood?',
    'Where can I find paper?',
    'How can I improve woodcutting?',
    'Where can I improve my jumping skill?'
]

predicted_intents = clf.predict(new_sentences)

print(predicted_intents)

> ['find_resource' 'find_resource' 'improve_skill' 'improve_skill']

Entity Extraction

Entity Extraction is the task of finding a specific sub-string within your sentence. This can be location, time, person_name, etc... or in your case resource_type.

Typical training data looks like:

X = [
    'Where can I find [wood](resource_type)?',
    'What is the location of [wood](resource_type)?',
    'Where do I find [fire](resource_type)?',
    'How can I level up [strength](skill_type)?',
    'Where can I train my [woodcutting](skill_type) skill?'
]

Indeed spaCy offers state of the art models. It has pre-trained entity types, but it also allows you to expand it with custom entities (resource_type in your case).


Side note

User 1: Where can I find cryptonite?
User 2: It can be found in lex luthors lab
Bot: Shall I add "lex luthors lab" as location for resource "cryptonite"?
User 2: @bot: yes
Bot: Done. 

You could model your problem as:

Intents:

X = [
  'Where can I find cryptonite?'
  'It can be found in lex luthors lab',
  'yes'
] 

y = [
  'find_resource',
  'provide_location',
  'affirm'
]

Entities:

X = [
  'Where can I find [cryptonite](resource_type)?'
  'It can be found in [lex luthors lab](location)',
  'yes'
] 

The trick will be for you to figure out if User 2 actually replied to User 1 or not. Also, you need to keep the state of the conversation, but that is dependant on the bot framework that you are using. Nevertheless, it is not an NLP issue anymore.

Related