Bonjour,
au boulot, on développe un logiciel qui classifie des site en fonction qu’il sont ou non des menaces style phishing/distributeur de malware ou pas.
Pour cela on a un process qui consiste à extraire des features puis à les passer à différents classifieurs.
par expérience, on sait que sur certains cas, un classifieur empirique sera meilleur (moins de FN et pas de FP) qu’un classifieur en machine learning mais dans beaucoup d’autres cas, le ML c’est plus efficace.
Du coup on a organisé notre système ainsi :
On considère une interface "Classifier" (qu’on appelé AnalysisFunction pour pas confondre les trucs empiriques et les ML) :
1 2 3 4 5 | public abstract class AnalysisFunction { abstract public StatusType analyze(List<TokenEntity> tokens); abstract public double getPhishingProbability(List<TokenEntity> tokens); } |
A partir de là, je crée une "pool" de fonction d’analyse qui va se charger de choisir la fonction (l’algo n’est pas optimal mais bon) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class PoolAnalysisFunction extends AnalysisFunction{ private final List<AnalysisFunction> candidates; private final ChoiceFunction choice; private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class); public PoolAnalysisFunction(List<AnalysisFunction> candidates, ChoiceFunction choice) { this.candidates = candidates; this.choice = choice; } @Override public StatusType analyze(List<TokenEntity> tokens) { try { return choice.chooseAmong(candidates, tokens).analyze(tokens); } catch (ImpossibleChoiceException e){ LOG.fatal("Not enough analysis function.", e); return StatusType.CLEAN; } } @Override public double getPhishingProbability(List<TokenEntity> tokens) { try { return choice.chooseAmong(candidates, tokens).getPhishingProbability(tokens); } catch (ImpossibleChoiceException e){ LOG.fatal("Not enough analysis function.", e); return 0; } } } |
Il s’avère que cette pool est totalement configurable : c’est à dire que je lui donne toutes les fonctions que je veux mettre du moment qu’elles sont dans le classpath.
Pour cela j’utilise une factory :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import org.glassfish.hk2.api.Factory; public class PoolFunctionFactory implements Factory<AnalysisFunction> { private final PoolAnalysisParameters parameters; private static final Logger LOG = LogManager.getLogger(PoolAnalysisFunction.class); @Inject public PoolFunctionFactory(PoolAnalysisParameters parameters) { this.parameters = parameters; } @Override public AnalysisFunction provide() { try { Class<?> choice = Class.forName(parameters.getChoiceFunctionFQDN()); ChoiceFunction choiceFunction = new PhishingPriorityChoiceFunction(); // default choice if(choice.getSuperclass().isInstance(ChoiceFunction.class)){ choiceFunction = (ChoiceFunction) choice.newInstance(); } List<AnalysisFunction> analysisFunctions = new LinkedList<>(); // C'est ici que ça foire } return new PoolAnalysisFunction(analysisFunctions, choiceFunction); } catch (ClassNotFoundException|IllegalAccessException|InstantiationException e){ LOG.fatal(e, e); } return null; } @Override public void dispose(AnalysisFunction analysisFunction) { LOG.trace(String.format("%s end of life", analysisFunction)); } } ` |
En fait j’aimerai pouvoir instancier les fonctions à mettre dans la pool grâce à leur nom. Pour cela j’ai une clef style analysis.pool.functions=com.vadesecure.analysis.function.EmpiricalFunction1,com.vadesecure.analysis.function.MLFunction1
.
Le problème c’est que ces objets on des dépendances : qui des propriétés pour configurer des seuils et des coefs, qui un modèle (oui, le ML se base sur des modèles). Cela j’aimerai pouvois l’injecter dynamiquement.
Prenons un exemple avec une fonction basée sur un SVM simple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class SVMF2AnalysisFunction extends AnalysisFunction { private final SVMContainer modelContainer; private double probability = 0.0; private double threshold = 0.9; @Inject // ça, c'est un truc injecté car j'ai un process qui tourne en parallèle pour builder et mettre à jour le modèle public SVMF2AnalysisFunction(SVMContainer modelContainer) { this.modelContainer = modelContainer; } @Override public StatusType analyze(List<TokenEntity> tokens) { if (modelContainer.getModel() == null) { return null; } probability = modelContainer.getModel().analyse(tokens.stream()); return probability >= threshold ? StatusType.PHISHING : StatusType.CLEAN; } @Override public double getPhishingProbability(List<TokenEntity> tokens) { return probability; } } |
Ma question est donc : comment faire pour pouvoir générer les différentes fonctions et y injecter les dépendances dans ma factory?
J’avais penser injecter le serviceLocator mais ça ne semble pas être une chose commune (j’ai pas trouvé de doc à ce propos). Un collègue m’a dit que les Proxy pouvaient aider mais je ne vois pas en quoi perso.
Avez-vous des idées.