Using cross validation to actually fit the final model I usually use cross validation only for the tuning part and once I have my hyperparameters, I fit the final model using the actual model with these hyperparameters like this:
lr = LogisticRegression(hyperparm1 = val1, hyperparam2 = val2)
model = lr.fit(train)

pred_train = model.perdict(train)
pred_test = model.predict(test)

However, sometimes I see people on the internet fitting the final model directly through the cross validator like this:
lr = LogisticRegression()
cv = CrossValidator(estimator = lr, paramMap = epm, numfolds = k)
model = cv.fit(train)

pred_train = model.perdict(train)
pred_test = model.predict(test)

Even though, the hyperparameters are the same in these two methods (since I selected the best hyperparameters I got from the cross validation and inputted them in the logistic regression model), I get (significantly) different results between the two methods when looking at the training set and the testing set performance. Is this normal ? I thought cross validation was only used to evaluate the model. what is the correct way to fit the final model? and how does the cross validator estimate the beta coefficients if it's basically training k times on k different training set. Is is the average of the beta coefficients?

This is a bit library dependent, but if the cross validator object returns you the best performing model, the significant difference is not normal. Because what it does is just a wrapper of what you do manually. Besides, cross validator(s) do not (and should not) average the coefficients found via different folds. They should return the best hyper parameters and sometimes the refitted model with these HPs.
