import sys sys.path.insert(0, "/home/mikeys/src/hoot") from dataclasses import dataclass import inspect from typing import Optional import torch import torch.nn.functional as F from hoot.modules.featurizer import Featurizer from hoot.modules.encoder import Encoder @dataclass class HootConfig: n_layers: int = 12 n_embd: int = 256 n_classes: int = 2 highfreq: Optional[float] = None class Decoder(torch.nn.Module): def __init__(self, feat_in, num_classes): super().__init__() self._feat_in = feat_in self._num_classes = num_classes # self.conv = torch.nn.Conv1d(self._feat_in, self._num_classes, kernel_size=1, bias=True) self.relu = torch.nn.ReLU() self.drop = torch.nn.Dropout(0.2) self.head = torch.nn.Linear(self._feat_in, self._num_classes, bias=True) def forward(self, encoder_output): x = encoder_output x = torch.mean(x, dim=-1) x = self.drop(x) x = self.relu(x) return self.head(x) class Model(torch.nn.Module): def __init__(self, config=None): super(Model, self).__init__() self.config = config if config is not None else HootConfig() config = self.config self.featurizer = Featurizer(highfreq=config.highfreq) self.encoder = Encoder(config.n_layers, config.n_embd) self.decoder = Decoder(config.n_embd, config.n_classes) # init all weights self.apply(self._init_weights) def forward(self, signals, signals_len, targets=None, targets_len=None): assert (targets is None) == (targets_len is None) processed_signal, processed_signal_length = self.featurizer( input_signal=signals, length=signals_len ) encoded, encoded_len = self.encoder( audio_signal=processed_signal, length=processed_signal_length ) logits = self.decoder(encoder_output=encoded) loss = None if targets is not None: # loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1) loss = F.cross_entropy(logits, targets, ignore_index=-1) return logits, loss def _init_weights(self, module): if isinstance(module, torch.nn.Linear): torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) if module.bias is not None: torch.nn.init.zeros_(module.bias) elif isinstance(module, torch.nn.Embedding): torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) def eval(self): for name, module in self.named_modules(): module.train("batch" in name.split(".")[-1]) def configure_optimizers(self, weight_decay, learning_rate, betas, device_type, use_fused=True): # start with all of the candidate parameters param_dict = {pn: p for pn, p in self.named_parameters()} # filter out those that do not require grad param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad} # create optim groups. Any parameters that is 2D will be weight decayed, otherwise no. # i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't. decay_params = [p for n, p in param_dict.items() if p.dim() >= 2] nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2] optim_groups = [ {"params": decay_params, "weight_decay": weight_decay}, {"params": nodecay_params, "weight_decay": 0.0}, ] num_decay_params = sum(p.numel() for p in decay_params) num_nodecay_params = sum(p.numel() for p in nodecay_params) print( f"num decayed parameter tensors: {len(decay_params)}," f" with {num_decay_params:,} parameters" ) print( f"num non-decayed parameter tensors: {len(nodecay_params)}," f" with {num_nodecay_params:,} parameters" ) # Create AdamW optimizer and use the fused version if it is available fused_available = "fused" in inspect.signature(torch.optim.AdamW).parameters extra_args = ( dict(fused=True) if fused_available and device_type == "cuda" and use_fused else dict() ) optimizer = torch.optim.AdamW(optim_groups, lr=learning_rate, betas=betas, **extra_args) print(f"using fused AdamW: {use_fused}") return optimizer