機(jī)器學(xué)習(xí)簡(jiǎn)單流程:
- 使用大量和任務(wù)相關(guān)的數(shù)據(jù)集來(lái)訓(xùn)練模型;
- 通過(guò)模型在數(shù)據(jù)集上的誤差不斷迭代訓(xùn)練模型,得到對(duì)數(shù)據(jù)集擬合合理的模型纳猫;
- 將訓(xùn)練好調(diào)整好的模型應(yīng)用到真實(shí)的場(chǎng)景中婆咸;
我們最終的目的是將訓(xùn)練好的模型部署到真實(shí)的環(huán)境中,希望訓(xùn)練好的模型能夠在真實(shí)的數(shù)據(jù)上得到好的預(yù)測(cè)效果芜辕,換句話說(shuō)就是希望模型在真實(shí)數(shù)據(jù)上預(yù)測(cè)的結(jié)果誤差越小越好尚骄。我們把模型在真實(shí)環(huán)境中的誤差叫做泛化誤差,最終的目的是希望訓(xùn)練好的模型泛化誤差越低越好侵续。
我們希望通過(guò)某個(gè)信號(hào)來(lái)了解模型的泛化誤差倔丈,這樣就可以指導(dǎo)我們得到泛化能力更強(qiáng)的模型:
- 使用泛化誤差本身。這是很自然的想法状蜗,我們訓(xùn)練模型的最終目的就是希望模型的泛化誤差最低需五,當(dāng)然可以使用泛化誤差本身來(lái)作為檢測(cè)信號(hào)。如果泛化誤差小的話還可以接受诗舰,但是通常情況下沒有那么幸運(yùn),泛化誤差可能很大训裆,這個(gè)時(shí)候你肯定會(huì)將部署的模型撤回眶根,重新訓(xùn)練,你可能需要部署和訓(xùn)練之間往復(fù)很多次边琉,這種方式雖然能夠更好的指導(dǎo)我們的模型属百,但是成本和效率非常的差;
- 使用模型在數(shù)據(jù)集上訓(xùn)練的擬合程度來(lái)作為評(píng)估模型的信號(hào)变姨。但是往往我們獲取的數(shù)據(jù)集并不是完全的干凈以及有代表性族扰,通常我們獲取到的數(shù)據(jù)集可能很少、數(shù)據(jù)的代表性不夠定欧、包含太多的噪聲或者是被一些無(wú)關(guān)特征污染渔呵,我們獲取到的數(shù)據(jù)集或多或少都會(huì)有這些問題,那么模型對(duì)訓(xùn)練數(shù)據(jù)集的擬合程度不能指導(dǎo)泛化誤差砍鸠,也就是說(shuō)訓(xùn)練的時(shí)候擬合的好并不代表模型的泛化誤差就小扩氢,你甚至可以將模型在數(shù)據(jù)集上的誤差減小到0,但是因?yàn)閷?duì)模型訓(xùn)練時(shí)候的數(shù)據(jù)集往往不干凈爷辱,所以這樣的模型并不代表泛化能力就強(qiáng)录豺。
1.訓(xùn)練集與測(cè)試集
前面說(shuō)到我們既不能通過(guò)直接將泛化誤差作為了解模型泛化能力的信號(hào),因?yàn)樵诓渴瓠h(huán)境和訓(xùn)練模型之間往復(fù)饭弓,代價(jià)很高双饥,也不能使用模型對(duì)訓(xùn)練數(shù)據(jù)集的擬合程度來(lái)作為了解模型泛化能力的信號(hào),因?yàn)槲覀儷@得的數(shù)據(jù)往往不干凈弟断。
更好的方式就是將數(shù)據(jù)分割成兩部分:訓(xùn)練集和測(cè)試集咏花。我們可以使用訓(xùn)練集的數(shù)據(jù)來(lái)訓(xùn)練模型,然后用測(cè)試集上的誤差作為最終模型在應(yīng)對(duì)現(xiàn)實(shí)場(chǎng)景中的泛化誤差阀趴。有了測(cè)試集迟螺,我們想要驗(yàn)證模型的最終效果冲秽,只需將訓(xùn)練好的模型在測(cè)試集上計(jì)算誤差,即可認(rèn)為此誤差即為泛化誤差的近似矩父,我們只需讓我們訓(xùn)練好的模型在測(cè)試集上的誤差最小即可锉桑。
這里有幾點(diǎn)需要注意:
- 通常將數(shù)據(jù)集的80%作為訓(xùn)練集,20%作為測(cè)試集窍株;
- 通常需要在開始構(gòu)建模型之前把數(shù)據(jù)集進(jìn)行劃分民轴,防止數(shù)據(jù)窺探偏誤,也就是說(shuō)我們避免了解太多關(guān)于測(cè)試集中的樣本特點(diǎn)球订,防止我們認(rèn)為的挑選有助于測(cè)試集數(shù)據(jù)的模型后裸,這樣的結(jié)果會(huì)過(guò)于樂觀,但是實(shí)際上并沒有預(yù)期的那樣優(yōu)秀冒滩;
- 通常我們?cè)跇?gòu)建模型的時(shí)候需要將數(shù)據(jù)進(jìn)行處理微驶,包括一些數(shù)據(jù)的清洗,數(shù)據(jù)的特征縮放(標(biāo)準(zhǔn)化或者歸一化)开睡,此時(shí)我們只需要在訓(xùn)練集上進(jìn)行這些操作因苹,然后將其在訓(xùn)練集上得到的參數(shù)應(yīng)用到測(cè)試集中,也就是說(shuō)篇恒,在工作流程中扶檐,你不能使用在測(cè)試數(shù)據(jù)集上計(jì)算的得到的任何結(jié)果。比如:我們得到的屬性中可能有缺失值胁艰,因?yàn)樵谶@些操作之前款筑,我們已經(jīng)把數(shù)據(jù)集分成了訓(xùn)練集和測(cè)試集,通常的做法是通過(guò)計(jì)算屬性值的中位數(shù)來(lái)填充缺失值腾么,注意此時(shí)計(jì)算屬性值的中位數(shù)是通過(guò)訓(xùn)練集上的數(shù)據(jù)進(jìn)行計(jì)算的奈梳,當(dāng)我們得到一個(gè)模型的時(shí)候,如果想要測(cè)試模型的測(cè)試誤差來(lái)近似泛化誤差的時(shí)候解虱,可能此時(shí)的測(cè)試集也會(huì)有一些缺失值颈嚼,此時(shí)對(duì)應(yīng)屬性的缺失值是通過(guò)訓(xùn)練集計(jì)算的中位數(shù)來(lái)進(jìn)行填充的;
- 由于測(cè)試集作為對(duì)泛化誤差的近似饭寺,所以訓(xùn)練好模型阻课,最后在測(cè)試集上近似估計(jì)模型的泛化能力。此時(shí)假設(shè)有兩個(gè)不同的機(jī)器學(xué)習(xí)模型艰匙,猶豫不決的時(shí)候限煞,可以通過(guò)訓(xùn)練兩個(gè)模型,然后對(duì)比他們?cè)跍y(cè)試數(shù)據(jù)上的泛化誤差员凝,選擇泛化能力強(qiáng)的模型署驻。
前面說(shuō)了這么多,那如何劃分?jǐn)?shù)據(jù)集為訓(xùn)練集和測(cè)試集呢?其實(shí)很簡(jiǎn)單旺上,可以自己編寫程序瓶蚂,也可以使用sklearn提供的模塊:
通過(guò)簡(jiǎn)單代碼實(shí)現(xiàn):
import numpy as np
def split_train_test(data,test_ratio):
#設(shè)置隨機(jī)數(shù)種子,保證每次生成的結(jié)果都是一樣的
np.random.seed(42)
#permutation隨機(jī)生成0-len(data)隨機(jī)序列
shuffled_indices = np.random.permutation(len(data))
#test_ratio為測(cè)試集所占的百分比
test_set_size = int(len(data) * test_ratio)
test_indices = shuffled_indices[:test_set_size]
train_indices = shuffled_indices[test_set_size:]
#iloc選擇參數(shù)序列中所對(duì)應(yīng)的行
return data.iloc[train_indices],data.iloc[test_indices]
#測(cè)試
train_set,test_set = split_train_test(data,0.2)
print(len(train_set), "train +", len(test_set), "test")
通過(guò)sklearn實(shí)現(xiàn):
from sklearn.model_selection import train_test_split
#data:需要進(jìn)行分割的數(shù)據(jù)集
#random_state:設(shè)置隨機(jī)種子宣吱,保證每次運(yùn)行生成相同的隨機(jī)數(shù)
#test_size:將數(shù)據(jù)分割成訓(xùn)練集的比例
train_set, test_set = train_test_split(data, test_size=0.2, random_state=42)
前面介紹的兩種分割數(shù)據(jù)集的方式都是采用純隨機(jī)的采樣方式窃这,這種方式對(duì)于大量數(shù)據(jù)集以及對(duì)于目標(biāo)值分布均勻的情況是可行的。比如對(duì)于分類任務(wù)征候,我們訓(xùn)練一個(gè)二值分類器杭攻,可能數(shù)據(jù)中包含大量的正例樣本,僅僅包含10%的反例樣本疤坝,此時(shí)的標(biāo)簽分布很不均勻兆解,如果我們通過(guò)隨機(jī)采樣的方式,極端情況下可能將正例樣本都劃分到訓(xùn)練集上跑揉,而反例樣本恰好都分到測(cè)試集锅睛,這樣訓(xùn)練出來(lái)的模型,效果一定不會(huì)太好历谍,所以我們需要采用分層采樣的方式進(jìn)行劃分?jǐn)?shù)據(jù)集现拒,也就是說(shuō)保證訓(xùn)練集中既包含一定比例的正例樣本又要包含一定比例的負(fù)例樣本。
幸運(yùn)的是sklearn提供了我們分層抽樣的函數(shù)扮饶,在這之前先看看官方提供的例子:
from sklearn.model_selection import StratifiedShuffleSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 1, 1])
split = StratifiedShuffleSplit(n_splits=3, test_size=0.5, random_state=0)
print(split ) # doctest: +ELLIPSIS
# StratifiedShuffleSplit(n_splits=3, random_state=0, ...)
for train_index, test_index in split.split(X, y):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
"""
StratifiedShuffleSplit(n_splits=3, random_state=0, test_size=0.5,train_size=None)
TRAIN: [1 2] TEST: [3 0]
TRAIN: [0 2] TEST: [1 3]
TRAIN: [0 2] TEST: [3 1]
"""
通過(guò)上面的例子我們可以了解使用分層進(jìn)行劃分?jǐn)?shù)據(jù)集的大概流程具练,以及各個(gè)參數(shù)的含義:
- n_splits:分割迭代的次數(shù)乍构,如果我們要?jiǎng)澐钟?xùn)練集和測(cè)試集的話甜无,將其設(shè)置為1即可;
- test_size:分割測(cè)試集的比例哥遮;
- random_state:設(shè)置隨機(jī)種子岂丘;
下面通過(guò)兩種方式對(duì)原始的mnist數(shù)據(jù)集進(jìn)行劃分,首先要準(zhǔn)備數(shù)據(jù)集:
from sklearn.datasets import fetch_mldata
#我將最原始的mnist數(shù)據(jù)集下載到當(dāng)前路徑下眠饮,指定data_home
mnist = fetch_mldata('MNIST original',data_home=r"./")
x_data = mnist["data"].reshape((mnist["data"].shape[0],-1))
y_data = mnist["target"].reshape((mnist["target"].shape[0],-1))
print(x_data.shape) #(70000, 784)
print(y_data.shape) #(70000, 1)
- 使用隨機(jī)采樣的方式分割數(shù)據(jù)集:
#使用隨機(jī)采樣方式劃分?jǐn)?shù)據(jù)集
from sklearn.model_selection import train_test_split
import numpy as np
data = np.hstack((x_data,y_data))
#先將數(shù)據(jù)集進(jìn)行拼接奥帘,要不然我們只針對(duì)樣本進(jìn)行采樣的話,會(huì)找不到對(duì)應(yīng)的標(biāo)簽的
train_set,test_set = train_test_split(data,test_size = 0.2,random_state = 42)
print(len(train_set),len(test_set))
"""
56000 14000
"""
- 使用分層采樣的方式分割數(shù)據(jù)集:
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits = 1,test_size = 0.2,random_state = 42)
#根據(jù)mnist["target"]來(lái)進(jìn)行分層采樣
for train_index,test_index in split.split(data[:,:-1],data[:,-1]):
train_set = data[train_index,:]
test_set = data[test_index,:]
print(len(train_set),len(test_set))
"""
56000 14000
"""
如果想要知道抽取的各個(gè)樣本的比例仪召,你可以將數(shù)據(jù)轉(zhuǎn)換成DataFrame對(duì)象(當(dāng)然在處理數(shù)據(jù)的開始你也可以將數(shù)據(jù)轉(zhuǎn)換為DataFrame方便操作):
#將分割后的訓(xùn)練數(shù)據(jù)轉(zhuǎn)換為DataFrame
#這里的參數(shù)data可以是分割之后的訓(xùn)練集或者測(cè)試集
train_data = pd.DataFrame(train_set)
#快速查看對(duì)數(shù)據(jù)的描述
train_data.info()
#查看各個(gè)類別的比例
print(train_data[784].value_counts() / len(train_data))
下面各圖分別是:原始數(shù)據(jù)10個(gè)類別所占的比例寨蹋、隨機(jī)采樣訓(xùn)練集中10個(gè)類別所占比例以及分層采樣訓(xùn)練集中10個(gè)類別所占的比例(當(dāng)然也可以對(duì)測(cè)試集進(jìn)行統(tǒng)計(jì))。
統(tǒng)計(jì)對(duì)比
通過(guò)上面的分析可以看出扔茅,分層采樣出的10個(gè)類別所占的比例和原數(shù)據(jù)中的10個(gè)類別所占的比例很接近已旧。
2.驗(yàn)證集
前面說(shuō)到我們將數(shù)據(jù)集劃分為訓(xùn)練集和測(cè)試集,我們讓模型在訓(xùn)練集上進(jìn)行訓(xùn)練召娜,然后在測(cè)試集上來(lái)近似模型的泛化能力运褪。我們?nèi)绻胍暨x不同的模型的話,可以讓兩個(gè)模型分別在訓(xùn)練集上訓(xùn)練,然后將兩個(gè)訓(xùn)練好的模型分別在測(cè)試集上進(jìn)行測(cè)試秸讹,由于我們把測(cè)試集上的誤差近似近似為泛化誤差檀咙,所以我們自然可以選擇在測(cè)試集上誤差小的模型作為最終我們要選擇的泛化能力強(qiáng)的模型。
但是我們要做的不僅是不同的模型與模型之間的對(duì)比璃诀,很多時(shí)候我們需要對(duì)模型本身進(jìn)行選擇弧可,假如我們有兩個(gè)模型,線性模型和神經(jīng)網(wǎng)絡(luò)模型文虏,我們知道神經(jīng)網(wǎng)絡(luò)的泛化能力要比線性模型要強(qiáng)侣诺,我們選擇了神經(jīng)網(wǎng)絡(luò)模型,但是神經(jīng)網(wǎng)絡(luò)中還有很多的需要人工進(jìn)行選擇的參數(shù)氧秘,比如神經(jīng)網(wǎng)絡(luò)的層數(shù)和每層神經(jīng)網(wǎng)絡(luò)的神經(jīng)元個(gè)數(shù)以及正則化的一些參數(shù)等等年鸳,我們將這些參數(shù)稱為超參數(shù)。這些參數(shù)不同選擇對(duì)模型最終的效果也很重要丸相,我們?cè)陂_發(fā)模型的時(shí)候總是需要調(diào)節(jié)這些超參數(shù)搔确。
現(xiàn)在我們需要調(diào)節(jié)這些超參數(shù)來(lái)使得模型泛化能力最強(qiáng)。我們使用測(cè)試集來(lái)作為泛化誤差估計(jì)灭忠,而我們最終的目的就是選擇泛化能力強(qiáng)的模型膳算,那么我們可以直接通過(guò)模型在測(cè)試集上的誤差來(lái)調(diào)節(jié)這些參數(shù)不就可以了〕谧鳎可能模型在測(cè)試集上的誤差為0涕蜂,但是你拿著這樣的模型去部署到真實(shí)場(chǎng)景中去使用的話,效果可能會(huì)非常差映琳。
這一現(xiàn)象叫做信息泄露机隙。我們使用測(cè)試集作為泛化誤差的近似,所以不到最后是不能將測(cè)試集的信息泄露出去的萨西,就好比考試一樣有鹿,我們平時(shí)做的題相當(dāng)于訓(xùn)練集,測(cè)試集相當(dāng)于最終的考試谎脯,我們通過(guò)最終的考試來(lái)檢驗(yàn)我們最終的學(xué)習(xí)能力葱跋,將測(cè)試集信息泄露出去,相當(dāng)于學(xué)生提前知道了考試題目源梭,那最后再考這些提前知道的考試題目娱俺,當(dāng)然代表不了什么,你在最后的考試中得再高的分?jǐn)?shù)废麻,也不能代表你學(xué)習(xí)能力強(qiáng)荠卷。而如果通過(guò)測(cè)試集來(lái)調(diào)節(jié)模型,相當(dāng)于不僅知道了考試的題目脑溢,學(xué)生還都學(xué)會(huì)怎么做這些題了(因?yàn)槲覀兛隙〞?huì)人為的讓模型在測(cè)試集上的誤差最小僵朗,因?yàn)檫@是你調(diào)整超參數(shù)的目的)赖欣,那再拿這些題考試的話,人人都有可能考滿分验庙,但是并沒有起到檢測(cè)學(xué)生學(xué)習(xí)能力的作用顶吮。原來(lái)我們通過(guò)測(cè)試集來(lái)近似泛化誤差,也就是通過(guò)考試來(lái)檢驗(yàn)學(xué)生的學(xué)習(xí)能力粪薛,但是由于信息泄露悴了,此時(shí)的測(cè)試集即考試無(wú)任何意義,現(xiàn)實(shí)中可能學(xué)生的能力很差违寿。所以湃交,我們?cè)趯W(xué)習(xí)的時(shí)候,老師會(huì)準(zhǔn)備一些小測(cè)試來(lái)幫助我們查缺補(bǔ)漏藤巢,這些小測(cè)試也就是要說(shuō)的驗(yàn)證集搞莺。我們通過(guò)驗(yàn)證集來(lái)作為調(diào)整模型的依據(jù),這樣不至于將測(cè)試集中的信息泄露掂咒。
也就是說(shuō)我們將數(shù)據(jù)劃分訓(xùn)練集才沧、驗(yàn)證集和測(cè)試集。在訓(xùn)練集上訓(xùn)練模型绍刮,在驗(yàn)證集上評(píng)估模型温圆,一旦找到的最佳的參數(shù),就在測(cè)試集上最后測(cè)試一次孩革,測(cè)試集上的誤差作為泛化誤差的近似岁歉。關(guān)于驗(yàn)證集的劃分可以參考測(cè)試集的劃分,其實(shí)都是一樣的膝蜈,這里不再贅述锅移。
吳恩達(dá)老師的視頻中,如果當(dāng)數(shù)據(jù)量不是很大的時(shí)候(萬(wàn)級(jí)別以下)的時(shí)候?qū)⒂?xùn)練集彬檀、驗(yàn)證集以及測(cè)試集劃分為6:2:2帆啃;若是數(shù)據(jù)很大瞬女,可以將訓(xùn)練集窍帝、驗(yàn)證集、測(cè)試集比例調(diào)整為98:1:1诽偷;但是當(dāng)可用的數(shù)據(jù)很少的情況下也可以使用一些高級(jí)的方法坤学,比如留出方,K折交叉驗(yàn)證等报慕。