一、決策樹與隨機森林
1、信息論基礎
香農:奠定了現代信息論基礎,定義信息的單位比特。
32支球隊,預測世界杯冠軍,不知道任何信息的情況下,使用二分法最少需要猜5次。(log32=5)
5 = - (1/32log1/32 + 1/32log1/32 + ...+ 1/32log1/32)
而在開放一些信息后(如前10次比賽的情況或者已知一些球隊的獲勝概率),“誰是世界杯冠軍”的信息量應該比5比特少。
香農指出,它的准確信息量應該是:
如 5 > - (1/4log1/4 + 1/8log1/8 + ...+ 1/6log1/6) 信息熵減少了
信息熵 H:單位為比特
![]()
當32支球隊奪冠幾率相同時,對應的信息熵為5比特。
信息和消除不確定性是相聯系的,不確定性下降,信息熵也下降。
所以在決策樹中,減少的信息熵越多的特征,越先判斷。
決策樹的划分依據之一:信息增益,表示得知特征X的信息而使得類Y的信息的不確定性減少的程度。
特征A對訓練數據集D的信息增益g(D,A),定義為集合D的信息熵H(D)與特征A給定條件下D的信息條件熵H(D|A)之差:
![]()
如上例中開放一些信息后,減少的信息熵的大小。
信息增益的計算:

如下例:


下面這個式子更好理解:

信息增益最大的,選為最有效特征。
信息增益是決策樹的分類依據之一。

2、決策樹
常見決策樹使用的算法:
ID3:信息增益最大的准則。
C4.5:信息增益比最大的准則。
CART:
- 回歸樹:平方誤差最小。
- 分類樹:基尼系數最小。(基尼系數的划分更仔細,一般用這個)
class sklearn.tree.DecisionTreeClassifier(criterion="gini", max_depth=None, random_state=None)
- 決策樹分類器
- criterion默認是基尼系數,也可以選擇信息增益的熵 “entropy”
- max_depth:樹的深度大小
- random_state:隨機樹種子
- decision_path() :返回決策樹的路徑
案例:

步驟:
- pd讀取數據
- 選擇有影響的特征,處理缺失值
- 進行特征工程,pd轉換字典,特征抽取
- x_train.to_dict(orient="records")
- 決策樹估計器流程
數據地址:http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt
import pandas as pd from sklearn.feature_extraction import DictVectorizer from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier def decision(): """ 決策樹 :return: None """ # 獲取數據 titan = pd.read_csv("http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt") # 處理數據,找出特征值和目標值 x = titan[['pclass', 'age', 'sex']] y = titan['survived'] # 缺失值處理 x['age'].fillna(x['age'].mean(), inplace=True) print(x) # 分割數據 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25) # 特征工程 dict = DictVectorizer(sparse=False) x_train = dict.fit_transform(x_train.to_dict(orient="records")) x_test = dict.fit_transform(x_test.to_dict(orient="records")) print(dict.get_feature_names()) print(x_train) # 用決策樹進行分類 dec = DecisionTreeClassifier() dec.fit(x_train, y_train) print("預測的准確率:", dec.score(x_test, y_test)) return None if __name__ == '__main__': decision()

決策樹的結構、本地保存:
① sklearn.tree.export_graphviz() 該函數能夠導出DOT格式
tree.export_graphviz(estimator,out_file='tree.dot’,feature_names=[‘’,’’])
# 導出決策樹的結構 export_graphviz(dec, out_file="./tree.dot", feature_names=['年齡', '一等座', '二等座', '三等座', '女性', '男性'])
② 工具:(能夠將dot文件轉換為pdf、png)
安裝graphviz
ubuntu:sudo apt-get install graphviz Mac:brew install graphviz
③ 運行命令
然后我們運行這個命令
$ dot -Tpng tree.dot -o tree.png
決策樹的優缺點
優點:
- 簡單的理解和解釋,樹木可視化。
- 需要很少的數據准備,其他技術通常需要數據歸一化
缺點:
- 決策樹學習者可以創建不能很好地推廣數據的過於復雜的樹,這被稱為過擬合。
- 決策樹可能不穩定,因為數據的小變化可能會導致完全不同的樹被生成
改進:
- 剪枝cart算法(決策樹API中已經實現)
- 隨機森林

3、隨機森林
集成學習方法:集成學習通過建立幾個模型的組合來解決單一預測問題。它的工作原理是生成多個分類器/模型,各自獨立地學習和作出預測。這些預測最后結合成單預測,因此優於任何一個單分類的做出預測。
隨機森林:在機器學習中,隨機森林是一個包含多個決策樹的分類器,並且其輸出的類別是由個別樹輸出的類別的眾數而定。
隨機森林建立多個決策樹過程: N個樣本 M個特征
單個樹建立過程:
① 隨機在N個樣本中選擇一個樣本,重復N次,樣本有可能重復;
② 隨機在M個特征中選出m個特征,
建立10棵決策樹,樣本、特征大多不一樣(隨機有放回的抽樣)
學習算法:
根據下列算法而建造每棵樹:
用N來表示訓練用例(樣本)的個數,M表示特征數目。
輸入特征數目m,用於確定決策樹上一個節點的決策結果;其中m應遠小於M。
從N個訓練用例(樣本)中以有放回抽樣的方式,取樣N次,形成一個訓練集(即bootstrap取樣),並用未抽到的用例(樣本)作預測,評估其誤差。
為什么要隨機抽樣訓練集?
如果不進行隨機抽樣,每棵樹的訓練集都一樣,那么最終訓練出的樹分類結果也是完全一樣的
為什么要有放回地抽樣?
如果不是有放回的抽樣,那么每棵樹的訓練樣本都是不同的,都是沒有交集的,這樣每棵樹都是“有偏的”,都是絕對“片面的”(當然這樣說可能不對),也就是說每棵樹訓練出來都是有很大的差異的;而隨機森林最后分類取決於多棵樹(弱分類器)的投票表決。
集成學習API:
class sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion=’gini’, max_depth=None, bootstrap=True, random_state=None)
- 隨機森林分類器
- n_estimators:integer,optional(default = 10) 森林里的樹木數量
- criteria:string,可選(default =“gini”)分割特征的測量方法
- max_depth:integer或None,可選(默認=無)樹的最大深度
- bootstrap:boolean,optional(default = True)是否在構建樹時使用放回抽樣
- max_features="auto" 每個決策樹的最大特征數量,可以為auto,sqrt,log2,即取sqrt(總特征數)個特征,auto=sqrt
隨機森林的超參數有:n_estimators決策樹的數量、max_depth每棵樹的深度限制
預測泰坦尼克號生存人數:
import pandas as pd from sklearn.feature_extraction import DictVectorizer from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV def decision(): """ 決策樹 :return: None """ # 獲取數據 titan = pd.read_csv("http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt") # 處理數據,找出特征值和目標值 x = titan[['pclass', 'age', 'sex']] y = titan['survived'] # 缺失值處理 x['age'].fillna(x['age'].mean(), inplace=True) # 分割數據 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25) # 特征工程 dict = DictVectorizer(sparse=False) x_train = dict.fit_transform(x_train.to_dict(orient="records")) x_test = dict.fit_transform(x_test.to_dict(orient="records")) # 隨機森林進行預測,超參數調優 rf = RandomForestClassifier() # 網格搜索交叉驗證 param = {"n_estimators":[120, 200, 300, 500, 800, 1200], "max_depth":[5, 8, 15, 25, 30]} # 以上這些值都是常用的,它們在網格搜索中兩兩組合 gc = GridSearchCV(rf, param_grid=param, cv=4) gc.fit(x_train, y_train) print("准確率:", gc.score(x_test, y_test)) print("查看選擇的參數模型:", gc.best_params_) return None if __name__ == '__main__': decision()

隨機森林的優點:
- 在當前所有算法中,具有極好的准確率
- 能夠有效地運行在大數據集上(樣本大、特征大)
- 能夠處理具有高維特征的輸入樣本,而且不需要降維
- 能夠評估各個特征在分類問題上的重要性
- 對於缺省值問題也能夠獲得很好得結果

二、線性回歸
1、線性回歸
試圖學得一個通過屬性的線性組合來進行預測的函數:
𝑓(𝑥)=𝑤1𝑥1+𝑤2𝑥2+…+𝑤𝑑𝑥𝑑+𝑏
w為權重,b稱為偏置項,可以理解為:𝑤0×1
如的房子的面積與價格的關系:
import matplotlib.pyplot as plt plt.figure(figsize=(10, 10)) plt.scatter([60, 72, 75, 80, 83], [126, 151.2, 157.5, 168, 174.3]) plt.show()

定義:線性回歸通過一個或者多個自變量與因變量之間之間進行建模的回歸分析。其中特點為一個或多個稱為回歸系數的模型參數的線性組合
多元線性回歸:涉及到的變量兩個或兩個以上
![]()
其中w, x為矩陣: 
預測結果與真實值有一定的誤差。
損失函數:誤差大小
yi為第𝑖個訓練樣本的真實值,hw(xi)為第𝑖個訓練樣本特征值組合預測函數
總損失定義為:
又稱最小二乘法
如何求解模型中的w,使得損失最小?目的是找到最小損失對應的w值。
2、正規方程
求解:
𝑋為特征值矩陣,𝑦為目標值矩陣
缺點:當特征過於復雜,求解速度太慢
對於復雜的算法,不能使用正規方程求解(邏輯回歸等)
一般不用正規方程的方法來求解
直接求解到最小值
3、梯度下降
以單變量中的w1,w0為例:y=w1*x+w0

α為學習速率,需要手動指定,
表示方向。
沿着這個函數下降的方向找,最后就能找到山谷的最低點,然后更新W值。
面對訓練數據規模十分龐大的任務,使用梯度下降。

小結
線性回歸:
策略:最小二乘法計算誤差平方和
優化:正規方程、梯度下降
4、正規方程、梯度下降API
sklearn.linear_model.LinearRegression() 正規方程
- 普通最小二乘線性回歸
- coef_:回歸系數
sklearn.linear_model.SGDRegressor( ) 梯度下降
- 通過使用SGD最小化線性模型
- coef_:回歸系數
線性回歸需要標准化處理。
波士頓房價數據案例:
① 波士頓地區房價數據獲取
② 波士頓地區房價數據分割
③ 訓練與測試數據標准化處理
④ 使用最簡單的線性回歸模型LinearRegression和梯度下降估計SGDRegressor對房價進行預測

from sklearn.datasets import load_boston from sklearn.linear_model import LinearRegression, SGDRegressor from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler def linear(): """ 線性回歸預測房價 :return: None """ # 獲取數據 lb = load_boston() # 分割數據集 x_train, x_test, y_train, y_test = train_test_split(lb.data, lb.target, test_size=0.25) # 標准化:線性回歸特征值和目標值都要處理 # 要實例化兩個標准化API(因為目標值只有一列,n個特征值計算出來的n個平均值和方差無法用於目標值) std_x = StandardScaler() x_train = std_x.fit_transform(x_train) # x_train本來就是二維數組不用轉 x_test = std_x.transform(x_test) std_y = StandardScaler() y_train = std_y.fit_transform(y_train.reshape(-1, 1)) # 0.19版本轉換器要求傳入參數必須轉成二維數組 y_test = std_y.transform(y_test.reshape(-1, 1)) # estimator預測 # 正規方程求解 lr = LinearRegression() lr.fit(x_train, y_train) print(lr.coef_) # 權重參數 y_pred = lr.predict(x_test) print(std_y.inverse_transform(y_pred)) # 轉換 print("准確率:", lr.score(x_test, y_test)) # 梯度下降預測 sgd = SGDRegressor() sgd.fit(x_train, y_train) print(sgd.coef_) y_pred = sgd.predict(x_test) print(std_y.inverse_transform(y_pred)) print("准確率:", sgd.score(x_test, y_test)) return None if __name__ == '__main__': linear()

5、回歸性能評估
均方誤差(Mean Squared Error,MSE):

![]()
sklearn.metrics.mean_squared_error
mean_squared_error(y_true, y_pred)
- 均方誤差回歸損失
- y_true:真實值
- y_pred:預測值
- return:浮點數結果
- 真實值預測值都是標准化之前的值
from sklearn.datasets import load_boston from sklearn.linear_model import LinearRegression, SGDRegressor from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error def linear(): """ 線性回歸預測房價 :return: None """ # 獲取數據 lb = load_boston() # 分割數據集 x_train, x_test, y_train, y_test = train_test_split(lb.data, lb.target, test_size=0.25) # 標准化:線性回歸特征值和目標值都要處理 # 要實例化兩個標准化API(因為目標值只有一列,n個特征值計算出來的n個平均值和方差無法用於目標值) std_x = StandardScaler() x_train = std_x.fit_transform(x_train) # x_train本來就是二維數組不用轉 x_test = std_x.transform(x_test) std_y = StandardScaler() y_train = std_y.fit_transform(y_train.reshape(-1, 1)) # 0.19版本轉換器要求傳入參數必須轉成二維數組 y_test = std_y.transform(y_test.reshape(-1, 1)) # estimator預測 # 正規方程求解 lr = LinearRegression() lr.fit(x_train, y_train) print(lr.coef_) # 權重參數 y_pred = lr.predict(x_test) # print(std_y.inverse_transform(y_pred)) # 轉換 print("准確率:", lr.score(x_test, y_test)) print("正規方程的MSE:", mean_squared_error(std_y.inverse_transform(y_test), std_y.inverse_transform(y_pred))) # 梯度下降預測 sgd = SGDRegressor() sgd.fit(x_train, y_train) print(sgd.coef_) y_pred = sgd.predict(x_test) # print(std_y.inverse_transform(y_pred)) print("准確率:", sgd.score(x_test, y_test)) print("梯度下降的MSE:", mean_squared_error(std_y.inverse_transform(y_test), std_y.inverse_transform(y_pred))) return None if __name__ == '__main__': linear()

方法對比:

特點:線性回歸器是最為簡單、易用的回歸模型。從某種程度上限制了使用,盡管如此,在不知道特征之間關系的前提下,我們仍然使用線性回歸器作為大多數系統的首要選擇。
小規模數據:LinearRegression(不能解決擬合問題)以及其它
大規模數據:SGDRegressor
6、過擬合、欠擬合
問題:訓練數據訓練的很好,誤差也不大,為什么在測試集上面有問題呢?

經過訓練后,知道了天鵝是有翅膀的,天鵝的嘴巴是長長的。簡單的認為有這些特征的都是天鵝。因為機器學習到的天鵝特征太少了,導致區分標准太粗糙,不能准確識別出天鵝。

機器通過這些圖片來學習天鵝的特征,經過訓練后,知道了天鵝是有翅膀的,天鵝的嘴巴是長長的彎曲的,天鵝的脖子是長長的有點曲度,天鵝的整個體型像一個"2"且略大於鴨子。這時候機器已經基本能區別天鵝和其他動物了。但是機器在訓練集學習的太好了,很不巧已有的天鵝圖片全是白天鵝的,於是機器經過學習后,會認為天鵝的羽毛都是白的,以后看到羽毛是黑的天鵝就會認為那不是天鵝。

過擬合:一個假設在訓練數據上能夠獲得比其他假設更好的擬合, 但是在訓練數據外的數據集上卻不能很好地擬合數據,此時認為這個假設出現了過擬合的現象。(模型過於復雜)
欠擬合:一個假設在訓練數據上不能獲得更好的擬合, 但是在訓練數據外的數據集上也不能很好地擬合數據,此時認為這個假設出現了欠擬合的現象。(模型過於簡單)
對線性模型進行訓練學習會變成復雜模型:

模型復雜的原因:數據的特征和目標值之間的關系不僅僅是線性關系。
欠擬合:
原因:學習到數據的特征過少
解決辦法:增加數據的特征數量
過擬合:
原因:原始特征過多,存在一些嘈雜特征,模型過於復雜是因為模型嘗試去兼顧各個測試數據點
解決辦法:
- 進行特征選擇,消除關聯性大的特征(很難做)
- 交叉驗證(讓所有數據都有過訓練)(交叉驗證訓練集,根據結果現象可以看出過擬合或欠擬合)
- 正則化
L2正則化進行特征選擇:解決過擬合

作用:可以使得W的每個元素都很小,都接近於0(不知道哪個特征是高次的復雜特征,就將前面的權重減小)
優點:越小的參數說明模型越簡單,越簡單的模型則越不容易產生過擬合現象
7、嶺回歸
線性回歸為了把所有訓練數據表現好,容易出現權重過大過擬合。
帶有正則化的線性回歸:嶺回歸
sklearn.linear_model.Ridge
sklearn.linear_model.Ridge(alpha=1.0)
- 具有l2正則化的線性最小二乘法
- alpha:正則化力度(超參數)(默認1.0,一般可以取0~1、1~10)
- coef_:回歸系數

正則化力度越大,權重越接近於0,模型越簡單(但也不能過於簡單)
# 嶺回歸 rd = Ridge() rd.fit(x_train, y_train) print(rd.coef_) y_pred = rd.predict(x_test) print(std_y.inverse_transform(y_pred)) print("准確率:", rd.score(x_test, y_test)) print("嶺回歸的MSE:", mean_squared_error(std_y.inverse_transform(y_test), std_y.inverse_transform(y_pred)))
嶺回歸得到的回歸系數更符合實際,更可靠。另外,能讓估計參數的波動范圍變小,變的更穩定。在存在病態數據偏多的研究中有較大的實用價值。
8、模型的保存與加載
sklearn模型的保存與加載:
from sklearn.externals import joblib
保存:joblib.dump(rf, 'test.pkl')
加載:estimator = joblib.load('test.pkl')
# 正規方程求解 lr = LinearRegression() lr.fit(x_train, y_train) print(lr.coef_) # 權重參數 # 保存訓練好的模型 joblib.dump(lr, './tmp/lr.pkl')
