Stimuler les ventes avec l'apprentissage automatique

Comment nous utilisons le traitement du langage naturel pour qualifier les prospects

Dans cet article de blog, j'expliquerai comment nous rendons notre processus de vente chez Xeneta plus efficace en formant un algorithme d'apprentissage automatique pour prédire la qualité de nos prospects en fonction des descriptions de leur entreprise.

Rendez-vous sur GitHub si vous souhaitez consulter le script immédiatement et n'hésitez pas à suggérer des améliorations car il est en développement continu.

Le problème

Cela a commencé par une demande du représentant du développement des affaires, Edvard, qui était fatigué d'effectuer la tâche fastidieuse de parcourir de grandes feuilles Excel remplies avec les noms des entreprises, essayant d'identifier celles que nous devrions contacter.

Un exemple de liste d'entreprises potentielles à contacter, tiré de sec.gov

Ce type de présélection des prospects peut prendre des heures, car il oblige le représentant des ventes à comprendre ce que chaque entreprise fait (par exemple, en les lisant sur LinkedIn) afin qu'il puisse faire une estimation qualifiée de savoir si oui ou non l'entreprise convient parfaitement à notre application SaaS.

Et comment faites-vous une supposition qualifiée? Pour comprendre cela, vous devez d'abord savoir ce que nous faisons:

Essentiellement, Xeneta aide les entreprises qui expédient des conteneurs à découvrir le potentiel d'économie en fournissant des informations sur le marché du fret maritime.
Ce client avait un potentiel d'économies de 748 000 USD jusqu'à la moyenne du marché sur ses dépenses de fret maritime.

Plus précisément, si votre entreprise expédie plus de 500 conteneurs par an, vous découvrirez probablement un potentiel d'économies important en utilisant Xeneta, car nous pouvons vous dire exactement où vous payez au-dessus du prix moyen du marché.

Ce widget compare le tarif contracté d'un client (ligne violette) à la moyenne du marché (graphique vert) pour des conteneurs de 20 pieds de la Chine à l'Europe du Nord.

Cela signifie que nos clients cibles sont très différents les uns des autres, car leur seul dénominateur commun est qu'ils sont quelque peu impliqués dans le fret maritime. Voici quelques exemples de catégories d'entreprises que nous ciblons:

  • Automobile
  • Expédition de fret
  • Produits chimiques
  • Consommation et vente au détail
  • Produits peu rémunérés

L'hypothèse

Bien que le large éventail de clients représente un défi lors de la recherche de prospects, nous sommes normalement en mesure de dire si une entreprise présente un intérêt pour Xeneta en lisant la description de leur entreprise, car elle contient souvent des indices indiquant s'ils participent ou non à l'envoi de trucs. autour du monde.

Cela nous a fait penser:

Compte tenu de la description de l'entreprise, pouvons-nous former un algorithme pour prédire s'il s'agit ou non d'un client Xeneta potentiel?

Si tel est le cas, cet algorithme pourrait s'avérer un énorme gain de temps pour l'équipe des ventes, car il pourrait trier grossièrement les feuilles Excel avant de commencer à qualifier les pistes manuellement.

Le développement

Lorsque j'ai commencé à travailler sur ce sujet, j'ai rapidement réalisé que la partie d'apprentissage automatique n'était pas le seul problème. Nous avions également besoin d'un moyen d'obtenir les descriptions de l'entreprise.

Nous avons envisagé d'explorer les sites Web des entreprises et d'aller chercher la section À propos de nous. Mais cela sentait comme une activité désordonnée, imprévisible et longue, nous avons donc commencé à chercher des API à utiliser à la place. Après quelques recherches, nous avons découvert FullContact, qui possède une API d'entreprise qui vous fournit des descriptions de millions d'entreprises.

Cependant, leur API n'accepte que les URL de l'entreprise en tant qu'entrées, qui sont rarement présentes dans nos feuilles Excel.

Nous avons donc dû trouver un moyen d'obtenir également les URL, ce qui nous a fait atterrir sur le flux de travail suivant:

  • Utiliser l'API Google pour rechercher le nom de l'entreprise sur Google (hacky, je sais…)
  • Parcourez le résultat de la recherche et trouvez l'URL correcte la plus probable
  • Utilisez cette URL pour interroger l'API FullContact

Il y a bien sûr une perte à chaque étape ici, nous allons donc trouver une meilleure façon de procéder. Cependant, cela a assez bien fonctionné pour tester l'idée.

L'ensemble de données

Une fois ces scripts en place, l'étape suivante consistait à créer notre ensemble de données de formation. Il devait contenir au moins 1 000 entreprises qualifiées et 1 000 entreprises disqualifiées.

La première catégorie était facile, car nous pouvions simplement exporter une liste de 1000 utilisateurs Xeneta depuis SalesForce.

Trouver 1000 disqualifiés a été un peu plus difficile, car nous ne suivons pas les entreprises que nous avons évitées de contacter. Edvard a donc disqualifié manuellement 1000 entreprises.

Nettoyage des données

Cela fait, il était temps de commencer à écrire le script de traitement du langage naturel, la première étape étant de nettoyer les descriptions, car elles sont assez sales et contiennent beaucoup d'informations non pertinentes.

Dans les exemples ci-dessous, je vais passer en revue chacune des techniques de nettoyage que nous appliquons actuellement et vous montrer comment une description brute se termine par un tableau de nombres.

Un exemple de description brute.

RegExp

La première chose que nous faisons est d'utiliser des expressions régulières pour supprimer les caractères non alphabétiques, car notre modèle ne pourra apprendre que des mots.

description = re.sub ("[^ a-zA-Z]", "", description)
Après avoir supprimé les caractères non alphabétiques.

Stemmer

Nous endiguons également les mots. Cela signifie réduire plusieurs variantes du même mot à sa racine. Ainsi, au lieu d'accepter des mots comme fabricant, fabrication, manufacturé et manufacturier, nous les simplifions plutôt pour fabriquer.

de nltk.stem.snowball import SnowballStemmer stemmer = SnowballStemmer ('anglais') description = getDescription ()
description = [stemmer.stem (mot) pour le mot dans la description]
Après avoir endigué les mots.

Arrêter les mots

Nous supprimons ensuite les mots vides en utilisant Natural Language Toolkit. Les mots vides sont des mots qui ont peu de pertinence pour la compréhension conceptuelle du texte, tels que est, pour, pour, je, etc.

à partir de nltk.corpus importez des mots vides stopWords = set (stopwords.words ('english')) description = getDescription ()
description = [mot pour mot dans la description sinon mot dans les mots vides]
Après avoir supprimé les mots vides.

Transformer les données

Mais le nettoyage et l'enracinement des données ne nous aideront en fait à aucun apprentissage automatique, car nous devons également transformer les descriptions en quelque chose que la machine comprend, à savoir des chiffres.

Sac de mots

Pour cela, nous utilisons l'approche Bag of Words (BoW). Si vous n'êtes pas familier avec BoW, je vous recommande de lire ce tutoriel Kaggle.

BoW est une technique simple pour transformer des phrases de texte en vecteurs, où chaque élément dans les vecteurs représente un mot spécifique. CountVectorizer de Scikit learn vous offre un moyen très simple de le faire:

à partir de sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer (analyseur = 'word', max_features = 5000) vectorizer.fit (training_data) vectorized_training_data = vectorizer.transform (training_data)

Le paramètre max_features indique au vectoriseur combien de mots vous voulez avoir dans notre vocabulaire. Dans cet exemple, le vectoriseur inclura les 5000 mots les plus fréquents dans notre jeu de données et rejettera les autres.

Un exemple d'un très petit vecteur de sac de mots (35 objets). (Le nôtre fait 5 000 articles).

Transformation Tf-idf

Enfin, nous appliquons également une transformation tf-idf, qui est une fréquence de document inverse de fréquence à court terme. C'est une technique qui ajuste l'importance des différents mots dans vos documents.

Plus précisément, tf-idf mettra l'accent sur les mots qui se produisent fréquemment dans une description (fréquence du terme), tandis que les mots qui se produisent fréquemment dans l'ensemble de données (fréquence inverse du document) sont atténués.

à partir de sklearn.feature_extraction.text import TfidfTransformer tfidf = TfidfTransformer (norm = 'l1')
tfidf.fit (vectorized_training_data) tfidf_vectorized_data = tfidf.transform (vectorized_training_data)

Encore une fois, scikit learn sauve la situation en fournissant tf-idf prêt à l'emploi. Ajustez simplement le modèle à vos données d'entraînement vectorisées, puis utilisez la méthode de transformation pour le transformer.

Le vecteur après l'application de tf-idf. (Désolé pour le mauvais formatage)

L'algorithme

Une fois que toutes les données ont été nettoyées, vectorisées et transformées, nous pouvons enfin commencer à faire du machine learning, qui est l'une des parties les plus simples de cette tâche.

J'ai d'abord découpé les données en 70% de données d'entraînement et 30% de données de test, puis j'ai commencé avec deux algorithmes d'apprentissage scikit: Random Forest (RF) et K Nearest Neighbors (KNN). Il est rapidement devenu clair que RF a surpassé KNN, car le premier a rapidement atteint une précision de plus de 80% tandis que le second est resté à 60%.

Installer un modèle Scikit Learn est super simple:

def runForest (X_train, X_test, Y_train, Y_test): forest = RandomForestClassifier (n_estimators = 100) forest = forest.fit (X_train, Y_train) score = forest.score (X_test, Y_test) renvoie le score
forest_score = runForest (X_train, X_test, Y_train, Y_test)

J'ai donc continué avec RF pour voir dans quelle mesure je pouvais augmenter la précision en ajustant les paramètres suivants:

  • Vocabulaire: combien de mots le CountVectorizer inclut dans le vocabulaire (actuellement 5K)
  • Gamme de Gram: taille des phrases à inclure dans Bag Of Words (actuellement 1 à 3, ce qui signifie jusqu'à 3 phrases de mots)
  • Estimateurs: nombre d'estimateurs à inclure dans la forêt aléatoire (actuellement 90)

Avec ces paramètres réglés, l'algorithme atteint une précision de 86,4% sur l'ensemble de données de test et commence à devenir utile pour notre équipe de vente.

La route à suivre

Cependant, le script n'est en aucun cas terminé. Il y a des tonnes de façons de l'améliorer. Par exemple, l'algorithme est susceptible d'être biaisé vers le type de descriptions que nous avons actuellement dans nos données d'entraînement. Cela pourrait devenir un goulot d'étranglement lors des tests sur des données plus réelles.

Voici quelques activités que nous envisageons de faire pour l'avenir:

  • Obtenez plus de données (grattage, autres API, améliorez le nettoyage des données)
  • Tester d'autres types de transformation de données (par exemple word2vec)
  • Tester d'autres algorithmes ml (par ex. Réseaux neuronaux)

Nous allons pousser régulièrement vers GitHub si vous souhaitez suivre la progression. Et n'hésitez pas à laisser un commentaire ci-dessous si vous avez quelque chose à ajouter.

À votre santé,

Per Harald Borgen

Merci d'avoir lu! Nous sommes Xeneta - la première plateforme mondiale de renseignement sur le fret maritime. Nous recherchons toujours des esprits brillants pour nous rejoindre, alors rendez-vous sur notre site Web si vous êtes intéressé!

Vous pouvez nous suivre sur Twitter et Medium.