def train(<EOL>self,<EOL>X_train,<EOL>Y_train,<EOL>n_epochs=<NUM_LIT>,<EOL>lr=<NUM_LIT>,<EOL>batch_size=<NUM_LIT>,<EOL>shuffle=True,<EOL>X_dev=None,<EOL>Y_dev=None,<EOL>print_freq=<NUM_LIT:5>,<EOL>dev_ckpt=True,<EOL>dev_ckpt_delay=<NUM_LIT>,<EOL>b=<NUM_LIT:0.5>,<EOL>pos_label=<NUM_LIT:1>,<EOL>seed=<NUM_LIT>,<EOL>host_device="<STR_LIT>",<EOL>):
<EOL>self.settings.update(<EOL>{<EOL>"<STR_LIT>": n_epochs,<EOL>"<STR_LIT>": lr,<EOL>"<STR_LIT>": batch_size,<EOL>"<STR_LIT>": shuffle,<EOL>"<STR_LIT>": <NUM_LIT>,<EOL>"<STR_LIT>": host_device,<EOL>}<EOL>)<EOL>self._set_random_seed(self.settings["<STR_LIT>"])<EOL>self._check_input(X_train)<EOL>verbose = print_freq > <NUM_LIT:0><EOL>self.cardinality = Y_train.shape[<NUM_LIT:1>]<EOL>if not np.all(Y_train.sum(axis=<NUM_LIT:1>) - <NUM_LIT:1> < <NUM_LIT>):<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>if not np.all(Y_train >= <NUM_LIT:0>):<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>diffs = Y_train.max(axis=<NUM_LIT:1>) - Y_train.min(axis=<NUM_LIT:1>)<EOL>train_idxs = np.where(diffs > <NUM_LIT>)[<NUM_LIT:0>]<EOL>self._update_settings(X_train)<EOL>_X_train, _Y_train = self._preprocess_data(<EOL>X_train, Y_train, idxs=train_idxs, train=True<EOL>)<EOL>train_dataloader = DataLoader(<EOL>MultiModalDataset(_X_train, _Y_train),<EOL>batch_size=self.settings["<STR_LIT>"],<EOL>collate_fn=self._collate_fn(),<EOL>shuffle=self.settings["<STR_LIT>"],<EOL>)<EOL>if X_dev is not None:<EOL><INDENT>_X_dev, _Y_dev = self._preprocess_data(X_dev, Y_dev)<EOL><DEDENT>if self.settings["<STR_LIT>"] in self._gpu:<EOL><INDENT>if not torch.cuda.is_available():<EOL><INDENT>self.settings["<STR_LIT>"] = "<STR_LIT>"<EOL>self.logger.info("<STR_LIT>")<EOL><DEDENT>else:<EOL><INDENT>self.logger.info("<STR_LIT>")<EOL><DEDENT><DEDENT>self.logger.info(f"<STR_LIT>")<EOL>self._build_model()<EOL>self._setup_model_loss(self.settings["<STR_LIT>"])<EOL>if self.settings["<STR_LIT>"] in self._gpu:<EOL><INDENT>nn.Module.cuda(self)<EOL><DEDENT>n = len(_X_train)<EOL>if self.settings["<STR_LIT>"] > n:<EOL><INDENT>self.logger.info(f"<STR_LIT>")<EOL><DEDENT>batch_size = min(self.settings["<STR_LIT>"], n)<EOL>if verbose:<EOL><INDENT>st = time()<EOL>self.logger.info(f"<STR_LIT>")<EOL>self.logger.info(<EOL>f"<STR_LIT>"<EOL>f"<STR_LIT>"<EOL>f"<STR_LIT>"<EOL>f"<STR_LIT>"<EOL>)<EOL><DEDENT>dev_score_opt = <NUM_LIT:0.0><EOL>for epoch in range(self.settings["<STR_LIT>"]):<EOL><INDENT>iteration_losses = []<EOL>nn.Module.train(self, True)<EOL>for X_batch, Y_batch in train_dataloader:<EOL><INDENT>self.optimizer.zero_grad()<EOL>output = self._calc_logits(X_batch)<EOL>loss = self.loss(output, Y_batch)<EOL>loss.backward()<EOL>self.optimizer.step()<EOL>iteration_losses.append(self._non_cuda(loss))<EOL><DEDENT>if (<EOL>verbose and (epoch + <NUM_LIT:1>) % print_freq == <NUM_LIT:0><EOL>) or epoch + <NUM_LIT:1> == self.settings["<STR_LIT>"]:<EOL><INDENT>self.tensorboard_logger.add_scalar("<STR_LIT>", loss.item(), epoch + <NUM_LIT:1>)<EOL>msg = (<EOL>f"<STR_LIT>"<EOL>f"<STR_LIT>"<EOL>f"<STR_LIT>"<EOL>)<EOL>if X_dev is not None:<EOL><INDENT>scores = self.score(_X_dev, _Y_dev, b=b, pos_label=pos_label)<EOL>score = scores["<STR_LIT>"] if self.cardinality > <NUM_LIT:2> else scores["<STR_LIT>"]<EOL>score_label = "<STR_LIT>" if self.cardinality > <NUM_LIT:2> else "<STR_LIT>"<EOL>msg += f"<STR_LIT>"<EOL>for metric in scores.keys():<EOL><INDENT>self.tensorboard_logger.add_scalar(<EOL>metric, scores[metric], epoch + <NUM_LIT:1><EOL>)<EOL><DEDENT><DEDENT>self.logger.info(msg)<EOL>model_file = f"<STR_LIT>"<EOL>self.save(model_file=model_file, save_dir=self.settings["<STR_LIT>"])<EOL>if (<EOL>X_dev is not None<EOL>and dev_ckpt<EOL>and epoch > dev_ckpt_delay * self.settings["<STR_LIT>"]<EOL>and score > dev_score_opt<EOL>):<EOL><INDENT>dev_score_opt = score<EOL>self.logger.info(<EOL>f"<STR_LIT>"<EOL>f'<STR_LIT>'<EOL>)<EOL>copyfile(<EOL>f'<STR_LIT>',<EOL>f'<STR_LIT>',<EOL>)<EOL><DEDENT>if (X_dev is None or dev_ckpt is False) and epoch + <NUM_LIT:1> == self.settings[<EOL>"<STR_LIT>"<EOL>]:<EOL><INDENT>self.logger.info(<EOL>f"<STR_LIT>"<EOL>f'<STR_LIT>'<EOL>)<EOL>copyfile(<EOL>f'<STR_LIT>',<EOL>f'<STR_LIT>',<EOL>)<EOL><DEDENT><DEDENT><DEDENT>if verbose:<EOL><INDENT>self.logger.info(f"<STR_LIT>")<EOL><DEDENT>self.logger.info("<STR_LIT>")<EOL>self.load(model_file="<STR_LIT>", save_dir=self.settings["<STR_LIT>"])<EOL>
Generic training procedure for PyTorch model

:param X_train: The training data which is a (list of Candidate objects,
    a sparse matrix of corresponding features) pair.
:type X_train: pair
:param Y_train: Array of marginal probabilities for each Candidate.
:type Y_train: list or numpy.array
:param n_epochs: Number of training epochs.
:type n_epochs: int
:param lr: Learning rate.
:type lr: float
:param batch_size: Batch size for learning model.
:type batch_size: int
:param shuffle: If True, shuffle training data every epoch.
:type shuffle: bool
:param X_dev: Candidates for evaluation, same format as X_train.
:param Y_dev: Labels for evaluation, same format as Y_train.
:param print_freq: number of epochs at which to print status, and if present,
    evaluate the dev set (X_dev, Y_dev).
:type print_freq: int
:param dev_ckpt: If True, save a checkpoint whenever highest score
    on (X_dev, Y_dev) reached. Note: currently only evaluates at
    every @print_freq epochs.
:param dev_ckpt_delay: Start dev checkpointing after this portion
    of n_epochs.
:type dev_ckpt_delay: float
:param b: Decision boundary *for binary setting only*.
:type b: float
:param pos_label: Positive class index *for binary setting only*. Default: 1
:type pos_label: int
:param seed: Random seed
:type seed: int
:param host_device: Host device
:type host_device: str