This article shows you how to build a Neural Network from scratch(no libraries) for the purpose of detecting whether a movie review on IMDB is negative or positive.
Curating a dataset and developing a "Predictive Theory"
Making Learning Faster by Reducing "Neural Noise"
Note: The data in reviews.txt we're contains only lower case characters. That's so we treat different variations of the same word, like The, the, and THE, all the same way.
We'll create three Counter objects, one for words from postive reviews, one for words from negative reviews, and one for all the words.
Examine all the reviews. For each word in a positive review, increase the count for that word in both your positive counter and the total words counter; likewise, for each word in a negative review, increase the count for that word in both your negative counter and the total words counter. You should use split(' ') to divide a piece of text (such as a review) into individual words.
As you can see, common words like "the" appear very often in both positive and negative reviews. Instead of finding the most common words in positive or negative reviews, what you really want are the words found in positive reviews more often than in negative reviews, and vice versa. To accomplish this, you'll need to calculate the ratios of word usage between positive and negative reviews.
The positive-to-negative ratio for a given word can be calculated with positive_counts[word] / float(negative_counts[word]+1). Notice the +1 in the denominator – that ensures we don't divide by zero for words that are only seen in positive reviews.
Words that you would expect to see more often in positive reviews – like "amazing" – have a ratio greater than 1. The more skewed a word is toward postive, the farther from 1 its positive-to-negative ratio will be.
Words that you would expect to see more often in negative reviews – like "terrible" – have positive values that are less than 1. The more skewed a word is toward negative, the closer to zero its positive-to-negative ratio will be.
Neutral words, which don't really convey any sentiment because you would expect to see them in all sorts of reviews – like "the" – have values very close to 1. A perfectly neutral word – one that was used in exactly the same number of positive reviews as negative reviews – would be almost exactly 1.
Right now, 1 is considered neutral, but the absolute value of the postive-to-negative rations of very postive words is larger than the absolute value of the ratios for the very negative words. So there is no way to directly compare two numbers and see if one word conveys the same magnitude of positive sentiment as another word conveys negative sentiment. So we should center all the values around netural so the absolute value fro neutral of the postive-to-negative ratio for a word would indicate how much sentiment (positive or negative) that word conveys.
When comparing absolute values it's easier to do that around zero than one.
In the end, extremely positive and extremely negative words will have positive-to-negative ratios with similar magnitudes but opposite signs.
If everything worked, now you should see neutral words with values close to zero. In this case, "the" is near zero but slightly positive, so it was probably used in more positive reviews than negative reviews. But look at "amazing"'s ratio - it's above 1, showing it is clearly a word with positive sentiment. And "terrible" has a similar score, but in the opposite direction, so it's below -1. It's now clear that both of these words are associated with specific, opposing sentiments.
Run the below code to see more ratios.
It displays all the words, ordered by how associated they are with postive reviews.
Create a set named vocab that contains every word in the vocabulary.
Th following image rpresents the layers of the neural network you'll be building throughout this notebook. layer_0 is the input layer, layer_1 is a hidden layer, and layer_2 is the output layer.
TODO: Create a numpy array called layer_0 and initialize it to all zeros. Create layer_0 as a 2-dimensional matrix with 1 row and vocab_size columns.
layer_0 contains one entry for every word in the vocabulary, as shown in the above image. We need to make sure we know the index of each word, so run the following cell to create a lookup table that stores the index of every word.
TODO: Complete the implementation of update_input_layer. It should count how many times each word is used in the given review, and then store those counts at the appropriate indices inside layer_0.
Lets implement some functions for simplifying our inputs to the neural network.
how many times the given word occurs in the review.
Run the following cell to test updating the input layer with the first review. The indices assigned may not be the same as in the solution, but hopefully you'll see some non-zero values in layer_0.
get_target_for_labels should return 0 or 1, depending on whether the given label is NEGATIVE or POSITIVE, respectively.
# there are vocabulary words and create a single output node.
# populate label_vocab with all of the words in the given labels.
# Store the sizes of the review and label vocabularies.
# These are the weights between the input layer and the hidden layer.
# These are the weights between the hidden layer and the output layer.
# Keep track of correct predictions.
and uses the test_labels to calculate the accuracy of those predictions.
Returns a POSITIVE or NEGATIVE prediction for the given review.
# Run a forward pass through the network, like in the "train" function.
Run the following code to create the network with a small learning rate, 0.001, and then train the new network. Using learning rate larger than this, for example 0.1 or even 0.01 would result in poor performance.
Counting how many times each word occured in our review might not be the most efficient way. Instead just including whether a word was there or not will improve our training time and accuracy. Hence we update our update_input_layer() function.
Calculate the positive-to-negative ratios of words used in the reviews.
Change so words are only added to the vocabulary if they occur in the vocabulary more than min_count times.
Die Funktionsweise der in immer mehr Anwendungen genutzten neuronalen Netzwerke stieß bei weniger technik-affinen Menschen bislang nur auf wenig Interesse. Geschuldet wird das sicher vor allem der eher trockenen Theorie, die hinter diesen Konstrukten steht und die sich für die meisten nicht auf Anhieb erschließt. Ein populäres Beispiel für die Fähigkeiten, die ein solches neuronales Netzwerk bereits heute hat, lieferte in jüngster Zeit Googles “Inception”, welches ohne den Anspruch auf einen praktischen Nutzen eigenständig eine spektakuläre Bilderwelt kreierte, die auch Menschen ohne großes Interesse an den dahinter steckenden Technologien ins Staunen versetzte. Ansonsten bieten sich die neuronalen Netze vor allem überall dort an, wo wenig systematisches Wissen zur Verfügung steht, wie etwa bei der Bilderkennung und der Text- bzw. Sprachanalyse.
Weniger effektheischend, als die Ergebnisse von “Inception”, dafür jedoch überaus hilfreich für den vernetzten Alltag, sind neuronale Netzwerke, die zum Aufspüren und zur Kategorisierung von Spam-Seiten entwickelt werden. In diesem Anwendungsbereich können diese ein wertvolles Werkzeug sein.
Wie bei allen selbstlernenden Netzwerken muss dafür zunächst ein Grundgerüst aufgebaut werden, welches später von Hand mit Informationen gefüttert wird, bis es schließlich in der Lage ist, sich selbstständig weiter zu entwickeln, hinzuzulernen und auf diese Weise immer genauere Ergebnisse liefert.
Unerwünschte Webseiten mit störenden und oft illegalen Inhalten findet man im Internet zu Hauf und meist locken sie mit dubiosen Angeboten für vermeintliche Wundermittel oder gaukeln leichtgläubigen Nutzern vor, man könne ohne großes Zutun viel Geld verdienen – meist ohne ein tatsächliches Produkt oder eine Dienstleistung dahinter. Ein entsprechend programmiertes neuronales Netzwerk spürt diese Seiten anhand von bestimmten Faktoren automatisch auf. Als Trainingsdaten werden dafür zunächst von Hand Kriterien wie die Registrierungs-IP, der Nutzername und die verwendete Sprachversion eingegeben. Da das Netzwerk nur mit den Zahlen 0 und 1 arbeiten kann, müssen diese Datensätze zuvor manuell aufbereitet werden. Indem alle gewünschten Registrierungs-IPs erst auf den jeweiligen Internetdienstanbieter abgebildet werden und der Grad ihrer jeweiligen Spammigkeit von Hand bestimmt wird, lässt sich der jeweilige Durchschnitt der “Spammigkeit” eines Internetdienstanbieters berechnen. Teilt man die Anzahl der Spammer durch die Gesamtnutzerzahl eines einzelnen Anbieters, erhält man bereits ein Ergebnis, das sich zur Eingabe in das neuronale Netzwerk eignet. Ähnlich kann z. B. bei der Kombination aus Geolocation und Sprachversion verfahren werden. Mit einer Vielzahl weiterer Faktoren kann die Effizienz des neuronalen Netzwerks verbessert werden. So lassen sich etwa große Unterschiede bei dem Herkunftsland feststellen, in dem die Spam-Seiten angesiedelt sind. Ein besonders großes Erkennungspotential bieten bestimmte Keywords und Keyword-Kombinationen, die mitunter eindeutige Rückschlüsse auf ein Spam-Angebot ziehen lassen. Befindet sich z. B. die Wortkombination “Geld verdienen” besonders häufig auf einer Seite, ist dies ein recht deutliches Kriterium für die Klassifizierung als Spam. Doch auch weniger offensichtliche Faktoren helfen dem neuronalen Netzwerk dabei, hellhörig zu werden: Ein ungewöhnliches Verhältnis zwischen Vokalen und Konsonanten oder auch Seitennamen, die vermehrt Zahlen und unübliche Zeichen beinhalten, können die Spam-Wahrscheinlichkeit steigern. Kommt die verwendete IP-Adresse aus einem anonymisierten Netzwerk oder VPN, schürt dies ebenfalls den Verdacht auf unseriöse Inhalte.
Da jedes der einbezogenen Kriterien zur Bestimmung der Spammigkeit einer Seite eine unterschiedlich hohe Relevanz hat, müssen die einzelnen Faktoren verschieden stark gewichtet werden. Damit das neuronale Netzwerk genau das tun kann, wird deshalb eine Korrelationsmatrix erstellt. In dieser Matrix werden alle gesammelten Kriterien in Verbindung zueinander gesetzt, um es dem Netzwerk zu ermöglichen, nicht jeden Punkt nur einzeln zu werten. So ist ein Keyword wie z. B. “100 mg” an sich vergleichsweise unverdächtig. Stammt die Seite, auf der das Wort vorkommt jedoch aus einer Gegend, in der erfahrungsgemäß viele unseriöse Arzneimittelanbieter angesiedelt sind, kann dies die Spam-Wahrscheinlichkeit erhöhen.
Ein wertvolles Tool, das sich für die Implementierung des jeweiligen neuronalen Netzwerks eignet, ist die Open Source Machine Learning Library “Tensor Flow” von Google. Diese Programmierschnittstelle der zweiten Generation verfügt über einige handfeste Vorteile gegenüber anderen Libraries und ermöglicht die Parallelisierung der Arbeit. Berechnet wird sie auf der schnellen GPU des Rechners, was in direkten Vergleichen die Rechenzeit um ein Vielfaches senken konnte. Bewährt hat sich “Tensor Flow” bereits in zahlreichen kommerziellen Diensten von Google, darunter Spracherkennungssoftware, Google Photos, und Gmail.
Für eine bessere Abstraktion des Netzwerks, können zusätzlich zu der hinteren mehrere weitere Schichten angelegt werden. Die hintere Schicht bleibt dabei oft die einzige, die von außerhalb sichtbar ist.
Es liegt in der Natur der Sache, dass ein eigenständig lernfähiges Netzwerk nicht von Anfang an durch höchste Zuverlässigkeit hinsichtlich seiner Trefferquote besticht. Zum Lernen gehört Erfahrung und die muss das Netz erst noch sammeln. Zwar gelingt es auch einem noch frisch programmierten Netzwerk bereits die Erfüllung seiner Aufgabe oft recht gut, die Fehlerquote kann jedoch im Laufe der Zeit immer weiter verbessert werden. Gerade am Anfang werden noch viele Spam-Seiten nicht erkannt und einige vermeintliche Spammer stellen sich bei der Überprüfung durch den Menschen als unbedenklich heraus. Darum ist es für die Steigerung der Effizienz praktisch unerlässlich, immer wieder von Hand einzugreifen, falsche Ergebnisse zu korrigieren und dem Netzwerk auf diese Weise zu helfen.
Artificial neural networks are very strong universal approximators. Google recently defeated the worlds strongest Go (“chinese chess”) player with two neural nets, which captured the game board as a picture. Aside from these classification tasks, neural nets can be used to predict future values, behaviors or patterns solely based on learned history. In the machine learning literature, this is often referred to as time series prediction, because, you know, values over time need to be predicted. Hah! To illustrate the concept, we will train a neural net to learn the shape of a sinusoidal wave, so it can continue to draw the shape without any help. We will do this with Scala. Scala is a great lang, because it is strongly typed but feels easy like Python. Throughout this article, I will use the library NeuroFlow, which is a simple, lightweight library I wrote to build and train nets. Because Open Source is the way to go, feel free to check (and contribute to? :-)) the code on GitHub.
If we, as humans, want to predict the future based on historic observations, we would have no other chance but to be guided by the shape drawn so far. Let’s study the plot below, asking ourselves: How would a human continue the plot?
Ranging from 0.0 until 0.75, these discrete values drawn from our function with step size 0.05 will be the basis for training. Now, one could come up with the idea to just memorize all values, so a sufficiently reasonable value can be picked based on comparison. For instance, to continue at the point 0.75 in our plot, we could simply examine the area close to 0.15, noticing a similar value close to 1, and hence go downwards. Well, of course this is cheating, but if a good cheat is a superior solution, why not cheat? Being hackers, we wouldn’t care. What’s really limiting here is the fact that the whole data set needs to be kept in memory, which can be infeasible for large sets, plus for more complex shapes, this approach would quickly result in a lot of weird rules and exceptions to be made in order to find comprehensible predictions.
Let’s go back to our table and see if a neural net can learn the shape, instead of simply memorizing it. Here, we want our net architecture to be of kind [3, 5, 3, 1]. Three input neurons, two hidden layers with five and three neurons respectively, as well as one neuron for the output layer will capture the data shown in the table.
We will draw samples from the range with step size 0.05. After this, we will construct our training values xs as well as our supervised output values ys. Here, a group consists of 4 steps, with 3 steps as input and the last step as the supervised value.
The purple line is the original sinusoidal wave, whereas the green line is the prediction of our net. The first steps show great consistency, but slowly the curves diverge a little over time, as uncertainties will add up. To keep this divergence rather low, one could fine tune settings, for instance numeric precision. However, if one is taking the limit towards infinity, a perfect fit is illusory.
That’s it! We have trained our net to learn and continue the sinusoidal shape. Now, I know that this is a rather academic example, but to train a neural net to learn more complex shapes is straightforward from here.