Tutorial Android – Implementação SVM Multiclass
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.
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.
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++.
* 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).
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:
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.
Importadas as classes, feche a janela do seu projeto e expanda a tela do projeto SVM com as classes importadas, mais ou menos assim:
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:
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.
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.
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.
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/