[TOC]
# Reporte de procesos
En el **API CONABIO-ML**, un proceso se indica como una acción a realizar y de la cual se llevará seguimiento dentro del mismo API.
Un ejemplo de un proceso se muestra a continuación, para una acción realizada a un Dataset.
```python
.add_process(name="split_news",
action=Dataset.split,
inputs_from_processes=["from_folder_news"],
args={
"train_perc": 0.8,
"test_perc": 0.1,
"val_perc": 0.1
})
```
En este ejemplo se hace un split a un Dataset en las particiones ["train", "test" y "validation"]. Como resultado, en el archivo `pipeline.json` se tendrá la siguiente salida:
```json
{
"name": "split_news",
"action": "Dataset.split",
"inputs_from_processes": [
"from_folder_news"
],
"args": {
"train_perc": "0.8",
"test_perc": "0.1",
"val_perc": "0.1"
}
}
```
Sin embargo, existe la necesidad de poder guardar algunos recursos como producto de la ejecución de este proceso.
## Estrategia
Se plantea la ejecución automática de un método que guarde los recursos necesarios para cada proceso con un mínimo de cambios en la definición del mismo.
Para realizar el reporte de un proceso se debe indicar el parámetro `reportable` en la definición del mismo. Como se muestra:
```python
.add_process(name="split_news",
action=Dataset.split,
inputs_from_processes=["from_folder_news"],
reportable = True,
args={
"train_perc": 0.8,
"test_perc": 0.1,
"val_perc": 0.1
})
```
---
*Note el parámetro `reportable` en `True`, que indica el reporte de ese proceso.*
---
Como resultado, dentro de la carpeta de Pipeline se tendrá una subcarpeta con el nombre del proceso reportado que contendrá todos los recursos contruidos.
Para el ejemplo anterior, un dataset de texto [TextDataset](LINK), que es subclase de un dataset [Dataset](LINK), los recursos contruidos son:
- DataFrame con el conjunto de datos particionado.
- Rows por partición.
- Dataframe y gráfica de muestras por clase.
- Dataframe y gráfica de palabras por partición.
---
**Nota** : Estos recursos están dictados por la clase que maneja el proceso. Para un mayor conocimiento de que recursos se contruyen por tipo de proceso, consulte la siguiente [sección](https://hackmd.io/y-uuPRJ5S6mAYefn-b9oEQ#Recursos-reportados-por-default).
---

Además de una sección correspondiente en el archivo `pipeline.json` correspondiente a la lista de los recursos guardados.
```json
"report": {
"split_news": {
"process_type": "~DatasetType",
"asset": {
"samples_per_class": {
"type": 1,
"asset": "pipeline_path/split_news/samples_per_class.csv\""
},
"partitions": {
"type": 1,
"asset": "pipeline_path/split_news/partition_rows.json\""
},
"categories": {
"type": 3,
"asset": "[ . . .]"
},
"word_count": {
"type": 3,
"asset": "pipeline_path/split_news/word_count.csv\""
},
"dataset": {
"type": 1,
"asset": "pipeline_path/split_news/dataset.csv\""
},
"word_count_plot": {
"type": 2,
"asset": "pipeline_path/split_news/plot_word_count.png\""
},
"samples_per_class_plot": {
"type": 2,
"asset": "pipeline_path/split_news/plot_samples_per_class.png\""
}
}
}
```
## Seccion **report** del Pipeline
Cuando algún proceso sea reportado, se indicará en el archivo `pipeline.json` en la sección `report`.
La sección contendrá como llave el proceso reportado y éste a su vez definirá el tipo de proceso y los recursos guardados. Como se muestra:
```json
"report":{
[process_name]: {
"process_type": [Type of the process see: ProcessTypes],
"asset": {
[asset_name]:{
"type": [AssetType],
"asset": asset
},
[asset_name]:{
"type": [AssetType],
"asset": asset
}
}
}
}
```
Dentro del campo `asset` de un proceso reportado (defininido por el nombre del recurso), se tiene el tipo de recurso `type` y el recurso en sí `asset`.
### Tipos de recursos
El campo `AssetType` indica el tipo de recursos guardado, para la fácil identificación de éstos. Estan definidos en [assets.py](LINK) y son los siguientes:
```python
(1, FILE) : Archivos con datos (DataFrame CSV, JSON, etc)
(2, PLOT) : Gráficas (png, tiff, etc)
(3, DATA) : Datos (Lista de clases, lista de particiones, etc )
```
Por lo tanto, para el extracto de ejemplo
```json
"report": {
"split_news": {
"process_type": "~DatasetType",
"asset": {
"samples_per_class": {
"type": 1,
"asset": "pipeline_path/split_news/samples_per_class.csv
[ . . .]
```
Para el proceso `split_news` del tipo `Dataset`. Se reporta el asset `samples_per_class` que es in archivo `(1, FILE)` en la ruta `pipeline_path/process_name/samples_per_class.csv`
---
# Recursos reportados por default
A continuación se enlistan los recursos reportados por defaul para los tipos utilizados en el API CONABIO-ML.
## Dataset
1. Dataset particionado
```json
{ "dataset": {
"type": AssetType.FILE,
"asset": pipeline_path/process/dataset.csv
}
}
```
**dataset**

2. Categorias en el dataset (Train)
```json
{"categories": {
"type": AssetType.DATA,
"asset": "[\"alt.atheism\",
\"comp.graphics\",
\"comp.os.ms-windows.misc\",
\"comp.sys.ibm.pc.hardware\",
\"comp.sys.mac.hardware\",
\"comp.windows.x\",
\"misc.forsale\",
\"rec.autos\",
[. . .] ]"
},
}
```
[Creo que no fue una de mis mejores ideas]
3. Muestras por partición
```json
{
"partitions": {
"type": AssetType.FILE,
"asset": pipeline_path/process/partition_rows
},
}
```
**partitions**

4. Muestras por clase (DataFrame y Gráfica)
Conteo de muestras en csv y su gráfico, particionado y por clase
```json
{"samples_per_class_plot": {
"type": AssetType.PLOT,
"asset":pipeline_path/process/plot_samples_per_class
},
"samples_per_class": {
"type": AssetType.FILE,
"asset":pipeline_path/process/samples_per_class
},
}
```
**samples_per_class_plot**

**samples_per_class**

### Image Dataset
**[Aún por definir]**
### Text Dataset
1. Conteo de palabras por particion (FILE, GRAPH)
```json
{"word_count": {
"asset": pipeline_path/process/word_count,
"type": AssetTypes.FILE}
},
"word_count_plot": {
"asset": pipeline_path/process/plot_word_count,
"type": AssetTypes.PLOT
}
}
```
**word_count**

**word_count_plot**

2. Diccionario de palabras (Para partition 'train')
Solo si se ha construido, de otra manera se envía `null`
```json
{"word_index": {
"asset": pipeline_path/process/word_index,
"type": AssetTypes.FILE}
}
```
**word_index**

## Model
### Keras/TFKeras Model
1. Model summary
```json
{"model_summary": {
"type": AssetTypes.DATA,
"asset": pipeline_path/process/model_name
}
```
**model_summary**

## Evaluation
### Default
1. Resultados de evaluación
```json=
"results": {
"type": AssetType.DATA,
"asset": pipeline_path/process/results
}
```
**results**
```json
{
"MULTILABEL_1": {
"per_class": {
[ . . . ]
},
"one_class": {
"precision": 0.875,
"recall": 0.625,
"f1_score": 0.822,
"hamming_loss": 0.2334
}
},
"BINARY_2": {
"precision": 0.8,
"recall": 0.2,
"f1_score": 0.6
}
}
```
Vease [Evaluation defaults](LINK)
2. Recursos de resultados
```json
{"result_plots": {
"type": AssetType.DATA,
"asset": {
[metric]: pipeline/process/plot.png
[metric]: pipeline/process/plot_results.csv
}
}
}
```
**result_plots**
- MULTLABEL


---
# Extensión del reporte
Es posible agregar nuevos recursos a reportar para un escenario específico.
Cualquier extensión para enlazar un nuevo reporte debe responder al tipo correspondiente de proceso que se está reportando. Hay 3 tipos generales de procesos que el API CONABIO-ML maneja, que son:
- **Dataset**
- **Model**
- **Evaluation**
Estos pueden ser identificadas por el `typing` de salida de la función a manejar por el proceso. Para el ejemplo anterior, `split_news`, la funcion envuelta en el proceso (por medio del parámetro `action`), se define como:
```python
def split(self: DatasetType, train_perc
[ . . .]) -> DatasetType:
[ . . . ]
```
Indicando que la salida es un **`Dataset`**.
Hay 2 formas de enlazar un nuevo reporte: por medio de subclase (en caso de que necesite un nuevo tipo) o por medio de una función reporter con la firma indicada.
### Extensión por subclase
Cualquier extensión de reporte por medio de un `subclass` se lleva a cabo por medio del método `reporter`, correspondiente al tipo de proceso que se está reportando.
Para el ejemplo anterior, una clase de derivada de **Dataset** debe implementar el método `reporter`. Cuya firma se define como sigue:
```python
def reporter(self,
dest_path: str,
process_args: dict,
**kwargs):
[ . . . ]
data_to_report = [ Resources built ]
return super().reporter(dest_path,
process_args,
data_to_report)
```
El método se enlazará automáticamente una vez que el proceso termine y será el primero en ejecutarse antes de enlazar clases superiores.
**Nota**: *Todos los recursos que se deseen reportar deben establecerse en la variable **data_to_report** a la clase base.*
#### Consideraciones del método **reporter**
Hay 3 consideraciones generales para adicionar nuevos recursos al reporte.
1. **Enlace a clase superior.** Una vez terminado el proceso específico se debe llama a la clase super por medio de:
```python
return super(ClassName, self).reporter(dest_path,
process_args,
data_to_report)
```
2. **Recursos a reportar.** Todos los recursos a reportar deben estar contenidos dentro de un diccionario y ser reportados a la clase superior en el parámetro `data_to_report`. Utilizando el formato definido en [Tipos de recursos](https://hackmd.io/y-uuPRJ5S6mAYefn-b9oEQ#Tipos-de-recursos)
```python
data_to_report = {[asset_name]:
{"asset": [asset],
"type": AssetTypes.DATA}
}
return super([...]).reporter(dest_path,
process_args,
data_to_report)
```
3. **Be polite.** Utilice el recurso más apropiado para su recurso, *i.e.*, si va a guardar un diccionario con una gran cantidad de entrada elija el typo (1, FILE) para el asset en un archivo JSON, en lugar del typo (3, DATA) para escribir todo el diccionario en la sección report.
Después de todo, ud leerá el archivo pipeline.
### Función de reporte
Si no es necesario generar un nuevo tipo (Ya que los tipos actuales satisfacen las necesidades), pero es necesario reportar nuevos 'assets', se puede realizar por medio del parámetro `report_functions` del proceso correspondiente.
La firma de la función de reporte se define como:
```python
def report_function(resource: [ reporter_type ],
dest_path: str,
process_args: dict,
**kwargs):
return {"new_asset":{"asset": [. . . ], "type": [ . . . ]} }
```
Es necesario indicar el tipo de recurso esperado, que responden a los tipos de extension del reporte. Para el ejemplo anterior, la implementación completa quedaría de la manera siguiente:
1. **Función de report**
```python
def report_split(resource: Dataset.DatasetType,
dest_path: str,
process_args: dict,
**kwargs):
return {
"new_asset":{
"asset": [ . . . ],
"type": [ . . .]
}
}
```
2. **Proceso a reportar**
```python
.add_process(name="split_news",
action=Dataset.split,
inputs_from_processes=["from_folder_news"],
reportable=True,
report_functions=[report_split],
args={
[. . . ]
})\
```
Notese que la función se debe indicar en el parámetro `report_funcion` y establecer que el proceso se reportará por medio de `reportable`.
#### Consideraciones de la función que reporta
Hay 3 consideraciones generales para adicionar nuevos recursos al reporte.
1. **Return del asset a reportar.** Siempre se deben regresar los nuevos assets a repotar en un diccionario.
2. **Firma de la función.** Siempre se debe indicar el tipo de recuso esperado en la función que reporta. Estos pueden ser: Dataset.DatasetType, Model.ModelType o Evaluation.EvaluationType.
3. **Be polite.** Utilice el recurso más apropiado para su recurso, *i.e.*, si va a guardar un diccionario con una gran cantidad de entrada elija el typo (1, FILE) para el asset en un archivo JSON, en lugar del typo (3, DATA) para escribir todo el diccionario en la sección report.
Después de todo, ud leerá el archivo pipeline.