From a0c19286783e040d0ee87a70b8257f99474b8714 Mon Sep 17 00:00:00 2001 From: Size43 Date: Thu, 21 May 2015 15:28:58 +0200 Subject: Reduced LOC + FeedSorter sorts by category, author, title and date/time + reduced learning rate from 0.3 to 0.2 --- .../java/org/rssin/neurons/FeedSorterTest.java | 96 +++++++++++++++++---- .../java/org/rssin/neurons/NeuralNetworkTest.java | 10 +-- .../main/java/org/rssin/neurons/FeedSorter.java | 97 ++++++++++++++-------- .../java/org/rssin/neurons/MultiNeuralNetwork.java | 1 + .../main/java/org/rssin/neurons/NeuralNetwork.java | 15 +++- app/src/main/java/org/rssin/neurons/Neuron.java | 4 +- .../java/org/rssin/neurons/SentenceSplitter.java | 27 ++++++ 7 files changed, 189 insertions(+), 61 deletions(-) create mode 100755 app/src/main/java/org/rssin/neurons/SentenceSplitter.java (limited to 'app') diff --git a/app/src/androidTest/java/org/rssin/neurons/FeedSorterTest.java b/app/src/androidTest/java/org/rssin/neurons/FeedSorterTest.java index a2d59ee..a6e3581 100755 --- a/app/src/androidTest/java/org/rssin/neurons/FeedSorterTest.java +++ b/app/src/androidTest/java/org/rssin/neurons/FeedSorterTest.java @@ -20,7 +20,7 @@ public class FeedSorterTest extends TestCase { Assert.assertTrue(true); } - public void testSortItems() throws Exception { + public void testSortItemsByCategory() throws Exception { List gameList = new ArrayList<>(); gameList.add("Games"); List sportList = new ArrayList<>(); @@ -39,28 +39,90 @@ public class FeedSorterTest extends TestCase { FeedSorter s = new FeedSorter(); //I like games & I hate sports - for(int i = 0; i < 100; i++) - { - for(FeedItem item : likedItems) - { - s.feedback(item, Feedback.Like); - } - - for(FeedItem item : dislikedItems) - { - s.feedback(item, Feedback.Dislike); - } - } + trainNetwork(likedItems, dislikedItems, s); FeedItem sportsItem = new FeedItem("", new Date(2014, 1, 1, 1, 2, 1), "SPORT ARTICLE", "DESCRIPTION", "", "Randy", sportList, "", "", ""); FeedItem gamesItem = new FeedItem("", new Date(2014, 1, 1, 1, 1, 1), "GAME ARTICLE", "DESCRIPTION", "", "Randy", gameList, "", "", ""); + testSortingOrder(s, sportsItem, gamesItem); + } + + public void testSortItemsByTitle() throws Exception { + List emptyList = new ArrayList<>(); + + FeedItem[] likedItems = new FeedItem[] + { + new FeedItem("", new Date(), "Video games are cool", "DESCRIPTION", "", "Randy", emptyList, "", "", ""), + new FeedItem("", new Date(), "The new video game", "DESCRIPTION", "", "Camil", emptyList, "", "", ""), + new FeedItem("", new Date(), "Best games of 2015", "DESCRIPTION", "", "Jos", emptyList, "", "", ""), + }; + + FeedItem[] dislikedItems = new FeedItem[] + { + new FeedItem("", new Date(), "Video of a cat", "DESCRIPTION", "", "Randy", emptyList, "", "", ""), + new FeedItem("", new Date(), "It's raining", "DESCRIPTION", "", "Joep", emptyList, "", "", ""), + new FeedItem("", new Date(), "Shocking video of a cat in the rain.", "DESCRIPTION", "", "Joep", emptyList, "", "", ""), + }; + + FeedSorter s = new FeedSorter(); + + //I like games & I hate sports + trainNetwork(likedItems, dislikedItems, s); + + FeedItem dislikedItem = new FeedItem("", new Date(2014, 1, 1, 1, 2, 1), "Another cool video of a cat in the sun.", "DESCRIPTION", "", "Randy", emptyList, "", "", ""); + FeedItem likedItem = new FeedItem("", new Date(2014, 1, 1, 1, 1, 1), "Coolest retro games", "DESCRIPTION", "", "Jos", emptyList, "", "", ""); + + testSortingOrder(s, dislikedItem, likedItem); + } + + public void testSortItemsByAuthor() throws Exception { + List emptyList = new ArrayList<>(); + + FeedItem[] likedItems = new FeedItem[] + { + new FeedItem("", new Date(), "Best games of 2015", "DESCRIPTION", "", "Jos", emptyList, "", "", ""), + new FeedItem("", new Date(), "It's raining cats and dogs!", "DESCRIPTION", "", "Jos", emptyList, "", "", ""), + }; + + FeedItem[] dislikedItems = new FeedItem[] + { + new FeedItem("", new Date(), "Video of a cat", "DESCRIPTION", "", "Randy", emptyList, "", "", ""), + new FeedItem("", new Date(), "It's raining", "DESCRIPTION", "", "Joep", emptyList, "", "", ""), + new FeedItem("", new Date(), "Shocking video of a cat in the rain.", "DESCRIPTION", "", "Joep", emptyList, "", "", ""), + new FeedItem("", new Date(), "Video games are cool", "DESCRIPTION", "", "Randy", emptyList, "", "", ""), + new FeedItem("", new Date(), "The new video game", "DESCRIPTION", "", "Camil", emptyList, "", "", ""), + }; + + FeedSorter s = new FeedSorter(); + + //I like games & I hate sports + trainNetwork(likedItems, dislikedItems, s); + + FeedItem dislikedItem = new FeedItem("", new Date(2014, 1, 1, 1, 2, 1), "Another cool video of a cat in the sun.", "DESCRIPTION", "", "Randy", emptyList, "", "", ""); + FeedItem likedItem = new FeedItem("", new Date(2014, 1, 1, 1, 1, 1), "Coolest retro games", "DESCRIPTION", "", "Jos", emptyList, "", "", ""); + + testSortingOrder(s, dislikedItem, likedItem); + } + + private void testSortingOrder(FeedSorter s, FeedItem dislikedItem, FeedItem likedItem) { List testItems = new LinkedList<>(); - testItems.add(sportsItem); - testItems.add(gamesItem); + testItems.add(dislikedItem); + testItems.add(likedItem); List sortedItems = s.sortItems(testItems); - Assert.assertEquals(sortedItems.get(0), gamesItem); - Assert.assertEquals(sortedItems.get(1), sportsItem); + Assert.assertEquals(sortedItems.get(0), likedItem); + Assert.assertEquals(sortedItems.get(1), dislikedItem); + } + + private void trainNetwork(FeedItem[] likedItems, FeedItem[] dislikedItems, FeedSorter s) { + for(int i = 0; i < 200; i++) { + for (FeedItem item : likedItems) { + s.feedback(item, Feedback.Like); + } + + for (FeedItem item : dislikedItems) { + s.feedback(item, Feedback.Dislike); + } + } } } \ No newline at end of file diff --git a/app/src/androidTest/java/org/rssin/neurons/NeuralNetworkTest.java b/app/src/androidTest/java/org/rssin/neurons/NeuralNetworkTest.java index b0f6eea..57776f3 100755 --- a/app/src/androidTest/java/org/rssin/neurons/NeuralNetworkTest.java +++ b/app/src/androidTest/java/org/rssin/neurons/NeuralNetworkTest.java @@ -7,12 +7,12 @@ public class NeuralNetworkTest extends TestCase { public void testAnd() throws Exception { MultiNeuralNetwork nn = new MultiNeuralNetwork(10, 2); - nn.addInput(); - nn.addInput(); - nn.addInput(); + nn.addInput();//bias + nn.addInput();//inputA + nn.addInput();//inputB //Simple AND - for (int i = 0; i < 100; i++) + for (int i = 0; i < 300; i++) { PredictionInterface p1 = nn.computeOutput(new double[] { 1, @@ -75,7 +75,7 @@ public class NeuralNetworkTest extends TestCase { nn.addInput(); //Simple AND - for (int i = 0; i < 100; i++) + for (int i = 0; i < 300; i++) { PredictionInterface p1 = nn.computeOutput(new double[] { 1, diff --git a/app/src/main/java/org/rssin/neurons/FeedSorter.java b/app/src/main/java/org/rssin/neurons/FeedSorter.java index 157a8f7..28d45c1 100755 --- a/app/src/main/java/org/rssin/neurons/FeedSorter.java +++ b/app/src/main/java/org/rssin/neurons/FeedSorter.java @@ -25,6 +25,7 @@ public class FeedSorter implements Serializable{ private final int MAX_TRAINING_HISTORY = 250; private final int SECONDS_IN_DAY = 24 * 60 * 60; + private final SentenceSplitter splitter = new SentenceSplitter(); private MultiNeuralNetwork nn = new MultiNeuralNetwork(25, 50); @@ -34,8 +35,8 @@ public class FeedSorter implements Serializable{ private int[] isNthWeekDayInput = new int[7]; private int isMorning, isAfternoon, isEvening, isNight, biasInput; private Hashtable categoryInputs = new Hashtable(); - //private Hashtable wordInputs = new Hashtable(); - //private Hashtable feedSourceInputs = new Hashtable(); + private Hashtable wordInputs = new Hashtable(); + private Hashtable authorInputs = new Hashtable(); public FeedSorter() { createNewNetwork(); @@ -60,46 +61,24 @@ public class FeedSorter implements Serializable{ } private PredictionInterface getPrediction(FeedItem item) { - //Add new inputs for categories. - for(String category : item.getCategory()) - { - category = category.toLowerCase(); - if(!categoryInputs.containsKey(category)) - { - categoryInputs.put(category, nn.addInput()); - } - } + List words = splitter.splitSentence(item.getTitle()); - double[] inputs = new double[nn.getInputCount()]; + addNewCategoryInputs(item); + addNewTitleWordInputs(words); + addNewAuthorInputs(item); + double[] inputs = newArrayInitializedToNegativeOne(); inputs[biasInput] = 1; - //Initialize all inputs to -1 / false - for(int i = 0; i < inputs.length; i++) - { - inputs[i] = -1; - } + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); //Set month - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - for(int i = 0; i < isNthMonthInput.length; i++) - { - if(cal.get(Calendar.MONTH) - cal.getMinimum(Calendar.MONTH) == i) - { - inputs[isNthMonthInput[i]] = 1; - } - } + inputs[isNthMonthInput[cal.get(Calendar.MONTH) - cal.getMinimum(Calendar.MONTH)]] = 1; //Set weekday - for(int i = 0; i < isNthWeekDayInput.length; i++) - { - if(cal.get(Calendar.DAY_OF_WEEK) - cal.getMinimum(Calendar.DAY_OF_WEEK) == i) - { - inputs[isNthMonthInput[i]] = 1; - } - } + inputs[isNthWeekDayInput[cal.get(Calendar.DAY_OF_WEEK) - cal.getMinimum(Calendar.DAY_OF_WEEK)]] = 1; - //Set day + //Set time int hourOfDay = cal.get(Calendar.HOUR_OF_DAY); if(hourOfDay > 6 && hourOfDay < 12) { @@ -120,9 +99,56 @@ public class FeedSorter implements Serializable{ inputs[categoryInputs.get(category.toLowerCase())] = 1; } + for(String word : words) + { + inputs[wordInputs.get(word)] = 1; + } + + if(item.getAuthor() != null) { + inputs[authorInputs.get(item.getAuthor().toLowerCase())] = 1; + } + return nn.computeOutput(inputs); } + private double[] newArrayInitializedToNegativeOne() { + double[] inputs = new double[nn.getInputCount()]; + Arrays.fill(inputs, 0, inputs.length, -1); + return inputs; + } + + private void addNewCategoryInputs(FeedItem item) { + for(String category : item.getCategory()) + { + category = category.toLowerCase(); + if(!categoryInputs.containsKey(category)) + { + categoryInputs.put(category, nn.addInput()); + } + } + } + + private void addNewAuthorInputs(FeedItem item) + { + if(item.getAuthor() != null) { + String author = item.getAuthor().toLowerCase(); + if (!authorInputs.containsKey(author)) { + authorInputs.put(author, nn.addInput()); + } + } + } + + private void addNewTitleWordInputs(List words) { + for(String word : words) + { + word = word.toLowerCase(); + if(!wordInputs.containsKey(word)) + { + wordInputs.put(word, nn.addInput()); + } + } + } + /** * Provides feedback to the neural network. * @param item The feeditem. @@ -167,12 +193,13 @@ public class FeedSorter implements Serializable{ */ public List sortItems(List items) { // Sort list based on something like date + nn.computeOutput() * DAY. - List newItems = new ArrayList(items); + final List newItems = new ArrayList(items); final Hashtable predictions = new Hashtable<>(); for(FeedItem feed : newItems) { - predictions.put(feed, getPrediction(feed)); + PredictionInterface prediction = getPrediction(feed); + predictions.put(feed, prediction); } Collections.sort(newItems, new Comparator() { diff --git a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java index a2f06eb..68ff390 100755 --- a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java @@ -4,6 +4,7 @@ import java.io.Serializable; /** * Created by Jos on 14-5-2015. + * Is used to migitate the problem of neural networks ending up in the wrong local minimum. */ class MultiNeuralNetwork implements Serializable{ private static final long serialVersionUID = 0; diff --git a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java index 620d5bd..7fe003f 100755 --- a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java @@ -26,9 +26,11 @@ class NeuralNetwork implements Serializable{ } public int addInput() { + assert hiddenNodes.length > 0; + int result = 0; - for (int i = 0; i < hiddenNodes.length; i++) { - result = hiddenNodes[i].addWeight(); + for (Neuron hiddenNode : hiddenNodes) { + result = hiddenNode.addWeight(); } return result; @@ -37,6 +39,7 @@ class NeuralNetwork implements Serializable{ public PredictionInterface computeOutput(double[] inputs) { double[] intermediateValues = new double[outputNode.getWeightCount()]; + //Output of hidden neurons for (int neuronNum = 0; neuronNum < hiddenNodes.length; neuronNum++) { Neuron n = hiddenNodes[neuronNum]; @@ -57,6 +60,7 @@ class NeuralNetwork implements Serializable{ } void learn(NeuralNetworkPrediction p, double expectedOutput) { + //TODO: See if adding momentum helps avoid local minima double actualOutput = p.getOutput(); double[] intermediateValues = p.getIntermediateValues(); double[] inputs = p.getInputs(); @@ -76,8 +80,13 @@ class NeuralNetwork implements Serializable{ hiddenGradients[i] = hiddenDerivative * outputGradient * outputNode.getWeight(i); } + updateWeights(intermediateValues, inputs, hiddenGradients, outputGradient); + } + + private void updateWeights(double[] intermediateValues, double[] inputs, double[] hiddenGradients, double outputGradient) { + final double learningRate = 0.2; + //Update input => hidden weights. - final double learningRate = 0.3; for (int neuronNum = 0; neuronNum < hiddenNodes.length; neuronNum++) { Neuron n = hiddenNodes[neuronNum]; diff --git a/app/src/main/java/org/rssin/neurons/Neuron.java b/app/src/main/java/org/rssin/neurons/Neuron.java index 724732d..d668ebc 100755 --- a/app/src/main/java/org/rssin/neurons/Neuron.java +++ b/app/src/main/java/org/rssin/neurons/Neuron.java @@ -23,7 +23,9 @@ class Neuron { } public int addWeight() { - weights.add(r.nextDouble() * 2 - 1); + // Initial values range from -.5 to .5. The exact value does not matter, + // as long as they aren't all 0. + weights.add(r.nextDouble() - .5); return weights.size() - 1; } diff --git a/app/src/main/java/org/rssin/neurons/SentenceSplitter.java b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java new file mode 100755 index 0000000..fc5f46f --- /dev/null +++ b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java @@ -0,0 +1,27 @@ +package org.rssin.neurons; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Jos on 21-5-2015. + */ +public class SentenceSplitter { + public SentenceSplitter() + { } + + public List splitSentence(String sentence) + { + List allMatches = new ArrayList<>(); + Matcher m = Pattern.compile("[\\w-]+").matcher(sentence); + + while (m.find()) + { + allMatches.add(m.group().toLowerCase()); + } + + return allMatches; + } +} -- cgit v1.2.3 From eaf127ffbcb9a113df21ace2e6d5505b03bd0e87 Mon Sep 17 00:00:00 2001 From: Size43 Date: Thu, 21 May 2015 15:35:50 +0200 Subject: This wasn't committed for some reason. --- app/src/main/java/org/rssin/neurons/SentenceSplitter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/src/main/java/org/rssin/neurons/SentenceSplitter.java b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java index fc5f46f..6fefe52 100755 --- a/app/src/main/java/org/rssin/neurons/SentenceSplitter.java +++ b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java @@ -9,13 +9,14 @@ import java.util.regex.Pattern; * Created by Jos on 21-5-2015. */ public class SentenceSplitter { + private Pattern wordMatch = Pattern.compile("[\\w-]+"); public SentenceSplitter() { } public List splitSentence(String sentence) { List allMatches = new ArrayList<>(); - Matcher m = Pattern.compile("[\\w-]+").matcher(sentence); + Matcher m = wordMatch.matcher(sentence); while (m.find()) { -- cgit v1.2.3 From 31e5e27e01fe868fa73960d62fcae74ec4f6058d Mon Sep 17 00:00:00 2001 From: Halzyn Date: Thu, 21 May 2015 15:09:49 +0200 Subject: Needs Testing doe het wel een keer als mn internet werkt want FUCK EDUROAM --- .../java/org/rssin/rss/FeedLoaderTest.java | 23 +++++++++ app/src/main/java/org/rssin/rss/FeedLoader.java | 56 +++++++++++----------- 2 files changed, 50 insertions(+), 29 deletions(-) create mode 100644 app/src/androidTest/java/org/rssin/rss/FeedLoaderTest.java (limited to 'app') diff --git a/app/src/androidTest/java/org/rssin/rss/FeedLoaderTest.java b/app/src/androidTest/java/org/rssin/rss/FeedLoaderTest.java new file mode 100644 index 0000000..f4bbfdd --- /dev/null +++ b/app/src/androidTest/java/org/rssin/rss/FeedLoaderTest.java @@ -0,0 +1,23 @@ +package org.rssin.rss; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import java.net.URL; + +/** + * Created by Randy on 21-5-2015. + */ +public class FeedLoaderTest extends TestCase { + + + public void testFetchXML() throws Exception { + String urlstring = "http://www.pcworld.com/index.rss"; + URL url = new URL(urlstring); + FeedLoader loader = new FeedLoader(url); + loader.fetchXML(); + FeedItem f = loader.getFeed().getPosts().get(0); + Assert.assertEquals(f.getTitle(), "Amazon adds local groceries and meals to one-hour Prime Now delivery service"); + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/rssin/rss/FeedLoader.java b/app/src/main/java/org/rssin/rss/FeedLoader.java index 726cbac..eabfebd 100644 --- a/app/src/main/java/org/rssin/rss/FeedLoader.java +++ b/app/src/main/java/org/rssin/rss/FeedLoader.java @@ -25,7 +25,7 @@ public class FeedLoader { private String text; public FeedLoader(URL url){ - this.urlString = url; + this.setUrlString(url); } /** @@ -48,15 +48,15 @@ public class FeedLoader { case "item": post = new FeedItem(null, null, null, null, null, null, new LinkedList(), null, null, null); - chan = false; //this starts collection information for the + chan = false; //this starts collecting information for the //separate items. break; case "image": imageTagParse(myParser); case "channel": feed = new Feed(new LinkedList(), null, null, null, null, - null, null, null, null, null, null, null, null, null, null, - null, null, null, null); + null, null, null, null, null, null, null, null, null, null, + null, null, null, null); chan = true; break; } @@ -214,31 +214,25 @@ public class FeedLoader { /** * Retrieves the XML and parses it. */ - public void fetchXML(){ - Thread thread = new Thread(new Runnable(){ - @Override - public void run() { - try { - URL url = urlString; - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setReadTimeout(10000 /* milliseconds */); - conn.setConnectTimeout(15000 /* milliseconds */); - conn.setRequestMethod("GET"); - conn.setDoInput(true); - // Starts the query - conn.connect(); - InputStream stream = conn.getInputStream(); - xmlFactoryObject = XmlPullParserFactory.newInstance(); - XmlPullParser myparser = xmlFactoryObject.newPullParser(); - myparser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - myparser.setInput(stream, null); - parseXMLAndStoreIt(myparser); - stream.close(); - } catch (Exception ignored) { - } - } - }); - thread.start(); + public void fetchXML() { + try { + URL url = urlString; + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(10000 /* milliseconds */); + conn.setConnectTimeout(15000 /* milliseconds */); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + // Starts the query + conn.connect(); + InputStream stream = conn.getInputStream(); + xmlFactoryObject = XmlPullParserFactory.newInstance(); + XmlPullParser myparser = xmlFactoryObject.newPullParser(); + myparser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + myparser.setInput(stream, null); + parseXMLAndStoreIt(myparser); + stream.close(); + } catch (Exception ignored) { + } } public void setFeed(Feed feed) { @@ -248,4 +242,8 @@ public class FeedLoader { public Feed getFeed() { return feed; } + + public void setUrlString(URL urlString) { + this.urlString = urlString; + } } -- cgit v1.2.3 From 82160a53daa4e7f1598680f7b3e009268d0e9099 Mon Sep 17 00:00:00 2001 From: Halzyn Date: Thu, 21 May 2015 16:32:17 +0200 Subject: New icon --- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 2791 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 1838 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 3976 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 6489 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9188 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (limited to 'app') diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bc..2e7f377 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0c..c7d5ca5 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0..8d1bf3a 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72c..9e7e711 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..63c5a0d Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ -- cgit v1.2.3 From 0ec23727a995dea4ca2ae595f39cbc177668159a Mon Sep 17 00:00:00 2001 From: Size43 Date: Thu, 21 May 2015 16:33:18 +0200 Subject: Fixed Serializable attributes & cleaned up code. --- .../main/java/org/rssin/neurons/FeedSorter.java | 145 +++++++-------------- app/src/main/java/org/rssin/neurons/Feedback.java | 23 ++-- .../java/org/rssin/neurons/MultiNeuralNetwork.java | 11 +- .../neurons/MultiNeuralNetworkPrediction.java | 23 ++-- .../main/java/org/rssin/neurons/NeuralNetwork.java | 31 ++--- .../org/rssin/neurons/NeuralNetworkPrediction.java | 10 +- app/src/main/java/org/rssin/neurons/Neuron.java | 9 +- .../org/rssin/neurons/PredictionInterface.java | 10 +- .../java/org/rssin/neurons/SentenceSplitter.java | 21 +-- .../main/java/org/rssin/neurons/TrainingCase.java | 11 +- .../java/org/rssin/rssin/FeedLoaderAndSorter.java | 12 +- 11 files changed, 129 insertions(+), 177 deletions(-) (limited to 'app') diff --git a/app/src/main/java/org/rssin/neurons/FeedSorter.java b/app/src/main/java/org/rssin/neurons/FeedSorter.java index 28d45c1..8549fcf 100755 --- a/app/src/main/java/org/rssin/neurons/FeedSorter.java +++ b/app/src/main/java/org/rssin/neurons/FeedSorter.java @@ -1,56 +1,41 @@ package org.rssin.neurons; -import android.gesture.Prediction; - import org.rssin.rss.FeedItem; -import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.TimeZone; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ -public class FeedSorter implements Serializable{ +public class FeedSorter implements Serializable { private static final long serialVersionUID = 0; - private final int MAX_TRAINING_HISTORY = 250; - private final int SECONDS_IN_DAY = 24 * 60 * 60; private final SentenceSplitter splitter = new SentenceSplitter(); + private final MultiNeuralNetwork nn = new MultiNeuralNetwork(25, 50); + private final List trainingCases = new ArrayList<>(); - private MultiNeuralNetwork nn = new MultiNeuralNetwork(25, 50); - - private List trainingCases = new ArrayList<>(); - - private int[] isNthMonthInput = new int[12]; - private int[] isNthWeekDayInput = new int[7]; - private int isMorning, isAfternoon, isEvening, isNight, biasInput; - private Hashtable categoryInputs = new Hashtable(); - private Hashtable wordInputs = new Hashtable(); - private Hashtable authorInputs = new Hashtable(); + private final int[] isNthMonthInput = new int[12]; + private final int[] isNthWeekDayInput = new int[7]; + private final int isMorning, isAfternoon, isEvening, isNight, biasInput; + private final Hashtable categoryInputs = new Hashtable<>(); + private final Hashtable wordInputs = new Hashtable<>(); + private final Hashtable authorInputs = new Hashtable<>(); public FeedSorter() { - createNewNetwork(); - } - - private void createNewNetwork() { biasInput = nn.addInput(); - for(int i = 0; i < 12; i++) - { + for (int i = 0; i < 12; i++) { isNthMonthInput[i] = nn.addInput(); } - for(int i = 0; i < 7; i++) - { + for (int i = 0; i < 7; i++) { isNthWeekDayInput[i] = nn.addInput(); } @@ -63,48 +48,40 @@ public class FeedSorter implements Serializable{ private PredictionInterface getPrediction(FeedItem item) { List words = splitter.splitSentence(item.getTitle()); - addNewCategoryInputs(item); - addNewTitleWordInputs(words); - addNewAuthorInputs(item); + addNewInputs(item.getCategory(), categoryInputs); + addNewInputs(words, wordInputs); + addNewInput(item.getAuthor(), authorInputs); double[] inputs = newArrayInitializedToNegativeOne(); inputs[biasInput] = 1; Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - //Set month + //Set month & weekday inputs[isNthMonthInput[cal.get(Calendar.MONTH) - cal.getMinimum(Calendar.MONTH)]] = 1; - - //Set weekday inputs[isNthWeekDayInput[cal.get(Calendar.DAY_OF_WEEK) - cal.getMinimum(Calendar.DAY_OF_WEEK)]] = 1; //Set time int hourOfDay = cal.get(Calendar.HOUR_OF_DAY); - if(hourOfDay > 6 && hourOfDay < 12) - { + if (hourOfDay > 6 && hourOfDay < 12) { inputs[isMorning] = 1; - }else if(hourOfDay >= 12 && hourOfDay <= 6) - { + } else if (hourOfDay >= 12 && hourOfDay <= 6) { inputs[isAfternoon] = 1; - }else if(hourOfDay >= 6 && hourOfDay < 23) - { + } else if (hourOfDay >= 6 && hourOfDay < 23) { inputs[isEvening] = 1; - }else if(hourOfDay >= 23 || hourOfDay <= 6) - { + } else if (hourOfDay >= 23 || hourOfDay <= 6) { inputs[isNight] = 1; } - for(String category : item.getCategory()) - { + for (String category : item.getCategory()) { inputs[categoryInputs.get(category.toLowerCase())] = 1; } - for(String word : words) - { + for (String word : words) { inputs[wordInputs.get(word)] = 1; } - if(item.getAuthor() != null) { + if (item.getAuthor() != null) { inputs[authorInputs.get(item.getAuthor().toLowerCase())] = 1; } @@ -117,52 +94,35 @@ public class FeedSorter implements Serializable{ return inputs; } - private void addNewCategoryInputs(FeedItem item) { - for(String category : item.getCategory()) - { - category = category.toLowerCase(); - if(!categoryInputs.containsKey(category)) - { - categoryInputs.put(category, nn.addInput()); - } - } - } - - private void addNewAuthorInputs(FeedItem item) - { - if(item.getAuthor() != null) { - String author = item.getAuthor().toLowerCase(); - if (!authorInputs.containsKey(author)) { - authorInputs.put(author, nn.addInput()); - } + private void addNewInputs(Iterable words, Hashtable map) { + for (String word : words) { + addNewInput(word, map); } } - private void addNewTitleWordInputs(List words) { - for(String word : words) - { + private void addNewInput(String word, Hashtable map) { + if (word != null) { word = word.toLowerCase(); - if(!wordInputs.containsKey(word)) - { - wordInputs.put(word, nn.addInput()); + if (!map.containsKey(word)) { + map.put(word, nn.addInput()); } } } /** * Provides feedback to the neural network. - * @param item The feeditem. + * + * @param item The feeditem. * @param feedback The feedback. Like will move these types of items up in the list, * dislike will move them down. */ - public void feedback(FeedItem item, Feedback feedback) - { + public void feedback(FeedItem item, Feedback feedback) { PredictionInterface prediction = getPrediction(item); prediction.learn(feedback.toExpectedOutput()); trainingCases.add(new TrainingCase(prediction.getInputs(), feedback)); - while(trainingCases.size() > MAX_TRAINING_HISTORY) - { + final int MAX_TRAINING_HISTORY = 250; + while (trainingCases.size() > MAX_TRAINING_HISTORY) { trainingCases.remove(0); } } @@ -170,13 +130,10 @@ public class FeedSorter implements Serializable{ /** * Runs an iteration of training, using feedback that was provided previously using FeedSorter.feedback(...). */ - public void train() - { - for(TrainingCase t : trainingCases) - { + public void train() { + for (TrainingCase t : trainingCases) { double[] inputs = t.getInputs(); - if(inputs.length < nn.getInputCount()) - { + if (inputs.length < nn.getInputCount()) { // Resize array to fit new input size inputs = Arrays.copyOf(inputs, nn.getInputCount()); } @@ -188,30 +145,28 @@ public class FeedSorter implements Serializable{ /** * Returns a sorted list of all the items in the List, according to the neural network. + * * @param items The list of items. * @return A new, sorted, list of items. The parameter items is not modified. */ public List sortItems(List items) { - // Sort list based on something like date + nn.computeOutput() * DAY. - final List newItems = new ArrayList(items); - final Hashtable predictions = new Hashtable<>(); - - for(FeedItem feed : newItems) - { - PredictionInterface prediction = getPrediction(feed); - predictions.put(feed, prediction); + final int SECONDS_IN_DAY = 24 * 60 * 60; + + final List newItems = new ArrayList<>(items); + final Hashtable predictions = new Hashtable<>(); + + for (FeedItem item : newItems) { + PredictionInterface prediction = getPrediction(item); + predictions.put(item, (long) (prediction.getOutput() * SECONDS_IN_DAY)); } Collections.sort(newItems, new Comparator() { @Override public int compare(FeedItem lhs, FeedItem rhs) { - PredictionInterface lPrediction = predictions.get(lhs), - rPrediction = predictions.get(rhs); - - long lhsSeconds = (long)(lhs.getPubDate().getTime() / 1000 + lPrediction.getOutput() * SECONDS_IN_DAY); - long rhsSeconds = (long)(rhs.getPubDate().getTime() / 1000 + rPrediction.getOutput() * SECONDS_IN_DAY); + long lhsScore = lhs.getPubDate().getTime() / 1000 + predictions.get(lhs); + long rhsScore = rhs.getPubDate().getTime() / 1000 + predictions.get(rhs); - return (int)Math.signum(rhsSeconds - lhsSeconds); + return (int) Math.signum(rhsScore - lhsScore); } }); diff --git a/app/src/main/java/org/rssin/neurons/Feedback.java b/app/src/main/java/org/rssin/neurons/Feedback.java index 9652b2b..fe59b1b 100755 --- a/app/src/main/java/org/rssin/neurons/Feedback.java +++ b/app/src/main/java/org/rssin/neurons/Feedback.java @@ -1,21 +1,18 @@ package org.rssin.neurons; /** - * Created by Jos on 19-5-2015. + * @author Jos. */ public enum Feedback { - Like, Dislike; + Like(1.0d), Dislike(-1.0d); - double toExpectedOutput() - { - switch(this) - { - case Like: - return 1; - case Dislike: - return -1; - default: - throw new IllegalArgumentException(); - } + private final double expectedOutput; + + private Feedback(double expectedOutput) { + this.expectedOutput = expectedOutput; + } + + double toExpectedOutput() { + return expectedOutput; } } diff --git a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java index 68ff390..03ad2d1 100755 --- a/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/MultiNeuralNetwork.java @@ -3,12 +3,12 @@ package org.rssin.neurons; import java.io.Serializable; /** - * Created by Jos on 14-5-2015. - * Is used to migitate the problem of neural networks ending up in the wrong local minimum. + * @author Jos + * Is used to migitate the problem of neural networks ending up in the wrong local minimum. */ -class MultiNeuralNetwork implements Serializable{ +class MultiNeuralNetwork implements Serializable { private static final long serialVersionUID = 0; - private NeuralNetwork[] networks; + private final NeuralNetwork[] networks; public MultiNeuralNetwork(int numNetworks, int numHiddenNodes) { networks = new NeuralNetwork[numNetworks]; @@ -28,8 +28,7 @@ class MultiNeuralNetwork implements Serializable{ public PredictionInterface computeOutput(double[] inputs) { PredictionInterface[] predictions = new PredictionInterface[networks.length]; - for(int i = 0; i < predictions.length; i++) - { + for (int i = 0; i < predictions.length; i++) { predictions[i] = networks[i].computeOutput(inputs); } diff --git a/app/src/main/java/org/rssin/neurons/MultiNeuralNetworkPrediction.java b/app/src/main/java/org/rssin/neurons/MultiNeuralNetworkPrediction.java index dc85261..f618749 100755 --- a/app/src/main/java/org/rssin/neurons/MultiNeuralNetworkPrediction.java +++ b/app/src/main/java/org/rssin/neurons/MultiNeuralNetworkPrediction.java @@ -1,22 +1,20 @@ package org.rssin.neurons; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ class MultiNeuralNetworkPrediction implements PredictionInterface { - private PredictionInterface[] predictions; - MultiNeuralNetworkPrediction(PredictionInterface[] predictions) - { - if(predictions.length <= 0) - { + private final PredictionInterface[] predictions; + + MultiNeuralNetworkPrediction(PredictionInterface[] predictions) { + if (predictions.length <= 0) { throw new IllegalArgumentException("predictions"); } this.predictions = predictions; } - public double getOutput() - { + public double getOutput() { double average = 0; for (PredictionInterface prediction : predictions) { average += prediction.getOutput(); @@ -25,16 +23,13 @@ class MultiNeuralNetworkPrediction implements PredictionInterface { return average / (double) predictions.length; } - public void learn(double expectedOutput) - { - for(PredictionInterface prediction : predictions) - { + public void learn(double expectedOutput) { + for (PredictionInterface prediction : predictions) { prediction.learn(expectedOutput); } } - public double[] getInputs() - { + public double[] getInputs() { return predictions[0].getInputs(); } } diff --git a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java index 7fe003f..0acfda7 100755 --- a/app/src/main/java/org/rssin/neurons/NeuralNetwork.java +++ b/app/src/main/java/org/rssin/neurons/NeuralNetwork.java @@ -1,18 +1,19 @@ package org.rssin.neurons; +import android.annotation.SuppressLint; + import java.io.Serializable; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ -class NeuralNetwork implements Serializable{ +class NeuralNetwork implements Serializable { private static final long serialVersionUID = 0; - private Neuron[] hiddenNodes; - private Neuron outputNode; + private final Neuron[] hiddenNodes; + private final Neuron outputNode; - public NeuralNetwork(int numHiddenNodes) { - if(numHiddenNodes < 1) - { + NeuralNetwork(int numHiddenNodes) { + if (numHiddenNodes < 1) { throw new IllegalArgumentException("numHiddenNodes must be > 0"); } @@ -25,7 +26,8 @@ class NeuralNetwork implements Serializable{ outputNode = new Neuron(numHiddenNodes + 1); } - public int addInput() { + @SuppressLint("Assert") + int addInput() { assert hiddenNodes.length > 0; int result = 0; @@ -36,7 +38,7 @@ class NeuralNetwork implements Serializable{ return result; } - public PredictionInterface computeOutput(double[] inputs) { + PredictionInterface computeOutput(double[] inputs) { double[] intermediateValues = new double[outputNode.getWeightCount()]; //Output of hidden neurons @@ -60,20 +62,19 @@ class NeuralNetwork implements Serializable{ } void learn(NeuralNetworkPrediction p, double expectedOutput) { - //TODO: See if adding momentum helps avoid local minima + //TODO: See if adding momentum helps avoid local minimum double actualOutput = p.getOutput(); double[] intermediateValues = p.getIntermediateValues(); double[] inputs = p.getInputs(); - double[] hiddenGradients = new double[hiddenNodes.length]; - - //Calculate output gradients + //Calculate output gradient double outputDerivative = (1 - actualOutput) * (1 + actualOutput); //Derivative of HyperTan function double outputGradient = outputDerivative * (expectedOutput - actualOutput); - //Calulate hidden gradients + //Calculate hidden gradients + double[] hiddenGradients = new double[hiddenNodes.length]; for (int i = 0; i < hiddenGradients.length; i++) { //Derivative of HyperTan function double hiddenDerivative = (1 - intermediateValues[i]) * (1 + intermediateValues[i]); @@ -111,7 +112,7 @@ class NeuralNetwork implements Serializable{ else return Math.tanh(x); } - public int getInputCount() { + int getInputCount() { return hiddenNodes[0].getWeightCount(); } } diff --git a/app/src/main/java/org/rssin/neurons/NeuralNetworkPrediction.java b/app/src/main/java/org/rssin/neurons/NeuralNetworkPrediction.java index 9d6fc89..169caee 100755 --- a/app/src/main/java/org/rssin/neurons/NeuralNetworkPrediction.java +++ b/app/src/main/java/org/rssin/neurons/NeuralNetworkPrediction.java @@ -1,13 +1,13 @@ package org.rssin.neurons; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ class NeuralNetworkPrediction implements PredictionInterface { - private double[] inputs; - private double[] intermediateValues; - private double output; - private NeuralNetwork nn; + private final double[] inputs; + private final double[] intermediateValues; + private final double output; + private final NeuralNetwork nn; NeuralNetworkPrediction(NeuralNetwork nn, double[] inputs, double[] intermediateValues, double output) { this.inputs = inputs; diff --git a/app/src/main/java/org/rssin/neurons/Neuron.java b/app/src/main/java/org/rssin/neurons/Neuron.java index d668ebc..23f69e1 100755 --- a/app/src/main/java/org/rssin/neurons/Neuron.java +++ b/app/src/main/java/org/rssin/neurons/Neuron.java @@ -1,17 +1,18 @@ package org.rssin.neurons; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Random; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ -class Neuron { +class Neuron implements Serializable { private static final long serialVersionUID = 0; - private static Random r = new Random(); + private static final Random r = new Random(); - private List weights = new ArrayList(); + private final List weights = new ArrayList<>(); public Neuron() { } diff --git a/app/src/main/java/org/rssin/neurons/PredictionInterface.java b/app/src/main/java/org/rssin/neurons/PredictionInterface.java index 27a214f..ff46992 100755 --- a/app/src/main/java/org/rssin/neurons/PredictionInterface.java +++ b/app/src/main/java/org/rssin/neurons/PredictionInterface.java @@ -1,10 +1,12 @@ package org.rssin.neurons; /** - * Created by Jos on 14-5-2015. + * @author Jos. */ interface PredictionInterface { - public double getOutput(); - public void learn(double expectedOutput); - public double[] getInputs(); + double getOutput(); + + void learn(double expectedOutput); + + double[] getInputs(); } diff --git a/app/src/main/java/org/rssin/neurons/SentenceSplitter.java b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java index 6fefe52..887439d 100755 --- a/app/src/main/java/org/rssin/neurons/SentenceSplitter.java +++ b/app/src/main/java/org/rssin/neurons/SentenceSplitter.java @@ -6,20 +6,25 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Created by Jos on 21-5-2015. + * @author Jos. */ public class SentenceSplitter { - private Pattern wordMatch = Pattern.compile("[\\w-]+"); - public SentenceSplitter() - { } + private final Pattern wordMatch = Pattern.compile("[\\w-]+");//For unicode support, add the Pattern.UNICODE_CHARACTER_CLASS flag. Works only in Java 7+. - public List splitSentence(String sentence) - { + public SentenceSplitter() { + } + + /** + * Returns all the words in a sentence. + * + * @param sentence The sentence. + * @return The list of words in a sentence. + */ + public List splitSentence(String sentence) { List allMatches = new ArrayList<>(); Matcher m = wordMatch.matcher(sentence); - while (m.find()) - { + while (m.find()) { allMatches.add(m.group().toLowerCase()); } diff --git a/app/src/main/java/org/rssin/neurons/TrainingCase.java b/app/src/main/java/org/rssin/neurons/TrainingCase.java index 77162be..69f72cb 100755 --- a/app/src/main/java/org/rssin/neurons/TrainingCase.java +++ b/app/src/main/java/org/rssin/neurons/TrainingCase.java @@ -3,15 +3,14 @@ package org.rssin.neurons; import java.io.Serializable; /** - * Created by Jos on 20-5-2015. + * @author Jos. */ class TrainingCase implements Serializable { - private static long serialVersionID; - private double[] inputs; - private Feedback feedback; + private static final long serialVersionUID = 0; + private final double[] inputs; + private final Feedback feedback; - public TrainingCase(double[] inputs, Feedback feedback) - { + public TrainingCase(double[] inputs, Feedback feedback) { this.inputs = inputs; this.feedback = feedback; } diff --git a/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java b/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java index eda0526..e9d3e5d 100755 --- a/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java +++ b/app/src/main/java/org/rssin/rssin/FeedLoaderAndSorter.java @@ -28,8 +28,7 @@ public class FeedLoaderAndSorter { { FeedLoader loader = new FeedLoader(feed.getURL()); loader.fetchXML(); - org.rssin.rss.Feed loadedFeed = loader.getFeed(); - for(FeedItem item : loadedFeed.getPosts()) + for(FeedItem item : loader.getFeed().getPosts()) { if(matchesKeyword(item)) { @@ -55,10 +54,9 @@ public class FeedLoaderAndSorter { return filter.getKeywords().size() == 0; } - private static boolean contains( String haystack, String needle ) { - haystack = haystack == null ? "" : haystack; - needle = needle == null ? "" : needle; - - return haystack.toLowerCase().contains(needle.toLowerCase()); + private static boolean contains(String haystack, String needle) { + return haystack != null + && needle != null + && haystack.toLowerCase().contains(needle.toLowerCase()); } } -- cgit v1.2.3