Sentiment Analysis for Amazon Reviews using Neo4j

sentiment analysis neo4j micro

Sentiment analysis is the use of natural language processing to extract features from a text that relate to subjective information found in source materials. Simply put, it’s a series of methods that are used to objectively classify subjective content. 

Amazon Review Sentiment Analysis

Many companies and applications might draw value from adding some sort of sentiment analysis, whether it’s identifying potential churn, discovering and improving upon poor performing products, or simply digesting massive swaths of text information and categorizing it.
For example, consider the value sentiment analysis might pose for large retailers like Amazon. Sophisticated extraction might lead to more rapid detection of people selling counterfeit goods or it might be used to in a democratic way reward better items in search results. Sentiment analysis allows for objective and actionable analysis of often difficult to utilize “dark data.” There are several methods for scraping Amazon reviews, this process can be difficult and is beyond the scope of this blog post. For our purposes, I’ve scraped about 25 reviews. For extended sentiment analysis on Amazon reviews, a tool like Adam Griffiths’s Amazon Scraper could be used. 

Sentiment Analysis Method

One reason many organizations default to a “manual first” approach to sentiment analysis or maybe something a small step beyond that is natural language processing using traditional state-of-the-art methods is really hard. Approaches like Naive BayesSupport Vector Machines, and Maximum Entropy not only require a rich understanding statistics but piles of elegant code to facilitate such methods in a real-time production environment.

 

I’m going to show you how to brow-beat your problems using graphs and some clever pattern matching.

  

While there are other methods using Neo4j to perform sentiment analysis, far and away one of the most effective / popular methods is Graphify, written by Kenny Bastani.

I’m not nearly as clever as Kenny, so I needed to try something else. Thus, here’s a method that’s so easy that my grandmother, who is still somehow paying for AOL, could do it. We’re going to use a short 10,000 word lexicon from the University of Pittsburgh’s computer science department’s MPQA project. This small corpus categorizes keywords as either positive, neutral, or negative polarity. My approach will be using Neo4j’s Graph Database to dissect reviews into their words and then looking for patterns between our corpus and these reviews. We’ll then score the review’s polarity based on these relationships. 

 

Neo4j Data Model

I imported the MPQA dictionary into Neo4j using the below model. I created two `:Polarity` nodes, one for “positive sentiment” and another for “negative sentiment.” I then connected words affiliated with a given polarity to that node. When we pull in a Review, we’ll parse each word out and create a new node representing that word that is attached to the original :Review node.  

Sentiment Corpus

neo4j sentiment analysis data model

Words Contained in Reviews

neo4j sentiment analysis data model

Graph-based NLP Example

Parsing Strings and Assigning to our Data Model with Cypher
Let’s take a sample review from Amazon.com’s listing for Dockers D3 Fit Perfect Shorts:
neo4j sentiment analysis data model

Sample Review

“i believe that these are the best looking longest wearing pants you will ever find i bought them for my nephew to wear in a very hot and greasy commercial kitchen hoping they would be as good as the dickie medical uniforms i used to wear to work in a hospital they came through with flying colors they wash and dry like a dream with no shrinkage very little fading and no wrinkles even after 10 or more times through the washer and dryer with my nephew mostly washing them in hot water i would highly recommend these pants to anyone”

 

We start by digesting the review into individual words using a few lines of Neo4j’s query language, Cypher:
MATCH (n:Review)
WHERE n.analyzed = FALSE
WITH n, split(n.review, " ") as words
UNWIND words as word
CREATE (rw:ReviewWords {word:word})
WITH n, rw
CREATE (rw)-[r:IN_REVIEW]->(n)
WITH count(r) as wordCount, n
SET n.wordCount = wordCount;

Next we’ll match between our corpus words and the words contained within the review. If there is a matching word, I will create a new relationship between them called `:TEMP`. The reason why I create this temporary relationship is because we’ll be comparing the number of paths between a given review and the positive pole and the negative pole.

MATCH (n:Review)-[:IN_REVIEW]-(wordReview)
WITH distinct wordReview
MATCH  (wordSentiment:Word)
WHERE wordReview.word = wordSentiment.word AND (wordSentiment)-[:SENTIMENT]-()
MERGE (wordReview)-[:TEMP]->(wordSentiment);

Sentiment Analysis Using Cypher

The next step is to compare the number of words that are under the “positive” pole and the number of words that fall under the “negative” pole. We’ll use more Cypher to look for a pattern within which a review has a path between itself and either pole. We’ll then count those pathways as a proxy for counting the number of words that are keyword-positive or keyword-negative. The assumption is that the more positive keywords a review has the more likely that the review is positive. On the other hand, the more negative keywords the review has the more likely that review is negative. The method I’m using will simply compare the relative density of positive words vs negative words. This is a very clumsy approximation of sentiment, but for our purposes it will help us identify positive / negative reviews and use that information to inform our recommendation engine.

Amazon Reviews Sentiment Analysis Scoring Function

The scoring function my approach uses works in three basic steps:

First, it identifies keywords that are contained in a review and matches them up with keywords in our corpus. In the same query, it will calculate a “sentiment score.”

MATCH (n:Review)-[rr:IN_REVIEW]-(w)-[r:TEMP]-(word)-[:SENTIMENT]-(:Polarity)
OPTIONAL MATCH pos = (n:Review)-[:IN_REVIEW]-(wordReview)-[:TEMP]-(word)-[:SENTIMENT]-(:Polarity {polarity:'positive'})
WITH n, toFloat(count(pos)) as plus
OPTIONAL MATCH neg = (n:Review)-[:IN_REVIEW]-(wordReview)-[:TEMP]-(word)-[:SENTIMENT]-(:Polarity {polarity:'negative'})
WITH ((plus - COUNT(neg))/n.wordCount) as score, n
SET n.sentimentScore = score;
neo4j sentiment analysis data model

Sentiment score within this approach is a basic “keyword density” calculation that will range form -1 to 1, where 1 is all words are positive keywords and -1 is all words are negative keywords.

neo4j sentiment analysis data model

The second step then taking that sentiment score and assigning a positive, negative, or neutral property to the :Review node. I very scientifically guesstimated that 0.01% keyword density of either positive or negative words safely indicates the polarity of the review. The second half of the query breaks the relationships between our :ReviewWords and our keyword corpus. This was done to keep density on the keyword nodes if this method was scaled to 10s of 1000s of reviews. Also, it should be noted that if this process were being used as a part of a larger recommendation system that the scoring be done upon the receipt of the review rather than in a global process.

MATCH (n:Review)-[rr:IN_REVIEW]-(w)-[r:TEMP]-(word)-[:SENTIMENT]-(:Polarity)
WHERE n.sentimentScore >= (.01)
SET n.sentiment = 'positive', n.analyzed = TRUE
DELETE w, r, rr;
MATCH (n:Review)-[rr:IN_REVIEW]-(w)-[r:TEMP]-(word)-[:SENTIMENT]-(:Polarity)
WHERE n.sentimentScore <= (-.001)
SET n.sentiment = 'negative', n.analyzed = TRUE
DELETE w, r, rr;
MATCH (n:Review)-[rr:IN_REVIEW]-(w)-[r:TEMP]-(word)-[:SENTIMENT]-(:Polarity)
WHERE (.01) > n.sentimentScore > (-.01)
SET n.sentiment = 'neutral', n.analyzed =TRUE
DELETE w, r, rr;
 
So, let’s take a look at our example review after it has passed through our Neo4j sentiment analyzer. We see that this review had roughly 40% more positive words that negative, well above our .01% threshold.
neo4j sentiment analysis data model

On the Efficacy of ThreeEye

I found a scored movie review dataset released by the Stanford AI group so I decided to pull that in and see how well my method worked. Over the 25,000 movie reviews, this crude method scored ~72% correct. Which sounds pretty abysmal, however when compared with generally accepted human classification error of 0.3 or 70% success— my hacky Neo4j method seems pretty great considering it’s about 100 lines of cypher.

Notes

Neo4j is an open source graph database.

ThreeEye also apparently an open source graph project.

Huge Thank You to Kenny Bastani and his Deep Learning Sentiment Analysis post which served as the inspiration for this post / project.

Leave a Reply

Your email address will not be published.