def fit( self, L_train: np.ndarray, Y_dev: Optional[np.ndarray] = None, class_balance: Optional[List[float]] = None, **kwargs: Any, ) -> None: """Train label model. Train label model to estimate mu, the parameters used to combine LFs. Parameters ---------- L_train An [n,m] matrix with values in {-1,0,1,...,k-1} Y_dev Gold labels for dev set for estimating class_balance, by default None class_balance Each class's percentage of the population, by default None **kwargs Arguments for changing train config defaults Raises ------ Exception If loss in NaN Examples -------- >>> L = np.array([[0, 0, -1], [-1, 0, 1], [1, -1, 0]]) >>> Y_dev = [0, 1, 0] >>> label_model = LabelModel(verbose=False) >>> label_model.fit(L) >>> label_model.fit(L, Y_dev=Y_dev) >>> label_model.fit(L, class_balance=[0.7, 0.3]) """ # Set random seed self.train_config: TrainConfig = merge_config( # type:ignore TrainConfig(), kwargs # type:ignore ) # Update base config so that it includes all parameters random.seed(self.train_config.seed) np.random.seed(self.train_config.seed) torch.manual_seed(self.train_config.seed) L_shift = L_train + 1 # convert to {0, 1, ..., k} if L_shift.max() > self.cardinality: raise ValueError( f"L_train has cardinality {L_shift.max()}, cardinality={self.cardinality} passed in." ) self._set_constants(L_shift) self._set_class_balance(class_balance, Y_dev) self._create_tree() lf_analysis = LFAnalysis(L_train) self.coverage = lf_analysis.lf_coverages() # Compute O and initialize params if self.config.verbose: # pragma: no cover logging.info("Computing O...") self._generate_O(L_shift) self._init_params() # Estimate \mu if self.config.verbose: # pragma: no cover logging.info("Estimating \mu...") # Set model to train mode self.train() # Move model to GPU self.mu_init = self.mu_init.to(self.config.device) if self.config.verbose and self.config.device != "cpu": # pragma: no cover logging.info("Using GPU...") self.to(self.config.device) # Set training components self._set_logger() self._set_optimizer() self._set_lr_scheduler() # Restore model if necessary start_iteration = 0 # Train the model metrics_hist = {} # The most recently seen value for all metrics for epoch in range(start_iteration, self.train_config.n_epochs): self.running_loss = 0.0 self.running_examples = 0 # Zero the parameter gradients self.optimizer.zero_grad() # Forward pass to calculate the average loss per example loss = self._loss_mu(l2=self.train_config.l2) if torch.isnan(loss): msg = "Loss is NaN. Consider reducing learning rate." raise Exception(msg) # Backward pass to calculate gradients # Loss is an average loss per example loss.backward() # Perform optimizer step self.optimizer.step() # Calculate metrics, log, and checkpoint as necessary metrics_dict = self._execute_logging(loss) metrics_hist.update(metrics_dict) # Update learning rate self._update_lr_scheduler(epoch) # Post-processing operations on mu self._clamp_params() self._break_col_permutation_symmetry() # Return model to eval mode self.eval() # Print confusion matrix if applicable if self.config.verbose: # pragma: no cover logging.info("Finished Training")
def fit( self, L_train: np.ndarray, Y_dev: Optional[np.ndarray] = None, class_balance: Optional[List[float]] = None, **kwargs: Any, ) -> None: """Train label model. Train label model to estimate mu, the parameters used to combine LFs. Parameters ---------- L_train An [n,m] matrix with values in {-1,0,1,...,k-1} Y_dev Gold labels for dev set for estimating class_balance, by default None class_balance Each class's percentage of the population, by default None **kwargs Arguments for changing train config defaults. n_epochs The number of epochs to train (where each epoch is a single optimization step), default is 100 lr Base learning rate (will also be affected by lr_scheduler choice and settings), default is 0.01 l2 Centered L2 regularization strength, default is 0.0 optimizer Which optimizer to use (one of ["sgd", "adam", "adamax"]), default is "sgd" optimizer_config Settings for the optimizer lr_scheduler Which lr_scheduler to use (one of ["constant", "linear", "exponential", "step"]), default is "constant" lr_scheduler_config Settings for the LRScheduler prec_init LF precision initializations / priors, default is 0.7 seed A random seed to initialize the random number generator with log_freq Report loss every this many epochs (steps), default is 10 mu_eps Restrict the learned conditional probabilities to [mu_eps, 1-mu_eps], default is None Raises ------ Exception If loss in NaN Examples -------- >>> L = np.array([[0, 0, -1], [-1, 0, 1], [1, -1, 0]]) >>> Y_dev = [0, 1, 0] >>> label_model = LabelModel(verbose=False) >>> label_model.fit(L) >>> label_model.fit(L, Y_dev=Y_dev, seed=2020, lr=0.05) >>> label_model.fit(L, class_balance=[0.7, 0.3], n_epochs=200, l2=0.4) """ # Set random seed self._set_config_and_seed(**kwargs) L_shift = L_train + 1 # convert to {0, 1, ..., k} if L_shift.max() > self.cardinality: raise ValueError( f"L_train has cardinality {L_shift.max()}, cardinality={self.cardinality} passed in." ) self._set_constants(L_shift) self._training_preamble(class_balance=class_balance, Y_dev=Y_dev, **kwargs) lf_analysis = LFAnalysis(L_train) self.coverage = lf_analysis.lf_coverages() # Compute O and initialize params if self.config.verbose: # pragma: no cover logging.info("Computing O...") self._generate_O(L_shift) self._training_loop()
def fit( self, L_train: np.ndarray, Y_dev: Optional[np.ndarray] = None, class_balance: Optional[List[float]] = None, progress_bar: bool = True, **kwargs: Any, ) -> None: """Train label model. Train label model to estimate mu, the parameters used to combine LFs. Parameters ---------- L_train An [n,m] matrix with values in {-1,0,1,...,k-1} Y_dev Gold labels for dev set for estimating class_balance, by default None class_balance Each class's percentage of the population, by default None progress_bar To display a progress bar, by default True **kwargs Arguments for changing train config defaults. n_epochs The number of epochs to train (where each epoch is a single optimization step), default is 100 lr Base learning rate (will also be affected by lr_scheduler choice and settings), default is 0.01 l2 Centered L2 regularization strength, default is 0.0 optimizer Which optimizer to use (one of ["sgd", "adam", "adamax"]), default is "sgd" optimizer_config Settings for the optimizer lr_scheduler Which lr_scheduler to use (one of ["constant", "linear", "exponential", "step"]), default is "constant" lr_scheduler_config Settings for the LRScheduler prec_init LF precision initializations / priors, default is 0.7 seed A random seed to initialize the random number generator with log_freq Report loss every this many epochs (steps), default is 10 mu_eps Restrict the learned conditional probabilities to [mu_eps, 1-mu_eps], default is None Raises ------ Exception If loss in NaN Examples -------- >>> L = np.array([[0, 0, -1], [-1, 0, 1], [1, -1, 0]]) >>> Y_dev = [0, 1, 0] >>> label_model = LabelModel(verbose=False) >>> label_model.fit(L) >>> label_model.fit(L, Y_dev=Y_dev, seed=2020, lr=0.05) >>> label_model.fit(L, class_balance=[0.7, 0.3], n_epochs=200, l2=0.4) """ # Set random seed self.train_config: TrainConfig = merge_config( # type:ignore TrainConfig(), kwargs # type:ignore ) # Update base config so that it includes all parameters random.seed(self.train_config.seed) np.random.seed(self.train_config.seed) torch.manual_seed(self.train_config.seed) # Set Logger self._set_logger() L_shift = L_train + 1 # convert to {0, 1, ..., k} if L_shift.max() > self.cardinality: raise ValueError( f"L_train has cardinality {L_shift.max()}, cardinality={self.cardinality} passed in." ) self._set_constants(L_shift) self._set_class_balance(class_balance, Y_dev) self._create_tree() lf_analysis = LFAnalysis(L_train) self.coverage = lf_analysis.lf_coverages() # Compute O and initialize params if self.config.verbose: # pragma: no cover logging.info("Computing O...") self._generate_O(L_shift) self._init_params() # Estimate \mu if self.config.verbose: # pragma: no cover logging.info(r"Estimating \mu...") # Set model to train mode self.train() # Move model to GPU self.mu_init = self.mu_init.to(self.config.device) if self.config.verbose and self.config.device != "cpu": # pragma: no cover logging.info("Using GPU...") self.to(self.config.device) # Set training components self._set_optimizer() self._set_lr_scheduler() # Restore model if necessary start_iteration = 0 # Train the model metrics_hist = {} # The most recently seen value for all metrics if progress_bar: epochs = trange(start_iteration, self.train_config.n_epochs, unit="epoch") else: epochs = range(start_iteration, self.train_config.n_epochs) for epoch in epochs: self.running_loss = 0.0 self.running_examples = 0 # Zero the parameter gradients self.optimizer.zero_grad() # Forward pass to calculate the average loss per example loss = self._loss_mu(l2=self.train_config.l2) if torch.isnan(loss): msg = "Loss is NaN. Consider reducing learning rate." raise Exception(msg) # Backward pass to calculate gradients # Loss is an average loss per example loss.backward() # Perform optimizer step self.optimizer.step() # Calculate metrics, log, and checkpoint as necessary metrics_dict = self._execute_logging(loss) metrics_hist.update(metrics_dict) # Update learning rate self._update_lr_scheduler(epoch) # Cleanup progress bar if enabled if progress_bar: epochs.close() # Post-processing operations on mu self._clamp_params() self._break_col_permutation_symmetry() # Return model to eval mode self.eval() # Print confusion matrix if applicable if self.config.verbose: # pragma: no cover logging.info("Finished Training")
def partial_fit( self, L_train: np.ndarray, alpha: Optional[float] = 0.05, Y_dev: Optional[np.ndarray] = None, class_balance: Optional[List[float]] = None, update_balance: bool = False, update_tree: bool = False, threshold: Optional[float] = 1e-16, **kwargs: Any, ) -> None: """Train label model. Train label model to estimate mu, the parameters used to combine LFs. Parameters ---------- L_train An [n,m] matrix with values in {-1,0,1,...,k-1} alpha Exponential smoothing factor, by default 0.05 Y_dev Gold labels for dev set for estimating class_balance, by default None class_balance Each class's percentage of the population, by default None **kwargs Arguments for changing train config defaults Raises ------ Exception If loss in NaN Examples -------- >>> L = np.array([[0, 0, -1], [-1, 0, 1], [1, -1, 0]]) >>> Y_dev = [0, 1, 0] >>> label_model = LabelModel(verbose=False) >>> label_model.fit(L) >>> label_model.fit(L, Y_dev=Y_dev) >>> label_model.fit(L, class_balance=[0.7, 0.3]) """ if not self.is_trained: raise RuntimeError( f"This instance is not fitted yet. Call 'fit' with " "appropriate arguments before using this method.") n, m = L_train.shape if m != self.m: raise ValueError(f"L_train must have shape[1]={self.m}.") # Set number of epochs to one self.train_config: TrainConfig = merge_config( # type:ignore TrainConfig(), { "n_epochs": 1, **kwargs } # type:ignore ) L_shift = L_train + 1 # convert to {0, 1, ..., k} if L_shift.max() > self.cardinality: raise ValueError( f"L_train has cardinality {L_shift.max()}, cardinality={self.cardinality} passed in." ) self._set_constants(L_shift) if update_balance: self._update_balance(class_balance, Y_dev) if update_tree: self._update_tree(L_train, alpha, threshold) # Build the mask over O^{-1} self._update_mask() lf_analysis = LFAnalysis(L_train) self.coverage = lf_analysis.lf_coverages() # Compute O if self.config.verbose: # pragma: no cover logging.info("Computing O...") self._update_O(L_shift, alpha) # Estimate \mu if self.config.verbose: # pragma: no cover logging.info("Estimating \mu...") # Set model to train mode self.train() # Move model to GPU if self.config.verbose and self.config.device != "cpu": # pragma: no cover logging.info("Using GPU...") self.to(self.config.device) # Set training components self._set_optimizer() # Restore model start_iteration = 0 # Train the model metrics_hist = {} # The most recently seen value for all metrics for epoch in range(start_iteration, self.train_config.n_epochs): self.running_loss = 0.0 self.running_examples = 0 # Zero the parameter gradients self.optimizer.zero_grad() # Forward pass to calculate the loss loss = self._loss_mu(l2=self.train_config.l2) if torch.isnan(loss): msg = "Loss is NaN. Consider reducing learning rate." raise Exception(msg) # Backward pass to calculate gradients # Loss is an average loss per example loss.backward() # Perform optimizer step self.optimizer.step() # Calculate metrics, log, and checkpoint as necessary metrics_dict = self._execute_logging(loss) metrics_hist.update(metrics_dict) # Update learning rate self._update_lr_scheduler(epoch) # Post-processing operations on mu self._clamp_params() self._break_col_permutation_symmetry() # Return model to eval mode self.eval() # Print confusion matrix if applicable if self.config.verbose: # pragma: no cover logging.info("Finished Training") self.is_trained = True