Custom Components

Hi

I want to have a sentiment analyzer in my RASA NLU and trying to add it by using a library called TextBlob but unable to do so Here is my code so that you can help me in this

from future import absolute_import from future import division from future import print_function from future import unicode_literals from textblob import TextBlob

from rasa_nlu.components import Component

class SentimentAnalyzer(Component): “”“A new component”“”

# Name of the component to be used when integrating it in a
# pipeline. E.g. ``[ComponentA, ComponentB]``
# will be a proper pipeline definition where ``ComponentA``
# is the name of the first component of the pipeline.
name = "SentimentAnalyzer"

# Defines what attributes the pipeline component will
# provide when called. The listed attributes
# should be set by the component on the message object
# during test and train, e.g.
# ```message.set("entities", [...])```
provides = ["polarity","subjectivity","sentiment_indicator"]

# Which attributes on a message are required by this
# component. e.g. if requires contains "tokens", than a
# previous component in the pipeline needs to have "tokens"
# within the above described `provides` property.
requires = ["tokens"]

# Defines the default configuration parameters of a component
# these values can be overwritten in the pipeline configuration
# of the model. The component should choose sensible defaults
# and should be able to create reasonable results with the defaults.
defaults = {}

# Defines what language(s) this component can handle.
# This attribute is designed for instance method: `can_handle_language`.
# Default value is None which means it can handle all languages.
# This is an important feature for backwards compatibility of components.
language_list = None

def __init__(self, component_config=None):
    super(SentimentAnalyzer, self).__init__(component_config)

def train(self, training_data, cfg, **kwargs):
    """Train this component.

    This is the components chance to train itself provided
    with the training data. The component can rely on
    any context attribute to be present, that gets created
    by a call to :meth:`components.Component.pipeline_init`
    of ANY component and
    on any context attributes created by a call to
    :meth:`components.Component.train`
    of components previous to this one."""
    pass

def process(self, message, **kwargs):
    testimonial = TextBlob(str(message))
    if(testimonial.sentiment.polarity>0):
        sentiment_indicator="Positive"
    elif(testimonial.sentiment.polarity<0):
        sentiment_indicator="Negative"
    else:
        sentiment_indicator="Neutral"
    polarity=testimonial.sentiment.polarity
    subjectivity=testimonial.sentiment.polarity
    return [polarity,subjectivity,sentiment_indicator]
    
        
    
    

def persist(self, model_dir):
    """Persist this component to disk for future loading."""

    pass

@classmethod
def load(cls, model_dir=None, model_metadata=None, cached_component=None,
         **kwargs):
    """Load this component from file."""

    if cached_component:
        return cached_component
    else:
        component_config = model_metadata.for_component(cls.name)
        return cls(component_config)

what exactly are you unable to do? Have you followed the advice in the documentation?

yes I have followed the documentation and have pasted it in the forum to know what was going wrong I am using textblob library for sentiment analysis and the output is not reflecting in NLU output So can you just check the code so that you can point out where I am going wrong and is there any video link or other link which can explain how to build an end to end custom component for NLU or how we can integrate sentiment analyzer in NLU I am doing this as a Proof of concept for my company so that I can showcase this to potential client as a solution. My email Id is Shreyas.a.p@accenture.com so that if we can connect on this problem I am ready to go forward

import typing
from typing import Any, Optional, Text, Dict, List, Type
from rasa.nlu.components import Component
from rasa.nlu import utils
if typing.TYPE_CHECKING:
   from rasa.nlu.model import Metadata
import rasa.utils.io as io_utils
from textblob import TextBlob

from rasa.nlu.config import RasaNLUModelConfig
from rasa.nlu.training_data import Message, TrainingData
from rasa.nlu.tokenizers.tokenizer import Tokenizer


class SentimentAnalyzer(Component):
    """A custom sentiment analysis component"""
    """A new component"""

    @classmethod
    def required_components(cls) -> List[Type[Component]]:
        """Specify which components need to be present in the pipeline."""
        return [Tokenizer]

    #language_list = ["en", "fr"]
    language_list = None
    defaults = {}
    provides = ["entities"]
    name = "sentiment"

    def __init__(self, component_config: Optional[Dict[Text, Any]] = None) -> None:
        super().__init__(component_config)

    def train(
        self,
        training_data: TrainingData,
        config: Optional[RasaNLUModelConfig] = None,
        **kwargs: Any,
    ) -> None:
        pass

    def convert_to_rasa(self, sentiment, confidence):
        """Convert model output into the Rasa NLU compatible output format."""
        entity = {"value": sentiment,
                  "entity": "sentiment",
                  "confidence":confidence,
                  "extractor": "sentiment_extractor"}
        return entity

    def preprocessing(self, tokens):
        pass


    def process(self, message: Message, **kwargs: Any) -> None:
        testimonial = TextBlob(str(message.text))
        if (testimonial.sentiment.polarity>0):
                sentiment="pos"
        elif (testimonial.sentiment.polarity<0):
                sentiment="neg"
        else:
                sentiment="neu"
      # To introduce confidence we can use and 'recenter' polarity (0 is neutral, 1 is positive, -1 is negative)
      # It doesn't give very good confidence results though
        confidence = (abs(abs(testimonial.sentiment.polarity)-0.5))/0.5
        entity = self.convert_to_rasa(sentiment, confidence)
        message.set("entities", [entity], add_to_output=True)

    def persist(self, file_name: Text, model_dir: Text) -> Optional[Dict[Text, Any]]:
        pass

    @classmethod
    def load(
        cls,
        meta: Dict[Text, Any],
        model_dir: Optional[Text] = None,
        model_metadata: Optional["Metadata"] = None,
        cached_component: Optional["Component"] = None,
        **kwargs: Any,
    ) -> "Component":
        if cached_component:
            return cached_component
        else:
            return cls(meta)

Based on RASA 1.9.4

All in Docker (added TextBlob in RASA Docker requirements.txt)

It works (much better in english than in french, I guess due to TextBlob performance)