85 votes

Comment construire un modèle permettant de distinguer les tweets sur Apple (Inc.) des tweets sur la pomme (fruit) ?

Voir ci-dessous les 50 tweets sur "apple". J'ai étiqueté à la main les correspondances positives sur Apple Inc. Ils sont marqués comme 1 ci-dessous.

Voici quelques lignes :

1|“@chrisgilmer: Apple targets big business with new iOS 7 features http://bit.ly/15F9JeF ”. Finally.. A corp iTunes account!
0|“@Zach_Paull: When did green skittles change from lime to green apple? #notafan” @Skittles
1|@dtfcdvEric: @MaroneyFan11 apple inc is searching for people to help and tryout all their upcoming tablet within our own net page No.
0|@STFUTimothy have you tried apple pie shine?
1|#SuryaRay #India Microsoft to bring Xbox and PC games to Apple, Android phones: Report: Microsoft Corp... http://dlvr.it/3YvbQx  @SuryaRay

Voici l'ensemble des données : http://pastebin.com/eJuEb4eB

Je dois construire un modèle qui classe "Apple" (Inc). parmi les autres.

Je ne cherche pas un aperçu général de l'apprentissage automatique, mais plutôt un modèle réel dans le code ( Python préféré).

18 votes

Vous voulez essentiellement ça : fr.wikipedia.org/wiki/Bayesian_spam_filtering

1 votes

Vous étiquetez vos données à la main, mais vous voulez des bibliothèques à l'échelle. S'agit-il de données supervisées ou non supervisées ?

0 votes

Elle commencerait par être supervisée, l'objectif étant de la laisser se dérouler sans supervision.

73voto

Neil McGuigan Points 10123

Ce que vous recherchez s'appelle Reconnaissance d'entités nommées . Il s'agit d'une technique statistique qui utilise (le plus souvent) Champs aléatoires conditionnels pour trouver des entités nommées, en se basant sur le fait qu'il a été formé pour apprendre des choses sur les entités nommées.

Essentiellement, elle examine le contenu et contexte du mot, (en regardant quelques mots en arrière et en avant), pour estimer la probabilité que le mot soit une entité nommée.

Un bon logiciel peut examiner d'autres caractéristiques des mots, comme leur longueur ou leur forme (comme "Vcv" s'il commence par "voyelle-consonne-voyelle").

Une très bonne bibliothèque (GPL) est Le NER de Stanford

Voici la démo : http://nlp.stanford.edu:8080/ner/

Quelques exemples de textes à essayer :

Je mangeais une pomme au siège d'Apple et j'ai pensé à Apple Martin, la fille du gars de Coldplay.

(les classificateurs 3class et 4class ont raison)

5 votes

C'était vraiment intéressant. Est-il possible de voir le code pour english.conll.4class.distsim.crf.ser.gz ? J'aimerais bien voir comment on construit quelque chose comme ça.

0 votes

Le code de NER est open source, mais les données utilisées lors des conférences CONLL ne le sont pas. Cependant, vous pouvez trouver le Corpus Reuters en ligne au NIST.

39voto

AMADANON Inc. Points 2678

Je procéderais de la manière suivante :

  1. Découper la phrase en mots, les normaliser, construire un dictionnaire
  2. Pour chaque mot, enregistrez le nombre de fois où il apparaît dans des tweets concernant l'entreprise et le nombre de fois où il apparaît dans des tweets concernant le fruit - ces tweets doivent être confirmés par un humain.
  3. Lorsqu'un nouveau tweet arrive, trouvez chaque mot du tweet dans le dictionnaire, calculez un score pondéré - les mots qui sont utilisés fréquemment en relation avec l'entreprise obtiendront un score élevé pour l'entreprise, et vice versa ; les mots utilisés rarement, ou utilisés à la fois avec l'entreprise et le fruit, n'auront pas un grand score.

2 votes

Merci pour votre réponse à ce sujet. Votre réponse en conjonction avec un commentaire ci-dessus m'a vraiment aidé à trouver une solution. Pouvez-vous m'aider à affiner cette solution ?

10 votes

Il s'agit d'une description informelle de la classification bayésienne.

1 votes

Je préfère "implémentation en pseudo-code de la classification bayésienne" :)

31voto

Ian Ozsvald Points 817

J'ai un système semi-fonctionnel qui résout ce problème, en open sourcing en utilisant scikit-learn, avec une série d'articles de blog décrivant ce que je fais. Le problème auquel je m'attaque est la désambiguïsation du sens des mots (choisir un mot parmi de multiples sens du mot ), ce qui n'est pas la même chose que la reconnaissance des entités nommées. Mon approche de base est quelque peu compétitive par rapport aux solutions existantes et (surtout) elle est personnalisable.

Il existe quelques outils commerciaux de NER (OpenCalais, DBPedia Spotlight et AlchemyAPI) qui pourraient vous donner un résultat commercial suffisamment bon - essayez-les d'abord !

J'en ai utilisé quelques-uns pour un projet client (je suis consultant en PNL/ML à Londres), mais je n'étais pas satisfait de leur rappel ( précision et rappel ). En gros, ils peuvent être précis (lorsqu'ils disent "C'est Apple Inc", ils sont généralement corrects), mais avec un faible taux de rappel (ils disent rarement "C'est Apple Inc" même si pour un humain, le tweet parle manifestement d'Apple Inc). Je me suis dit que ce serait un exercice intellectuellement intéressant de construire une version open source adaptée aux tweets. Voici le code actuel : https://github.com/ianozsvald/social_media_brand_disambiguator

Je tiens à préciser que je n'essaie pas de résoudre le problème de la désambiguïsation généralisée du sens des mots avec cette approche. marque désambiguïsation (entreprises, personnes, etc.) lorsque vous avez déjà leur nom. C'est pourquoi je pense que cette approche directe fonctionnera.

J'ai commencé ce projet il y a six semaines, et il est écrit en Python 2.7 avec scikit-learn. Il utilise une approche très basique. Je vectorise en utilisant un vectoriseur à comptage binaire (je compte seulement si un mot apparaît, pas combien de fois) avec 1-3  n-grams . Je n'utilise pas le TF-IDF (le TF-IDF est bon lorsque la longueur du document est variable ; pour moi, les tweets ne font qu'une ou deux phrases, et les résultats de mes tests n'ont pas montré d'amélioration avec le TF-IDF).

J'utilise le tokenizer de base qui est très basique mais étonnamment utile. Il ignore les @ # (ce qui fait que vous perdez un peu de contexte) et, bien sûr, ne développe pas une URL. Je m'entraîne ensuite en utilisant régression logistique et il semble que ce problème soit en quelque sorte linéairement séparable (beaucoup de termes pour une classe n'existent pas pour l'autre). Pour l'instant, j'évite d'effectuer un nettoyage ou une épuration (j'essaie d'utiliser la méthode la plus simple possible qui pourrait fonctionner).

Le code a un README complet, et vous devriez être en mesure d'ingérer vos tweets relativement facilement, puis de suivre mes suggestions pour les tester.

Cela fonctionne pour Apple car les gens ne mangent pas ou ne boivent pas d'ordinateurs Apple, pas plus qu'ils ne tapent ou ne jouent avec des fruits, de sorte que les mots sont facilement répartis dans l'une ou l'autre catégorie. Cette condition peut ne pas être valable si l'on considère quelque chose comme #definance pour l'émission de télévision (où les gens utilisent également #definance en relation avec le printemps arabe, les matchs de cricket, la révision des examens et un groupe de musique). Des approches plus intelligentes pourraient bien être nécessaires dans ce cas.

J'ai une série d'articles de blog décrivant ce projet, y compris une présentation d'une heure que j'ai donnée au groupe d'utilisateurs BrightonPython (qui s'est transformée en une présentation plus courte pour 140 personnes à DataScienceLondon).

Si vous utilisez quelque chose comme LogisticRegression (où vous obtenez une probabilité pour chaque classification) vous pouvez choisir seulement les classifications sûres, et de cette façon vous pouvez forcer une précision élevée en négociant contre le rappel (ainsi vous obtenez des résultats corrects, mais moins d'entre eux). Vous devrez adapter cette méthode à votre système.

Voici une approche algorithmique possible en utilisant scikit-learn :

  • Utiliser un CountVectorizer binaire (je ne pense pas que le comptage des termes dans les messages courts apporte beaucoup d'informations, car la plupart des mots n'apparaissent qu'une seule fois).
  • Commencez par un classificateur à arbre de décision. Il aura des performances explicables (voir Sur-mesure avec un arbre de décision pour un exemple).
  • Passer à la régression logistique
  • Examinez les erreurs générées par les classificateurs (lisez la sortie exportée du DecisionTree ou regardez les coefficients dans LogisticRegression, retravaillez les tweets mal classés à travers le Vectorizer pour voir à quoi ressemble la représentation sous-jacente du sac de mots - il y aura moins de tokens que dans le tweet brut - y en a-t-il assez pour une classification ?)
  • Regardez mon exemple de code dans https://github.com/ianozsvald/social_media_brand_disambiguator/blob/master/learn1.py pour une version travaillée de cette approche

Les choses à considérer :

  • Vous avez besoin d'un ensemble de données plus important. J'utilise 2000 tweets étiquetés (cela m'a pris cinq heures), et vous voulez au minimum un ensemble équilibré avec >100 par classe (voir la note sur l'overfitting ci-dessous).
  • Améliorer le tokeniser (très facile avec scikit-learn) pour garder # @ dans les tokens, et peut-être ajouter un détecteur de marque capitalisée (comme le note l'utilisateur @user2425429)
  • Envisagez un classificateur non linéaire (comme la suggestion de @oiez ci-dessus) lorsque les choses se compliquent. Personnellement, j'ai trouvé que LinearSVC faisait moins bien que la régression logistique (mais cela peut être dû à l'espace de caractéristiques à haute dimension que je dois encore réduire).
  • Un marqueur de parties du discours spécifique au tweet (à mon humble avis, pas celui de Standford, comme le suggère @Neil - d'après mon expérience, il est peu performant en cas de mauvaise grammaire sur Twitter).
  • Une fois que vous avez beaucoup de jetons, vous voudrez probablement faire une réduction de la dimensionnalité (je n'ai pas encore essayé - voir mon article de blog sur la pénalisation l1 l2 de LogisticRegression).

Re. overfitting. Dans mon ensemble de données de 2000 éléments, j'ai un instantané de 10 minutes des tweets "apple" de Twitter. Environ 2/3 des tweets sont pour Apple Inc, 1/3 pour d'autres utilisations de la pomme. J'extrais un sous-ensemble équilibré (environ 584 lignes, je pense) de chaque classe et je fais une validation croisée cinq fois pour la formation.

Comme je n'ai qu'une fenêtre temporelle de 10 minutes, j'ai beaucoup de tweets sur le même sujet, et c'est probablement la raison pour laquelle mon classificateur s'en sort si bien par rapport aux outils existants - il s'est suradapté aux caractéristiques de formation sans bien se généraliser (alors que les outils commerciaux existants sont moins performants sur cet atelier instantané, mais plus fiables sur un ensemble plus large de données). J'élargirai ma fenêtre de temps pour tester cela dans un travail ultérieur.

0 votes

Je n'ai pas eu le plaisir d'examiner votre code et d'essayer de le dupliquer/émuler/éduquer, mais je vous dois des excuses pour ne pas avoir accordé la totalité des 50pts de la prime. J'étais absent de SO pendant le week-end et j'ai manqué la date limite pour l'attribuer. Heureusement, la communauté SO est intervenue et a jugé bon de vous attribuer 25pts.

1 votes

Pas de problème :-) Le code, le fichier README et les articles du blog devraient vous donner une idée de mon approche. Elle est délibérément simple mais semble fonctionner correctement.

12voto

Sudipta Points 1769

Vous pouvez procéder comme suit :

  1. Dressez une liste de mots contenant leur nombre d'occurrences dans les tweets relatifs aux fruits et aux entreprises. Cela peut être réalisé en lui fournissant quelques échantillons de tweets dont nous connaissons l'inclinaison.

  2. En utilisant suffisamment de données précédentes, nous pouvons déterminer la probabilité qu'un mot apparaisse dans un tweet sur apple inc.

  3. Multipliez les probabilités individuelles des mots pour obtenir la probabilité du tweet entier.

Un exemple simplifié :

p_f \= Probabilité de tweets de fruits.

p_w_f \= Probabilité qu'un mot apparaisse dans un tweet sur les fruits.

p_t_f \= Probabilité combinée de tous les mots du tweet apparaissant dans un tweet de fruit = p_w1_f * p_w2_f * ...

p_f_t \= Probabilité d'un fruit donné par un tweet particulier.

p_c, p_w_c, p_t_c, p_c_t sont les valeurs respectives de l'entreprise.

Un lisseur laplacien de valeur 1 est ajouté pour éliminer le problème de la fréquence nulle des nouveaux mots qui n'existent pas dans notre base de données.

old_tweets = {'apple pie sweet potatoe cake baby https://vine.co/v/hzBaWVA3IE3': '0', ...}
known_words = {}
total_company_tweets = total_fruit_tweets =total_company_words = total_fruit_words = 0

for tweet in old_tweets:
    company = old_tweets[tweet]
    for word in tweet.lower().split(" "):
        if not word in known_words:
            known_words[word] = {"company":0, "fruit":0 }
        if company == "1":
            known_words[word]["company"] += 1
            total_company_words += 1
        else:
            known_words[word]["fruit"] += 1
            total_fruit_words += 1

    if company == "1":
        total_company_tweets += 1
    else:
        total_fruit_tweets += 1
total_tweets = len(old_tweets)

def predict_tweet(new_tweet,K=1):
    p_f = (total_fruit_tweets+K)/(total_tweets+K*2)
    p_c = (total_company_tweets+K)/(total_tweets+K*2)
    new_words = new_tweet.lower().split(" ")

    p_t_f = p_t_c = 1
    for word in new_words:
        try:
            wordFound = known_words[word]
        except KeyError:
            wordFound = {'fruit':0,'company':0}
        p_w_f = (wordFound['fruit']+K)/(total_fruit_words+K*(len(known_words)))
        p_w_c = (wordFound['company']+K)/(total_company_words+K*(len(known_words)))
    p_t_f *= p_w_f
    p_t_c *= p_w_c

    #Applying bayes rule
    p_f_t = p_f * p_t_f/(p_t_f*p_f + p_t_c*p_c)
    p_c_t = p_c * p_t_c/(p_t_f*p_f + p_t_c*p_c)
    if p_c_t > p_f_t:
        return "Company"
    return "Fruit"

9voto

oiez Points 310

Si vous n'avez pas de problème à utiliser une bibliothèque extérieure, je vous recommande scikit-learn puisqu'il peut probablement faire cela mieux et plus rapidement que tout ce que vous pourriez coder par vous-même. Je ferais juste quelque chose comme ça :

Construisez votre corpus. J'ai fait les compréhensions de liste pour la clarté, mais selon la façon dont vos données sont stockées, vous pourriez avoir besoin de faire des choses différentes :

def corpus_builder(apple_inc_tweets, apple_fruit_tweets):
    corpus = [tweet for tweet in apple_inc_tweets] + [tweet for tweet in apple_fruit_tweets]
    labels = [1 for x in xrange(len(apple_inc_tweets))] + [0 for x in xrange(len(apple_fruit_tweets))]
    return (corpus, labels)

L'important est que vous vous retrouviez avec deux listes qui ressemblent à ceci :

([['apple inc tweet i love ios and iphones'], ['apple iphones are great'], ['apple fruit tweet i love pie'], ['apple pie is great']], [1, 1, 0, 0])

Les [1, 1, 0, 0] représentent les étiquettes positives et négatives.

Ensuite, vous créez une Pipeline ! Pipeline est une classe scikit-learn qui facilite le chaînage des étapes de traitement de texte afin de n'avoir à appeler qu'un seul objet lors de la formation/prédiction :

def train(corpus, labels)
    pipe = Pipeline([('vect', CountVectorizer(ngram_range=(1, 3), stop_words='english')),
                        ('tfidf', TfidfTransformer(norm='l2')),
                        ('clf', LinearSVC()),])
    pipe.fit_transform(corpus, labels)
    return pipe

Dans le pipeline, il y a trois étapes de traitement. Le CountVectorizer tokenise les mots, les divise, les compte et transforme les données en une matrice éparse. Le TfidfTransformer est facultatif, et vous pourriez vouloir le supprimer en fonction de l'évaluation de la précision (effectuer des tests de validation croisée et une recherche de grille pour les meilleurs paramètres est un peu compliqué, donc je ne m'y attarderai pas ici). Le LinearSVC est un algorithme standard de classification de texte.

Enfin, vous prédisez la catégorie des tweets :

def predict(pipe, tweet):
    prediction = pipe.predict([tweet])
    return prediction

Encore une fois, le tweet doit être dans une liste, donc j'ai supposé qu'il entrait dans la fonction comme une chaîne.

Mettez tout ça dans une classe ou autre, et vous avez terminé. Du moins, avec cet exemple très basique.

Je n'ai pas testé ce code, il se peut donc qu'il ne fonctionne pas si vous faites un simple copier-coller, mais si vous voulez utiliser scikit-learn, cela devrait vous donner une idée de par où commencer.

EDIT : j'ai essayé d'expliquer les étapes plus en détail.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X