75 votos

¿Cómo puedo construir un modelo para distinguir los tweets sobre Apple (Inc.) de los tweets sobre la manzana (fruta)?

Vea a continuación los 50 tweets sobre la "manzana". He etiquetado a mano las coincidencias positivas sobre Apple Inc. Están marcados como 1 a continuación.

Aquí hay un par de líneas:

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

Este es el conjunto total de datos: http://pastebin.com/eJuEb4eB

Necesito construir un modelo que clasifique a "Apple" (Inc). del resto.

No estoy buscando una visión general del aprendizaje automático, más bien estoy buscando un modelo real en código ( Python preferido).

66voto

Neil McGuigan Puntos 10123

Lo que busca se llama Reconocimiento de Entidades Nombradas . Es una técnica estadística que utiliza Campos aleatorios condicionales para encontrar entidades con nombre, basándose en haber sido entrenado para aprender cosas sobre entidades con nombre.

Esencialmente, examina el contenido y contexto de la palabra, (mirando hacia atrás y hacia delante unas cuantas palabras), para estimar la probabilidad de que la palabra sea una entidad con nombre.

Un buen software puede fijarse en otras características de las palabras, como su longitud o su forma (como Vcv o para los inicios con Vocal-consonante-vocal)

Una muy buena biblioteca (GPL) es TNE de Stanford

Aquí está la demostración: http://nlp.stanford.edu:8080/ner/

Algunos ejemplos de texto para probar:

Estaba comiendo una manzana en la sede de Apple y pensé en Apple Martin, la hija del chico de Coldplay

(los clasificadores 3class y 4class lo hacen bien)

32voto

AMADANON Inc. Puntos 2678

Yo lo haría de la siguiente manera:

  1. Dividir la frase en palabras, normalizarlas, construir un diccionario
  2. Con cada palabra, almacene el número de veces que aparecen en tweets sobre la empresa, y el número de veces que aparecen en tweets sobre la fruta - estos tweets deben ser confirmados por un humano
  3. Cuando llega un nuevo tuit, busca todas las palabras del tuit en el diccionario, calcula una puntuación ponderada: las palabras que se utilizan con frecuencia en relación con la empresa obtendrían una puntuación alta de la empresa, y viceversa; las palabras que se utilizan raramente, o que se utilizan tanto con la empresa como con la fruta, no tendrían mucha puntuación.

27voto

Ian Ozsvald Puntos 817

Tengo un sistema semiprofesional que resuelve este problema, de código abierto usando scikit-learn, con una serie de entradas de blog que describen lo que estoy haciendo. El problema que estoy abordando es la desambiguación del sentido de las palabras (elegir una de las múltiples https://en.wikipedia.org/wiki/Word_sense opciones), que no es lo mismo que el Reconocimiento de Entidades Nombradas. Mi enfoque básico es algo competitivo con las soluciones existentes y (fundamentalmente) es personalizable.

Existen algunas herramientas comerciales de NER (OpenCalais, DBPedia Spotlight, AlchemyAPI) que pueden proporcionarle un resultado comercial suficientemente bueno.

Utilicé algunos de ellos para un proyecto de un cliente (hago consultoría utilizando PNL/ML en Londres) pero no estaba contento con su Recall ( https://en.wikipedia.org/wiki/Precision_and_recall ). Básicamente, pueden ser precisos (cuando dicen "Esto es Apple Inc" suelen ser correctos) pero con un bajo nivel de recuerdo (rara vez dicen "Esto es Apple Inc" aunque para un humano el tuit sea obviamente sobre Apple Inc). Pensé que sería un ejercicio intelectualmente interesante construir una versión de código abierto adaptada a los tweets, aquí está el código actual: https://github.com/ianozsvald/social_media_brand_disambiguator

No estoy tratando de resolver el problema de la desambiguación del sentido de la palabra con este enfoque, sólo marca desambiguación (empresas, personas, etc.) cuando ya se tiene su nombre. Por eso creo que este enfoque directo funcionará.

Empecé esto hace 6 semanas, está escrito en Python 2.7 usando scikit-learn. Utiliza un enfoque muy básico. Vectorizo usando un vectorizador de conteo binario (sólo cuento si una palabra aparece, no cuántas veces) con 1-3 ngramas. No escalo con TF-IDF (TF-IDF es bueno cuando tienes una longitud de documento variable, para mí los tweets son sólo 1 o 2 frases, mis resultados de prueba no mostraron mejora con TF-IDF).

Utilizo el tokenizador básico que es muy básico pero sorprendentemente útil, ignora @ # (por lo que se pierde algo de contexto) y por supuesto no expande una URL. A continuación, entreno utilizando Regresión Logística, parece que este problema es algo linealmente separable (muchos términos para una clase no existen para la otra). Actualmente, evito cualquier tipo de limpieza (estoy intentando la cosa más simple posible que pueda funcionar).

El código tiene un README completo, deberías ser capaz de ingerir tus tweets con relativa facilidad y luego seguir mis sugerencias para las pruebas.

Esto funciona para Apple, ya que las personas no comemos ni bebemos ordenadores Apple, ni tecleamos ni jugamos con fruta, por lo que las palabras se dividen fácilmente en una u otra categoría. Esta condición puede no ser válida cuando se considera algo como #definir para el programa de televisión (donde la gente también utiliza #definir en relación con el árabe Spring, los partidos de cricket, la revisión de exámenes y un grupo musical). En este caso, es posible que se necesiten enfoques más inteligentes.

Tengo una serie de entradas de blog que describen este proyecto, incluyendo una presentación de 1 hora que di en el grupo de usuarios de BrightonPython (que se convirtió en una presentación más corta para 140 personas en DataScienceLondon): http://ianozsvald.com/category/socialmediabranddisambiguator/

Si utilizas algo como LogisticRegression (donde obtienes una probabilidad para cada clasificación) puedes elegir sólo las clasificaciones seguras, de esa manera puedes forzar una alta precisión al negociar contra la recuperación (por lo que obtienes resultados correctos, pero menos de ellos). Tendrás que ajustar esto a tu sistema.

He aquí un posible enfoque algorítmico utilizando scikit-learn:

  • Utilizar un CountVectorizer binario (no creo que el recuento de términos en los mensajes cortos añada mucha información, ya que la mayoría de las palabras sólo aparecen una vez)
  • Empiece con un clasificador de Árbol de Decisión, tendrá un rendimiento explicable (vea aquí un ejemplo: http://ianozsvald.com/2013/07/07/overfitting-with-a-decision-tree/ )
  • Pasar a la regresión logística
  • Investiga los errores generados por los clasificadores (lee la salida exportada del Árbol de Decisión o mira los coeficientes en LogisticRegression, vuelve a trabajar con los tuits mal clasificados a través del Vectorizador para ver cómo es la representación subyacente de la Bolsa de Palabras - habrá menos tokens que los que tenías al principio en el tuit crudo - ¿hay suficientes para una clasificación?)
  • Mira mi código de ejemplo en https://github.com/ianozsvald/social_media_brand_disambiguator/blob/master/learn1.py para una versión trabajada de este enfoque

Cosas a tener en cuenta:

  • necesitas un conjunto de datos más grande, yo estoy usando 2000 tweets etiquetados (me llevó 5 horas), como mínimo quieres un conjunto equilibrado con >100 por clase (ver la nota de sobreajuste más abajo)
  • mejorar el tokenizador (muy fácil con scikit-learn) para mantener # @ en los tokens, tal vez añadir un detector de marcas en mayúsculas (como señala el usuario @user2425429)
  • considerar un clasificador no lineal (como la sugerencia de @oiez arriba) cuando las cosas se ponen más difíciles, personalmente encontré LinearSVC para hacer peor que la regresión logística (pero eso puede ser debido a la alta dimensión del espacio de características que todavía tengo que reducir)
  • un etiquetador de partes de la oración específico para tweets (en mi humilde opinión, no el de Standford, como sugiere @Neil; según mi experiencia, funciona mal con la gramática pobre de Twitter)
  • una vez que tenga muchos tokens, probablemente querrá hacer alguna reducción de la dimensionalidad (todavía no lo he probado - vea mi entrada en el blog sobre la penalización de LogisticRegression l1 l2)

Re. overfitting. En mi conjunto de datos con 2.000 elementos tengo una instantánea de 10 minutos de Twitter de los tweets de 'apple'. Alrededor de 2/3 de los tweets son para Apple Inc, 1/3 para otros usos de la manzana. Extraigo un subconjunto equilibrado (creo que unas 584 filas) de cada clase y hago una validación cruzada de 5 veces para el entrenamiento.

Dado que sólo dispongo de una ventana de tiempo de 10 minutos, tengo muchos tuits sobre el mismo tema, por lo que es probable que mi clasificador funcione tan bien en relación con las herramientas existentes: se habrá ajustado en exceso a las características de entrenamiento sin generalizar bien (mientras que las herramientas comerciales existentes funcionan peor en esta instantánea, pero de forma más fiable en un conjunto más amplio de datos). Ampliaré mi ventana de tiempo para probar esto como un trabajo posterior.

10voto

Sudipta Puntos 1769

Puedes hacer lo siguiente:

  1. Haz un dictado de palabras con su número de apariciones en tuits relacionados con la fruta y la empresa. Esto se puede lograr alimentando algunos tweets de muestra cuya inclinación conocemos.

  2. Utilizando suficientes datos anteriores, podemos averiguar la probabilidad de que una palabra aparezca en un tweet sobre apple inc.

  3. Multiplica las probabilidades individuales de las palabras para obtener la probabilidad de todo el tuit.

Un ejemplo simplificado:

p_f \= Probabilidad de tweets de frutas.

p_w_f \= Probabilidad de que una palabra aparezca en un tweet de fruta.

p_t_f \= Probabilidad combinada de que todas las palabras del tuit aparezcan en un tuit de fruta = p_w1_f * p_w2_f * ...

p_f_t \= Probabilidad de que la fruta dé un determinado tuit.

p_c, p_w_c, p_t_c, p_c_t son los valores respectivos de la empresa.

Se añade un suavizador laplaciano de valor 1 para eliminar el problema de la frecuencia cero de las palabras nuevas que no existen en nuestra base de datos.

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 Puntos 310

Si no tiene problemas para utilizar una biblioteca externa, le recomendaría scikit-learn ya que probablemente puede hacer esto mejor y más rápido que cualquier cosa que puedas codificar por ti mismo. Yo haría algo como esto:

Construye tu corpus. Hice las comprensiones de la lista para mayor claridad, pero dependiendo de cómo se almacenan sus datos puede que tenga que hacer cosas diferentes:

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)

Lo importante es que terminas con dos listas que se ven así:

([['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])

Los [1, 1, 0, 0] representan las etiquetas positivas y negativas.

Entonces, ¡crea una tubería! Pipeline es una clase de scikit-learn que facilita el encadenamiento de los pasos de procesamiento de texto para que sólo tengas que llamar a un objeto cuando entrenes/predigas:

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

Dentro de la tubería hay tres pasos de procesamiento. El CountVectorizer tokeniza las palabras, las divide, las cuenta y transforma los datos en una matriz dispersa. El TfidfTransformer es opcional, y es posible que quiera quitarlo dependiendo de la clasificación de precisión (hacer pruebas de validación cruzada y una búsqueda de cuadrícula para los mejores parámetros es un poco complicado, así que no voy a entrar en ello aquí). El LinearSVC es un algoritmo de clasificación de texto estándar.

Por último, se predice la categoría de los tweets:

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

Una vez más, el tweet tiene que estar en una lista, así que asumí que entraba en la función como una cadena.

Poner todo eso en una clase o lo que sea, y ya está. Al menos, con este ejemplo tan básico.

No he probado este código así que puede que no funcione si sólo copias y pegas, pero si quieres usar scikit-learn debería darte una idea de por dónde empezar.

EDIT: he intentado explicar los pasos con más detalle.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X