Skip to content

Text classification

Getting started

Deploy the Argilla server

If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following this guide.

Set up the environment

To complete this tutorial, you need to install the Argilla SDK and a few third-party libraries via pip.

!pip install argilla
!pip install setfit==1.0.3 transformers==4.40.2

Let's make the required imports:

import argilla as rg

from datasets import load_dataset, Dataset
from setfit import SetFitModel, Trainer, get_templated_dataset, sample_dataset

You also need to connect to the Argilla server using the api_url and api_key.

# Replace api_url with your url if using Docker
# Replace api_key with your API key under "My Settings" in the UI
# Uncomment the last line and set your HF_TOKEN if your space is private
client = rg.Argilla(
    api_url="https://[your-owner-name]-[your_space_name].hf.space",
    api_key="[your-api-key]",
    # headers={"Authorization": f"Bearer {HF_TOKEN}"}
)

Vibe check the dataset

We will have a look at the dataset to understand its structure and the kind of data it contains. We do this by using the embedded Hugging Face Dataset Viewer.

Configure and create the Argilla dataset

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 label question, corresponding to the text and label columns.

Note

Check this how-to guide to know more about configuring and creating a dataset.

labels = ["positive", "negative"]

settings = rg.Settings(
    guidelines="Classify the reviews as positive or negative.",
    fields=[
        rg.TextField(
            name="review",
            title="Text from the review",
            use_markdown=False,
        ),
    ],
    questions=[
        rg.LabelQuestion(
            name="sentiment_label",
            title="In which category does this article fit?",
            labels=labels,
        )
    ],
)

Let's create the dataset with the name and the defined settings:

dataset = rg.Dataset(
    name="text_classification_dataset",
    settings=settings,
)
dataset.create()

Add records

Even if we have created the dataset, it still lacks the information to be annotated (you can check it in the UI). We will use the imdb dataset from the Hugging Face Hub. Specifically, we will use 100 samples from the train split.

hf_dataset = load_dataset("imdb", split="train[:100]")

We will easily add them to the dataset using log and the mapping, where we indicate that the column text is the data that should be added to the field review.

dataset.records.log(records=hf_dataset, mapping={"text": "review"})

Add initial model suggestions

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.

We will start by defining an example training set with the required labels: positive and negative. Using get_templated_dataset will create sentences from the default template: "This sentence is {label}."

zero_ds = get_templated_dataset(
    candidate_labels=labels,
    sample_size=8,
)

Now, we will prepare a function to train the SetFit model.

Note

For further customization, you can check the SetFit documentation.

def train_model(model_name, dataset):
    model = SetFitModel.from_pretrained(model_name)

    trainer = Trainer(
        model=model,
        train_dataset=dataset,
    )

    trainer.train()

    return model

Let's train the model. We will use TaylorAI/bge-micro-v2, available in the Hugging Face Hub.

model = train_model(model_name="TaylorAI/bge-micro-v2", dataset=zero_ds)

You can save it locally or push it to the Hub. And then, load it from there.

# Save and load locally
# model.save_pretrained("text_classification_model")
# model = SetFitModel.from_pretrained("text_classification_model")

# Push and load in HF
# model.push_to_hub("[username]/text_classification_model")
# model = SetFitModel.from_pretrained("[username]/text_classification_model")

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.

def predict(model, input, labels):
    model.labels = labels

    prediction = model.predict([input])

    return prediction[0]

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.

data = dataset.records.to_list(flatten=True)
updated_data = [
    {
        "sentiment_label": predict(model, sample["review"], labels),
        "id": sample["id"],
    }
    for sample in data
]
dataset.records.log(records=updated_data)

Voilà! We have added the suggestions to the dataset, and they will appear in the UI marked with a ✨.

Evaluate with Argilla

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.

Note

Check this how-to guide to know more about annotating in the UI.

Train your model

After the annotation, we will have a robust dataset to train the main model. In our case, we will fine-tune using SetFit. However, you can select the one that best fits your requirements. So, let's start by retrieving the annotated records.

Note

Check this how-to guide to know more about filtering and querying in Argilla. Also, you can check the Hugging Face docs on fine-tuning an text classification model.

dataset = client.datasets("text_classification_dataset")
status_filter = rg.Query(filter=rg.Filter(("response.status", "==", "submitted")))

submitted = dataset.records(status_filter).to_list(flatten=True)

As we have a single response per record, we can retrieve the selected label straightforwardly and create the training set with 8 samples per label. We selected 8 samples per label to have a balanced dataset for few-shot learning.

train_records = [
    {
        "text": r["review"],
        "label": r["sentiment_label.responses"][0],
    }
    for r in submitted
]
train_dataset = Dataset.from_list(train_records)
train_dataset = sample_dataset(train_dataset, label_column="label", num_samples=8)

We can train the model using our previous function, but this time with a high-quality human-annotated training set.

model = train_model(model_name="TaylorAI/bge-micro-v2", dataset=train_dataset)

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.

data = dataset.records.to_list(flatten=True)
updated_data = [
    {
        "sentiment_label": predict(model, sample["review"], labels),
        "id": sample["id"],
    }
    for sample in data
]
dataset.records.log(records=updated_data)

Conclusions

In this tutorial, we present an end-to-end example of a text 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.

We started by configuring the dataset, adding records, and training a zero-shot SetFit model, as an example, to add suggestions. After the annotation process, we trained a new model with the annotated data and updated the remaining records with the new suggestions.