Beğenileri Tahmin Etme: Basit Bir Öneri Motorunun Algoritmalarının İçinde
Yayınlanan: 2022-03-11Bir öneri motoru (bazen öneri sistemi olarak anılır), algoritma geliştiricilerinin bir kullanıcının belirli öğeler listesinden neyi sevip neyi sevmeyeceğini tahmin etmelerini sağlayan bir araçtır. Öneri motorları, kullanıcıların başka türlü karşılaşamayacakları ürünleri veya içeriği keşfetmelerine yardımcı olduğundan, öneri motorları arama alanlarına oldukça ilginç bir alternatiftir. Bu, öneri motorlarını Facebook, YouTube, Amazon ve daha fazlası gibi web sitelerinin ve hizmetlerin büyük bir parçası haline getirir.
Öneri motorları ideal olarak iki yoldan biriyle çalışır. Kullanıcının başka neleri beğenebileceğini belirlemek için analiz edilen, kullanıcının beğendiği öğelerin özelliklerine güvenebilir; veya tavsiye motorunun daha sonra kullanıcılar arasında bir benzerlik indeksi hesaplamak ve onlara buna göre öğeler önermek için kullandığı diğer kullanıcıların beğeni ve beğenmemelerine güvenebilir. Çok daha sağlam bir öneri motoru oluşturmak için bu iki yöntemi birleştirmek de mümkündür. Ancak, diğer tüm bilgi ile ilgili problemler gibi, ele alınan probleme uygun bir algoritma seçmek esastır.
Bu öğreticide, işbirliğine dayalı ve bellek tabanlı bir öneri motoru oluşturma sürecinde size yol göstereceğiz. Bu öneri motoru, kullanıcılara beğendikleri ve beğenmedikleri şeylere göre film önerecek ve daha önce bahsedilen ikinci örnek gibi çalışacaktır. Bu proje için temel küme işlemleri, biraz matematik ve Node.js/CoffeeScript kullanacağız. Bu eğitimle ilgili tüm kaynak kodları burada bulunabilir.
Kümeler ve Denklemler
İşbirliğine dayalı bellek tabanlı bir öneri motorunu uygulamadan önce, böyle bir sistemin arkasındaki ana fikri anlamamız gerekir. Bu motor için her öğe ve her kullanıcı tanımlayıcılardan başka bir şey değildir. Bu nedenle, öneriler üretirken bir filmin başka hiçbir özelliğini (örneğin oyuncu kadrosu, yönetmen, tür vb.) dikkate almayacağız. İki kullanıcı arasındaki benzerlik, -1.0 ile 1.0 arasında bir ondalık sayı kullanılarak temsil edilir. Bu sayıya benzerlik indeksi diyeceğiz. Son olarak, bir kullanıcının bir filmi beğenme olasılığı -1.0 ile 1.0 arasında başka bir ondalık sayı kullanılarak temsil edilecektir. Artık basit terimler kullanarak dünyayı bu sistem etrafında modellediğimize göre, bu tanımlayıcılar ve sayılar arasındaki ilişkiyi tanımlamak için bir avuç zarif matematiksel denklemi açığa çıkarabiliriz.
Öneri algoritmamızda bir dizi kümeyi koruyacağız. Her kullanıcının iki seti olacaktır: kullanıcının beğendiği bir film seti ve kullanıcının beğenmediği bir film seti. Ayrıca her filmin kendisiyle ilişkilendirilmiş iki grubu olacaktır: filmi beğenen bir kullanıcı grubu ve filmi beğenmeyen bir kullanıcı grubu. Önerilerin üretildiği aşamalarda, çoğunlukla diğer kümelerin birleşimleri veya kesişimleri olmak üzere bir dizi küme üretilecektir. Ayrıca her kullanıcı için öneri ve benzer kullanıcı listeleri de sipariş etmiş olacağız.
Benzerlik indeksini hesaplamak için Jaccard indeks formülünün bir varyasyonunu kullanacağız. Orijinal olarak “communaute katsayısı” (Paul Jaccard tarafından icat edilmiştir) olarak bilinen formül, iki kümeyi karşılaştırır ve 0 ile 1.0 arasında basit bir ondalık istatistik üretir:
Formül, herhangi bir kümedeki ortak öğelerin sayısının her iki kümedeki tüm öğelerin sayısına (sadece bir kez sayılır) bölünmesini içerir. İki özdeş kümenin Jaccard indeksi her zaman 1 olacak, ortak elemanı olmayan iki kümenin Jaccard indeksi ise her zaman 0 verecek. Artık iki kümeyi nasıl karşılaştıracağımızı bildiğimize göre, iki kümeyi karşılaştırmak için kullanabileceğimiz bir strateji düşünelim. kullanıcılar. Daha önce tartışıldığı gibi, sistem açısından kullanıcılar üç şeydir: bir tanımlayıcı, bir dizi beğenilen film ve bir dizi beğenilmeyen film. Kullanıcılarımızın benzerlik indeksini sadece beğendikleri film setine göre tanımlayacak olsaydık, doğrudan Jaccard indeks formülünü kullanabilirdik:
Burada U1 ve U2 karşılaştırdığımız iki kullanıcı ve L1 ve L2 sırasıyla U1 ve U2'nin beğendiği film grupları. Şimdi düşünürseniz, aynı filmi beğenen iki kullanıcı benzerse, aynı filmi beğenmeyen iki kullanıcı da benzer olmalıdır. Denklemi biraz değiştirdiğimiz yer burası:
Formül payında sadece yaygın beğenileri dikkate almak yerine, şimdi yaygın beğenmemelerin sayısını da ekliyoruz. Paydada, kullanıcının beğendiği veya beğenmediği tüm öğelerin sayısını alırız. Artık hem hoşlananları hem de hoşlanmayanları bağımsız bir şekilde ele aldığımıza göre, iki kullanıcının tercihlerinde zıt kutuplar olduğu durumu da düşünmeliyiz. Birinin bir filmi beğendiği ve diğerinin beğenmediği iki kullanıcının benzerlik indeksi 0 olmamalıdır:
Bu uzun bir formül! Ama çok basit, söz veriyorum. Payda küçük bir farkla önceki formülümüze benzer. Şimdi, iki kullanıcının ortak beğeni ve beğenmeme sayısından çakışan beğeni ve beğenmeme sayısını çıkarıyoruz. Bu, benzerlik indeksi formülünün -1.0 ile 1.0 arasında bir değer aralığına sahip olmasına neden olur. Aynı zevklere sahip iki kullanıcının benzerlik indeksi 1.0 olurken, film zevkleri tamamen çelişen iki kullanıcının benzerlik indeksi -1.0 olacaktır.
Artık iki kullanıcıyı film zevklerine göre nasıl karşılaştıracağımızı bildiğimize göre, ev yapımı öneri motoru algoritmamızı uygulamaya başlamadan önce bir formül daha keşfetmemiz gerekiyor:
Bu denklemi biraz parçalayalım. P(U,M)
ile kastettiğimiz, bir U
kullanıcısının M
filmini beğenme olasılığıdır. ZL
ve ZD
, sırasıyla M
filmini beğenen veya beğenmeyen tüm kullanıcılarla U
kullanıcısının benzerlik indekslerinin toplamıdır. |ML|+|MD|
M
filmini beğenen veya beğenmeyen toplam kullanıcı sayısını temsil eder. P(U,M)
sonucu -1.0 ile 1.0 arasında bir sayı üretir.
Bununla ilgili. Bir sonraki bölümde, ortak bellek tabanlı öneri motorumuzu uygulamaya başlamak için bu formülleri kullanabiliriz.
Öneri Motorunu Oluşturma
Bu öneri motorunu çok basit bir Node.js uygulaması olarak oluşturacağız. Ayrıca ön uçta çok az çalışma olacak, çoğunlukla bazı HTML sayfaları ve formları olacak (sayfaların düzgün görünmesi için Bootstrap kullanacağız). Sunucu tarafında CoffeeScript kullanacağız. Uygulamanın birkaç GET ve POST yolu olacaktır. Uygulamada kullanıcı kavramına sahip olmamıza rağmen, ayrıntılı bir kayıt/giriş mekanizmasına sahip olmayacağız. Kalıcılık için, bir uygulamanın verileri düz JSON dosyalarında saklamasını ve bunlar üzerinde temel veritabanı sorguları gerçekleştirmesini sağlayan NPM aracılığıyla sağlanan Bourne paketini kullanacağız. Rotaları ve işleyicileri yönetme sürecini kolaylaştırmak için Express.js'yi kullanacağız.
Bu noktada, Node.js geliştirmede yeniyseniz, bu öğreticiyi takip etmeyi kolaylaştırmak için GitHub deposunu klonlamak isteyebilirsiniz. Diğer Node.js projelerinde olduğu gibi, bir package.json dosyası oluşturarak ve bu proje için gerekli olan bir dizi bağımlılık paketini kurarak başlayacağız. Klonlanmış depoyu kullanıyorsanız, package.json dosyası zaten orada olmalıdır, buradan bağımlılıkları yüklemek için “$ npm kurulumunu” yürütmeniz gerekir. Bu, package.json dosyasında listelenen tüm paketleri yükleyecektir.
Bu proje için ihtiyacımız olan Node.js paketleri:
- zaman uyumsuz
- bourne
- kahve yazısı
- ifade etmek
- yeşim taşı
- vurgulamak
Öneri motorunu, ilgili tüm yöntemleri, her biri "lib/engine" altında depolanacak dört ayrı CoffeeScript sınıfına bölerek oluşturacağız: Motor, Değerlendirici, Benzerler ve Öneriler. Engine sınıfı, öneri motoru için basit bir API sağlamaktan sorumlu olacak ve diğer üç sınıfı birbirine bağlayacaktır. Değerlendirici, beğenileri ve beğenileri takip etmekten sorumlu olacaktır (Değerlendirici sınıfının iki ayrı örneği olarak). Benzerler ve Öneriler, sırasıyla benzer kullanıcıları ve kullanıcılar için önerilen öğeleri belirlemek ve takip etmekten sorumlu olacaktır.
Beğenilenleri ve Beğenilmeyenleri İzleme
İlk olarak Değerlendiriciler sınıfımızla başlayalım. Bu basit bir tanesidir:
class Rater constructor: (@engine, @kind) -> add: (user, item, done) -> remove: (user, item, done) -> itemsByUser: (user, done) -> usersByItem: (item, done) ->
Bu öğreticide daha önce belirtildiği gibi, beğeniler için bir Rater örneği ve beğenmeyenler için başka bir örneğine sahip olacağız. Bir kullanıcının bir öğeyi beğendiğini kaydetmek için onları “Rater#add()” öğesine ileteceğiz. Benzer şekilde, derecelendirmeyi kaldırmak için onları “Değerlendiren#kaldır()”a ileteceğiz.
Bourne'u sunucusuz bir veritabanı çözümü olarak kullandığımızdan, bu derecelendirmeleri "./db-#{@kind}.json" adlı bir dosyada saklayacağız, burada tür "beğeniler" veya "beğenmemeler"dir. Veritabanını Rater örneğinin yapıcısı içinde açacağız:
constructor: (@engine, @kind) -> @db = new Bourne "./db-#{@kind}.json"
Bu, derecelendirme kayıtlarının eklenmesini, “Rater#add()” yöntemimiz içinde bir Bourne veritabanı yöntemini çağırmak kadar basit hale getirecektir:
@db.insert user: user, item: item, (err) =>
Bunları kaldırmak da benzerdir (“db.insert” yerine “db.delete”). Ancak, bir şey eklemeden veya çıkarmadan önce, veritabanında mevcut olmadığından emin olmalıyız. İdeal olarak, gerçek bir veritabanı ile bunu tek bir işlem olarak yapabilirdik. Bourne ile önce manuel bir kontrol yapmamız gerekiyor; ve ekleme veya silme işlemi tamamlandıktan sonra, bu kullanıcı için benzerlik indekslerini yeniden hesapladığımızdan emin olmamız ve ardından bir dizi yeni öneri oluşturmamız gerekir. “Rater#add()” ve “Rater#remove()” yöntemleri şuna benzer:
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
Kısaca, hataları kontrol ettiğimiz kısımları atlayacağız. Bu, bir makalede yapılması makul bir şey olabilir, ancak gerçek koddaki hataları yok saymak için bir mazeret değildir.
Bu sınıfın diğer iki yöntemi, "Rater#itemsByUser()" ve "Rater#usersByItem()", adlarının ima ettiği şeyi yapmayı içerir - sırasıyla bir kullanıcı tarafından derecelendirilen öğeler ve bir öğeyi derecelendiren kullanıcılar tarafından aranır. Örneğin, Değerlendirici kind = “likes”
ile başlatıldığında, “Rater#itemsByUser()” kullanıcının derecelendirdiği tüm öğeleri bulacaktır.
Benzer Kullanıcıları Bulma
Bir sonraki sınıfımıza geçiyoruz: Benzerler. Bu sınıf, kullanıcılar arasındaki benzerlik indekslerini hesaplamamıza ve takip etmemize yardımcı olacaktır. Daha önce tartışıldığı gibi, iki kullanıcı arasındaki benzerliği hesaplamak, sevdikleri ve sevmedikleri öğelerin gruplarını analiz etmeyi içerir. Bunu yapmak için, ilgili öğe kümelerini getirmek için Rater örneklerine güveneceğiz ve ardından benzerlik indeksi formülünü kullanarak belirli kullanıcı çiftleri için benzerlik indeksini belirleyeceğiz.
Bir önceki sınıfımız olan Rater gibi her şeyi Rater'ın yapıcısında açacağımız “./db-similars.json” isimli bir Bourne veritabanına koyacağız. Sınıf, basit bir veritabanı araması yoluyla belirli bir kullanıcıya benzer kullanıcıları aramamıza izin verecek olan “Similars#byUser()” yöntemine sahip olacaktır:
@db.findOne user: user, (err, {others}) =>
Ancak, bu sınıfın en önemli yöntemi, bir kullanıcıyı alıp benzer diğer kullanıcıların bir listesini hesaplayarak ve bu listeyi benzerlik indeksleriyle birlikte veritabanında saklayarak çalışan “Similars#update()” yöntemidir. Kullanıcının beğenilerini ve beğenmediklerini bularak başlar:
async.auto userLikes: (done) => @engine.likes.itemsByUser user, done userDislikes: (done) => @engine.dislikes.itemsByUser user, done , (err, {userLikes, userDislikes}) => items = _.flatten([userLikes, userDislikes])
Ayrıca, bu öğeleri derecelendiren tüm kullanıcıları da buluyoruz:

async.map items, (item, done) => async.map [ @engine.likes @engine.dislikes ], (rater, done) => rater.usersByItem item, done , done , (err, others) =>
Daha sonra, bu diğer kullanıcıların her biri için benzerlik indeksini hesaplar ve hepsini veritabanında saklarız:
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
Yukarıdaki pasajda, Jaccard indeks formülünün bir varyantı olan benzerlik indeks formülümüzle doğası gereği aynı olan bir ifadeye sahip olduğumuzu fark edeceksiniz.
Öneriler Oluşturma
Bir sonraki dersimiz, Öneriler, tüm tahminlerin gerçekleştiği yerdir. Benzerler sınıfı gibi, yapıcı içinde açılan “./db-suggestions.json” adlı başka bir Bourne veritabanına güveniyoruz.
Sınıf, verilen kullanıcı için hesaplanmış önerileri aramak için “Öneriler#Kullanıcı İçin()” yöntemine sahip olacaktır:
forUser: (user, done) -> @db.findOne user: user, (err, {suggestions}={suggestion: []}) -> done null, suggestions
Bu sonuçları hesaplayacak yöntem “Öneriler#güncelleme()”dir. “Similars#update()” gibi bu yöntem bir kullanıcıyı argüman olarak alacaktır. Yöntem, belirtilen kullanıcıya benzer tüm kullanıcıları ve verilen kullanıcının derecelendirmediği tüm öğeleri listeleyerek başlar:
@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
Diğer tüm kullanıcıları ve derecelendirilmemiş öğeleri listeledikten sonra, önceki önerileri kaldırarak, her öğeyi yineleyerek ve mevcut bilgilere dayanarak kullanıcının onu beğenme olasılığını hesaplayarak yeni bir öneri kümesini hesaplamaya başlayabiliriz:
@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) =>
Bu yapıldıktan sonra, onu veritabanına geri kaydediyoruz:
@db.insert user: user suggestions: suggestions , done
Kitaplık API'sini kullanıma sunma
Engine sınıfının içinde, dış dünyadan kolay erişim için her şeyi düzgün bir API benzeri yapı içinde birleştiririz:
class Engine constructor: -> @likes = new Rater @, 'likes' @dislikes = new Rater @, 'dislikes' @similars = new Similars @ @suggestions = new Suggestions @
Bir Engine nesnesini başlattığımızda:
e = new Engine
Beğenileri ve beğenmemeleri kolayca ekleyebilir veya kaldırabiliriz:
e.likes.add user, item, (err) -> e.dislikes.add user, item, (err) ->
Ayrıca kullanıcı benzerlik indekslerini ve önerilerini güncellemeye başlayabiliriz:
e.similars.update user, (err) -> e.suggestions.update user, (err) ->
Son olarak, bu Engine sınıfını (ve diğer tüm sınıfları) ilgili ".coffee" dosyalarından dışa aktarmak önemlidir:
module.exports = Engine
Ardından, tek satırlık bir “index.coffee” dosyası oluşturarak Engine'i paketten dışa aktarın:
module.exports = require './engine'
Kullanıcı Arayüzü Oluşturma
Bu eğitimde öneri motoru algoritmasını kullanabilmek için web üzerinden basit bir kullanıcı arayüzü sağlamak istiyoruz. Bunu yapmak için, "web.iced" dosyamızın içinde bir Express uygulaması oluşturduk ve birkaç rotayı ele aldık:
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] )
Uygulama içinde dört rotayı ele alıyoruz. "/" dizin yolu, bir Jade şablonu oluşturarak ön uç HTML'yi sunduğumuz yerdir. Şablonun oluşturulması, filmlerin bir listesini, mevcut kullanıcının kullanıcı adını, kullanıcının beğenilerini ve beğenmediklerini ve kullanıcı için ilk dört öneriyi gerektirir. Jade şablonunun kaynak kodu makalenin dışında bırakılmıştır, ancak GitHub deposunda mevcuttur.
“/like” ve “/dislike” yolları, kullanıcının beğenilerini ve beğenmediklerini kaydetmek için POST isteklerini kabul ettiğimiz yerlerdir. Her iki rota da, önce gerekirse çakışan derecelendirmeleri kaldırarak bir derecelendirme ekler. Örneğin, daha önce hoşlanmadığı bir şeyi beğenen bir kullanıcı, işleyicinin önce "beğenmeme" derecesini kaldırmasına neden olur. Bu rotalar ayrıca, kullanıcının istenirse bir öğeyi "beğenmemesine" veya "beğenmemesinden" vazgeçmesine izin verir.
Son olarak, "/refresh" yolu, kullanıcının istek üzerine önerilerini yeniden oluşturmasına olanak tanır. Bununla birlikte, bu eylem, kullanıcı bir öğeye herhangi bir derecelendirme yaptığında otomatik olarak gerçekleştirilir.
Test sürüşü
Bu makaleyi izleyerek bu uygulamayı sıfırdan uygulamaya çalıştıysanız, test etmeden önce son bir adımı gerçekleştirmeniz gerekecektir. "data/movies.json"da bir ".json" dosyası oluşturmanız ve aşağıdaki gibi bazı film verileriyle doldurmanız gerekir:
[ { "id": "1", "name": "Transformers: Age of Extinction", "thumb": { "url": "//upload.wikimedia.org/wikipedia/en/7/7f/Inception_ver3.jpg" } }, // … ]
Bir avuç film adı ve küçük resim URL'si ile önceden doldurulmuş GitHub deposunda bulunanı kopyalamak isteyebilirsiniz.
Tüm kaynak kodu hazır olduğunda ve birbirine bağlandıktan sonra, sunucu işleminin başlatılması için aşağıdaki komutun çağrılması gerekir:
$ npm start
Her şeyin yolunda gittiğini varsayarsak, terminalde aşağıdaki metnin göründüğünü görmelisiniz:
Listening on 5000
Herhangi bir gerçek kullanıcı kimlik doğrulama sistemi uygulamadığımız için, prototip uygulama yalnızca “http://localhost:5000” ziyaret edildikten sonra seçilen bir kullanıcı adına dayanır. Bir kullanıcı adı girildikten ve form gönderildikten sonra, “Önerilen Filmler” ve “Tüm Filmler” olmak üzere iki bölümden oluşan başka bir sayfaya yönlendirilmelisiniz. Ortak bellek tabanlı bir öneri motorunun (veri) en önemli unsuruna sahip olmadığımız için, bu yeni kullanıcıya herhangi bir film öneremeyeceğiz.
Bu noktada “http://localhost:5000” olarak başka bir tarayıcı penceresi açmalı ve oradan farklı bir kullanıcı olarak giriş yapmalısınız. Bu ikinci kullanıcı olarak bazı filmleri beğenin ve beğenmeyin. İlk kullanıcının tarayıcı penceresine dönün ve bazı filmleri de değerlendirin. Her iki kullanıcı için en az birkaç yaygın filmi değerlendirdiğinizden emin olun. Önerileri hemen görmeye başlamalısınız.
İyileştirmeler
Bu algoritma eğitiminde, oluşturduğumuz şey bir prototip öneri motorudur. Bu motoru geliştirmenin kesinlikle yolları var. Bu bölüm, bunun geniş ölçekte kullanılması için iyileştirmelerin gerekli olduğu bazı alanlara kısaca değinecektir. Bununla birlikte, ölçeklenebilirlik, kararlılık ve benzeri diğer özelliklerin gerekli olduğu durumlarda, her zaman zaman içinde test edilmiş iyi bir çözüme başvurmalısınız. Makalenin geri kalanı gibi, buradaki fikir de bir öneri motorunun nasıl çalıştığına dair bir fikir vermektir. Mevcut yöntemin bariz kusurlarını (uyguladığımız bazı yöntemlerdeki yarış durumu gibi) tartışmak yerine, iyileştirmeler daha üst düzeyde tartışılacaktır.
Buradaki çok belirgin bir gelişme, dosya tabanlı çözümümüz yerine gerçek bir veritabanı kullanmaktır. Dosya tabanlı çözüm, küçük ölçekte bir prototipte iyi çalışabilir, ancak gerçek kullanım için hiç de makul bir seçim değildir. Pek çok seçenek arasından biri Redis'tir. Redis hızlıdır ve küme benzeri veri yapılarıyla uğraşırken yararlı olan özel yeteneklere sahiptir.
Basitçe çözebileceğimiz bir diğer sorun da, bir kullanıcı filmler için derecelendirmelerini her yaptığında veya değiştirdiğinde yeni öneriler hesaplıyor olmamızdır. Gerçek zamanlı olarak anında yeniden hesaplamalar yapmak yerine, bu öneri güncelleme isteklerini kullanıcılar için sıraya koymalı ve sahne arkasında gerçekleştirmeliyiz - belki de zamanlanmış bir yenileme aralığı ayarlayarak.
Bu “teknik” seçimlerin yanı sıra, önerileri geliştirmek için yapılabilecek bazı stratejik seçimler de vardır. Öğelerin ve kullanıcıların sayısı arttıkça, öneriler oluşturmak (zaman ve sistem kaynakları açısından) giderek daha maliyetli hale gelecektir. Her seferinde veritabanının tamamını işlemek yerine, öneriler oluşturmak için yalnızca bir kullanıcı alt kümesini seçerek bunu daha hızlı hale getirmek mümkündür. Örneğin, bu restoranlar için bir öneri motoruysa, benzer kullanıcı grubunu yalnızca aynı şehirde veya eyalette yaşayan kullanıcıları içerecek şekilde sınırlayabilirsiniz.
Diğer iyileştirmeler, önerilerin hem işbirlikçi filtrelemeye hem de içerik tabanlı filtrelemeye dayalı olarak oluşturulduğu hibrit bir yaklaşım benimsemeyi içerebilir. Bu, özellikle içeriğin özelliklerinin iyi tanımlandığı filmler gibi içerikler için iyi olur. Örneğin Netflix, hem diğer kullanıcıların etkinliklerine hem de filmlerin özelliklerine göre filmler önererek bu yolu izliyor.
Çözüm
Bellek tabanlı işbirlikçi öneri motoru algoritmaları oldukça güçlü bir şey olabilir. Bu makalede denediğimiz ilkel olabilir, ancak aynı zamanda basit: anlaması basit ve inşa etmesi basit. Mükemmel olmaktan uzak olabilir, ancak Tavsiye Edilebilir gibi öneri motorlarının sağlam uygulamaları benzer temel fikirler üzerine kuruludur.
Çok sayıda veri içeren diğer bilgisayar bilimi problemlerinin çoğu gibi, doğru öneriler almak, üzerinde çalışılacak içeriğin doğru algoritmasını ve uygun niteliklerini seçmekle ilgilidir. Umarım bu makale, ortak bellek tabanlı bir öneri motorunu kullandığınızda içinde neler olduğuna dair bir fikir vermiştir.