3
votes

Enums pour implémenter différentes méthodes en Java

Supposons que j'ai construit la classe suivante

MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward)
        .setNumberThreads(10)
        .setBatch(5)
        .setEpochs(100)
        .setOptimizer(OptimizationAlgorithmType.MOMENTUM.setAlpha(0.01).setBeta(0.99));

Mon objectif était de construire une API qui, lorsque je sélectionne un Enum spécifique, différentes méthodes seraient disponibles. Par exemple

MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation(feedForward)
        .setNumberThreads(10)
        .setBatch(5)
        .setEpochs(100)
        .setOptimizer(OptimizationAlgorithmType.VANILLA.setEta(0.001));

Ou

public enum OptimizationAlgorithmType {

    VANILLA(new HashMap<String, Double>()) {

        private final static String ETA = "eta";
        private Double eta = null;

        public OptimizationAlgorithmType setEta(Double eta) {
            this.eta = eta;
            return this;
        }

        @Override
        public Map<String, Double> getHyperarameters() {
            this.map.put(ETA, eta);
            return this.map;
        }       
    }, 

    MOMENTUM(new HashMap<String, Double>()) {

        private final static String ALPHA = "alpha";
        private final static String BETA = "beta";

        private Double alpha = null;
        private Double beta = null;

        public OptimizationAlgorithmType setAlpha(Double alpha) {
            this.alpha = alpha;
            return this;
        }

        public OptimizationAlgorithmType setBeta(Double beta) {
            this.beta = beta;
            return this;
        }

        @Override
        public Map<String, Double> getHyperarameters() {
            this.map.put(ALPHA, alpha);
            this.map.put(BETA, beta);
            return this.map;
        }
    };

    protected Map<String, Double> map;

    private OptimizationAlgorithmType(Map<String, Double> map) {
        this.map = map;
    }

    public abstract Map<String, Double> getHyperarameters();    
}

Malheureusement, cela n'est pas autorisé. L'ide met en garde contre les méthodes inutilisées (par exemple: setEta () ) - et les méthodes ne sont pas du tout disponibles pour sélectionner dans l'énumération spécifique.

Puis-je utiliser une astuce pour obtenir l'API souhaitée?

Merci

Modifier Ajout d'une autre réponse ci-dessous p>


3 commentaires

Il semble vraiment que vous ne devriez pas utiliser enum pour cela. En plus du problème que vous avez posé, vous utilisez également les énumérations mutuellement, ce qui est assez douteux. Il semble que vous devriez écrire des constructeurs normaux ou quelque chose à la place. (Et pour mémoire, il n'y a pas d'astuce ici pour que cela fonctionne. N'utilisez pas d'énumérations.)


@Radiodef J'essayais juste de présenter l'API d'une certaine manière - je peux la faire fonctionner avec d'autres moyens.


Vous pouvez présenter l'API de la même manière pour vos clients en utilisant des champs finaux statiques publics au lieu d'énumérer des valeurs.


3 Réponses :


0
votes

J'ai trouvé une meilleure

D'abord une interface, puis les deux classes de paramètres qui implémentent l'interface

MultiThreadBackpropagation backpropagation = new MultiThreadBackpropagation.BackpropagationBuilder(feedForward)
        .setBatch(5)
        .setEpochs(100)
        .setThreads(10)
        .setOptimizer(OptimizationType.MOMENTUM
                .set(MomentumParameter.ALPHA, 0.001)
                .set(MomentumParameter.BETA, 0.92))
        .build();

Maintenant, ajoutez la classe enum principale

    public enum OptimizationType {

        VANILLA{
            private VanillaParameter eta = VanillaParameter.ETA;

            @Override
            public Hyperparameter get(Hyperparameter parameter) {
                switch ((VanillaParameter) parameter) {
                case ETA: return this.eta;
                }
                return null;
            }

            @Override
            public OptimizationType set(Hyperparameter parameter, Double value) {

                switch ((VanillaParameter) parameter) {
                case ETA: 
                    this.eta.setValue(value);
                    break;
                }
                return OptimizationType.VANILLA;
            }
        },
        ...
}

Et l'API a l'air vraiment sympa à mon avis et ressemble à mon intention initiale. Dans l'attente des suggestions

public interface Hyperparameter {...}   

public enum VanillaParameter implements Hyperparameter {
        ETA {

            @Override
            public Double getValue() {
                return this.etaValue;
            }

            @Override
            public void setValue(Double value) {

                if (value == null) {
                    throw new IllegalStateException("Parameter value cannot be set to null!");
                }
                this.etaValue = value;
            }
        };
        protected Double etaValue = 0.005;
    }

    public enum MomentumParameter implements Hyperparameter {
    ...
    }


2 commentaires

chaque fois que vous appelez: set (paramètre Hyperparameter, Double value), vous modifiez la valeur de eta pour tous ceux qui utilisent OptimizationType.VANILLA


@RayTayek Oui très vrai et très problématique.



0
votes

essayez peut-être quelque chose comme:

import java.util.Arrays;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

public class So54067082enums_to_implement_different_methods_in_java {
    enum Type {
        vanilla(new TreeSet<>(Arrays.asList(new String[] {"eta"}))),
        momentum(new TreeSet<>(Arrays.asList(new String[] {"alpha","beta"})));
        Type(Set<String> names) {
            this.names=names;

        }
        final Set<String> names;
    }
    static class ParameterSet {
        ParameterSet(Type type) {
            this.type=type;
        }
        Double get(String name) {
            if(type.names.contains(name))
                return map.get(name);
            else throw new RuntimeException("oops");
        }
        void set(String name,Double value) {
            if(type.names.contains(name))
                map.put(name,value);
            else throw new RuntimeException("oops");
        }
        @Override public String toString() {
            return "ParameterSet [type="+type+", map="+map+"]";
        }
        final Type type;
        private final SortedMap<String,Double> map=new TreeMap<>();

    }
    public static void main(String[] args) {
        ParameterSet parameterSet=new ParameterSet(Type.vanilla);
        parameterSet.set("eta",.01);
        System.out.println(parameterSet);
        ParameterSet parameterSet2=new ParameterSet(Type.momentum);
        parameterSet2.set("alpha",.1);
        parameterSet2.set("beta",.9);
        System.out.println(parameterSet2);
    }
}


0 commentaires

0
votes

Je ne vois pas pourquoi vous ne pourriez pas simplement utiliser une approche moins naïve du problème:

class OptimizationAlgorithm {
  public static class Vanilla implements OptimizationAlgorithm {
    public Vanilla setAlpha(float a) {
      return this;
    }

    public Vanilla setBeta(float a) {
      return this;
    }
  }

  public static Vanilla vanilla() { return new Vanilla(); }
}

backpropagation.setOptimizer(OptimizationAlgorithm.vanilla().setAlpha(0.05f).setBeta(0.10f));

Beaucoup moins de code standard, pas de problème d'instances statiques modifiables et fondamentalement la même interface de l'extérieur.


0 commentaires