Image par l'éditeur
Avec l'intérêt croissant pour le traitement du langage naturel, de plus en plus de praticiens se heurtent au mur non pas parce qu'ils ne peuvent pas créer ou affiner des LLM, mais parce que leurs données sont désordonnées !
Nous montrerons des procédures de codage simples mais très efficaces pour corriger les étiquettes bruitées dans les données textuelles. Nous traiterons 2 scénarios courants dans les données textuelles du monde réel :
- Avoir une catégorie qui contient des exemples mixtes de quelques autres catégories. J'aime appeler ce genre de catégorie une méta catégorie.
- Avoir 2 catégories ou plus qui doivent être fusionnées en 1 catégorie car les textes qui leur appartiennent font référence au même sujet.
Nous utiliserons le jeu de données ITSM (IT Service Management) créé pour ce tutoriel (licence CCO). Il est disponible sur Kaggle à partir du lien ci-dessous :
https://www.kaggle.com/datasets/nikolagreb/small-itsm-dataset
Il est temps de commencer par l'importation de toutes les bibliothèques nécessaires et l'examen des données de base. Préparez-vous, le code arrive !
import pandas as pd
import numpy as np
import string from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import ComplementNB
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn import metrics df = pd.read_excel("ITSM_data.xlsx")
df.info()
RangeIndex: 118 entries, 0 to 117
Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ID_request 118 non-null int64 1 Text 117 non-null object 2 Category 115 non-null object 3 Solution 115 non-null object 4 Date_request_recieved 118 non-null datetime64[ns] 5 Date_request_solved 118 non-null datetime64[ns] 6 ID_agent 118 non-null int64 dtypes: datetime64[ns](2), int64(2), object(3)
memory usage: 6.6+ KB
Chaque ligne représente une entrée dans la base de données ITSM. Nous essaierons de prédire la catégorie du ticket en fonction du texte du ticket écrit par un utilisateur. Examinons plus en profondeur les champs les plus importants pour les cas d'utilisation métier décrits.
for text, category in zip(df.Text.sample(3, random_state=2), df.Category.sample(3, random_state=2)): print("TEXT:") print(text) print("CATEGORY:") print(category) print("-"*100)
TEXT:
I just want to talk to an agent, there are too many problems on my pc to be explained in one ticket. Please call me when you see this, whoever you are. (talk to agent)
CATEGORY:
Asana
----------------------------------------------------------------------------------------------------
TEXT:
Asana funktionierte nicht mehr, nachdem ich meinen Laptop neu gestartet hatte. Bitte helfen Sie.
CATEGORY:
Help Needed
----------------------------------------------------------------------------------------------------
TEXT:
My mail stopped to work after I updated Windows.
CATEGORY:
Outlook
----------------------------------------------------------------------------------------------------
Si nous jetons un coup d'œil aux deux premiers tickets, bien qu'un ticket soit en allemand, nous pouvons voir que les problèmes décrits font référence au même logiciel ? — ? Asana, mais ils portent des étiquettes différentes. C'est le début de la distribution de nos catégories :
df.Category.value_counts(normalize=True, dropna=False).mul(100).round(1).astype(str) + "%"
Outlook 19.1%
Discord 13.9%
CRM 12.2%
Internet Browser 10.4%
Mail 9.6%
Keyboard 9.6%
Asana 8.7%
Mouse 8.7%
Help Needed 7.8%
Name: Category, dtype: object
L'aide nécessaire semble suspecte, comme la catégorie qui peut contenir des tickets de plusieurs autres catégories. De plus, les catégories Outlook et Mail semblent similaires, elles devraient peut-être être fusionnées en une seule catégorie. Avant de plonger plus profondément dans les catégories mentionnées, nous nous débarrasserons des valeurs manquantes dans les colonnes qui nous intéressent.
important_columns = ["Text", "Category"]
for cat in important_columns: df.drop(df[df[cat].isna()].index, inplace=True)
df.reset_index(inplace=True, drop=True)
Il n'existe pas de substitut valable à l'examen des données à l'œil nu. La fonction fantaisiste pour le faire dans les pandas est .sample(), nous allons donc faire exactement cela une fois de plus, maintenant pour la catégorie suspecte :
meta = df[df.Category == "Help Needed"] for text in meta.Text.sample(5, random_state=2): print(text) print("-"*100)
Discord emojis aren't available to me, I would like to have this option enabled like other team members have.
---------------------------------------------------------------------------
Bitte reparieren Sie mein Hubspot CRM. Seit gestern funktioniert es nicht mehr
---------------------------------------------------------------------------
My headphones aren't working. I would like to order new.
---------------------------------------------------------------------------
Bundled problems with Office since restart:
Messages not sent
Outlook does not connect, mails do not arrive
Error 0x8004deb0 appears when Connection attempt, see attachment
The company account is affected: AB123
Access via Office.com seems to be possible.
---------------------------------------------------------------------------
Asana funktionierte nicht mehr, nachdem ich meinen Laptop neu gestartet hatte. Bitte helfen Sie.
---------------------------------------------------------------------------
Évidemment, nous avons des billets parlant de Discord, Asana et CRM. Ainsi, le nom de la catégorie doit être changé de "Aide nécessaire" à des catégories existantes plus spécifiques. Pour la première étape du processus de réaffectation, nous allons créer la nouvelle colonne "Mots clés" qui indique si le ticket contient le mot de la liste des catégories dans la colonne "Texte".
words_categories = np.unique([word.strip().lower() for word in df.Category]) # list of categories def keywords(row): list_w = [] for word in row.translate(str.maketrans("", "", string.punctuation)).lower().split(): if word in words_categories: list_w.append(word) return list_w df["Keywords"] = df.Text.apply(keywords) # since our output is in the list, this function will give us better looking final output. def clean_row(row): row = str(row) row = row.replace("[", "") row = row.replace("]", "") row = row.replace("'", "") row = string.capwords(row) return row df["Keywords"] = df.Keywords.apply(clean_row)
Notez également que l'utilisation de "if word in str(words_categories)" au lieu de "if word in words_categories" intercepterait les mots des catégories contenant plus d'un mot (navigateur Internet dans notre cas), mais nécessiterait également plus de prétraitement des données. Pour garder les choses simples et aller droit au but, nous utiliserons le code des catégories composées d'un seul mot. Voici à quoi ressemble notre ensemble de données :
df.head(2)
sortie sous forme d'image :
Après avoir extrait la colonne des mots-clés, nous supposerons la qualité des billets. Notre hypothèse :
- Un ticket avec un seul mot-clé dans le champ Texte qui est le même que la catégorie à laquelle appartient le ticket serait facile à classer.
- Un ticket avec plusieurs mots-clés dans le champ Texte, où au moins un des mots-clés est le même que la catégorie à laquelle appartient le ticket serait facile à classer dans la majorité des cas.
- Le ticket qui a des mots-clés, mais aucun d'entre eux n'est égal au nom de la catégorie à laquelle appartient le ticket est probablement un cas d'étiquette bruyant.
- Les autres tickets sont neutres en fonction des mots-clés.
cl_list = [] for category, keywords in zip(df.Category, df.Keywords): if category.lower() == keywords.lower() and keywords != "": cl_list.append("easy_classification") elif category.lower() in keywords.lower(): # to deal with multiple keywords in the ticket cl_list.append("probably_easy_classification") elif category.lower() != keywords.lower() and keywords != "": cl_list.append("potential_problem") else: cl_list.append("neutral") df["Ease_classification"] = cl_list
df.Ease_classification.value_counts(normalize=True, dropna=False).mul(100).round(1).astype(str) + "%"
neutral 45.6%
easy_classification 37.7%
potential_problem 9.6%
probably_easy_classification 7.0%
Name: Ease_classification, dtype: object
Nous avons fait notre nouvelle distribution et il est maintenant temps d'examiner les tickets classés comme un problème potentiel. En pratique, l'étape suivante nécessiterait beaucoup plus d'échantillonnage et examinerait les plus gros blocs de données à l'œil nu, mais la logique serait la même. Vous êtes censé trouver les tickets problématiques et décider si vous pouvez améliorer leur qualité ou si vous devez les supprimer de l'ensemble de données. Lorsque vous êtes confronté à un grand ensemble de données, restez calme et n'oubliez pas que l'examen et la préparation des données prennent généralement beaucoup plus de temps que la création d'algorithmes ML !
pp = df[df.Ease_classification == "potential_problem"] for text, category in zip(pp.Text.sample(5, random_state=2), pp.Category.sample(3, random_state=2)): print("TEXT:") print(text) print("CATEGORY:") print(category) print("-"*100)
TEXT:
outlook issue , I did an update Windows and I have no more outlook on my notebook ? Please help !
Outlook
CATEGORY:
Mail
-------------------------------------------------------------------- TEXT:
Please relase blocked attachements from the mail I got from name.surname@company.com. These are data needed for social media marketing campaing.
CATEGORY:
Outlook
--------------------------------------------------------------------
TEXT:
Asana funktionierte nicht mehr, nachdem ich meinen Laptop neu gestartet hatte. Bitte helfen Sie.
CATEGORY:
Help Needed
--------------------------------------------------------------------
Nous comprenons que les tickets des catégories Outlook et Mail sont liés au même problème, nous allons donc fusionner ces 2 catégories et améliorer les résultats de notre futur algorithme de classification ML.
mail_categories_to_merge = ["Outlook", "Mail"] sum_mail_cluster = 0
for x in mail_categories_to_merge: sum_mail_cluster += len(df[df["Category"] == x]) print("Number of categories to be merged into new cluster: ", len(mail_categories_to_merge))
print("Expected number of tickets in the new cluster: ", sum_mail_cluster) def rename_to_mail_cluster(category): if category in mail_categories_to_merge: category = "Mail_CLUSTER" else: category = category return category df["Category"] = df["Category"].apply(rename_to_mail_cluster) df.Category.value_counts()
Number of categories to be merged into new cluster: 2
Expected number of tickets in the new cluster: 33
Mail_CLUSTER 33
Discord 15
CRM 14
Internet Browser 12
Keyboard 11
Asana 10
Mouse 10
Help Needed 9
Name: Category, dtype: int64
Dernier point, mais non des moindres, nous souhaitons renommer certains tickets de la méta-catégorie "Aide nécessaire" à la catégorie appropriée.
df.loc[(df["Category"] == "Help Needed") & ([set(x).intersection(words_categories) for x in df["Text"].str.lower().str.replace("[^ws]", "", regex=True).str.split()]), "Category"] = "Change" def cat_name_change(cat, keywords): if cat == "Change": cat = keywords else: cat = cat return cat df["Category"] = df.apply(lambda x: cat_name_change(x.Category, x.Keywords), axis=1)
df["Category"] = df["Category"].replace({"Crm":"CRM"}) df.Category.value_counts(dropna=False)
Mail_CLUSTER 33
Discord 16
CRM 15
Internet Browser 12
Asana 11
Keyboard 11
Mouse 10
Help Needed 6
Name: Category, dtype: int64
Nous avons fait notre réétiquetage et notre nettoyage des données, mais nous ne devrions pas nous appeler des scientifiques des données si nous ne faisons pas au moins une expérience scientifique et testons l'impact de notre travail sur la classification finale. Nous le ferons en implémentant le classificateur Complement Naive Bayes dans sklearn. N'hésitez pas à essayer d'autres algorithmes plus complexes. Sachez également qu'un nettoyage supplémentaire des données pourrait être effectué - par exemple, nous pourrions également supprimer tous les tickets restants dans la catégorie "Aide nécessaire".
model = make_pipeline(TfidfVectorizer(), ComplementNB()) # old df
df_o = pd.read_excel("ITSM_data.xlsx") important_categories = ["Text", "Category"]
for cat in important_categories: df_o.drop(df_o[df_o[cat].isna()].index, inplace=True) df_o.name = "dataset just without missing"
df.name = "dataset after deeper cleaning" for dataframe in [df_o, df]: # Split dataset into training set and test set X_train, X_test, y_train, y_test = train_test_split(dataframe.Text, dataframe.Category, test_size=0.2, random_state=1) # Training the model with train data model.fit(X_train, y_train) # Predict the response for test dataset y_pred = model.predict(X_test) print(f"Accuracy of Complement Naive Bayes classifier model on {dataframe.name} is: {round(metrics.accuracy_score(y_test, y_pred),2)}")
Accuracy of Complement Naive Bayes classifier model on dataset just without missing is: 0.48
Accuracy of Complement Naive Bayes classifier model on dataset after deeper cleaning is: 0.65
Assez impressionnant, non ? L'ensemble de données que nous avons utilisé est petit (à dessein, vous pouvez donc facilement voir ce qui se passe à chaque étape), donc différentes graines aléatoires peuvent produire des résultats différents, mais dans la grande majorité des cas, le modèle fonctionnera nettement mieux sur l'ensemble de données après le nettoyage comparé à l'ensemble de données d'origine. Nous avons fait du bon travail !
Nicolas Greb code depuis plus de quatre ans et, depuis deux ans, il se spécialise en PNL. Avant de se tourner vers la science des données, il a réussi dans les ventes, les ressources humaines, l'écriture et les échecs.
- Contenu propulsé par le référencement et distribution de relations publiques. Soyez amplifié aujourd'hui.
- Platoblockchain. Intelligence métaverse Web3. Connaissance Amplifiée. Accéder ici.
- Frapper l'avenir avec Adryenn Ashley. Accéder ici.
- La source: https://www.kdnuggets.com/2023/04/dealing-noisy-labels-text-data.html?utm_source=rss&utm_medium=rss&utm_campaign=dealing-with-noisy-labels-in-text-data
- :possède
- :est
- :ne pas
- :où
- 1
- 10
- 100
- 11
- 2%
- 7
- 8
- 9
- a
- Qui sommes-nous
- Compte
- précision
- Après
- Agent
- algorithme
- algorithmes
- Tous
- aussi
- Bien que
- an
- ainsi que
- SONT
- AS
- At
- disponibles
- basé
- Essentiel
- BE
- car
- était
- before
- ci-dessous
- Améliorée
- bloqué
- navigateur
- construire
- Développement
- la performance des entreprises
- mais
- by
- Appelez-nous
- CAN
- porter
- maisons
- cas
- CHAT
- Attraper
- catégories
- Catégories
- Change
- Échecs
- classification
- classifié
- Classer
- Nettoyage
- Grappe
- code
- Codage
- Colonne
- Colonnes
- COM
- Commun
- Société
- par rapport
- Complément
- complexe
- NOUS CONTACTER
- connexion
- contient
- pourriez
- engendrent
- créée
- CRM
- données
- Préparation des données
- science des données
- Base de données
- affaire
- traitement
- décider
- profond
- décrit
- DID
- différent
- discorde
- distribution
- do
- Don
- Goutte
- chacun
- même
- Efficace
- activé
- entrée
- Ether (ETH)
- exactement
- exemple
- exemples
- existant
- attendu
- expérience
- expliqué
- œil
- few
- champ
- Des champs
- finale
- Trouvez
- Prénom
- Abonnement
- Pour
- quatre
- Test d'anglais
- de
- fonction
- plus
- avenir
- Allemand
- obtenez
- Donner
- donne
- Go
- Bien
- arrive
- Vous avez
- he
- casque audio
- aider
- frappe
- Comment
- hr
- HTTPS
- HubSpot
- i
- image
- Impact
- la mise en œuvre
- importer
- important
- impressionnant
- améliorer
- in
- indice
- d'information
- plutôt ;
- intérêt
- Internet
- développement
- aide
- IT
- Service informatique
- juste
- juste un
- KDnuggetsGenericName
- XNUMX éléments à
- Genre
- Libellé
- Etiquettes
- langue
- portatif
- gros
- plus importantes
- bibliothèques
- Licence
- comme
- LINK
- Liste
- Style
- recherchez-
- LOOKS
- love
- LES PLANTES
- Majorité
- gestion
- de nombreuses
- Stratégie
- Médias
- Membres
- Mémoire
- mentionné
- aller
- Meta
- Métrique
- pourrait
- manquant
- mixte
- ML
- modèle
- PLUS
- (en fait, presque toutes)
- plusieurs
- prénom
- Nature
- Langage naturel
- Traitement du langage naturel
- nécessaire
- Neutri
- Nouveauté
- nlp
- cahier
- maintenant
- nombre
- numpy
- objet
- of
- Bureaux
- Vieux
- on
- ONE
- Option
- or
- de commander
- original
- Autre
- nos
- Outlook
- sortie
- pandas
- passé
- PC
- effectuer
- pipeline
- Platon
- Intelligence des données Platon
- PlatonDonnées
- veuillez cliquer
- Point
- possible
- défaillances
- pratique
- prévoir
- Probablement
- Problème
- d'ouvrabilité
- procédures
- processus
- traitement
- produire
- correct
- but
- qualité
- aléatoire
- monde réel
- en relation
- représente
- exigent
- réponse
- Résultats
- retourner
- Débarrasser
- hausse
- RANGÉE
- vente
- même
- scénarios
- Sciences
- scientifiques
- sur le lien
- graines
- semble
- service
- set
- devrait
- montrer
- de façon significative
- similaires
- étapes
- depuis
- petit
- So
- Réseaux sociaux
- réseaux sociaux
- Logiciels
- sur mesure
- quelques
- Son
- spécialisé
- groupe de neurones
- scission
- Commencer
- Commencez
- rester
- étapes
- arrêté
- droit
- Chaîne
- réussi
- supposé
- soupçonneux
- Prenez
- discutons-en
- parlant
- équipe
- Membres de l'équipe
- tester
- que
- qui
- Les
- les informations
- leur
- Les
- Là.
- Ces
- l'ont
- des choses
- this
- billet
- billets
- fiable
- à
- trop
- sujet
- Total
- Train
- Formation
- Tournant
- tutoriel
- comprendre
- Mises à jour
- a actualisé
- us
- Utilisation
- utilisé
- d'utiliser
- Utilisateur
- en utilisant
- d'habitude
- Valeurs
- Vaste
- très
- via
- Wall
- était
- we
- Quoi
- quand
- qui
- quiconque
- sera
- fenêtres
- comprenant
- sans
- Word
- des mots
- Activités principales
- de travail
- pourra
- écriture
- code écrit
- X
- années
- you
- vous-même
- zéphyrnet