Agrupamento de cargos semelhantes com FastText

Laís Van Vossen
Senior Sistemas
Published in
5 min readNov 7, 2023
imagem de um crachá escrito Keyboard Cowboy, preso em uma blusa social xadrez azul
fonte: https://www.inc.com/joe-reynolds/the-case-for-letting-employees-choose-their-own-creative-titles.html

Introdução

Analista de RH,

Assistente de Recursos Humanos

Assistente de Atendimento a Colaboradores (RH)

Auxiliar Administrativo de Pessoas e Cultura (RH)

O que todos esses cargos, retirados do LinkedIn, tem em comum? Todos se referem a um profissional da área de recursos humanos. Essa diversidade de nomes para se referir a um único cargo é bastante comum e acontece em diversas áreas. Com isso, os recrutadores têm um problema para encontrar candidatos no banco de talentos utilizando as ferramentas convencionais de busca, que procuram por palavras exatas.

Então, como fazer para que todos os cargos relacionados com RH, por exemplo, apareçam em uma única busca? Para resolver esse desafio podemos utilizar Machine Learning (ML), mais especificamente, o algoritmo FastText, que é uma biblioteca do Python especializada no aprendizado de representação de palavras e classificação de frases. Contudo, para treinar qualquer modelo de ML, é necessária uma boa base de dados, por isso, montamos uma base, que será apresentada neste artigo. Então, considerando que esse pode ser um problema comum, e visando auxiliar a comunidade na resolução do mesmo, disponibilizamos publicamente a base criada no Kaggle.

Sobre as Bases de dados

Para treinar o modelo foram testados dois conjuntos de dados públicos, um em português e outro em inglês, que serão apresentados em mais detalhes em seguida.

Cargos em inglês

Para o conjunto de dados em inglês foi utilizado a base de dados da ONET, que contêm uma coletânea de mais de 900 cargos, suas descrições e diversas outras informações. Os cargos são divididos utilizando a famosa divisão em 16 clusters:

  • Agriculture, food, and natural resources (Agricultura, comida e recursos naturais);
  • Architecture and construction (Arquitetura e construção);
  • Arts, audio/video technology, and communication (Artes, tecnologia em audio/vídeo e comunicação);
  • Businesses management and administration (Gerenciamento de negócios e administração);
  • Education and training (Educação e treinamento);
  • Finance (Finanças);
  • Government and public administration (Governo e administração pública);
  • Health science (Ciências da saúde);
  • Hospitality and tourism (Hotelaria e turismo);
  • Human services (Serviços humanos);
  • Information technology (Tecnologia da informação);
  • Law, public safety, corrections, and security (Direito, segurança pública, correções e segurança);
  • Manufacturing (Manufatura);
  • Marketing (Marketing);
  • Science, technology, engineering, and mathematics (Ciência, tecnologia, engenharia e matemática);
  • Transportation, distribution, and logistics (Transporte, distribuição e logística)

No entanto, um problema comum a bases de cargos públicas é sua periodicidade de atualização baixa. Diversos cargos e profissões novos surgem todos os anos, fazendo com que as bases fiquem desatualizadas. Para mitigar esse problema, utilizamos do ChatGPT para aumentar as bases de dados com variações de nomes de cargos e gerar um novo dataset.

Cargos em português

O conjunto de dados em português utilizou a Classificação Brasileira de Ocupações, que possui mais de 2500 cargos distintos e 7774 sinônimos para os nomes dos cargos. Diferente da base em inglês, o conjunto de dados da CBO se divide em grupos de especificidades diversas:

  • Grande grupo: possui 9 grupos
  • Subgrupo principal: possui 49 grupos
  • Subgrupo: possui 195 grupos
  • Família: possui 626 grupos

Semelhante ao que acontece com a base em inglês, a base de dados da CBO está desatualizada quanto aos cargos que ela possui. Por isso, foi utilizado o ChatGPT para gerar mais sinônimos para os cargos que ela contêm, com a expectativa de gerar variação mais semelhantes com as que estão sendo usadas atualmente.

Experimentos e Resultados

Diversos experimentos foram feitos com as quatro bases de dados, no entanto, o que se saiu melhor para classificar cargos em português foi a base em português com sinônimos gerados pelo ChatGPT. Para isso, os cargos foram limpos com os seguintes passos:

  1. Remoção de nível: são removidos caracteres que fazem referência ao nível do cargo (ex.: I, II, III).
  2. Remoção de caracteres especiais: são removidos caracteres especiais como acentos, pontuações e as palavras são colocadas no diminutivo.
def remove_nivel(texto:str):
texto_sem_numeros = re.sub(r'\d+', '', texto)
nivel_replace_map = {" III":"", " II":"", " I":"",
" III ":"", " II ":"", " I ":""}
for caractere, substituto in nivel_replace_map.items():
texto_sem_numeros = texto_sem_numeros.replace(caractere, substituto)
texto_sem_numeros = texto_sem_numeros.split('-')[0]
return texto_sem_numeros.lstrip().rstrip()
def remove_accents_and_special_chars(input_str:str):
# Normaliza a string para 'NFD' e mantém apenas caracteres não combinados
input_str = input_str.lower()
nfkd_form = unicodedata.normalize('NFD', input_str)
only_ascii = nfkd_form.encode('ASCII', 'ignore').decode('ASCII')

# Remove caracteres especiais, mantendo apenas letras, números e espaços
return re.sub(r'[^a-zA-Z0-9 ]', '', only_ascii)

Então, todos os grupos são transformados em labels para a classificação, removendo o espaço entre as palavras e substituindo por ‘-’. Para este exemplo, foram considerados como labels os campos de família e o título principal do cargo, resultando em 560 labels de família e 1855 labels para o nome do cargo

def prepara_dataset(data, file):
s = ""
for i in data.values.tolist():
for j in i[:-1]:
s += f'__label__{j} '
s += f'{i[-1]}\n'
with open(file, 'w') as f:
f.write(s)

Em seguida, a base é separada em treino e teste para o treinamento do modelo, com 80% da base separada para o treinamento.

def create_train_test(data, name):
train, test = train_test_split(data, test_size=0.2, random_state=42)
prepara_dataset(train, f'data_brasil/{name}.train')
prepara_dataset(test, f'data_brasil/{name}.test')

Por fim, o treinamento é feito, considerando 50 épocas, uma taxa de aprendizado de 0.2, uma função de perda do tipo ova (One vs. All) e considerando palavras de tamanho 1 no processo de treinamento, ou seja, o algoritmo aprenderá cada palavra individualmente. A função ova funciona dividindo o problema de classificação multiclasse em múltiplos problemas de classificação binária, ou seja, ao invés de perguntar qual é label do cargo, é perguntado se aquele cargo pertence a uma determinada label ou não.

model = fasttext.train_supervised(input="data_brasil/cargos.train", epoch=50, lr=0.2, loss='ova', wordNgrams=1)
model.test("data_brasil/cargos.test")
resultado = model.predict('analista de rh', threshold=0.7, k=2)
# resposta
# (('__label__profissionais-de-recursos-humanos', '__label__analista-de-recursos-humanos')

Prevendo então as possíveis labels para alguns cargos, temos como respostas:

  • Analista de rh: profissionais-de-recursos-humanos, analista-de-recursos-humanos;
  • Repositor de hortifruti: operadores-do-comercio-em-lojas-e-mercados, repositor-de-mercadorias;
  • Advogado criminalista: advogados, advogado-direito-penal;
  • Desenvolvedor de software java: tecnicos-de-desenvolvimento-de-sistemas-e-aplicacoes, desenvolvedor-de-sistemas-de-tecnologia-da-informacao-tecnico.

Conclusão

A classificação de textos utilizando o FastText pode ter diversos usos, não só no agrupamento de cargos, mas também para a classificação de textos nas mais diversas categorias. Como, por exemplo, identificar se uma receita em um fórum de culinária é de doce ou salgado, se uma pergunta em um fórum de tecnologia fala sobre Python ou Java, e assim por diante.

Com esse artigo, buscou-se apresentar essa tecnologia, mas, principalmente, montar essa base de dados, para que outras pessoas da comunidade com problemas similares possam usá-la de uma forma simplificada.

--

--