├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── mltoolkit ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── si │ │ └── uni_lj │ │ └── fri │ │ └── lrss │ │ └── machinelearningtoolkit │ │ ├── ClassifierList.java │ │ ├── MachineLearningManager.java │ │ ├── classifier │ │ ├── Classifier.java │ │ ├── DensityClustering.java │ │ ├── ID3.java │ │ ├── NaiveBayes.java │ │ ├── OnlineClassifier.java │ │ └── ZeroR.java │ │ └── utils │ │ ├── ClassifierConfig.java │ │ ├── Constants.java │ │ ├── Feature.java │ │ ├── FeatureNominal.java │ │ ├── FeatureNumeric.java │ │ ├── Instance.java │ │ ├── MLException.java │ │ ├── Signature.java │ │ └── Value.java │ └── res │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ └── values │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MachineLearningToolkit 2 | ====================== 3 | 4 | Machine learning toolkit is an Android library that enables local 5 | machine learning computations on the phone. The library preserves 6 | the state of a learner over the application lifecycle. I.e. it allows 7 | saving the state on a persistent file, and loading it once the application 8 | restarts. It also allows external file loading, thus enables learner 9 | sharing among multiple devices. 10 | 11 | https://github.com/vpejovic/MachineLearningToolkit/ 12 | 13 | To use Machine Learning Toolkit in your Android project, simply put: 14 | 15 | compile 'si.uni_lj.fri.lrss.machinelearningtoolkit:mltoolkit:1.2' 16 | 17 | in your app's build.gradle file 18 | 19 | 20 | Copyright (c) 2013, University of Birmingham, UK 21 | Veljko Pejovic, 22 | 23 | This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 24 | Computing for Positive Behaviour Change) Project. For more information, please visit 25 | http://www.ubhave.org 26 | 27 | Permission to use, copy, modify, and/or distribute this software for any purpose with 28 | or without fee is hereby granted, provided that the above copyright notice and this 29 | permission notice appear in all copies. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 32 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 33 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 34 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 35 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 36 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 37 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 38 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /mltoolkit/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | library.iml 3 | -------------------------------------------------------------------------------- /mltoolkit/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.jfrog.bintray' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.1" 7 | resourcePrefix "mltoolkit__" 8 | 9 | defaultConfig { 10 | minSdkVersion 8 11 | targetSdkVersion 23 12 | versionCode 2 13 | versionName "1.2" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | 24 | def siteUrl = 'https://github.com/vpejovic/MachineLearningToolkit' // Homepage URL of the library 25 | def gitUrl = 'https://github.com/vpejovic/MachineLearningToolkit.git' // Git repository URL 26 | group = "si.uni_lj.fri.lrss.machinelearningtoolkit" // Maven Group ID for the artifact 27 | version = "1.2" 28 | 29 | Properties properties = new Properties() 30 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 31 | 32 | bintray { 33 | user = properties.getProperty("bintray.user") 34 | key = properties.getProperty("bintray.apikey") 35 | 36 | configurations = ['archives'] 37 | pkg { 38 | repo = "maven" 39 | name = "Machinelearningtoolkit" 40 | websiteUrl = siteUrl 41 | vcsUrl = gitUrl 42 | licenses = ["Apache-2.0"] 43 | publish = true 44 | version { 45 | name = '1.2' 46 | desc = 'Machine learning toolkit 1.2 final' 47 | released = new Date() 48 | vcsTag = '1.2' 49 | } 50 | } 51 | } 52 | 53 | install { 54 | repositories.mavenInstaller { 55 | // This generates POM.xml with proper parameters 56 | pom { 57 | project { 58 | packaging 'aar' 59 | 60 | // Add your description here 61 | name 'Machine learning library for Android' 62 | url siteUrl 63 | 64 | // Set your license 65 | licenses { 66 | license { 67 | name 'The Apache Software License, Version 2.0' 68 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 69 | } 70 | } 71 | developers { 72 | developer { 73 | id '' 74 | name 'Veljko Pejovic' 75 | email 'Veljko.Pejovic@fri.uni-lj.si' 76 | } 77 | } 78 | scm { 79 | connection gitUrl 80 | developerConnection gitUrl 81 | url siteUrl 82 | 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | dependencies { 90 | compile fileTree(dir: 'libs', include: ['*.jar']) 91 | compile 'com.google.code.gson:gson:2.3' 92 | } 93 | 94 | 95 | task sourcesJar(type: Jar) { 96 | from android.sourceSets.main.java.srcDirs 97 | classifier = 'sources' 98 | } 99 | 100 | task javadoc(type: Javadoc) { 101 | source = android.sourceSets.main.java.srcDirs 102 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 103 | } 104 | 105 | task javadocJar(type: Jar, dependsOn: javadoc) { 106 | classifier = 'javadoc' 107 | from javadoc.destinationDir 108 | } 109 | artifacts { 110 | archives javadocJar 111 | archives sourcesJar 112 | } -------------------------------------------------------------------------------- /mltoolkit/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/veljko/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /mltoolkit/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/ClassifierList.java: -------------------------------------------------------------------------------- 1 | package si.uni_lj.fri.lrss.machinelearningtoolkit; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import java.util.HashMap; 7 | 8 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.Classifier; 9 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.DensityClustering; 10 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.ID3; 11 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.NaiveBayes; 12 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.ZeroR; 13 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 14 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 15 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 16 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 17 | 18 | /** 19 | * 20 | * Takes care of classifier instantiation and registration. 21 | * Every classifier that is created has a unique name. 22 | * 23 | * @author Veljko Pejovic, University of Birmingham, UK 24 | * 25 | */ 26 | public class ClassifierList { 27 | 28 | private static final String TAG = "ClassifierList"; 29 | 30 | private HashMap mNamedClassifiers; 31 | 32 | //private final Random d_keyGenerator; 33 | 34 | protected ClassifierList(){ 35 | if (Constants.DEBUG) Log.d(TAG, "ClassifierList empty constructor"); 36 | mNamedClassifiers = new HashMap(); 37 | //d_keyGenerator = new Random(); 38 | } 39 | 40 | private static Classifier createClassifier ( 41 | int type, 42 | Signature signature, 43 | ClassifierConfig config) throws MLException{ 44 | 45 | if (Constants.DEBUG) Log.d(TAG, "createClassifier"); 46 | 47 | switch (type) { 48 | case Constants.TYPE_NAIVE_BAYES: 49 | if (Constants.DEBUG) Log.d(TAG, "create NaiveBayes"); 50 | return new NaiveBayes(signature, config); 51 | case Constants.TYPE_ID3: 52 | if (Constants.DEBUG) Log.d(TAG, "create ID3"); 53 | return new ID3(signature, config); 54 | case Constants.TYPE_DENSITY_CLUSTER: 55 | if (Constants.DEBUG) Log.d(TAG, "create DensityClustering"); 56 | return new DensityClustering(signature, config); 57 | case Constants.TYPE_ZERO_R: 58 | if (Constants.DEBUG) Log.d(TAG, "create ZeroR"); 59 | return new ZeroR(signature, config); 60 | default: 61 | if (Constants.DEBUG) Log.d(TAG, "create default (NaiveBayes)"); 62 | return new NaiveBayes(signature, config); 63 | } 64 | } 65 | 66 | protected void removeClassifier(String a_classifierID) { 67 | if (mNamedClassifiers.containsKey(a_classifierID)) { 68 | mNamedClassifiers.remove(a_classifierID); 69 | } 70 | } 71 | 72 | protected Classifier getClassifier(String a_classifierID) 73 | { 74 | if (mNamedClassifiers.containsKey(a_classifierID)) { 75 | return mNamedClassifiers.get(a_classifierID); 76 | } else { 77 | return null; 78 | } 79 | } 80 | 81 | protected synchronized Classifier addClassifier( 82 | int type, Signature signature, ClassifierConfig config, String name) throws MLException { 83 | 84 | if (Constants.DEBUG) Log.d(TAG, "addClassifier"); 85 | 86 | Classifier classifier = createClassifier(type, signature, config); 87 | mNamedClassifiers.put(name, classifier); 88 | return classifier; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/MachineLearningManager.java: -------------------------------------------------------------------------------- 1 | package si.uni_lj.fri.lrss.machinelearningtoolkit; 2 | 3 | 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.io.OutputStreamWriter; 13 | import java.lang.reflect.Type; 14 | import java.util.Arrays; 15 | 16 | import android.content.Context; 17 | import android.os.Environment; 18 | import android.util.Log; 19 | 20 | import com.google.gson.Gson; 21 | import com.google.gson.GsonBuilder; 22 | import com.google.gson.JsonDeserializationContext; 23 | import com.google.gson.JsonDeserializer; 24 | import com.google.gson.JsonElement; 25 | import com.google.gson.JsonObject; 26 | import com.google.gson.JsonParseException; 27 | 28 | import si.uni_lj.fri.lrss.machinelearningtoolkit.ClassifierList; 29 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.Classifier; 30 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.DensityClustering; 31 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.ID3; 32 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.NaiveBayes; 33 | import si.uni_lj.fri.lrss.machinelearningtoolkit.classifier.ZeroR; 34 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 35 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 36 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 37 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 38 | 39 | /** 40 | * Deals with instantiating classifiers, sending training/test data 41 | * to the right classifier, namely the one that an application has instantiated earlier. 42 | * Has methods that allow it to save classifiers to a file, and load them from a file. 43 | * This should be used when a service that uses the manager is (re)started/destroyed. 44 | * Only one MachineLearningManager exists per context to ensure consistency. 45 | * 46 | * @author Veljko Pejovic, University of Birmingham, UK 47 | * 48 | */ 49 | public class MachineLearningManager { 50 | 51 | private static final String TAG = "MLManager"; 52 | 53 | private static MachineLearningManager sManager; 54 | private final ClassifierList mClassifiers; 55 | private final Context mContext; 56 | private static final Object sLock = new Object(); 57 | 58 | /** 59 | * Instantiates a new manager to be used for communication between the ML library and the 60 | * overlying application. If the manager for the given context has been instantiated already, 61 | * the existing objects will be returned. The returned manager is a singleton class for the 62 | * given context. 63 | * @param context Context for which the manager is to be instantiated. 64 | * @return Manager for the given context. 65 | * @throws MLException 66 | */ 67 | public static MachineLearningManager getMLManager(Context context) throws MLException { 68 | 69 | if (context == null) { 70 | throw new MLException(MLException.INVALID_PARAMETER, 71 | " Invalid parameter, context object passed is null"); 72 | } 73 | if (sManager == null) 74 | synchronized (sLock) 75 | { 76 | if (sManager == null) sManager = new MachineLearningManager(context); 77 | } 78 | return sManager; 79 | } 80 | 81 | private MachineLearningManager(Context context) throws MLException { 82 | mContext = context; 83 | // automatic loading if classifiers exist on the device 84 | if (Arrays.asList(mContext.fileList()).contains(Constants.CLASSIFIER_STORAGE_FILE)){ 85 | mClassifiers = loadFromPersistent(); 86 | } 87 | else{ 88 | mClassifiers = new ClassifierList(); 89 | } 90 | } 91 | 92 | /** 93 | * Instantiates a new, or returns an existing classifier with the given name and properties. 94 | * Properties include classifier type (available options can be found in {@link Constants}), 95 | * classifier signature ({@link Signature}) and classifier configuration 96 | * ({@link ClassifierConfig}). 97 | * @param type Type of classifier. One of the options from {@link Constants}). 98 | * @param signature Classifier signature 99 | * @param config Classifier configuration, if any. 100 | * @param name Unique classifier name. 101 | * @return A new, or an existing classifier if one with the given name is already instantiated. 102 | */ 103 | public Classifier addClassifier(int type, Signature signature, 104 | ClassifierConfig config, String name) throws MLException { 105 | 106 | Log.d(TAG, "addClassifier"); 107 | 108 | Classifier cls = mClassifiers.getClassifier(name); 109 | 110 | // TODO: Expose classifier properties so that we can check 111 | // if the existing classifier is the same as the one we require. 112 | if (cls != null) { 113 | //Log.d(TAG, "return existing classifier"); 114 | return cls; 115 | } 116 | //throw new MLException(MLException.CLASSIFIER_EXISTS, "Classifier "+name+" already exists."); 117 | //Log.d(TAG, "return brand new classifier"); 118 | return mClassifiers.addClassifier(type, signature, config, name); 119 | } 120 | 121 | /** 122 | * Removes a classifier if it exists. If a classifier with the given name does not exist, nothing 123 | * will happen. 124 | * @param name Classifier name. 125 | */ 126 | public void removeClassifier(String name){ 127 | mClassifiers.removeClassifier(name); 128 | } 129 | 130 | /** 131 | * Returns a classifier with the given name, or null if such a classifier doesn't exist. 132 | * @param name Classifier name. 133 | * @return An instance of the classifier with the given name. 134 | */ 135 | public Classifier getClassifier(String name){ 136 | return mClassifiers.getClassifier(name); 137 | } 138 | 139 | public String getJSON() { 140 | Gson gson = new Gson(); 141 | return gson.toJson(mClassifiers); 142 | } 143 | 144 | /** 145 | * Saves classifiers to an external json-formatted file. 146 | * @param filename Desired classifier file name. 147 | */ 148 | public void saveToPersistentExternal(String filename) throws MLException { 149 | Gson gson = new Gson(); 150 | String jsonString = gson.toJson(mClassifiers); 151 | 152 | try { 153 | String root = Environment.getExternalStorageDirectory().toString(); 154 | File file = new File(root + filename); 155 | FileOutputStream fos = new FileOutputStream(file); 156 | OutputStreamWriter osw = new OutputStreamWriter(fos); 157 | osw.write(jsonString); 158 | osw.flush(); 159 | osw.close(); 160 | } catch (FileNotFoundException e) { 161 | throw new MLException(MLException.IO_FILE_NOT_FOUND_ERROR, "File "+filename+" not found."); 162 | } catch (IOException e) { 163 | throw new MLException(MLException.IO_ERROR, "IO exception while writing "+filename+"."); 164 | } 165 | } 166 | 167 | /** 168 | * Saves classifiers to a persistent internal file. The overlying application should call this 169 | * method before the application is closed/destroyed. 170 | */ 171 | public void saveToPersistent() throws MLException { 172 | Gson gson = new Gson(); 173 | String jsonString = gson.toJson(mClassifiers); 174 | 175 | try { 176 | FileOutputStream fos = mContext 177 | .openFileOutput(Constants.CLASSIFIER_STORAGE_FILE, Context.MODE_PRIVATE); 178 | OutputStreamWriter osw = new OutputStreamWriter(fos); 179 | osw.write(jsonString); 180 | osw.flush(); 181 | osw.close(); 182 | } catch (FileNotFoundException e) { 183 | throw new MLException(MLException.IO_FILE_NOT_FOUND_ERROR, "Classifier file not found."); 184 | } catch (IOException e) { 185 | throw new MLException(MLException.IO_ERROR, "IO exception while writing internal storage."); 186 | } 187 | } 188 | 189 | /** 190 | * Loads classifiers from a given file residing on the external storage. 191 | * @param filename JSON formatted file with classifier information. 192 | * @return List of classifiers loaded from the file. 193 | */ 194 | public ClassifierList loadFromExternalPersistent(String filename) throws MLException { 195 | 196 | StringBuilder jsonString = new StringBuilder(); 197 | 198 | try { 199 | File sdcard = Environment.getExternalStorageDirectory(); 200 | File file = new File(sdcard,filename); 201 | FileReader fr = new FileReader(file); 202 | BufferedReader br = new BufferedReader(fr); 203 | 204 | String line; 205 | while ((line = br.readLine()) != null) { 206 | jsonString.append(line); 207 | Log.d(TAG, "Read "+line); 208 | } 209 | br.close(); 210 | } catch (FileNotFoundException e) { 211 | throw new MLException(MLException.IO_FILE_NOT_FOUND_ERROR, "File "+filename+" not found."); 212 | } catch (IOException e) { 213 | throw new MLException(MLException.IO_ERROR, "IO exception while writing "+filename+"."); 214 | } 215 | 216 | Gson gson = new GsonBuilder() 217 | .registerTypeHierarchyAdapter(Classifier.class, new ClassifierAdapter()) 218 | .create(); 219 | 220 | return (ClassifierList) gson.fromJson(jsonString.toString(), ClassifierList.class); 221 | } 222 | 223 | /** 224 | * Loads classifiers from a persistent internal file. The method is called automatically when 225 | * the ML manager is instantiated. 226 | */ 227 | public ClassifierList loadFromPersistent() throws MLException { 228 | 229 | StringBuilder jsonString = new StringBuilder(); 230 | 231 | try { 232 | FileInputStream is = mContext.openFileInput(Constants.CLASSIFIER_STORAGE_FILE); 233 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 234 | 235 | String line; 236 | while ((line = br.readLine()) != null) { 237 | jsonString.append(line); 238 | } 239 | br.close(); 240 | } catch (FileNotFoundException e) { 241 | throw new MLException(MLException.IO_FILE_NOT_FOUND_ERROR, "Classifier file not found."); 242 | } catch (IOException e) { 243 | throw new MLException(MLException.IO_ERROR, "IO exception while writing internal storage."); 244 | } 245 | 246 | Gson gson = new GsonBuilder() 247 | .registerTypeHierarchyAdapter(Classifier.class, new ClassifierAdapter()) 248 | .create(); 249 | 250 | return (ClassifierList) gson.fromJson(jsonString.toString(), ClassifierList.class); 251 | } 252 | 253 | static class ClassifierAdapter implements JsonDeserializer { 254 | 255 | Gson gson; 256 | 257 | ClassifierAdapter(){ 258 | GsonBuilder gsonBuilder = new GsonBuilder(); 259 | gson = gsonBuilder.create(); 260 | } 261 | 262 | public Classifier deserialize(JsonElement elem, Type type, JsonDeserializationContext context) 263 | throws JsonParseException { 264 | Classifier result = null; 265 | 266 | JsonObject object = elem.getAsJsonObject(); 267 | int intType = object.get("mType").getAsInt(); 268 | switch(intType){ 269 | case Constants.TYPE_NAIVE_BAYES: 270 | result = gson.fromJson(elem, NaiveBayes.class); 271 | break; 272 | case Constants.TYPE_ID3: 273 | result = gson.fromJson(elem, ID3.class); 274 | break; 275 | case Constants.TYPE_DENSITY_CLUSTER: 276 | result = gson.fromJson(elem, DensityClustering.class); 277 | break; 278 | case Constants.TYPE_ZERO_R: 279 | result = gson.fromJson(elem, ZeroR.class); 280 | break; 281 | } 282 | if (result != null) result.printClassifierInfo(); 283 | return result; 284 | } 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/classifier/Classifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 24 | 25 | import java.util.ArrayList; 26 | 27 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 28 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 29 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 30 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 31 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Value; 32 | 33 | /** 34 | * Every classifier has a signature that defines the features it uses, 35 | * as well as the class feature. It needs to support training and classification. 36 | * 37 | * @author Veljko Pejovic, University of Birmingham, UK 38 | * 39 | */ 40 | public abstract class Classifier { 41 | 42 | protected Signature mSignature; 43 | 44 | protected int mType; 45 | 46 | protected ClassifierConfig mConfig; 47 | 48 | protected boolean mTrained; 49 | 50 | private static final String TAG = "Classifier"; 51 | 52 | public Classifier(Signature a_signature, ClassifierConfig a_config) { 53 | mSignature = a_signature; 54 | mConfig = a_config; 55 | } 56 | 57 | /** 58 | * Train the classifier with labelled data instances. 59 | * @param instances Labelled data instances. The instances have to correspond to the classifier 60 | * signature. 61 | * @throws MLException 62 | */ 63 | public abstract void train(ArrayList instances) throws MLException; 64 | 65 | /** 66 | * Classify an unlabelled instance. 67 | * @param instance Instance to be classified. 68 | * @return The inferred label corresponding to the given instance. 69 | * @throws MLException 70 | */ 71 | public abstract Value classify(Instance instance) throws MLException; 72 | 73 | public abstract void printClassifierInfo(); 74 | 75 | public boolean isTrained() { 76 | return mTrained; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/classifier/DensityClustering.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 24 | 25 | import java.util.ArrayList; 26 | import java.util.HashMap; 27 | import java.util.Iterator; 28 | 29 | import android.util.Log; 30 | 31 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 32 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 33 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Feature; 34 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.FeatureNominal; 35 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 36 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 37 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 38 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Value; 39 | 40 | /** 41 | * This classifier calculates centroids of labelled, clustered data instances. 42 | * The classifier first removes cluster outliers using the density method, 43 | * i.e. if less than a given percentage of other data instances are in the 44 | * epsilon environment of a point, the point is removed as an outlier. 45 | * Cluster centroids are then calculated. The classifier is not an online 46 | * classifier, and is batch trained from given instances. 47 | * 48 | * At the classification time, an instance is given a label that corresponds 49 | * to the closest cluster centroid. The distance used for density and closeness 50 | * calculation is euclidean distance. In case only two nominal features exist, 51 | * the classifier assumes that GPS coordinates are given, thus it adjusts the 52 | * distance calculation according to the harvesine formula. 53 | * 54 | * The classifier can only be instantiated with a nominal class feature and one 55 | * or more numeric attribute features. 56 | * 57 | * LIMITATION: If no data are present for a label, the assigned centroid will 58 | * have all zeros coordinates. 59 | * 60 | * @author Veljko Pejovic, University of Birmingham, UK 61 | * 62 | */ 63 | public class DensityClustering extends Classifier { 64 | 65 | private static final String TAG = "DensityClustering"; 66 | 67 | private HashMap mCentroids; 68 | 69 | private HashMap mNumTrains; 70 | 71 | private double mMaxDistance; 72 | 73 | private double mMinInclusionPct; 74 | 75 | private static double toRad(double a_degree) { 76 | return Math.PI*a_degree/180.0; 77 | } 78 | 79 | private static double distance(final double[] coordsA,final double[] coordsB) 80 | throws MLException { 81 | 82 | if (coordsA.length != coordsB.length) { 83 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 84 | "Instance is not compatible with the dataset used for classifier construction."); 85 | } 86 | 87 | // We assume GPS coordinates if vectors of size two are given 88 | if (coordsA.length == 2) { 89 | 90 | double lat1 = coordsA[0]; 91 | double lon1 = coordsA[1]; 92 | double lat2 = coordsB[0]; 93 | double lon2 = coordsB[1]; 94 | double R = 6371.0; 95 | double dLat = toRad(lat2 - lat1); 96 | double dLon = toRad(lon2 - lon1); 97 | double radLat1 = toRad(lat1); 98 | double radLat2 = toRad(lat2); 99 | 100 | double a = Math.sin(dLat/2.0) * Math.sin(dLat/2.0) + 101 | Math.sin(dLon/2.0) * Math.sin(dLon/2.0) * Math.cos(radLat1) * Math.cos(radLat2); 102 | double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 103 | 104 | return R * c * 1000.0; 105 | } 106 | // Otherwise Euclidean distance 107 | else { 108 | double sqrSum = 0; 109 | for(int i=0; i classValues = classFeature.getValues(); 140 | 141 | mNumTrains = new HashMap(); 142 | mCentroids = new HashMap(); 143 | for (String classValue : classValues) { 144 | mCentroids.put(classValue, new double[mSignature.size() - 1]); 145 | mNumTrains.put(classValue, 0); 146 | } 147 | 148 | } 149 | 150 | @Override 151 | public void train(ArrayList instances) throws MLException { 152 | 153 | if (Constants.DEBUG) Log.d(TAG, "train with "+instances.size()+" instances"); 154 | 155 | // Remove outliers (density based) 156 | String curLabel; 157 | double curCoordValues[] = new double[mSignature.size()-1]; 158 | 159 | for (Iterator curIter = instances.iterator(); curIter.hasNext();) { 160 | 161 | Instance curInstance = curIter.next(); 162 | 163 | if (!mSignature.checkCompliance(curInstance, true)){ 164 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 165 | "Instance is not compatible with the dataset used for classifier construction."); 166 | } 167 | 168 | curLabel = (String) curInstance.getValueAtIndex(mSignature.getClassIndex()).getValue(); 169 | 170 | for(int i=0; i< mSignature.size()-1; i++) { 171 | curCoordValues[i] = (Double) curInstance.getValueAtIndex(i).getValue(); 172 | } 173 | 174 | String otherLabel; 175 | double otherCoordValues[] = new double[mSignature.size()-1]; 176 | 177 | int total = 0; 178 | int totalInside = 0; 179 | 180 | for (Iterator otherIter = instances.iterator(); otherIter.hasNext();) { 181 | 182 | Instance otherInstance = otherIter.next(); 183 | 184 | if (otherInstance != curInstance) { 185 | 186 | if (!mSignature.checkCompliance(otherInstance, true)){ 187 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 188 | "Instance is not compatible with the dataset used for classifier construction."); 189 | } 190 | 191 | otherLabel = (String) otherInstance.getValueAtIndex(mSignature.getClassIndex()).getValue(); 192 | 193 | if (otherLabel.equals(curLabel)) { 194 | 195 | for(int i=0; i< mSignature.size()-1; i++) { 196 | otherCoordValues[i] = (Double) otherInstance.getValueAtIndex(i).getValue(); 197 | } 198 | 199 | double distance = distance(curCoordValues, otherCoordValues); 200 | 201 | total++; 202 | 203 | if (distance< mMaxDistance) totalInside++; 204 | } 205 | } 206 | } 207 | 208 | if (Constants.DEBUG) Log.d(TAG, "Points: "+totalInside+"/"+total+" vs " 209 | +mMinInclusionPct+"/100"); 210 | if (total > 0) { 211 | if (totalInside/(double)total < (mMinInclusionPct /100.0)){ 212 | if (Constants.DEBUG) Log.d(TAG, "Remove instance"); 213 | curIter.remove(); 214 | } 215 | } 216 | 217 | } 218 | // At this point only those instances that are tightly packed are in d_instanceQ 219 | if (Constants.DEBUG) Log.d(TAG, "Outliers removed. "+instances.size()+" instances left."); 220 | 221 | // Find cluster centroids 222 | double centroidCoords[]; 223 | for (Instance curInstance : instances) { 224 | 225 | curLabel = (String) curInstance.getValueAtIndex(mSignature.getClassIndex()).getValue(); 226 | centroidCoords = mCentroids.get(curLabel); 227 | 228 | if (Constants.DEBUG) Log.d(TAG, "Current instance label "+curLabel); 229 | 230 | for(int i=0; i< mSignature.size()-1; i++) { 231 | if (Constants.DEBUG) Log.d(TAG, "added coord "+i+ " with value " 232 | +(Double) curInstance.getValueAtIndex(i).getValue()); 233 | centroidCoords[i] += (Double) curInstance.getValueAtIndex(i).getValue(); 234 | } 235 | 236 | mNumTrains.put(curLabel, mNumTrains.get(curLabel)+1); 237 | 238 | } 239 | 240 | int numTrains; 241 | for (String classValue : mCentroids.keySet()) { 242 | centroidCoords = mCentroids.get(classValue); 243 | numTrains = mNumTrains.get(classValue); 244 | 245 | if (Constants.DEBUG) Log.d(TAG, "Centroid with label "+classValue 246 | +" contains " +numTrains+ " points."); 247 | 248 | for (int i=0; i< mSignature.size()-1; i++) { 249 | if (numTrains > 0) 250 | centroidCoords[i] = centroidCoords[i]/numTrains; 251 | // otherwise keep them to zero 252 | } 253 | 254 | mCentroids.put(classValue, centroidCoords); 255 | } 256 | 257 | } 258 | 259 | @Override 260 | public Value classify(Instance instance) throws MLException { 261 | 262 | if (!mSignature.checkCompliance(instance, false)){ 263 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 264 | "Instance is not compatible with the dataset used for classifier construction."); 265 | } 266 | 267 | // Calculate the centroid that is the closest 268 | double minDistance = Double.MAX_VALUE; 269 | 270 | // if not yet trained, we return the first label 271 | String minStringValue = ((FeatureNominal)mSignature.getClassFeature()).getValues().get(0); 272 | double centroidCoords[]; 273 | double curCoordValues[] = new double[mSignature.size()-1]; 274 | for(int i=0; i< mSignature.size()-1; i++) { 275 | curCoordValues[i] = (Double) instance.getValueAtIndex(i).getValue(); 276 | } 277 | 278 | for (String classValue : mCentroids.keySet()) { 279 | centroidCoords = mCentroids.get(classValue); 280 | double curDistance = Double.MAX_VALUE; 281 | try { 282 | curDistance = distance(curCoordValues, centroidCoords); 283 | } catch (MLException e) { 284 | // TODO Auto-generated catch block 285 | e.printStackTrace(); 286 | } 287 | if (curDistance < minDistance){ 288 | minStringValue = classValue; 289 | minDistance = curDistance; 290 | } 291 | } 292 | 293 | return new Value(minStringValue, Value.NOMINAL_VALUE); 294 | } 295 | 296 | public HashMap getCentroids(){ 297 | return mCentroids; 298 | } 299 | 300 | @Override 301 | public void printClassifierInfo() { 302 | StringBuilder builder = new StringBuilder(); 303 | builder.append("Classifer type: "+ mType +"\n"); 304 | builder.append("Signature: "+ mSignature.toString()+"\n"); 305 | builder.append("Centroids:\n"); 306 | for(String classValue : mCentroids.keySet()) { 307 | double centroidCoords[] = mCentroids.get(classValue); 308 | String coords = "["; 309 | for (int i=0; i 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | 24 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.HashMap; 29 | import java.util.Iterator; 30 | import java.util.Map.Entry; 31 | 32 | import android.util.Log; 33 | 34 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 35 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 36 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Feature; 37 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.FeatureNominal; 38 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 39 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 40 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 41 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Value; 42 | 43 | // TODO: Nominal attributes are supported for now. 44 | // TODO: How to handle missing attributes? 45 | // TODO: Pruning? 46 | 47 | /** 48 | * ID3, a tree-based classifier according to: 49 | * Quinlan, J. R. 1986. Induction of Decision Trees. Mach. Learn. 1, 1 (Mar. 1986), 81-106 50 | * This implementation supports nominal attributes only. 51 | * 52 | * @author Veljko Pejovic, University of Birmingham, UK 53 | * 54 | */ 55 | public class ID3 extends Classifier{ 56 | 57 | /* 58 | * Algorithm pseudocode (from Wikipedia): 59 | ID3 (Examples, Target_Attribute, Attributes) 60 | Create a root node for the tree 61 | If all examples are positive, Return the single-node tree Root, with label = +. 62 | If all examples are negative, Return the single-node tree Root, with label = -. 63 | If number of predicting attributes is empty, then Return the single node tree Root, 64 | with label = most common value of the target attribute in the examples. 65 | Otherwise Begin 66 | A <- The Attribute that best classifies examples. 67 | Decision Tree attribute for Root = A. 68 | For each possible value, v_i, of A, 69 | Add a new tree branch below Root, corresponding to the test A = v_i. 70 | Let Examples(v_i) be the subset of examples that have the value v_i for A 71 | If Examples(v_i) is empty 72 | Then below this new branch add a leaf node with label = most common target value in the examples 73 | Else below this new branch add the subtree ID3 (Examples(v_i), Target_Attribute, Attributes – {A}) 74 | End 75 | Return Root 76 | */ 77 | 78 | private Feature mBestFeature; 79 | private int mBestFeatureIndex; 80 | private int[] mCandidateFeatures; 81 | private Value mMajorValue; 82 | private boolean mIsLeaf; 83 | private HashMap mSubtrees; 84 | 85 | private static final String TAG = "ID3"; 86 | 87 | private static int[] allFeaturesArray(Signature signature) { 88 | int[] candidateFeatures = new int[signature.getFeatures().size()]; 89 | Arrays.fill(candidateFeatures, 1); 90 | candidateFeatures[signature.getClassIndex()] = 0; 91 | return candidateFeatures; 92 | } 93 | 94 | /** 95 | * Creates a new ID3 classifier with the given signature and configuration. 96 | * @param signature Classifier signature. 97 | * @param config Optional configuration parameters. 98 | */ 99 | public ID3(Signature signature, ClassifierConfig config) { 100 | this(signature, config, allFeaturesArray(signature)); 101 | } 102 | 103 | private ID3(Signature signature, ClassifierConfig config, int[] candidateFeatures) { 104 | super(signature, config); 105 | mType = Constants.TYPE_ID3; 106 | mSubtrees = new HashMap(); 107 | mCandidateFeatures = candidateFeatures; 108 | mIsLeaf = true; 109 | mMajorValue = null; 110 | mBestFeature = null; 111 | mBestFeatureIndex = 0; 112 | } 113 | 114 | @Override 115 | public void train(ArrayList instances) throws MLException { 116 | // Calculate stats such as: 117 | FeatureNominal classFeature = (FeatureNominal) mSignature.getClassFeature(); 118 | int[] classCounts = new int[classFeature.numberOfCategories()]; 119 | Arrays.fill(classCounts, 0); 120 | 121 | // if all Instances belong to a single class 122 | for (Instance i : instances) { 123 | Value classValue = i.getValueAtIndex(mSignature.getClassIndex()); 124 | int classValueInt = classFeature.indexOfCategory((String) classValue.getValue()); 125 | classCounts[classValueInt] += 1; 126 | } 127 | int NZcounter = 0, nonZeroClassValueIndex = 0; 128 | for (int i = 0; i < classCounts.length; i ++) 129 | if (classCounts[i] > 0) { 130 | NZcounter ++; 131 | nonZeroClassValueIndex = i; 132 | } 133 | if (NZcounter == 1){ 134 | mIsLeaf = true; 135 | mMajorValue = new Value(classFeature 136 | .categoryOfIndex(nonZeroClassValueIndex), Value.NOMINAL_VALUE); 137 | return; 138 | } 139 | 140 | // If number of predicting attributes is empty, then Return the single node tree Root, 141 | // with label = most common value of the target attribute in the examples. 142 | int numCandidateFeatures = 0; 143 | for (int indicator : mCandidateFeatures) 144 | numCandidateFeatures += indicator; 145 | if (numCandidateFeatures == 0) { 146 | mIsLeaf = true; 147 | int maxClassValueInt = 0; 148 | int maxClassValueCount = 0; 149 | for (int i=0; i> maxSubsets = null; 165 | 166 | double totalSetEntropy = calculateEntropy(instances); 167 | // H(S) - sum(p(t)*H(t))_for_attribute_A_the_data_is_split_in_T_sets 168 | // H(t) = - sum(p(x)log(p(x))) where x in X (set of class values) 169 | //TODO: There should be a faster way to check if there are any 170 | // interesting attributes left, before the entropy is calculated. 171 | 172 | for(int i=0; i< mSignature.getFeatures().size(); i++){ 173 | 174 | Feature feature = mSignature.getFeatureAtIndex(i); 175 | 176 | if (feature.getFeatureType() == Feature.NOMINAL && mCandidateFeatures[i]==1) { 177 | FeatureNominal featureNom = (FeatureNominal) feature; 178 | HashMap> subsets = new HashMap>(); 179 | int classFeatureCounts[] = new int[featureNom.numberOfCategories()]; 180 | Arrays.fill(classFeatureCounts, 0); 181 | 182 | for (Instance instance : instances) { 183 | 184 | Value featureValue = instance.getValueAtIndex(i); 185 | String featureValueName = (String) featureValue.getValue(); 186 | int featureValueInt = featureNom.indexOfCategory(featureValueName); 187 | 188 | classFeatureCounts[featureValueInt] += 1; 189 | 190 | ArrayList subset = subsets.get(featureValueName); 191 | if (subset == null) { 192 | subset = new ArrayList(); 193 | } 194 | subset.add(instance); 195 | subsets.put(featureValueName, subset); 196 | } 197 | 198 | 199 | Iterator>> it = subsets.entrySet().iterator(); 200 | double sumEntropies = 0; 201 | while (it.hasNext()) { 202 | Entry> pairs = it.next(); 203 | double pFeatureValue = pairs.getValue().size()/(double)instances.size(); 204 | double entropy = calculateEntropy(pairs.getValue()); 205 | sumEntropies += pFeatureValue * entropy; 206 | } 207 | double IGvalue = totalSetEntropy - sumEntropies; 208 | 209 | if (IGvalue > maxIG) { 210 | maxIG = IGvalue; 211 | maxIGindex = i; 212 | maxSubsets = subsets; 213 | } 214 | IG[i] = IGvalue; 215 | } 216 | } 217 | // Pick the best attribute 218 | mBestFeature = mSignature.getFeatureAtIndex(maxIGindex); 219 | mBestFeatureIndex = maxIGindex; 220 | mIsLeaf = false; 221 | 222 | // TODO: what happens if some of the deciding feature values are not observed 223 | // in the training set? Right now we point it to the majority class. 224 | 225 | if (mBestFeature.getFeatureType() == Feature.NOMINAL) { 226 | ArrayList featureValueList = ((FeatureNominal)mBestFeature).getValues(); 227 | int candidateFeatures[] = (int[]) mCandidateFeatures.clone(); 228 | candidateFeatures[maxIGindex] = 0; 229 | 230 | for (String value : featureValueList) { 231 | ID3 subTree = null; 232 | // TODO: Check what happens if maxSubsets is null 233 | if (maxSubsets.containsKey(value)) { 234 | subTree = new ID3(mSignature, mConfig, candidateFeatures); 235 | subTree.train(maxSubsets.get(value)); 236 | 237 | } else { 238 | // Shortcut to the majority class leaf. 239 | Arrays.fill(candidateFeatures, 0); 240 | subTree = new ID3(mSignature, mConfig, candidateFeatures); 241 | } 242 | mSubtrees.put(value, subTree); 243 | } 244 | } 245 | } 246 | // if no attributes left -> return majority class 247 | 248 | } 249 | 250 | // H(t) = - sum(p(x)log(p(x))) where x in X (set of class values) 251 | private double calculateEntropy (ArrayList instances) { 252 | 253 | double entropy = 0; 254 | 255 | FeatureNominal classFeature = (FeatureNominal) mSignature.getClassFeature(); 256 | 257 | int classCounts[] = new int[classFeature.numberOfCategories()]; 258 | int classCountsTotal = 0; 259 | Arrays.fill(classCounts, 0); 260 | 261 | for (Instance i : instances) { 262 | Value classValue = i.getValueAtIndex(mSignature.getClassIndex()); 263 | int classValueInt = classFeature.indexOfCategory((String) classValue.getValue()); 264 | classCounts[classValueInt] += 1; 265 | } 266 | 267 | for (int j=0; j 0) { 271 | entropy -= (probabilityFeatureValue)*Math.log(probabilityFeatureValue); 272 | } 273 | } 274 | 275 | return entropy; 276 | } 277 | 278 | @Override 279 | public Value classify(Instance a_instance) throws MLException { 280 | 281 | if (!mSignature.checkCompliance(a_instance, false)){ 282 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 283 | "Instance is not compatible with the dataset used for classifier construction."); 284 | } 285 | 286 | if (mIsLeaf) { 287 | return mMajorValue; 288 | } else { 289 | Value v = a_instance.getValueAtIndex(mBestFeatureIndex); 290 | ID3 nextTree = mSubtrees.get(v.getValue()); 291 | return nextTree.classify(a_instance); 292 | } 293 | } 294 | 295 | private String print(int depth) { 296 | String output; 297 | if (mIsLeaf) { 298 | output = mMajorValue.getValue().toString()+"\n"; 299 | } else { 300 | output = mBestFeature.name()+"\n"; 301 | Iterator> it = mSubtrees.entrySet().iterator(); 302 | while(it.hasNext()){ 303 | Entry pair = it.next(); 304 | String tmp = "\t"; 305 | for (int i=0; i "+pair.getValue().print(depth+1)+"\n"); 309 | } 310 | } 311 | return output; 312 | } 313 | 314 | @Override 315 | public void printClassifierInfo() { 316 | Log.i(TAG, this.print(0)); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/classifier/NaiveBayes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | import java.util.HashMap; 28 | 29 | import android.util.Log; 30 | 31 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 32 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 33 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Feature; 34 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.FeatureNominal; 35 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 36 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 37 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 38 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Value; 39 | 40 | /** 41 | * Naive Bayesian classifier that supports both nominal and numeric attributes. 42 | * Numeric features are modelled with a Gaussian distribution. 43 | * Laplace smoothing is supported for nominal attributes, so that classes with 44 | * high preference for a single value do not overfit. 45 | * The classifier is an online classifier, i.e. training can happen iteratively. 46 | * 47 | * @author Veljko Pejovic, University of Birmingham, UK 48 | * 49 | */ 50 | public class NaiveBayes extends Classifier implements OnlineClassifier { 51 | 52 | private static final String TAG = "NaiveBayes"; 53 | 54 | private final static Object mLock = new Object(); 55 | 56 | // For each feature we hold the count of occurrences of every class variable value. 57 | // These are further bisected to the feature values in case of NOMINAL features. 58 | // For NUMERIC features we keep stats necessary for Gaussian distribution calculation. 59 | private HashMap> mValueCounts; 60 | 61 | // Holds the number of occurrences of each value that the class variable may take. 62 | private double[] mClassCounts; 63 | 64 | // Fixes the problem of too few occurrences in certain bins. 65 | private boolean mLaplaceSmoothing; 66 | 67 | 68 | /** 69 | * Creates a new Naive Bayesian classifier with the given signature and configuration. 70 | * @param signature Classifier signature. 71 | * @param config Optional configuration parameters. 72 | */ 73 | public NaiveBayes(Signature signature, ClassifierConfig config) throws MLException { 74 | super(signature, config); 75 | mType = Constants.TYPE_NAIVE_BAYES; 76 | 77 | if (config.containsParam(Constants.LAPLACE_SMOOTHING)) { 78 | mLaplaceSmoothing = (Boolean) config.getParam(Constants.LAPLACE_SMOOTHING); 79 | } else { 80 | mLaplaceSmoothing = Constants.DEFAULT_LAPLACE_SMOOTHING; 81 | } 82 | 83 | initialize(); 84 | } 85 | 86 | public void initialize() throws MLException{ 87 | 88 | mValueCounts = new HashMap>(); 89 | ArrayList features = mSignature.getFeatures(); 90 | FeatureNominal classFeature = (FeatureNominal) mSignature.getClassFeature(); 91 | ArrayList classValues = classFeature.getValues(); 92 | 93 | mClassCounts = new double[classFeature.numberOfCategories()]; 94 | Arrays.fill(mClassCounts, 0.0); 95 | 96 | for(Feature feature : features){ 97 | HashMap featureCount = new HashMap(); 98 | 99 | for (String classValue : classValues){ 100 | double[] classFeatureCounts; 101 | if (feature.getFeatureType() == Feature.NOMINAL) { 102 | classFeatureCounts = new double[((FeatureNominal)feature).numberOfCategories()]; 103 | } 104 | else if (feature.getFeatureType() == Feature.NUMERIC) { 105 | // For NUMERIC values we have to keep: 106 | // - count 107 | // - sum of values 108 | // - sum of square values 109 | // so that we can get the normal distribution in the end 110 | classFeatureCounts = new double[3]; 111 | } 112 | else { 113 | throw new MLException(MLException.INCOMPATIBLE_FEATURE_TYPE, 114 | "Feature type neither nominal nor numeric"); 115 | } 116 | 117 | Arrays.fill(classFeatureCounts, 0.0); 118 | featureCount.put(classValue, classFeatureCounts); 119 | //Log.d(TAG, "Feature counts put for class value "+classValue); 120 | } 121 | String output = "Feature counts put for "+feature.name()+": "; 122 | for (String key : featureCount.keySet()) { 123 | String miniOutput = ""; 124 | double classFeatureCountsTest[] = featureCount.get(key); 125 | for (int i=0; i a_instances) throws MLException { 189 | 190 | for(Instance a_instance : a_instances){ 191 | synchronized (mLock) { 192 | this.update(a_instance); 193 | } 194 | } 195 | } 196 | 197 | 198 | public double[] getDistribution(Instance instance) throws MLException { 199 | 200 | if (!mSignature.checkCompliance(instance, false)){ 201 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 202 | "Instance is not compatible with the dataset used for classifier construction."); 203 | } 204 | 205 | FeatureNominal classFeature = (FeatureNominal) mSignature.getClassFeature(); 206 | ArrayList classValues = classFeature.getValues(); 207 | double[] classPriors = new double[classValues.size()]; 208 | double[] classPosteriors = new double[classValues.size()]; 209 | 210 | int classCountsTotal = 0; 211 | for (int j=0; j< mClassCounts.length; j++) classCountsTotal += mClassCounts[j]; 212 | for (int j=0; j feature counts 236 | HashMap featureCounts = mValueCounts.get(feature.name()); 237 | 238 | 239 | for (String classValue : classValues){ 240 | 241 | double[] classFeatureCounts = featureCounts.get(classValue); 242 | int indexOfClassValue = classFeature.indexOfCategory(classValue); 243 | double classFeatureTotal = 0; 244 | 245 | if (featureValue.getValueType() == Value.NOMINAL_VALUE) { 246 | FeatureNominal featureNom = (FeatureNominal) feature; 247 | String printFeatureCounts = "Class feature counts: "; 248 | for (double classFeatureCount : classFeatureCounts) { 249 | classFeatureTotal += classFeatureCount; 250 | printFeatureCounts += classFeatureCount + ", "; 251 | } 252 | if (Constants.DEBUG) Log.d(TAG, printFeatureCounts); 253 | 254 | int featureValueIndex = featureNom.indexOfCategory((String) featureValue.getValue()); 255 | if (Constants.DEBUG) Log.d(TAG, "Feature value "+featureValue.getValue().toString()+" index: "+featureValueIndex); 256 | 257 | if (mLaplaceSmoothing){ 258 | classFeatureProbs[indexOfClassValue]=classFeatureCounts[featureValueIndex]+1/ 259 | (classFeatureTotal + featureNom.numberOfCategories()); 260 | } 261 | else { 262 | if (classFeatureTotal > 0){ 263 | classFeatureProbs[indexOfClassValue]=classFeatureCounts[featureValueIndex]/classFeatureTotal; 264 | } 265 | } 266 | if (Constants.DEBUG) Log.d(TAG, "classFeatureProbs["+indexOfClassValue+"]= "+classFeatureProbs[indexOfClassValue]); 267 | classPosteriors[indexOfClassValue] *= classFeatureProbs[indexOfClassValue]; 268 | 269 | } else if (featureValue.getValueType() == Value.NUMERIC_VALUE) { 270 | 271 | double mean; 272 | double stdDev; 273 | // ignore features for which we have no data (those will have normalProb = 1) 274 | double normalProbability = 1; 275 | double featureValueDouble = (Double)featureValue.getValue(); 276 | 277 | if (classFeatureCounts[0] > 0) { 278 | mean = classFeatureCounts[1]/classFeatureCounts[0]; 279 | stdDev = Math.sqrt(classFeatureCounts[2] - Math.pow(mean,2)); 280 | normalProbability = Math.exp(Math.pow(featureValueDouble - mean ,2))/(2*stdDev*Math.sqrt(2*Math.PI)); 281 | // NOTE: if the current value equals the mean the normal probability goes to infinity; 282 | // to prevent this we, cap it to 1.0. 283 | if (Double.isInfinite(normalProbability)) normalProbability = 1.0; 284 | 285 | } 286 | 287 | if (Constants.DEBUG) Log.d(TAG, "calc for: "+ classValue 288 | +" and " + feature.name() 289 | +" resulting probability "+normalProbability+" total "+classPosteriors[indexOfClassValue]); 290 | 291 | classPosteriors[indexOfClassValue] *= normalProbability; 292 | } 293 | } 294 | } 295 | 296 | String outputPosterior="["; 297 | for (int i=0; i maxAposteriori){ 315 | maxAposteriori = classDistribution[i]; 316 | maxAposterioriIndex = i; 317 | } 318 | } 319 | 320 | // When the classifier is not yet trained we return the first class value 321 | if (maxAposterioriIndex == -1) { 322 | maxAposterioriIndex = 0; 323 | } 324 | 325 | if (mSignature.getClassFeature().getFeatureType() == Feature.NOMINAL) { 326 | return new Value(((FeatureNominal) mSignature.getClassFeature()) 327 | .categoryOfIndex(maxAposterioriIndex),Value.NOMINAL_VALUE); 328 | } 329 | else { 330 | throw new MLException(MLException.INCOMPATIBLE_FEATURE_TYPE, "class feature must be nominal"); 331 | } 332 | } 333 | } 334 | 335 | @Override 336 | public void printClassifierInfo() { 337 | 338 | FeatureNominal classFeature = (FeatureNominal)mSignature.getClassFeature(); 339 | StringBuilder builder = new StringBuilder(); 340 | builder.append("Classifier type: "+ mType +"\n"); 341 | builder.append("Signature: "+ mSignature.toString()+"\n"); 342 | builder.append("Class feature value counts: "); 343 | for (int i=0; i< classFeature.getValues().size(); i++){ 344 | 345 | builder.append("["+ classFeature.getValues().get(i)+":"+ mClassCounts[i]+"]"); 346 | } 347 | builder.append("\nOther feature value counts: \n"); 348 | for (String featureName : mValueCounts.keySet()){ 349 | builder.append(featureName+" "); 350 | HashMap counts = mValueCounts.get(featureName); 351 | for (String classFeatureValue : counts.keySet()) { 352 | builder.append("["+classFeatureValue+":"); 353 | double[] values = counts.get(classFeatureValue); 354 | for (int i=0; i 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 24 | 25 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 26 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 27 | 28 | /** 29 | * A classifier implements this interface if it allows iterative training. 30 | * 31 | * @author Veljko Pejovic, University of Birmingham, UK 32 | * 33 | */ 34 | public interface OnlineClassifier { 35 | 36 | /** 37 | * Update an online classifier with the given training instance. 38 | * @param instance A labelled instance with which we train the classifier. 39 | * @throws MLException 40 | */ 41 | public void update(Instance instance) throws MLException; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/classifier/ZeroR.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.classifier; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | 28 | import android.util.Log; 29 | 30 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.ClassifierConfig; 31 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Constants; 32 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Feature; 33 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.FeatureNominal; 34 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Instance; 35 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.MLException; 36 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Signature; 37 | import si.uni_lj.fri.lrss.machinelearningtoolkit.utils.Value; 38 | 39 | /** 40 | * 41 | * ZeroR classifier is not taking any features into account during the classification. 42 | * It merely outputs the mean value/most frequent class. 43 | * Besides classification, we can use ZeroR for regression. 44 | * 45 | * @author Veljko Pejovic, University of Birmingham, UK 46 | * 47 | */ 48 | public class ZeroR extends Classifier implements OnlineClassifier { 49 | 50 | private static final String TAG = "ZeroR"; 51 | 52 | private double[] mClassCounts; 53 | 54 | private static final Object mLock = new Object(); 55 | 56 | public ZeroR(Signature a_signature, ClassifierConfig a_config) { 57 | super(a_signature, a_config); 58 | Feature classFeature = mSignature.getClassFeature(); 59 | if (classFeature.getFeatureType() == Feature.NOMINAL) 60 | mClassCounts = new double[((FeatureNominal)classFeature).numberOfCategories()]; 61 | else if (classFeature.getFeatureType() == Feature.NUMERIC) 62 | mClassCounts = new double[2]; 63 | Arrays.fill(mClassCounts, 0.0); 64 | } 65 | 66 | @Override 67 | public void update(Instance instance) throws MLException { 68 | 69 | if (!mSignature.checkCompliance(instance, true)){ 70 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 71 | "Instance is not compatible with the dataset used for classifier construction."); 72 | } 73 | 74 | Feature classFeature = mSignature.getClassFeature(); 75 | 76 | Value classValue = instance.getValueAtIndex(mSignature.getClassIndex()); 77 | 78 | if (classFeature.getFeatureType() == Feature.NOMINAL) { 79 | int classValueInt = ((FeatureNominal)classFeature).indexOfCategory((String) classValue.getValue()); 80 | mClassCounts[classValueInt] += 1; 81 | } else if (classFeature.getFeatureType() == Feature.NUMERIC) { 82 | mClassCounts[0] += (Double) classValue.getValue(); 83 | mClassCounts[1] += 1; 84 | } 85 | } 86 | 87 | @Override 88 | public void train(ArrayList a_instances) throws MLException { 89 | for(Instance a_instance : a_instances){ 90 | synchronized (mLock) { 91 | this.update(a_instance); 92 | } 93 | } 94 | } 95 | 96 | @Override 97 | public Value classify(Instance instance) throws MLException { 98 | 99 | if (!mSignature.checkCompliance(instance, false)){ 100 | throw new MLException(MLException.INCOMPATIBLE_INSTANCE, 101 | "Instance is not compatible with the dataset used for classifier construction."); 102 | } 103 | if (((FeatureNominal)mSignature.getClassFeature()).getFeatureType() == Feature.NOMINAL) { 104 | double maxCount = 0; 105 | int maxValueIndex = 0; 106 | 107 | for (int i=0; i< mClassCounts.length; i++) { 108 | if (Constants.DEBUG) Log.d(TAG, "Class value index "+i+" count "+ mClassCounts[i]); 109 | if (mClassCounts[i] > maxCount) { 110 | maxValueIndex = i; 111 | maxCount = mClassCounts[i]; 112 | } 113 | } 114 | 115 | return new Value(((FeatureNominal)mSignature.getClassFeature()).categoryOfIndex(maxValueIndex), 116 | Value.NOMINAL_VALUE); 117 | } else { //it's NUMERIC 118 | double mean = mClassCounts[0]/ mClassCounts[1]; 119 | return new Value(mean, Value.NUMERIC_VALUE); 120 | } 121 | } 122 | 123 | @Override 124 | public void printClassifierInfo() { 125 | // TODO Auto-generated method stub 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/ClassifierConfig.java: -------------------------------------------------------------------------------- 1 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Set; 5 | 6 | public class ClassifierConfig { 7 | 8 | private HashMap mParams; 9 | 10 | public ClassifierConfig() { 11 | 12 | mParams = new HashMap(); 13 | 14 | } 15 | 16 | public void addParam(String param, Object value){ 17 | mParams.put(param, value); 18 | } 19 | 20 | public Object getParam(String param) { 21 | if (mParams.containsKey(param)) { 22 | return mParams.get(param); 23 | } 24 | return null; 25 | } 26 | 27 | public boolean containsParam(String param) { 28 | 29 | return mParams.containsKey(param); 30 | } 31 | 32 | public Set getAllParams() { 33 | return mParams.keySet(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 24 | 25 | public class Constants { 26 | 27 | public static final int TYPE_ZERO_R = 1000; 28 | public static final int TYPE_NAIVE_BAYES = 1001; 29 | public static final int TYPE_BAYES_NET = 1002; 30 | public static final int TYPE_ID3 = 1003; 31 | public static final int TYPE_DENSITY_CLUSTER = 1004; 32 | 33 | public static final String CLASSIFIER_STORAGE_FILE = "classifiers.json"; 34 | 35 | // Config params 36 | 37 | // Density clustering 38 | public static final String MAX_CLUSTER_DISTANCE = "maxClusterDistance"; 39 | public static final String MIN_INCLUSION_PERCENT = "minInclusionPercent"; 40 | 41 | public static final double DEFAULT_MAX_CLUSTER_DISTANCE = 1; // in km if GPS 42 | public static final double DEFAULT_MIN_INCLUSION_PERCENT = 50.0; 43 | 44 | // Naive Bayes 45 | public static final String LAPLACE_SMOOTHING = "laplaceSmoothing"; 46 | 47 | public static final boolean DEFAULT_LAPLACE_SMOOTHING = true; 48 | 49 | public static final boolean DEBUG = false; 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/Feature.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2013, University of Birmingham, UK 3 | * Veljko Pejovic, 4 | * 5 | * 6 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 7 | * Computing for Positive Behaviour Change) Project. For more information, please visit 8 | * http://www.ubhave.org 9 | * 10 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 11 | * or without fee is hereby granted, provided that the above copyright notice and this 12 | * permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | ******************************************************************************/ 22 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.HashMap; 27 | 28 | /** 29 | * Features can be nominal or numeric. 30 | * 31 | * @author Veljko Pejovic, University of Birmingham, UK 32 | * 33 | */ 34 | public abstract class Feature { 35 | 36 | private static final String TAG = "Feature"; 37 | public static final int NOMINAL = 0; 38 | public static final int NUMERIC = 1; 39 | 40 | protected int mType; 41 | protected String mName; 42 | 43 | 44 | /** 45 | * Create a feature object with the given name and type. 46 | * @param fname Feature name. 47 | * @param ftype Feature type. Has to be {@link Feature#NUMERIC}. 48 | * @throws MLException 49 | */ 50 | protected Feature(String fname, int ftype) { 51 | mType = ftype; 52 | mName = fname; 53 | } 54 | 55 | 56 | /*public Feature(String fname, int ftype, String[] fvalues) throws MLException{ 57 | if (ftype == NUMERIC){ 58 | throw new MLException(MLException.INCOMPATIBLE_FEATURE_TYPE, 59 | "Numeric features do not need a list of possible categories;" + 60 | " use Feature(String, int) instead."); 61 | } 62 | mType = ftype; 63 | mName = fname; 64 | mCategories = new ArrayList(Arrays.asList(fvalues)); 65 | mCategoryIndex = new HashMap(); 66 | for(int i=0;i mCategories; 12 | protected HashMap mCategoryIndex; 13 | 14 | /** 15 | * 16 | * Create a feature object with the given name and type. 17 | * @param fname Feature name. 18 | * @param fvalues 19 | * @throws MLException 20 | */ 21 | public FeatureNominal(String fname, ArrayList fvalues) { 22 | super(fname, NOMINAL); 23 | mCategories = (ArrayList) fvalues.clone(); 24 | mCategoryIndex = new HashMap(); 25 | for(int i=0;i getValues(){ 29 | return mCategories; 30 | } 31 | 32 | public int indexOfCategory(String value){ 33 | //Log.d(TAG, "feature "+mName+" going for value "+value); 34 | return mCategoryIndex.get(value); 35 | 36 | } 37 | 38 | public String categoryOfIndex(int index){ 39 | return mCategories.get(index); 40 | } 41 | 42 | public int numberOfCategories(){ 43 | if (mType == NOMINAL) return mCategories.size(); 44 | else return 1; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/FeatureNumeric.java: -------------------------------------------------------------------------------- 1 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 2 | 3 | /** 4 | * Created by veljko on 04/09/15. 5 | */ 6 | public class FeatureNumeric extends Feature { 7 | 8 | public FeatureNumeric(String name) { 9 | super(name, NUMERIC); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/Instance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | 24 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 25 | 26 | import java.util.ArrayList; 27 | 28 | /** 29 | * Instance is merely a list of Values. It should be used, and comply with, 30 | * the signature of the classifier. 31 | * 32 | * @author Veljko Pejovic, University of Birmingham, UK 33 | * 34 | */ 35 | public class Instance { 36 | 37 | private ArrayList mValues; 38 | 39 | public Instance(int a_numFeatures){ 40 | mValues = new ArrayList(a_numFeatures); 41 | } 42 | 43 | public Instance(){ 44 | this(0); 45 | } 46 | 47 | public Instance(ArrayList featureValues){ 48 | mValues = featureValues; 49 | } 50 | 51 | public void addValue(Value value){ 52 | mValues.add(value); 53 | } 54 | 55 | public Value getValueAtIndex(int i){ 56 | return mValues.get(i); 57 | } 58 | 59 | public void setValueAtIndex(int i, Value value) throws IndexOutOfBoundsException { 60 | mValues.set(i, value); 61 | } 62 | 63 | 64 | public int size(){ 65 | return mValues.size(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/MLException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 24 | 25 | public class MLException extends Exception { 26 | 27 | public static final int INCOMPATIBLE_FEATURE_TYPE = 100; 28 | public static final int INCOMPATIBLE_INSTANCE = 101; 29 | public static final int INVALID_PARAMETER = 102; 30 | public static final int INVALID_STATE = 103; 31 | public static final int IO_ERROR = 104; 32 | public static final int IO_FILE_NOT_FOUND_ERROR = 105; 33 | public static final int CLASSIFIER_EXISTS = 200; 34 | 35 | private int mErrorCode; 36 | private String mMessage; 37 | 38 | public MLException(int errorCode, String message) { 39 | super(message); 40 | mMessage = message; 41 | mErrorCode = errorCode; 42 | } 43 | 44 | public int getErrorCode() 45 | { 46 | return mErrorCode; 47 | } 48 | 49 | public String getMessage() 50 | { 51 | return mMessage; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /mltoolkit/src/main/java/si/uni_lj/fri/lrss/machinelearningtoolkit/utils/Signature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, University of Birmingham, UK, 3 | * Copyright (c) 2013, University of Ljubljana, Slovenia, 4 | * Veljko Pejovic, 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 24 | 25 | import java.util.ArrayList; 26 | 27 | import android.util.Log; 28 | 29 | /** 30 | * Signature defines features used by a classifier, their names and the 31 | * index of the class feature. Instances of data (which consist of values) 32 | * need to correspond to the signature of the classifier they are used for. 33 | * Signature exposes a method for checking that compliance. 34 | * 35 | * @author Veljko Pejovic, University of Birmingham, UK 36 | * 37 | */ 38 | public class Signature { 39 | 40 | private static final String TAG = "Signature"; 41 | 42 | private ArrayList mFeatures; 43 | 44 | private int mClassIndex; 45 | 46 | public Signature(ArrayList features, int classIndex){ 47 | mFeatures = features; 48 | mClassIndex = classIndex; 49 | 50 | } 51 | 52 | public Signature(ArrayList features){ 53 | this(features, features.size() - 1); 54 | } 55 | 56 | public void setClassIndex(int classIndex){ 57 | mClassIndex = classIndex; 58 | } 59 | 60 | public int getClassIndex(){ 61 | return mClassIndex; 62 | } 63 | 64 | public Feature getClassFeature(){ 65 | return getFeatureAtIndex(getClassIndex()); 66 | } 67 | 68 | public Feature getFeatureAtIndex(int i){ 69 | return mFeatures.get(i); 70 | } 71 | 72 | public ArrayList getFeatures(){ 73 | return mFeatures; 74 | } 75 | 76 | public int size() { 77 | return mFeatures.size(); 78 | } 79 | 80 | public boolean checkCompliance(Instance instance, boolean training) { 81 | Log.d(TAG, "checkInstanceCompliance"); 82 | int checkSize = instance.size(); 83 | 84 | // Instances that are used for training should have the exact same features as the signature. 85 | // Those that are about to be classified, should have one feature less -- the class feature. 86 | if (!training) { 87 | checkSize++; 88 | } 89 | 90 | if (checkSize != this.getFeatures().size()){ 91 | Log.d(TAG, "Expected number of features: "+this.getFeatures().size()+" got "+checkSize); 92 | for (int i=0; i 5 | * 6 | * 7 | * This library was developed as part of the EPSRC Ubhave (Ubiquitous and Social 8 | * Computing for Positive Behaviour Change) Project. For more information, please visit 9 | * http://www.ubhave.org 10 | * 11 | * Permission to use, copy, modify, and/or distribute this software for any purpose with 12 | * or without fee is hereby granted, provided that the above copyright notice and this 13 | * permission notice appear in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | */ 23 | 24 | package si.uni_lj.fri.lrss.machinelearningtoolkit.utils; 25 | 26 | /** 27 | * Values can be double for numeric, int for nominal, 28 | * and a special NaN for missing values. 29 | * 30 | * @author Veljko Pejovic (v.pejovic@cs.bham.ac.uk) 31 | * 32 | */ 33 | public class Value { 34 | 35 | public static final int NOMINAL_VALUE=0; 36 | public static final int NUMERIC_VALUE=1; 37 | public static final int MISSING_VALUE=2; 38 | 39 | private Object mValue; 40 | private int mType; 41 | 42 | public Value(Object value, int type){ 43 | switch (type) { 44 | case NOMINAL_VALUE: mValue = value; 45 | mType = NOMINAL_VALUE; 46 | break; 47 | case NUMERIC_VALUE: mValue = value; 48 | mType = NUMERIC_VALUE; 49 | break; 50 | case MISSING_VALUE: mValue = Double.NaN; 51 | mType = MISSING_VALUE; 52 | break; 53 | } 54 | } 55 | 56 | public int getValueType(){ 57 | return mType; 58 | } 59 | 60 | public Object getValue(){ 61 | return mValue; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /mltoolkit/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpejovic/MachineLearningToolkit/aee82565ceac03d377974244156759c5f1189289/mltoolkit/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /mltoolkit/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpejovic/MachineLearningToolkit/aee82565ceac03d377974244156759c5f1189289/mltoolkit/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /mltoolkit/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpejovic/MachineLearningToolkit/aee82565ceac03d377974244156759c5f1189289/mltoolkit/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mltoolkit/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpejovic/MachineLearningToolkit/aee82565ceac03d377974244156759c5f1189289/mltoolkit/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mltoolkit/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MachineLearningToolkit 3 | 4 | -------------------------------------------------------------------------------- /mltoolkit/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':mltoolkit' 2 | --------------------------------------------------------------------------------