Классификация с использованием корпуса обзора фильмов в NLTK/Python

Я собираюсь провести некоторую классификацию в духе главы 6 НЛТК. Книга, кажется, пропускает шаг в создании категорий, и я не уверен, что делаю неправильно. У меня есть мой сценарий здесь с ответом ниже. Мои проблемы в основном проистекают из первой части - создание категории на основе имен каталогов. Некоторые другие вопросы здесь использовали имена файлов (т.е. pos_1.txt а также neg_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!

--------- ОБНОВЛЕНИЕ ------------- Спасибо alvas за ваш подробный ответ! У меня есть два вопроса, однако.

  1. Можно ли получить категорию из имени файла, как я пытался сделать? Я надеялся сделать это в том же духе, что и review_pos.txt метод, только хватая pos из имени папки, а не имени файла.
  2. Я запустил ваш код и у меня возникла синтаксическая ошибка

    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`

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). Следующий код:

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. Обычно fileids в movie_reviews, начинается с любого pos/neg затем косая черта, затем имя файла и, наконец, .txt например, pos/cv001_18431.txt,

Так что я думаю, может быть, вам следует заново загрузить файлы с помощью:

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

Затем убедитесь, что корпус обзора фильмов правильно загружен на вкладке Корпорация:

MR dl

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

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:]]

Затем подайте данные в классификатор и вуаля! Итак, вот код без комментариев и прохождения:

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
Другие вопросы по тегам