Как я могу построить модель, чтобы отличать твиты от Apple (Inc.) от твитов о яблоке (фрукты)?

См. ниже 50 твитов о "яблоке". У меня есть рука, обозначенная позитивными совпадениями об Apple Inc. Они помечены как 1 ниже.

Вот пара строк:

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

Вот полный набор данных: http://pastebin.com/eJuEb4eB

Мне нужно создать модель, которая классифицирует "Apple" (Inc). от остальных.

Я не ищу общий обзор машинного обучения, скорее я ищу фактическую модель в коде (Python предпочитаемый).

Ответ 1

Я бы сделал это следующим образом:

  • Разделите предложение на слова, нормализуйте их, создайте словарь
  • С каждым словом храните, сколько раз они происходили в твитах о компании, и сколько раз они появлялись в твитах о плоде - эти твиты должны быть подтверждены человеческим
  • Когда появляется новый твит, найдите каждое слово в твите в словаре, вычислите взвешенную оценку - слова, которые часто используются в отношении компании, получат высокую оценку компании, и наоборот; слова, используемые редко или используемые как с компанией, так и с фруктами, не будут иметь большого количества баллов.

Ответ 2

То, что вы ищете, называется Именованное распознавание сущностей. Это статистический метод, который (чаще всего) использует условные случайные поля для поиска названных объектов на основе обучения, чтобы узнать о названных объектах.

По существу, он рассматривает контент и контекст слова (оглядываясь назад и перебрасывая несколько слов), чтобы оценить вероятность того, что это слово является именованным объектом.

Хорошее программное обеспечение может смотреть на другие функции слов, такие как их длина или форма (например, "Vcv", если она начинается с "гласных-согласных-гласных" )

Очень хорошая библиотека (GPL) Stanford NER

Здесь демо: http://nlp.stanford.edu:8080/ner/

Пример текста примера:

Я ел яблоко в штаб-квартире Apple, и я думал о Apple Martin, дочь парня Coldplay

(классификаторы класса 3class и 4class верны)

Ответ 3

У меня есть полуработающая система, которая решает эту проблему, открывая исходники, используя scikit-learn, с серией сообщений в блогах, описывающих, что я делаю. Проблема, которую я решаю, - это однозначность смысла слова (выбор одного из опций word sense), что не то же самое, что именованное распознавание сущности, Мой базовый подход несколько конкурирует с существующими решениями и (принципиально) настраивается.

Есть некоторые коммерческие инструменты NER (OpenCalais, DBPedia Spotlight и AlchemyAPI), которые могут дать вам достаточно хороший коммерческий результат - попробуйте эти первые!

Я использовал некоторые из них для клиентского проекта (я консультируюсь с использованием NLP/ML в Лондоне), но я не был доволен их отзывом (точность и вспомните). В основном они могут быть точными (когда они говорят: "Это Apple Inc", они, как правило, правильны), но с низким уровнем отзыва (они редко говорят "Это Apple Inc", хотя для человека твиты, очевидно, касаются Apple Inc). Я решил, что это будет интеллектуально интересное упражнение для создания версии с открытым исходным кодом, адаптированной к твитам. Здесь текущий код: https://github.com/ianozsvald/social_media_brand_disambiguator

Я хочу отметить, что я не пытаюсь решить эту проблему с обобщенным смысловым смыслом с этим подходом, только значения бренда (компании, люди и т.д.), когда у вас уже есть свое имя, Поэтому я считаю, что этот простой подход будет работать.

Я начал это шесть недель назад, и он написан на Python 2.7, используя scikit-learn. Он использует очень простой подход. Я вектурирую использование двоичного счетчика векторов (я считаю только, появляется ли слово, а не сколько раз) с 1-3 n-gram. Я не масштабируюсь с TF-IDF (TF-IDF хорош, когда у вас есть переменная длина документа, для меня твиты - это только одно или два предложения, и мои результаты тестирования не показали улучшения с TF-IDF).

Я использую базовый токенизатор, который является очень простым, но на удивление полезным. Он игнорирует @# (поэтому вы теряете некоторый контекст) и, конечно же, не расширяет URL. Затем я тренируюсь с использованием логистической регрессии, и кажется, что эта проблема несколько линейно отделима (много терминов для одного класса не существует для Другие). В настоящее время я избегаю каких-либо операций по очистке/очистке (я пытаюсь использовать Простейшую возможную вещь, которая может работать).

В коде есть полный README, и вы должны легко глотать ваши твиты, а затем следовать моим рекомендациям для тестирования.

Это работает для Apple, поскольку люди не едят и не пьют компьютеры Apple, и мы не печатаем и не играем с фруктами, поэтому слова легко разделяются на одну категорию или другую. Это условие может не выполняться при рассмотрении чего-то вроде #definance для телешоу (где люди также используют #definance в отношении арабского Spring, матчей по крикету, пересмотра экзамена и музыкальной группы). Здесь могут потребоваться более разумные подходы.

У меня серия сообщений в блоге, описывающих этот проект, включая одночасовую презентацию, которую я дал в группе пользователей BrightonPython (которая превратилась в более короткая презентация для 140 человек в DataScienceLondon).

Если вы используете что-то вроде LogisticRegression (где вы получаете вероятность для каждой классификации), вы можете выбрать только уверенную классификацию, и таким образом вы можете заставить высокую точность торговать против отзыва (так что вы получаете правильные результаты, но меньшее количество из них). Вам нужно будет настроить его на свою систему.

Здесь возможен алгоритмический подход, использующий scikit-learn:

  • Использовать двоичный CountVectorizer (я не думаю, что термин-счет в коротких сообщениях содержит много информации, поскольку большинство слов происходит только один раз)
  • Начните с классификатора дерева решений. Он будет иметь объясняемую производительность (см. Переопределение с деревом решений).
  • Перейти к логистической регрессии
  • Исследуйте ошибки, сгенерированные классификаторами (прочитайте экспортированный вывод DecisionTree или посмотрите на коэффициенты в LogisticRegression, обработайте неправильно классифицированные твиты обратно через Vectorizer, чтобы увидеть, как выглядит представление, лежащее в основе Bag of Words, - будет меньше там же, чем вы начали с чистого листа, - достаточно ли для классификации?)
  • Посмотрите на мой пример кода в https://github.com/ianozsvald/social_media_brand_disambiguator/blob/master/learn1.pyдля отработанной версии этого подхода

Что нужно учитывать:

  • Вам нужен больший набор данных. Я использую 2000 помеченных твитов (мне потребовалось пять часов), и, как минимум, вам нужен сбалансированный набор s > 100 на класс (см. Примечание переобучения ниже).
  • Улучшите tokeniser (очень легко с scikit-learn), чтобы сохранить # @в токенах и, возможно, добавить детектор с заглавной буквы (как пользователь @user2425429)
  • Рассмотрим нелинейный классификатор (например, предложение @oiez выше), когда все становится сложнее. Лично я нашел LinearSVC хуже, чем логистическая регрессия (но это может быть связано с пространством с большими размерами, которое я еще не уменьшил).
  • Твиттерная часть речевого теггера (по моему скромному мнению, не Standford, как @Neil предлагает - он плохо работает на плохой грамматике Twitter в моем опыте)
  • Как только у вас будет много токенов, вы, вероятно, захотите уменьшить размерность (я еще не пробовал это делать) - см. мое сообщение в блоге о нарушении правил LogisticRegression l1 l2)

Re. переобучения. В моем наборе данных с 2000 элементами у меня есть 10-минутный снимок из Твиттера из "яблочных" твитов. Около 2/3 твитов для Apple Inc, 1/3 для других приложений Apple. Я вытаскиваю сбалансированное подмножество (около 584 строк, я думаю) каждого класса и выполняю пятикратную перекрестную проверку для обучения.

Так как у меня есть только 10-минутное окно времени, у меня есть много твитов по одной и той же теме, и, вероятно, поэтому мой классификатор так хорошо по отношению к существующим инструментам - он будет обладать навыками обучения без хорошего обобщения (тогда как существующие коммерческие инструменты хуже работают в этой оснастке, но более надежно работают с более широким набором данных). Я буду расширять свое окно времени, чтобы проверить это как последующую часть работы.

Ответ 4

Вы можете сделать следующее:

  • Сделайте диктовку слов, содержащих их количество случаев в твитах, связанных с фруктами и компаниями. Этого можно добиться, подав ему несколько тестовых твитов, о которых мы знаем.

  • Используя достаточное количество предыдущих данных, мы можем узнать вероятность появления слова в твите о яблоке inc.

  • Умножьте индивидуальные вероятности слов, чтобы получить вероятность всего твита.

Упрощенный пример:

p_f= Вероятность плодовых твитов.

p_w_f= Вероятность слова, происходящего в твиттере фруктов.

p_t_f= Комбинированная вероятность всех слов в твите, связанных с твитом фруктов     = p_w1_f * p_w2_f *...

p_f_t= Вероятность фруктов с учетом конкретного твита.

p_c, p_w_c, p_t_c, p_c_t являются соответствующими значениями для компании.

Добавлена ​​лапласианская плавность значения 1 для устранения проблемы нулевой частоты новых слов, которых нет в нашей базе данных.

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"

Ответ 5

Если у вас нет проблемы с использованием внешней библиотеки, я бы рекомендовал scikit-learn, так как это, вероятно, может сделать это лучше и быстрее, чем все, что вы могли бы написать самим. Я бы просто сделал что-то вроде этого:

Создайте свой корпус. Я сделал список понятий для ясности, но в зависимости от того, как хранятся ваши данные, вам может потребоваться сделать разные вещи:

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)

Важно то, что у вас есть два списка, которые выглядят так:

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

[1, 1, 0, 0] представляют положительные и отрицательные метки.

Затем вы создаете трубопровод! Pipeline - это класс scikit-learn, который упрощает цепочку шагов обработки текста, поэтому вам нужно только вызвать один объект при обучении/прогнозировании:

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

Внутри трубопровода есть три этапа обработки. CountVectorizer токенизирует слова, разбивает их, подсчитывает и преобразует данные в разреженную матрицу. TfidfTransformer не является обязательным, и вы можете удалить его в зависимости от рейтинга точности (выполнение кросс-проверочных тестов и поиск сетки для наилучших параметров немного связаны, поэтому я не буду вдаваться в него здесь). LinearSVC является стандартным алгоритмом классификации текста.

Наконец, вы прогнозируете категорию твитов:

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

Опять же, твит должен быть в списке, поэтому я предположил, что он вводит функцию в виде строки.

Поместите все это в класс или что-то еще, и все готово. По крайней мере, с этим самым основным примером.

Я не тестировал этот код, чтобы он не работал, если вы просто копируете-вставляете, но если вы хотите использовать scikit-learn, он должен дать вам представление о том, с чего начать.

EDIT: попытался объяснить шаги более подробно.

Ответ 6

Использование дерева решений, похоже, очень хорошо работает для этой проблемы. По крайней мере, он дает более высокую точность, чем наивный классификатор заливов с моими избранными функциями.

Если вы хотите поиграть с некоторыми возможностями, вы можете использовать следующий код, который требует установки nltk. Книга nltk также свободно доступна в Интернете, поэтому вы можете немного прочитать о том, как все это работает: http://nltk.googlecode.com/svn/trunk/doc/book/ch06.html

#coding: utf-8
import nltk
import random
import re

def get_split_sets():
    structured_dataset = get_dataset()
    train_set = set(random.sample(structured_dataset, int(len(structured_dataset) * 0.7)))
    test_set = [x for x in structured_dataset if x not in train_set]

    train_set = [(tweet_features(x[1]), x[0]) for x in train_set]
    test_set = [(tweet_features(x[1]), x[0]) for x in test_set]
    return (train_set, test_set)

def check_accurracy(times=5):
    s = 0
    for _ in xrange(times):
        train_set, test_set = get_split_sets()
        c = nltk.classify.DecisionTreeClassifier.train(train_set)
        # Uncomment to use a naive bayes classifier instead
        #c = nltk.classify.NaiveBayesClassifier.train(train_set)
        s += nltk.classify.accuracy(c, test_set)

    return s / times


def remove_urls(tweet):
    tweet = re.sub(r'http:\/\/[^ ]+', "", tweet)
    tweet = re.sub(r'pic.twitter.com/[^ ]+', "", tweet)
    return tweet

def tweet_features(tweet):
    words = [x for x in nltk.tokenize.wordpunct_tokenize(remove_urls(tweet.lower())) if x.isalpha()]
    features = dict()
    for bigram in nltk.bigrams(words):
        features["hasBigram(%s)" % ",".join(bigram)] = True
    for trigram in nltk.trigrams(words):
        features["hasTrigram(%s)" % ",".join(trigram)] = True  
    return features

def get_dataset():
    dataset = """copy dataset in here
"""
    structured_dataset = [('fruit' if x[0] == '0' else 'company', x[2:]) for x in dataset.splitlines()]
    return structured_dataset

if __name__ == '__main__':
    print check_accurracy()

Ответ 7

Спасибо за комментарии до сих пор. Вот рабочее решение Я подготовил PHP. Мне все равно хотелось бы услышать от других более алгоритмический подход к этому же решению.

<?php

// Confusion Matrix Init
$tp = 0;
$fp = 0;
$fn = 0;
$tn = 0;
$arrFP = array();
$arrFN = array();

// Load All Tweets to string
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://pastebin.com/raw.php?i=m6pP8ctM');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$strCorpus = curl_exec($ch);
curl_close($ch);

// Load Tweets as Array
$arrCorpus = explode("\n", $strCorpus);
foreach ($arrCorpus as $k => $v) {
    // init
    $blnActualClass = substr($v,0,1);
    $strTweet = trim(substr($v,2));

    // Score Tweet
    $intScore = score($strTweet);

    // Build Confusion Matrix and Log False Positives & Negatives for Review
    if ($intScore > 0) {
        if ($blnActualClass == 1) {
            // True Positive
            $tp++;
        } else {
            // False Positive
            $fp++;
            $arrFP[] = $strTweet;
        }
    } else {
        if ($blnActualClass == 1) {
            // False Negative
            $fn++;
            $arrFN[] = $strTweet;
        } else {
            // True Negative
            $tn++;
        }
    }
}

// Confusion Matrix and Logging
echo "
           Predicted
            1     0
Actual 1   $tp     $fp
Actual 0    $fn    $tn

";

if (count($arrFP) > 0) {
    echo "\n\nFalse Positives\n";
    foreach ($arrFP as $strTweet) {
        echo "$strTweet\n";
    }
}

if (count($arrFN) > 0) {
    echo "\n\nFalse Negatives\n";
    foreach ($arrFN as $strTweet) {
        echo "$strTweet\n";
    }
}

function LoadDictionaryArray() {
    $strDictionary = <<<EOD
10|iTunes
10|ios 7
10|ios7
10|iPhone
10|apple inc
10|apple corp
10|apple.com
10|MacBook
10|desk top
10|desktop
1|config
1|facebook
1|snapchat
1|intel
1|investor
1|news
1|labs
1|gadget
1|apple store
1|microsoft
1|android
1|bonds
1|Corp.tax
1|macs
-1|pie
-1|clientes
-1|green apple
-1|banana
-10|apple pie
EOD;

    $arrDictionary = explode("\n", $strDictionary);
    foreach ($arrDictionary as $k => $v) {
        $arr = explode('|', $v);
        $arrDictionary[$k] = array('value' => $arr[0], 'term' => strtolower(trim($arr[1])));
    }
    return $arrDictionary;
}

function score($str) {
    $str = strtolower($str);
    $intScore = 0;
    foreach (LoadDictionaryArray() as $arrDictionaryItem) {
        if (strpos($str,$arrDictionaryItem['term']) !== false) {
            $intScore += $arrDictionaryItem['value'];
        }
    }
    return $intScore;
}
?>

Вышеуказанные выходы:

           Predicted
            1     0
Actual 1   31     1
Actual 0    1    17


False Positives
1|Royals apple #ASGame @mlb @ News Corp Building http://instagram.com/p/bBzzgMrrIV/


False Negatives
-1|RT @MaxFreixenet: Apple no tiene clientes. Tiene FANS// error.... PAGAS por productos y apps, ergo: ERES CLIENTE.

Ответ 8

Во всех примерах, которые вы указали, Apple (inc) называлась как A pple или apple inc, поэтому возможным способом может быть поиск:

  • капитал "А" в Apple

  • a "inc" после apple

  • слова/фразы типа "ОС", "операционная система", "Mac", "iPhone",...

  • или их комбинация

Ответ 9

Чтобы упростить ответы на основе условных случайных полей немного... контекст здесь огромный. Вы захотите выбрать в этих твитах, которые ясно показывают Apple, что компания против яблока - это плод. Позвольте мне изложить список функций, которые могут быть полезны для вас. Для получения дополнительной информации найдите фрагмент именной фразы и что-то, что называется этикетками BIO. См. (http://www.cis.upenn.edu/~pereira/papers/crf.pdf)

Окружающие слова: создайте вектор функций для предыдущего слова и следующего слова, или если вы хотите больше возможностей, возможно, предыдущие 2 и следующие 2 слова. Вы не хотите, чтобы слишком много слов в модели, или оно не будет соответствовать данным очень хорошо. В обработке естественного языка вы захотите сохранить это как можно более общее.

Другие функции для получения от окружающих слов включают следующее:

Является ли первый символ капиталом

Является ли последний символ в слове периодом

Часть речи слова (см. часть тегов речей)

Текст самого слова

Я не советую этого, но даю больше примеров функций специально для Apple:

WordIs (Apple)

NextWordIs (Inc.)

Вы понимаете. Подумайте об Именованном Распознавании Сущности, описывая последовательность, а затем используя некоторую математику, чтобы сообщить компьютеру, как вычислить это.

Имейте в виду, что обработка естественного языка - это система на основе конвейера. Как правило, вы нарушаете предложения в предложениях, переходите к токенизации, затем выполняете часть речевых тегов или даже анализ синтаксиса.

Это все, чтобы получить список функций, которые вы можете использовать в своей модели, чтобы определить, что вы ищете.

Ответ 10

Там действительно хорошая библиотека для обработки текста естественного языка в Python называется nltk. Вы должны взглянуть на него.

Одна стратегия, которую вы могли бы попробовать, - посмотреть на n-граммы (группы слов) со словом "яблоко" в них. Некоторые слова, скорее всего, будут использоваться рядом с "яблоком", когда речь заходит о фруктах, другие - о компании, и вы можете использовать их для классификации твитов.

Ответ 11

Используйте LibShortText. Эта утилита Python уже настроена для работы для коротких задач категоризации текста, и она работает хорошо. Максимум, который вам нужно сделать, - написать цикл, чтобы выбрать наилучшую комбинацию флагов. Я использовал его, чтобы выполнять классическую классификацию речевого акта в электронных письмах, и результаты были точными до 95-97% (в течение 5-кратной перекрестной проверки!).

И это происходит от создателей LIBSVM и LIBLINEAR, где поддержка векторной машины (SVM) используется в sklearn и cran, поэтому вы можете быть уверены, что их реализация не является ошибкой.

Ответ 12

Сделайте AI-фильтр, чтобы отличить Apple Inc (компанию) от яблока (фрукты). Поскольку это твиты, определите свой набор тренировок с вектором из 140 полей, каждое поле которого будет символом, записанным в твите в позиции X (от 0 до 139). Если твит является короче, просто укажите значение для пустого.

Затем создайте тренировочный набор, достаточно большой, чтобы получить хорошую точность (субъективно на ваш вкус). Назначьте значение результата каждому твиту, твиту Apple Inc получите 1 (true), а твиту яблока (фрукты) - 0. Это будет случай контролируемое обучение в логистической регрессии.

Это машинное обучение, как правило, легче кодировать и работать лучше. Он должен учиться на множестве, который вы ему даете, и он не жестко закодирован.

Я не знаю Python, поэтому я не могу написать код для него, но если у вас будет больше времени для логика машинного обучения и теория, вы можете захотеть посмотреть класс, за которым я следую.

Попробуйте Coursera курс машинное обучение по Andrew Ng. Вы узнаете машинное обучение на MATLAB или Octave, но как только вы получите основы, вы сможете писать машинное обучение на любом языке, если понимаете простую математику (просто в логистической регрессии).

То есть, получение кода от кого-то не позволит вам понять, что происходит в коде машинного обучения. Вы можете потратить пару часов на эту тему, чтобы посмотреть, что происходит на самом деле.

Ответ 13

Я бы рекомендовал избегать ответов, предполагающих распознавание сущностей. Потому что эта задача - сначала классификация текста, а затем распознавание объекта (вы можете сделать это вообще без распознавания объекта).

Я думаю, что самый быстрый путь к результатам будет spacy + вундеркинд. Spacy хорошо продумал модель для английского языка, поэтому вам не нужно создавать свою собственную. В то время как вундеркинд позволяет быстро создавать обучающие наборы данных и точно настраивать пространственную модель для ваших нужд.

Если у вас достаточно образцов, вы можете получить приличную модель за 1 день.