我如何build立一个模型来区分关于苹果(公司)的推文与关于苹果(水果)的推文呢?
请参阅以下50条关于“苹果”的推文。 我手上标有关于苹果公司的正面匹配,他们在下面标记为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
我需要build立一个分类“苹果”(公司)的模型。 从其余。
我不是在寻找机器学习的一般概述,而是在代码中寻找实际的模型(首选Python )。
我会做如下:
- 将句子拆分成单词,规范化,build立一个词典
- 每一个字都会存储多less次关于公司的推特,以及他们在推特上看到了多less次关于这些水果 – 这些推文必须经过一个人的确认
- 当一个新的推特进来时,find字典中的鸣叫的每一个字,计算一个加权分数 – 经常使用的关于公司的词会得到高的公司得分,反之亦然; 很less使用的词语,或与公司和水果一起使用的词汇,都不会有太多的分数。
你正在寻找什么叫做命名实体识别 。 这是一种统计技术(最常用)使用条件随机场来find命名实体,基于已被训练学习有关命名实体的事情。
从本质上讲,它会查看单词的内容和上下文 (回顾并转发一些单词),以估计该单词是命名实体的概率。
好的软件可以查看单词的其他特征,如长度或形状(如“Vcv”,如果以“元音 – 辅音 – 元音”开头)
一个非常好的图书馆(GPL)是斯坦福大学的NER
这是演示: http : //nlp.stanford.edu : 8080/ner/
一些示例文本可以尝试:
我在苹果总部吃了一个苹果,我想到了Coldplay家伙的女儿苹果马丁
(3类和4类分类器是正确的)
我有一个半工作系统可以解决这个问题,使用scikit-learn开源,并且有一系列的博客文章描述了我在做什么。 我正在处理的问题是词义消歧(select多个词义选项中的一个),这与名称实体识别不同。 我的基本方法与现有解决scheme有些竞争,并且(关键)是可定制的。
有一些现有的商业NER工具(OpenCalais,DBPedia Spotlight和AlchemyAPI)可能会给你一个足够好的商业结果 – 首先尝试这些工具!
我使用了其中一些客户端项目(我在伦敦使用NLP / ML进行了咨询),但是我不满意他们的召回( 精确度和召回率 )。 基本上,他们可以是准确的(当他们说“这是苹果公司”,他们通常是正确的),但回忆率低(他们很less说“这是苹果公司”,即使对人来说,鸣叫显然是关于苹果公司)。 我认为这将是一个智力上有趣的演习,build立一个开源版本的tweets定制。 以下是当前的代码: https : //github.com/ianozsvald/social_media_brand_disambiguator
我会注意到 – 我没有试图用这种方法来解决广义的词义消歧问题,只是当你已经有了他们的名字时,消除了品牌歧义(公司,人员等)。 这就是为什么我相信这个直接的方法将起作用。
我六个星期前就开始了这个工作,并且使用scikit-learn来编写Python 2.7。 它使用了一个非常基本的方法。 我使用一个二进制数向量化vector化(我只计算一个单词是否出现,而不是多less次)与1-3 n-gram 。 我没有用TF-IDF进行缩放(TF-IDF对于可变文档长度是很好的;对于我来说,这些推文只有一两句话,而我的testing结果并没有显示TF-IDF的改进)。
我使用基本的分词器这是非常基本的,但令人惊讶的有用的。 它忽略@#(所以你失去了一些上下文),当然不会扩展一个URL。 然后,我使用逻辑回归进行训练,似乎这个问题有点线性可分(一个类的许多术语不存在)。 目前我正在避免任何干扰/清理(我正在尝试可能的最简单的可能的事情)。
代码有一个完整的README,你应该能够相对容易地接收你的推文,然后按照我的build议进行testing。
这对于苹果来说是有效的,因为人们不吃或不喝苹果电脑,也不打字或玩水果,所以这些词很容易分成一类或另一类。 当考虑像电视节目的#definance这样的情况(人们也使用#definance相对于阿拉伯之春,板球比赛,考试版本和音乐乐队)时,这种情况可能不会成立。 这里可能需要更聪明的方法。
我有一系列的博客文章描述了这个项目,其中包括我在BrightonPython用户组(一个在DataScienceLondon有140个人的简短演讲)的一个小时的演讲。
如果你使用LogisticRegression(你可以得到每个分类的概率),你可以只select自信的分类,这样你就可以通过交易来回想召回的高精度(所以你得到正确的结果,但更less的)。 你必须调整到你的系统。
下面是使用scikit-learn的一个可能的algorithm:
- 使用二进制CountVectorizer(我不认为在短消息中的术语计数添加很多信息,因为大多数单词只出现一次)
- 从决策树分类器开始。 它将具有可解释的性能(请参阅以一个决策树为例进行过度configuration )。
- 转到逻辑回归
- 调查分类器产生的错误(读取DecisionTree的输出结果或查看LogisticRegression中的系数,通过Vectorizer处理错误的推文,看看底层Bag of Words表示看起来是什么样子 – 那里的令牌会less于你开始在原始的鸣叫 – 有足够的分类?)
- 看看我的示例代码在https://github.com/ianozsvald/social_media_brand_disambiguator/blob/master/learn1.py这种方法的工作版本;
需要考虑的事项:
- 你需要一个更大的数据集。 我使用了2000个标记的推文(花了我五个小时),至less你想要一个平衡的设置,每个类别超过100(见下面的过度填写的注释)
- 改进记号器(用scikit-learn很容易)将#@保存在令牌中,并且可能添加一个大写的品牌检测器(如user @ user2425429所注)
- 当事情变得更加困难时,考虑一个非线性分类器(如上面的@ oiez的build议)。 就我个人而言,我发现LinearSVC比逻辑回归更糟糕(但这可能是由于我尚未减less的高维特征空间)。
- 一个微博特定的部分言语标签(在我的愚见,不是斯坦福的@尼尔build议 – 它performance不佳,在我的经验,可怜的微博语法)
- 一旦你有很多的令牌,你可能会想要做一些维度减less(我还没有尝试过 – 看到我的博客文章LogisticRegression l1 l2惩罚)
回覆。 过度拟合。 在2000个项目的数据集中,我有一个来自Twitter的“苹果”推文的10分钟快照。 大约三分之二的推特是苹果公司的,三分之一是其他苹果的。 我抽出一个平衡的子集(约584行,我认为)每个类,并进行了五倍交叉validation的培训。
由于我只有10分钟的时间窗口,因此我有许多关于同一主题的推文,这可能就是为什么我的分类器相对于现有的工具做得如此出色 – 它会过度适应培训function而没有一般化(而现有的商业工具在这个snapshop上performance更差,但在更广泛的数据集中更可靠)。 我会扩大我的时间窗口来testing这个工作。
您可以执行以下操作:
-
做一个含有水果和公司相关推文的词语的字典。 这可以通过提供一些我们知道倾向的样本推文来实现。
-
使用足够的以前的数据,我们可以找出在苹果公司的鸣叫一个字发生的概率。
-
将单词的概率乘以得到整个推文的概率。
一个简单的例子:
p_f =水果推文的可能性。
p_w_f =水果推文中出现的单词的概率。
p_t_f = tweet中发生的所有单词的组合概率tweet = 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"
如果你没有使用外部库的问题,我build议使用scikit-learn,因为它可以比你自己编写的代码更好,更快。 我只是做这样的事情:
build立你的语料库。 为了清晰起见,我做了列表parsing,但取决于数据的存储方式,您可能需要做不同的事情:
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]表示正负标签。
然后,你创build一个pipe道! Pipeline是一个scikit学习类,可以很容易地将文本处理步骤链接在一起,因此在训练/预测时只需要调用一个对象:
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
在pipe道内部有三个处理步骤。 CountVectorizer对单词进行标记,将它们分开,对它们进行计数,然后将数据转换为稀疏matrix。 TfidfTransformer是可选的,你可能想要删除它取决于准确率(交叉validationtesting和网格search最好的参数有点涉及,所以我不会在这里进入)。 LinearSVC是一种标准的文本分类algorithm。
最后,你预测推文的类别:
def predict(pipe, tweet): prediction = pipe.predict([tweet]) return prediction
同样,鸣叫需要在一个列表中,所以我认为它是作为一个string进入函数。
把所有的人放在一个class或其他什么,你已经完成了。 至less,用这个非常基本的例子。
我没有testing这个代码,所以如果你只是复制粘贴,它可能不起作用,但如果你想使用scikit学习它应该让你知道从哪里开始。
编辑:试图解释更详细的步骤。
对于这个问题,使用决策树似乎工作得很好。 至less它比我自己select的特性的朴素贝叶斯分类器产生更高的精度。
如果你想玩一些可能性,你可以使用下面的代码,这需要安装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()
感谢您的评论迄今。 这是我用PHP编写的一个工作解决scheme 。 我仍然有兴趣从别人那里听到更多的algorithm来解决这个问题。
<?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.
在你给出的所有例子中,Apple(inc)被称为A pple或apple inc ,所以可能的方法是search:
-
苹果公司的资本“A”
-
苹果之后的“inc”
-
“OS”,“操作系统”,“Mac”,“iPhone”等单词/短语
-
或者它们的组合
为了简化基于条件随机场的答案,这里有一点…上下文是巨大的。 你会想挑出那些清楚地显示苹果公司与苹果公司的这些推文。 让我列出一些可能对您有用的function列表。 有关更多信息,请查找名词短语“chunking”,以及名为“BIO标签”的内容。 参见( http://www.cis.upenn.edu/~pereira/papers/crf.pdf )
周围的单词:为前一个单词和下一个单词构build一个特征向量,或者如果您想要更多的特征,可能是前两个单词和后两个单词。 你不需要模型中太多的单词,或者它不能很好地匹配数据。 在自然语言处理中,你将要尽可能保持一般。
从周围的单词得到的其他function包括以下内容:
第一个字是否是首都
单词中的最后一个字符是否是句号
词的词性(查词法标注)
这个词的文本本身
我不build议这样做,但要提供更多专门针对Apple的function示例:
WordIs(苹果)
NextWordIs(INC。)
你明白了。 将名称实体识别看作描述一个序列,然后用一些math来告诉计算机如何计算这个识别。
请记住,自然语言处理是一个基于pipe道的系统。 通常情况下,你把事情分解成句子,移动到标记,然后做词性标记或甚至依赖分析。
这就是为您提供您可以在您的模型中使用的function列表,以确定您要查找的内容。
有一个非常好的库,用于处理Python中的自然语言文本,称为nltk
。 你应该看看它。
你可以尝试的一个策略是看看n-gram(单词组),其中包含单词“apple”。 在谈到水果的时候,有些词更可能被用在“苹果”旁边,另外一些词在谈到公司时可能会被用在“苹果”旁边,而你可以用这些词来分类推文。
使用LibShortText 。 这个Python实用程序已经被调整用于短文本分类任务,并且运行良好。 你需要做的最大的工作就是编写一个循环来select最好的标志组合。 我用它在电子邮件中进行有监督的言语行为分类,其结果高达95-97%准确(在5倍交叉validation期间!)。
它来自LIBSVM和LIBLINEAR的制造商,其支持向量机 (SVM)实现在sklearn和cran中使用,所以您可以合理地确信它们的实现不会有问题。
制作一个AIfilter来区分苹果公司 ( 苹果公司)和苹果公司(水果)。 由于这些是推文,使用140个字段的向量来定义您的训练集,每个字段都是在位置X(0到139)的推文中写入的字符。 如果推文较短,只是给空白值。
然后build立一个足够大的训练集,以获得一个很好的准确性(主观根据你的口味)。 为每条推文分配一个结果值, 苹果公司的鸣叫获得1(真),苹果鸣叫(水果)得到0.这将是一个逻辑回归 监督学习的情况。
这是机器学习,通常更容易编码和执行更好。 它必须从你给的设置中学习,而且不是硬编码的。
我不知道Python ,所以我不能为它编写代码,但如果你花更多的时间来学习机器学习的逻辑和理论,你可能想看看我正在关注的课程。
尝试Coursera课程机器学习 Andrew Ng 。 你将学习MATLAB或Octave的机器学习,但是一旦你掌握了基础知识,如果你理解简单的math(简单的逻辑回归),你将能够用任何语言编写机器学习。
也就是说,从某人那里获取代码不会让你能够理解机器学习代码中的内容。 您可能需要花费几个小时的时间来了解实际情况。