# 淺入淺出瞭解機器學習_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機器學習