8

Ich habe kürzlich versucht, Apache Spark als Ersatz für Scikit Learn kennenzulernen, aber es scheint mir, dass Scikit selbst in einfachen Fällen viel schneller zu einem akkuraten Modell konvergiert als Spark. Zum Beispiel erzeugte I 1000 Datenpunkte für eine sehr einfache lineare Funktion (z = x + y) mit dem folgende Skript:Ist Apache Spark weniger genau als 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)) 

ich folgendes Scikit Skript dann lief:

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)) 

Und dann dieser Funke Skript: (mit Funken einreichen, keine weiteren Argumente)

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() 

Merkwürdig zwar, wird der Fehler durch Funken gegeben ist eine Größenordnung größer als gegeben, dass durch Scikit (0.185 bzw. 0.045) trotz der beiden Modelle, die eine nahezu identische Konfiguration haben (soweit ich das beurteilen kann) Ich verstehe, dass dies SGD mit sehr wenigen Iterationen verwendet und so die Ergebnisse abweichen können, aber ich hätte das nicht gedacht es wäre irgendwo in der Nähe eines so großen Unterschieds oder eines so großen Fehlers, besonders angesichts der außergewöhnlich einfachen Daten.


Gibt es etwas, das ich in Spark falsch verstehe? Ist es nicht richtig konfiguriert? Sicherlich sollte ich einen kleineren Fehler bekommen als das?

+1

Ich schlage vor, Sie Fehlergrenzen schaffen, durch das Experiment zu wiederholen mehrere Male mit verschiedenen zufälligen Seeds und prüfen, ob Sie das gleiche Ergebnis erhalten, 1000 Datenpunkte und 100 Iterationen ist nicht viel Außerdem verwenden Sklearn und MLLIB den gleichen Lerntarif für SGD Sie verwenden Invescaling für Sklearn aber ist MLLIB verwenden das Gleiche? –

Antwort

2

Weil Spark parallelisiert ist, muss jeder Knoten in der Lage sein, unabhängig von den anderen Knoten zu arbeiten, wenn die Berechnung im Gange ist, um [zeit-] teure Verschiebungen zwischen den Knoten zu vermeiden. Folglich verwendet es eine Prozedur namens Stochastic Gradient Descent, um ein Minimum zu erreichen, das lokalen Gradienten nach unten folgt.

Der 'genaue' Weg zur Lösung eines [einfache, kleinste Quadrate] Regressionsproblems beinhaltet das Lösen einer Matrixgleichung. Dies ist wahrscheinlich, was Scikit-Learn macht, also wird es in diesem Fall genauer sein.

Der Kompromiss besteht darin, dass das Lösen von Matrixgleichungen im Allgemeinen als N^3 für eine quadratische Größe-N-Matrix skaliert, was für große Datensätze schnell undurchführbar wird. Spark tauscht Genauigkeit für Rechenleistung aus. Wie bei jeder maschinellen Lernprozedur sollten Sie in Ihren Algorithmen viele Plausibilitätsprüfungen einbauen, um sicherzustellen, dass die Ergebnisse des vorherigen Schritts Sinn ergeben.

Hoffe, das hilft!

3

SGD, was für Stochastic Gradient Descent steht, ist ein Online-konvexe Optimierungsalgorithmus und daher sehr schwierig zu parallelisieren, da es eine Aktualisierung pro Iteration macht (es gibt intelligentere Varianten wie SGD mit Mini-Batches, aber immer noch nicht) sehr gut für parallele Umgebung

Auf der anderen Seite, Batch-Algorithmen, wie L-BFGS, ich rate Ihnen, mit Spark (LogigisticRegressionWithLBFGS) zu verwenden, kann leicht parallelisiert werden, da es auf Iteration pro Epoche macht müssen alle Datenpunkte sehen, den Wert und den Gradient der Verlustfunktion jedes Punkts berechnen und dann die Aggregation durchführen, um den vollen Gradienten zu berechnen.)

Python wird auf einer einzelnen Maschine ausgeführt, SGD funktioniert also gut.

By the way, wenn Sie in MLlib Code aussehen, das Äquivalent von scikit lernen die Lambda-Lambda/Größe des Datensatzes ist (mllib optimiert 1/n*sum(l_i(x_i,f(y_i)) + lambda während scikit optimiert lernen sum(l_i(x_i,f(y_i)) + lambda