[ML] 機器學習技法:第七講 Blending and Bagging

ML:基礎技法學習
Package:scikit-learn
課程:機器學習技法
簡介:第七講 Blending and Bagging

Aggregation

  • select
    • 挑出最好的
    • G(x)=gt(x) with t=argmint{1,2,,T}Eval(gt) 
    • 採用 Eval(gt) 而不使用 Ein(gt) 在於會提高複雜度,也就是 dvc,可參考此篇
    • 只採用一個,可能並不符合 aggregation 的期望
      因為若在一堆廢渣中挑選,怎麼挑還是廢渣
      但 aggregation 希望的是集合廢渣的能力,造出一個還可以接受的 model
  • mix uniformly
    • 採用所有人的意見,皆給於一票
    • G(x)=sign(t=1T1gt(x))
    • G(x)=1Tt=1T1gt(x)
  • mix non-uniformly
    • 採用所有人的意見,依其專業給於適當票數
    • G(x)=sign(t=1Tαtgt(x)) with α0
    • G(x)=1Tt=1Tαtgt(x) with α0
    • 事實上,此 model 包含著
      • select αt=[Eval(gt) smallest]
      • mix uniformly αt=1
  • combine
    • 採用所有人的意見,依不同條件及其專業給於適當票數
    • G(x)=sign(t=1Tqt(x)gt(x)) with qt(x)0
    • G(x)=1Tt=1Tqt(x)gt(x) with qt(x)0
    • 事實上,此 model 包含著
      • mix non-uniformly qt(x)=αt
  • 可能具備的效果
    • 類似 feature transform,三個 gt(x) 組合出更好的解
    • 類似 regularization,不同的 PLA 解合併出更中庸的解

Uniform Blending

  • blending (voting)
    • 適用於已知 gt 的情況 
  • Uniform
    • 每個 gt 只給予一票
    • G(x)=sign(t=1T1gt(x))
    • G(x)=1Tt=1T1gt(x)
  • 適用情況
    • gt 皆一樣,合併後只會跟原本的 gt 表現一樣
    • gt 需具備多樣性,才可能得到更好的 model
      • classifier
        借由多數 model 的正確性修正少數 mode 的錯誤性
      • Regression
        有的高估,有的低估,合併可回到平均值,更加的中庸
        G(x)=1Tt=1T1gt(x)avg(Eout(gt))=avg(E(gtG)2)+Eout(G)Eout(G)
        avg((gt(x)f(x))2)=avg(gt(x)22gt(x)f(x)+f(x)2)=avg(gt(x)2)2avg(gt(x))f(x)+f(x)2=avg(gt(x)2)2G(x)f(x)+f(x)2=avg(gt(x)2)G(x)2+G(x)22G(x)f(x)+f(x)2=avg(gt(x)2)G(x)2+(G(x)f(x))2=avg(gt(x)2)2G(x)2+G(x)2+(G(x)f(x))2=avg(gt(x)2)2avg(gt(x))G(x)+G(x)2+(G(x)f(x))2=avg(gt(x)22gt(x)G(x)+G(x)2)+(G(x)f(x))2=avg(gt(x)G(x))2+(G(x)f(x))2avg(Eout(gt))=avg(E(gtG)2)+Eout(G)Eout(G)
        • 考慮一虛擬的過程 for t=1,2,,T
          • 從某個分佈 PN(i.i.d.) 挑出 N 筆資料 Dt
          • A(Dt) 得到 gt
          • avg(Eout(gt))=avg(E(gtg¯)2)+Eout(g¯) 物理意義 bias variance decomposition
            演算法的期望表現 = 共識的變異量 + 共識的表現
            g¯=limTG=limT1Tt=1Tgt=EDA(D) 依此代入之前推導的式子
            avg(Eout(gt))=avg(E(gtG)2)+Eout(G)=avg(E(gtg¯)2)+Eout(g¯)
        • 實際上平均的過程,就是在消除變異量
    • 如何得到多樣性
      • 來自不同的 hypothesises:g1H1,g2H2,,gTHT
      • 來自不同的參數: GD with η=0.001,0.01,,10
      • 來自本身演算法的隨機性:PLA 不同的初始值,會有不同的結果
      • 來自不同的 data:不同的 data 訓練出不同的 gv

Linear and Any Blending

  • blending (voting)
    • 適用於已知 gt 的情況 
  • Linear
    • 每個 gt 只給予一票
    • G(x)=sign(t=1Tαtgt(x)) with α0
    • G(x)=1Tt=1Tαtgt(x) with α0
  • 如何求解
    • linear blending = LinModel + hypotheses as transform + constraints
      minα0 1Nn=1Nerr(yn,t=1Tαtgt(xn))
      minα0 Ein(α)=minα0 1Nn=1N(ynt=1Tαtgt(xn))2 實際上就是一個 two-level learning (同 Platt's Model),先用 gt 轉換,再求 Linear Regression
  • 實務運用 
    • 實務上求解,會忽略 constraints α0,因為即使小於 0,也只是把 gt 反過來用
      就像一個有 99% 錯誤性的 gt,但反過來用時就變成 99% 正確性的 gt
    • 不可用 Ein,會大大提高複雜度,甚至大過 best of best dVC(t=1THt)
      可參考此篇
    • 實務上 gt 其實是 gt,選擇最小 Etraingt,再利用 Eval 挑選 α
  • 演算法
    • Dtrain 得到 g1,g2,,gT
    • Dval((xn,yn) 轉換為 (zn=Φ(xn),yn)Φ(x)=(g1(x),,gT(x))
    • 依不同需求
      • Linear Blending
        • 使用 Linear Model(zn,yn),得到 α 
        • 重新用所有資料 D,得到 Φ(x)=(g1(x),,gT(x))
        • GLINB(x)=LinearModelw=α(Φ(x))
      • Any Blending (Stacking)
        • 使用 Any Model(zn,yn) 計算,得到 g~
        • 重新用所有資料 D,得到 Φ(x)=(g1(x),,gT(x))
        • GANYB(x)=g~(Φ(x)) 
        • 可達到 conditional blending
        • 非常強大,務必小心 overfitting

Bagging

在 Uniform Blending 中,有個虛擬的過程,若想實現並得到 g¯ 該如何做呢?
  • 使用有限但很大的 T 數量
  • gt=A(D~t) from D~tPND~t 只用 D 產生
    • bootstrapping
      • 從現有資料,取任意 N 筆,每取一次就放回去,可得 D~t
      • 假設 N 筆資料,抽 N 次,抽到完全相同的機率為 N!NN
兩者對照如下,bagging 是一個簡單的 meta algorithm,適用於任意演算法
(meta algorithm 意指建立在其他 base algorithm A 之上的演算法)

實際 Pocket 情況
每個 Pocket 跑一千輪,共有 25 條 Pocket
可看見產生出一條非線性的 model,分得比原本的好
若演算法對不同的資料越敏感,那麼合併起來可能會更好

程式碼

  1. from itertools import product
  2.  
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5.  
  6. from sklearn import linear_model
  7. from sklearn.svm import SVC
  8. from sklearn.ensemble import VotingClassifier
  9.  
  10. # 訓練資料
  11. X = np.c_[(.4, -.7),
  12. (-1.5, -1),
  13. (-1.4, -.9),
  14. (-1.3, -1.2),
  15. (-1.1, -.2),
  16. (-1.2, -.4),
  17. (-.5, 1.2),
  18. (-1.5, 2.1),
  19. (1, 1),
  20. # --
  21. (1.3, .8),
  22. (1.2, .5),
  23. (.2, -2),
  24. (.5, -2.4),
  25. (.2, -2.3),
  26. (0, -2.7),
  27. (1.3, 2.1)].T
  28. y = np.r_[[0] * 8 + [1] * 8]
  29.  
  30. # model
  31. clf1 = linear_model.Perceptron()
  32. clf2 = linear_model.LogisticRegression()
  33. clf3 = SVC(kernel='rbf')
  34. # voting hard 表示取多數決,若是 soft 則為平均
  35. eclf = VotingClassifier(estimators=[('Pocket', clf1), ('LogisticRegression', clf2), ('svc', clf3)],
  36. voting='hard')
  37.  
  38. clf1.fit(X, y)
  39. clf2.fit(X, y)
  40. clf3.fit(X, y)
  41. eclf.fit(X, y)
  42.  
  43. # 建立畫圖資料
  44. x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
  45. x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
  46. xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, 0.1),
  47. np.arange(x2_min, x2_max, 0.1))
  48.  
  49. f, axarr = plt.subplots(2, 2, figsize=(10, 8))
  50.  
  51. # product([0, 1], [0, 1]) => (0, 0), (0, 1), (1, 0), (1, 1)
  52. for idx, clf, title in zip(product([0, 1], [0, 1]), [clf1, clf2, clf3, eclf], ['Pocket', 'LogisticRegression', 'Kernel SVM', 'Hard Voting']):
  53.  
  54. Y = clf.predict(np.c_[xx1.ravel(), xx2.ravel()])
  55. Y = Y.reshape(xx1.shape)
  56. # 畫出範圍
  57. axarr[idx[0], idx[1]].contourf(xx1, xx2, Y, alpha=0.5, cmap="winter")
  58. # 畫出訓練資料的點
  59. axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
  60. # 設定 title
  61. axarr[idx[0], idx[1]].set_title(title)
  62.  
  63. plt.show()
Linear Blending,剛好跟上圖一樣,因 weight 皆相同
  1. from itertools import product
  2.  
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5.  
  6. from sklearn import linear_model
  7. from sklearn.svm import SVC
  8. from sklearn.ensemble import VotingClassifier
  9.  
  10. # 訓練資料
  11. X = np.c_[(.4, -.7),
  12. (-1.5, -1),
  13. (-1.4, -.9),
  14. (-1.3, -1.2),
  15. (-1.1, -.2),
  16. (-1.2, -.4),
  17. (-.5, 1.2),
  18. (-1.5, 2.1),
  19. (1, 1),
  20. # --
  21. (1.3, .8),
  22. (1.2, .5),
  23. (.2, -2),
  24. (.5, -2.4),
  25. (.2, -2.3),
  26. (0, -2.7),
  27. (1.3, 2.1)].T
  28. y = np.r_[[-1] * 8 + [1] * 8]
  29.  
  30. X_val = X[0:-1:2]
  31. y_val = y[0:-1:2]
  32. X_train = X[1:-1:2]
  33. y_train = y[1:-1:2]
  34.  
  35. # model
  36. clf1 = linear_model.Perceptron()
  37. clf2 = linear_model.LogisticRegression()
  38. clf3 = SVC(kernel='rbf')
  39.  
  40. # linear blending training
  41. clf4 = linear_model.LinearRegression()
  42. clf1.fit(X_train, y_train)
  43. clf2.fit(X_train, y_train)
  44. clf3.fit(X_train, y_train)
  45. phi = np.c_[clf1.predict(X_val), clf2.predict(X_val), clf3.predict(X_val)]
  46. clf4.fit(phi, y_val)
  47. # 重新用全部資料訓練
  48. clf1.fit(X, y)
  49. clf2.fit(X, y)
  50. clf3.fit(X, y)
  51.  
  52. # 建立畫圖資料
  53. x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
  54. x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
  55. xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, 0.1),
  56. np.arange(x2_min, x2_max, 0.1))
  57. #xx1, xx2 = np.meshgrid(np.arange(-1, 0, 0.1),
  58. # np.arange(-3, -2, 0.1))
  59.  
  60. f, axarr = plt.subplots(2, 2, figsize=(10, 8))
  61. XX = np.c_[xx1.ravel(), xx2.ravel()]
  62. # product([0, 1], [0, 1]) => (0, 0), (0, 1), (1, 0), (1, 1)
  63. for idx, clf, title in zip(product([0, 1], [0, 1]), [clf1, clf2, clf3, clf4], ['Pocket', 'LogisticRegression', 'Kernel SVM', 'Linear Blending']):
  64. if title == 'Linear Blending':
  65. # 轉換資料
  66. phi = np.c_[clf1.predict(XX), clf2.predict(XX), clf3.predict(XX)]
  67. # 預測資料
  68. Y = clf.predict(phi)
  69. # 將值轉換為 label
  70. Y[Y>0] = 1
  71. Y[Y<0] = -1
  72. else:
  73. Y = clf.predict(XX)
  74. Y = Y.reshape(xx1.shape)
  75. # 畫出範圍
  76. axarr[idx[0], idx[1]].contourf(xx1, xx2, Y, alpha=0.5, cmap="winter")
  77. # 畫出訓練資料的點
  78. axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)
  79. # 設定 title
  80. axarr[idx[0], idx[1]].set_title(title)
  81.  
  82. # 設定最上面的 title
  83. f.suptitle('Linear Blending $\\alpha$: {}'.format(clf4.coef_))
  84. plt.show()
錯誤的使用 bagging,不見得能降低變異量
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3.  
  4. from sklearn import linear_model
  5. from sklearn.ensemble import BaggingRegressor
  6.  
  7. # Settings
  8. n_repeat = 50 # Number of iterations for computing expectations
  9. n_train = 50 # 訓練資料的大小
  10. n_test = 1000 # 測試資料的大小
  11. noise = 1 # noise 的標準差
  12. np.random.seed(0) # 固定隨機的種子
  13.  
  14. estimators = [("Linear Regression", linear_model.LinearRegression()),
  15. ("Bagging(Linear Regression)", BaggingRegressor(linear_model.LinearRegression()))]
  16.  
  17. n_estimators = len(estimators)
  18.  
  19. # 目標函數
  20. def f(x):
  21. x = x.ravel()
  22.  
  23. return -x ** 2 + 1.5 * -(x - 2)
  24.  
  25.  
  26. # 產生資料並加入 noise,並依 n_repeat 產生對應 y 的數目
  27. def generate(n_samples, noise, n_repeat=1):
  28. # 產生 -5 ~ 5 的資料
  29. X = np.random.rand(n_samples) * 10 - 5
  30. X = np.sort(X)
  31.  
  32. if n_repeat == 1:
  33. y = f(X) + np.random.normal(0.0, noise, n_samples)
  34. else:
  35. y = np.zeros((n_samples, n_repeat))
  36.  
  37. for i in range(n_repeat):
  38. y[:, i] = f(X) + np.random.normal(0.0, noise, n_samples)
  39.  
  40. X = X.reshape((n_samples, 1))
  41.  
  42. return X, y
  43.  
  44.  
  45. X_train = []
  46. y_train = []
  47.  
  48. # 產生 n_repeat 筆的訓練資料
  49. for i in range(n_repeat):
  50. X, y = generate(n_samples=n_train, noise=noise)
  51. X_train.append(X)
  52. y_train.append(y)
  53. # X_train 50x1
  54. # y_train 50x1
  55.  
  56. # 產生 n_repeat 筆的測試資料
  57. X_test, y_test = generate(n_samples=n_test, noise=noise, n_repeat=n_repeat)
  58. # X_test 1000x1
  59. # y_test 1000x50
  60.  
  61. fig = plt.figure()
  62. summary = ""
  63. for n, (name, estimator) in enumerate(estimators):
  64. # 初始預測值
  65. y_predict = np.zeros((n_test, n_repeat))
  66.  
  67. for i in range(n_repeat):
  68. # 依不同的資料訓練
  69. estimator.fit(X_train[i], y_train[i])
  70. y_predict[:, i] = estimator.predict(X_test)
  71. # y_predict 1000x50
  72.  
  73. # Bias^2 + Variance + Noise decomposition of the mean squared error
  74. y_error = np.zeros(n_test)
  75.  
  76. for i in range(n_repeat):
  77. for j in range(n_repeat):
  78. # 同樣的 X 而有 50 筆 y
  79. y_error += (y_test[:, j] - y_predict[:, i]) ** 2
  80. # 每個 row 的平均 error
  81. y_error /= (n_repeat*n_repeat)
  82. # y_test 的變異量,本身存在的 noise
  83. y_noise = np.var(y_test, axis=1)
  84. # y_predict 的偏差
  85. y_bias = (f(X_test) - np.mean(y_predict, axis=1)) ** 2
  86. # y_predict 的變異量
  87. y_var = np.var(y_predict, axis=1)
  88.  
  89. summary += "{0}: {1:.4f} (error) = {2:.4f} (bias^2) + {3:.4f} (var) + {4:.4f} (noise)\n".format(name,
  90. np.mean(y_error),
  91. np.mean(y_bias),
  92. np.mean(y_var),
  93. np.mean(y_noise))
  94.  
  95. # 畫圖
  96. plt.subplot(2, n_estimators, n + 1)
  97. plt.plot(X_test, f(X_test), "b", label="$f(x)$")
  98. plt.plot(X_train[0], y_train[0], ".b", label="LS ~ $y = f(x)+noise$")
  99.  
  100. for i in range(n_repeat):
  101. if i == 0:
  102. plt.plot(X_test, y_predict[:, i], "r", label="$g(x)$")
  103. else:
  104. plt.plot(X_test, y_predict[:, i], "r", alpha=0.05)
  105.  
  106. plt.plot(X_test, np.mean(y_predict, axis=1), "c",
  107. label="$\\bar{g}(x)$")
  108.  
  109. plt.xlim([-5, 5])
  110. plt.title(name)
  111. # 只在第一個顯示 legend 並設定字型大小
  112. if n == 0:
  113. plt.legend(loc="upper right", prop={"size": 11})
  114.  
  115. plt.subplot(2, n_estimators, n_estimators + n + 1)
  116. plt.plot(X_test, y_error, "r", label="$error(x)$")
  117. plt.plot(X_test, y_bias, "b", label="$bias^2(x)$"),
  118. plt.plot(X_test, y_var, "g", label="$variance(x)$"),
  119. plt.plot(X_test, y_noise, "c", label="$noise(x)$")
  120.  
  121. plt.xlim([-5, 5])
  122. plt.ylim([0, 350])
  123. # 只在第一個顯示 legend 並設定字型大小
  124. if n == 0:
  125. plt.legend(loc="upper right", prop={"size": 11})
  126.  
  127. fig.suptitle(summary, fontsize=12)
  128. plt.show()

參考

Ensemble methods
sklearn.ensemble.VotingClassifier
sklearn.ensemble.BaggingClassifier
fukatani/stacked_generalization

留言