kerreg-MPSKICB/kernel_regression.ipynb
2021-06-01 00:47:01 +02:00

3.5 MiB

Regresja jądrowa

Karolina Oparczyk, Tomasz Grzybowski, Jan Nowak

Regresja jądrowa używana jest jako funkcja wagi do opracowania modelu regresji nieparametrycznej. Nadaje ona niektórym elementom zbioru większą "wagę", która ma wpływ na ostateczny wynik.

Można ją porównać do rysowania krzywej na wykresie punktowym tak, aby była jak najlepiej do nich dopasowana.

Właściwości regresji jądrowej:

  • symetryczna - wartość maksymalna leży pośrodku krzywej
  • powierzchnia pod krzywą funkcji wynosi 1
  • wartość funkcji jądrowej nie jest ujemna

Do implementacji regresji jądrowej można użyć wielu różnych jąder. Przykłady użyte w projekcie:

  • jądro Gaussa \begin{equation} K(x) = \frac1{h\sqrt{2\pi}}e^{-\frac12(\frac{x - x_i}h)^2} \end{equation}
import KernelRegression
import plotly.express as px
import numpy as np
kernel_x = np.arange(-4,4,0.1)
col = KernelRegression.kernel_function(1, kernel_x, 0)
px.line(x=kernel_x, y=col, title='Funkcja jądrowa Gaussa')
  • jądro Epanechnikova \begin{equation} K(x) = (\frac34)(1-(\frac{x - x_i}h)^2) \text{ dla } {|x|\leq1} \end{equation}
kernel_x = np.arange(-2,2,0.1)
col = KernelRegression.epanechnikov_list(1, kernel_x, 0)
px.line(x=kernel_x, y=col, title='Funkcja jądrowa Epanechnikova')

Istotne znaczenie ma nie tylko dobór jądra, ale również parametru wygładzania, czyli szerokości okna. W zależności od niego, punkty są grupowane i dla każdej grupy wyliczana jest wartość funkcji. Jeśli okno będzie zbyt szerokie, funkcja będzie bardziej przypominała prostą (under-fitting). Natomiast jeśli będzie zbyt wąskie, funkcja będzie za bardzo "skakać" (over-fitting).

Wyliczenie wartości funkcji polega na wzięciu średniej ważonej z $y_{i}$ dla takich $x_{i}$, które znajdują się blisko x, dla którego wyznaczamy wartość. Wagi przy $y_{i}$ dla x sumują się do 1 i są wyższe, kiedy $x_{i}$ jest bliżej x oraz niższe w przeciwnym przypadku.

\begin{equation}w_i= \frac{K_i}{\sum\limits_{i=1}^n K_i} \end{equation}

\begin{equation} Y=w_1*x_1+w_2*x_2+ \text{...} +w_n*x_n \end{equation}
import ipywidgets as widgets
import plotly.graph_objs as go
import pandas as pd 

fires_thefts = pd.read_csv('fires_thefts.csv', names=['x','y'])
XXX = np.sort(np.array(fires_thefts.x))
YYY = np.array(fires_thefts.y)

dropdown_bw = widgets.Dropdown(options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], value=1, description='Szerokość okna')

def interactive_kernel(bw_manual):    
    Y_pred_gauss = KernelRegression.ker_reg(XXX, YYY, bw_manual, 'gauss')
    Y_pred_epanechnikov = KernelRegression.ker_reg(XXX, YYY, bw_manual, 'epanechnikov')

    fig = px.scatter(x=XXX,y=YYY)
    fig.add_trace(go.Scatter(x=XXX, y=np.array(Y_pred_gauss), name='Gauss',  mode='lines'))
    fig.add_trace(go.Scatter(x=XXX, y=np.array(Y_pred_epanechnikov), name='Epanechnikov',  mode='lines'))
    fig.show()
    
    # kernel regression
    kernel_x = np.arange(min(XXX)-5,max(XXX)+5, 0.1)

    ## Plotting gaussian for all input x points 
    kernel_fns = {'kernel_x': kernel_x}
    for input_x in XXX: 
        input_string= 'x_value_{}'.format(np.round(input_x,2)) 
        kernel_fns[input_string] = KernelRegression.kernel_function(bw_manual, kernel_x, input_x)

    kernels_df = pd.DataFrame(data=kernel_fns)
    y_all = kernels_df.drop(columns='kernel_x')
    fig = px.line(kernels_df, x='kernel_x', y=y_all.columns, title='Gaussian for all input points', range_x=[min(XXX)-5,max(XXX)+5])    
    fig.show()
widgets.interact(interactive_kernel, bw_manual=dropdown_bw)
interactive(children=(Dropdown(description='Szerokość okna', options=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), value=1)…
<function __main__.interactive_kernel(bw_manual)>
bw_manual = 3

kernel_x = XXX
col3 = KernelRegression.kernel_function(bw_manual, kernel_x, XXX[0])
# Dataframe for a single observation point x_i. In the code x_i comes from new_x
data = {'Input_x': [XXX[0] for x in kernel_x],
        'kernel_x': kernel_x,
        'weigth': col3,
        'Y': YYY,
        'Y=w0*x0+w1*x1+...+w41*x41': ''
        }
single_pt_KE = pd.DataFrame(data=data)
single_pt_KE.at[len(YYY)//2, 'Y=w0*x0+w1*x1+...+w41*x41'] = sum([col3[index]*YYY[index] for index, x in enumerate(kernel_x)])
single_pt_KE['weigth'] = single_pt_KE['weigth'].map('{:,.10f}'.format)
single_pt_KE['kernel_x'] = single_pt_KE['kernel_x'].map('K({})'.format)
single_pt_KE
Input_x kernel_x weigth Y Y=w0*x0+w1*x1+...+w41*x41
0 2.0 K(2.0) 0.1329807601 29
1 2.0 K(2.2) 0.1326855754 44
2 2.0 K(2.2) 0.1326855754 36
3 2.0 K(2.5) 0.1311465720 37
4 2.0 K(3.4) 0.1192611431 53
5 2.0 K(3.6) 0.1153512977 68
6 2.0 K(4.0) 0.1064826685 75
7 2.0 K(4.8) 0.0860259420 18
8 2.0 K(5.0) 0.0806569082 31
9 2.0 K(5.4) 0.0699640987 25
10 2.0 K(5.6) 0.0647286850 34
11 2.0 K(5.7) 0.0621560962 14
12 2.0 K(6.2) 0.0499091552 11
13 2.0 K(6.9) 0.0350338791 11
14 2.0 K(7.2) 0.0296061537 22
15 2.0 K(7.3) 0.0279285343 16
16 2.0 K(7.7) 0.0218719383 27
17 2.0 K(8.6) 0.0118248643 9
18 2.0 K(9.0) 0.0087406297 29
19 2.0 K(9.5) 0.0058427668 30
20 2.0 K(10.5) 0.0024020333 40
21 2.0 K(10.5) 0.0024020333 32 54.121103
22 2.0 K(10.7) 0.0019841775 41
23 2.0 K(10.8) 0.0018003521 147
24 2.0 K(11.0) 0.0014772828 22
25 2.0 K(11.3) 0.0010889397 29
26 2.0 K(11.9) 0.0005741896 46
27 2.0 K(12.2) 0.0004107397 23
28 2.0 K(15.1) 0.0000096222 4
29 2.0 K(15.1) 0.0000096222 31
30 2.0 K(16.5) 0.0000011246 39
31 2.0 K(17.4) 0.0000002522 15
32 2.0 K(18.4) 0.0000000431 32
33 2.0 K(18.5) 0.0000000359 27
34 2.0 K(21.6) 0.0000000001 32
35 2.0 K(21.8) 0.0000000000 34
36 2.0 K(23.3) 0.0000000000 17
37 2.0 K(28.6) 0.0000000000 46
38 2.0 K(29.1) 0.0000000000 42
39 2.0 K(34.1) 0.0000000000 43
40 2.0 K(36.2) 0.0000000000 34
41 2.0 K(39.7) 0.0000000000 19
from sklearn.linear_model import LinearRegression
# linear regression
reg = LinearRegression().fit(XXX.reshape(-1, 1), YYY.reshape(-1, 1))
Y_pred_linear = reg.predict(XXX.reshape(-1, 1))

# kernel regression
Y_pred_gauss = KernelRegression.ker_reg(XXX, YYY, bw_manual, 'gauss')
Y_pred_epanechnikov = KernelRegression.ker_reg(XXX, YYY, bw_manual, 'epanechnikov')

fig = px.scatter(x=XXX,y=YYY)
fig.add_trace(go.Scatter(x=XXX, y=np.array(Y_pred_gauss), name='Gauss',  mode='lines'))
fig.add_trace(go.Scatter(x=XXX, y=np.array(Y_pred_epanechnikov), name='Epanechnikov',  mode='lines'))
fig.add_trace(go.Scatter(x=XXX, y=np.array(Y_pred_linear.flatten().tolist()), name='Linear',  mode='lines'))