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
eClusterPlotter
ereditano tutte daStage
. - Ogni
StageTuner
prende la classe diStage
per mettere a punto e implementare il ciclo di feedback sopra. - Le variabili
pointClusters
eclusterPlots
sono diResult
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?