Code Monkey home page Code Monkey logo

frame-semantic-transformer's Introduction

Frame Semantic Transformer

ci PyPI

Frame-based semantic parsing library trained on FrameNet and built on HuggingFace's T5 Transformer

Live Demo: chanind.github.io/frame-semantic-transformer

Full docs: frame-semantic-transformer.readthedocs.io

About

This library draws heavily on Open-Sesame (paper) for inspiration on training and evaluation on FrameNet 1.7, and uses ideas from the paper Open-Domain Frame Semantic Parsing Using Transformers for using T5 as a frame-semantic parser. SimpleT5 was also used as a base for the initial training setup.

More details: FrameNet Parsing with Transformers Blog Post

Performance

This library uses the same train/dev/test documents and evaluation methodology as Open-Sesame, so that the results should be comparable between the 2 libraries. There are 2 pretrained models available, base and small, corresponding to t5-base and t5-small in Huggingface, respectively.

Task Sesame F1 (dev/test) Small Model F1 (dev/test) Base Model F1 (dev/test)
Trigger identification 0.80 / 0.73 0.75 / 0.71 0.78 / 0.74
Frame classification 0.90 / 0.87 0.87 / 0.86 0.91 / 0.89
Argument extraction 0.61 / 0.61 0.76 / 0.73 0.78 / 0.75

The base model performs similarly to Open-Sesame on trigger identification and frame classification tasks, but outperforms it by a significant margin on argument extraction. The small pretrained model has lower F1 than base across the board, but is 1/4 the size and still outperforms Open-Sesame at argument extraction.

Installation

pip install frame-semantic-transformer

Usage

Inference

The main entry to interacting with the library is the FrameSemanticTransformer class, as shown below. For inference the detect_frames() method is likely all that is needed to perform frame parsing.

from frame_semantic_transformer import FrameSemanticTransformer

frame_transformer = FrameSemanticTransformer()

result = frame_transformer.detect_frames("The hallway smelt of boiled cabbage and old rag mats.")

print(f"Results found in: {result.sentence}")
for frame in result.frames:
    print(f"FRAME: {frame.name}")
    for element in frame.frame_elements:
        print(f"{element.name}: {element.text}")

The result returned from detect_frames() is an object containing sentence, a parsed version of the original sentence text, trigger_locations, the indices within the sentence where frame triggers were detected, and frames, a list of all detected frames in the sentence. Within frames, each object containes name which corresponds to the FrameNet name of the frame, trigger_location corresponding to which trigger in the text this frame this frame uses, and frame_elements containing a list of all relevant frame elements found in the text.

For more efficient bulk processing of text, there's a detect_frames_bulk method which will process a list of sentences in batches. You can control the batch size using the batch_size param. By default this is 8.

frame_transformer = FrameSemanticTransformer(batch_size=16)

result = frame_transformer.detect_frames_bulk([
    "I'm getting quite hungry, but I can wait a bit longer.",
    "The chef gave the food to the customer.",
    "The hallway smelt of boiled cabbage and old rag mats.",
])

Note: It's not recommended to pass more than a single sentence per string to detect_frames() or detect_frames_bulk(). If you have a paragraph of text to process, it's best to split the paragraph into a list of sentences and pass the sentences as a list to detect_frames_bulk(). Only single sentences per string were used during training, so it's not clear how the model will handle multiple sentences in the same string.

# โŒ Bad, don't do this
frame_transformer.detect_frames("Fuzzy Wuzzy was a bear. Fuzzy Wuzzy had no hair.")

# ๐Ÿ‘ Do this instead
frame_transformer.detect_frames_bulk([
  "Fuzzy Wuzzy was a bear.",
  "Fuzzy Wuzzy had no hair.",
])

Running on GPU vs CPU

By default, FrameSemanticTransformer will attempt to use a GPU if one is available. If you'd like to explictly set whether to run on GPU vs CPU, you can pass the use_gpu param.

# force the model to run on the CPU
frame_transformer = FrameSemanticTransformer(use_gpu=False)

Loading Models

There are currently 2 available pre-trained models for inference, called base and small, fine-tuned from HuggingFace's t5-base and t5-small model respectively. If a local fine-tuned t5 model exists that can be loaded as well. If no model is specified, the base model will be used.

base_transformer = FrameSemanticTransformer("base") # this is also the default
small_transformer = FrameSemanticTransformer("small") # a smaller pretrained model which is faster to run
custom_transformer = FrameSemanticTransformer("/path/to/model") # load a custom t5 model

By default, models are lazily loaded when detect_frames() is first called. If you want to load the model sooner, you can call setup() on a FrameSemanticTransformer instance to load models immediately.

frame_transformer = FrameSemanticTransformer()
frame_transformer.setup() # load models immediately

Contributing

Any contributions to improve this project are welcome! Please open an issue or pull request in this repo with any bugfixes / changes / improvements you have!

This project uses Black for code formatting, Flake8 for linting, and Pytest for tests. Make sure any changes you submit pass these code checks in your PR. If you have trouble getting these to run feel free to open a pull-request regardless and we can discuss further in the PR.

License

The code contained in this repo is released under a MIT license, however the pretrained models are released under an Apache 2.0 license in accordance with FrameNet training data and HuggingFace's T5 base models.

Citation

If you use Frame semantic transformer in your work, please cite the following:

@article{chanin2023opensource,
  title={Open-source Frame Semantic Parsing},
  author={Chanin, David},
  journal={arXiv preprint arXiv:2303.12788},
  year={2023}
}

frame-semantic-transformer's People

Contributors

actions-user avatar chanind avatar dipta007 avatar ruckc avatar striebel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

frame-semantic-transformer's Issues

detect_frames_bulk function

Hi, Chanin,
Great job! But I have a problem. In September 2023, I used the 'pip install frame-semantic-transformer' to perform a pip install. However, the installed version does not have the function 'detect_frames'bulk'. Is this a problem with the version? May I ask how this should be resolved?

Citing this code repo

Can you please add a CITATION.cff file to the root of the repository to let others know how you would like them to cite your work.

GPU Acceleration Available?

Hi I am wondering if we can use GPU to accelerate the inference time? Right now it's somewhat slow using CPU. Thank you

Inconsistency with demo_page and local run

Greetings. It seems like the demo page provides better results than local version. Why is the difference?

For instance, given the sentence "The question of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour, has also been studied."

Demo page returns: 
Point_of_dispute
Question
of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour,

Scrutiny
Ground
The question of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour

However on local version:

from frame_semantic_transformer import FrameSemanticTransformer

frame_transformer = FrameSemanticTransformer()
result = frame_transformer.detect_frames("The question of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour, has also been studied.	")
print(f"Results found in: {result.sentence}")
for frame in result.frames:
    print(f"FRAME: {frame.name}")
    for element in frame.frame_elements:
        print(f"{element.name}: {element.text}: Trigger Location: {frame.trigger_location}")
Results found in: The question of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour
FRAME: Point_of_dispute
Question: of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour: Trigger Location: 4

DetectFramesResult(sentence='The question of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour', trigger_locations=[4], frames=[FrameResult(name='Point_of_dispute', trigger_location=4, frame_elements=[FrameElementResult(name='Question', text='of indirect aggression, whether in the form of verbal insults or more subtle antisocial behaviour')])])

frame-semantic-transformer-0.10.0

Can normal mortals use this too?

This looks quite fascinating, but when I try to run it on some of our corpora (as opposed to just some sentences through your demo), it always throws errors that neither your documentation nor chatGPT can (so far) solve. Therefore: could you share some Jupyter Notebook or Google Colab that would take some text field/column from a json or csv-file as an input and would generate semantic frames? Thanks!

Docker container does not return the result

Hi,

I have built and run the docker container but it does not work.

It give me the following output:

`~$ curl http://localhost:8888/detect-frames?sentence=i bought a new car

curl: (52) Empty reply from server

curl: (6) Could not resolve host: bought

curl: (6) Could not resolve host: a

curl: (6) Could not resolve host: new

curl: (6) Could not resolve host: car`

While for trying:
curl http://localhost:8888/

It give me the following output:
{"name":"Frame Semantic Transformer Demo"}

I am uncertain if I am making any mistakes.

KeyError when running frame_transformer.detect_frames

Great thanks for the contributions! The implemented semantic role labeling model helps me a lot!!

Some small issue when using this tool is when I call the frame_transformer.detect_frames(SENTENCE). The KeyError occurs given some specific sentences.

The log:

Traceback (most recent call last):
  File "/Users/CYY/opt/anaconda3/envs/ofa/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3442, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-11-0930869b8149>", line 1, in <module>
    frame_transformer.detect_frames("Fog is in the air at an intersection with several traffic lights.")
  File "/Users/CYY/opt/anaconda3/envs/ofa/lib/python3.8/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py", line 138, in detect_frames
    return self.detect_frames_bulk([sentence])[0]
  File "/Users/CYY/opt/anaconda3/envs/ofa/lib/python3.8/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py", line 216, in detect_frames_bulk
    return [results_map[parsed_sentences_map[sentence]] for sentence in sentences]
  File "/Users/CYY/opt/anaconda3/envs/ofa/lib/python3.8/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py", line 216, in <listcomp>
    return [results_map[parsed_sentences_map[sentence]] for sentence in sentences]
KeyError: 'Fog is in the air at an intersection with several traffic lights.'

Some specific inputs that trigger this error:

  1. two cars parked on the sidewalk on the street
  2. a modern flush toilet in a bathroom with tile.
  3. Fog is in the air at an intersection with several traffic lights.
  4. a man with a bike at a marina
  5. four urinals in a public restroom with a window
  6. a home office with laptop, printer, scanner, and extra monitor

Thanks!

GPU

Any suggestions for which versions of Tensorflow, CUDA and cuDNN to run?

IndexError for some texts using t5-small

For some texts, using t5-small, detect_frames returns a cryptic index error.

MRE:

#the two texts below differ *only* in that text_2 has a trailing period

text_1 = "Well, I came out and put on these and run around. And uh I like to run. I see the car. And I don't know, I uh. Yeah they're over there is going to put up and have a party uh. It's a child getting water. And she's she's going to making something nice. That's my father. He goes everywhere on that one. And that's people a lot of people like to go out on those"

text_2 = "Well, I came out and put on these and run around. And uh I like to run. I see the car. And I don't know, I uh. Yeah they're over there is going to put up and have a party uh. It's a child getting water. And she's she's going to making something nice. That's my father. He goes everywhere on that one. And that's people a lot of people like to go out on those."

from frame_semantic_transformer import FrameSemanticTransformer
fst_small = FrameSemanticTransformer("small")
fst_base = FrameSemanticTransformer("base")

>>>fst_small.detect_frames(text_1)
>>> DetectFramesResult(sentence="Well, I came out and put on these and run around. And uh I like to run. I see the car. And I don't know, I uh. Yeah they're over there is going to put up and have a party uh. It's a child getting water. And she's she's going to making something nice. That's my father. He goes everywhere on that one. And that's people a lot of people like to go out on those", trigger_locations=[8, 38, 59, 67, 99, 182, 261, 335], frames=[FrameResult(name='Arriving', trigger_location=8, frame_elements=[FrameElementResult(name='Theme', text='I'), FrameElementResult(name='Goal', text='out')]), FrameResult(name='Self_motion', trigger_location=38, frame_elements=[FrameElementResult(name='Self_mover', text='I'), FrameElementResult(name='Goal', text='around')]), FrameResult(name='Likelihood', trigger_location=59, frame_elements=[FrameElementResult(name='Hypothetical_event', text='I'), FrameElementResult(name='Hypothetical_event', text='to run')]), FrameResult(name='Self_motion', trigger_location=67, frame_elements=[FrameElementResult(name='Self_mover', text='I')]), FrameResult(name='Awareness', trigger_location=99, frame_elements=[FrameElementResult(name='Cognizer', text='I'), FrameElementResult(name='Content', text='I')]), FrameResult(name='People_by_age', trigger_location=182, frame_elements=[FrameElementResult(name='Person', text='child')]), FrameResult(name='Kinship', trigger_location=261, frame_elements=[FrameElementResult(name='Ego', text='my'), FrameElementResult(name='Alter', text='father')]), FrameResult(name='Likelihood', trigger_location=335, frame_elements=[FrameElementResult(name='Hypothetical_event', text='people a lot of people'), FrameElementResult(name='Hypothetical_event', text='to go out on those')])])

>>>fst_small.detect_frames(text_2)
>>>---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_1767205/3259689296.py in <module>
----> 1 fst_small.detect_frames(text_2)

~/anaconda3/envs/ktrain_loo/lib/python3.7/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py in detect_frames(self, sentence)
    167         base_sentence, trigger_locs = self._identify_triggers(sentence)
    168         # next detect frames for each trigger
--> 169         frames = self._classify_frames(base_sentence, trigger_locs)
    170 
    171         frame_and_locs = [

~/anaconda3/envs/ktrain_loo/lib/python3.7/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py in _classify_frames(self, sentence, trigger_locs)
    126             frame_classification_tasks, chunk_size=self.max_batch_size
    127         ):
--> 128             batch_results = self._batch_predict([task.get_input() for task in batch])
    129             for preds, frame_task in zip(
    130                 chunk_list(batch_results, self.predictions_per_sample),

~/anaconda3/envs/ktrain_loo/lib/python3.7/site-packages/frame_semantic_transformer/FrameSemanticTransformer.py in <listcomp>(.0)
    126             frame_classification_tasks, chunk_size=self.max_batch_size
    127         ):
--> 128             batch_results = self._batch_predict([task.get_input() for task in batch])
    129             for preds, frame_task in zip(
    130                 chunk_list(batch_results, self.predictions_per_sample),

~/anaconda3/envs/ktrain_loo/lib/python3.7/site-packages/frame_semantic_transformer/data/tasks/FrameClassificationTask.py in get_input(self)
     25 
     26     def get_input(self) -> str:
---> 27         potential_frames = get_possible_frames_for_trigger_bigrams(self.trigger_bigrams)
     28         return f"FRAME {' '.join(potential_frames)} : {self.trigger_labeled_text}"
     29 

~/anaconda3/envs/ktrain_loo/lib/python3.7/site-packages/frame_semantic_transformer/data/tasks/FrameClassificationTask.py in trigger_bigrams(self)
     44         pre_trigger_tokens = self.text[: self.trigger_loc].split()
     45         trigger_and_after_tokens = self.text[self.trigger_loc :].split()
---> 46         trigger = trigger_and_after_tokens[0]
     47         post_trigger_tokens = trigger_and_after_tokens[1:]
     48         bigrams: list[list[str]] = []

IndexError: list index out of range

>>> fst_base.detect_frames(text_1)
>>> DetectFramesResult(sentence="Well, I came out and put on these and run around. And uh I like to run. And I don't know, I uh. Yeah they're over there is going to put up and have a party uh. It's a child getting water. And she's she's going to make something nice. That's my father. He goes everywhere on that one. And that's people a lot of people like to go out on those", trigger_locations=[8, 38, 59, 67, 84, 173, 228, 244, 295, 302, 304, 311, 318, 326], frames=[FrameResult(name='Arriving', trigger_location=8, frame_elements=[FrameElementResult(name='Theme', text='I'), FrameElementResult(name='Goal', text='out')]), FrameResult(name='Self_motion', trigger_location=38, frame_elements=[FrameElementResult(name='Self_mover', text='I'), FrameElementResult(name='Path', text='around')]), FrameResult(name='Experiencer_focus', trigger_location=59, frame_elements=[FrameElementResult(name='Experiencer', text='I'), FrameElementResult(name='Content', text='to run')]), FrameResult(name='Self_motion', trigger_location=67, frame_elements=[FrameElementResult(name='Self_mover', text='I')]), FrameResult(name='Awareness', trigger_location=84, frame_elements=[FrameElementResult(name='Cognizer', text='I')]), FrameResult(name='Getting', trigger_location=173, frame_elements=[FrameElementResult(name='Recipient', text='a child'), FrameElementResult(name='Theme', text='water')]), FrameResult(name='Stimulus_focus', trigger_location=228, frame_elements=[FrameElementResult(name='Stimulus', text='something')]), FrameResult(name='Kinship', trigger_location=244, frame_elements=[FrameElementResult(name='Ego', text='my'), FrameElementResult(name='Alter', text='father')]), FrameResult(name='People', trigger_location=295, frame_elements=[FrameElementResult(name='Person', text='people')]), FrameResult(name='Quantified_mass', trigger_location=302, frame_elements=[FrameElementResult(name='Quantity', text='a lot'), FrameElementResult(name='Individuals', text='of people')]), FrameResult(name='Quantified_mass', trigger_location=304, frame_elements=[FrameElementResult(name='Quantity', text='a lot'), FrameElementResult(name='Individuals', text='of people')]), FrameResult(name='People', trigger_location=311, frame_elements=[FrameElementResult(name='Person', text='people')]), FrameResult(name='Desiring', trigger_location=318, frame_elements=[FrameElementResult(name='Experiencer', text='people'), FrameElementResult(name='Event', text='to go out on those')]), FrameResult(name='Motion', trigger_location=326, frame_elements=[FrameElementResult(name='Theme', text='people'), FrameElementResult(name='Goal', text='out on those')])])

>>> fst_base.detect_frames(text_2)
>>>DetectFramesResult(sentence="Well, I came out and put on these and run around. And uh I like to run. And I don't know, I uh. Yeah they're over there is going to put up and have a party uh. It's a child getting water. And she's she's going to make something nice. That's my father. He goes everywhere on that one. And that's people a lot of people like to go out on those.", trigger_locations=[8, 38, 59, 67, 84, 173, 228, 244, 295, 302, 304, 311, 318, 326], frames=[FrameResult(name='Arriving', trigger_location=8, frame_elements=[FrameElementResult(name='Theme', text='I'), FrameElementResult(name='Goal', text='out')]), FrameResult(name='Self_motion', trigger_location=38, frame_elements=[FrameElementResult(name='Self_mover', text='I'), FrameElementResult(name='Path', text='around')]), FrameResult(name='Experiencer_focus', trigger_location=59, frame_elements=[FrameElementResult(name='Experiencer', text='I'), FrameElementResult(name='Content', text='to run')]), FrameResult(name='Self_motion', trigger_location=67, frame_elements=[FrameElementResult(name='Self_mover', text='I')]), FrameResult(name='Awareness', trigger_location=84, frame_elements=[FrameElementResult(name='Cognizer', text='I')]), FrameResult(name='Getting', trigger_location=173, frame_elements=[FrameElementResult(name='Recipient', text='a child'), FrameElementResult(name='Theme', text='water')]), FrameResult(name='Stimulus_focus', trigger_location=228, frame_elements=[FrameElementResult(name='Stimulus', text='something')]), FrameResult(name='Kinship', trigger_location=244, frame_elements=[FrameElementResult(name='Ego', text='my'), FrameElementResult(name='Alter', text='father')]), FrameResult(name='People', trigger_location=295, frame_elements=[FrameElementResult(name='Person', text='people')]), FrameResult(name='Quantified_mass', trigger_location=302, frame_elements=[FrameElementResult(name='Quantity', text='a lot'), FrameElementResult(name='Individuals', text='of people')]), FrameResult(name='Quantified_mass', trigger_location=304, frame_elements=[FrameElementResult(name='Quantity', text='a lot'), FrameElementResult(name='Individuals', text='of people')]), FrameResult(name='People', trigger_location=311, frame_elements=[FrameElementResult(name='Person', text='people')]), FrameResult(name='Desiring', trigger_location=318, frame_elements=[FrameElementResult(name='Experiencer', text='people'), FrameElementResult(name='Event', text='to go out on those')]), FrameResult(name='Motion', trigger_location=326, frame_elements=[FrameElementResult(name='Theme', text='people'), FrameElementResult(name='Source', text='out'), FrameElementResult(name='Goal', text='on those')])])

Seems like a bug to me!

Particularly low accuracy with passive sentences without complements

Greetings all,

Has anyone noticed particularly low accuracy with passive sentences? Also, the parser acts strangely in those cases.

For instance:
Great results with

The participants' answers were looked at for general trends that might identify the differences in these emotions. 
{'Ground': "The participants'answers", 'Verb': 'look', 'Phenomenon': 'at for general trends that might identify the differences in these emotions'}

Also great with:

for example, specific words and phrases were looked for their accuracy.
{'Ground': "specific words and phrases", 'Verb': 'look', 'Phenomenon': 'for their accuracy'}
However: 

specific words and phrases were looked for
{'Sought_entity': 'for', 'Verb': 'look'}

P.S. I use my custom script to replace trigger_location with the actual verb, but this is handled after frame annotation.

Getting trigger word (not just location)

Looking at the API, we get a trigger_location in inference result. Any chance we can expand this to include the actual trigger word?

Along with this is it possible to get the frame element locations? A sentence may have "brown" multiple times in different contexts, and it would be helpful to have those locations within the sentence.

Just trying to make a frame visualizer wrapped around this implementation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.