human
I am working on the file "BikeSharing-Linear.ipynb".
The first task is:
11 min!!! Now we also learn FeaureExtractor every time and the pipeline becomes heavier. Why? Can you speed it up?

What was the point about Maximum Likelihood
The process is described by possion distribution better
https://en.wikipedia.org/wiki/Poisson_distribution
In probability theory and statistics, the Poisson distribution (French pronunciation: ​[pwasɔ̃].
in English often rendered /ˈpwɑːsɒn/), named after French mathematician Siméon Denis Poisson, is a discrete probability distribution that expresses the probability of a given number of events occurring in a fixed interval of time or space if these events occur with a known constant rate and independently of the time since the last event.[1] The Poisson distribution can also be used for the number of events in other specified intervals such as distance, area or volume.
The other point of view: we have 200 people with 3% probability to pick up the bike.
What about CLT??? It works when $n \rightarrow \inf$. For poisson distribution there is a special case called De Moivre–Laplace theorem.
The list of different kinds of Generalized Linear Regression methods in sklearn: https://scikit-learn.org/stable/modules/linear_model.html
And there is no Poisson regression there.
So, let's write a probabilistic model for poisson distribution and optimize maximum likelihood.
Hausaufgaben: try to do it.
Hint: 
start from the assumption $\hat{y} = \exp{\langle x, \theta \rangle}$ and find the derivative of log-likelihood by $\theta$. It's zero + check the sign of the second derivative.
The conclusion: we can simulate poisson regression with simple wrapper.
Poisson hierarchical regression
Check if we have issues with np.log(y == 0)
Can you write Python code for it?

=====
What is a Python code to find the derivative of log-likelihood by $\theta$?
assistant
#Here is Python code to find the derivative of log-likelihood by $\theta$.

df[df["count"] == 0]

np.log(0)

class PoissonRegression(linear_model.ElasticNet):
    
    def __init__(self, alpha=1.0, l1_ratio=0.5, fit_intercept=True,
                 normalize=False, precompute=False, max_iter=1000,
                 copy_X=True, tol=1e-4, warm_start=False, positive=False,
                 random_state=None, selection='cyclic'):
        super().__init__(alpha, l1_ratio, fit_intercept, normalize, precompute, max_iter,
                         copy_X, tol, warm_start, positive, random_state, selection)
        
    def fit(self, X, y, *args):
        return super().fit(X, np.log(y), *args)
        
    def predict(self, X):
        return np.exp(super().predict(X))

exctractor = FeatureExtractor()
exctractor.collect_stats(df)
clf = Pipeline([
    ("extractor", exctractor),
    ("regression", PoissonRegression()),
])
param_grid = {
    "regression__alpha": np.logspace(-5, 1, 20),
    "regression__l1_ratio": np.linspace(0, 1, 10)
}
pd.options.mode.chained_assignment = None
scorerer = make_scorer(rmsle, greater_is_better=False)
researcher = GridSearchCV(clf, param_grid, scoring=scorerer, cv=5, n_jobs=4, verbose=1, refit=False)
researcher.fit(df, df["count"].values)

researcher.best_params_

researcher.best_score_