import numpy as np
42)
np.random.seed(= 100
m = 2*np.random.rand(m,1)
X = 4, 3
a_0, a_1 = a_0 + a_1 * X + np.random.randn(m,1) y
Optymalizacja modeli w Pythonie
Na poprzednich zajęciach omawialiśmy wykorzystanie modelu regresji liniowej dla danych ustrukturyzowanych. W najprostszym przypadku dla jednej zmiennej \(X\) i jednej zmiennej celu moglibyśmy np. przypisać model w postaci:
satysfakcja_z_zycia = \(\alpha_0\) + \(\alpha_1\) PKB_per_capita
\(\alpha_0\) nazywamy punktem odcięcia (intercept) albo punktem obciążenia (bias)
import matplotlib.pyplot as plt
plt.scatter(X, y) plt.show()
W ogólności model liniowy: \(\hat{y} = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \dots + \alpha_n x_n\) gdzie \(\hat{y}\) to predykcja naszego modelu (wartość prognozowana), dla \(n\) cech przy wartościach cechy \(x_i\).
W postaci zwektoryzowanej możemy napisać: \(\hat{y} = \vec{\alpha}^{T} \vec{x}\)
W tej postaci widać dlaczego w tym modelu dokłada się kolumnę jedynek - wynikają one z wartości \(x_0\) dla \(\alpha_0\).
# dodajmy jedynkę do naszej tabeli
from sklearn.preprocessing import add_dummy_feature
= add_dummy_feature(X) X_b
Powiedzieliśmy, że możemy w tym modelu znaleźć funkcję kosztu
\(MSE(\vec{x}, \hat{y}) = \sum_{i=1}^{m} \left( \vec{\alpha}^{T} \vec{x}^{(i)} - y^{(i)} \right)^{2}\)
Tak naprawdę możemy \(MSE(\vec{x}, \hat{y}) = MSE(\vec{\alpha})\)
Rozwiązanie analityczne: \(\vec{\alpha} = (X^{T}X)^{-1} X^T y\)
# rozwiązanie analityczne
= np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y alpha_best
4,3]) alpha_best, np.array([
(array([[4.21509616],
[2.77011339]]),
array([4, 3]))
= np.array([[0],[2]])
X_new = add_dummy_feature(X_new)
X_new_b = X_new_b @ alpha_best
y_predict
import matplotlib.pyplot as plt
"r-", label="prediction")
plt.plot(X_new, y_predict, "b.")
plt.plot(X,y, plt.show()
from sklearn.linear_model import LinearRegression
= LinearRegression()
lin_reg
lin_reg.fit(X,y)
print(f"a_0={lin_reg.intercept_[0]}, a_1 = {lin_reg.coef_[0][0]}")
print("predykcja", lin_reg.predict(X_new))
a_0=4.215096157546746, a_1 = 2.770113386438484
predykcja [[4.21509616]
[9.75532293]]
# Logistic Regression w scikit learn oparta jest o metodę lstsq
= np.linalg.lstsq(X_b, y, rcond=1e-6)
alpha_best_svd, _, _, _ alpha_best_svd
array([[4.21509616],
[2.77011339]])
Gradient prosty
Pamiętaj o standaryzacji zmiennych (aby były one reprezentowane w tej samej skali).
Wsadowy gradient prosty
W celu implementacji musimy policzyć pochodne cząstkowe dla funkcji kosztu wobec każdego parametru \(\alpha_i\).
\(\frac{\partial}{\partial \alpha_j}MSE(\vec{x}, \hat{y}) = 2 \sum_{i=1}^{m} \left( \vec{\alpha}^{T} \vec{x}^{(i)} - y^{(i)} \right) x_j^{(i)}\)
Komputery posiadają własność mnożenia macierzy co pozwala obliczyć nam wszystkie pochodne w jednym obliczeniu. Wzór i algorytm liczący wszystkie pochodne “na raz” wykorzystuje cały zbiór X dlatego też nazywamy go wsadowym.
Po obliczeniu gradientu po prostu idziemy “w przeciwną stronę”
$ {next} = - {} MSE()$
from IPython.display import Image
='../img/02_10.png', width=500) Image(filename
= 0.1
eta = 1000
n_epochs = len(X_b)
m 42)
np.random.seed(= np.random.randn(2,1) # losowo wybieramy rozwiązanie
alpha
for epoch in range(n_epochs):
= 2/m* X_b.T @ (X_b @ alpha - y)
gradients #print(alpha)
= alpha - eta*gradients alpha
alpha
array([[4.21509616],
[2.77011339]])
sprawdz jak wygladają wyniki dla różnych eta dla 0.02, 0.1, 0.5
Stochastic gradient descent
Jednym z poważniejszych problemów wsadowego gradientu jest jego zależność od wykorzystania (w każdym kroku) całej macierzy danych. Korzystając z własności statystycznych możemy zobaczyć jak będzie realizowała się zbieżność rozwiązania jeśli za każdym razem wylosujemy próbkę danych i na niej określimy gradient. Ze względu, iż w pamięci przechowujemy tylko pewną porcję danych algorytm ten może być używany dla bardzo dużych zbiorów danych. Warto jednak mieć świadomość, że tak otrzymane wyniki mają charakter chaotyczny, co oznacza, że funkcja kosztu nie zbiega się w kierunku minimum lecz przeskakuje dążąc do minimun w sensie średniej.
= 50
n_epochs = len(X_b)
m
def learning_schedule(t, t0=5, t1=50):
return t0/(t+t1)
42)
np.random.seed(= np.random.randn(2,1)
alpha
for epoch in range(n_epochs):
for iteration in range(m):
= np.random.randint(m)
random_index = X_b[random_index : random_index + 1]
xi = y[random_index : random_index + 1]
yi = 2 * xi.T @ (xi @ alpha - yi)
gradients = learning_schedule(epoch * m + iteration)
eta = alpha - eta * gradients
alpha
alpha
array([[4.21076011],
[2.74856079]])
from sklearn.linear_model import SGDRegressor
= SGDRegressor(max_iter=1000, tol=1e-5,
sgd_reg =None, eta0=0.01,
penalty=100, random_state=42)
n_iter_no_change
sgd_reg.fit(X, y.ravel())
SGDRegressor(n_iter_no_change=100, penalty=None, random_state=42, tol=1e-05)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SGDRegressor(n_iter_no_change=100, penalty=None, random_state=42, tol=1e-05)
sgd_reg.intercept_, sgd_reg.coef_
(array([4.21278812]), array([2.77270267]))
Perceptron i OOP
from random import randint
1,6) randint(
3
from random import randint
class Kosc():
"""opis"""
def __init__(self, sciany=6):
"""
To jest metoda uruchamiana podczas inicjalizacji obiektu
params:
sciany (int)
"""
# zdefiniuj zmienną sciany i przypisz do niej domyślną wartość 6
self.sciany = sciany
def roll(self):
"""opis metody
metoda realizująca rzut kością - zwraca liczbę losową w zakresie 1 do liczby scian
"""
return randint(1, self.sciany)
= Kosc()
a # rzuć 10 razy kością i wyniki zapisz do listy
for _ in range(10)] [a.roll()
[1, 1, 4, 2, 4, 2, 4, 3, 2, 6]
from random import choice
0,1,2,3,4]) choice([
1
from random import choice
class RandomWalk():
def __init__(self, num_points=5000):
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
while len(self.x_values) < self.num_points:
# ruch prawo-lewo
# wylosuj kierunek dodatni lub ujemy oraz odległość 0-5 i przypisz do zmiennych
= choice([-1,1])
x_direction = choice([0,1,2,3,4])
x_distance = x_direction*x_distance
x_step
= choice([-1,1])
y_direction = choice([0,1,2,3,4])
y_distance = y_direction*y_distance
y_step
# napisz warunek pomijający krok gdy x i y step = 0 (użyj continue)
if x_step == 0 and y_step == 0:
continue
= self.x_values[-1] + x_step
next_x = self.y_values[-1] + y_step
next_y
self.x_values.append(next_x)
self.y_values.append(next_y)
= RandomWalk()
rw
rw.fill_walk()
5] rw.x_values[:
[0, 0, -1, 2, 5]
import matplotlib.pyplot as plt
= list(range(rw.num_points))
point_number =point_number, cmap=plt.cm.Blues,
plt.scatter(rw.x_values, rw.y_values, c='none', s=15)
edgecolor0,0,c='green', edgecolor='none', s=100)
plt.scatter(-1], rw.y_values[-1],c='red', edgecolor='none', s=100)
plt.scatter(rw.x_values[
plt.show()
Sztuczne neurony - rys historyczny
W 1943 roku W. McCulloch i W. Pitts zaprezentowali pierwszą koncepcję uproszczonego modelu komórki nerwowej tzw. Nuronu McCulloch-Pittsa (MCP). W.S. McCulloch, W. Pitts, A logical Calculus of the Ideas Immanent in Nervous Activity. “The Bulletin of Mathematical Biophysics” 1943 nr 5(4)
Neuronami nazywamy wzajemnie połączone komórki nerwowe w mózgu, które są odpowiedzialne za przetwarzanie i przesyłanie sygnałów chemicznych i elektrycznych. Komórka taka opisana jest jako bramka logiczna zawierająca binarne wyjścia. Do dendrytów dociera duża liczba sygnałów, które są integrowane w ciele komórki i (jeżeli energia przekracza określoną wartość progową) zostaje wygenerowany sygnał wyjściowy przepuszczany przez akson.
='../img/02_01.png', width=800) Image(filename
Po kilku latach Frank Rosenblatt (na podstawie MCP) zaproponował pierwszą koncepcję reguły uczenia perceprtonu. F. Rosenblatt, The Perceptron, a Perceiving and Recognizing Automaton, Cornell Aeronautical Laboratory, 1957
='../img/02_04.png', width=800) Image(filename
='../img/02_02.png', width=800) Image(filename
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
= load_iris()
iris = pd.DataFrame(data= np.c_[iris['data'], iris['target']],
df = iris['feature_names'] + ['target']) columns
= df.iloc[:100,[0,2]].values
X = df.iloc[0:100,4].values
y = np.where(y == 0, -1, 1)
y
import matplotlib.pyplot as plt
50,0],X[:50,1],color='red', marker='o',label='setosa')
plt.scatter(X[:50:100,0],X[50:100,1],color='blue', marker='x',label='versicolor')
plt.scatter(X['sepal length (cm)')
plt.xlabel('petal length (cm)')
plt.ylabel(='upper left')
plt.legend(loc plt.show()
dziecko = Perceptron()
dziecko.fit()
# dziecko musi mieć parametr uczenia
dziecko.eta
# możemy sprawdzić jak szybko się uczy == ile błędów robi
dziecko.errors_
# rozwiązania znajdą się w wagach
dziecko.w_
# w naszym przypadku dziecko uczy się dwóch wag !
class Perceptron():
def __init__(self, n_iter=10, eta=0.01):
self.n_iter = n_iter
self.eta = eta
def fit(self, X, y):
self.w_ = np.zeros(1+X.shape[1])
self.errors_ = []
for _ in range(self.n_iter):
pass
return self
import random
class Perceptron():
def __init__(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter
def fit(self, X, y):
#self.w_ = np.zeros(1+X.shape[1])
self.w_ = [random.uniform(-1.0, 1.0) for _ in range(1+X.shape[1])]
self.errors_ = []
for _ in range(self.n_iter):
= 0
errors for xi, target in zip(X,y):
#print(xi, target)
= self.eta*(target-self.predict(xi))
update #print(update)
self.w_[1:] += update*xi
self.w_[0] += update
#print(self.w_)
+= int(update != 0.0)
errors self.errors_.append(errors)
return self
def net_input(self, X):
return np.dot(X, self.w_[1:])+self.w_[0]
def predict(self, X):
return np.where(self.net_input(X)>=0.0, 1, -1)
# uzycie jak wszsytkie klasy sklearn
= Perceptron()
ppn ppn.fit(X,y)
<__main__.Perceptron at 0x30454a810>
print(ppn.errors_)
print(ppn.w_)
[7, 3, 2, 3, 2, 1, 0, 0, 0, 0]
[-0.7593420638708699, 0.06978770951926482, 0.13667189800765753]
-3, 5])) ppn.predict(np.array([
array(-1)
# dodatkowa funkcja
from matplotlib.colors import ListedColormap
def plot_decision_regions(X,y,classifier, resolution=0.02):
= ('s','x','o','^','v')
markers = ('red','blue','lightgreen','gray','cyan')
colors = ListedColormap(colors[:len(np.unique(y))])
cmap
= X[:,0].min() - 1, X[:,0].max()+1
x1_min, x1_max = X[:,1].min() -1, X[:,1].max()+1
x2_min, x2_max = np.meshgrid(np.arange(x1_min, x1_max, resolution),
xx1, xx2
np.arange(x2_min, x2_max, resolution))= classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
Z =0.4, cmap=cmap)
plt.contourf(xx1, xx2, Z, alphamin(), xx1.max())
plt.xlim(xx1.min(),xx2.max())
plt.ylim(xx2.
for idx, cl in enumerate(np.unique(y)):
=X[y == cl,0], y=X[y==cl,1], alpha=0.8, color=cmap(idx), marker=markers[idx], label=cl)
plt.scatter(x
# dla kwiatków
=ppn)
plot_decision_regions(X,y,classifier"dlugosc dzialki [cm]")
plt.xlabel("dlugosc platka [cm]")
plt.ylabel(='upper left')
plt.legend(loc plt.show()
='../img/02_09.png', width=600) Image(filename
# ZADANIE - Opisz czym różni się poniższy algorytm od Perceprtona ?
class Adaline():
'''Klasyfikator - ADAptacyjny LIniowy NEuron'''
def __init__(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter
def fit(self, X,y):
#self.w_ = np.zeros(1+X.shape[1])
import random
self.w_ = [random.uniform(-1.0, 1.0) for _ in range(1+X.shape[1])]
self.cost_ = []
for i in range(self.n_iter):
= self.net_input(X)
net_input = self.activation(X)
output = (y-output)
errors self.w_[1:] += self.eta * X.T.dot(errors)
self.w_[0] += self.eta * errors.sum()
= (errors**2).sum() / 2.0
cost self.cost_.append(cost)
return self
def net_input(self, X):
return np.dot(X, self.w_[1:]) + self.w_[0]
def activation(self, X):
return self.net_input(X)
def predict(self, X):
return np.where(self.activation(X) >= 0.0, 1, -1)
= Adaline(n_iter=20, eta=0.01)
ad
ad.fit(X,y)
print(ad.w_)
=ad)
plot_decision_regions(X,y,classifier"dlugosc dzialki [cm]")
plt.xlabel("dlugosc platka [cm]")
plt.ylabel(='upper left')
plt.legend(loc plt.show()
[-7.007375104548615e+30, -3.913586524697914e+31, -2.1895923576993013e+31]
10] ad.cost_[:
[589.6216559520215,
894654.4940675091,
1395529303.6601284,
2176827739791.7388,
3395542469963400.5,
5.29656456254259e+18,
8.261889348562429e+21,
1.2887375354700108e+25,
2.010247735426649e+28,
3.1357012941461006e+31]
= Adaline(n_iter=100, eta=0.0001)
ad2
ad2.fit(X,y)
=ad2)
plot_decision_regions(X,y,classifier"dlugosc dzialki [cm]")
plt.xlabel("dlugosc platka [cm]")
plt.ylabel(='upper left')
plt.legend(loc plt.show()
print(ad2.w_)
-10:] ad2.cost_[
[0.03259855193831394, -0.2832913340615083, 0.5509821059793044]
[9.092674275002349,
8.949907500366704,
8.810273785299684,
8.673704364120878,
8.540131980448582,
8.409490854073036,
8.28171664855683,
8.15674643954629,
8.034518683778513,
7.91497318876858]
%%file app.py
import pickle
import numpy as np
from flask import Flask, request, jsonify
class Perceptron():
def __init__(self, eta=0.01, n_iter=10):
self.eta = eta
self.n_iter = n_iter
def fit(self, X, y):
self.w_ = [random.uniform(-1.0, 1.0) for _ in range(1+X.shape[1])]
self.errors_ = []
for _ in range(self.n_iter):
= 0
errors for xi, target in zip(X,y):
= self.eta*(target-self.predict(xi))
update self.w_[1:] += update*xi
self.w_[0] += update
+= int(update != 0.0)
errors self.errors_.append(errors)
return self
def net_input(self, X):
return np.dot(X, self.w_[1:])+self.w_[0]
def predict(self, X):
return np.where(self.net_input(X)>=0.0, 1, -1)
# Create a flask
= Flask(__name__)
app
# Create an API end point
@app.route('/predict_get', methods=['GET'])
def get_prediction():
# sepal length
= float(request.args.get('sl'))
sepal_length = float(request.args.get('pl'))
petal_length
= [sepal_length, petal_length]
features
# Load pickled model file
with open('model.pkl',"rb") as picklefile:
= pickle.load(picklefile)
model
# Predict the class using the model
= int(model.predict(features))
predicted_class
# Return a json object containing the features and prediction
return jsonify(features=features, predicted_class=predicted_class)
@app.route('/predict_post', methods=['POST'])
def post_predict():
= request.get_json(force=True)
data # sepal length
= float(data.get('sl'))
sepal_length = float(data.get('pl'))
petal_length
= [sepal_length, petal_length]
features
# Load pickled model file
with open('model.pkl',"rb") as picklefile:
= pickle.load(picklefile)
model
# Predict the class using the model
= int(model.predict(features))
predicted_class = dict(features=features, predicted_class=predicted_class)
output # Return a json object containing the features and prediction
return jsonify(output)
if __name__ == '__main__':
='0.0.0.0', port=5000) app.run(host
Writing app.py
import requests
= requests.get("http://127.0.0.1:5000/predict_get?sl=6.3&pl=2.6")
response print(response.content)
import requests
= {"sl":2.4, "pl":2.6}
json = requests.post("http://127.0.0.1:5000/predict_post", json=json)
response print(response.content)