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.classification.sgd.objectives; 018 019import com.oracle.labs.mlrg.olcut.config.Config; 020import com.oracle.labs.mlrg.olcut.provenance.ConfiguredObjectProvenance; 021import com.oracle.labs.mlrg.olcut.provenance.impl.ConfiguredObjectProvenanceImpl; 022import com.oracle.labs.mlrg.olcut.util.Pair; 023import org.tribuo.classification.sgd.LabelObjective; 024import org.tribuo.math.la.SGDVector; 025import org.tribuo.math.la.SparseVector; 026import org.tribuo.math.util.NoopNormalizer; 027import org.tribuo.math.util.VectorNormalizer; 028 029/** 030 * Hinge loss, scores the correct value margin and any incorrect predictions -margin. 031 * By default the margin is 1.0. 032 * <p> 033 * The Hinge loss does not generate a probabilistic model, and uses a {@link NoopNormalizer}. 034 */ 035public class Hinge implements LabelObjective { 036 037 @Config(description="The classification margin.") 038 private double margin = 1.0; 039 040 /** 041 * Construct a hinge objective with the supplied margin. 042 * @param margin The margin to use. 043 */ 044 public Hinge(double margin) { 045 this.margin = margin; 046 } 047 048 /** 049 * Construct a hinge objective with a margin of 1.0. 050 */ 051 public Hinge() { 052 this(1.0); 053 } 054 055 @Deprecated 056 @Override 057 public Pair<Double, SGDVector> valueAndGradient(int truth, SGDVector prediction) { 058 return lossAndGradient(truth, prediction); 059 } 060 061 /** 062 * Returns a {@link Pair} of {@link Double} and {@link SGDVector} representing the loss 063 * and per label gradients respectively. 064 * @param truth The true label id. 065 * @param prediction The prediction for each label id. 066 * @return The loss and per label gradient. 067 */ 068 @Override 069 public Pair<Double,SGDVector> lossAndGradient(Integer truth, SGDVector prediction) { 070 prediction.add(truth,-margin); 071 int predIndex = prediction.indexOfMax(); 072 073 if (truth == predIndex) { 074 return new Pair<>(0.0, SparseVector.createSparseVector(prediction.size(),new int[0], new double[0])); 075 } else { 076 int[] indices = new int[2]; 077 double[] values = new double[2]; 078 if (truth < predIndex) { 079 indices[0] = truth; 080 values[0] = margin; 081 indices[1] = predIndex; 082 values[1] = -margin; 083 } else { 084 indices[0] = predIndex; 085 values[0] = -margin; 086 indices[1] = truth; 087 values[1] = margin; 088 } 089 SparseVector output = SparseVector.createSparseVector(prediction.size(),indices,values); 090 double loss = prediction.get(truth) - prediction.get(predIndex); 091 return new Pair<>(loss,output); 092 } 093 } 094 095 /** 096 * Returns a new {@link NoopNormalizer}. 097 * @return The vector normalizer. 098 */ 099 @Override 100 public VectorNormalizer getNormalizer() { 101 return new NoopNormalizer(); 102 } 103 104 /** 105 * Returns false. 106 * @return False. 107 */ 108 @Override 109 public boolean isProbabilistic() { 110 return false; 111 } 112 113 @Override 114 public String toString() { 115 return "Hinge(margin="+margin+")"; 116 } 117 118 @Override 119 public ConfiguredObjectProvenance getProvenance() { 120 return new ConfiguredObjectProvenanceImpl(this,"LabelObjective"); 121 } 122}