Implicit Feedback ile Tavsiye Motoru Tasarımı

Tavsiye sistemleri ya da öneri motorları, kullanıcı verileri üzerinden analizler yaparak kullanıcının ilgisini çekebilecek farklı kitap, video veya iş gibi alanlar üzerine önerilerde bulunan bir yazılımdır.

İçinde bulunduğumuz büyük veri çağında tavsiye sistemleri; e-ticaret, içerik odaklı platformlar ve sosyal medya siteleri başta olmak üzere birçok çevrimiçi hizmet tarafından tercih edilmektedir. Sanal dünyada yönlendirmeye ihtiyaç duyulan her platformda gittikçe artan tavsiye motorlarının fonksiyonları, örneğin konforlu bir alışveriş deneyimi sağlamanın yanı sıra ilgimizi daha çok çekebilecek içerikleri bizlere hizmet olarak servis eder.

İş Birlikçi Filtre (Collaborative Filtering)

Tavsiye motorlarının ilk geliştirildiği dönemlerden itibaren yaklaşım olarak benimsedikleri yöntemlerin en başında içeriğe dayalı filtreleme (content-based filtering) ve işbirlikçi filtreleme (collaborative filtering) gelmektedir. Her iki yöntemde de baz alınan ürün üzerinden benzerlik grupları çıkartılarak tavsiyeler üretilir.

İşbirliğine dayalı filtreleme (içeriğe dayalı filtrelemeye karşı), bir öğeyi başka kimin sevdiği, görüntülediği, görmezden geldiği veya bir şekilde tükettiği dışında hiçbir şeyi önemsemediğimiz anlamına gelir. Her ikisi de blues şarkısı olduğu için öğelerin benzer olup olmadığı umurumuzda değil, ancak benzer bir kullanıcı grubu tarafından dinlendikleri için (sert blues hayranları olan veya olmayan).

User-Based Collaborative Filtering; İnsanlar geçmişte benzer ilgi alanlarına sahipse, gelecekte de benzer ilgi alanlarına sahip olacaklardır. Bu yüzden algoritma kullanıcıları kendi aralarında gruplar ve aynı grup içerisindeki farklı kullanıcılara benzer ürünler tavsiye eder.

Item-Based Collaborative Filtering ise öğeler arasındaki benzerlik temel alınarak farklı ürünlerin tavsiye edilmesi yöntemidir.

PySpark, Matrix Factorization ve ALS

Bu aşamada ilk olarak bazı terimlere açıklık getirmekte fayda var. Öncelikle tavsiye motorlarında kullanılan veri setlerinin hacimleri göz önünde bulundurulduğunda, bir scikit-learn data yapısı olan DataFrame’ler bizim için oldukça yavaş kalmakta.

Bu sebeple büyük veriyi analiz etmek ve işlemek için birçok farklı alternatif (Dask, …, … vb.) arasından tercih edilebilecek PySpark çalışma prensibi sayesinde datanın oldukça hızlı işlenebilmesi üzerine büyük avantajlar sunar.

Açıklamak gerekirse Apache Spark; hız, kullanım kolaylığı ve akış analizi etrafında oluşturulmuş açık kaynaklı bir küme hesaplama çerçevesidir. Python ise genel amaçlı, yüksek seviyeli bir programlama dilidir. Python geniş bir kütüphane yelpazesi sağlar ve büyük ölçüde makine öğrenimi ve gerçek zamanlı akış analizi için kullanılır.

Başka bir deyişle, Big Data’yı işlemek için Python’un basitliğinden ve Apache Spark’ın gücünden yararlanmanızı sağlayan Spark için bir Python API’sidir.

Bir collaborative filtering için sayılabilecek bazı temel problemler popülerlik yanlılığı (popularity bias), soğuk başlama sorunu (item cold-start problem) ve ölçeklenebilirlik sorunu (scalability issues) olarak değerlendirilebilir. Bu problemler, datanın kendisiyle birlikte gelen problemlerdir.

Kullanıcıların ve okudukları kitapların bir matriste toplandığı durumda, matrisin geneli NaN değerini içerecektir. Özellikle daha büyük hacimli data setlerinde bu durum seyrek (sparse) data olarak adlandırılır ve bir tavsiye motoru geliştirilirken aşılması gereken önemli bir engeli oluşturur.

Aşağıdaki örnekte de %99’u eksik değerlerle oluşturulmuş oldukça sparse bir matrisi görüyoruz. Neyse ki sparsity problemini aşmak için alternatif yollarımız var !

Ayrıştırması (Matrix Factorization)

Matris ayrıştırması, collaborative filtrelerde veri seyrekliğini elimine etmek için geliştirilmiş state-of-the-art (son teknoloji) seviyesinde bir çözümdür.

Spesifik olmak gerekirse, bir matris çarpanlarına ayırma, bir matrisin bir matris çarpımı haline getirilmesidir. İşbirliğine dayalı filtreleme durumunda, matris çarpanlara ayırma algoritmaları, kullanıcı-öğe etkileşim matrisini iki düşük boyutlu dikdörtgen matrisin ürününe ayrıştırarak çalışır. Bir matris, satırların kullanıcıları temsil ettiği ve sütunların gizli faktörler olduğu kullanıcı matrisi olarak görülebilir. Diğer matris, satırların gizli faktörler olduğu ve sütunların öğeleri temsil ettiği öğe matrisidir.

ALS (Alternatif En Küçük Kareler)

ALS aynı zamanda bir matris çarpanlara ayırma algoritmasıdır ve kendisini paralel bir şekilde çalıştırır. ALS, Apache Spark ML’de uygulanır ve büyük ölçekli işbirliğine dayalı filtreleme sorunları için oluşturulmuştur. ALS, Puan verilerinin ölçeklenebilirliğini ve seyrekliğini çözmede oldukça iyi bir iş çıkarmaktadır ve basittir ve çok büyük veri kümelerine iyi ölçeklenir.

ALS alternatif olarak iki kayıp fonksiyonunu en aza indirir. Önce kullanıcı matrisini sabit tutar (U) ve öğe matrisiyle (R) gradyan inişini çalıştırır daha sonra öğe matrisini sabit tutar ve kullanıcı matrisiyle gradyan inişini çalıştırır.

Diğer makine öğrenimi algoritmaları gibi, ALS’nin de kendi hyper-parametreleri vardır. Bu algoritmada kullanılan en önemli hyper-parametreler:

maxIter: Çalıştırılacak maksimum yineleme sayısı (varsayılan değeri 10’dur)

rank: modeldeki gizli faktörlerin sayısı (varsayılan değeri 10’dur)

regParam: ALS’deki normalleştirme parametresi (varsayılan değeri 1.0’dır)

Hyper-parametre ayarı, birçok makine öğrenimi projesinde oldukça tekrar eden bir görevdir. Ayar yinelemelerini hızlandırmak ve optimum parametreleri bulmak için GridSearchCV ya da RandomSearchCV yöntemleri kullanılabilir.

Örnek Uygulama

Gerekli kütüphaleri import ederek başlayalım. Uygulama Colab’de geliştirildiği için google kütüphanesinde eklenmesi gerekli;

  1. import warnings
  2. warnings.simplefilter(action='ignore', category= Warning)
  3. from pyspark.ml.recommendation importALS
  4. from pyspark.sql import SparkSession,SQLContext
  5. from pyspark.sql.functions import desc
  6. from pyspark.ml.feature import StringIndexer
  7. from pyspark.ml import Pipeline
  8. from google.colab import files
  9. from google.colab import drive
  10. drive.mount("/content/drive")
  11. import logging
  12. logging.getLogger("py4j").setLevel(logging.ERROR)

Bu uygulamada, verimizin büyüklüğünden dolayı PySpark’ı kullanmayı tercih ettik ve bu sebepten dolayı bir Spark Session tanıtmamız gerekiyor;

Ardından recommendation için kullanacağımız veriyi spark’ın read fonksiyonunu kullanarak tanıtıyoruz. Burada dikkat edeceğimiz bir nokta, options’ın altında header değişkeninin True ya da False olarak tanımlanması. Bu parametreyi True seçtiğimizde, datanın içinde tanımlı olan sütun başlıkları varsa, onlar da datayla beraber otomatik alınır. Biz bu uygulama için bu değişkeni False olarak tutacağız ve sütun isimlerinin bu durumda otomatik olarak _cx , x = {0, n}, n : total feature number şeklinde tanımlanır. Burada n, data setindeki toplam feature sayısıdır.

  1. #Read and Show the data
  2. #------------
  3. df = spark.read \
  4.   .options(header = False) \
  5.   .csv(('Processed_Data.csv'))

ALS yöntemi için özellikle kullanıcı (user)  ve ürün (item) isimli sütunları vermemiz gerekiyor, bu sebepten _c3 ve _c5 sütunlarının isimlerini user ve item olarak değiştiriyoruz.

Ardından data setimiz herhangi bir explicit feedback (rate gibi) içermediğinden, her bir kullanıcının  ziyaret ettiği kitapların sıklıklarından implicit feedback oluşturarak, rating adı altında algoritmaya veriyoruz.

  1. df = df.withColumnRenamed("_c3", "user")
  2. df = df.withColumnRenamed("_c5", "item")
  3. df = df.groupBy("user", "item", "_c6", "_c7", "_c8", "_c9").count().orderBy(desc('count'))
  4. df = df.withColumnRenamed("count", "rating")
  5. df.show(10, truncate=False
  6. #Filter data to eliminate seldom visiters
  7. df = df.filter(df.rating > 10)

Ancak verilerimiz, ALS’ye verilmek için henüz hazır değil. ALS yöntemi string kabul etmediğinden, veriyi indexlere çevirmemiz gerekli.

  1. #String Indexer
  2. #--------------
  3. stages = []
  4. categoricalColumns = [item[0] for item in df.dtypes if item[1].startswith('string')]
  5.  
  6. for categoricalCol in categoricalColumns:
  7.   #create a string indexer for those categorical values and assign a new name including the word'Index'
  8.   stringIndexer = StringIndexer(inputCol = categoricalCol, outputCol = categoricalCol + 'Index', handleInvalid= "keep")
  9.  
  10.   #Append the string Indexer to our list of stages
  11.   stages += [stringIndexer]
  12.  
  13. #Create the pipeline. Assign the stages list to the pipeline key word stages
  14. pipeline = Pipeline(stages = stages)
  15. #fit the pipeline to our dataframe
  16. pipelineModel = pipeline.fit(df)
  17. #transform the dataframe
  18. df = pipelineModel.transform(df)

Ardından elimizdeki veriyi %70 ve %30 oranlarında train ve test olarak bölüyoruz,  verinin parse edilmesinin random olarak yapılmaması için seed’i 0 olarak besledik ki;

  1. (train, test) = df.randomSplit([0.7, 0.3], seed = 0)
  2. #Total number of the data that we divided
  3. print("Training :", train.count(), "test : ", test.count())
  4. train.show(10, truncate=False)

Artık train ve test setlerimizi de hazırladığımıza göre ALS yöntemini deneyebiliriz. Bunun için ilk adımda ALS yönteminin hyper-parametrelerini algoritmaya besleyip ardından train setimizle de fit etmemiz gerekiyor;

  1. #Model Training
  2. als = ALS(rank= 5, maxIter= 5, alpha = float(10), implicitPrefs=True, seed=0)
  3. model = als.fit(train)

Bu adımdan sonra test için ayırdığımız verilerle algoritmanın bize neler önereceğini görmek kalıyor;

  1. model.userFactors.orderBy("id").collect()
  2. predictions = sorted(model.transform(test).collect(), key=lambda r: r[0])
  3. user_recs = model.recommendForAllUsers(10)
  4. user_recs.where(user_recs.user == 0).select("recommendations.item", "recommendations.rating").collect()

Kodların tamamına ulaşmak için: https://github.com/kubra1tas/Book-Recommendation-with-PySpark-ALS-

İyi eğlenceler!

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir