Custom Component message attribute

Hi. I added my custom sentiment component (last one in my pipeline) where I use a pretrained model. I added init method (loading of the model and tokenizer), train method (just pass) and process method (making prediction). In the process method I add the sentiment to the output like that: message.set("sentiment", label2emotion[np.argmax(prediction)], add_to_output=True). It works when I use just interpreter and I see the sentiment in the output dict. But if I make rasa_core.run it doesn’t work – it is no longer in output dict (I check it in my custom action).

What am I doing wrong?

@Sam Sorry for bothering you, but it seems that you had similar issue (Access rasa_nlu custom components from rasa_core), could you help me please?

Hi @egorLab, no problem at all.

It’s been a while that I haven’t touched this but If my memory serves me right, this is how i solved it. Basically, after going through the rasa source code, i noticed that the method ‘_handle_message_with_tracker’ in processor.py passes values stored as entities to the tracker (this was back in september though, i don’t know if this has been changed). Anyway, you can tell from the line below that your data must be mapped to the key ‘entities’ not ‘sentiment’ as in your case.

tracker.update(UserUttered(message.text, parse_data["intent"],
                                   parse_data["entities"], parse_data,
                                   input_channel=message.input_channel))

But you will still need to format your output (predictions) accordingly,

{‘start’: starting_offset, ‘end’: ending_offset, ‘value’: sentiment_value, ‘entity’: ‘sentiment’, ‘extractor’: ‘sentiment extractor’}

starting_offset and ending_offset are both of type integers, sentiment_value must be a list entity and extractor are strings

So your code could be something like this

starting_offset, ending_offset = 0,0
sentiment_value = [0.6]

rasa_format = {‘start’: starting_offset, ‘end’: ending_offset, ‘value’: sentiment_value, ‘entity’: ‘sentiment’, ‘extractor’: ‘sentiment extractor’}

message.set(‘entities’, rasa_format, add_to_output=True)

But please do note that this is just a workaround. I honestly doubt that this should be the right way of doing it (assuming rasa does support such a thing).

Hi @Sam

Thanks a lot for this quick answer.

It worked, I just added rasa_format as a list:

message.set("entities", [rasa_format], add_to_output=True)

But it seems that it overwrites other entities, which, for example, got extracted from the text as usual (by ner_crf, for example). I want to have a sentiment all the time as just an addition to the regular entities, maybe there is a way to just add it. Did you worked around this issue also?

Maybe there is a better way to do it. @akelad, do you know if rasa supports such thing?

It doesn’t overwrite other entities for me. just a sanity check, is ‘ner_crf’ referenced in your config file?

Yeah, it is referenced:

language: "en"

pipeline:
- name: "tokenizer_whitespace"                              #defines how unstructured sentences will be tokenized
- name: "ner_crf"                                           #defines the model which will be used for entity extraction
- name: "intent_featurizer_count_vectors"                   #creates sentence representation
- name: "universal_sentence_encoder_featurizer.UniversalSentenceEncoderFeaturizer"
- name: "intent_classifier_tensorflow_embedding"            #defines a classifier for intent classification
  intent_tokenization_flag: true                            #sets the flag for intent label tokenization
  intent_split_symbol: "+"                                  #defines the character on which intent labels should be tokenized
- name: "sentiment.SentimentAnalyzer"

policies:
  - name: KerasPolicy
    batch_size: 32
    epochs: 10
    rnn_size: 64
    featurizer:
      - name: MaxHistoryTrackerFeaturizer
        max_history: 38
        state_featurizer:
          - name: LabelTokenizerSingleStateFeaturizer

When I remove - name: "sentiment.SentimentAnalyzer" I get the entities, which are extracted with ner_crf, otherwise I get only sentiment. By the way, here is the code, which I use in process method of the SentimentAnalyzer:

starting_offset, ending_offset = 0, 0 
sentiment_value = [label2emotion[np.argmax(prediction)]]
rasa_format = {"start": starting_offset,
                       "end": ending_offset,
                       "value": sentiment_value,
                       "entity": "sentiment",
                       "extractor": "sentiment"}
                                        
message.set("entities", [rasa_format], add_to_output=True)

I use GitHub - RasaHQ/rasa-workshop-pydata-nyc: This repository contains the code of the Rasa workshop at PyData NYC 2018 as a template for my experiments.

In your pipeline you have put the sentiment analyzer right after the intent classifier. I’m guessing this is why the ner_crf gets replaced with the sentiment analyzer. I suggest you put the sentiment analyzer right after the ner_crf in your pipeline.

Your pipeline should look something like:

language: “en”

pipeline:

  • name: “tokenizer_whitespace”

  • name: “ner_crf”

  • name: “sentiment.SentimentAnalyzer” # sentiment analyzer should go here

  • name: “intent_featurizer_count_vectors”

  • name: “universal_sentence_encoder_featurizer.UniversalSentenceEncoderFeaturizer”

  • name: “intent_classifier_tensorflow_embedding”
    intent_tokenization_flag: true
    intent_split_symbol: “+”

Try this and let me know if it works

Thanks! It worked, but other way around:

pipeline:
- name: "tokenizer_whitespace"
- name: "sentiment.SentimentAnalyzer"  
- name: "ner_crf"
- name: "intent_featurizer_count_vectors"
- name: "universal_sentence_encoder_featurizer.UniversalSentenceEncoderFeaturizer"
- name: "intent_classifier_tensorflow_embedding"
  intent_tokenization_flag: true
  intent_split_symbol: "+" 

Thanks again for your help.

Maybe if there is no other way to work with it already I could have a look and add it to Rasa.

1 Like

Hey, If you can share your sentiment.SentimentAnalyzer custom component, that would be great. Thanks