從零開始實現邏輯回歸模型

聲明:版權所有,轉載請聯繫作者並註明出處: 風雪夜歸子 - CSDN博客

知乎專欄: zhihu.com/people/feng-x

由於知乎不支持markdown格式,所以有公式的地方都使用了截圖,若影響閱讀效果,可移步我的Blog:風雪夜歸子 - CSDN博客,文章會同步更新!

邏輯回歸

上一篇文章介紹了線性回歸、嶺回歸、lasso回歸和多項式回歸模型。這些模型都是廣義線性回歸模型的具體形式,廣義線性回歸是一種靈活的框架,比普通線性回歸要求更少的假設。這一章,我們討論廣義線性回歸模型的具體形式的另一種形式,邏輯回歸(logistic regression)。

邏輯回歸模型在工業界是工程師用的非常多的模型了,比如在CTR預測等項目中被大量使用,之所以用的非常普遍,是因為邏輯回歸擁有簡單、解釋性好、計算速度快等優點。

和前面討論的模型不同,邏輯回歸是用來做分類任務的。分類任務的目標是找一個函數,把觀測值匹配到相關的類和標籤上。學習演算法必須用成對的特徵向量和對應的標籤來估計匹配函數的參數,從而實現更好的分類效果。在二元分類(binary classification)中,分類演算法必須把一個實例分為兩個類別。二元分類案例包括,預測患者是否患有某種疾病,音頻中是否含有人聲,杜克大學男子籃球隊在NCAA比賽中第一場的輸贏。

普通的線性回歸假設響應變數呈正態分布,也稱為高斯分布(Gaussian distribution )或鐘形曲線(bell curve)。正態分布數據是對稱的,且均值,中位數和眾數(mode)是一樣的。很多自然現象都服從正態分布。比如,人類的身高就服從正態分布,姚明那樣的高度極少,在99%之外了。

在某些問題里,響應變數不是正態分布的。比如,擲一個硬幣獲取正反兩面的概率分布是伯努力分布(Bernoulli distribution),又稱兩點分布或者0-1分布。表示一個事件發生的概率是P,不發生的概率1?P,概率在[0, 1]之間。線性回歸假設自變數(解釋變數)值的變化會引起因變數(響應變數)值的變化,如果響應變數的值是概率,這條假設就不滿足了。廣義線性回歸去掉了這條假設,用一個聯連函數(link function)來描述解釋變數與響應變數的關係。實際上,在線性回歸模型裡面,我們已經用了聯連函數。普通線性回歸作為廣義線性回歸的特例使用的是恆等聯連函數(identity link function),將解釋變數的通過線性組合的方式來聯接服從正態分布的響應變數。如果響應變數不服從正態分布,就要用另外一種聯連函數了。

from __future__ import print_function, divisionimport sysimport osimport mathfrom sklearn import datasetsimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltdef shuffle_data(X, y, seed=None): if seed: np.random.seed(seed) idx = np.arange(X.shape[0]) np.random.shuffle(idx) return X[idx], y[idx]# 正規化數據集 Xdef normalize(X, axis=-1, p=2): lp_norm = np.atleast_1d(np.linalg.norm(X, p, axis)) lp_norm[lp_norm == 0] = 1 return X / np.expand_dims(lp_norm, axis)# 標準化數據集 Xdef standardize(X): X_std = np.zeros(X.shape) mean = X.mean(axis=0) std = X.std(axis=0) # 做除法運算時請永遠記住分母不能等於0的情形 # X_std = (X - X.mean(axis=0)) / X.std(axis=0) for col in range(np.shape(X)[1]): if std[col]: X_std[:, col] = (X_std[:, col] - mean[col]) / std[col] return X_std# 劃分數據集為訓練集和測試集def train_test_split(X, y, test_size=0.2, shuffle=True, seed=None): if shuffle: X, y = shuffle_data(X, y, seed) n_train_samples = int(X.shape[0] * (1-test_size)) x_train, x_test = X[:n_train_samples], X[n_train_samples:] y_train, y_test = y[:n_train_samples], y[n_train_samples:] return x_train, x_test, y_train, y_test# 將一個向量轉換成對角陣,其中對角陣上的元素就是向量中元素def vec2diagonal(vec): vec_length = len(vec) diagonal = np.zeros((vec_length, vec_length)) for i in range(vec_length): diagonal[i][i] = vec[i] return diagonaldef accuracy(y, y_pred): y = y.reshape(y.shape[0], -1) y_pred = y_pred.reshape(y_pred.shape[0], -1) return np.sum(y == y_pred)/len(y)class Sigmoid: def function(self, x): return 1/(1 + np.exp(-x)) def derivative(self, x): return self.function(x) * (1 - self.function(x))class LogisticRegression(): """邏輯回歸分類模型. Parameters: ----------- learning_rate: float 學習率. """ def __init__(self, learning_rate=.1): self.w = None self.learning_rate = learning_rate self.sigmoid = Sigmoid() def fit(self, X, y, n_iterations=4000): # 在第一列添加偏置列,全部初始化為1 X = np.insert(X, 0, 1, axis=1) X = X.reshape(X.shape[0], -1) y = y.reshape(y.shape[0], -1) n_samples, n_features = np.shape(X) # 參數初始化 [-1/n_features, 1/n_features] limit = 1 / math.sqrt(n_features) self.w = np.random.uniform(-limit, limit, (n_features, 1)) for i in range(n_iterations): # 通過初始化的參數w計算預測值 y_pred = self.sigmoid.function(X.dot(self.w)) # 梯度下降更新參數w. self.w -= self.learning_rate * X.T.dot(-(y - y_pred) * self.sigmoid.function(X.dot(self.w)) * (1 - self.sigmoid.function(X.dot(self.w)))) def predict(self, X): # 訓練模型的時候我們添加了偏置,預測的時候也需要添加偏置 X = X.reshape(X.shape[0], -1) X = np.insert(X, 0, 1, axis=1) # 預測 y_pred = np.round(self.sigmoid.function(X.dot(self.w))).astype(int) return y_pred def main(): # Load dataset data = datasets.load_iris() X = normalize(data.data[data.target != 0]) y = data.target[data.target != 0] y[y == 1] = 0 y[y == 2] = 1 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, seed=1) clf = LogisticRegression() clf.fit(X_train, y_train) y_pred = clf.predict(X_test) accu = accuracy(y_test, y_pred) print ("Accuracy:", accu) plt.figure(figsize=(12, 8)) plt.scatter(X[y==0][:,0], X[y==0][:,1]) plt.scatter(X[y==1][:,0], X[y==1][:,1]) plt.show() if __name__ == "__main__": main()

Accuracy: 0.939393939394

推薦閱讀:

專家分享:入門深度學習應該學什麼
學習筆記CB001:NLTK庫、語料庫、詞概率、雙連詞、詞典
損失函數——Hinge
Perceptual loss for Real time Style Transfer and Super-Resolution 論文閱讀

TAG:機器學習 | 神經網路 |