Lesson 13.1 深度学习建模目标与性能评估理论
Lesson 13.1 深度學(xué)習(xí)建模目標(biāo)與性能評(píng)估理論
??從Lesson 13起,我們講開始系統(tǒng)介紹深度學(xué)習(xí)建模理論,以及建模過程中的優(yōu)化方法。
二、機(jī)器學(xué)習(xí)目標(biāo)與模型評(píng)估方法
??在了解深度學(xué)習(xí)基本模型的概念與實(shí)現(xiàn)方法后,接下來(lái),我們將詳細(xì)探討深度學(xué)習(xí)模型優(yōu)化的常用方法。從上一課的實(shí)驗(yàn)中不難發(fā)現(xiàn),要把一個(gè)模型“建好”已是不容易,而要想辦法把模型的效果進(jìn)行提升,如果沒有基礎(chǔ)理論支持和方法論工具,優(yōu)化過程無(wú)異于盲人摸象。因此,本節(jié)課將從建模的根本目標(biāo)出發(fā),圍繞模型優(yōu)化的基本概念和核心理論進(jìn)行全面梳理,并在此基礎(chǔ)之上介紹相關(guān)實(shí)踐方法,逐漸撥開模型優(yōu)化面前的迷霧。
??我們經(jīng)常會(huì)對(duì)模型的好壞優(yōu)劣進(jìn)行評(píng)估,Lesson 12中我們也使用準(zhǔn)確率、MSE等指標(biāo)評(píng)估建模結(jié)果,看起來(lái)模型評(píng)估是圍繞某項(xiàng)指標(biāo)在進(jìn)行評(píng)估,指標(biāo)好模型就好,指標(biāo)不好模型就不好,其實(shí)并不完全如此。要了解模型的性能其實(shí)并不簡(jiǎn)單,固然我們會(huì)使用某些指標(biāo)去進(jìn)行模型評(píng)估,但其實(shí)指標(biāo)也只是我們了解模型性能的途徑而不是模型性能本身。而要真實(shí)、深刻的評(píng)判模型性能,就必須首先了解機(jī)器學(xué)習(xí)的建模目標(biāo),并在此基礎(chǔ)之上熟悉我們判斷模型是否能夠完成目標(biāo)的一些方法,當(dāng)然,只有真實(shí)了解的模型性能,我們才能進(jìn)一步考慮如何提升模型性能。因此,在正式講解模型優(yōu)化方法之前,我們需要花些時(shí)間討論機(jī)器學(xué)習(xí)算法的建模目標(biāo)、機(jī)器學(xué)習(xí)算法為了能夠達(dá)到目標(biāo)的一般思路,以及評(píng)估模型性能的手段,也就是模型評(píng)估指標(biāo)。
??無(wú)論是機(jī)器學(xué)習(xí)還是傳統(tǒng)的統(tǒng)計(jì)分析模型,核心使命就是探索數(shù)字規(guī)律,而有監(jiān)督學(xué)習(xí)則是希望在探索數(shù)字規(guī)律的基礎(chǔ)上進(jìn)一步對(duì)未來(lái)進(jìn)行預(yù)測(cè),當(dāng)然,在數(shù)字的世界,這個(gè)預(yù)測(cè)未來(lái),也就是預(yù)測(cè)未來(lái)某項(xiàng)事件的某項(xiàng)數(shù)值指標(biāo),如某地區(qū)未來(lái)患病人次、具備某種數(shù)字特征的圖片上的動(dòng)物是哪一類,此處的未來(lái)也并非指絕對(duì)意義上的以后的時(shí)間,而是在模型訓(xùn)練階段暫時(shí)未接觸到的數(shù)據(jù)。正是因?yàn)槟P陀辛嗽谖粗獦?biāo)簽情況下進(jìn)行預(yù)判的能力,有監(jiān)督學(xué)習(xí)才有了存在的價(jià)值,但我們知道,基本上所有的模型,都只能從以往的歷史經(jīng)驗(yàn)當(dāng)中進(jìn)行學(xué)習(xí),也就是在以往的、已經(jīng)知道的數(shù)據(jù)集上進(jìn)行訓(xùn)練(如上述利用已知數(shù)據(jù)集進(jìn)行模型訓(xùn)練,如利用過往股票數(shù)據(jù)訓(xùn)練時(shí)間序列模型),這里的核心矛盾在于,在以往的數(shù)據(jù)中提取出來(lái)的經(jīng)驗(yàn)(也就是模型),怎么證明能夠在接下來(lái)的數(shù)據(jù)中也具備一定的預(yù)測(cè)能力呢?或者說(shuō),要怎么訓(xùn)練模型,才能讓模型在未知的數(shù)據(jù)集上也擁有良好的表現(xiàn)呢?
??目的相同,但在具體的實(shí)現(xiàn)方法上,傳統(tǒng)的數(shù)理統(tǒng)計(jì)分析建模和機(jī)器學(xué)習(xí)采用了不同的解決方案。
??首先,在統(tǒng)計(jì)分析領(lǐng)域,我們會(huì)假設(shè)現(xiàn)在的數(shù)據(jù)和未來(lái)的數(shù)據(jù)其實(shí)都屬于某個(gè)存在但不可獲得的總體,也就是說(shuō),現(xiàn)在和未來(lái)的數(shù)據(jù)都是從某個(gè)總體中抽樣而來(lái)的,都是這個(gè)總體的樣本。而正式因?yàn)檫@些數(shù)據(jù)屬于同一個(gè)總體,因此具備某些相同的規(guī)律,而現(xiàn)在挖掘到的數(shù)據(jù)規(guī)律也就在某些程度上可以應(yīng)用到未來(lái)的數(shù)據(jù)當(dāng)中去,不過呢,不同抽樣的樣本之間也會(huì)有個(gè)體之間的區(qū)別,另外模型本身也無(wú)法完全捕獲規(guī)律,而這些就是誤差的來(lái)源。
??雖然樣本和總體的概念是統(tǒng)計(jì)學(xué)概念,但樣本和總體的概念所假設(shè)的前后數(shù)據(jù)的“局部規(guī)律一致性”,卻是所有機(jī)器學(xué)習(xí)建模的基礎(chǔ)。試想一下,如果獲取到的數(shù)據(jù)前后描繪的不是一件事情,那么模型訓(xùn)練也就毫無(wú)價(jià)值(比如拿著A股走勢(shì)預(yù)測(cè)的時(shí)間序列預(yù)測(cè)某地區(qū)下個(gè)季度患病人次)。因此,無(wú)論是機(jī)器學(xué)習(xí)所強(qiáng)調(diào)的從業(yè)務(wù)角度出發(fā),要確保前后數(shù)據(jù)描述的一致性,還是統(tǒng)計(jì)分析所強(qiáng)調(diào)的樣本和總體的概念,都是建模的基礎(chǔ)。
??在有了假設(shè)基礎(chǔ)之后,統(tǒng)計(jì)分析就會(huì)利用一系列的數(shù)學(xué)方法和數(shù)理統(tǒng)計(jì)工具去推導(dǎo)總體的基本規(guī)律,也就是變量的分布規(guī)律和一些統(tǒng)計(jì)量的取值,由于這個(gè)過程是通過已知的樣本去推斷未知的總體,因此會(huì)有大量的“估計(jì)”和“檢驗(yàn)”,在確定了總體的基本分布規(guī)律之后,才能夠進(jìn)一步使用統(tǒng)計(jì)分析模型構(gòu)建模型(這也就是為什么在數(shù)理統(tǒng)計(jì)分析領(lǐng)域,構(gòu)建線性回歸模型需要先進(jìn)行一系列的檢驗(yàn)和變換的原因),當(dāng)然,這些模型都是在總體規(guī)律基礎(chǔ)之上、根據(jù)樣本具體的數(shù)值進(jìn)行的建模,我們自然有理由相信這些模型對(duì)接下來(lái)仍然是從總體中抽樣而來(lái)的樣本還是會(huì)具備一定的預(yù)測(cè)能力,這也就是我們對(duì)統(tǒng)計(jì)分析模型“信心”的來(lái)源。簡(jiǎn)單來(lái)說(shuō),就是我們通過樣本推斷總體的規(guī)律,然后結(jié)合總體的規(guī)律和樣本的數(shù)值構(gòu)建模型,由于模型也描繪了總體規(guī)律,所以模型對(duì)接下來(lái)從總體當(dāng)中抽樣而來(lái)的數(shù)據(jù)也會(huì)有不錯(cuò)的預(yù)測(cè)效果,這個(gè)過程我們可以通過下圖來(lái)進(jìn)行表示。
??而對(duì)于機(jī)器學(xué)習(xí)來(lái)說(shuō),并沒有借助“樣本-總體”的基本理論,而是簡(jiǎn)單的采用了一種后驗(yàn)的方法來(lái)判別模型有效性,前面說(shuō)到,我們假設(shè)前后獲取的數(shù)據(jù)擁有規(guī)律一致性,但數(shù)據(jù)彼此之間又略有不同,為了能夠在捕捉規(guī)律的同時(shí)又能考慮到“略有不同”所帶來(lái)的誤差,機(jī)器學(xué)習(xí)會(huì)把當(dāng)前能獲取到的數(shù)據(jù)劃分成訓(xùn)練集(trainSet)和測(cè)試集(testSet),在訓(xùn)練集上構(gòu)建模型,然后帶入測(cè)試集的數(shù)據(jù),觀測(cè)在測(cè)試集上模型預(yù)測(cè)結(jié)果和真實(shí)結(jié)果之間的差異。這個(gè)過程其實(shí)就是在模擬獲取到真實(shí)數(shù)據(jù)之后模型預(yù)測(cè)的情況,此前說(shuō)到,模型能夠在未知標(biāo)簽的數(shù)據(jù)集上進(jìn)行預(yù)測(cè),就是模型的核心價(jià)值,此時(shí)的測(cè)試集就是用于模擬未來(lái)的未知標(biāo)簽的數(shù)據(jù)集。如果模型能夠在測(cè)試集上有不錯(cuò)的預(yù)測(cè)效果,我們就“簡(jiǎn)單粗暴”的認(rèn)為模型可以在真實(shí)的未來(lái)獲取的未知數(shù)據(jù)集上有不錯(cuò)的表現(xiàn)。其一般過程可以由下圖表示。
??雖然對(duì)比起數(shù)理統(tǒng)計(jì)分析,機(jī)器學(xué)習(xí)的證明模型有效性的過程更加“簡(jiǎn)單”,畢竟只要一次“模擬”成功,我們就認(rèn)為模型對(duì)未來(lái)的數(shù)據(jù)也擁有判別效力,但這種“簡(jiǎn)單”的處理方式卻非常實(shí)用,可以說(shuō),這是一種經(jīng)過長(zhǎng)期實(shí)踐被證明的行之有效的方法。這也是為什么機(jī)器學(xué)習(xí)很多時(shí)候也被認(rèn)為是實(shí)證類的方法,而在以后的學(xué)習(xí)中,我們也將了解到,機(jī)器學(xué)習(xí)有很多方法都是“經(jīng)驗(yàn)總結(jié)的結(jié)果”。相比數(shù)理統(tǒng)計(jì)分析,確實(shí)沒有“那么嚴(yán)謹(jǐn)”,但更易于理解的理論和更通用的方法,卻使得機(jī)器學(xué)習(xí)可以在更為廣泛的應(yīng)用場(chǎng)景中發(fā)揮作用。(當(dāng)然,負(fù)面影響卻是,機(jī)器學(xué)習(xí)在曾經(jīng)的很長(zhǎng)一段時(shí)間內(nèi)并不是主流的算法。)
??據(jù)此,我們稱模型在訓(xùn)練集上誤差稱為訓(xùn)練誤差,在測(cè)試集上的誤差稱為泛化誤差,不過畢竟在測(cè)試集上進(jìn)行測(cè)試還只是模擬演習(xí),我們采用模型的泛化能力來(lái)描述模型在未知數(shù)據(jù)上的判別能力,當(dāng)然泛化能力無(wú)法準(zhǔn)確衡量(未知的數(shù)據(jù)還未到來(lái),到來(lái)的數(shù)據(jù)都變成了已知數(shù)據(jù)),我們只能通過模型在訓(xùn)練集和測(cè)試集上的表現(xiàn),判別模型泛化能力,當(dāng)然,就像此前說(shuō)的一樣,最基本的,我們會(huì)通過模型在測(cè)試集上的表現(xiàn)來(lái)判斷模型的泛化能力。
三、手動(dòng)實(shí)現(xiàn)訓(xùn)練集和測(cè)試集切分
??接下來(lái)我們開始實(shí)踐模型評(píng)估過程,首先是對(duì)訓(xùn)練集和測(cè)試集的劃分,我們嘗試創(chuàng)建一個(gè)切分訓(xùn)練集和測(cè)試集的函數(shù)。
def data_split(features, labels, rate=0.7):"""訓(xùn)練集和測(cè)試集切分函數(shù):param features: 輸入的特征張量:param labels:輸入的標(biāo)簽張量:param rate:訓(xùn)練集占所有數(shù)據(jù)的比例:return Xtrain, Xtest, ytrain, ytest:返回特征張量的訓(xùn)練集、測(cè)試集,以及標(biāo)簽張量的訓(xùn)練集、測(cè)試集 """num_examples = len(features) # 總數(shù)據(jù)量indices = list(range(num_examples)) # 數(shù)據(jù)集行索引random.shuffle(indices) # 亂序調(diào)整 num_train = int(num_examples * rate) # 訓(xùn)練集數(shù)量 indices_train = torch.tensor(indices[: num_train]) # 在已經(jīng)亂序的的indices中挑出前num_train數(shù)量的行索引值indices_test = torch.tensor(indices[num_train: ]) Xtrain = features[indices_train] # 訓(xùn)練集特征ytrain = labels[indices_train] # 訓(xùn)練集標(biāo)簽Xtest = features[indices_test] # 測(cè)試集特征ytest = labels[indices_test] # 測(cè)試集標(biāo)簽return Xtrain, Xtest, ytrain, ytest??一般來(lái)說(shuō),訓(xùn)練集和測(cè)試集可以按照8:2或7:3比例進(jìn)行劃分。在進(jìn)行數(shù)據(jù)劃分的過程中,如果測(cè)試集劃分?jǐn)?shù)據(jù)過多,參與模型訓(xùn)練的數(shù)據(jù)就會(huì)相應(yīng)減少,而訓(xùn)練數(shù)據(jù)不足則會(huì)導(dǎo)致模型無(wú)法正常訓(xùn)練、損失函數(shù)無(wú)法收斂、模型過擬合等問題,但如果反過來(lái)測(cè)試集劃分?jǐn)?shù)據(jù)過少,則無(wú)法代表一般數(shù)據(jù)情況測(cè)試模型是否對(duì)未知數(shù)據(jù)也有很好的預(yù)測(cè)作用。因此,根據(jù)經(jīng)驗(yàn),我們一般來(lái)說(shuō)會(huì)按照8:2或7:3比例進(jìn)行劃分。
??看到這里,相信肯定有小伙伴覺得根據(jù)所謂的“經(jīng)驗(yàn)”來(lái)定數(shù)據(jù)集劃分比例不太嚴(yán)謹(jǐn),有沒有一種方法能夠“精準(zhǔn)”的確定什么劃分比例最佳呢?例如通過類似最小二乘法或者梯度下降這類優(yōu)化算法來(lái)計(jì)算劃分比例?各位同學(xué)可以嘗試著進(jìn)行思考,并給出自己的答案。課程中將在下一節(jié)介紹參數(shù)和超參數(shù)時(shí)給出詳細(xì)解答。
??值得一提的是,在機(jī)器學(xué)習(xí)領(lǐng)域,充斥著大量的“經(jīng)驗(yàn)之談”或者“約定俗成”的規(guī)則,一方面這些經(jīng)驗(yàn)為建模提供了諸多便捷、也節(jié)省了很多算力,但另一方面,通過經(jīng)驗(yàn)來(lái)決定影響模型效果的一些“超參數(shù)”取值的不嚴(yán)謹(jǐn)?shù)淖龇?#xff0c;也被數(shù)理統(tǒng)計(jì)分析流派所詬病。
接下來(lái),測(cè)試函數(shù)性能
f = torch.arange(10) # 創(chuàng)建特征0-9 f #tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) l = torch.arange(1, 11) # 創(chuàng)建標(biāo)簽1-10,保持和特征+1的關(guān)系 l #tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) data_split(f, l) #(tensor([1, 6, 4, 5, 8, 3, 0]), # tensor([9, 7, 2]), # tensor([2, 7, 5, 6, 9, 4, 1]), # tensor([10, 8, 3]))接下來(lái),還是在上一節(jié)課的內(nèi)容上,嘗試帶入訓(xùn)練集進(jìn)行建模,利用測(cè)試集評(píng)估模型建模效果
# 設(shè)置隨機(jī)數(shù)種子 torch.manual_seed(420) # 生成回歸類數(shù)據(jù)集 features, labels = tensorGenReg() #<torch._C.Generator at 0x26f6196e570> features #tensor([[-0.0070, 0.5044, 1.0000], # [ 0.6704, -0.3829, 1.0000], # [ 0.0302, 0.3826, 1.0000], # ..., # [-0.9164, -0.6087, 1.0000], # [ 0.7815, 1.2865, 1.0000], # [ 1.4819, 1.1390, 1.0000]]) torch.manual_seed(420) # 初始化數(shù)據(jù) features, labels = tensorGenReg()# 切分訓(xùn)練集和測(cè)試集 Xtrain, Xtest, ytrain, ytest = data_split(features, labels)# 初始化核心參數(shù) batch_size = 10 # 小批的數(shù)量 lr = 0.03 # 學(xué)習(xí)率 num_epochs = 5 # 訓(xùn)練過程遍歷幾次數(shù)據(jù) w = torch.zeros(3, 1, requires_grad = True) # 隨機(jī)設(shè)置初始權(quán)重# 參與訓(xùn)練的模型方程 net = linreg # 使用回歸方程 loss = MSE_loss # 均方誤差的一半作為損失函數(shù)# 模型訓(xùn)練過程 for epoch in range(num_epochs):for X, y in data_iter(batch_size, Xtrain, ytrain):l = loss(net(X, w), y)l.backward()sgd(w, lr)查看訓(xùn)練結(jié)果
w #tensor([[ 2.0002], # [-1.0002], # [ 0.9996]], requires_grad=True)查看模型在訓(xùn)練集、測(cè)試集上的MSE
MSE_loss(torch.mm(Xtrain, w), ytrain) #tensor(0.0001, grad_fn=<DivBackward0>) MSE_loss(torch.mm(Xtest, w), ytest) #tensor(9.9141e-05, grad_fn=<DivBackward0>)至此,我們就完成了一整個(gè)從數(shù)據(jù)集劃分,到訓(xùn)練集訓(xùn)練,再到測(cè)試集上測(cè)試模型性能的一整個(gè)流程。
四、Dataset和DataLoader基本使用方法與數(shù)據(jù)集切分函數(shù)
??接下來(lái),我們嘗試使用PyTorch原生庫(kù)來(lái)實(shí)現(xiàn)上述功能,不過這個(gè)實(shí)現(xiàn)過程略顯復(fù)雜,首先我們需要了解Dataset和DataLoader的基本使用方法。
1.Dataset和DataLoader的基本使用方法
- random_split隨機(jī)切分函數(shù)
??首先,在PyTorch的torch.utils.data中,提供了random_split函數(shù)可用于數(shù)據(jù)集切分。
簡(jiǎn)單測(cè)試函數(shù)功能
t = torch.arange(12).reshape(4, 3) t #tensor([[ 0, 1, 2], # [ 3, 4, 5], # [ 6, 7, 8], # [ 9, 10, 11]]) random_split(t, [2, 2]) # 輸入切分的每部分?jǐn)?shù)據(jù)集數(shù)量 #[<torch.utils.data.dataset.Subset at 0x15872bd9b08>, # <torch.utils.data.dataset.Subset at 0x15872bd9c48>]根據(jù)生成結(jié)果可知,random_split函數(shù)其實(shí)生成了生成器切分結(jié)果的生成器,并不是和此前定義的函數(shù)一樣,直接切分?jǐn)?shù)據(jù)后返回。當(dāng)然這也符合utils.data模塊主要生成映射式和迭代式對(duì)象的一般規(guī)定。
train, test = random_split(t, [2, 2])使用print函數(shù)查看生成器內(nèi)容
for tr, te in random_split(t, [2, 2]):print(tr, te) #tensor([ 9, 10, 11]) tensor([0, 1, 2]) #tensor([3, 4, 5]) tensor([6, 7, 8])- Dataset和Dataloader
??由于在大多數(shù)調(diào)庫(kù)建模過程中,我們都是先通過創(chuàng)建Dataset的子類并將數(shù)據(jù)保存為該子類類型,然后再使用DataLoader進(jìn)行數(shù)據(jù)載入,因此更為通用的做法是先利用Dataset和DatasetLoader這兩個(gè)類進(jìn)行數(shù)據(jù)的讀取、預(yù)處理和載入,然后再使用random_split函數(shù)進(jìn)行切分。
??再次強(qiáng)調(diào),Dataset類主要負(fù)責(zé)數(shù)據(jù)類的生成,在PyTorch中,所有數(shù)據(jù)集都是Dataset的子類;而DatasetLoader類則是加載模型訓(xùn)練的接口,二者基本使用流程如下:
- 創(chuàng)建數(shù)據(jù)類
??根據(jù)此前描述,PyTorch中所有的數(shù)據(jù)都是Dataset的子類,換而言之就是在使用PyTorch建模訓(xùn)練數(shù)據(jù)時(shí),需要?jiǎng)?chuàng)建一個(gè)和數(shù)據(jù)集對(duì)應(yīng)的類來(lái)表示該數(shù)據(jù)集,此前我們使用的TensorDataset函數(shù)其實(shí)就是一個(gè)簡(jiǎn)單的類型轉(zhuǎn)化函數(shù),將數(shù)據(jù)統(tǒng)一轉(zhuǎn)化為“TensorDataset”類然后帶入模型進(jìn)行計(jì)算。
features, labels = tensorGenReg(bias=False) features #tensor([[ 0.5846, 0.4064, -0.7022], # [ 0.5943, 0.5927, 0.8111], # [-0.4947, 0.2168, 0.0981], # ..., # [-0.4568, -1.1319, -1.7560], # [-1.3112, -0.9356, 1.5156], # [-1.0726, 0.8814, -1.6092]]) data = TensorDataset(features, labels) data #<torch.utils.data.dataset.TensorDataset at 0x17be64eeb48>而TensorDataset其實(shí)使用面較窄,最直接的限制就是該函數(shù)只能將張量類型轉(zhuǎn)化為TensorDataset類
TensorDataset([1,2], 1) #AttributeError: 'list' object has no attribute 'size' TensorDataset? #Init signature: TensorDataset(*args, **kwds) #Docstring: #Dataset wrapping tensors.#Each sample will be retrieved by indexing tensors along the first dimension.#Arguments: # *tensors (Tensor): tensors that have the same size of the first dimension. #File: d:\users\asus\anaconda3\lib\site-packages\torch\utils\data\dataset.py #Type: type #Subclasses:更加通用的數(shù)據(jù)讀取方法則是手動(dòng)創(chuàng)建一個(gè)繼承自torch.utils.data.dataset的數(shù)據(jù)類,用來(lái)作為當(dāng)前數(shù)據(jù)的表示。例如Lesson 11中的乳腺癌數(shù)據(jù),通過如下方式進(jìn)行讀取
from sklearn.datasets import load_breast_cancer as LBC data = LBC()簡(jiǎn)單查看data數(shù)據(jù)集
data.data # 返回?cái)?shù)據(jù)集的特征數(shù)組 #array([[1.799e+01, 1.038e+01, 1.228e+02, ..., 2.654e-01, 4.601e-01, # 1.189e-01], # [2.057e+01, 1.777e+01, 1.329e+02, ..., 1.860e-01, 2.750e-01, # 8.902e-02], # [1.969e+01, 2.125e+01, 1.300e+02, ..., 2.430e-01, 3.613e-01, # 8.758e-02], # ..., # [1.660e+01, 2.808e+01, 1.083e+02, ..., 1.418e-01, 2.218e-01, # 7.820e-02], # [2.060e+01, 2.933e+01, 1.401e+02, ..., 2.650e-01, 4.087e-01, # 1.240e-01], # [7.760e+00, 2.454e+01, 4.792e+01, ..., 0.000e+00, 2.871e-01, # 7.039e-02]]) data.target # 返回?cái)?shù)據(jù)集的標(biāo)簽數(shù)組 #array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, # 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, # 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, # . . . . . . # 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, # 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1]) len(data.data) # 返回?cái)?shù)據(jù)集總個(gè)數(shù) #569??接下來(lái),創(chuàng)建一個(gè)用于表示該數(shù)據(jù)集的Dataset的子類。在創(chuàng)建Dataset的子類過程中,必須要重寫__getitem__方法和__len__方法,其中__getitem__方法返回輸入索引后對(duì)應(yīng)的特征和標(biāo)簽,而__len__方法則返回?cái)?shù)據(jù)集的總數(shù)據(jù)個(gè)數(shù)。當(dāng)然,在必須要進(jìn)行的__init__初始化過程中,我們也可輸入可代表數(shù)據(jù)集基本屬性的相關(guān)內(nèi)容,包括數(shù)據(jù)集的特征、標(biāo)簽、大小等等,視情況而定。
class LBCDataset(Dataset):def __init__(self,data): # 創(chuàng)建該類時(shí)需要輸入sklearn導(dǎo)入的數(shù)據(jù)集self.features = data.data # features屬性返回?cái)?shù)據(jù)集特征self.labels = data.target # labels屬性返回?cái)?shù)據(jù)集標(biāo)簽self.lens = len(data.data) # lens屬性返回?cái)?shù)據(jù)集大小def __getitem__(self, index):# 調(diào)用該方法時(shí)需要輸入index數(shù)值,方法最終返回index對(duì)應(yīng)的特征和標(biāo)簽return self.features[index,:],self.labels[index] def __len__(self):# 調(diào)用該方法不需要輸入額外參數(shù),方法最終返回?cái)?shù)據(jù)集大小return self.lensdata = LBC() LBC_data = LBCDataset(data)LBC_data.features #array([[1.799e+01, 1.038e+01, 1.228e+02, ..., 2.654e-01, 4.601e-01, # 1.189e-01], # [2.057e+01, 1.777e+01, 1.329e+02, ..., 1.860e-01, 2.750e-01, # 8.902e-02], # [1.969e+01, 2.125e+01, 1.300e+02, ..., 2.430e-01, 3.613e-01, # 8.758e-02], # ..., # [1.660e+01, 2.808e+01, 1.083e+02, ..., 1.418e-01, 2.218e-01, # 7.820e-02], # [2.060e+01, 2.933e+01, 1.401e+02, ..., 2.650e-01, 4.087e-01, # 1.240e-01], # [7.760e+00, 2.454e+01, 4.792e+01, ..., 0.000e+00, 2.871e-01, # 7.039e-02]]) LBC_data.lens #569 # 查看第三條數(shù)據(jù) LBC_data.__getitem__(2) #(array([1.969e+01, 2.125e+01, 1.300e+02, 1.203e+03, 1.096e-01, 1.599e-01, # 1.974e-01, 1.279e-01, 2.069e-01, 5.999e-02, 7.456e-01, 7.869e-01, # 4.585e+00, 9.403e+01, 6.150e-03, 4.006e-02, 3.832e-02, 2.058e-02, # 2.250e-02, 4.571e-03, 2.357e+01, 2.553e+01, 1.525e+02, 1.709e+03, # 1.444e-01, 4.245e-01, 4.504e-01, 2.430e-01, 3.613e-01, 8.758e-02]), # 0) LBC_data.features[2] #array([1.969e+01, 2.125e+01, 1.300e+02, 1.203e+03, 1.096e-01, 1.599e-01, # 1.974e-01, 1.279e-01, 2.069e-01, 5.999e-02, 7.456e-01, 7.869e-01, # 4.585e+00, 9.403e+01, 6.150e-03, 4.006e-02, 3.832e-02, 2.058e-02, # 2.250e-02, 4.571e-03, 2.357e+01, 2.553e+01, 1.525e+02, 1.709e+03, # 1.444e-01, 4.245e-01, 4.504e-01, 2.430e-01, 3.613e-01, 8.758e-02]) LBC_data.labels[2] #0封裝好的數(shù)據(jù)可以直接進(jìn)行索引,并且能夠返回實(shí)體結(jié)果
LBC_data[1] #(array([2.057e+01, 1.777e+01, 1.329e+02, 1.326e+03, 8.474e-02, 7.864e-02, # 8.690e-02, 7.017e-02, 1.812e-01, 5.667e-02, 5.435e-01, 7.339e-01, # 3.398e+00, 7.408e+01, 5.225e-03, 1.308e-02, 1.860e-02, 1.340e-02, # 1.389e-02, 3.532e-03, 2.499e+01, 2.341e+01, 1.588e+02, 1.956e+03, # 1.238e-01, 1.866e-01, 2.416e-01, 1.860e-01, 2.750e-01, 8.902e-02]), # 0) LBC_data.__getitem__(1) #(array([2.057e+01, 1.777e+01, 1.329e+02, 1.326e+03, 8.474e-02, 7.864e-02, # 8.690e-02, 7.017e-02, 1.812e-01, 5.667e-02, 5.435e-01, 7.339e-01, # 3.398e+00, 7.408e+01, 5.225e-03, 1.308e-02, 1.860e-02, 1.340e-02, # 1.389e-02, 3.532e-03, 2.499e+01, 2.341e+01, 1.588e+02, 1.956e+03, # 1.238e-01, 1.866e-01, 2.416e-01, 1.860e-01, 2.750e-01, 8.902e-02]), # 0) LBC_data[:] #(array([[1.799e+01, 1.038e+01, 1.228e+02, ..., 2.654e-01, 4.601e-01, # 1.189e-01], # [2.057e+01, 1.777e+01, 1.329e+02, ..., 1.860e-01, 2.750e-01, # 8.902e-02], # [1.969e+01, 2.125e+01, 1.300e+02, ..., 2.430e-01, 3.613e-01, # 8.758e-02], # ..., # [1.660e+01, 2.808e+01, 1.083e+02, ..., 1.418e-01, 2.218e-01, # 7.820e-02], # [2.060e+01, 2.933e+01, 1.401e+02, ..., 2.650e-01, 4.087e-01, # 1.240e-01], # [7.760e+00, 2.454e+01, 4.792e+01, ..., 0.000e+00, 2.871e-01, # 7.039e-02]]), # array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, # 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, # 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, # 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, # . . . . . . # 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, # 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, # 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1]))另外,我們可以使用random_split方法對(duì)其進(jìn)行切分
# 確定訓(xùn)練集、測(cè)試集大小,此處以7:3劃分訓(xùn)練集和測(cè)試集 num_train = int(LBC_data.lens * 0.7) num_test = LBC_data.lens - num_trainnum_train # 訓(xùn)練集個(gè)數(shù) num_test # 測(cè)試集個(gè)數(shù) #398 #171 LBC_train, LBC_test = random_split(LBC_data, [num_train, num_test])注,此時(shí)切分的結(jié)果是一個(gè)映射式的對(duì)象,只有dataset和indices兩個(gè)屬性,其中dataset屬性用于查看原數(shù)據(jù)集對(duì)象,indices屬性用于查看切分后數(shù)據(jù)集的每一條數(shù)據(jù)的index(序號(hào))。
LBC_train? #Type: Subset #String form: <torch.utils.data.dataset.Subset object at 0x0000017B8803D648> #Length: 398 #File: d:\users\asus\anaconda3\lib\site-packages\torch\utils\data\dataset.py #Docstring: #Subset of a dataset at specified indices. # #Arguments: # dataset (Dataset): The whole Dataset # indices (sequence): Indices in the whole set selected for subset LBC_train.dataset #<__main__.LBCDataset at 0x7fb8ac3adb50>通過切分結(jié)果還原原始數(shù)據(jù)集
LBC_train.dataset == LBC_data # 還原原數(shù)據(jù)集 #True在原始數(shù)據(jù)集中查找切分?jǐn)?shù)據(jù)集
LBC_train.indices[:10] # 抽取的訓(xùn)練集數(shù)據(jù)的index #[384, 475, 64, 490, 496, 136, 123, 300, 175, 342]當(dāng)然,無(wú)論是迭代式生成數(shù)據(jù)還是映射式生成數(shù)據(jù),都可以使用print查看數(shù)據(jù)
for i in LBC_train:print(i)break #(array([1.328e+01, 1.372e+01, 8.579e+01, 5.418e+02, 8.363e-02, 8.575e-02, # 5.077e-02, 2.864e-02, 1.617e-01, 5.594e-02, 1.833e-01, 5.308e-01, # 1.592e+00, 1.526e+01, 4.271e-03, 2.073e-02, 2.828e-02, 8.468e-03, # 1.461e-02, 2.613e-03, 1.424e+01, 1.737e+01, 9.659e+01, 6.237e+02, # 1.166e-01, 2.685e-01, 2.866e-01, 9.173e-02, 2.736e-01, 7.320e-02]), 1) LBC_data.__getitem__(384) # 驗(yàn)證是否是LBC_train的第一條數(shù)據(jù) #(array([1.328e+01, 1.372e+01, 8.579e+01, 5.418e+02, 8.363e-02, 8.575e-02, # 5.077e-02, 2.864e-02, 1.617e-01, 5.594e-02, 1.833e-01, 5.308e-01, # 1.592e+00, 1.526e+01, 4.271e-03, 2.073e-02, 2.828e-02, 8.468e-03, # 1.461e-02, 2.613e-03, 1.424e+01, 1.737e+01, 9.659e+01, 6.237e+02, # 1.166e-01, 2.685e-01, 2.866e-01, 9.173e-02, 2.736e-01, 7.320e-02]), # 1) LBC_data[LBC_train.indices][1] #array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, # 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, # 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, # 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, # 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, # 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, # 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, # 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, # 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, # 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, # 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, # 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, # 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, # 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, # 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, # 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, # 0, 1])還是需要強(qiáng)調(diào),雖然PyTorch的數(shù)據(jù)表示形式會(huì)略顯復(fù)雜,但這是應(yīng)對(duì)復(fù)雜大規(guī)模數(shù)據(jù)計(jì)算之必須,面對(duì)海量、非結(jié)構(gòu)化數(shù)據(jù),我們很難去查看一條條數(shù)據(jù),而只能通過一些數(shù)據(jù)集的特性來(lái)探索數(shù)據(jù)信息。
??然后使用DataLoader函數(shù)進(jìn)行數(shù)據(jù)轉(zhuǎn)化,由一般數(shù)據(jù)狀態(tài)轉(zhuǎn)化為“可建模”的狀態(tài)。所謂“可建模”狀態(tài),指的是經(jīng)過DataLoader處理的數(shù)據(jù),不僅包含數(shù)據(jù)原始的數(shù)據(jù)信息,還包含數(shù)據(jù)處理方法信息,如調(diào)用幾個(gè)線程進(jìn)行訓(xùn)練、分多少批次等,DataLoader常用參數(shù)如下:
- batch_size:每次迭代輸入多少數(shù)據(jù),如果是小批量梯度下降,則輸入的數(shù)據(jù)量就是小批量迭代過程中“小批”的數(shù)量
- shuffle:是否需要先打亂順序然后再進(jìn)行小批量的切分,一般訓(xùn)練集需要亂序,而測(cè)試集亂序沒有意義
- num_worker:啟動(dòng)多少線程進(jìn)行計(jì)算
其他更多參數(shù),將隨著我們介紹的深入逐步進(jìn)行介紹
DataLoader?train_loader = DataLoader(LBC_train, batch_size=10, shuffle=True)test_loader = DataLoader(LBC_test, batch_size=10, shuffle=False)此處需要注意,對(duì)于測(cè)試集來(lái)說(shuō),數(shù)據(jù)裝載并不是一定要進(jìn)行的,如果測(cè)試集只是用于檢測(cè)模型效果,有時(shí)可以不用裝載直接帶入計(jì)算。
同樣,經(jīng)過DataLoader處理后的數(shù)據(jù)也可以使用dataset屬性查看原數(shù)據(jù)
train_loader.dataset #<torch.utils.data.dataset.Subset at 0x15819156e88> LBC_train #<torch.utils.data.dataset.Subset at 0x15819156e88> train_loader.dataset == LBC_train #True這里值得一提的是,市面上有很多教材在介紹PyTorch深度學(xué)習(xí)建模過程中的數(shù)據(jù)集劃分過程,會(huì)推薦使用scikit-learn中的train_test_split函數(shù)。該函數(shù)是可以非常便捷的完成數(shù)據(jù)集切分,但這種做法只能用于單機(jī)運(yùn)行的數(shù)據(jù),并且切分之后還要調(diào)用Dataset、DataLoader模塊進(jìn)行數(shù)據(jù)封裝和加載,切分過程看似簡(jiǎn)單,但其實(shí)會(huì)額外占用非常多的存儲(chǔ)空間和計(jì)算資源,當(dāng)進(jìn)行超大規(guī)模數(shù)據(jù)訓(xùn)練時(shí),所造成的影響會(huì)非常明顯(當(dāng)然,也有可能由于數(shù)據(jù)規(guī)模過大,本地?zé)o法運(yùn)行)。因此,為了更好的適應(yīng)深度學(xué)習(xí)真實(shí)應(yīng)用場(chǎng)景,在使用包括數(shù)據(jù)切分等常用函數(shù)時(shí),函數(shù)使用優(yōu)先級(jí)是
Pytorch原生函數(shù)和類>依據(jù)張量及其常用方法手動(dòng)創(chuàng)建的函數(shù)>Scikit-Learn函數(shù)2.建模及評(píng)估過程
??接下來(lái),我們嘗試通過調(diào)庫(kù)實(shí)現(xiàn)完整的數(shù)據(jù)切分、訓(xùn)練、查看建模結(jié)果一整個(gè)流程。
- 數(shù)據(jù)準(zhǔn)備過程
- 構(gòu)建模型
- 模型訓(xùn)練與測(cè)試
查看模型在訓(xùn)練集上表現(xiàn),首先我們可以通過dataset和indices方法還原訓(xùn)練數(shù)據(jù)集
data_train.indices # 返回訓(xùn)練集索引 #[705, # 223, # 553, # 386, # ... data[data_train.indices] # 返回訓(xùn)練集 #(tensor([[ 1.1406, 0.4082], # [ 0.4877, 0.8226], # [-0.2876, 0.8771], # ..., # [ 2.0659, 0.6420], # [-0.5856, 1.1246], # [-0.3630, -1.5988]]), # tensor([[ 2.8812e+00], # [ 1.1609e+00], # [-4.6634e-01], # [ 3.9260e-01], # ........ data[data_train.indices][0] # 返回訓(xùn)練集的特征 #tensor([[ 1.1406, 0.4082], # [ 0.4877, 0.8226], # [-0.2876, 0.8771], # ..., # [ 2.0659, 0.6420], # [-0.5856, 1.1246], # [-0.3630, -1.5988]]) # 計(jì)算訓(xùn)練集MSE F.mse_loss(LR_model(data[data_train.indices][0]), data[data_train.indices][1]) #tensor(9.4741e-05, grad_fn=<MseLossBackward>) # 計(jì)算測(cè)試集MSE F.mse_loss(LR_model(data[data_test.indices][0]), data[data_test.indices][1]) #tensor(0.0001, grad_fn=<MseLossBackward>)至此,即完成了整個(gè)從數(shù)據(jù)集切分到模型訓(xùn)練,再到查看模型在不同數(shù)據(jù)集上表現(xiàn)的全過程。
五、實(shí)用函數(shù)補(bǔ)充
??結(jié)合上述過程,我們可以補(bǔ)充一些實(shí)用函數(shù),方便簡(jiǎn)化后續(xù)建模流程。
- 數(shù)據(jù)封裝、切分和加載函數(shù)
??該函數(shù)可以直接將輸入的特征和標(biāo)簽直接進(jìn)行封裝、切分和加載。該函數(shù)可以直接處理此前定義的數(shù)據(jù)生成器創(chuàng)建的數(shù)據(jù)。
def split_loader(features, labels, batch_size=10, rate=0.7):"""數(shù)據(jù)封裝、切分和加載函數(shù)::param features:輸入的特征 :param labels: 數(shù)據(jù)集標(biāo)簽張量:param batch_size:數(shù)據(jù)加載時(shí)的每一個(gè)小批數(shù)據(jù)量 :param rate: 訓(xùn)練集數(shù)據(jù)占比:return:加載好的訓(xùn)練集和測(cè)試集"""data = GenData(features, labels) num_train = int(data.lens * 0.7)num_test = data.lens - num_traindata_train, data_test = random_split(data, [num_train, num_test])train_loader = DataLoader(data_train, batch_size=batch_size, shuffle=True)test_loader = DataLoader(data_test, batch_size=batch_size, shuffle=False)return(train_loader, test_loader)測(cè)試函數(shù)性能
# 設(shè)置隨機(jī)數(shù)種子 torch.manual_seed(420) # 創(chuàng)建數(shù)據(jù)集 features, labels = tensorGenReg() features = features[:, :-1] # 進(jìn)行數(shù)據(jù)加載 train_loader, test_loader = split_loader(features, labels) #<torch._C.Generator at 0x17be1691530> # 查看第一條訓(xùn)練集數(shù)據(jù) train_loader.dataset[0] #(tensor([-1.4463, -0.6221]), tensor([-1.2863])) len(train_loader.dataset[:][0]) #700- 模型訓(xùn)練函數(shù)
??模型訓(xùn)練函數(shù)并不是新的函數(shù),此處正式對(duì)其進(jìn)行定義并寫入自定義模塊中,方便后續(xù)調(diào)用。
def fit(net, criterion, optimizer, batchdata, epochs=3, cla=False):"""模型訓(xùn)練函數(shù):param net:待訓(xùn)練的模型 :param criterion: 損失函數(shù):param optimizer:優(yōu)化算法:param batchdata: 訓(xùn)練數(shù)據(jù)集:param cla: 是否是分類問題:param epochs: 遍歷數(shù)據(jù)次數(shù)"""for epoch in range(epochs):for X, y in batchdata:if cla == True:y = y.flatten().long() # 如果是分類問題,需要對(duì)y進(jìn)行整數(shù)轉(zhuǎn)化yhat = net.forward(X)loss = criterion(yhat, y)optimizer.zero_grad()loss.backward()optimizer.step()- MSE計(jì)算函數(shù)
??接下來(lái),我們借助F.mse_loss,定義一個(gè)可以直接根據(jù)模型輸出結(jié)果和加載后的數(shù)據(jù)計(jì)算MSE的函數(shù)。
def mse_cal(data_loader, net):"""mse計(jì)算函數(shù):param data_loader:加載好的數(shù)據(jù):param net: 模型:return:根據(jù)輸入的數(shù)據(jù),輸出其MSE計(jì)算結(jié)果"""data = data_loader.dataset # 還原Dataset類X = data[:][0] # 還原數(shù)據(jù)的特征y = data[:][1] # 還原數(shù)據(jù)的標(biāo)簽yhat = net(X)return F.mse_loss(yhat, y)train_loader.dataset[:][0] #tensor([[-1.4463, -0.6221], # [-0.4742, -0.2939], # [ 1.9870, 0.1949], # ..., # [-1.6366, -2.1399], # [-1.8178, -1.4618], # [ 0.2646, 2.3555]])接下來(lái),測(cè)試函數(shù)性能。借助上述建模實(shí)驗(yàn)中構(gòu)建的回歸模型,測(cè)試函數(shù)能否順利執(zhí)行。
# 設(shè)置隨機(jī)數(shù)種子 torch.manual_seed(420) # 實(shí)例化模型 LR_model = LR()# Stage 2.定義損失函數(shù) criterion = nn.MSELoss()# Stage 3.定義優(yōu)化方法 optimizer = optim.SGD(LR_model.parameters(), lr = 0.03)# Stage 4.訓(xùn)練模型 fit(net = LR_model,criterion = criterion,optimizer = optimizer,batchdata = train_loader,epochs = 3 ) #<torch._C.Generator at 0x17be1691530> LR_model #LR( # (linear): Linear(in_features=2, out_features=1, bias=True) #) mse_cal(train_loader, LR_model) # 計(jì)算訓(xùn)練誤差 #tensor(0.0001, grad_fn=<MseLossBackward>) mse_cal(test_loader, LR_model) # 計(jì)算測(cè)試誤差 #tensor(8.8412e-05, grad_fn=<MseLossBackward>)和F.mse_loss對(duì)比
F.mse_loss(LR_model(train_loader.dataset[:][0]), train_loader.dataset[:][1]) #tensor(0.0001, grad_fn=<MseLossBackward>) F.mse_loss(LR_model(test_loader.dataset[:][0]), test_loader.dataset[:][1]) #tensor(8.8412e-05, grad_fn=<MseLossBackward>)- 準(zhǔn)確率計(jì)算函數(shù)
??類似的,定義一個(gè)分類問題的準(zhǔn)確率計(jì)算函數(shù),同樣要求輸入是加載后的數(shù)據(jù)集和訓(xùn)練完成的模型。
def accuracy_cal(data_loader, net):"""準(zhǔn)確率:param data_loader:加載好的數(shù)據(jù):param net: 模型:return:根據(jù)輸入的數(shù)據(jù),輸出其準(zhǔn)確率計(jì)算結(jié)果"""data = data_loader.dataset # 還原Dataset類X = data[:][0] # 還原數(shù)據(jù)的特征y = data[:][1] # 還原數(shù)據(jù)的標(biāo)簽zhat = net(X) # 默認(rèn)是分類問題,并且輸出結(jié)果是未經(jīng)softmax轉(zhuǎn)化的結(jié)果soft_z = F.softmax(zhat, 1) # 進(jìn)行softmax轉(zhuǎn)化acc_bool = torch.argmax(soft_z, 1).flatten() == y.flatten()acc = torch.mean(acc_bool.float())return acc t = torch.arange(9).reshape(3, 3).float() t #tensor([[0., 1., 2.], # [3., 4., 5.], # [6., 7., 8.]]) F.softmax(t, 1) #tensor([[0.0900, 0.2447, 0.6652], # [0.0900, 0.2447, 0.6652], # [0.0900, 0.2447, 0.6652]])接下來(lái),測(cè)試函數(shù)性能:
# 設(shè)置隨機(jī)數(shù)種子 torch.manual_seed(420) # 創(chuàng)建分類數(shù)據(jù)集 features, labels = tensorGenCla()# 進(jìn)行數(shù)據(jù)加載 train_loader, test_loader = split_loader(features, labels)class softmaxR(nn.Module):def __init__(self, in_features=2, out_features=3, bias=False): # 定義模型的點(diǎn)線結(jié)構(gòu)super(softmaxR, self).__init__()self.linear = nn.Linear(in_features, out_features)def forward(self, x): # 定義模型的正向傳播規(guī)則out = self.linear(x) return out# 實(shí)例化模型和 softmax_model = softmaxR()# 定義損失函數(shù) criterion = nn.CrossEntropyLoss()# 定義優(yōu)化算法 optimizer = optim.SGD(softmax_model.parameters(), lr = lr)# 執(zhí)行模型訓(xùn)練 fit(net = softmax_model, criterion = criterion, optimizer = optimizer, batchdata = train_loader, epochs = num_epochs, cla=True)accuracy_cal(train_loader, softmax_model) #tensor(0.8571) accuracy_cal(test_loader, softmax_model) #tensor(0.8533)至此,完成本階段實(shí)用函數(shù)的添加。
總結(jié)
以上是生活随笔為你收集整理的Lesson 13.1 深度学习建模目标与性能评估理论的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 12.5 softmax回
- 下一篇: Lesson 13.2 模型拟合度概念介