在 NLTK/Python 中使用电影评论语料库进行分类

Posted

技术标签:

【中文标题】在 NLTK/Python 中使用电影评论语料库进行分类【英文标题】:Classification using movie review corpus in NLTK/Python 【发布时间】:2014-02-02 03:08:10 【问题描述】:

我希望按照NLTK Chapter 6 的方式进行一些分类。这本书似乎跳过了创建类别的步骤,我不确定我做错了什么。我的脚本在这里,响应如下。我的问题主要源于第一部分——基于目录名称的类别创建。这里的其他一些问题使用了文件名(即pos_1.txtneg_1.txt),但我更愿意创建可以将文件转储到的目录。

from nltk.corpus import movie_reviews

reviews = CategorizedPlaintextCorpusReader('./nltk_data/corpora/movie_reviews', r'(\w+)/*.txt', cat_pattern=r'/(\w+)/.txt')
reviews.categories()
['pos', 'neg']

documents = [(list(movie_reviews.words(fileid)), category)
            for category in movie_reviews.categories()
            for fileid in movie_reviews.fileids(category)]

all_words=nltk.FreqDist(
    w.lower() 
    for w in movie_reviews.words() 
    if w.lower() not in nltk.corpus.stopwords.words('english') and w.lower() not in  string.punctuation)
word_features = all_words.keys()[:100]

def document_features(document): 
    document_words = set(document) 
    features = 
    for word in word_features:
        features['contains(%s)' % word] = (word in document_words)
    return features
print document_features(movie_reviews.words('pos/11.txt'))

featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[100:], featuresets[:100]
classifier = nltk.NaiveBayesClassifier.train(train_set)

print nltk.classify.accuracy(classifier, test_set)
classifier.show_most_informative_features(5)

这会返回:

File "test.py", line 38, in <module>
    for w in movie_reviews.words()

File "/usr/local/lib/python2.6/dist-packages/nltk/corpus/reader/plaintext.py", line 184, in words
    self, self._resolve(fileids, categories))

File "/usr/local/lib/python2.6/dist-packages/nltk/corpus/reader/plaintext.py", line 91, in words
    in self.abspaths(fileids, True, True)])

File "/usr/local/lib/python2.6/dist-packages/nltk/corpus/reader/util.py", line 421, in concat
    raise ValueError('concat() expects at least one object!')

ValueError: concat() expects at least one object!

---------更新------------- 感谢阿尔瓦斯的详细回答!不过,我有两个问题。

    是否可以像我尝试的那样从文件名中获取类别?我希望以与review_pos.txt 方法相同的方式执行此操作,只从文件夹名称而不是文件名中获取pos

    我运行了您的代码,但遇到语法错误

    train_set =[(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[:numtrain]] test_set = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[numtrain:]]

第一个 for 下的胡萝卜。我是 Python 初学者,我对这种语法还不够熟悉,无法尝试对其进行故障排除。

----更新 2---- 错误是

File "review.py", line 17
  for i in word_features, tag)
    ^
SyntaxError: invalid syntax`

【问题讨论】:

我更喜欢用我的方式来提取每个文件的类别。但是你可以吃你自己的狗粮(en.wikipedia.org/wiki/Eating_your_own_dog_food)。关于语法错误,能把控制台显示的错误贴出来吗? 已删除 - 添加到原件 您使用的是py2.7及以上版本吗?由于dict理解,语法似乎失败了 确保使用交叉验证 cross validation example 将数据拆分到训练集和测试集,这是拆分数据的适当方式。 【参考方案1】:

是的,第 6 章的教程旨在为学生提供基本知识,从那里开始,学生应该在此基础上探索 NLTK 中可用的内容和不可用的内容。因此,让我们一次解决一个问题。

首先,通过目录获取“pos”/“neg”文档的方式很可能是正确的做法,因为语料库是这样组织的。

from nltk.corpus import movie_reviews as mr
from collections import defaultdict

documents = defaultdict(list)

for i in mr.fileids():
    documents[i.split('/')[0]].append(i)

print documents['pos'][:10] # first ten pos reviews.
print
print documents['neg'][:10] # first ten neg reviews.

[出]:

['pos/cv000_29590.txt', 'pos/cv001_18431.txt', 'pos/cv002_15918.txt', 'pos/cv003_11664.txt', 'pos/cv004_11636.txt', 'pos/cv005_29443.txt', 'pos/cv006_15448.txt', 'pos/cv007_4968.txt', 'pos/cv008_29435.txt', 'pos/cv009_29592.txt']

['neg/cv000_29416.txt', 'neg/cv001_19502.txt', 'neg/cv002_17424.txt', 'neg/cv003_12683.txt', 'neg/cv004_12641.txt', 'neg/cv005_29357.txt', 'neg/cv006_17022.txt', 'neg/cv007_4992.txt', 'neg/cv008_29326.txt', 'neg/cv009_29417.txt']

另外,我喜欢一个元组列表,其中第一个元素是 .txt 文件中的单词列表,第二个是类别。同时删除停用词和标点符号:

from nltk.corpus import movie_reviews as mr
import string
from nltk.corpus import stopwords
stop = stopwords.words('english')
documents = [([w for w in mr.words(i) if w.lower() not in stop and w.lower() not in string.punctuation], i.split('/')[0]) for i in mr.fileids()]

接下来是FreqDist(for w in movie_reviews.words() ...) 的错误。您的代码没有任何问题,只是您应该尝试使用命名空间(请参阅http://en.wikipedia.org/wiki/Namespace#Use_in_common_languages)。以下代码:

from nltk.corpus import movie_reviews as mr
from nltk.probability import FreqDist
from nltk.corpus import stopwords
import string
stop = stopwords.words('english')

all_words = FreqDist(w.lower() for w in mr.words() if w.lower() not in stop and w.lower() not in string.punctuation)

print all_words

[输出]:

<FreqDist: 'film': 9517, 'one': 5852, 'movie': 5771, 'like': 3690, 'even': 2565, 'good': 2411, 'time': 2411, 'story': 2169, 'would': 2109, 'much': 2049, ...>

由于上面的代码正确打印了FreqDist,因此错误似乎是您在nltk_data/ 目录中没有文件。

您拥有fic/11.txt 的事实表明您正在使用一些旧版本的 NLTK 或 NLTK 语料库。通常,movie_reviews 中的 fileidspos/neg 开头,然后是斜线,然后是文件名,最后是 .txt,例如pos/cv001_18431.txt

所以我想,也许你应该重新下载文件:

$ python
>>> import nltk
>>> nltk.download()

然后确保在语料库选项卡下正确下载了电影评论语料库:

回到代码,如果您已经在文档中过滤了所有单词,那么遍历电影评论语料库中的所有单词似乎是多余的,所以我宁愿这样做来提取所有特征集:

word_features = FreqDist(chain(*[i for i,j in documents]))
word_features = word_features.keys()[:100]

featuresets = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents]

接下来,按功能拆分训练/测试是可以的,但我认为最好使用文档,所以不要这样:

featuresets = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents]
train_set, test_set = featuresets[100:], featuresets[:100]

我会推荐这个:

numtrain = int(len(documents) * 90 / 100)
train_set = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[:numtrain]]
test_set = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[numtrain:]]

然后将数据输入分类器,瞧!所以这里是没有 cmets 和演练的代码:

import string
from itertools import chain

from nltk.corpus import movie_reviews as mr
from nltk.corpus import stopwords
from nltk.probability import FreqDist
from nltk.classify import NaiveBayesClassifier as nbc
import nltk

stop = stopwords.words('english')
documents = [([w for w in mr.words(i) if w.lower() not in stop and w.lower() not in string.punctuation], i.split('/')[0]) for i in mr.fileids()]

word_features = FreqDist(chain(*[i for i,j in documents]))
word_features = word_features.keys()[:100]

numtrain = int(len(documents) * 90 / 100)
train_set = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[:numtrain]]
test_set = [(i:(i in tokens) for i in word_features, tag) for tokens,tag in documents[numtrain:]]

classifier = nbc.train(train_set)
print nltk.classify.accuracy(classifier, test_set)
classifier.show_most_informative_features(5)

[出]:

0.655
Most Informative Features
                     bad = True              neg : pos    =      2.0 : 1.0
                  script = True              neg : pos    =      1.5 : 1.0
                   world = True              pos : neg    =      1.5 : 1.0
                 nothing = True              neg : pos    =      1.5 : 1.0
                     bad = False             pos : neg    =      1.5 : 1.0

【讨论】:

我明白了。但我得到的一个奇怪结果是朴素贝叶斯结果给出的答案是 0.16 到 0.17,我觉得这很奇怪。发生这种情况的任何可能原因? alvas 我尝试了相同的代码。但是我只得到 0,16 为什么?

以上是关于在 NLTK/Python 中使用电影评论语料库进行分类的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 imdb Movie_Reviews 语料库在 SVM 分类 (NLP) 中实现否定特征

互联网电影资料库的特色亮点

实训项目:基于TextCNN汽车行业评论文本的情感分析

使用Python分析《我不是药神》电影豆瓣评论

Rails 用户对电影的评论

通过使用 Python 插入代码字来修改语料库