1. Descrição do Problema

A empresa Amazon deseja obter um sistema inteligente para processar os comentários de seus clientes sobre os seus produtos, podendo classificar tais comentários dentre as categorias: positivo ou negativo. Para isso ela disponibiliza três bases de dados com sentenças rotuladas.

2. Dados

Os dados estão organizados em sentença e rótulo, sendo 0 negativo e 1 positivo As bases são provenientes dos seguintes sites: imdb.com amazon.com yelp.com

3. Solução


In [20]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Importing Data


In [21]:
X_1 = pd.read_csv('amazon_cells_labelled.txt', sep="\t", usecols=[0], header=None)
Y_1 = pd.read_csv('amazon_cells_labelled.txt', sep="\t", usecols=[1], header=None)

X_2 = pd.read_csv('imdb_labelled.txt', sep="\t", usecols=[0], header=None)
Y_2 = pd.read_csv('imdb_labelled.txt', sep="\t", usecols=[1], header=None)

X_3 = pd.read_csv('yelp_labelled.txt', sep="\t", usecols=[0], header=None)
Y_3 = pd.read_csv('yelp_labelled.txt', sep="\t", usecols=[1], header=None)


X = np.concatenate((X_1.values[:,0], X_2.values[:,0]), axis=0)
Y = np.concatenate((Y_1.values[:,0], Y_2.values[:,0]), axis=0)
X = np.concatenate((X, X_3.values[:,0]), axis=0)
Y = np.concatenate((Y, Y_3.values[:,0]), axis=0)

Creating Dictionary

Na variável "charsRemove" são indicados os caracteres a serem removidos do input.

Todas as palavras são transformadas para lower case e os caracteres indesejados são removidos.

Na variável "allWords", são armazenadas todas as palavras a serem utilizadas.


In [22]:
# Treating Sentences
allSentences = []
charsSplit = "\\`*_{}[]()>#+-.!$,:&?"
charsRemove = ".,-_;\"\'"
for x in X :
    for c in charsSplit:
        x = x.replace(c, ' '+ c +' ')
    for c in charsRemove:
        x = x.replace(c, '')
    allSentences.append(x.lower() )

In [23]:
allWords = []
for x in allSentences :
    allWords.extend(x.split(" "))


allWords = list(set(allWords))
allWords.sort()

Removing Words / Features

Aqui são removidas as palavras que aparecem menos de 52% como positivo ou como negativo.


In [24]:
positiveCount = [0] * len(allWords)
negativeCount = [0] * len(allWords)

sentenceNumber = 0
for sentenceNumber, sentence in enumerate(allSentences) :
    for word in sentence.split(" "):
        wordIndex = allWords.index(word)
        if (Y[sentenceNumber] == 1):
            positiveCount[wordIndex] = positiveCount[wordIndex] + 1
        else:
            negativeCount[wordIndex] = negativeCount[wordIndex] + 1

    
allCount =  np.array(positiveCount) + np.array(negativeCount)
probPositive = np.divide(positiveCount, allCount)

len(allWords)
allWordsFiltered =  np.array(allWords)

len(allWordsFiltered)
index_to_remove = []
for index, prob in enumerate(probPositive):
    if not((prob <= 0.48) or (prob >= 0.52)):
        index_to_remove.append(index)
        a = 1

allWordsFiltered = np.delete(allWordsFiltered, index_to_remove)

Sentence to Vector

Cria-se um vetor para cada sentença utilizando o dicionário elaborado como base.

Este vetor dependerá das palavras contidas em cada uma das sentenças. Cada ocorrência de palavra acarretará em igualarmos o índice associado a 1 (testou-se também incrementando esse valor, mas não foi viável).


In [25]:
allWords = np.array(allWordsFiltered).tolist()
X = []
for x in allSentences :
    sentenceV = [0] * len(allWords)
    words = x.split(" ")
    for w in words :
        try:
            index = allWords.index(w)
            sentenceV[index] = 1 #sentenceV[index] + 1
        except ValueError:
            pass

    X.append(sentenceV)

Classifier Model

Foram testados uma série de métodos para realizar a classificação dos comentários entre positivo e negativo. O melhor resultado obtido foi utilizando Ensemble, combinando:

- Multinomial Naive Bayes (peso 3)
- KNN (peso 2)
- Logistic Regression (peso 2)

In [34]:
from sklearn.cross_validation import cross_val_score
cross_val_k = 10


from sklearn.naive_bayes import MultinomialNB
clf1 = MultinomialNB()

from sklearn.neighbors import KNeighborsClassifier
clf2 = KNeighborsClassifier(n_neighbors=5)

from sklearn.linear_model import LogisticRegression
clf3 = LogisticRegression(penalty='l2', C=1.0)

from sklearn.ensemble import VotingClassifier
eclf = VotingClassifier(estimators=[('mnb', clf1), ('knn', clf2), ('lr', clf3)], voting='soft', weights=[3,2,2])

accuracy = cross_val_score(eclf, X, Y, cv=cross_val_k, scoring='accuracy').mean()
print('Precisão: ', accuracy)


Precisão:  0.852974803574

Melhor resultado:

A melhor classificação obtida consistituiu de utilizar três classificadores para decidir o resultado. Isso foi feito a partir de um esquema de votação, em que cada um deles possui um peso na decisão. Foi usado portanto um método de Ensemble.

Método Peso
MultinomialNB 3
KNeighborsClassifier 2
LogisticRegression 2

Precisão: 0.852974803574

Resultados Isolados:

Avaliando cada um desses métodos isoladamente, foram obtidos os seguintes resultados:

Método Precisão
MultinomialNB 0.833680803593
KNeighborsClassifier 0.673224700191
LogisticRegression 0.828600463537