2016-05-16 10 views
9

Sto tentando di ottimizzare i parametri di un modello di fattorizzazione della matrice ALS che utilizza dati impliciti. Per questo, sto cercando di utilizzare pyspark.ml.tuning.CrossValidator per scorrere attraverso una griglia di parametri e selezionare il modello migliore. Credo che il mio problema sia nel valutatore, ma non riesco a capirlo.Parametri di ottimizzazione per implicita pyspark.ml Modello di fattorizzazione della matrice ALS tramite pyspark.ml CrossValidator

posso ottenere questo a lavorare per un modello di dati esplicita con un valutatore di regressione RMSE, come segue:

from pyspark import SparkConf, SparkContext 
from pyspark.sql import SQLContext 
from pyspark.ml.recommendation import ALS 
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder 
from pyspark.ml.evaluation import BinaryClassificationEvaluator 
from pyspark.ml.evaluation import RegressionEvaluator 

from pyspark.sql.functions import rand 


conf = SparkConf() \ 
    .setAppName("MovieLensALS") \ 
    .set("spark.executor.memory", "2g") 
sc = SparkContext(conf=conf) 

sqlContext = SQLContext(sc) 

dfRatings = sqlContext.createDataFrame([(0, 0, 4.0), (0, 1, 2.0), (1, 1, 3.0), (1, 2, 4.0), (2, 1, 1.0), (2, 2, 5.0)], 
           ["user", "item", "rating"]) 
dfRatingsTest = sqlContext.createDataFrame([(0, 0), (0, 1), (1, 1), (1, 2), (2, 1), (2, 2)], ["user", "item"]) 

alsExplicit = ALS() 
defaultModel = alsExplicit.fit(dfRatings) 

paramMapExplicit = ParamGridBuilder() \ 
        .addGrid(alsExplicit.rank, [8, 12]) \ 
        .addGrid(alsExplicit.maxIter, [10, 15]) \ 
        .addGrid(alsExplicit.regParam, [1.0, 10.0]) \ 
        .build() 

evaluatorR = RegressionEvaluator(metricName="rmse", labelCol="rating") 

cvExplicit = CrossValidator(estimator=alsExplicit, estimatorParamMaps=paramMapExplicit, evaluator=evaluatorR) 
cvModelExplicit = cvExplicit.fit(dfRatings) 

predsExplicit = cvModelExplicit.bestModel.transform(dfRatingsTest) 
predsExplicit.show() 

Quando provo a fare questo per i dati impliciti (diciamo conti di vista piuttosto che feedback) , Ricevo un errore che non riesco a capire. Ecco il codice (molto simile al precedente):

dfCounts = sqlContext.createDataFrame([(0,0,0), (0,1,12), (0,2,3), (1,0,5), (1,1,9), (1,2,0), (2,0,0), (2,1,11), (2,2,25)], 
           ["user", "item", "rating"]) 
dfCountsTest = sqlContext.createDataFrame([(0, 0), (0, 1), (1, 1), (1, 2), (2, 1), (2, 2)], ["user", "item"]) 

alsImplicit = ALS(implicitPrefs=True) 
defaultModelImplicit = alsImplicit.fit(dfCounts) 

paramMapImplicit = ParamGridBuilder() \ 
        .addGrid(alsImplicit.rank, [8, 12]) \ 
        .addGrid(alsImplicit.maxIter, [10, 15]) \ 
        .addGrid(alsImplicit.regParam, [1.0, 10.0]) \ 
        .addGrid(alsImplicit.alpha, [2.0,3.0]) \ 
        .build() 

evaluatorB = BinaryClassificationEvaluator(metricName="areaUnderROC", labelCol="rating") 
evaluatorR = RegressionEvaluator(metricName="rmse", labelCol="rating") 

cv = CrossValidator(estimator=alsImplicit, estimatorParamMaps=paramMapImplicit, evaluator=evaluatorR) 
cvModel = cv.fit(dfCounts) 

predsImplicit = cvModel.bestModel.transform(dfCountsTest) 
predsImplicit.show() 

Ho provato a fare questo con un valutatore RMSE ed ottengo un errore. Come ho capito, dovrei anche essere in grado di utilizzare la metrica AUC per il valutatore di classificazione binaria, perché le previsioni della fattorizzazione implicita della matrice sono una matrice di confidenza c_ui per le previsioni di una matrice binaria p_ui per this paper, che cita la documentazione per PSSpark ALS.

L'utilizzo di uno dei due valutatori mi dà un errore e non riesco a trovare alcuna discussione fruttuosa sui modelli ALS impliciti di cross-validazione online. Sto cercando attraverso il codice sorgente CrossValidator per cercare di capire cosa c'è che non va, ma sto avendo problemi. Uno dei miei pensieri è che dopo che il processo converte la matrice di dati implicita r_ui nella matrice binaria p_ui e nella matrice di confidenza c_ui, non sono sicuro di cosa stia confrontando la matrice c_ui prevista contro durante la fase di valutazione.

Ecco l'errore:

Traceback (most recent call last): 

    File "<ipython-input-16-6c43b997005e>", line 1, in <module> 
    cvModel = cv.fit(dfCounts) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\ml\pipeline.py", line 69, in fit 
    return self._fit(dataset) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\ml\tuning.py", line 239, in _fit 
    model = est.fit(train, epm[j]) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\ml\pipeline.py", line 67, in fit 
    return self.copy(params)._fit(dataset) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\ml\wrapper.py", line 133, in _fit 
    java_model = self._fit_java(dataset) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\ml\wrapper.py", line 130, in _fit_java 
    return self._java_obj.fit(dataset._jdf) 

    File "C:\spark-1.6.1-bin-hadoop2.6\python\lib\py4j-0.9-src.zip\py4j\java_gateway.py", line 813, in __call__ 
    answer, self.gateway_client, self.target_id, self.name) 

    File "C:/spark-1.6.1-bin-hadoop2.6/python\pyspark\sql\utils.py", line 45, in deco 
    return f(*a, **kw) 

    File "C:\spark-1.6.1-bin-hadoop2.6\python\lib\py4j-0.9-src.zip\py4j\protocol.py", line 308, in get_return_value 
    format(target_id, ".", name), value) 

etc....... 

UPDATE

Ho provato scalare l'ingresso quindi è nell'intervallo da 0 a 1 e utilizzando un valutatore RMSE. Sembra funzionare bene fino a quando non cerco di inserirlo in CrossValidator.

Il seguente codice funziona. Ottengo le previsioni e ottengo un valore RMSE dal mio valutatore.

from pyspark import SparkConf, SparkContext 
from pyspark.sql import SQLContext 
from pyspark.sql.types import FloatType 
import pyspark.sql.functions as F 
from pyspark.ml.recommendation import ALS 
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder 
from pyspark.ml.evaluation import RegressionEvaluator 


conf = SparkConf() \ 
    .setAppName("ALSPractice") \ 
    .set("spark.executor.memory", "2g") 
sc = SparkContext(conf=conf) 

sqlContext = SQLContext(sc) 

# Users 0, 1, 2, 3 - Items 0, 1, 2, 3, 4, 5 - Ratings 0.0-5.0 
dfCounts2 = sqlContext.createDataFrame([(0,0,5.0), (0,1,5.0),   (0,3,0.0), (0,4,0.0), 
             (1,0,5.0),   (1,2,4.0), (1,3,0.0), (1,4,0.0), 
             (2,0,0.0),   (2,2,0.0), (2,3,5.0), (2,4,5.0), 
             (3,0,0.0), (3,1,0.0),   (3,3,4.0)   ], 
             ["user", "item", "rating"]) 

dfCountsTest2 = sqlContext.createDataFrame([(0,0), (0,1), (0,2), (0,3), (0,4), 
              (1,0), (1,1), (1,2), (1,3), (1,4), 
              (2,0), (2,1), (2,2), (2,3), (2,4), 
              (3,0), (3,1), (3,2), (3,3), (3,4)], ["user", "item"]) 

# Normalize rating data to [0,1] range based on max rating 
colmax = dfCounts2.select(F.max('rating')).collect()[0].asDict().values()[0] 
normalize = udf(lambda x: x/colmax, FloatType()) 
dfCountsNorm = dfCounts2.withColumn('ratingNorm', normalize(col('rating'))) 

alsImplicit = ALS(implicitPrefs=True) 
defaultModelImplicit = alsImplicit.fit(dfCountsNorm) 
preds = defaultModelImplicit.transform(dfCountsTest2) 

evaluatorR2 = RegressionEvaluator(metricName="rmse", labelCol="ratingNorm") 
evaluatorR2.evaluate(defaultModelImplicit.transform(dfCountsNorm)) 

preds = defaultModelImplicit.transform(dfCountsTest2) 

Quello che non capisco è il motivo per cui il seguente non funziona. Sto usando lo stesso stimatore, lo stesso valutatore e gli stessi dati. Perché questi lavoro di cui sopra, ma non all'interno del CrossValidator:

paramMapImplicit = ParamGridBuilder() \ 
        .addGrid(alsImplicit.rank, [8, 12]) \ 
        .addGrid(alsImplicit.maxIter, [10, 15]) \ 
        .addGrid(alsImplicit.regParam, [1.0, 10.0]) \ 
        .addGrid(alsImplicit.alpha, [2.0,3.0]) \ 
        .build() 

cv = CrossValidator(estimator=alsImplicit, estimatorParamMaps=paramMapImplicit, evaluator=evaluatorR2) 
cvModel = cv.fit(dfCountsNorm) 
+0

Grazie per aver postato questa domanda. Nella tua modifica, perché stai calcolando il RMSE usando: 'valutatorR2.evaluate (defaultModelImplicit.transform (dfCountsNorm))' Invece di 'valutatorR2.evaluate (defaultModelImplicit.transform (dfCountsTest2))' – Archimeow

risposta

8

Ignorando i problemi tecnici, a rigor di termini né il metodo è corretto dato l'input generato da SLA con feedback implicito.

  • non è possibile utilizzare RegressionEvaluator perché, come già noto, previsione può essere interpretato come un valore di confidenza ed è rappresentata come un numero in virgola mobile nell'intervallo [0, 1] e colonna etichetta è solo un numero intero non legato. Questi valori non sono chiaramente comparabili.
  • non è possibile utilizzare BinaryClassificationEvaluator perché anche se la previsione può essere interpretata come un'etichetta di probabilità non rappresenta una decisione binaria.colonna di previsione Inoltre è di tipo valido e non può essere utilizzato direttamente con BinaryClassificationEvaluator

È possibile tenta di convertire una delle colonne in modo da soddisfare i requisiti di ingresso, ma questo è non è proprio un approccio giustificato dal punto di vista teorico e introduce parametri aggiuntivi che sono difficili da sintonizzare.

  • colonna etichetta mappa su [0, 1] intervallo e utilizzare RMSE.

  • convertire la colonna di etichette in indicatore binario con soglia fissa ed estendere ALS/ALSModel per restituire il tipo di colonna previsto. Supponendo che il valore di soglia è 1 potrebbe essere qualcosa di simile

    from pyspark.ml.recommendation import * 
    from pyspark.sql.functions import udf, col 
    from pyspark.mllib.linalg import DenseVector, VectorUDT 
    
    class BinaryALS(ALS): 
        def fit(self, df): 
         assert self.getImplicitPrefs() 
         model = super(BinaryALS, self).fit(df) 
         return ALSBinaryModel(model._java_obj) 
    
    class ALSBinaryModel(ALSModel): 
        def transform(self, df): 
         transformed = super(ALSBinaryModel, self).transform(df) 
         as_vector = udf(lambda x: DenseVector([1 - x, x]), VectorUDT()) 
         return transformed.withColumn(
          "rawPrediction", as_vector(col("prediction"))) 
    
    # Add binary label column 
    with_binary = dfCounts.withColumn(
        "label_binary", (col("rating") > 0).cast("double")) 
    
    als_binary_model = BinaryALS(implicitPrefs=True).fit(with_binary) 
    
    evaluatorB = BinaryClassificationEvaluator(
        metricName="areaUnderROC", labelCol="label_binary") 
    
    evaluatorB.evaluate(als_binary_model.transform(with_binary)) 
    ## 1.0 
    

In generale, il materiale sulla valutazione dei sistemi di raccomandazione con una valutazione implicita è una specie di mancante nei libri di testo, vi suggerisco di prendere una lettura su eliasah 's answer sulla valutazione di questo tipo di consiglieri.

+0

Grazie mille per la risposta. Ho provato entrambi i tuoi suggerimenti e ho deciso di adottare il metodo di valutazione RMSE e ridimensionamento. Sembra funzionare bene, tranne quando inserisco la funzione CrossValidator. Ho pensato che CrossValidator usasse lo stimatore e il valutatore nello stesso modo mentre automatizzava la convalida incrociata di k-fold e iterando attraverso le combinazioni di parametri dalla griglia. Ho aggiornato il mio post sopra con il mio nuovo codice (le cose che funzionano e cose che non lo fanno). Avresti qualche idea su cosa sta facendo CrossValidator che sta causando un errore nella dichiarazione di adattamento? – ilyab

+0

Non è correlato alla strategia di valutazione. Semplicemente (o non così semplicemente) significa che non è possibile risolvere il sistema per un dato dato e un insieme di parametri. – zero323

+0

Ho provato a impostare la griglia dei parametri per contenere solo i valori dei parametri stimatore predefiniti. Sto ancora ricevendo l'errore, quindi presumo che non sia la combinazione dei parametri. Avresti un'idea di dove potrebbe esserci un problema? Suppongo di poter configurare i miei loop nidificati per eseguire l'iterazione attraverso le combinazioni di parametri e la convalida incrociata, ma preferirei farlo funzionare usando le funzioni integrate di Spark. – ilyab

0

Con feedback impliciti non abbiamo reazioni dell'utente alle nostre raccomandazioni. Pertanto, non possiamo utilizzare metriche basate sulla precisione.

Nel già cited paper, viene invece utilizzata la metrica del ranking percentile previsto.

È possibile provare a implementare un Evaluator basato su una metrica simile nella lib Spark ML e utilizzarlo nella pipeline Cross convalida.

Problemi correlati