{ "cells": [ { "cell_type": "markdown", "id": "db5f4f9a-7776-42b3-8758-85624d4c15ea", "metadata": { "id": "db5f4f9a-7776-42b3-8758-85624d4c15ea" }, "source": [ "![JohnSnowLabs](https://nlp.johnsnowlabs.com/assets/images/logo.png)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "21e9eafb", "metadata": { "id": "21e9eafb" }, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/JohnSnowLabs/spark-nlp-workshop/blob/master/legal-nlp/08.2.NER_using_Question_Answering.ipynb)" ] }, { "cell_type": "markdown", "id": "9859b3bc-cec4-4189-88ed-37add5484623", "metadata": { "id": "9859b3bc-cec4-4189-88ed-37add5484623" }, "source": [ "# NER using Question Answering models\n", "Which may be very useful to extract long entities / spans which usually are not extracted properly with traditional NER models" ] }, { "cell_type": "markdown", "id": "gk3kZHmNj51v", "metadata": { "collapsed": false, "id": "gk3kZHmNj51v" }, "source": [ "# Installation" ] }, { "cell_type": "code", "execution_count": null, "id": "_914itZsj51v", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "_914itZsj51v", "outputId": "4a0bb6f9-ba64-4e34-e15b-68d2058ff6ce", "pycharm": { "is_executing": true } }, "outputs": [], "source": [ "! pip install -q johnsnowlabs" ] }, { "cell_type": "markdown", "id": "YPsbAnNoPt0Z", "metadata": { "id": "YPsbAnNoPt0Z" }, "source": [ "## Automatic Installation\n", "Using my.johnsnowlabs.com SSO" ] }, { "cell_type": "code", "execution_count": null, "id": "fY0lcShkj51w", "metadata": { "id": "fY0lcShkj51w", "pycharm": { "is_executing": true } }, "outputs": [], "source": [ "from johnsnowlabs import nlp, legal\n", "\n", "# nlp.install(force_browser=True)" ] }, { "cell_type": "markdown", "id": "hsJvn_WWM2GL", "metadata": { "id": "hsJvn_WWM2GL" }, "source": [ "## Manual downloading\n", "If you are not registered in my.johnsnowlabs.com, you received a license via e-email or you are using Safari, you may need to do a manual update of the license.\n", "\n", "- Go to my.johnsnowlabs.com\n", "- Download your license\n", "- Upload it using the following command" ] }, { "cell_type": "code", "execution_count": null, "id": "i57QV3-_P2sQ", "metadata": { "id": "i57QV3-_P2sQ" }, "outputs": [], "source": [ "from google.colab import files\n", "print('Please Upload your John Snow Labs License using the button below')\n", "license_keys = files.upload()" ] }, { "cell_type": "markdown", "id": "xGgNdFzZP_hQ", "metadata": { "id": "xGgNdFzZP_hQ" }, "source": [ "- Install it" ] }, { "cell_type": "code", "execution_count": null, "id": "OfmmPqknP4rR", "metadata": { "id": "OfmmPqknP4rR" }, "outputs": [], "source": [ "nlp.install()" ] }, { "cell_type": "markdown", "id": "DCl5ErZkNNLk", "metadata": { "id": "DCl5ErZkNNLk" }, "source": [ "# Starting" ] }, { "cell_type": "code", "execution_count": null, "id": "wRXTnNl3j51w", "metadata": { "id": "wRXTnNl3j51w" }, "outputs": [], "source": [ "spark = nlp.start()" ] }, { "cell_type": "markdown", "id": "37bb8ae4", "metadata": { "id": "37bb8ae4" }, "source": [ "# NER, Question Generation and Question Answering for Long-Span extraction" ] }, { "cell_type": "markdown", "id": "43420eee-1c29-4148-b1c8-fa7884eff9b3", "metadata": { "id": "43420eee-1c29-4148-b1c8-fa7884eff9b3" }, "source": [ "Legal documents are known to be very long. Although you can divide the docuuments into paragraphs or sections, and those into sentences, the resulted sentences are still long.\n", "\n", "Let's take a look at this example:\n", "\n", "`Buyer shall use such materials and supplies only in accordance with the present agreement`\n", "\n", "Not, let's imagine we want to extract three entities:\n", "1) The Subject (`Buyer`)\n", "2) The Action (`shall use`)\n", "3) The Object (what the Buyer shall use? - `such materials and supplies only in accordance with the present agreement`)\n", "\n", "Although Subject and Action can be totally manageable by traditional NER, it usually struggles the longer the spans are. Trying to model the extraction of Object with a simple NER may result in word fading, when some of the initial or ending words fade into `O`.\n", "\n", "We present in this notebook a solution for Long Span Extraction: Using an Automatic Question Generator and a Question Answering model to:\n", "1) First, using NER, detect entities as the `Subject` and the `Action`. \n", "\n", "Example: `Buyer - SUBJECT`, `shall use - OBJECT`\n", "\n", "2) Automatically generate a question to ask for the `Object`, using `Subject` and `Action`;\n", "\n", "Example: `What shall the Buyer use?`\n", "\n", "3) Use the question and the sentence to retrieve `Object`, without the limitations of traditional NER;\n", "\n", "Example: `What shall the Buyer use? such materials and supplies only in accordance with the present agreement`\n", "\n", "Last, but not least, it's very important to chose a domain-specific Question Answering model.\n" ] }, { "cell_type": "markdown", "id": "03c6cb7d-b34f-4974-b63b-c04640f6a668", "metadata": { "id": "03c6cb7d-b34f-4974-b63b-c04640f6a668" }, "source": [ "# Answering the question - `What?`\n", "Let's suppose we have the sentence of the example:\n", "\n", "`The Buyer shall use such materials and supplies only in accordance with the present agreement`\n", "\n", "In Spark NLP for Legal, we have a trained NER model which is able to extract Subjects (`Buyer`) and Actions (`shall use`) of agreements / obligations with good accuracy.\n", "\n", "It's also trained for extracting the `Object` using NER, but it's usage is limited due to the restrictions commented above.\n", "\n", "Let's get SUBJECT and ACTION and automatically create a question with them." ] }, { "cell_type": "code", "execution_count": null, "id": "b342ab82", "metadata": { "id": "b342ab82" }, "outputs": [], "source": [ "text = \"\"\"The Buyer shall use such materials and supplies only in accordance with the present agreement\"\"\"" ] }, { "cell_type": "code", "execution_count": null, "id": "2948d346-d522-43b9-9cd7-99430882621f", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "2948d346-d522-43b9-9cd7-99430882621f", "outputId": "0cbcfe55-13db-4c29-f41b-e9ed717f4ccb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "legner_obligations download started this may take some time.\n", "[OK!]\n", "legqa_bert_large download started this may take some time.\n", "Approximate size to download 1.2 GB\n", "[OK!]\n" ] } ], "source": [ "import pandas as pd\n", "\n", "documentAssembler = nlp.DocumentAssembler()\\\n", " .setInputCol(\"text\")\\\n", " .setOutputCol(\"document\")\n", "\n", "sparktokenizer = nlp.Tokenizer()\\\n", " .setInputCols(\"document\")\\\n", " .setOutputCol(\"token\")\n", "\n", "tokenClassifier = legal.BertForTokenClassification.pretrained(\"legner_obligations\", \"en\", \"legal/models\")\\\n", " .setInputCols(\"token\", \"document\")\\\n", " .setOutputCol(\"label\")\\\n", " .setCaseSensitive(True)\n", "\n", "nerconverter = nlp.NerConverter()\\\n", " .setInputCols([\"document\", \"token\", \"label\"])\\\n", " .setOutputCol(\"ner_chunk\")\n", "\n", "# setEntities1 says which entity from NER goes first in the question\n", "# setEntities2 says which entity from NER goes second in the question\n", "# setQuestionMark to True adds a '?' at the end of the sentence (after entity 2)\n", "# To sum up, the pattern is [QUESTIONPRONOUN] [ENTITY1] [ENTITY2] [QUESTIONMARK]\n", "qagenerator = legal.NerQuestionGenerator()\\\n", " .setInputCols([\"ner_chunk\"])\\\n", " .setOutputCol(\"question\")\\\n", " .setQuestionMark(False)\\\n", " .setQuestionPronoun(\"What\")\\\n", " .setEntities1([\"OBLIGATION_SUBJECT\"])\\\n", " .setEntities2([\"OBLIGATION_ACTION\"])\n", "\n", "qa =nlp.BertForQuestionAnswering.pretrained(\"legqa_bert_large\",\"en\", \"legal/models\") \\\n", " .setInputCols([\"question\", \"document\"]) \\\n", " .setOutputCol(\"answer\") \\\n", " .setCaseSensitive(True)\n", " \n", "pipeline = nlp.Pipeline(stages=[\n", " documentAssembler,\n", " sparktokenizer,\n", " tokenClassifier,\n", " nerconverter,\n", " qagenerator,\n", " qa\n", " ]\n", ")\n", "\n", "p_model = pipeline.fit(spark.createDataFrame(pd.DataFrame({'text': ['']})))\n", "\n", "res = p_model.transform(spark.createDataFrame([[text]]).toDF(\"text\"))" ] }, { "cell_type": "code", "execution_count": null, "id": "183fb2db-1cee-4f78-a486-dd6c9f6abd57", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "183fb2db-1cee-4f78-a486-dd6c9f6abd57", "outputId": "7838e677-b497-4bad-b72a-5a906c200620" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+-----------------------+\n", "|result |\n", "+-----------------------+\n", "|[What Buyer shall use ]|\n", "+-----------------------+\n", "\n" ] } ], "source": [ "res.select('question.result').show(truncate=False)" ] }, { "cell_type": "code", "execution_count": null, "id": "5422560c-718e-4606-9054-678371f539b3", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "5422560c-718e-4606-9054-678371f539b3", "outputId": "58794682-2b02-431d-ce45-5b63661788b8" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "+-------------------------------------------------+\n", "|result |\n", "+-------------------------------------------------+\n", "|[The Buyer shall use such materials and supplies]|\n", "+-------------------------------------------------+\n", "\n" ] } ], "source": [ "res.select('answer.result').show(truncate=False)" ] }, { "cell_type": "markdown", "id": "a85caa4b", "metadata": { "id": "a85caa4b" }, "source": [ "Let's get 4 additional examples" ] }, { "cell_type": "code", "execution_count": null, "id": "49ee5ee7-208c-463e-9e04-0af46b69dd0e", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 175 }, "id": "49ee5ee7-208c-463e-9e04-0af46b69dd0e", "outputId": "062b5b57-c308-45c6-d3ff-18989e9096df" }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " | text | \n", "
---|---|
0 | \n", "The Buyer shall use such materials and supplie... | \n", "
1 | \n", "The Provider will notify the Buyer about the r... | \n", "
2 | \n", "Amazon agrees to supply 1-year license without... | \n", "
3 | \n", "The Supplier should ship the product in less t... | \n", "