În timpul liber, m-am gândit puțin la metodele de control sintetice ca abordare a evaluării și lucrez la o postare pe blog. Dar o problemă secundară care a apărut a fost suficient de interesantă pentru a fi tratată separat.
Din punct de vedere tehnic, este o chestiune de ajustare a unei regresii în care coeficienții sunt constrânși să fie nenegativi și să însumeze unul; cu alte cuvinte, coeficienții sunt a simplex. Motivul pentru care apare acest lucru este că, în metodele de control sintetic, o astfel de regresie este utilizată pentru a determina ponderile de utilizat în construirea unei medii ponderate a mai multor unități (țări, firme, oameni) care este cât mai comparabilă cu unitatea care a primit. intervenția în curs de evaluare.
Argumentul pentru adunarea ponderilor la unu este evitarea extrapolării din intervalul datelor reale. Nu mi se pare un argument convingător, dar îl voi lăsa pentru o postare ulterioară, dacă reușesc. Deocamdată, sunt interesat de întrebarea tehnică a modului de a obține un astfel de set de greutăți diferite de zero însumând până la una; sau abstragând-o departe de aplicație, cum să se potrivească o regresie în care coeficienții sunt constrânși să fie un simplex.
Desigur, există mai multe moduri în care ați putea face acest lucru. A existat o întrebare și un răspuns bun la acest lucru pe validarea încrucișată încă din 2012. Două dintre cele patru metode prin care voi trece mai jos provin aproape direct din răspunsurile sau comentariile de acolo. Celelalte două sunt invenția mea proprie (nu pretind că sunt primul, doar că nu i-am văzut pe alții făcând asta).
Mai întâi, să începem prin a simula unele date. O să fac o matrice numită X
cu cinci coloane, toate din distribuții diferite. Apoi creez un vector de coeficienți adevărați care vor lega y
Sunt pe cale să fac asta X
. Pentru a face exercițiul interesant și realist, coeficienții adevărați vor implica un număr negativ și nu se vor adăuga exact la 1. Unul dintre ei va fi zero.
Acum, voi potrivi diverse regresii (fără interceptare) la aceste date și voi extrage coeficienții și îi voi compara cu adevăratul proces de generare a datelor. Mai întâi, să începem cu cele mai mici pătrate obișnuite, fără constrângeri
Această abordare este foarte bună pentru a se apropia de adevăratul proces de generare a datelor, așa cum ne-am aștepta. Dar, desigur, ca și procesul adevărat, are un coeficient negativ pentru x3 și însumează mai mult de 1:
> round(coefs1, 2) Xx1 Xx2 Xx3 Xx4 Xx5 0.20 0.61 -0.11 -0.02 0.45 > sum(coefs1) (1) 1.122484
Acesta este graficul pe care îl obținem, comparând coeficienții modelului cu cei din procesul real de generare a datelor:
OK, totul foarte bine, dar nu face niciun efort (în afară de doar speranța că funcționează) pentru a constrânge coeficienții să se adună la unul. Așadar, iată prima mea încercare reală, care se bazează pe o sugestie pe care whuber a făcut-o în comentariile la postarea cu validare încrucișată. El subliniază cu o algebră de bază că, dacă scădeți una dintre coloanele X din celelalte și din y și apoi potriviți o regresie cu MCO la acele date transformate, obțineți coeficienții corecti pe toate coloanele de X pe care le-ați lăsat în , și îl poate calcula pe cel rămas scăzându-le pe toate din 1.
De data aceasta, coeficienții se adună exact la unu după cum este necesar, dar, din păcate, există încă un negativ acolo
> round(coefs2, 2) X2x1 X2x2 X2x3 X2x4 0.24 0.40 -0.04 0.00 0.40 > sum(coefs2) (1) 1
Aceasta nu este o știre – a fost identificată ca o problemă în discuția despre Cross-Validated, dar cu datele simulate folosite acolo nu s-a manifestat. De fapt, acesta este unul dintre motivele pentru care datele mele simulate sunt puțin mai murdare, cu coeficienți originali care nu se adaugă la unul și care au o valoare negativă autentică printre ei.
Cam atât pentru modelul 2.
Modelul 3 este unul pe care l-am inventat eu și folosește faptul că glmnet
nu vă permite doar să vă regularizați coeficienții (strângeți-i spre zero, pentru a ajuta la gestionarea multicoliniarității, supraajustării și a problemelor conexe), ci vă permite să specificați limite superioare și inferioare pentru coeficienți. Așadar, am putea obține primul nostru set complet de coeficienți de regresie (nenegativi, adăugând la unul) utilizând abordarea transformării datelor în model2, dar potrivind modelul cu glmnet
în schimb. Ca aceasta:
Rezultatele sunt toate nenegative și se adaugă corect la unul:
> round(coefs3, 2) (1) 0.11 0.24 0.00 0.00 0.64 > sum(coefs3) (1) 1
Ele diferă semnificativ de coeficienții adevărați:
Vom reveni la asta.
În continuare este ceea ce am putea numi soluția „adecvată”, care este să tratăm problema ca pe cazul clasic al programării pătratice. Programarea pătratică a fost dezvoltată exact pentru a face față acestei situații – optimizați o funcție pătratică multivariată (cum ar fi cele mai mici pătrate într-o regresie) supuse unor constrângeri liniare (cum ar fi, coeficienții trebuie să fie nenegativi și să adună până la unul). Pachetul R al lui Hans Borchers („Funcții matematice numerice practice”) are o interfață ușor de înțeles pentru a face acest lucru fără a fi nevoie să inversați propriile matrice, așa că singurul lucru pe care trebuie să-l faceți este să exprimați constrângerile în mod corect. Iată cum facem asta cu problema noastră:
La fel ca metoda anterioară, rezultatul nostru este nenegativ și se însumează la zero:
> round(coefs4, 2) (1) 0.17 0.44 0.00 0.00 0.38 > sum(coefs4) (1) 1
De data aceasta coeficienții arată puțin mai aproape de cei inițiali:
În cele din urmă, cealaltă metodă pe care am inventat-o a fost să merg all-in Bayesian și să specific pur și simplu în Stan că coeficienții trebuie să fie simplex și că au o distribuție Dirichlet. Aceasta mi se pare de fapt cea mai intuitivă soluție – ce modalitate mai bună de a face ceva un simplex decât să consideri că are o distribuție Dirichlet?
Așa că am scris acest program în Stan:
… și l-a numit de la R cu asta:
Aceasta este acum a treia metodă pentru care rezultatul nostru este corect tot nenegativ și se însumează la zero:
> round(coefs5, 2) (1) 0.17 0.45 0.00 0.00 0.38 > sum(coefs5) (1) 1
… și așa arată, practic foarte asemănător cu soluția de programare pătratică:
OK, care metodă este cea mai bună? Avem nevoie de un fel de benchmark pentru a le testa, mai bine decât coeficienții inițiali din procesul de generare a datelor în conformitate cu restricțiile fundamentale. Având în vedere că scopul final este de a construi o medie ponderată, dorim ceva care să aibă proporții similare cu coeficienții inițiali. Așadar, dorind doar să fiu pragmatic în acest sens, am creat un set „normalizat” de coeficienți eliminându-i pe cel negativ și scalând restul pentru a adăuga la unu.
Acest lucru ne permite să definim un criteriu de succes – metoda câștigătoare este cea care îndeplinește constrângerile (excluzând modelele 1 și 2) și se potrivește cel mai bine cu acele proporții. Iată un complot frumos de perechi care compară totul dintr-o privire:
Ceea ce vedem este că modelele 4 și 5 sunt câștigătoare clare, efectiv egale. Nu numai că sunt aproape identici, ci se potrivesc și cu coeficienții reali normalizați (corelații de 0,98 sau mai sus). Din păcate, metoda glmnet nu funcționează foarte bine la recuperarea proporțiilor inițiale. Poate că s-ar fi descurcat mai bine dacă aș lăsa coloanele lui X să fie corelate între ele, mai degrabă decât independente (care se ocupă cu multi-colinearitatea fiind terenul de acasă al regresiei nete elastice). Poate că, de asemenea, s-ar apropia de soluția de programare pătratică dacă aș forța cantitatea de regularizare la aproape zero. Dar, având deja două soluții bune, nu sunt înclinat să experimentez în continuare în acest sens.
Abordarea de programare pătratică a fost mai ușor de scris și de depanat și mai rapid de rulat decât modelarea explicită a distribuției Dirichlet în Stan, așa că pragmatic devine câștigătoare; singurul avertisment fiind acel semn de întrebare din ultimul paragraf despre ce se întâmplă dacă coloanele lui X sunt multi-coliniare.
Oh, să terminăm cu codul pentru a desena acel complot de perechi. Dintr-un motiv sau altul, am folosit o bază veche bună R pairs()
pentru a face asta, mai degrabă GGally
pachetul pe care îl folosesc în mod mai normal pentru matrice de dispersie. Îmi place controlul ușor pairs()
vă oferă peste triunghiurile superioare și inferioare ale matricei scatterplot, prin definirea propriei funcții din mers. Deci, pentru cei mai vechi, iată acel cod R de bază: