def solve(self, sample, c_value, kernel): r""" Solve the variable-quality SVM classification optimization problem corresponding to the supplied sample, according to specified value for tradeoff constant `C` and kernel `k`. INPUT: - ``sample`` -- list or tuple of ``AccuracyExample`` instances whose labels are all set either to `1` or `-1`. - ``c_value`` -- float or None (the former choice selects the soft-margin version of the algorithm) value for the tradeoff constant `C`. - ``kernel`` -- ``Kernel`` instance defining the kernel to be used. OUTPUT: list of float values -- optimal values for the optimization problem. EXAMPLES: Consider the following representation of the AND binary function, and a default instantiation for ``CVXOPTVQClassificationSolver``: :: >>> from yaplf.data import LabeledExample, AccuracyExample >>> and_sample = [AccuracyExample(LabeledExample((1, 1), 1), 0), ... AccuracyExample(LabeledExample((0, 0), -1), 0), ... AccuracyExample(LabeledExample((0, 1), -1), 1), ... AccuracyExample(LabeledExample((1, 0), -1), 0)] >>> from yaplf.algorithms.svm.solvers \ ... import CVXOPTVQClassificationSolver >>> s = CVXOPTVQClassificationSolver() Once the solver instance is available, it is possible to invoke its ``solve``function, specifying a labeled sample such as ``and_sample``, a positive value for the constant `c_value` and a kernel instance in order to get the solution of the corresponding SV classification optimization problem: :: >>> from yaplf.models.kernel import LinearKernel >>> s.solve(and_sample, 2, LinearKernel()) [2, 0.0, 2, 0.0] The value for `c_value` can be set to ``None``, in order to build and solve the original optimization problem rather than the soft-margin formulation; analogously, a different kernel can be used as argument to the solver: :: >>> from yaplf.models.kernel import PolynomialKernel >>> s.solve(and_sample, None, PolynomialKernel(3)) [0.15135135150351597, 0.0, 0.097297297016552056, 0.054054053943170456] Note however that this class should never be used directly. It is automatically used by ``SVMVQClassificationAlgorithm``. AUTHORS: - Dario Malchiodi (2010-04-12) """ # cvxopt solves the problem # min 1/2 x' Q x + p' x # subject to G x >= h and A x = b # dict below is mapped to the above symbols as follows: # problem["obj_quad"] -> Q # problem["obj_lin"] -> p # problem["ineq_coeff"] -> G # problem["ineq_const"] -> h # problem["eq_coeff"] -> A # problem["eq_const"] -> b solvers.options['show_progress'] = self.verbose solvers.options['maxiters'] = self.max_iterations solvers.options['solver'] = self.solver # coercion to float in the following assignment is required # in order to work with sage notebook num_examples = len(sample) problem = {} problem["obj_quad"] = cvxopt_matrix([[ \ float(elem_i.example.label * elem_j.example.label * \ (kernel.compute(elem_i.example.pattern, elem_j.example.pattern) - \ elem_i.example.label * elem_j.example.label * (elem_i.accuracy + \ elem_j.accuracy))) for elem_i in sample] for elem_j in sample]) problem["obj_lin"] = cvxopt_matrix([-1.0 for i in range(num_examples)]) if c_value is None: problem["ineq_coeff"] = cvxopt_matrix([ [float(-1.0 * kronecker_delta(i, j)) for i in range(num_examples)] + [float(-1.0 * sample[j].accuracy)] for j in range(num_examples)]) problem["ineq_const"] = cvxopt_matrix( [float(0.0)] * num_examples + [float(1.0 - self.epsilon)]) else: problem["ineq_coeff"] = cvxopt_matrix([ [float(-1.0 * kronecker_delta(i, j)) for i in range(num_examples)] + [float(kronecker_delta(i, j)) for i in range(num_examples)] + [float(-1.0 * sample[j].accuracy)] for j in range(num_examples)]) problem["ineq_const"] = cvxopt_matrix([float(0.0)] * num_examples + [float(c_value)] * num_examples + [float(1.0 - self.epsilon)]) problem["eq_coeff"] = cvxopt_matrix([float(elem.example.label) for elem in sample], (1, num_examples)) problem["eq_const"] = cvxopt_matrix(0.0) sol = solvers.qp(problem["obj_quad"], problem["obj_lin"], problem["ineq_coeff"], problem["ineq_const"], problem["eq_coeff"], problem["eq_const"]) return [chop(x, right=c_value) for x in list(sol['x'])]
def solve(self, sample, c=float("inf"), kernel=LinearKernel()): r""" Solve the SVM classification optimization problem corresponding to the supplied sample, according to specified value for the tradeoff constant `C`. INPUT: - ``sample`` -- list or tuple of ``LabeledExample`` instances whose labels are all set either to `1` or `-1`. - ``c`` -- float or ``float('inf')`` (the former choice selects the soft-margin version of the algorithm) value for the tradeoff constant `C`. - ``kernel`` -- ``Kernel`` instance defining the kernel to be used (default: ``LinearKernel()``, accounting for a linear kernel). OUTPUT: list of float values -- optimal values for the optimization problem. EXAMPLES: Consider the following representation of the AND binary function, and a default instantiation for ``NEOSClassificationSolver``: :: >>> from yaplf.data import LabeledExample >>> and_sample = [LabeledExample((1, 1), 1), ... LabeledExample((0, 0), -1), LabeledExample((0, 1), -1), ... LabeledExample((1, 0), -1)] >>> from yaplf.algorithms.svm.classification.solvers \ ... import NEOSClassificationSolver >>> s = NEOSClassificationSolver() Once the solver instance is available, it is possible to invoke its ``solve``function, specifying a labeled sample such as ``and_sample``, a positive value for the constant `C` and a kernel instance in order to get the solution of the corresponding SV classification optimization problem: :: >>> from yaplf.models.kernel import LinearKernel >>> s.solve(and_sample, 2, LinearKernel()) [2, 0, 1.0, 1.0] The value for `C` can be set to ``float('inf')``, in order to build and solve the original optimization problem rather than the soft-margin formulation: :: >>> s.solve(and_sample, float('inf'), LinearKernel()) [4.0, 0, 2.0, 2.0] Note however that this class should never be used directly. It is automatically used by ``SVMClassificationAlgorithm``. AUTHORS: - Dario Malchiodi (2011-02-05) """ neos = xmlrpclib.Server("http://%s:%d" % ("www.neos-server.org", 3332)) num_examples = len(sample) input_dimension = len(sample[0].pattern) constraint = " <= " + str(c) if c != float("inf") else "" kernel_description = AMPLKernelFactory(kernel).get_kernel_description() # that is, something like sum{k in 1..n}(x[i,k]*x[j,k]) pattern_description = ["param x:\t"] label_description = ["param y:=\n"] for component_index in range(input_dimension): pattern_description.append(str(component_index + 1)) pattern_description.append("\t") pattern_description.append(":=\n") example_number = 1 for example in sample: pattern_description.append(str(example_number)) for component in example.pattern: pattern_description.append("\t") pattern_description.append(str(component)) label_description.append(str(example_number)) label_description.append("\t") label_description.append(str(sample[example_number - 1].label)) example_number = example_number + 1 if example_number > len(sample): pattern_description.append(";") label_description.append(";") pattern_description.append("\n") label_description.append("\n") xml = """ <document> <category>nco</category> <solver>SNOPT</solver> <inputMethod>AMPL</inputMethod> <model><![CDATA[ param m integer > 0 default %d; # number of sample points param n integer > 0 default %d; # sample space dimension param x {1..m,1..n}; # sample points param y {1..m}; # sample labels param dot{i in 1..m,j in 1..m}:=%s; var alpha{1..m} >=0%s; maximize quadratic_form: sum{i in 1..m} alpha[i] -1/2*sum{i in 1..m,j in 1..m}alpha[i]*alpha[j]*y[i]*y[j]*dot[i,j]; subject to linear_constraint: sum{i in 1..m} alpha[i]*y[i]=0; ]]></model> <data><![CDATA[ data; %s %s ]]></data> <commands><![CDATA[ option solver snopt; solve; printf: "("; printf {i in 1..m-1}:"%%f,",alpha[i]; printf: "%%f)",alpha[m]; ]]></commands> </document> """ % ( num_examples, input_dimension, kernel_description, constraint, "".join(pattern_description), "".join(label_description), ) (job_number, password) = neos.submitJob(xml) if self.verbose: print xml print "job number: %s" % job_number offset = 0 status = "" while status != "Done": (msg, offset) = neos.getIntermediateResults(job_number, password, offset) if self.verbose: print msg.data status = neos.getJobStatus(job_number, password) msg = neos.getFinalResults(job_number, password).data if self.verbose: print msg begin = 0 while msg[begin] != "(": begin = begin + 1 end = len(msg) - 1 while msg[end] != ")": end = end - 1 return [chop(alpha, right=c) for alpha in eval(msg[begin : end + 1])]
def solve(self, sample, c=float('inf'), kernel=LinearKernel(), tolerance=1e-6): r""" Solve the SVM classification optimization problem corresponding to the supplied sample, according to specified value for the tradeoff constant `C`. INPUT: - ``sample`` -- list or tuple of ``LabeledExample`` instances whose labels are all set either to `1` or `-1`. - ``c`` -- float value for the tradeoff constant `C`. ``float('inf')`` selects the soft-margin version of the algorithm) - ``kernel`` -- ``Kernel`` instance defining the kernel to be used. - ``tolerance`` -- tolerance to be used when clipping values to the extremes of an interval. OUTPUT: list of float values -- optimal values for the optimization problem. EXAMPLES: Consider the following representation of the AND binary function, and a default instantiation for ``GurobiClassificationSolver``: :: >>> from yaplf.data import LabeledExample >>> and_sample = [LabeledExample((1, 1), 1), ... LabeledExample((0, 0), -1), LabeledExample((0, 1), -1), ... LabeledExample((1, 0), -1)] >>> from yaplf.algorithms.svm.classification.solvers \ ... import GurobiClassificationSolver >>> s = GurobiClassificationSolver() Once the solver instance is available, it is possible to invoke its ``solve`` function, specifying a labeled sample such as ``and_sample``, a positive value for the constant `C` and a kernel instance in order to get the solution of the corresponding SV classification optimization problem: :: >>> from yaplf.models.kernel import LinearKernel >>> s.solve(and_sample, 2, LinearKernel()) [2, 0, 0.999999999992222, 0.999999999992222] The value for `C` can be set to ``float('inf')`` (which is also its default value), in order to build and solve the original optimization problem rather than the soft-margin formulation: :: >>> s.solve(and_sample, float('inf'), LinearKernel()) [4.00000000000204, 0, 1.999999999976717, 1.99999999997672] Note however that this class should never be used directly. It is automatically used by ``SVMClassificationAlgorithm``. AUTHORS: - Dario Malchiodi (2014-03-03) """ m = len(sample) patterns = [e.example.pattern for e in sample] labels = [e.example.label for e in sample] accuracy = [e.accuracy for e in sample] model = gurobipy.Model('classify') for i in range(m): if c == float('inf'): model.addVar(name='alpha_%d' % i, lb=0, vtype=gurobipy.GRB.CONTINUOUS) else: model.addVar(name='alpha_%d' % i, lb=0, ub=c, vtype=gurobipy.GRB.CONTINUOUS) model.update() alphas = model.getVars() obj = gurobipy.QuadExpr() + sum(alphas) map(lambda (i, j): obj.add(alphas[i] * alphas[j] * labels[i] * labels[j], -0.5*(kernel.compute(patterns[i], patterns[j]) - labels[i] * labels[j] * (accuracy[i]+accuracy[j]))), [(i, j) for i in xrange(m) for j in xrange(m)]) model.setObjective(obj, gurobipy.GRB.MAXIMIZE) constEqual = gurobipy.LinExpr() # map(lambda x: constEqual.add(x, 1.0), # [a*l for a, l in zip(alphas, labels)]) # model.addConstr(constEqual, gurobipy.GRB.EQUAL, 0) constGreater = gurobipy.LinExpr(1 + tolerance) for addend in [a*l for a, l in zip(alphas, labels)]: constEqual.add(addend, 1.0) constGreater.add(addend, 1.0) model.addConstr(constEqual, gurobipy.GRB.EQUAL, 0) model.addConstr(constGreater, gurobipy.GRB.GREATER_EQUAL, tolerance) if not self.verbose: model.setParam('OutputFlag', False) model.optimize() alphas_opt = [chop(a.x, right=c, tolerance=tolerance) for a in alphas] return alphas_opt
def solve(self, sample, c=float("inf"), kernel=LinearKernel()): r""" Solve the SVM classification optimization problem corresponding to the supplied sample, according to specified value for the tradeoff constant `C`. INPUT: - ``sample`` -- list or tuple of ``LabeledExample`` instances whose labels are all set either to `1` or `-1`. - ``c`` -- float or ``float('inf')`` (the former choice selects the soft-margin version of the algorithm) value for the tradeoff constant `C`. - ``kernel`` -- ``Kernel`` instance defining the kernel to be used (default value: ``LinearKernel()``, using a linear kernel) OUTPUT: list of float values -- optimal values for the optimization problem. EXAMPLES: Consider the following representation of the AND binary function, and a default instantiation for ``CVXOPTClassificationSolver``: :: >>> from yaplf.data import LabeledExample >>> and_sample = [LabeledExample((1, 1), 1), ... LabeledExample((0, 0), -1), LabeledExample((0, 1), -1), ... LabeledExample((1, 0), -1)] >>> from yaplf.algorithms.svm.classification.solvers \ ... import CVXOPTClassificationSolver >>> s = CVXOPTClassificationSolver() Once the solver instance is available, it is possible to invoke its ``solve``function, specifying a labeled sample such as ``and_sample``, a positive value for the constant `C` and a kernel instance in order to get the solution of the corresponding SV classification optimization problem: :: >>> from yaplf.models.kernel import LinearKernel >>> s.solve(and_sample, 2, LinearKernel()) [2, 0, 0.9999998669645057, 0.9999998669645057] The value for `C` can be set to ``float('inf')``, in order to build and solve the original optimization problem rather than the soft-margin formulation: :: >>> s.solve(and_sample, float('inf'), LinearKernel()) [4.000001003300218, 0, 2.000000364577095, 2.000000364577095] Note however that this class should never be used directly. It is automatically used by ``SVMClassificationAlgorithm``. AUTHORS: - Dario Malchiodi (2010-02-22) """ solvers.options["show_progress"] = self.verbose solvers.options["maxiters"] = self.max_iterations solvers.options["solver"] = self.solver # cvxopt solves the problem # min 1/2 x' Q x + p' x # subject to G x >= h and A x = b # dict below is mapped to the above symbols as follows: # problem["obj_quad"] -> Q # problem["obj_lin"] -> p # problem["ineq_coeff"] -> G # problem["ineq_const"] -> h # problem["eq_coeff"] -> A # problem["eq_const"] -> b num_examples = len(sample) problem = {} problem["obj_quad"] = cvxopt_matrix( [ [elem_i.label * elem_j.label * kernel.compute(elem_i.pattern, elem_j.pattern) for elem_i in sample] for elem_j in sample ] ) problem["obj_lin"] = cvxopt_matrix([-1.0] * num_examples) if c == float("inf"): problem["ineq_coeff"] = cvxopt_matrix(-1.0 * eye(num_examples)) problem["ineq_const"] = cvxopt_matrix([0.0] * num_examples) else: problem["ineq_coeff"] = cvxopt_matrix( [ [-1.0 * kronecker_delta(i, j) for i in range(num_examples)] + [kronecker_delta(i, j) for i in range(num_examples)] for j in range(num_examples) ] ) problem["ineq_const"] = cvxopt_matrix([float(0.0)] * num_examples + [float(c)] * num_examples) # coercion to float in the following assignment is required # in order to work with sage notebooks problem["eq_coeff"] = cvxopt_matrix([float(elem.label) for elem in sample], (1, num_examples)) problem["eq_const"] = cvxopt_matrix(0.0) # was # sol = solvers.qp(quad_coeff, lin_coeff, ineq_coeff, ineq_const, \ # eq_coeff, eq_const) sol = solvers.qp( problem["obj_quad"], problem["obj_lin"], problem["ineq_coeff"], problem["ineq_const"], problem["eq_coeff"], problem["eq_const"], ) if sol["status"] != "optimal": raise ValueError("cvxopt returned status " + sol["status"]) # was # alpha = map(lambda x: chop(x, right = c), list(sol['x'])) alpha = [chop(x, right=c) for x in list(sol["x"])] return alpha