373 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
%
 | 
						|
% generative learning in MLNs:
 | 
						|
%
 | 
						|
% maximise SUM log Pw(Xi=xi|mb(Xi))
 | 
						|
%
 | 
						|
% or sum N(X) - p(0|mb)n0 - P(1|mb)n1
 | 
						|
%
 | 
						|
% note that Pw(X|MB) = P(0|MB) || P(1|MB)
 | 
						|
%
 | 
						|
%
 | 
						|
 | 
						|
:- module(learn_mlns_generative,
 | 
						|
	[learn_mln_generative/0,
 | 
						|
	 portray_mln/0]).
 | 
						|
 | 
						|
:- use_module(library(lists)).
 | 
						|
 | 
						|
:- use_module(library(tries)).
 | 
						|
 | 
						|
:- use_module(library(maplist)).
 | 
						|
 | 
						|
:- use_module(library(nb)).
 | 
						|
 | 
						|
:- use_module(library(matrix)).
 | 
						|
 | 
						|
:- reexport(library(mlns)).
 | 
						|
 | 
						|
:- reexport(library(pfl)).
 | 
						|
 | 
						|
:- use_module(library(lbfgs)).
 | 
						|
 | 
						|
:-  yap_flag(tabling_mode,local).
 | 
						|
 | 
						|
:- dynamic diff/4, lit/1, i/2.
 | 
						|
 | 
						|
prior_means(_, 0.0).
 | 
						|
prior_dev(_, 1.0).
 | 
						|
 | 
						|
learn_mln_generative :-
 | 
						|
	compile,
 | 
						|
	optimize.	
 | 
						|
 | 
						|
set_weights :-
 | 
						|
	retract( mln:mln_w(Id, _) ),
 | 
						|
	optimizer_get_x( Id, W),
 | 
						|
	%writeln(weight:W),
 | 
						|
	assert( mln:mln_w(Id, W) ),
 | 
						|
	fail.
 | 
						|
set_weights.
 | 
						|
 | 
						|
adjust_lprior(Lik0, Lik) :-
 | 
						|
	Lik0 = Lik, !.
 | 
						|
adjust_lprior(Lik0, Lik) :-
 | 
						|
	findall(I-WI, mln_w(I,WI), WIs),
 | 
						|
	foldl(add_lprior, WIs, Lik0, Lik).
 | 
						|
	
 | 
						|
add_lprior(Id-WI, Lik0, Lik) :-
 | 
						|
	prior_means(Id, PM),
 | 
						|
	prior_dev(Id, PV),
 | 
						|
	Lik is Lik0 + ((WI-PM)*(WI-PM))/(2*PV*PV).
 | 
						|
 | 
						|
 | 
						|
likelihood(Lik) :-
 | 
						|
	S = s(0.0),
 | 
						|
%	nb_create_accumulator(0.0, Acc),
 | 
						|
	(
 | 
						|
	    recorded(i, [Ref|N], _),
 | 
						|
	    peval(Ref, LogP),
 | 
						|
	    %writeln(N*P),
 | 
						|
	    S = s(V),
 | 
						|
	    V1 is V+N*LogP,
 | 
						|
	    nb_setarg(1, S, V1),
 | 
						|
%	    nb_add_to_accumulator( Acc, LogP),
 | 
						|
	    fail
 | 
						|
        ;
 | 
						|
%	    nb_accumulator_value(Acc, Lik)
 | 
						|
	    S = s(Lik0),
 | 
						|
%writeln(lik:Lik0),
 | 
						|
	    adjust_lprior(Lik0, Lik1),
 | 
						|
	    Lik is -Lik1
 | 
						|
        ).
 | 
						|
 | 
						|
derive :-
 | 
						|
	nb_getval(i2, Mat),
 | 
						|
	nb_getval(d2, MatD),
 | 
						|
	matrix_set_all(MatD, 0.0),
 | 
						|
	recorded(i, [Ref|NI], _),
 | 
						|
	trie_get_entry(Ref, e(_, Ds, Ps)),
 | 
						|
	member(n(Id,Occs,DN0,DN1), Ds),
 | 
						|
	matrix_get(Mat, [Id], N),
 | 
						|
	matrix_get(MatD, [Id], V),
 | 
						|
	peval(Ps, P0, P1),
 | 
						|
	X is Occs*(N-P0*(N+DN0)-P1*(N+DN1)),
 | 
						|
%writeln(X is NI*(-P0*(DN0)-P1*(DN1))),
 | 
						|
	V1 is V-NI*X,
 | 
						|
	matrix_set(MatD, [Id], V1),
 | 
						|
	fail.
 | 
						|
derive :-
 | 
						|
	nb_getval(d2, MatD),
 | 
						|
	mln(Ms),
 | 
						|
	N1 is Ms-1,
 | 
						|
	between(0, N1, Id),
 | 
						|
	matrix_get(MatD, [Id], Sum),
 | 
						|
	%writeln(d:Id:Sum),
 | 
						|
	adjust_prior(Sum, Id, NSum),
 | 
						|
	optimizer_set_g(Id, NSum  ),
 | 
						|
	fail.
 | 
						|
derive.
 | 
						|
 | 
						|
adjust_prior(Lik0, _, Lik) :-
 | 
						|
	Lik0 = Lik, !.
 | 
						|
adjust_prior(Sum, Id, NSum) :-
 | 
						|
	mln_w(Id, Wi),
 | 
						|
	prior_means(Id, PM),
 | 
						|
	prior_dev(Id, PV),
 | 
						|
	NSum is Sum+(Wi-PM)/(PV*PV).
 | 
						|
 | 
						|
:- dynamic old_fx/1.
 | 
						|
 | 
						|
old_fx(+inf).
 | 
						|
 | 
						|
% This is the call back function which is invoked to report the progress
 | 
						|
% if the last argument is set to anything else than 0, the optimizer will
 | 
						|
% stop right now
 | 
						|
user:progress(FX,X_Norm,G_Norm,Step,_N,Iteration,Ls, Out) :-
 | 
						|
	( Iteration mod 100 =:= 0 -> atomic_concat([tmp_,Iteration,'.pfl'], File), open( File, write, S), portray_mln(S), close(S) ; true ),
 | 
						|
	retract(old_fx(FX0)),
 | 
						|
	( Delta is FX-FX0, abs(Delta/FX) < 0.00001 -> Out = 1 ; Out = 0),
 | 
						|
	optimizer_get_x(0,X0),
 | 
						|
	assert(old_fx(FX)),
 | 
						|
	format('/* ~d: w[0]=~10f  f(X)=~4f  |X|=~4f  |X\'|=~4f  Step=~4f  Ls=~4f */~n',[Iteration,X0,FX,X_Norm,G_Norm,Step,Ls]).
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 % This is the call back function which evaluates F and the gradient of F
 | 
						|
user:evaluate(FX,_N,_Step) :-
 | 
						|
	 set_weights,
 | 
						|
	 likelihood(FX),
 | 
						|
	 derive.
 | 
						|
 | 
						|
init_vars(Ev, Pr) :-
 | 
						|
	 mln(N),
 | 
						|
	 N1 is N-1,
 | 
						|
	 format('/* We start the search for ~d weights at weight[_]=0 */~2n',[N]),
 | 
						|
	 optimizer_initialize(N, Ev, Pr),
 | 
						|
	 between(0, N1, I),
 | 
						|
	 optimizer_set_x(I,0.0),
 | 
						|
	 fail.
 | 
						|
init_vars(_, _).
 | 
						|
 | 
						|
output_stat(BestF, Status) :-
 | 
						|
	 ( portray_mln,
 | 
						|
	   fail
 | 
						|
	 ;
 | 
						|
	   Lik is -BestF,
 | 
						|
	   format('/* Final likelihood=~f */~n/* LBFGS Status=~w */~n',[Lik,Status])
 | 
						|
	 ).
 | 
						|
 | 
						|
optimize :-
 | 
						|
	 init_vars(evaluate, progress),
 | 
						|
	 optimizer_run(BestF,Status),
 | 
						|
	 output_stat(BestF, Status),
 | 
						|
	 optimizer_finalize,
 | 
						|
	 format('~2nOptimization done~n',[]).	
 | 
						|
 | 
						|
compile :-
 | 
						|
	 init_compiler,
 | 
						|
	 compile_literals,
 | 
						|
	 fail.
 | 
						|
/*
 | 
						|
compile :-
 | 
						|
	recorded(i, [Ref|N], _),
 | 
						|
	trie_get_entry(Ref, E),
 | 
						|
	writeln(N:E),
 | 
						|
	fail.
 | 
						|
*/
 | 
						|
compile.
 | 
						|
 | 
						|
init_compiler :-
 | 
						|
	mln(HowMany),
 | 
						|
	D is HowMany+1,
 | 
						|
	matrix_new(ints, [D], M),
 | 
						|
	matrix_new(floats, [D], MD),
 | 
						|
	nb_setval(i2,M),
 | 
						|
	nb_setval(d2,MD),
 | 
						|
	collect_literals,
 | 
						|
	init_trie,
 | 
						|
	retractall(p_l(_,_,_,_)),
 | 
						|
	retractall(lmln:p(_,_,_,_)),
 | 
						|
	fail.
 | 
						|
init_compiler.
 | 
						|
 | 
						|
init_trie :-
 | 
						|
	catch(nb_getval( mln_trie, Trie ), _, fail),
 | 
						|
	trie_close( Trie ),
 | 
						|
	eraseall( i ),
 | 
						|
	fail.
 | 
						|
init_trie :-
 | 
						|
	trie_open( Trie ),
 | 
						|
	nb_setval( mln_trie , Trie ).
 | 
						|
 | 
						|
collect_literals :-
 | 
						|
	mln(ParFactor, _Type, _Els, _G, _DConstraints),
 | 
						|
	factor(markov, ParFactor, Ks, _, _Phi, _Constraints),
 | 
						|
	maplist(add_lit, Ks),
 | 
						|
	fail.
 | 
						|
collect_literals.
 | 
						|
 | 
						|
add_lit(K) :-
 | 
						|
	functor(K, N, A),
 | 
						|
	functor(K0, N, A),
 | 
						|
	( lit(K0) -> true ; assert(lit(K0)) ).
 | 
						|
 | 
						|
compile_literals :-
 | 
						|
	lit(K),
 | 
						|
	functor(K, N, A),
 | 
						|
	statistics(runtime,_),
 | 
						|
	format(user_error, '/** grounding ~a/~d.~45+**/~n',[N,A]),
 | 
						|
       ( evidence(K, 1), % only look at literals with evidence...
 | 
						|
%	( ground_lit(K),
 | 
						|
	%writeln(k:K),
 | 
						|
	  compile_pw(K)
 | 
						|
        ;
 | 
						|
	  statistics(runtime,[_,T]),
 | 
						|
	  format(user_error, '/**          took ~d msec.~45+**/~n',[T]),
 | 
						|
          fail
 | 
						|
        ).
 | 
						|
	   
 | 
						|
 | 
						|
ground_lit(K) :- 
 | 
						|
	functor(K, _, Ar),
 | 
						|
	ground_lit(0, Ar, K).
 | 
						|
 | 
						|
ground_lit(Ar, Ar, _K).
 | 
						|
ground_lit(I0, Ar, K) :-
 | 
						|
	I is I0+1,
 | 
						|
	(mln):mln_domain(I, K, G, _A),
 | 
						|
	user:G,
 | 
						|
	ground_lit(I, Ar, K).
 | 
						|
 | 
						|
compile_pw(VId) :-
 | 
						|
	(evidence(VId, 1) -> P = 1 ; P = 0),
 | 
						|
	compile(VId, P).
 | 
						|
 | 
						|
compile(VId, Val) :-
 | 
						|
	findall(p(FId,W,P0,P1,I0,I1), find_prob(VId, Val, FId, W, P0, P1, I0, I1), Fs),
 | 
						|
	(
 | 
						|
	  Fs == [] -> fail
 | 
						|
        ;
 | 
						|
	   Fs = [p(FId,W,1,1,I0,I1)]
 | 
						|
           ->
 | 
						|
	   fail
 | 
						|
       ;   
 | 
						|
	  sort(Fs, FsS),
 | 
						|
	  merge_lits(FsS, FsN, Ws),
 | 
						|
	  nb_getval( mln_trie, Trie ),
 | 
						|
	  store( Trie, e(Val, Ws, FsN) )
 | 
						|
       ).
 | 
						|
 | 
						|
store( T , E ) :-
 | 
						|
	trie_check_entry(T, E, R), !,
 | 
						|
	recorded(i, [R|I], Ref),
 | 
						|
	erase(Ref),
 | 
						|
	I1 is I+1,
 | 
						|
	recorda(i, [R|I1], _).
 | 
						|
store( T , E ) :-
 | 
						|
	trie_put_entry(T, E, R), !,
 | 
						|
	recorda(i, [R|1], _).
 | 
						|
 | 
						|
merge_lits([], [], []).
 | 
						|
merge_lits([N*p(F,W,A1,A2,I1,I2), p(F,W,A3,A4,I3,I4)|FsS], FsM, Is) :-
 | 
						|
	A1 == A3,
 | 
						|
	A2 == A4,
 | 
						|
	I1 == I3,
 | 
						|
	I2 == I4, !,
 | 
						|
	N1 is N+1,
 | 
						|
	merge_lits([N1*p(F,W,A3,A4,I3,I4)|FsS], FsM, Is).
 | 
						|
merge_lits([p(F,W,A1,A2,I1,I2), p(F,W,A3,A4,I3,I4)|FsS], FsM, Is) :-
 | 
						|
	A1 == A3,
 | 
						|
	A2 == A4,
 | 
						|
	I1 == I3,
 | 
						|
	I2 == I4, !,
 | 
						|
	merge_lits([2*p(F,W,A3,A4,I3,I4)|FsS], FsM, Is).
 | 
						|
merge_lits([p(F,W,A1,A2,I1,I2) | FsS], [p(F,1,W,A1,A2)|FsM], [n(F,1,I1,I2)|Is]) :-
 | 
						|
	merge_lits(FsS, FsM, Is).
 | 
						|
merge_lits([N*p(F,W,A1,A2,I1,I2) | FsS], [p(F,N,W,A1,A2)|FsM], [n(F,N,I1,I2)|Is]) :-
 | 
						|
	merge_lits(FsS, FsM, Is).
 | 
						|
 | 
						|
find_prob(VId, E, ParFactor, W, P0, P1, I0, I1) :-
 | 
						|
	mln(ParFactor, _, _Type, _, Constraints),
 | 
						|
%	maplist(call,Constraints),
 | 
						|
	deletei(Constraints, VId, ConstraintsF, Pol),
 | 
						|
	maplist(expand_domain(VId-Pol), ConstraintsF),
 | 
						|
	% all other literals are false
 | 
						|
        ( Pol == (+) -> 
 | 
						|
	      P0 = 0, P1 = W,
 | 
						|
	      (E == 1 -> /* we are making this true */ 
 | 
						|
	         inc(ParFactor),
 | 
						|
	         I0 = -1, I1 = 0
 | 
						|
	      ;
 | 
						|
                 /* it is false */
 | 
						|
	         I0 = 0, I1 = 1
 | 
						|
	      )
 | 
						|
	;
 | 
						|
	      P0 = W, P1 = 0,
 | 
						|
	      (E == 1 -> /* we are making this false */ 
 | 
						|
	         I0 = 1, I1 = 0
 | 
						|
	      ;
 | 
						|
                 /* it is true */
 | 
						|
	         inc(ParFactor),
 | 
						|
	         I0 = 0, I1 = -1
 | 
						|
	      )
 | 
						|
        ).
 | 
						|
 | 
						|
expand_domain(VIdPol, true - Lits) :- !,
 | 
						|
	maplist( false_literal(VIdPol), Lits).
 | 
						|
 | 
						|
expand_domain(VIdPol, Dom-Lits) :-
 | 
						|
	call(user:Dom),
 | 
						|
	maplist( true_literal(VIdPol), Lits).
 | 
						|
 | 
						|
% we  need to check if we have 
 | 
						|
% L ; L or L ; -L
 | 
						|
% in this case skip or it is always true, so fail.
 | 
						|
false_literal(L-(-), L).
 | 
						|
false_literal(VId-_, L) :-
 | 
						|
	evidence(L, 1),
 | 
						|
	L \= VId.
 | 
						|
 | 
						|
% L is ground
 | 
						|
true_literal(L-(+), L) :- !.
 | 
						|
true_literal(VId-_, L) :-
 | 
						|
	L \= VId,
 | 
						|
	\+ evidence(L, 1).
 | 
						|
 | 
						|
deletei([true-Lits|More], K, [true-NLits|More], -) :-
 | 
						|
	force_delete(Lits, K, NLits).
 | 
						|
deletei([true-Lits|More], K, [true-Lits|NMore], -) :- !,
 | 
						|
	force_delete(More, K, NMore).
 | 
						|
deletei(More, K, NMore, +) :-
 | 
						|
	deletei(More, K, NMore).
 | 
						|
 | 
						|
deletei([Dom-Lits|More], K, [Dom-NLits|More]) :-
 | 
						|
	force_delete(Lits, K, NLits).
 | 
						|
deletei([DomLits|More], K, [DomLits|NMore]) :-
 | 
						|
	deletei(More, K, NMore).
 | 
						|
 | 
						|
force_delete([Elem|List], Elem, List).
 | 
						|
force_delete([Head|List], Elem, [Head|Residue]) :-
 | 
						|
	force_delete(List, Elem, Residue).
 | 
						|
 | 
						|
inc(Id) :-
 | 
						|
	nb_getval(i2, M),
 | 
						|
	matrix_inc(M, [Id]).
 | 
						|
 | 
						|
peval(Ref, P) :-
 | 
						|
	trie_get_entry(Ref, e(Side, _, Ps)),
 | 
						|
	foldl2(p_eval, Ps, 0.0, P0, 0.0, P1),
 | 
						|
	logsum(P0, P1, P01),
 | 
						|
	( Side == 0 -> P = P0-P01 ; P = P1-P01 ).
 | 
						|
 | 
						|
peval(Ps, P0, P1) :-
 | 
						|
%writeln(p:Ds:Ps),
 | 
						|
	foldl2(p_eval, Ps, 0.0, AP0, 0.0, AP1),
 | 
						|
	logsum(AP0, AP1, AP01),
 | 
						|
	P0 is exp( AP0 - AP01 ),
 | 
						|
	P1 is 1-P0.
 | 
						|
 | 
						|
p_eval(p(WId, N, W, P0, P1), AP0, A0, AP1, A1) :-
 | 
						|
	mln_w(WId, W),
 | 
						|
	A0 is AP0+N*P0,
 | 
						|
	A1 is AP1+N*P1.
 |