LINUX.ORG.RU

Вывод неправильно распознанных текстов

 ,


0

1

Привет всем. По сабжу: есть сеть, написанная на keras, её задача - распознать сентимент текста, а именно - хороший комментарий к фильму, или плохой(использовал дата сет imdb). Точность на выходе очевидно не 100%, значит есть тексты, которые классифицируются неверно. Офк я попробовал вывести те самые тексты, и даже есть кое-какие результаты, но тексты постоянно одни и те же, что и настораживает меня. Скорее всего я где-то ошибся, но где? Вот сам скрипt

pos_train_data = pd.read_csv('train_pos.tsv',sep = '\t')
neg_train_data = pd.read_csv('train_neg.tsv',sep = '\t')
pos_test_data = pd.read_csv('test_pos.tsv',sep = '\t')
neg_test_data = pd.read_csv('test_neg.tsv',sep = '\t')

pos_train_data = pos_train_data[['Text','Sentiment']]
neg_train_data = neg_train_data[['Text','Sentiment']]
pos_test_data = pos_test_data[['Text','Sentiment']]
neg_test_data = neg_test_data[['Text','Sentiment']]


data_train = pd.concat([pos_train_data,neg_train_data],ignore_index = True)
data_train = data_train.sample(frac=1).reset_index(drop=True)
#print(data_train.head())

data_test = pd.concat([pos_test_data,neg_test_data],ignore_index = True)
data_test = data_test.sample(frac=1).reset_index(drop=True)
#print(data_test.head())

stop_words = set(stopwords.words('english'))
table = str.maketrans('', '', punctuation)

def textclean(text):
    #tokens = word_tokenize(text)
    tokens = (text.lower()).split()
    tokens = [word for word in tokens if word.isalpha()]
    tokens = [w.translate(table) for w in tokens]
    tokens = [word for word in tokens if not word in stop_words]
    tokens = [word for word in tokens if len(word) > 1]
    return tokens


def review_to_words(text):
    clean_text = BeautifulSoup(text, "html5lib").get_text()
    clean_text = re.sub(r"[^a-zA-Z]", " ", clean_text)
    words = (clean_text.lower()).split()
    words = [w for w in words if w not in stopwords.words("english")]
    return words


reviews = []
for index,row in data_train.iterrows():
    text = (row['Text'].lower())
    reviews.append(textclean(text))
print(reviews[0])

linked_reviews = list(itertools.chain.from_iterable(reviews))
vocab_freq = dict()
#print(linked_reviews[1])

for word in linked_reviews:
    if word not in vocab_freq:
        vocab_freq[word] = 1
    else:
        vocab_freq[word] += 1
sorted_vocab_freq = list(reversed(sorted(vocab_freq.items(), key=operator.itemgetter(1))))
print(sorted_vocab_freq)
print(len(sorted_vocab_freq))


TOTAL_VOCAB = 5000

word_to_id = dict()
id_to_word = dict()
for i in range(TOTAL_VOCAB):
    word_to_id[sorted_vocab_freq[i][0]] = i
    id_to_word[i] = sorted_vocab_freq[i][0]
print(id_to_word[0])

#review_lengths
review_lengths = pd.DataFrame([len(review) for review in reviews])
review_lengths.columns = ['Len']
print(review_lengths)
#stats
print(review_lengths.describe())


def convert(l):
    new_l = []
    for word in l:
        if word in word_to_id:
            new_l.append(word_to_id[word])
    return new_l
#print(len(data_train['Sentiment']))


X_train = []
y_train = []

#Tukey's method
first_q = review_lengths.Len.quantile([0.25])[0.25]
third_q = review_lengths.Len.quantile([0.75])[0.75]

upper_threshold = third_q + 1.5*(third_q-first_q)
lower_threshold = first_q - 1.5*(third_q-first_q)

print(upper_threshold,lower_threshold)



for i in range(len(data_train)):
    converted_review = convert(reviews[i])
    if len(converted_review) <= upper_threshold:
        X_train.append(converted_review)
        y_train.append(data_train['Sentiment'][i])


X_train = np.array(X_train)
y_train = np.array(y_train)
print(X_train)
print(y_train)

X_train = sequence.pad_sequences(X_train, maxlen=int(upper_threshold),value = 0)
print(X_train.shape,y_train.shape)




data_test = pd.concat([pos_test_data,pos_test_data, neg_test_data], ignore_index=True)
data_test = data_test.sample(frac=0.3).reset_index(drop=True)
print(data_test)
print(pos_test_data)
validation_reviews = []
for index, row in data_test.iterrows():
    text = (row['Text'].lower())
    validation_reviews.append(textclean(text))

X_val = []
y_val = []
for i in range(len(data_test)):
    converted_review = convert(validation_reviews[i])
    if len(converted_review) <= upper_threshold:
        X_val.append(converted_review)
        y_val.append(data_test['Sentiment'][i])
X_val = np.array(X_val)
X_val = sequence.pad_sequences(X_val, maxlen=int(upper_threshold), value=0)
print(X_val)
y_val = np.array(y_val)
#print(X_train)
#print(y_train)




EMBEDDING_LEN = 32

model = Sequential()
model.add(Embedding(TOTAL_VOCAB,EMBEDDING_LEN,input_length = int(upper_threshold)))
model.add(Conv1D(128,3,padding = 'same'))
model.add(Conv1D(64,3,padding = 'same'))
model.add(Conv1D(32,2,padding = 'same'))
model.add(Conv1D(16,2,padding = 'same'))
model.add(Flatten())
model.add(Dropout(0.25))
model.add(Dense(100,activation = 'relu'))
model.add(Dropout(0.25))
model.add(Dense(1,activation='sigmoid'))
model.summary()
opt = optimizers.Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
model.compile(loss = 'binary_crossentropy',optimizer = opt ,metrics = ['accuracy'])
model.fit(X_train,y_train,validation_data = (X_val,y_val),epochs = 1 ,batch_size = 1000)


y_pred_vect = model.predict(X_val)
# bolean mask
mask = (y_pred_vect != y_val).any(axis=1)
print(mask)
print(len(mask))
num_words=1000 # only use top 1000 words
INDEX_FROM=3   # word index offset
# этот шаг нужен чтобы получить `test_x` в изначальном виде (до токенизации):    
(train_x, _), (test_x, _) = imdb.load_data(num_words=num_words, index_from=index_from)
x_wrong = test_x[mask]

word_to_id = imdb.get_word_index()
word_to_id = {k:(v+index_from) for k,v in word_to_id.items()}
word_to_id["<PAD>"] = 0
word_to_id["<START>"] = 1
word_to_id["<UNK>"] = 2

id_to_word = {value:key for key,value in word_to_id.items()}
all_wrong_sents = [' '.join(id_to_word[id] for id in sent) for sent in x_wrong]
Cобственно это часть кода, отвечающая за вывод текстов.
y_pred_vect = model.predict(X_val)
# bolean mask
mask = (y_pred_vect != y_val).any(axis=1)
print(mask)
print(len(mask))
num_words=1000 # only use top 1000 words
INDEX_FROM=3   # word index offset
# этот шаг нужен чтобы получить `test_x` в изначальном виде (до токенизации):    
(train_x, _), (test_x, _) = imdb.load_data(num_words=num_words, index_from=index_from)
x_wrong = test_x[mask]

word_to_id = imdb.get_word_index()
word_to_id = {k:(v+index_from) for k,v in word_to_id.items()}
word_to_id["<PAD>"] = 0
word_to_id["<START>"] = 1
word_to_id["<UNK>"] = 2

id_to_word = {value:key for key,value in word_to_id.items()}
all_wrong_sents = [' '.join(id_to_word[id] for id in sent) for sent in x_wrong]



Последнее исправление: Pepel (всего исправлений: 1)

Почему Convolutional network? Анализ текстов, распознавание голоса и вообще всевозможных последовательностей - это работа для RNN. Туда и копай.

srj ★★
()
Ответ на: комментарий от srj

Как вы могли заметить, если смотрели код, то архитектура сети никак не относится к части кода, которая выводит текст

Pepel
() автор топика

Убогий размер словаря, слишком маленький размер embeddings (в sentiment чем больше, тем лучше). Архитектуру сети лучше взять из статьи Kim. Сами embeddings - скачать fasttext.

Это если про саму сеть говорить.

Ну и да, одна эпоха с таким количеством параметров - это смешно.

Solace ★★
()
Последнее исправление: Solace (всего исправлений: 1)
Ответ на: комментарий от Solace

Одна эпоха взята была для быстрого выполнения программы, ценю ваши советы, но можно по сабжу услышать что-то?

Pepel
() автор топика
Ответ на: комментарий от srj

Почему сразу RNN? В сентимент-анализе нам вроде редко нужно учитывать сложные зависимости - так что CNN поверх 1-2 слов должно зайти. (да и вообще они таки внезапно заходят в NLP).

Pepel - Если я верно понял - вопрос в том, почему она часто ошибается на похожих текстах (т.е. - почему она сходится к схожему состоянию в разных запусках, не смотря на рандом в построении батчей для оптимизатора и в дропауте)?

з.ы. и да, я бы попробовал следующее:

- как бейзлайн - и вовсе заюзал бы логистическую регрессию поверх tf-idf (а тот - поверх стемминга или лемматизации). - как бейзлайн №2 - переобучить готовую сеть. DeepMoji, возможно? - нарастить словарь. - приюзать готовые эмбеддинги (возможно - fasttext). - упростить сетку. Как готовым (необучаемым) эмбеддингом вместо своего, так и изменением устройства. Сейчас у тебя, условно:

model.add(Embedding(TOTAL_VOCAB,EMBEDDING_LEN,input_length = int(upper_threshold)))
model.add(Conv1D(128,3,padding = 'same'))
model.add(Conv1D(64,3,padding = 'same'))
model.add(Conv1D(32,2,padding = 'same'))
model.add(Conv1D(16,2,padding = 'same'))
model.add(Flatten())
model.add(Dropout(0.25))
model.add(Dense(100,activation = 'relu'))
model.add(Dropout(0.25))
model.add(Dense(1,activation='sigmoid'))
model.summary()
Я бы попытался в подобное:
embedding = Embedding(weights=my_pretrained_weights, trainable=False) (input)
conv1 = Conv1D(64, length=1, activation='relu') (embedding)
conv2 = Conv1D(64, length=2, activation='relu') (embedding)
conv3 = Conv1D(64, length=3, activation='relu') (embedding)
pool = MaxPool1D() (conv1, conv2, conv3)
batch1 = BatchNormalization() (pool)
fc1 = Dense(10, activation='relu') (batch1)
batch2 = BatchNormalization() (fc1)
fc2 = Dense(1, activation='sigmoid')
Вроде как меньше параметров, при этом нам не надо пытаться учить фильтр длины 3 учить (условно говоря) униграммам и биграммам.

- учил бы дольше одной эпохи, чтобы видеть динамику train_loss,val_loss. Наверняка у тебя будет переобучение (что выразится в улучшении train_loss при стабильно непадающем val_loss).

anonymous
()
Ответ на: комментарий от anonymous

Да, вы верно поняли, неправильно распознанные обзоры всегда одни и те же, и спасибо за наводку, обязательно все затраю. у меня есть еще одна проблема, а именно - взвешивание терминов, я написал свою функцию для Tf, TfIDF, TFC взвешивания, вот как выглядит:

import math

def frequency(term, document):
    return document.count(term) / len(document)

def totalNumOfDocuments(inList):
    return len(inList)

def numOfDocumentsForTerm(inList, term):
    return len([doc for doc in inList if term in doc])

def TFCWeighting(inList):

    tfc = []

    N = totalNumOfDocuments(inList)

    for document in inList:

        temp = []
        for term in document:

            #numerator
            freq = frequency(term, document)
            n = numOfDocumentsForTerm(inList, term)
            logarithm = math.log(N/n)
            numerator = freq * logarithm

            #denominator
            summation = sum([(frequency(t, document) * math.log(N / numOfDocumentsForTerm(inList, term))) ** 2 for t in document])
            denominator = math.sqrt(summation)

            temp.append(round(numerator / denominator,3))

        tfc.append(temp)

Но проблема в том, что и-за большого объема обучающей выборки(хотя 25000 документов это немного, на мой взгляд), функция просто не может закончить взвешивание терминов. Вся обучающая выборка лежит в списке, где каждый вложенный список - документ с терминами(уже токеннизованый). Я пробовал разделить выборку на 5 частей и провести хотя бы TF взвешивание одной части, и это заняло у меня 2 часа, TFС я просто не дождался. Пробовал что-то типо такого:
import scipy.sparse as sparse

def tfc(freqs_arr):
    if sparse.isspmatrix_csr(freqs_arr):
        v = freqs_arr
    else:
        v = sparse.csr_matrix(freqs_arr)
    l = sparse.csc_matrix(np.log10(v.shape[0]/(v > 0).sum(axis=0)).reshape(-1))
    v = v.multiply(l)
    denom = v.multiply(v).sum() ** 0.5
    return v / denom
В результате вообще получил out of memory. И не совсем понимаю как мне быть.

Pepel
() автор топика

непотеме и мимокрокодил.

А как вообще нейросетью с текстом работать? Я к тому, что там же вроде входные нейроны ограничены по количеству, или я ошибаюсь? Или всеж строится нейросеть сразу с охулиардом нейронов на входе?

seryoga
()
Ответ на: комментарий от Pepel

Сильно не вглядывался, но - не пытался заюзай готовые векторизаторы из sklearn?

anonymous
()
Ответ на: комментарий от srj

это работа для RNN

An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling

For most deep learning practitioners, sequence modeling is synonymous with recurrent networks. Yet recent results indicate that convolutional architectures can outperform recurrent networks on tasks such as audio synthesis and machine translation. Given a new sequence modeling task or dataset, which architecture should one use? We conduct a systematic evaluation of generic convolutional and recurrent architectures for sequence modeling. The models are evaluated across a broad range of standard tasks that are commonly used to benchmark recurrent networks. Our results indicate that a simple convolutional architecture outperforms canonical recurrent networks such as LSTMs across a diverse range of tasks and datasets, while demonstrating longer effective memory. We conclude that the common association between sequence modeling and recurrent networks should be reconsidered, and convolutional networks should be regarded as a natural starting point for sequence modeling tasks.

ymn ★★★★★
()
Ответ на: комментарий от Pepel

https://github.com/facebookresearch/fastText/blob/master/docs/crawl-vectors.md

Ниже по ссылке веса.

По сути все это вариации word2vec: функции, что переводит слово (формально n-gram) в вектор. Учится все это дело в unsupervised манере, на больших объемах текста. Полученные репрезентации обычно улавливают разные типы близости слов. Семантическую, например.

Добрые дяди из Фейсбук взяли и обучили такую сеть, выложив результат.

На практике это позволяет достигать высокой точности во многих задачах. Но глубина сети в таких случаях обычно небольшая, 1-2 слоя.

Solace ★★
()
Последнее исправление: Solace (всего исправлений: 1)
Ответ на: комментарий от Pepel

После первой эпохи твоя модель начинает испытывать overfitting. Слишком много параметров,слишком мало данных. Возьми другую архитектуру. А так в коде вроде все ок. То, что тексты одни и те же плохие - хз, у тебя в pd.DataFrame.sample даже random state не зафиксирован, то есть твой код каждый раз даёт другую выборку для обучения. Видимо, сама сеть скатывается в какой-то плохенький локальный минимум и перестает учить что-то полезное.

Solace ★★
()
Ответ на: комментарий от Solace

Никак не могу подобрать архитектуру, в большинстве случаев точность почему-то всегда на 50%, хотя шаг 0.0001

Pepel
() автор топика
Ответ на: комментарий от Solace

Вот чего смог добиться https://ibb.co/nmiNe7 https://ibb.co/k9jvz7 При данной архитектуре model = Sequential() model.add(Dense(100, activation='relu', input_dim=10000)) model.add(Dropout(0.25)) model.add(Dense(50,activation = 'relu')) model.add(Dense(1, activation='sigmoid'))

Использовал TFIDF взвешивание терминов. Имеет ли смысл поменять пропорции обучающей и тестовой выборок? Т.е сейчас у меня 25к к 25к, а если сделать допустим для обучающей 40к документов, а для тестовой 10? Или как еще можно улучшить результат

Pepel
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.