Dagli hack all’armonia: strutturare le regole del prodotto in raccomandazioni |  di Michael Roizner |  Settembre 2023

 | Intelligenza-Artificiale

Non lasciare che le euristiche minino il tuo ML, impara a combinarle

Nel panorama odierno basato sui dati, i sistemi di raccomandazione alimentano qualsiasi cosa, dai feed dei social media all’e-commerce. Sebbene sia forte la tentazione di pensare che gli algoritmi di apprendimento automatico facciano tutto il lavoro pesante, questa è solo metà della storia. I sistemi del mondo reale spesso si basano su un mix di apprendimento automatico e regole euristiche – comunemente denominate regole di prodotto, regole aziendali o semplicemente hack – per generare le raccomandazioni più pertinenti.

Per esempio:

  • Non puoi consigliare troppo spesso brani dello stesso artista;
  • Dovresti includere i contenuti degli abbonamenti nel feed, ma non sovraccaricarli;
  • Se un utente ha già messo “non mi piace” a una determinata categoria o autore, il contenuto correlato dovrebbe essere penalizzato o addirittura filtrato;
  • I contenuti espliciti non possono essere consigliati, tranne quando appropriato.
fotografato da Cam Bradford SU Unsplash

Le regole sono di due tipi: dure e morbide. Le regole rigide agiscono come filtri, impedendo che determinati documenti vengano raccomandati in contesti specifici; la mancata osservanza è considerata un bug del prodotto. Non c’è nulla di intrinsecamente sbagliato in queste regole, ma il loro numero dovrebbe essere limitato. Inoltre, dovrebbero essere applicati il ​​più presto possibile nel processo di classificazione, nella fase di generazione dei candidati o anche durante la costruzione dell’indice. Le regole soft, d’altro canto, sono più simili a linee guida: puoi consigliare tali articoli, ma preferibilmente non troppo (o il contrario, di più è meglio). Avere troppe di queste regole soft può rendere il debug e lo sviluppo del sistema molto impegnativi.

Le regole sono un debito tecnico.

Trovo che la quantità di tali regole in un sistema spesso dipenda dalle dinamiche di potere interne al team. I product manager di solito trovano conveniente esprimere i vincoli attraverso regole, mentre gli ingegneri in genere non amano questi hack. Nella mia squadra precedente, ero orgoglioso della nostra capacità di mantenere il numero di tali regole al minimo.

Nel corso della mia carriera, ho spesso riscontrato uno schema ricorrente. Il team di ingegneri fatica ad addestrare il sistema in modo da produrre buoni consigli (nel complesso o in aspetti specifici). Il team di prodotto ricorre quindi a ciò che conosce meglio, ovvero l’aggiunta di nuove regole. Tali patch sono giustificate quando sono necessarie soluzioni rapide, ma sono difficili da rimuovere in seguito. Il sistema spesso rimane in questo stato fino a quando non avviene un importante refactoring, proprio come il normale debito tecnico.

La morale della storia: non lesinare sull’assunzione di ingegneri forti 🙂

In un sistema ideale non dovrebbero esserci regole del genere; tutta la logica fuzzy dovrebbe essere gestita da un modello sufficientemente avanzato. Sogno che un giorno raggiungeremo questo stato tecnologico (e ho un’ipotesi su come raggiungerlo). Tuttavia, per il momento, non è realistico. Quindi, invece di vietare completamente queste regole, parlerò di un approccio che consenta una certa organizzazione e limiti il ​​caos.

Questo framework consente l’integrazione di modelli di machine learning con l’applicazione delle regole di prodotto, aiutando a strutturare queste regole evitando il caos completo. Tuttavia, è flessibile e non eccessivamente restrittivo, quindi non può garantire l’ordine totale. In un certo senso, è semplicemente un linguaggio per descrivere le regole. Secondo me è abbastanza conveniente.

In questa discussione, ci concentreremo sulla fase finale della classifica, dove non sono rimasti troppi documenti – diciamo, da poche dozzine a un paio di centinaia – e vogliamo compilare l’elenco migliore da essi. La cosa interessante di questa fase è che non cerchiamo solo di valutare ogni documento nel contesto attuale nel modo più preciso possibile, ma consideriamo anche come questi documenti si combinano tra loro. È qui che entra in gioco la classificazione listwise (da non confondere con l’apprendimento listwise per imparare a classificare, dove solo la funzione di perdita dipende da tutti i documenti in una query, non dalla funzione di classificazione). Una tipica applicazione di questo approccio listwise è quella di migliorare la diversità dei risultati.

Ecco i principi chiave dell’approccio.

  1. I risultati vengono generati in modo iterativo, iniziando dalla prima posizione e terminando con l’ultima. Ad ogni iterazione, selezioniamo il documento più adatto per la posizione imminente. Ecco come funziona la maggior parte delle strategie di riclassificazione, come il noto DPP per la diversificazione. Per gli output non lineari, le posizioni possono essere classificate in base all’importanza.
  2. Ad ogni iterazione, prendiamo tutti i documenti rimanenti e li ordiniamo in base a una funzione valore. Ciò potrebbe variare da qualcosa di semplice come l’output di un modello di probabilità di clic a qualcosa di più complesso: una combinazione di vari output del modello (o più modelli) che prevedono eventi diversi, componenti di diversità (come la somiglianza con documenti precedenti) e potenziamenti manuali, ecc. La funzione valore può essere ricalcolata ad ogni iterazione e può quindi dipendere sia dalla posizione che dai documenti già presenti nell’output finale. Deve essere computazionalmente efficiente. Creare la giusta funzione di valore è di per sé un argomento ricco; il quadro non limita né semplifica questo aspetto.
  3. Le regole del prodotto sono espresse come segue: all’interno di un sottoinsieme di posizioni Xil numero di documenti con proprietà F dovrebbe essere al di sopra o al di sotto di una certa soglia C. Generalmente, X è un intervallo di posizioni iniziali, ad esempio da 1 a 10 (la prima pagina). Proprietà F è meglio espresso come una regola di soglia di qualche caratteristica, cioè, (funzionalità (doc) > soglia). Se necessario, questo formato può essere generalizzato per includere proprietà non binarie.
  4. Le regole hanno la priorità. Se non riusciamo a soddisfare tutte le regole, scartiamo quelle meno importanti. Per essere più precisi: se la regola con la massima priorità è realizzabile in una data posizione, verrà sicuramente applicata; altrimenti non lo sarà. Se la regola con la priorità successiva è realizzabile in tali condizioni, verrà applicata; altrimenti lo saltiamo. E così via. In altre parole, selezioniamo la maschera lessicograficamente più alta delle regole soddisfatte.

Ecco alcuni esempi di regole in questo formato:

  • Almeno la metà dei documenti nell’intero output dovrebbero essere abbonamenti. Tuttavia, se tutti i documenti degli abbonamenti sono già stati letti, questa regola diventa irrealizzabile e verrà scartata.
  • Il numero di documenti di bassa qualità nelle prime 10 posizioni non deve superare 2.
  • Tra le posizioni 10 e 20 dovrebbe esserci almeno un documento di una nuova categoria.

Vale la pena notare che regole come “almeno 5 documenti con una determinata proprietà devono essere nelle prime 10 posizioni” possono far sì che le prime 5 posizioni vengano riempite con documenti privi di tale proprietà, seguite da 5 con essa. Per distribuirlo in modo più uniforme, puoi aggiungere regole per intervalli intermedi: almeno 1 nelle prime 2 posizioni, almeno 2 nelle prime 4 e così via.

Implementare questo quadro in modo efficiente è una bella sfida ma del tutto fattibile. Ecco uno schizzo del codice Python per illustrare come si potrebbe implementare il framework di riclassificazione descritto. Tieni presente che questo non è ottimizzato per l’efficienza ma dovrebbe fornire un buon punto di partenza.

def rerank(documents, count, rules, scorer):
result = ()
while len(result) < count and len(documents) > 0:
position = len(result)
candidates = documents
for rule in rules:
filtered = (doc for doc in candidates if rule(position, doc))
if len(filtered) > 0:
candidates = filtered
next_doc = max(candidates, key=lambda doc: scorer(position, doc))
result.append(next_doc)
documents.remove(next_doc)
scorer.update(position, next_doc)
for rule in rules:
rule.update(position, next_doc)
return result

Infine, il miglioramento della possibilità di debug e della controllabilità del sistema è notevolmente facilitato registrando tutte le regole eseguite e scartate.

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *