Previsione dei Mi piace: all'interno degli algoritmi di un semplice motore di suggerimenti
Pubblicato: 2022-03-11Un motore di suggerimenti (a volte indicato come un sistema di suggerimenti) è uno strumento che consente agli sviluppatori di algoritmi di prevedere cosa potrebbe piacere o meno a un utente in un elenco di elementi dati. I motori di raccomandazione sono un'alternativa piuttosto interessante ai campi di ricerca, poiché i motori di raccomandazione aiutano gli utenti a scoprire prodotti o contenuti che altrimenti non potrebbero incontrare. Ciò rende i motori di raccomandazione una parte importante di siti Web e servizi come Facebook, YouTube, Amazon e altri.
I motori di raccomandazione funzionano idealmente in due modi. Può fare affidamento sulle proprietà degli elementi che piacciono a un utente, che vengono analizzati per determinare cos'altro potrebbe piacere all'utente; oppure, può fare affidamento su Mi piace e non mi piace di altri utenti, che il motore di raccomandazione utilizza quindi per calcolare un indice di somiglianza tra gli utenti e consigliare loro gli elementi di conseguenza. È anche possibile combinare entrambi questi metodi per creare un motore di suggerimenti molto più robusto. Tuttavia, come tutti gli altri problemi relativi alle informazioni, è essenziale scegliere un algoritmo adatto al problema affrontato.
In questo tutorial, ti guideremo attraverso il processo di creazione di un motore di suggerimenti collaborativo e basato sulla memoria. Questo motore di suggerimenti consiglierà i film agli utenti in base a ciò che gli piace e ciò che non gli piace e funzionerà come il secondo esempio menzionato prima. Per questo progetto, utilizzeremo operazioni di base sugli insiemi, un po' di matematica e Node.js/CoffeeScript. Tutto il codice sorgente relativo a questo tutorial può essere trovato qui.
Insiemi ed equazioni
Prima di implementare un motore di raccomandazione collaborativo basato sulla memoria, dobbiamo prima comprendere l'idea centrale alla base di un tale sistema. Per questo motore, ogni elemento e ogni utente non è altro che identificatori. Pertanto, non prenderemo in considerazione nessun altro attributo di un film (ad esempio il cast, il regista, il genere, ecc.) durante la generazione dei consigli. La somiglianza tra due utenti è rappresentata utilizzando un numero decimale compreso tra -1,0 e 1,0. Chiameremo questo numero indice di somiglianza. Infine, la possibilità che un utente apprezzi un film sarà rappresentata utilizzando un altro numero decimale compreso tra -1.0 e 1.0. Ora che abbiamo modellato il mondo attorno a questo sistema usando termini semplici, possiamo liberare una manciata di eleganti equazioni matematiche per definire la relazione tra questi identificatori e numeri.
Nel nostro algoritmo di raccomandazione, manterremo un numero di set. Ogni utente avrà due set: un set di film che piacciono all'utente e un set di film che non piacciono all'utente. Ogni film avrà anche due set associati: un set di utenti a cui è piaciuto il film e un set di utenti a cui il film non è piaciuto. Durante le fasi in cui vengono generate le raccomandazioni, verrà prodotto un certo numero di insiemi, principalmente unioni o intersezioni degli altri insiemi. Avremo anche ordinato elenchi di suggerimenti e utenti simili per ciascun utente.
Per calcolare l'indice di similarità utilizzeremo una variazione della formula dell'indice di Jaccard. Originariamente noto come "coefficient de communaute" (coniato da Paul Jaccard), la formula confronta due insiemi e produce una semplice statistica decimale tra 0 e 1,0:
La formula prevede la divisione del numero di elementi comuni in entrambi gli insiemi per il numero di tutti gli elementi (contati solo una volta) in entrambi gli insiemi. L'indice Jaccard di due insiemi identici sarà sempre 1, mentre l'indice Jaccard di due insiemi senza elementi comuni darà sempre 0. Ora che sappiamo come confrontare due insiemi, pensiamo a una strategia che possiamo usare per confrontare due utenti. Come discusso in precedenza, gli utenti, dal punto di vista del sistema, sono tre cose: un identificatore, una serie di film piaciuti e una serie di film non piaciuti. Se dovessimo definire l'indice di somiglianza dei nostri utenti in base solo all'insieme dei loro film preferiti, potremmo utilizzare direttamente la formula dell'indice di Jaccard:
Qui, U1 e U2 sono i due utenti che stiamo confrontando, e L1 e L2 sono i set di film che hanno apprezzato rispettivamente gli U1 e gli U2. Ora, se ci pensi, due utenti a cui piacciono gli stessi film sono simili, quindi anche due utenti a cui non piacciono gli stessi film dovrebbero essere simili. Qui è dove modifichiamo un po' l'equazione:
Invece di considerare solo i Mi piace comuni nel numeratore della formula, ora aggiungiamo anche il numero di Non mi piace comuni. Al denominatore, prendiamo il numero di tutti gli elementi che l'utente ha apprezzato o meno. Ora che abbiamo considerato sia i Mi piace che i Non mi piace in modo indipendente, dovremmo anche pensare al caso in cui due utenti sono agli opposti nelle loro preferenze. L'indice di somiglianza di due utenti in cui a uno piace un film e all'altro non piace non dovrebbe essere 0:
Questa è una formula lunga! Ma è semplice, lo prometto. È simile alla nostra formula precedente con una piccola differenza nel numeratore. Ora stiamo sottraendo il numero di Mi piace e Non mi piace in conflitto dei due utenti dal numero dei loro Mi piace e Non mi piace comuni. Ciò fa sì che la formula dell'indice di somiglianza abbia un intervallo di valori compreso tra -1,0 e 1,0. Due utenti con gusti identici avranno un indice di somiglianza di 1,0 mentre due utenti con gusti completamente contrastanti nei film avranno un indice di somiglianza di -1,0.
Ora che sappiamo come confrontare due utenti in base ai loro gusti nei film, dobbiamo esplorare un'altra formula prima di poter iniziare a implementare il nostro algoritmo del motore di raccomandazione preparato in casa:
Analizziamo un po' questa equazione. Ciò che intendiamo con P(U,M)
è la possibilità che un utente U
apprezzi il film M
. ZL
e ZD
sono la somma degli indici di somiglianza dell'utente U
con tutti gli utenti a cui è piaciuto o meno il film M
, rispettivamente. |ML|+|MD|
rappresenta il numero totale di utenti a cui è piaciuto o non è piaciuto il film M
. Il risultato P(U,M)
produce un numero compreso tra -1,0 e 1,0.
Questo è tutto. Nella sezione successiva, possiamo utilizzare queste formule per iniziare a implementare il nostro motore di raccomandazione basato sulla memoria collaborativa.
Costruire il motore di suggerimenti
Costruiremo questo motore di suggerimenti come un'applicazione Node.js molto semplice. Ci sarà anche pochissimo lavoro sul front-end, principalmente alcune pagine e moduli HTML (useremo Bootstrap per rendere le pagine pulite). Sul lato server, utilizzeremo CoffeeScript. L'applicazione avrà alcuni percorsi GET e POST. Anche se avremo la nozione di utenti nell'applicazione, non avremo alcun elaborato meccanismo di registrazione/accesso. Per la persistenza, utilizzeremo il pacchetto Bourne disponibile tramite NPM che consente a un'applicazione di archiviare dati in semplici file JSON ed eseguire query di database di base su di essi. Useremo Express.js per facilitare il processo di gestione delle rotte e dei gestori.
A questo punto, se non conosci lo sviluppo di Node.js, potresti voler clonare il repository GitHub in modo che sia più facile seguire questo tutorial. Come con qualsiasi altro progetto Node.js, inizieremo creando un file package.json e installando una serie di pacchetti di dipendenze necessari per questo progetto. Se stai utilizzando il repository clonato, il file package.json dovrebbe essere già lì, da dove l'installazione delle dipendenze richiederà l'esecuzione di "$ npm install". Questo installerà tutti i pacchetti elencati all'interno del file package.json.
I pacchetti Node.js di cui abbiamo bisogno per questo progetto sono:
- asincrono
- bourne
- copione del caffè
- esprimere
- giada
- sottolineare
Costruiremo il motore di raccomandazione suddividendo tutti i metodi rilevanti in quattro classi CoffeeScript separate, ognuna delle quali verrà archiviata in "lib/engine": Engine, Rater, Similars e Suggestions. Il motore di classe sarà responsabile di fornire una semplice API per il motore di suggerimenti e legherà insieme le altre tre classi. Rater sarà responsabile del monitoraggio di Mi piace e Non mi piace (come due istanze separate della classe Rater). Simili e Suggerimenti saranno rispettivamente responsabili della determinazione e del monitoraggio di utenti simili e articoli consigliati per gli utenti.
Monitoraggio di Mi piace e Non mi piace
Cominciamo prima con la nostra classe Valutatori. Questo è semplice:
class Rater constructor: (@engine, @kind) -> add: (user, item, done) -> remove: (user, item, done) -> itemsByUser: (user, done) -> usersByItem: (item, done) ->
Come indicato in precedenza in questo tutorial, avremo un'istanza di Rater per i Mi piace e un'altra per i Non mi piace. Per registrare che a un utente piace un elemento, lo passeremo a "Rater#add()". Allo stesso modo, per rimuovere la valutazione, la passeremo a "Rater#remove()".
Poiché utilizziamo Bourne come soluzione di database senza server, memorizzeremo queste valutazioni in un file denominato "./db-#{@kind}.json", dove tipo può essere "mi piace" o "non mi piace". Apriremo il database all'interno del costruttore dell'istanza Rater:
constructor: (@engine, @kind) -> @db = new Bourne "./db-#{@kind}.json"
Ciò renderà l'aggiunta di record di valutazione semplice come chiamare un metodo di database Bourne all'interno del nostro metodo "Rater#add()":
@db.insert user: user, item: item, (err) =>
Ed è simile rimuoverli ("db.delete" invece di "db.insert"). Tuttavia, prima di aggiungere o rimuovere qualcosa, dobbiamo assicurarci che non esista già nel database. Idealmente, con un database reale, avremmo potuto farlo come una singola operazione. Con Bourne, dobbiamo prima fare un controllo manuale; e, una volta terminato l'inserimento o l'eliminazione, dobbiamo assicurarci di ricalcolare gli indici di somiglianza per questo utente e quindi generare una serie di nuovi suggerimenti. I metodi "Rater#add()" e "Rater#remove()" avranno un aspetto simile a questo:
add: (user, item, done) -> @db.find user: user, item: item, (err, res) => if res.length > 0 return done() @db.insert user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done remove: (user, item, done) -> @db.delete user: user, item: item, (err) => async.series [ (done) => @engine.similars.update user, done (done) => @engine.suggestions.update user, done ], done
Per brevità, salteremo le parti in cui verifichiamo la presenza di errori. Questa potrebbe essere una cosa ragionevole da fare in un articolo, ma non è una scusa per ignorare gli errori nel codice reale.
Gli altri due metodi, "Rater#itemsByUser()" e "Rater#usersByItem()" di questa classe implicano fare ciò che i loro nomi implicano: cercare gli elementi valutati rispettivamente da un utente e dagli utenti che hanno valutato un elemento. Ad esempio, quando Rater viene istanziata con kind = “likes”
, "Rater#itemsByUser()" troverà tutti gli elementi che l'utente ha valutato.
Trovare utenti simili
Passando alla nostra prossima lezione: Simili. Questa classe ci aiuterà a calcolare e tenere traccia degli indici di somiglianza tra gli utenti. Come discusso in precedenza, il calcolo della somiglianza tra due utenti implica l'analisi degli insiemi di elementi che gli piacciono e non gli piacciono. Per fare ciò, faremo affidamento sulle istanze Rater per recuperare gli insiemi di elementi rilevanti, quindi determinare l'indice di somiglianza per determinate coppie di utenti utilizzando la formula dell'indice di somiglianza.
Proprio come la nostra classe precedente, Rater, inseriremo tutto in un database Bourne chiamato "./db-similars.json", che apriremo nel costruttore di Rater. La classe avrà un metodo "Similars#byUser()", che ci permetterà di cercare utenti simili a un determinato utente attraverso una semplice ricerca nel database:
@db.findOne user: user, (err, {others}) =>
Tuttavia, il metodo più importante di questa classe è "Similars#update()" che funziona prendendo un utente e calcolando un elenco di altri utenti simili e memorizzando l'elenco nel database, insieme ai loro indici di somiglianza. Inizia trovando i Mi piace e i Non mi piace dell'utente:

async.auto userLikes: (done) => @engine.likes.itemsByUser user, done userDislikes: (done) => @engine.dislikes.itemsByUser user, done , (err, {userLikes, userDislikes}) => items = _.flatten([userLikes, userDislikes])
Troviamo anche tutti gli utenti che hanno valutato questi articoli:
async.map items, (item, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.usersByItem item, done , done , (err, others) =>
Successivamente, per ciascuno di questi altri utenti, calcoliamo l'indice di somiglianza e memorizziamo tutto nel database:
async.map others, (other, done) => async.auto otherLikes: (done) => @engine.likes.itemsByUser other, done otherDislikes: (done) => @engine.dislikes.itemsByUser other, done , (err, {otherLikes, otherDislikes}) => done null, user: other similarity: (_.intersection(userLikes, otherLikes).length+_.intersection(userDislikes, otherDislikes).length-_.intersection(userLikes, otherDislikes).length-_.intersection(userDislikes, otherLikes).length) / _.union(userLikes, otherLikes, userDislikes, otherDislikes).length , (err, others) => @db.insert user: user others: others , done
All'interno dello snippet sopra, noterai che abbiamo un'espressione di natura identica alla nostra formula dell'indice di somiglianza, una variante della formula dell'indice di Jaccard.
Generazione di raccomandazioni
La nostra prossima lezione, Suggerimenti, è il luogo in cui si svolgono tutte le previsioni. Come la classe Similars, ci affidiamo a un altro database Bourne denominato “./db-suggestions.json”, aperto all'interno del costruttore.
La classe avrà un metodo "Suggestions#forUser()" per cercare suggerimenti calcolati per l'utente specificato:
forUser: (user, done) -> @db.findOne user: user, (err, {suggestions}={suggestion: []}) -> done null, suggestions
Il metodo che calcolerà questi risultati è "Suggestions#update()". Questo metodo, come "Similars#update()", prenderà un utente come argomento. Il metodo inizia elencando tutti gli utenti simili all'utente specificato e tutti gli elementi che l'utente specificato non ha valutato:
@engine.similars.byUser user, (err, others) => async.auto likes: (done) => @engine.likes.itemsByUser user, done dislikes: (done) => @engine.dislikes.itemsByUser user, done items: (done) => async.map others, (other, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.itemsByUser other.user, done , done , done , (err, {likes, dislikes, items}) => items = _.difference _.unique(_.flatten items), likes, dislikes
Una volta che abbiamo elencato tutti gli altri utenti e gli elementi non classificati, possiamo iniziare a calcolare un nuovo set di consigli rimuovendo qualsiasi set di consigli precedente, scorrendo ogni elemento e calcolando la possibilità che l'utente lo apprezzi in base alle informazioni disponibili:
@db.delete user: user, (err) => async.map items, (item, done) => async.auto likers: (done) => @engine.likes.usersByItem item, done dislikers: (done) => @engine.dislikes.usersByItem item, done , (err, {likers, dislikers}) => numerator = 0 for other in _.without _.flatten([likers, dislikers]), user other = _.findWhere(others, user: other) if other? numerator += other.similarity done null, item: item weight: numerator / _.union(likers, dislikers).length , (err, suggestions) =>
Fatto ciò, lo salviamo di nuovo nel database:
@db.insert user: user suggestions: suggestions , done
Esporre l'API della libreria
All'interno della classe Engine, leghiamo tutto in una struttura simile a un'API per un facile accesso dal mondo esterno:
class Engine constructor: -> @likes = new Rater @, 'likes' @dislikes = new Rater @, 'dislikes' @similars = new Similars @ @suggestions = new Suggestions @
Dopo aver istanziato un oggetto Engine:
e = new Engine
Possiamo facilmente aggiungere o rimuovere Mi piace e Non mi piace:
e.likes.add user, item, (err) -> e.dislikes.add user, item, (err) ->
Possiamo anche iniziare ad aggiornare gli indici di somiglianza degli utenti e i suggerimenti:
e.similars.update user, (err) -> e.suggestions.update user, (err) ->
Infine, è importante esportare questa classe Engine (e tutte le altre classi) dai rispettivi file “.coffee”:
module.exports = Engine
Quindi, esporta l'Engine dal pacchetto creando un file “index.coffee” con una sola riga:
module.exports = require './engine'
Creazione dell'interfaccia utente
Per poter utilizzare l'algoritmo del motore di raccomandazione in questo tutorial, vogliamo fornire una semplice interfaccia utente sul web. Per fare ciò, generiamo un'app Express all'interno del nostro file "web.iced" e gestiamo alcuni percorsi:
movies = require './data/movies.json' Engine = require './lib/engine' e = new Eengine app = express() app.set 'views', "#{__dirname}/views" app.set 'view engine', 'jade' app.route('/refresh') .post(({query}, res, next) -> async.series [ (done) => e.similars.update query.user, done (done) => e.suggestions.update query.user, done ], (err) => res.redirect "/?user=#{query.user}" ) app.route('/like') .post(({query}, res, next) -> if query.unset is 'yes' e.likes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.dislikes.remove query.user, query.movie, (err) => e.likes.add query.user, query.movie, (err) => if err? return next err res.redirect "/?user=#{query.user}" ) app.route('/dislike') .post(({query}, res, next) -> if query.unset is 'yes' e.dislikes.remove query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" else e.likes.remove query.user, query.movie, (err) => e.dislikes.add query.user, query.movie, (err) => res.redirect "/?user=#{query.user}" ) app.route('/') .get(({query}, res, next) -> async.auto likes: (done) => e.likes.itemsByUser query.user, done dislikes: (done) => e.dislikes.itemsByUser query.user, done suggestions: (done) => e.suggestions.forUser query.user, (err, suggestions) => done null, _.map _.sortBy(suggestions, (suggestion) -> -suggestion.weight), (suggestion) => _.findWhere movies, id: suggestion.item , (err, {likes, dislikes, suggestions}) => res.render 'index', movies: movies user: query.user likes: likes dislikes: dislikes suggestions: suggestions[...4] )
All'interno dell'app, gestiamo quattro percorsi. Il percorso dell'indice "/" è il punto in cui serviamo l'HTML front-end eseguendo il rendering di un modello Jade. La generazione del modello richiede un elenco di film, il nome utente dell'utente corrente, i Mi piace e i Non mi piace dell'utente e i primi quattro suggerimenti per l'utente. Il codice sorgente del modello Jade è escluso dall'articolo, ma è disponibile nel repository GitHub.
I percorsi "/like" e "/dislike" sono quelli in cui accettiamo le richieste POST per registrare i Mi piace e i Non mi piace dell'utente. Entrambe le rotte aggiungono una valutazione rimuovendo prima qualsiasi valutazione in conflitto, se necessario. Ad esempio, un utente che apprezza qualcosa che in precedenza non gli piaceva farà sì che il gestore rimuova prima la valutazione "non mi piace". Questi percorsi consentono anche all'utente di "non mi piace" o "non mi piace" un elemento, se lo si desidera.
Infine, il percorso "/refresh" consente all'utente di rigenerare la propria serie di raccomandazioni su richiesta. Tuttavia, questa azione viene eseguita automaticamente ogni volta che l'utente assegna una valutazione a un elemento.
Test di guida
Se hai tentato di implementare questa applicazione da zero seguendo questo articolo, dovrai eseguire un ultimo passaggio prima di poterla testare. Dovrai creare un file ".json" in "data/movies.json" e popolarlo con alcuni dati del film in questo modo:
[ { "id": "1", "name": "Transformers: Age of Extinction", "thumb": { "url": "//upload.wikimedia.org/wikipedia/en/7/7f/Inception_ver3.jpg" } }, // … ]
Potresti voler copiare quello disponibile nel repository GitHub, che è precompilato con una manciata di nomi di film e URL di miniature.
Una volta che tutto il codice sorgente è pronto e cablato insieme, l'avvio del processo del server richiede l'invocazione del seguente comando:
$ npm start
Supponendo che tutto sia andato liscio, dovresti vedere il seguente testo apparire sul terminale:
Listening on 5000
Dal momento che non abbiamo implementato alcun vero sistema di autenticazione dell'utente, l'applicazione prototipo si basa solo su un nome utente selezionato dopo aver visitato "http://localhost:5000". Dopo aver inserito un nome utente e inviato il modulo, dovresti essere indirizzato a un'altra pagina con due sezioni: "Film consigliati" e "Tutti i film". Poiché ci manca l'elemento più importante di un motore di raccomandazione basato sulla memoria collaborativo (dati), non saremo in grado di consigliare nessun film a questo nuovo utente.
A questo punto, dovresti aprire un'altra finestra del browser su "http://localhost:5000" e accedere come utente diverso lì. Mi piace e non mi piacciono alcuni film come questo secondo utente. Torna alla finestra del browser del primo utente e valuta anche alcuni film. Assicurati di valutare almeno un paio di film comuni per entrambi gli utenti. Dovresti iniziare a vedere i consigli immediatamente.
Miglioramenti
In questo tutorial sull'algoritmo, quello che abbiamo costruito è un motore di raccomandazione prototipo. Ci sono sicuramente modi per migliorare questo motore. Questa sezione toccherà brevemente alcune aree in cui i miglioramenti sono essenziali affinché questo possa essere utilizzato su larga scala. Tuttavia, nei casi in cui sono richieste scalabilità, stabilità e altre proprietà simili, dovresti sempre ricorrere all'utilizzo di una buona soluzione testata nel tempo. Come il resto dell'articolo, l'idea qui è di fornire alcune informazioni su come funziona un motore di suggerimenti. Invece di discutere gli ovvi difetti del metodo attuale (come la race condition in alcuni dei metodi che abbiamo implementato), i miglioramenti verranno discussi a un livello superiore.
Un miglioramento molto evidente qui è utilizzare un database reale, invece della nostra soluzione basata su file. La soluzione basata su file può funzionare bene in un prototipo su piccola scala, ma non è affatto una scelta ragionevole per un uso reale. Un'opzione tra le tante è Redis. Redis è veloce e ha funzionalità speciali utili quando si tratta di strutture di dati simili a set.
Un altro problema che possiamo semplicemente aggirare è il fatto che stiamo calcolando nuovi consigli ogni volta che un utente effettua o modifica le proprie valutazioni per i film. Invece di eseguire ricalcoli al volo in tempo reale, dovremmo mettere in coda queste richieste di aggiornamento dei consigli per gli utenti ed eseguirle dietro le quinte, magari impostando un intervallo di aggiornamento temporizzato.
Oltre a queste scelte “tecniche”, ci sono anche alcune scelte strategiche che possono essere fatte per migliorare le raccomandazioni. Man mano che il numero di articoli e di utenti cresce, diventerà sempre più costoso (in termini di tempo e risorse di sistema) generare consigli. È possibile renderlo più veloce scegliendo solo un sottoinsieme di utenti da cui generare consigli, invece di elaborare l'intero database ogni volta. Ad esempio, se si trattava di un motore di suggerimenti per i ristoranti, è possibile limitare l'insieme di utenti simili per contenere solo gli utenti che vivono nella stessa città o stato.
Altri miglioramenti possono comportare l'adozione di un approccio ibrido, in cui i consigli vengono generati in base sia al filtro collaborativo che al filtro basato sul contenuto. Ciò sarebbe particolarmente utile con contenuti come i film, in cui le proprietà del contenuto sono ben definite. Netflix, ad esempio, segue questa strada, consigliando film basati sia sulle attività degli altri utenti che sugli attributi dei film.
Conclusione
Gli algoritmi del motore di raccomandazione collaborativo basati sulla memoria possono essere una cosa piuttosto potente. Quello che abbiamo sperimentato in questo articolo può essere primitivo, ma è anche semplice: semplice da capire e semplice da costruire. Potrebbe essere tutt'altro che perfetto, ma implementazioni solide di motori di raccomandazione, come Recommendable, sono basate su idee fondamentali simili.
Come la maggior parte degli altri problemi di informatica che coinvolgono molti dati, ottenere consigli corretti è molto importante per scegliere l'algoritmo giusto e gli attributi appropriati del contenuto su cui lavorare. Spero che questo articolo ti abbia dato un'idea di ciò che accade all'interno di un motore di raccomandazione basato sulla memoria collaborativa quando lo stai utilizzando.