Cet article est une version formatée de mon article de blog pour Qiita. S'il y a des articles supplémentaires, je les écrirai sur le blog. __ "Introduction à la bibliothèque SHOGUN de Machine Learning" __ http://rest-term.com/archives/3090/
The machine learning toolbox's focus is on large scale kernel methods and especially on Support Vector Machines (SVM)
Installation REMARQUE: installation de la bibliothèque de dépendances SWIG BLAS (ATLAS) / LAPACK / GLPK / Eigen3 NumPy Précautions lors de la compilation hello, world (libshogun) Remarques sur la gestion de la mémoire Python Modular
Le site officiel décrit la procédure d'installation, etc. sur les prémisses du système d'exploitation Debian, mais vous pouvez l'installer sans aucun problème, même avec redhat. S'il s'agit d'un système Debian, l'ancienne version est distribuée dans le paquet deb, mais comme c'est CentOS ici, compilez / installez à partir de la source. Étant donné que les tâches liées à l'apprentissage automatique prennent souvent beaucoup de temps, nous vous recommandons de créer un tel logiciel, et pas seulement SHOGUN, dans l'environnement dans lequel il fonctionne réellement et de l'utiliser dans un état optimal.
Il semble qu'il ait été construit avec Autotools (./configure && make) auparavant, mais la dernière version du package était compatible avec CMake. Le nombre d'utilisateurs CMake comme OpenCV et MySQL a augmenté ces dernières années.
$ git clone git://github.com/shogun-toolbox/shogun.git
$ cd shogun
$ mkdir build && cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/shogun-2.1.0 \
-DCMAKE_BUILD_TYPE=Release \
-DBUNDLE_EIGEN=ON \
-DBUNDLE_JSON=ON \
-DCmdLineStatic=ON \
-DPythonModular=ON ..
##Les bibliothèques dépendantes sont vérifiées et la configuration de construction est affichée.
-- Summary of Configuration Variables
--
-- The following OPTIONAL packages have been found:
* GDB
* OpenMP
* BLAS
* Threads
* LAPACK
* Atlas
* GLPK
* Doxygen
* LibXml2
* CURL
* ZLIB
* BZip2
* Spinlock
-- The following REQUIRED packages have been found:
* SWIG (required version >= 2.0.4)
* PythonLibs
* PythonInterp
* NumPy
-- The following OPTIONAL packages have not been found:
* CCache
* Mosek
* CPLEX
* ARPACK
* NLopt
* LpSolve
* ColPack
* ARPREC
* HDF5
* LibLZMA
* SNAPPY
* LZO
-- ==============================================================================================================
-- Enabled Interfaces
-- libshogun is ON
-- python modular is ON
-- octave modular is OFF - enable with -DOctaveModular=ON
-- java modular is OFF - enable with -DJavaModular=ON
-- perl modular is OFF - enable with -DPerlModular=ON
-- ruby modular is OFF - enable with -DRubyModular=ON
-- csharp modular is OFF - enable with -DCSharpModular=ON
-- R modular is OFF - enable with -DRModular=ON
-- lua modular is OFF - enable with -DLuaModular=ON
--
-- Enabled legacy interfaces
-- cmdline static is ON
-- python static is OFF - enable with -DPythonStatic=ON
-- octave static is OFF - enable with -DOctaveStatic=ON
-- matlab static is OFF - enable with -DMatlabStatic=ON
-- R static is OFF - enable with -DRStatic=ON
-- ==============================================================================================================
##S'il ne semble y avoir aucun problème, compilez et installez
$ make -j32
$ sudo make install
Il semble que tout compilateur prenant en charge les fonctionnalités C ++ 11 profitera de certaines fonctionnalités (std :: atomic, etc.). La plupart des fonctions de C ++ 11 ne sont pas supportées par le système redhat 6.x GCC (v4.4.x).
Dans mon environnement, j'ai installé la ligne de commande et l'interface pour Python en plus de libshogun dans la bibliothèque elle-même. Si vous souhaitez disposer de nombreuses interfaces de langage de script, vous devez installer SWIG séparément.
SWIG
SWIG est un outil qui crée des liaisons pour utiliser des modules (bibliothèques partagées) écrits en C / C ++ à partir de langages de niveau supérieur tels que les langages de script. Je fais aussi occasionnellement des liaisons PHP pour des interfaces Web en utilisant SWIG dans mon entreprise. Depuis novembre 2013, les packages qui peuvent être installés avec yum ne répondent pas aux exigences de version de SHOGUN, donc compilez / installez-les également à partir de la source. Pour Debian, $ apt-get install swig2.0
est OK.
##Paquet dépendant PCRE(Perl Compatible Regular Expressions)Si non inclus, entrez
$ sudo yum pcre-devel.x86_64
##S'il existe un ancien package rpm, effacez-le
$ sudo yum remove swig
$ wget http://prdownloads.sourceforge.net/swig/swig-2.0.11.tar.gz
$ tar zxf swig-2.0.11.tar.gz
$ cd swig-2.0.11
$ ./configure --prefix=/usr/local/swig-2.0.11
$ make -j2
$ sudo make install
##Mettre un lien symbolique binaire dans le PATH
$ sudo ln -s /usr/local/swig-2.0.11/bin/swig /usr/local/bin
Reconstruisons SHOGUN après avoir aligné le système de traitement de la langue dans laquelle vous souhaitez créer la liaison avec SWIG.
BLAS(ATLAS)/LAPACK/GLPK/Eigen3
Un groupe de bibliothèques liées à l'algèbre linéaire. Un package qui peut être installé avec yum est OK. ATLAS est l'une des implémentations optimisées de BLAS et doit être incluse car elle est facile à installer (BLAS est une implémentation de référence). De plus, SHOGUN semble prendre en charge CPLEX en plus de GLPK, donc si vous souhaitez l'utiliser pour les affaires, CPLEX Il semble que les performances seront encore améliorées en introduisant /). Faisons de notre mieux pour rédiger le formulaire d'approbation (il semble qu'il puisse être utilisé gratuitement à des fins académiques). Comme pour Eigen3, à partir de novembre 2013, les packages qui peuvent être installés avec yum ne répondent pas aux exigences de version de SHOGUN, mais si Eigen3 n'est pas à portée de main, CMake téléchargera le code source (fichiers d'en-tête car il s'agit d'une bibliothèque de modèles). Il semble que vous puissiez instruire. Vous pouvez ajouter -DBUNDLE_EIGEN = ON
à l'option CMake.
##Installer toutes les bibliothèques liées à l'algèbre linéaire
## atlas-lapack si vous souhaitez inclure devel-aucun développement requis
$ sudo yum install blas-devel.x86_64 lapack-devel.x86_64 atlas-devel.x86_64 glpk-devel.x86_64
NumPy ** NumPy est requis pour installer l'interface Python **. J'ai déjà présenté NumPy dans mon blog, donc pour référence. À propos, l'interface Python d'OpenCV (Computer Vision Library) utilise également NumPy.
L'installation est facile avec pip (Un outil pour installer et gérer les packages Python.).
$ sudo pip install numpy
L'image globale de la bibliothèque SHOGUN est comme indiqué dans la figure ci-dessous, et je ne suis pas sûr. Les interfaces pour les langages de script autres que ceux illustrés dans cette figure sont également prises en charge, comme Java, Ruby et Lua. Comme mentionné ci-dessus, installez SWIG et créez une liaison pour la langue que vous souhaitez utiliser.
Si vous publiez et compilez SHOGUN dans un environnement VPS bon marché avec une petite quantité de mémoire physique / mémoire virtuelle, il y a de fortes chances que le processus cc1plus soit tué de force par OOM Killer. Je l'ai essayé dans un environnement virtuel avec 1 Go de RAM / 2 Go de Swap, mais il a été tué avec un score terrible. ..
kernel: Out of memory: Kill process 30340 (cc1plus) score 723 or sacrifice child
kernel: Killed process 30340, UID 500, (cc1plus) total-vm:2468236kB, anon-rss:779716kB, file-rss:2516kB
kernel: cc1plus invoked oom-killer: gfp_mask=0x200da, order=0, oom_adj=0, oom_score_adj=0
Dans ce cas, envisagez d'augmenter la capacité de swap. Si la mémoire physique n'est que de 1 Go, il n'est probablement pas suffisant d'avoir environ deux fois la capacité de mémoire réelle, donc je pense qu'il est prudent de sécuriser temporairement environ 4 fois. Il vaut peut-être mieux abandonner un peu s'il s'agit d'un environnement virtuel OpenVZ. ..
De plus, dans un environnement virtuel comme VPS, il devrait y avoir peu d'inodes, donc je ne pense pas qu'il soit possible de mettre une grande quantité de données d'entraînement. Il est plus difficile de le construire docilement sur un serveur physique. Je l'ai construit dans un environnement avec 32 cœurs / 96 Go de RAM, mais les ressources étaient suffisantes et j'ai pu le construire en douceur.
Au fait, les options d'optimisation de GCC dans mon environnement sont les suivantes.
-march=core2 -mcx16 -msahf -maes -mpclmul -mavx --param l1-cache-size=32 --param l1-cache-line-size=64 --param l2-cache-size=15360 -mtune=generic
hello, world (libshogun) Commençons par une tâche simple en utilisant libshogun. [SVM (Support Vector Machine)](http://ja.wikipedia.org/wiki/%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%83% Il s'agit d'un échantillon pour classer les données en utilisant 99% E3% 82% AF% E3% 82% BF% E3% 83% BC% E3% 83% 9E% E3% 82% B7% E3% 83% B3). ..
/* hello_shogun.cpp */
#include <shogun/labels/BinaryLabels.h>
#include <shogun/features/DenseFeatures.h>
#include <shogun/kernel/GaussianKernel.h>
#include <shogun/classifier/svm/LibSVM.h>
#include <shogun/base/init.h>
#include <shogun/lib/common.h>
#include <shogun/io/SGIO.h>
using namespace shogun;
int main(int argc, char** argv) {
// initialize
init_shogun_with_defaults();
// create some data
SGMatrix<float64_t> matrix(2,3);
for(int i=0; i<6; i++) {
matrix.matrix[i] = i;
}
matrix.display_matrix();
// create three 2-dimensional vectors
CDenseFeatures<float64_t>* features = new CDenseFeatures<float64_t>();
features->set_feature_matrix(matrix);
// create three labels
CBinaryLabels* labels = new CBinaryLabels(3);
labels->set_label(0, -1);
labels->set_label(1, +1);
labels->set_label(2, -1);
// create gaussian kernel(RBF) with cache 10MB, width 0.5
CGaussianKernel* kernel = new CGaussianKernel(10, 0.5);
kernel->init(features, features);
// create libsvm with C=10 and train
CLibSVM* svm = new CLibSVM(10, kernel, labels);
svm->train();
SG_SPRINT("total sv:%d, bias:%f\n", svm->get_num_support_vectors(), svm->get_bias());
// classify on training examples
for(int i=0; i<3; i++) {
SG_SPRINT("output[%d]=%f\n", i, svm->apply_one(i));
}
// free up memory
SG_UNREF(svm);
exit_shogun();
return 0;
}
##C pour un compilateur relativement nouveau++Il est bon de compiler avec 11 activés.
$ g++ -g -Wall -std=c++0x -L/usr/local/lib64 -lshogun hello_shogun.cpp -o hello_shogun
$ ./hello_shogun
matrix=[
[ 0, 2, 4],
[ 1, 3, 5]
]
total sv:3, bias:-0.333333
output[0]=-0.999997
output[1]=1.000003
output[2]=-1.000005
Extrayez le vecteur de caractéristiques (CDense Features) de la matrice, définissez l'étiquette de réponse correcte (CBinaly Labels) et apprenez avec SVM (CLib SVM) en utilisant le noyau gaussien. Voici quelques fonctionnalités.
Les vecteurs de caractéristiques sont lus à partir d'une matrice avec Colonne-Majeure, similaire à OpenGL, CUBLAS, etc. (une colonne devient un vecteur de caractéristiques). Les bibliothèques externes telles que LibSVM et SVMLight peuvent être utilisées en interne à partir de l'interface SHOGUN pour apprendre SVM (voir shogun / classifier / svm / ci-dessous).
Le code ci-dessus semble clairement être une fuite de mémoire, mais quand je l'ai vérifié avec valgrind, il semble que la mémoire soit correctement gérée en interne. SHOGUN gère en interne les objets par nombre de références. J'utilise ceci parce qu'une macro (SG_REF / SG_UNREF) qui incrémente / décrémente ce nombre de références est définie, mais je n'ai pas besoin de manipuler manuellement le nombre pour toutes les instances. J'ai lu l'implémentation autour de la gestion de la mémoire dans SHOGUN et elle décrémente le nombre de références de l'instance référencée lorsque l'instance de référence est libérée (c'est-à-dire dans le destructeur). Comme l'instance est libérée lorsque le nombre de références devient 0 ou moins, dans le code ci-dessus, si vous libérez l'instance SVM, d'autres instances seront libérées dans une réaction en chaîne.
Veuillez noter que ** rien n'est fait lorsque la portée est hors de portée **, donc la décision de libérer l'instance n'est prise que lorsque le nombre de références change. Cela ressemble à une petite gentillesse et une grande aide, mais cela ne peut pas être aidé, il semble donc nécessaire de bien gérer ce mécanisme. J'écrirai quelques politiques.
Gestion entièrement manuelle du nombre de références Si vous le supprimez vous-même sous le contrôle du nombre de références de SHOGUN, vous courez le risque de doubler gratuitement. Par conséquent, en même temps que la création d'une instance de la classe, incrémentez manuellement le nombre de références (SG_REF) et décrémentez manuellement (SG_UNREF) lorsque l'instance n'est plus nécessaire. C'est une méthode ARC hors Objective-C. Cependant, l'écriture d'un code exceptionnellement sécurisé peut être difficile.
Pointeur intelligent + gestion semi-manuelle du nombre de références Si vous avez un compilateur C ++ relativement nouveau, vous pouvez utiliser std :: unique_ptr / std :: shared_ptr etc., c'est donc la méthode à utiliser. Comme mentionné ci-dessus, sous le contrôle du nombre de références de SHOGUN, si le nombre de références de l'instance devient 0 ou moins, il sera supprimé en interne, donc si vous l'enveloppez simplement dans un pointeur intelligent, il existe également un risque de double libération. .. Par conséquent, une fois l'instance créée, le décompte de références est incrémenté manuellement et la décrémentation n'est pas effectuée afin que la suppression ne soit pas exécutée dans SHOGUN. En d'autres termes, gardez toujours le nombre de références supérieur à 1 pour rendre la gestion d'objets de SHOGUN pratiquement dénuée de sens. Cela a l'avantage d'être facile à écrire du code sans exception sans avoir à craindre d'oublier SG_UNREF.
// C++Incrémentez manuellement le nombre de références SHOGUN après avoir créé une instance à l'aide de pointeurs intelligents standard
std::unique_ptr<CDenseFeatures<float64_t> > features(new CDenseFeatures<float64_t>());
SG_REF(features);
//Ne décrémentez pas le nombre de références
Ensuite, entrons des données inconnues. Utilisez les données d'entraînement / données de test générées de manière appropriée par Python et écrites dans un fichier comme indiqué ci-dessous.
import numpy as np
def genexamples(n):
class1 = 0.6*np.random.randn(n, 2)
class2 = 1.2*np.random.randn(n, 2) + np.array([5, 1])
labels = np.hstack((np.ones(n), -np.ones(n)))
return (class1, class2, labels)
** Note) Le code suivant a été confirmé pour fonctionner sous libshogun.so.14 **
#include <shogun/labels/BinaryLabels.h>
#include <shogun/features/DenseFeatures.h>
#include <shogun/kernel/GaussianKernel.h>
#include <shogun/classifier/svm/LibSVM.h>
#include <shogun/io/SGIO.h>
#include <shogun/io/CSVFile.h>
#include <shogun/evaluation/ContingencyTableEvaluation.h>
#include <shogun/base/init.h>
#include <shogun/lib/common.h>
using namespace std;
using namespace shogun;
int main(int argc, char** argv) {
try {
init_shogun_with_defaults();
// training examples
CCSVFile train_data_file("traindata.dat");
// labels of the training examples
CCSVFile train_labels_file("labeldata.dat");
// test examples
CCSVFile test_data_file("testdata.dat");
SG_SPRINT("training ...\n");
SGMatrix<float64_t> train_data;
train_data.load(&train_data_file);
CDenseFeatures<float64_t>* train_features = new CDenseFeatures<float64_t>(train_data);
SG_REF(train_features);
SG_SPRINT("num train vectors: %d\n", train_features->get_num_vectors());
CBinaryLabels* train_labels = new CBinaryLabels();
SG_REF(train_labels);
train_labels->load(&train_labels_file);
SG_SPRINT("num train labels: %d\n", train_labels->get_num_labels());
float64_t width = 2.1;
CGaussianKernel* kernel = new CGaussianKernel(10, width);
SG_REF(kernel);
kernel->init(train_features, train_features);
int C = 1.0;
CLibSVM* svm = new CLibSVM(C, kernel, train_labels);
SG_REF(svm);
svm->train();
SG_SPRINT("total sv:%d, bias:%f\n", svm->get_num_support_vectors(), svm->get_bias());
SG_UNREF(train_features);
SG_UNREF(train_labels);
SG_UNREF(kernel);
CBinaryLabels* predict_labels = svm->apply_binary(train_features);
SG_REF(predict_labels);
CErrorRateMeasure* measure = new CErrorRateMeasure();
SG_REF(measure);
measure->evaluate(predict_labels, train_labels);
float64_t accuracy = measure->get_accuracy()*100;
SG_SPRINT("accuracy: %f\%\n", accuracy);
SG_UNREF(predict_labels);
SG_UNREF(measure);
SG_SPRINT("testing ...\n");
SGMatrix<float64_t> test_data;
test_data.load(&test_data_file);
CDenseFeatures<float64_t>* test_features = new CDenseFeatures<float64_t>(test_data);
SG_REF(test_features);
SG_SPRINT("num test vectors: %d\n", test_features->get_num_vectors());
CBinaryLabels* test_labels = svm->apply_binary(test_features);
SG_REF(test_labels);
SG_SPRINT("num test labels: %d\n", test_labels->get_num_labels());
SG_SPRINT("test labels: ");
test_labels->get_labels().display_vector();
CCSVFile test_labels_file("test_labels_file.dat", 'w');
test_labels->save(&test_labels_file);
SG_UNREF(svm);
SG_UNREF(test_features);
SG_UNREF(test_labels);
exit_shogun();
} catch(ShogunException& e) {
SG_SPRINT(e.get_exception_string());
return - 1;
}
return 0;
}
training ...
num train vectors: 400
num train labels: 400
total sv:37, bias:-0.428868
accuracy: 99.750000%
testing ...
num test vectors: 400
num test labels: 400
test labels: vector=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
SHOGUN a juste le bon niveau de granularité dans la conception de classe, et l'API est suffisamment abstraite, mais le code devient sale lorsque l'opération de comptage de références est incluse. .. En outre, les données d'apprentissage sont lues à partir du fichier à l'aide de la classe CCSVFile. Bien qu'il soit nommé CSV, il peut également lire des fichiers dans lesquels des données bidimensionnelles sont écrites séparées par des espaces au lieu du format CSV.
Pour s'entendre avec SHOGUN et OpenCV, il peut être utile de créer un adaptateur capable de convertir entre shogun :: SGMatrix et cv :: Mat. De plus, OpenCV est également compatible avec CUDA, donc j'aimerais que SHOGUN le prenne également en charge.
Python Modular Ensuite, utilisons la liaison Python de SHOGUN. Deux sont fournis, python_static et python_modular, mais j'utiliserai python_modular car il a une interface plus intelligente.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import modshogun as sg
import numpy as np
import matplotlib.pyplot as plt
def classifier():
train_datafile = sg.CSVFile('traindata.dat')
train_labelsfile = sg.CSVFile('labeldata.dat')
test_datafile = sg.CSVFile('testdata.dat')
train_features = sg.RealFeatures(train_datafile)
train_labels = sg.BinaryLabels(train_labelsfile)
test_features = sg.RealFeatures(test_datafile)
print('training ...')
width = 2.1
kernel = sg.GaussianKernel(train_features, train_features, width)
C = 1.0
svm = sg.LibSVM(C, kernel, train_labels)
svm.train()
sv = svm.get_support_vectors()
bias = svm.get_bias()
print('total sv:%s, bias:%s' % (len(sv), bias))
predict_labels = svm.apply(train_features)
measure = sg.ErrorRateMeasure()
measure.evaluate(predict_labels, train_labels)
print('accuracy: %s%%' % (measure.get_accuracy()*100))
print('testing ...')
test_labels = svm.apply(test_features)
print(test_labels.get_labels())
if __name__=='__main__':
classifier()
training ...
total sv:37, bias:-0.428868128708
accuracy: 99.75%
testing ...
[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
...réduction
J'ai eu le même résultat que la version C ++ (libshogun). Visualisons le résultat de la classification en utilisant matplotlib.
import numpy as np
import matplotlib.pyplot as plt
##Obtenir la limite de classification
def getboundary(plotrange, classifier):
x = np.arange(plotrange[0], plotrange[1], .1)
y = np.arange(plotrange[2], plotrange[3], .1)
xx, yy = np.meshgrid(x, y)
gridmatrix = np.vstack((xx.flatten(), yy.flatten()))
gridfeatures = sg.RealFeatures(gridmatrix)
gridlabels = classifier.apply(gridfeatures)
zz = gridlabels.get_labels().reshape(xx.shape)
return (xx, yy, zz)
##Obtenez la limite de classification en spécifiant la plage de dessin et le classificateur
xx, yy, zz = getboundary([-4,8,-4,5], svm)
##Tracer des limites de classification
plt.contour(xx, yy, zz, [1,-1])
Pour le moment, j'ai organisé une utilisation simple en C ++ et Python. Étant donné que SHOGUN implémente non seulement SVM mais également divers algorithmes d'apprentissage automatique, je voudrais procéder à la vérification en utilisant des tâches plus pratiques.
Recommended Posts