001/*
002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo.common.libsvm;
018
019import org.tribuo.Output;
020import libsvm.svm_parameter;
021
022import java.io.Serializable;
023import java.util.Arrays;
024import java.util.logging.Logger;
025
026/**
027 * A container for SVM parameters and the kernel.
028 */
029public class SVMParameters<T extends Output<T>> implements Serializable {
030    private static final long serialVersionUID = 1L;
031    
032    private static final Logger logger = Logger.getLogger(SVMParameters.class.getName());
033
034    /**
035     * The type of the SVM.
036     */
037    protected final SVMType<T> svmType;
038
039    /**
040     * The kernel.
041     */
042    protected final KernelType kernelType;
043
044    /**
045     * The libSVM format parameters.
046     */
047    protected final svm_parameter parameters = new svm_parameter();
048
049    /**
050     * Constructs the default SVMParameters using the specified SVMType and KernelType.
051     * @param svmType The SVM algorithm.
052     * @param kernelType The kernel.
053     */
054    public SVMParameters(SVMType<T> svmType, KernelType kernelType) {
055        this.svmType = svmType;
056        this.kernelType = kernelType;
057        parameters.svm_type = svmType.getNativeType();
058        parameters.kernel_type = kernelType.getNativeType();
059        //
060        // These are defaults, which are only compatible with SVM type
061        // C_SVC and kernel type RBF
062        parameters.degree = 3;
063        parameters.gamma = 0;   // 1/num_features
064        parameters.coef0 = 0;
065        parameters.nu = 0.5;
066        parameters.cache_size = 500;
067        parameters.C = 1;
068        parameters.eps = 1e-3;
069        parameters.p = 0.1;
070        parameters.shrinking = 1;
071        parameters.probability = 0;
072        parameters.nr_weight = 0;
073        parameters.weight_label = new int[0];
074        parameters.weight = new double[0];
075    }
076
077    /**
078     * Gets the SVM type.
079     * @return The SVM type.
080     */
081    public SVMType<T> getSvmType() {
082        return svmType;
083    }
084
085    /**
086     * Gets the kernel type.
087     * @return The kernel type.
088     */
089    public KernelType getKernelType() {
090        return kernelType;
091    }
092
093    /**
094     * Gets the underlying SVM parameter object.
095     * @return The SVM parameters.
096     */
097    public svm_parameter getParameters() {
098        return parameters;
099    }
100
101    @Override
102    public String toString() {
103        return svmParamsToString(parameters);
104    }
105    
106    /**
107     * Makes the model that is built provide probability estimates.
108     */
109    public void setProbability() {
110        parameters.probability = 1;
111    }
112
113    /**
114     * Sets the cost for C_SVC.
115     * @param c The cost.
116     */
117    public void setCost(double c) {
118        if(svmType.isNu() || !svmType.isClassification()) {
119            logger.warning(String.format("Setting cost %f for non-C_SVC model", c));
120        }
121        parameters.C = c;
122    }
123
124    /**
125     * Sets the value of nu for NU_SVM.
126     * @param nu The nu.
127     */
128    public void setNu(double nu) {
129        if(!svmType.isNu()) {
130            logger.warning(String.format("Setting nu %f for non-NU_SVM model", nu));
131        }
132        parameters.nu = nu;
133    }
134
135    /**
136     * Sets the coefficient.
137     * @param coeff The coefficient.
138     */
139    public void setCoeff(double coeff) {
140        parameters.coef0 = coeff;
141    }
142
143    /**
144     * Sets the termination closeness.
145     * @param epsilon The termination criterion.
146     */
147    public void setEpsilon(double epsilon) {
148        parameters.p = epsilon;
149    }
150
151    /**
152     * Sets the degree of the polynomial kernel.
153     * @param degree The polynomial degree.
154     */
155    public void setDegree(int degree) {
156        parameters.degree = degree;
157    }
158
159    /**
160     * Sets gamma in the RBF kernel.
161     * @param gamma The gamma.
162     */
163    public void setGamma(double gamma) {
164        parameters.gamma = gamma;
165    }
166
167    /**
168     * Gets the gamma value.
169     * @return The gamma value.
170     */
171    public double getGamma() {
172        return parameters.gamma;
173    }
174
175    /**
176     * Sets the cache size.
177     * @param cacheMB The cache size.
178     */
179    public void setCacheSize(double cacheMB) {
180        if(cacheMB <= 0) {
181            throw new IllegalArgumentException("Cache must be larger than 0MB");
182        }
183        parameters.cache_size = cacheMB;
184    }
185
186    /**
187     * Deep copy of the svm_parameters including the arrays.
188     * @param input The parameters to copy.
189     * @return A copy of the svm_parameters.
190     */
191    public static svm_parameter copyParameters(svm_parameter input) {
192        svm_parameter copy = new svm_parameter();
193        copy.svm_type = input.svm_type;
194        copy.kernel_type = input.kernel_type;
195        copy.degree = input.degree;
196        copy.gamma = input.gamma;
197        copy.coef0 = input.coef0;
198        copy.cache_size = input.cache_size;
199        copy.eps = input.eps;
200        copy.C = input.C;
201        copy.nr_weight = input.nr_weight;
202        copy.nu = input.nu;
203        copy.p = input.p;
204        copy.shrinking = input.shrinking;
205        copy.probability = input.probability;
206        copy.weight_label = input.weight_label != null ? Arrays.copyOf(input.weight_label,input.weight_label.length) : null;
207        copy.weight = input.weight != null ? Arrays.copyOf(input.weight,input.weight.length) : null;
208        return copy;
209    }
210
211    /**
212     * A sensible toString for svm_parameter.
213     * @param param The parameters.
214     * @return A String describing the parameters.
215     */
216    public static String svmParamsToString(svm_parameter param) {
217        StringBuilder sb = new StringBuilder();
218        sb.append("svm_parameter(svm_type=");
219        sb.append(param.svm_type);
220        sb.append(", kernel_type=");
221        sb.append(param.kernel_type);
222        sb.append(", degree=");
223        sb.append(param.degree);
224        sb.append(", gamma=");
225        sb.append(param.gamma);
226        sb.append(", coef0=");
227        sb.append(param.coef0);
228        sb.append(", cache_size=");
229        sb.append(param.coef0);
230        sb.append(", eps=");
231        sb.append(param.eps);
232        sb.append(", C=");
233        sb.append(param.C);
234        sb.append(", nr_weight=");
235        sb.append(param.nr_weight);
236        if (param.weight_label != null) {
237            sb.append(", weight_label=");
238            sb.append(Arrays.toString(param.weight_label));
239        }
240        if (param.weight != null) {
241            sb.append(", weight=");
242            sb.append(Arrays.toString(param.weight));
243        }
244        sb.append(", nu=");
245        sb.append(param.nu);
246        sb.append(", p=");
247        sb.append(param.p);
248        sb.append(", shrinking=");
249        sb.append(param.shrinking);
250        sb.append(", probability=");
251        sb.append(param.probability);
252        sb.append(')');
253        return sb.toString();
254    }
255}