系列文章目錄
機器學習演算法(一):1. numpy從零實現線性迴歸
機器學習演算法(一):2. 線性迴歸之多項式迴歸(特徵選取)
@
- 系列文章目錄
- 前言
- 一、理論介紹
- 二、程式碼實現
- 1、匯入庫
- 2、準備資料集
- 3、定義預測函式(predict)
- 4 代價(損失)函式
- 5 計算引數梯度
- 6 批次梯度下降
- 7 訓練
- 8 視覺化一下損失
- 總結
前言
最近,想將本科學過的一些機器學習演算法從零開始實現一下,畢竟天天當調包俠,雖然也能夠很愉快的玩耍,但對於我這個強迫症患者是很難受的,底層邏輯是怎麼樣的,還是需要知道的。接下來我會從最簡單的多元線性迴歸開始,一步一步在\(jupyter\)裡面實現。
【注1】:本文預設讀者具有基本的機器學習基礎、\(numpy\)基礎,如果沒有建議看完吳恩達老師的機器學習課程在來看本文。
【注2】:本文大部分程式碼會採用向量化加速程式設計,當然部分情況下也會用到迴圈遍歷的情況。後面有時間會再出一起迴圈遍歷實現的。
【注3】:作者實力有限,有錯在所難免,歡迎大家指出。
一、理論介紹
線性迴歸的預測函式:
寫成向量的形式:$$y = \mathbf{w^Tx}+b $$
其中:\(\mathbf{w} = (w_1,...,w_n)^T,\mathbf{x}=(x_1,...,x_n)^T\)
為了程式設計方便,將\(b\)收縮到\(w\)向量裡面去,有$$\mathbf{w} = (w_0,w_1,...,w_n)^T,w_0=b$$
上面模型變為$$y = \mathbf{w^Tx}$$
損失函式和偏導公式如下:
有了以上的理論操作,下面可以開始操作了
【注】:上述理論推導本文不作說明,有需要可參考周志華老師西瓜書。
二、程式碼實現
1、匯入庫
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
庫就不用介紹了,很常見的機器學習會用到的庫。
2、準備資料集
生成資料集的方式很多,random庫,或者你有資料(csv等)都可以。本文重點不是這個,因此簡單用numpy手打生成了。
X_train = np.array([[2104, 5, 1, 45], [1416, 3, 2, 40], [852, 2, 1, 35]])
y_train = np.array([[460], [232], [178]])
print(X_train)
print(y_train)
輸出結果:
注:我一般喜歡將向量是列向量還是行向量與數學公式中嚴格對應起來,否則,廣播機制出現了問題真的很頭疼的。
# 訓練集第一列插入全為1的1列
new_column = np.ones((X_train.shape[0], 1)) # 建立一列全是 1 的陣列
X_train = np.concatenate((new_column, X_train), axis=1) # 在索引為 0 的位置插入新列
print(X_train)
3、定義預測函式(predict)
def predict(x,w):
'''
:param x: 要預測的樣本點向量,x是行向量
:param w: 訓練出來的引數向量,w,是列向量
:param b: 訓練出來的引數 b ,b可以是標量,也可以是一個元素的陣列
:return: prediction,預測值
'''
return np.dot(x,w)
w_init = np.array([ [785.1811367994083],[0.39133535], [18.75376741], [-53.36032453], [-26.42131618]])
x_vec = X_train[0,:]
print(f"x_vec shape {x_vec.shape}, x_vec value: {x_vec}")
# make a prediction
f_wb = predict(x_vec,w_init)
print(f"f_wb shape {f_wb.shape}, prediction: {f_wb}")
輸出:
4 代價(損失)函式
向量化加速:
def loss(X,y,w):
m = X.shape[0]
err = np.dot(X,w) -y
return (1/(2*m))*np.dot(err.T,err)
cost = loss(X_train, y_train, w_init)
print(f'Cost at optimal w : {cost}')
輸出:
5 計算引數梯度
向量化加速:
def compute_gradient(X, y, w):
err = np.dot(X,w) - y
return (1/len(X))*np.dot(X.T,err)
g = compute_gradient(X_train,y_train,w_init)
print(g)
輸出:
6 批次梯度下降
def gradient_descent(X, y, w_in, loss, gradient_function, alpha, num_iters):
J_history = []
# 用來存每進行一次梯度後的損失,方便檢視損失變化。如果要畫損失變化也是用這個
# w = copy.deepcopy(w_in) #avoid modifying global w within function
w = w_in
for i in range(num_iters):
dj_dw = gradient_function(X, y, w)
w = w - alpha * dj_dw
if i<100000:
J_history.append( loss(X, y, w))
if i % math.ceil(num_iters / 10) == 0:
# 控制間隔 列印出一次損失結果
# 總迭代次數分成均分10組,在每組最後列印一次損失
print(f"迭代次數(梯度下降次數) {i}: Cost {J_history[-1]}")
return w,J_history
7 訓練
initial_w = np.zeros_like(w_init)
iterations = 1000
alpha = 5.0e-7
# run gradient descent
w_final,J_hist = gradient_descent(X_train, y_train, initial_w,loss, compute_gradient,
alpha, iterations)
print(f"b,w found by gradient descent: {w_final} ")
m = X_train.shape[0]
for i in range(m):
print(f"prediction: {np.dot(X_train[i], w_final)}, target value: {y_train[i]}")
輸出:
8 視覺化一下損失
J_hist = [i[0,0] for i in J_hist]
# 畫一下損失變化圖
fig, (ax1, ax2) = plt.subplots(1, 2, constrained_layout=True, figsize=(12, 4))
ax1.plot(J_hist)
ax2.plot(100 + np.arange(len(J_hist[100:])), J_hist[100:])
ax1.set_title("Cost vs. iteration"); ax2.set_title("Cost vs. iteration (tail)")
ax1.set_ylabel('Cost') ; ax2.set_ylabel('Cost')
ax1.set_xlabel('iteration step') ; ax2.set_xlabel('iteration step')
plt.show()
輸出:
明顯看到,隨便取的資料集,\(loss\)迭代到後面就穩定600多了,線性迴歸不太適合該資料集。有時間在用多項式迴歸試試效果。
總結
以上就是基本的線性迴歸實現思路了,這裡都是用矩陣向量化實現的,大家也可以試試迭代實現。後面有時間,我也會將迭代版本上傳的。
【注】:需要\(jupyter\)原始檔的可以評論區留言,看到我會回覆的。
【注】:最後,作者實力有限,有錯誤在所難免,歡迎大家指出,會慢慢修改。