探索監督機器學習算法
已發表: 2022-03-11本次閱讀的主要目標是理解足夠的統計方法,以便能夠利用 Python 的 scikit-learn 庫中的機器學習算法,然後應用這些知識來解決經典的機器學習問題。
我們旅程的第一站將帶我們了解機器學習的簡史。 然後我們將深入研究不同的算法。 在我們的最後一站,我們將使用我們學到的知識來解決泰坦尼克號生存率預測問題。
一些免責聲明:
- 我是一名全棧軟件工程師,而不是機器學習算法專家。
- 我假設你知道一些基本的 Python。
- 這是探索性的,所以並不是每個細節都像教程一樣解釋。
有了這一點,讓我們開始吧!
機器學習算法簡介
一旦你涉足這個領域,你就會意識到機器學習並沒有你想像的那麼浪漫。 起初,我滿懷希望,當我了解更多之後,我可以構建自己的 Jarvis AI,它會整天為我編寫軟件和賺錢,這樣我就可以整天在戶外看書,開摩托車,享受魯莽的生活方式,而我的私人賈維斯讓我的口袋更深。 然而,我很快意識到機器學習算法的基礎是統計,我個人覺得這很枯燥乏味。 幸運的是,事實證明“枯燥”的統計數據有一些非常有趣的應用。
您很快就會發現,要獲得這些引人入勝的應用程序,您需要非常了解統計數據。 機器學習算法的目標之一是在提供的數據中找到統計相關性。
提供的數據可以是任何東西,從根據年齡檢查血壓到根據各種像素的顏色查找手寫文本。
也就是說,我很想知道是否可以使用機器學習算法來查找加密哈希函數(SHA、MD5 等)中的依賴關係——但是,你不能真正做到這一點,因為正確的加密原語是以這種方式構建的它們消除了依賴關係並產生了難以預測的輸出。 我相信,給定無限的時間,機器學習算法可以破解任何加密模型。
不幸的是,我們沒有那麼多時間,所以我們需要找到另一種有效挖掘加密貨幣的方法。 到目前為止,我們已經走了多遠?
機器學習算法簡史
機器學習算法的根源來自生活在 18 世紀的英國統計學家 Thomas Bayes。 他的論文《An Essay To Solveing a Problem in the Doctrine of Chances》奠定了貝葉斯定理的基礎,該定理被廣泛應用於統計學領域。
19 世紀,Pierre-Simon Laplace 發表了Theorie analytique des probabilites ,擴展了貝葉斯的工作,並定義了我們今天所知的貝葉斯定理。 不久之前,Adrien-Marie Legendre 描述了“最小二乘法”,今天也廣泛用於監督學習。
20 世紀是在這一領域取得了大多數眾所周知的發現的時期。 安德烈馬爾科夫發明了馬爾科夫鏈,他用它來分析詩歌。 Alan Turing 提出了一種可以成為人工智能的學習機器,基本上預示了遺傳算法。 Frank Rosenblatt 發明了感知器,在媒體上引發了巨大的興奮和廣泛的報導。
但在 1970 年代,人們對 AI 的想法產生了很多悲觀情緒,因此資金減少了,因此這一時期被稱為AI 寒冬。 1980 年代反向傳播的重新發現引起了機器學習研究的複蘇。 而今天,這又是一個熱門話題。
已故的 Leo Breiman 區分了兩種統計建模範式:數據建模和算法建模。 “算法建模”或多或少意味著像隨機森林這樣的機器學習算法。
機器學習和統計是密切相關的領域。 根據 Michael I. Jordan 的說法,機器學習的思想,從方法論原則到理論工具,在統計學中有著悠久的歷史。 他還建議將數據科學作為機器學習專家和統計學家都在暗中研究的整體問題的佔位符術語。
機器學習算法的類別
機器學習領域有兩個主要支柱,稱為監督學習和無監督學習。 有些人還考慮將一個新的研究領域——深度學習——與有監督學習與無監督學習的問題分開。
監督學習是指向計算機提供輸入示例及其所需輸出的情況。 計算機的目標是學習一個將輸入映射到輸出的通用公式。 這可以進一步細分為:
- 半監督學習,即給計算機一個不完整的訓練集,缺少一些輸出
- 主動學習,即計算機只能獲得非常有限的一組實例的訓練標籤。 當以交互方式使用時,他們的訓練集可以呈現給用戶進行標記。
- 強化學習,即訓練數據僅作為程序在動態環境中的動作的反饋,例如駕駛車輛或與對手玩遊戲
相反,無監督學習是指根本沒有給出標籤,由算法在其輸入中找到結構。 當我們只需要發現隱藏的模式時,無監督學習本身就可以成為一個目標。
深度學習是一個新的研究領域,其靈感來自人腦的結構和功能,並基於人工神經網絡而不僅僅是統計概念。 深度學習可用於有監督和無監督方法。
在本文中,我們將僅介紹一些更簡單的監督機器學習算法,並使用它們來計算個人在泰坦尼克號沉沒中的生存機會。 但總的來說,如果您不確定要使用哪種算法,一個不錯的起點是 scikit-learn 的機器學習算法備忘單。
基本的監督機器學習模型
也許最簡單的算法是線性回歸。 有時這可以用圖形表示為一條直線,但儘管它的名字,如果有一個多項式假設,這條線可能是一條曲線。 無論哪種方式,它都對標量因變量 $y$ 和一個或多個由 $x$ 表示的解釋值之間的關係進行建模。
通俗地說,這意味著線性回歸是一種算法,它學習每個已知的 $x$ 和 $y$ 之間的依賴關係,以便稍後我們可以使用它來預測 $y$ 的未知樣本 $x$。
在我們的第一個監督學習示例中,我們將使用基本的線性回歸模型來根據年齡預測一個人的血壓。 這是一個非常簡單的數據集,具有兩個有意義的特徵:年齡和血壓。
如上所述,大多數機器學習算法通過在提供給它們的數據中找到統計依賴性來工作。 這種依賴稱為假設,通常用 $h(\theta)$ 表示。
為了弄清楚這個假設,讓我們從加載和探索數據開始。
import matplotlib.pyplot as plt from pandas import read_csv import os # Load data data_path = os.path.join(os.getcwd(), "data/blood-pressure.txt") dataset = read_csv(data_path, delim_whitespace=True) # We have 30 entries in our dataset and four features. The first feature is the ID of the entry. # The second feature is always 1. The third feature is the age and the last feature is the blood pressure. # We will now drop the ID and One feature for now, as this is not important. dataset = dataset.drop(['ID', 'One'], axis=1) # And we will display this graph %matplotlib inline dataset.plot.scatter(x='Age', y='Pressure') # Now, we will assume that we already know the hypothesis and it looks like a straight line h = lambda x: 84 + 1.24 * x # Let's add this line on the chart now ages = range(18, 85) estimated = [] for i in ages: estimated.append(h(i)) plt.plot(ages, estimated, 'b')
[<matplotlib.lines.Line2D at 0x11424b828>]
在上圖中,每個藍點代表我們的數據樣本,藍線是我們的算法需要學習的假設。 那麼這個假設到底是什麼?
為了解決這個問題,我們需要學習$x$和$y$之間的依賴關係,記為$y = f(x)$。 因此 $f(x)$ 是理想的目標函數。 機器學習算法將嘗試猜測與未知 $f(x)$ 最接近的假設函數 $h(x)$。
線性回歸問題的最簡單假設形式如下所示:$h_\theta(x) = \theta_0 + \theta_1 * x$。 我們有一個輸入標量變量 $x$,它輸出單個標量變量 $y$,其中 $\theta_0$ 和 $\theta_1$ 是我們需要學習的參數。 在數據中擬合這條藍線的過程稱為線性回歸。 重要的是要了解我們只有一個輸入參數 $x_1$; 然而,很多假設函數也會包含偏差單元($x_0$)。 所以我們得到的假設具有 $h_\theta(x) = \theta_0 * x_0 + \theta_1 * x_1$ 的形式。 但是我們可以避免寫入 $x_0$,因為它幾乎總是等於 1。
回到藍線。 我們的假設看起來像 $h(x) = 84 + 1.24x$,這意味著 $\theta_0 = 84$ 和 $\theta_1 = 1.24$。 我們如何自動推導出這些 $\theta$ 值?
我們需要定義一個成本函數。 本質上,成本函數所做的只是計算模型預測和實際輸出之間的均方根誤差。
\[J(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]例如,我們的假設預測,對於 48 歲的人,他們的血壓應該是 $h(48) = 84 + 1.24 * 48 = 143mmHg$; 但是,在我們的訓練樣本中,我們的值為 130 毫米汞柱。 因此錯誤是 $(143 - 130)^2 = 169$。 現在我們需要為訓練數據集中的每一個條目計算這個誤差,然後把它加在一起 ($\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i )})^2$) 並從中取出平均值。
這給了我們一個代表函數成本的標量數。 我們的目標是找到使成本函數最低的 $\theta$ 值; 換句話說,我們希望最小化成本函數。 希望這看起來很直觀:如果我們的成本函數值很小,這意味著預測的誤差也很小。
import numpy as np # Let's calculate the cost for the hypothesis above h = lambda x, theta_0, theta_1: theta_0 + theta_1 * x def cost(X, y, t0, t1): m = len(X) # the number of the training samples c = np.power(np.subtract(h(X, t0, t1), y), 2) return (1 / (2 * m)) * sum(c) X = dataset.values[:, 0] y = dataset.values[:, 1] print('J(Theta) = %2.2f' % cost(X, y, 84, 1.24))
J(Theta) = 1901.95
現在,我們需要找到這樣的 $\theta$ 值,使得我們的成本函數值最小。 但是我們該怎麼做呢?
\[minJ(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]有幾種可能的算法,但最流行的是梯度下降。 為了理解梯度下降法背後的直覺,我們先把它畫在圖上。 為了簡單起見,我們將假設一個更簡單的假設 $h(\theta) = \theta_1 * x$。 接下來,我們將繪製一個簡單的 2D 圖表,其中 $x$ 是 $\theta$ 的值,$y$ 是此時的成本函數。
import matplotlib.pyplot as plt fig = plt.figure() # Generate the data theta_1 = np.arange(-10, 14, 0.1) J_cost = [] for t1 in theta_1: J_cost += [ cost(X, y, 0, t1) ] plt.plot(theta_1, J_cost) plt.xlabel(r'$\theta_1$') plt.ylabel(r'$J(\theta)$') plt.show()
成本函數是凸的,這意味著在區間 $[a, b]$ 上只有一個最小值。 這再次意味著最好的 $\theta$ 參數位於成本函數最小的點。
基本上,梯度下降是一種試圖找到最小化函數的參數集的算法。 它從一組初始參數開始,並在函數梯度的負方向上迭代地採取步驟。
如果我們計算假設函數在特定點的導數,這將為我們提供該點處曲線的切線斜率。 這意味著我們可以計算圖表上每個點的斜率。
該算法的工作方式是這樣的:
- 我們選擇一個隨機起點(隨機 $\theta$)。
- 計算此時成本函數的導數。
- 向斜率 $\theta_j := \theta_j - \lambda * \frac{\partial}{\partial \theta_j} * J(\theta)$ 邁出一小步。
- 重複步驟 2-3,直到我們收斂。
現在,收斂條件取決於算法的實現。 我們可能會在 50 步後、某個閾值或其他任何東西後停止。
import math # Example of the simple gradient descent algorithm taken from Wikipedia cur_x = 2.5 # The algorithm starts at point x gamma = 0.005 # Step size multiplier precision = 0.00001 previous_step_size = cur_x df = lambda x: 2 * x * math.cos(x) # Remember the learning curve and plot it while previous_step_size > precision: prev_x = cur_x cur_x += -gamma * df(prev_x) previous_step_size = abs(cur_x - prev_x) print("The local minimum occurs at %f" % cur_x)
The local minimum occurs at 4.712194
我們不會在本文中實現這些算法。 相反,我們將使用廣泛採用的scikit-learn
,這是一個開源 Python 機器學習庫。 它為不同的數據挖掘和機器學習問題提供了許多非常有用的 API。
from sklearn.linear_model import LinearRegression # LinearRegression uses the gradient descent method # Our data X = dataset[['Age']] y = dataset[['Pressure']] regr = LinearRegression() regr.fit(X, y) # Plot outputs plt.xlabel('Age') plt.ylabel('Blood pressure') plt.scatter(X, y, color='black') plt.plot(X, regr.predict(X), color='blue') plt.show() plt.gcf().clear()
<matplotlib.figure.Figure at 0x120fae1d0>
print( 'Predicted blood pressure at 25 yo = ', regr.predict(25) ) print( 'Predicted blood pressure at 45 yo = ', regr.predict(45) ) print( 'Predicted blood pressure at 27 yo = ', regr.predict(27) ) print( 'Predicted blood pressure at 34.5 yo = ', regr.predict(34.5) ) print( 'Predicted blood pressure at 78 yo = ', regr.predict(78) )
Predicted blood pressure at 25 yo = [[ 122.98647692]] Predicted blood pressure at 45 yo = [[ 142.40388395]] Predicted blood pressure at 27 yo = [[ 124.92821763]] Predicted blood pressure at 34.5 yo = [[ 132.20974526]] Predicted blood pressure at 78 yo = [[ 174.44260555]]
統計數據的類型
在處理機器學習問題的數據時,識別不同類型的數據非常重要。 我們可能有數字(連續或離散)、分類或有序數據。
數值數據具有測量意義。 例如,年齡、體重、一個人擁有的比特幣數量,或者這個人每月可以寫多少篇文章。 數值數據可以進一步細分為離散和連續類型。
- 離散數據表示可以用整數計數的數據,例如,公寓中的房間數或擲硬幣的次數。
- 連續數據不一定用整數表示。 例如,如果您要測量可以跳躍的距離,它可能是 2 米、1.5 米或 1.652245 米。
分類數據表示諸如人的性別、婚姻狀況、國家等值。這些數據可以取數值,但這些數字沒有數學意義。 您不能將它們加在一起。
序數數據可以是其他兩種類型的混合,因為類別可以以數學上有意義的方式編號。 一個常見的例子是評分:我們經常被要求以一到十的等級對事物進行評分,並且只允許使用整數。 雖然我們可以在數字上使用它——例如,找到某物的平均評分——但在將機器學習方法應用於數據時,我們通常將數據視為分類數據。
邏輯回歸
線性回歸是一種很棒的算法,它可以幫助我們預測數值,例如,具有特定大小和房間數量的房屋價格。 但是,有時,我們可能還想預測分類數據,以獲得以下問題的答案:
- 這是狗還是貓?
- 這種腫瘤是惡性的還是良性的?
- 這酒是好是壞?
- 這封電子郵件是不是垃圾郵件?
甚至:
- 圖中是哪個數字?
- 此電子郵件屬於哪個類別?
所有這些問題都特定於分類問題。 而最簡單的分類算法稱為邏輯回歸,它最終與線性回歸相同,只是它有不同的假設。
首先,我們可以重用相同的線性假設 $h_\theta(x) = \theta^TX$(這是向量化的形式)。 線性回歸可以輸出區間 $[a, b]$ 中的任意數字,而邏輯回歸只能輸出 $[−1, 1]$ 中的值,即對像是否屬於給定類別的概率。
使用sigmoid 函數,我們可以將任何數值轉換為表示區間 $[−1, 1]$ 上的值。
\[f(x) = \frac{1}{1 + e^x}\]現在,我們需要傳遞一個現有假設,而不是 $x$,因此我們將得到:
\[f(x) = \frac{1}{1 + e^{\theta_0 + \theta_1 * x_1 + ... + \theta_n * x_n}}\]之後,我們可以應用一個簡單的閾值,即如果假設大於零,則為真值,否則為假。
\[h_\theta(x) = \begin{cases} 1 & \mbox{if } \theta^TX > 0 \\ 0 & \mbox{else} \end{cases}\]這意味著我們可以使用相同的成本函數和相同的梯度下降算法來學習邏輯回歸的假設。
在我們的下一個機器學習算法示例中,我們將建議航天飛機的飛行員是否應該使用自動或手動著陸控制。 我們有一個非常小的數據集——15 個樣本——由六個特徵和基本事實組成。
在機器學習算法中,術語“ ground truth ”是指訓練集分類的監督學習技術的準確性。
我們的數據集是完整的,這意味著沒有缺失的特徵; 但是,某些功能有一個“*”而不是類別,這意味著此功能無關緊要。 我們將用零替換所有這些星號。
from sklearn.linear_model import LogisticRegression # Data data_path = os.path.join(os.getcwd(), "data/shuttle-landing-control.csv") dataset = read_csv(data_path, header=None, names=['Auto', 'Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility'], na_values='*').fillna(0) # Prepare features X = dataset[['Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility']] y = dataset[['Auto']].values.reshape(1, -1)[0] model = LogisticRegression() model.fit(X, y) # For now, we're missing one important concept. We don't know how well our model # works, and because of that, we cannot really improve the performance of our hypothesis. # There are a lot of useful metrics, but for now, we will validate how well # our algorithm performs on the dataset it learned from. "Score of our model is %2.2f%%" % (model.score(X, y) * 100)
Score of our model is 73.33%
驗證?
在前面的示例中,我們使用學習數據驗證了模型的性能。 但是,考慮到我們的算法既可以欠擬合數據,也可以過擬合數據,這現在是一個不錯的選擇嗎? 讓我們看一個更簡單的例子,當我們有一個表示房屋大小的特徵和另一個表示價格的特徵時。
from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_score # Ground truth function ground_truth = lambda X: np.cos(15 + np.pi * X) # Generate random observations around the ground truth function n_samples = 15 degrees = [1, 4, 30] X = np.linspace(-1, 1, n_samples) y = ground_truth(X) + np.random.randn(n_samples) * 0.1 plt.figure(figsize=(14, 5)) models = {} # Plot all machine learning algorithm models for idx, degree in enumerate(degrees): ax = plt.subplot(1, len(degrees), idx + 1) plt.setp(ax, xticks=(), yticks=()) # Define the model polynomial_features = PolynomialFeatures(degree=degree) model = make_pipeline(polynomial_features, LinearRegression()) models[degree] = model # Train the model model.fit(X[:, np.newaxis], y) # Evaluate the model using cross-validation scores = cross_val_score(model, X[:, np.newaxis], y) X_test = X plt.plot(X_test, model.predict(X_test[:, np.newaxis]), label="Model") plt.scatter(X, y, edgecolor='b', s=20, label="Observations") plt.xlabel("x") plt.ylabel("y") plt.ylim((-2, 2)) plt.title("Degree {}\nMSE = {:.2e}".format( degree, -scores.mean())) plt.show()
如果機器學習算法模型既不能概括訓練數據也不能概括新的觀察結果,那麼它就是欠擬合的。 在上面的示例中,我們使用了一個簡單的線性假設,它並不能真正代表實際的訓練數據集,並且性能會很差。 通常,欠擬合不會被討論,因為它可以很容易地檢測到一個好的度量。
如果我們的算法記住了它所顯示的每一個觀察結果,那麼它在訓練數據集之外的新觀察結果上的性能就會很差。 這稱為過擬合。 例如,一個 30 次多項式模型通過了大部分點並且在訓練集上獲得了非常好的分數,但除此之外的任何東西都會表現不佳。
我們的數據集由一個特徵組成,並且很容易在 2D 空間中繪製; 然而,在現實生活中,我們可能擁有具有數百個特徵的數據集,這使得它們無法在歐幾里得空間中直觀地繪製。 為了查看模型是欠擬合還是過擬合,我們還有哪些其他選擇?
是時候向您介紹學習曲線的概念了。 這是一個簡單的圖表,繪製了訓練樣本數量的均方誤差。
在學習材料中,您通常會看到類似於以下的圖表:
但是,在現實生活中,您可能無法獲得如此完美的畫面。 讓我們繪製每個模型的學習曲線。
from sklearn.model_selection import learning_curve, ShuffleSplit # Plot learning curves plt.figure(figsize=(20, 5)) for idx, degree in enumerate(models): ax = plt.subplot(1, len(degrees), idx + 1) plt.title("Degree {}".format(degree)) plt.grid() plt.xlabel("Training examples") plt.ylabel("Score") train_sizes = np.linspace(.6, 1.0, 6) # Cross-validation with 100 iterations to get a smoother mean test and training # score curves, each time with 20% of the data randomly selected as a validation set. cv = ShuffleSplit(n_splits=100, test_size=0.2, random_state=0) model = models[degree] train_sizes, train_scores, test_scores = learning_curve( model, X[:, np.newaxis], y, cv=cv, train_sizes=train_sizes, n_jobs=4) train_scores_mean = np.mean(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score") plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Test score") plt.legend(loc = "best") plt.show()

在我們的模擬場景中,代表訓練分數的藍線看起來像一條直線。 實際上,它仍然略有下降——您實際上可以在一次多項式圖中看到這一點,但在其他圖中,在這個分辨率下它太微妙了,無法分辨。 我們至少清楚地看到,在“高偏差”場景下,訓練和測試觀察的學習曲線之間存在巨大差距。
在中間的“正常”學習率圖上,您可以看到訓練分數線和測試分數線是如何結合在一起的。
在“高方差”圖上,您可以看到在樣本數量較少的情況下,測試和訓練分數非常相似; 但是,當您增加樣本數量時,訓練分數幾乎保持完美,而測試分數卻遠離它。
如果我們使用非線性假設,例如具有更多多項式特徵的假設,我們可以修復欠擬合模型(也稱為具有高偏差的模型)。
我們的過擬合模型(高方差)通過了它顯示的每一個例子; 然而,當我們引入測試數據時,學習曲線之間的差距會擴大。 我們可以使用正則化、交叉驗證和更多數據樣本來修復過擬合模型。
交叉驗證
避免過度擬合的一種常見做法是保留部分可用數據並將其用作測試集。 然而,在評估不同的模型設置時,例如多項式特徵的數量,我們仍然存在過度擬合測試集的風險,因為可以調整參數以實現最佳估計器性能,因此,我們對測試集的了解可以洩漏到模型中。 為了解決這個問題,我們需要保留數據集的另一部分,稱為“驗證集”。 在訓練集上進行訓練,當我們認為我們已經達到了最佳模型性能時,我們可以利用驗證集進行最終評估。
然而,通過將可用數據分成三組,我們大大減少了可用於訓練模型的樣本數量,並且結果可能取決於訓練-驗證對組的特定隨機選擇。
這個問題的一種解決方案是一種稱為交叉驗證的過程。 在標準的 $k$-fold 交叉驗證中,我們將數據劃分為 $k$ 子集,稱為折疊。 然後,我們在 $k-1$ 折疊上迭代訓練算法,同時使用剩餘折疊作為測試集(稱為“保持折疊”)。
交叉驗證允許您僅使用原始訓練集調整參數。 這使您可以將測試集保留為真正看不見的數據集,以選擇最終模型。
還有更多的交叉驗證技術,例如leave P out 、分層 $k$-fold 、 shuffle 和 split等,但它們超出了本文的範圍。
正則化
這是另一種可以幫助解決模型過度擬合問題的技術。 大多數數據集都有模式和一些噪音。 正則化的目標是減少噪聲對模型的影響。
有三種主要的正則化技術:Lasso、Tikhonov 和彈性網絡。
L1 正則化(或Lasso 正則化)將選擇一些特徵縮小到零,這樣它們就不會在最終模型中發揮任何作用。 L1可以看作是一種選擇重要特徵的方法。
L2 正則化(或Tikhonov 正則化)將迫使所有特徵都相對較小,這樣它們對模型的影響就會更小。
彈性網是 L1 和 L2 的組合。
歸一化(特徵縮放)
特徵縮放也是預處理數據時的重要一步。 我們的數據集可能具有值為 $[-\infty、\infty]$ 的特徵和具有不同尺度的其他特徵。 這是一種標準化獨立值範圍的方法。
特徵縮放也是提高學習模型性能的重要過程。 首先,如果所有特徵都縮放到相同的範數,梯度下降將收斂得更快。 此外,許多算法——例如支持向量機 (SVM)——通過計算兩點之間的距離來工作,如果其中一個特徵具有廣泛的值,那麼距離將受到該特徵的高度影響。
支持向量機
SVM 是另一種廣泛流行的機器學習算法,可用於分類和回歸問題。 在 SVM 中,我們將每個觀察值繪製為 $n$ 維空間中的一個點,其中 $n$ 是我們擁有的特徵數。 每個特徵的值是特定坐標的值。 然後,我們嘗試找到一個能夠很好地分離兩個類的超平面。
在我們確定了最好的超平面之後,我們想要添加邊距,這將進一步分離兩個類。
在特徵數量非常多或特徵數量大於數據樣本數量的情況下,SVM 非常有效。 然而,由於 SVM 是在向量基礎上運行的,因此在使用之前對數據進行歸一化至關重要。
神經網絡
神經網絡算法可能是機器學習研究中最令人興奮的領域。 神經網絡試圖模仿大腦的神經元是如何連接在一起的。
這就是神經網絡的樣子。 我們將許多節點組合在一起,每個節點接受一組輸入,對它們進行一些計算,然後輸出一個值。
有監督學習和無監督學習的神經網絡算法種類繁多。 神經網絡可用於駕駛自動駕駛汽車、玩遊戲、陸地飛機、圖像分類等。
臭名昭著的泰坦尼克號
泰坦尼克號是一艘英國客輪,在與冰山相撞後於 1912 年 4 月 15 日在北大西洋沉沒。 大約有 2,224 名船員和乘客,超過 1,500 人死亡,使其成為有史以來最致命的商業海難之一。
現在,由於我們了解用於分類問題的最基本機器學習算法背後的直覺,我們可以應用我們的知識來預測泰坦尼克號上的人的生存結果。
我們的數據集將從 Kaggle 數據科學競賽平台借用。
import os from pandas import read_csv, concat # Load data data_path = os.path.join(os.getcwd(), "data/titanic.csv") dataset = read_csv(data_path, skipinitialspace=True) dataset.head(5)
乘客編號 | 倖存下來 | 類 | 姓名 | 性別 | 年齡 | 同胞 | 胹 | 票 | 票價 | 艙 | 登船 | |
0 | 1 | 0 | 3 | Braund,歐文·哈里斯先生 | 男性 | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | 鈉 | 小號 |
1 | 2 | 1 | 1 | 卡明斯,約翰·布拉德利夫人(弗洛倫斯·布里格斯... | 女性 | 38.0 | 1 | 0 | 電腦 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | 海基寧小姐萊納 | 女性 | 26.0 | 0 | 0 | 斯通/O2。 3101282 | 7.9250 | 鈉 | 小號 |
3 | 4 | 1 | 1 | Futrelle,雅克·希思夫人(莉莉·梅·皮爾) | 女性 | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | 小號 |
4 | 5 | 0 | 3 | 艾倫,威廉·亨利先生 | 男性 | 35.0 | 0 | 0 | 373450 | 8.0500 | 鈉 | 小號 |
我們的第一步是加載和探索數據。 我們有891條測試記錄; 每條記錄具有以下結構:
- 乘客 ID – 機上乘客的 ID
- 生存——這個人是否在墜機事故中倖存下來
- pclass – 機票等級,例如 1st、2nd、3rd
- 性別 – 乘客性別:男性或女性
- 名稱 - 包括標題
- 年齡 - 年齡
- sibsp – 泰坦尼克號上的兄弟姐妹/配偶的數量
- parch – 泰坦尼克號上的父母/孩子人數
- 票——票號
- 票價——乘客票價
- 客艙 - 客艙號碼
- 登船 – 登船港
該數據集包含數值數據和分類數據。 通常,深入研究數據並在此基礎上提出假設是一個好主意。 但是,在這種情況下,我們將跳過這一步並直接進行預測。
import pandas as pd # We need to drop some insignificant features and map the others. # Ticket number and fare should not contribute much to the performance of our models. # Name feature has titles (eg, Mr., Miss, Doctor) included. # Gender is definitely important. # Port of embarkation may contribute some value. # Using port of embarkation may sound counter-intuitive; however, there may # be a higher survival rate for passengers who boarded in the same port. dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False) dataset = dataset.drop(['PassengerId', 'Ticket', 'Cabin', 'Name'], axis=1) pd.crosstab(dataset['Title'], dataset['Sex'])
Title \ Sex | 女性 | 男性 |
上尉 | 0 | 1 |
科爾 | 0 | 2 |
伯爵夫人 | 1 | 0 |
大學教師 | 0 | 1 |
博士 | 1 | 6 |
瓊克爾 | 0 | 1 |
淑女 | 1 | 0 |
主要的 | 0 | 2 |
掌握 | 0 | 40 |
錯過 | 182 | 0 |
米勒 | 2 | 0 |
嗯 | 1 | 0 |
先生 | 0 | 517 |
太太 | 125 | 0 |
小姐 | 1 | 0 |
轉 | 0 | 6 |
先生 | 0 | 1 |
# We will replace many titles with a more common name, English equivalent, # or reclassification dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\ 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Other') dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss') dataset['Title'] = dataset['Title'].replace('Ms', 'Miss') dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs') dataset[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
標題 | 倖存下來 | |
0 | 掌握 | 0.575000 |
1 | 錯過 | 0.702703 |
2 | 先生 | 0.156673 |
3 | 太太 | 0.793651 |
4 | 其他 | 0.347826 |
# Now we will map alphanumerical categories to numbers title_mapping = { 'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Other': 5 } gender_mapping = { 'female': 1, 'male': 0 } port_mapping = { 'S': 0, 'C': 1, 'Q': 2 } # Map title dataset['Title'] = dataset['Title'].map(title_mapping).astype(int) # Map gender dataset['Sex'] = dataset['Sex'].map(gender_mapping).astype(int) # Map port freq_port = dataset.Embarked.dropna().mode()[0] dataset['Embarked'] = dataset['Embarked'].fillna(freq_port) dataset['Embarked'] = dataset['Embarked'].map(port_mapping).astype(int) # Fix missing age values dataset['Age'] = dataset['Age'].fillna(dataset['Age'].dropna().median()) dataset.head()
倖存下來 | Pclass | 性別 | 年齡 | SibSp | Parch | 票價 | Embarked | 標題 | |
0 | 0 | 3 | 0 | 22.0 | 1 | 0 | 7.2500 | 0 | 1 |
1 | 1 | 1 | 1 | 38.0 | 1 | 0 | 71.2833 | 1 | 3 |
2 | 1 | 3 | 1 | 26.0 | 0 | 0 | 7.9250 | 0 | 2 |
3 | 1 | 1 | 1 | 35.0 | 1 | 0 | 53.1000 | 0 | 3 |
4 | 0 | 3 | 0 | 35.0 | 0 | 0 | 8.0500 | 0 | 1 |
At this point, we will rank different types of machine learning algorithms in Python by using scikit-learn
to create a set of different models. It will then be easy to see which one performs the best.
- Logistic regression with varying numbers of polynomials
- Support vector machine with a linear kernel
- Support vector machine with a polynomial kernel
- 神經網絡
For every single model, we will use $k$-fold validation.
from sklearn.model_selection import KFold, train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures, StandardScaler from sklearn.neural_network import MLPClassifier from sklearn.svm import SVC # Prepare the data X = dataset.drop(['Survived'], axis = 1).values y = dataset[['Survived']].values X = StandardScaler().fit_transform(X) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state = None) # Prepare cross-validation (cv) cv = KFold(n_splits = 5, random_state = None) # Performance p_score = lambda model, score: print('Performance of the %s model is %0.2f%%' % (model, score * 100)) # Classifiers names = [ "Logistic Regression", "Logistic Regression with Polynomial Hypotheses", "Linear SVM", "RBF SVM", "Neural Net", ] classifiers = [ LogisticRegression(), make_pipeline(PolynomialFeatures(3), LogisticRegression()), SVC(kernel="linear", C=0.025), SVC(gamma=2, C=1), MLPClassifier(alpha=1), ]
# iterate over classifiers models = [] trained_classifiers = [] for name, clf in zip(names, classifiers): scores = [] for train_indices, test_indices in cv.split(X): clf.fit(X[train_indices], y[train_indices].ravel()) scores.append( clf.score(X_test, y_test.ravel()) ) min_score = min(scores) max_score = max(scores) avg_score = sum(scores) / len(scores) trained_classifiers.append(clf) models.append((name, min_score, max_score, avg_score)) fin_models = pd.DataFrame(models, columns = ['Name', 'Min Score', 'Max Score', 'Mean Score'])
fin_models.sort_values(['Mean Score']).head()
姓名 | Min Score | 最高分 | 平均分 | |
2 | 線性支持向量機 | 0.793296 | 0.821229 | 0.803352 |
0 | Logistic Regression | 0.826816 | 0.860335 | 0.846927 |
4 | 神經網絡 | 0.826816 | 0.860335 | 0.849162 |
1 | Logistic Regression with Polynomial Hypotheses | 0.854749 | 0.882682 | 0.869274 |
3 | RBF SVM | 0.843575 | 0.888268 | 0.869274 |
Ok, so our experimental research says that the SVM classifier with a radial basis function (RBF) kernel performs the best. Now, we can serialize our model and re-use it in production applications.
import pickle svm_model = trained_classifiers[3] data_path = os.path.join(os.getcwd(), "best-titanic-model.pkl") pickle.dump(svm_model, open(data_path, 'wb'))
Machine learning is not complicated, but it's a very broad field of study, and it requires knowledge of math and statistics in order to grasp all of its concepts.
Right now, machine learning and deep learning are among the hottest topics of discussion in Silicon Valley, and are the bread and butter of almost every data science company, mainly because they can automate many repetitive tasks including speech recognition, driving vehicles, financial trading, caring for patients, cooking, marketing, and so on.
Now you can take this knowledge and solve challenges on Kaggle.
This was a very brief introduction to supervised machine learning algorithms. Luckily, there are a lot of online courses and information about machine learning algorithms. I personally would recommend starting with Andrew Ng's course on Coursera.
資源
- Andrew Ng's course on Coursera
- Kaggle 數據集
- A deep learning reading list
- A list of free books on machine learning algorithms, data mining, deep learning, and related topics
- 機器學習理論及其應用簡介:帶有示例的可視化教程