@ptrblck Thank you for the solution. However, I believe that this requires naming all layers that go inside an nn.Sequential layer. By default, layers within an nn.Sequential do not retain their names.
As an alternative, I have considered adding the following code.
no_decay = list()
decay = list()
for m in model.modules():
  if isinstance(m, (nn.Linear, nn.Conv2d, ...)):
    decay.append(m.weight)
    no_decay.append(m.bias)
  elif hasattr(m, 'weight'):
    no_decay.append(m.weight)
  elif hasattr(m, 'bias'):
    no_decay.append(m.bias)
for name, param in self.model.named_modules():
  if not (name.endswith('.weight') or name.endswith('.bias')):
    no_decay.append(param)
optimizer = optim.AdamW([{'params': no_decay, 'weight_decay', 0}, {'params': decay}, **kwargs])

The reason for extracting only the weight and bias values is that .modules() returns all modules, including modules that contain other modules, whereas .named_parameters() only returns the parameters at the very end of the recursion.