8

Recentemente ho cercato di conoscere Apache Spark come sostituto di Scikit Learn, tuttavia mi sembra che anche in casi semplici, Scikit converge in un modello accurato molto più veloce di Spark. Per esempio ho generato 1000 punti di dati per una semplice funzione lineare (z = x + y) con il seguente script:Apache Spark è meno preciso di Scikit Learn?

from random import random 

def func(in_vals): 
    '''result = x (+y+z+w....)''' 
    result = 0 
    for v in in_vals: 
     result += v 
    return result 

if __name__ == "__main__": 
    entry_count = 1000 
    dim_count = 2 
    in_vals = [0]*dim_count 
    with open("data_yequalsx.csv", "w") as out_file: 
     for entry in range(entry_count): 
      for i in range(dim_count): 
       in_vals[i] = random() 
      out_val = func(in_vals) 
      out_file.write(','.join([str(x) for x in in_vals])) 
      out_file.write(",%s\n" % str(out_val)) 

Ho poi eseguito il seguente script Scikit:

import sklearn 
from sklearn import linear_model 

import numpy as np 

data = [] 
target = [] 
with open("data_yequalsx.csv") as inFile: 
    for row in inFile: 
     vals = row.split(",") 
     data.append([float(x) for x in vals[:-1]]) 
     target.append(float(vals[-1])) 

test_samples= len(data)/10 

train_data = [0]*(len(data) - test_samples) 
train_target = [0]*(len(data) - test_samples) 
test_data = [0]*(test_samples) 
test_target = [0]*(test_samples) 
train_index = 0 
test_index = 0 
for j in range(len(data)): 
    if j >= test_samples: 
     train_data[train_index] = data[j] 
     train_target[train_index] = target[j] 
     train_index += 1 
    else: 
     test_data[test_index] = data[j] 
     test_target[test_index] = target[j] 
     test_index += 1 

model = linear_model.SGDRegressor(n_iter=100, learning_rate="invscaling", eta0=0.0001, power_t=0.5, penalty="l2", alpha=0.0001, loss="squared_loss") 
model.fit(train_data, train_target) 
print(model.coef_) 
print(model.intercept_) 

result = model.predict(test_data) 
mse = np.mean((result - test_target) ** 2) 
print("Mean Squared Error = %s" % str(mse)) 

Poi questo script Spark: (con accensione a presentare, senza altri argomenti)

from pyspark.mllib.regression import LinearRegressionWithSGD, LabeledPoint 
from pyspark import SparkContext 

sc = SparkContext (appName="mllib_simple_accuracy") 

raw_data = sc.textFile ("data_yequalsx.csv", minPartitions=10) #MinPartitions doesnt guarantee that you get that many partitions, just that you wont have fewer than that many partitions 
data = raw_data.map(lambda line: [float(x) for x in line.split (",")]).map(lambda entry: LabeledPoint (entry[-1], entry[:-1])).zipWithIndex() 
test_samples= data.count()/10 

training_data = data.filter(lambda (entry, index): index >= test_samples).map(lambda (lp,index): lp) 
test_data = data.filter(lambda (entry, index): index < test_samples).map(lambda (lp,index): lp) 

model = LinearRegressionWithSGD.train(training_data, step=0.01, iterations=100, regType="l2", regParam=0.0001, intercept=True) 
print(model._coeff) 
print(model._intercept) 

mse = (test_data.map(lambda lp: (lp.label - model.predict(lp.features))**2).reduce(lambda x,y: x+y))/test_samples; 
print("Mean Squared Error: %s" % str(mse)) 

sc.stop() 

Stranamente, però, l'errore data dalla candela è un ordine di grandezza maggiore di quella data dalla Scikit (rispettivamente 0.185 e 0.045) nonostante i due modelli abbiano un setup quasi identico (per quanto posso dire) Capisco che questo stia usando SGD con pochissime iterazioni e quindi i risultati potrebbero essere diversi ma non avrei pensato che sarebbe dovunque vicino una differenza così grande o un errore così grande, soprattutto dati i dati eccezionalmente semplici.


C'è qualcosa che sto fraintendendo in Spark? Non è configurato correttamente? Sicuramente dovrei avere un errore più piccolo di quello?

+1

ti suggerisco di fornire limiti di errore ripetendo l'esperimento più volte con semi casuali diversi e controlla se ottieni lo stesso risultato: 1000 punti dati e 100 iterazioni non sono molti.Inoltre, sklearn e mllib usano lo stesso programma di apprendimento per SGD? tu usi l'invalaling per sklearn ma mllib usa lo stesso? –

risposta

2

Poiché Spark è parallelizzato, ciascun nodo deve essere in grado di funzionare indipendentemente dagli altri nodi quando il calcolo è in corso per evitare [time-] shuffle costosi tra i nodi. Di conseguenza, utilizza una procedura denominata Discesa graduale stocastica per avvicinarsi a un minimo, che segue i gradienti locali verso il basso.

Il modo "esatto" per risolvere un problema di regressione [semplice, a minimi quadrati] comporta la risoluzione di un'equazione a matrice. Questo è probabilmente ciò che Scikit-Learn sta facendo, quindi in questo caso sarà più accurato.

Il compromesso è che la risoluzione delle equazioni di matrice generalmente scala come N^3 per una matrice quadrata di dimensione N, che diventa rapidamente irrealizzabile per dataset di grandi dimensioni. Spark scambia la precisione per la potenza di calcolo. Come con qualsiasi procedura di apprendimento automatico, è necessario creare MOLTI controlli di integrità negli algoritmi per assicurarsi che i risultati del passaggio precedente abbiano un senso.

Spero che questo aiuti!

3

SGD, che sta per Stochastic Gradient Descent, è un algoritmo di ottimizzazione convessa online, e quindi molto difficile da parallelizzare, poiché effettua un aggiornamento per iterazione (ci sono varianti più intelligenti come SGD con mini-batch, ma non ancora ottimo per l'ambiente parallelo

D'altro canto, gli algoritmi batch, come L-BFGS, hwich che ti consiglio di usare con Spark (LogigisticRegressionWithLBFGS), possono essere facilmente parallelizzati, dato che fa in iterazione per epoca (è necessario vedere tutti i punti dati, calcolare il valore e il gradiente della funzione di perdita di ciascun punto, quindi eseguire l'aggregazione per calcolare il gradiente completo.

Python viene eseguito in una singola macchina, pertanto SGD funziona bene.

A proposito, se si guarda nel codice MLlib, l'equivalente di scikit imparare di lambda è lambda/dimensioni del set di dati (mllib ottimizza 1/n*sum(l_i(x_i,f(y_i)) + lambda mentre scikit imparare ottimizza sum(l_i(x_i,f(y_i)) + lambda