При обработке данных IMDB, подготовленных мной с использованием RNN Keras, точность никогда не превышает 0,5 - PullRequest
0 голосов
/ 04 июня 2019

Происходит очень странная вещь. Я взял корпус IMDB из Kaggle, сохранил только 50 000 положительных и отрицательных текстов, посчитал частоты слов, отсортировал слова по убыванию частоты, заменил в текстах 10 000 самых частых слов по рангу (плюс 3 единицы), вставил 1 все предложения начинаются и заменяются все слова, не входящие в число 10 000 наиболее часто встречающихся, числом 2. В этом я точно следовал инструкциям, приведенным в документации класса Keras imdb.

Затем я запустил RNN с уровнем Embedded, уровнем SimpleRNN, плотным уровнем. Результат, который я получаю, - точность всегда около 0,5, как бы я ни старался. Затем я заменяю свой код на imdb.load_data(num_words=10000) и получаю точность 0,86 уже в третьей эпохе. Как это возможно? Почему такая крайняя разница? Что я сделал не так?

Вот код, который я использовал:

import re, os, time, pickle
word=re.compile(r'^[A-Za-z]+$')
spacyline=re.compile(r'^([0-9]+) ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+) ([A-Za-z]+)')

DICT=dict()

inp=open("imdb_master_lemma.txt")
for ligne in inp:
    if (ligne[0:9]=="DEBUT DOC"):
        if (ligne[-4:-1]=="neg"):
            classe=-1
        elif (ligne[-4:-1]=="pos"):
            classe=1
    elif (ligne[0:9]=="FIN DOCUM"):
        a=1
    else:
        res=spacyline.findall(ligne)
        if res:
            lemma=str(res[0][3])
            if (word.match(lemma)):
                if (lemma in DICT.keys()):
                    DICT[lemma] += 1
                else:
                    DICT[lemma]=1
inp.close()

SORTED_WORDS=sorted(DICT.keys(), key=lambda x:DICT[x], reverse=True)
THOUSAND=SORTED_WORDS[:9997]
ORDRE=dict()
c=0
for w in THOUSAND:
    ORDRE[w]=c
    c+=1
CORPUS=[]
CLASSES=[]

inp=open("imdb_master_lemma.txt")
for ligne in inp:
    if (ligne[0:9]=="DEBUT DOC"):
        if (ligne[-4:-1]=="neg"):
            classe=0
        elif (ligne[-4:-1]=="pos"):
            classe=1
        a=[]
    if (ligne[0:9]=="DEBUT PHR"):
        a.append(1)
    elif (ligne[0:9]=="FIN DOCUM"):
        CORPUS.append(a)
        CLASSES.append(classe)
    else:
        res=spacyline.findall(ligne)
        if res:
            lemma=str(res[0][3])
            if lemma in ORDRE:
                a.append(ORDRE[lemma]+3)
            elif (word.match(lemma)):
                a.append(2)
inp.close()

from sklearn.utils import shuffle
CORPUS, CLASSES=shuffle(CORPUS, CLASSES)

out=open("keras1000.pickle","wb")
pickle.dump((CORPUS,CLASSES,ORDRE),out)
out.close()

Файл imdb_master_lemma.txt содержит тексты IMDB, обработанные Spacy, и я сохраняю только лемму (которая уже в нижнем регистре, так что это более или менее то, что используется в Keras imdb, только она должна работать еще лучше, так как во множественном числе нет глаголов и лемматизированы). После сохранения файла маринада я вспоминаю его и использую его следующим образом:

picklefile=open("keras1000.pickle","rb")
(CORPUS,CLASSES,ORDRE)=pickle.load(picklefile)
picklefile.close()

import numpy as np
def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results
x_train = np.array(vectorize_sequences(CORPUS[:25000]),dtype=object)
x_test = np.array(vectorize_sequences(CORPUS[25000:]),dtype=object)
train_labels = np.array(CLASSES[:25000])
test_labels = np.array(CLASSES[25000:])

from keras import models
from keras import layers
from keras.models import Sequential
from keras.layers import Flatten, Dense, Embedding, SimpleRNN, LSTM, Bidirectional
from keras.preprocessing import sequence

input_train = sequence.pad_sequences(x_train, maxlen=500)
input_test = sequence.pad_sequences(x_test, maxlen=500)
print('input_train shape:', input_train.shape)
print('input_test shape:', input_test.shape)

model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['acc'])

history = model.fit(input_train,
                    train_labels,
                    epochs=10,
                    batch_size=128,
                    validation_split=0.2)
results = model.evaluate(input_test, test_labels)

print(results)

Результат крайне разочаровывающий, точность около 0,5. Когда я заменяю 14 первых строк на

from keras.datasets import imdb
(x_train, train_labels), (x_test, test_labels) = imdb.load_data(num_words=10000)

тогда все работает, как описано в книге Чоллет, и я сразу получаю очень высокую точность.

Может кто-нибудь сказать мне, что я делаю не так?

PS. Вот небольшая выборка данных, чтобы проиллюстрировать процесс подготовки: Два первых предложения первого документа IMDB, обработанные spaCy,

DEBUT DOCUMENT neg
DEBUT PHRASE
0 Once RB once 1 advmod
1 again RB again 5 advmod
2 Mr. NNP mr. 3 compound
3 Costner NNP costner 5 nsubj
4 has VBZ have 5 aux
5 dragged VBN drag 5 ROOT
6 out RP out 5 prt
7 a DT a 8 det
8 movie NN movie 5 dobj
9 for IN for 5 prep
10 far RB far 11 advmod
11 longer JJR long 9 pcomp
12 than IN than 11 prep
13 necessary JJ necessary 12 amod
14 . . . 5 punct
FIN PHRASE
DEBUT PHRASE
15 Aside RB aside 16 advmod
16 from IN from 33 prep
17 the DT the 21 det
18 terrific JJ terrific 19 amod
19 sea NN sea 21 compound
20 rescue NN rescue 21 compound
21 sequences NNS sequence 16 pobj
22 , , , 21 punct
23 of IN of 26 prep
24 which WDT which 23 pobj
25 there EX there 26 expl
26 are VBP be 21 relcl
27 very RB very 28 advmod
28 few JJ few 26 acomp
29 I PRP -PRON- 33 nsubj
30 just RB just 33 advmod
31 did VBD do 33 aux
32 not RB not 33 neg
33 care VB care 33 ROOT
34 about IN about 33 prep
35 any DT any 34 pobj
36 of IN of 35 prep
37 the DT the 38 det
38 characters NNS character 36 pobj
39 . . . 33 punct
FIN PHRASE

Это становится:

[1, 258, 155, 5920, 13, 979, 38, 6, 14, 17, 207, 165, 68, 1526, 1, 1044, 33, 3, 1212, 1380, 1396, 382, 7, 58, 34, 4, 51, 150, 37, 19, 12, 338, 39, 91, 7, 3, 46,

и т.д.. Как вы можете видеть, 1 показывает начало предложения, 258 это once, 155 это again, я пропустил mr., потому что он содержит точку (но это вряд ли может быть причиной того, что мой система отказывает), 5920 - это costner (очевидно, имя Кевина Костнера появляется так часто, что оно входит в число 10 000 наиболее часто встречающихся слов), 13 - это have, 979 - drag, 38 - это out, 6 - это статья a, 14 - это слово movie и так далее. Думаю, все эти звания очень разумны, поэтому я не вижу, что могло пойти не так.

1 Ответ

2 голосов
/ 18 июня 2019

Я думаю, что проблема в том, что вы кодируете свои входные данные (x_train и x_test) одним нажатием в функции vectorize_sequences. Если вы пропустите этот шаг, ваша модель должна работать как с данными примера Keras.

Причина в том, что ваш входной слой model.add(Embedding(10000, 32)) ожидает фактические индексы каждого слова в последовательности. Итак, кое-что, как вы показываете в своем примере:

In [1] : print(x_train[0])
Out[1] : [1, 258, 155, 5920, 13, 979, 38, 6, 14, 17, 207, ...]

Слой встраивания затем сопоставит эти индексы с соответствующими векторами слов и сложит их вместе в правильном порядке, прежде чем подавать их в RNN.

Когда вы кодируете последовательности одним нажатием, вы не только теряете упорядоченность в тексте, но и получаете вектор с размером 10000, который, вероятно, отключает Keras, когда вы определяете maxlen для заполнения.

input_train = sequence.pad_sequences(x_train, maxlen=500)

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

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

Обновление

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

Просто замените

def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results
x_train = np.array(vectorize_sequences(CORPUS[:25000]),dtype=object)
x_test = np.array(vectorize_sequences(CORPUS[25000:]),dtype=object)

с

x_train = CORPUS[:25000]
x_test = CORPUS[25000:]

и ваш код должен хорошо работать. Я быстро достиг 95% точности за 5 эпох.

...