├── .gitignore ├── LICENSE.txt ├── Makefile ├── README_MATLAB.txt ├── README_STRUCT.txt ├── makefile_windows.m ├── svm-struct-matlab.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── svm_light ├── LICENSE.txt ├── Makefile ├── kernel.h ├── svm_classify.c ├── svm_common.c ├── svm_common.h ├── svm_hideo.c ├── svm_learn.c ├── svm_learn.h ├── svm_learn_main.c └── svm_loqo.c ├── svm_struct ├── Makefile ├── svm_struct_classify.c ├── svm_struct_common.c ├── svm_struct_common.h ├── svm_struct_learn.c ├── svm_struct_learn.h └── svm_struct_main.c ├── svm_struct_api.c ├── svm_struct_api.h ├── svm_struct_api_types.h ├── svm_struct_learn.m ├── svm_struct_learn_custom.c ├── svm_struct_learn_mex.c ├── test_svm_struct_learn.m └── test_svm_struct_learn_ker.m /.gitignore: -------------------------------------------------------------------------------- 1 | svm_struct_learn.mex* 2 | .DS_Store 3 | 4 | *.pbxuser 5 | *.perspective 6 | *.perspectivev3 7 | 8 | build/ 9 | 10 | *.mode1v3 11 | *.mode2v3 12 | 13 | svm-struct-matlab.xcodeproj/xcuserdata/ 14 | svm-struct-matlab.xcodeproj/project.xcworkspace/xcuserdata/ 15 | 16 | svm-struct-matlab-*.tar.gz 17 | 18 | versions 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | SVM-Struct 2 | ---------- 3 | 4 | Available at http://svmlight.joachims.org/ 5 | 6 | Author: Thorsten Joachims 7 | thorsten@joachims.org 8 | 9 | Cornell University 10 | Department of Computer Science 11 | 4153 Upson Hall 12 | Ithaca, NY 14853 13 | USA 14 | 15 | LICENSING TERMS 16 | 17 | This program is granted free of charge for non-commercial research and 18 | education purposes. However you must obtain a license from the author 19 | to use it for commercial purposes. 20 | 21 | Scientific results produced using the software provided shall 22 | acknowledge the use of SVM-Struct. Please cite as 23 | 24 | I. Tsochantaridis, T. Joachims, T. Hofmann, and Y. Altun, 25 | Large Margin Methods for Structured and Interdependent Output Variables, 26 | Journal of Machine Learning Research (JMLR), 6(Sep):1453-1484, 2005. 27 | http://jmlr.csail.mit.edu/papers/volume6/tsochantaridis05a/tsochantaridis05a.pdf 28 | 29 | Moreover shall the author of SVM-Struct be informed about the 30 | publication. 31 | 32 | The software and derivatives of the software must not be distributed 33 | without prior permission of the author. 34 | 35 | By using SVM-Struct you agree to the licensing terms. 36 | 37 | 38 | NO WARRANTY 39 | 40 | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 41 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT 42 | WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER 43 | PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, 44 | EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 45 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 46 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 47 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 48 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 49 | 50 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 51 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 52 | REDISTRIBUTE THE PROGRAM, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 53 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF 54 | THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO 55 | LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 56 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY 57 | OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED 58 | OF THE POSSIBILITY OF SUCH DAMAGES. 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # file: Makefile 2 | # brief: Compile MEXified SVM-struct 3 | # author: Andrea Vedaldi 4 | 5 | MEX ?= mex 6 | VER = 1.2 7 | PACKAGE = svm-struct-matlab 8 | 9 | # -------------------------------------------------------------------- 10 | # Auto-detect architecture 11 | # -------------------------------------------------------------------- 12 | 13 | Darwin_PPC_ARCH := mac 14 | Darwin_Power_Macintosh_ARCH := mac 15 | Darwin_i386_ARCH := maci64 16 | Darwin_x86_64_ARCH := maci64 17 | Linux_i386_ARCH := glnx86 18 | Linux_i686_ARCH := glnx86 19 | Linux_unknown_ARC := glnx86 20 | Linux_x86_64_ARCH := glnxa64 21 | 22 | UNAME := $(shell uname -sm) 23 | ARCH ?= $($(shell echo "$(UNAME)" | tr \ _)_ARCH) 24 | 25 | # Mac OS X Intel 32 26 | ifeq ($(ARCH),maci) 27 | SDKROOT ?= $(shell xcodebuild -version -sdk macosx | sed -n '/^Path\:/p' | sed 's/^Path: //') 28 | MACOSX_DEPLOYMENT_TARGET ?= 10.4 29 | CFLAGS += -m32 -isysroot $(SDKROOT) -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) 30 | LDFLAGS += -Wl,-syslibroot,$(SDKROOT) -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) 31 | MEXEXT = mexmaci 32 | CC = gcc 33 | MEXFLAGS += CC='$(CC)' LD='$(CC)' 34 | endif 35 | 36 | # Mac OS X Intel 64 37 | ifeq ($(ARCH),maci64) 38 | SDKROOT ?= $(shell xcodebuild -version -sdk macosx | sed -n '/^Path\:/p' | sed 's/^Path: //') 39 | MACOSX_DEPLOYMENT_TARGET ?= 10.4 40 | CFLAGS += -m64 -isysroot $(SDKROOT) -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) 41 | LDFLAGS += -Wl,-syslibroot,$(SDKROOT) -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) 42 | MEXEXT = mexmaci64 43 | CC = gcc 44 | MEXFLAGS += CC='$(CC)' LD='$(CC)' 45 | endif 46 | 47 | # Linux-32 48 | ifeq ($(ARCH),glnx86) 49 | CFLAGS += -march=i686 50 | LDFLAGS += 51 | MEXEXT = mexglx 52 | endif 53 | 54 | # Linux-64 55 | ifeq ($(ARCH),glnxa64) 56 | LDFLAGS += 57 | MEXEXT = mexa64 58 | endif 59 | 60 | MEXFLAGS += -largeArrayDims -$(ARCH) CFLAGS='$$CFLAGS $(CFLAGS) -Wall' LDFLAGS='$$LDFLAGS $(LDFLAGS)' 61 | BUILD = build/$(ARCH) 62 | 63 | # -------------------------------------------------------------------- 64 | # Build 65 | # -------------------------------------------------------------------- 66 | 67 | svm_light_objs := \ 68 | $(BUILD)/svm_light/svm_hideo.o \ 69 | $(BUILD)/svm_light/svm_learn.o \ 70 | $(BUILD)/svm_light/svm_common.o 71 | 72 | svm_struct_objs := \ 73 | $(BUILD)/svm_struct/svm_struct_learn.o \ 74 | $(BUILD)/svm_struct/svm_struct_common.o 75 | 76 | svm_custom_objs := \ 77 | $(BUILD)/svm_struct_api.o \ 78 | $(BUILD)/svm_struct_learn_custom.o 79 | 80 | $(BUILD)/%.o : %.c 81 | $(MEX) $(MEXFLAGS) -outdir "$(dir $@)" -c "$<" 82 | 83 | svm_struct_learn.$(MEXEXT) : svm_struct_learn_mex.c \ 84 | $(svm_custom_objs) \ 85 | $(svm_light_objs) \ 86 | $(svm_struct_objs) 87 | $(MEX) $(MEXFLAGS) $^ -output "$@" 88 | 89 | .PHONY: clean 90 | clean: 91 | rm -fv $(svm_custom_objs) $(svm_struct_objs) $(svm_light_objs) 92 | find . -name '*~' -delete 93 | 94 | .PHONY: distclean 95 | distclean: clean 96 | for ext in mexmaci mexmaci64 mexglx mexa64 ; \ 97 | do \ 98 | rm -fv svm_struct_learn.$${ext} ; \ 99 | done 100 | rm -rf build 101 | rm -rf $(PACKAGE)-*.tar.gz 102 | 103 | .PHONY: dist 104 | dist: 105 | git archive --format=tar --prefix=$(PACKAGE)-$(VER)/ v$(VER) | gzip >$(PACKAGE)-$(VER).tar.gz 106 | @if [ -n "$$(git diff v$(VER) HEAD)" ] ; \ 107 | then \ 108 | echo "Warning: the repository HEAD is not the same as the tag v$(VER)" ; \ 109 | fi 110 | 111 | 112 | # svm_struct dependencies 113 | svm_struct_api.o: \ 114 | $(BUILD)/.dir \ 115 | svm_struct_api.c \ 116 | svm_struct_api.h \ 117 | svm_struct_api_types.h \ 118 | svm_struct/svm_struct_common.h 119 | 120 | svm_struct_learn_custom.o: \ 121 | $(BUILD)/.dir \ 122 | svm_struct_learn_custom.c \ 123 | svm_struct_api.h \ 124 | svm_struct_api_types.h \ 125 | svm_light/svm_common.h \ 126 | svm_struct/svm_struct_common.h 127 | 128 | svm_struct/svm_struct_mex.o : \ 129 | $(BUILD)/.dir \ 130 | svm_struct/svm_struct_mex.c 131 | 132 | svm_struct/svm_struct_common.o : \ 133 | $(BUILD)/.dir \ 134 | svm_struct/svm_struct_common.c \ 135 | svm_struct/svm_struct_common.h \ 136 | svm_light/svm_common.h \ 137 | svm_struct_api_types.h 138 | 139 | svm_struct/svm_struct_learn.o : \ 140 | $(BUILD)/.dir \ 141 | svm_struct/svm_struct_learn.c \ 142 | svm_struct/svm_struct_common.h \ 143 | svm_light/svm_common.h \ 144 | svm_light/svm_learn.h \ 145 | svm_struct_api_types.h \ 146 | svm_struct_api.h 147 | 148 | svm_struct/svm_struct_classify.o : \ 149 | $(BUILD)/.dir \ 150 | svm_struct/svm_struct_classify.c \ 151 | svm_struct/svm_struct_common.h \ 152 | svm_light/svm_common.h \ 153 | svm_struct_api_types.h \ 154 | svm_struct_api.h 155 | 156 | # svm_light dependencies 157 | svm_light/svm_learn.o : \ 158 | $(BUILD)/.dir \ 159 | svm_light/svm_learn.c \ 160 | svm_light/svm_learn.h \ 161 | svm_light/svm_common.h 162 | 163 | svm_light/svm_common.o : \ 164 | $(BUILD)/.dir \ 165 | svm_light/svm_common.c \ 166 | svm_light/svm_common.h \ 167 | svm_light/kernel.h 168 | 169 | svm_light/svm_hideo.o : \ 170 | $(BUILD)/.dir \ 171 | svm_light/svm_hideo.c 172 | 173 | -------------------------------------------------------------------------------- /README_MATLAB.txt: -------------------------------------------------------------------------------- 1 | SVM^struct for MATLAB 2 | V1.2 3 | Andrea Vedaldi 4 | 5 | This is a patch to Thorsten Joachims (http://www.joachims.org/) 6 | SVM-struct implementation that provides a simple-to-use MATLAB 7 | interface. 8 | 9 | OBTAINING: 10 | 11 | svm-struct-matlab homepage is 12 | http://www.vlfeat.org/~vedaldi/code/svm-struct-matlab.html. The GIT 13 | repository of the project can be downloaded at 14 | https://github.com/vedaldi/svm-srtuct-matlab. 15 | 16 | COMPILING: 17 | 18 | To compile this software use the included Makefile. It should work out 19 | of the box on Linux and Mac OS X provided that MATLAB MEX program is 20 | in the command line path. So just issue 21 | 22 | > make 23 | 24 | The only files that are needed to run the package with MATLAB are 25 | svm_struct_learn.mex* (MEX program) and svm_struct_learn.m 26 | (documentation). 27 | 28 | If MEX is not on the command line, the path can be specified as 29 | 30 | > make MEX=/bin/mex 31 | 32 | where is MATLAB root directory. Finally, it is also 33 | possible to specify manually the architecture 34 | 35 | > make ARCH=maci # Mac Intel 32 bit 36 | > make ARCH=maci64 # Mac Intel 64 bit 37 | > make ARCH=glnx86 # Linux 32 bit 38 | > make ARCH=glnxa64 # Linux 64 bit 39 | 40 | To clean the build products use 41 | 42 | > make clean # clean all build but the MEX file 43 | > make distclean # clean all build products 44 | 45 | USAGE: 46 | 47 | There is only one MATLAB command, i.e. SVM_STRUCT_LEARN. This commands 48 | take as input handles to the function implementing the maximal 49 | constraint violation search, the loss, and the feature map. See 50 | TEST_SVM_STRUCT_LEARN and TEST_SVM_STRUCT_LEARN_KER for example usage 51 | and SVM_STRUCT_LEARN built-in help for further information. 52 | 53 | CHANGES: 54 | 55 | 1.3 - Adds support for the endIterationFn callback. 56 | 1.2 - Adds support for Xcode 4.0 and Mac OS X 10.7 and greater 57 | 1.1 - Adds Windows support (thanks to Iasonas Kokkinos). 58 | 1.0 - Initial public release 59 | 60 | LICENSE: 61 | 62 | The MATLAB wrapper of T. Joachim's SVM^struct is distributed under the 63 | following ``MIT license''. This license covers only the additions to 64 | T. Joachims SVM^struct code that constitute the MATLAB interface (the 65 | "Software") and does not cover SVM^struct itself. See the file 66 | LICENSE.txt for the SVM^struct license. 67 | 68 | Copyright (C) 2011 by Andrea Vedaldi 69 | 70 | Permission is hereby granted, free of charge, to any person obtaining a copy 71 | of this software and associated documentation files (the "Software"), to deal 72 | in the Software without restriction, including without limitation the rights 73 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 74 | copies of the Software, and to permit persons to whom the Software is 75 | furnished to do so, subject to the following conditions: 76 | 77 | The above copyright notice and this permission notice shall be included in 78 | all copies or substantial portions of the Software. 79 | 80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 81 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 82 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 83 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 84 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 85 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 86 | THE SOFTWARE. 87 | 88 | -------------------------------------------------------------------------------- /README_STRUCT.txt: -------------------------------------------------------------------------------- 1 | Readme for the SVM-light structure learning API 2 | ----------------------------------------------- 3 | Thorsten Joachims, 03.07.2004 4 | 5 | The API allows to implement different versions of the learning 6 | algorithm for learning different kinds of structures. To adapt to a 7 | new structure, one needs to modify the files 8 | 9 | svm_struct_api_types.h 10 | svm_struct_api.c 11 | 12 | Both files already contain empty templates. The first file contains 13 | the type definitions that need to be changed. PATTERN is the structure 14 | for storing the x-part of an example (x,y), LABEL is the y-part. The 15 | learned model will be stored in STRUCTMODEL. Finally, 16 | STRUCT_LEARN_PARM can be used to store any parameters that you might 17 | want to pass to the function. 18 | 19 | The second file contains the function you need to implement. See the 20 | documentation in the file for details. 21 | -------------------------------------------------------------------------------- /makefile_windows.m: -------------------------------------------------------------------------------- 1 | %% svm_light .o files 2 | fprintf('doing hideo \n'); 3 | mex -largeArrayDims -c -DWIN ./svm_light/svm_hideo.c 4 | fprintf('doing learn \n'); 5 | mex -largeArrayDims -c -DWIN ./svm_light/svm_learn.c 6 | fprintf('doing common \n'); 7 | mex -largeArrayDims -c -DWIN ./svm_light/svm_common.c 8 | 9 | %% svm_struct .o files 10 | mex -largeArrayDims -c -DWIN ./svm_struct/svm_struct_learn.c 11 | mex -largeArrayDims -c -DWIN ./svm_struct/svm_struct_common.c 12 | 13 | %% svm_struct - custom .o files 14 | mex -largeArrayDims -c -DWIN ./svm_struct_api.c 15 | mex -largeArrayDims -c -DWIN ./svm_struct_learn_custom.c 16 | 17 | mex -largeArrayDims -DWIN -output svm_struct_learn svm_struct_learn_mex.c svm_struct_api.obj svm_struct_learn_custom.obj svm_struct_learn.obj svm_struct_common.obj svm_common.obj svm_learn.obj svm_hideo.obj 18 | 19 | delete *.obj 20 | -------------------------------------------------------------------------------- /svm-struct-matlab.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXFileReference section */ 10 | 2D002F7B104FCC3200981B2C /* Makefile */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; path = Makefile; sourceTree = ""; wrapsLines = 0; }; 11 | 2D002F7D104FCC3200981B2C /* kernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kernel.h; sourceTree = ""; tabWidth = 8; }; 12 | 2D002F80104FCC3200981B2C /* svm_classify.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_classify.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 13 | 2D002F81104FCC3200981B2C /* svm_common.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_common.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 14 | 2D002F82104FCC3200981B2C /* svm_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_common.h; sourceTree = ""; tabWidth = 8; }; 15 | 2D002F84104FCC3200981B2C /* svm_hideo.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_hideo.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 16 | 2D002F86104FCC3200981B2C /* svm_learn.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_learn.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 17 | 2D002F87104FCC3200981B2C /* svm_learn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_learn.h; sourceTree = ""; tabWidth = 8; }; 18 | 2D002F89104FCC3200981B2C /* svm_learn_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svm_learn_main.c; sourceTree = ""; tabWidth = 8; }; 19 | 2D002F8A104FCC3200981B2C /* svm_loqo.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_loqo.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 20 | 2D002F8D104FCC3200981B2C /* svm_struct_classify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svm_struct_classify.c; sourceTree = ""; tabWidth = 8; }; 21 | 2D002F8E104FCC3200981B2C /* svm_struct_common.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_struct_common.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 22 | 2D002F8F104FCC3200981B2C /* svm_struct_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_struct_common.h; sourceTree = ""; tabWidth = 8; }; 23 | 2D002F91104FCC3200981B2C /* svm_struct_learn.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_struct_learn.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 24 | 2D002F92104FCC3200981B2C /* svm_struct_learn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_struct_learn.h; sourceTree = ""; tabWidth = 8; }; 25 | 2D002F94104FCC3200981B2C /* svm_struct_main.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = svm_struct_main.c; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; 26 | 2D002F95104FCC3200981B2C /* svm_struct_api.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = svm_struct_api.c; sourceTree = ""; tabWidth = 8; xcLanguageSpecificationIdentifier = xcode.lang.c; }; 27 | 2D002F96104FCC3200981B2C /* svm_struct_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_struct_api.h; sourceTree = ""; tabWidth = 8; }; 28 | 2D002F97104FCC3200981B2C /* svm_struct_api_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svm_struct_api_types.h; sourceTree = ""; tabWidth = 8; }; 29 | 2D002F98104FCC3200981B2C /* svm_struct_learn.m */ = {isa = PBXFileReference; explicitFileType = sourcecode; fileEncoding = 4; lineEnding = 0; path = svm_struct_learn.m; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 30 | 2D002F99104FCC3200981B2C /* svm_struct_learn_custom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svm_struct_learn_custom.c; sourceTree = ""; tabWidth = 8; }; 31 | 2D002F9A104FCC3200981B2C /* test_svm_struct_learn.m */ = {isa = PBXFileReference; explicitFileType = sourcecode; fileEncoding = 4; lineEnding = 0; path = test_svm_struct_learn.m; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 32 | 2D002F9B104FCC3200981B2C /* test_svm_struct_learn_ker.m */ = {isa = PBXFileReference; explicitFileType = sourcecode; fileEncoding = 4; lineEnding = 0; path = test_svm_struct_learn_ker.m; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 33 | 2D0030081050194100981B2C /* svm_struct_learn_mex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svm_struct_learn_mex.c; sourceTree = ""; }; 34 | 2D60637D13C8CAB70083CAB2 /* README_MATLAB.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README_MATLAB.txt; sourceTree = ""; wrapsLines = 0; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXGroup section */ 38 | 2D002F66104FCBBE00981B2C = { 39 | isa = PBXGroup; 40 | children = ( 41 | 2D60637D13C8CAB70083CAB2 /* README_MATLAB.txt */, 42 | 2D002F7B104FCC3200981B2C /* Makefile */, 43 | 2D002F7C104FCC3200981B2C /* svm_light */, 44 | 2D002F8B104FCC3200981B2C /* svm_struct */, 45 | 2D002F95104FCC3200981B2C /* svm_struct_api.c */, 46 | 2D002F96104FCC3200981B2C /* svm_struct_api.h */, 47 | 2D002F97104FCC3200981B2C /* svm_struct_api_types.h */, 48 | 2D002F99104FCC3200981B2C /* svm_struct_learn_custom.c */, 49 | 2D0030081050194100981B2C /* svm_struct_learn_mex.c */, 50 | 2D002F98104FCC3200981B2C /* svm_struct_learn.m */, 51 | 2D002F9A104FCC3200981B2C /* test_svm_struct_learn.m */, 52 | 2D002F9B104FCC3200981B2C /* test_svm_struct_learn_ker.m */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 2D002F7C104FCC3200981B2C /* svm_light */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 2D002F7D104FCC3200981B2C /* kernel.h */, 60 | 2D002F80104FCC3200981B2C /* svm_classify.c */, 61 | 2D002F81104FCC3200981B2C /* svm_common.c */, 62 | 2D002F82104FCC3200981B2C /* svm_common.h */, 63 | 2D002F84104FCC3200981B2C /* svm_hideo.c */, 64 | 2D002F86104FCC3200981B2C /* svm_learn.c */, 65 | 2D002F87104FCC3200981B2C /* svm_learn.h */, 66 | 2D002F89104FCC3200981B2C /* svm_learn_main.c */, 67 | 2D002F8A104FCC3200981B2C /* svm_loqo.c */, 68 | ); 69 | path = svm_light; 70 | sourceTree = ""; 71 | tabWidth = 8; 72 | }; 73 | 2D002F8B104FCC3200981B2C /* svm_struct */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 2D002F8D104FCC3200981B2C /* svm_struct_classify.c */, 77 | 2D002F8E104FCC3200981B2C /* svm_struct_common.c */, 78 | 2D002F8F104FCC3200981B2C /* svm_struct_common.h */, 79 | 2D002F91104FCC3200981B2C /* svm_struct_learn.c */, 80 | 2D002F92104FCC3200981B2C /* svm_struct_learn.h */, 81 | 2D002F94104FCC3200981B2C /* svm_struct_main.c */, 82 | ); 83 | path = svm_struct; 84 | sourceTree = ""; 85 | tabWidth = 8; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXLegacyTarget section */ 90 | 2D002F9C104FCC8300981B2C /* all */ = { 91 | isa = PBXLegacyTarget; 92 | buildArgumentsString = "$(ACTION)"; 93 | buildConfigurationList = 2D002FA7104FCCBB00981B2C /* Build configuration list for PBXLegacyTarget "all" */; 94 | buildPhases = ( 95 | ); 96 | buildToolPath = /usr/bin/make; 97 | dependencies = ( 98 | ); 99 | name = all; 100 | passBuildSettingsInEnvironment = 1; 101 | productName = all; 102 | }; 103 | /* End PBXLegacyTarget section */ 104 | 105 | /* Begin PBXProject section */ 106 | 2D002F68104FCBBE00981B2C /* Project object */ = { 107 | isa = PBXProject; 108 | attributes = { 109 | LastUpgradeCheck = 0450; 110 | }; 111 | buildConfigurationList = 2D002F6B104FCBBE00981B2C /* Build configuration list for PBXProject "svm-struct-matlab" */; 112 | compatibilityVersion = "Xcode 3.2"; 113 | developmentRegion = English; 114 | hasScannedForEncodings = 0; 115 | knownRegions = ( 116 | en, 117 | ); 118 | mainGroup = 2D002F66104FCBBE00981B2C; 119 | projectDirPath = ""; 120 | projectRoot = ""; 121 | targets = ( 122 | 2D002F9C104FCC8300981B2C /* all */, 123 | ); 124 | }; 125 | /* End PBXProject section */ 126 | 127 | /* Begin XCBuildConfiguration section */ 128 | 2D002F69104FCBBE00981B2C /* Debug */ = { 129 | isa = XCBuildConfiguration; 130 | buildSettings = { 131 | ARCH = "$(ARCH_$(ARCHS))"; 132 | ARCHS = ( 133 | i386, 134 | x86_64, 135 | ); 136 | ARCH_i386 = maci; 137 | ARCH_x86_64 = maci64; 138 | DOXYGEN = doxygen; 139 | HEADER_SEARCH_PATHS = "$(MATLABROOT)/extern/include"; 140 | MACOSX_DEPLOYMENT_TARGET = 10.5; 141 | MATLABROOT = /Applications/Matlab_R2011A.app; 142 | MEX = "$(MATLABROOT)/bin/mex"; 143 | ONLY_ACTIVE_ARCH = YES; 144 | SDKROOT = macosx; 145 | }; 146 | name = Debug; 147 | }; 148 | 2D002F6A104FCBBE00981B2C /* Release */ = { 149 | isa = XCBuildConfiguration; 150 | buildSettings = { 151 | ARCH = "$(ARCH_$(ARCHS))"; 152 | ARCHS = ( 153 | i386, 154 | x86_64, 155 | ); 156 | ARCH_i386 = maci; 157 | ARCH_x86_64 = maci64; 158 | DOXYGEN = doxygen; 159 | HEADER_SEARCH_PATHS = "$(MATLABROOT)/extern/include"; 160 | MACOSX_DEPLOYMENT_TARGET = 10.5; 161 | MATLABROOT = /Applications/Matlab_R2011A.app; 162 | MEX = "$(MATLABROOT)/bin/mex"; 163 | NDEBUG = yes; 164 | ONLY_ACTIVE_ARCH = YES; 165 | SDKROOT = macosx; 166 | }; 167 | name = Release; 168 | }; 169 | 2D002F9D104FCC8300981B2C /* Debug */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | COMBINE_HIDPI_IMAGES = YES; 173 | }; 174 | name = Debug; 175 | }; 176 | 2D002F9E104FCC8300981B2C /* Release */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | COMBINE_HIDPI_IMAGES = YES; 180 | }; 181 | name = Release; 182 | }; 183 | /* End XCBuildConfiguration section */ 184 | 185 | /* Begin XCConfigurationList section */ 186 | 2D002F6B104FCBBE00981B2C /* Build configuration list for PBXProject "svm-struct-matlab" */ = { 187 | isa = XCConfigurationList; 188 | buildConfigurations = ( 189 | 2D002F69104FCBBE00981B2C /* Debug */, 190 | 2D002F6A104FCBBE00981B2C /* Release */, 191 | ); 192 | defaultConfigurationIsVisible = 0; 193 | defaultConfigurationName = Release; 194 | }; 195 | 2D002FA7104FCCBB00981B2C /* Build configuration list for PBXLegacyTarget "all" */ = { 196 | isa = XCConfigurationList; 197 | buildConfigurations = ( 198 | 2D002F9D104FCC8300981B2C /* Debug */, 199 | 2D002F9E104FCC8300981B2C /* Release */, 200 | ); 201 | defaultConfigurationIsVisible = 0; 202 | defaultConfigurationName = Release; 203 | }; 204 | /* End XCConfigurationList section */ 205 | }; 206 | rootObject = 2D002F68104FCBBE00981B2C /* Project object */; 207 | } 208 | -------------------------------------------------------------------------------- /svm-struct-matlab.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /svm_light/LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vedaldi/svm-struct-matlab/3fd1a1ca6cd76a7a822fb8079a79344992ae9f66/svm_light/LICENSE.txt -------------------------------------------------------------------------------- /svm_light/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # makefile for svm_light 3 | # 4 | # Thorsten Joachims, 2002 5 | # 6 | 7 | #Use the following to compile under unix or cygwin 8 | CC = gcc 9 | LD = gcc 10 | 11 | #Uncomment the following line to make CYGWIN produce stand-alone Windows executables 12 | #SFLAGS= -mno-cygwin 13 | 14 | CFLAGS= $(SFLAGS) -O3 # release C-Compiler flags 15 | LFLAGS= $(SFLAGS) -O3 # release linker flags 16 | #CFLAGS= $(SFLAGS) -pg -Wall -pedantic # debugging C-Compiler flags 17 | #LFLAGS= $(SFLAGS) -pg # debugging linker flags 18 | LIBS=-L. -lm # used libraries 19 | 20 | all: svm_learn_hideo svm_classify 21 | 22 | tidy: 23 | rm -f *.o 24 | rm -f pr_loqo/*.o 25 | 26 | clean: tidy 27 | rm -f svm_learn 28 | rm -f svm_classify 29 | rm -f libsvmlight.so 30 | 31 | help: info 32 | 33 | info: 34 | @echo 35 | @echo "make for SVM-light Thorsten Joachims, 1998" 36 | @echo 37 | @echo "Thanks to Ralf Herbrich for the initial version." 38 | @echo 39 | @echo "USAGE: make [svm_learn | svm_learn_loqo | svm_learn_hideo | " 40 | @echo " libsvmlight_hideo | libsvmlight_loqo | " 41 | @echo " svm_classify | all | clean | tidy]" 42 | @echo 43 | @echo " svm_learn builds the learning module (prefers HIDEO)" 44 | @echo " svm_learn_hideo builds the learning module using HIDEO optimizer" 45 | @echo " svm_learn_loqo builds the learning module using PR_LOQO optimizer" 46 | @echo " svm_classify builds the classfication module" 47 | @echo " libsvmlight_hideo builds shared object library that can be linked into" 48 | @echo " other code using HIDEO" 49 | @echo " libsvmlight_loqo builds shared object library that can be linked into" 50 | @echo " other code using PR_LOQO" 51 | @echo " all (default) builds svm_learn + svm_classify" 52 | @echo " clean removes .o and target files" 53 | @echo " tidy removes .o files" 54 | @echo 55 | 56 | # Create executables svm_learn and svm_classify 57 | 58 | svm_learn_hideo: svm_learn_main.o svm_learn.o svm_common.o svm_hideo.o 59 | $(LD) $(LFLAGS) svm_learn_main.o svm_learn.o svm_common.o svm_hideo.o -o svm_learn $(LIBS) 60 | 61 | #svm_learn_loqo: svm_learn_main.o svm_learn.o svm_common.o svm_loqo.o loqo 62 | # $(LD) $(LFLAGS) svm_learn_main.o svm_learn.o svm_common.o svm_loqo.o pr_loqo/pr_loqo.o -o svm_learn $(LIBS) 63 | 64 | svm_classify: svm_classify.o svm_common.o 65 | $(LD) $(LFLAGS) svm_classify.o svm_common.o -o svm_classify $(LIBS) 66 | 67 | 68 | # Create library libsvmlight.so, so that external code can get access to the 69 | # learning and classification functions of svm-light by linking this library. 70 | 71 | svm_learn_hideo_noexe: svm_learn_main.o svm_learn.o svm_common.o svm_hideo.o 72 | 73 | libsvmlight_hideo: svm_learn_main.o svm_learn.o svm_common.o svm_hideo.o 74 | $(LD) -shared svm_learn.o svm_common.o svm_hideo.o -o libsvmlight.so 75 | 76 | #svm_learn_loqo_noexe: svm_learn_main.o svm_learn.o svm_common.o svm_loqo.o loqo 77 | 78 | #libsvmlight_loqo: svm_learn_main.o svm_learn.o svm_common.o svm_loqo.o 79 | # $(LD) -shared svm_learn.o svm_common.o svm_loqo.o pr_loqo/pr_loqo.o -o libsvmlight.so 80 | 81 | # Compile components 82 | 83 | svm_hideo.o: svm_hideo.c 84 | $(CC) -c $(CFLAGS) svm_hideo.c -o svm_hideo.o 85 | 86 | #svm_loqo.o: svm_loqo.c 87 | # $(CC) -c $(CFLAGS) svm_loqo.c -o svm_loqo.o 88 | 89 | svm_common.o: svm_common.c svm_common.h kernel.h 90 | $(CC) -c $(CFLAGS) svm_common.c -o svm_common.o 91 | 92 | svm_learn.o: svm_learn.c svm_common.h 93 | $(CC) -c $(CFLAGS) svm_learn.c -o svm_learn.o 94 | 95 | svm_learn_main.o: svm_learn_main.c svm_learn.h svm_common.h 96 | $(CC) -c $(CFLAGS) svm_learn_main.c -o svm_learn_main.o 97 | 98 | svm_classify.o: svm_classify.c svm_common.h kernel.h 99 | $(CC) -c $(CFLAGS) svm_classify.c -o svm_classify.o 100 | 101 | #loqo: pr_loqo/pr_loqo.o 102 | 103 | #pr_loqo/pr_loqo.o: pr_loqo/pr_loqo.c 104 | # $(CC) -c $(CFLAGS) pr_loqo/pr_loqo.c -o pr_loqo/pr_loqo.o 105 | 106 | -------------------------------------------------------------------------------- /svm_light/kernel.h: -------------------------------------------------------------------------------- 1 | /************************************************************************/ 2 | /* */ 3 | /* kernel.h */ 4 | /* */ 5 | /* User defined kernel function. Feel free to plug in your own. */ 6 | /* */ 7 | /* Copyright: Thorsten Joachims */ 8 | /* Date: 16.12.97 */ 9 | /* */ 10 | /************************************************************************/ 11 | 12 | /* KERNEL_PARM is defined in svm_common.h The field 'custom' is reserved for */ 13 | /* parameters of the user defined kernel. You can also access and use */ 14 | /* the parameters of the other kernels. Just replace the line 15 | return((double)(1.0)); 16 | with your own kernel. */ 17 | 18 | /* Example: The following computes the polynomial kernel. sprod_ss 19 | computes the inner product between two sparse vectors. 20 | 21 | return((CFLOAT)pow(kernel_parm->coef_lin*sprod_ss(a,b) 22 | +kernel_parm->coef_const,(double)kernel_parm->poly_degree)); 23 | */ 24 | 25 | /* If you are implementing a kernel that is not based on a 26 | feature/value representation, you might want to make use of the 27 | field "userdefined" in SVECTOR. By default, this field will contain 28 | whatever string you put behind a # sign in the example file. So, if 29 | a line in your training file looks like 30 | 31 | -1 1:3 5:6 #abcdefg 32 | 33 | then the SVECTOR field "words" will contain the vector 1:3 5:6, and 34 | "userdefined" will contain the string "abcdefg". */ 35 | 36 | #include "../svm_struct_api_types.h" 37 | 38 | double custom_kernel(KERNEL_PARM *kernel_parm, SVECTOR *a, SVECTOR *b) 39 | /* plug in you favorite kernel */ 40 | { 41 | mxArray* structParm_array ; 42 | mxArray* kernelFn_array ; 43 | mxArray* args [6] ; 44 | mxArray* k_array ; 45 | double k ; 46 | int status ; 47 | 48 | { 49 | MexKernelInfo* info = (MexKernelInfo*) kernel_parm -> custom ; 50 | structParm_array = (mxArray*) info -> structParm ; 51 | kernelFn_array = (mxArray*) info -> kernelFn ; 52 | } 53 | 54 | args[0] = kernelFn_array ; 55 | args[1] = structParm_array ; 56 | args[2] = MexPhiCustomGetPattern (a->userdefined) ; 57 | args[3] = MexPhiCustomGetLabel (a->userdefined) ; 58 | args[4] = MexPhiCustomGetPattern (b->userdefined) ; 59 | args[5] = MexPhiCustomGetLabel (b->userdefined) ; 60 | 61 | status = mexCallMATLAB(1, &k_array, 6, args, "feval") ; 62 | 63 | if (status) { 64 | mexErrMsgTxt("Error while executing SPARM.KERNELFN") ; 65 | } 66 | if (mxGetClassID(k_array) == mxUNKNOWN_CLASS) { 67 | mexErrMsgTxt("SPARM.KERNELFN did not reutrn a result") ; 68 | } 69 | 70 | k = mxGetScalar (k_array) ; 71 | mxDestroyArray (k_array) ; 72 | 73 | return (k) ; 74 | } 75 | -------------------------------------------------------------------------------- /svm_light/svm_classify.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_classify.c */ 4 | /* */ 5 | /* Classification module of Support Vector Machine. */ 6 | /* */ 7 | /* Author: Thorsten Joachims */ 8 | /* Date: 02.07.02 */ 9 | /* */ 10 | /* Copyright (c) 2002 Thorsten Joachims - All rights reserved */ 11 | /* */ 12 | /* This software is available for non-commercial use only. It must */ 13 | /* not be modified and distributed without prior permission of the */ 14 | /* author. The author is not responsible for implications from the */ 15 | /* use of this software. */ 16 | /* */ 17 | /************************************************************************/ 18 | 19 | # include "svm_common.h" 20 | 21 | char docfile[200]; 22 | char modelfile[200]; 23 | char predictionsfile[200]; 24 | 25 | void read_input_parameters(int, char **, char *, char *, char *, long *, 26 | long *); 27 | void print_help(void); 28 | 29 | 30 | int main (int argc, char* argv[]) 31 | { 32 | DOC *doc; /* test example */ 33 | WORD *words; 34 | long max_docs,max_words_doc,lld; 35 | long totdoc=0,queryid,slackid; 36 | long correct=0,incorrect=0,no_accuracy=0; 37 | long res_a=0,res_b=0,res_c=0,res_d=0,wnum,pred_format; 38 | long j; 39 | double t1,runtime=0; 40 | double dist,doc_label,costfactor; 41 | char *line,*comment; 42 | FILE *predfl,*docfl; 43 | MODEL *model; 44 | 45 | read_input_parameters(argc,argv,docfile,modelfile,predictionsfile, 46 | &verbosity,&pred_format); 47 | 48 | nol_ll(docfile,&max_docs,&max_words_doc,&lld); /* scan size of input file */ 49 | max_words_doc+=2; 50 | lld+=2; 51 | 52 | line = (char *)my_malloc(sizeof(char)*lld); 53 | words = (WORD *)my_malloc(sizeof(WORD)*(max_words_doc+10)); 54 | 55 | model=read_model(modelfile); 56 | 57 | if(model->kernel_parm.kernel_type == 0) { /* linear kernel */ 58 | /* compute weight vector */ 59 | add_weight_vector_to_linear_model(model); 60 | } 61 | 62 | if(verbosity>=2) { 63 | printf("Classifying test examples.."); fflush(stdout); 64 | } 65 | 66 | if ((docfl = fopen (docfile, "r")) == NULL) 67 | { perror (docfile); exit (1); } 68 | if ((predfl = fopen (predictionsfile, "w")) == NULL) 69 | { perror (predictionsfile); exit (1); } 70 | 71 | while((!feof(docfl)) && fgets(line,(int)lld,docfl)) { 72 | if(line[0] == '#') continue; /* line contains comments */ 73 | parse_document(line,words,&doc_label,&queryid,&slackid,&costfactor,&wnum, 74 | max_words_doc,&comment); 75 | totdoc++; 76 | if(model->kernel_parm.kernel_type == LINEAR) {/* For linear kernel, */ 77 | for(j=0;(words[j]).wnum != 0;j++) { /* check if feature numbers */ 78 | if((words[j]).wnum>model->totwords) /* are not larger than in */ 79 | (words[j]).wnum=0; /* model. Remove feature if */ 80 | } /* necessary. */ 81 | } 82 | doc = create_example(-1,0,0,0.0,create_svector(words,comment,1.0)); 83 | t1=get_runtime(); 84 | 85 | if(model->kernel_parm.kernel_type == LINEAR) { /* linear kernel */ 86 | dist=classify_example_linear(model,doc); 87 | } 88 | else { /* non-linear kernel */ 89 | dist=classify_example(model,doc); 90 | } 91 | 92 | runtime+=(get_runtime()-t1); 93 | free_example(doc,1); 94 | 95 | if(dist>0) { 96 | if(pred_format==0) { /* old weired output format */ 97 | fprintf(predfl,"%.8g:+1 %.8g:-1\n",dist,-dist); 98 | } 99 | if(doc_label>0) correct++; else incorrect++; 100 | if(doc_label>0) res_a++; else res_b++; 101 | } 102 | else { 103 | if(pred_format==0) { /* old weired output format */ 104 | fprintf(predfl,"%.8g:-1 %.8g:+1\n",-dist,dist); 105 | } 106 | if(doc_label<0) correct++; else incorrect++; 107 | if(doc_label>0) res_c++; else res_d++; 108 | } 109 | if(pred_format==1) { /* output the value of decision function */ 110 | fprintf(predfl,"%.8g\n",dist); 111 | } 112 | if((int)(0.01+(doc_label*doc_label)) != 1) 113 | { no_accuracy=1; } /* test data is not binary labeled */ 114 | if(verbosity>=2) { 115 | if(totdoc % 100 == 0) { 116 | printf("%ld..",totdoc); fflush(stdout); 117 | } 118 | } 119 | } 120 | fclose(predfl); 121 | fclose(docfl); 122 | free(line); 123 | free(words); 124 | free_model(model,1); 125 | 126 | if(verbosity>=2) { 127 | printf("done\n"); 128 | 129 | /* Note by Gary Boone Date: 29 April 2000 */ 130 | /* o Timing is inaccurate. The timer has 0.01 second resolution. */ 131 | /* Because classification of a single vector takes less than */ 132 | /* 0.01 secs, the timer was underflowing. */ 133 | printf("Runtime (without IO) in cpu-seconds: %.2f\n", 134 | (float)(runtime/100.0)); 135 | 136 | } 137 | if((!no_accuracy) && (verbosity>=1)) { 138 | printf("Accuracy on test set: %.2f%% (%ld correct, %ld incorrect, %ld total)\n",(float)(correct)*100.0/totdoc,correct,incorrect,totdoc); 139 | printf("Precision/recall on test set: %.2f%%/%.2f%%\n",(float)(res_a)*100.0/(res_a+res_b),(float)(res_a)*100.0/(res_a+res_c)); 140 | } 141 | 142 | return(0); 143 | } 144 | 145 | void read_input_parameters(int argc, char **argv, char *docfile, 146 | char *modelfile, char *predictionsfile, 147 | long int *verbosity, long int *pred_format) 148 | { 149 | long i; 150 | 151 | /* set default */ 152 | strcpy (modelfile, "svm_model"); 153 | strcpy (predictionsfile, "svm_predictions"); 154 | (*verbosity)=2; 155 | (*pred_format)=1; 156 | 157 | for(i=1;(i=argc) { 169 | printf("\nNot enough input parameters!\n\n"); 170 | print_help(); 171 | exit(0); 172 | } 173 | strcpy (docfile, argv[i]); 174 | strcpy (modelfile, argv[i+1]); 175 | if((i+2) this help\n"); 191 | printf(" -v [0..3] -> verbosity level (default 2)\n"); 192 | printf(" -f [0,1] -> 0: old output format of V1.0\n"); 193 | printf(" -> 1: output the value of decision function (default)\n\n"); 194 | } 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /svm_light/svm_common.h: -------------------------------------------------------------------------------- 1 | /************************************************************************/ 2 | /* */ 3 | /* svm_common.h */ 4 | /* */ 5 | /* Definitions and functions used in both svm_learn and svm_classify. */ 6 | /* */ 7 | /* Author: Thorsten Joachims */ 8 | /* Date: 31.10.05 */ 9 | /* */ 10 | /* Copyright (c) 2005 Thorsten Joachims - All rights reserved */ 11 | /* */ 12 | /* This software is available for non-commercial use only. It must */ 13 | /* not be modified and distributed without prior permission of the */ 14 | /* author. The author is not responsible for implications from the */ 15 | /* use of this software. */ 16 | /* */ 17 | /************************************************************************/ 18 | 19 | #ifndef SVM_COMMON 20 | #define SVM_COMMON 21 | 22 | #include "mex.h" 23 | #include 24 | 25 | #define malloc(x) (mxMalloc(x)) 26 | #define realloc(x,y) (mxRealloc((x),(y))) 27 | #define free(x) (mxFree(x)) 28 | 29 | struct MexPhiCustomImpl_ 30 | { 31 | int counter ; 32 | mxArray * x ; 33 | mxArray * y ; 34 | } ; 35 | 36 | typedef struct MexPhiCustomImpl_ * MexPhiCustom ; 37 | 38 | #ifndef WIN 39 | #define inline_comm __inline__ 40 | #else 41 | #define inline_comm __inline 42 | #endif 43 | 44 | inline_comm static MexPhiCustom 45 | newMexPhiCustomFromPatternLabel (mxArray const * x, mxArray const *y) 46 | { 47 | MexPhiCustom phi ; 48 | phi = mxMalloc (sizeof(struct MexPhiCustomImpl_)) ; 49 | phi -> counter = 1 ; 50 | phi -> x = mxDuplicateArray (x) ; 51 | phi -> y = mxDuplicateArray (y) ; 52 | return phi ; 53 | } 54 | 55 | inline_comm static void 56 | releaseMexPhiCustom (MexPhiCustom phi) 57 | { 58 | if (phi) { 59 | phi -> counter -- ; 60 | if (phi -> counter == 0) { 61 | mxDestroyArray (phi -> x) ; 62 | mxDestroyArray (phi -> y) ; 63 | mxFree (phi) ; 64 | } 65 | } 66 | } 67 | 68 | inline_comm static void 69 | retainMexPhiCustom (MexPhiCustom phi) { 70 | if (phi) { 71 | phi -> counter ++ ; 72 | } 73 | } 74 | 75 | inline_comm static mxArray * 76 | MexPhiCustomGetPattern (MexPhiCustom phi) { 77 | assert (phi) ; 78 | return phi -> x ; 79 | } 80 | 81 | inline_comm static mxArray * 82 | MexPhiCustomGetLabel (MexPhiCustom phi) { 83 | assert (phi) ; 84 | return phi -> y ; 85 | } 86 | 87 | # include 88 | 89 | #ifdef _MSC_VER 90 | typedef __int32 int32_t; 91 | typedef unsigned __int32 uint32_t; 92 | typedef __int64 int64_t; 93 | typedef unsigned __int64 uint64_t; 94 | #else 95 | #include 96 | #endif 97 | 98 | # include 99 | # include 100 | # include 101 | # include 102 | # include 103 | # include 104 | 105 | # define VERSION "V6.20" 106 | # define VERSION_DATE "14.08.08" 107 | 108 | # define CFLOAT float /* the type of float to use for caching */ 109 | /* kernel evaluations. Using float saves */ 110 | /* us some memory, but you can use double, too */ 111 | # define FNUM int32_t /* the type used for storing feature ids */ 112 | # define FNUM_MAX 2147483647 /* maximum value that FNUM type can take */ 113 | # define FVAL float /* the type used for storing feature values */ 114 | # define MAXFEATNUM 99999999 /* maximum feature number (must be in 115 | valid range of FNUM type and long int!) */ 116 | 117 | # define LINEAR 0 /* linear kernel type */ 118 | # define POLY 1 /* polynomial kernel type */ 119 | # define RBF 2 /* rbf kernel type */ 120 | # define SIGMOID 3 /* sigmoid kernel type */ 121 | # define CUSTOM 4 /* userdefined kernel function from kernel.h */ 122 | # define GRAM 5 /* use explicit gram matrix from kernel_parm */ 123 | 124 | # define CLASSIFICATION 1 /* train classification model */ 125 | # define REGRESSION 2 /* train regression model */ 126 | # define RANKING 3 /* train ranking model */ 127 | # define OPTIMIZATION 4 /* train on general set of constraints */ 128 | 129 | # define MAXSHRINK 50000 /* maximum number of shrinking rounds */ 130 | 131 | typedef struct word { 132 | FNUM wnum; /* word number */ 133 | FVAL weight; /* word weight */ 134 | } WORD; 135 | 136 | typedef struct svector { 137 | WORD *words; /* The features/values in the vector by 138 | increasing feature-number. Feature 139 | numbers that are skipped are 140 | interpreted as having value zero. */ 141 | double twonorm_sq; /* The squared euclidian length of the 142 | vector. Used to speed up the RBF kernel. */ 143 | /* char *userdefined; */ 144 | MexPhiCustom userdefined ; 145 | /* You can put additional information 146 | here. This can be useful, if you are 147 | implementing your own kernel that 148 | does not work with feature/values 149 | representations (for example a 150 | string kernel). By default, 151 | svm-light will put here the string 152 | after the # sign from each line of 153 | the input file. */ 154 | long kernel_id; /* Feature vectors with different 155 | kernel_id's are orthogonal (ie. the 156 | feature number do not match). This 157 | is used for computing component 158 | kernels for linear constraints which 159 | are a sum of several different 160 | weight vectors. (currently not 161 | implemented). */ 162 | struct svector *next; /* Let's you set up a list of SVECTOR's 163 | for linear constraints which are a 164 | sum of multiple feature 165 | vectors. List is terminated by 166 | NULL. */ 167 | double factor; /* Factor by which this feature vector 168 | is multiplied in the sum. */ 169 | } SVECTOR; 170 | 171 | typedef struct doc { 172 | long docnum; /* Document ID. This has to be the position of 173 | the document in the training set array. */ 174 | long queryid; /* for learning rankings, constraints are 175 | generated for documents with the same 176 | queryID. */ 177 | double costfactor; /* Scales the cost of misclassifying this 178 | document by this factor. The effect of this 179 | value is, that the upper bound on the alpha 180 | for this example is scaled by this factor. 181 | The factors are set by the feature 182 | 'cost:' in the training data. */ 183 | long slackid; /* Index of the slack variable 184 | corresponding to this 185 | constraint. All constraints with the 186 | same slackid share the same slack 187 | variable. This can only be used for 188 | svm_learn_optimization. */ 189 | long kernelid; /* Position in gram matrix where kernel 190 | value can be found when using an 191 | explicit gram matrix 192 | (i.e. kernel_type=GRAM). */ 193 | SVECTOR *fvec; /* Feature vector of the example. The 194 | feature vector can actually be a 195 | list of feature vectors. For 196 | example, the list will have two 197 | elements, if this DOC is a 198 | preference constraint. The one 199 | vector that is supposed to be ranked 200 | higher, will have a factor of +1, 201 | the lower ranked one should have a 202 | factor of -1. */ 203 | } DOC; 204 | 205 | typedef struct learn_parm { 206 | long type; /* selects between regression and 207 | classification */ 208 | double svm_c; /* upper bound C on alphas */ 209 | double eps; /* regression epsilon (eps=1.0 for 210 | classification */ 211 | double svm_costratio; /* factor to multiply C for positive examples */ 212 | double transduction_posratio;/* fraction of unlabeled examples to be */ 213 | /* classified as positives */ 214 | long biased_hyperplane; /* if nonzero, use hyperplane w*x+b=0 215 | otherwise w*x=0 */ 216 | long sharedslack; /* if nonzero, it will use the shared 217 | slack variable mode in 218 | svm_learn_optimization. It requires 219 | that the slackid is set for every 220 | training example */ 221 | long svm_maxqpsize; /* size q of working set */ 222 | long svm_newvarsinqp; /* new variables to enter the working set 223 | in each iteration */ 224 | long kernel_cache_size; /* size of kernel cache in megabytes */ 225 | double epsilon_crit; /* tolerable error for distances used 226 | in stopping criterion */ 227 | double epsilon_shrink; /* how much a multiplier should be above 228 | zero for shrinking */ 229 | long svm_iter_to_shrink; /* iterations h after which an example can 230 | be removed by shrinking */ 231 | long maxiter; /* number of iterations after which the 232 | optimizer terminates, if there was 233 | no progress in maxdiff */ 234 | long remove_inconsistent; /* exclude examples with alpha at C and 235 | retrain */ 236 | long skip_final_opt_check; /* do not check KT-Conditions at the end of 237 | optimization for examples removed by 238 | shrinking. WARNING: This might lead to 239 | sub-optimal solutions! */ 240 | long compute_loo; /* if nonzero, computes leave-one-out 241 | estimates */ 242 | double rho; /* parameter in xi/alpha-estimates and for 243 | pruning leave-one-out range [1..2] */ 244 | long xa_depth; /* parameter in xi/alpha-estimates upper 245 | bounding the number of SV the current 246 | alpha_t is distributed over */ 247 | char predfile[200]; /* file for predicitions on unlabeled examples 248 | in transduction */ 249 | char alphafile[200]; /* file to store optimal alphas in. use 250 | empty string if alphas should not be 251 | output */ 252 | 253 | /* you probably do not want to touch the following */ 254 | double epsilon_const; /* tolerable error on eq-constraint */ 255 | double epsilon_a; /* tolerable error on alphas at bounds */ 256 | double opt_precision; /* precision of solver, set to e.g. 1e-21 257 | if you get convergence problems */ 258 | 259 | /* the following are only for internal use */ 260 | long svm_c_steps; /* do so many steps for finding optimal C */ 261 | double svm_c_factor; /* increase C by this factor every step */ 262 | double svm_costratio_unlab; 263 | double svm_unlabbound; 264 | double *svm_cost; /* individual upper bounds for each var */ 265 | long totwords; /* number of features */ 266 | } LEARN_PARM; 267 | 268 | typedef struct matrix { 269 | int n; /* number of rows */ 270 | int m; /* number of colums */ 271 | double **element; 272 | } MATRIX; 273 | 274 | typedef struct kernel_parm { 275 | long kernel_type; /* 0=linear, 1=poly, 2=rbf, 3=sigmoid, 276 | 4=custom, 5=matrix */ 277 | long poly_degree; 278 | double rbf_gamma; 279 | double coef_lin; 280 | double coef_const; 281 | char custom[50]; /* for user supplied kernel */ 282 | MATRIX *gram_matrix; /* here one can directly supply the kernel 283 | matrix. The matrix is accessed if 284 | kernel_type=5 is selected. */ 285 | } KERNEL_PARM; 286 | 287 | typedef struct model { 288 | long sv_num; 289 | long at_upper_bound; 290 | double b; 291 | DOC **supvec; 292 | double *alpha; 293 | long *index; /* index from docnum to position in model */ 294 | long totwords; /* number of features */ 295 | long totdoc; /* number of training documents */ 296 | KERNEL_PARM kernel_parm; /* kernel */ 297 | 298 | /* the following values are not written to file */ 299 | double loo_error,loo_recall,loo_precision; /* leave-one-out estimates */ 300 | double xa_error,xa_recall,xa_precision; /* xi/alpha estimates */ 301 | double *lin_weights; /* weights for linear case using 302 | folding */ 303 | double maxdiff; /* precision, up to which this 304 | model is accurate */ 305 | } MODEL; 306 | 307 | /* The following specifies a quadratic problem of the following form 308 | 309 | minimize g0 * x + 1/2 x' * G * x 310 | subject to ce*x - ce0 = 0 311 | l <= x <= u 312 | */ 313 | typedef struct quadratic_program { 314 | long opt_n; /* number of variables */ 315 | long opt_m; /* number of linear equality constraints */ 316 | double *opt_ce,*opt_ce0; /* linear equality constraints 317 | opt_ce[i]*x - opt_ceo[i]=0 */ 318 | double *opt_g; /* hessian of objective */ 319 | double *opt_g0; /* linear part of objective */ 320 | double *opt_xinit; /* initial value for variables */ 321 | double *opt_low,*opt_up; /* box constraints */ 322 | } QP; 323 | 324 | typedef struct kernel_cache { 325 | long *index; /* cache some kernel evalutations */ 326 | CFLOAT *buffer; /* to improve speed */ 327 | long *invindex; 328 | long *active2totdoc; 329 | long *totdoc2active; 330 | long *lru; 331 | long *occu; 332 | long elems; 333 | long max_elems; 334 | long time; 335 | long activenum; 336 | long buffsize; 337 | } KERNEL_CACHE; 338 | 339 | 340 | typedef struct timing_profile { 341 | double time_kernel; 342 | double time_opti; 343 | double time_shrink; 344 | double time_update; 345 | double time_model; 346 | double time_check; 347 | double time_select; 348 | } TIMING; 349 | 350 | typedef struct shrink_state { 351 | long *active; 352 | long *inactive_since; 353 | long deactnum; 354 | double **a_history; /* for shrinking with non-linear kernel */ 355 | long maxhistory; 356 | double *last_a; /* for shrinking with linear kernel */ 357 | double *last_lin; /* for shrinking with linear kernel */ 358 | } SHRINK_STATE; 359 | 360 | typedef struct randpair { 361 | long val,sort; 362 | } RANDPAIR; 363 | 364 | double classify_example(MODEL *, DOC *); 365 | double classify_example_linear(MODEL *, DOC *); 366 | double kernel(KERNEL_PARM *, DOC *, DOC *); 367 | double single_kernel(KERNEL_PARM *, SVECTOR *, SVECTOR *); 368 | double custom_kernel(KERNEL_PARM *, SVECTOR *, SVECTOR *); 369 | SVECTOR *create_svector(WORD *, MexPhiCustom, double); 370 | SVECTOR *create_svector_shallow(WORD *, MexPhiCustom, double); 371 | SVECTOR *create_svector_n(double *, long, MexPhiCustom, double); 372 | SVECTOR *create_svector_n_r(double *, long, MexPhiCustom, double, double); 373 | SVECTOR *copy_svector(SVECTOR *); 374 | SVECTOR *copy_svector_shallow(SVECTOR *); 375 | void free_svector(SVECTOR *); 376 | void free_svector_shallow(SVECTOR *); 377 | double sprod_ss(SVECTOR *, SVECTOR *); 378 | SVECTOR* sub_ss(SVECTOR *, SVECTOR *); 379 | SVECTOR* sub_ss_r(SVECTOR *, SVECTOR *, double min_non_zero); 380 | SVECTOR* add_ss(SVECTOR *, SVECTOR *); 381 | SVECTOR* add_ss_r(SVECTOR *, SVECTOR *, double min_non_zero); 382 | SVECTOR* multadd_ss(SVECTOR *a, SVECTOR *b, double fa, double fb); 383 | SVECTOR* multadd_ss_r(SVECTOR *a,SVECTOR *b,double fa, double fb, 384 | double min_non_zero); 385 | SVECTOR* add_list_ns(SVECTOR *a); 386 | SVECTOR* add_dual_list_ns_r(SVECTOR *, SVECTOR *, double min_non_zero); 387 | SVECTOR* add_list_ns_r(SVECTOR *a, double min_non_zero); 388 | SVECTOR* add_list_ss(SVECTOR *); 389 | SVECTOR* add_dual_list_ss_r(SVECTOR *, SVECTOR *, double min_non_zero); 390 | SVECTOR* add_list_ss_r(SVECTOR *, double min_non_zero); 391 | SVECTOR* add_list_sort_ss(SVECTOR *); 392 | SVECTOR* add_dual_list_sort_ss_r(SVECTOR *, SVECTOR *, double min_non_zero); 393 | SVECTOR* add_list_sort_ss_r(SVECTOR *, double min_non_zero); 394 | void add_list_n_ns(double *vec_n, SVECTOR *vec_s, double faktor); 395 | void append_svector_list(SVECTOR *a, SVECTOR *b); 396 | void mult_svector_list(SVECTOR *a, double factor); 397 | void setfactor_svector_list(SVECTOR *a, double factor); 398 | SVECTOR* smult_s(SVECTOR *, double); 399 | SVECTOR* shift_s(SVECTOR *a, long shift); 400 | int featvec_eq(SVECTOR *, SVECTOR *); 401 | double model_length_s(MODEL *); 402 | double model_length_n(MODEL *); 403 | void mult_vector_ns(double *, SVECTOR *, double); 404 | void add_vector_ns(double *, SVECTOR *, double); 405 | double sprod_ns(double *, SVECTOR *); 406 | void add_weight_vector_to_linear_model(MODEL *); 407 | DOC *create_example(long, long, long, double, SVECTOR *); 408 | void free_example(DOC *, long); 409 | long *random_order(long n); 410 | void print_percent_progress(long *progress, long maximum, 411 | long percentperdot, char *symbol); 412 | MATRIX *create_matrix(int n, int m); 413 | MATRIX *realloc_matrix(MATRIX *matrix, int n, int m); 414 | double *create_nvector(int n); 415 | void clear_nvector(double *vec, long int n); 416 | MATRIX *copy_matrix(MATRIX *matrix); 417 | void free_matrix(MATRIX *matrix); 418 | void free_nvector(double *vector); 419 | MATRIX *transpose_matrix(MATRIX *matrix); 420 | MATRIX *cholesky_matrix(MATRIX *A); 421 | double *find_indep_subset_of_matrix(MATRIX *A, double epsilon); 422 | MATRIX *invert_ltriangle_matrix(MATRIX *L); 423 | double *prod_nvector_matrix(double *v, MATRIX *A); 424 | double *prod_matrix_nvector(MATRIX *A, double *v); 425 | double *prod_nvector_ltmatrix(double *v, MATRIX *A); 426 | double *prod_ltmatrix_nvector(MATRIX *A, double *v); 427 | MATRIX *prod_matrix_matrix(MATRIX *A, MATRIX *B); 428 | void print_matrix(MATRIX *matrix); 429 | MODEL *read_model(char *); 430 | MODEL *copy_model(MODEL *); 431 | MODEL *compact_linear_model(MODEL *model); 432 | void free_model(MODEL *, int); 433 | void read_documents(char *, DOC ***, double **, long *, long *); 434 | int parse_document(char *, WORD *, double *, long *, long *, double *, long *, long, char **); 435 | int read_word(char *in, char *out); 436 | double *read_alphas(char *,long); 437 | void set_learning_defaults(LEARN_PARM *, KERNEL_PARM *); 438 | int check_learning_parms(LEARN_PARM *, KERNEL_PARM *); 439 | void nol_ll(char *, long *, long *, long *); 440 | long minl(long, long); 441 | long maxl(long, long); 442 | double get_runtime(void); 443 | int space_or_null(int); 444 | void *my_malloc(size_t); 445 | void copyright_notice(void); 446 | # ifdef _MSC_VER 447 | int isnan(double); 448 | # endif 449 | 450 | extern long verbosity; /* verbosity level (0-4) */ 451 | extern long kernel_cache_statistic; 452 | 453 | #endif 454 | -------------------------------------------------------------------------------- /svm_light/svm_hideo.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_hideo.c */ 4 | /* */ 5 | /* The Hildreth and D'Espo solver specialized for SVMs. */ 6 | /* */ 7 | /* Author: Thorsten Joachims */ 8 | /* Date: 02.07.02 */ 9 | /* */ 10 | /* Copyright (c) 2002 Thorsten Joachims - All rights reserved */ 11 | /* */ 12 | /* This software is available for non-commercial use only. It must */ 13 | /* not be modified and distributed without prior permission of the */ 14 | /* author. The author is not responsible for implications from the */ 15 | /* use of this software. */ 16 | /* */ 17 | /***********************************************************************/ 18 | 19 | # include 20 | # include "svm_common.h" 21 | 22 | /* 23 | solve the quadratic programming problem 24 | 25 | minimize g0 * x + 1/2 x' * G * x 26 | subject to ce*x - ce0 = 0 27 | l <= x <= u 28 | 29 | The linear constraint vector ce can only have -1/+1 as entries 30 | */ 31 | 32 | /* Common Block Declarations */ 33 | 34 | /* long verbosity; */ 35 | 36 | # define PRIMAL_OPTIMAL 1 37 | # define DUAL_OPTIMAL 2 38 | # define MAXITER_EXCEEDED 3 39 | # define NAN_SOLUTION 4 40 | # define ONLY_ONE_VARIABLE 5 41 | 42 | # define LARGEROUND 0 43 | # define SMALLROUND 1 44 | 45 | /* /////////////////////////////////////////////////////////////// */ 46 | 47 | # define DEF_PRECISION 1E-5 48 | # define DEF_MAX_ITERATIONS 200 49 | # define DEF_LINDEP_SENSITIVITY 1E-8 50 | # define EPSILON_HIDEO 1E-20 51 | # define EPSILON_EQ 1E-5 52 | 53 | double *optimize_qp(QP *, double *, long, double *, LEARN_PARM *); 54 | double *primal=0,*dual=0; 55 | long precision_violations=0; 56 | double opt_precision=DEF_PRECISION; 57 | long maxiter=DEF_MAX_ITERATIONS; 58 | double lindep_sensitivity=DEF_LINDEP_SENSITIVITY; 59 | double *buffer; 60 | long *nonoptimal; 61 | 62 | long smallroundcount=0; 63 | long roundnumber=0; 64 | 65 | /* /////////////////////////////////////////////////////////////// */ 66 | 67 | void *my_malloc(); 68 | 69 | int optimize_hildreth_despo(long,long,double,double,double,long,long,long,double,double *, 70 | double *,double *,double *,double *,double *, 71 | double *,double *,double *,long *,double *,double *); 72 | int solve_dual(long,long,double,double,long,double *,double *,double *, 73 | double *,double *,double *,double *,double *,double *, 74 | double *,double *,double *,double *,long); 75 | 76 | void linvert_matrix(double *, long, double *, double, long *); 77 | void lprint_matrix(double *, long); 78 | void ladd_matrix(double *, long, double); 79 | void lcopy_matrix(double *, long, double *); 80 | void lswitch_rows_matrix(double *, long, long, long); 81 | void lswitchrk_matrix(double *, long, long, long); 82 | 83 | double calculate_qp_objective(long, double *, double *, double *); 84 | 85 | 86 | void init_qp_solver() 87 | { 88 | primal = 0 ; 89 | dual = 0 ; 90 | precision_violations = 0 ; 91 | opt_precision = DEF_PRECISION ; 92 | maxiter = DEF_MAX_ITERATIONS ; 93 | lindep_sensitivity = DEF_LINDEP_SENSITIVITY ; 94 | buffer = 0 ; 95 | nonoptimal = 0 ; 96 | smallroundcount = 0 ; 97 | roundnumber = 0 ; 98 | } 99 | 100 | void free_qp_solver() 101 | { 102 | if(buffer) {free(buffer) ; buffer = 0 ;} 103 | if(nonoptimal) {free(nonoptimal) ; nonoptimal = 0; } 104 | if(dual) {free(dual) ; dual = 0 ;} 105 | if(primal) {free(primal) ; primal = 0;} 106 | } 107 | 108 | double *optimize_qp(qp,epsilon_crit,nx,threshold,learn_parm) 109 | QP *qp; 110 | double *epsilon_crit; 111 | long nx; /* Maximum number of variables in QP */ 112 | double *threshold; 113 | LEARN_PARM *learn_parm; 114 | /* start the optimizer and return the optimal values */ 115 | /* The HIDEO optimizer does not necessarily fully solve the problem. */ 116 | /* Since it requires a strictly positive definite hessian, the solution */ 117 | /* is restricted to a linear independent subset in case the matrix is */ 118 | /* only semi-definite. */ 119 | { 120 | long i,j; 121 | int result; 122 | double eq,progress; 123 | 124 | roundnumber++; 125 | 126 | if(!primal) { /* allocate memory at first call */ 127 | primal=(double *)my_malloc(sizeof(double)*nx); 128 | dual=(double *)my_malloc(sizeof(double)*((nx+1)*2)); 129 | nonoptimal=(long *)my_malloc(sizeof(long)*(nx)); 130 | buffer=(double *)my_malloc(sizeof(double)*((nx+1)*2*(nx+1)*2+ 131 | nx*nx+2*(nx+1)*2+2*nx+1+2*nx+ 132 | nx+nx+nx*nx)); 133 | (*threshold)=0; 134 | for(i=0;i=4) { /* really verbose */ 140 | printf("\n\n"); 141 | eq=qp->opt_ce0[0]; 142 | for(i=0;iopt_n;i++) { 143 | eq+=qp->opt_xinit[i]*qp->opt_ce[i]; 144 | printf("%f: ",qp->opt_g0[i]); 145 | for(j=0;jopt_n;j++) { 146 | printf("%f ",qp->opt_g[i*qp->opt_n+j]); 147 | } 148 | printf(": a=%.10f < %f",qp->opt_xinit[i],qp->opt_up[i]); 149 | printf(": y=%f\n",qp->opt_ce[i]); 150 | } 151 | if(qp->opt_m) { 152 | printf("EQ: %f*x0",qp->opt_ce[0]); 153 | for(i=1;iopt_n;i++) { 154 | printf(" + %f*x%ld",qp->opt_ce[i],i); 155 | } 156 | printf(" = %f\n\n",-qp->opt_ce0[0]); 157 | } 158 | } 159 | 160 | result=optimize_hildreth_despo(qp->opt_n,qp->opt_m, 161 | opt_precision,(*epsilon_crit), 162 | learn_parm->epsilon_a,maxiter, 163 | /* (long)PRIMAL_OPTIMAL, */ 164 | (long)0, (long)0, 165 | lindep_sensitivity, 166 | qp->opt_g,qp->opt_g0,qp->opt_ce,qp->opt_ce0, 167 | qp->opt_low,qp->opt_up,primal,qp->opt_xinit, 168 | dual,nonoptimal,buffer,&progress); 169 | if(verbosity>=3) { 170 | printf("return(%d)...",result); 171 | } 172 | 173 | if(learn_parm->totwords < learn_parm->svm_maxqpsize) { 174 | /* larger working sets will be linear dependent anyway */ 175 | learn_parm->svm_maxqpsize=maxl(learn_parm->totwords,(long)2); 176 | } 177 | 178 | if(result == NAN_SOLUTION) { 179 | lindep_sensitivity*=2; /* throw out linear dependent examples more */ 180 | /* generously */ 181 | if(learn_parm->svm_maxqpsize>2) { 182 | learn_parm->svm_maxqpsize--; /* decrease size of qp-subproblems */ 183 | } 184 | precision_violations++; 185 | } 186 | 187 | /* take one round of only two variable to get unstuck */ 188 | if((result != PRIMAL_OPTIMAL) || (!(roundnumber % 31)) || (progress <= 0)) { 189 | 190 | smallroundcount++; 191 | 192 | result=optimize_hildreth_despo(qp->opt_n,qp->opt_m, 193 | opt_precision,(*epsilon_crit), 194 | learn_parm->epsilon_a,(long)maxiter, 195 | (long)PRIMAL_OPTIMAL,(long)SMALLROUND, 196 | lindep_sensitivity, 197 | qp->opt_g,qp->opt_g0,qp->opt_ce,qp->opt_ce0, 198 | qp->opt_low,qp->opt_up,primal,qp->opt_xinit, 199 | dual,nonoptimal,buffer,&progress); 200 | if(verbosity>=3) { 201 | printf("return_srd(%d)...",result); 202 | } 203 | 204 | if(result != PRIMAL_OPTIMAL) { 205 | if(result != ONLY_ONE_VARIABLE) 206 | precision_violations++; 207 | if(result == MAXITER_EXCEEDED) 208 | maxiter+=100; 209 | if(result == NAN_SOLUTION) { 210 | lindep_sensitivity*=2; /* throw out linear dependent examples more */ 211 | /* generously */ 212 | /* results not valid, so return inital values */ 213 | for(i=0;iopt_n;i++) { 214 | primal[i]=qp->opt_xinit[i]; 215 | } 216 | } 217 | } 218 | } 219 | 220 | 221 | if(precision_violations > 50) { 222 | precision_violations=0; 223 | (*epsilon_crit)*=10.0; 224 | if(verbosity>=1) { 225 | printf("\nWARNING: Relaxing epsilon on KT-Conditions (%f).\n", 226 | (*epsilon_crit)); 227 | } 228 | } 229 | 230 | if((qp->opt_m>0) && (result != NAN_SOLUTION) && (!isnan(dual[1]-dual[0]))) 231 | (*threshold)=dual[1]-dual[0]; 232 | else 233 | (*threshold)=0; 234 | 235 | if(verbosity>=4) { /* really verbose */ 236 | printf("\n\n"); 237 | eq=qp->opt_ce0[0]; 238 | for(i=0;iopt_n;i++) { 239 | eq+=primal[i]*qp->opt_ce[i]; 240 | printf("%f: ",qp->opt_g0[i]); 241 | for(j=0;jopt_n;j++) { 242 | printf("%f ",qp->opt_g[i*qp->opt_n+j]); 243 | } 244 | printf(": a=%.30f",primal[i]); 245 | printf(": nonopti=%ld",nonoptimal[i]); 246 | printf(": y=%f\n",qp->opt_ce[i]); 247 | } 248 | printf("eq-constraint=%.30f\n",eq); 249 | printf("b=%f\n",(*threshold)); 250 | printf(" smallroundcount=%ld ",smallroundcount); 251 | } 252 | 253 | return(primal); 254 | } 255 | 256 | 257 | 258 | int optimize_hildreth_despo(n,m,precision,epsilon_crit,epsilon_a,maxiter,goal, 259 | smallround,lindep_sensitivity,g,g0,ce,ce0,low,up, 260 | primal,init,dual,lin_dependent,buffer,progress) 261 | long n; /* number of variables */ 262 | long m; /* number of linear equality constraints [0,1] */ 263 | double precision; /* solve at least to this dual precision */ 264 | double epsilon_crit; /* stop, if KT-Conditions approx fulfilled */ 265 | double epsilon_a; /* precision of alphas at bounds */ 266 | long maxiter; /* stop after this many iterations */ 267 | long goal; /* keep going until goal fulfilled */ 268 | long smallround; /* use only two variables of steepest descent */ 269 | double lindep_sensitivity; /* epsilon for detecting linear dependent ex */ 270 | double *g; /* hessian of objective */ 271 | double *g0; /* linear part of objective */ 272 | double *ce,*ce0; /* linear equality constraints */ 273 | double *low,*up; /* box constraints */ 274 | double *primal; /* primal variables */ 275 | double *init; /* initial values of primal */ 276 | double *dual; /* dual variables */ 277 | long *lin_dependent; 278 | double *buffer; 279 | double *progress; /* delta in the objective function between 280 | before and after */ 281 | { 282 | long i,j,k,from,to,n_indep,changed; 283 | double sum,bmin=0,bmax=0; 284 | double *d,*d0,*ig,*dual_old,*temp,*start; 285 | double *g0_new,*g_new,*ce_new,*ce0_new,*low_new,*up_new; 286 | double add,t; 287 | int result; 288 | double obj_before,obj_after; 289 | long b1,b2; 290 | double g0_b1,g0_b2,ce0_b; 291 | 292 | g0_new=&(buffer[0]); /* claim regions of buffer */ 293 | d=&(buffer[n]); 294 | d0=&(buffer[n+(n+m)*2*(n+m)*2]); 295 | ce_new=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2]); 296 | ce0_new=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n]); 297 | ig=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m]); 298 | dual_old=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n]); 299 | low_new=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n+(n+m)*2]); 300 | up_new=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n+(n+m)*2+n]); 301 | start=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n+(n+m)*2+n+n]); 302 | g_new=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n+(n+m)*2+n+n+n]); 303 | temp=&(buffer[n+(n+m)*2*(n+m)*2+(n+m)*2+n+m+n*n+(n+m)*2+n+n+n+n*n]); 304 | 305 | b1=-1; 306 | b2=-1; 307 | for(i=0;i=( up[i]-epsilon_a)) && (ce[i]>0.0))) 315 | ) { 316 | bmin=sum; 317 | b1=i; 318 | } 319 | if(((b2==-1) || (sum>=bmax)) 320 | && (!((init[i]<=(low[i]+epsilon_a)) && (ce[i]>0.0))) 321 | && (!((init[i]>=( up[i]-epsilon_a)) && (ce[i]<0.0))) 322 | ) { 323 | bmax=sum; 324 | b2=i; 325 | } 326 | } 327 | /* in case of unbiased hyperplane, the previous projection on */ 328 | /* equality constraint can lead to b1 or b2 being -1. */ 329 | if((b1 == -1) || (b2 == -1)) { 330 | b1=maxl(b1,b2); 331 | b2=maxl(b1,b2); 332 | } 333 | 334 | for(i=0;i g0_b2) { /* set b2 to upper bound */ 375 | /* printf("case +=>\n"); */ 376 | changed=1; 377 | t=up[b2]-init[b2]; 378 | if((init[b1]-low[b1]) < t) { 379 | t=init[b1]-low[b1]; 380 | } 381 | start[b1]=init[b1]-t; 382 | start[b2]=init[b2]+t; 383 | } 384 | } 385 | else if(((g[b1*n+b1]>0) || (g[b2*n+b2]>0))) { /* (ce[b1] != ce[b2]) */ 386 | /* printf("case +!\n"); */ 387 | t=((ce[b2]/ce[b1])*g0[b1]-g0[b2]+ce0[0]*(g[b1*n+b1]*ce[b2]/ce[b1]-g[b1*n+b2]/ce[b1]))/((ce[b2]*ce[b2]/(ce[b1]*ce[b1]))*g[b1*n+b1]+g[b2*n+b2]-2*(g[b1*n+b2]*ce[b2]/ce[b1]))-init[b2]; 388 | changed=1; 389 | if((up[b2]-init[b2]) < t) { 390 | t=up[b2]-init[b2]; 391 | } 392 | if((init[b2]-low[b2]) < -t) { 393 | t=-(init[b2]-low[b2]); 394 | } 395 | if((up[b1]-init[b1]) < t) { 396 | t=(up[b1]-init[b1]); 397 | } 398 | if((init[b1]-low[b1]) < -t) { 399 | t=-(init[b1]-low[b1]); 400 | } 401 | start[b1]=init[b1]+t; 402 | start[b2]=init[b2]+t; 403 | } 404 | } 405 | if((-g[b1*n+b2] == g[b1*n+b1]) && (-g[b1*n+b2] == g[b2*n+b2])) { 406 | /* printf("diffeuqal\n"); */ 407 | if(ce[b1] != ce[b2]) { 408 | if((g0_b1+g0_b2) < 0) { /* set b1 and b2 to upper bound */ 409 | /* printf("case -!<\n"); */ 410 | changed=1; 411 | t=up[b1]-init[b1]; 412 | if((up[b2]-init[b2]) < t) { 413 | t=up[b2]-init[b2]; 414 | } 415 | start[b1]=init[b1]+t; 416 | start[b2]=init[b2]+t; 417 | } 418 | else if((g0_b1+g0_b2) >= 0) { /* set b1 and b2 to lower bound */ 419 | /* printf("case -!>\n"); */ 420 | changed=1; 421 | t=init[b1]-low[b1]; 422 | if((init[b2]-low[b2]) < t) { 423 | t=init[b2]-low[b2]; 424 | } 425 | start[b1]=init[b1]-t; 426 | start[b2]=init[b2]-t; 427 | } 428 | } 429 | else if(((g[b1*n+b1]>0) || (g[b2*n+b2]>0))) { /* (ce[b1]==ce[b2]) */ 430 | /* printf("case -=\n"); */ 431 | t=((ce[b2]/ce[b1])*g0[b1]-g0[b2]+ce0[0]*(g[b1*n+b1]*ce[b2]/ce[b1]-g[b1*n+b2]/ce[b1]))/((ce[b2]*ce[b2]/(ce[b1]*ce[b1]))*g[b1*n+b1]+g[b2*n+b2]-2*(g[b1*n+b2]*ce[b2]/ce[b1]))-init[b2]; 432 | changed=1; 433 | if((up[b2]-init[b2]) < t) { 434 | t=up[b2]-init[b2]; 435 | } 436 | if((init[b2]-low[b2]) < -t) { 437 | t=-(init[b2]-low[b2]); 438 | } 439 | if((up[b1]-init[b1]) < -t) { 440 | t=-(up[b1]-init[b1]); 441 | } 442 | if((init[b1]-low[b1]) < t) { 443 | t=init[b1]-low[b1]; 444 | } 445 | start[b1]=init[b1]-t; 446 | start[b2]=init[b2]+t; 447 | } 448 | } 449 | } 450 | /* if we have a biased hyperplane, then adding a constant to the */ 451 | /* hessian does not change the solution. So that is done for examples */ 452 | /* with zero diagonal entry, since HIDEO cannot handle them. */ 453 | if((m>0) 454 | && ((fabs(g[b1*n+b1]) < lindep_sensitivity) 455 | || (fabs(g[b2*n+b2]) < lindep_sensitivity))) { 456 | /* printf("Case 0\n"); */ 457 | add+=0.093274; 458 | } 459 | /* in case both examples are linear dependent */ 460 | else if((m>0) 461 | && (g[b1*n+b2] != 0 && g[b2*n+b2] != 0) 462 | && (fabs(g[b1*n+b1]/g[b1*n+b2] - g[b1*n+b2]/g[b2*n+b2]) 463 | < lindep_sensitivity)) { 464 | /* printf("Case lindep\n"); */ 465 | add+=0.078274; 466 | } 467 | 468 | /* special case for zero diagonal entry on unbiased hyperplane */ 469 | if((m==0) && (b1>=0)) { 470 | if(fabs(g[b1*n+b1]) < lindep_sensitivity) { 471 | /* printf("Case 0b1\n"); */ 472 | for(i=0;i=0) 487 | start[b1]=low[b1]; 488 | } 489 | } 490 | if((m==0) && (b2>=0)) { 491 | if(fabs(g[b2*n+b2]) < lindep_sensitivity) { 492 | /* printf("Case 0b2\n"); */ 493 | for(i=0;i=0) 508 | start[b2]=low[b2]; 509 | } 510 | } 511 | 512 | /* printf("b1=%ld,b2=%ld\n",b1,b2); */ 513 | 514 | lcopy_matrix(g,n,d); 515 | if((m==1) && (add>0.0)) { 516 | for(j=0;j2) { /* switch, so that variables are better mixed */ 527 | lswitchrk_matrix(d,n,b1,(long)0); 528 | if(b2 == 0) 529 | lswitchrk_matrix(d,n,b1,(long)1); 530 | else 531 | lswitchrk_matrix(d,n,b2,(long)1); 532 | } 533 | if(smallround == SMALLROUND) { 534 | for(i=2;i0) { /* for biased hyperplane, pick two variables */ 538 | lin_dependent[0]=0; 539 | lin_dependent[1]=0; 540 | } 541 | else { /* for unbiased hyperplane, pick only one variable */ 542 | lin_dependent[0]=smallroundcount % 2; 543 | lin_dependent[1]=(smallroundcount+1) % 2; 544 | } 545 | } 546 | else { 547 | for(i=0;i2) { /* now switch back */ 553 | if(b2 == 0) { 554 | lswitchrk_matrix(ig,n,b1,(long)1); 555 | i=lin_dependent[1]; 556 | lin_dependent[1]=lin_dependent[b1]; 557 | lin_dependent[b1]=i; 558 | } 559 | else { 560 | lswitchrk_matrix(ig,n,b2,(long)1); 561 | i=lin_dependent[1]; 562 | lin_dependent[1]=lin_dependent[b2]; 563 | lin_dependent[b2]=i; 564 | } 565 | lswitchrk_matrix(ig,n,b1,(long)0); 566 | i=lin_dependent[0]; 567 | lin_dependent[0]=lin_dependent[b1]; 568 | lin_dependent[b1]=i; 569 | } 570 | /* lprint_matrix(d,n); */ 571 | /* lprint_matrix(ig,n); */ 572 | 573 | lcopy_matrix(g,n,g_new); /* restore g_new matrix */ 574 | if(add>0) 575 | for(j=0;j0) ce0_new[0]=-ce0[0]; 585 | for(i=0;i0) ce0_new[0]-=(start[i]*ce[i]); 593 | } 594 | } 595 | from=0; /* remove linear dependent vectors */ 596 | to=0; 597 | n_indep=0; 598 | for(i=0;i=3) { 618 | printf("real_qp_size(%ld)...",n_indep); 619 | } 620 | 621 | /* cannot optimize with only one variable */ 622 | if((n_indep<=1) && (m>0) && (!changed)) { 623 | for(i=n-1;i>=0;i--) { 624 | primal[i]=init[i]; 625 | } 626 | return((int)ONLY_ONE_VARIABLE); 627 | } 628 | 629 | if((!changed) || (n_indep>1)) { 630 | result=solve_dual(n_indep,m,precision,epsilon_crit,maxiter,g_new,g0_new, 631 | ce_new,ce0_new,low_new,up_new,primal,d,d0,ig, 632 | dual,dual_old,temp,goal); 633 | } 634 | else { 635 | result=PRIMAL_OPTIMAL; 636 | } 637 | 638 | j=n_indep; 639 | for(i=n-1;i>=0;i--) { 640 | if(!lin_dependent[i]) { 641 | j--; 642 | primal[i]=primal[j]; 643 | } 644 | else { 645 | primal[i]=start[i]; /* leave as is */ 646 | } 647 | temp[i]=primal[i]; 648 | } 649 | 650 | obj_before=calculate_qp_objective(n,g,g0,init); 651 | obj_after=calculate_qp_objective(n,g,g0,primal); 652 | (*progress)=obj_before-obj_after; 653 | if(verbosity>=3) { 654 | printf("before(%.30f)...after(%.30f)...result_sd(%d)...", 655 | obj_before,obj_after,result); 656 | } 657 | 658 | return((int)result); 659 | } 660 | 661 | 662 | int solve_dual(n,m,precision,epsilon_crit,maxiter,g,g0,ce,ce0,low,up,primal, 663 | d,d0,ig,dual,dual_old,temp,goal) 664 | /* Solves the dual using the method of Hildreth and D'Espo. */ 665 | /* Can only handle problems with zero or exactly one */ 666 | /* equality constraints. */ 667 | 668 | long n; /* number of variables */ 669 | long m; /* number of linear equality constraints */ 670 | double precision; /* solve at least to this dual precision */ 671 | double epsilon_crit; /* stop, if KT-Conditions approx fulfilled */ 672 | long maxiter; /* stop after that many iterations */ 673 | double *g; 674 | double *g0; /* linear part of objective */ 675 | double *ce,*ce0; /* linear equality constraints */ 676 | double *low,*up; /* box constraints */ 677 | double *primal; /* variables (with initial values) */ 678 | double *d,*d0,*ig,*dual,*dual_old,*temp; /* buffer */ 679 | long goal; 680 | { 681 | long i,j,k,iter; 682 | double sum,w,maxviol,viol,temp1,temp2,isnantest; 683 | double model_b,dist; 684 | long retrain,maxfaktor,primal_optimal=0,at_bound,scalemaxiter; 685 | double epsilon_a=1E-15,epsilon_hideo; 686 | double eq; 687 | 688 | if((m<0) || (m>1)) 689 | perror("SOLVE DUAL: inappropriate number of eq-constrains!"); 690 | 691 | /* 692 | printf("\n"); 693 | for(i=0;i0) { 715 | sum=0; /* dual hessian for eq constraints */ 716 | for(j=0;j0) { 751 | sum=0; /* dual linear component for eq constraints */ 752 | for(j=0;j 0) && (iter < (scalemaxiter*maxfaktor))) { 767 | iter++; 768 | 769 | while((maxviol > precision) && (iter < (scalemaxiter*maxfaktor))) { 770 | iter++; 771 | maxviol=0; 772 | for(i=0;i<2*(n+m);i++) { 773 | sum=d0[i]; 774 | for(j=0;j<2*(n+m);j++) { 775 | sum+=d[i*2*(n+m)+j]*dual_old[j]; 776 | } 777 | sum-=d[i*2*(n+m)+i]*dual_old[i]; 778 | dual[i]=-sum/d[i*2*(n+m)+i]; 779 | if(dual[i]<0) dual[i]=0; 780 | 781 | viol=fabs(dual[i]-dual_old[i]); 782 | if(viol>maxviol) 783 | maxviol=viol; 784 | dual_old[i]=dual[i]; 785 | } 786 | /* 787 | printf("%d) maxviol=%20f precision=%f\n",iter,maxviol,precision); 788 | */ 789 | } 790 | 791 | if(m>0) { 792 | for(i=0;i=(up[i])) { 811 | primal[i]=up[i]; 812 | } 813 | } 814 | 815 | if(m>0) 816 | model_b=dual[n+n+1]-dual[n+n]; 817 | else 818 | model_b=0; 819 | 820 | epsilon_hideo=EPSILON_HIDEO; 821 | for(i=0;i(low[i]+epsilon_hideo)) &&(dist>(1.0+epsilon_crit))) { 834 | epsilon_hideo=(primal[i]-low[i])*2.0; 835 | } 836 | } 837 | /* printf("\nEPSILON_HIDEO=%.30f\n",epsilon_hideo); */ 838 | 839 | for(i=0;i=(up[i]-epsilon_hideo)) { 844 | primal[i]=up[i]; 845 | } 846 | } 847 | 848 | retrain=0; 849 | primal_optimal=1; 850 | at_bound=0; 851 | for(i=0;(i(low[i]+epsilon_a)) && (dist > (1.0+epsilon_crit))) { 865 | retrain=1; 866 | primal_optimal=0; 867 | } 868 | if((primal[i]<=(low[i]+epsilon_a)) || (primal[i]>=(up[i]-epsilon_a))) { 869 | at_bound++; 870 | } 871 | /* printf("HIDEOtemp: a[%ld]=%.30f, dist=%.6f, b=%f, at_bound=%ld\n",i,primal[i],dist,model_b,at_bound); */ 872 | } 873 | if(m>0) { 874 | eq=-ce0[0]; /* check precision of eq-constraint */ 875 | for(i=0;i=(up[i]-epsilon_a)) { 909 | primal[i]=up[i]; 910 | } 911 | } 912 | } 913 | 914 | isnantest=0; 915 | for(i=0;i0) { 920 | temp1=dual[n+n+1]; /* copy the dual variables for the eq */ 921 | temp2=dual[n+n]; /* constraints to a handier location */ 922 | for(i=n+n+1;i>=2;i--) { 923 | dual[i]=dual[i-2]; 924 | } 925 | dual[0]=temp2; 926 | dual[1]=temp1; 927 | isnantest+=temp1+temp2; 928 | } 929 | 930 | if(isnan(isnantest)) { 931 | return((int)NAN_SOLUTION); 932 | } 933 | else if(primal_optimal) { 934 | return((int)PRIMAL_OPTIMAL); 935 | } 936 | else if(maxviol == 0.0) { 937 | return((int)DUAL_OPTIMAL); 938 | } 939 | else { 940 | return((int)MAXITER_EXCEEDED); 941 | } 942 | } 943 | 944 | 945 | void linvert_matrix(matrix,depth,inverse,lindep_sensitivity,lin_dependent) 946 | double *matrix; 947 | long depth; 948 | double *inverse,lindep_sensitivity; 949 | long *lin_dependent; /* indicates the active parts of matrix on 950 | input and output*/ 951 | { 952 | long i,j,k; 953 | double factor; 954 | 955 | for(i=0;i=0;i--) { 979 | if(!lin_dependent[i]) { 980 | factor=1/matrix[i*depth+i]; 981 | for(k=0;k=0;j--) { 986 | factor=matrix[j*depth+i]; 987 | matrix[j*depth+i]=0; 988 | for(k=0;k 20 | # include "pr_loqo/pr_loqo.h" 21 | # include "svm_common.h" 22 | 23 | /* Common Block Declarations */ 24 | 25 | long verbosity; 26 | 27 | /* /////////////////////////////////////////////////////////////// */ 28 | 29 | # define DEF_PRECISION_LINEAR 1E-8 30 | # define DEF_PRECISION_NONLINEAR 1E-14 31 | 32 | double *optimize_qp(); 33 | double *primal=0,*dual=0; 34 | double init_margin=0.15; 35 | long init_iter=500,precision_violations=0; 36 | double model_b; 37 | double opt_precision=DEF_PRECISION_LINEAR; 38 | 39 | /* /////////////////////////////////////////////////////////////// */ 40 | 41 | void *my_malloc(); 42 | 43 | double *optimize_qp(qp,epsilon_crit,nx,threshold,learn_parm) 44 | QP *qp; 45 | double *epsilon_crit; 46 | long nx; /* Maximum number of variables in QP */ 47 | double *threshold; 48 | LEARN_PARM *learn_parm; 49 | /* start the optimizer and return the optimal values */ 50 | { 51 | register long i,j,result; 52 | double margin,obj_before,obj_after; 53 | double sigdig,dist,epsilon_loqo; 54 | int iter; 55 | 56 | if(!primal) { /* allocate memory at first call */ 57 | primal=(double *)my_malloc(sizeof(double)*nx*3); 58 | dual=(double *)my_malloc(sizeof(double)*(nx*2+1)); 59 | } 60 | 61 | if(verbosity>=4) { /* really verbose */ 62 | printf("\n\n"); 63 | for(i=0;iopt_n;i++) { 64 | printf("%f: ",qp->opt_g0[i]); 65 | for(j=0;jopt_n;j++) { 66 | printf("%f ",qp->opt_g[i*qp->opt_n+j]); 67 | } 68 | printf(": a%ld=%.10f < %f",i,qp->opt_xinit[i],qp->opt_up[i]); 69 | printf(": y=%f\n",qp->opt_ce[i]); 70 | } 71 | for(j=0;jopt_m;j++) { 72 | printf("EQ-%ld: %f*a0",j,qp->opt_ce[j]); 73 | for(i=1;iopt_n;i++) { 74 | printf(" + %f*a%ld",qp->opt_ce[i],i); 75 | } 76 | printf(" = %f\n\n",-qp->opt_ce0[0]); 77 | } 78 | } 79 | 80 | obj_before=0; /* calculate objective before optimization */ 81 | for(i=0;iopt_n;i++) { 82 | obj_before+=(qp->opt_g0[i]*qp->opt_xinit[i]); 83 | obj_before+=(0.5*qp->opt_xinit[i]*qp->opt_xinit[i]*qp->opt_g[i*qp->opt_n+i]); 84 | for(j=0;jopt_xinit[j]*qp->opt_xinit[i]*qp->opt_g[j*qp->opt_n+i]); 86 | } 87 | } 88 | 89 | result=STILL_RUNNING; 90 | qp->opt_ce0[0]*=(-1.0); 91 | /* Run pr_loqo. If a run fails, try again with parameters which lead */ 92 | /* to a slower, but more robust setting. */ 93 | for(margin=init_margin,iter=init_iter; 94 | (margin<=0.9999999) && (result!=OPTIMAL_SOLUTION);) { 95 | sigdig=-log10(opt_precision); 96 | 97 | result=pr_loqo((int)qp->opt_n,(int)qp->opt_m, 98 | (double *)qp->opt_g0,(double *)qp->opt_g, 99 | (double *)qp->opt_ce,(double *)qp->opt_ce0, 100 | (double *)qp->opt_low,(double *)qp->opt_up, 101 | (double *)primal,(double *)dual, 102 | (int)(verbosity-2), 103 | (double)sigdig,(int)iter, 104 | (double)margin,(double)(qp->opt_up[0])/4.0,(int)0); 105 | 106 | if(isnan(dual[0])) { /* check for choldc problem */ 107 | if(verbosity>=2) { 108 | printf("NOTICE: Restarting PR_LOQO with more conservative parameters.\n"); 109 | } 110 | if(init_margin<0.80) { /* become more conservative in general */ 111 | init_margin=(4.0*margin+1.0)/5.0; 112 | } 113 | margin=(margin+1.0)/2.0; 114 | (opt_precision)*=10.0; /* reduce precision */ 115 | if(verbosity>=2) { 116 | printf("NOTICE: Reducing precision of PR_LOQO.\n"); 117 | } 118 | } 119 | else if(result!=OPTIMAL_SOLUTION) { 120 | iter+=2000; 121 | init_iter+=10; 122 | (opt_precision)*=10.0; /* reduce precision */ 123 | if(verbosity>=2) { 124 | printf("NOTICE: Reducing precision of PR_LOQO due to (%ld).\n",result); 125 | } 126 | } 127 | } 128 | 129 | if(qp->opt_m) /* Thanks to Alex Smola for this hint */ 130 | model_b=dual[0]; 131 | else 132 | model_b=0; 133 | 134 | /* Check the precision of the alphas. If results of current optimization */ 135 | /* violate KT-Conditions, relax the epsilon on the bounds on alphas. */ 136 | epsilon_loqo=1E-10; 137 | for(i=0;iopt_n;i++) { 138 | dist=-model_b*qp->opt_ce[i]; 139 | dist+=(qp->opt_g0[i]+1.0); 140 | for(j=0;jopt_g[j*qp->opt_n+i]); 142 | } 143 | for(j=i;jopt_n;j++) { 144 | dist+=(primal[j]*qp->opt_g[i*qp->opt_n+j]); 145 | } 146 | /* printf("LOQO: a[%d]=%f, dist=%f, b=%f\n",i,primal[i],dist,dual[0]); */ 147 | if((primal[i]<(qp->opt_up[i]-epsilon_loqo)) && (dist < (1.0-(*epsilon_crit)))) { 148 | epsilon_loqo=(qp->opt_up[i]-primal[i])*2.0; 149 | } 150 | else if((primal[i]>(0+epsilon_loqo)) && (dist > (1.0+(*epsilon_crit)))) { 151 | epsilon_loqo=primal[i]*2.0; 152 | } 153 | } 154 | 155 | for(i=0;iopt_n;i++) { /* clip alphas to bounds */ 156 | if(primal[i]<=(0+epsilon_loqo)) { 157 | primal[i]=0; 158 | } 159 | else if(primal[i]>=(qp->opt_up[i]-epsilon_loqo)) { 160 | primal[i]=qp->opt_up[i]; 161 | } 162 | } 163 | 164 | obj_after=0; /* calculate objective after optimization */ 165 | for(i=0;iopt_n;i++) { 166 | obj_after+=(qp->opt_g0[i]*primal[i]); 167 | obj_after+=(0.5*primal[i]*primal[i]*qp->opt_g[i*qp->opt_n+i]); 168 | for(j=0;jopt_g[j*qp->opt_n+i]); 170 | } 171 | } 172 | 173 | /* if optimizer returned NAN values, reset and retry with smaller */ 174 | /* working set. */ 175 | if(isnan(obj_after) || isnan(model_b)) { 176 | for(i=0;iopt_n;i++) { 177 | primal[i]=qp->opt_xinit[i]; 178 | } 179 | model_b=0; 180 | if(learn_parm->svm_maxqpsize>2) { 181 | learn_parm->svm_maxqpsize--; /* decrease size of qp-subproblems */ 182 | } 183 | } 184 | 185 | if(obj_after >= obj_before) { /* check whether there was progress */ 186 | (opt_precision)/=100.0; 187 | precision_violations++; 188 | if(verbosity>=2) { 189 | printf("NOTICE: Increasing Precision of PR_LOQO.\n"); 190 | } 191 | } 192 | 193 | if(precision_violations > 500) { 194 | (*epsilon_crit)*=10.0; 195 | precision_violations=0; 196 | if(verbosity>=1) { 197 | printf("\nWARNING: Relaxing epsilon on KT-Conditions.\n"); 198 | } 199 | } 200 | 201 | (*threshold)=model_b; 202 | 203 | if(result!=OPTIMAL_SOLUTION) { 204 | printf("\nERROR: PR_LOQO did not converge. \n"); 205 | return(qp->opt_xinit); 206 | } 207 | else { 208 | return(primal); 209 | } 210 | } 211 | 212 | -------------------------------------------------------------------------------- /svm_struct/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for SVM-struct, 03.10.06 2 | 3 | #Use the following to compile under unix or cygwin 4 | CC = gcc 5 | LD = gcc 6 | 7 | #Call 'make' using the following line to make CYGWIN produce stand-alone Windows executables 8 | # make 'SFLAGS=-mno-cygwin' 9 | 10 | CFLAGS = $(SFLAGS) -O3 -fomit-frame-pointer -ffast-math -Wall 11 | LDFLAGS = $(SFLAGS) -O3 -lm -Wall 12 | #CFLAGS = $(SFLAGS) -pg -Wall 13 | #LDFLAGS = $(SFLAGS) -pg -Wall 14 | 15 | all: svm_struct_noexe 16 | 17 | svm_struct_noexe: svm_struct_learn.o svm_struct_classify.o svm_struct_common.o svm_struct_main.o 18 | 19 | .PHONY: clean 20 | clean: 21 | rm -f *.o *.tcov *.d core gmon.out *.stackdump 22 | 23 | 24 | #----------------------# 25 | #---- STRUCT SVM ----# 26 | #----------------------# 27 | 28 | svm_struct_common.o: svm_struct_common.c svm_struct_common.h ../svm_struct_api_types.h 29 | $(CC) -c $(CFLAGS) svm_struct_common.c -o svm_struct_common.o 30 | 31 | svm_struct_learn.o: svm_struct_learn.c ../svm_light/svm_learn.h svm_struct_common.h ../svm_struct_api.h ../svm_struct_api_types.h 32 | $(CC) -c $(CFLAGS) svm_struct_learn.c -o svm_struct_learn.o 33 | 34 | svm_struct_main.o: svm_struct_main.c ../svm_light/svm_common.h ../svm_light/svm_learn.h svm_struct_learn.h svm_struct_common.h ../svm_struct_api.h ../svm_struct_api_types.h 35 | $(CC) -c $(CFLAGS) svm_struct_main.c -o svm_struct_main.o 36 | 37 | svm_struct_classify.o: svm_struct_classify.c svm_struct_common.h ../svm_struct_api_types.h ../svm_struct_api.h ../svm_light/svm_common.h 38 | $(CC) -c $(CFLAGS) svm_struct_classify.c -o svm_struct_classify.o 39 | -------------------------------------------------------------------------------- /svm_struct/svm_struct_classify.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_classify.c */ 4 | /* */ 5 | /* Classification module of SVM-struct. */ 6 | /* */ 7 | /* Author: Thorsten Joachims */ 8 | /* Date: 03.07.04 */ 9 | /* */ 10 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 11 | /* */ 12 | /* This software is available for non-commercial use only. It must */ 13 | /* not be modified and distributed without prior permission of the */ 14 | /* author. The author is not responsible for implications from the */ 15 | /* use of this software. */ 16 | /* */ 17 | /************************************************************************/ 18 | 19 | #include 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | #include "../svm_light/svm_common.h" 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | #include "../svm_struct_api.h" 28 | #include "svm_struct_common.h" 29 | 30 | char testfile[200]; 31 | char modelfile[200]; 32 | char predictionsfile[200]; 33 | 34 | void read_input_parameters(int, char **, char *, char *, char *, 35 | STRUCT_LEARN_PARM *, long*, long *); 36 | void print_help(void); 37 | 38 | 39 | int main (int argc, char* argv[]) 40 | { 41 | long correct=0,incorrect=0,no_accuracy=0; 42 | long i; 43 | double t1,runtime=0; 44 | double avgloss=0,l; 45 | FILE *predfl; 46 | STRUCTMODEL model; 47 | STRUCT_LEARN_PARM sparm; 48 | STRUCT_TEST_STATS teststats; 49 | SAMPLE testsample; 50 | LABEL y; 51 | 52 | svm_struct_classify_api_init(argc,argv); 53 | 54 | read_input_parameters(argc,argv,testfile,modelfile,predictionsfile,&sparm, 55 | &verbosity,&struct_verbosity); 56 | 57 | if(struct_verbosity>=1) { 58 | printf("Reading model..."); fflush(stdout); 59 | } 60 | model=read_struct_model(modelfile,&sparm); 61 | if(struct_verbosity>=1) { 62 | fprintf(stdout, "done.\n"); 63 | } 64 | 65 | if(model.svm_model->kernel_parm.kernel_type == LINEAR) { /* linear kernel */ 66 | /* compute weight vector */ 67 | add_weight_vector_to_linear_model(model.svm_model); 68 | model.w=model.svm_model->lin_weights; 69 | } 70 | 71 | if(struct_verbosity>=1) { 72 | printf("Reading test examples..."); fflush(stdout); 73 | } 74 | testsample=read_struct_examples(testfile,&sparm); 75 | if(struct_verbosity>=1) { 76 | printf("done.\n"); fflush(stdout); 77 | } 78 | 79 | if(struct_verbosity>=1) { 80 | printf("Classifying test examples..."); fflush(stdout); 81 | } 82 | 83 | if ((predfl = fopen (predictionsfile, "w")) == NULL) 84 | { perror (predictionsfile); exit (1); } 85 | 86 | for(i=0;i=2) { 103 | if((i+1) % 100 == 0) { 104 | printf("%ld..",i+1); fflush(stdout); 105 | } 106 | } 107 | free_label(y); 108 | } 109 | avgloss/=testsample.n; 110 | fclose(predfl); 111 | 112 | if(struct_verbosity>=1) { 113 | printf("done\n"); 114 | printf("Runtime (without IO) in cpu-seconds: %.2f\n", 115 | (float)(runtime/100.0)); 116 | } 117 | if((!no_accuracy) && (struct_verbosity>=1)) { 118 | printf("Average loss on test set: %.4f\n",(float)avgloss); 119 | printf("Zero/one-error on test set: %.2f%% (%ld correct, %ld incorrect, %d total)\n",(float)100.0*incorrect/testsample.n,correct,incorrect,testsample.n); 120 | } 121 | print_struct_testing_stats(testsample,&model,&sparm,&teststats); 122 | free_struct_sample(testsample); 123 | free_struct_model(model); 124 | 125 | svm_struct_classify_api_exit(); 126 | 127 | return(0); 128 | } 129 | 130 | void read_input_parameters(int argc,char *argv[],char *testfile, 131 | char *modelfile,char *predictionsfile, 132 | STRUCT_LEARN_PARM *struct_parm, 133 | long *verbosity,long *struct_verbosity) 134 | { 135 | long i; 136 | 137 | /* set default */ 138 | strcpy (modelfile, "svm_model"); 139 | strcpy (predictionsfile, "svm_predictions"); 140 | (*verbosity)=0;/*verbosity for svm_light*/ 141 | (*struct_verbosity)=1; /*verbosity for struct learning portion*/ 142 | struct_parm->custom_argc=0; 143 | 144 | for(i=1;(icustom_argv[struct_parm->custom_argc++],argv[i]);i++; strcpy(struct_parm->custom_argv[struct_parm->custom_argc++],argv[i]);break; 150 | case 'v': i++; (*struct_verbosity)=atol(argv[i]); break; 151 | case 'y': i++; (*verbosity)=atol(argv[i]); break; 152 | default: printf("\nUnrecognized option %s!\n\n",argv[i]); 153 | print_help(); 154 | exit(0); 155 | } 156 | } 157 | if((i+1)>=argc) { 158 | printf("\nNot enough input parameters!\n\n"); 159 | print_help(); 160 | exit(0); 161 | } 162 | strcpy (testfile, argv[i]); 163 | strcpy (modelfile, argv[i+1]); 164 | if((i+2) this help\n"); 179 | printf(" -v [0..3] -> verbosity level (default 2)\n\n"); 180 | 181 | print_struct_help_classify(); 182 | } 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /svm_struct/svm_struct_common.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_common.h */ 4 | /* */ 5 | /* Functions and types used by multiple components of SVM-struct. */ 6 | /* */ 7 | /* Author: Thorsten Joachims */ 8 | /* Date: 03.07.04 */ 9 | /* */ 10 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 11 | /* */ 12 | /* This software is available for non-commercial use only. It must */ 13 | /* not be modified and distributed without prior permission of the */ 14 | /* author. The author is not responsible for implications from the */ 15 | /* use of this software. */ 16 | /* */ 17 | /***********************************************************************/ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "svm_struct_common.h" 24 | 25 | long struct_verbosity; /* verbosity level (0-4) */ 26 | 27 | void printIntArray(int* x, int n) 28 | { 29 | int i; 30 | for(i=0;i= rhs[i] */ 46 | int m; /* m is the total number of constrains */ 47 | DOC **lhs; 48 | double *rhs; 49 | } CONSTSET; 50 | 51 | 52 | /**** print methods ****/ 53 | void printIntArray(int*,int); 54 | void printDoubleArray(double*,int); 55 | void printWordArray(WORD*); 56 | void printModel(MODEL *); 57 | void printW(double *, long, long, double); 58 | 59 | extern long struct_verbosity; /* verbosity level (0-4) */ 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /svm_struct/svm_struct_learn.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_learn.h */ 4 | /* */ 5 | /* Basic algorithm for learning structured outputs (e.g. parses, */ 6 | /* sequences, multi-label classification) with a Support Vector */ 7 | /* Machine. */ 8 | /* */ 9 | /* Author: Thorsten Joachims */ 10 | /* Date: 03.07.04 */ 11 | /* */ 12 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 13 | /* */ 14 | /* This software is available for non-commercial use only. It must */ 15 | /* not be modified and distributed without prior permission of the */ 16 | /* author. The author is not responsible for implications from the */ 17 | /* use of this software. */ 18 | /* */ 19 | /***********************************************************************/ 20 | 21 | #ifndef SVM_STRUCT_LEARN 22 | #define SVM_STRUCT_LEARN 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | #include "../svm_light/svm_common.h" 28 | #include "../svm_light/svm_learn.h" 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | #include "svm_struct_common.h" 33 | #include "../svm_struct_api_types.h" 34 | 35 | #define SLACK_RESCALING 1 36 | #define MARGIN_RESCALING 2 37 | 38 | #define NSLACK_ALG 0 39 | #define NSLACK_SHRINK_ALG 1 40 | #define ONESLACK_PRIMAL_ALG 2 41 | #define ONESLACK_DUAL_ALG 3 42 | #define ONESLACK_DUAL_CACHE_ALG 4 43 | 44 | typedef struct ccacheelem { 45 | SVECTOR *fydelta; /* left hand side of constraint */ 46 | double rhs; /* right hand side of constraint */ 47 | double viol; /* violation score under current model */ 48 | struct ccacheelem *next; /* next in linked list */ 49 | } CCACHEELEM; 50 | 51 | typedef struct ccache { 52 | int n; /* number of examples */ 53 | CCACHEELEM **constlist; /* array of pointers to constraint lists 54 | - one list per example. The first 55 | element of the list always points to 56 | the most violated constraint under the 57 | current model for each example. */ 58 | STRUCTMODEL *sm; /* pointer to model */ 59 | double *avg_viol_gain; /* array of average values by which 60 | violation of globally most violated 61 | constraint exceeds that of most violated 62 | constraint in cache */ 63 | int *changed; /* array of boolean indicating whether the 64 | most violated ybar change compared to 65 | last iter? */ 66 | } CCACHE; 67 | 68 | void find_most_violated_constraint(SVECTOR **fydelta, double *lossval, 69 | EXAMPLE *ex, SVECTOR *fycached, long n, 70 | STRUCTMODEL *sm,STRUCT_LEARN_PARM *sparm, 71 | double *rt_viol, double *rt_psi, 72 | long *argmax_count); 73 | CCACHE *create_constraint_cache(SAMPLE sample, STRUCT_LEARN_PARM *sparm, 74 | STRUCTMODEL *sm); 75 | void free_constraint_cache(CCACHE *ccache); 76 | double add_constraint_to_constraint_cache(CCACHE *ccache, MODEL *svmModel, 77 | int exnum, SVECTOR *fydelta, 78 | double rhs, double gainthresh, 79 | int maxconst, double *rt_cachesum); 80 | void update_constraint_cache_for_model(CCACHE *ccache, MODEL *svmModel); 81 | double compute_violation_of_constraint_in_cache(CCACHE *ccache, double thresh); 82 | double find_most_violated_joint_constraint_in_cache(CCACHE *ccache, 83 | double thresh, double *lhs_n, SVECTOR **lhs, double *rhs); 84 | void svm_learn_struct(SAMPLE sample, STRUCT_LEARN_PARM *sparm, 85 | LEARN_PARM *lparm, KERNEL_PARM *kparm, 86 | STRUCTMODEL *sm, int alg_type); 87 | void svm_learn_struct_joint(SAMPLE sample, STRUCT_LEARN_PARM *sparm, 88 | LEARN_PARM *lparm, KERNEL_PARM *kparm, 89 | STRUCTMODEL *sm, int alg_type); 90 | void svm_learn_struct_joint_custom(SAMPLE sample, STRUCT_LEARN_PARM *sparm, 91 | LEARN_PARM *lparm, KERNEL_PARM *kparm, 92 | STRUCTMODEL *sm); 93 | void remove_inactive_constraints(CONSTSET *cset, double *alpha, 94 | long i, long *alphahist, long mininactive); 95 | MATRIX *init_kernel_matrix(CONSTSET *cset, KERNEL_PARM *kparm); 96 | MATRIX *update_kernel_matrix(MATRIX *matrix, int newpos, CONSTSET *cset, 97 | KERNEL_PARM *kparm); 98 | 99 | #endif 100 | 101 | 102 | -------------------------------------------------------------------------------- /svm_struct/svm_struct_main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vedaldi/svm-struct-matlab/3fd1a1ca6cd76a7a822fb8079a79344992ae9f66/svm_struct/svm_struct_main.c -------------------------------------------------------------------------------- /svm_struct_api.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_api.c */ 4 | /* */ 5 | /* Definition of API for attaching implementing SVM learning of */ 6 | /* structures (e.g. parsing, multi-label classification, HMM) */ 7 | /* */ 8 | /* Author: Thorsten Joachims */ 9 | /* Date: 03.07.04 */ 10 | /* */ 11 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 12 | /* */ 13 | /* This software is available for non-commercial use only. It must */ 14 | /* not be modified and distributed without prior permission of the */ 15 | /* author. The author is not responsible for implications from the */ 16 | /* use of this software. */ 17 | /* */ 18 | /***********************************************************************/ 19 | 20 | #include 21 | #include 22 | #include "svm_struct/svm_struct_common.h" 23 | #include "svm_struct_api.h" 24 | 25 | /** ------------------------------------------------------------------ 26 | ** @brief 27 | ** 28 | ** Called in learning part before anything else is done to allow any 29 | ** initializations that might be necessary. 30 | **/ 31 | 32 | void 33 | svm_struct_learn_api_init(int argc, char* argv[]) 34 | { } 35 | 36 | /** ------------------------------------------------------------------ 37 | ** @brief 38 | ** 39 | ** Called in learning part at the very end to allow any clean-up 40 | ** that might be necessary. 41 | **/ 42 | 43 | void 44 | svm_struct_learn_api_exit() 45 | { } 46 | 47 | /** ------------------------------------------------------------------ 48 | ** @brief 49 | ** 50 | ** Called in prediction part before anything else is done to allow 51 | ** any initializations that might be necessary. 52 | **/ 53 | 54 | void 55 | svm_struct_classify_api_init (int argc, char* argv[]) 56 | { } 57 | 58 | /** ------------------------------------------------------------------ 59 | ** @brief 60 | ** 61 | ** Called in prediction part at the very end to allow any clean-up 62 | ** that might be necessary. 63 | **/ 64 | 65 | void 66 | svm_struct_classify_api_exit() 67 | { } 68 | 69 | /** ------------------------------------------------------------------ 70 | ** @brief Initialize structured model 71 | ** 72 | ** Initialize structmodel sm. The weight vector w does not need to be 73 | ** initialized, but you need to provide the maximum size of the 74 | ** feature space in sizePsi. This is the maximum number of different 75 | ** weights that can be learned. Later, the weight vector w will 76 | ** contain the learned weights for the model. 77 | **/ 78 | 79 | void 80 | init_struct_model (SAMPLE sample, STRUCTMODEL *sm, 81 | STRUCT_LEARN_PARM *sparm, LEARN_PARM *lparm, 82 | KERNEL_PARM *kparm) 83 | { 84 | if (kparm->kernel_type == LINEAR) { 85 | mxArray const * sizePsi_array = mxGetField(sparm->mex, 0, "dimension") ; 86 | if (! sizePsi_array) { 87 | mexErrMsgTxt("Field PARM.DIMENSION not found") ; 88 | } 89 | if (! uIsRealScalar(sizePsi_array)) { 90 | mexErrMsgTxt("PARM.DIMENSION must be a scalar") ; 91 | } 92 | 93 | sm->sizePsi = *mxGetPr(sizePsi_array) ; 94 | if (sm->sizePsi < 1) { 95 | mexErrMsgTxt("PARM.DIMENSION must be not smaller than 1") ; 96 | } 97 | } else { 98 | sm -> sizePsi = 0 ; 99 | } 100 | } 101 | 102 | /** ------------------------------------------------------------------ 103 | ** @brief Initialize structred model constraints 104 | ** 105 | ** Initializes the optimization problem. Typically, you do not need 106 | ** to change this function, since you want to start with an empty set 107 | ** of constraints. However, if for example you have constraints that 108 | ** certain weights need to be positive, you might put that in 109 | ** here. The constraints are represented as lhs[i]*w >= rhs[i]. lhs 110 | ** is an array of feature vectors, rhs is an array of doubles. m is 111 | ** the number of constraints. The function returns the initial set of 112 | ** constraints. 113 | **/ 114 | 115 | CONSTSET 116 | init_struct_constraints (SAMPLE sample, STRUCTMODEL *sm, 117 | STRUCT_LEARN_PARM *sparm) 118 | { 119 | 120 | CONSTSET c; 121 | long sizePsi=sm->sizePsi; 122 | long i; 123 | WORD words[2]; 124 | 125 | if(1) { /* normal case: start with empty set of constraints */ 126 | c.lhs=NULL; 127 | c.rhs=NULL; 128 | c.m=0; 129 | } 130 | else { /* add constraints so that all learned weights are 131 | positive. WARNING: Currently, they are positive only up to 132 | precision epsilon set by -e. */ 133 | c.lhs=my_malloc(sizeof(DOC *)*sizePsi); 134 | c.rhs=my_malloc(sizeof(double)*sizePsi); 135 | for(i=0; isizePsi. If the 156 | ** function cannot find a label, it shall return an empty label as 157 | ** recognized by the function empty_label(y). 158 | **/ 159 | 160 | LABEL 161 | classify_struct_example (PATTERN x, 162 | STRUCTMODEL *sm, 163 | STRUCT_LEARN_PARM *sparm) 164 | { 165 | LABEL y ; 166 | 167 | mxArray* fn_array ; 168 | mxArray* w_array ; 169 | mxArray* args [4] ; 170 | int status ; 171 | 172 | fn_array= mxGetField(sparm->mex, 0, "classifyFn") ; 173 | if (! fn_array) { 174 | mexErrMsgTxt("Field PARM.CLASSIFYFN not found") ; 175 | } 176 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 177 | mexErrMsgTxt("PARM.CLASSIFYFN must be a valid function handle") ; 178 | } 179 | 180 | /* encapsulate sm->w into a Matlab array */ 181 | w_array = mxCreateDoubleMatrix(sm->sizePsi, 1, mxREAL) ; 182 | memcpy(mxGetPr(w_array), 183 | sm->w + 1, 184 | sm->sizePsi * sizeof(double)) ; 185 | 186 | /* evaluate Matlab callback */ 187 | args[0] = fn_array ; 188 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 189 | args[2] = w_array ; 190 | args[3] = x.mex ; 191 | 192 | status = mexCallMATLAB(1, &y.mex, 4, args, "feval") ; 193 | 194 | mxDestroyArray(w_array) ; 195 | 196 | if (status) { 197 | mexErrMsgTxt("Error while executing PARM.CLASSIFYFN") ; 198 | } 199 | if (mxGetClassID(y.mex) == mxUNKNOWN_CLASS) { 200 | mexErrMsgTxt("PARM.CLASSIFYFN did not reutrn a result") ; 201 | } 202 | 203 | /* flag this label has one tha should be freed when discared */ 204 | y.isOwner = 1 ; 205 | 206 | return (y) ; 207 | } 208 | 209 | /** ------------------------------------------------------------------ 210 | ** @brief Find the most violated constraint with slack rescaling 211 | ** 212 | ** Finds the label ybar for pattern x that that is responsible for 213 | ** the most violated constraint for the slack rescaling 214 | ** formulation. For linear slack variables, this is that label ybar 215 | ** that maximizes 216 | ** 217 | ** argmax_{ybar} loss(y,ybar)*(1-psi(x,y)+psi(x,ybar)) 218 | ** 219 | ** Note that ybar may be equal to y (i.e. the max is 0), which is 220 | ** different from the algorithms described in 221 | ** [Tschantaridis/05]. Note that this argmax has to take into account 222 | ** the scoring function in sm, especially the weights sm.w, as well 223 | ** as the loss function, and whether linear or quadratic slacks are 224 | ** used. The weights in sm.w correspond to the features defined by 225 | ** psi() and range from index 1 to index sm->sizePsi. Most simple is 226 | ** the case of the zero/one loss function. For the zero/one loss, 227 | ** this function should return the highest scoring label ybar (which 228 | ** may be equal to the correct label y), or the second highest 229 | ** scoring label ybar, if Psi(x,ybar)>Psi(x,y)-1. If the function 230 | ** cannot find a label, it shall return an empty label as recognized 231 | ** by the function empty_label(y). 232 | **/ 233 | 234 | LABEL 235 | find_most_violated_constraint_slackrescaling (PATTERN x, LABEL y, 236 | STRUCTMODEL *sm, 237 | STRUCT_LEARN_PARM *sparm) 238 | { 239 | LABEL ybar ; 240 | 241 | mxArray* fn_array ; 242 | mxArray* model_array ; 243 | mxArray* args [5] ; 244 | int status ; 245 | 246 | fn_array = mxGetField(sparm->mex, 0, "constraintFn") ; 247 | if (! fn_array) { 248 | mexErrMsgTxt("Field PARM.CONSTRAINTFN not found") ; 249 | } 250 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 251 | mexErrMsgTxt("PARM.CONSTRAINTFN must be a valid function handle") ; 252 | } 253 | 254 | /* encapsulate sm->w into a Matlab array */ 255 | model_array = newMxArrayEncapsulatingSmodel (sm) ; 256 | 257 | args[0] = fn_array ; 258 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 259 | args[2] = model_array ; 260 | args[3] = x.mex ; 261 | args[4] = y.mex ; 262 | 263 | /* Apparently we must make sure that model_array is not destoryed by 264 | the automatic cleaning up function. It seems that, because it is constructed by 265 | encapsulating input argument to the MEX file, it must be considered an hybrid 266 | array, i.e. an array which cannot be destroyed by MATLAB automatic 267 | cleanup system, invoked when an error is raised */ 268 | 269 | mexSetTrapFlag (1) ; 270 | status = mexCallMATLAB(1, &ybar.mex, 5, args, "feval") ; 271 | mexSetTrapFlag (0) ; 272 | 273 | destroyMxArrayEncapsulatingSmodel (model_array) ; 274 | 275 | if (status) { 276 | mxArray * error_array ; 277 | mexCallMATLAB(1, &error_array, 0, NULL, "lasterror") ; 278 | mexCallMATLAB(0, NULL, 1, &error_array, "disp") ; 279 | mexCallMATLAB(0, NULL, 1, &error_array, "rethrow") ; 280 | } 281 | if (mxGetClassID(ybar.mex) == mxUNKNOWN_CLASS) { 282 | mexErrMsgTxt("PARM.CONSTRAINTFN did not reutrn a result") ; 283 | } 284 | ybar.isOwner = 1 ; 285 | 286 | return(ybar); 287 | } 288 | 289 | /** ------------------------------------------------------------------ 290 | ** @brief Find the most violated constraint with margin rescaling 291 | ** Finds the label ybar for pattern x that that is responsible for 292 | ** the most violated constraint for the margin rescaling 293 | ** formulation. For linear slack variables, this is that label ybar 294 | ** that maximizes 295 | ** 296 | ** argmax_{ybar} loss(y,ybar)+psi(x,ybar) 297 | ** 298 | ** Note that ybar may be equal to y (i.e. the max is 0), which is 299 | ** different from the algorithms described in 300 | ** [Tschantaridis/05]. Note that this argmax has to take into account 301 | ** the scoring function in sm, especially the weights sm.w, as well 302 | ** as the loss function, and whether linear or quadratic slacks are 303 | ** used. The weights in sm.w correspond to the features defined by 304 | ** psi() and range from index 1 to index sm->sizePsi. Most simple is 305 | ** the case of the zero/one loss function. For the zero/one loss, 306 | ** this function should return the highest scoring label ybar (which 307 | ** may be equal to the correct label y), or the second highest 308 | ** scoring label ybar, if Psi(x,ybar)>Psi(x,y)-1. If the function 309 | ** cannot find a label, it shall return an empty label as recognized 310 | ** by the function empty_label(y). 311 | **/ 312 | 313 | LABEL 314 | find_most_violated_constraint_marginrescaling (PATTERN x, LABEL y, 315 | STRUCTMODEL *sm, 316 | STRUCT_LEARN_PARM *sparm) 317 | { 318 | LABEL ybar ; 319 | mxArray* fn_array ; 320 | mxArray* model_array ; 321 | mxArray* args [5] ; 322 | int status ; 323 | 324 | fn_array = mxGetField(sparm->mex, 0, "constraintFn") ; 325 | if (! fn_array) { 326 | mexErrMsgTxt("Field PARM.CONSTRAINTFN not found") ; 327 | } 328 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 329 | mexErrMsgTxt("PARM.CONSTRAINTFN is not a valid function handle") ; 330 | } 331 | 332 | /* encapsulate sm->w into a Matlab array */ 333 | model_array = newMxArrayEncapsulatingSmodel (sm) ; 334 | 335 | args[0] = fn_array ; 336 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 337 | args[2] = model_array ; 338 | args[3] = x.mex ; 339 | args[4] = y.mex ; 340 | 341 | mexSetTrapFlag (1) ; 342 | status = mexCallMATLAB(1, &ybar.mex, 5, args, "feval") ; 343 | mexSetTrapFlag (0) ; 344 | 345 | destroyMxArrayEncapsulatingSmodel (model_array) ; 346 | 347 | if (status) { 348 | mxArray * error_array ; 349 | mexCallMATLAB(1, &error_array, 0, NULL, "lasterror") ; 350 | mexCallMATLAB(0, NULL, 1, &error_array, "error") ; 351 | } 352 | if (mxGetClassID(ybar.mex) == mxUNKNOWN_CLASS) { 353 | mexErrMsgTxt("PARM.CONSTRAINTFN did not reutrn a result") ; 354 | } 355 | ybar.isOwner = 1 ; 356 | 357 | return (ybar) ; 358 | } 359 | 360 | /** ------------------------------------------------------------------ 361 | ** @brief Is the label empty? 362 | ** 363 | ** Returns true, if y is an empty label. An empty label might be 364 | ** returned by find_most_violated_constraint_???(x, y, sm) if there 365 | ** is no incorrect label that can be found for x, or if it is unable 366 | ** to label x at all. 367 | **/ 368 | 369 | int 370 | empty_label (LABEL y) 371 | { 372 | return (y.mex == NULL) ; 373 | } 374 | 375 | /** ------------------------------------------------------------------ 376 | ** @brief Evaluate Psi(x, y) 377 | ** 378 | ** Returns a feature vector describing the match between pattern x 379 | ** and label y. The feature vector is returned as a list of 380 | ** SVECTOR's. Each SVECTOR is in a sparse representation of pairs 381 | ** , where the last pair has 382 | ** featurenumber 0 as a terminator. Featurenumbers start with 1 and 383 | ** end with sizePsi. Featuresnumbers that are not specified default 384 | ** to value 0. As mentioned before, psi() actually returns a list of 385 | ** SVECTOR's. Each SVECTOR has a field 'factor' and 'next'. 'next' 386 | ** specifies the next element in the list, terminated by a NULL 387 | ** pointer. The list can be though of as a linear combination of 388 | ** vectors, where each vector is weighted by its 'factor'. This 389 | ** linear combination of feature vectors is multiplied with the 390 | ** learned (kernelized) weight vector to score label y for pattern 391 | ** x. Without kernels, there will be one weight in sm.w for each 392 | ** feature. Note that psi has to match 393 | ** find_most_violated_constraint_???(x, y, sm) and vice versa. In 394 | ** particular, find_most_violated_constraint_???(x, y, sm) finds that 395 | ** ybar!=y that maximizes psi(x,ybar,sm)*sm.w (where * is the inner 396 | ** vector product) and the appropriate function of the loss + 397 | ** margin/slack rescaling method. See that paper for details. 398 | **/ 399 | 400 | SVECTOR * 401 | psi (PATTERN x, LABEL y, STRUCTMODEL *sm, 402 | STRUCT_LEARN_PARM *sparm) 403 | { 404 | SVECTOR *sv = NULL; 405 | 406 | /* The algorith can use either a linear kernel (explicit feature map) 407 | * or a custom kernel (implicit feature map). For the explicit feature 408 | * map, this function returns a sizePhi-dimensional vector. For 409 | * the implicit feature map this function returns a placeholder 410 | */ 411 | 412 | if (sm -> svm_model -> kernel_parm .kernel_type == LINEAR) { 413 | /* For the linear kernel computes the vector Phi(x,y) */ 414 | mxArray* out ; 415 | mxArray* fn_array ; 416 | mxArray* args [4] ; 417 | WORD* words = NULL ; 418 | double twonorm_sq = 0 ; 419 | int status ; 420 | 421 | fn_array = mxGetField(sparm->mex, 0, "featureFn") ; 422 | if (! fn_array) { 423 | mexErrMsgTxt("Field PARM.FEATUREFN not found") ; 424 | } 425 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 426 | mexErrMsgTxt("PARM.FEATUREFN must be a valid function handle") ; 427 | } 428 | 429 | args[0] = fn_array ; 430 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 431 | args[2] = x.mex ; /* pattern */ 432 | args[3] = y.mex ; /* label */ 433 | status = mexCallMATLAB(1, &out, 4, args, "feval") ; 434 | 435 | if (status) { 436 | mexErrMsgTxt("Error while executing PARM.FEATUREFN") ; 437 | } 438 | if (mxGetClassID(out) == mxUNKNOWN_CLASS) { 439 | mexErrMsgTxt("PARM.FEATUREFN must reutrn a result") ; 440 | } 441 | 442 | if (! mxIsSparse(out) || 443 | ! mxGetClassID(out) == mxDOUBLE_CLASS || 444 | ! mxGetN(out) == 1 || 445 | ! mxGetM(out) == sm->sizePsi) { 446 | mexErrMsgTxt("PARM.FEATUREFN must return a sparse column vector " 447 | "of the prescribed size") ; 448 | } 449 | 450 | { 451 | double * data = mxGetPr(out) ; 452 | int i ; 453 | mwIndex * colOffsets = mxGetJc(out) ; 454 | mwIndex * rowIndexes = mxGetIr(out) ; 455 | int numNZ = colOffsets[1] - colOffsets[0] ; 456 | 457 | words = (WORD*) my_malloc (sizeof(WORD) * (numNZ + 1)) ; 458 | 459 | for (i = 0 ; i < numNZ ; ++ i) { 460 | words[i].wnum = rowIndexes[i] + 1 ; 461 | words[i].weight = data[i] ; 462 | twonorm_sq += data[i] * data[i] ; 463 | } 464 | words[numNZ].wnum = 0 ; 465 | words[numNZ].weight = 0 ; 466 | } 467 | 468 | sv = create_svector_shallow (words, NULL, 1.0) ; 469 | sv->twonorm_sq = twonorm_sq ; 470 | 471 | mxDestroyArray (out) ; 472 | } 473 | else { 474 | /* For the ustom kernel returns a placeholder for (x,y). */ 475 | MexPhiCustom phi = newMexPhiCustomFromPatternLabel(x.mex, y.mex) ; 476 | WORD * words = mxMalloc(sizeof(WORD)) ; 477 | words[0].wnum = 0 ; 478 | words[0].weight = 0 ; 479 | sv = create_svector_shallow(words, phi, 1.0) ; 480 | } 481 | 482 | return (sv) ; 483 | } 484 | 485 | /** ------------------------------------------------------------------ 486 | ** @brief Evaluate loss function Delta(y, ybar) 487 | **/ 488 | 489 | double 490 | loss (LABEL y, LABEL ybar, STRUCT_LEARN_PARM *sparm) 491 | { 492 | 493 | double loss_value ; 494 | mxArray* fn_array ; 495 | mxArray* out ; 496 | mxArray* args [4] ; 497 | int status ; 498 | 499 | fn_array = mxGetField(sparm->mex, 0, "lossFn") ; 500 | if (! fn_array) { 501 | mexErrMsgTxt("Field PARM.LOSSFN not found") ; 502 | } 503 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 504 | mexErrMsgTxt("PARM.LOSSFN must be a valid function handle") ; 505 | } 506 | 507 | args[0] = fn_array ; 508 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 509 | args[2] = y.mex ; 510 | args[3] = ybar.mex ; 511 | 512 | status = mexCallMATLAB (1, &out, 4, args, "feval") ; 513 | 514 | if (status) { 515 | mexErrMsgTxt("Error while executing PARM.LOSSFN") ; 516 | } 517 | if (! uIsRealScalar(out)) { 518 | mexErrMsgTxt("PARM.LOSSFN must reutrn a scalar") ; 519 | } 520 | 521 | loss_value = *mxGetPr(out) ; 522 | mxDestroyArray(out) ; 523 | 524 | return (loss_value) ; 525 | } 526 | 527 | /** ------------------------------------------------------------------ 528 | ** This function is called just before the end of each cutting plane 529 | ** iteration. ceps is the amount by which the most violated 530 | ** constraint found in the current iteration was 531 | ** violated. cached_constraint is true if the added constraint was 532 | ** constructed from the cache. If the return value is FALSE, then the 533 | ** algorithm is allowed to terminate. If it is TRUE, the algorithm 534 | ** will keep iterating even if the desired precision sparm->epsilon 535 | ** is already reached. 536 | **/ 537 | 538 | int 539 | finalize_iteration (double ceps, int cached_constraint, 540 | SAMPLE sample, STRUCTMODEL *sm, 541 | CONSTSET cset, double *alpha, 542 | STRUCT_LEARN_PARM *sparm) 543 | { 544 | mxArray* fn_array ; 545 | mxArray* model_array ; 546 | mxArray* out ; 547 | mxArray* args [3] ; 548 | int status ; 549 | int result = 0 ; 550 | 551 | fn_array = mxGetField(sparm->mex, 0, "endIterationFn") ; 552 | 553 | if (! fn_array) return 0 ; 554 | if (! mxGetClassID(fn_array) == mxFUNCTION_CLASS) { 555 | mexErrMsgTxt("PARM.ENDITERATIONFN must be a valid function handle") ; 556 | } 557 | 558 | /* encapsulate sm->w into a Matlab array */ 559 | model_array = newMxArrayEncapsulatingSmodel (sm) ; 560 | 561 | args[0] = fn_array ; 562 | args[1] = (mxArray*) sparm->mex ; /* model (discard conts) */ 563 | args[2] = model_array ; 564 | 565 | status = mexCallMATLAB (1, &out, 3, args, "feval") ; 566 | 567 | destroyMxArrayEncapsulatingSmodel (model_array) ; 568 | 569 | if (status) { 570 | mexErrMsgTxt("Error while executing PARM.ENDITERATIONFN") ; 571 | } 572 | 573 | if (! uIsLogicalScalar(out)) { 574 | mexErrMsgTxt("PARM.ENDITERATIONFN must reutrn nothing or a scalar") ; 575 | } 576 | result = (int) (*mxGetLogicals(out)) ; 577 | mxDestroyArray(out) ; 578 | return result ; 579 | } 580 | 581 | /** ------------------------------------------------------------------ 582 | ** This function is called after training and allows final touches to 583 | ** the model sm. But primarly it allows computing and printing any 584 | ** kind of statistic (e.g. training error) you might want. 585 | **/ 586 | 587 | void 588 | print_struct_learning_stats (SAMPLE sample, STRUCTMODEL *sm, 589 | CONSTSET cset, double *alpha, 590 | STRUCT_LEARN_PARM *sparm) 591 | { 592 | 593 | } 594 | 595 | /** ------------------------------------------------------------------ 596 | ** This function is called after making all test predictions in 597 | ** svm_struct_classify and allows computing and printing any kind of 598 | ** evaluation (e.g. precision/recall) you might want. You can use the 599 | ** function eval_prediction to accumulate the necessary statistics for 600 | ** each prediction. 601 | **/ 602 | 603 | void 604 | print_struct_testing_stats (SAMPLE sample, STRUCTMODEL *sm, 605 | STRUCT_LEARN_PARM *sparm, 606 | STRUCT_TEST_STATS *teststats) 607 | { 608 | 609 | } 610 | 611 | /** ------------------------------------------------------------------ 612 | ** This function allows you to accumlate statistic for how well the 613 | ** predicition matches the labeled example. It is called from 614 | ** svm_struct_classify. See also the function 615 | ** print_struct_testing_stats. 616 | **/ 617 | 618 | void 619 | eval_prediction (long exnum, EXAMPLE ex, LABEL ypred, 620 | STRUCTMODEL *sm, STRUCT_LEARN_PARM *sparm, 621 | STRUCT_TEST_STATS *teststats) 622 | { 623 | if(exnum == 0) { /* this is the first time the function is 624 | called. So initialize the teststats */ 625 | } 626 | } 627 | 628 | /** ------------------------------------------------------------------ 629 | ** Frees the memory of x. 630 | **/ 631 | 632 | void 633 | free_pattern (PATTERN x) 634 | { } 635 | 636 | /** ------------------------------------------------------------------ 637 | ** Frees the memory of y. 638 | **/ 639 | 640 | void 641 | free_label (LABEL y) 642 | { 643 | if (y.isOwner && y.mex) { 644 | mxDestroyArray(y.mex) ; 645 | } 646 | } 647 | 648 | /** ------------------------------------------------------------------ 649 | ** Frees the memory of model. 650 | **/ 651 | 652 | void 653 | free_struct_model (STRUCTMODEL sm) 654 | { 655 | if(sm.svm_model) free_model(sm.svm_model, 1 ); 656 | /* add free calls for user defined data here */ 657 | } 658 | 659 | /** ------------------------------------------------------------------ 660 | ** Frees the memory of a sample. 661 | **/ 662 | 663 | void 664 | free_struct_sample (SAMPLE s) 665 | { 666 | int i; 667 | for(i=0;i custom parameters that can be adapted for struct\n"); 683 | printf(" learning. The * can be replaced by any character\n"); 684 | printf(" and there can be multiple options starting with --.\n"); 685 | } 686 | 687 | /** ------------------------------------------------------------------ 688 | ** Parses the command line parameters that start with -- 689 | **/ 690 | 691 | void 692 | parse_struct_parameters(STRUCT_LEARN_PARM *sparm) 693 | { 694 | 695 | int i; 696 | 697 | for(i=0;(icustom_argc) && ((sparm->custom_argv[i])[0] == '-');i++) { 698 | switch ((sparm->custom_argv[i])[2]) { 699 | case 'a': i++; /* strcpy(learn_parm->alphafile,argv[i]); */ break; 700 | case 'e': i++; /* sparm->epsilon=atof(sparm->custom_argv[i]); */ break; 701 | case 'k': i++; /* sparm->newconstretrain=atol(sparm->custom_argv[i]); */ break; 702 | default: printf("\nUnrecognized option %s!\n\n",sparm->custom_argv[i]); 703 | exit(0); 704 | } 705 | } 706 | } 707 | 708 | /** ------------------------------------------------------------------ 709 | ** Prints a help text that is appended to the common help text of 710 | ** svm_struct_classify. 711 | **/ 712 | 713 | void 714 | print_struct_help_classify() 715 | { 716 | printf(" --* string -> custom parameters that can be adapted for struct\n"); 717 | printf(" learning. The * can be replaced by any character\n"); 718 | printf(" and there can be multiple options starting with --.\n"); 719 | } 720 | 721 | /** ------------------------------------------------------------------ 722 | ** Parses the command line parameters that start with -- for the 723 | ** classification module. 724 | **/ 725 | 726 | void 727 | parse_struct_parameters_classify (STRUCT_LEARN_PARM *sparm) 728 | { 729 | int i; 730 | 731 | for(i=0;(icustom_argc) && ((sparm->custom_argv[i])[0] == '-');i++) { 732 | switch ((sparm->custom_argv[i])[2]) { 733 | /* case 'x': i++; strcpy(xvalue,sparm->custom_argv[i]); break; */ 734 | default: printf("\nUnrecognized option %s!\n\n",sparm->custom_argv[i]); 735 | exit(0); 736 | } 737 | } 738 | } 739 | 740 | -------------------------------------------------------------------------------- /svm_struct_api.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_api.h */ 4 | /* */ 5 | /* Definition of API for attaching implementing SVM learning of */ 6 | /* structures (e.g. parsing, multi-label classification, HMM) */ 7 | /* */ 8 | /* Author: Thorsten Joachims */ 9 | /* Date: 03.07.04 */ 10 | /* */ 11 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 12 | /* */ 13 | /* This software is available for non-commercial use only. It must */ 14 | /* not be modified and distributed without prior permission of the */ 15 | /* author. The author is not responsible for implications from the */ 16 | /* use of this software. */ 17 | /* */ 18 | /***********************************************************************/ 19 | 20 | #include "svm_struct_api_types.h" 21 | #include "svm_struct/svm_struct_common.h" 22 | 23 | #ifndef svm_struct_api 24 | #define svm_struct_api 25 | 26 | void svm_struct_learn_api_init(int argc, char* argv[]); 27 | void svm_struct_learn_api_exit(); 28 | void svm_struct_classify_api_init(int argc, char* argv[]); 29 | void svm_struct_classify_api_exit(); 30 | SAMPLE read_struct_examples(char *file, STRUCT_LEARN_PARM *sparm); 31 | void init_struct_model(SAMPLE sample, STRUCTMODEL *sm, 32 | STRUCT_LEARN_PARM *sparm, LEARN_PARM *lparm, 33 | KERNEL_PARM *kparm); 34 | CONSTSET init_struct_constraints(SAMPLE sample, STRUCTMODEL *sm, 35 | STRUCT_LEARN_PARM *sparm); 36 | LABEL find_most_violated_constraint_slackrescaling(PATTERN x, LABEL y, 37 | STRUCTMODEL *sm, 38 | STRUCT_LEARN_PARM *sparm); 39 | LABEL find_most_violated_constraint_marginrescaling(PATTERN x, LABEL y, 40 | STRUCTMODEL *sm, 41 | STRUCT_LEARN_PARM *sparm); 42 | LABEL classify_struct_example(PATTERN x, STRUCTMODEL *sm, 43 | STRUCT_LEARN_PARM *sparm); 44 | int empty_label(LABEL y); 45 | SVECTOR *psi(PATTERN x, LABEL y, STRUCTMODEL *sm, 46 | STRUCT_LEARN_PARM *sparm); 47 | double loss(LABEL y, LABEL ybar, STRUCT_LEARN_PARM *sparm); 48 | int finalize_iteration(double ceps, int cached_constraint, 49 | SAMPLE sample, STRUCTMODEL *sm, 50 | CONSTSET cset, double *alpha, 51 | STRUCT_LEARN_PARM *sparm); 52 | void print_struct_learning_stats(SAMPLE sample, STRUCTMODEL *sm, 53 | CONSTSET cset, double *alpha, 54 | STRUCT_LEARN_PARM *sparm); 55 | void print_struct_testing_stats(SAMPLE sample, STRUCTMODEL *sm, 56 | STRUCT_LEARN_PARM *sparm, 57 | STRUCT_TEST_STATS *teststats); 58 | void eval_prediction(long exnum, EXAMPLE ex, LABEL prediction, 59 | STRUCTMODEL *sm, STRUCT_LEARN_PARM *sparm, 60 | STRUCT_TEST_STATS *teststats); 61 | void write_struct_model(char *file,STRUCTMODEL *sm, 62 | STRUCT_LEARN_PARM *sparm); 63 | STRUCTMODEL read_struct_model(char *file, STRUCT_LEARN_PARM *sparm); 64 | void write_label(FILE *fp, LABEL y); 65 | void free_pattern(PATTERN x); 66 | void free_label(LABEL y); 67 | void free_struct_model(STRUCTMODEL sm); 68 | void free_struct_sample(SAMPLE s); 69 | void print_struct_help(); 70 | void parse_struct_parameters(STRUCT_LEARN_PARM *sparm); 71 | void print_struct_help_classify(); 72 | void parse_struct_parameters_classify(STRUCT_LEARN_PARM *sparm); 73 | void svm_learn_struct_joint_custom(SAMPLE sample, 74 | STRUCT_LEARN_PARM *sparm, 75 | LEARN_PARM *lparm, KERNEL_PARM *kparm, 76 | STRUCTMODEL *sm); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /svm_struct_api_types.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_api_types.h */ 4 | /* */ 5 | /* Definition of API for attaching implementing SVM learning of */ 6 | /* structures (e.g. parsing, multi-label classification, HMM) */ 7 | /* */ 8 | /* Author: Thorsten Joachims */ 9 | /* Date: 13.10.03 */ 10 | /* */ 11 | /* Copyright (c) 2003 Thorsten Joachims - All rights reserved */ 12 | /* */ 13 | /* This software is available for non-commercial use only. It must */ 14 | /* not be modified and distributed without prior permission of the */ 15 | /* author. The author is not responsible for implications from the */ 16 | /* use of this software. */ 17 | /* */ 18 | /***********************************************************************/ 19 | 20 | #ifndef svm_struct_api_types 21 | #define svm_struct_api_types 22 | 23 | # include "svm_light/svm_common.h" 24 | # include "svm_light/svm_learn.h" 25 | 26 | # ifndef WIN 27 | # include "strings.h" 28 | # else 29 | # include 30 | # endif 31 | 32 | #ifndef WIN 33 | #define inline_comm __inline__ 34 | #else 35 | #define inline_comm __inline 36 | #endif 37 | 38 | 39 | typedef struct MexKernelInfo_ 40 | { 41 | mxArray const * structParm ; 42 | mxArray const * kernelFn ; 43 | } MexKernelInfo ; 44 | 45 | inline_comm static int 46 | uIsString(const mxArray* A, int L) 47 | { 48 | int M = mxGetM(A) ; 49 | int N = mxGetN(A) ; 50 | return 51 | mxIsChar(A) && 52 | mxGetNumberOfDimensions(A) == 2 && 53 | (M == 1 || (M == 0 && N == 0)) && 54 | (L < 0 || N == L) ; 55 | } 56 | 57 | inline_comm static int 58 | uIsReal (const mxArray* A) 59 | { 60 | return 61 | mxIsDouble(A) && 62 | ! mxIsComplex(A) ; 63 | } 64 | 65 | inline_comm static int 66 | uIsRealScalar(const mxArray* A) 67 | { 68 | return 69 | uIsReal (A) && mxGetNumberOfElements(A) == 1 ; 70 | } 71 | 72 | inline_comm static int 73 | uIsLogicalScalar(const mxArray* A) 74 | { 75 | return 76 | mxIsLogical(A) && mxGetNumberOfElements(A) == 1 ; 77 | } 78 | 79 | inline_comm static mxArray * 80 | newMxArrayFromDoubleVector (int n, double const* v) 81 | { 82 | mxArray* array = mxCreateDoubleMatrix(n, 1, mxREAL) ; 83 | memcpy(mxGetPr(array), v, sizeof(double) * n) ; 84 | return (array) ; 85 | } 86 | 87 | inline_comm static mxArray * 88 | newMxArrayFromSvector (int n, SVECTOR const* sv) 89 | { 90 | WORD* wi ; 91 | double *pr ; 92 | mwSize nz = 0 ; 93 | mwIndex *ir, *jc ; 94 | mxArray* sv_array ; 95 | 96 | /* count words */ 97 | for (wi = sv->words ; wi->wnum >= 1 ; ++ wi) nz ++ ; 98 | 99 | /* allocate sparse array */ 100 | sv_array = mxCreateSparse(n, 1, nz, mxREAL) ; 101 | /* mxSetPr(mxMalloc(sizeof(double) * nz)) ; 102 | mxSetIr(mxMalloc(sizeof(mwIndex) * nz)) ; 103 | mxSetJc(mxMalloc(sizeof(mwIndex) * 2)) ;*/ 104 | ir = mxGetIr (sv_array) ; 105 | jc = mxGetJc (sv_array) ; 106 | pr = mxGetPr (sv_array) ; 107 | 108 | /* copy fields */ 109 | for (wi = sv->words ; wi->wnum >= 1 ; ++ wi) { 110 | *pr ++ = wi -> weight ; 111 | *ir ++ = wi -> wnum ; 112 | if (wi -> wnum > n) { 113 | char str [512] ; 114 | #ifndef WIN 115 | snprintf(str, sizeof(str), 116 | "Component index %d larger than sparse vector dimension %d", 117 | wi -> wnum, n) ; 118 | #else 119 | sprintf(str, sizeof(str), 120 | "Component index %d larger than sparse vector dimension %d", 121 | wi -> wnum, n) ; 122 | #endif 123 | mexErrMsgTxt(str) ; 124 | } 125 | } 126 | jc [0] = 0 ; 127 | jc [1] = nz ; 128 | 129 | return (sv_array) ; 130 | } 131 | 132 | 133 | # define INST_NAME "MATLAB MEX interface" 134 | # define INST_VERSION "V0.1" 135 | # define INST_VERSION_DATE "??.??.??" 136 | 137 | /* default precision for solving the optimization problem */ 138 | # define DEFAULT_EPS 0.1 139 | /* default loss rescaling method: 1=slack_rescaling, 2=margin_rescaling */ 140 | # define DEFAULT_RESCALING 2 141 | /* default loss function: */ 142 | # define DEFAULT_LOSS_FCT 0 143 | /* default optimization algorithm to use: */ 144 | # define DEFAULT_ALG_TYPE 3 145 | /* store Psi(x,y) (for ALG_TYPE 1) instead of recomputing it every time: */ 146 | # define USE_FYCACHE 1 147 | /* decide whether to evaluate sum before storing vectors in constraint 148 | cache: 149 | 0 = NO, 150 | 1 = YES (best, if sparse vectors and long vector lists), 151 | 2 = YES (best, if short vector lists), 152 | 3 = YES (best, if dense vectors and long vector lists) */ 153 | # define COMPACT_CACHED_VECTORS 1 154 | /* minimum absolute value below which values in sparse vectors are 155 | rounded to zero. Values are stored in the FVAL type defined in svm_common.h 156 | RECOMMENDATION: assuming you use FVAL=float, use 157 | 10E-15 if COMPACT_CACHED_VECTORS is 1 158 | 10E-10 if COMPACT_CACHED_VECTORS is 2 or 3 159 | */ 160 | # define COMPACT_ROUNDING_THRESH 10E-15 161 | 162 | typedef struct pattern { 163 | /* this defines the x-part of a training example, e.g. the structure 164 | for storing a natural language sentence in NLP parsing */ 165 | mxArray* mex ; 166 | } PATTERN; 167 | 168 | typedef struct label { 169 | /* this defines the y-part (the label) of a training example, 170 | e.g. the parse tree of the corresponding sentence. */ 171 | mxArray* mex ; 172 | int isOwner ; 173 | } LABEL; 174 | 175 | typedef struct structmodel { 176 | double *w; /* pointer to the learned weights */ 177 | MODEL *svm_model; /* the learned SVM model */ 178 | long sizePsi; /* maximum number of weights in w */ 179 | double walpha; 180 | /* other information that is needed for the stuctural model can be 181 | added here, e.g. the grammar rules for NLP parsing */ 182 | } STRUCTMODEL; 183 | 184 | typedef struct struct_learn_parm { 185 | double epsilon; /* precision for which to solve 186 | quadratic program */ 187 | double newconstretrain; /* number of new constraints to 188 | accumulate before recomputing the QP 189 | solution (used in w=1 algorithm) */ 190 | int ccache_size; /* maximum number of constraints to 191 | cache for each example (used in w=4 192 | algorithm) */ 193 | double batch_size; /* size of the mini batches in percent 194 | of training set size (used in w=4 195 | algorithm) */ 196 | double C; /* trade-off between margin and loss */ 197 | char custom_argv[50][300]; /* storage for the --* command line options */ 198 | int custom_argc; /* number of --* command line options */ 199 | int slack_norm; /* norm to use in objective function 200 | for slack variables; 1 -> L1-norm, 201 | 2 -> L2-norm */ 202 | int loss_type; /* selected loss type from -r 203 | command line option. Select between 204 | slack rescaling (1) and margin 205 | rescaling (2) */ 206 | int loss_function; /* select between different loss 207 | functions via -l command line 208 | option */ 209 | /* further parameters that are passed to init_struct_model() */ 210 | mxArray const * mex ; 211 | } STRUCT_LEARN_PARM ; 212 | 213 | typedef struct struct_test_stats { 214 | #ifdef WIN 215 | int dum; 216 | #endif 217 | /* you can add variables for keeping statistics when evaluating the 218 | test predictions in svm_struct_classify. This can be used in the 219 | function eval_prediction and print_struct_testing_stats. */ 220 | } STRUCT_TEST_STATS; 221 | 222 | inline_comm static mxArray * 223 | newMxArrayEncapsulatingDoubleVector (int n, double * v) 224 | { 225 | #if 1 226 | mxArray * v_array = mxCreateDoubleMatrix (0, 0, mxREAL) ; 227 | mxSetPr (v_array, v) ; 228 | mxSetM (v_array, n) ; 229 | mxSetN (v_array, 1) ; 230 | return v_array ; 231 | #else 232 | return newMxArrayFromDoubleVector (n, v) ; 233 | #endif 234 | } 235 | 236 | inline_comm static mxArray * 237 | newMxArrayEncapsulatingSmodel (STRUCTMODEL * smodel) 238 | { 239 | mxArray * alpha_array; 240 | mxArray * svPatterns_array; 241 | mxArray * svLabels_array; 242 | 243 | mwSize dims [] = {1, 1} ; 244 | char const * fieldNames [] = { 245 | "w", "alpha", "svPatterns", "svLabels" 246 | } ; 247 | mxArray * smodel_array = mxCreateStructArray (2, dims, 4, fieldNames) ; 248 | 249 | /* we cannot just encapsulate the arrays because we need to shift by 250 | * one */ 251 | if (smodel -> svm_model -> kernel_parm .kernel_type == LINEAR) { 252 | mxSetField (smodel_array, 0, "w", 253 | newMxArrayFromDoubleVector 254 | (smodel->sizePsi, smodel->w + 1) ) ; 255 | } else { 256 | int numFeatures = 0, fi, svi ; 257 | SVECTOR * sv ; 258 | double * alpha ; 259 | 260 | /* count how much space we need to store the expansion */ 261 | for (svi = 1 ; svi < smodel->svm_model->sv_num ; ++svi) { 262 | for (sv = smodel->svm_model->supvec[svi]->fvec ; 263 | sv ; 264 | sv = sv -> next) { 265 | ++ numFeatures ; 266 | } 267 | } 268 | 269 | alpha_array = mxCreateDoubleMatrix (numFeatures, 1, mxREAL) ; 270 | svPatterns_array = mxCreateCellMatrix (1, numFeatures) ; 271 | svLabels_array = mxCreateCellMatrix (1, numFeatures) ; 272 | 273 | /* fill in the values */ 274 | alpha = mxGetPr (alpha_array) ; 275 | for (fi = 0, svi = 1 ; svi < smodel->svm_model->sv_num ; ++svi) { 276 | for (sv = smodel->svm_model->supvec[svi]->fvec ; 277 | sv ; 278 | sv = sv -> next, ++ fi) { 279 | alpha [fi] = smodel->svm_model->alpha[svi] * sv->factor ; 280 | mxSetCell(svPatterns_array, fi, MexPhiCustomGetPattern (sv->userdefined)) ; 281 | mxSetCell(svLabels_array, fi, MexPhiCustomGetLabel (sv->userdefined)) ; 282 | } 283 | } 284 | 285 | mxSetField (smodel_array, 0, "alpha", alpha_array) ; 286 | mxSetField (smodel_array, 0, "svPatterns", svPatterns_array) ; 287 | mxSetField (smodel_array, 0, "svLabels", svLabels_array) ; 288 | } 289 | return smodel_array ; 290 | } 291 | 292 | inline_comm static void 293 | destroyMxArrayEncapsulatingDoubleVector (mxArray * array) 294 | { 295 | if (array) { 296 | mxSetN (array, 0) ; 297 | mxSetM (array, 0) ; 298 | mxSetPr (array, NULL) ; 299 | mxDestroyArray (array) ; 300 | } 301 | } 302 | 303 | inline_comm static void 304 | destroyMxArrayEncapsulatingSmodel (mxArray * array) 305 | { 306 | if (array) { 307 | /* w and alpha are freed by mxDestroyArray, but we do not want this 308 | * to happen to the encapsulated patterns and labels yet (or are these shared?) */ 309 | int i, n ; 310 | mxArray * svPatterns_array = mxGetField (array, 0, "svPatterns") ; 311 | mxArray * svLabels_array = mxGetField (array, 0, "svLabels") ; 312 | if (svPatterns_array) { 313 | n = mxGetNumberOfElements (svPatterns_array) ; 314 | for (i = 0 ; i < n ; ++ i) { 315 | mxSetCell (svPatterns_array, i, NULL) ; 316 | mxSetCell (svLabels_array, i, NULL) ; 317 | } 318 | } 319 | mxDestroyArray (array) ; 320 | } 321 | } 322 | 323 | 324 | #endif 325 | -------------------------------------------------------------------------------- /svm_struct_learn.m: -------------------------------------------------------------------------------- 1 | % SVM_STRUCT_LEARN Calls the SVM-struct solver 2 | % MODEL = SVM_STRUCT_LEARN(ARGS, PARM) runs SVM-struct solver with 3 | % parameters ARGS on the problem PARM. See [1-6] for the 4 | % theory. SPARM is a structure of with the fields 5 | % 6 | % PATTERNS:: patterns (X) 7 | % A cell array of patterns. The entries can have any nature 8 | % (they can just be indexes of the actual data for example). 9 | % 10 | % LABELS:: labels (Y) 11 | % A cell array of labels. The entries can have any nature. 12 | % 13 | % LOSSFN:: loss function callback 14 | % A handle to the loss function. This function has the form 15 | % L = LOSS(PARAM, Y, YBAR) where PARAM is the SPARM structure, 16 | % Y a ground truth label, YBAR another label, and L a 17 | % non-negative scalar. 18 | % 19 | % CONSTRAINTFN:: constraint callback 20 | % A handle to the constraint generation function. This function 21 | % has the form YBAR = FUNC(PARAM, MODEL, X, Y) where PARAM is 22 | % the input PARM structure, MODEL is the a structure 23 | % representing the current model, X is an input pattern, and Y 24 | % is its ground truth label. YBAR is the most violated labels. 25 | % 26 | % FEATUREN:: feature map callback 27 | % A handle to the feature map. This function has the form PSI = 28 | % FEATURE(PARAM, X, Y) where PARAM is the input PARM structure, 29 | % X is a pattern, Y is a label, and PSI a sparse vector of 30 | % dimension PARM.DIMENSION. This handle does not need to be 31 | % specified if kernels are used. 32 | % 33 | % ENDITERATIONFN:: end iteration callback 34 | % The optional callback CONTINUE = ENDITERATIONFN(PARAM, MODEL) 35 | % is called at the end of each cutting plane iteration. This can 36 | % be used to display diagnostic information. The callback should 37 | % return a logcial value, usually equal to FALSE. If the value 38 | % is TRUE, then the algorithm keeps iterating even if the 39 | % convergence criterion has been satisfied. 40 | % 41 | % DIMENSION:: dimension of the feature map 42 | % The dimension of the feature map. This value does not need to 43 | % be specified i kernels are used. 44 | % 45 | % KERNELFN:: kernel function callback 46 | % A handle to the kernel function. This function has the form K 47 | % = KERN(PARAM, X, Y, XP, YP) where PARAM is the input PARM 48 | % structure, and X, Y and XP, YP are two pattern-label pairs, 49 | % input of the joint kernel. This handle does not need to be 50 | % specified if feature maps are used. 51 | % 52 | % MODEL is a structure with fields: 53 | % 54 | % W:: weight vector 55 | % This is a spare vector of size PARAM.DIMENSION. It is used 56 | % with feature maps. 57 | % 58 | % ALPHA:: dual variables 59 | % SVPATTERNS:: patterns which are support vectors 60 | % SVLABELS:: labels which are support vectors 61 | % Used with kernels. 62 | % 63 | % ARGS is a string specifying options in the usual struct 64 | % SVM. These are: 65 | % 66 | % General Options:: 67 | % -v [0..3] -> verbosity level (default 1) 68 | % -y [0..3] -> verbosity level for svm_light (default 0) 69 | % 70 | % Learning Options:: 71 | % -c float -> C: trade-off between training error 72 | % and margin (default 0.01) 73 | % -p [1,2] -> L-norm to use for slack variables. Use 1 for L1-norm, 74 | % use 2 for squared slacks. (default 1) 75 | % -o [1,2] -> Rescaling method to use for loss. 76 | % 1: slack rescaling 77 | % 2: margin rescaling 78 | % -l [0..] -> Loss function to use. 79 | % 0: zero/one loss 80 | % ?: see below in application specific options 81 | % 82 | % Optimization Options (see [2][5]):: 83 | % -w [0,..,9] -> choice of structural learning algorithm 84 | % 0: n-slack algorithm described in [2] 85 | % 1: n-slack algorithm with shrinking heuristic 86 | % 2: 1-slack algorithm (primal) described in [5] 87 | % 3: 1-slack algorithm (dual) described in [5] 88 | % 4: 1-slack algorithm (dual) with constraint cache [5] 89 | % 9: custom algorithm in svm_struct_learn_custom.c 90 | % -e float -> epsilon: allow that tolerance for termination 91 | % criterion 92 | % -k [1..] -> number of new constraints to accumulate before 93 | % recomputing the QP solution (default 100) (-w 0 and 1 only) 94 | % -f [5..] -> number of constraints to cache for each example 95 | % (default 5) (used with -w 4) 96 | % -b [1..100] -> percentage of training set for which to refresh cache 97 | % when no epsilon violated constraint can be constructed 98 | % from current cache (default 100%%) (used with -w 4) 99 | % 100 | % SVM-light Options for Solving QP Subproblems (see [3]):: 101 | % -n [2..q] -> number of new variables entering the working set 102 | % in each svm-light iteration (default n = q). 103 | % Set n < q to prevent zig-zagging. 104 | % -m [5..] -> size of svm-light cache for kernel evaluations in MB 105 | % (default 40) (used only for -w 1 with kernels) 106 | % -h [5..] -> number of svm-light iterations a variable needs to be 107 | % optimal before considered for shrinking (default 100) 108 | % -# int -> terminate svm-light QP subproblem optimization, if no 109 | % progress after this number of iterations. 110 | % (default 100000) 111 | % 112 | % Kernel Options:: 113 | % -t int -> type of kernel function: 114 | % 0: linear (default) 115 | % 1: polynomial (s a*b+c)^d 116 | % 2: radial basis function exp(-gamma ||a-b||^2) 117 | % 3: sigmoid tanh(s a*b + c) 118 | % 4: user defined kernel from kernel.h 119 | % -d int -> parameter d in polynomial kernel 120 | % -g float -> parameter gamma in rbf kernel 121 | % -s float -> parameter s in sigmoid/poly kernel 122 | % -r float -> parameter c in sigmoid/poly kernel 123 | % -u string -> parameter of user defined kernel 124 | % 125 | % Output Options:: 126 | % -a string -> write all alphas to this file after learning 127 | % (in the same order as in the training set) 128 | % References:: 129 | % [1] T. Joachims, Learning to Align Sequences: A Maximum Margin Approach. 130 | % Technical Report, September, 2003. 131 | % [2] I. Tsochantaridis, T. Joachims, T. Hofmann, and Y. Altun, Large Margin 132 | % Methods for Structured and Interdependent Output Variables, Journal 133 | % of Machine Learning Research (JMLR), Vol. 6(Sep):1453-1484, 2005. 134 | % [3] T. Joachims, Making Large-Scale SVM Learning Practical. Advances in 135 | % Kernel Methods - Support Vector Learning, B. Schölkopf and C. Burges and 136 | % A. Smola (ed.), MIT Press, 1999. 137 | % [4] T. Joachims, Learning to Classify Text Using Support Vector 138 | % Machines: Methods, Theory, and Algorithms. Dissertation, Kluwer, 139 | % 2002. 140 | % [5] T. Joachims, T. Finley, Chun-Nam Yu, Cutting-Plane Training of Structural 141 | % SVMs, Machine Learning Journal, to appear. 142 | % [6] http://svmlight.joachims.org/ 143 | 144 | % Authors:: Andrea Vedaldi (MATLAB MEX version) 145 | -------------------------------------------------------------------------------- /svm_struct_learn_custom.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_learn_custom.c (instantiated for SVM-perform) */ 4 | /* */ 5 | /* Allows implementing a custom/alternate algorithm for solving */ 6 | /* the structual SVM optimization problem. The algorithm can use */ 7 | /* full access to the SVM-struct API and to SVM-light. */ 8 | /* */ 9 | /* Author: Thorsten Joachims */ 10 | /* Date: 09.01.08 */ 11 | /* */ 12 | /* Copyright (c) 2008 Thorsten Joachims - All rights reserved */ 13 | /* */ 14 | /* This software is available for non-commercial use only. It must */ 15 | /* not be modified and distributed without prior permission of the */ 16 | /* author. The author is not responsible for implications from the */ 17 | /* use of this software. */ 18 | /* */ 19 | /***********************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | #include "svm_struct_api.h" 25 | #include "svm_light/svm_common.h" 26 | #include "svm_struct/svm_struct_common.h" 27 | #include "svm_struct/svm_struct_learn.h" 28 | 29 | 30 | void svm_learn_struct_joint_custom(SAMPLE sample, STRUCT_LEARN_PARM *sparm, 31 | LEARN_PARM *lparm, KERNEL_PARM *kparm, 32 | STRUCTMODEL *sm) 33 | /* Input: sample (training examples) 34 | sparm (structural learning parameters) 35 | lparm (svm learning parameters) 36 | kparm (kernel parameters) 37 | Output: sm (learned model) */ 38 | { 39 | /* Put your algorithm here. See svm_struct_learn.c for an example of 40 | how to access this API. */ 41 | } 42 | 43 | -------------------------------------------------------------------------------- /svm_struct_learn_mex.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* svm_struct_main.c */ 4 | /* */ 5 | /* Command line interface to the alignment learning module of the */ 6 | /* Support Vector Machine. */ 7 | /* */ 8 | /* Author: Thorsten Joachims */ 9 | /* Date: 03.07.04 */ 10 | /* */ 11 | /* Copyright (c) 2004 Thorsten Joachims - All rights reserved */ 12 | /* */ 13 | /* This software is available for non-commercial use only. It must */ 14 | /* not be modified and distributed without prior permission of the */ 15 | /* author. The author is not responsible for implications from the */ 16 | /* use of this software. */ 17 | /* */ 18 | /***********************************************************************/ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "svm_light/svm_common.h" 25 | #include "svm_light/svm_learn.h" 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | # include "svm_struct/svm_struct_learn.h" 32 | # include "svm_struct/svm_struct_common.h" 33 | # include "svm_struct_api.h" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | void read_input_parameters (int, char **, 40 | long *, long *, 41 | STRUCT_LEARN_PARM *, LEARN_PARM *, KERNEL_PARM *, 42 | int *); 43 | 44 | void arg_split (char *string, int *argc, char ***argv) ; 45 | void init_qp_solver() ; 46 | void free_qp_solver() ; 47 | 48 | /** ------------------------------------------------------------------ 49 | ** @brief MEX entry point 50 | **/ 51 | 52 | void 53 | mexFunction (int nout, mxArray ** out, int nin, mxArray const ** in) 54 | { 55 | SAMPLE sample; /* training sample */ 56 | LEARN_PARM learn_parm; 57 | KERNEL_PARM kernel_parm; 58 | STRUCT_LEARN_PARM struct_parm; 59 | STRUCTMODEL structmodel; 60 | int alg_type; 61 | 62 | enum {IN_ARGS=0, IN_SPARM} ; 63 | enum {OUT_W=0} ; 64 | 65 | char arg [1024 + 1] ; 66 | int argc ; 67 | char ** argv ; 68 | 69 | mxArray const * sparm_array; 70 | mxArray const * patterns_array ; 71 | mxArray const * labels_array ; 72 | mxArray const * kernelFn_array ; 73 | int numExamples, ei ; 74 | mxArray * model_array; 75 | 76 | /* SVM-light is not fully reentrant, so we need to run this patch first */ 77 | init_qp_solver() ; 78 | verbosity = 0 ; 79 | kernel_cache_statistic = 0 ; 80 | 81 | if (nin != 2) { 82 | mexErrMsgTxt("Two arguments required") ; 83 | } 84 | 85 | /* Parse ARGS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 86 | 87 | if (! uIsString(in[IN_ARGS], -1)) { 88 | mexErrMsgTxt("ARGS must be a string") ; 89 | } 90 | 91 | mxGetString(in[IN_ARGS], arg, sizeof(arg) / sizeof(char)) ; 92 | arg_split (arg, &argc, &argv) ; 93 | 94 | svm_struct_learn_api_init(argc+1, argv-1) ; 95 | 96 | read_input_parameters (argc+1,argv-1, 97 | &verbosity, &struct_verbosity, 98 | &struct_parm, &learn_parm, 99 | &kernel_parm, &alg_type ) ; 100 | 101 | if (kernel_parm.kernel_type != LINEAR && 102 | kernel_parm.kernel_type != CUSTOM) { 103 | mexErrMsgTxt ("Only LINEAR or CUSTOM kerneles are supported") ; 104 | } 105 | 106 | /* Parse SPARM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 107 | sparm_array = in [IN_SPARM] ; 108 | // jk remove 109 | 110 | if (! sparm_array) { 111 | mexErrMsgTxt("SPARM must be a structure") ; 112 | } 113 | struct_parm.mex = sparm_array ; 114 | 115 | patterns_array = mxGetField(sparm_array, 0, "patterns") ; 116 | if (! patterns_array || 117 | ! mxIsCell(patterns_array)) { 118 | mexErrMsgTxt("SPARM.PATTERNS must be a cell array") ; 119 | } 120 | 121 | numExamples = mxGetNumberOfElements(patterns_array) ; 122 | 123 | labels_array = mxGetField(sparm_array, 0, "labels") ; 124 | if (! labels_array || 125 | ! mxIsCell(labels_array) || 126 | ! mxGetNumberOfElements(labels_array) == numExamples) { 127 | mexErrMsgTxt("SPARM.LABELS must be a cell array " 128 | "with the same number of elements of " 129 | "SPARM.PATTERNS") ; 130 | } 131 | 132 | sample.n = numExamples ; 133 | sample.examples = (EXAMPLE *) my_malloc (sizeof(EXAMPLE) * numExamples) ; 134 | for (ei = 0 ; ei < numExamples ; ++ ei) { 135 | sample.examples[ei].x.mex = mxGetCell(patterns_array, ei) ; 136 | sample.examples[ei].y.mex = mxGetCell(labels_array, ei) ; 137 | sample.examples[ei].y.isOwner = 0 ; 138 | } 139 | 140 | if (struct_verbosity >= 1) { 141 | mexPrintf("There are %d training examples\n", numExamples) ; 142 | } 143 | 144 | kernelFn_array = mxGetField(sparm_array, 0, "kernelFn") ; 145 | if (! kernelFn_array && kernel_parm.kernel_type == CUSTOM) { 146 | mexErrMsgTxt("SPARM.KERNELFN must be defined for CUSTOM kernels") ; 147 | } 148 | if (kernelFn_array) { 149 | MexKernelInfo * info ; 150 | if (mxGetClassID(kernelFn_array) != mxFUNCTION_CLASS) { 151 | mexErrMsgTxt("SPARM.KERNELFN must be a valid function handle") ; 152 | } 153 | info = (MexKernelInfo*) kernel_parm.custom ; 154 | info -> structParm = sparm_array ; 155 | info -> kernelFn = kernelFn_array ; 156 | } 157 | 158 | /* Learning ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 159 | switch (alg_type) { 160 | case 0: 161 | svm_learn_struct(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel,NSLACK_ALG) ; 162 | break ; 163 | case 1: 164 | svm_learn_struct(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel,NSLACK_SHRINK_ALG); 165 | break ; 166 | case 2: 167 | svm_learn_struct_joint(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel,ONESLACK_PRIMAL_ALG); 168 | break ; 169 | case 3: 170 | svm_learn_struct_joint(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel,ONESLACK_DUAL_ALG); 171 | break ; 172 | case 4: 173 | svm_learn_struct_joint(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel,ONESLACK_DUAL_CACHE_ALG); 174 | break ; 175 | case 9: 176 | svm_learn_struct_joint_custom(sample,&struct_parm,&learn_parm,&kernel_parm,&structmodel); 177 | break ; 178 | default: 179 | mexErrMsgTxt("Unknown algorithm type") ; 180 | } 181 | 182 | /* Write output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 183 | 184 | /* Warning: The model contains references to the original data 'docs'. 185 | If you want to free the original data, and only keep the model, you 186 | have to make a deep copy of 'model'. */ 187 | 188 | // jk change 189 | model_array = newMxArrayEncapsulatingSmodel (&structmodel) ; 190 | out[OUT_W] = mxDuplicateArray (model_array) ; 191 | destroyMxArrayEncapsulatingSmodel (model_array) ; 192 | 193 | free_struct_sample (sample) ; 194 | free_struct_model (structmodel) ; 195 | svm_struct_learn_api_exit () ; 196 | free_qp_solver () ; 197 | } 198 | 199 | /** ------------------------------------------------------------------ 200 | ** @brief Parse argument string 201 | **/ 202 | 203 | void 204 | read_input_parameters (int argc,char *argv[], 205 | long *verbosity,long *struct_verbosity, 206 | STRUCT_LEARN_PARM *struct_parm, 207 | LEARN_PARM *learn_parm, KERNEL_PARM *kernel_parm, 208 | int *alg_type) 209 | { 210 | long i ; 211 | 212 | (*alg_type)=DEFAULT_ALG_TYPE; 213 | 214 | /* SVM struct options */ 215 | (*struct_verbosity)=1; 216 | 217 | struct_parm->C=-0.01; 218 | struct_parm->slack_norm=1; 219 | struct_parm->epsilon=DEFAULT_EPS; 220 | struct_parm->custom_argc=0; 221 | struct_parm->loss_function=DEFAULT_LOSS_FCT; 222 | struct_parm->loss_type=DEFAULT_RESCALING; 223 | struct_parm->newconstretrain=100; 224 | struct_parm->ccache_size=5; 225 | struct_parm->batch_size=100; 226 | 227 | /* SVM light options */ 228 | (*verbosity)=0; 229 | 230 | strcpy (learn_parm->predfile, "trans_predictions"); 231 | strcpy (learn_parm->alphafile, ""); 232 | learn_parm->biased_hyperplane=1; 233 | learn_parm->remove_inconsistent=0; 234 | learn_parm->skip_final_opt_check=0; 235 | learn_parm->svm_maxqpsize=10; 236 | learn_parm->svm_newvarsinqp=0; 237 | learn_parm->svm_iter_to_shrink=-9999; 238 | learn_parm->maxiter=100000; 239 | learn_parm->kernel_cache_size=40; 240 | learn_parm->svm_c=99999999; /* overridden by struct_parm->C */ 241 | learn_parm->eps=0.001; /* overridden by struct_parm->epsilon */ 242 | learn_parm->transduction_posratio=-1.0; 243 | learn_parm->svm_costratio=1.0; 244 | learn_parm->svm_costratio_unlab=1.0; 245 | learn_parm->svm_unlabbound=1E-5; 246 | learn_parm->epsilon_crit=0.001; 247 | learn_parm->epsilon_a=1E-10; /* changed from 1e-15 */ 248 | learn_parm->compute_loo=0; 249 | learn_parm->rho=1.0; 250 | learn_parm->xa_depth=0; 251 | 252 | kernel_parm->kernel_type=0; 253 | kernel_parm->poly_degree=3; 254 | kernel_parm->rbf_gamma=1.0; 255 | kernel_parm->coef_lin=1; 256 | kernel_parm->coef_const=1; 257 | strcpy (kernel_parm->custom,"empty"); 258 | 259 | /* Parse -x options, delegat --x ones */ 260 | for(i=1;(ialphafile,argv[i]); break; 264 | case 'c': i++; struct_parm->C=atof(argv[i]); break; 265 | case 'p': i++; struct_parm->slack_norm=atol(argv[i]); break; 266 | case 'e': i++; struct_parm->epsilon=atof(argv[i]); break; 267 | case 'k': i++; struct_parm->newconstretrain=atol(argv[i]); break; 268 | case 'h': i++; learn_parm->svm_iter_to_shrink=atol(argv[i]); break; 269 | case '#': i++; learn_parm->maxiter=atol(argv[i]); break; 270 | case 'm': i++; learn_parm->kernel_cache_size=atol(argv[i]); break; 271 | case 'w': i++; (*alg_type)=atol(argv[i]); break; 272 | case 'o': i++; struct_parm->loss_type=atol(argv[i]); break; 273 | case 'n': i++; learn_parm->svm_newvarsinqp=atol(argv[i]); break; 274 | case 'q': i++; learn_parm->svm_maxqpsize=atol(argv[i]); break; 275 | case 'l': i++; struct_parm->loss_function=atol(argv[i]); break; 276 | case 'f': i++; struct_parm->ccache_size=atol(argv[i]); break; 277 | case 'b': i++; struct_parm->batch_size=atof(argv[i]); break; 278 | case 't': i++; kernel_parm->kernel_type=atol(argv[i]); break; 279 | case 'd': i++; kernel_parm->poly_degree=atol(argv[i]); break; 280 | case 'g': i++; kernel_parm->rbf_gamma=atof(argv[i]); break; 281 | case 's': i++; kernel_parm->coef_lin=atof(argv[i]); break; 282 | case 'r': i++; kernel_parm->coef_const=atof(argv[i]); break; 283 | case 'u': i++; strcpy(kernel_parm->custom,argv[i]); break; 284 | case 'v': i++; (*struct_verbosity)=atol(argv[i]); break; 285 | case 'y': i++; (*verbosity)=atol(argv[i]); break; 286 | case '-': 287 | strcpy(struct_parm->custom_argv[struct_parm->custom_argc++],argv[i]); 288 | i++; 289 | strcpy(struct_parm->custom_argv[struct_parm->custom_argc++],argv[i]); 290 | break; 291 | default: 292 | { 293 | char msg [1024+1] ; 294 | #ifndef WIN 295 | snprintf(msg, sizeof(msg)/sizeof(char), 296 | "Unrecognized option '%s'",argv[i]) ; 297 | #else 298 | sprintf(msg, sizeof(msg)/sizeof(char), 299 | "Unrecognized option '%s'",argv[i]) ; 300 | #endif 301 | mexErrMsgTxt(msg) ; 302 | } 303 | } 304 | } 305 | 306 | /* whatever is left is an error */ 307 | if (i < argc) { 308 | char msg [1024+1] ; 309 | #ifndef WIN 310 | snprintf(msg, sizeof(msg)/sizeof(char), 311 | "Unrecognized argument '%s'", argv[i]) ; 312 | #else 313 | sprintf(msg, sizeof(msg)/sizeof(char), 314 | "Unrecognized argument '%s'", argv[i]) ; 315 | #endif 316 | mexErrMsgTxt(msg) ; 317 | } 318 | 319 | /* Check parameter validity */ 320 | if(learn_parm->svm_iter_to_shrink == -9999) { 321 | learn_parm->svm_iter_to_shrink=100; 322 | } 323 | 324 | if((learn_parm->skip_final_opt_check) 325 | && (kernel_parm->kernel_type == LINEAR)) { 326 | mexWarnMsgTxt("It does not make sense to skip the final optimality check for linear kernels."); 327 | learn_parm->skip_final_opt_check=0; 328 | } 329 | if((learn_parm->skip_final_opt_check) 330 | && (learn_parm->remove_inconsistent)) { 331 | mexErrMsgTxt("It is necessary to do the final optimality check when removing inconsistent examples."); 332 | } 333 | if((learn_parm->svm_maxqpsize<2)) { 334 | char msg [1025] ; 335 | #ifndef WIN 336 | snprintf(msg, sizeof(msg)/sizeof(char), 337 | "Maximum size of QP-subproblems not in valid range: %ld [2..]",learn_parm->svm_maxqpsize) ; 338 | #else 339 | sprintf(msg, sizeof(msg)/sizeof(char), 340 | "Maximum size of QP-subproblems not in valid range: %ld [2..]",learn_parm->svm_maxqpsize) ; 341 | #endif 342 | mexErrMsgTxt(msg) ; 343 | } 344 | if((learn_parm->svm_maxqpsizesvm_newvarsinqp)) { 345 | char msg [1025] ; 346 | #ifndef WIN 347 | snprintf(msg, sizeof(msg)/sizeof(char), 348 | "Maximum size of QP-subproblems [%ld] must be larger than the number of" 349 | " new variables [%ld] entering the working set in each iteration.", 350 | learn_parm->svm_maxqpsize, learn_parm->svm_newvarsinqp) ; 351 | #else 352 | sprintf(msg, sizeof(msg)/sizeof(char), 353 | "Maximum size of QP-subproblems [%ld] must be larger than the number of" 354 | " new variables [%ld] entering the working set in each iteration.", 355 | learn_parm->svm_maxqpsize, learn_parm->svm_newvarsinqp) ; 356 | #endif 357 | mexErrMsgTxt(msg) ; 358 | } 359 | if(learn_parm->svm_iter_to_shrink<1) { 360 | char msg [1025] ; 361 | #ifndef WIN 362 | snprintf(msg, sizeof(msg)/sizeof(char), 363 | "Maximum number of iterations for shrinking not in valid range: %ld [1,..]", 364 | learn_parm->svm_iter_to_shrink); 365 | #else 366 | sprintf(msg, sizeof(msg)/sizeof(char), 367 | "Maximum number of iterations for shrinking not in valid range: %ld [1,..]", 368 | learn_parm->svm_iter_to_shrink); 369 | #endif 370 | mexErrMsgTxt(msg) ; 371 | } 372 | if(struct_parm->C<0) { 373 | mexErrMsgTxt("You have to specify a value for the parameter '-c' (C>0)!"); 374 | } 375 | if(((*alg_type) < 0) || (((*alg_type) > 5) && ((*alg_type) != 9))) { 376 | mexErrMsgTxt("Algorithm type must be either '0', '1', '2', '3', '4', or '9'!"); 377 | } 378 | if(learn_parm->transduction_posratio>1) { 379 | mexErrMsgTxt("The fraction of unlabeled examples to classify as positives must " 380 | "be less than 1.0 !!!"); 381 | } 382 | if(learn_parm->svm_costratio<=0) { 383 | mexErrMsgTxt("The COSTRATIO parameter must be greater than zero!"); 384 | } 385 | if(struct_parm->epsilon<=0) { 386 | mexErrMsgTxt("The epsilon parameter must be greater than zero!"); 387 | } 388 | if((struct_parm->ccache_size<=0) && ((*alg_type) == 4)) { 389 | mexErrMsgTxt("The cache size must be at least 1!"); 390 | } 391 | if(((struct_parm->batch_size<=0) || (struct_parm->batch_size>100)) 392 | && ((*alg_type) == 4)) { 393 | mexErrMsgTxt("The batch size must be in the interval ]0,100]!"); 394 | } 395 | if((struct_parm->slack_norm<1) || (struct_parm->slack_norm>2)) { 396 | mexErrMsgTxt("The norm of the slacks must be either 1 (L1-norm) or 2 (L2-norm)!"); 397 | } 398 | if((struct_parm->loss_type != SLACK_RESCALING) 399 | && (struct_parm->loss_type != MARGIN_RESCALING)) { 400 | mexErrMsgTxt("The loss type must be either 1 (slack rescaling) or 2 (margin rescaling)!"); 401 | } 402 | if(learn_parm->rho<0) { 403 | mexErrMsgTxt("The parameter rho for xi/alpha-estimates and leave-one-out pruning must" 404 | " be greater than zero (typically 1.0 or 2.0, see T. Joachims, Estimating the" 405 | " Generalization Performance of an SVM Efficiently, ICML, 2000.)!"); 406 | } 407 | if((learn_parm->xa_depth<0) || (learn_parm->xa_depth>100)) { 408 | mexErrMsgTxt("The parameter depth for ext. xi/alpha-estimates must be in [0..100] (zero" 409 | "for switching to the conventional xa/estimates described in T. Joachims," 410 | "Estimating the Generalization Performance of an SVM Efficiently, ICML, 2000.)") ; 411 | } 412 | 413 | parse_struct_parameters (struct_parm) ; 414 | } 415 | 416 | void 417 | arg_split (char *string, int *argc, char ***argv) 418 | { 419 | size_t size; 420 | char *d, *p; 421 | 422 | for (size = 1, p = string; *p; p++) { 423 | if (isspace((int) *p)) { 424 | size++; 425 | } 426 | } 427 | size++; /* leave space for final NULL pointer. */ 428 | 429 | *argv = (char **) my_malloc(((size * sizeof(char *)) + (p - string) + 1)); 430 | 431 | for (*argc = 0, p = string, d = ((char *) *argv) + size*sizeof(char *); 432 | *p != 0; ) { 433 | (*argv)[*argc] = NULL; 434 | while (*p && isspace((int) *p)) p++; 435 | if (*argc == 0 && *p == '#') { 436 | break; 437 | } 438 | if (*p) { 439 | char *s = p; 440 | (*argv)[(*argc)++] = d; 441 | while (*p && !isspace((int) *p)) p++; 442 | memcpy(d, s, p-s); 443 | d += p-s; 444 | *d++ = 0; 445 | while (*p && isspace((int) *p)) p++; 446 | } 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /test_svm_struct_learn.m: -------------------------------------------------------------------------------- 1 | function test_svm_struct_learn 2 | % TEST_SVM_STRUCT_LEARN 3 | % A demo function for SVM_STRUCT_LEARN(). It shows how to use 4 | % SVM-struct to learn a standard linear SVM. 5 | 6 | randn('state',0) ; 7 | rand('state',0) ; 8 | 9 | % ------------------------------------------------------------------ 10 | % Generate data 11 | % ------------------------------------------------------------------ 12 | 13 | th = pi/3 ; 14 | c = cos(th) ; 15 | s = sin(th) ; 16 | 17 | patterns = {} ; 18 | labels = {} ; 19 | for i=1:100 20 | patterns{i} = diag([2 .5]) * randn(2, 1) ; 21 | labels{i} = 2*(randn > 0) - 1 ; 22 | patterns{i}(2) = patterns{i}(2) + labels{i} ; 23 | patterns{i} = [c -s ; s c] * patterns{i} ; 24 | end 25 | 26 | % ------------------------------------------------------------------ 27 | % Run SVM struct 28 | % ------------------------------------------------------------------ 29 | 30 | parm.patterns = patterns ; 31 | parm.labels = labels ; 32 | parm.lossFn = @lossCB ; 33 | parm.constraintFn = @constraintCB ; 34 | parm.featureFn = @featureCB ; 35 | parm.dimension = 2 ; 36 | parm.verbose = 1 ; 37 | model = svm_struct_learn(' -c 1.0 -o 1 -v 1 ', parm) ; 38 | w = model.w ; 39 | 40 | % ------------------------------------------------------------------ 41 | % Plots 42 | % ------------------------------------------------------------------ 43 | 44 | figure(1) ; clf ; hold on ; 45 | x = [patterns{:}] ; 46 | y = [labels{:}] ; 47 | plot(x(1, y>0), x(2,y>0), 'g.') ; 48 | plot(x(1, y<0), x(2,y<0), 'r.') ; 49 | set(line([0 w(1)], [0 w(2)]), 'color', 'y', 'linewidth', 4) ; 50 | xlim([-3 3]) ; 51 | ylim([-3 3]) ; 52 | set(line(10*[w(2) -w(2)], 10*[-w(1) w(1)]), ... 53 | 'color', 'y', 'linewidth', 2, 'linestyle', '-') ; 54 | axis equal ; 55 | set(gca, 'color', 'b') ; 56 | w 57 | end 58 | 59 | % ------------------------------------------------------------------ 60 | % SVM struct callbacks 61 | % ------------------------------------------------------------------ 62 | 63 | function delta = lossCB(param, y, ybar) 64 | delta = double(y ~= ybar) ; 65 | if param.verbose 66 | fprintf('delta = loss(%3d, %3d) = %f\n', y, ybar, delta) ; 67 | end 68 | end 69 | 70 | function psi = featureCB(param, x, y) 71 | psi = sparse(y*x/2) ; 72 | if param.verbose 73 | fprintf('w = psi([%8.3f,%8.3f], %3d) = [%8.3f, %8.3f]\n', ... 74 | x, y, full(psi(1)), full(psi(2))) ; 75 | end 76 | end 77 | 78 | function yhat = constraintCB(param, model, x, y) 79 | % slack resaling: argmax_y delta(yi, y) (1 + - ) 80 | % margin rescaling: argmax_y delta(yi, y) + 81 | if dot(y*x, model.w) > 1, yhat = y ; else yhat = - y ; end 82 | if param.verbose 83 | fprintf('yhat = violslack([%8.3f,%8.3f], [%8.3f,%8.3f], %3d) = %3d\n', ... 84 | model.w, x, y, yhat) ; 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /test_svm_struct_learn_ker.m: -------------------------------------------------------------------------------- 1 | function test_svm_struct_learn_ker 2 | % TEST_SVM_STRUCT_LEARN 3 | % Test function for SVM_STRUCT_LEARN(). It shows how to use 4 | % SVM-struct to learn a standard linear SVM while using the generic 5 | % kernel interface. 6 | 7 | randn('state',0) ; 8 | rand('state',0) ; 9 | 10 | % ------------------------------------------------------------------ 11 | % Generate data 12 | % ------------------------------------------------------------------ 13 | 14 | th = pi/3 ; 15 | c = cos(th) ; 16 | s = sin(th) ; 17 | 18 | patterns = {} ; 19 | labels = {} ; 20 | for i=1:100 21 | patterns{i} = diag([2 .5]) * randn(2, 1) ; 22 | labels{i} = 2*(randn > 0) - 1 ; 23 | patterns{i}(2) = patterns{i}(2) + labels{i} ; 24 | patterns{i} = [c -s ; s c] * patterns{i} ; 25 | end 26 | 27 | % ------------------------------------------------------------------ 28 | % Run SVM struct 29 | % ------------------------------------------------------------------ 30 | 31 | parm.patterns = patterns ; 32 | parm.labels = labels ; 33 | parm.lossFn = @lossCB 34 | parm.constraintFn =@constraintCB ; 35 | parm.kernelFn = @kernelCB ; 36 | parm.verbose = 1 ; 37 | model = svm_struct_learn(' -c 1.0 -o 1 -v 1 -t 4 ', parm) ; 38 | w = cat(2, model.svPatterns{:}) * (model.alpha .* cat(1, model.svLabels{:})) / 2 ; 39 | 40 | % ------------------------------------------------------------------ 41 | % Plots 42 | % ------------------------------------------------------------------ 43 | 44 | figure(1) ; clf ; hold on ; 45 | x = [patterns{:}] ; 46 | y = [labels{:}] ; 47 | plot(x(1, y>0), x(2,y>0), 'g.') ; 48 | plot(x(1, y<0), x(2,y<0), 'r.') ; 49 | set(line([0 w(1)], [0 w(2)]), 'color', 'y', 'linewidth', 4) ; 50 | xlim([-3 3]) ; 51 | ylim([-3 3]) ; 52 | set(line(10*[w(2) -w(2)], 10*[-w(1) w(1)]), ... 53 | 'color', 'y', 'linewidth', 2, 'linestyle', '-') ; 54 | axis equal ; 55 | set(gca, 'color', 'b') ; 56 | w 57 | end 58 | 59 | % -------------------------------------------------------------------- 60 | % SVM struct callbacks 61 | % -------------------------------------------------------------------- 62 | 63 | function delta = lossCB(param, y, ybar) 64 | % loss function delta(y, ybar) 65 | delta = double(y ~= ybar) ; 66 | if param.verbose 67 | fprintf('delta = loss(%3d, %3d) = %f\n', y, ybar, delta) ; 68 | end 69 | end 70 | 71 | function k = kernelCB(param, x,y, xp, yp) 72 | k = x' * xp * y * yp / 4 ; 73 | end 74 | 75 | function yhat = constraintCB(param, model, x, y) 76 | % slack resaling: argmax_y delta(yi, y) (1 + - ) 77 | % margin rescaling: argmax_y delta(yi, y) + 78 | 79 | % the kernel is linear, get a weight vector back 80 | if size(model.svPatterns, 2) == 0 81 | w = zeros(size(x)) ; 82 | else 83 | w = [model.svPatterns{:}] * (model.alpha .* [model.svLabels{:}]') / 2 ; 84 | end 85 | if dot(y*x, w) > 1, yhat = y ; else yhat = -y ; end 86 | if param.verbose 87 | fprintf('yhat = violslack([%8.3f,%8.3f], [%8.3f,%8.3f], %3d) = %3d\n', ... 88 | w, x, y, yhat) ; 89 | end 90 | end 91 | --------------------------------------------------------------------------------