{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Monografía - Matías Macías Gómez\n### Parte 1 - Diciembre 2020\n##### Especialización en Analítica y Ciencia de Datos\n##### Universidad de Antioquia","metadata":{}},{"cell_type":"code","source":"!pip install --upgrade pip\n!pip install pymap3d==2.1.0\n!pip install -U l5kit","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**l5kit** es una libreria creada y mantenida por **lyft** la cual contiene toda las funciones necesarias para hacer una primera iteracion del modelo.","metadata":{}},{"cell_type":"code","source":"from tqdm.notebook import tqdm\nimport numpy as np\nimport pandas as pd\nimport scipy as sp\nimport gc\nimport os\nimport random\nimport sys\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom IPython.core.display import display, HTML\n\n# --- plotly ---\nfrom plotly import tools, subplots\nimport plotly.offline as py\npy.init_notebook_mode(connected=True)\nimport plotly.graph_objs as go\nimport plotly.express as px\nimport plotly.figure_factory as ff\nimport plotly.io as pio\npio.templates.default = \"plotly_dark\"\n\n# --- models ---\nfrom sklearn import preprocessing\nfrom sklearn.model_selection import KFold\nimport lightgbm as lgb\nimport xgboost as xgb\nimport catboost as cb\n\nfrom typing import Dict\n\nfrom tempfile import gettempdir\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nfrom torch import nn, optim\nfrom torch.utils.data import DataLoader\nfrom torchvision.models.resnet import resnet50\nfrom tqdm import tqdm\n\nfrom l5kit.configs import load_config_data\nfrom l5kit.data import LocalDataManager, ChunkedDataset\nfrom l5kit.dataset import AgentDataset, EgoDataset\n#from l5kit.rasterization import build_rasterizer\nfrom l5kit.rasterization.rasterizer_builder import build_rasterizer\nfrom l5kit.evaluation import write_pred_csv, compute_metrics_csv, read_gt_csv, create_chopped_dataset\nfrom l5kit.evaluation.chop_dataset import MIN_FUTURE_STEPS\nfrom l5kit.evaluation.metrics import neg_multi_log_likelihood, time_displace\nfrom l5kit.geometry import transform_points\nfrom l5kit.visualization import PREDICTED_POINTS_COLOR, TARGET_POINTS_COLOR, draw_trajectory\nfrom prettytable import PrettyTable\nfrom pathlib import Path","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Se debe crear una carpeta extra en el sistema, ya que la función **\"create_chopped_dataset\"**, asume que se esta corriendo el notebook de manera local e intenta escribir en el sistema de archivos de la carpeta de los datos en la cual no tiene permisos de escritura por lo cual se debe que sobre escribir la funcion y darle una nueva ubicacion para escribir el archivo CSV que tiene que crea para que funcione de manera correcta este notebook en el kernel de kaggle","metadata":{}},{"cell_type":"code","source":"!mkdir -p scenes","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Configuración para el manejo de los datos\n\n\n- Se le da al sistema el path de la base de datos.\n- Se crea el objeto **dm --> Data Manager** el cual se encarga del manejo de los datos en formato **.zarr** *(compresion, descompresion, carga en memoria)*.\n- Se carga el objeto de configuración provisto por lyft, el cual tiene los path para la carga de los diferentes datasets, y los atributos/parametros por defecto de las diferentes funciones contenidas en l5kit. Como es un archivo en formato **.yaml** python lo carga como un diccionario.","metadata":{}},{"cell_type":"code","source":"# set env variable for data\nos.environ[\"L5KIT_DATA_FOLDER\"] = \"/kaggle/input/lyft-motion-prediction-autonomous-vehicles\"\ndm = LocalDataManager(None)\n# get config\ncfg = load_config_data(\"../input/agent-motion-config/agent_motion_config.yaml\")\ncfg","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Construcción del modelo:\nel modelo esta basado en una resnet pre-entrenada con la base de datos imageNet incluida en pytorch, a la cual se le configura entrada para recibir los datos de las imagenes generadas por el rasterizador y la ultima capa para que la salida este ajustada al problema.\n\n- **Entrada** --> numero de frames históricos (Frames previos al punto de corte, para este caso 100) * 2 + 3 canales correspondientes al RGB de una imagen\n- **Salida** --> (X, Y) de las coordenadas del agente * número de estados futuros","metadata":{}},{"cell_type":"code","source":"def build_model(cfg: Dict) -> torch.nn.Module:\n    # load pre-trained Conv2D model\n    model = resnet50(pretrained=True)\n\n    # change input channels number to match the rasterizer's output\n    num_history_channels = (cfg[\"model_params\"][\"history_num_frames\"] + 1) * 2\n    num_in_channels = 3 + num_history_channels\n    model.conv1 = nn.Conv2d(\n        num_in_channels,\n        model.conv1.out_channels,\n        kernel_size=model.conv1.kernel_size,\n        stride=model.conv1.stride,\n        padding=model.conv1.padding,\n        bias=False,\n    )\n    # change output size to (X, Y) * number of future states\n    num_targets = 2 * cfg[\"model_params\"][\"future_num_frames\"]\n    model.fc = nn.Linear(in_features=2048, out_features=num_targets)\n\n    return model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Función de entrenamiento del modelo\n\n\nEntrenamiento del modelo al cual se le entregan las imagenes rasterizadas de los datos de entrenamiento ","metadata":{}},{"cell_type":"code","source":"def forward(data, model, device, criterion):\n    inputs = data[\"image\"].to(device)\n    target_availabilities = data[\"target_availabilities\"].unsqueeze(-1).to(device)\n    targets = data[\"target_positions\"].to(device)\n    # Forward pass\n    outputs = model(inputs).reshape(targets.shape)\n    loss = criterion(outputs, targets)\n    # not all the output steps are valid, but we can filter them out from the loss using availabilities\n    loss = loss * target_availabilities\n    loss = loss.mean()\n    return loss, outputs","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inicialización y formato de los datos","metadata":{}},{"cell_type":"code","source":"from l5kit.data import DataManager\nfrom l5kit.rasterization import *\n\ndef xbuild_rasterizer(cfg: dict, data_manager: DataManager) -> Rasterizer:\n    \"\"\"Factory function for rasterizers, reads the config, loads required data and initializes the correct rasterizer.\n    Args:\n        cfg (dict): Config.\n        data_manager (DataManager): Datamanager that is used to require files to be present.\n    Raises:\n        NotImplementedError: Thrown when the ``map_type`` read from the config doesn't have an associated rasterizer\n        type in this factory function. If you have custom rasterizers, you can wrap this function in your own factory\n        function and catch this error.\n    Returns:\n        Rasterizer: Rasterizer initialized given the supplied config.\n    \"\"\"\n    raster_cfg = cfg[\"raster_params\"]\n    map_type = raster_cfg[\"map_type\"]\n    dataset_meta_key = raster_cfg[\"dataset_meta_key\"]\n\n    render_context = RenderContext(\n        raster_size_px=np.array(raster_cfg[\"raster_size\"]),\n        pixel_size_m=np.array(raster_cfg[\"pixel_size\"]),\n        center_in_raster_ratio=np.array(raster_cfg[\"ego_center\"]),\n        set_origin_to_bottom=raster_cfg[\"set_origin_to_bottom\"],\n    )\n\n    filter_agents_threshold = raster_cfg[\"filter_agents_threshold\"]\n    history_num_frames = cfg[\"model_params\"][\"history_num_frames\"]\n\n    if map_type in [\"py_satellite\", \"satellite_debug\"]:\n        sat_image = _load_satellite_map(raster_cfg[\"satellite_map_key\"], data_manager)\n\n        try:\n            dataset_meta = _load_metadata(dataset_meta_key, data_manager)\n            world_to_ecef = np.array(dataset_meta[\"world_to_ecef\"], dtype=np.float64)\n            ecef_to_aerial = np.array(dataset_meta[\"ecef_to_aerial\"], dtype=np.float64)\n\n        except (KeyError, FileNotFoundError):  # TODO remove when new dataset version is available\n            world_to_ecef = get_hardcoded_world_to_ecef()\n            ecef_to_aerial = get_hardcoded_ecef_to_aerial()\n\n        world_to_aerial = np.matmul(ecef_to_aerial, world_to_ecef)\n        if map_type == \"py_satellite\":\n            return SatBoxRasterizer(\n                render_context, filter_agents_threshold, history_num_frames, sat_image, world_to_aerial,\n            )\n        else:\n            return SatelliteRasterizer(render_context, sat_image, world_to_aerial)\n\n    elif map_type in [\"py_semantic\", \"semantic_debug\"]:\n        semantic_map_filepath = data_manager.require(raster_cfg[\"semantic_map_key\"])\n        try:\n            dataset_meta = _load_metadata(dataset_meta_key, data_manager)\n            world_to_ecef = np.array(dataset_meta[\"world_to_ecef\"], dtype=np.float64)\n        except (KeyError, FileNotFoundError):  # TODO remove when new dataset version is available\n            world_to_ecef = get_hardcoded_world_to_ecef()\n        if map_type == \"py_semantic\":\n            return SemBoxRasterizer(\n                render_context, filter_agents_threshold, history_num_frames, semantic_map_filepath, world_to_ecef,\n            )\n        else:\n            return SemanticRasterizer(render_context, semantic_map_filepath, world_to_ecef)\n\n    elif map_type == \"box_debug\":\n        return BoxRasterizer(render_context, filter_agents_threshold, history_num_frames)\n    elif map_type == \"stub_debug\":\n        return StubRasterizer(render_context)\n    else:\n        raise NotImplementedError(f\"Rasterizer for map type {map_type} is not supported.\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ===== INIT DATASET\n# Carga de los datos de entrenamiento  \ntrain_cfg = cfg[\"train_data_loader\"]\n# Construcción de la imagen con el rasterizador incluido en la libreria de l5kit\nrasterizer = build_rasterizer(cfg, dm)\n# Carga y descompresion de los valores de los datos de entreamiento\ntrain_zarr = ChunkedDataset(dm.require(train_cfg[\"key\"])).open()\n# Carga de los datos de los agentes\ntrain_dataset = AgentDataset(cfg, train_zarr, rasterizer)\n# Carga y formato final de los datos de entrenamiento, se hace una estrategia de K-fold, implementada en la libreria de l5kit\ntrain_dataloader = DataLoader(train_dataset, shuffle=train_cfg[\"shuffle\"], batch_size=train_cfg[\"batch_size\"], \n                             num_workers=train_cfg[\"num_workers\"])\nprint(train_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### TR --> Trafic Lights para esta primer iteración no se utilizaron estos datos","metadata":{}},{"cell_type":"markdown","source":"## Inicialización del modelo","metadata":{}},{"cell_type":"code","source":"# ==== INIT MODEL\n# Cargar el dispositivo, en caso de tener GPU con nucles CUDA disponibles utilizar ese hardware, en caso contrario utilizar la CPU\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n# Pasarle el modelo configurado con los parametros del configurador al dispositivo\nmodel = build_model(cfg).to(device)\n# Cargar un optimizador\noptimizer = optim.Adam(model.parameters(), lr=1e-3)\n# Cargar y definir un criterio para el calculo de la perdida, en este caso \"mean square error\" (Error cuadrático medio)\ncriterion = nn.MSELoss(reduction=\"none\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Entrenamiento del modelo","metadata":{}},{"cell_type":"code","source":"# ==== TRAIN LOOP\ntr_it = iter(train_dataloader)\nprogress_bar = tqdm(range(cfg[\"train_params\"][\"max_num_steps\"]))\nlosses_train = []\nfor _ in progress_bar:\n    try:\n        data = next(tr_it)\n    except StopIteration:\n        tr_it = iter(train_dataloader)\n        data = next(tr_it)\n    model.train()\n    torch.set_grad_enabled(True)\n    loss, _ = forward(data, model, device, criterion)\n\n    # Backward pass\n    optimizer.zero_grad()\n    loss.backward()\n    optimizer.step()\n\n    losses_train.append(loss.item())\n    progress_bar.set_description(f\"loss: {loss.item()} loss(avg): {np.mean(losses_train)}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(data.keys())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Gráfica de la perdida con el set de entrenamiento.","metadata":{}},{"cell_type":"code","source":"plt.plot(np.arange(len(losses_train)), losses_train, label=\"train loss\")\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Ajuste de la función *create_chopped_data* para su correcto funcionamiento en el kernel de Kaggle","metadata":{}},{"cell_type":"code","source":"import argparse\nfrom pathlib import Path\n\nimport numpy as np\nfrom zarr import convenience\n\nfrom l5kit.data import ChunkedDataset, get_agents_slice_from_frames\nfrom l5kit.data.zarr_utils import zarr_scenes_chop\nfrom l5kit.dataset.select_agents import TH_DISTANCE_AV, TH_EXTENT_RATIO, TH_YAW_DEGREE, select_agents\n\nfrom l5kit.evaluation.extract_ground_truth import *\n\nMIN_FUTURE_STEPS = 10\n\ndef xcreate_chopped_dataset(\n    zarr_path: str, th_agent_prob: float, num_frames_to_copy: int, num_frames_gt: int, min_frame_future: int\n) -> str:\n    \"\"\"\n    Create a chopped version of the zarr that can be used as a test set.\n    This function was used to generate the test set for the competition so that the future GT(Ground Truth) is not in the data.\n    Store:\n     - a dataset where each scene has been chopped at `num_frames_to_copy` frames;\n     - a mask for agents for those final frames based on the original mask and a threshold on the future_frames;\n     - the GT csv for those agents\n     For the competition, only the first two (dataset and mask) will be available in the notebooks\n    Args:\n        zarr_path (str): input zarr path to be chopped\n        th_agent_prob (float): threshold over agents probabilities used in select_agents function\n        num_frames_to_copy (int):  number of frames to copy from the beginning of each scene, others will be discarded\n        min_frame_future (int): minimum number of frames that must be available in the future for an agent\n        num_frames_gt (int): number of future predictions to store in the GT file\n    Returns:\n        str: the parent folder of the new datam\n    \"\"\"\n    zarr_path = Path(zarr_path)\n    dest_path = Path(\"scenes\") / f\"{zarr_path.stem}_chopped_{num_frames_to_copy}\"\n    chopped_path = dest_path / zarr_path.name\n    gt_path = dest_path / \"gt.csv\"\n    mask_chopped_path = dest_path / \"mask\"\n\n    # Create standard mask for the dataset so we can use it to filter out unreliable agents\n    zarr_dt = ChunkedDataset(str(zarr_path))\n    zarr_dt.open()\n\n    agents_mask_path = Path(zarr_path) / f\"agents_mask/{th_agent_prob}\"\n    if not agents_mask_path.exists():  # don't check in root but check for the path\n        select_agents(\n            zarr_dt,\n            th_agent_prob=th_agent_prob,\n            th_yaw_degree=TH_YAW_DEGREE,\n            th_extent_ratio=TH_EXTENT_RATIO,\n            th_distance_av=TH_DISTANCE_AV,\n        )\n    agents_mask_origin = np.asarray(convenience.load(str(agents_mask_path)))\n\n    # create chopped dataset\n    zarr_scenes_chop(str(zarr_path), str(chopped_path), num_frames_to_copy=num_frames_to_copy)\n    zarr_chopped = ChunkedDataset(str(chopped_path))\n    zarr_chopped.open()\n\n    # compute the chopped boolean mask, but also the original one limited to frames of interest for GT csv\n    agents_mask_chop_bool = np.zeros(len(zarr_chopped.agents), dtype=np.bool)\n    agents_mask_orig_bool = np.zeros(len(zarr_dt.agents), dtype=np.bool)\n\n    for idx in range(len(zarr_dt.scenes)):\n        scene = zarr_dt.scenes[idx]\n\n        frame_original = zarr_dt.frames[scene[\"frame_index_interval\"][0] + num_frames_to_copy - 1]\n        slice_agents_original = get_agents_slice_from_frames(frame_original)\n        frame_chopped = zarr_chopped.frames[zarr_chopped.scenes[idx][\"frame_index_interval\"][-1] - 1]\n        slice_agents_chopped = get_agents_slice_from_frames(frame_chopped)\n\n        mask = agents_mask_origin[slice_agents_original][:, 1] >= min_frame_future\n        agents_mask_orig_bool[slice_agents_original] = mask.copy()\n        agents_mask_chop_bool[slice_agents_chopped] = mask.copy()\n\n    # store the mask and the GT csv of frames on interest\n    np.savez(str(mask_chopped_path), agents_mask_chop_bool)\n    export_zarr_to_csv(zarr_dt, str(gt_path), num_frames_gt, th_agent_prob, agents_mask=agents_mask_orig_bool)\n    return str(dest_path)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Carga y formato de dataset de evaluación\nDada la naturaleza del proyecto, el dataset de evaluación solo puede contener una porcion de los frames de cada escena (para este caso 100), para evitar que el modelo se adelante en la linea de tiempo incluida en el data set y utilice esos datos para la evaluación entregando resultados poco realistas. ","metadata":{}},{"cell_type":"code","source":"# ===== GENERATE AND LOAD CHOPPED DATASET\n\n# Número de frames que se quieren utilizar del data set de validación para la evaluación\n# y evitar el problema de mirar \"El futuro\" y obtener un resultado impreciso sobre el desempeño del modelo\nnum_frames_to_chop = 100\n# Carga de los datos de validación\neval_cfg = cfg[\"val_data_loader\"]\n\n# Creación y del nuevo CSV con unicamente los primeros 100 frames de las escenas del \n# data set de evaluación - función proporcionada en el l5kit pero modificada para funcionar en el kernel de kaggle\neval_base_path = xcreate_chopped_dataset(dm.require(eval_cfg[\"key\"]), cfg[\"raster_params\"][\"filter_agents_threshold\"], \n                              num_frames_to_chop, cfg[\"model_params\"][\"future_num_frames\"], MIN_FUTURE_STEPS)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# datos de evaluación\neval_zarr_path = str(Path(eval_base_path) / Path(dm.require(eval_cfg[\"key\"])).name)\n# máscara para los agentes\neval_mask_path = str(Path(eval_base_path) / \"mask.npz\")\n# path del ground-truth para la evaluación\neval_gt_path = str(Path(eval_base_path) / \"gt.csv\")\n\n# Descomprension de los datos de evaluación\neval_zarr = ChunkedDataset(eval_zarr_path).open()\neval_mask = np.load(eval_mask_path)[\"arr_0\"]\n\n# ===== INIT DATASET AND LOAD MASK\n# Carga los datos de los agentes para la evaluación\neval_dataset = AgentDataset(cfg, eval_zarr, rasterizer, agents_mask=eval_mask)\n# Carga los datos de evaluación\neval_dataloader = DataLoader(eval_dataset, shuffle=eval_cfg[\"shuffle\"], batch_size=eval_cfg[\"batch_size\"], \n                             num_workers=eval_cfg[\"num_workers\"])\nprint(eval_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ==== EVAL LOOP\nmodel.eval()\ntorch.set_grad_enabled(False)\n\n# store information for evaluation\nfuture_coords_offsets_pd = []\ntimestamps = []\n\nagent_ids = []\nprogress_bar = tqdm(eval_dataloader)\n# Loop de evaluación\nfor data in progress_bar:\n    _, ouputs = forward(data, model, device, criterion)\n    future_coords_offsets_pd.append(ouputs.cpu().numpy().copy())\n    timestamps.append(data[\"timestamp\"].numpy().copy())\n    agent_ids.append(data[\"track_id\"].numpy().copy())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Se genera un archivo con las predicciones hechas por el modelo","metadata":{}},{"cell_type":"code","source":"pred_path = f\"{gettempdir()}/pred.csv\"\n# Escritura del CSV con las predicciones\nwrite_pred_csv(pred_path,\n               timestamps=np.concatenate(timestamps),\n               track_ids=np.concatenate(agent_ids),\n               coords=np.concatenate(future_coords_offsets_pd),\n              )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Cálculo de las metricas del desempeño del modelo: \n\n\nl5kit tiene predifinidas varias funciones para medir el desempeño del modelo, en este caso se implementara *time_displace* por su facilidad de interpretación en una primer iteración.\n\nA la funcion de *compute_metrics_csv* se le entrega el GT, las predicciones, y una lista con las metricas que se quieren medir.\n\n- time_displace = Desviación de la predicción en T timestamps en el sistemas de coordenadas globales ","metadata":{}},{"cell_type":"code","source":"metrics = compute_metrics_csv(eval_gt_path, pred_path, [time_displace])\nfor metric_name, metric_mean in metrics.items():\n    print(metric_name, metric_mean)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualización de los resultados:\nPara visualizar los resultados desde el punto de vista del vehículo autónomo del frame de interes (frame 100) se tienen que usar los datos de GT para poder visualizar las trayectorias futuras ya que los datos de evaluacion fueron cortados para el data set de evaluación. \n\nPara la visualización los datos de posicion de los agentes se guardan como coordenadas absolutas en el mundo con el origen en [37°25'45.6\"N, 122°09'15.7\"W] **Palo Alto California** y estan representadas como un set de coordenas 2D (X,Y) con una direccion respresentada como un yaw(*rotación sobre el eje Z*). Se utiliza este sistema dee coordenadas ya que el resot se utilizan para funciones de mas alto nivel con los data sets de ***Agent_dataset o Ego_dataset*** o tiene usos especificos para la identificación de diferentes agentes de tráfico dentro de la escena.\n\n*Nota*: el dataset tiene varios sistemas de coordenadas disponibles: \n- Sistema de Coordenadas absolutas en el globo\n- Sistema de Coordenadas satelitales \n- Sistema de Coordenadas de la imagen \n- Sistema de Coordenadas de los agentes\n- Sistema de Coordenadas Semántico\nLa descripción de los diferentes sistemas de coordenadas se puedre encontrar en este [link](http://https://github.com/lyft/l5kit/blob/master/coords_systems.md#image-coordinate-system)","metadata":{}},{"cell_type":"code","source":"model.eval()\ntorch.set_grad_enabled(False)\n\n# build a dict to retrieve future trajectories from GT\ngt_rows = {}\nfor row in read_gt_csv(eval_gt_path):\n    gt_rows[row[\"track_id\"] + row[\"timestamp\"]] = row[\"coord\"]\n\neval_ego_dataset = EgoDataset(cfg, eval_dataset.dataset, rasterizer)\n\nfor frame_number in range(99, len(eval_zarr.frames), 100):  # start from last frame of scene_0 and increase by 100\n    agent_indices = eval_dataset.get_frame_indices(frame_number) \n    if not len(agent_indices):\n        continue\n\n    # get AV point-of-view frame\n    data_ego = eval_ego_dataset[frame_number]\n    im_ego = rasterizer.to_rgb(data_ego[\"image\"].transpose(1, 2, 0))\n    center = np.asarray(cfg[\"raster_params\"][\"ego_center\"]) * cfg[\"raster_params\"][\"raster_size\"]\n    \n    predicted_positions = []\n    target_positions = []\n\n    for v_index in agent_indices:\n        data_agent = eval_dataset[v_index]\n\n        out_net = model(torch.from_numpy(data_agent[\"image\"]).unsqueeze(0).to(device))\n        out_pos = out_net[0].reshape(-1, 2).detach().cpu().numpy()\n        # store absolute world coordinates\n        predicted_positions.append(transform_points(out_pos, data_agent[\"world_from_agent\"]))\n        # retrieve target positions from the GT and store as absolute coordinates\n        track_id, timestamp = data_agent[\"track_id\"], data_agent[\"timestamp\"]\n        target_positions.append(gt_rows[str(track_id) + str(timestamp)] + data_agent[\"centroid\"][:2])\n\n\n    # convert coordinates to AV point-of-view so we can draw them\n    predicted_positions = transform_points(np.concatenate(predicted_positions), data_ego[\"raster_from_world\"])\n    target_positions = transform_points(np.concatenate(target_positions), data_ego[\"raster_from_world\"])\n\n    draw_trajectory(im_ego, predicted_positions, PREDICTED_POINTS_COLOR)\n    draw_trajectory(im_ego, target_positions, TARGET_POINTS_COLOR)\n\n    plt.imshow(im_ego)\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Conclusiones:\nEn este primer acercamiento al problema basado en la documentacion ofrecida por lyft como solución base del problema. \nLos resultados del entrenamiento indican que las predicciones hechas por el sistema no son muy buenas ya que con el tiempo su desviación siempre crece.\n\n\nEn un futuro se buscara explorar e implementar nuevas métricas de para medir el desempeño del modelo e implementar un modelo con una salida multi-modal para resolver el problema.","metadata":{}}]}