分類法/範例五:Linear and Quadratic Discriminant Analysis with confidence ellipsoid

線性判別以及二次判別的比較

http://scikit-learn.org/stable/auto_examples/classification/plot_lda_qda.html

(一)資料產生function

這個範例引入的套件,主要特點在:

  1. scipy.linalg:線性代數相關函式,這裏主要使用到linalg.eigh 特徵值相關問題
  2. matplotlib.colors: 用來處理繪圖時的色彩分佈
  3. LinearDiscriminantAnalysis:線性判別演算法
  4. QuadraticDiscriminantAnalysis:二次判別演算法
  1. %matplotlib inline
  2. from scipy import linalg
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. import matplotlib as mpl
  6. from matplotlib import colors
  7. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  8. from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis

接下來是設定一個線性變化的colormap,LinearSegmentedColormap(name, segmentdata) 預設會傳回一個256個值的數值顏色對應關係。用一個具備有三個項目的dict變數segmentdata來設定。以'red': [(0, 1, 1), (1, 0.7, 0.7)]來解釋,就是我們希望數值由0到1的過程,紅色通道將由1線性變化至0.7。

  1. cmap = colors.LinearSegmentedColormap(
  2. 'red_blue_classes',
  3. {'red': [(0, 1, 1), (1, 0.7, 0.7)],
  4. 'green': [(0, 0.7, 0.7), (1, 0.7, 0.7)],
  5. 'blue': [(0, 0.7, 0.7), (1, 1, 1)]})
  6. plt.cm.register_cmap(cmap=cmap)

我們可以用以下程式碼來觀察。當輸入數值為np.arange(0,1.1,0.1)也就是0,0.1…,1.0 時RGB數值的變化情形。

  1. values = np.arange(0,1.1,0.1)
  2. cmap_values = mpl.cm.get_cmap('red_blue_classes')(values)
  3. import pandas as pd
  4. pd.set_option('precision',2)
  5. df=pd.DataFrame(np.hstack((values.reshape(11,1),cmap_values)))
  6. df.columns = ['Value', 'R', 'G', 'B', 'Alpha']
  7. print(df)
  1. Value R G B Alpha
  2. 0 0.0 1.0 0.7 0.7 1
  3. 1 0.1 1.0 0.7 0.7 1
  4. 2 0.2 0.9 0.7 0.8 1
  5. 3 0.3 0.9 0.7 0.8 1
  6. 4 0.4 0.9 0.7 0.8 1
  7. 5 0.5 0.8 0.7 0.9 1
  8. 6 0.6 0.8 0.7 0.9 1
  9. 7 0.7 0.8 0.7 0.9 1
  10. 8 0.8 0.8 0.7 0.9 1
  11. 9 0.9 0.7 0.7 1.0 1
  12. 10 1.0 0.7 0.7 1.0 1

接著我們產生兩組資料, 每組資料有 600筆資料,2個特徵 X: 600x2以及2個類別 y:600 (前300個元素為0,餘下為1)

  1. dataset_fixed_cov():2個類別的特徵具備有相同共變數(covariance)
  2. dataset_fixed_cov():2個類別的特徵具備有不同之共變數
    差異落在X資料的產生np.dot(np.random.randn(n, dim), C)np.dot(np.random.randn(n, dim), C.T)的不同。np.dot(np.random.randn(n, dim), C)會產生300x2之矩陣,其亂數產生的範圍可交由C矩陣來控制。在dataset_fixed_cov()中,前後300筆資料產生之範圍皆由C來調控。我們可以在最下方的結果圖示看到上排影像(相同共變數)的資料分佈無論是紅色(代表類別1)以及藍色(代表類別2)其分佈形狀相似。而下排影像(不同共變數),分佈形狀則不同。圖示中,橫軸及縱軸分別表示第一及第二個特徵,讀者可以試著將 0.83這個數字減少或是將C.T改成C,看看最後結果圖形有了什麼改變?
  1. def dataset_fixed_cov():
  2. '''Generate 2 Gaussians samples with the same covariance matrix'''
  3. n, dim = 300, 2
  4. np.random.seed(0)
  5. C = np.array([[0., -0.23], [0.83, .23]])
  6. X = np.r_[np.dot(np.random.randn(n, dim), C),
  7. np.dot(np.random.randn(n, dim), C) + np.array([1, 1])] #利用 + np.array([1, 1]) 產生類別間的差異
  8. y = np.hstack((np.zeros(n), np.ones(n))) #產生300個零及300個1並連接起來
  9. return X, y
  10. def dataset_cov():
  11. '''Generate 2 Gaussians samples with different covariance matrices'''
  12. n, dim = 300, 2
  13. np.random.seed(0)
  14. C = np.array([[0., -1.], [2.5, .7]]) * 2.
  15. X = np.r_[np.dot(np.random.randn(n, dim), C),
  16. np.dot(np.random.randn(n, dim), C.T) + np.array([1, 4])]
  17. y = np.hstack((np.zeros(n), np.ones(n)))
  18. return X, y

(二)繪圖函式

  1. 找出 True Positive及False Negative 之辨認點
  2. 以紅色及藍色分別表示分類為 0及1的資料點,而以深紅跟深藍來表示誤判資料
  3. lda.predict_proba()畫出分類的機率分佈(請參考範例三)

(為了方便在ipython notebook環境下顯示,下面函式有經過微調)

  1. def plot_data(lda, X, y, y_pred, fig_index):
  2. splot = plt.subplot(2, 2, fig_index)
  3. if fig_index == 1:
  4. plt.title('Linear Discriminant Analysis',fontsize=28)
  5. plt.ylabel('Data with fixed covariance',fontsize=28)
  6. elif fig_index == 2:
  7. plt.title('Quadratic Discriminant Analysis',fontsize=28)
  8. elif fig_index == 3:
  9. plt.ylabel('Data with varying covariances',fontsize=28)
  10. # 步驟一:找出 True Positive及False postive 之辨認點
  11. tp = (y == y_pred) # True Positive
  12. tp0, tp1 = tp[y == 0], tp[y == 1] #tp0 代表分類為0且列為 True Positive之資料點
  13. X0, X1 = X[y == 0], X[y == 1]
  14. X0_tp, X0_fp = X0[tp0], X0[~tp0]
  15. X1_tp, X1_fp = X1[tp1], X1[~tp1]
  16. # 步驟二:以紅藍來畫出分類資料,以深紅跟深藍來表示誤判資料
  17. # class 0: dots
  18. plt.plot(X0_tp[:, 0], X0_tp[:, 1], 'o', color='red')
  19. plt.plot(X0_fp[:, 0], X0_fp[:, 1], '.', color='#990000') # dark red
  20. # class 1: dots
  21. plt.plot(X1_tp[:, 0], X1_tp[:, 1], 'o', color='blue')
  22. plt.plot(X1_fp[:, 0], X1_fp[:, 1], '.', color='#000099') # dark blue
  23. #步驟三:畫出分類的機率分佈(請參考範例三)
  24. # class 0 and 1 : areas
  25. nx, ny = 200, 100
  26. x_min, x_max = plt.xlim()
  27. y_min, y_max = plt.ylim()
  28. xx, yy = np.meshgrid(np.linspace(x_min, x_max, nx),
  29. np.linspace(y_min, y_max, ny))
  30. Z = lda.predict_proba(np.c_[xx.ravel(), yy.ravel()])
  31. Z = Z[:, 1].reshape(xx.shape)
  32. plt.pcolormesh(xx, yy, Z, cmap='red_blue_classes',
  33. norm=colors.Normalize(0., 1.))
  34. plt.contour(xx, yy, Z, [0.5], linewidths=2., colors='k')
  35. # means
  36. plt.plot(lda.means_[0][0], lda.means_[0][1],
  37. 'o', color='black', markersize=10)
  38. plt.plot(lda.means_[1][0], lda.means_[1][1],
  39. 'o', color='black', markersize=10)
  40. return splot
  1. def plot_ellipse(splot, mean, cov, color):
  2. v, w = linalg.eigh(cov)
  3. u = w[0] / linalg.norm(w[0])
  4. angle = np.arctan(u[1] / u[0])
  5. angle = 180 * angle / np.pi # convert to degrees
  6. # filled Gaussian at 2 standard deviation
  7. ell = mpl.patches.Ellipse(mean, 2 * v[0] ** 0.5, 2 * v[1] ** 0.5,
  8. 180 + angle, color=color)
  9. ell.set_clip_box(splot.bbox)
  10. ell.set_alpha(0.5)
  11. splot.add_artist(ell)
  12. splot.set_xticks(())
  13. splot.set_yticks(())

(三)測試資料並繪圖

  1. def plot_lda_cov(lda, splot):
  2. plot_ellipse(splot, lda.means_[0], lda.covariance_, 'red')
  3. plot_ellipse(splot, lda.means_[1], lda.covariance_, 'blue')
  4. def plot_qda_cov(qda, splot):
  5. plot_ellipse(splot, qda.means_[0], qda.covariances_[0], 'red')
  6. plot_ellipse(splot, qda.means_[1], qda.covariances_[1], 'blue')
  7. ###############################################################################
  8. figure = plt.figure(figsize=(30,20), dpi=300)
  9. for i, (X, y) in enumerate([dataset_fixed_cov(), dataset_cov()]):
  10. # Linear Discriminant Analysis
  11. lda = LinearDiscriminantAnalysis(solver="svd", store_covariance=True)
  12. y_pred = lda.fit(X, y).predict(X)
  13. splot = plot_data(lda, X, y, y_pred, fig_index=2 * i + 1)
  14. plot_lda_cov(lda, splot)
  15. plt.axis('tight')
  16. # Quadratic Discriminant Analysis
  17. qda = QuadraticDiscriminantAnalysis(store_covariances=True)
  18. y_pred = qda.fit(X, y).predict(X)
  19. splot = plot_data(qda, X, y, y_pred, fig_index=2 * i + 2)
  20. plot_qda_cov(qda, splot)
  21. plt.axis('tight')
  22. plt.suptitle('Linear Discriminant Analysis vs Quadratic Discriminant Analysis',fontsize=28)
  23. plt.show()

png

Python source code: plot_lda_qda.py

http://scikit-learn.org/stable/_downloads/plot_lda_qda.py

  1. print(__doc__)
  2. from scipy import linalg
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. import matplotlib as mpl
  6. from matplotlib import colors
  7. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  8. from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
  9. ###############################################################################
  10. # colormap
  11. cmap = colors.LinearSegmentedColormap(
  12. 'red_blue_classes',
  13. {'red': [(0, 1, 1), (1, 0.7, 0.7)],
  14. 'green': [(0, 0.7, 0.7), (1, 0.7, 0.7)],
  15. 'blue': [(0, 0.7, 0.7), (1, 1, 1)]})
  16. plt.cm.register_cmap(cmap=cmap)
  17. ###############################################################################
  18. # generate datasets
  19. def dataset_fixed_cov():
  20. '''Generate 2 Gaussians samples with the same covariance matrix'''
  21. n, dim = 300, 2
  22. np.random.seed(0)
  23. C = np.array([[0., -0.23], [0.83, .23]])
  24. X = np.r_[np.dot(np.random.randn(n, dim), C),
  25. np.dot(np.random.randn(n, dim), C) + np.array([1, 1])]
  26. y = np.hstack((np.zeros(n), np.ones(n)))
  27. return X, y
  28. def dataset_cov():
  29. '''Generate 2 Gaussians samples with different covariance matrices'''
  30. n, dim = 300, 2
  31. np.random.seed(0)
  32. C = np.array([[0., -1.], [2.5, .7]]) * 2.
  33. X = np.r_[np.dot(np.random.randn(n, dim), C),
  34. np.dot(np.random.randn(n, dim), C.T) + np.array([1, 4])]
  35. y = np.hstack((np.zeros(n), np.ones(n)))
  36. return X, y
  37. ###############################################################################
  38. # plot functions
  39. def plot_data(lda, X, y, y_pred, fig_index):
  40. splot = plt.subplot(2, 2, fig_index)
  41. if fig_index == 1:
  42. plt.title('Linear Discriminant Analysis')
  43. plt.ylabel('Data with fixed covariance')
  44. elif fig_index == 2:
  45. plt.title('Quadratic Discriminant Analysis')
  46. elif fig_index == 3:
  47. plt.ylabel('Data with varying covariances')
  48. tp = (y == y_pred) # True Positive
  49. tp0, tp1 = tp[y == 0], tp[y == 1]
  50. X0, X1 = X[y == 0], X[y == 1]
  51. X0_tp, X0_fp = X0[tp0], X0[~tp0]
  52. X1_tp, X1_fp = X1[tp1], X1[~tp1]
  53. # class 0: dots
  54. plt.plot(X0_tp[:, 0], X0_tp[:, 1], 'o', color='red')
  55. plt.plot(X0_fp[:, 0], X0_fp[:, 1], '.', color='#990000') # dark red
  56. # class 1: dots
  57. plt.plot(X1_tp[:, 0], X1_tp[:, 1], 'o', color='blue')
  58. plt.plot(X1_fp[:, 0], X1_fp[:, 1], '.', color='#000099') # dark blue
  59. # class 0 and 1 : areas
  60. nx, ny = 200, 100
  61. x_min, x_max = plt.xlim()
  62. y_min, y_max = plt.ylim()
  63. xx, yy = np.meshgrid(np.linspace(x_min, x_max, nx),
  64. np.linspace(y_min, y_max, ny))
  65. Z = lda.predict_proba(np.c_[xx.ravel(), yy.ravel()])
  66. Z = Z[:, 1].reshape(xx.shape)
  67. plt.pcolormesh(xx, yy, Z, cmap='red_blue_classes',
  68. norm=colors.Normalize(0., 1.))
  69. plt.contour(xx, yy, Z, [0.5], linewidths=2., colors='k')
  70. # means
  71. plt.plot(lda.means_[0][0], lda.means_[0][1],
  72. 'o', color='black', markersize=10)
  73. plt.plot(lda.means_[1][0], lda.means_[1][1],
  74. 'o', color='black', markersize=10)
  75. return splot
  76. def plot_ellipse(splot, mean, cov, color):
  77. v, w = linalg.eigh(cov)
  78. u = w[0] / linalg.norm(w[0])
  79. angle = np.arctan(u[1] / u[0])
  80. angle = 180 * angle / np.pi # convert to degrees
  81. # filled Gaussian at 2 standard deviation
  82. ell = mpl.patches.Ellipse(mean, 2 * v[0] ** 0.5, 2 * v[1] ** 0.5,
  83. 180 + angle, color=color)
  84. ell.set_clip_box(splot.bbox)
  85. ell.set_alpha(0.5)
  86. splot.add_artist(ell)
  87. splot.set_xticks(())
  88. splot.set_yticks(())
  89. def plot_lda_cov(lda, splot):
  90. plot_ellipse(splot, lda.means_[0], lda.covariance_, 'red')
  91. plot_ellipse(splot, lda.means_[1], lda.covariance_, 'blue')
  92. def plot_qda_cov(qda, splot):
  93. plot_ellipse(splot, qda.means_[0], qda.covariances_[0], 'red')
  94. plot_ellipse(splot, qda.means_[1], qda.covariances_[1], 'blue')
  95. ###############################################################################
  96. for i, (X, y) in enumerate([dataset_fixed_cov(), dataset_cov()]):
  97. # Linear Discriminant Analysis
  98. lda = LinearDiscriminantAnalysis(solver="svd", store_covariance=True)
  99. y_pred = lda.fit(X, y).predict(X)
  100. splot = plot_data(lda, X, y, y_pred, fig_index=2 * i + 1)
  101. plot_lda_cov(lda, splot)
  102. plt.axis('tight')
  103. # Quadratic Discriminant Analysis
  104. qda = QuadraticDiscriminantAnalysis(store_covariances=True)
  105. y_pred = qda.fit(X, y).predict(X)
  106. splot = plot_data(qda, X, y, y_pred, fig_index=2 * i + 2)
  107. plot_qda_cov(qda, splot)
  108. plt.axis('tight')
  109. plt.suptitle('Linear Discriminant Analysis vs Quadratic Discriminant Analysis')
  110. plt.show()