Matma_AI_cyber/Projekt_2/fixed/algorytm.ipynb
2022-06-26 16:38:17 +02:00

77 KiB

Algorytm najszybszego spadku dla regresji wielomianowej

Algorytm przyjmuje zbiór danych - x oraz y i próbuje wyzaczyć funkcję wilomianową, która najlepiej przewiduje wartości y na podstawie x. Wynikiem jest wyznaczenie współczynników wielomianu.

Importy

from matplotlib import pyplot as plt
import numpy as np
import random

Początkowe współczynniki

Tworzymy losowe początkowe współczynniki wielomianu - od nich algorytm rozpocznie dopasowanie. Oraz oryginalne współczynniki na podstawie których zostanie wyznaczony zbiór danych. Aby zmienić generowany zbiór danych należy zmienić tablicę coeffs.

rand_coeffs = (random.randrange(-10, 10), random.randrange(-10, 10), random.randrange(-10,10))
coeffs = [2, -5, 4] # a, b, c
#coeffs = [17, -8, 4] # a, b, c

k = input("Podaj stopień wielomianu: ")
k = int(k)
print(k)
2

Wyznaczenie wartości wielomianu

Funkcja na podstawie współczynników oraz x wyznacza wartość y wielomianu.

def eval_2nd_degree(coeffs, x, k):
    a = (coeffs[0]*(x**k))
    b = coeffs[1]*(x**(k-1))
    c = coeffs[2]

    y = a+b+c
    return y

#eval_2nd_degree(coeffs, 3, 4)
    

Wartości wielomianu z szumem

Funkcja jest analogiczna do poprzedniej - wyznacza wartość wielomianu na podstawie wpółczynników oraz x, ale dodatkowo dodaje szum do wyjściowych wartości - funkcja zostanie użyta przy generowaniu danych.

def eval_2nd_degree_jitter(coeffs, x, j):
    y = eval_2nd_degree(coeffs, x, k)
    
    interval_min = y-j
    interval_max = y+j
    
    return random.uniform(interval_min, interval_max)

Wygenerowanie danych

Kod generuje zbiór danych. Na podstawie wartości x od -10 do 10 i losowych współczynników wielomianu generuje wartości y z szumem. Parametr j określa jak moco dane będą zaszumione

hundred_xs=np.random.uniform(-10,10,100)
print(hundred_xs)

j=50
x_y_pairs = []
for x in hundred_xs:
    y  = eval_2nd_degree_jitter(coeffs, x, j)
    x_y_pairs.append((x,y))
    
xs = []
ys = []
for a,b in x_y_pairs:
    xs.append(a)
    ys.append(b)
    
plt.figure(figsize=(20,10))
plt.plot(xs, ys, 'g+')
plt.title('Original data')
plt.show()
[ 1.93805554 -7.14146898  4.80847965  6.7923386  -4.9820572  -8.36721666
 -3.03590771  7.10302797  3.6567952  -4.61893312 -7.77544379  4.33448858
 -0.84984343 -0.4818275   7.11385026 -9.51525895 -4.38281891  8.47750482
  8.4505284  -2.03857269  6.43008266  9.91978403  9.68784877 -6.51416545
 -9.46191372  9.53098301 -3.26192799  4.78375147 -0.48803478  4.2719198
  7.87940586 -9.71405924 -8.59615622 -0.99001719 -5.70862611  4.81815229
 -4.75636826  3.00461241  8.0931194  -8.73839205  3.5703026  -1.70997658
  8.397044   -7.13453958 -5.68033115  6.43496878  0.63967305  3.47292048
 -1.96283003 -5.96053698  7.02062456  6.6672641   1.14217053 -9.21276365
  4.7201797  -5.29291118  3.36596207  4.81029689 -6.72797697  0.21795055
 -9.81276562  1.06328435  7.70633934  4.66017061 -2.2623938  -7.86967321
 -2.87083371 -2.75685526  6.93841449 -2.01111387 -7.23430145  4.37277354
  0.88088525  8.62278739 -9.56199168  9.86296681 -6.41765534 -9.41510031
  3.27903273  4.84209117  3.0707047   3.82251549  6.72063776  7.53313017
  5.93242504  4.2171624   9.82285025 -4.81145849 -7.40363677  5.89354852
  9.03248351 -0.90170662 -1.86450263 -7.55012017  9.99192671 -1.20030438
  4.7200473  -6.71344168  1.93127689 -1.30512735]

Funkcja straty

Do określenia jak mocno przewidziane wartości y są różne zostanie użyta kwadratowa funkcja straty.

def loss_mse(ys, y_bar):
    return sum((ys - y_bar)*(ys - y_bar)) / len(ys)

Gradient

Funkcja przyjmuje współczynniki wielomianu, x, y oraz parametr prękości uczenia i wylicza za pomocą gradientu nowe wartości współczynników wielomianu.

def calc_gradient_2nd_poly_for_GD(coeffs, inputs_x, outputs_y, lr, k): 
    a_s = []
    b_s = []
    c_s = []
        
    y_bars = eval_2nd_degree(coeffs, inputs_x, k)


    # tu zamiast 2 dodałem k i w potencial_b k-1, czy tylko to powinno byś zmiaenione?
    for x,y,y_bar in list(zip(inputs_x, outputs_y, y_bars)):    # take tuple of (x datapoint, actual y label, predicted y label)
        x_squared = x**k      
        partial_a = x_squared * (y - y_bar)
        a_s.append(partial_a)
        partial_b = x**(k-1) * (y-y_bar)
        b_s.append(partial_b)
        partial_c = (y-y_bar)
        c_s.append(partial_c)
    
    num = [i for i in y_bars]
    n = len(num)
    
    gradient_a = (-2 / n) * sum(a_s)
    gradient_b = (-2 / n) * sum(b_s)
    gradient_c = (-2 / n) * sum(c_s)


    a_new = coeffs[0] - lr * gradient_a
    b_new = coeffs[1] - lr * gradient_b
    c_new = coeffs[2] - lr * gradient_c
    
    new_model_coeffs = (a_new, b_new, c_new)
    
    #update with these new coeffs:
    new_y_bar = eval_2nd_degree(new_model_coeffs, inputs_x, k)
    
    updated_model_loss = loss_mse(outputs_y, new_y_bar)
    return updated_model_loss, new_model_coeffs, new_y_bar

Minimalizacja

Funkcja powtarza proces wyznaczenia nowych współczynników wielomianu zadaną ilość razy - epoch.

def gradient_descent(epochs, lr):
    losses = []
    rand_coeffs_to_test = rand_coeffs
    for i in range(epochs):
        loss = calc_gradient_2nd_poly_for_GD(rand_coeffs_to_test, hundred_xs, ys, lr, k)
        rand_coeffs_to_test = loss[1]
        losses.append(loss[0])
    #print(losses)
    return loss[0], loss[1], loss[2], losses  #(updated_model_loss, new_model_coeffs, new_y_bar, saved loss updates)

Uruchomienie

Wartości wyznaczonego wielomionu zostają wyświetlone na wykresie z porównaniem do zbioru danych.

GD = gradient_descent(1500, 0.0001)

plt.figure(figsize=(20,10))
plt.plot(xs, ys, 'g+', label = 'original')
plt.plot(xs, GD[2], 'b.', label = 'final_prediction')
plt.title('Original vs Final prediction after Gradient Descent')
plt.legend(loc="lower right")
plt.show()

Wyznaczone współczynniki wielomianu

print("Początkowe współczynniki {}".format(rand_coeffs))
print("Wyznaczone współczynniki {}".format(GD[1]))
print("Oryginalne współczynniki {}".format(coeffs))
Początkowe współczynniki (7, 5, -6)
Wyznaczone współczynniki (2.1546532016162714, -4.490001059494276, -5.122120484596819)
Oryginalne współczynniki [2, -5, 4]

Funkcja straty

Wykres przedstawia jak zmianiała się wartość funkcji straty w kolejnych krokach algorytmu.

plt.figure(figsize=(20,10))
plt.plot(GD[3], 'b-', label = 'loss')
plt.title('Loss over 1500 iterations')
plt.legend(loc="lower right")
plt.xlabel('Iterations')
plt.ylabel('MSE')
plt.show()