我如何建立一个模型来区分有关Apple(Inc.)的推文和关于apple(水果)的推文?

SAL*_*SAL 83 python java r classification machine-learning

有关"苹果"的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
Run Code Online (Sandbox Code Playgroud)

以下是总数据集:http://pastebin.com/eJuEb4eB

我需要构建一个分类"Apple"(Inc)的模型.从其他人.

我不是在寻找机器学习的概述,而是在寻找代码中的实际模型(Python首选).

Nei*_*gan 71

您正在寻找的是名为实体识别.这是一种统计技术(最常见的)使用条件随机场来查找命名实体,这是基于经过训练来学习有关命名实体的事情.

从本质上讲,它会查看单词的内容和上下文(回顾和转发几个单词),以估计单词是命名实体的概率.

好的软件可以查看单词的其他功能,例如它们的长度或形状(如果以"Vowel-consonant-vowel"开头,则为"Vcv")

一个非常好的图书馆(GPL)是斯坦福大学的NER

这是演示:http://nlp.stanford.edu:8080/en /

尝试一些示例文本:

我在Apple总部吃了一个苹果,我想到了Coldplay家伙的女儿Apple Martin

(3类和4类分类器使其正确)

  • 这真的很有趣.是否可以查看`english.conll.4class.distsim.crf.ser.gz`的代码?我很想知道如何构建这样的东西. (4认同)

AMA*_*nc. 36

我会这样做:

  1. 将句子拆分为单词,将其标准化,构建字典
  2. 每个单词,存储它们在关于公司的推文中出现的次数,以及它们在推文中出现的关于水果的次数 - 这些推文必须由人类确认
  3. 当一条新推文进来时,找到字典中推文中的每个单词,计算一个加权分数 - 与公司相关的频繁使用的单词会获得较高的公司分数,反之亦然; 很少使用,或与公司和水果一起使用的单词,不会有太大的分数.

  • 这是贝叶斯分类的非正式描述. (9认同)
  • 谢谢你对此的回答.您的答案与上述评论相结合确实帮助我找到了解决方案.你能帮助我磨练这个解决方案吗? (2认同)

Ian*_*ald 29

我有一个解决这个问题的半工作系统,使用scikit-learn开源,一系列描述我正在做的博客文章.我正在解决的问题是词义消歧(选择多个词义选项中的一个),这与命名实体识别不同.我的基本方法与现有解决方案相比具有一定的竞争力,并且(至关重要)可以自定义.

有一些现有的商业NER工具(OpenCalais,DBPedia Spotlight和AlchemyAPI)可能会给你一个足够好的商业结果 - 首先尝试这些!

我将其中一些用于客户项目(我在伦敦使用NLP/ML咨询),但我对他们的召回(精确度和召回率)不满意.基本上它们可以是精确的(当他们说"这是苹果公司"他们通常是正确的),但回忆率低(他们很少说"这是苹果公司",即使对人类而言,推文显然是关于苹果公司).我认为构建针对推文定制的开源版本是一项智力上有趣的练习.这是当前的代码:https: //github.com/ianozsvald/social_media_brand_disambiguator

我会注意到 - 我不是试图用这种方法来解决广义的词义消歧问题,只是当你已经有了自己的名字时,就会消除品牌消歧(公司,人等等).这就是为什么我相信这种直截了当的方法会奏效.

我在六个星期前开始使用scikit-learn在Python 2.7中编写.它使用了一种非常基本的方法.我使用二进制计数矢量化器(我只计算一个单词是否出现,而不是多少次),使用1-3 n-gram进行矢量化  .我没有使用TF-IDF进行扩展(当你有一个可变的文档长度时,TF-IDF是好的;对我来说,推文只有一两句话,我的测试结果没有显示TF-IDF的改进).

我使用基本的标记器,这是非常基本但非常有用.它忽略了@#(因此你丢失了一些上下文),当然也没有扩展URL.然后我使用逻辑回归进行训练,似乎这个问题在某种程度上是线性可分的(对于另一个类,很多术语都不存在).目前我正在避免任何堵塞/清洁(我正在尝试最简单的可能的事情).

代码有一个完整的自述文件,您应该能够相对容易地提取您的推文,然后按照我的建议进行测试.

这适用于Apple,因为人们不会吃或喝Apple电脑,也不会打字或玩水果,所以这些单词很容易分成一个类别或另一个类别.当考虑像电视节目的#definance这样的情况时,这种情况可能不成立(人们也在阿拉伯之春,板球比赛,考试修订和音乐乐队中使用#definance).这里可能需要更聪明的方法.

我有一系列描述这个项目的博客文章,包括我在BrightonPython用户组上发表的一小时演示文稿(在DataScienceLondon上为140人制作了一个较短的演示文稿).

如果你使用像LogisticRegression这样的东西(你得到每个分类的概率),你只能选择自信的分类,这样你就可以通过交易来避免高度精确的召回(所以你得到的结果是正确的,但结果却少了).您必须将其调整到您的系统.

这是使用scikit-learn的一种可能的算法方法:

  • 使用Binary CountVectorizer(我不认为短消息中的术语计数会增加很多信息,因为大多数单词只出现一次)
  • 从决策树分类器开始.它具有可解释的性能(有关示例,请参阅使用决策树过度拟合).
  • 转向逻辑回归
  • 调查分类器生成的错误(阅读DecisionTree的导出输出或查看LogisticRegression中的系数,通过Vectorizer处理错误分类的推文,看看基本的Bag of Words表示是什么样的 - 那里的令牌会比你是从原始推文开始的 - 有足够的分类吗?)
  • 请查看https://github.com/ianozsvald/social_media_brand_disambiguator/blob/master/learn1.py中的示例代码,了解此方法的工作版本

需要考虑的事项:

  • 您需要更大的数据集.我正在使用2000个带标签的推文(花了我五个小时),并且至少你想要一个平衡的套装,每个等级> 100(参见下面的过度拟合说明)
  • 改进令牌器(使用scikit-learn非常容易)将#@保留在令牌中,并且可以添加大写品牌检测器(如用户@ user2425429备注)
  • 当事情变得更难时,考虑一个非线性分类器(如上面的@ oiez建议).就个人而言,我发现LinearSVC比逻辑回归更糟糕(但这可能是由于我尚未减少的高维特征空间).
  • 一个特定于推文的词性标记器(根据我的拙见,不是像@Neil所说的那样Standford的 - 根据我的经验,它在糟糕的Twitter语法上表现不佳)
  • 一旦你有很多令牌,你可能想要做一些维数减少(我还没试过这个 - 看看关于LogisticRegression l1 l2惩罚的博客文章)

回覆.过度拟合.在我的2000个项目的数据集中,我有一个来自Twitter的"苹果"推文的10分钟快照.约有2/3的推文适用于Apple Inc,1/3适用于其他苹果用途.我拿出每个类的平衡子集(我认为大约584行)并进行五重交叉验证以进行训练.

由于我只有一个10分钟的时间窗口,我有很多关于相同主题的推文,这可能是我的分类器相对于现有工具做得很好的原因 - 它会过度适应训练功能而不能很好地推广(而现有的商业广告)这个工具在这个snapshop上的表现更差,但在更广泛的数据集中更可靠.我将扩展我的时间窗口,将其作为后续工作进行测试.


Sud*_*pta 11

您可以执行以下操作:

  1. 在水果和公司相关的推文中制作包含其发生次数的单词.这可以通过提供一些我们知道的倾向的样本推文来实现.

  2. 使用足够的先前数据,我们可以找到关于apple inc的推文中出现单词的概率.

  3. 将单词的单个概率相乘以获得整个推文的概率.

一个简化的例子:

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的laplacian平滑器,以消除我们数据库中没有的新单词的零频率问题.

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"
Run Code Online (Sandbox Code Playgroud)


oie*_*iez 9

如果您在使用外部库时没有问题,我建议使用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)
Run Code Online (Sandbox Code Playgroud)

重要的是你最终得到两个看起来像这样的列表:

([['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])
Run Code Online (Sandbox Code Playgroud)

[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
Run Code Online (Sandbox Code Playgroud)

管道内部有三个处理步骤.CountVectorizer对单词进行标记,对其进行分割,对其进行计数,并将数据转换为稀疏矩阵.TfidfTransformer是可选的,您可能希望根据准确度等级将其删除(进行交叉验证测试和最佳参数的网格搜索有点涉及,所以我不会在这里介绍它).LinearSVC是标准文本分类算法.

最后,您预测推文的类别:

def predict(pipe, tweet):
    prediction = pipe.predict([tweet])
    return prediction
Run Code Online (Sandbox Code Playgroud)

同样,推文需要在列表中,所以我假设它是以字符串形式输入函数.

将所有这些放入课堂或其他任何事情,你就完成了.至少,有了这个非常基本的例子.

我没有测试这段代码,所以如果你只是复制粘贴它可能不起作用,但如果你想使用scikit-learn它应该让你知道从哪里开始.

编辑:尝试更详细地解释步骤.


Pau*_*ubs 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()
Run Code Online (Sandbox Code Playgroud)

  • 使用tweet_features函数提取功能.它基本上从推文中删除了网址,然后创建了一个功能字典,其条目读取的内容类似于'hasBigram(foo,bar)'= True. (2认同)
  • 究竟.使用bayes分类器时,您还可以通过调用"show_most_informative_features()"来获得最有用的功能. (2认同)

SAL*_*SAL 5

感谢您迄今为止的评论.这是我用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;
}
?>
Run Code Online (Sandbox Code Playgroud)

以上输出:

           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.
Run Code Online (Sandbox Code Playgroud)