From b825401cb0fa00acafe48c9e9895ace94c5a7983 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 15:07:21 -0700 Subject: [PATCH 01/19] Compile on Mavericks with gcc47. --- Makefile | 31 ++++++++++++++++++++++++----- second-stage/programs/wlle/Makefile | 10 +++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index bc8325d..680a4e2 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ # # The following high-level goals may also be useful: # -# make nbestrain-clean # removes temporary files used in nbesttrain +# make nbesttrain-clean # removes temporary files used in nbesttrain # make nbest-oracle # oracle evaluation of n-best results # make features # extracts features from 20-fold parses # make train-reranker # trains reranker model @@ -70,10 +70,25 @@ # # GCCFLAGS = -march=native -mfpmath=sse -msse2 -mmmx -m32 +# GCCFLAGS = -march=x86_64 -mfpmath=sse -msse2 -mssse3 -mmmx -m64 + +# Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. + +GCCFLAGS = -m64 -march=core2 -mfpmath=sse +export GCCFLAGS + +# CC = condor_compile gcc +CC = gcc +export CC + +# CXX = condor_compile g++ +CXX = g++ +export CXX + # CFLAGS is used for all C and C++ compilation # CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) -LDFLAGS = $(GCCLDFLAGS) +LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) EXEC = time # for SWIG wrappers, use these flags instead @@ -93,6 +108,11 @@ export CFLAGS export CXXFLAGS export LDFLAGS +CC=gcc +CXX=g++ +export CC +export CXX + # Building the 20-fold training data with nbesttrain # -------------------------------------------------- @@ -101,7 +121,8 @@ export LDFLAGS # # PENNWSJTREEBANK must be set to the base directory of the Penn WSJ Treebank # -PENNWSJTREEBANK=/usr/local/data/Penn3/parsed/mrg/wsj/ +# PENNWSJTREEBANK=/usr/local/data/Penn3/parsed/mrg/wsj/ +PENNWSJTREEBANK=/corpora/LDC/LDC99T42/RAW/parsed/mrg/wsj # NPARSES is the number of alternative parses to consider for each sentence # @@ -193,11 +214,11 @@ FEATURESNICKNAME=sp ESTIMATOR=second-stage/programs/wlle/cvlm-lbfgs # ESTIMATORFLAGS are flags given to the estimator -# + ESTIMATORFLAGS=-l 1 -c 10 -F 1 -n -1 -p 2 # ESTIMATORNICKNAME is used to name the feature weights file -# + ESTIMATORNICKNAME=lbfgs-l1c10F1n1p2 # ESTIMATORSTACKSIZE is the size (in KB) of the per-thread stacks diff --git a/second-stage/programs/wlle/Makefile b/second-stage/programs/wlle/Makefile index 75a803c..26cbd20 100644 --- a/second-stage/programs/wlle/Makefile +++ b/second-stage/programs/wlle/Makefile @@ -46,15 +46,15 @@ libdata.a: data.o liblmdata.a: lmdata.o ar rcv liblmdata.a lmdata.o; ranlib liblmdata.a -CC=gcc +# CC=gcc # fast options # Compilation help: you may need to remove -march=native on older compilers. -GCCFLAGS=-march=native -mfpmath=sse -msse2 -mmmx +# GCCFLAGS=-march=native -mfpmath=sse -msse2 -mmmx FOPENMP=-fopenmp -CFLAGS=-MMD -O3 -ffast-math -fstrict-aliasing -Wall -finline-functions $(GCCFLAGS) $(FOPENMP) -LDFLAGS=$(FOPENMP) -CXXFLAGS=${CFLAGS} -Wno-deprecated +# CFLAGS=-MMD -O3 -ffast-math -fstrict-aliasing -Wall -finline-functions $(GCCFLAGS) $(FOPENMP) +LDFLAGS+=$(FOPENMP) +# CXXFLAGS=${CFLAGS} -Wno-deprecated # debugging options # GCCFLAGS= From 7c55d76201436fe9f8b539220ffa8c0a4d531752 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 15:11:51 -0700 Subject: [PATCH 02/19] Add my MacPorts setup steps. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 680a4e2..8e3370f 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,12 @@ # GCCFLAGS = -march=x86_64 -mfpmath=sse -msse2 -mssse3 -mmmx -m64 +# For Mavericks (and Mountain Lion) I set up gcc using macports: +# sudo port install gcc47 +# sudo port select --set gcc mp-gcc47 +# sudo port install boost +# sudo port install liblbfgs + # Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. GCCFLAGS = -m64 -march=core2 -mfpmath=sse From f37a1b50edf5ae6df30902ddc0eb80b2b32c3fd2 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 21:07:52 -0700 Subject: [PATCH 03/19] Chase down why -march=native isn't working for gcc after 4.2. Add some comments about that. --- Makefile | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 8e3370f..2c8c7d9 100644 --- a/Makefile +++ b/Makefile @@ -69,8 +69,23 @@ # versions will need -march=pentium4 or -march=opteron # # GCCFLAGS = -march=native -mfpmath=sse -msse2 -mmmx -m32 +# +# On Mac OS X using -march=native doesn't seem to work, so we need to set it. +# This should be safe for any 64bit machine: +# GCCFLAGS = -m64 -march=x86-64 +# You can find out what switches gcc would use for your machine this way: +# gcc -Q --help=target -march=native +# So for a 2013 MacBook Air we might expect to use something like this: +# GCCFLAGS = -m64 -march=ivybridge -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mavx -mssse3 +# But we can't use the -mavx switch because of a problem with the assembler setup. +# Which is presumably why -march=native fails, as indicated by other folks' experience as well: +# http://stackoverflow.com/questions/12016281/g-no-such-instruction-with-avx +# http://mac-os-forge.2317878.n4.nabble.com/gcc-as-AVX-binutils-and-MacOS-X-10-7-td144472.html +# So I wind up with this: +GCCFLAGS = -m64 -march=x86-64 -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mssse3 -# GCCFLAGS = -march=x86_64 -mfpmath=sse -msse2 -mssse3 -mmmx -m64 +# Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. +export GCCFLAGS # For Mavericks (and Mountain Lion) I set up gcc using macports: # sudo port install gcc47 @@ -78,11 +93,6 @@ # sudo port install boost # sudo port install liblbfgs -# Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. - -GCCFLAGS = -m64 -march=core2 -mfpmath=sse -export GCCFLAGS - # CC = condor_compile gcc CC = gcc export CC @@ -94,6 +104,7 @@ export CXX # CFLAGS is used for all C and C++ compilation # CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) +# For some reason macports does put liblbfgs' files on the right path, so I add it on here. LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) EXEC = time @@ -127,8 +138,7 @@ export CXX # # PENNWSJTREEBANK must be set to the base directory of the Penn WSJ Treebank # -# PENNWSJTREEBANK=/usr/local/data/Penn3/parsed/mrg/wsj/ -PENNWSJTREEBANK=/corpora/LDC/LDC99T42/RAW/parsed/mrg/wsj +PENNWSJTREEBANK=/usr/local/data/Penn3/parsed/mrg/wsj/ # NPARSES is the number of alternative parses to consider for each sentence # @@ -220,11 +230,11 @@ FEATURESNICKNAME=sp ESTIMATOR=second-stage/programs/wlle/cvlm-lbfgs # ESTIMATORFLAGS are flags given to the estimator - +# ESTIMATORFLAGS=-l 1 -c 10 -F 1 -n -1 -p 2 # ESTIMATORNICKNAME is used to name the feature weights file - +# ESTIMATORNICKNAME=lbfgs-l1c10F1n1p2 # ESTIMATORSTACKSIZE is the size (in KB) of the per-thread stacks From 00b2ea8fadec73d613153930196840b4291f427d Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 21:22:10 -0700 Subject: [PATCH 04/19] The simple switch is just disabling AVX. --- Makefile | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 2c8c7d9..a1fefe3 100644 --- a/Makefile +++ b/Makefile @@ -70,19 +70,12 @@ # # GCCFLAGS = -march=native -mfpmath=sse -msse2 -mmmx -m32 # -# On Mac OS X using -march=native doesn't seem to work, so we need to set it. -# This should be safe for any 64bit machine: -# GCCFLAGS = -m64 -march=x86-64 -# You can find out what switches gcc would use for your machine this way: -# gcc -Q --help=target -march=native -# So for a 2013 MacBook Air we might expect to use something like this: -# GCCFLAGS = -m64 -march=ivybridge -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mavx -mssse3 -# But we can't use the -mavx switch because of a problem with the assembler setup. -# Which is presumably why -march=native fails, as indicated by other folks' experience as well: +# On Mac OS X using -march=native doesn't seem to work (a compilation error will occur). +# Turns out there is a problem with AVX instructions on OSX for gcc after 4.2. # http://stackoverflow.com/questions/12016281/g-no-such-instruction-with-avx # http://mac-os-forge.2317878.n4.nabble.com/gcc-as-AVX-binutils-and-MacOS-X-10-7-td144472.html -# So I wind up with this: -GCCFLAGS = -m64 -march=x86-64 -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mssse3 +# So here's what works for me (with or without the -mfpmath=sse - the default is 387): +GCCFLAGS = -m64 -march=native -mno-avx -mfpmath=sse # Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. export GCCFLAGS From 95680e92399f7f061477976009594cb6110ed2fb Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 21:31:39 -0700 Subject: [PATCH 05/19] Missing a negation does bad things to meaning. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a1fefe3..b1b32ea 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ export CXX # CFLAGS is used for all C and C++ compilation # CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) -# For some reason macports does put liblbfgs' files on the right path, so I add it on here. +# For some reason MacPorts does not put liblbfgs' files on the right path, so I add it on here. LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) EXEC = time From 39a51eae3e4536480e99fba515f08c444c240f43 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 20 Mar 2014 21:36:32 -0700 Subject: [PATCH 06/19] MacPorts will gladly install more than one library at a time. --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b1b32ea..8a1dde1 100644 --- a/Makefile +++ b/Makefile @@ -83,8 +83,7 @@ export GCCFLAGS # For Mavericks (and Mountain Lion) I set up gcc using macports: # sudo port install gcc47 # sudo port select --set gcc mp-gcc47 -# sudo port install boost -# sudo port install liblbfgs +# sudo port install boost liblbfgs # CC = condor_compile gcc CC = gcc From bc3500b8c53210d7f44e4e737ca1a26591902945 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 00:06:14 -0700 Subject: [PATCH 07/19] Move Mac-specific settings into Makefile.mac from Makefile. --- Makefile | 30 +++++++++++++++++++--------- Makefile.mac | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 Makefile.mac diff --git a/Makefile b/Makefile index bc8325d..20af839 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ # # The following high-level goals may also be useful: # -# make nbestrain-clean # removes temporary files used in nbesttrain +# make nbesttrain-clean # removes temporary files used in nbesttrain # make nbest-oracle # oracle evaluation of n-best results # make features # extracts features from 20-fold parses # make train-reranker # trains reranker model @@ -70,11 +70,19 @@ # # GCCFLAGS = -march=native -mfpmath=sse -msse2 -mmmx -m32 +# CC = condor_compile gcc +CC ?= gcc +export CC + +# CXX = condor_compile g++ +CXX ?= g++ +export CXX + # CFLAGS is used for all C and C++ compilation # -CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) -LDFLAGS = $(GCCLDFLAGS) -EXEC = time +CFLAGS ?= -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) +LDFLAGS ?= $(GCCLDFLAGS) +EXEC ?= time # for SWIG wrappers, use these flags instead # @@ -88,7 +96,7 @@ EXEC = time # LDFLAGS = -g -Wall $(GCCLDFLAGS) # EXEC = valgrind -CXXFLAGS = $(CFLAGS) -Wno-deprecated +CXXFLAGS ?= $(CFLAGS) -Wno-deprecated export CFLAGS export CXXFLAGS export LDFLAGS @@ -101,11 +109,11 @@ export LDFLAGS # # PENNWSJTREEBANK must be set to the base directory of the Penn WSJ Treebank # -PENNWSJTREEBANK=/usr/local/data/Penn3/parsed/mrg/wsj/ +PENNWSJTREEBANK ?= /usr/local/data/Penn3/parsed/mrg/wsj/ # NPARSES is the number of alternative parses to consider for each sentence # -NPARSES=50 +NPARSES ?= 50 # NFOLDS is the number of folds to use, and FOLDS is a list of the numbers # from 00 to NFOLDS-1 (I couldn't see how to program this in make). @@ -520,8 +528,12 @@ train-reranker: $(WEIGHTSFILEGZ) # $(WEIGHTSFILEGZ): $(ESTIMATOR) $(WEIGHTSFILEGZ): $(ESTIMATOR) $(MODELDIR)/features.gz $(FEATDIR)/train.gz $(FEATDIR)/dev.gz $(FEATDIR)/test1.gz $(ESTIMATORENV) $(ZCAT) $(FEATDIR)/train.gz | $(EXEC) $(ESTIMATOR) $(ESTIMATORFLAGS) -e $(FEATDIR)/dev.gz -f $(MODELDIR)/features.gz -o $(WEIGHTSFILE) -x $(FEATDIR)/test1.gz - rm -f $(WEIGHTSFILEGZ) - gzip $(WEIGHTSFILE) + # If you use gzip's automagic renaming, be sure to use -f in case some backup program + # throws in some extra hardlinks (I'm looking at you Time Machine /.MobileBackups). + # gzip -f $(WEIGHTSFILE) + # But let's avoid that business entirely and use gzip as a filter like elsewhere in this Makefile. + gzip -c $(WEIGHTSFILE) >$(WEIGHTSFILEGZ) + rm -f $(WEIGHTSFILE) ######################################################################## # # diff --git a/Makefile.mac b/Makefile.mac new file mode 100644 index 0000000..4da6a53 --- /dev/null +++ b/Makefile.mac @@ -0,0 +1,55 @@ +# To use these defaults set the MAKEFILES environment variable when calling make. +# export MAKEFILES=`pwd`/Makefile.mac + +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') + +# For Mavericks (and Mountain Lion) I set up gcc using macports: +# sudo port install gcc47 +# sudo port select --set gcc mp-gcc47 +# sudo port install boost liblbfgs + +# On Mac OS X using -march=native doesn't seem to work (a compilation error will occur). +# Turns out there is a problem with AVX instructions on OSX for gcc after 4.2. +# http://stackoverflow.com/questions/12016281/g-no-such-instruction-with-avx +# http://mac-os-forge.2317878.n4.nabble.com/gcc-as-AVX-binutils-and-MacOS-X-10-7-td144472.html +# So here's what works for me (with or without the -mfpmath=sse - the default is 387): + +GCCFLAGS = -m64 -march=x86-64 -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mssse3 -I/opt/local/include + +# Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. +export GCCFLAGS + +# CC = condor_compile gcc +CC = gcc +export CC + +# CXX = condor_compile g++ +CXX = g++ +export CXX + +# fast options +# Compilation help: you may need to remove -march=native on older compilers. +# GCCFLAGS=-march=native -mfpmath=sse -msse2 -mmmx +FOPENMP=-fopenmp +# CFLAGS=-MMD -O3 -ffast-math -fstrict-aliasing -Wall -finline-functions $(GCCFLAGS) $(FOPENMP) +# LDFLAGS=$(FOPENMP) -L/opt/local/lib + +# debugging options +# GCCFLAGS= +# FOPENMP= +# CFLAGS=-MMD -O0 -g $(GCCFLAGS) $(FOPENMP) +# LDFLAGS=-g $(FOPENMP) +# CXXFLAGS=${CFLAGS} -Wno-deprecated + +# CFLAGS is used for all C and C++ compilation +# +CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) +export CFLAGS + +LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) +export LDFLAGS + +CXXFLAGS=${CFLAGS} -Wno-deprecated +export CXXFLAGS + +PENNWSJTREEBANK = /usr/local/data/Penn3/parsed/mrg/wsj From bd9be5c1d45abfd342e67f5ab02e0a3db67f689a Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 00:06:51 -0700 Subject: [PATCH 08/19] Use ESTIMATORNICKNAME=lbfgs-l1c10F1n1p2 to match the other current settings. --- parse.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parse.sh b/parse.sh index 4c7a76c..0bf5668 100755 --- a/parse.sh +++ b/parse.sh @@ -14,5 +14,6 @@ # RERANKDATA=ec50-connll-ic-s5 # RERANKDATA=ec50-f050902-lics5 MODELDIR=second-stage/models/ec50spfinal -ESTIMATORNICKNAME=cvlm-l1c10P1 +# ESTIMATORNICKNAME=cvlm-l1c10P1 +ESTIMATORNICKNAME=lbfgs-l1c10F1n1p2 first-stage/PARSE/parseIt -l399 -N50 first-stage/DATA/EN/ $* | second-stage/programs/features/best-parses -l $MODELDIR/features.gz $MODELDIR/$ESTIMATORNICKNAME-weights.gz From 77837e591694693575749076dc7268a26e710307 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 13:43:23 -0700 Subject: [PATCH 09/19] A Git .gitignore copied from .hgignore. --- .gitignore | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69e8f09 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +glob:*.dep +glob:*.o +glob:*.a +glob:*.d +glob:second-stage/programs/*/read-tree.cc +glob:evalb/evalb +glob:first-stage/PARSE/parseIt +glob:first-stage/TRAIN/iScale +glob:first-stage/TRAIN/kn3Counts +glob:first-stage/TRAIN/pSfgT +glob:first-stage/TRAIN/pSgT +glob:first-stage/TRAIN/pTgNt +glob:first-stage/TRAIN/pUgT +glob:first-stage/TRAIN/rCounts +glob:first-stage/TRAIN/selFeats +glob:first-stage/TRAIN/trainRs +glob:second-stage/programs/eval-beam/main +glob:second-stage/programs/eval-weights/eval-weights +glob:second-stage/programs/features/best-*parses +glob:second-stage/programs/features/extract-*features +glob:second-stage/programs/features/count-*features +glob:second-stage/programs/features/oracle-score +glob:second-stage/programs/features/parallel-extract-nfeatures +glob:second-stage/programs/features/parallel-extract-spfeatures +glob:second-stage/programs/prepare-data/copy-trees-ss +glob:second-stage/programs/prepare-data/prepare-ec-data +glob:second-stage/programs/prepare-data/prepare-ec-data100 +glob:second-stage/programs/prepare-data/prepare-new-data +glob:second-stage/programs/prepare-data/ptb +glob:second-stage/programs/wlle/avper +glob:second-stage/programs/wlle/gavper +glob:second-stage/programs/wlle/cvlm +glob:second-stage/programs/wlle/cvlm-lbfgs +glob:second-stage/programs/wlle/oracle +glob:*.swp +glob:*.orig +glob:tags +glob:TAGS +glob:first-stage/PARSE/evalTree +glob:first-stage/PARSE/parseAndEval +glob:*.py[co] +glob:*.class +glob:*.so +glob:*_wrapper.cxx +glob:first-stage/PARSE/swig/*/lib/* +glob:second-stage/programs/features/swig/*/lib/* +glob:first-stage/PARSE/swig/*/build/* +glob:second-stage/programs/features/swig/*/build/* +glob:SParseval/* +glob:regression-test-* +glob:build* +glob:dist* +glob:first-stage/PARSE/parser_wrapper.C +glob:second-stage/programs/features/reranker_wrapper.C +glob:python/bllipparser/CharniakParser.py +glob:python/bllipparser/JohnsonReranker.py +glob:MANIFEST From cfaa9746dfae80b110d978bfce1bb1a1ebedc6b6 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 14:05:22 -0700 Subject: [PATCH 10/19] Tidied up .gitignore and add second-stage/nbest and tmp dirs. --- .gitignore | 124 +++++++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 69e8f09..d9be4e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,57 +1,67 @@ -glob:*.dep -glob:*.o -glob:*.a -glob:*.d -glob:second-stage/programs/*/read-tree.cc -glob:evalb/evalb -glob:first-stage/PARSE/parseIt -glob:first-stage/TRAIN/iScale -glob:first-stage/TRAIN/kn3Counts -glob:first-stage/TRAIN/pSfgT -glob:first-stage/TRAIN/pSgT -glob:first-stage/TRAIN/pTgNt -glob:first-stage/TRAIN/pUgT -glob:first-stage/TRAIN/rCounts -glob:first-stage/TRAIN/selFeats -glob:first-stage/TRAIN/trainRs -glob:second-stage/programs/eval-beam/main -glob:second-stage/programs/eval-weights/eval-weights -glob:second-stage/programs/features/best-*parses -glob:second-stage/programs/features/extract-*features -glob:second-stage/programs/features/count-*features -glob:second-stage/programs/features/oracle-score -glob:second-stage/programs/features/parallel-extract-nfeatures -glob:second-stage/programs/features/parallel-extract-spfeatures -glob:second-stage/programs/prepare-data/copy-trees-ss -glob:second-stage/programs/prepare-data/prepare-ec-data -glob:second-stage/programs/prepare-data/prepare-ec-data100 -glob:second-stage/programs/prepare-data/prepare-new-data -glob:second-stage/programs/prepare-data/ptb -glob:second-stage/programs/wlle/avper -glob:second-stage/programs/wlle/gavper -glob:second-stage/programs/wlle/cvlm -glob:second-stage/programs/wlle/cvlm-lbfgs -glob:second-stage/programs/wlle/oracle -glob:*.swp -glob:*.orig -glob:tags -glob:TAGS -glob:first-stage/PARSE/evalTree -glob:first-stage/PARSE/parseAndEval -glob:*.py[co] -glob:*.class -glob:*.so -glob:*_wrapper.cxx -glob:first-stage/PARSE/swig/*/lib/* -glob:second-stage/programs/features/swig/*/lib/* -glob:first-stage/PARSE/swig/*/build/* -glob:second-stage/programs/features/swig/*/build/* -glob:SParseval/* -glob:regression-test-* -glob:build* -glob:dist* -glob:first-stage/PARSE/parser_wrapper.C -glob:second-stage/programs/features/reranker_wrapper.C -glob:python/bllipparser/CharniakParser.py -glob:python/bllipparser/JohnsonReranker.py -glob:MANIFEST +*_wrapper.cxx +*.a +*.class +*.d +*.dep +*.o +*.orig +*.py[co] +*.so +*.swp + +/SParseval +/tmp + +evalb/evalb + +build* +dist* + +MANIFEST +regression-test-* +tags +TAGS + +python/bllipparser/CharniakParser.py +python/bllipparser/JohnsonReranker.py + +first-stage/PARSE/evalTree +first-stage/PARSE/parseAndEval +first-stage/PARSE/parseIt +first-stage/PARSE/parser_wrapper.C +first-stage/PARSE/swig/*/build/* +first-stage/PARSE/swig/*/lib/* +first-stage/TRAIN/iScale +first-stage/TRAIN/kn3Counts +first-stage/TRAIN/pSfgT +first-stage/TRAIN/pSgT +first-stage/TRAIN/pTgNt +first-stage/TRAIN/pUgT +first-stage/TRAIN/rCounts +first-stage/TRAIN/selFeats +first-stage/TRAIN/trainRs + +/second-stage/nbest + +second-stage/programs/*/read-tree.cc +second-stage/programs/eval-beam/main +second-stage/programs/eval-weights/eval-weights +second-stage/programs/features/best-*parses +second-stage/programs/features/count-*features +second-stage/programs/features/extract-*features +second-stage/programs/features/oracle-score +second-stage/programs/features/parallel-extract-nfeatures +second-stage/programs/features/parallel-extract-spfeatures +second-stage/programs/features/reranker_wrapper.C +second-stage/programs/features/swig/*/build/* +second-stage/programs/features/swig/*/lib/* +second-stage/programs/prepare-data/copy-trees-ss +second-stage/programs/prepare-data/prepare-ec-data +second-stage/programs/prepare-data/prepare-ec-data100 +second-stage/programs/prepare-data/prepare-new-data +second-stage/programs/prepare-data/ptb +second-stage/programs/wlle/avper +second-stage/programs/wlle/cvlm +second-stage/programs/wlle/cvlm-lbfgs +second-stage/programs/wlle/gavper +second-stage/programs/wlle/oracle From 13fb007ed00d98ef0f11cb9c15c964e8f3d44325 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 14:13:35 -0700 Subject: [PATCH 11/19] Set LD_LIBRARY_PATH too. --- Makefile.mac | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Makefile.mac b/Makefile.mac index 4da6a53..5c455bd 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -8,13 +8,25 @@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') # sudo port select --set gcc mp-gcc47 # sudo port install boost liblbfgs +# Using MacPorts means that we have to override the default include and library locations. +LD_INCLUDE_PATH=/opt/local/include +LD_LIBRARY_PATH=/opt/local/lib + +export LD_INCLUDE_PATH +export LD_LIBRARY_PATH + +# The SParseval makefile uses a -lm dependency (a bad idea imho) which fails because there +# is no libm.a to be used. This trick works by mapping that to the system's libm.dylib. +# .LIBPATTERNS+=lib%.dylib +# export .LIBPATTERNS + # On Mac OS X using -march=native doesn't seem to work (a compilation error will occur). # Turns out there is a problem with AVX instructions on OSX for gcc after 4.2. # http://stackoverflow.com/questions/12016281/g-no-such-instruction-with-avx # http://mac-os-forge.2317878.n4.nabble.com/gcc-as-AVX-binutils-and-MacOS-X-10-7-td144472.html # So here's what works for me (with or without the -mfpmath=sse - the default is 387): -GCCFLAGS = -m64 -march=x86-64 -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mssse3 -I/opt/local/include +GCCFLAGS = -m64 -march=x86-64 -mfpmath=sse -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2 -mssse3 -I${LD_INCLUDE_PATH} # Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. export GCCFLAGS @@ -46,10 +58,11 @@ FOPENMP=-fopenmp CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) export CFLAGS -LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) -export LDFLAGS - CXXFLAGS=${CFLAGS} -Wno-deprecated export CXXFLAGS -PENNWSJTREEBANK = /usr/local/data/Penn3/parsed/mrg/wsj +LDFLAGS = -L${LD_LIBRARY_PATH} $(GCCLDFLAGS) +export LDFLAGS + +# This is a handy place to put a local setting without changing Makefile. +# PENNWSJTREEBANK = /usr/local/data/Penn3/parsed/mrg/wsj From 9343b8c13104777ac21f28f38f051c2b4a5f1989 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 14:14:14 -0700 Subject: [PATCH 12/19] Add a rule to .LIBPATTERNS so that SParseval will make. --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 20af839..55dccfc 100644 --- a/Makefile +++ b/Makefile @@ -279,9 +279,17 @@ SParseval: tar xvzf SParseval.tgz rm SParseval.tgz +# The SParseval makefile uses a -lm dependency (a bad idea imho) which fails because there +# is no libm.a to be used. This trick works by mapping that to the system's libm.dylib. +# .LIBPATTERNS+=lib%.dylib +# export .LIBPATTERNS +# But we can't put that in Makefile.mac because it would mess up some of the other programs. +# So I put it here in the Make arguments and it shouldn't hurt Linuxen where the default +# rules were already working. + SParseval/src/sparseval: SParseval rm -f SParseval/src/*.o - $(MAKE) -C SParseval/src sparseval + $(MAKE) -C SParseval/src .LIBPATTERNS+=lib%.dylib sparseval # clean removes object files. # From 7a4d1e00eb4ef471cf21ce4715f43ce4e2afac02 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 14:46:14 -0700 Subject: [PATCH 13/19] Don't set standard make variables if they are already set. --- second-stage/programs/eval-weights/Makefile | 2 +- second-stage/programs/wlle/Makefile | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/second-stage/programs/eval-weights/Makefile b/second-stage/programs/eval-weights/Makefile index b6dbd65..a5612f6 100644 --- a/second-stage/programs/eval-weights/Makefile +++ b/second-stage/programs/eval-weights/Makefile @@ -14,7 +14,7 @@ SOURCES = best-indices.cc best-parse.cc best-parses.cc compare-models.cc data.c TARGETS = eval-weights # best-indices best-parse best-parses compare-models pretty-print OBJECTS = $(patsubst %.l,%.o,$(patsubst %.c,%.o,$(SOURCES:%.cc=%.o))) -CC = gcc +CC ?= gcc all: $(TARGETS) diff --git a/second-stage/programs/wlle/Makefile b/second-stage/programs/wlle/Makefile index 75a803c..2efa68f 100644 --- a/second-stage/programs/wlle/Makefile +++ b/second-stage/programs/wlle/Makefile @@ -46,15 +46,15 @@ libdata.a: data.o liblmdata.a: lmdata.o ar rcv liblmdata.a lmdata.o; ranlib liblmdata.a -CC=gcc +CC?=gcc # fast options # Compilation help: you may need to remove -march=native on older compilers. -GCCFLAGS=-march=native -mfpmath=sse -msse2 -mmmx -FOPENMP=-fopenmp -CFLAGS=-MMD -O3 -ffast-math -fstrict-aliasing -Wall -finline-functions $(GCCFLAGS) $(FOPENMP) -LDFLAGS=$(FOPENMP) -CXXFLAGS=${CFLAGS} -Wno-deprecated +GCCFLAGS?=-march=native -mfpmath=sse -msse2 -mmmx +FOPENMP?=-fopenmp +CFLAGS?=-MMD -O3 -ffast-math -fstrict-aliasing -Wall -finline-functions $(GCCFLAGS) $(FOPENMP) +LDFLAGS?=$(FOPENMP) +CXXFLAGS?=${CFLAGS} -Wno-deprecated # debugging options # GCCFLAGS= From d4d375bcdf86f95132ee7713b34b25ed24dde0a3 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 14:48:07 -0700 Subject: [PATCH 14/19] index on BLLIP_FOR_MACOS: bd9be5c Use ESTIMATORNICKNAME=lbfgs-l1c10F1n1p2 to match the other current settings. From 4bc24de26480f9452d42b48b7b3f0db0617546b1 Mon Sep 17 00:00:00 2001 From: Jim White Date: Thu, 10 Jul 2014 15:29:22 -0700 Subject: [PATCH 15/19] Remove Mac-specific stuff from Makefile. --- Makefile | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 8a1dde1..fed52f5 100644 --- a/Makefile +++ b/Makefile @@ -68,36 +68,12 @@ # Version 4.1 and later gcc permit -march=native, but older # versions will need -march=pentium4 or -march=opteron # -# GCCFLAGS = -march=native -mfpmath=sse -msse2 -mmmx -m32 -# -# On Mac OS X using -march=native doesn't seem to work (a compilation error will occur). -# Turns out there is a problem with AVX instructions on OSX for gcc after 4.2. -# http://stackoverflow.com/questions/12016281/g-no-such-instruction-with-avx -# http://mac-os-forge.2317878.n4.nabble.com/gcc-as-AVX-binutils-and-MacOS-X-10-7-td144472.html -# So here's what works for me (with or without the -mfpmath=sse - the default is 387): -GCCFLAGS = -m64 -march=native -mno-avx -mfpmath=sse - -# Must use export because otherwise second-stage/programs/wlle/Makefile doesn't get the message. -export GCCFLAGS - -# For Mavericks (and Mountain Lion) I set up gcc using macports: -# sudo port install gcc47 -# sudo port select --set gcc mp-gcc47 -# sudo port install boost liblbfgs - -# CC = condor_compile gcc -CC = gcc -export CC - -# CXX = condor_compile g++ -CXX = g++ -export CXX +# GCCFLAGS ?= -march=native -mfpmath=sse -msse2 -mmmx -m32 # CFLAGS is used for all C and C++ compilation # CFLAGS = -MMD -O3 -Wall -ffast-math -finline-functions -fomit-frame-pointer -fstrict-aliasing $(GCCFLAGS) -# For some reason MacPorts does not put liblbfgs' files on the right path, so I add it on here. -LDFLAGS = -L/opt/local/lib $(GCCLDFLAGS) + EXEC = time # for SWIG wrappers, use these flags instead @@ -112,13 +88,13 @@ EXEC = time # LDFLAGS = -g -Wall $(GCCLDFLAGS) # EXEC = valgrind -CXXFLAGS = $(CFLAGS) -Wno-deprecated +CXXFLAGS ?= $(CFLAGS) -Wno-deprecated export CFLAGS export CXXFLAGS export LDFLAGS -CC=gcc -CXX=g++ +CC ?= gcc +CXX ?= g++ export CC export CXX From 6b6ccde05fbf2b74c4ef502f3719f762c2274ab2 Mon Sep 17 00:00:00 2001 From: Jim White Date: Fri, 11 Jul 2014 09:47:09 -0700 Subject: [PATCH 16/19] Always use 'gzip -c' to avoid failures caused by extra hardlinks. --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index fed52f5..243b762 100644 --- a/Makefile +++ b/Makefile @@ -522,11 +522,14 @@ train-reranker: $(WEIGHTSFILEGZ) # This goal estimates the reranker feature weights (i.e., trains the # reranker). # +# Don't use auto-renaming as in "gzip foo" because it fails if there is +# more than one hardlink on the file (I'm looking at you Time Machine!). +# # $(WEIGHTSFILEGZ): $(ESTIMATOR) $(WEIGHTSFILEGZ): $(ESTIMATOR) $(MODELDIR)/features.gz $(FEATDIR)/train.gz $(FEATDIR)/dev.gz $(FEATDIR)/test1.gz $(ESTIMATORENV) $(ZCAT) $(FEATDIR)/train.gz | $(EXEC) $(ESTIMATOR) $(ESTIMATORFLAGS) -e $(FEATDIR)/dev.gz -f $(MODELDIR)/features.gz -o $(WEIGHTSFILE) -x $(FEATDIR)/test1.gz - rm -f $(WEIGHTSFILEGZ) - gzip $(WEIGHTSFILE) + gzip -c $(WEIGHTSFILE) >$(WEIGHTSFILEGZ) + rm -f $(WEIGHTSFILE) ######################################################################## # # From a83efd7e9f6d4707b24e47d3e684fd38fc72c2fe Mon Sep 17 00:00:00 2001 From: Jim White Date: Fri, 11 Jul 2014 09:49:54 -0700 Subject: [PATCH 17/19] index on BLLIP_ON_MAVERICKS: 4bc24de Remove Mac-specific stuff from Makefile. From 1b4e005d38ead48b4ac8c3591d33c959718f3070 Mon Sep 17 00:00:00 2001 From: Jim White Date: Fri, 11 Jul 2014 09:53:17 -0700 Subject: [PATCH 18/19] Restore OWLQN stuff in wlle for systems without liblbgfs. --- Makefile.here | 6 + second-stage/programs/wlle/OWLQN.cpp | 313 ++++++++ second-stage/programs/wlle/OWLQN.h | 101 +++ .../programs/wlle/TerminationCriterion.cpp | 28 + .../programs/wlle/TerminationCriterion.h | 21 + second-stage/programs/wlle/cvlm-owlqn | Bin 0 -> 120343 bytes second-stage/programs/wlle/cvlm-owlqn.cc | 758 ++++++++++++++++++ second-stage/programs/wlle/powell.h | 575 +++++++++++++ 8 files changed, 1802 insertions(+) create mode 100644 Makefile.here create mode 100644 second-stage/programs/wlle/OWLQN.cpp create mode 100644 second-stage/programs/wlle/OWLQN.h create mode 100644 second-stage/programs/wlle/TerminationCriterion.cpp create mode 100644 second-stage/programs/wlle/TerminationCriterion.h create mode 100755 second-stage/programs/wlle/cvlm-owlqn create mode 100644 second-stage/programs/wlle/cvlm-owlqn.cc create mode 100644 second-stage/programs/wlle/powell.h diff --git a/Makefile.here b/Makefile.here new file mode 100644 index 0000000..c2485d5 --- /dev/null +++ b/Makefile.here @@ -0,0 +1,6 @@ +# export MAKEFILES=/projects/SSTP/MYBLLIP/bllip-parser/Makefile.here + +PENNWSJTREEBANK=/corpora/LDC/LDC99T42/RAW/parsed/mrg/wsj + +USE_OLD_NONLBFGS_ESTIMATOR=1 + diff --git a/second-stage/programs/wlle/OWLQN.cpp b/second-stage/programs/wlle/OWLQN.cpp new file mode 100644 index 0000000..df730a3 --- /dev/null +++ b/second-stage/programs/wlle/OWLQN.cpp @@ -0,0 +1,313 @@ +#include "OWLQN.h" + +#include "TerminationCriterion.h" + +#include +#include +#include +#include +#include + +#include + +#define EPSILON 1e-20 + +using namespace std; + +double OptimizerState::dotProduct(const DblVec& a, const DblVec& b) { + double result = 0; + for (size_t i=0; i= 0; i--) { + if (roList[i] > 0) { + alphas[i] = -dotProduct(*sList[i], dir) / roList[i]; + addMult(dir, *yList[i], alphas[i]); + if (lastGoodRo == -1) lastGoodRo = i; + } + } + + if (lastGoodRo == -1) return; + + const DblVec& lastY = *yList[lastGoodRo]; + double yDotY = dotProduct(lastY, lastY); + + if (yDotY == 0) return; + + double scalar = roList[lastGoodRo] / yDotY; + scale(dir, scalar); + + for (int i = 0; i < count; i++) { + if (roList[i] > 0) { + double beta = dotProduct(*yList[i], dir) / roList[i]; + addMult(dir, *sList[i], -alphas[i] - beta); + } + } + } +} + +void OptimizerState::MakeSteepestDescDir() { + if (l1weight == 0) { + scaleInto(dir, grad, -1); + } else { + + for (size_t i=0; i 0) { + dir[i] = -grad[i] - l1weight; + } else { + if (grad[i] < -l1weight) { + dir[i] = -grad[i] - l1weight; + } else if (grad[i] > l1weight) { + dir[i] = -grad[i] + l1weight; + } else { + dir[i] = 0; + } + } + } + } + + steepestDescDir = dir; +} + +void OptimizerState::FixDirSigns() { + if (l1weight > 0) { + for (size_t i = 0; i 0) { + val += dir[i] * (grad[i] + l1weight); + } else if (dir[i] < 0) { + val += dir[i] * (grad[i] - l1weight); + } else if (dir[i] > 0) { + val += dir[i] * (grad[i] + l1weight); + } + } + } + + return val; + } +} + +void OptimizerState::GetNextPoint(double alpha) { + addMultInto(newX, x, dir, alpha); + if (l1weight > 0) { + for (size_t i=0; i 0) { + for (size_t i=0; i= 0) { + cerr << "L-BFGS chose a non-descent direction: check your gradient!" << endl; + exit(1); + } + + double alpha = 1.0; + double backoff = 0.5; + if (iter == 1) { + //alpha = 0.1; + //backoff = 0.5; + double normDir = sqrt(dotProduct(dir, dir)); + assert(normDir != 0); + assert(normDir != normDir+1); + alpha = (1 / normDir); + backoff = 0.1; + } + + const double c1 = 1e-4; + double oldValue = value; + + while (true) { + GetNextPoint(alpha); + value = EvalL1(); + + if (value <= oldValue + c1 * origDirDeriv * alpha) break; + + if (!quiet) cout << "." << flush; + + alpha *= backoff; + } + + if (!quiet) cout << endl; +} + +void OptimizerState::Shift() { + DblVec *nextS = NULL, *nextY = NULL; + + int listSize = (int)sList.size(); + + if (listSize < m) { + try { + nextS = new vector(dim); + nextY = new vector(dim); + } catch (bad_alloc) { + m = listSize; + if (nextS != NULL) { + delete nextS; + nextS = NULL; + } + } + } + + if (nextS == NULL) { + nextS = sList.front(); + sList.pop_front(); + nextY = yList.front(); + yList.pop_front(); + roList.pop_front(); + } + + addMultInto(*nextS, newX, x, -1); + addMultInto(*nextY, newGrad, grad, -1); + double ro = dotProduct(*nextS, *nextY); + if (ro == 0) + ro = EPSILON; + + sList.push_back(nextS); + yList.push_back(nextY); + roList.push_back(ro); + + x.swap(newX); + grad.swap(newGrad); + + iter++; +} + +void OWLQN::Minimize(DifferentiableFunction& function, const DblVec& initial, DblVec& minimum, double l1weight, double tol, int m) const { + OptimizerState state(function, initial, m, l1weight, quiet); + + if (!quiet) { + cout << setprecision(4) << scientific << right; + cout << endl << "Optimizing function of " << state.dim << " variables with OWL-QN parameters:" << endl; + cout << " l1 regularization weight: " << l1weight << "." << endl; + cout << " L-BFGS memory parameter (m): " << m << endl; + cout << " Convergence tolerance: " << tol << endl; + cout << endl; + cout << "Iter n: new_value (conv_crit) line_search" << endl << flush; + cout << "Iter 0: " << setw(10) << state.value << " (***********) " << flush; + } + + ostringstream str; + termCrit->GetValue(state, str); + + while (true) { + state.UpdateDir(); + state.BackTrackingLineSearch(); + + ostringstream str; + double termCritVal = termCrit->GetValue(state, str); + if (!quiet) { + cout << "Iter " << setw(4) << state.iter << ": " << setw(10) << state.value; + cout << str.str() << flush; + } + + if (termCritVal < tol) break; + + state.Shift(); + } + + if (!quiet) cout << endl; + + minimum = state.newX; +} diff --git a/second-stage/programs/wlle/OWLQN.h b/second-stage/programs/wlle/OWLQN.h new file mode 100644 index 0000000..50294db --- /dev/null +++ b/second-stage/programs/wlle/OWLQN.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include +#include + +typedef std::vector DblVec; + +struct DifferentiableFunction { + virtual double Eval(const DblVec& input, DblVec& gradient) = 0; +}; + +#include "TerminationCriterion.h" + +class OWLQN { + bool quiet; + bool responsibleForTermCrit; + +public: + TerminationCriterion *termCrit; + + OWLQN(bool quiet = false) : quiet(quiet) { + termCrit = new RelativeMeanImprovementCriterion(5); + responsibleForTermCrit = true; + } + + OWLQN(TerminationCriterion *termCrit, bool quiet = false) : quiet(quiet), termCrit(termCrit) { + responsibleForTermCrit = false; + } + + ~OWLQN() { + if (termCrit && responsibleForTermCrit) delete termCrit; + } + + void Minimize(DifferentiableFunction& function, const DblVec& initial, DblVec& minimum, double l1weight = 1.0, double tol = 1e-4, int m = 10) const; + void SetQuiet(bool q) { quiet = q; } + +}; + +class OptimizerState { + friend class OWLQN; + + struct DblVecPtrDeque : public std::deque { + ~DblVecPtrDeque() { + for (size_t s = 0; s < size(); ++s) { + if ((*this)[s] != NULL) delete (*this)[s]; + } + } + }; + + DblVec x, grad, newX, newGrad, dir; + DblVec& steepestDescDir; // references newGrad to save memory, since we don't ever use both at the same time + DblVecPtrDeque sList, yList; + std::deque roList; + std::vector alphas; + double value; + int iter, m; + const size_t dim; + DifferentiableFunction& func; + double l1weight; + bool quiet; + + static double dotProduct(const DblVec& a, const DblVec& b); + static void add(DblVec& a, const DblVec& b); + static void addMult(DblVec& a, const DblVec& b, double c); + static void addMultInto(DblVec& a, const DblVec& b, const DblVec& c, double d); + static void scale(DblVec& a, double b); + static void scaleInto(DblVec& a, const DblVec& b, double c); + + void MapDirByInverseHessian(); + void UpdateDir(); + double DirDeriv() const; + void GetNextPoint(double alpha); + void BackTrackingLineSearch(); + void Shift(); + void MakeSteepestDescDir(); + double EvalL1(); + void FixDirSigns(); + void TestDirDeriv(); + + OptimizerState(DifferentiableFunction& f, const DblVec& init, int m, double l1weight, bool quiet) + : x(init), grad(init.size()), newX(init), newGrad(init.size()), dir(init.size()), steepestDescDir(newGrad), alphas(m), iter(1), m(m), dim(init.size()), func(f), l1weight(l1weight), quiet(quiet) { + if (m <= 0) { + std::cerr << "m must be an integer greater than zero." << std::endl; + exit(1); + } + value = EvalL1(); + grad = newGrad; + } + +public: + const DblVec& GetX() const { return newX; } + const DblVec& GetLastX() const { return x; } + const DblVec& GetGrad() const { return newGrad; } + const DblVec& GetLastGrad() const { return grad; } + const DblVec& GetLastDir() const { return dir; } + double GetValue() const { return value; } + int GetIter() const { return iter; } + size_t GetDim() const { return dim; } +}; diff --git a/second-stage/programs/wlle/TerminationCriterion.cpp b/second-stage/programs/wlle/TerminationCriterion.cpp new file mode 100644 index 0000000..a0e8488 --- /dev/null +++ b/second-stage/programs/wlle/TerminationCriterion.cpp @@ -0,0 +1,28 @@ +#include "TerminationCriterion.h" + +#include "OWLQN.h" + +#include +#include +#include + +using namespace std; + +double RelativeMeanImprovementCriterion::GetValue(const OptimizerState& state, std::ostream& message) { + double retVal = numeric_limits::infinity(); + + if (prevVals.size() > 5) { + double prevVal = prevVals.front(); + if (prevVals.size() == 10) prevVals.pop_front(); + double averageImprovement = (prevVal - state.GetValue()) / prevVals.size(); + double relAvgImpr = averageImprovement /(1+fabs(state.GetValue())); // !!!! modified MJ, 26th Aug 2008 + message << setprecision(4) << scientific << right; + message << " (" << setw(10) << relAvgImpr << ") " << flush; + retVal = relAvgImpr; + } else { + message << " (wait for five iters) " << flush; + } + + prevVals.push_back(state.GetValue()); + return retVal; +} diff --git a/second-stage/programs/wlle/TerminationCriterion.h b/second-stage/programs/wlle/TerminationCriterion.h new file mode 100644 index 0000000..3059972 --- /dev/null +++ b/second-stage/programs/wlle/TerminationCriterion.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +class OptimizerState; + +struct TerminationCriterion { + virtual double GetValue(const OptimizerState& state, std::ostream& message) = 0; +}; + +class RelativeMeanImprovementCriterion : public TerminationCriterion { + const int numItersToAvg; + std::deque prevVals; + +public: + RelativeMeanImprovementCriterion(int numItersToAvg = 5) : numItersToAvg(numItersToAvg) {} + + double GetValue(const OptimizerState& state, std::ostream& message); +}; diff --git a/second-stage/programs/wlle/cvlm-owlqn b/second-stage/programs/wlle/cvlm-owlqn new file mode 100755 index 0000000000000000000000000000000000000000..ec7c64892c5c3a1aaf1a916fd85a3b2edf92560a GIT binary patch literal 120343 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 literal 0 HcmV?d00001 diff --git a/second-stage/programs/wlle/cvlm-owlqn.cc b/second-stage/programs/wlle/cvlm-owlqn.cc new file mode 100644 index 0000000..d8c37d8 --- /dev/null +++ b/second-stage/programs/wlle/cvlm-owlqn.cc @@ -0,0 +1,758 @@ +// cvlm-owlqn.cc -- A linear model estimator for a variety of user-selectable loss functions. + +const char usage[] = +"cvlm-owlqn -- A cross-validating linear model estimator for a variety of user-selectable loss functions.\n" +"\n" +" Mark Johnson, 21st July 2008\n" +"\n" +" It uses a modified version of L-BFGS developed by Galen Andrew at Microsoft Research\n" +" to be especially efficient for L1-regularized loss functions.\n" +"\n" +" The regularizer weight(s) are set by cross-validation on development data.\n" +"\n" +"Usage: cvlm-owlqn [-h] [-d debug_level] [-c c0] [-C c00] [-p p] [-r r] [-s s] [-t tol]\n" +" [-l ltype] [-F f] [-G] [-n ns] [-f feat-file]\n" +" [-o weights-file] [-e eval-file] [-x eval-file2]\n" +" < train-file\n" +"\n" +"where:\n" +"\n" +" debug_level > 0 controls the amount of output produced\n" +"\n" +" -c c0 is the initial value for the regularizer constant.\n" +"\n" +" -C c00 multiplies the regularizer constant for the first feature class\n" +" by c00 (this can be used to allow the first feature class to be regularized less).\n" +"\n" +" -l ltype identifies the type of loss function used:\n" +"\n" +" -l 0 - log loss (c0 ~ 5)\n" +" -l 1 - EM-style log loss (c0 ~ 5)\n" +" -l 2 - pairwise log loss \n" +" -l 3 - exp loss (c0 ~ 25, s ~ 1e-5)\n" +" -l 4 - log exp loss (c0 ~ 1e-4)\n" +" -l 5 - maximize expected F-score (c ~ ?)\n" +"\n" +" -r r specifies that the weights are initialized to random values in\n" +" [-r ... +r],\n" +"\n" +" -t tol specifies the stopping tolerance for the IWLQN optimizer\n" +"\n" +" -F f indicates that a parse should be taken as correct\n" +" proportional to f raised to its f-score, and\n" +"\n" +" -G indicates that each sentence is weighted by the number of\n" +" edges in its gold parse.\n" +"\n" +" -n ns is the maximum number of ':' characters in a , used to\n" +" determine how features are binned into feature classes (ns = -1 bins\n" +" all features into the same class)\n" +"\n" +" -f feat-file is a file of lines, used for\n" +" cross-validating regularizer weights,\n" +"\n" +" train-file, eval-file and eval-file2 are files from which training and evaluation\n" +" data are read (if eval-file ends in the suffix .bz2 then bzcat is used\n" +" to read it; if no eval-file is specified, then the program tests on the\n" +" training data),\n" +"\n" +" weights-file is a file to which the estimated weights are written,\n" +"\n" +"The function that the program minimizes is:\n" +"\n" +" Q(w) = s * (- L(w) + c * sum_j pow(fabs(w[j]), p) ), where:\n" +"\n" +" L(w) is the loss function to be optimized.\n" +"\n" +"With debug = 0, the program writes a single line to stdout:\n" +"\n" +"c p r s it nzeroweights/nweights neglogP/nsentence ncorrect/nsentences\n" +"\n" +"With debug >= 10, the program writes out a histogram of weights as well\n" +"\n" +"Data format:\n" +"-----------\n" +"\n" +" --> [S=] *\n" +" --> [G=] N= *\n" +" --> [P=

] [W=] *,\n" +" --> [=]\n" +"\n" +"NS is the number of sentences.\n" +"\n" +"Each consists of N s. is the gold standard\n" +"score. To get parsing precision and recall results, set to the\n" +"number of edges in the gold standard parse. To get accuracy results,\n" +"set to 1 (the default).\n" +"\n" +"A consists of pairs.

is the parse's possible highest\n" +"score and is the parse's actual score. To get parsing precision and\n" +"recall results, set

to the number of edges in the parse and to\n" +"the number of edges in common between the gold and parse trees.\n" +"\n" +"A consists of a feature (a non-negative integer) and an optional\n" +"count (a real).\n" +"\n" +"The default for all numbers except is 1. The default for is 0.\n"; + +#include "custom_allocator.h" // must come first +#define _GLIBCPP_CONCEPT_CHECKS // uncomment this for checking + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lmdata.h" +#include "powell.h" +#include "utility.h" + +#include "OWLQN.h" +#include "TerminationCriterion.h" + +typedef std::vector doubles; +typedef std::vector size_ts; + +int debug_level = 0; + +enum loss_type { log_loss, em_log_loss, em_log_loss_noomp, pairwise_log_loss, exp_loss, log_exp_loss, + expected_fscore_loss }; + +const char* loss_type_name[] = { "log_loss", "em_log_loss", "em_log_loss_noomp", "pairwise_log_loss", "exp_loss", + "log_exp_loss", "expected_fscore_loss" }; + +void print_histogram(int nx, const double x[], int nbins=20) { + int nx_nonzero = 0; + for (int i = 0; i < nx; ++i) + if (x[i] != 0) + ++nx_nonzero; + + std::cout << "# There are " << nx_nonzero << " non-zero values and " + << nx-nx_nonzero << " zero values." << std::endl; + + if (nx_nonzero > 0) { + std::vector s; + s.reserve(nx_nonzero); + for (int i = 0; i < nx; ++i) + if (x[i] != 0) + s.push_back(x[i]); + std::sort(s.begin(), s.end()); + for (int i = 0; i <= nbins; ++i) { + int j = i*(nx_nonzero-1); + j /= nbins; + std::cout << float(i)/float(nbins) << '\t' << s[j] << std::endl; + } + } +} // print_histogram() + +// f_df() evaluates the statistics of the corpus, and prints the f-score +// if required +// +double f_df(loss_type ltype, corpus_type* corpus, const double x[], double df_dx[]) { + Float sum_g = 0, sum_p = 0, sum_w = 0, L = 0; + + switch (ltype) { + case log_loss: + L = corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case em_log_loss: + L = emll_corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case em_log_loss_noomp: + L = emll_corpus_stats_noomp(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case pairwise_log_loss: + L = pwlog_corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case exp_loss: + L = exp_corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case log_exp_loss: + L = log_exp_corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + break; + case expected_fscore_loss: + L = 1 - fscore_corpus_stats(corpus, &x[0], &df_dx[0], &sum_g, &sum_p, &sum_w); + for (size_type j = 0; j < corpus->nfeatures; ++j) + df_dx[j] = -df_dx[j]; + break; + default: + L = 0; + std::cerr << "## Error: unrecognized loss_type loss = " << int(ltype) + << std::endl; + } + + if (debug_level >= 1000) + std::cerr << "f score = " << 2*sum_w/(sum_g+sum_p) << ", " << std::flush; + + assert(finite(L)); + return L; +} + +// LossFn() is the loss function +// +class LossFn : public DifferentiableFunction { +public: + + const loss_type ltype; + corpus_type* corpus; + const size_ts& f_c; //!< feature -> cross-validation class + const doubles& cs; //!< cross-validation class -> regularizer factor + double p, s; + int it; + double L, R, Q; + + LossFn(loss_type ltype, corpus_type* corpus, const size_ts& f_c, + const doubles& cs, double p, double s) + : ltype(ltype), corpus(corpus), f_c(f_c), cs(cs), p(p), s(s), it(0) + { + assert(f_c.size() == corpus->nfeatures); + for (size_type f = 0; f < f_c.size(); ++f) + assert(f_c[f] < cs.size()); + } + + virtual double Eval(const DblVec& x, DblVec& df_dx) { + size_t nx = x.size(); + + assert(size_t(nx) == corpus->nfeatures); + assert(f_c.size() == corpus->nfeatures); + + it++; + if (debug_level >= 1000) + std::cerr << "it = " << it << ", " << std::flush; + + L = f_df(ltype, corpus, &x[0], &df_dx[0]); + + if (s != 1) { + L *= s; + + for (size_t i = 0; i < nx; ++i) { + assert(finite(df_dx[i])); + df_dx[i] *= s; + } + } + + R = 0; + if (p != 1) { + for (size_t i = 0; i < nx; ++i) + R += cs[f_c[i]] * pow(fabs(x[i]), p); + R *= s; + double sp = s * p; + for (size_t i = 0; i < nx; ++i) + df_dx[i] += sp * cs[f_c[i]] * pow(fabs(x[i]), p-1) + * (x[i] >= 0 ? (x[i] == 0 ? 0 : 1) : -1); + } + + Q = L + R; + + if (debug_level >= 1000) + std::cerr << "Q = " << Q << " = L = " << L << " + R = " << R << std::endl; + + assert(finite(Q)); + + if (debug_level >= 10000) { + std::cerr << "Histogram of derivatives:" << std::endl; + print_histogram(nx, &df_dx[0]); + std::cerr << "--------------------------" << std::endl; + } + + for (size_t i = 0; i < nx; ++i) + assert(finite(df_dx[i])); + return Q; + } // LossFn::operator() + +}; // LossFn{} + +// The Estimator1 does one round of estimation +// +struct Estimator1 { + typedef std::vector size_ts; + typedef std::vector doubles; + + corpus_type* train; //!< training data + size_type nx; //!< number of features + corpus_type* eval; //!< evaluation data + corpus_type* eval2; //!< 2nd evaluation data + loss_type ltype; //!< type of loss function + double c0; //!< default regularizer factor + double c00; //!< multiply default regularizer factor for first feature class + double p; //!< regularizer power + double r; //!< random initialization + double s; //!< scale factor + double tol; //!< stopping tolerance + bool opt_fscore; //!< optimize f-score or - log likelihood + + doubles x; //!< feature -> weight + size_ts f_c; //!< feature -> cross-validation class + doubles lcs; //!< cross-validation class -> log factor + size_type nc; //!< number of cross-validation classes + + size_type nits; //!< number of iterations of last round + size_type sum_nits; //!< total number of iterations + size_type nrounds; //!< number of cross-validation rounds so far + double best_score; //!< best score seen so far + std::string weightsfile; //!< name of weights file + + typedef std::map S_C; + S_C identifier_regclass; //!< map from feature class identifiers to regularization class + typedef std::vector Ss; + Ss regclass_identifiers; //!< vector of class identifiers + + Estimator1(loss_type ltype, double c0, double c00, double p, double r, double s, + double tol=1e-5, bool opt_fscore = true, std::string weightsfile = "") + : train(NULL), nx(0), eval(NULL), eval2(NULL), + ltype(ltype), c0(c0), c00(c00), p(p), r(r), s(s), tol(tol), + opt_fscore(opt_fscore), lcs(1, log(c0)), nc(1), + nits(0), sum_nits(0), nrounds(0), best_score(0), + weightsfile(weightsfile) + { } // Estimator1::Estimator1() + + //! set_data() sets the training and evaluation data + // + void set_data(corpus_type* t, corpus_type* e, corpus_type* e2) { + train = t; + eval = e; + eval2 = e2; + nx = train->nfeatures; + x.resize(nx); + assert(f_c.size() <= nx); + assert(eval == NULL || eval->nfeatures <= train->nfeatures); + assert(eval2 == NULL || eval2->nfeatures <= train->nfeatures); + } // Estimator1::set_data() + + // operator() actually runs one round of estimation + // + double operator() (const doubles& lccs) { + assert(lccs.size() == nc); + doubles ccs(nc); + double L, R, Q; + + for (size_type i = 0; i < nc; ++i) + ccs[i] = exp(lccs[i]); + + assert(x.size() == nx); + nits = 0; + nrounds++; + + if (debug_level >= 10) { + if (nrounds == 1) + std::cerr << "# round nfeval L R Q neglogP f-score css" << std::endl; + std::cerr << nrounds << std::flush; + } + + LossFn fn(ltype, train, f_c, ccs, p, s); + + DblVec x0(nx); + + if (r != 0) + for (size_type i = 0; i < nx; ++i) + x0[i] = r*double(random()-RAND_MAX/2)/double(RAND_MAX/2); + + OWLQN owlqn(true); + if (p == 1) { + assert(ccs.size() == 1); + owlqn.Minimize(fn, x0, x, ccs[0], tol); + } + else + owlqn.Minimize(fn, x0, x, 0, tol); + + nits = fn.it; + L = fn.L; + R = fn.R; + Q = fn.Q; + + // Clean up, collect stats + + sum_nits += nits; + + if (debug_level >= 10) + std::cerr << '\t' << nits << '\t' << L << '\t' << R << '\t' << Q; + + double score = evaluate(opt_fscore, true); + + if (debug_level >= 10) + std::cerr << '\t' << ccs << std::endl; + + return score; + + } // Estimator1::operator() + + // evaluate() evaluates the current model on the eval data, prints + // out debugging information if appropriate, and returns either + // the - log likelihood or 1 - f-score. + // + double evaluate(bool opt_fscore = false, bool internal = false) { + + std::vector df_dx(nx); + Float sum_g = 0, sum_p = 0, sum_w = 0; + Float neglogP = corpus_stats(eval, &x[0], &df_dx[0], + &sum_g, &sum_p, &sum_w); + Float fscore = 2*sum_w/(sum_g+sum_p); + + if (internal) { // internal evaluation, use a short print-out + if (debug_level >= 10) { + std::cerr << '\t' << neglogP << '\t' << fscore; + if (eval2 != NULL) { + Float sum_g2 = 0, sum_p2 = 0, sum_w2 = 0; + Float neglogP2 = corpus_stats(eval2, &x[0], &df_dx[0], + &sum_g2, &sum_p2, &sum_w2); + Float fscore2 = 2*sum_w2/(sum_g2+sum_p2); + std::cerr << '\t' << neglogP2 << '\t' << fscore2; + } + } + } + else { // final evaluation, print out more info + + int nzeros = 0; + for (size_type i = 0; i < nx; ++i) + if (x[i] == 0) + ++nzeros; + + std::cerr << "# Regularizer power p = " << p << std::endl; + std::cerr << "# " << nx-nzeros << " non-zero feature weights of " + << nx << " features." << std::endl; + std::cerr << "# Eval neglogP = " << neglogP + << ", neglogP/nsentences = " << neglogP/eval->nsentences + << std::endl; + std::cerr << "# Eval precision = " << sum_w/sum_p + << ", recall = " << sum_w/sum_g + << ", f-score = " << 2*sum_w/(sum_g+sum_p) + << std::endl; + if (eval2 != NULL) { + Float sum_g = 0, sum_p = 0, sum_w = 0; + Float neglogP = corpus_stats(eval2, &x[0], &df_dx[0], + &sum_g, &sum_p, &sum_w); + std::cerr << "# Eval2 neglogP = " << neglogP + << ", neglogP/nsentences = " << neglogP/train->nsentences + << std::endl; + std::cerr << "# Eval2 precision = " << sum_w/sum_p + << ", recall = " << sum_w/sum_g + << ", f-score = " << 2*sum_w/(sum_g+sum_p) + << std::endl; + } + { + Float sum_g = 0, sum_p = 0, sum_w = 0; + Float neglogP = corpus_stats(train, &x[0], &df_dx[0], + &sum_g, &sum_p, &sum_w); + std::cerr << "# Train neglogP = " << neglogP + << ", neglogP/nsentences = " << neglogP/train->nsentences + << std::endl; + std::cerr << "# Train precision = " << sum_w/sum_p + << ", recall = " << sum_w/sum_g + << ", f-score = " << 2*sum_w/(sum_g+sum_p) + << std::endl; + } + + std::cerr << "# regclass_identifiers = " << regclass_identifiers << std::endl; + std::cerr << "# lcs = " << lcs << std::endl; + { + doubles cs(nc); + for (size_type i = 0; i < nc; ++i) + cs[i] = exp(lcs[i]); + std::cerr << "# cs = " << cs << std::endl; + } + if (debug_level >= 100) { + std::cerr << "# Cumulative distribution of feature weights:" << std::endl; + print_histogram(nx, &x[0]); + } + } + + double score = (opt_fscore ? 1 - fscore : neglogP); + + if (nrounds == 1 || score < best_score) { + best_score = score; + + // Write out weights file + + if (!weightsfile.empty()) { + FILE* out = fopen(weightsfile.c_str(), "w"); + // fprintf(out, "%d@", nx-nzeros); + for (size_type i = 0; i < x.size(); ++i) + if (x[i] != 0) { + fprintf(out, "%d", i); + if (x[i] != 1) + fprintf(out, "=%g", x[i]); + fprintf(out, "\n"); + } + fclose(out); + } + } + return score; + } // Estimator1::evaluate() + + //! fc_bin() maps a feature count to its corresponding bin + // + static int fc_bin(double feature_count_base, int feature_count) { + if (feature_count <= 4) + return feature_count; + else + return lrint(4.0 + pow(feature_count_base, + lrint(log(feature_count-4)/log(feature_count_base)))); + } // Estimator1::fc_bin() + + // read_featureclasses() reads the feature classes from a feature file + // + void read_featureclasses(const char* filename, + int nseparators = 1, + const char* separators = ":") { + + const char* filesuffix = strrchr(filename, '.'); + bool popen_flag = false; + FILE *in; + if (strcasecmp(filesuffix, ".bz2") == 0) { + std::string command("bzcat "); + command += filename; + in = popen(command.c_str(), "r"); + if (in == NULL) { + perror("## Error in cvlm-owlqn: "); + std::cerr << "## popen(\"" << command << "\", \"r\") failed, usage = " << resource_usage() << std::endl; + } + popen_flag = true; + } + else if (strcasecmp(filesuffix, ".gz") == 0) { + std::string command("zcat "); + command += filename; + errno = 0; + in = popen(command.c_str(), "r"); + if (in == NULL) { + perror("## Error in cvlm-owlqn: "); + std::cerr << "## popen(\"" << command << "\", \"r\") failed, usage = " << resource_usage() << std::endl; + } + popen_flag = true; + } + else + in = fopen(filename, "r"); + if (in == NULL) { + std::cerr << "## Couldn't open evalfile " << filename + << ", errno = " << errno << "\n" + << usage << std::endl; + exit(EXIT_FAILURE); + } + + size_type featno; + + // read feature number + + while (fscanf(in, " %u ", &featno) == 1) { + int c = ':'; + + // read the prefix of the feature class identifier + + std::string identifier; + int iseparators = 0; + if (nseparators >= 0) + while ((c = getc(in)) != EOF && !isspace(c)) { + if (index(separators, c) != NULL) + if (++iseparators > nseparators) + break; + identifier.push_back(c); + } + + // skip the rest of the line + + while ((c = getc(in)) != EOF && c != '\n') + ; + + // insert the prefix into the prefix -> regularization class map + + S_C::iterator it + = identifier_regclass.insert(S_C::value_type(identifier, + identifier_regclass.size())).first; + + size_type cl = it->second; // regularization class + + f_c.resize(featno+1); + f_c[featno] = cl; // set feature's regularization class + } + + nc = identifier_regclass.size(); // set nc + lcs.resize(nc, log(c0)); // set each regularizer class' factor to c0 + lcs[0] += log(c00); // increment first regularizer class' factor by c00 + + // construct regclass_identifiers + + regclass_identifiers.resize(nc); + cforeach (S_C, it, identifier_regclass) { + assert(it->second < regclass_identifiers.size()); + regclass_identifiers[it->second] = it->first; + } + + if (debug_level >= 0) + std::cerr << "# Regularization classes: " << regclass_identifiers << std::endl; + + if (popen_flag) + pclose(in); + else + fclose(in); + } // Estimator1::read_featureclasses() + + void estimate(const int maxruns=10) + { + powell::control cntrl(1e-4, 1e-2, maxruns, maxruns); + powell::minimize(lcs, *this, log(2), cntrl); + + if (debug_level > 0) { + std::cerr << "# Regularizer class weights = ("; + for (size_type i = 0; i < lcs.size(); ++i) { + if (i > 0) + std::cerr << ' '; + std::cerr << exp(lcs[i]); + } + std::cerr << ')' << std::endl; + } + + } // Estimator1::estimate() + +}; // Estimator1{} + +//! exit_failure() causes the program to halt immediately +// +inline std::ostream& exit_failure(std::ostream& os) { + os << std::endl; + exit(EXIT_FAILURE); + return os; +} // util::exit_failure + +int main(int argc, char** argv) +{ + std::ios::sync_with_stdio(false); + + loss_type ltype = log_loss; + double c0 = 2.0; + double c00 = 1.0; + double p = 2.0; + double r = 0.0; + double s = 1.0; + double tol = 1e-5; + double Pyx_factor = 0.0; + bool Px_propto_g = false; + int maxruns = 10; + int nseparators = 1; + std::string feat_file, weights_file, eval_file, eval2_file; + int opt; + while ((opt = getopt(argc, argv, "hd:c:C:p:r:s:t:l:F:Gm:n:f:o:e:x:")) != -1) + switch (opt) { + case 'h': + std::cerr << usage << exit_failure; + break; + case 'd': + debug_level = atoi(optarg); + break; + case 'c': + c0 = atof(optarg); + break; + case 'C': + c00 = atof(optarg); + break; + case 'p': + p = atof(optarg); + break; + case 'r': + r = atof(optarg); + break; + case 's': + s = atof(optarg); + break; + case 't': + tol = atof(optarg); + break; + case 'l': + ltype = loss_type(atoi(optarg)); + break; + case 'F': + Pyx_factor = atof(optarg); + break; + case 'G': + Px_propto_g = true; + break; + case 'm': + maxruns = atoi(optarg); + break; + case 'n': + nseparators = atoi(optarg); + break; + case 'f': + feat_file = optarg; + break; + case 'o': + weights_file = optarg; + break; + case 'e': + eval_file = optarg; + break; + case 'x': + eval2_file = optarg; + break; + } + + if (debug_level >= 10) + std::cerr << "# ltype -l = " << ltype + << " (" << loss_type_name[ltype] << ")" + << " regularization -c = " << c0 + << ", c00 -C = " << c00 + << ", power -p = " << p + << ", scale -s = " << s + << ", tol -t = " << tol + << ", random init -r = " << r + << ", Pyx_factor -F = " << Pyx_factor + << ", Px_propto_g -G = " << Px_propto_g + << ", maxruns -m = " << maxruns + << ", nseparators -n = " << nseparators + << ", feat_file -f = " << feat_file + << ", weights_file -o = " << weights_file + << ", eval_file -e = " << eval_file + << ", eval2_file -x = " << eval2_file + << std::endl; + + // I discovered a couple of years after I wrote this program that popen + // uses fork, which doubles your virtual memory for a short instant! + + Estimator1 e(ltype, c0, c00, p, r, s, tol, true, weights_file); + + if (!feat_file.empty()) + e.read_featureclasses(feat_file.c_str(), nseparators, ":"); + + // Read in eval data first, as that way we may squeeze everything into 4GB + + corpusflags_type corpusflags = { Pyx_factor, Px_propto_g }; + + corpus_type* evaldata = NULL; + if (!eval_file.empty()) { + evaldata = read_corpus_file(&corpusflags, eval_file.c_str()); + if (debug_level >= 10) + std::cerr << "# read eval_file = " << eval_file + << ", nsentences = " << evaldata->nsentences + << std::endl; + } + + corpus_type* evaldata2 = NULL; + if (!eval2_file.empty()) { + evaldata2 = read_corpus_file(&corpusflags, eval2_file.c_str()); + if (debug_level >= 10) + std::cerr << "# read eval2_file = " << eval2_file + << ", nsentences = " << evaldata2->nsentences + << std::endl; + } + + corpus_type* traindata = read_corpus(&corpusflags, stdin); + int nx = traindata->nfeatures; + + if (errno != 0) { + perror("## cvlm-owlqn, after reading main corpus, nonzero errno "); + errno = 0; + } + + std::cerr << "# " << nx << " features in training data, " << resource_usage() << std::endl; + + if (evaldata == NULL) + evaldata = traindata; + + e.set_data(traindata, evaldata, evaldata2); + e.estimate(maxruns); + +} // main() diff --git a/second-stage/programs/wlle/powell.h b/second-stage/programs/wlle/powell.h new file mode 100644 index 0000000..0f80e21 --- /dev/null +++ b/second-stage/programs/wlle/powell.h @@ -0,0 +1,575 @@ +//! powell.h +//! +//! Powell's algorithm for minimizing an n-dimensional function +//! without derivatives, based on Numerical Recipes in C++ code. +//! +//! Mark Johnson, 29th September 2002, last modified 14th August 2008 +//! +//! The main routine is powell::minimize(). Its calling sequence +//! is: +//! +//! Float minimize( // => value of f at minimum +//! p, // <= starting x value, => minimizing x value +//! f, // <= function object being minimized +//! initial_step, // <= initial step length +//! cntrl) // <= control{} structure +//! +//! +//! There is also a specialized version for 1-d minimization +//! +//! Float minimize1d( // => minimizing x value +//! p, // <= starting x value +//! f, // <= function object being minimized +//! initial_step, // <= initial step length +//! cntrl) // <= control{} structure + +#ifndef POWELL_H +#define POWELL_H + +#include +#include +#include +#include +#include +#include +#include + +namespace powell { + + typedef double Float; + typedef std::vector Floats; + typedef std::vector Array; + + template + inline void error_abort(const T& t) { + std::cerr << "Error in powell.h: " << t << std::endl; + abort(); + } + + //! The control class gives the user a way to monitor and + //! control the minimization process. You can specialize its + //! behaviour by deriving your own monitor from this class + //! and changing its behaviour. + // + class control { + + private: + Float tol; //!< Stopping function tolerance + Float linmin_tol; //!< Line minimization stopping tolerance + int max_nfeval; //!< Max number of function evaluations + int linmin_max_nfeval_; //!< Max number of function evaluations in brent() + int debug; //!< controls tracing, etc + + public: + Float linmin_rel_tol() const { return linmin_tol; } + Float linmin_abs_tol() const { return linmin_tol; } + int linmin_max_nfeval() const { return linmin_max_nfeval_; } + Float linmin_xinit() const { return 1.0; } + size_t cache_size() const { return 20; } //!< function cache size + + // control() sets the defaults for the program + // + control(Float tol = 1e-7, //!< stopping tolerance + Float linmin_tol = 1e-7, //!< line minimization tolerance + int max_nfeval = 0, //!< max number of fn evalns, + //!< if neg, halt w/ error + int linmin_max_nfeval_ = 0, //!< max number of fn evalns + //!< per line minimization + size_t debug = 0) + : tol(tol), + linmin_tol(linmin_tol), + max_nfeval(max_nfeval), + linmin_max_nfeval_(linmin_max_nfeval_), + debug(debug) + { } // control() + + //! operator() is called at each powell iteration + //! If operator() returns true, the powell routine halts. + //! Specialize its behaviour if you want custom tracing + // + bool operator() (const Floats& ps, + const Float fx, const Float fx_last, + const int iteration, + const int nfeval) const + { + const Float TINY = 1.0e-25; + if (debug >= 1000) { // print tracing information + if (iteration == 1) + std::cout << "#" << std::setw(7) << "iter" << std::setw(10) << "f(x)" + << std::setw(10) << "nfeval" << std::endl; + std::cout << std::setw(8) << iteration << ' ' << std::setw(9) << fx + << ' ' << std::setw(9) << nfeval << std::endl; + + if (debug >= 100000) { + std::cout << " x = (" << ps[0]; + for (size_t j = 1; j < ps.size(); ++j) + std::cerr << ' ' << ps[j]; + std::cerr << ')' << std::endl; + } + } + + if (fx_last < fx) + std::cerr << " *** Error in powell::powell() iteration " << iteration + << ", fx = " << fx << " > fx_last = " << fx_last << std::endl; + + // This is the termination criterion used by Numerical Recipies + // + if (2.0*fabs(fx-fx_last) <= tol*(fabs(fx_last)+fabs(fx))+TINY) + return true; + + if (max_nfeval != 0 && nfeval >= abs(max_nfeval)) { + if (max_nfeval > 0) { + if (debug >= 100) + std::cerr << "*** powell::powell() returning at iteration " << iteration + << " in with error " << fx_last-fx + << std::endl; + return true; + } + else + error_abort("Too many iterations in powell::powell"); + } + + return false; + } // operator() + }; // control{} + + + template + inline T SQR(T x) { return x*x; } + + inline void shft3(Float& a, Float& b, Float& c, const Float& d) + { + a=b; + b=c; + c=d; + } + + inline Float sign(Float a, Float b) { + return ((b) > 0.0 ? fabs(a) : -fabs(a)); + } + + inline void shift(Float& a, Float& b, Float& c, Float d) { + a = b; b = c; c = d; + } + + //! mnbrak() + //! + //! Given a function f, and given distinct initial points ax and bx, + //! this routine searches in the downhill direction (defined by the + //! function as evaluated at the initial points) and returns new points + //! ax, bx, cx that bracket a minimum of the function. Also returned + //! are the function values at the three points, fa, fb, and fc. + //! + //! The arguments to f() are: + //! x <= point at which to evaluate f + //! df => derivative of f at x + //! f() => value of f at x + //! + //! Here GOLD is the default ratio by which successive intervals are + //! magnified; GLIMIT is the maximum magnification allowed for a + //! parabolic-fit step. + // + template + void mnbrak(Float& ax, Float& bx, Float& cx, + Float& fa, Float& fb, Float& fc, + F& f) + { + const float GOLD = 1.618034; + const float GLIMIT = 100.0; + const double TINY = 1.0e-20; + + Float ulim, u, r, q, fu; + fa = f(ax); + fb = f(bx); + if (fb > fa) { + // Switch roles of a and b so that we can go downhill in the + // direction from a to b. + std::swap(ax, bx); + std::swap(fa, fb); + } + cx = bx+GOLD*(bx-ax); // First guess for c. + fc = f(cx); + while (fb > fc) { // Keep returning here until we bracket. + r = (bx-ax)*(fb- fc); + // Compute u by parabolic extrapolation from a; b; c. + // TINY is used to prevent any possible division by zero. + q = (bx-cx)*(fb-fa); + u = bx-((bx-cx)*q-(bx-ax)*r)/(2.0*sign(std::max(fabs(q-r),TINY),q-r)); + ulim = bx+GLIMIT*(cx-bx); + // We won't go farther than this. Test various possibilities: + if ((bx-u)*(u-cx) > 0.0) { + // Parabolic u is between b and c: try it. + fu = f(u); + if (fu < fc) { + // Got a minimum between b and c. + ax = bx; + bx = u; + fa = fb; + fb = fu; + return; + } + else if (fu > fb) { + // Got a minimum between between a and u. */ + cx = u; + fc = fu; + return; + } + u = cx+GOLD*(cx-bx); + // Parabolic fit was no use. Use default magnification. + fu = f(u); + } + else if ((cx-u)*(u-ulim) > 0.0) { + // Parabolic fit is between c and its allowed limit. + fu = f(u); + if (fu < fc) { + bx = cx; cx = u; u = cx+GOLD*(cx-bx); + fb = fc; fc = fu; fu = f(u); + } + } + else if ((u-ulim)*(ulim-cx) >= 0.0) { + // Limit parabolic u to maximum allowed value. + u = ulim; + fu = f(u); + } + else { + // Reject parabolic u, use default magnification. + u = cx+GOLD*(cx-bx); + fu = f(u); + } + // Eliminate oldest point and continue. + ax = bx; bx = cx; cx = u; + fa = fb; fb = fc; fc = fu; + } + } // mnbrak() + + //! brent() was modified so that fxmin is the smallest f() seen so far, + //! and xmin is the value of x at which fxmin was encountered. + // + template + Float brent(const Float ax, const Float bx, const Float cx, F& f, + C& control, Float &xmin) + { + const Float CGOLD = 0.3819660; // Golden ratio + Float a, b, d=0.0, etemp, fu, fv, fw, fx; + Float p, q, r, u, v, w, x, xm; + Float e=0.0; + Float fxmin; + + a = (ax < cx ? ax : cx); + b = (ax > cx ? ax : cx); + fw = fv = fx = f(x = w = v = bx); + xmin = x; + fxmin = fx; + size_t max_nfeval = f.nfeval() + control.linmin_max_nfeval(); + for (size_t iter = 0; iter < 200; iter++) { + xm = 0.5*(a+b); + Float tol1 = control.linmin_rel_tol() * fabs(x) + control.linmin_abs_tol(); + Float tol2 = 2.0*tol1; + if (fabs(x-xm) <= (tol2-0.5*(b-a))) { + // xmin = x; + // return fx; + return fxmin; + } + if (control.linmin_max_nfeval() != 0 && f.nfeval() >= max_nfeval) { + // xmin = x; + // return fx; + return fxmin; + } + if (fabs(e) > tol1) { + r = (x-w)*(fx-fv); + q = (x-v)*(fx-fw); + p = (x-v)*q-(x-w)*r; + q = 2.0*(q-r); + if (q > 0.0) + p = -p; + q = fabs(q); + etemp=e; + e=d; + if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x)) + d=CGOLD*(e=(x >= xm ? a-x : b-x)); + else { + d=p/q; + u=x+d; + if (u-a < tol2 || b-u < tol2) + d=sign(tol1,xm-x); + } + } else { + d=CGOLD*(e=(x >= xm ? a-x : b-x)); + } + u=(fabs(d) >= tol1 ? x+d : x+sign(tol1,d)); + fu=f(u); + if (fu < fxmin) { + fxmin = fu; + xmin = u; + } + if (fu <= fx) { + if (u >= x) a=x; else b=x; + shift(v, w, x, u); + shift(fv, fw, fx, fu); + } else { + if (u < x) a=u; else b=u; + if (fu <= fw || w == x) { + v=w; + w=u; + fv=fw; + fw=fu; + } else if (fu <= fv || v == x || v == w) { + v=u; + fv=fu; + } + } + } + // xmin = x; + // return fx; + return fxmin; + } // brent() + + template + struct f1cache { + const size_t cache_size; //!< number of function evaluations to cache + F& f; //!< original function + Floats xs; //!< cached x values + Floats fs; //!< cached f vector + size_t last; //!< last position inserted + size_t nfeval_; //!< number of function evaluations + + f1cache(F& f, size_t cache_size=2) + : cache_size(cache_size), f(f), last(cache_size-1), nfeval_(0) { } + + Float operator() (Float x) { + for (size_t i = 0; i < cache_size; ++i) { + if (i >= xs.size()) { + Float fx = f(x); + xs.push_back(x); + fs.push_back(fx); + ++nfeval_; + return fx; + } + if (x == xs[i]) + return fs[i]; + } + Float fx = f(x); + if (++last >= xs.size()) + last = 0; + xs[last] = x; + fs[last] = fx; + ++nfeval_; + return fx; + } // f1cache::operator() + + size_t nfeval() const { return nfeval_; } + + }; // f1cache{} + + //! minimize1d() is the 1d-minimization routine. It returns the minimizing x value. + // + template + Float minimize1d(Float p, F& f, Float initial_step = 1.0, Float tol=1e-7, int max_nfeval=100) + { + control c(tol, tol, max_nfeval, max_nfeval); + f1cache func(f, c.cache_size()); + Float a = p; + Float x = a + initial_step; + Float b, fa, fx, fb, xmin; + + mnbrak(a, x, b, fa, fx, fb, func); + brent(a, x, b, func, c, xmin); // brent() returns min_f, if you want it + return xmin; + } // minimize1d() + + //! linmin_f{} maps the n-dimensional function into a 1-d function + //! in direction p + // + template + struct linmin_f { /* structure to communicate with linmin */ + F& f; + Floats& p; + Floats& xi; + Floats xt; + + linmin_f(F& f, Floats& p, Floats& xi) + : f(f), p(p), xi(xi), xt(p.size()) { } + + Float operator() (Float x) { + const size_t n = p.size(); + for (size_t j = 0; j < n; ++j) + xt[j] = p[j] + x*xi[j]; + Float fx = f(xt); + return fx; + } + + size_t nfeval() const { return f.nfeval; } + + }; // linmin_f{} + + template + Float linmin(Floats &p, Floats &xi, F& f, C& control) + { + assert(p.size() == xi.size()); + linmin_f f1dim(f, p, xi); + + Float a = 0.0; + Float x = control.linmin_xinit(); + Float b, fa, fx, fb, xmin; + + mnbrak(a, x, b, fa, fx, fb, f1dim); + Float fret = brent(a, x, b, f1dim, control, xmin); + for (size_t j = 0; j < p.size(); j++) { + xi[j] *= xmin; + p[j] += xi[j]; + } + return fret; + } // linmin() + + template + struct fcache { + typedef std::vector Fss; + const size_t cache_size; //!< number of function evaluations to cache + F& f; //!< original function + Fss xs; //!< cached x vectors + Floats fs; //!< cached f vector + size_t last; //!< last position inserted + size_t nfeval; //!< number of function evaluations + + fcache(F& f, size_t cache_size=2) + : cache_size(cache_size), f(f), last(cache_size-1), nfeval(0) { } + + Float operator() (const Floats& x) { + for (size_t i = 0; i < cache_size; ++i) { + if (i >= xs.size()) { + Float fx = f(x); + xs.push_back(x); + fs.push_back(fx); + ++nfeval; + return fx; + } + if (x == xs[i]) + return fs[i]; + } + Float fx = f(x); + if (++last >= xs.size()) + last = 0; + xs[last] = x; + fs[last] = fx; + ++nfeval; + return fx; + } // fcache::operator() + + }; // fcache{} + + // Minimization of a function func of n variables. Input consists of an initial + // starting point p[1..n]; an initial matrix xi[1..n][1..n], whose columns contain + // the initial set of directions (usually the n unit vectors); and ftol, the + // fractional tolerance in the function value such that failure to decrease by + // more than this amount on one iteration signals doneness. On output, p is set + // to the best point found, xi is the then-current direction set, fret is the + // returned function value at p, and iter is the number of iterations taken. + // The routine linmin is used. + // + template + Float minimize_n(Floats& p, F& f, Float initial_step, Control& cntrl) + { + fcache func(f, cntrl.cache_size()); + size_t n = p.size(); + + Array xi(n); // array of conjugate directions + for (size_t i = 0; i < n; ++i) { // initialize to unit vectors + xi[i].resize(n); + xi[i][i] = initial_step; + } + + Float del,fp,fptt,t; + + Floats pt(p); // Save the initial point + Floats ptt(n), xit(n); + + Float fret = func(p); + + for (size_t iter = 1; ; ++iter) { + fp = fret; + size_t ibig=0; + del = 0.0; // Will be the biggest function decrease. + for (size_t i = 0; i < n; i++) { // In each iteration, loop over all directions. + for (size_t j = 0; j < n; j++) // Copy the direction, + xit[j] = xi[j][i]; + fptt = fret; + fret = linmin(p, xit, func, cntrl); // minimize along it, + if (fptt-fret > del) { // and record it if it is the largest decrease + del = fptt-fret; // so far. + ibig = i+1; + } + } + + if (cntrl(p, fret, fp, iter, func.nfeval)) + return fret; // Normal termination + + for (size_t j = 0; j < n; j++) { // Construct the extrapolated point and the + ptt[j] = 2.0*p[j]-pt[j]; // average direction moved. Save the + xit[j] = p[j]-pt[j]; // old starting point. + pt[j] = p[j]; + } + + fptt = func(ptt); // Function value at extrapolated point. + + if (fptt < fp) { + t=2.0*(fp-2.0*fret+fptt)*SQR(fp-fret-del)-del*SQR(fp-fptt); + if (t < 0.0) { + fret = linmin(p, xit, func, cntrl); + for (size_t j = 0; j < n; j++) { + xi[j][ibig-1] = xi[j][n-1]; + xi[j][n-1] = xit[j]; + } + } + } + } + } // minimize_n() + + //! minimize() calls minimize_n() if p.size() > 1, otherwise it does a one-dimensional + //! minimization. + // + template + Float minimize(Floats& p, F& f, Float initial_step, Control& control) { + assert(p.size() > 0); + if (p.size() > 1) + return minimize_n(p, f, initial_step, control); + else { // 1-d minimization + fcache func(f, control.cache_size()); + Floats xi(1, 1); + return linmin(p, xi, func, control); + } + } // minimize() + + //! minimize() is a wrapper that calls the main minimization routine with a default control. + // + template + Float minimize(Floats& p, F& f, Float initial_step = 1.0) + { + control c; + return minimize(p, f, initial_step, c); + } // minimize() + +} // namespace powell + +#endif // POWELL_H + +/* + +// Code for testing the 1-d minimization routines + +#include +#include "powell.h" + +struct f_type { + double operator() (double x) const { + double fx = sin(x); + std::cout << "x = " << x << ", fx = " << fx << std::endl; + return fx; + } +}; + +int main(int argc, char** argv) { + f_type f; + double xmin = powell::minimize1d(0, f, 0.1, 1e-5, 10); + std::cout << "xmin = " << xmin << std::endl; +} +*/ From b89428c78b4bc311200c39c174013430d24837ca Mon Sep 17 00:00:00 2001 From: Jim White Date: Fri, 11 Jul 2014 10:22:10 -0700 Subject: [PATCH 19/19] Remove binary that was inadvertently added. --- second-stage/programs/wlle/cvlm-owlqn | Bin 120343 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 second-stage/programs/wlle/cvlm-owlqn diff --git a/second-stage/programs/wlle/cvlm-owlqn b/second-stage/programs/wlle/cvlm-owlqn deleted file mode 100755 index ec7c64892c5c3a1aaf1a916fd85a3b2edf92560a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120343 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