# 淺入淺出瞭解機器學習_02_Linear Regression(下)
###### tags: `淺入淺出瞭解機器學習`
## 多變量性迴歸
在上一篇文章中已經說明單變量的線性迴歸求解過程,這邊我們將這個過程擴展到多變量,並加回偏差項,也就是bias。
先將上一篇文章中已知的數學式寫出來,比較方便回憶:
* hypothesis $h_\theta(x)=\theta_1 x$
* loss function $\dfrac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2$
可以看到目前的hypothesis就只有一個參數,讓我們加入更多的參數,因為實務上通常擁有的特徵數都是不少的:
* new hypothesis,$h_\theta(x) = \theta_1 x_1 + \theta_2 x_2 + \theta_3 x_3 + ... + \theta_n x_n + \theta_0 x_0$
* 其中$x_0 = 1$,原本我們有$n$個特徵與$n$個參數,但因為加入bias,$\theta_0$,因此參數的維度變成$n+1$,所以我們加入一個相對應的$x_0=1$就可以讓兩者維度相同,這麼做的好處在於可以批次處理,只需要利用內積就可以一次求解,而不需要做那麼多的加法
以內積來一次性的計算,調整公式如下:
* $h_\theta(x) = \theta^T \cdot x$
* 假設,$\theta$表示所有的參數,以$x$表示所有的特徵,在兩者維度皆為$n+1$的情況下就可以調整為上面的數學式,非常優美、簡潔
看起來似乎不一樣,但其實以向量的角度來看的話,基本是沒有什麼變化。現在,我們的loss function就變成:
* $L(\theta) = \dfrac{1}{2m} \sum_{i=1}^m (h_\theta(x^{(i)}) - y^{(i)})^2$
* 其中$h_\theta(x) = \theta^T \cdot x$
加入更多的參數之後,我們的參數更新就變成如下:
* $\theta_j := \theta_j - \alpha\dfrac{\partial L}{\partial \theta_j}$
* 下標$j$代表第$j$個參數
其實加入再多的參數對於求偏微分沒有太大的差異,因為微分只會對相對應的參數有影響,其它的微了之後就不見,因此:
* $\forall \theta, \space \theta_j := \theta_j - \alpha \cdot \space \dfrac{1}{2m} \cdot 2 \sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)}) x^{(i)}_j$
* $\forall \theta$表示對所有的$\theta$,$\forall$表示"for all"
記得在上一篇文章提過的部份,更新這個動作必需等到所有的參數通通計算過梯度之後才可以做,如果你一邊計算一邊更新的話,那後面的參數更新通通都會是異常的,這點非常重要。也就是,
```python=
# 先寫入暫存
tmp_theta_1 = theta_1 - gradient_1
tmp_theta_2 = theta_2 - gradient_2
# 全部計算之後才可以更新
theta_1 = tmp_theta_1
theta_2 = tmp_theta_2
```
如果你是用tensorflow的話,作法也是雷同:
```python
import tensorflow as tf
# 注意到輸入維度與參數維度的變化
X = tf.constant([[1., 4.], [2., 5.], [3., 6.]]) # input
y = tf.constant([[1.], [2.], [3.]]) # output
w = tf.Variable(initial_value=[[0.], [0.]]) # theta
# 計算gradient
with tf.GradientTape() as tape:
# 定義loss function
L = tf.reduce_sum(tf.square(tf.matmul(X, w) - y)) / (X.shape[0] * 2)
# 計算L(w, b)關於w, b的偏導數
w_grad = tape.gradient(L, [w])
print(L, [w.numpy() for w in w_grad])
>>> tf.Tensor(2.3333333, shape=(), dtype=float32) [array([[ -4.666667],
[-10.666667]], dtype=float32)]
```
第一個特徵值的梯度與上一個案例一樣不變,因此得到相同的值,第二個特徵值我們可以怒算一波:
* $\dfrac{1}{3}*((0-1)*4 + (0-2)*5) + (0-3)*6)=\dfrac{1}{3}*-32=-10.67$
第一次迭代所計算的loss會跟上一次一樣只是單純因為初始化參數為0,因此怎麼算結果都會是一樣。
## 預測結果
假設,我們就是只訓練一個迭代,然後得到剛剛所計算出來的系數,這意味著我們的hypothesis現在是長這樣子,$h_\theta(x) = -4.67 \cdot x_1 - 10.67 \cdot x_2$。你只需要帶入你想預測的資料,也許我們想看看$[1, 4]$這組輸入現在會得到什麼結果,那只要帶入相對應的位置即可,也就是$-4.67 - 42.68 = -47.35$,好像爛的有點over,不過相信我,多訓練幾次迭代就會收斂的。
下面給出迭代執行更新的範例:
```python
X = tf.constant([[1., 4.], [2., 5.], [3., 6.]]) # input
y = tf.constant([[1.], [2.], [3.]]) # output
w = tf.Variable(initial_value=[[0.], [0.]]) # theta
variables = [w]
# 定義最佳化方式
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
# 迭代處理
for i in range(10):
with tf.GradientTape() as tape:
print(f'iteration: {i}')
L = tf.reduce_sum(tf.square(tf.matmul(X, w) - y)) / (X.shape[0] * 2)
# 計算L(w, b)關於w, b的偏導數
w_grad = tape.gradient(L, [w])
print(f'loss: {L.numpy()}, gradient: {w_grad}')
# 更新參數
optimizer.apply_gradients(grads_and_vars=zip(w_grad, variables))
print(f'after training theta: {w.numpy()}')
```
## 結論
直觀來看,特徵再多,只要向量化就可以批次處理,因此整體求解並沒有太大的變化。有一件事一直到這邊才說明,關於線性迴歸的loss function又稱為Mean Square Error,均方誤差,這不難理解,我們計算實際資料與預測資料之間的距離,然後計算平方取平均。而這個loss function或許並不適用於其它的演算法,因為每個演算法都可以有屬於它自己的loss function,當然可以自創,只是在你用自創的loss function之後,線性迴歸可能就不是大家所知道的線性迴歸。
另外,最佳化的方式有很多,在深度學習的領域中有著眾多最佳化的方式,sgd、momentum、adam、rmsprop以及最近非常火熱的LookAhead、Ranger、Ranger LARS..沒有絕對,只有不斷嚐試。
知道線性迴歸是怎麼一回事之後我們就可以瞭解下一個演算法,也就是Logistic Regression。
## 參考
吳恩達老師機器學習課程
李宏毅老師機器學習課程
Sebastian Raschka機器學習