ground truth fit is worse than cross validated fit on noisy data? I am having these weird results when playing around with cross validation that I would greatly appreciate to have any comments.
Briefly, I have a lower mean squared error (MSE) when doing regression (least-squares) using cross-valitation (CV), than when using the &quot;ground truth weights&quot; that I used to generate the data.
Note however, that I compute the MSE on noisy data (generated data + noise), so MSE of 0 would not be expected for noise levels above 0.
Weirdly, for high noise conditions, I get lower MSE with cross validated least squares than with the &quot;ground&quot; truth weights used to generate the clean data - to which I then add different levels of noise to the input (X). Instead, if I add guassian noise to the output (y) the &quot;ground truth weights&quot; perform better.
More details below.
Simulation of data
I am generating beta from a guassian and X from a uniform distribution. I then compute the to-be-regressed y as y = beta * X.
python 3 code:
def generate_data(noise_frac):
  X = np.random.rand(ntrials,nneurons)
  X = np.random.normal(size=(ntrials,nneurons))
  
  beta = np.random.randn(nneurons)
  y = X @ beta

  # not very important how I generated noise here
  noise_x = np.random.multivariate_normal(mean=zeros(nneurons), cov=diag(np.random.rand(nneurons)), size=ntrials)

                            
  X_noise = X + noise_x*noise_frac

  return X_noise, y, beta
  
  

As you can see I also add noise to X.
Regression
I then project this noised data X_noise for different values of noise onto beta:
y_hat = (X_noise) @ beta

And compute the MSE:
mse = mean((y_hat - y)**2)

As expected, MSE increases with noise (blue line in the figure).

However, I get lower MSE if I use cross validated least-squares! This is now orange line in the figure.
To do CV, I split X_noise in random 100 train and test sets. In broad terms, This is how I do CV in python:
beta_lsq = pinv(X_train) @ y_train
y_hat_lsq = (X_test) @ beta_lsq
mse = mean((y_hat_lsq - y_test)**2)

On the other hand, if I add noise to y, instead of X, then everything makes sense:

Thank you very much in advance!
PS: This is a crosspost from stack overflow
The main problem in your test is that your X has the same scale as your noise level (0-1), as a result, adding a noise changes your data distribution significantly.
This is your data distribution before and after adding noise.

It is like the noise is 50-200% more than your initial data. That's why you get a better result with CV than the &quot;ground truth weights&quot;. Because the new fitted model by CV is tuned for an X with a new distribution.
However, if you increase your X values scale, for example 100*np.random.rand(ntrials,nneurons), the your data have same distribution before and after adding noise.

Now if you run your code, you will get what you expected.