Questa progettazione della pipeline di elaborazione dei dati deve essere così complessa?

4

Ho una pipeline di elaborazione dati con fasi ben definite e confini IO. Posso scegliere una lingua per soddisfare le esigenze di questo design. Inizia con InputObject . Alla fine di ogni fase, ci sono alcuni dati aggiuntivi derivati da alcuni o tutti i risultati dei passaggi precedenti e il InputObject . Cioè, StageN estende Stage e produce un ResultsObjN immutabile, che estende Result . Una volta completata la pipeline, l'output finale a cui sono interessato è un sottoinsieme di tutti i risultati di ogni passaggio:

InputObject
    |
    |
    v
 -----------
|  Stage 1  | <-- Tunable Parameters
 -----------
    |
    |
InputObject
ResultsObj1
    |
    |
    v
 -----------
|  Stage 2  | <-- Tunable Parameters
 -----------
    |
    |
InputObject
ResultsObj1
ResultsObj2
    |
    |
    v
   ...

Attualmente sto modellando ogni fase come oggetto Stage . Spiegherò presto cosa intendo per parametri "sintonizzabili"

  • Ogni Stage ha ogni parametro sintonizzabile come attributi esposti da getter e setter.
  • Ogni Stage è costruita con le sue dipendenze di input.
  • Ogni Stage ha un metodo per calcolare il risultato in base ai parametri sintonizzabili

Ecco in quasi-UML:

 --------------------------------------------------
| <<abstract>>                                     |
| Stage                                            |
|--------------------------------------------------|
|--------------------------------------------------|
| +computeResult() : Result                        |
 --------------------------------------------------

Ad esempio, Stage3 calcola ResultObj3 utilizzando InputObject e i risultati di Stage2 . Ci sono 2 parametri che possono essere impostati per cambiare i risultati.

 --------------------------------------------------
| Stage3                                           |
|--------------------------------------------------|
| -param1 : Int                                    |
| -param2 : Float                                  |
|--------------------------------------------------|
| +Stage3(raw : InputObject, corners : ResultObj2) |
| +get/setParam1()                                 |
| +get/setParam2()                                 |
| +computeResult() : ResultObj3                    |
 --------------------------------------------------

Vorrei riutilizzare questo modello di pipeline di elaborazione, con fasi diverse in quantità diverse con diverse dipendenze di input. I parametri sintonizzabili possono essere ottimizzati da un ottimizzatore automatico o da un umano che utilizza alcune UI. In entrambi i casi, seguono lo stesso processo di feedback. Qui è lo stadio di sintonizzazione 3:

ResultObj3 performStage(InputObject raw, ResultObj2 corners):

1. Stage processor = new Stage3(raw, corners)
2. ResultObj result = processor.computeResult()
3. while (notAcceptable(result)):
4.     tuneParams(processor, result)  // tune to "more acceptable" params 
5.     result = processor.computeResult()
6. return result

Ritengo che il processo di ottimizzazione debba essere eseguito per ogni fase da un esecutore di code, ma non sono ancora sicuro su come sistemare il crescente insieme di risultati tipizzati in modo diverso o i diversi requisiti di costruzione di ogni fase.

Esempio di pipeline

Il punto di questa pipeline è leggere un insieme di coordinate da un CSV (in String raw ) e passare la stringa attraverso una pipeline che analizzerà la stringa, raggrupperà i punti e traccerà i cluster. I dati del cluster e le immagini di trama vengono estratti dalla pipeline dopo l'esecuzione.

La stringa viene analizzata in una raccolta di oggetti Point nello stage "ExtractPoints" . Quella collezione è raggruppata / segmentata in un insieme di oggetti Cluster nello stage "ClusterPoints" . I dati Point e Cluster vengono utilizzati per tracciare visivamente i punti in un oggetto immagine Plot allo stage "PlotClusters" .

QueuedPipeline pipeline = new QueuedPipeline()

String raw = readFile("points.csv")
pipeline.setInput(raw)

// addStage() takes the name of the stage, the stage runner, and the names of the stages it needs results from
pipeline.addStage("ExtractPoints", new StageTuner(PointExtractor, ), [])
pipeline.addStage("ClusterPoints", new StageTuner(PointClusterer), ["ExtractPoints"])
pipeline.addStage("PlotClusters", new StageTuner(ClusterPlotter), ["ExtractPoints", "ClusterPoints"])

// tune and execute each stage with the StageTuners
PipelineResultList results = pipeline.run()

// pipeline is done, collect the interesting results
Result pointClusters = results.getResult("ClusterPoints")
Result clusterPlot = results.getResult("PlotClusters")

saveClusterFile(pointClusters)
saveCllusterPlotImage(clusterPlot)
  • Le classi PointExtractor , PointClusterer e ClusterPlotter ereditano tutte da Stage .
  • Ogni StageTuner prende la classe di Stage per mettere a punto e implementare il ciclo di feedback sopra.
  • Le variabili pointClusters e clusterPlots sono di Result tipo

Problema

Ma il semplice passaggio di classi per la costruzione è un mal di testa in alcune lingue. Inoltre, non sono sicuro di come generare e impostare genericamente i parametri di ogni sottoclasse Stage a causa del diverso numero / tipo di parametri - forse ogni sottoclasse Stage ha bisogno di una sottoclasse StageRunner corrispondente. Infine, la fusione di Result di oggetti con il tipo di cui ho bisogno nelle ultime due funzioni sembra pericolosa. ... ma questi sono solo i sintomi del mio vero problema: tutto comincia a diventare complesso e confuso per quello che mi aspettavo fosse un problema semplice.

Sto definendo male i miei oggetti e comportamenti? O non è così semplice come pensavo? Qualcos'altro interamente?

    
posta kdbanman 20.05.2015 - 19:54
fonte

2 risposte

3

Questo tipo di problema è adatto per la digitazione dinamica. Questo ti darà la soluzione più semplice, con gli ovvi compromessi.

Se desideri utilizzare la digitazione statica, avrai più fortuna se non centralizzi la costruzione della tua pipeline. I tuoi stadi sono quelli che sanno di più sui tipi delle loro dipendenze e risultati, quindi dovresti dare loro la responsabilità di gestirlo. Vorrei iniziare facendo in modo che il tuo esempio leggesse qualcosa del tipo:

extractedPoints = new PointExtractor(raw)
clusterPoints   = new PointClusterer(extractedPoints)
plot            = new ClusterPlotter(extractedPoints, clusterPoints)

saveClusterFile(clusterPoints.result())
saveClusterPlotImage(plot.result())

Si noti che questo utilizza direttamente il sistema dei tipi, il che rende la sintassi molto naturale e idiomatica. Dentro result() chiameresti codice comune per gestire l'accordatura e memoizzare il risultato. Poiché ogni calcolo chiama result() su ciascuna dipendenza prima di eseguire il proprio calcolo, la "coda" finisce per essere formata naturalmente nello stack di chiamate e la memoizzazione impedisce l'esecuzione multipla.

    
risposta data 21.05.2015 - 02:55
fonte
-1

Potresti essere in grado di modellarlo usando un motore di workflow. Puoi modellare ciascuno dei tuoi stage come una fase del flusso di lavoro e anche parametrizzare ogni passaggio. A seconda del motore scelto, l'azione manuale può essere configurata per ogni passo.

Il seguente post potrebbe aiutarti a decidere se è pertinente al tuo caso d'uso: link

Se la tua esperienza è con Java e Spring, allora il seguente post aiuterà: link

    
risposta data 21.05.2015 - 00:02
fonte

Leggi altre domande sui tag