<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第二部分 分類 ## 十三、KNN 分類入門 歡迎閱讀第十三篇機器學習系列講義。我們開始了一個全新的部分:分類。這面,我們會涉及兩個主要的分類算法:K 最近鄰和支持向量機(SVM)。這兩個算法都是分類算法,它們的工作方式不同。 首先,讓我們考慮一個數據集,創建下面的圖像: ![](https://img.kancloud.cn/ca/8f/ca8fcd1b6a3ff1b161549f953fca4330_891x645.png) 直觀上,你應該能夠看到兩個組。但是,分類是監督式機器學習。當我們將數據提供給機器學習算法的時候,我們實際上已經告訴它組的存在,以及哪個數據屬于哪個組。一個機器學習的相似形式是聚類,其中你讓機器尋找組,但它是非監督機器學習算法,后面我們會降到。所以,使用監督式機器學習,我們需要擁有預置標簽的數據用于訓練,像這樣: ![](https://img.kancloud.cn/a6/b4/a6b4c01d7453f88155ca7da8fec6fa0d_1205x730.png) 這里我們擁有黑的點和紅的點。分類的目標就是拿已知的數據訓練機器,就像這樣,使機器能夠識別新數據的分類(紅的還是黑的)。例如,我們會處理乳腺腫瘤的數據,來基于一些屬性嘗試判斷是良性的還是惡性的。我們實現它的方式,就是獲取已知的樣本屬性,例如大小、形狀作為特征,標簽或者分類就是良性或者惡性。這里,我們可以根據縱六的相同屬性來評估未來的腫瘤,并且預測是良性還是惡性。 所以,分類的目標就是識別下面的點屬于哪個類: ![](https://img.kancloud.cn/58/f4/58f42c769a84c167ef449949c9e819cf_1205x730.png) 你可能能猜到它是紅的類,但是為什么呢?嘗試為自己定義這里有什么參數。下面這種情況呢? ![](https://img.kancloud.cn/ee/6b/ee6bcc71b8dc971cf7cdd5b7015a7b49_1205x730.png) 第二種情況中我們可能選取黑色。同樣,嘗試定義為啥這么選擇。最后,如果是這樣: ![](https://img.kancloud.cn/24/db/24db3b7cc8c2cb64fa1e94c60e8eee38_1205x730.png) 這種情況比較復雜,嘗試選取一種分類。 大多數人都會選擇黑色。無論哪種,考慮為什么你會做出這種選擇。多數人會根據近似性對數據集分組。直覺上它是最有意義的。如果你拿尺子畫出它到最近的黑色點的直線,之后畫出它到最近的紅色點的直線,你就會發現黑色點更近一些。與之相似,當數據點距離一個分組比另一個更近時,你就會基于近似性做出判斷。因此 KNN 機器學習算法就誕生了。 KNN 是個簡單高效的機器學習分類算法。如果這非常簡單,就像我們看到的那樣,我們為什么需要一個算法,而不是直接拿眼睛看呢?就像回歸那樣,機器可以計算得更快,處理更大的數據集,擴展,以及更重要的是,處理更多維度,例如 100 維。 它的工作方式就是它的名字。K 就是你選取的數量,近鄰就是已知數據中的相鄰數據點。我們尋找任意數量的“最近”的相鄰點。假設`K=3`,所以我們就尋找三個最近的相鄰點。例如: ![](https://img.kancloud.cn/5c/77/5c775c6cdf0f4bb7382e4eef2bd89a7d_1205x730.png) 上面的圖中,我圈出了三個最近的相鄰點。這里,所有三個點都是紅色分類。KNN 會基于相鄰點進行計數。所有三個近鄰都是紅色,所以它 100% 是紅色分類。如果兩個近鄰都是紅色,一個是黑色,我們也將其分類為紅色,只是置信度就少了。要注意,由于計數的本質,你會更希望使用奇數 K,否則會產生 50:50 的情況。有一種方式在距離上應用權重,來懲罰那些更遠的點,所以你就可以使用偶數的 K 值了。 下一個教程中,我們會涉及到 Scikit 的 KNN 算法,來處理乳腺腫瘤數據,之后我們會嘗試自己來編寫這個算法。 ## 十四、對數據使用 KNN 歡迎閱讀第十四個部分。上一個部分我們介紹了分類,它是一種機器學習的監督算法,并且解釋了 KNN 算法的直覺。這個教程中,我們打算使用 Sklearn,講解一個簡單的算法示例,之后在后面的教程中,我們會構建我們自己的算法來更多了解背后的工作原理。 為了使用例子說明分類,我們打算使用[乳腺腫瘤數據集](https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Original%29),它是 UCI 所貢獻的數據集,從威斯康星大學收集。UCI 擁有龐大的[機器學習倉庫](https://archive.ics.uci.edu/ml/datasets.html)。這里的數據集組織為經常使用的機器學習算法類型、數據類型、屬性類型、主題范圍以及其它。它們對教學和機器學習算法開發都很實用。我自己經常瀏覽那里,非常值得收藏。在乳腺腫瘤數據集的頁面,選擇[`Data Folder`鏈接](https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/)。之后,下載`breast-cancer-wisconsin.data`和`breast-cancer-wisconsin.names`。這些可能不能下載,而是會在瀏覽器中展示。如果是這樣右鍵點擊“另存為”。 下載之后,打開`breast-cancer-wisconsin.names`文件。查看文件,向下滾動 100 行,我們就能獲取屬性(列)的名稱、使用這些信息,我們打算手動將這些標簽添加到` breast-cancer-wisconsin.data`文件中。打開它,并輸入新的第一行: ``` id,clump_thickness,uniform_cell_size, uniform_cell_shape,marginal_adhesion, single_epi_cell_size,bare_nuclei,bland_chromation, normal_nucleoli,mitoses,class ``` 之后,你應該會思考,我們的特征和標簽應該是什么。我們嘗試對數據進行分類,所以很顯然分類就是這些屬性會導致良性還是惡性。同樣,大多數這些屬性看起來都是可用的,但是是否有任何屬性與其它屬性類似,或者是無用的?ID 屬性并不是我們打算扔給分類器的東西。 缺失或者損壞的數據:這個數據集擁有一些缺失數據,我們需要清理。讓我們以導入來開始,拉取數據,之后做一些清理: ```py import numpy as np from sklearn import preprocessing, cross_validation, neighbors import pandas as pd df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) ``` 在讀取數據之后,我們注意到,有一些列存在缺失數據。這些缺失數據以`?`填充。`.names`文件告訴了我們,但是我們最終可以通過錯誤來發現,如果我們嘗試將這些信息扔給分類為。這個時候,我們選擇將缺失數據填充為 -99999 值。你可以選擇你自己的方法來處理缺失數據,但是在真實世界中,你可能發現 50% 或者更多記錄,一個或多個列都含有缺失數據。尤其是,如果你使用可擴展的屬性來收集數據。-99999 并不完美,但是它足夠有效了。下面,我們丟棄了 ID 列。完成之后,我們會注釋掉 ID 列的丟棄,只是看看包含他可能有哪些影響。 下面,我們定義我們的特征和標簽。 特征`X`是除了分類的任何東西。調用`df.drop`會返回一個新的 DataFrame,不帶丟棄的列。標簽`y`僅僅是分類列。 現在我們創建訓練和測試樣本,使用 Sklearn 的`cross_validation.train_test_split`。 ```py X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2) ``` 定義分類器: ```py clf = neighbors.KNeighborsClassifier() ``` 這里,我們使用 KNN 分類器。 訓練分類器: ```py clf.fit(X_train, y_train) ``` 測試: ```py accuracy = clf.score(X_test, y_test) print(accuracy) ``` 結果應該是 95%,并且開箱即用,無需任何調整。非常棒。讓我們展示一下,當我們注釋掉 ID 列,包含一些無意義和誤導性的數據之后,會發生什么。 ```py import numpy as np from sklearn import preprocessing, cross_validation, neighbors import pandas as pd df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) #df.drop(['id'], 1, inplace=True) X = np.array(df.drop(['class'], 1)) y = np.array(df['class']) X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2) clf = neighbors.KNeighborsClassifier() clf.fit(X_train, y_train) accuracy = clf.score(X_test, y_test) print(accuracy) ``` 影響很令人吃驚,準確率從 95% 降到了 60%。在未來,如果 AI 通知了這個星球,要注意你只需要給它一些無意義的屬性來智取它。非常有意思,添加噪聲是一種損害你的算法的方式。當你和你的機器人霸主較量時,分辨有意義和惡意的噪聲會節省你的時間。 下面你可以大致猜測,我們如何做預測,如果你遵循了 Sklearn 的教程。首先,我們需要一些沿革本數據。我們可以自己編。例如,我們會查看樣本文件的某一行。你可以添加噪聲來執行進一步的分析,假設標準差不是那么離譜。這么做也比較安全,由于你并不在篡改的數據上訓練,你僅僅做了測試。我會通過編造一行來手動實現它。 ```py example_measures = np.array([4,2,1,1,1,2,3,2,1]) ``` 你可以盡管在文檔中搜索特征列表。它不存在。現在你可以: ```py prediction = clf.predict(example_measures) print(prediction) ``` 或者取決于你的閱讀時間,你可能不能這么做。在這么做的時候,我得到了一個警告: ```py DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and will raise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample. ``` 好的,沒有問題。我們只擁有一個特征嗎?不是。我們只擁有一個記錄嗎?是的。所以我們使用`X.reshape(1, -1)`。 ```py example_measures = np.array([4,2,1,1,1,2,3,2,1]) example_measures = example_measures.reshape(1, -1) prediction = clf.predict(example_measures) print(prediction) # 0.95 # [2] ``` 這里的第一個輸出是準確率(95%)和預測(2)。這就是我們的偽造數據的建模。 如果我們有兩條呢? ```py example_measures = np.array([[4,2,1,1,1,2,3,2,1],[4,2,1,1,1,2,3,2,1]]) example_measures = example_measures.reshape(2, -1) prediction = clf.predict(example_measures) print(prediction) ``` 忽略這個硬編碼。如果我們不知道有幾何樣例會怎么樣? ```py example_measures = np.array([[4,2,1,1,1,2,3,2,1],[4,2,1,1,1,2,3,2,1]]) example_measures = example_measures.reshape(len(example_measures), -1) prediction = clf.predict(example_measures) print(prediction) ``` 你可以看到,KNN 算法的實現不僅僅很簡單,而且這里也很準確。下一個教程中,我們打算從零構建我們自己的 KNN 算法,而不是使用 Sklearn,來嘗試了解更多算法的東西,理解它的工作原理,最重要的是,了解它的陷阱。 ## 十五、對數據使用 KNN 歡迎閱讀第十五篇教程,其中我們當前涉及到使用 KNN 算法來分類。上一篇教程中,我們涉及到如何使用 Sklearn 的 KNN 算法來預測良性或者惡性腫瘤,基于腫瘤的屬性,準確率有 95%。現在,我們打算深入 KNN 的工作原理,以便完全理解算法本身,來使其更好為我們工作。 我們會回到我們的乳腺腫瘤數據集,對其使用我們自定義 KNN 算法,并將其與 Sklearn 的比較,但是我們打算首先以一個非常簡單的理論開始。KNN 基于近似性,不是分組,而是單獨的點。所以,所有這種算法所做的,實際上是計算點之間的距離,并且之后選取距離最近的前 K 個點的最常出現的分類。有幾種方式來計算平面上的距離,他們中許多都可以在這里使用,但是最常使用的版本是歐氏距離,以歐幾里得命名。他是一個著名的數學家,被稱為幾何之父,他編寫了《幾何原本》,被稱為數學家的圣經。歐氏距離為: ![](https://img.kancloud.cn/de/f0/def0deea24250c04e38b3a1635ea4355_141x92.png) 所以這是什么意思?基本上,它是每個點之間距離的平方和的平方根。在 Python 的術語中,是這樣: ```py plot1 = [1,3] plot2 = [2,5] euclidean_distance = sqrt( (plot1[0]-plot2[0])**2 + (plot1[1]-plot2[1])**2 ) ``` 這里距離是 2.236。 這就是 KNN 背后的基本數學原理了,現在我們僅僅需要構建一個系統來處理算法的剩余部分,例如尋找最近距離,它們的分組,然后是計數。 ## 十六、從零創建 KNN 分類器:第一部分 歡迎閱讀第十六個部分,我們現在涉及到 KNN 算法的分類。在上一個教程中,我們涉及到了歐氏距離,現在我們開始使用純粹的 Python 代碼來建立我們自己的簡單樣例。 最開始,讓我們導入下列東西并為 Matplotlib 設置一個樣式。 ```py import numpy as np import matplotlib.pyplot as plt from matplotlib import style import warnings from math import sqrt from collections import Counter style.use('fivethirtyeight') ``` 我們打算使用警告來避免使用比分組數量更低的 K 值,至少是最開始(因為我會展示一個更加高效的方法),之后對集合計數來獲取出現次數最多的分類。 下面,我們創建一些數據: ```py dataset = {'k':[[1,2],[2,3],[3,1]], 'r':[[6,5],[7,7],[8,6]]} new_features = [5,7] ``` 這個數據集只是個 Python 字典,鍵是點的顏色(將這些看做分類),值是屬于這個分類的數據點。如果你回憶我們的乳腺腫瘤數據集,分類都是數字,通常 Sklearn 只能處理數字。例如,向量翻譯為任意數字`2`,而惡性翻譯為任意數字`4`,而不是一個字符串。這是因為,Sklearn 只能使用數字,但是你并不一定要使用數字來代表分類。下面,我們創建簡單的數據集`5, 7`,用于測試。我們可以這樣來快速繪圖: ```py [[plt.scatter(ii[0],ii[1],s=100,color=i) for ii in dataset[i]] for i in dataset] plt.scatter(new_features[0], new_features[1], s=100) plt.show() ``` ![](https://img.kancloud.cn/c2/a7/c2a7a9327d48c6061b3ff2f74221d9e2_1460x872.png) ` [[plt.scatter(ii[0],ii[1],s=100,color=i) for ii in dataset[i]] for i in dataset]`這一行和下面這個相同: ```py for i in dataset: for ii in dataset[i]: plt.scatter(ii[0],ii[1],s=100,color=i) ``` 你可以看到紅色和黑色的明顯分組,并且我們還有藍色的點,它是`new_features`,我們打算對其分類。 我們擁有了數據,現在我們打算創建一些函數,來分類數據。 ```py def k_nearest_neighbors(data, predict, k=3): return vote_result ``` 這就是我們的框架,從這里開始。我們想要一個函數,它接受要訓練的數據,預測的數據,和 K 值,它的默認值為 3。 下面,我們會開始填充函數,首先是一個簡單的警告: ```py def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') return vote_result ``` 如果選取的最近鄰的數量小于或等于分類數量,那么就給出警告(因為這樣會產生偏差)。 現在,如何尋找最近的三個點呢?是否有一些用于搜索的魔法呢?沒有,如果有的話,也是很復雜而。為什么呢?KNN 的工作原理是,我們需要將問題中的數據與之前的數據比較,之后才能知道最近的點是什么。因此,如果你的數據越多,KNN 就越慢。我們這里告一段落,但是要考慮是否有方法來加速這個過程。 ## 十七、從零創建 KNN 分類器:第二部分 歡迎閱讀第十七個部分,我們正在講解 KNN 算法的分類。上一個教程中,我們開始構建我們的 KNN 示例,這里我們將其完成。 我處理它的方式,就是首先創建一個 Python 列表,它包含另一個列表,里面包含數據集中每個點的距離和分類。一旦填充完畢,我們就可以根據距離來排序列表,截取列表的前 K 個值,找到出現次數最多的,就找到了答案。 ```py def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = sqrt( (features[0]-predict[0])**2 + (features[1]-predict[1])**2 ) distances.append([euclidean_distance,group]) ``` 有一種方式來計算歐氏距離,最簡潔的方式就是遵循定義。也就是說,使用 NumPy 會更快一點。由于 KNN 是一種機器學習的爆破方法,我們需要我們能得到的所有幫助。因此,我們可以將函數修改一點。一個選項是: ```py euclidean_distance = np.sqrt(np.sum((np.array(features)-np.array(predict))**2)) print(euclidean_distance) ``` 這還是很清楚,我們剛剛使用了 NumPy 版本。NumPy 使用 C 優化,是個非常高效的庫,很多時候允許我們計算更快的算術。也就是說,NumPy 實際上擁有大量的線性代數函數。例如,這是范數: ```py euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) print(euclidean_distance) ``` 歐式距離度量兩個端點之間的線段長度。歐幾里得范數度量向量的模。向量的模就是它的長度,這個是等價的。名稱僅僅告訴你你所在的控件。 我打算使用后面那一個,但是我會遵循我的約定,使其易于拆解成代碼。如果你不了解 NumPy 的內建功能,你需要去了解如何使用。 現在,`for`循環之外,我們得到了距離列表,它包含距離和分類的列表。我們打算對列表排序,之后截取前 K 個元素,選取下標 1,它就是分類。 ```py votes = [i[1] for i in sorted(distances)[:k]] ``` 上面,我們遍歷了排序后的距離列表的每個列表。排序方法會(首先)基于列表中每個列表的第一個元素。第一個元素是距離,所以執行`orted(distances)`之后我們就按照從小到大的距離排序了列表。之后我們截取了列表的`[:k]`,因為我們僅僅對前 K 個感興趣。最后,在`for`循環的外層,我們選取了`i[1]`,其中`i`就是列表中的列表,它包含`[diatance, class]`(距離和分類的列表)。按照距離排序之后,我們無需再關心距離,只需要關心分類。 所以現在有三個候選分類了。我們需要尋找出現次數最多的分類。我們會使用 Python 標準庫模塊`collections.Counter`。 ```py vote_result = Counter(votes).most_common(1)[0][0] ``` `Collections`會尋找最常出現的元素。這里,我們想要一個最常出現的元素,但是你可以尋找前 3 個或者前`x`個。如果沒有`[0][0]`這部分,你會得到`[('r', 3)]`(元素和計數的元組的列表)。所以`[0][0]`會給我們元組的第一個元素。你看到的 3 是`'r'`的計數。 最后,返回預測結果,就完成了。完整的代碼是: ```py def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([euclidean_distance,group]) votes = [i[1] for i in sorted(distances)[:k]] vote_result = Counter(votes).most_common(1)[0][0] return vote_result ``` 現在,如果我們打算基于我們之前所選的點,來做預測: ```py result = k_nearest_neighbors(dataset, new_features) print(result) ``` 非常肯定,我得到了`r`,這就是預期的值。讓我們繪制它吧。 ```py import numpy as np import matplotlib.pyplot as plt from matplotlib import style import warnings from math import sqrt from collections import Counter style.use('fivethirtyeight') def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([euclidean_distance,group]) votes = [i[1] for i in sorted(distances)[:k]] vote_result = Counter(votes).most_common(1)[0][0] return vote_result dataset = {'k':[[1,2],[2,3],[3,1]], 'r':[[6,5],[7,7],[8,6]]} new_features = [5,7] [[plt.scatter(ii[0],ii[1],s=100,color=i) for ii in dataset[i]] for i in dataset] # same as: ##for i in dataset: ## for ii in dataset[i]: ## plt.scatter(ii[0],ii[1],s=100,color=i) plt.scatter(new_features[0], new_features[1], s=100) result = k_nearest_neighbors(dataset, new_features) plt.scatter(new_features[0], new_features[1], s=100, color = result) plt.show() ``` ![](https://img.kancloud.cn/86/f6/86f62d1d18337d3c5351a0d5689ad76a_1223x780.png) 你可以看到,我們添加了新的點`5, 7`,它分類為紅色的點,符合預期。 這只是小規模的處理,但是如果我們處理乳腺腫瘤數據集呢?我們如何和 Sklearn 的 KNN 算法比較?下一個教程中,我們會將算法用于這個數據集。 ## 十八、測試 KNN 分類器 歡迎閱讀第十八篇教程,我們剛剛編寫了我們自己的 KNN 分類器算法,現在我們準備好了使用一些真實數據來測試它。開始,我們打算使用之前的乳腺腫瘤數據集。如果你沒有它,返回教程 13 并抓取數據。 目前為止,我們的算法像這樣處理數據: ![](img/18-1.png) 其中藍色的點是位置數據,運行算法,并正確分類數據: ![](img/18-2.png) 現在,我們打算回顧乳腺腫瘤數據集,它記錄腫瘤的屬性變將它們按照良性還是惡性分類。Sklearn 的 KNN 分類器有 95% 的準確率,并且我們打算測試我們自己的算法。 我們會以下列代碼開始: ```py import numpy as np import warnings from collections import Counter import pandas as pd import random def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([euclidean_distance,group]) votes = [i[1] for i in sorted(distances)[:k]] vote_result = Counter(votes).most_common(1)[0][0] return vote_result ``` 這應該看起來很熟悉。要注意我導入了 Pandas 和 random。我已經移除了 Matplotlib 的導入,因為我們不打算繪制任何東西。下面,我們打算加載數據: ```py df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) full_data = df.astype(float).values.tolist() ``` 這里,我們加載了數據,替換掉了問號,丟棄了 ID 列,并且將數據轉危為列表的列表。要注意我們顯式將 DataFrame 轉換為浮點類型。出于一些原因,至少對于我來說,一些數據點仍然是數字,但是字符串數據類型并不是很好。 下面,我們打算把數據打亂,之后將其分割: ```py Next, we're going to shuffle the data, and then split it up: random.shuffle(full_data) test_size = 0.2 train_set = {2:[], 4:[]} test_set = {2:[], 4:[]} train_data = full_data[:-int(test_size*len(full_data))] test_data = full_data[-int(test_size*len(full_data)):] ``` 首先我們打亂了數據(它包含特征和標簽)。之后我們為訓練和測試集準備了一個字典用于填充。下面,我們指定了哪個是`train_data `,哪個是`test_data`。我們選取前 80% 作為`train_data `(邏輯是在后 20% 的地方分割),之后我們通過在后 20% 的地方分割,來創建`test_data`。 現在我們開始填充字典。如果不清楚的話,字典有兩個鍵:2 和 4。2 是良性腫瘤(和實際數據集相同),4 是惡性腫瘤,也和數據集相同。我們將其硬編碼,但是其他人可以選取分類,并像這樣創建字典,它的鍵是分類中的唯一值。我們僅僅是使其簡單。 ```py for i in train_data: train_set[i[-1]].append(i[:-1]) for i in test_data: test_set[i[-1]].append(i[:-1]) ``` 現在我們填充了字典,我們擁有了測試集,其中鍵是分類,值是屬性。 最后就是訓練和測試的時候了。使用 KNN,這些步驟基本就完成了,因為訓練步驟就是把點村進內存,測試步驟就是比較距離: ```py correct = 0 total = 0 for group in test_set: for data in test_set[group]: vote = k_nearest_neighbors(train_set, data, k=5) if group == vote: correct += 1 total += 1 print('Accuracy:', correct/total) ``` 現在我們首先迭代測試集的分組(分類,2 或者 4,也是字典的鍵),之后我們遍歷每個數據點,將數據點扔給`k_nearest_neighbors`,以及我們的訓練集`train_set`,之后是我們的 K,它是 5。我選擇了 5,純粹是因為它是 SKlearn 的`KNeighborsClassifier`的默認值。所以我們的完整代碼是: ```py import numpy as np import matplotlib.pyplot as plt from matplotlib import style import warnings from collections import Counter #dont forget this import pandas as pd import random style.use('fivethirtyeight') def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([euclidean_distance,group]) votes = [i[1] for i in sorted(distances)[:k]] vote_result = Counter(votes).most_common(1)[0][0] return vote_result df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) full_data = df.astype(float).values.tolist() random.shuffle(full_data) test_size = 0.2 train_set = {2:[], 4:[]} test_set = {2:[], 4:[]} train_data = full_data[:-int(test_size*len(full_data))] test_data = full_data[-int(test_size*len(full_data)):] for i in train_data: train_set[i[-1]].append(i[:-1]) for i in test_data: test_set[i[-1]].append(i[:-1]) correct = 0 total = 0 for group in test_set: for data in test_set[group]: vote = k_nearest_neighbors(train_set, data, k=5) if group == vote: correct += 1 total += 1 print('Accuracy:', correct/total) ``` ## 十九、KNN 的最終見解 既然我們了解了它的工作原理,這里我們打算涉及一些 KNN 算法的最終見解,包含 K 值,置信度,速度,以及算法的優點和缺點。 在執行 100 個樣例的測試之后,Sklearn 的`neighbors.KNeighborsClassifier`分類器的準確率是 0.97,我們自己編寫的分類器也一樣。不要故步自封,因為這個算法非常簡單和基礎。KNN 分類器的真正價值并不在準確率上,而是它的速度。KNN 分類器的主要缺陷就是就是速度,你可以用它來執行操作。 對于速度,Sklearn 的 KNN 版本的每個周期是 0.044 秒,我們的是 0.55 秒。因此,雖然我們實現了相同的結果,我們比 Sklearn 慢很多。好的消息是,如果你對它們如何實現的感興趣,你可以查看源代碼、我們也提到了,我們也可以使用一個主流方式來提升速度。KNN 并不需要過多的訓練。訓練僅僅是將數據集加載到內存。你可以將數據集保留在內存中,但是 KNN 分類器的真正痛點就是對比每個數據集來尋找最近的那個。之后,如果你打算對 1000 個數據集分類,會發生什么呢?是的,一個選項是可以并發。串行執行它們沒有任何好處。我們的方式是這樣,僅僅使用一點點的處理器的能力。但是,我們可以一次性至少計算 100~200 個數據,即使是在便宜的處理器上。如果你打算了解如何并發,看一看這個[并發教程](https://pythonprogramming.net/threading-tutorial-python/)。使用 Sklearn,KNN 分類器自帶一個并行處理參數`n_jobs`。你可以將其設置為任何數值,你可以以這個線程數來并發。如果你打算一次運行 100 個操作,`n_jobs=100`。如果你僅僅打算運行盡可能做的操作,設置`n_jobs=-1`。閱讀[最近鄰文檔](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors),你可以了解更多選項。有幾種方式將你的數據與指定半徑之內的數據對比,如果你對加速 KNN,以及 Sklearn 的 KNN 版本感興趣,你可能想要看一看。 最后,我要講的最后一點就是預測的置信度。有兩種方式來度量置信度。一種是比較你預測對了多少個點,另一個是,檢查計數的百分比。例如,你的算法準確率可能是 97%,但是對于一些分類,計數可能是 3 比 2。其中 3 是主流,它的占有率是 60%,而不是理想情況下的 100%。但是告訴別人它是否有癌癥的話,就像自動駕駛汽車分辨一團土和毛毯上的孩子,你可能更希望是 100%。可能 60% 的計數就是 3% 的不準確度量的一部分。 好的,所以我們剛剛編寫了準確率為 97% 的分類器,但是沒有把所有事情都做好。KNN 非常擁有,因為它對線性和非線性數據都表現出色。主要的缺陷是規模、離群點和不良數據(要記得 ID 列的無效引入)。 我們仍然專注于監督式機器學習,特別是分類,我們下面打算設計支持向量機。 最后的代碼: ```py import numpy as np from math import sqrt import warnings from collections import Counter import pandas as pd import random def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K is set to a value less than total voting groups!') distances = [] for group in data: for features in data[group]: euclidean_distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([euclidean_distance, group]) votes = [i[1] for i in sorted(distances)[:k]] vote_result = Counter(votes).most_common(1)[0][0] confidence = Counter(votes).most_common(1)[0][1] / k return vote_result, confidence df = pd.read_csv("breast-cancer-wisconsin.data.txt") df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) full_data = df.astype(float).values.tolist() random.shuffle(full_data) test_size = 0.4 train_set = {2:[], 4:[]} test_set = {2:[], 4:[]} train_data = full_data[:-int(test_size*len(full_data))] test_data = full_data[-int(test_size*len(full_data)):] for i in train_data: train_set[i[-1]].append(i[:-1]) for i in test_data: test_set[i[-1]].append(i[:-1]) correct = 0 total = 0 for group in test_set: for data in test_set[group]: vote,confidence = k_nearest_neighbors(train_set, data, k=5) if group == vote: correct += 1 total += 1 print('Accuracy:', correct/total) ``` ## 二十、支持向量機簡介 歡迎閱讀第二十篇。我們現在打算深入另一個監督式機器學習和分類的形式:支持向量機。 支持向量機,由 Vladimir Vapnik 在上個世紀 60 年代發明,但是 90 年代之前都被忽視,并且是最熱門的機器學習分類器之一。 支持向量的目標就是尋找數據之間的最佳分割邊界。在二維空間中,你可以將其看做分隔你的數據集的最佳擬合直線。使用支持向量機,我們在向量空間中處理問題,因此分隔直線實際上是個單獨的超平面。最佳的分隔超平面定義為,支持向量之間間距“最寬”的超平面。超平面也可以叫做決策邊界。最簡單的講解方式就是圖片: ![](https://img.kancloud.cn/7c/60/7c600a136166bee44dd173f87088c092_1205x730.png) 我們會使用上面的數據開始。我們注意到,之前最普遍的直覺就是,你會將一個新的點基于它的近鄰來分類,這就是 KNN 的工作原理。這個方式的主要問題是,對于每個數據點,你將其與每個其它數據點比較,來獲取距離,因為算法不能很好擴展,盡管準確率上很可靠。支持向量機的目標就是,一次性生成“最佳擬合”直線(實際上是個平面,甚至是個超平面),他可以最優劃分數據。一旦計算出了超平面,我們就將其作為決策邊界。我們這樣做,因為決策邊界劃分兩個分類的數據。一旦我們計算了決策邊界,我們就再也不需要計算了,除非我們重新訓練數據集。因此,算法易于擴展,不像 KNN 分類器。 好奇之處在于,我們如何找出最佳分隔超平面?我們可以先使用眼睛來找。 ![](https://img.kancloud.cn/b4/fd/b4fd6f8d6f3af9c8c881b110132e148d_1205x730.png) 這幾乎是爭取的,但是如何尋找呢?首先尋找支持向量。 ![](https://img.kancloud.cn/c7/5a/c75a4e350c17fef387a9080feb427e47_1205x730.png) 一旦你找到了支持向量,你就可以創建直線,最大分隔彼此。這里,我們可以通過計算總寬度來輕易找到決策邊界。 ![](https://img.kancloud.cn/b7/e3/b7e36d5ca24c39ab7a0c1d3fa86fe629_1205x730.png) 一分為二。 ![](https://img.kancloud.cn/20/13/2013f8ff242463e4249c91f8ef189546_1205x730.png) 你就會得到邊界。 ![](https://img.kancloud.cn/71/e4/71e4e2405e038b9ca49efaed88079d8e_1205x730.png) 現在如果一個點位于決策邊界或者分割超平面的左側,我們就認為它是黑色分類,否則就是紅色分類。 值得注意的是,這個方式本質上只能處理線性分隔的數據,如果你的數據是: ![](https://img.kancloud.cn/02/b6/02b6aaf50280141651675de49d4c5325_570x452.png) 這里你能夠創建分隔超平面嘛?不能。還有沒有辦法了?當我們深入支持向量機的時候,我會讓你考慮這個問題。這里是使用 Sklearn 非常方便的原因。記得我們之前使用 Sklearn KNN 分類器的代碼嘛?這里就是了。 ```py import numpy as np from sklearn import preprocessing, cross_validation, neighbors import pandas as pd df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) X = np.array(df.drop(['class'], 1)) y = np.array(df['class']) X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2) clf = neighbors.KNeighborsClassifier() clf.fit(X_train, y_train) confidence = clf.score(X_test, y_test) print(confidence) example_measures = np.array([[4,2,1,1,1,2,3,2,1]]) example_measures = example_measures.reshape(len(example_measures), -1) prediction = clf.predict(example_measures) print(prediction) ``` 我們只需要改動兩個地方,第一個就是從`sklearn`導入`svm`。第二個就是使用支持向量分類為,它是`svm.SVC`。改動之后是: ```py import numpy as np from sklearn import preprocessing, cross_validation, neighbors, svm import pandas as pd df = pd.read_csv('breast-cancer-wisconsin.data.txt') df.replace('?',-99999, inplace=True) df.drop(['id'], 1, inplace=True) X = np.array(df.drop(['class'], 1)) y = np.array(df['class']) X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2) clf = svm.SVC() clf.fit(X_train, y_train) confidence = clf.score(X_test, y_test) print(confidence) example_measures = np.array([[4,2,1,1,1,2,3,2,1]]) example_measures = example_measures.reshape(len(example_measures), -1) prediction = clf.predict(example_measures) print(prediction) # 0.978571428571 # [2] ``` 取決于你爹隨機樣例,你應該得到 94% 到 99% ,平均值為 97%。同樣,對操作計時,要記得我通過 Sklearn 執行 KNN 代碼花費了 0.044 秒。使用`svm.SVC`,執行時間僅僅是 0.00951,在這個非常小的數據集上也有 4.6 倍。 所以我們可以認為,支持向量機似乎有同樣的準確度,但是速度更快。要注意如果我們注釋掉丟棄 ID 列的代碼,準確率會降到 60%。支持向量機通常比 KNN 算法處理大量數據要好,并且處理離群點要好。但是,這個例子中,無意義數據仍然會誤導它。我們之前使用默認參數,查看[支持向量機的文檔](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html),確實有一些參數,我們不知道它們干什么用。在后面的教程中,我們打算深入支持向量機算法,以便我們能夠實際理解所有這些參數的含義,以及它們有什么影響。雖然我們在這里告一段落,思考一下:如何處理非線性分隔,多個分類的數據和數據集(由于 SVM 是個二元分類器,也就是它生成直線來劃分兩個分組)。 ## 二十一、向量基礎 歡迎閱讀第二十一篇教程,下面就是支持向量機的部分了。這個教程中,我們打算設計一些向量的基礎,它們是支持向量機概念的組成部分。 首先,向量擁有模(大小)和方向: ![](https://img.kancloud.cn/dc/7f/dc7fb3ba01693e25cfc8a61fe4de4fa4_943x516.png) 上面的例子中,向量 A(使用字母上面的箭頭來表示),向`[3, 4]`移動。可以將每個坐標看做該維度上的方向。我們這里,有兩個維度。我們在第一維里面移動 3 個單位,第二維里面移動 4 個。這就是方向了,那么模是什么呢?我們之前看到過它,它就是歐氏距離,范式,或者是大小。對我們來說,最重要的是,它們的計算方式相同(平方和的平方根)。 ![](https://img.kancloud.cn/10/9e/109e6c609f7f800ba0d8a597123caa70_1017x549.png) 我們這里,向量的模是 5。如果你仔細觀察圖片,你可能會注意一些其它東西: ![](https://img.kancloud.cn/ad/8f/ad8fd8b52907620851e2d05f746a53a3_1020x555.png) 看起來像是直角三角形的勾股(帕斯卡)定理。的確是相同的公式,只要我們進入更高的維度,它就不是簡單的三角形了。 很簡單,下面是點積。如果我們對向量計算點積會發生什么呢?假設有兩個向量,A 和 B。A 是`[1, 3]`,B 是`[4, 2]`。我們所做的是,將對應分量相乘再相加。例如: ![](https://img.kancloud.cn/74/4e/744e6d965e80712e027c3928ae30789c_990x453.png) 好的,既然我們知道了這些東西,我們就要講解支持向量機本身了。我們作為科學家,首先會在機器上做一些斷言。 ## 二十二、支持向量斷言 歡迎閱讀機器學習教程的第二十二章。這個教程中,我們會涉及一些 SVM 的斷言。理解這些斷言,它們中一些是約束,是是理解 SVM 的數學與美的一部分。 首先,讓我們看一看 SVM 的示例目標。它的理念是接受已知數據,之后 SVM 的擬合或者訓練是個最優化問題,尋找數據的最佳分隔直線,也就是決策邊界,像這樣: ![](https://img.kancloud.cn/8c/39/8c39aeebb4b8972b887bd958167951c8_718x482.png) 我們在二維空間中,所以分隔超平面(決策邊界)也就是簡單的直線(紅色直線)。決策邊界分隔了藍色減號分組,和綠色加號分組。下面,如果我們在圖中任意位置放一個點,我們就可以做一個簡單的檢查,來看看它位于分隔直線的哪一邊,我們就會有答案了。是不是很簡單?如果我們僅僅停留在二維空間,我們這里的維度是什么呢?每個特征都是一個維度,所有特征組成了我們的特征集。因此,我們可能擁有一條簡單的直線,超級簡單。我們可以使用線性代數來解決它。但如果我們擁有 63 個特征,也就是 63 維呢? 目前為止還不清楚,但是勾股定理多于二維是沒問題的。好的,我們來看看向量空間吧。我們現在在向量空間中了,我們擁有了未知的特征集,記為`v`。之后,我們有了另一個向量(法向量),和決策邊界正交,記為`w`。看起來是: ![](https://img.kancloud.cn/51/e5/51e53941fc248b8cbe3f5254f4e7a2cb_791x498.png) 現在如何呢?我們可以用眼睛看出來,但是如何用數學表達呢?同樣,要記得你需要一個方法,在 2 維和 5902 維都工作。你可以僅僅將向量`v`和`w`點乘,并加上一些偏移`b`(就是超平面的一般式方程),之后觀察這個值大于還是小于 0。 ![](https://img.kancloud.cn/fd/85/fd85750ad9b1348fc46a6678a4b19c38_928x508.png) 好的,盡管我們這里不知道`w`和`b`都是什么。 然后就復雜了。 我們有了兩個未知變量,并且有個壞消息:我們要求解它們。根據優化來說,這應該是個危險信號,也就是有無限個`w`和`b`滿足我們的方程,但是我們也知道有一種約束,已經在我們的腦子里定義了邏輯:我們想要最佳的分隔超平面。我們可以大致猜測這是`w`和`b`優化的一部分。最開始我們打算設置一些真實的數學約束。 目前為止,我們僅僅看到了分隔超平面,但是分隔超平面在兩個超平面之間。所謂的支持向量經過兩個超平面,這些支持向量是特征集(圖上的數據點),如果移動了它們,就會對最佳分隔超平面有影響。 由于這些支持向量會產生重大影響,我們打算為其設置一個常量值。前面說,分類函數是`sign(x·w + b)`,如果它是 0,那就說明在決策邊界上。如果大于零,就是正向分類,如果小于零,就是負向分類。我們打算利用它,并且認為,如果`x·w + b`為 1,就是正向支持向量,如果為 -1,就是負向支持向量。如果一個未知值是 -0.52,仍然是負向分類,即使它沒有超過支持向量的標記 -1。我們簡單使用支持向量來幫助我們選取最佳分隔超平面,這就是它們的作用。我們的斷言是: ![](https://img.kancloud.cn/6b/68/6b687a9a89822129ef3cf34323b9fad1_603x345.png) 也就是說,第一行,我們讓`X`負向支持向量(這是任何為負向支持向量的特征)點乘向量`w`再加`b`等于 -1。我們斷言了這個。之后對正向支持向量:`X`正向支持向量點乘向量`w`再加`b`為正一。同樣,我們剛開始,沒有真正的證明,我們剛剛說這是一個案例。現在,我們打算引入新的值,`Yi`。 ![](https://img.kancloud.cn/a6/94/a694842bc07e454bde0e330b9895beb1_214x265.png) y 在 Python 代碼中是我們的分類,這里也是。 ![](https://img.kancloud.cn/43/32/43323fb7a0874de42d3dec273424d868_787x440.png) 我們打算向之前的斷言引入它,如果你記得正向和負向支持向量的值:`x·w+b=1`是正向,`x·w+b=-1`是負向。我們打算將我們的原始斷言相乘: ![](https://img.kancloud.cn/6b/68/6b687a9a89822129ef3cf34323b9fad1_603x345.png) 根據這個新的`Yi`值,它同樣是 -1 或者 1(取決于分類是 -1 還是 1)。當我們將原始斷言相乘時,我們就需要將兩邊都乘`Yi`,也就是: ![](img/22-8.png) 我們將`Yi`的符號留在左邊,但是我們實際上將其應用到了右邊(1 或者 -1)。這樣意味著對于正向支持向量,我們得到了`1x1=1`,對于負向支持向量,我們得到了`(-1)x(-1)=1`,也等于 1。我們可以將每個方程的右邊設為 0,通過兩邊都減一,我們就有了相同的方程`Yi(Xi·w+b)-1 = 0`。 ![](https://img.kancloud.cn/6b/d1/6bd13cb0026bc4aea9da7e0e38daee8b_1006x518.png) 現在我們擁有了約束,下個教程中我們會深入講解。 ## 二十三、支持向量機基礎 歡迎閱讀第二十三篇教程。這篇教程中,我們打算為支持向量機的優化來解方程。 我們需要計算的支持向量為:`Yi(Xi·w+b)-1 = 0`。 ![](https://img.kancloud.cn/6b/d1/6bd13cb0026bc4aea9da7e0e38daee8b_1006x518.png) 現在我們打算討論一下,我們如何處理這個支持向量機的形式優化問題。特別是,我們如何獲取向量`w`和`b`的最優解。我們也會涉及一些支持向量機的其它基礎。 開始,之前說過超平面的定義為`w·x+b`。因此,我們斷言了該方程中支持向量機的定義,正向類為 1,負向類為 -1。 ![](https://img.kancloud.cn/1c/f9/1cf9bcd00891076cc3af449f09814f25_883x471.png) 我們也推測,一旦我們找到了滿足約束問題(`w`的模最小,`b`最大)的`w`和`b`,我們用于未知點的分類決策函數,只需要簡單計算`x·w+b`。如果值為 0.99 呢?它在圖中是什么樣子? ![](https://img.kancloud.cn/e3/8e/e38e8481b65af4b0040a9b4cf2211136_631x482.png) 所以它并不在正向支持向量平面上,但是很接近了。它超過了決策邊界沒有?是的,決策邊界是`x·w+b=0`。因此,未知數據集的實際決策函數只是`sign(x·w+b)`。就是它了。如果它是正的,就是`+`分類,負的就是`-`分類。現在為了求得這個函數,我們需要`w`和`b`。我們的約束函數,`Yi(Xi·W+b) >= 1`,需要滿足每個數據集。我們如何使其大于等于 1 呢?如果不乘 Yi,就僅僅需要我們的已知數據集,如果代入`x·w+b`大于 1 或者小于 -1,盡管我們之前提到過,0.98 的值也是正向分類。原因就是,新的或者未知的數據可以位于支持向量平面和決策邊界之間,但是訓練集或已知數據不可以。 于是,我們的目標就是最小化`|w|`,最大化`b`,并保持`Yi(X·W+b)>=1`的約束。 ![](https://img.kancloud.cn/20/ec/20ec1f0112ca1d1d2903dc971675b8e1_937x384.png) 要注意,我們嘗試滿足向量`w`的約束,但是我們需要最小化`w`的模,而不是`w`,不要混淆。 有許多方式來計算這個帶約束的最優化。第一個方式就是支持向量機的傳統描述。開始,我們嘗試將分隔超平面之間的寬度最大化。 ![](https://img.kancloud.cn/39/90/399080b9f5b8bc6910df1827538356fd_665x462.png) 下面,向量之間的距離可以記為: ![](https://img.kancloud.cn/32/df/32dfe3d54c592df28d37d2e9cb0229a3_857x246.png) 要注意,這里我們得到了`X+`和`X-`,這是兩個超平面,我們嘗試最大化之間的距離。幸運的是,這里沒有`b`,非常好。那么,`X+`和`X-`又是什么呢?我們知道嗎?是的,我們知道。 ![](https://img.kancloud.cn/34/49/344950055c7ece4cf7823bd2567db781_1008x513.png) 這里就有`b`了。總有一天我們會將其解出來。無論如何,我們做一些代數,將`X+`和`X-`替換為`1-b`和`1+b`。 ![](https://img.kancloud.cn/3a/8f/3a8ff3890ccb9b358646dfc2816b7685_971x491.png) 記得你的操作順序嗎?這非常方便,我們就將`b`移走了,現在我們的方程極大簡化了。 ![](https://img.kancloud.cn/d3/48/d34867e7403c3ef99585bf018cde0ff9_883x463.png) 為了更好地滿足我們未來的要求,我們可以認為,如果我們打算最大化`2/|w|`,我們就可以最小化`|w|`,這個之前已經講過了。由于我們打算最小化`|w|`,相當于最小化`1/2 * |w|^2`: ![](https://img.kancloud.cn/00/fc/00fc802d160595e4b30cbcf19b47e073_618x435.png) 我們的約束是` Yi(Xi·W+b)-1 = 0`。因此,所有特征集的和應該也是 0。所以我們引入了拉格朗日乘數法: ![](https://img.kancloud.cn/b9/30/b930083cf81e43fa3d0c35268f5263fb_1039x432.png) 在這里求導: ![](https://img.kancloud.cn/83/cf/83cfc9f369a34035183dd0a77ae0fb72_1031x492.png) 把所有東西放到一起: ![](https://img.kancloud.cn/da/ef/daefa9ee00c197485cb29e0b08226cc8_932x192.png) 于是,如果你沒有對求出來的東西不滿意,你就到這里了。我們得到了`alpha`的平方,也就是說,我們需要解決一個平方規劃。 很快就變復雜了。 下一篇教程中,我們的興趣是從零編寫 SVM,我們看看是否可以將其簡化。 ## 二十四、約束優化 歡迎閱讀第二十四篇教程。這個教程中,我們打算深入討論 SVM 的約束優化。 上一個教程中,我們剩下了 SVM 的形式約束優化問題: ![](https://img.kancloud.cn/da/ef/daefa9ee00c197485cb29e0b08226cc8_932x192.png) 看起來很丑陋,并且由于`alpha`的平方,我們看到了一個平方規劃問題,這不是很容易完成。約束優化不是一個很大的范圍嗎?有沒有別的方式?你怎么問我會很高興,因為是的,的確存在其他方式。SVM 的優化問題是個凸優化問題,其中凸優化的形狀是`w`的模。 ![](https://img.kancloud.cn/4a/37/4a3703c091a288c9bbf4ac3f2a2d4063_812x484.png) 這個凸優化的目標是尋找`w`的最大模。一種解決凸優化問題的方式就是“下降”,直到你不能再往下走了。一旦你到達了底部,你就能通過其他路徑慢慢回去,重復這個步驟,直到你到達了真正的底部。將凸優化問題看做一個碗,求解過程就是沿著碗的邊緣扔進去一個球。球會很快滾下邊緣,正好達到最中間的位置,之后可能會在另一側上升,但是會再次下降,沿著另一個路徑,可能會重復幾次,每次都會移動得更慢,并且距離更短,最終,球會落在碗的底部。 我們會使用 Python 來模擬這個十分相同的問題。我們會專注于向量`w`,以一個很大的模來開始。之前提到過向量的模就是分量的平方和的平方根。也就是說,向量`w`為`[5,5]`或者`[-5,5]`的模都一樣。但是和特征集的點積有很大不同,并且是完全不同的超平面。出于這個原因,我們需要檢查每個向量的每個變種。 我們的基本思想就是像一個球那樣,快速沿側壁下降,重復知道我們不能再下降了。這個時候,我們需要重復我們的最后幾個步驟。我們以更小的步驟來執行。之后可能將這個步驟重復幾次,例如: ![](img/24-3.png) 首先,我們最開始就像綠色的線,我們用大的步長下降。我們會繞過中心,之后用更小的步長,就像紅色的線。之后我們會像藍色的線。這個方式,我們的步長會越來越小(每一步我們都會計算新的向量`w`和`b`)。這樣,我們就可以獲取最優化的向量`w`,而不需要一開始就使用較大的步長來完成相同結果,并且在處理時浪費很多時間。 如果我們找到了碗或者凸形狀的底部,我們就說我們找到了全局最小值。凸優化問題非常好的原因,就是我們可以使用這個迭代方式來找到底部。如果不是凸優化,我們的形狀就是這樣: ![](https://img.kancloud.cn/b9/ac/b9ac226c915bed51bbe2ad6d77bb8683_708x450.png) 現在,當從左側開始時,你可能檢測到上升了,所以你返回并找到了局部最小值。 ![](https://img.kancloud.cn/5d/ad/5dad9ce51c7e83a35a4f7960607f05ef_853x487.png) 再說一遍,我們在處理一個很好的凸優化問題,所以我們不需要擔心錯誤。我的計劃就是給定一個向量,緩慢減小向量的模(也就是講笑向量中數據的絕對值)。對于每個向量,假設是`[10, 10]`,我們會使用這些東西來變換向量:`[1,1],[-1,1],[-1,-1],[1,-1]`。這會向我們提供這個向量的所有變種,我們需要檢查它們,盡管它們擁有相同的模。這就是下個教程中要做的事情。 ## 二十五、使用 Python 從零開始編寫 SVM 歡迎閱讀第 25 篇教程,下面就是我們的 SVM 部分了。這個教程中,我們打算從零編寫 SVM。 在深入之前,我們會專注于一些選項,用于解決約束優化問題。 首先,約束優化的話題很多,也有很多材料。即使是我們的子話題:凸優化,也是很龐大的。一個不錯的起始點是 <https://web.stanford.edu/~boyd/cvxbook/bv_cvxbook.pdf>。對于約束優化,你可以查看 <http://www.mit.edu/~dimitrib/Constrained-Opt.pdf>。 特別是在 Python 中,[CVXOPT](http://cvxopt.org/) 包擁有多種凸優化方法,其中之一就是我們的平方規劃問題(`cvxopt.solvers.qp`)。 同樣,也有[ libsvm 的 Python 接口](https://github.com/cjlin1/libsvm/tree/master/python),或者[ libsvm 包](https://www.csie.ntu.edu.tw/~cjlin/libsvm/)。我們選擇不要用這些東西,因為 SVM 的最優化問題幾乎就是 SVM 問題的全部了。 現在,為了使用 Python 來開始寫 SVM,我們以這些導入來開始。 ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') ``` 我們使用 Matplotlib 來繪圖,NumPy 來處理數組。下面我們會擁有一些起始數據: ```py data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 現在我們打算開始構建我們的 SVM 類。如果你不熟悉面向對象編程,不要害怕。我們這里的例子是個非常基本的 OOP 形式。只要知道 OOP 創建帶有對象,類中帶有屬性、函數(實際上是方法),以及我們使用`self`變量來代表對象本身。解釋再多也沒有意義,已經足以開始了。如果你對代碼感到疑惑,可以去在線社區提問。 ```py class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) ``` 類的`__init__`方法是使用類創建對象時,執行的方法。其它方法只在調用時執行。對于每個方法,我們傳入`self`作為第一個參數,主要是一種約定。下面,我們添加可視化參數。我們想看看 SVM,所以將其設為`True`。下面米可以看見一些變量,例如`self.color`和`self.visualization`。這樣做能夠讓我們在類的其它方法中,引用`self.color`,最后,如果我們開啟了可視化,我們打算繪制我們的圖像。 下面,讓我們繼續并體感家更多方法:`fit`和`predict`。 ```py class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): pass def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification ``` `fit`方法會用于訓練我們的 SVM。這就是最優化的步驟。一旦我們完成了訓練,`predict`方法會預測新特征集的值,一旦我們知道了`w`和`b`,它就是`sign(x·w+b)`。 目前為止的代碼。 ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): pass def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 下個教程中,我們會繼續并開始處理`fit`方法。 ## 二十六、支持向量機優化 歡迎閱讀第二十六篇教程,下面就是我們的支持向量機章節。這篇教程中,我們打算處理 SVM 的優化方法`fit`。 目前為止的代碼為: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): pass def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 我們開始填充`fit`方法: ```py def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] ``` 要注意這個方法首先傳遞`self`(記住這是方法的約定),之后傳遞`data`。`data`就是我們我們打算訓練或者優化的數據。我們這里,它是`data_dict`,我們已經創建好了。 我們將`self.data`設為該數據。現在,我們可以在類中的任何地方引用這個訓練數據了(但是,我們需要首先使用數據來調用這個訓練方法,來避免錯誤)。 下面,我們開始構建最優化字典`opt_dict`,它包含任何最優化的值。隨著我們減小我們的`w`向量,我們會使用約束函數來測試向量,如果存在的話,尋找最大的滿足方程的`b`,之后將所有數據儲存在我們的最華友字典中。字典是`{ ||w|| : [w,b] }`。當我們完成所有優化時,我們會選擇字典中鍵最小的`w`和`b`值。 最后,我們會設置我們的轉換。我們已經解釋了我們的意圖,來確保我們檢查了每個可能的向量版本。 下面,我們需要一些匹配數據的起始點。為此,我們打算首先引用我們的訓練數據,來選取一些合適的起始值。 ```py # finding values to work with for our ranges. all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) # no need to keep this memory. all_data=None ``` 我們所做的就是遍歷所有數據,尋找最大值和最小值。現在我們打算定義我們的步長。 ```py step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # starts getting very high cost after this. self.max_feature_value * 0.001] ``` 這里我們設置了一些大小的步長,我們打算這樣執行。對于我們的第一遍,我們會采取大跨步(10%)。一旦我們使用這些步長找到了最小值,我們就將步長降至 1% 來調優。我們會繼續下降,取決于你想要多么精確。我會在這個項目的末尾討論,如何在程序中判斷是否應該繼續優化。 下面,我們打算設置一些變量,來幫助我們給`b`生成步長(用于生成比`w`更大的步長,因為我們更在意`w`的精確度),并跟蹤最后一個最優值。 ```py # extremely expensive b_range_multiple = 5 b_multiple = 5 latest_optimum = self.max_feature_value*10 ``` 現在我們開始了: ```py for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: pass ``` 這里的思想就是沿著向量下降。開始,我們將`optimized`設為`False`,并為我們會在每個主要步驟重置它。`optimized`變量再我們檢查所有步驟和凸形狀(我們的碗)的底部之后,會設為`True`。 我們下個教程中會繼續實現這個邏輯,那里我們會實際使用約束問題來檢查值,檢查我們是否找到了可以保存的值。 目前為止的代碼: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001,] # extremely expensive b_range_multiple = 5 # b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: pass def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` ## 二十七、支持向量機優化 第二部分 歡迎閱讀第二十七篇教程,下面就是支持向量機的部分。這個教程中,我們打算繼續使用 Python 代碼處理 SVM 優化問題。 在我們停止的地方,我們的代碼為: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001,] # extremely expensive b_range_multiple = 5 # b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: pass def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 選取`while not optimized `部分: ```py optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): ``` 這里我們開始迭代所有可能的`b`值,并且現在可以看到,之前設置的`b`值。這里要注意,我們使用一個固定的步長,直接迭代`b`。我們也可以拆分`b`的步長,就像我們對`w`所做的那樣。為了使事情更加準確,你可能打算這樣實現。也就是說,出于簡潔,我打算跳過這個部分,因為我們要完成近似的結果。而不是嘗試獲得什么獎項。 繼續: ```py optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): for transformation in transforms: w_t = w*transformation found_option = True # weakest link in the SVM fundamentally # SMO attempts to fix this a bit # yi(xi.w+b) >= 1 # # #### add a break here later.. for i in self.data: for xi in self.data[i]: yi=i if not yi*(np.dot(w_t,xi)+b) >= 1: found_option = False if found_option: opt_dict[np.linalg.norm(w_t)] = [w_t,b] ``` 現在我們迭代了每個變形,對我們的約束條件測試了每個東西。如果我們數據集中的任何特征集不滿足我們的約束,我們就會去掉這個變量,因為它不匹配,并繼續。我建議在這里停頓一下。如果僅僅是一個變量不工作,你可能要放棄其余部分,因為一個變量不匹配,就足以扔掉`w`和`b`了。你應該在這里停頓,并且處理循環。現在,我們會將代碼保持原樣,但是我在錄制視頻的時候,會有所修改。 現在我們完成`fit`方法,我會貼出完整代碼并解釋差異: ```py def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None # support vectors yi(xi.w+b) = 1 step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001,] # extremely expensive b_range_multiple = 5 # we dont need to take as small of steps # with b as we do w b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): for transformation in transforms: w_t = w*transformation found_option = True # weakest link in the SVM fundamentally # SMO attempts to fix this a bit # yi(xi.w+b) >= 1 # # #### add a break here later.. for i in self.data: for xi in self.data[i]: yi=i if not yi*(np.dot(w_t,xi)+b) >= 1: found_option = False if found_option: opt_dict[np.linalg.norm(w_t)] = [w_t,b] if w[0] < 0: optimized = True print('Optimized a step.') else: w = w - step norms = sorted([n for n in opt_dict]) #||w|| : [w,b] opt_choice = opt_dict[norms[0]] self.w = opt_choice[0] self.b = opt_choice[1] latest_optimum = opt_choice[0][0]+step*2 ``` 一旦我們越過了`w`向量的零點,就沒有理由繼續了,因為我們通過變換測試了負值。所以我們已經完成了這個步長,要么繼續下一個步長,要么就完全完成了。如果沒有經過 0,那就向下走一步。一旦我們走完了能走的所有步驟,我們就對`opt_dict `字典的鍵數組記性排序(它包含`||w|| : [w,b]`)。我們想要向量`w`的最小模,所以我們選取列表的第一個元素。我們給這里的`self.w`和`self.b`賦值,并設置最后的優化值。之后,我們選取另一個步長,或者完全完成了整個過程(如果沒有更多的步長可選取了)。 這里,完整代碼是: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None # support vectors yi(xi.w+b) = 1 step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001,] # extremely expensive b_range_multiple = 5 # we dont need to take as small of steps # with b as we do w b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): for transformation in transforms: w_t = w*transformation found_option = True # weakest link in the SVM fundamentally # SMO attempts to fix this a bit # yi(xi.w+b) >= 1 # # #### add a break here later.. for i in self.data: for xi in self.data[i]: yi=i if not yi*(np.dot(w_t,xi)+b) >= 1: found_option = False if found_option: opt_dict[np.linalg.norm(w_t)] = [w_t,b] if w[0] < 0: optimized = True print('Optimized a step.') else: w = w - step norms = sorted([n for n in opt_dict]) #||w|| : [w,b] opt_choice = opt_dict[norms[0]] self.w = opt_choice[0] self.b = opt_choice[1] latest_optimum = opt_choice[0][0]+step*2 def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 現在我們已經準備好可視化以及測試支持向量機的預測了。我們會在下一個教程中完成它們。 ## 二十八、使用我們的 SVM 來可視化和預測 歡迎閱讀第二十八篇教程。這個教程中,我們完成我們從零開始的基本 SVM,并使用它來可視化并作出預測。 我們目前為止的代碼: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None # support vectors yi(xi.w+b) = 1 step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001,] # extremely expensive b_range_multiple = 5 # we dont need to take as small of steps # with b as we do w b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): for transformation in transforms: w_t = w*transformation found_option = True # weakest link in the SVM fundamentally # SMO attempts to fix this a bit # yi(xi.w+b) >= 1 # # #### add a break here later.. for i in self.data: for xi in self.data[i]: yi=i if not yi*(np.dot(w_t,xi)+b) >= 1: found_option = False if found_option: opt_dict[np.linalg.norm(w_t)] = [w_t,b] if w[0] < 0: optimized = True print('Optimized a step.') else: w = w - step norms = sorted([n for n in opt_dict]) #||w|| : [w,b] opt_choice = opt_dict[norms[0]] self.w = opt_choice[0] self.b = opt_choice[1] latest_optimum = opt_choice[0][0]+step*2 def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) return classification data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} ``` 我們已經擁有預測方法了,因為這非常簡單。但是現在我們打算添加一些,來處理預測的可視化。 ```py def predict(self,features): # classifiction is just: # sign(xi.w+b) classification = np.sign(np.dot(np.array(features),self.w)+self.b) # if the classification isn't zero, and we have visualization on, we graph if classification != 0 and self.visualization: self.ax.scatter(features[0],features[1],s=200,marker='*', c=self.colors[classification]) else: print('featureset',features,'is on the decision boundary') return classification ``` 上面,我們添加了代碼來可視化預測,如果存在的話。我們打算一次做一個,但是你可以擴展代碼來一次做許多個,就像 Sklearn 那樣。 下面,讓我們構建`visualize`方法: ```py def visualize(self): #scattering known featuresets. [[self.ax.scatter(x[0],x[1],s=100,color=self.colors[i]) for x in data_dict[i]] for i in data_dict] ``` 這一行所做的就是,遍歷我們的數據,并繪制它和它的相應顏色。 下面,我們打算繪制正向和負向支持向量的超平面,以及決策邊界。為此,我們至少需要兩個點,來創建“直線”,它就是我們的超平面。 一旦我們知道了`w`和`b`,我們就可以使用代數來創建函數,它對`x`值返回`y`值來生成直線: ```py def hyperplane(x,w,b,v): # w[0] * x + w[1] * y + b = v # 正向支持超平面 v = 1 # 最佳分隔超平面 v = 0 # 負向支持超平面 v = -1 # y = (v - b - w[0] * x) / w[1] return (-w[0]*x-b+v) / w[1] ``` 然后,我們創建一些變量,來存放我們打算引用的多種數據: ```py datarange = (self.min_feature_value*0.9,self.max_feature_value*1.1) hyp_x_min = datarange[0] hyp_x_max = datarange[1] ``` 我們的主要目標就是弄清楚為了繪制我們的超平面,我們需要什么值。 現在,讓我們繪制正向支持向量超平面。 ```py # w.x + b = 1 # pos sv hyperplane psv1 = hyperplane(hyp_x_min, self.w, self.b, 1) psv2 = hyperplane(hyp_x_max, self.w, self.b, 1) self.ax.plot([hyp_x_min,hyp_x_max], [psv1,psv2], "k") ``` 非常簡單,我們獲得了`x_min`和`x_max`的`y`值,然后我們繪制了它們。 ··· # w.x + b = -1 # negative sv hyperplane nsv1 = hyperplane(hyp_x_min, self.w, self.b, -1) nsv2 = hyperplane(hyp_x_max, self.w, self.b, -1) self.ax.plot([hyp_x_min,hyp_x_max], [nsv1,nsv2], "k") # w.x + b = 0 # decision db1 = hyperplane(hyp_x_min, self.w, self.b, 0) db2 = hyperplane(hyp_x_max, self.w, self.b, 0) self.ax.plot([hyp_x_min,hyp_x_max], [db1,db2], "g--") plt.show() ``` 現在,在底部添加一些代碼來訓練、預測和可視化: ```py import matplotlib.pyplot as plt from matplotlib import style import numpy as np style.use('ggplot') class Support_Vector_Machine: def __init__(self, visualization=True): self.visualization = visualization self.colors = {1:'r',-1:'b'} if self.visualization: self.fig = plt.figure() self.ax = self.fig.add_subplot(1,1,1) # train def fit(self, data): self.data = data # { ||w||: [w,b] } opt_dict = {} transforms = [[1,1], [-1,1], [-1,-1], [1,-1]] all_data = [] for yi in self.data: for featureset in self.data[yi]: for feature in featureset: all_data.append(feature) self.max_feature_value = max(all_data) self.min_feature_value = min(all_data) all_data = None # support vectors yi(xi.w+b) = 1 step_sizes = [self.max_feature_value * 0.1, self.max_feature_value * 0.01, # point of expense: self.max_feature_value * 0.001, ] # extremely expensive b_range_multiple = 2 # we dont need to take as small of steps # with b as we do w b_multiple = 5 latest_optimum = self.max_feature_value*10 for step in step_sizes: w = np.array([latest_optimum,latest_optimum]) # we can do this because convex optimized = False while not optimized: for b in np.arange(-1*(self.max_feature_value*b_range_multiple), self.max_feature_value*b_range_multiple, step*b_multiple): for transformation in transforms: w_t = w*transformation found_option = True # weakest link in the SVM fundamentally # SMO attempts to fix this a bit # yi(xi.w+b) >= 1 # # #### add a break here later.. for i in self.data: for xi in self.data[i]: yi=i if not yi*(np.dot(w_t,xi)+b) >= 1: found_option = False #print(xi,':',yi*(np.dot(w_t,xi)+b)) if found_option: opt_dict[np.linalg.norm(w_t)] = [w_t,b] if w[0] < 0: optimized = True print('Optimized a step.') else: w = w - step norms = sorted([n for n in opt_dict]) #||w|| : [w,b] opt_choice = opt_dict[norms[0]] self.w = opt_choice[0] self.b = opt_choice[1] latest_optimum = opt_choice[0][0]+step*2 for i in self.data: for xi in self.data[i]: yi=i print(xi,':',yi*(np.dot(self.w,xi)+self.b)) def predict(self,features): # sign( x.w+b ) classification = np.sign(np.dot(np.array(features),self.w)+self.b) if classification !=0 and self.visualization: self.ax.scatter(features[0], features[1], s=200, marker='*', c=self.colors[classification]) return classification def visualize(self): [[self.ax.scatter(x[0],x[1],s=100,color=self.colors[i]) for x in data_dict[i]] for i in data_dict] # hyperplane = x.w+b # v = x.w+b # psv = 1 # nsv = -1 # dec = 0 def hyperplane(x,w,b,v): return (-w[0]*x-b+v) / w[1] datarange = (self.min_feature_value*0.9,self.max_feature_value*1.1) hyp_x_min = datarange[0] hyp_x_max = datarange[1] # (w.x+b) = 1 # positive support vector hyperplane psv1 = hyperplane(hyp_x_min, self.w, self.b, 1) psv2 = hyperplane(hyp_x_max, self.w, self.b, 1) self.ax.plot([hyp_x_min,hyp_x_max],[psv1,psv2], 'k') # (w.x+b) = -1 # negative support vector hyperplane nsv1 = hyperplane(hyp_x_min, self.w, self.b, -1) nsv2 = hyperplane(hyp_x_max, self.w, self.b, -1) self.ax.plot([hyp_x_min,hyp_x_max],[nsv1,nsv2], 'k') # (w.x+b) = 0 # positive support vector hyperplane db1 = hyperplane(hyp_x_min, self.w, self.b, 0) db2 = hyperplane(hyp_x_max, self.w, self.b, 0) self.ax.plot([hyp_x_min,hyp_x_max],[db1,db2], 'y--') plt.show() data_dict = {-1:np.array([[1,7], [2,8], [3,8],]), 1:np.array([[5,1], [6,-1], [7,3],])} svm = Support_Vector_Machine() svm.fit(data=data_dict) predict_us = [[0,10], [1,3], [3,4], [3,5], [5,5], [5,6], [6,-5], [5,8]] for p in predict_us: svm.predict(p) svm.visualize() ``` 我們的結果: ![](https://img.kancloud.cn/c3/5a/c35adfd0776cec633c2957b2a39c1176_723x408.png) ## 二十九、核的簡介 歡迎閱讀第二十九篇教程。這個教程中,我們打算使用機器學習談論核的概念。 回憶一開始的 SVM 話題,我們的問題是,你可不可以使用 SVM 來處理這樣的數據: ![](https://img.kancloud.cn/3e/7a/3e7acf6a4d8c6c5674216b8a6a33e4f2_697x516.png) 至少我們現在為止,它可能嗎?不,完全不可能,至少不能是這樣。但是一個選擇,就是采取新的視角。我們可以通過添加一個新的維度來實現。例如上面的數據中,我們可以添加第三個維度,使用一些函數,比如`X3 = X1*X2`。在這里可能管用,但是也可以不管用。同樣,一些案例,比如圖像分析又如何呢?其中你可能有多于幾百和維度。它就是性能很重要的場景,并且你是否應該添加一個維度到已經有很多維度的數據中,我們會進一步把事情變慢。 如果我告訴你,你可以在無限的維度上做計算,或者,你可以讓那些計算在這些維度上實現,而不需要在這些維度上工作,并且仍然能得到結果呢? 就是這樣。我們實際上使用叫做核的東西來實現。相對于 SVM 來說,許多人一開始就接觸它了,也可能最后才接觸。這可能會讓你認為,核主要用于 SVM,但是并不是這樣。 核就是相似度函數,它接受兩個輸出,并使用內積來返回相似度。由于這是個機器學習教程,你們中的一些可能想知道,為什么人們不將核用于機器學習算,以及,我在這里告訴你它們實際上使用了。你不僅僅可以使用核來創建自己的機器學習算法,你可以將現有的機器學習算法翻譯為使用核的版本。 核所做的就是允許你,處理許多維度,而不需要花費處理的開銷。核的確有個要求:它們依賴于內核。對于這篇教程的目的,“點積”和“內積”可以互相代替。 為了驗證我們是否可以使用核,我們需要做的,就是驗證我們的特征空間的每個交互,都是內積。我們會從末尾開始,然后返回來確認它。 首先,我們如何在訓練之后判斷特征的分類呢? ![](https://img.kancloud.cn/4c/c5/4cc54114a2401c0cadee5d640ee6afbd_603x170.png) 它是不是內積的交互呢?當然是,我們可以將`x`換成`z`。 ![](https://img.kancloud.cn/bc/88/bc88cfeaa958932d401740c9a527f786_708x407.png) 繼續,我們打算回顧我們的約束,約束方程為: ![](https://img.kancloud.cn/1e/a5/1ea526e150b5ac3e3c4068b7835b80b0_894x329.png) 這里如何呢?這個交互式內積嘛?當然,` yi(xi.w+b)-1 >= 0`等價于`yi(xi.w+b) >= 1`。所以這里我們可以講義將`x_i`替換為`z_i`。 最后,我們的形式優化方程`w`如何呢? ![](https://img.kancloud.cn/8e/90/8e904ed6a1d8ae601c77df31bd825f39_537x258.png) 它是另一個點積或內積。有任何問題嗎?這樣: ![](https://img.kancloud.cn/a0/9c/a09c1b5cf986d5f00bba450c8bfea186_840x330.png) 太好了。我們可以使用核。你可能想知道,這個“零開銷來計算無窮維度”是什么?好吧,首先我們需要確保我們能這樣做。對于零開銷的處理,你需要看下一篇教程來了解。 ## 三十、為什么是核 歡迎閱讀第三十篇教程。這篇教程中,我們打算繼續討論核,既然我們知道了我們能使用它之后,主要弄清楚如何實際使用它們。 我們之前了解到,我們可以利用核來幫助我們將數據轉換為無窮數量的維度,以便找到線性分隔。我們也了解到,核可以讓我們處理這些維度,而不需要實際為這些高維度花費開銷。通常,核定義為這樣: ![](https://img.kancloud.cn/83/31/8331fb792f8e4991d29739fba9906b28_362x190.png) 核函數應用于`x`和`x'`,并等于`z`和`z'`的內積,其中`z`就是`z`維度的值(我們新的維度空間)。 ![](https://img.kancloud.cn/05/a3/05a386a6792814a2475b818bd4242c66_708x239.png) `z`值就是一些`function(x)`的結果,這些`z`值點乘在一起就是我們核函數的結果。 ![](https://img.kancloud.cn/bf/31/bf31d4209a0a0a2faf0bef6c169c22ea_835x465.png) 我們仍然需要涉及,它如何節省我們的處理步驟。所以看一個例子吧。我們以多項式來開始,并將多項式核的要求,與簡單使用我們的向量來創建二階多項式來比較: ![](https://img.kancloud.cn/e2/72/e272da030c0694c2184ff76e3923ba17_956x181.png) 核對`x`和`x'`使用相同函數,所以我們對`z'`也使用相同的東西(`x'`對二階多項式)。這里,最終步驟就是計算二者的點積。 ![](https://img.kancloud.cn/d7/8e/d78e8c08956d9ad669de290d6be18dcc_1029x545.png) 所以所有工作就是手動執行一個和核函數類似的操作。幸運的是,我們的起始維度只有兩維。現在讓我們考慮多項式核: ![](https://img.kancloud.cn/92/2c/922ca588a3566decaacc9f54ca4301d9_778x213.png) 要注意,這里沒有提到`z`。整個核僅僅使用`x`來計算。你所需的所有東西,就是使用維度數量`n`和你想使用的權重`p`來計算。你的方程是這樣: ![](https://img.kancloud.cn/63/46/6346a5768613cf25a4f64b6fbaa36161_756x387.png) 如果你計算了出來,你的新向量是這樣,它對應`z`空間的向量: ![](https://img.kancloud.cn/a5/fb/a5fb44ffae8f80bf27b37f6bba3e8611_885x160.png) 也就是說,你永遠不需要繼續深入了。你只需要專注于多項式和,它簡單返回點積給你,你不需要實際計算向量,之后計算非常大的點積。 也有一些預先創建的核,但是我這里僅僅會展示徑向基函數(RBF)核。只是因為它通常是默認使用的核,并且可以將我們帶到無限的維度中。 ![](https://img.kancloud.cn/0c/ee/0ceea641d00cd771555218f503cef74e_879x332.png) 這里的 Gamma 值是后面教程的話題。所以這里以擁有了核,了解了為什么使用它們,如何使用它們,以及它們如何讓你處理更大的維度,而不需要花費非常大的處理開銷。下一篇教程中,我們打算討論另一個非線性數據的解決方案,以及數據的過擬合問題。 ## 三十一、軟邊界 SVM 歡迎閱讀第 31 個部分。這篇教程中,我們打算討論軟邊界 SVM。 首先,為什么軟邊界分類器更加優秀,主要有兩個原因。一是你的數據可能不是完全線性分隔的,但是很接近了,并且繼續使用默認的線性核有更大意義。另一個原因是,即使你使用了某個核,如果你打算使用硬邊界的話,你最后也會過擬合。例如,考慮這個: ![](https://img.kancloud.cn/b9/84/b984f099d0ed481a5c97b9b82ce4c3d4_873x494.png) 這里是一個數據案例,當前并不是線性可分的。假設使用硬邊界(也就是我們之前看到的那種),我們可能使用核來生成這樣的決策邊界: ![](https://img.kancloud.cn/b2/a9/b2a9b8d6ced78ff5032fc7aab98f731c_863x493.png) 下面,注意我的繪圖工具中的缺陷,讓我們繪制支持向量平面,并圈出支持向量: ![](https://img.kancloud.cn/26/47/26475c946dea644efb18d5525f3fd84b_876x497.png) 這里,每個正向的數據樣例都是支持向量,只有兩個負向分類不是支持向量。這個信號就是可能過擬合了,我們應該避免它。因為,當我們用它來預測未來的點時,我們就沒有余地了,并且可能會錯誤分類新的數據。如果我們這樣做,會怎么樣呢? ![](https://img.kancloud.cn/78/38/78388f087805619e5e59eac9926ac12d_928x524.png) 我們有一些錯誤或者誤差,由箭頭標記,但是這個可能能夠更好地為將來的數據集分類。我們這里就擁有了“軟邊界”分類器,它允許一些誤差上的“彈性”,我們可以在優化過程中獲得它。 ![](https://img.kancloud.cn/b4/45/b445080bf2b8076a0df362f9b5f3c28d_997x413.png) 我們的新的優化就是上面的計算,其中彈性大于等于 0。彈性越接近 0,就越接近“硬邊界”。彈性越高,邊界就越軟。如果彈性是 0,我們就得到了一個典型的硬邊界分類器。但是你可能能夠菜刀,我們希望最小化彈性。為此,我們將其添加到向量`w`的模的最小值中。 ![](https://img.kancloud.cn/78/90/7890f7499c30fac6535407add2641ba5_957x340.png) 因此,我們實際上打算最小化`1/2||w||^2 + C * 所有使用的彈性之和`。使用它,我們引入了另一個變量`C`。`C`是個系數,關于我們打算讓彈性對方程的剩余部分有多少影響。`C`閱讀,彈性的和與向量`w`的模相比,就越不重要,反之亦然。多數情況下,`C`的值默認為 1。 所以這里你了解了軟邊界 SVM,以及為什么打算使用它。下面,我們打算展示一些樣例代碼,它們由軟邊界、核和 CVXOPT 組成。 ## 三十二、核、軟邊界和使用 Python 和 CVXOPT 的平方規劃 歡迎閱讀第三十二篇機器學習教程。這篇教程中,我們打算展示核、軟邊界的 Python 版本,并使用 CVXOPT 來解決平方規劃問題。 在這個簡短的章節中,我打算主要向你分享其它資源,你應該想要使用 Python 和 CVXOPT 深入研究 SVM 或者平方規劃。為了開始,你可以閱讀[ CVXOPT 平方規劃文檔](https://cvxopt.org/userguide/coneprog.html#quadratic-programming),來深入了解 Python 中的平方規劃。你也可以查看[ CVXOPT 平方規劃示例](https://cvxopt.org/examples/tutorial/qp.html)。 對于 CVXOPT 的更加深入的平方規劃示例,請查看[這個 PDF](https://courses.csail.mit.edu/6.867/wiki/images/a/a7/Qp-cvxopt.pdf)。 最后,我們打算看一看來自[ Mathieu Blondel 的博客](http://www.mblondel.org/journal/2010/09/19/support-vector-machines-in-python/)的一些代碼,它由核、軟邊界 SVM 以及 CVXOPT 平方規劃組成。所有代碼都優于我寫的任何東西。 ```py # Mathieu Blondel, September 2010 # License: BSD 3 clause # http://www.mblondel.org/journal/2010/09/19/support-vector-machines-in-python/ # visualizing what translating to another dimension does # and bringing back to 2D: # https://www.youtube.com/watch?v=3liCbRZPrZA # Docs: http://cvxopt.org/userguide/coneprog.html#quadratic-programming # Docs qp example: http://cvxopt.org/examples/tutorial/qp.html # Nice tutorial: # https://courses.csail.mit.edu/6.867/wiki/images/a/a7/Qp-cvxopt.pdf import numpy as np from numpy import linalg import cvxopt import cvxopt.solvers def linear_kernel(x1, x2): return np.dot(x1, x2) def polynomial_kernel(x, y, p=3): return (1 + np.dot(x, y)) ** p def gaussian_kernel(x, y, sigma=5.0): return np.exp(-linalg.norm(x-y)**2 / (2 * (sigma ** 2))) class SVM(object): def __init__(self, kernel=linear_kernel, C=None): self.kernel = kernel self.C = C if self.C is not None: self.C = float(self.C) def fit(self, X, y): n_samples, n_features = X.shape # Gram matrix K = np.zeros((n_samples, n_samples)) for i in range(n_samples): for j in range(n_samples): K[i,j] = self.kernel(X[i], X[j]) P = cvxopt.matrix(np.outer(y,y) * K) q = cvxopt.matrix(np.ones(n_samples) * -1) A = cvxopt.matrix(y, (1,n_samples)) b = cvxopt.matrix(0.0) if self.C is None: G = cvxopt.matrix(np.diag(np.ones(n_samples) * -1)) h = cvxopt.matrix(np.zeros(n_samples)) else: tmp1 = np.diag(np.ones(n_samples) * -1) tmp2 = np.identity(n_samples) G = cvxopt.matrix(np.vstack((tmp1, tmp2))) tmp1 = np.zeros(n_samples) tmp2 = np.ones(n_samples) * self.C h = cvxopt.matrix(np.hstack((tmp1, tmp2))) # solve QP problem solution = cvxopt.solvers.qp(P, q, G, h, A, b) # Lagrange multipliers a = np.ravel(solution['x']) # Support vectors have non zero lagrange multipliers sv = a > 1e-5 ind = np.arange(len(a))[sv] self.a = a[sv] self.sv = X[sv] self.sv_y = y[sv] print("%d support vectors out of %d points" % (len(self.a), n_samples)) # Intercept self.b = 0 for n in range(len(self.a)): self.b += self.sv_y[n] self.b -= np.sum(self.a * self.sv_y * K[ind[n],sv]) self.b /= len(self.a) # Weight vector if self.kernel == linear_kernel: self.w = np.zeros(n_features) for n in range(len(self.a)): self.w += self.a[n] * self.sv_y[n] * self.sv[n] else: self.w = None def project(self, X): if self.w is not None: return np.dot(X, self.w) + self.b else: y_predict = np.zeros(len(X)) for i in range(len(X)): s = 0 for a, sv_y, sv in zip(self.a, self.sv_y, self.sv): s += a * sv_y * self.kernel(X[i], sv) y_predict[i] = s return y_predict + self.b def predict(self, X): return np.sign(self.project(X)) if __name__ == "__main__": import pylab as pl def gen_lin_separable_data(): # generate training data in the 2-d case mean1 = np.array([0, 2]) mean2 = np.array([2, 0]) cov = np.array([[0.8, 0.6], [0.6, 0.8]]) X1 = np.random.multivariate_normal(mean1, cov, 100) y1 = np.ones(len(X1)) X2 = np.random.multivariate_normal(mean2, cov, 100) y2 = np.ones(len(X2)) * -1 return X1, y1, X2, y2 def gen_non_lin_separable_data(): mean1 = [-1, 2] mean2 = [1, -1] mean3 = [4, -4] mean4 = [-4, 4] cov = [[1.0,0.8], [0.8, 1.0]] X1 = np.random.multivariate_normal(mean1, cov, 50) X1 = np.vstack((X1, np.random.multivariate_normal(mean3, cov, 50))) y1 = np.ones(len(X1)) X2 = np.random.multivariate_normal(mean2, cov, 50) X2 = np.vstack((X2, np.random.multivariate_normal(mean4, cov, 50))) y2 = np.ones(len(X2)) * -1 return X1, y1, X2, y2 def gen_lin_separable_overlap_data(): # generate training data in the 2-d case mean1 = np.array([0, 2]) mean2 = np.array([2, 0]) cov = np.array([[1.5, 1.0], [1.0, 1.5]]) X1 = np.random.multivariate_normal(mean1, cov, 100) y1 = np.ones(len(X1)) X2 = np.random.multivariate_normal(mean2, cov, 100) y2 = np.ones(len(X2)) * -1 return X1, y1, X2, y2 def split_train(X1, y1, X2, y2): X1_train = X1[:90] y1_train = y1[:90] X2_train = X2[:90] y2_train = y2[:90] X_train = np.vstack((X1_train, X2_train)) y_train = np.hstack((y1_train, y2_train)) return X_train, y_train def split_test(X1, y1, X2, y2): X1_test = X1[90:] y1_test = y1[90:] X2_test = X2[90:] y2_test = y2[90:] X_test = np.vstack((X1_test, X2_test)) y_test = np.hstack((y1_test, y2_test)) return X_test, y_test def plot_margin(X1_train, X2_train, clf): def f(x, w, b, c=0): # given x, return y such that [x,y] in on the line # w.x + b = c return (-w[0] * x - b + c) / w[1] pl.plot(X1_train[:,0], X1_train[:,1], "ro") pl.plot(X2_train[:,0], X2_train[:,1], "bo") pl.scatter(clf.sv[:,0], clf.sv[:,1], s=100, c="g") # w.x + b = 0 a0 = -4; a1 = f(a0, clf.w, clf.b) b0 = 4; b1 = f(b0, clf.w, clf.b) pl.plot([a0,b0], [a1,b1], "k") # w.x + b = 1 a0 = -4; a1 = f(a0, clf.w, clf.b, 1) b0 = 4; b1 = f(b0, clf.w, clf.b, 1) pl.plot([a0,b0], [a1,b1], "k--") # w.x + b = -1 a0 = -4; a1 = f(a0, clf.w, clf.b, -1) b0 = 4; b1 = f(b0, clf.w, clf.b, -1) pl.plot([a0,b0], [a1,b1], "k--") pl.axis("tight") pl.show() def plot_contour(X1_train, X2_train, clf): pl.plot(X1_train[:,0], X1_train[:,1], "ro") pl.plot(X2_train[:,0], X2_train[:,1], "bo") pl.scatter(clf.sv[:,0], clf.sv[:,1], s=100, c="g") X1, X2 = np.meshgrid(np.linspace(-6,6,50), np.linspace(-6,6,50)) X = np.array([[x1, x2] for x1, x2 in zip(np.ravel(X1), np.ravel(X2))]) Z = clf.project(X).reshape(X1.shape) pl.contour(X1, X2, Z, [0.0], colors='k', linewidths=1, origin='lower') pl.contour(X1, X2, Z + 1, [0.0], colors='grey', linewidths=1, origin='lower') pl.contour(X1, X2, Z - 1, [0.0], colors='grey', linewidths=1, origin='lower') pl.axis("tight") pl.show() def test_linear(): X1, y1, X2, y2 = gen_lin_separable_data() X_train, y_train = split_train(X1, y1, X2, y2) X_test, y_test = split_test(X1, y1, X2, y2) clf = SVM() clf.fit(X_train, y_train) y_predict = clf.predict(X_test) correct = np.sum(y_predict == y_test) print("%d out of %d predictions correct" % (correct, len(y_predict))) plot_margin(X_train[y_train==1], X_train[y_train==-1], clf) def test_non_linear(): X1, y1, X2, y2 = gen_non_lin_separable_data() X_train, y_train = split_train(X1, y1, X2, y2) X_test, y_test = split_test(X1, y1, X2, y2) clf = SVM(polynomial_kernel) clf.fit(X_train, y_train) y_predict = clf.predict(X_test) correct = np.sum(y_predict == y_test) print("%d out of %d predictions correct" % (correct, len(y_predict))) plot_contour(X_train[y_train==1], X_train[y_train==-1], clf) def test_soft(): X1, y1, X2, y2 = gen_lin_separable_overlap_data() X_train, y_train = split_train(X1, y1, X2, y2) X_test, y_test = split_test(X1, y1, X2, y2) clf = SVM(C=1000.1) clf.fit(X_train, y_train) y_predict = clf.predict(X_test) correct = np.sum(y_predict == y_test) print("%d out of %d predictions correct" % (correct, len(y_predict))) plot_contour(X_train[y_train==1], X_train[y_train==-1], clf) #test_linear() #test_non_linear() test_soft() ``` 如果你想要讓我執行這個代碼,你可以查看[這個視頻](https://www.youtube.com/embed/XdcfJX-mDG4?list=PLQVvvaa0QuDfKTOs3Keq_kaG2P55YRn5v)。我會僅僅提及,你可能不需要使用 CVXOPT。多數人用于 SVM 的庫是[ LibSVM](https://www.csie.ntu.edu.tw/~cjlin/libsvm/)。 大家都說,這個代碼可以讓你理解內部的工作原理,并不是為了讓你實際創建一個健壯的 SVM,超過你可以自由使用的那個。 下一篇教程中,我們打算再討論一個 SVM 的概念,它就是當你擁有多于兩個分組時,你該怎么做。我們也會在總結中,瀏覽 Sklearn 的 SVM 的所有參數,因為我們很少涉及這個話題。 # 第三十三章 支持向量機的參數 > 原文:[Support Vector Machine Parameters](https://pythonprogramming.net/support-vector-machine-parameters-machine-learning-tutorial/) > 譯者:[飛龍](https://github.com/wizardforcel) > 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 歡迎閱讀第三十三篇教程,這篇教程中,我們打算通過解釋如何處理多于 2 個分類,以及盧蘭 Sklearn 的 SVM 的參數,來對 SVM 做個收尾,并且讓你見識一下用于 SVM 的現代方法論。 首先,你已經學到了,SVM 是個二元分類器。也就是說,任何時候,SVM 的最優化都只能將一個分組與另一個分組分離。之后問題是我們如何對三個或更多分組分類。通常,方法就是“一對其它”(OVR)。這里的理念就是,將每個分組從其余的分組分離。例如,為了分類三個分組(1,2 和 3),你應該首先將 1 從 2 和 3 分離。之后將 2 從 1 和 3。最后將 3 從 1 和 2 分離。這樣有一些問題,因為類似置信度的東西,可能對于每個分類邊界都不同,以及分隔邊界可能有一些缺陷,因為有一些不僅僅是正向和負向的東西,你將一個分組與其它三個比較。假設最開始有一個均衡的數據集,也就是說每個分類的邊界可能是不均衡的。 ![](https://img.kancloud.cn/5f/e8/5fe855bde39ca477431ce8da0e3a8e1d_1004x508.png) 另一個方法是“一對一”(OVO)。這個情況下,考慮你總共擁有三個分組。它的工作方式是,你的邊界從 1 分離 3,以及從 1 分離 2,并且對其余分類重復這個過程。這樣,邊界就會更均衡。 ![](https://img.kancloud.cn/19/78/197864493c4b0c1684cb9955e2f261b8_981x534.png) 第一個參數是`C`。它告訴你這是一個軟邊界分類器。你可以按需調整`C`,并且可以使`C`足夠高來創建硬邊界分類器。`C`是`||w||`的軟邊界優化函數。 ![](https://img.kancloud.cn/78/90/7890f7499c30fac6535407add2641ba5_957x340.png) `C`的默認值是 1,并且多數情況下都很好。 下面我們有個`kernel`的選項。這里默認是`rbf`核,但是你可以調整為`linear`,`poly`(多項式)和`sigmoid`核,甚至你選擇或設計的自定義核。 然后,還有`degree`值,默認為 3,這個是多項式的階數,如果你將`poly`用于`kernel`參數的話。 `gamma`是你為`rbf`核設置 Gamma 值的地方。你應該將其保留為`auto`。 `coef0`允許你調整核函數的獨立項,但是你應該保留不變,并且它只用于多項式和 sigmoid 核。 `probability `參數項可能對你很使用。回憶 KNN 算法不僅僅擁有模型準確度,每個預測還擁有置信度。SVM 本質上沒有這個屬性,但是你可以使用`probability `參數來獲取一種形式。這是個開銷大的功能,但是可能對你來說足夠重要,或者默認值為`False`。 下面是`shrinking`布爾值,它默認為`True`。這個用于表示你是否將啟發式用于 SVM 的優化,它使用了序列最小優化(SMO)。你應該將其保留為`True`,因為它可以極大提升你的性能,并且只損失一點點準確性。 `tol`參數設置了 SVM 的容差。前面說過`yi(xi.w+b)-1 >= 0`。對于 SVM 來說,所有值都必須大于等于 0,每一邊至少一個值要等于 0,這就是你的支持向量。由于你不可能讓值(浮點數)完全等于 0,你需要設置一個容差來獲取一些彈性空間。Sklearn 中默認的 `tol`是`1e-3`,也就是 0.001。 下一個重要的參數是`max_iter`,它是你可以為平方規劃設置最大迭代次數的地方。默認值為`-1`,也就是沒有限制。 `decision_function_shape `是一對一(OVO),或者一對其它(OVR),那就是教程開始討論的概念。 `random_state `用于概率估計中的種子,如果你打算指定的話。 除了這些參數,我們還有幾個屬性。 `support_ `提供了支持向量的索引。`support_vectors_ `提供了實際的支持向量。`n_support_`是支持向量的個數,如果你的數據集有一些統計問題,將它與你的數據集尺寸相比非常實用。最后三個參數是`dual_coef_`、` coef_`和`intercept_`,如果你打算繪制 SVM,會非常實用。 SVM 就講完了。下一個話題是聚類。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看