diff --git a/dreem/models/embedding.py b/dreem/models/embedding.py index 134960e0..7ef9b0ba 100644 --- a/dreem/models/embedding.py +++ b/dreem/models/embedding.py @@ -99,8 +99,9 @@ def forward(self, x: Tensor, input_pos: Optional[Tensor] = None) -> Tensor: # create the lookup array based on how many instances there are # max(101, seq_len) is for positional vs temporal; pos can only have idx up to # 100 since it's a fraction of [0,1]*100. temp is from [0, clip_len]; since clip_len - # not available, we use # of instances from input x; this is always >= clip_len - self.build_rope_cache(max(101, seq_len)) # registers cache + # not available, we use the last value in the indexing array since this will be the + # last possible frame that we would need to index since no instances in a frame after that + self.build_rope_cache(max(101, input_pos[:, -1].max() + 1)) # registers cache self.cache = self.cache.to(input_pos.device) # extract the values based on whether input_pos is set or not rope_cache = ( @@ -269,7 +270,13 @@ def _check_init_args(self, emb_type: str, mode: str): def _transform(self, x, emb): - + """Routes to the relevant embedding function to transform the input queries + + Args: + x: Input queries of shape (batch_size, N, embed_dim) + emb: Embedding array to apply to data; can be (N, embed_dim) or + (batch_size, n_query, num_heads, embed_dim // 2, 2) if using RoPE + """ if self._emb_func == self._rope_embedding: return self._apply_rope(x, emb) else: @@ -277,8 +284,7 @@ def _transform(self, x, emb): def _apply_rope(self, x, emb): - """ - Applies Rotary Positional Embedding to input queries + """Applies Rotary Positional Embedding to input queries Args: x: Input queries of shape (batch_size, n_query, embed_dim) @@ -308,8 +314,7 @@ def _apply_rope(self, x, emb): def _apply_additive_embeddings(self, x, emb): - """ - Applies additive embeddings to input queries + """Applies additive embeddings to input queries Args: x: Input tensor of shape (batch_size, N, embed_dim) @@ -361,8 +366,7 @@ def _torch_int_div( def _rope_embedding(self, seq_positions: torch.Tensor, input_shape: torch.Size) -> torch.Tensor: - """ - Computes the rotation matrix to apply RoPE to input queries + """Computes the rotation matrix to apply RoPE to input queries Args: seq_positions: Pos array of shape (embed_dim,) used to compute rotational embedding input_shape: Shape of the input queries; needed for rope diff --git a/dreem/models/transformer.py b/dreem/models/transformer.py index f64d6b2d..272d6883 100644 --- a/dreem/models/transformer.py +++ b/dreem/models/transformer.py @@ -229,7 +229,6 @@ def forward( query_boxes = ref_boxes query_times = ref_times - decoder_features, pos_emb_traceback, temp_emb_traceback = self.decoder( query_features, encoder_features, embedding_map={"pos": self.pos_emb, "temp": self.temp_emb}, @@ -553,7 +552,7 @@ def forward( if self.return_intermediate: intermediate.pop() intermediate.append(decoder_features) - return torch.stack(intermediate) + return torch.stack(intermediate), pos_emb_traceback, temp_emb_traceback return decoder_features.unsqueeze(0), pos_emb_traceback, temp_emb_traceback @@ -561,8 +560,16 @@ def forward( def apply_embeddings(queries: torch.Tensor, embedding_map: Dict[str, Embedding], boxes: torch.Tensor, times: torch.Tensor, embedding_agg_method: str): - """ - Enter docstring here + """ Applies embeddings to input queries for various aggregation methods. This function + is called from the transformer encoder and decoder + + Args: + queries: The input tensor of shape (n_query, batch_size, embed_dim). + embedding_map: Dict of Embedding objects defining the pos/temp embeddings to be applied + to the input data + boxes: Bounding box based embedding ids of shape (n_query, n_anchors, 4) + times: Times based embedding ids of shape (n_query,) + embedding_agg_method: method of aggregation of embeddings e.g. stack/concatenate/average """ pos_emb, temp_emb = embedding_map["pos"], embedding_map["temp"] @@ -635,14 +642,15 @@ def _get_activation_fn(activation: str) -> callable: def collate_queries(queries: Tuple[torch.Tensor], embedding_agg_method: str ) -> torch.Tensor: - """ - Aggregates queries transformed by embeddings + """Aggregates queries transformed by embeddings + Args: _queries: 5-tuple of queries (already transformed by embeddings) for _, x, y, t, original input each of shape (batch_size, n_query, embed_dim) embedding_agg_method: String representing the aggregation method for embeddings - Returns: Tensor of aggregated queries of shape; can be concatenated (increased length of tokens), + Returns: + Tensor of aggregated queries of shape; can be concatenated (increased length of tokens), stacked (increased number of tokens), or averaged (original token number and length) """ @@ -670,6 +678,7 @@ def collate_queries(queries: Tuple[torch.Tensor], embedding_agg_method: str def spatial_emb_from_bb(bb: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: """ Computes embedding arrays for x,y spatial dimensions using centroids from bounding boxes + Args: bb: Bounding boxes of shape (n_query, n_anchors, 4) from which to compute x,y centroids; each bounding box is [ymin, xmin, ymax, xmax] diff --git a/rope.ipynb b/rope.ipynb deleted file mode 100644 index 593439b6..00000000 --- a/rope.ipynb +++ /dev/null @@ -1,598 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 156, - "id": "1bd666a7-0ad1-4ae7-a56e-43429a1228d8", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import dreem\n", - "import os\n", - "import matplotlib.pyplot as plt\n", - "import math\n", - "import torch\n", - "import logging\n", - "from dreem.models.mlp import MLP\n", - "from dreem.models.model_utils import *\n", - "from dreem.datasets import SleapDataset\n", - "from dreem.models.transformer import *\n", - "from dreem.models import VisualEncoder\n", - "from dreem.models import GlobalTrackingTransformer\n", - "from dreem.models.gtr_runner import GTRRunner" - ] - }, - { - "cell_type": "code", - "execution_count": 130, - "id": "a8736593-71f7-4ab6-a594-eb52d2fd94ac", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "\"\"\"Module containing different position and temporal embeddings.\"\"\"\n", - "\n", - "logger = logging.getLogger(\"dreem.models\")\n", - "# todo: add named tensors, clean variable names\n", - "\n", - "\n", - "class Embedding(torch.nn.Module):\n", - " \"\"\"Class that wraps around different embedding types.\n", - "\n", - " Used for both learned and fixed embeddings.\n", - " \"\"\"\n", - "\n", - " EMB_TYPES = {\n", - " \"temp\": {},\n", - " \"pos\": {\"over_boxes\"},\n", - " \"off\": {},\n", - " None: {},\n", - " } # dict of valid args:keyword params\n", - " EMB_MODES = {\n", - " \"fixed\": {\"temperature\", \"scale\", \"normalize\"},\n", - " \"learned\": {\"emb_num\"},\n", - " \"off\": {},\n", - " } # dict of valid args:keyword params\n", - "\n", - " def __init__(\n", - " self,\n", - " emb_type: str,\n", - " mode: str,\n", - " features: int,\n", - " n_points: int = 1,\n", - " emb_num: int = 16,\n", - " over_boxes: bool = True,\n", - " temperature: int = 10000,\n", - " normalize: bool = False,\n", - " scale: float | None = None,\n", - " mlp_cfg: dict | None = None,\n", - " ):\n", - " \"\"\"Initialize embeddings.\n", - "\n", - " Args:\n", - " emb_type: The type of embedding to compute. Must be one of `{\"temp\", \"pos\", \"off\"}`\n", - " mode: The mode or function used to map positions to vector embeddings.\n", - " Must be one of `{\"fixed\", \"learned\", \"off\"}`\n", - " features: The embedding dimensions. Must match the dimension of the\n", - " input vectors for the transformer model.\n", - " n_points: the number of points that will be embedded.\n", - " emb_num: the number of embeddings in the `self.lookup` table (Only used in learned embeddings).\n", - " over_boxes: Whether to compute the position embedding for each bbox coordinate (y1x1y2x2) or the centroid + bbox size (yxwh).\n", - " temperature: the temperature constant to be used when computing the sinusoidal position embedding\n", - " normalize: whether or not to normalize the positions (Only used in fixed embeddings).\n", - " scale: factor by which to scale the positions after normalizing (Only used in fixed embeddings).\n", - " mlp_cfg: A dictionary of mlp hyperparameters for projecting embedding to correct space.\n", - " Example: {\"hidden_dims\": 256, \"num_layers\":3, \"dropout\": 0.3}\n", - " \"\"\"\n", - " self._check_init_args(emb_type, mode)\n", - "\n", - " super().__init__()\n", - "\n", - " self.emb_type = emb_type\n", - " self.mode = mode\n", - " self.features = features\n", - " self.emb_num = emb_num\n", - " self.over_boxes = over_boxes\n", - " self.temperature = temperature\n", - " self.normalize = normalize\n", - " self.scale = scale\n", - " self.n_points = n_points\n", - "\n", - " if self.normalize and self.scale is None:\n", - " self.scale = 2 * math.pi\n", - "\n", - " if self.emb_type == \"pos\" and mlp_cfg is not None and mlp_cfg[\"num_layers\"] > 0:\n", - " if self.mode == \"fixed\":\n", - " self.mlp = MLP(\n", - " input_dim=n_points * self.features,\n", - " output_dim=self.features,\n", - " **mlp_cfg,\n", - " )\n", - " else:\n", - " in_dim = (self.features // (4 * n_points)) * (4 * n_points)\n", - " self.mlp = MLP(\n", - " input_dim=in_dim,\n", - " output_dim=self.features,\n", - " **mlp_cfg,\n", - " )\n", - " else:\n", - " self.mlp = torch.nn.Identity()\n", - "\n", - " self._emb_func = lambda tensor: torch.zeros(\n", - " (tensor.shape[0], self.features), dtype=tensor.dtype, device=tensor.device\n", - " ) # turn off embedding by returning zeros\n", - "\n", - " self.lookup = None\n", - "\n", - " if self.mode == \"learned\":\n", - " if self.emb_type == \"pos\":\n", - " self.lookup = torch.nn.Embedding(\n", - " self.emb_num * 4 * self.n_points, self.features // (4 * n_points)\n", - " )\n", - " self._emb_func = self._learned_pos_embedding\n", - " elif self.emb_type == \"temp\":\n", - " self.lookup = torch.nn.Embedding(self.emb_num, self.features)\n", - " self._emb_func = self._learned_temp_embedding\n", - "\n", - " elif self.mode == \"fixed\":\n", - " if self.emb_type == \"pos\":\n", - " self._emb_func = self._sine_box_embedding\n", - " elif self.emb_type == \"temp\":\n", - " self._emb_func = self._sine_temp_embedding\n", - "\n", - " def _check_init_args(self, emb_type: str, mode: str):\n", - " \"\"\"Check whether the correct arguments were passed to initialization.\n", - "\n", - " Args:\n", - " emb_type: The type of embedding to compute. Must be one of `{\"temp\", \"pos\", \"\"}`\n", - " mode: The mode or function used to map positions to vector embeddings.\n", - " Must be one of `{\"fixed\", \"learned\"}`\n", - "\n", - " Raises:\n", - " ValueError:\n", - " * if the incorrect `emb_type` or `mode` string are passed\n", - " NotImplementedError: if `emb_type` is `temp` and `mode` is `fixed`.\n", - " \"\"\"\n", - " if emb_type.lower() not in self.EMB_TYPES:\n", - " raise ValueError(\n", - " f\"Embedding `emb_type` must be one of {self.EMB_TYPES} not {emb_type}\"\n", - " )\n", - "\n", - " if mode.lower() not in self.EMB_MODES:\n", - " raise ValueError(\n", - " f\"Embedding `mode` must be one of {self.EMB_MODES} not {mode}\"\n", - " )\n", - "\n", - " def forward(self, seq_positions: torch.Tensor) -> torch.Tensor:\n", - " \"\"\"Get the sequence positional embeddings.\n", - "\n", - " Args:\n", - " seq_positions:\n", - " * An (`N`, 1) tensor where seq_positions[i] represents the temporal position of instance_i in the sequence.\n", - " * An (`N`, n_anchors x 4) tensor where seq_positions[i, j, :] represents the [y1, x1, y2, x2] spatial locations of jth point of instance_i in the sequence.\n", - "\n", - " Returns:\n", - " An `N` x `self.features` tensor representing the corresponding spatial or temporal embedding.\n", - " \"\"\"\n", - " emb = self._emb_func(seq_positions)\n", - "\n", - " if emb.shape[-1] != self.features:\n", - " raise RuntimeError(\n", - " (\n", - " f\"Output embedding dimension is {emb.shape[-1]} but requested {self.features} dimensions! \\n\"\n", - " f\"hint: Try turning the MLP on by passing `mlp_cfg` to the constructor to project to the correct embedding dimensions.\"\n", - " )\n", - " )\n", - " return emb\n", - "\n", - " def _torch_int_div(\n", - " self, tensor1: torch.Tensor, tensor2: torch.Tensor\n", - " ) -> torch.Tensor:\n", - " \"\"\"Perform integer division of two tensors.\n", - "\n", - " Args:\n", - " tensor1: dividend tensor.\n", - " tensor2: divisor tensor.\n", - "\n", - " Returns:\n", - " torch.Tensor, resulting tensor.\n", - " \"\"\"\n", - " return torch.div(tensor1, tensor2, rounding_mode=\"floor\")\n", - "\n", - " def _sine_box_embedding(self, boxes: torch.Tensor) -> torch.Tensor:\n", - " \"\"\"Compute sine positional embeddings for boxes using given parameters.\n", - "\n", - " Args:\n", - " boxes: the input boxes of shape N, n_anchors, 4 or B, N, n_anchors, 4\n", - " where the last dimension is the bbox coords in [y1, x1, y2, x2].\n", - " (Note currently `B=batch_size=1`).\n", - "\n", - " Returns:\n", - " torch.Tensor, the sine positional embeddings\n", - " (embedding[:, 4i] = sin(x)\n", - " embedding[:, 4i+1] = cos(x)\n", - " embedding[:, 4i+2] = sin(y)\n", - " embedding[:, 4i+3] = cos(y)\n", - " )\n", - " \"\"\"\n", - " if self.scale is not None and self.normalize is False:\n", - " raise ValueError(\"normalize should be True if scale is passed\")\n", - "\n", - " if len(boxes.size()) == 3:\n", - " boxes = boxes.unsqueeze(0)\n", - "\n", - " if self.normalize:\n", - " boxes = boxes / (boxes[:, :, -1:] + 1e-6) * self.scale\n", - "\n", - " dim_t = torch.arange(self.features // 4, dtype=torch.float32)\n", - "\n", - " dim_t = self.temperature ** (\n", - " 2 * self._torch_int_div(dim_t, 2) / (self.features // 4)\n", - " )\n", - "\n", - " # (b, n_t, n_anchors, 4, D//4)\n", - " pos_emb = boxes[:, :, :, :, None] / dim_t.to(boxes.device)\n", - "\n", - " pos_emb = torch.stack(\n", - " (pos_emb[:, :, :, :, 0::2].sin(), pos_emb[:, :, :, :, 1::2].cos()), dim=4\n", - " )\n", - " pos_emb = pos_emb.flatten(2).squeeze(0) # (N_t, n_anchors * D)\n", - "\n", - " pos_emb = self.mlp(pos_emb)\n", - "\n", - " pos_emb = pos_emb.view(boxes.shape[1], self.features)\n", - "\n", - " return pos_emb\n", - "\n", - " def _sine_temp_embedding(self, times: torch.Tensor) -> torch.Tensor:\n", - " \"\"\"Compute fixed sine temporal embeddings.\n", - "\n", - " Args:\n", - " times: the input times of shape (N,) or (N,1) where N = (sum(instances_per_frame))\n", - " which is the frame index of the instance relative\n", - " to the batch size\n", - " (e.g. `torch.tensor([0, 0, ..., 0, 1, 1, ..., 1, 2, 2, ..., 2,..., B, B, ...B])`).\n", - "\n", - " Returns:\n", - " an n_instances x D embedding representing the temporal embedding.\n", - " \"\"\"\n", - " T = times.int().max().item() + 1\n", - " d = self.features\n", - " n = self.temperature\n", - "\n", - " positions = torch.arange(0, T).unsqueeze(1)\n", - " temp_lookup = torch.zeros(T, d, device=times.device)\n", - "\n", - " denominators = torch.pow(\n", - " n, 2 * torch.arange(0, d // 2) / d\n", - " ) # 10000^(2i/d_model), i is the index of embedding\n", - " temp_lookup[:, 0::2] = torch.sin(\n", - " positions / denominators\n", - " ) # sin(pos/10000^(2i/d_model))\n", - " temp_lookup[:, 1::2] = torch.cos(\n", - " positions / denominators\n", - " ) # cos(pos/10000^(2i/d_model))\n", - "\n", - " temp_emb = temp_lookup[times.int()]\n", - " return temp_emb # .view(len(times), self.features)" - ] - }, - { - "cell_type": "code", - "execution_count": 131, - "id": "fc8aa9bf-7e83-4fa6-892e-8a7703777f95", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# create Embedding object\n", - "emb_t = Embedding(emb_type=\"temp\",mode=\"fixed\",features=1024,emb_num=16,n_points=1,temperature=10000)\n", - "emb_p = Embedding(emb_type=\"pos\",mode=\"fixed\",features=1024,emb_num=16,n_points=1,temperature=10000)" - ] - }, - { - "cell_type": "code", - "execution_count": 132, - "id": "4903f6c3-1cc8-412b-b988-ebeb2757c3b7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# get sample crops from training data to pass through the network\n", - "train_path = \"/home/jovyan/talmolab-smb/datasets/mot/microscopy/airyscan_proofread/Final/dreem-train\"\n", - "# train_path = \"/Users/mustafashaikh/dreem-data/dreem-train\"\n", - "data = SleapDataset([os.path.join(train_path,\"10-1.slp\")], [os.path.join(train_path,\"10-1.mp4\")], crop_size=64,\n", - " mode=\"train\", clip_length=32, anchors=\"centroid\")" - ] - }, - { - "cell_type": "code", - "execution_count": 133, - "id": "27bfdb50-2eee-4207-8a16-6481a9905e90", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# get a list of all instances in the first clip; this is the format that the model pipeline uses as input data\n", - "ref_instances = []\n", - "for frame in data[0]:\n", - " for instance in frame.instances:\n", - " ref_instances.append(instance)" - ] - }, - { - "cell_type": "code", - "execution_count": 134, - "id": "8ea441b2-b12a-4f10-8821-aef889a063ba", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# get the vector of times using the list of crops+labels\n", - "# query_instance is the instances in last frame (set to None)\n", - "ref_times, query_times = get_times(ref_instances, None)" - ] - }, - { - "cell_type": "code", - "execution_count": 135, - "id": "b863dbfe-d9fc-4ed1-bf97-3f304d3d03a6", - "metadata": { - "collapsed": true, - "jupyter": { - "outputs_hidden": true - }, - "tags": [] - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 135, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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lLvuukte+MIX9goXPQb77+d//uflBS94wcjn9Rjsv5e//OXyr/7Vv+ob96pXvUp+6qd+SkT0GOy3wcJlr/b3t771LQHkS1/6Um+aL37xiwLI//2//3fs9Zuqj4q63S733nsvN998c9/4m2++mXvuueeA1urytbS0BMDx48cBuP/++zl37lzf/q/X67zwhS/s7f97772XLMv6pjlz5gxnz57VYzSBN7/5zbz85S/nJS95Sd94PQb77w/+4A94znOewz//5/+cq6++mmc/+9n89m//du95PQb77wUveAH/63/9L/7qr/4KgP/zf/4PX/jCF/jRH/1RQI/BpbZX+/uLX/wiCwsLPPe5z+1N84//8T9mYWFhomMyVSGLjz/+ON57Tp061Tf+1KlTnDt37oDW6vIkIrz97W/nBS94AWfPngXo7eNh+/873/lOb5parcaxY8c2TaPHaDx33nknX/3qV/nKV76y6Tk9Bvvvb//2b/nwhz/M29/+dn7xF3+RL3/5y/zrf/2vqdfr/PRP/7Qeg0vg53/+51laWuLpT386zjm897z3ve/lJ3/yJwH9PbjU9mp/nzt3jquvvnrT/K+++uqJjslUFS4lY0zfYxHZNE7tzlve8hb+4i/+gi984QubntvJ/tdjNJ4HH3yQt73tbXz2s5+l0WiMnE6Pwf4JIfCc5zyH2267DYBnP/vZfPOb3+TDH/4wP/3TP92bTo/B/vnd3/1dPvGJT/DJT36S7/3e7+XrX/86t9xyC2fOnOH1r399bzo9BpfWXuzvYdNPekym6qOikydP4pzbVJmdP39+UyWodu6tb30rf/AHf8Cf/MmfcO211/bGnz59GmDL/X/69Gm63S6Li4sjp1Gj3XvvvZw/f56bbrqJJElIkoS7776b//gf/yNJkvT2oR6D/XPNNdfwjGc8o2/c93zP9/DAAw8A+ntwKfzbf/tv+YVf+AX+xb/4Fzzzmc/kda97Hf/m3/wbbr/9dkCPwaW2V/v79OnTPProo5vm/9hjj010TKaqcKnVatx0003cddddfePvuusunv/85x/QWl0+RIS3vOUtfOpTn+KP//iPueGGG/qev+GGGzh9+nTf/u92u9x99929/X/TTTeRpmnfNI888gjf+MY39BiN4cUvfjH33XcfX//613u35zznObz2ta/l61//Ok996lP1GOyzf/JP/smmNgB/9Vd/xZOf/GRAfw8uhfX1daztPz0553r/Dq3H4NLaq/39vOc9j6WlJb785S/3pvnzP/9zlpaWJjsm43/P+HAo/x36Ix/5iHzrW9+SW265RWZnZ+Xv/u7vDnrVpt7P/dzPycLCgvzpn/6pPPLII73b+vp6b5pf+7Vfk4WFBfnUpz4l9913n/zkT/7k0H+Ju/baa+Vzn/ucfPWrX5V/9s/+mf4L4i5U/6tIRI/Bfvvyl78sSZLIe9/7Xvnrv/5r+S//5b/IzMyMfOITn+hNo8dgf73+9a+XJz3pSb1/h/7Upz4lJ0+elHe96129afQY7K2VlRX52te+Jl/72tcEkPe///3yta99rddqZK/294/8yI/Is571LPniF78oX/ziF+WZz3zm5f/v0CIi/+k//Sd58pOfLLVaTb7/+7+/9++6aneAobePfvSjvWlCCPJLv/RLcvr0aanX6/KDP/iDct999/XNp9VqyVve8hY5fvy4NJtNecUrXiEPPPDAJd6ay8dg4aLHYP/94R/+oZw9e1bq9bo8/elPl9/6rd/qe16Pwf5aXl6Wt73tbXL99ddLo9GQpz71qfKe97xHOp1Obxo9BnvrT/7kT4a+/7/+9a8Xkb3b30888YS89rWvlfn5eZmfn5fXvva1sri4ONG6GhGRHVw5UkoppZS65KbqOy5KKaWUurJp4aKUUkqpqaGFi1JKKaWmhhYuSimllJoaWrgopZRSampo4aKUUkqpqaGFi1JKKaWmhhYuSimllJoaWrgopZRSampo4aKUUkqpqaGFi1JKKaWmhhYuSimllJoa/38EkGeSCdJLSQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# call fixed temporal embedding with the vector of 'times'\n", - "plt.imshow(emb(ref_times).numpy(), aspect='auto')" - ] - }, - { - "cell_type": "code", - "execution_count": 136, - "id": "8b17fdb7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "feat_dim = 1024\n", - "xfmr_encoder = TransformerEncoderLayer(d_model=feat_dim, nhead=8)\n", - "visual_encoder = VisualEncoder(d_model=feat_dim, model_name=\"resnet18\")" - ] - }, - { - "cell_type": "code", - "execution_count": 137, - "id": "7999fcef-953b-42cf-927c-f3b617f68157", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "def extract_features(\n", - " instances: list[\"Instance\"], \n", - " visual_encoder: \"dreem.models.VisualEncoder\",\n", - " force_recompute: bool = False\n", - " ) -> None:\n", - " \"\"\"Extract features from instances using visual encoder backbone.\n", - "\n", - " Args:\n", - " instances: A list of instances to compute features for\n", - " VisualEncoder : pass an instance of a visual encoder\n", - " force_recompute: indicate whether to compute features for all instances regardless of if they have instances\n", - " \"\"\"\n", - " if not force_recompute:\n", - " instances_to_compute = [\n", - " instance\n", - " for instance in instances\n", - " if instance.has_crop() and not instance.has_features()\n", - " ]\n", - " else:\n", - " instances_to_compute = instances\n", - "\n", - " if len(instances_to_compute) == 0:\n", - " return\n", - " elif len(instances_to_compute) == 1: # handle batch norm error when B=1\n", - " instances_to_compute = instances\n", - "\n", - " crops = torch.concatenate([instance.crop for instance in instances_to_compute])\n", - "\n", - " features = visual_encoder(crops)\n", - "\n", - " for i, z_i in enumerate(features):\n", - " instances_to_compute[i].features = z_i" - ] - }, - { - "cell_type": "code", - "execution_count": 138, - "id": "e299e8a0-61eb-4eee-901c-49aa7e678b3b", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# partial forward pass of the transformer - up until the encoder\n", - "\n", - "def prepare_for_xfmr(ref_instances):\n", - " # extract visual encoder features from instance object; shape=(1,n_instances,d=1024)\n", - " ref_features = torch.cat(\n", - " [instance.features for instance in ref_instances], dim=0\n", - " ).unsqueeze(0)\n", - "\n", - " # window_length = len(frames)\n", - " # instances_per_frame = [frame.num_detected for frame in frames]\n", - " total_instances = len(ref_instances)\n", - " embed_dim = ref_features.shape[-1]\n", - " # print(f'T: {window_length}; N: {total_instances}; N_t: {instances_per_frame} n_reid: {reid_features.shape}')\n", - " ref_boxes = get_boxes(ref_instances) # (n_instances,1,4)\n", - " ref_boxes = torch.nan_to_num(ref_boxes, -1.0)\n", - " ref_times, query_times = get_times(ref_instances, query_instances=None)\n", - "\n", - " # clip length \n", - " window_length = len(ref_times.unique())\n", - "\n", - " # computes the temporal embedding vector for each instance\n", - " ref_temp_emb = emb_t(ref_times)\n", - " # computes the positional embedding vector for each instance\n", - " ref_pos_emb = emb_p(ref_boxes)\n", - "\n", - " return_embedding=False\n", - " if return_embedding:\n", - " for i, instance in enumerate(ref_instances):\n", - " instance.add_embedding(\"pos\", ref_pos_emb[i])\n", - " instance.add_embedding(\"temp\", ref_temp_emb[i])\n", - "\n", - " # we need a single vector so average the temporal and spatial embeddings\n", - " ref_emb = (ref_pos_emb + ref_temp_emb) / 2.0\n", - "\n", - " # add a new dim at the beginning to represent the batch size (in our case 1)\n", - " ref_emb = ref_emb.view(1, total_instances, embed_dim)\n", - "\n", - " ref_emb = ref_emb.permute(1, 0, 2) # (total_instances, batch_size, embed_dim)\n", - "\n", - " batch_size, total_instances, embed_dim = ref_features.shape\n", - "\n", - " ref_features = ref_features.permute(\n", - " 1, 0, 2\n", - " ) # (total_instances, batch_size, embed_dim); note batch_size = 1\n", - "\n", - " return ref_features" - ] - }, - { - "cell_type": "code", - "execution_count": 139, - "id": "75ec8cab-25b9-4e9e-a64a-b5dbe00cc81a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# pass instances through visual encoder to get the feature vector (q,k,v); modifies the feature attribute of each Instance in ref_instances\n", - "extract_features(ref_instances, visual_encoder)" - ] - }, - { - "cell_type": "markdown", - "id": "a972707a-51a7-45ff-987e-80ee0dea4752", - "metadata": {}, - "source": [ - "### Rotary Positional Embeddings" - ] - }, - { - "cell_type": "code", - "execution_count": 140, - "id": "f0823cf1-2a35-4920-a62e-896bd9dbb078", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# create transformer instance to test embeddings \n", - "tfmr = Transformer()" - ] - }, - { - "cell_type": "code", - "execution_count": 143, - "id": "5e0b9d31-34be-40f8-91dc-b91d59aee170", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "assoc = tfmr(ref_instances)" - ] - }, - { - "cell_type": "code", - "execution_count": 157, - "id": "9f29ca35-9ff2-4e9a-bba0-37a3a14ad522", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "gtr = GTRRunner()" - ] - }, - { - "cell_type": "code", - "execution_count": 160, - "id": "0aa3876a-6246-4d02-80a5-013d382f6d38", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "metrics = gtr._shared_eval_step(data[0],\"train\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aee0d129-83f2-4f76-b452-132391554b4c", - "metadata": {}, - "outputs": [], - "source": [ - "metrics" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "dreem", - "language": "python", - "name": "dreem" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/run_trainer.py b/scripts/run_trainer.py similarity index 86% rename from run_trainer.py rename to scripts/run_trainer.py index fcf38ff9..50462226 100644 --- a/run_trainer.py +++ b/scripts/run_trainer.py @@ -4,7 +4,7 @@ # /Users/mustafashaikh/dreem/dreem/training # /Users/main/Documents/GitHub/dreem/dreem/training -os.chdir("/Users/mustafashaikh/dreem/dreem/training") +os.chdir("./dreem/training") base_config = "./configs/base.yaml" # params_config = "./configs/override.yaml"