Tutorial Android – Implementação SVM Multiclass

On 18 de fevereiro de 2016 by Carlos Mion

 

Introdução ao SVM Multiclass

Este tutorial irá demonstrar passo a passo a implementação do SVM Multiclass em dispositivos Android. As seguintes informações são necessárias para o começo do desenvolvimento:

  • O desenvolvimento usará o AndroidStudio v 1.5 e a versão mínima do SDK Android alvo é o SDK 15.
  • Este tutorial considera que o usuário já está familiarizado com a IDE e programação Android, caso não, faça o tutorial Entendendo e obtendo os dados do acelerômetro no Android para conhecer e entender melhor o tutorial seguinte.

xor_N2000_rbf

Introdução ao SVM

O SVM (Support Vector Machine) é um processo de aprendizado de máquina (Machine Learning), em que através de treinamentos prévios é capaz de “predizer” a qual grupo um novo conjunto de dados pertence. Resumidamente, é um reconhecedor de padrões.

No blog do Imobilis existe um post de 3 partes explicando toda a matemática do SVM caso queira entender melhor os fundamentos matemáticos do mesmo.

Tutorial SVM – Parte 1

Tutorial SVM – Parte 2

Tutorial SVM – Parte 3

Instalando o NDK

No mesmo local existe um tutorial explicando como instalar o NDK Android, módulo que iremos utilizar neste tutorial. O NDK serve para que a aplicação  reconheça funções nativas implementadas em linguagem C/C++.

Tutorial OpenCV

* Faça apenas a parte que envolve a instalação do NDK.

Obtendo os arquivos necessários

Neste tutorial, com o objetivo de deixar mais fácil a compreensão e uso do SVM Multiclass, será feita a importação do projeto atual para um projeto de implementação já pronta:

  •  Vá até a página clicando aqui e selecione a opção “Download ZIP” para baixar o projeto (Aqui será feito o download dos arquivos do projeto);
  • Agora, vá até o repositório git do link e faça o download clicando em “Download ZIP” (Aqui será feito o download dos arquivos do exemplo).

Além do projeto do SVM existem 2 arquivos, “input.data” e “treinamento_all.data”. Os mesmos deverão ser colocados na memória principal do aparelho, mais especificamente na localização “/storage/emulated/0/”, pois serão utilizados pelo código Android para o exemplo do reconhecimento de padrões que será apresentado posteriormente.

Importando seu projeto

Abra o AndroidStudio, e vá em File>Open>”Diretório onde a pasta do projeto está”(provavelmente Downloads/AndroidLibSvm-master).

Abrindo o projeto do SVM

Abrindo o projeto do SVM

Aberto o projeto, clique em File->New->Import Project… . Feito isso, selecione o projeto que você estava trabalhando, outra janela do AndroidStudio irá abrir. Para facilitar o processo, divida a tela entre os dois projetos:

Tela dividia entre o projeto do SVM e o projeto que foi importado

Tela dividia entre o projeto do SVM e o projeto que foi importado

Agora execute o seguinte passo:

  • Vá no seu projeto, aperte “Ctrl” e arraste todas as classes que desejar (todas do seu projeto original) para a pasta “java” do projeto SVM. Vale lembrar que se forem “services” ou uma “activity”, as mesmas devem ser criadas diretamente pelo AndroidStudio e posteriormente terem seu código interno copiado do seu projeto original.

 

 

Janela de confirmação da copia da classe

Janela de confirmação da cópia da classe

 

Importadas as classes, feche a janela do seu projeto e expanda a tela do projeto SVM com as classes importadas, mais ou menos assim:

imagem 4

Classes já importadas

Edição de métodos para melhor uso do SVM

A classe “MainActivity” do projeto SVM deve ser mesclada com a do seu projeto. No caso desse tutorial, todo o método onCreate() original foi comentado e substituído por outro copiado da MainActivity do projeto original.

Ficou assim:

package edu.umich.eecs.androidlibsvm;

import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends Activity {
    public static final String LOG_TAG = "AndroidLibSvm";

    String appFolderPath;
    String systemPath;

    // link jni library
    static {
        System.loadLibrary("jnilibsvm");
    }

    // connect the native functions
    private native void jniSvmTrain(String cmd);
    private native void jniSvmPredict(String cmd);


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        systemPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
        appFolderPath = systemPath+"libsvm/";

        startService(new Intent(getBaseContext(), Background.class));

        CreateAppFolderIfNeed();
        copyAssetsDataIfNeed();

        String dataTrainPath = appFolderPath+"heart_scale ";
        String dataPredictPath = appFolderPath+"heart_scale ";
        String modelPath = appFolderPath+"model ";
        String outputPath = appFolderPath+"predict ";

        String svmTrainOptions = "-c 100 ";
        jniSvmTrain(svmTrainOptions + systemPath + "treinamento_all.data " + systemPath + "model.data");
    }

//    @Override
//    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
//        systemPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
//        appFolderPath = systemPath+"libsvm/";
//
//        // 1. create necessary folder to save model files
//        CreateAppFolderIfNeed();
//        copyAssetsDataIfNeed();
//
//        // 2. assign model/output paths
//        String dataTrainPath = appFolderPath+"heart_scale ";
//        String dataPredictPath = appFolderPath+"heart_scale ";
//        String modelPath = appFolderPath+"model ";
//        String outputPath = appFolderPath+"predict ";
//
//        // 3. make SVM train
//        String svmTrainOptions = "-t 2 ";
//        jniSvmTrain(svmTrainOptions+dataTrainPath+modelPath);
//
//        // 4. make SVM predict
//        jniSvmPredict(dataPredictPath+modelPath+outputPath);
//
//    }

        public void Predict(String input){

            jniSvmPredict("storage/emulated/0/" + input + " storage/emulated/0/" +  "model.data " + "storage/emulated/0/" +  "saida.data");

        }




    /*
    * Some utility functions
    * */
    private void CreateAppFolderIfNeed(){
        // 1. create app folder if necessary
        File folder = new File(appFolderPath);

        if (!folder.exists()) {
            folder.mkdir();
            Log.d(LOG_TAG,"Appfolder is not existed, create one");
        } else {
            Log.w(LOG_TAG,"WARN: Appfolder has not been deleted");
        }


    }

    private void copyAssetsDataIfNeed(){
        String assetsToCopy[] = {"heart_scale_predict","heart_scale_train","heart_scale"};
        //String targetPath[] = {C.systemPath+C.INPUT_FOLDER+C.INPUT_PREFIX+AudioConfigManager.inputConfigTrain+".wav", C.systemPath+C.INPUT_FOLDER+C.INPUT_PREFIX+AudioConfigManager.inputConfigPredict+".wav",C.systemPath+C.INPUT_FOLDER+"SomeoneLikeYouShort.mp3"};

        for(int i=0; i<assetsToCopy.length; i++){
            String from = assetsToCopy[i];
            String to = appFolderPath+from;

            // 1. check if file exist
            File file = new File(to);
            if(file.exists()){
                Log.d(LOG_TAG, "copyAssetsDataIfNeed: file exist, no need to copy:"+from);
            } else {
                // do copy
                boolean copyResult = copyAsset(getAssets(), from, to);
                Log.d(LOG_TAG, "copyAssetsDataIfNeed: copy result = "+copyResult+" of file = "+from);
            }
        }
    }

    private boolean copyAsset(AssetManager assetManager, String fromAssetPath, String toPath) {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = assetManager.open(fromAssetPath);
            new File(toPath).createNewFile();
            out = new FileOutputStream(toPath);
            copyFile(in, out);
            in.close();
            in = null;
            out.flush();
            out.close();
            out = null;
            return true;
        } catch(Exception e) {
            e.printStackTrace();
            Log.e(LOG_TAG, "[ERROR]: copyAsset: unable to copy file = "+fromAssetPath);
            return false;
        }
    }

    private void copyFile(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[1024];
        int read;
        while((read = in.read(buffer)) != -1){
            out.write(buffer, 0, read);
        }
    }

}

Note que foram mescladas as duas classes “MainActivity” dos dois projetos. Além disso, o método onde é feita a atual classificação do arquivo modelo foi transformado em uma função, para que possa ser chamado por outras classes quando necessário. Então, para chamar este novo método (“Predict(…)”) na classe desejada, basta criar um objeto do tipo “MainActivity”:

MainActivity main = new MainActivity();

e chamar a função quando desejar:

main.Predict("input.data");

Exemplo de funcionamento do SVM no Android

Os arquivos baixados anteriormente do git são:

  • treinamento_all.data: Arquivo de treinamento para o SVM feito no seguinte padrão:
Arquivo treinamento_all.data

Arquivo treinamento_all.data

Os dados de cada amostra são separados por espaços (” “) e cada novo arquivo separado por uma nova linha. Onde o primeiro número é a qual classe pertencem os dados seguintes, o número “x:” é um contador que identifica qual é o dado a seguir. Este arquivo será  utilizado para o treinamento do SVM. Logo após, um novo arquivo com o modelo será gerado no sistema de arquivos do aparelho. A predição só é possível se antes executarmos o treinamento pelo menos uma vez.

  • input.data: Arquivo no mesmo padrão do “treinamento_all.data” que será classificado.
Arquivo input.data

Arquivo input.data

Execute o aplicativo no dispositivo e verifique no LogCat do AndroidStudio a saída. Com o código da “MainActivity” da forma como foi apresentado acima e chamando a função “Predict(…)” passando o arquivo input.data. No Logcat é possível observar a execução do programa, sendo que a predição ficará gravada no arquivo “saida.data” na memória interna do aparelho.

imagem 5

Execução do SVM

Esse teste foi feito com um arquivo de treinamento de quatro classes distintas, 1, 2, 3 e 4. O arquivo input.data pertence à classe 3, logo, no arquivo de saída. A classificação correta deverá apresentar o número 3.

Arquivo saida.data, na memória do dispositivo Android

Arquivo saida.data, na memória do dispositivo Android

Referências

[1] Implementação chinesa SVM Android – https://www.csie.ntu.edu.tw/~cjlin/libsvm/#download

[2] Tutorial Entendendo e obtendo os dados do acelerômetro no Android – http://www2.decom.ufop.br/imobilis/tutorial-android-obtendo-os-dados-do-acelerometro-no-android/

[3] Tutorial OpenCV no Android – http://www2.decom.ufop.br/imobilis/6436/

[4] SVM – Entendendo Sua Matemática – Parte 1 – http://www2.decom.ufop.br/imobilis/svm-entendendo-sua-matematica-parte-1-a-margem/

[5] SVM – Entendendo Sua Matemática – Parte 2 – http://www2.decom.ufop.br/imobilis/svm-entendendo-sua-matematica-parte-2-o-hiperplano/

[6]SVM – Entendendo Sua Matemática – Parte 3 – http://www2.decom.ufop.br/imobilis/svm-entendendo-sua-matematica-parte-3-o-hiperplano-otimo/

Comments are closed.