Article
· Jan 20 9m de lecture

Comparaison de similarité textuelle à l'aide d'IRIS, Python et de transformateurs de phrases

Avec l'avènement d'Embedded Python, une myriade de cas d'utilisation sont désormais possibles depuis IRIS, directement en utilisant les librairies Python pour des opérations plus complexes. L'une de ces opérations consiste à utiliser des outils de traitement du langage naturel tels que la comparaison de similarités textuelles.

 

Configuration de Python intégré pour utiliser la librairie de transformateurs de phrases

Note: Pour cet article, j'utiliserai un système Linux sur lequel IRIS est installé. Certains processus d'utilisation d'Embedded Python avec Windows, tels que l'installation de librairies, peuvent être légèrement différents de Linux à Windows. Veuillez donc vous référer à la documentation IRIS pour connaître les procédures appropriées lors de l'utilisation de Windows. Cependant, une fois les bibliothèques installées, le code et la configuration d'IRIS devraient être les mêmes entre Linux et Windows. Cet article utilisera Redhat Linux 9 et IRIS 2023.1.

La librairie Python utilisée pour cet article sera Sentence Transformers (https://github.com/UKPLab/sentence-transformers).Pour pouvoir utiliser n'importe quelle librairie Python depuis IRIS, elle doit être installée de telle manière qu'IRIS puisse en faire usage. Sous Linux, cela peut être effectué à l'aide de la commande d'installation de la bibliothèque Python standard, mais avec une cible du répertoire Python de l'instance IRIS. Notez que pour les transformateurs de phrases, votre système doit également déjà disposer d'un compilateur Rust installé comme pré-requis pour l'une des dépendances de librairie installées avec les transformateurs de phrases. (https://www.rust-lang.org/tools/install).

sudo python -m pip install sentence-transformers --target [IRIS install directory]/sys/mgr/python

Une fois installé, Sentence Transformers peut être utilisé directement dans le shell IRIS Python.

 

Comparaison de texte à l'aide de la librairie de transformateurs de phrases

Afin d'effectuer une comparaison de deux textes distincts, nous devons d'abord générer les intégrations pour chaque bloc de texte. Un intégration est une représentation numérique du texte basée sur la construction linguistique du texte en utilisant le modèle de langage donné. Pour Sentence Transformers, nous pouvons utiliser plusieurs modèles de langage pré-entraînés différents (https://www.sbert.net/docs/pretrained_models.html). Dans ce cas, nous utiliserons le modèle « all-MiniLM-L6-v2 » qui est répertorié comme un « Modèle polyvalent adapté à de nombreux cas d'utilisation. Formé sur un ensemble de données vaste et diversifié de plus d'un milliard de paires d'entraînement." et devrait suffire pour cet article. Il existe cependant de nombreux autres modèles pré-entraînés parmi lesquels choisir, y compris certains modèles multilingues capables de détecter et d'utiliser de nombreuses langues différentes. Choisissez donc le modèle qui fonctionne le mieux pour votre cas d'utilisation spécifique.

Le code utilisé ci-dessus permet de générer les intégrations, qui convertiront la chaîne de texte donnée en un vecteur Python qui pourra ensuite être utilisé dans la fonction de comparaison.

Une fois les intégrations pour chaque bloc de texte créées, celles-ci peuvent ensuite être utilisées dans la fonction cosine_similarity pour arriver à une valeur de 0 à 1 représentant la similarité relative entre les textes donnés.

L'exemple ci-dessus montre que les deux chaînes de texte données sont similaires à environ 64 % en fonction de leur structure linguistique.

 

Création et stockage d'incorporations de texte à l'aide d'IRIS Embedded Python et de Sentence Transformers

En utilisant ce même processus, nous pouvons incorporer ce processus dans une classe IRIS et stocker les intégrations pour une utilisation future, par exemple en comparant un texte existant à un texte nouvellement saisi.

Créez une nouvelle classe dans IRIS qui étend %Persistent afin que nous puissions stocker les données dans la table suivante.

Créez deux propriétés de chaîne sur la classe, une pour le texte lui-même et une autre pour stocker les données d'intégration. Il est important d'inclure MAXLEN = 100000 dans le champ Embedding, sinon les données incorporées ne rentreront pas dans le champ et la comparaison échouera. Le MAXLEN sur le champ Texte dépendra de votre cas d'utilisation quant à la quantité de texte pouvant être stockée.

Class TextSimilarity.TextSimilarity Extends %Persistent
{
    Property Text As %String(MAXLEN = 100000);
    Property Embedding As %String(MAXLEN = 100000);
}

Note: Dans la version actuelle d'IRIS au moment de la rédaction de cet article (IRIS 2023), les données incorporées doivent être converties de l'objet tensoriel, une forme de vecteur Python, en une chaîne de stockage, car aucun type de données natif actuel n'est compatible avec le vecteur Python. Dans un avenir proche (prévu pour IRIS 2024.1 au moment de la rédaction de cet article), un type de données vectoriel natif est ajouté à IRIS qui permettra le stockage des vecteurs Python directement dans IRIS sans avoir besoin de conversion de chaîne.

Créez un nouveau ClassMethod qui prend une chaîne, crée l'incorporation, convertit l'incorporation en valeur de chaîne, puis renvoie la chaîne résultante. Utilisez le décorateur de méthode comme indiqué ci-dessous pour indiquer à IRIS que cette méthode sera une méthode Python native plutôt qu'ObjectScript.

ClassMethod GetTextEmbedding(text As %String) As %String [ Language = python ]
{
    from sentence_transformers import SentenceTransformer
    from pickle import dumps

    model = SentenceTransformer('all-MiniLM-L6-v2')
    embedding = model.encode(text, convert_to_tensor=True)
    embeddingList = embedding.tolist()
    pickledObj = dumps(embeddingList, 0)
    pickledStr = pickledObj.decode('utf-8') 
    return pickledStr
}

Dans la méthode ci-dessus, la première partie du code est similaire à ce qui a été fait dans le shell Python pour créer l'intégration. Une fois l'objet tensoriel créé à partir de model.encode, la conversion en chaîne se déroule comme suit :

  1. Convertir l'objet tenseur obtenu à partir de model.encode en un objet liste Python
  2. Convertissez l'objet de liste en un objet pickled Python à l'aide de la fonction dumps de la bibliothèque pickle intégrée
    1. Note : Assurez-vous d'inclure le paramètre 0 comme indiqué ci-dessus, car il oblige la fonction dumps à utiliser une ancienne méthode d'encodage qui fonctionne correctement avec l'encodage UTF-8.
  3. Décoder l'objet pickled en une chaîne codée en UTF-8

Désormais, le tensor a été entièrement converti en chaîne et peut être renvoyé à la fonction appelante.

Ensuite, créez une ClassMethod qui utilisera ObjectScript pour importer le texte, configurez le nouvel objet pour le stockage de la base de données, appelez la méthode GetTextEmbedding, puis stockez le texte et l'incorporez dans la base de données.

ClassMethod NewText(text As %String)
{
    set newTextObj = ..%New()
    set newTextObj.Text = text
    set newTextObj.Embedding = ..GetTextEmbedding(text)
    set status = newTextObj.%Save()
    write status
}

Nous avons maintenant un moyen d'envoyer une chaîne de texte dans la classe, de lui faire créer les intégrations et de stocker les deux dans la base de données.

 

Comparaison de texte à l'aide de Python intégré et des transformateurs de phrases IRIS

Passons au processus de comparaison, je vais à nouveau créer une méthode de classe Python pour gérer la conversion réelle de la chaîne d'intégration en tenseur et exécuter la comparaison avec une méthode de classe ObjectScript pour gérer la récupération des données de la base de données et l'appel de la méthode Python de similarité.

Tout d’abord, la méthode de similarité Python.

ClassMethod GetSimilarity(embed1 As %String, embed2 As %String) As %String [ Language = python ]
{
    from sentence_transformers import util
    from pickle import loads
    from torch import tensor

    embed1Bytes = bytes(embed1, 'utf-8')
    embed1List = loads(embed1Bytes)

    embed2Bytes = bytes(embed2, 'utf-8')
    embed2List = loads(embed2Bytes)

    tensor1 = tensor(embed1List)
    tensor2 = tensor(embed2List)

    cosineScores = util.cos_sim(tensor1, tensor2)

    return str(abs(cosineScores.tolist()[0][0]))
}

Dans la méthode ci-dessus, chaque intégration est convertie en chaîne d'octets à l'aide de la fonction bytes (en utilisant à nouveau UTF-8 puisque c'est ce qui a été utilisé pour les décoder dans le processus de conversion d'encodage), puis la fonction loads de la bibliothèque pickle prendra la chaîne d'octets et la convertire en un objet de liste Python. Enfin, la fonction tenseur de la bibliothèque torch terminera le processus de conversion en convertissant la liste Python en un objet tenseur approprié, prêt à être utilisé dans la comparaison de similarité cosinus.

Ensuite, la fonction cos_sim de la bibliothèque util de Sentence Transformers est appelée pour comparer les tenseurs et renvoyer le score de similarité. Ce retour, cependant, sera également formaté comme un tenseur, il devra donc être converti en liste et déréférencé au premier et unique élément de la liste]. Ensuite, en raison de la nature géométrique de la similarité cosinus, prenez la valeur absolue et enfin convertissez la valeur décimale résultante en chaîne à renvoyer à la fonction appelante.

À partir de là, nous pouvons ensuite créer la fonction ObjectScript pour gérer les opérations de base de données afin de récupérer les objets texte spécifiques et appeler la fonction de similarité.

ClassMethod Compare(id1 As %Integer, id2 As %Integer)
{
    set obj1 = ..%OpenId(id1)
    set text1 = obj1.Text
    set embedding1 = obj1.Embedding

    set obj2 = ..%OpenId(id2)
    set text2 = obj2.Text
    set embedding2 = obj2.Embedding

    set sim = ..GetSimilarity(embedding1, embedding2)

    write !,"Text 1:",!,text1,!
    write !,"Text 2:",!,text2,!
    write !,"Similarity: ",sim
}

Cette méthode ouvrira chaque objet en utilisant l'ID de ligne fourni, obtiendra le texte et les données d'intégration, transmettra les intégrations à la méthode GetSimilarity pour obtenir le score de similarité, puis écrira chaque texte et la similarité.

 

Test

Pour tester, j'utiliserai le synopsis du film Hot Fuzz provenant de deux sources différentes (IMDB et Wikipedia) pour voir à quel point elles sont similaires. Si tout fonctionne comme prévu, la similarité devrait être suffisamment élevée.

Tout d'abord, je vais stocker chaque objet texte dans la base de données à l'aide de la fonction NewText :

Une fois stockées, les données devraient ressembler à ceci (ignorez les identifiants commençant à 3, j'ai eu un bug dans mon code initial et j'ai dû refaire la sauvegarde).

Maintenant que nos données texte ont été stockées avec l'intégration associée, nous pouvons appeler la fonction Compare avec les ID 3 et 4 pour obtenir le score de comparaison final :

En conséquence, nous constatons que la similitude entre les synopsis IMDB et Wikipédia pour Hot Fuzz est similaire à environ 75 %, ce que je considérerais comme exact étant donné la quantité de texte supplémentaire contenue dans le texte IMDB.

 

Conclusion

Ce n'est que l'une des nombreuses fonctions disponibles dans Sentence Transformers, et il existe de nombreuses autres boîtes à outils PNL qui peuvent exécuter diverses fonctions liées à l'analyse du texte et du langage. Le but de cet article est de montrer que si vous pouvez le faire en Python, vous pouvez également le faire dans IRIS en utilisant Embedded Python.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer