docs: def fit(self, counts_df, val_set=None):
  		"""
  		Fit Hierarchical Poisson Model to sparse count data
  
  		Fits a hierarchical Poisson model to count data using mean-field approximation with either
  		full-batch coordinate-ascent or mini-batch stochastic coordinate-ascent.
  		
  		Note
  		----
  		DataFrames and arrays passed to '.fit' might be modified inplace - if this is a problem you'll
  		need to pass a copy to them, e.g. 'counts_df=counts_df.copy()'.
  
  		Note
  		----
  		Forcibly terminating the procedure should still keep the last calculated shape and rate
  		parameter values, but is not recommended. If you need to make predictions on a forced-terminated
  		object, set the attribute 'is_fitted' to 'True'.
  
  		Note
  		----
  		Fitting in mini-batches is more prone to numerical instability and compared to full-batch
  		variational inference, it is more likely that all your parameters will turn to NaNs (which
  		means the optimization procedure failed).
  
  		Parameters
  		----------
  		counts_df : pandas data frame (nobs, 3) or coo_matrix
  			Input data with one row per non-zero observation, consisting of triplets ('UserId', 'ItemId', 'Count').
  			Must containin columns 'UserId', 'ItemId', and 'Count'.
  			Combinations of users and items not present are implicitly assumed to be zero by the model.
  			Can also pass a sparse coo_matrix, in which case 'reindex' will be forced to 'False'.
  		val_set : pandas data frame (nobs, 3)
  			Validation set on which to monitor log-likelihood. Same format as counts_df.
  
  		Returns
  		-------
  		self : obj
  			Copy of this object
  		"""
  
  		## a basic check
  		if self.stop_crit == 'val-llk':
  			if val_set is None:
  				raise ValueError("If 'stop_crit' is set to 'val-llk', must provide a validation set.")
  
  		## running each sub-process
  		if self.verbose:
  			self._print_st_msg()
  		self._process_data(counts_df)
  		if self.verbose:
  			self._print_data_info()
  		if (val_set is not None) and (self.stop_crit!='diff-norm') and (self.stop_crit!='train-llk'):
  			self._process_valset(val_set)
  		else:
  			self.val_set = None
  			
  		self._cast_before_fit()
  		self._fit()
  		
  		## after terminating optimization
  		if self.keep_data:
  			if self.users_per_batch == 0:
  				self._store_metadata()
  			else:
  				self._st_ix_user = self._st_ix_user[:-1]
  		if self.produce_dicts and self.reindex:
  			self.user_dict_ = {self.user_mapping_[i]:i for i in range(self.user_mapping_.shape[0])}
  			self.item_dict_ = {self.item_mapping_[i]:i for i in range(self.item_mapping_.shape[0])}
  		self.is_fitted = True
  		del self.input_df
  		del self.val_set
  		
  		return self