diff --git a/argilla/docs/tutorials/text_classification.ipynb b/argilla/docs/tutorials/text_classification.ipynb index cd9f4e3cf9..f22ccf7295 100644 --- a/argilla/docs/tutorials/text_classification.ipynb +++ b/argilla/docs/tutorials/text_classification.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Text classification task" + "# Text classification" ] }, { @@ -119,7 +119,7 @@ "# Uncomment the last line and set your HF_TOKEN if your space is private\n", "client = rg.Argilla(\n", " api_url=\"https://[your-owner-name]-[your_space_name].hf.space\",\n", - " api_key=\"owner.apikey\"\n", + " api_key=\"owner.apikey\",\n", " # headers={\"Authorization\": f\"Bearer {HF_TOKEN}\"}\n", ")" ] @@ -238,7 +238,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The next step is to add suggestions to the dataset. In our case, we will generate them using a zero-shot SetFit model. However, you can use a framework or technique of your choice." + "The next step is to add suggestions to the dataset. This will make things easier and faster for the annotation team. Suggestions will appear as preselected options, so annotators will only need to correct them. In our case, we will generate them using a zero-shot SetFit model. However, you can use a framework or technique of your choice." ] }, { @@ -277,7 +277,6 @@ "outputs": [], "source": [ "def train_model(model_name, dataset):\n", - " \n", " model = SetFitModel.from_pretrained(model_name)\n", "\n", " trainer = Trainer(\n", @@ -286,7 +285,7 @@ " )\n", "\n", " trainer.train()\n", - " \n", + "\n", " return model" ] }, @@ -342,11 +341,10 @@ "outputs": [], "source": [ "def predict(model, input, labels):\n", - " \n", " model.labels = labels\n", - " \n", + "\n", " prediction = model.predict([input])\n", - " \n", + "\n", " return prediction[0]" ] }, @@ -435,9 +433,7 @@ "metadata": {}, "outputs": [], "source": [ - "status_filter = rg.Query(\n", - " filter=rg.Filter((\"response.status\", \"==\", \"submitted\"))\n", - ")\n", + "status_filter = rg.Query(filter=rg.Filter((\"response.status\", \"==\", \"submitted\")))\n", "\n", "submitted = dataset.records(status_filter).to_list(flatten=True)" ] @@ -455,10 +451,12 @@ "metadata": {}, "outputs": [], "source": [ - "train_records = [{\n", - " \"text\" : r[\"review\"],\n", - " \"label\" : r[\"sentiment_label.responses\"][0],\n", - " } for r in submitted\n", + "train_records = [\n", + " {\n", + " \"text\": r[\"review\"],\n", + " \"label\": r[\"sentiment_label.responses\"][0],\n", + " }\n", + " for r in submitted\n", "]\n", "train_dataset = Dataset.from_list(train_records)\n", "train_dataset = sample_dataset(train_dataset, label_column=\"label\", num_samples=8)" diff --git a/argilla/docs/tutorials/token_classification.ipynb b/argilla/docs/tutorials/token_classification.ipynb index f1535bedd1..4c328f15ac 100644 --- a/argilla/docs/tutorials/token_classification.ipynb +++ b/argilla/docs/tutorials/token_classification.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Token classification task" + "# Token classification\n" ] }, { @@ -15,18 +15,18 @@ "\n", "We will follow these steps:\n", "\n", - "* Configure the Argilla dataset\n", - "* Add initial model suggestions\n", - "* Evaluate with Argilla\n", - "* Train your model\n", - "* Update the suggestions with the new model" + "- Configure the Argilla dataset\n", + "- Add initial model suggestions\n", + "- Evaluate with Argilla\n", + "- Train your model\n", + "- Update the suggestions with the new model\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Getting started" + "## Getting started\n" ] }, { @@ -35,26 +35,26 @@ "source": [ "If you have already deployed Argilla Server, you can skip this step. Otherwise, you can quickly deploy it in two different ways:\n", "\n", - "* Remotely using a [HF Space](https://huggingface.co/new-space?template=argilla/argilla-template-space). ⚠️ If persistent storage is not enabled, you will lose your data when the server is stopped.\n", + "- Remotely using a [HF Space](https://huggingface.co/new-space?template=argilla/argilla-template-space). ⚠️ If persistent storage is not enabled, you will lose your data when the server is stopped.\n", "\n", "!!! note\n", - " As this is a release candidate version, you'll need to manually change the version in the HF Space Files > Dockerfile to `argilla/argilla-quickstart:v2.0.0rc2`.\n", + "As this is a release candidate version, you'll need to manually change the version in the HF Space Files > Dockerfile to `argilla/argilla-quickstart:v2.0.0rc2`.\n", "\n", - "* Locally using Docker: `docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:v2.0.0rc2`" + "- Locally using Docker: `docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:v2.0.0rc2`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Set up the environment" + "### Set up the environment\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To complete this tutorial, you need to install the Argilla SDK and a few third-party libraries via `pip`." + "To complete this tutorial, you need to install the Argilla SDK and a few third-party libraries via `pip`.\n" ] }, { @@ -79,7 +79,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's make the needed imports:" + "Let's make the needed imports:\n" ] }, { @@ -102,7 +102,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You also need to connect to the Argilla server with the `api_url` and `api_key`." + "You also need to connect to the Argilla server with the `api_url` and `api_key`.\n" ] }, { @@ -116,7 +116,7 @@ "# Uncomment the last line and set your HF_TOKEN if your space is private\n", "client = rg.Argilla(\n", " api_url=\"https://[your-owner-name]-[your_space_name].hf.space\",\n", - " api_key=\"owner.apikey\"\n", + " api_key=\"owner.apikey\",\n", " # headers={\"Authorization\": f\"Bearer {HF_TOKEN}\"}\n", ")" ] @@ -125,14 +125,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Configure and create the Argilla dataset" + "## Configure and create the Argilla dataset\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we will need to configure the dataset. In the settings, we can specify the guidelines, fields, and questions. If needed, you can also add metadata and vectors. However, for our use case, we just need a text field and a span question." + "Now, we will need to configure the dataset. In the settings, we can specify the guidelines, fields, and questions. If needed, you can also add metadata and vectors. However, for our use case, we just need a text field and a span question. We will focus on Name Entity Recognition, but this workflow can also be applied to Span Classification, which differs in that the spans are less clearly defined and often overlap." ] }, { @@ -141,7 +141,26 @@ "metadata": {}, "outputs": [], "source": [ - "labels = [\"CARDINAL\", \"DATE\", \"PERSON\", \"NORP\", \"GPE\", \"LAW\", \"PERCENT\", \"ORDINAL\", \"MONEY\", \"WORK_OF_ART\", \"FAC\", \"TIME\", \"QUANTITY\", \"PRODUCT\", \"LANGUAGE\", \"ORG\", \"LOC\", \"EVENT\"]\n", + "labels = [\n", + " \"CARDINAL\",\n", + " \"DATE\",\n", + " \"PERSON\",\n", + " \"NORP\",\n", + " \"GPE\",\n", + " \"LAW\",\n", + " \"PERCENT\",\n", + " \"ORDINAL\",\n", + " \"MONEY\",\n", + " \"WORK_OF_ART\",\n", + " \"FAC\",\n", + " \"TIME\",\n", + " \"QUANTITY\",\n", + " \"PRODUCT\",\n", + " \"LANGUAGE\",\n", + " \"ORG\",\n", + " \"LOC\",\n", + " \"EVENT\",\n", + "]\n", "\n", "settings = rg.Settings(\n", " guidelines=\"Classify individual tokens according to the specified categories, ensuring that any overlapping or nested entities are accurately captured.\",\n", @@ -158,6 +177,7 @@ " field=\"text\",\n", " labels=labels,\n", " title=\"Classify the tokens according to the specified categories.\",\n", + " allow_overlapping=False,\n", " )\n", " ],\n", ")" @@ -167,7 +187,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's create the dataset with the name and the defined settings:" + "Let's create the dataset with the name and the defined settings:\n" ] }, { @@ -177,7 +197,7 @@ "outputs": [], "source": [ "dataset = rg.Dataset(\n", - " name=f\"token_classification_dataset\",\n", + " name=\"token_classification_dataset\",\n", " settings=settings,\n", ")\n", "dataset.create()" @@ -187,14 +207,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Add records" + "## Add records\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Even if we have created the dataset, it still lacks the information to be annotated (you can check it in the UI). In this case, we will use the `ontonote5` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/tner/ontonotes5?row=0). Specifically, we will use 2100 samples from the `test` split." + "We have created the dataset (you can check it in the UI), but we still need to add the data for annotation. In this case, we will use the `ontonote5` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/tner/ontonotes5?row=0). Specifically, we will use 2100 samples from the `test` split." ] }, { @@ -210,7 +230,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will iterate over the Hugging Face dataset, adding data to the corresponding field in the `Record` object for the Argilla dataset. Then, we will easily add them to the dataset using `log`." + "We will iterate over the Hugging Face dataset, adding data to the corresponding field in the `Record` object for the Argilla dataset. Then, we will easily add them to the dataset using `log`.\n" ] }, { @@ -219,10 +239,7 @@ "metadata": {}, "outputs": [], "source": [ - "records = [\n", - " rg.Record(fields={\"text\": \" \".join(row[\"tokens\"])})\n", - " for row in hf_dataset\n", - "]\n", + "records = [rg.Record(fields={\"text\": \" \".join(row[\"tokens\"])}) for row in hf_dataset]\n", "\n", "dataset.records.log(records)" ] @@ -231,14 +248,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Add initial model suggestions" + "### Add initial model suggestions\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The next step is to add suggestions to the dataset. In our case, we will generate them using a GLiNER model. However, you can use a framework or technique of your choice." + "The next step is to add suggestions to the dataset. This will make things easier and faster for the annotation team. Suggestions will appear as preselected options, so annotators will only need to correct them. In our case, we will generate them using a GLiNER model. However, you can use a framework or technique of your choice.\n" ] }, { @@ -246,14 +263,14 @@ "metadata": {}, "source": [ "!!! note\n", - " For further information, you can check the [GLiNER repository](https://github.com/urchade/GLiNER) and the [original paper](https://arxiv.org/abs/2311.08526)." + "For further information, you can check the [GLiNER repository](https://github.com/urchade/GLiNER) and the [original paper](https://arxiv.org/abs/2311.08526).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We will start by loading the pre-trained GLiNER model. Specifically, we will use `gliner_mediumv2`, available in [Hugging Face Hub](https://huggingface.co/urchade/gliner_medium-v1)." + "We will start by loading the pre-trained GLiNER model. Specifically, we will use `gliner_mediumv2`, available in [Hugging Face Hub](https://huggingface.co/urchade/gliner_medium-v1).\n" ] }, { @@ -269,7 +286,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we will create a function to generate predictions using this general model, which can identify the specified labels without being pre-trained on them. The function will return a dictionary formatted with the necessary schema to add entities to our Argilla dataset. This schema includes the keys 'start’ and ‘end’ to indicate the indices where the span begins and ends, as well as ‘label’ for the entity label." + "Next, we will create a function to generate predictions using this general model, which can identify the specified labels without being pre-trained on them. The function will return a dictionary formatted with the necessary schema to add entities to our Argilla dataset. This schema includes the keys 'start’ and ‘end’ to indicate the indices where the span begins and ends, as well as ‘label’ for the entity label.\n" ] }, { @@ -280,14 +297,16 @@ "source": [ "def predict_gliner(model, text, labels, threshold):\n", " entities = model.predict_entities(text, labels, threshold)\n", - " return [{k: v for k, v in ent.items() if k not in {'score', 'text'}} for ent in entities]" + " return [\n", + " {k: v for k, v in ent.items() if k not in {\"score\", \"text\"}} for ent in entities\n", + " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To update the records, we will need to retrieve them from the server and update them with the new suggestions. The `id` will always need to be provided as it is the records' identifier to update a record and avoid creating a new one." + "To update the records, we will need to retrieve them from the server and update them with the new suggestions. The `id` will always need to be provided as it is the records' identifier to update a record and avoid creating a new one.\n" ] }, { @@ -299,7 +318,9 @@ "data = dataset.records.to_list(flatten=True)\n", "updated_data = [\n", " {\n", - " \"span_label\": predict_gliner(model=gliner_model, text=sample[\"text\"], labels=labels, threshold=0.70),\n", + " \"span_label\": predict_gliner(\n", + " model=gliner_model, text=sample[\"text\"], labels=labels, threshold=0.70\n", + " ),\n", " \"id\": sample[\"id\"],\n", " }\n", " for sample in data\n", @@ -311,21 +332,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Voilà! We have added the suggestions to the dataset and they will appear in the UI marked with ✨. " + "Voilà! We have added the suggestions to the dataset and they will appear in the UI marked with ✨.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Evaluate with Argilla" + "## Evaluate with Argilla\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we can start the annotation process. Just open the dataset in the Argilla UI and start annotating the records. If the suggestions are correct, you can just click on `Submit`. Otherwise, you can select the correct label." + "Now, we can start the annotation process. Just open the dataset in the Argilla UI and start annotating the records. If the suggestions are correct, you can just click on `Submit`. Otherwise, you can select the correct label.\n" ] }, { @@ -333,14 +354,14 @@ "metadata": {}, "source": [ "!!! note\n", - " Check this [how-to guide](../how_to_guides/annotate.md) to know more about annotating in the UI." + "Check this [how-to guide](../how_to_guides/annotate.md) to know more about annotating in the UI.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Train your model" + "## Train your model\n" ] }, { @@ -349,7 +370,7 @@ "source": [ "After the annotation, we will have a robust dataset to train our model for entity recognition. For our case, we will train a SpanMarker model, but you can select any model of your choice. So, let's start by retrieving the annotated records.\n", "\n", - "> Check this [how-to guide](../how_to_guides/query_export.md) to learn more about filtering and querying in Argilla." + "> Check this [how-to guide](../how_to_guides/query_export.md) to learn more about filtering and querying in Argilla.\n" ] }, { @@ -365,7 +386,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - " In our case, we submitted 2000 annotations using the bulk view." + "In our case, we submitted 2000 annotations using the bulk view.\n" ] }, { @@ -374,9 +395,7 @@ "metadata": {}, "outputs": [], "source": [ - "status_filter = rg.Query(\n", - " filter=rg.Filter((\"response.status\", \"==\", \"submitted\"))\n", - ")\n", + "status_filter = rg.Query(filter=rg.Filter((\"response.status\", \"==\", \"submitted\")))\n", "\n", "submitted = dataset.records(status_filter).to_list(flatten=True)" ] @@ -385,7 +404,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "SpanMarker accepts any dataset as long as it has the `tokens` and `ner_tags` columns. The `ner_tags` can be annotated using the IOB, IOB2, BIOES or BILOU labeling scheme, as well as regular unschemed labels. In our case, we have chosen to use the IOB format. Thus, we will define a function to extract the annotated NER tags according to this schema." + "SpanMarker accepts any dataset as long as it has the `tokens` and `ner_tags` columns. The `ner_tags` can be annotated using the IOB, IOB2, BIOES or BILOU labeling scheme, as well as regular unschemed labels. In our case, we have chosen to use the IOB format. Thus, we will define a function to extract the annotated NER tags according to this schema.\n" ] }, { @@ -393,7 +412,7 @@ "metadata": {}, "source": [ "!!! note\n", - " For further information, you can check the [SpanMarker documentation](https://tomaarsen.github.io/SpanMarkerNER/)." + "For further information, you can check the [SpanMarker documentation](https://tomaarsen.github.io/SpanMarkerNER/).\n" ] }, { @@ -404,15 +423,16 @@ "source": [ "def get_iob_tag_for_token(token_start, token_end, ner_spans):\n", " for span in ner_spans:\n", - " if token_start >= span['start'] and token_end <= span['end']:\n", - " if token_start == span['start']:\n", + " if token_start >= span[\"start\"] and token_end <= span[\"end\"]:\n", + " if token_start == span[\"start\"]:\n", " return f\"B-{span['label']}\"\n", " else:\n", " return f\"I-{span['label']}\"\n", - " return 'O'\n", + " return \"O\"\n", + "\n", "\n", "def extract_ner_tags(text, responses):\n", - " tokens = re.split(r'(\\s+)', text)\n", + " tokens = re.split(r\"(\\s+)\", text)\n", " ner_tags = []\n", "\n", " current_position = 0\n", @@ -431,7 +451,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's now extract them and save two lists with the tokens and NER tags, which will help us build our dataset to train the SpanMarker model." + "Let's now extract them and save two lists with the tokens and NER tags, which will help us build our dataset to train the SpanMarker model.\n" ] }, { @@ -453,7 +473,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In addition, we will have to indicate the labels and they should be formatted as integers. So, we will retrieve them and map them." + "In addition, we will have to indicate the labels and they should be formatted as integers. So, we will retrieve them and map them.\n" ] }, { @@ -474,7 +494,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, we will create a dataset with the train and validation sets." + "Finally, we will create a dataset with the train and validation sets.\n" ] }, { @@ -483,22 +503,26 @@ "metadata": {}, "outputs": [], "source": [ - "records = [{\n", - " \"tokens\" : token,\n", - " \"ner_tags\" : ner_tag,\n", - " } for token, ner_tag in zip(tokens, mapped_ner_tags)\n", + "records = [\n", + " {\n", + " \"tokens\": token,\n", + " \"ner_tags\": ner_tag,\n", + " }\n", + " for token, ner_tag in zip(tokens, mapped_ner_tags)\n", "]\n", - "span_dataset = DatasetDict({\n", - " 'train': Dataset.from_list(records[:1500]),\n", - " 'validation': Dataset.from_list(records[1501:2000])\n", - "})" + "span_dataset = DatasetDict(\n", + " {\n", + " \"train\": Dataset.from_list(records[:1500]),\n", + " \"validation\": Dataset.from_list(records[1501:2000]),\n", + " }\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's prepare to train our model. For this, it is recommended to use GPU. You can check if it is available as shown below." + "Now, let's prepare to train our model. For this, it is recommended to use GPU. You can check if it is available as shown below.\n" ] }, { @@ -524,7 +548,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will define our model and arguments. In this case, we will use the `bert-base-cased`, available in the [Hugging Face Hub](https://huggingface.co/google-bert/bert-base-cased), but others can be applied." + "We will define our model and arguments. In this case, we will use the `bert-base-cased`, available in the [Hugging Face Hub](https://huggingface.co/google-bert/bert-base-cased), but others can be applied.\n" ] }, { @@ -532,7 +556,7 @@ "metadata": {}, "source": [ "!!! note\n", - " The training arguments are inherited from the Transformers library. You can check more information [here](https://huggingface.co/docs/transformers/en/main_classes/trainer#transformers.TrainingArguments)." + "The training arguments are inherited from the Transformers library. You can check more information [here](https://huggingface.co/docs/transformers/en/main_classes/trainer#transformers.TrainingArguments).\n" ] }, { @@ -557,7 +581,7 @@ " num_train_epochs=1,\n", " weight_decay=0.01,\n", " warmup_ratio=0.1,\n", - " fp16=False, # Set to True if available\n", + " fp16=False, # Set to True if available\n", " logging_first_step=True,\n", " logging_steps=50,\n", " evaluation_strategy=\"steps\",\n", @@ -579,7 +603,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's train it! This time, we use a high-quality human-annotated training set, so the results are expected to have improved." + "Let's train it! This time, we use a high-quality human-annotated training set, so the results are expected to have improved.\n" ] }, { @@ -604,7 +628,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You can save it locally or push it to the Hub. And then load it from there." + "You can save it locally or push it to the Hub. And then load it from there.\n" ] }, { @@ -626,7 +650,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "It's time to make the predictions! We will set a function that uses the `predict` method to get the suggested label. The model will infer the label based on the text. The function will return the spans in the corresponding structure for the Argilla dataset." + "It's time to make the predictions! We will set a function that uses the `predict` method to get the suggested label. The model will infer the label based on the text. The function will return the spans in the corresponding structure for the Argilla dataset.\n" ] }, { @@ -637,14 +661,21 @@ "source": [ "def predict_spanmarker(model, text):\n", " entities = model.predict(text)\n", - " return [{'start': ent['char_start_index'], 'end': ent['char_end_index'], 'label': ent['label']} for ent in entities]" + " return [\n", + " {\n", + " \"start\": ent[\"char_start_index\"],\n", + " \"end\": ent[\"char_end_index\"],\n", + " \"label\": ent[\"label\"],\n", + " }\n", + " for ent in entities\n", + " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As the training data was of better quality, we can expect a better model. So we can update the remaining non-annotated records with the new model's suggestions." + "As the training data was of better quality, we can expect a better model. So we can update the remaining non-annotated records with the new model's suggestions.\n" ] }, { @@ -661,14 +692,14 @@ " }\n", " for sample in data\n", "]\n", - "dataset.records.log(records=updated_data)\n" + "dataset.records.log(records=updated_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Conclusions" + "## Conclusions\n" ] }, { @@ -677,7 +708,7 @@ "source": [ "In this tutorial, we present an end-to-end example of a token classification task. This serves as the base, but it can be performed iteratively and seamlessly integrated into your workflow to ensure high-quality curation of your data and improved results.\n", "\n", - "We started by configuring the dataset, adding records, and adding suggestions based on the GLiNer predictions. After the annotation process, we trained a SpanMarker model with the annotated data and updated the remaining records with the new suggestions." + "We started by configuring the dataset, adding records, and adding suggestions based on the GLiNer predictions. After the annotation process, we trained a SpanMarker model with the annotated data and updated the remaining records with the new suggestions.\n" ] } ],