Scikit-Learn 與 TensorFlow 機器學習實用指南學習筆記1 — 機器學習基礎知識簡介

紅色石頭發表於2018-11-20

本章介紹的是每一個資料科學家都應該知道並聽說的機器學習許多基本的概念和術語。這將是一個高層次的概括(本書唯一沒有很多程式碼的一章)。內容很簡單,但是你要保證在進行下一章之前對本章每個概念都理解得很透徹。因此,端起一杯咖啡,讓我們開始吧!

1 什麼是機器學習?

機器學習是一門通過程式設計從而在資料中學習的科學技術,或者稱之為藝術。

這裡有一個更一般的定義:

在不直接針對問題進行程式設計的情況下,賦予計算機學習能力的一個研究領域。 —— Arthur Samuel, 1959

另外一個更加工程化的定義:

對於某類任務T和效能度量P,如果計算機程式在T上以P衡量的效能隨著經驗E而自我完善,那麼就稱這個計算機程式從經驗E學習。 —— Tom Mitchell, 1997

例如垃圾郵件過濾系統就是一個機器學習程式,可以用來區分垃圾郵件和非垃圾郵件。機器學習系統用來學習的樣本稱之為訓練樣本(training set),每個訓練樣本稱之為訓練例項(training instance)或樣本。在這個例子中,任務 T 就是要對新郵件進行區分,經驗 E 就是訓練資料,效能度量 P 需要定義,例如使用分類正確的郵件所佔的比例。這種度量方式稱之為準確率(accuracy),經常應用在分類問題中。

2 為什麼使用機器學習?

我們先來思考,使用傳統程式設計技術如何區分垃圾郵件和非垃圾郵件呢?(如下圖所示)

1.首先你應該考慮典型的垃圾郵件具有什麼特點。例如在郵件的主題中包含了“4U”、“信用卡”、“免費”等詞語。又或者是在郵件的發件人、郵件內容中有一些垃圾郵件常出現的特定的詞語等。

2.然後你就可以對這些情況編寫垃圾郵件檢測演算法,若一定數量的詞彙檢測到了,則可以判定是垃圾郵件。

3.最後驗證程式,重複 1 和 2,直到檢測演算法的準確率足夠高。

因為這個問題並不簡單,你按照上面思路編寫的程式可能很複雜、冗長,而且很難把控。

對比而言,同樣是垃圾郵件分類,機器學習可以自動從垃圾郵件和非垃圾郵件中檢測出哪些單詞是經常出現在垃圾郵件中的(如下圖所示),而不需要人為指出出現哪些單詞可能就是垃圾郵件。這樣使得問題更加簡化、容易掌控,而且分類的準確率更高。

而且,如果給你發垃圾郵件的人知道了他們的關鍵詞“4 U”被鎖定的話,他們可能會把這個關鍵詞替換成“For U”。如果使用傳統程式設計技術,就要針對“For U”重新編寫程式。那麼,垃圾郵件關鍵詞每次替換,都需要重新針對新的關鍵詞改寫程式,相當麻煩。

而機器學習則不用這麼麻煩,只需將新的郵件樣本交給模型,機器會自動檢測出哪些單詞是可能出現在垃圾郵件中的。整個過程不需要人工排查和干預(如下圖所示)。

機器學習強大的另一個原因是,某些問題非常複雜,傳統程式設計很難有較好的方案去解決。例如語音識別、影像識別,使用傳統人工程式設計方式來解決效率低而且準確率不高。相比之下,最好的解決方法就是使用機器學習,給出足夠多的資料,讓機器自己去學習,得到較高的識別率。

最後,機器學習還可以幫助人們去學習(如下圖所示)。為什麼這樣說呢?因為 ML 演算法可以從資料中提取出有用和關鍵的資訊。例如垃圾郵件分類,經過好的訓練得到的 ML 模型能夠揭示出哪些單詞或者哪些單詞的組合是預測為垃圾郵件最重要的特徵參考。機器得到的這些資訊可以幫助人們更好地揭示問題的本質,從而更好地解決問題。

將 ML 技術應用到大資料中,挖掘資料本身蘊含的規律和模式,這就是 資料探勘

總結下來,機器學習的強大之處體現在以下幾個方面:

  • 傳統的程式設計方式解決問題通常需要人工除錯、建立冗長規則,而機器學習演算法可以極大地簡化程式碼且效能更好。
  • 對於某些複雜問題,例如語音識別、影像識別,傳統程式設計沒有一個較好的解決方案,而機器學習通常表現得很好。

  • 機器學習系統可以自適應新的資料,只需採集新的資料,重新訓練 ML 模型即可。

  • 機器學習可以揭示覆雜問題和大資料內在規律和特性。

3 機器學習型別

機器學習型別可以按照不同的方式來劃分,具體表現在:

  • 是否需要人類的監督:監督式學習、非監督式學習、半監督式學習、強化學習
  • 是否可以線上上進行學習:線上學習、批量學習

  • 是否可以簡單地把新的資料集與已知資料集進行比較得出結果,還是從訓練集中檢測模式再建立預測模型:基於例項學習、基於模型學習。

當然以上這些劃分方式並不是各自獨立的,可以相互結合。例如一個垃圾郵件檢測系統,可以是線上的、基於神經網路模型的監督式學習。

接下來我們詳細看看這些機器學習型別。

3.1 監督式/非監督式學習

機器學習可以根據再訓練過程中受到的監督型別和程度進行劃分,型別包括:監督式學習、非監督式學習、半監督式學習、強化學習。

監督式學習

監督式學習,訓練集包含了樣本的輸出,即 label(如下圖所示)。

一個典型的監督式學習就是分類問題,例如垃圾郵件分類,通過對郵件內容包括它們對應的標籤(垃圾、非垃圾)進行訓練。最終得到的模型對新的郵件進行預測,判斷它是不是垃圾郵件。

另一個典型的監督式學習就是迴歸問題(如下圖所示),例如汽車價格預測,通過對汽車特徵(商標、車齡等)包括它們對應的價格進行訓練。最終得到的模型對新的汽車進行預測,得到它的價格。

值得注意的是,有些迴歸演算法也可以用於分類,反之亦然。例如邏輯迴歸(Logistic Regression)常用於分類,然而它也可以輸出對應該類別的概率(例如得到垃圾郵件的概率是 20%)。

下面列舉一些最常見的監督式學習演算法:

  • k-近鄰
  • 線性迴歸

  • 邏輯迴歸

  • 支援向量機

  • 決策樹和隨機森林

  • 神經網路

非監督式學習

非監督式學習,顧名思義,訓練集是沒有輸出 label 的(如下圖所示)。

下面列舉一些最常見的非監督式學習演算法:

  • 聚類

    —— k-均值

    —— 層次聚類分析(HCA)

    —— 最大期望演算法(EM)

  • 視覺化和降維

    —— 主成分分析(PCA)

    —— 核 PCA

    —— 區域性線性嵌入(LLE)

    —— t分佈隨機鄰居嵌入(t-SNE)

  • 關聯規則學習

    —— Apriori

    —— Eclat

例如現在你掌握了你的部落格訪客資訊,想使用聚類演算法對這些訪客進行分組(如下圖所示)。你不需要告訴演算法每個訪客應該屬於哪一組,聚類演算法會自己分析和判斷。比如訪客中有 40% 是男性,喜歡看漫畫書,訪問部落格的時間經常是晚上;20% 是年輕的科幻迷,訪問部落格的時間經常是週末,等等。如果你使用層次聚類分析,會對每個組劃分得更細。

視覺化演算法(visualizationv algorithm)也是非監督式學習的好例子。它能夠把高緯的複雜且沒有標籤的樣本資料在 2D 或 3D 空間中展示出來,便於作圖(如下圖所示)。這些演算法儘可能儲存原始資料足夠多的結構資訊(例如視覺化的時候避免原始輸入空間不同類別的樣本發生重疊)。這有助於我們理解不同類別之間的近似程度。

一個相關的演算法叫做降維(dimensionality reduction)。目的是簡化資料但是不能損失太多資訊。常用的思路就是講相似特徵合併成一個。例如汽車的里程數和它的車齡關係較為密切,降維演算法就是把這兩個特徵合併成一個特徵來表徵汽車的磨損程度。這種做法就是特徵提取。降維的好處是減少特徵個數,讓演算法執行更快,減小了計算成本和儲存空間成本。更重要的是,降維可以消除一些噪聲影響,提升演算法效能。

還有一個非監督式學習的例子就是異常檢測(anomaly detection)。例如檢測不正常的信用卡交易來避免詐騙,捕獲人工製造缺陷,或者剔除資料集中的異常點以便傳替給後面的機器學習演算法。異常檢測演算法是使用正常資料集進行訓練的,得到的模型對新的樣本進行測試,判斷它是不是異常點(如見圖所示)。

最後一個非監督式學習的例子就是關聯規則學習(association rule learning),目標就是挖掘大量資料中不同屬性之前的聯絡。例如,通過對超市使用者購買商品歷史記錄的分析,發現購買燒烤醬和土豆薯片的使用者也傾向於購買牛排。因此,基於此分析,超市老闆就可以把這些使用者可能會一起買的商品放在相近的貨架上,方便使用者購買。

半監督式學習

半監督式學習,簡單地說就是訓練集大多數樣本沒有 label,只有少量樣本有 label。例如一些照片託管服務,例如谷歌照片,就是一個例子。一旦你把所有家庭照片上傳到伺服器之後,雲端演算法會自動識別出每個家庭成員出現在哪些照片中,例如 A 出現在照片 1、3、5、6 中, B 出現在照片 2、3、5、8 中,這是一個非監督式學習的過程(聚類)。現在你要做的就只是告訴機器每個人的名字(相當於 label),即標註 A、B 等分別叫什麼名字(監督式)。然後,機器就可以根據你給的 label,把每張照片中的每個人都標註上名字,即 label。也就是說,通過標註少量的樣本,然後根據聚類演算法,把所有樣本的 label 都自動加上了。

大多數半監督式學習都是監督式學習和非監督式學習相結合的。例如深度置信網路(DBNs),其基礎構建模型為受限玻爾茲曼機(RBMs)。RBMs 的訓練過程是非監督式學習,但是整個 DBNs 系統是使用監督式學習調優的。

強化學習

強化學習相對來說比較難一些。如下圖所示,假設一個學習系統 Agent 可以觀察環境,並做出相應的行為,然後得到相應的反饋。正確的行為得到獎勵,錯誤的行為得到懲罰。Agent 必須自我學習最佳策略是什麼,來即時獲得最多的正反饋,從而實現學習的目的。策略定義為 Agent 在一個給定情境中需要做出的即時行為。

上面這張圖生動形象地解釋了強化學習。機器人 Agent 面臨的狀況是發生火災,這時候,它必須進行決策,是選擇接近火源,還是接水救火?這由 Agent 自己決定。如果 Agent 直接接近火源,那麼就會得到懲罰(負反饋)。通過這樣的訓練,讓 Agent 自己知道避免懲罰就必須遠離火源,而做出取水救火的策略。強化學習可以類比成訓練寵物的過程,比如我們要訓練狗狗坐下,但是狗狗無法直接聽懂我們的指令的。在訓練過程中,我們根據狗狗的行為做出相應的反饋,如果它表現得好,我們就給他獎勵,如果它做跟指令完全無關的動作,我們就給它小小的懲罰。這樣不斷修正狗狗的動作,最終能讓它按照我們的指令來行動。

強化學習非常強大,我們熟悉的 AlphaGo 就是強化學習的典型代表。2016 年,AlphaGo 戰敗了世界圍棋冠軍李世石。它就是通過分析數百萬場圍棋比賽,從中學習到贏的策略,然後跟自己進行很多長比賽。經過這樣的訓練和學習,最終 AlphaGo 成為了一名圍棋頂尖高手。

3.2 批量學習和線上學習

機器學習型別另一種分類方式是根據是否可以對新新增的資料進行線上的即時學習。如果可以的話就叫線上學習(online learning),如果是離線的話就叫批量學習(batch learning)。

批量學習

批量學習不是即時學習,它是將所有的訓練資料一起訓練,這會花費很多時間和計算資源,所以一般只能用離線的方式訓練。模型一旦訓練完成,就上線釋出,使用固定的模型工作,所以也常稱為離線學習(offline learning)。

這時候如果有新的資料產生,想要得到新的模型必須把新的資料和之前的資料結合起來,再次重新離線訓練機器學習模型,最後上線釋出。

批量學習比較簡單也是最常見的機器學習型別。但是,一次完整的訓練過程可能會花費很多時間,甚至幾天、一個星期都有可能。如果機器學習系統更新資料很頻繁,那麼使用這種離線方式訓練就比較麻煩,成本很大,需要不停地整合資料、離線訓練、釋出模型。這種情況下,批量學習並不是一個好方法。

而且,訓練整個資料集需要很多的計算資源(例如 CPU/GPU、記憶體、儲存、IO、網路等)。特別是再資料量很大的情況下,其消耗的資源和成本是巨大的。這時候,一般不建議使用批量學習模型。

最後,如果你的系統需要能夠自主學習,且資源有限(例如手機 App),那麼攜帶大量資料、消耗大量資源,每天花費數小時來進行模型訓練是不太現實的。

線上學習

線上學習可以即時地重新訓練模型,當新的資料點或者小批量(mini-batches)資料傳給模型的時候,模型立即根據新資料重新訓練。整個過程快速而且成本低。這是一種線上學習的方式,如下圖所示。

線上學習對於那些持續接受資料(例如,股票價格)並且需要快速更新或自主學習的系統非常有用。如果你的計算資源有限,線上學習也是一個不錯的選擇:一旦線上學習系統了學習了新的資料例項,它就不再需要它們了,因此可以丟棄(除非您想要回滾到以前的狀態並“重播”資料)。這樣可以節省大量的空間。

線上學習演算法也可以應用於當資料集很大,機器記憶體不夠的時候進行模型訓練(稱為核外學習,out of core learning)。做法是把整個資料集切分成許多小批量(mini-batches)樣本,依次對每個小批量樣本進行訓練,重複進行,直到整個資料集完成訓練(如下圖所示)。值得注意的是,整個過程一般是離線進行的,所以線上學習這個名字可能有點讓人疑惑,你也可以稱之它為增量學習(incremental learning)。

線上學習一個很重要的引數就是基於資料改變更新演算法的頻率,是隻要有新的資料進來就更新演算法還是積累一定的資料改變之後再更新演算法。這個引數被成為:學習率(learning rate)。如果設定很大的學習率,演算法對會快速適應新資料,而忘記舊的資料(然而,垃圾郵件檢測系統中你可能並不想讓模型只對新的垃圾郵件標記,而很快捨棄之前的標記方法)。相反,如果設定較小的學習率,系統就會具有較大的慣性。也就是說,學習的速度比較慢,但是對資料中可能存在的噪聲就不會特別敏感。系統的健壯性就會更強一些。

線上學習一個大的挑戰是隻要有壞資料輸入給系統,那麼系統的效能就會立刻逐步下降。如果這是一個即時系統,使用者就會立刻注意到。例如,壞資料可能來自於機器人上的故障感測器,可能是某人在搜尋引擎輸入垃圾資訊以提高搜尋排名。為了減少這種風險,您需要密切監視系統,如果檢測到系統效能下降,就立即關閉學習(或回滾到上一個版本的系統)。您可能還需要監視輸入資料並對異常資料作出反應(例如,使用異常檢測演算法)。

3.3 基於例項學習 vs 基於模型學習

機器學習還可以就根據它的歸納方法來劃分。大多數機器學習的任務是做出預測,這意味著給到大量的訓練樣本訓練模型,系統需要泛化到從未見過的樣本。模型在訓練集上有好的表現是個好事,但這還不夠;目標是模型在新的例項上的表現。

有兩種主要的歸納方法:基於例項學習和基於模型學習。

基於例項學習

或許最簡單的學習方式就是記憶學習。如果你用這種方式建立一個垃圾郵件過濾器,它只會標記所有與已經由使用者標記的郵件相同的郵件。這不是最壞的解決方案,但肯定不是最好的。

不能僅僅是標記與已知郵件相同的郵件,你的垃圾郵件檢測程式還應該可以標記與已知郵件相似的郵件。這就需要計算兩封郵件的相似程度。一種最基本的相似度測量方式就是計算兩封郵件包含相同單詞的個數。如果新郵件與已知的一封垃圾郵件相同單詞個數較多,那麼系統就會判斷其也是垃圾郵件。

這種學習方法就叫做基於例項學習:系統把所有訓練集樣本都儲存下來,然後計算新的樣本與儲存的這些例項的相似度(如下圖所示)。

基於模型學習

另一種歸納方法就是使用所有訓練集樣本建立一個模型,然後使用這個模型預測新的樣本。這中學習方法就叫做基於模型學習(如下圖所示)。

例如,假設你想知道是否錢能讓人快樂,因此,你從 OECD 網站上下載 Better Life Index 資料,從 IMF 網站上下載 GDP 資料。然後就可以根據人均 GDP 和生活滿意度建立對應的表格,如下所示。

下面畫出一些國家人均 GDP 與生活滿意度之間的關係散點圖:

似乎能看到一點趨勢。儘管資料有些噪聲,但是生活滿意度多少與 GDP 呈現線性增長關係。因此,你可以令生活滿意度是人均 GDP 的線性函式。這一步稱為模型選擇:選擇一個生活滿意度的線性模型,該模型只有一個屬性(attribute),即人均 GDP。

這個模型有兩個引數:\theta_0\theta_1,通過調整這些引數,你可以讓你的模型表示任何線性函式,如下圖所示。

在使用你的模型之前,你需要確定引數值 \theta_0\theta_1 讓模型表現最好。怎麼做呢?你需要制定一個效能評估方法。可以定義一個擬合函式測量模型有多好,也可以定義一個代價函式(loss function)來測量模型有多不好。對於線性迴歸問題,人們一般使用代價函式來測量線性模型預測值與實際樣本值之間的距離,目標就是讓這個距離越小越好,最小化。

這就是線性迴歸演算法,根據訓練集,線性迴歸演算法能夠找到最合適的引數,使得線性模型能最好程度地擬合這些資料。這個過程稱為模型訓練。在上面這個例子中,使用線性迴歸演算法,得到的最佳引數為:\theta_0 = 4.85\theta_1 = 4.91 × 10^{-5}

現在得到的線性模型擬合資料的效果就很好了,如下圖所示。

最後,你就可以使用這個模型進行預測了。例如,你想知道 Cyprus 人的生活滿意度,但是 OECD 並沒有提供這個資料。幸運的是,你可以使用剛剛訓練好的模型進行預測:你查到 Cyprus 人均 GDP 是 $22,587,然後根據模型的線性表示式,計算生活滿意度為:4.85 + 22,587 × 4.91 × 10^{-5} = 5.96

下面這段程式碼就是使用 Python 來匯入資料集,預處理,建立視覺化散點圖,然後訓練線性模型並作出預測。

# Code example
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn.linear_model
import os
datapath = os.path.join("datasets", "lifesat", "")

def prepare_country_stats(oecd_bli, gdp_per_capita):
    oecd_bli = oecd_bli[oecd_bli["INEQUALITY"]=="TOT"]
    oecd_bli = oecd_bli.pivot(index="Country", columns="Indicator", values="Value")
    gdp_per_capita.rename(columns={"2015": "GDP per capita"}, inplace=True)
    gdp_per_capita.set_index("Country", inplace=True)
    full_country_stats = pd.merge(left=oecd_bli, right=gdp_per_capita,
                                  left_index=True, right_index=True)
    full_country_stats.sort_values(by="GDP per capita", inplace=True)
    remove_indices = [0, 1, 6, 8, 33, 34, 35]
    keep_indices = list(set(range(36)) - set(remove_indices))
    return full_country_stats[["GDP per capita", 'Life satisfaction']].iloc[keep_indices]

# Load the data
oecd_bli = pd.read_csv(datapath + "oecd_bli_2015.csv", thousands=',')
gdp_per_capita = pd.read_csv(datapath + "gdp_per_capita.csv",thousands=',',delimiter='\t',
                             encoding='latin1', na_values="n/a")

# Prepare the data
country_stats = prepare_country_stats(oecd_bli, gdp_per_capita)
X = np.c_[country_stats["GDP per capita"]]
y = np.c_[country_stats["Life satisfaction"]]

# Visualize the data
country_stats.plot(kind='scatter', x="GDP per capita", y='Life satisfaction')
plt.show()

# Select a linear model
model = sklearn.linear_model.LinearRegression()

# Train the model
model.fit(X, y)

# Make a prediction for Cyprus
X_new = [[22587]]  # Cyprus' GDP per capita
print(model.predict(X_new)) # outputs [[ 5.96242338]]

[[ 5.96242338]]

值得注意的是,如果我們使用的是基於例項的方法學習,我們發現與 Cyprus 人均 GDP 最接近的國家是 Slovenia(人均 GDP 為 $20,732)。因為 Slovenia 人的生活滿意度是 5.7,則可以說 Cyprus 的生活滿意度也是 5.7。如果我們選擇與 Cyprus GDP 最接近的 3 個國家,分別是 Portugal、Slovenia、Spain,GDP 分別是 5.1、5.7、6.5,則平均計算,得到 Cyprus 的人均 GDP 為 5.77。這兩個結果與我們使用基於模型學習的結果很相近。這種例項學習方法就叫做 k-近鄰演算法。

對應到程式碼中,如果使用 k-近鄰替代線下回歸,則只要把下面這條語句:

clf = sklearn.linear_model.LinearRegression()

替換成:

clf = sklearn.neighbors.KNeighborsRegressor(n_neighbors=3)

就好了。

如果一切進展順利的話,這個模型就可以較好地預測了。如果效果不好,那你還需要使用更多的特徵(例如就業率、健康程度、空氣質量等),獲得更好的訓練集,或者使用更強大的模型(例如多項式迴歸)。

下面對基於模型學習作個總結:

  • 選取資料集
  • 選擇模型

  • 在訓練集上訓練模型(即使用學習演算法找到最佳引數,讓代價函式最小化)

  • 最後,將模型應用到新的樣本中,進行預測,希望的得到較好的泛化能力

這就是構建一個典型的機器學習專案的過程。

到目前為止,我們已經介紹了很多內容:你知道了什麼是機器學習,為什麼機器學習是有用的,機器學習有哪些型別,以及構建機器學習系統的一般流程是什麼樣的。

專案地址:

https://github.com/RedstoneWill/Hands-On-Machine-Learning-with-Sklearn-TensorFlow


相關文章