前言:本篇博文主要介紹線性迴歸模型(linear regression),首先介紹相關的基礎概念和原理,然後通過Python程式碼實現線性迴歸模型。特別強調,其中大多理論知識來源於《統計學習方法_李航》斯坦福課程翻譯筆記以及Coursera機器學習課程

1.線性迴歸

迴歸模型(regression model)也叫做擬合模型,通俗點解釋,就是假設我們有很多資料,包含房子的面積X和對應的房價y,那麼我們希望得到房價y關於面積X的關係(h(X) = y),那麼現在你知道一個房屋的面積Xi,基本就可以根據這個關係預測(推導)出房價yi了。

之所以叫做“線性迴歸”,因為我們假設y與X具有的是一種線性關係,稱為假設函式:


其中引數w0稱為“偏置”,w1稱為“權重”,x表示輸入變數X的一個“特徵變數”,這裡先假設只有一個特徵變數。

Note:我這裡用w代表theta,他的實際含義都是“weight”,建議相關的知識細節,到這裡學習!!!

2.成本函式(cost function)

我們要知道,擬合的函式h(x)有一定的誤差,我們用成本函式J來表示“預測輸出”h(x)與實際輸出y之間的誤差。這裡使用最簡單的“最小均方”(Least mean square)來描述:


其中,m表示樣本的數量,i表示第i個樣本,其中的1/2,是為了求導方便而新增的專業習慣。

3.引數學習(梯度下降)

所以我們現在的目標是,找出怎麼樣的引數,使得假設函式預測得到的誤差最小,也就是最小化成本函式J。


關於成本函式J的影象大致像一個“碗”(上),我們來看看J關於一個引數w1(其實應該是theta,代表權重引數)的影象(下):

   

所以按照一般的思路,就是對權重引數求導,使得倒數為0的引數的值,就是成本函式J取得最小值的地方。但是大多時候,求得的偏導的數學表示式很複雜,所以我們採用一種叫做“梯度下降(gradient descent)”的方法

也就是如下:


通俗一點講,就是希望權重引數“沿著J減小的方向(梯度減小的方向),一步一步(也就是學習率alpha)的走到那個最低點”。細節不解釋了,還是老地方講的深入淺出。

4.線性迴歸模型Python程式碼實現

先看看資料集

from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
print diabetes.keys()
data = diabetes.data #real -0.2<x<0.2
target = diabetes.target #integer 25<y<346
print data.shape
print target.shape
print data[:5]
print target[:5]
輸入:

劃分一下資料集,這裡就不劃分驗證集了:
import numpy as np
#use only one feature
X = data[:,:1]
y = target
#X_train = np.array([[0], [1], [2]])
#X_test = np.array([[3], [4], [6]])
#y_train = np.array([0,1,2]).reshape(-1,1)
#y_test = np.array([3,4,5]).reshape(-1,1)
X_train = X[:-20]
X_test = X[-20:]
y_train = y[:-20].reshape((-1,1))
y_test = y[-20:].reshape((-1,1))
print 'X_train=',X_train.shape
print 'X_test=',X_test.shape
print 'y_train=',y_train.shape
print 'y_test=',y_test.shape
接下來完成線性迴歸模型的類的編寫:
class linear(object):
    def __init__(self):
        self.W = None
        self.b = None
    
    def loss(self,X,y):
        num_feature  = X.shape[1]
        num_train = X.shape[0]
        
        h = X.dot(self.W) + self.b
        loss = 0.5 *np.sum(np.square(h - y)) / num_train
        
        dW = X.T.dot((h-y)) / num_train
        db = np.sum((h-y)) / num_train
        
        return loss,dW,db
        
    def train(self,X,y,learn_rate = 0.001,iters = 10000):
        num_feature = X.shape[1]
        self.W = np.zeros((num_feature,1))
        self.b = 0
        loss_list = []
        
        for i in xrange(iters):
            loss,dW,db = self.loss(X,y)
            loss_list.append(loss)
            self.W += -learn_rate*dW
            self.b += -learn_rate*db
            
            if i%500 == 0:
                print 'iters = %d,loss = %f' % (i,loss)
        return loss_list
        
    def predict(self,X_test):
        y_pred = X.dot(self.W) + self.b
        return y_pred
    
    pass

測試訓練一下:
classify = linear()
print 'start'
loss_list = classify.train(X_train,y_train)
print 'end'

print classify.W,classify.b
最後視覺化一下,訓練的結果:
import matplotlib.pyplot as plt
f = X_train.dot(classify.W) + classify.b
fig = plt.figure()
plt.subplot(211)
plt.scatter(X_train,y_train,color = 'black')
plt.scatter(X_test,y_test,color = 'blue')
plt.plot(X_train,f,color = 'red')
plt.xlabel('X')
plt.ylabel('y')

plt.subplot(212)
plt.plot(loss_list,color = 'blue')
plt.xlabel('epochs')
plt.ylabel('errors')
plt.show()



可以看到,線性迴歸學習得到的直線使得樣本資料均勻分佈在兩邊,由於樣本的複雜性,加上這裡模型只選擇一個特徵,太簡單(其實屬於欠擬合,高偏差),所以擬合的不好,誤差較大,但是基本的原理基本就是這樣了。

ps:至於其他的一些細節:正則化、特徵歸一化、多項式特徵等問題,等有空再上程式碼,畢竟一下子也說不清。