From 8e3c0c1cae18323a8ae9461b5f52b9930836dab7 Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:00:32 +0100 Subject: [PATCH 01/10] initial commit of new demo notebook --- ...ultilingual_datasets_and_vectorisers.ipynb | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 DEMO/multilingual_datasets_and_vectorisers.ipynb diff --git a/DEMO/multilingual_datasets_and_vectorisers.ipynb b/DEMO/multilingual_datasets_and_vectorisers.ipynb new file mode 100644 index 0000000..13cd5bb --- /dev/null +++ b/DEMO/multilingual_datasets_and_vectorisers.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Working with datasets in multiple languages with ClassifAI and Multilingual Vectoriser Models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ClassifAI is a tool to help in the creation and serving of searchable vector databases, for text classification tasks. \n", + "\n", + "It has three core components:\n", + "\n", + "1. Vectorisers - Models for converting text to vectors\n", + "2. Indexers - Classes for building VectorStores from text datasets, which you can search\n", + "3. Servers - Allow you to deploy VectorStores with a Rest-API interface\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook showcases how to work with text data that is in more than one language. If you have a dataset consisting of english and, for example, french text it is possible to build a Vector Database that can be 'searched' in either language and have results from both languages returned as results.\n", + "\n", + "This is possible through the use of Vectoriser embedding models that are capable of convert text from multiple languages into embeddings that capture the common sentiment, despite the different language surface forms. These are said to be 'multi-lingual encoder models' and they are directly compatible with ClassifAI package.\n", + "\n", + "In this notebook, we use a mulit-lingual LLM encoder from HuggingFace, with a dataset with multiple languages present, to demonstrate how this is possible and showcase some of the possible usages." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## In this Notebook...\n", + "\n", + "We will show:\n", + "\n", + "* The core workings of the Vectotiser Class and its responsibilities, in a multi-lingual setting.\n", + "* How to use multi-lingual encoding models from HuggingFace within ClassifAI using ClassifAI's HuggingFaceVectoriser class\n", + "* Examples of building a VectorStore knowledgebase containing english and non-english text, and examples of searching the knowledgebase with english and non-english queries." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Vectorisers and Multilingual Encoding" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![English_Vectoriser_Image](files/vectoriser.png)\n", + "\n", + "\n", + "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a transform() method that will:\n", + "\n", + "1. accept a string or list of N strings as an argument\n", + "2. return a numpy array of dimension [N,Y] where N matches the number of input strings, and Y is the embedding dimension)\n", + "\n", + "By enforcing this, the Indexers and Servers modules can reliably work with any Vectoriser object to perform the various search/classification functions required by ClassifAI.\n", + "\n", + "All a developer has to consider when building their own Vectoriser is the logic of this transform() method." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Multilingual_Vectoriser_Image](files/vectoriser.png)\n", + "\n", + "\n", + "There are categories of Vectoriser/embedding models that are trained to generate embeddings for multiple languages and can represent the semantic meaning of different languages within the same embedding space. Despite there different forms of language the embeddings for two semantically similar pieces of text should have similar embedding values.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the context of ClassifAI, using Multilingual Vectoriser models means we can build a VectorStore from a CSV file containing text from multiple languages, such that similarities in the text of different languages reflects their encoded position in the VectorStore:\n", + "\n", + "![Multilingual_Vectoriser_Image](files/vectoriser.png)" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From f91df4406f9f684df7805d966b01d99b180752cd Mon Sep 17 00:00:00 2001 From: Erlend Frayling <194791647+frayle-ons@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:04:57 +0100 Subject: [PATCH 02/10] Added new png files for multilingual notebook --- DEMO/files/vectoriser_multilingual.png | Bin 0 -> 22146 bytes DEMO/files/vectorstore_2d_vis_multilingual.png | Bin 0 -> 45288 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 DEMO/files/vectoriser_multilingual.png create mode 100644 DEMO/files/vectorstore_2d_vis_multilingual.png diff --git a/DEMO/files/vectoriser_multilingual.png b/DEMO/files/vectoriser_multilingual.png new file mode 100644 index 0000000000000000000000000000000000000000..5b868ec2937e24dab404d02fba7112e9b38de0bc GIT binary patch literal 22146 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%0DxW>VE5D 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 R;Kr_V{o~BB>Z88s{{osOBRBv6 literal 0 HcmV?d00001 diff --git a/DEMO/files/vectorstore_2d_vis_multilingual.png b/DEMO/files/vectorstore_2d_vis_multilingual.png new file mode 100644 index 0000000000000000000000000000000000000000..587c88de1fb112fe39dd956e29979474a2bb4a41 GIT binary patch literal 45288 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 Date: Thu, 3 Sep 2026 17:26:21 +0100 Subject: [PATCH 03/10] full notebook and new ai generated demo dataset in french and english --- DEMO/data/fake_multilingual_soc_dataset.csv | 84 +++++ ...ultilingual_datasets_and_vectorisers.ipynb | 324 +++++++++++++++++- 2 files changed, 395 insertions(+), 13 deletions(-) create mode 100644 DEMO/data/fake_multilingual_soc_dataset.csv diff --git a/DEMO/data/fake_multilingual_soc_dataset.csv b/DEMO/data/fake_multilingual_soc_dataset.csv new file mode 100644 index 0000000..09b84fd --- /dev/null +++ b/DEMO/data/fake_multilingual_soc_dataset.csv @@ -0,0 +1,84 @@ +label,text +101,"Fruit farmer: Grows and harvests fruits such as apples, oranges, and berries." +101,"Maraîcher : cultive et récolte des légumes comme les carottes, les pommes de terre et la laitue." +101,"Cultivateur de vergers : s'occupe de la récolte des pommes et des poires en saison." +102,"Dairy farmer: Manages cows for milk production and processes dairy products." +102,"Éleveur de moutons : élève des moutons pour la laine, la viande et d'autres produits." +103,"Construction laborer: Performs physical tasks on construction sites, such as digging and carrying materials." +103,"Maçon : pose des briques, des blocs de béton et des pierres pour construire des murs et des structures." +103,"Ouvrier du bâtiment : travaille sur différents chantiers, transport de matériaux et travaux de terrassement." +104,"Carpenter: Constructs, installs, and repairs wooden frameworks and structures." +104,"Charpentier : construit, installe et répare des structures et charpentes en bois." +105,"Electrician: Installs, maintains, and repairs electrical systems in buildings and equipment." +106,"Plumber: Installs and repairs water, gas, and drainage systems in homes and businesses." +106,"Plombier : installe et répare des systèmes d'eau, de gaz et de drainage dans les foyers et les entreprises." +107,"Software developer: Designs, writes, and tests computer programs and applications." +107,"Développeur web : conçoit et maintient des sites web et des applications web." +107,"Ingénieur logiciel : participe à la conception et aux tests de nouveaux systèmes informatiques." +108,"Data analyst: Analyzes data to provide insights and support decision-making." +109,"Accountant: Prepares and examines financial records, ensuring accuracy and compliance with regulations." +109,"Auditeur : examine les états financiers et les registres pour assurer la conformité et détecter les fraudes." +110,"Teacher: Educates students in schools, colleges, or universities." +110,"Enseignant : éduque les élèves dans les écoles, les collèges ou les universités." +111,"Nurse: Provides medical care and support to patients in hospitals, clinics, or homes." +111,"Ambulancier : intervient dans des situations médicales d'urgence et fournit des soins préhospitaliers." +111,"Aide-soignant : accompagne les patients au quotidien dans les établissements de santé." +112,"Chef: Prepares and cooks meals in restaurants, hotels, or other food establishments." +112,"Serveur : sert de la nourriture et des boissons aux clients dans les restaurants et les cafés." +113,"Graphic designer: Creates visual concepts for advertisements, websites, and branding." +113,"Illustrateur : produit des œuvres pour des livres, des magazines et d'autres médias." +114,"Mechanic: Repairs and maintains vehicles and machinery." +114,"Technicien automobile : diagnostique et répare les problèmes des voitures et des camions." +115,"Photographer: Captures images for events, advertising, or artistic purposes." +115,"Vidéaste : enregistre et monte du contenu vidéo pour divers projets." +116,"Barista: Prepares and serves coffee and other beverages in cafes and coffee shops." +116,"Gérant de café : supervise les opérations quotidiennes d'un café, y compris la gestion du personnel et des stocks." +117,"Fitness trainer: Designs and leads exercise programs for individuals or groups." +117,"Professeur de yoga : enseigne des cours de yoga pour améliorer la flexibilité, la force et la relaxation." +118,"Librarian: Manages library resources and assists patrons with research." +118,"Archiviste : préserve et organise des documents et registres historiques." +119,"Journalist: Researches and writes news articles for print or online media." +119,"Rédacteur : révise et corrige le contenu écrit en vue de sa publication." +119,"Rédacteur technique : produit des manuels et de la documentation pour des produits techniques." +120,"Scientist: Conducts research and experiments to advance knowledge in a specific field." +120,"Technicien de laboratoire : assiste dans les expériences scientifiques et entretient le matériel de laboratoire." +121,"Police officer: Enforces laws and maintains public safety." +121,"Détective : enquête sur des crimes et rassemble des preuves pour des affaires judiciaires." +122,"Firefighter: Responds to emergencies and extinguishes fires." +122,"Inspecteur incendie : inspecte les bâtiments pour assurer la conformité aux réglementations de sécurité incendie." +123,"Pilot: Operates aircraft to transport passengers or cargo." +123,"Hôtesse de l'air : assure la sécurité et le confort des passagers pendant les vols." +124,"Actor: Performs roles in theater, film, or television productions." +124,"Réalisateur : supervise les aspects créatifs d'un film, d'une pièce de théâtre ou d'une émission de télévision." +125,"Musician: Performs music as a soloist or part of a band or orchestra." +125,"Compositeur : crée de la musique originale pour des spectacles ou des enregistrements." +126,"Athlete: Competes in sports at a professional or amateur level." +126,"Entraîneur sportif : forme et encadre des athlètes pour améliorer leurs performances." +127,"Fashion designer: Creates clothing and accessories for production or custom orders." +127,"Tailleur : ajuste et répare des vêtements pour les clients." +128,"Real estate agent: Assists clients in buying, selling, or renting properties." +128,"Gestionnaire immobilier : supervise l'entretien et la location de biens immobiliers." +129,"Event planner: Organizes and coordinates events such as weddings and conferences." +129,"Coordinateur de mariage : gère tous les aspects de la planification et de l'exécution d'un mariage." +130,"Veterinarian: Provides medical care for animals." +130,"Technicien vétérinaire : assiste les vétérinaires dans le traitement et les soins aux animaux." +131,"Social worker: Supports individuals and families in overcoming challenges." +131,"Conseiller conjugal : accompagne les couples et les familles en difficulté." +132,"Entrepreneur: Starts and manages their own business ventures." +132,"Consultant en entreprise : conseille les entreprises sur des stratégies pour améliorer leurs opérations." +133,"Logistics manager: Oversees the transportation and storage of goods." +133,"Analyste de la chaîne d'approvisionnement : optimise le flux des biens et des matériaux dans une chaîne d'approvisionnement." +134,"Software tester: Tests software applications for bugs and usability issues." +134,"Ingénieur DevOps : gère les processus de développement logiciel et d'exploitation informatique." +135,"Research assistant: Supports researchers in conducting studies and experiments." +135,"Statisticien : analyse des données pour identifier des tendances et faire des prévisions." +136,"HR manager: Oversees recruitment, training, and employee relations." +136,"Chargé de recrutement : trouve et embauche des candidats pour des postes vacants." +137,"Chef de cuisine: Leads the kitchen team in a restaurant." +137,"Second de cuisine : assiste le chef principal dans la gestion des opérations en cuisine." +138,"Bartender: Mixes and serves drinks to customers." +138,"Sommelier : recommande et sert des vins dans les restaurants." +139,"Tour guide: Leads groups on tours and provides information about destinations." +139,"Agent de voyages : planifie et réserve des arrangements de voyage pour les clients." +140,"Game designer: Creates concepts and mechanics for video games." +140,"Concepteur de niveaux : conçoit des niveaux et des environnements pour des jeux vidéo." \ No newline at end of file diff --git a/DEMO/multilingual_datasets_and_vectorisers.ipynb b/DEMO/multilingual_datasets_and_vectorisers.ipynb index 13cd5bb..70be5ac 100644 --- a/DEMO/multilingual_datasets_and_vectorisers.ipynb +++ b/DEMO/multilingual_datasets_and_vectorisers.ipynb @@ -24,11 +24,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This notebook showcases how to work with text data that is in more than one language. If you have a dataset consisting of english and, for example, french text it is possible to build a Vector Database that can be 'searched' in either language and have results from both languages returned as results.\n", + "This notebook shows how to work with text data written in more than one language.\n", "\n", - "This is possible through the use of Vectoriser embedding models that are capable of convert text from multiple languages into embeddings that capture the common sentiment, despite the different language surface forms. These are said to be 'multi-lingual encoder models' and they are directly compatible with ClassifAI package.\n", + "If your dataset contains, for example, both English and French text, you can build a ClassifAI `VectorStore` that can be searched in **either** language — and get relevant results back in **both** languages, regardless of which language you searched in.\n", "\n", - "In this notebook, we use a mulit-lingual LLM encoder from HuggingFace, with a dataset with multiple languages present, to demonstrate how this is possible and showcase some of the possible usages." + "This works because of **multilingual encoder models**. These are Vectoriser models that convert text from many different languages into a shared embedding space, so that text with the same meaning ends up with similar embeddings, no matter what language it's written in.\n", + "\n", + "This notebook uses a multilingual model from HuggingFace, along with a dataset containing multiple languages, to demonstrate this in practice." ] }, { @@ -39,9 +41,9 @@ "\n", "We will show:\n", "\n", - "* The core workings of the Vectotiser Class and its responsibilities, in a multi-lingual setting.\n", - "* How to use multi-lingual encoding models from HuggingFace within ClassifAI using ClassifAI's HuggingFaceVectoriser class\n", - "* Examples of building a VectorStore knowledgebase containing english and non-english text, and examples of searching the knowledgebase with english and non-english queries." + "* The core workings of the Vectotiser Class and its responsibilities, in a multilingual setting.\n", + "* How to use multilingual encoding models from HuggingFace within ClassifAI using ClassifAI's `HuggingFaceVectoriser` class\n", + "* Examples of building a `VectorStore` knowledgebase containing english and non-english text, and examples of searching the knowledgebase with english and non-english queries." ] }, { @@ -58,24 +60,25 @@ "![English_Vectoriser_Image](files/vectoriser.png)\n", "\n", "\n", - "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a transform() method that will:\n", + "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a `transform()` method that will:\n", "\n", "1. accept a string or list of N strings as an argument\n", "2. return a numpy array of dimension [N,Y] where N matches the number of input strings, and Y is the embedding dimension)\n", "\n", "By enforcing this, the Indexers and Servers modules can reliably work with any Vectoriser object to perform the various search/classification functions required by ClassifAI.\n", "\n", - "All a developer has to consider when building their own Vectoriser is the logic of this transform() method." + "All a developer has to consider when building their own Vectoriser is the logic of this `transform()` method." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "![Multilingual_Vectoriser_Image](files/vectoriser.png)\n", + "![Multilingual_Vectoriser_Image](files/vectoriser_multilingual.png)\n", "\n", + "Some Vectoriser (embedding) models are trained to understand many languages at once. These are called **multilingual encoder models**.\n", "\n", - "There are categories of Vectoriser/embedding models that are trained to generate embeddings for multiple languages and can represent the semantic meaning of different languages within the same embedding space. Despite there different forms of language the embeddings for two semantically similar pieces of text should have similar embedding values.\n", + "As shown above, sentences in different languages go into the model, and come out as embeddings in a **shared embedding space**. If two sentences mean the same thing — even if they're written in different languages — their embeddings will be very similar.\n", "\n" ] }, @@ -83,15 +86,310 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the context of ClassifAI, using Multilingual Vectoriser models means we can build a VectorStore from a CSV file containing text from multiple languages, such that similarities in the text of different languages reflects their encoded position in the VectorStore:\n", + "In the context of ClassifAI, this means we can build a `VectorStore` from a CSV file containing text in multiple languages, and the resulting embeddings will still reflect the *meaning* of the text — not just its language.\n", + "\n", + "In the diagram below, each dot represents a piece of text, coloured by language (black = English, blue = French, green = Italian). Dots that are **close together** represent sentences that mean similar things, even though they're written in different languages:\n", "\n", - "![Multilingual_Vectoriser_Image](files/vectoriser.png)" + "![Multilingual_Vectoriser_Image](files/vectorstore_2d_vis_multilingual.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `VectorStore` can be searched as normal, using the same functionality as any other ClassifAI use case — but because its using a multilingual Vectoriser, the search query can be written in **any language** the model supports, and it will still return relevant results regardless of the language of the original text." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example Implementation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "### Vectoriser" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This implememtatopm requires an appropriate embedding model that works in a multilingual fashion. For this demo we've chosen to use `Granite-Embedding-97M-Multilingual-R2` - an embedding model provided by IBM and available on HuggingFace that supports over 200 languages. For more information check out the model (and find other multilingual embedding models) on HuggingFace at: https://huggingface.co/ibm-granite/granite-embedding-97m-multilingual-r2\n", + "\n", + "\n", + "With ClassifAI, HuggingFace embedding models can be loaded with the Vectorisers module's `HuggingFaceVectoriser` class, the exact same way a monolingual embedding model would be loaded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classifai.vectorisers import HuggingFaceVectoriser\n", + "\n", + "multilingual_vectoriser = HuggingFaceVectoriser(model_name=\"ibm-granite/granite-embedding-97m-multilingual-r2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The vectoriser's `transform()` method can be called that will convert text to embedding representation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "embedding_from_english = multilingual_vectoriser.transform(\"ambulance driver\")\n", + "\n", + "embedding_from_french = multilingual_vectoriser.transform(\"conducteur d'ambulance\")\n", + "\n", + "embedding_from_italian = multilingual_vectoriser.transform(\"autista di ambulanza\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(embedding_from_english.shape)\n", + "print(embedding_from_french.shape)\n", + "print(embedding_from_italian)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example notebook, Generative AI was used to make an example dataset that contains fake SOC data, with text written in English and French. This dataset can be used, with the Granite-embedding model to build a ClassifAI VectorStore. Then this notebook will try out searching the `VectorStore` in a variety of languages (inlcuding languages other than English and French).\n", + "\n", + "The below cell loads the CSV file into a Pandas dataframe, and displays the top 5 entries to showcase the content. It contains profession names with a short description of the work with a corresponding 'fake' SOC label. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "multilingual_dataset = pd.read_csv(\"./data/fake_multilingual_soc_dataset.csv\")\n", + "multilingual_dataset.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building the VectorStore\n", + "\n", + "The process for creating a `VectorStore` is identical to the case of making a `VectorStore` for a CSV file of text all in the same language." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classifai.indexers import VectorStore\n", + "\n", + "# this is standard code to construct a VectorStore from a CSV file of data and using an instantiated Vectoriser model.\n", + "demo_vectorstore = VectorStore(\n", + " file_name=\"./data/fake_multilingual_soc_dataset.csv\",\n", + " data_type=\"csv\",\n", + " vectoriser=multilingual_vectoriser,\n", + " skip_save=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Searching with Enlgish queries\n", + "\n", + "With the `VectorStore` object instantiated, now call the `search()` method - it can be seen that the below search query passed in English returns relevant results in multiple languages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from classifai.indexers.dataclasses import VectorStoreSearchInput\n", + "\n", + "# creating a VectorStoreSearchInput object to pass to the search method\n", + "english_search_input = VectorStoreSearchInput({\"id\": [1], \"query\": [\"medical doctor\"]})\n", + "\n", + "# calling the search method and displaying the results\n", + "demo_vectorstore.search(english_search_input, n_results=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The resulting output of the cell above shows that while there isn't a medical doctor profession listed in our dataset, the top few results are all medical related results from both French and English, despite the original query being only in English. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Searching with non-English Queries\n", + "\n", + "In the cell below, an example query is written in French.\n", + "\n", + "The english translation of the below query is \"A person who repairs cars and trucks\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# creating a VectorStoreSearchInput object to pass to the search method, this time with french\n", + "french_search_input = VectorStoreSearchInput(\n", + " {\"id\": [1], \"query\": [\"Une personne qui répare des voitures et des camions\"]}\n", + ")\n", + "\n", + "# calling the search method and displaying the results\n", + "demo_vectorstore.search(french_search_input, n_results=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Among the top results of the previous cell's output is the 'Mechanic\" entry from the dataset which shows that the VectorStore is accepting a french query and returning an english result. The other top results are in french but are also seemingly relevant." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The VectorStore can also be searched in other languages that are not English or French, beacause as mentioned earlier the IBM Granite embedding model supports many languages.\n", + "\n", + "Below the query \"Una persona che progetta e testa programmi informatici e applicazioni\" is passed to the VectorStore `search()` method - the English translation is: \"A person who designs and tests computer programs and applications\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# creating a VectorStoreSearchInput object to pass to the search method, this time with french\n", + "italian_search_input = VectorStoreSearchInput(\n", + " {\"id\": [1], \"query\": [\"Una persona che progetta e testa programmi informatici e applicazioni\"]}\n", + ")\n", + "\n", + "# calling the search method and displaying the results\n", + "demo_vectorstore.search(italian_search_input, n_results=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above output shows several highly ranked results that are relevant from both French and English sources including \"Ingénieur logiciel\" (Software Engineer), \"Software Developer\" (which is an english result), and \"Développeur web\" (the French for Web Developer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying in many languages" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, its also possible to pass multiple queries to the search method at one time where the queries are in different languages. In the next cell, the same query is passed in English, French and Italian.\n", + "\n", + "\n", + "The results for each query should be similar, as the queries are translations of one another." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# three queries that all mean the same thing in different languages\n", + "query_en = \"A craftsman who builds and repairs wooden furniture and structures\"\n", + "query_fr = \"Un artisan qui construit et répare des meubles et des structures en bois\"\n", + "query_it = \"Un artigiano che costruisce e ripara mobili e strutture in legno\"\n", + "\n", + "# creating a input object for the search method.\n", + "search_input_multiple = VectorStoreSearchInput({\"id\": [1, 2, 3], \"query\": [query_en, query_fr, query_it]})\n", + "\n", + "# searching, retrieving the top 3 candidates for each query.\n", + "demo_vectorstore.search(search_input_multiple, n_results=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While there is some difference in the top 3 results for each query, there is a large amount of overlap and generally the results are highly relevant throughout." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Thats it!\n", + "\n", + "* When choosing an embedding model from HuggingFace or from another service such as GCP, be sure to check which languages the embedding model supports. \n", + "* Generally, monoligual models perform stronger on tasks for a specific single language than a multilingual model would. \n", + "* Because these models are accessible through ClassifAI's `HuggingFaceVectoriser` class, the models are directly compatible with the other modules of the Package including the Servers module and Evaluation module," ] } ], "metadata": { + "kernelspec": { + "display_name": "classifai", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" } }, "nbformat": 4, From a4712b8cde2d002c78303fd00e0a04f9b6008103 Mon Sep 17 00:00:00 2001 From: Erlend Frayling <194791647+frayle-ons@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:35:47 +0100 Subject: [PATCH 04/10] updated multilingual vectoriser image --- DEMO/files/vectoriser_multilingual.png | Bin 22146 -> 20607 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/DEMO/files/vectoriser_multilingual.png b/DEMO/files/vectoriser_multilingual.png index 5b868ec2937e24dab404d02fba7112e9b38de0bc..c1f2d11047db617f4e9e08e87efacc4a7b7f8819 100644 GIT binary patch literal 20607 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%0DxW>VE5D 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 R;Kr_V{o~BB>Z88s{{osOBRBv6 From 85f2f816007112cdb334d4d6a10157c0fee4ad7c Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Thu, 3 Sep 2026 18:37:11 +0100 Subject: [PATCH 05/10] updated demo folder readme with new multilingual notebook details --- DEMO/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/DEMO/README.md b/DEMO/README.md index 5f6474a..8eef653 100644 --- a/DEMO/README.md +++ b/DEMO/README.md @@ -86,6 +86,20 @@ It covers: **Note:** The Evaluation module is currently in development and its API is subject to change in future releases. +### 6. Working with datasets in multiple languages with ClassifAI and Multilingual Vectoriser Models : `multilingual_datasets_and_vectorisers.ipynb` + +This notebook demonstrates how to work with datasets containing text in multiple languages using ClassifAI and multilingual Vectoriser models. + +It covers: + +* How multilingual Vectorisers can encode text from different languages into a shared embedding space, allowing semantically similar text to be represented similarly, regardless of language. + +* Building a `VectorStore` from a multilingual dataset and searching it using queries in different languages. + +* Visualising the multilingual embedding space to show how semantically similar text in different languages is positioned close together. + +* Performing searches in one language and retrieving relevant results in multiple languages, showcasing the multilingual capabilities of the VectorStore. + --- ## Installation of classifai From 4b84a1517a6ed19e156ad297b57035098cd9d783 Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:45:07 +0100 Subject: [PATCH 06/10] fixed file routing on new notebook --- DEMO/multilingual_datasets_and_vectorisers.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEMO/multilingual_datasets_and_vectorisers.ipynb b/DEMO/multilingual_datasets_and_vectorisers.ipynb index 70be5ac..f7f0f57 100644 --- a/DEMO/multilingual_datasets_and_vectorisers.ipynb +++ b/DEMO/multilingual_datasets_and_vectorisers.ipynb @@ -57,7 +57,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "![English_Vectoriser_Image](files/vectoriser.png)\n", + "![English_Vectoriser_Image](./files/vectoriser.png)\n", "\n", "\n", "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a `transform()` method that will:\n", @@ -74,7 +74,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "![Multilingual_Vectoriser_Image](files/vectoriser_multilingual.png)\n", + "![Multilingual_Vectoriser_Image](./files/vectoriser_multilingual.png)\n", "\n", "Some Vectoriser (embedding) models are trained to understand many languages at once. These are called **multilingual encoder models**.\n", "\n", @@ -90,7 +90,7 @@ "\n", "In the diagram below, each dot represents a piece of text, coloured by language (black = English, blue = French, green = Italian). Dots that are **close together** represent sentences that mean similar things, even though they're written in different languages:\n", "\n", - "![Multilingual_Vectoriser_Image](files/vectorstore_2d_vis_multilingual.png)" + "![Multilingual_Vectoriser_Image](./files/vectorstore_2d_vis_multilingual.png)" ] }, { From 68f7dc77ba19985f26442ba4be28046bf981f998 Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:51:23 +0100 Subject: [PATCH 07/10] fixed notebook file routing for proper rendering on github --- DEMO/custom_vectoriser.ipynb | 2 +- DEMO/general_workflow_demo.ipynb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEMO/custom_vectoriser.ipynb b/DEMO/custom_vectoriser.ipynb index 3b40eab..535c9ed 100644 --- a/DEMO/custom_vectoriser.ipynb +++ b/DEMO/custom_vectoriser.ipynb @@ -58,7 +58,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "![Server_Image](files/vectoriser.png)\n", + "![Server_Image](./files/vectoriser.png)\n", "\n", "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a transform() method that will:\n", "\n", diff --git a/DEMO/general_workflow_demo.ipynb b/DEMO/general_workflow_demo.ipynb index 135cb53..d0bf104 100644 --- a/DEMO/general_workflow_demo.ipynb +++ b/DEMO/general_workflow_demo.ipynb @@ -39,7 +39,7 @@ "source": [ "## Vectorising\n", "\n", - "![Vectoriser_image](files/vectoriser.png)\n", + "![Vectoriser_image](./files/vectoriser.png)\n", "\n", "#### We provide several vectoriser classes that you can use to convert text to embeddings/vectors;\n", "```python\n", @@ -114,7 +114,7 @@ "\n", "By default, the vector database is persisted to a local directory named after the input filename. You can use the `output_dir` argument to change the location of the persisted vector database when creating the VectorStore. If the directory already exists, it will exit with a warning - you can pass the `overwrite=True` argument to permit it to overwrite an existing directory. If you don't want the vector database to be persisted at all, you can pass the `skip_save=True` argument - note that this takes precedence over `output_dir` and `overwrite`.\n", "If you want to minimise verbose output, such as INFO-level logs and progress bars, you can set the `quiet_mode` parameter to `True`.\n", - "![VectorStore_image](files/VectorStore.png)\n" + "![VectorStore_image](./files/VectorStore.png)\n" ] }, { @@ -384,7 +384,7 @@ "\n", "#### *Now, how do I host it so others can use it?*\n", "\n", - "![Server_Image](files/servers.png)" + "![Server_Image](./files/servers.png)" ] }, { From 0e12817a607136f91ed6cfe8e41a6c7510e0c431 Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:54:07 +0100 Subject: [PATCH 08/10] correct formatting fix --- DEMO/evaluation_workflow_demo.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEMO/evaluation_workflow_demo.ipynb b/DEMO/evaluation_workflow_demo.ipynb index 89243e0..4b47c7d 100644 --- a/DEMO/evaluation_workflow_demo.ipynb +++ b/DEMO/evaluation_workflow_demo.ipynb @@ -29,7 +29,7 @@ "\n", "Currently the evaluation module only evaluates single label predictions meaning that, while ClassifAI is designed to return a ranked list of several semantically similar candidate entries to a provided query sample, only the top result will be considered when comparing the VectorStore result to a ground truth label provided by a user.\n", "\n", - "![top_1_eval_image](files/eval_top_1_diagram.png)\n", + "![top_1_eval_image](./files/eval_top_1_diagram.png)\n", "\n", "The Evaluation module is currently in development, and in the future its feature set may be extended to include a broader range of evaluation tasks such as multi-class multi-label classification, where potentially multiple labels for a ground truth sample can be compared and evaluated against multiple ranked candidate predictions of the VectorStore." ] @@ -353,7 +353,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "classifai", "language": "python", "name": "python3" }, @@ -367,7 +367,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.4" + "version": "3.13.7" } }, "nbformat": 4, From a3b81d9e7552e373459dd89979476962754e510e Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Tue, 22 Sep 2026 14:36:01 +0100 Subject: [PATCH 09/10] spelling errors tidyup --- DEMO/multilingual_datasets_and_vectorisers.ipynb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DEMO/multilingual_datasets_and_vectorisers.ipynb b/DEMO/multilingual_datasets_and_vectorisers.ipynb index f7f0f57..6c4172d 100644 --- a/DEMO/multilingual_datasets_and_vectorisers.ipynb +++ b/DEMO/multilingual_datasets_and_vectorisers.ipynb @@ -41,7 +41,7 @@ "\n", "We will show:\n", "\n", - "* The core workings of the Vectotiser Class and its responsibilities, in a multilingual setting.\n", + "* The core workings of the Vectoriser Class and its responsibilities, in a multilingual setting.\n", "* How to use multilingual encoding models from HuggingFace within ClassifAI using ClassifAI's `HuggingFaceVectoriser` class\n", "* Examples of building a `VectorStore` knowledgebase containing english and non-english text, and examples of searching the knowledgebase with english and non-english queries." ] @@ -122,7 +122,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This implememtatopm requires an appropriate embedding model that works in a multilingual fashion. For this demo we've chosen to use `Granite-Embedding-97M-Multilingual-R2` - an embedding model provided by IBM and available on HuggingFace that supports over 200 languages. For more information check out the model (and find other multilingual embedding models) on HuggingFace at: https://huggingface.co/ibm-granite/granite-embedding-97m-multilingual-r2\n", + "This implementation requires an appropriate embedding model that works in a multilingual fashion. For this demo we've chosen to use `Granite-Embedding-97M-Multilingual-R2` - an embedding model provided by IBM and available on HuggingFace that supports over 200 languages. For more information check out the model (and find other multilingual embedding models) on HuggingFace at: https://huggingface.co/ibm-granite/granite-embedding-97m-multilingual-r2\n", "\n", "\n", "With ClassifAI, HuggingFace embedding models can be loaded with the Vectorisers module's `HuggingFaceVectoriser` class, the exact same way a monolingual embedding model would be loaded." @@ -181,7 +181,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For this example notebook, Generative AI was used to make an example dataset that contains fake SOC data, with text written in English and French. This dataset can be used, with the Granite-embedding model to build a ClassifAI VectorStore. Then this notebook will try out searching the `VectorStore` in a variety of languages (inlcuding languages other than English and French).\n", + "For this example notebook, Generative AI was used to make an example dataset that contains fake SOC data, with text written in English and French. This dataset can be used, with the Granite-embedding model to build a ClassifAI VectorStore. Then this notebook will try out searching the `VectorStore` in a variety of languages (including languages other than English and French).\n", "\n", "The below cell loads the CSV file into a Pandas dataframe, and displays the top 5 entries to showcase the content. It contains profession names with a short description of the work with a corresponding 'fake' SOC label. \n" ] @@ -228,7 +228,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Searching with Enlgish queries\n", + "### Searching with English queries\n", "\n", "With the `VectorStore` object instantiated, now call the `search()` method - it can be seen that the below search query passed in English returns relevant results in multiple languages." ] @@ -292,7 +292,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The VectorStore can also be searched in other languages that are not English or French, beacause as mentioned earlier the IBM Granite embedding model supports many languages.\n", + "The VectorStore can also be searched in other languages that are not English or French, because as mentioned earlier the IBM Granite embedding model supports many languages.\n", "\n", "Below the query \"Una persona che progetta e testa programmi informatici e applicazioni\" is passed to the VectorStore `search()` method - the English translation is: \"A person who designs and tests computer programs and applications\"" ] @@ -303,7 +303,7 @@ "metadata": {}, "outputs": [], "source": [ - "# creating a VectorStoreSearchInput object to pass to the search method, this time with french\n", + "# creating a VectorStoreSearchInput object to pass to the search method, this time with italian\n", "italian_search_input = VectorStoreSearchInput(\n", " {\"id\": [1], \"query\": [\"Una persona che progetta e testa programmi informatici e applicazioni\"]}\n", ")\n", @@ -368,8 +368,8 @@ "## Thats it!\n", "\n", "* When choosing an embedding model from HuggingFace or from another service such as GCP, be sure to check which languages the embedding model supports. \n", - "* Generally, monoligual models perform stronger on tasks for a specific single language than a multilingual model would. \n", - "* Because these models are accessible through ClassifAI's `HuggingFaceVectoriser` class, the models are directly compatible with the other modules of the Package including the Servers module and Evaluation module," + "* Generally, monolingual models perform stronger on tasks for a specific single language than a multilingual model would. \n", + "* Because these models are accessible through ClassifAI's `HuggingFaceVectoriser` class, these models are directly compatible with the other modules of the Package including the Servers module and Evaluation module," ] } ], From de45498ae94036be0ff89e8adc5866c24930f8cc Mon Sep 17 00:00:00 2001 From: frayle-ons <194791647+frayle-ons@users.noreply.github.com> Date: Thu, 24 Sep 2026 12:07:03 +0100 Subject: [PATCH 10/10] formatting and grammar cleanup --- DEMO/multilingual_datasets_and_vectorisers.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DEMO/multilingual_datasets_and_vectorisers.ipynb b/DEMO/multilingual_datasets_and_vectorisers.ipynb index 6c4172d..358f987 100644 --- a/DEMO/multilingual_datasets_and_vectorisers.ipynb +++ b/DEMO/multilingual_datasets_and_vectorisers.ipynb @@ -43,7 +43,7 @@ "\n", "* The core workings of the Vectoriser Class and its responsibilities, in a multilingual setting.\n", "* How to use multilingual encoding models from HuggingFace within ClassifAI using ClassifAI's `HuggingFaceVectoriser` class\n", - "* Examples of building a `VectorStore` knowledgebase containing english and non-english text, and examples of searching the knowledgebase with english and non-english queries." + "* Examples of building a `VectorStore` knowledgebase containing English and non-English text, and examples of searching the knowledgebase with English and non-English queries." ] }, { @@ -62,8 +62,8 @@ "\n", "As seen above, a Vectorisers' sole responsibility is to convert text to a vector representation. Each Vectoriser class must implement a `transform()` method that will:\n", "\n", - "1. accept a string or list of N strings as an argument\n", - "2. return a numpy array of dimension [N,Y] where N matches the number of input strings, and Y is the embedding dimension)\n", + "1. accept a string or list of N strings as an argument,\n", + "2. return a numpy array of dimension [N,D] where N matches the number of input strings, and D is the embedding dimension.\n", "\n", "By enforcing this, the Indexers and Servers modules can reliably work with any Vectoriser object to perform the various search/classification functions required by ClassifAI.\n", "\n", @@ -263,7 +263,7 @@ "\n", "In the cell below, an example query is written in French.\n", "\n", - "The english translation of the below query is \"A person who repairs cars and trucks\"" + "The English translation of the below query is \"A person who repairs cars and trucks\"" ] }, { @@ -285,7 +285,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Among the top results of the previous cell's output is the 'Mechanic\" entry from the dataset which shows that the VectorStore is accepting a french query and returning an english result. The other top results are in french but are also seemingly relevant." + "Among the top results of the previous cell's output is the 'Mechanic\" entry from the dataset which shows that the VectorStore is accepting a French query and returning an English result. The other top results are in French but are also seemingly relevant." ] }, { @@ -316,7 +316,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The above output shows several highly ranked results that are relevant from both French and English sources including \"Ingénieur logiciel\" (Software Engineer), \"Software Developer\" (which is an english result), and \"Développeur web\" (the French for Web Developer)" + "The above output shows several highly ranked results that are relevant from both French and English sources including \"Ingénieur logiciel\" (Software Engineer), \"Software Developer\" (which is an English result), and \"Développeur web\" (the French for Web Developer)" ] }, {