Imagem do editor
Com o crescente interesse no processamento de linguagem natural, mais e mais profissionais estão batendo na parede não porque não podem construir ou ajustar LLMs, mas porque seus dados são confusos!
Mostraremos procedimentos de codificação simples, mas muito eficazes, para corrigir rótulos ruidosos em dados de texto. Lidaremos com 2 cenários comuns em dados de texto do mundo real:
- Ter uma categoria que contém exemplos mistos de algumas outras categorias. Adoro chamar esse tipo de categoria de metacategoria.
- Ter 2 ou mais categorias que devem ser mescladas em 1 categoria porque os textos pertencentes a elas referem-se ao mesmo tópico.
Usaremos o conjunto de dados ITSM (IT Service Management) criado para este tutorial (licença CCO). Está disponível no Kaggle no link abaixo:
https://www.kaggle.com/datasets/nikolagreb/small-itsm-dataset
É hora de começar com a importação de todas as bibliotecas necessárias e exame de dados básicos. Prepare-se, o código está chegando!
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
Cada linha representa uma entrada no banco de dados ITSM. Tentaremos prever a categoria do ticket com base no texto do ticket escrito por um usuário. Vamos examinar mais profundamente os campos mais importantes para os casos de uso de negócios descritos.
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
----------------------------------------------------------------------------------------------------
Se dermos uma olhada nos dois primeiros tickets, embora um ticket esteja em alemão, podemos ver que os problemas descritos referem-se ao mesmo software?—?Asana, mas eles carregam rótulos diferentes. Este é o início da distribuição de nossas categorias:
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
A ajuda necessária parece suspeita, como a categoria que pode conter tickets de várias outras categorias. Além disso, as categorias Outlook e Mail parecem semelhantes, talvez devam ser mescladas em uma categoria. Antes de nos aprofundarmos nas categorias mencionadas, vamos nos livrar dos valores ausentes nas colunas de nosso interesse.
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)
Não há um substituto válido para o exame dos dados a olho nu. A função sofisticada para fazer isso em pandas é .sample(), então faremos exatamente isso mais uma vez, agora para a categoria suspeita:
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.
---------------------------------------------------------------------------
Obviamente, temos tickets falando sobre Discord, Asana e CRM. Portanto, o nome da categoria deve ser alterado de “Ajuda necessária” para categorias existentes mais específicas. Para a primeira etapa do processo de reatribuição, criaremos a nova coluna “Palavras-chave” que informa se o ticket possui a palavra da lista de categorias na coluna “Texto”.
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)
Além disso, observe que usar “if word in str(words_categories)” em vez de “if word in words_categories” capturaria palavras de categorias com mais de 1 palavra (navegador da Internet em nosso caso), mas também exigiria mais pré-processamento de dados. Para manter as coisas simples e direto ao ponto, iremos com o código para categorias feitas de apenas uma palavra. É assim que nosso conjunto de dados se parece agora:
df.head(2)
saída como imagem:
Depois de extrair a coluna de palavras-chave, assumiremos a qualidade dos tickets. Nossa hipótese:
- O ticket com apenas 1 palavra-chave no campo Texto que seja igual à categoria à qual o ticket pertence seria fácil de classificar.
- Ticket com várias palavras-chave no campo Texto, onde pelo menos uma das palavras-chave é a mesma da categoria a qual o ticket pertence seria fácil de classificar na maioria dos casos.
- O ticket que possui palavras-chave, mas nenhuma delas é igual ao nome da categoria a qual o ticket pertence provavelmente é um caso de etiqueta ruidosa.
- Outros tickets são neutros com base em palavras-chave.
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
Fizemos nossa nova distribuição e agora é a hora de examinar os tickets classificados como um problema potencial. Na prática, a etapa seguinte exigiria muito mais amostragem e examinaria os blocos maiores de dados a olho nu, mas a lógica seria a mesma. Você deve encontrar tickets problemáticos e decidir se pode melhorar sua qualidade ou se deve excluí-los do conjunto de dados. Quando você estiver enfrentando um grande conjunto de dados, fique calmo e não se esqueça de que o exame e a preparação dos dados geralmente levam muito mais tempo do que a construção de algoritmos de 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
--------------------------------------------------------------------
Entendemos que os tickets das categorias Outlook e Mail estão relacionados ao mesmo problema, portanto, mesclaremos essas duas categorias e melhoraremos os resultados de nosso futuro algoritmo de classificação de 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
Por último, mas não menos importante, queremos renomear alguns tickets da metacategoria “Ajuda necessária” para a categoria apropriada.
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
Fizemos nossa reclassificação e limpeza de dados, mas não devemos nos chamar de cientistas de dados se não fizermos pelo menos um experimento científico e testarmos o impacto de nosso trabalho na classificação final. Faremos isso implementando o classificador The Complement Naive Bayes no sklearn. Sinta-se à vontade para experimentar outros algoritmos mais complexos. Além disso, esteja ciente de que mais limpeza de dados pode ser feita - por exemplo, também podemos descartar todos os tickets restantes na categoria "Ajuda necessária".
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
Bastante impressionante, certo? O conjunto de dados que usamos é pequeno (de propósito, para que você possa ver facilmente o que acontece em cada etapa), portanto, sementes aleatórias diferentes podem produzir resultados diferentes, mas, na grande maioria dos casos, o modelo terá um desempenho significativamente melhor no conjunto de dados após a limpeza em comparação ao conjunto de dados original. Fizemos um bom trabalho!
Nikola Greb está codificando há mais de quatro anos e, nos últimos dois anos, se especializou em PNL. Antes de se dedicar à ciência de dados, ele teve sucesso em vendas, RH, redação e xadrez.
- Conteúdo com tecnologia de SEO e distribuição de relações públicas. Seja amplificado hoje.
- Platoblockchain. Inteligência Metaverso Web3. Conhecimento Ampliado. Acesse aqui.
- Cunhando o Futuro com Adryenn Ashley. Acesse aqui.
- Fonte: 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
- :tem
- :é
- :não
- :onde
- 1
- 10
- 100
- 11
- 2%
- 7
- 8
- 9
- a
- Sobre
- Conta
- precisão
- Depois de
- Agente
- algoritmo
- algoritmos
- Todos os Produtos
- tb
- Apesar
- an
- e
- SOMOS
- AS
- At
- disponível
- baseado
- basic
- BE
- Porque
- sido
- antes
- abaixo
- Melhor
- bloqueado
- navegador
- construir
- Prédio
- negócio
- mas a
- by
- chamada
- CAN
- transportar
- casas
- casos
- CAT
- luta
- Categorias
- Categoria
- alterar
- xadrez
- classificação
- classificado
- classificar
- Limpeza
- Agrupar
- código
- Codificação
- Coluna
- colunas
- COM
- comum
- Empresa
- comparado
- Complemento
- integrações
- Contato
- da conexão
- contém
- poderia
- crio
- criado
- CRM
- dados,
- Preparação de dados
- ciência de dados
- banco de dados
- acordo
- lidar
- decidir
- mais profunda
- descrito
- DID
- diferente
- discórdia
- distribuição
- do
- don
- Cair
- cada
- facilmente
- Eficaz
- habilitado
- entrada
- Éter (ETH)
- exatamente
- exemplo
- exemplos
- existente
- esperado
- experimentar
- explicado
- olho
- enfrentando
- poucos
- campo
- Campos
- final
- Encontre
- Primeiro nome
- seguinte
- Escolha
- quatro
- Gratuito
- da
- função
- mais distante
- futuro
- Alemão
- ter
- OFERTE
- dá
- Go
- Bom estado, com sinais de uso
- acontece
- Ter
- he
- fones de ouvido
- ajudar
- batendo
- Como funciona o dobrador de carta de canal
- hr
- HTTPS
- HubSpot
- i
- imagem
- Impacto
- implementação
- importar
- importante
- impressionante
- melhorar
- in
- índice
- INFORMAÇÕES
- em vez disso
- interesse
- Internet
- para dentro
- emitem
- IT
- Serviço de TI
- apenas por
- apenas um
- KDnuggetsGenericName
- Guarda
- Tipo
- O rótulo
- Rótulos
- língua
- laptop
- grande
- Maior
- bibliotecas
- Licença
- como
- LINK
- Lista
- olhar
- procurando
- OLHARES
- gosta,
- moldadas
- Maioria
- de grupos
- muitos
- Marketing
- Mídia
- Membros
- Memória
- mencionado
- ir
- Meta
- Métrica
- poder
- desaparecido
- misto
- ML
- modelo
- mais
- a maioria
- múltiplo
- nome
- natural
- Linguagem Natural
- Processamento de linguagem natural
- necessário
- Neutro
- Novo
- PNL
- caderno
- agora
- número
- numpy
- objeto
- of
- Office
- Velho
- on
- ONE
- Opção
- or
- ordem
- original
- Outros
- A Nossa
- Outlook
- saída
- pandas
- passado
- PC
- realizar
- oleoduto
- platão
- Inteligência de Dados Platão
- PlatãoData
- por favor
- ponto
- possível
- potencial
- prática
- predizer
- provavelmente
- Problema
- problemas
- procedimentos
- processo
- em processamento
- produzir
- adequado
- propósito
- qualidade
- acaso
- mundo real
- relacionado
- representa
- requerer
- resposta
- Resultados
- retorno
- Livrar
- ascensão
- LINHA
- vendas
- mesmo
- cenários
- Ciência
- cientistas
- Vejo
- SEEDS
- parece
- serviço
- conjunto
- rede de apoio social
- mostrar
- de forma considerável
- semelhante
- simples
- desde
- pequeno
- So
- Redes Sociais
- meios de comunicação social
- Software
- solução
- alguns
- Parecer
- especializado
- específico
- divisão
- começo
- Comece
- ficar
- Passo
- parou
- direto
- Tanga
- bem sucedido
- suposto
- suspeito
- Tire
- Converse
- falando
- Profissionais
- Membros do time
- teste
- do que
- que
- A
- as informações
- deles
- Eles
- Lá.
- Este
- deles
- coisas
- isto
- bilhete
- bilhetes
- tempo
- para
- também
- tópico
- Total
- Trem
- Training
- Passando
- tutorial
- compreender
- Atualizar
- Atualizada
- us
- Uso
- usar
- usava
- Utilizador
- utilização
- geralmente
- Valores
- Grande
- muito
- via
- parede
- foi
- we
- O Quê
- quando
- qual
- quem quer que
- precisarão
- Windows
- de
- sem
- Word
- palavras
- Atividades:
- trabalhar
- seria
- escrita
- escrito
- X
- anos
- Você
- você mesmo
- zefirnet