李航的《統計學習方法》可以說是機器學習的入門寶典,許多機器學習培訓班、網際網路企業的面試、筆試題目,很多都參考這本書。之前,紅色石頭在本公眾號上也發表過一些關於這本書的一些筆記和 Python 程式碼,目的是給大家啃這本書帶來一些便利。剛剛,紅色石頭髮現黃海廣博士在自己的 GitHub 上又更新了《統計學習方法》的 Python 程式碼,就迫不及待地分享給大家。
緣由是《統計學習方法》第一版還是 2012 年出版的,包含了眾多主要的監督學習演算法與模型。2019 年 5 月 1 日,《統計學習方法》第二版正式釋出,通過 6 年時間的努力,在第一版的基礎上又增加了無監督學習的主要演算法與模型。
第二版的目錄為:
第1篇 監督掌習
第1章統計學習及監督學習概論
第2章感知機
第3章k近鄰法
第4章樸素貝葉斯法
第5章決策樹
第6章邏輯斯諦迴歸與優選熵模型
第7章支援向量機
第8章提升方法
第9章EM演算法及其推廣
第10章隱馬爾可夫模型
第11章條件隨機場
第12章監督學習方法總結
第2篇無監督學習
第13章無監督學習概論
第14章聚類方法
第15章奇異值分解
第16章主成分分析
第17章潛在語義分析
第18章概率潛在語義分析
第19章馬爾可夫鏈蒙特卡羅法
第20章 潛在狄利克雷分配
第21章 PageRank演算法
第22章 無監督學習方法總結
附錄A 梯度下降法
附錄B 牛頓法和擬牛頓法
附錄C 拉格朗日對偶性
附錄D 矩陣的基本子空間
附錄E KL散度的定義和狄利克雷分佈的性質
針對新增加的內容,黃海廣博士對原有的 GitHub 原始碼進行新內容的更新,直接放上地址:
https://github.com/fengdu78/lihang-code
本次修改了部分錯誤,增加了每章概述,更新完前 12 章,今後將增加第二版的內容。
修改主要錯誤包括:
第3章 k近鄰法的max_count錯誤
第10章 隱馬爾可夫模型的viterbi索引錯誤
增加的內容:
增加每章的概要
專案目前包含的內容截圖如下:
目前,該專案已經收穫 5000+ 的 star 了。
Python 程式碼
下面,以支援向量機為例,我們可以查閱 SVM 的完整示例程式碼:
class SVM:
def __init__(self, max_iter=100, kernel='linear'):
self.max_iter = max_iter
self._kernel = kernel
def init_args(self, features, labels):
self.m, self.n = features.shape
self.X = features
self.Y = labels
self.b = 0.0
# 將Ei儲存在一個列表裡
self.alpha = np.ones(self.m)
self.E = [self._E(i) for i in range(self.m)]
# 鬆弛變數
self.C = 1.0
def _KKT(self, i):
y_g = self._g(i) * self.Y[i]
if self.alpha[i] == 0:
return y_g >= 1
elif 0 < self.alpha[i] < self.C:
return y_g == 1
else:
return y_g <= 1
# g(x)預測值,輸入xi(X[i])
def _g(self, i):
r = self.b
for j in range(self.m):
r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
return r
# 核函式
def kernel(self, x1, x2):
if self._kernel == 'linear':
return sum([x1[k] * x2[k] for k in range(self.n)])
elif self._kernel == 'poly':
return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2
return 0
# E(x)為g(x)對輸入x的預測值和y的差
def _E(self, i):
return self._g(i) - self.Y[i]
def _init_alpha(self):
# 外層迴圈首先遍歷所有滿足0<a<C的樣本點,檢驗是否滿足KKT
index_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]
# 否則遍歷整個訓練集
non_satisfy_list = [i for i in range(self.m) if i not in index_list]
index_list.extend(non_satisfy_list)
for i in index_list:
if self._KKT(i):
continue
E1 = self.E[i]
# 如果E2是+,選擇最小的;如果E2是負的,選擇最大的
if E1 >= 0:
j = min(range(self.m), key=lambda x: self.E[x])
else:
j = max(range(self.m), key=lambda x: self.E[x])
return i, j
def _compare(self, _alpha, L, H):
if _alpha > H:
return H
elif _alpha < L:
return L
else:
return _alpha
def fit(self, features, labels):
self.init_args(features, labels)
for t in range(self.max_iter):
# train
i1, i2 = self._init_alpha()
# 邊界
if self.Y[i1] == self.Y[i2]:
L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
H = min(self.C, self.alpha[i1] + self.alpha[i2])
else:
L = max(0, self.alpha[i2] - self.alpha[i1])
H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])
E1 = self.E[i1]
E2 = self.E[i2]
# eta=K11+K22-2K12
eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(
self.X[i2],
self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])
if eta <= 0:
# print('eta <= 0')
continue
alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (
E1 - E2) / eta #此處有修改,根據書上應該是E1 - E2,書上130-131頁
alpha2_new = self._compare(alpha2_new_unc, L, H)
alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (
self.alpha[i2] - alpha2_new)
b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (
alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
self.X[i2],
self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (
alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
self.X[i2],
self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b
if 0 < alpha1_new < self.C:
b_new = b1_new
elif 0 < alpha2_new < self.C:
b_new = b2_new
else:
# 選擇中點
b_new = (b1_new + b2_new) / 2
# 更新引數
self.alpha[i1] = alpha1_new
self.alpha[i2] = alpha2_new
self.b = b_new
self.E[i1] = self._E(i1)
self.E[i2] = self._E(i2)
return 'train done!'
def predict(self, data):
r = self.b
for i in range(self.m):
r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])
return 1 if r > 0 else -1
def score(self, X_test, y_test):
right_count = 0
for i in range(len(X_test)):
result = self.predict(X_test[i])
if result == y_test[i]:
right_count += 1
return right_count / len(X_test)
def _weight(self):
# linear model
yx = self.Y.reshape(-1, 1) * self.X
self.w = np.dot(yx.T, self.alpha)
return self.w
其實,我看了下,專案中不僅包含 SVM 的示例程式碼,同時也有對應的讀書筆記和概括總結。
《統計學習方法》課件
作者袁春:清華大學深圳研究生院,提供了第一版全書 12 章的 PPT 課件。
課件獲取地址:
連結:https://pan.baidu.com/s/1_boHMIg6DqS7bgFuxlWF7Q 提取碼:ffxy
附加資源
總結整理我之前發表過的關於李航《統計學習方法》的相關資源,彙總如下,詳見文章:
李航《統計學習方法》最新資源,筆記、Python 程式碼一應俱全!
參考資料:
https://github.com/wzyonggege/statistical-learning-method
https://github.com/WenDesi/lihang_book_algorithm
https://blog.csdn.net/tudaodiaozhale