最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Pytorch教程——全連接神經(jīng)網(wǎng)絡(luò)

2023-02-24 22:54 作者:遠(yuǎn)-方-的-風(fēng)  | 我要投稿

一、全連接神經(jīng)網(wǎng)絡(luò)簡(jiǎn)介

? ? ? ? 人工神經(jīng)網(wǎng)絡(luò)(ANN)簡(jiǎn)稱神經(jīng)網(wǎng)絡(luò),可以對(duì)一組輸入信號(hào)和一組輸出信號(hào)之間的關(guān)系進(jìn)行建模,是機(jī)器學(xué)習(xí)和認(rèn)知科學(xué)領(lǐng)域中一種模仿生物神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)和功能的數(shù)學(xué)模型。用于對(duì)函數(shù)進(jìn)行估計(jì)或近似,其靈感來(lái)源于動(dòng)物的中樞神經(jīng)系統(tǒng),特別是大腦。神經(jīng)網(wǎng)絡(luò)由大量的人工神經(jīng)元(或節(jié)點(diǎn))聯(lián)結(jié)進(jìn)行計(jì)算,大多數(shù)情況下人工神經(jīng)網(wǎng)絡(luò)能在外界信息的基礎(chǔ)上改變內(nèi)部結(jié)構(gòu),是一種自適應(yīng)系統(tǒng)。

圖1

? ? ? ?具有n個(gè)輸入一個(gè)輸出的單一的神經(jīng)元模型的結(jié)構(gòu)如圖1所示。在這個(gè)模型中,神經(jīng)元接收到來(lái)自 n個(gè)其他神經(jīng)元傳遞過(guò)來(lái)的輸入信號(hào),這些輸入信號(hào)通過(guò)帶權(quán)重的連接進(jìn)行傳遞,神經(jīng)元收到的總輸入值將經(jīng)過(guò)激活函數(shù)f處理后產(chǎn)生神經(jīng)元的輸出

? ? ? ?全連接神經(jīng)網(wǎng)絡(luò)(Multi-Layer Perception, MLP)或者叫多層感知機(jī),是一種連接方式較為簡(jiǎn)單的人工神經(jīng)網(wǎng)絡(luò),屬于前饋神經(jīng)網(wǎng)絡(luò)的一種,主要由輸入層、隱藏層和輸出層構(gòu)成,并且在每個(gè)隱藏層中可以有多個(gè)神經(jīng)元。MLP網(wǎng)絡(luò)是可以應(yīng)用于幾乎所有任務(wù)的多功能學(xué)習(xí)方法,包括分類、回歸,甚至是無(wú)監(jiān)督學(xué)習(xí)。

? ? ? ?神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)能力主要來(lái)源于網(wǎng)絡(luò)結(jié)構(gòu),而且根據(jù)層的數(shù)量不同、每層神經(jīng)元數(shù)量的多少,以及信息在層之間的傳播方式,可以組合成多種神經(jīng)網(wǎng)絡(luò)模型。全連接神經(jīng)網(wǎng)絡(luò)主要由輸入層、隱藏層和輸出層構(gòu)成。輸入層僅接收外界的輸入,不進(jìn)行任何函數(shù)處理,所以輸入層的神經(jīng)元個(gè)數(shù)往往和輸入的特征數(shù)量相同,隱藏層和輸出層神經(jīng)元對(duì)信號(hào)進(jìn)行加工處理,最終結(jié)果由輸出層神經(jīng)元輸出。根據(jù)隱藏層的數(shù)量可以分為單隱藏層MLP和多隱藏層MLP,它們的網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)如圖2所示。

圖2

? ? ? ?針對(duì)單隱藏層MLP和多隱藏層MLP,每個(gè)隱藏層的神經(jīng)元數(shù)量是可以變化的,通常沒(méi)有一個(gè)很好的標(biāo)準(zhǔn)用于確定每層神經(jīng)元的數(shù)量和隱藏層的個(gè)數(shù)。根據(jù)經(jīng)驗(yàn),更多的神經(jīng)元就會(huì)有更強(qiáng)的 表達(dá)能力,同時(shí)更容易造成網(wǎng)絡(luò)的過(guò)擬合,所以在使用全連接神經(jīng)網(wǎng)絡(luò)時(shí),對(duì)模型泛化能力的測(cè)試很重要,最好的方式是在訓(xùn)練模型時(shí)使用驗(yàn)證集來(lái)驗(yàn)證模型的泛化能力,且盡可能地去嘗試多種網(wǎng)絡(luò)結(jié)構(gòu),以尋找更好的模型,但這往往需要耗費(fèi)大量的時(shí)間。

? ? ? ?下面使用Pytorch中的相關(guān)模塊搭建多隱藏層的全連接神經(jīng)網(wǎng)絡(luò),使用真實(shí)數(shù)據(jù)集探索MLP在分類中的應(yīng)用。學(xué)習(xí)如何利用Pytorch搭建、訓(xùn)練、驗(yàn)證建立的MLP網(wǎng)絡(luò)及相關(guān)網(wǎng)絡(luò)可視化和訓(xùn)練技巧。在分析之前先導(dǎo)入所需要的庫(kù)和相關(guān)模塊,程序如下:

import numpy as np

import pandas as pd

from sklearn.preprocessing import StandardScaler,MinMaxScaler

from sklearn.model_selection import train_test_split

from sklearn.metrics import accuracy_score,confusion_matrix,classification_report

from sklearn.manifold import TSNE

import torch

import torch.nn as nn

from torch.optim import SGD,Adam

import torch.utils.data as Data

import matplotlib.pyplot as plt

import seaborn as sns

import hiddenlayer as hl

from torchviz import make_dot

? ? ? ?在上面導(dǎo)入的庫(kù)和模塊中,sklearn.preprocessing模塊用于數(shù)據(jù)標(biāo)準(zhǔn)化預(yù)處理,sklearn.model_selection模塊用于數(shù)據(jù)集的切分,sklearn.metrics模塊用于評(píng)價(jià)模型的預(yù)測(cè)效果,sklearn.manifold模塊用于數(shù)據(jù)的降維及可視化,torch庫(kù)則是用于全連接網(wǎng)絡(luò)的搭建和訓(xùn)練。

二、MLP分類模型

? ? ? ?為了比較數(shù)據(jù)標(biāo)準(zhǔn)化是否對(duì)模型的訓(xùn)練過(guò)程有影響,通常使用相同的網(wǎng)絡(luò)結(jié)構(gòu),分別對(duì)標(biāo)準(zhǔn)化和未標(biāo)準(zhǔn)化的數(shù)據(jù)訓(xùn)練,并將結(jié)果和訓(xùn)練過(guò)程進(jìn)行比較。例如使用MLP進(jìn)行分類,其分析流程如圖3所示。

圖3

2.1 數(shù)據(jù)準(zhǔn)備和探索

? ? ? ?本節(jié)使用一個(gè)垃圾郵件數(shù)據(jù)介紹如何使用Pytorch建立MLP分類模型,該數(shù)據(jù)集可以從UCI機(jī)器學(xué)習(xí)數(shù)據(jù)庫(kù)進(jìn)行下載,網(wǎng)址為:http://archive.ics.uci.edu/ml/datasets/Spambase。

? ? ? ?在該數(shù)據(jù)集中,包含57個(gè)郵件內(nèi)容的統(tǒng)計(jì)特征,其中有48個(gè)特征是關(guān)鍵詞出現(xiàn)頻率 x 100的取值,范圍為[0, 100],變量名使用word_freq_WORD命名,WORD表示該特征統(tǒng)計(jì)的詞語(yǔ);6個(gè)特征為關(guān)鍵字符出現(xiàn)的頻率 x 100取值,范圍為[0, 100],變量名使用char_freq_CHAR命名;1個(gè)變量為capital_run_length_average,表示大寫(xiě)字母不間斷的平均長(zhǎng)度;1個(gè)變量為capital_run_length_longest,表示大寫(xiě)字母不間斷的最大長(zhǎng)度;1個(gè)變量capital_run_length_total表示郵件中大寫(xiě)字母的數(shù)量。數(shù)據(jù)集中最后一個(gè)變量是待預(yù)測(cè)目標(biāo)變量(0、1),表示電子郵件被認(rèn)為是垃圾郵件(1)或不是(0)。

? ? ? ?垃圾郵件數(shù)據(jù)下載后保存為spambase.csv,使用pandas包將其讀入Python工作環(huán)境中,程序如下所示:

##讀取并顯示數(shù)據(jù)

spam = pd.read_csv("data/spambase.csv")

spam

統(tǒng)計(jì)兩種類型的郵件樣本數(shù),使用pd.value_counts()函數(shù)進(jìn)行計(jì)算,程序如下:

#計(jì)算垃圾郵件和非垃圾郵件的數(shù)量

pd.value_counts(spam.label)


? ? ? ? 發(fā)現(xiàn)數(shù)據(jù)集中垃圾郵件有1813個(gè)樣本,非垃圾郵件有2788個(gè)樣本。為了驗(yàn)證訓(xùn)練好的MLP網(wǎng)絡(luò)的性能,需要將數(shù)據(jù)集spam切分為訓(xùn)練集和測(cè)試集,其中使用75%的數(shù)據(jù)作為訓(xùn)練集,剩余25%的數(shù)據(jù)作為測(cè)試集,以測(cè)試訓(xùn)練好的模型的泛化能力。數(shù)據(jù)集切分可以使用train_test_split()函數(shù),程序如下:

#將數(shù)據(jù)隨機(jī)切分為訓(xùn)練集和測(cè)試集

X = spam.iloc[:,0:57].values

y = spam.label.values

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.25,random_state=123)

? ? ? ?切分好數(shù)據(jù)后,需要對(duì)數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化處理。此處采用MinMaxScaler()將數(shù)據(jù)進(jìn)行最大—最小值標(biāo)準(zhǔn)化,將數(shù)據(jù)集中的每個(gè)特征取值范圍轉(zhuǎn)化到0 ~ 1之間

#對(duì)數(shù)據(jù)的前57列特征進(jìn)行數(shù)據(jù)的標(biāo)準(zhǔn)化處理

scales = MinMaxScaler(feature_range=(0,1))

X_train_s = scales.fit_transform(X_train)

X_test_s = scales.fit_transform(X_test)

? ? ? ?得到標(biāo)準(zhǔn)化數(shù)據(jù)后,將訓(xùn)練數(shù)據(jù)集的每個(gè)特征變量使用箱線圖進(jìn)行顯示,對(duì)比不同類型的郵件(垃圾郵件和非垃圾郵件)在每個(gè)特征變量上的數(shù)據(jù)分布情況

#得到標(biāo)準(zhǔn)化數(shù)據(jù)后使用箱線圖將訓(xùn)練集的每個(gè)特征變量進(jìn)行顯示

colname = spam.columns.values[:-1]?? #:-1表示除了最后一個(gè)取全部

plt.figure(figsize=(20,14))

for ii in range(len(colname)):

? ? plt.subplot(7,9,ii+1)

? ? sns.boxplot(x = y_train,y = X_train_s[:,ii])? ?#[:,i]表示取第i列的所有行

? ? plt.title(colname[ii])

plt.subplots_adjust(hspace=0.5)

plt.show()

? ? ? ?上面程序使用sns.boxplot()函數(shù)將數(shù)據(jù)集X_train_s中的57個(gè)特征變量進(jìn)行了可視化,得到的圖像如圖4所示。

圖4

? ? ? ? 通過(guò)圖像發(fā)現(xiàn),有些特征在兩種類型的郵件上分布有較大的差異,如word_freq_all、word_freq_our、word_freq_your、word_freq_you、word_freq_000等。

下面將使用全部特征作為MLP網(wǎng)絡(luò)的輸入,因?yàn)橛行┨卣鞅M管在兩種類型的郵件上差異并不明顯,但是全連接神經(jīng)網(wǎng)絡(luò)包含多個(gè)神經(jīng)元,而且每個(gè)神經(jīng)元都是接受前面層的所有神經(jīng)元的輸出,所以MLP網(wǎng)絡(luò)的每個(gè)神經(jīng)元都有變換、篩選、綜合輸入的能力,故用于輸入的特征越多,網(wǎng)絡(luò)獲取的信息就會(huì)越充分,從而網(wǎng)絡(luò)就會(huì)越有效。雖然有些特征在兩種類型的郵件上差異并不明顯,但是如果將多個(gè)特征綜合考慮,差異可能就會(huì)非常明顯。所以在下面建立的MLP分類器中,將使用全部數(shù)據(jù)特征作為網(wǎng)絡(luò)的輸入。

2.2 搭建網(wǎng)絡(luò)并可視化

? ? ? ?在數(shù)據(jù)準(zhǔn)備、探索和可視化分析后,下面搭建需要使用的全連接神經(jīng)網(wǎng)絡(luò)分類器。網(wǎng)絡(luò)的每個(gè)全連接隱藏層由nn.Linear()函數(shù)和nn.ReLU()函數(shù)構(gòu)成,其中nn.ReLU()表示使用激活函數(shù)ReLU。構(gòu)建全連接層分類網(wǎng)絡(luò)的程序如下所示:

##全連接網(wǎng)絡(luò)

class MLPclassifica(nn.Module):

? ? def __init__(self):

? ? ? ? super(MLPclassifica,self).__init__()

? ? ? ? ##定義第一個(gè)隱藏層

? ? ? ? self.hidden1 = nn.Sequential(

? ? ? ? ? ? ? ? ? ? ? ? nn.Linear(in_features=57,? #第一個(gè)隱藏層的輸入,數(shù)據(jù)的特征數(shù)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?out_features=30,? #第一個(gè)隱藏層的輸出,神經(jīng)元的數(shù)量

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bias=True),? #默認(rèn)會(huì)有偏置

? ? ? ? ? ? ? ? ? ? ? ? nn.ReLU())

? ? ? ? ##定義第二個(gè)隱藏層

? ? ? ? self.hidden2 = nn.Sequential(

? ? ? ? ? ? ? ? ? ? ? ? nn.Linear(in_features=30,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?out_features=10,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bias=True),

? ? ? ? ? ? ? ? ? ? ? ? nn.ReLU())

? ? ? ? ##分類層

? ? ? ? self.classifica = nn.Sequential(

? ? ? ? ? ? ? ? ? ? ? ? nn.Linear(in_features=10,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?out_features=2,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?bias=True),

? ? ? ? ? ? ? ? ? ? ? ? nn.Sigmoid())

? ? ##定義網(wǎng)絡(luò)的前向傳播途徑

? ? def forward(self,x):

? ? ? ? ? ? fc1 = self.hidden1(x)

? ? ? ? ? ? fc2 = self.hidden2(fc1)

? ? ? ? ? ? output = self.classifica(fc2)

? ? ? ? ? ? #輸出為兩個(gè)隱藏層和輸出層

? ? ? ? ? ? return fc1,fc2,output

? ? ? ?上面的程序中定義了一個(gè)MLPclassifica函數(shù)類,其網(wǎng)絡(luò)結(jié)構(gòu)中含有hidden1和hidden2兩個(gè)隱藏層,分別包含30和10個(gè)神經(jīng)元以及1個(gè)分類層classifica,并且分類層使用Sigmoid函數(shù)作為激活函數(shù)。由于數(shù)據(jù)有57個(gè)特征,所以第一個(gè)隱藏層的輸入特征為57,而且該數(shù)據(jù)為二分類問(wèn)題,所以分類層有2個(gè)神經(jīng)元。在定義完網(wǎng)絡(luò)結(jié)構(gòu)后,需要定義網(wǎng)絡(luò)的正向傳播過(guò)程,分別輸出了網(wǎng)絡(luò)的兩個(gè)隱藏層fc1、fc2和分類層的輸出output。

? ? ? ?針對(duì)定義好的MLPclassifica()函數(shù)類,使用mlpc = MLPclassifica()得到全連接網(wǎng)絡(luò)mlpc,并利用torchviz庫(kù)中的make_dot函數(shù),將網(wǎng)絡(luò)結(jié)構(gòu)進(jìn)行可視化,程序如下:

##輸出網(wǎng)絡(luò)結(jié)構(gòu)

mlpc = MLPclassifica()

##使用make_dot可視化網(wǎng)絡(luò)

x = torch.randn(1,57).requires_grad_(True)

y = mlpc(x)

Mymlpvis = make_dot(y,params=dict(list(mlpc.named_parameters())+[('x',x)]))

Mymlpvis

圖5

? ? ? ? 得到的網(wǎng)絡(luò)結(jié)構(gòu)傳播過(guò)程的可視化結(jié)構(gòu)如圖5所示。

2.3 使用未預(yù)處理的數(shù)據(jù)訓(xùn)練模型

? ? ? ?在網(wǎng)絡(luò)搭建完畢后,首先使用未標(biāo)準(zhǔn)化的訓(xùn)練數(shù)據(jù)訓(xùn)練模型,然后利用未標(biāo)準(zhǔn)化的測(cè)試數(shù)據(jù)驗(yàn)證模型的泛化能力,分析網(wǎng)絡(luò)在未標(biāo)準(zhǔn)化的數(shù)據(jù)集中是否也能很好地?cái)M合數(shù)據(jù)。首先將未標(biāo)準(zhǔn)化的數(shù)據(jù)轉(zhuǎn)化為張量,并將張量處理為數(shù)據(jù)加載器,程序如下:

#將數(shù)據(jù)轉(zhuǎn)化成張量

X_train_nots = torch.from_numpy(X_train_s.astype(np.float32))

y_train_t = torch.from_numpy(y_train.astype(np.int64))

X_test_nots = torch.from_numpy(X_test_s.astype(np.float32))

y_test_t = torch.from_numpy(y_test.astype(np.int64))

#將訓(xùn)練集轉(zhuǎn)化為張量后,使用TensorDataset將X和Y整理到一起

train_data_nots = Data.TensorDataset(X_train_nots,y_train_t)

#定義數(shù)據(jù)加載器,將訓(xùn)練集進(jìn)行批量處理

train_nots_loader = Data.DataLoader(

? ? dataset=train_data_nots,? #使用的數(shù)據(jù)集

? ? batch_size=64,? #批處理樣本大小

? ? shuffle=True,? #每次迭代前打亂數(shù)據(jù)

? ? num_workers=1,

)

? ? ? ?在上面的程序中,為了模型正常的訓(xùn)練,需要將數(shù)據(jù)集X轉(zhuǎn)化為32位浮點(diǎn)型張量,以及將Y轉(zhuǎn)化為64位整型張量,將數(shù)據(jù)設(shè)置為加載器,將Data.TensorDataset()函數(shù)和Data.DataLoader()結(jié)合在一起使用。在上述數(shù)據(jù)加載器中每個(gè)batch包含64個(gè)樣本。

? ? ? ?數(shù)據(jù)準(zhǔn)備完畢后,需要使用訓(xùn)練集對(duì)全連接神經(jīng)網(wǎng)絡(luò)mlpc進(jìn)行訓(xùn)練和測(cè)試。在優(yōu)化模型時(shí),優(yōu)化函數(shù)使用torch.optim.Adam(),損失函數(shù)使用交叉熵?fù)p失函數(shù)nn.CrossEntropyLoss()。為了觀察網(wǎng)絡(luò)在訓(xùn)練過(guò)程中損失的變化情況以及在測(cè)試集上預(yù)測(cè)精度的變化,可以使用HiddenLayer庫(kù),將相應(yīng)數(shù)值的變化進(jìn)行可視化。模型的訓(xùn)練和相關(guān)可視化程序如下所示:

#定義優(yōu)化器

optimizer = torch.optim.Adam(mlpc.parameters(),lr=0.01)

loss_func = nn.CrossEntropyLoss()? #交叉熵?fù)p失函數(shù)

#記錄訓(xùn)練過(guò)程的指標(biāo)

history1 = hl.History()

#使用canvas進(jìn)行可視化

canvas1 = hl.Canvas()

print_step = 25? ?#25次迭代后輸出損失?

#對(duì)模型進(jìn)行迭代處理 所有數(shù)據(jù)訓(xùn)練30輪

for epoch in range(30):

? ? #對(duì)訓(xùn)練數(shù)據(jù)的加載器進(jìn)行迭代計(jì)算

? ? for step,(b_x,b_y) in enumerate(train_nots_loader):

? ? ? ? #計(jì)算每個(gè)batch的損失

? ? ? ? _,_,output = mlpc(b_x)? ?#MLP在訓(xùn)練batch的輸出

? ? ? ? train_loss = loss_func(output,b_y)? ?#二分類交叉熵?fù)p失函數(shù)

? ? ? ? optimizer.zero_grad()? #每個(gè)迭代步的梯度初始化為0

? ? ? ? train_loss.backward()? #損失的后向傳播,計(jì)算梯度

? ? ? ? optimizer.step()? #使用梯度優(yōu)化

? ? ? ? #計(jì)算迭代次數(shù)

? ? ? ? niter = epoch*len(train_nots_loader)+step+1

? ? ? ? #計(jì)算每經(jīng)過(guò)print_step次迭代后的輸出(25)

? ? ? ? if niter % print_step == 0:

? ? ? ? ? ? _,_,output = mlpc(X_test_nots)

? ? ? ? ? ? _,pre_lab = torch.max(output,1)

? ? ? ? ? ? test_accuracy = accuracy_score(y_test_t,pre_lab)

? ? ? ? ? ? #為history添加epoch,損失和精度

? ? ? ? ? ? history1.log(niter,train_loss = train_loss,test_accuracy = test_accuracy)

? ? ? ? ? ? #使用兩個(gè)圖像可視化損失函數(shù)和精度

? ? ? ? ? ? with canvas1:

? ? ? ? ? ? ? ? canvas1.draw_plot(history1["train_loss"])

? ? ? ? ? ? ? ? canvas1.draw_plot(history1["test_accuracy"])

? ? ? ?上面的程序?qū)τ?xùn)練數(shù)據(jù)集進(jìn)行了30個(gè)epoch訓(xùn)練,在網(wǎng)絡(luò)訓(xùn)練過(guò)程中,每經(jīng)過(guò)25次迭代就對(duì)測(cè)試集進(jìn)行一次預(yù)測(cè),并且將迭代次數(shù)、訓(xùn)練集上損失函數(shù)的取值、模型在測(cè)試集上的識(shí)別精度都使用history1.log()函數(shù)進(jìn)行保存,再使用canvas1.draw_plot()函數(shù)將損失函數(shù)大小、預(yù)測(cè)精度實(shí)時(shí)可視化出來(lái),得到的模型訓(xùn)練過(guò)程如圖6所示。

圖6

? ? ? ?從圖6中可以看出,損失函數(shù)一直在波動(dòng),并沒(méi)有收斂到一個(gè)平穩(wěn)的數(shù)值區(qū)間,在測(cè)試集上的精度也具有較大的波動(dòng)范圍,而且最大精度低于72%。說(shuō)明使用未標(biāo)準(zhǔn)化的數(shù)據(jù)集訓(xùn)練的模型并沒(méi)有訓(xùn)練效果,即MLP分類器沒(méi)有收斂。

導(dǎo)致這樣結(jié)果的原因可能很多,例如:

(1)數(shù)據(jù)沒(méi)有經(jīng)過(guò)標(biāo)準(zhǔn)化處理,所以網(wǎng)絡(luò)沒(méi)有收斂。

(2)使用的訓(xùn)練數(shù)據(jù)樣本太少,導(dǎo)致網(wǎng)絡(luò)沒(méi)有收斂。

(3)搭建的MLP網(wǎng)絡(luò)使用的神經(jīng)元太多或太少,所以網(wǎng)絡(luò)沒(méi)有收斂。

2.4 使用預(yù)處理后的數(shù)據(jù)訓(xùn)練模型

? ? ? ?MLP分類器沒(méi)有收斂的原因可能有多個(gè),但是最可能的原因是數(shù)據(jù)沒(méi)有進(jìn)行標(biāo)準(zhǔn)化預(yù)處理,為了驗(yàn)證猜想的正確性,使用標(biāo)準(zhǔn)化數(shù)據(jù)集重新對(duì)上面的MLP網(wǎng)絡(luò)進(jìn)行訓(xùn)練,觀察訓(xùn)練集和測(cè)試集在網(wǎng)絡(luò)訓(xùn)練過(guò)程中的表現(xiàn),查看網(wǎng)絡(luò)是否收斂。

? ? ? ?下面使用標(biāo)準(zhǔn)化后的數(shù)據(jù)進(jìn)行訓(xùn)練,探索是否會(huì)得到收斂的模型。首先對(duì)標(biāo)準(zhǔn)化后的數(shù)據(jù)進(jìn)行預(yù)處理,得到數(shù)據(jù)加載器。

#將數(shù)據(jù)轉(zhuǎn)化成張量

X_train_t = torch.from_numpy(X_train_s.astype(np.float32))

y_train_t = torch.from_numpy(y_train.astype(np.int64))

X_test_t = torch.from_numpy(X_test_s.astype(np.float32))

y_test_t = torch.from_numpy(y_test.astype(np.int64))

#將訓(xùn)練集轉(zhuǎn)化為張量后,使用TensorDataset將X和Y整理到一起

train_data = Data.TensorDataset(X_train_t,y_train_t)

#定義數(shù)據(jù)加載器,將訓(xùn)練數(shù)據(jù)集進(jìn)行批量處理

train_loader = Data.DataLoader(

? ? dataset=train_data,? #使用的數(shù)據(jù)集

? ? batch_size=64,? #批處理樣本大小

? ? shuffle=True,? #每次迭代前打亂數(shù)據(jù)

? ? num_workers=1,

)

? ? ? ?在訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)準(zhǔn)備好后,使用與2.3節(jié)相似的程序,訓(xùn)練全連接神經(jīng)網(wǎng)絡(luò)分類器,程序如下:

#定義優(yōu)化器

optimizer = torch.optim.Adam(mlpc.parameters(),lr=0.01)

loss_func = nn.CrossEntropyLoss()? #交叉熵?fù)p失函數(shù)

#記錄訓(xùn)練過(guò)程的指標(biāo)

history1 = hl.History()

#使用canvas進(jìn)行可視化

canvas1 = hl.Canvas()

print_step = 25? #25次迭代后輸出損失

#對(duì)模型進(jìn)行迭代處理 所有數(shù)據(jù)訓(xùn)練30輪

for epoch in range(30):

? ? #對(duì)訓(xùn)練數(shù)據(jù)的加載器進(jìn)行迭代計(jì)算

? ? for step,(b_x,b_y) in enumerate(train_loader):

? ? ? ? #計(jì)算每個(gè)batch的損失

? ? ? ? _,_,output = mlpc(b_x)? ?#MLP在訓(xùn)練batch上的輸出

? ? ? ? train_loss = loss_func(output,b_y)? ?#二分類交叉熵?fù)p失

? ? ? ? optimizer.zero_grad()? #每個(gè)迭代步的梯度初始化為0

? ? ? ? train_loss.backward()? #損失的后向傳播,計(jì)算梯度

? ? ? ? optimizer.step()? #使用梯度優(yōu)化

? ? ? ? #計(jì)算迭代次數(shù)

? ? ? ? niter = epoch*len(train_loader)+step+1

? ? ? ? #計(jì)算每經(jīng)過(guò)print_step次迭代后的輸出(25)

? ? ? ? if niter % print_step == 0:

? ? ? ? ? ? _,_,output = mlpc(X_test_t)

? ? ? ? ? ? _,pre_lab = torch.max(output,1)

? ? ? ? ? ? test_acc?= accuracy_score(y_test_t,pre_lab)

? ? ? ? ? ? #為history添加epoch,損失和精度

? ? ? ? ? ? history1.log(niter,train_loss = train_loss,test_acc?= test_acc)

? ? ? ? ? ? #使用兩個(gè)圖像可視化損失函數(shù)和精度

? ? ? ? ? ? with canvas1:

? ? ? ? ? ? ? ? canvas1.draw_plot(history1["train_loss"])

? ? ? ? ? ? ? ? canvas1.draw_plot(history1["test_acc"])

? ? ? ?在上面的程序中,利用標(biāo)準(zhǔn)化處理后的數(shù)據(jù)集對(duì)網(wǎng)絡(luò)進(jìn)行訓(xùn)練,并且在網(wǎng)絡(luò)訓(xùn)練過(guò)程中,每經(jīng)過(guò)25次迭代就對(duì)標(biāo)準(zhǔn)化的測(cè)試集進(jìn)行一次預(yù)測(cè)。同樣將迭代次數(shù)、訓(xùn)練集上的損失函數(shù)取值、模型在測(cè)試集上的識(shí)別精度都使用history1.log()函數(shù)進(jìn)行保存,并且使用canvas1.draw_plot()函數(shù)將損失函數(shù)大小、預(yù)測(cè)精度實(shí)時(shí)可視化出來(lái),得到如圖7所示的模型訓(xùn)練過(guò)程。

圖7

? ? ? ?從圖7中得到了標(biāo)準(zhǔn)化數(shù)據(jù)訓(xùn)練的分類器,并且損失函數(shù)最終收斂到一個(gè)平穩(wěn)的數(shù)值區(qū)間,在測(cè)試集上的精度也得到了收斂,預(yù)測(cè)精度穩(wěn)定在90%以上。說(shuō)明模型在使用標(biāo)準(zhǔn)化數(shù)據(jù)后得到有效的訓(xùn)練。即數(shù)據(jù)標(biāo)準(zhǔn)化預(yù)處理對(duì)MLP網(wǎng)絡(luò)非常重要。

? ? ? ?在獲得收斂的模型后,可以在測(cè)試集上計(jì)算模型的垃圾郵件識(shí)別最終精度,程序代碼如下所示:

_,_,output = mlpc(X_test_t)

_,pre_lab = torch.max(output,1)

test_acc = accuracy_score(y_test_t,pre_lab)

print("test_acc:",test_acc)

Out[11]:test_acc:0.9209383145091226

2.5 獲取中間層的輸出并可視化

? ? ? ?在全連接神經(jīng)網(wǎng)絡(luò)訓(xùn)練好后,為了更好地理解全連接神經(jīng)網(wǎng)絡(luò)的計(jì)算過(guò)程,以獲取網(wǎng)絡(luò)在計(jì)算過(guò)程中中間隱藏層的輸出,可以使用兩種方法:

(1)如果在網(wǎng)絡(luò)的前向過(guò)程想要輸出隱藏層,可以使用單獨(dú)的變量進(jìn)行命名,然后在輸出時(shí)輸出該變量,例如:

def forward(self,x):

? ? ? fc1 = self.hidden1(x)

? ? ? fc2 = self.hidden2(fc1)

? ? ? output = self.classifica(fc2)

? ? ? #輸出為兩個(gè)隱藏層和輸出層

? ? ? return fc1,fc2,output

? ? ? ?在上面的forward()函數(shù)中,fc1、fc2分別是第一隱藏層和第二隱藏層的輸出,output則為分類層的輸出,最后使用return fc1,fc2,output同時(shí)將三個(gè)變量輸出,便于在調(diào)用模型時(shí),輕松獲得隱藏層的輸出。

(2)如果在網(wǎng)絡(luò)的前向過(guò)程中只輸出了最后一層的輸出,并沒(méi)有輸出中間變量,這是想要獲取中間層的輸出,則使用鉤子(hook)技術(shù)。鉤子技術(shù)可以理解為對(duì)一個(gè)完整的業(yè)務(wù)流程,使用鉤子可以在不修改原始網(wǎng)絡(luò)代碼的情況下,將額外的功能依附于業(yè)務(wù)流程,并獲取想要的輸出。

? ? ? ?接下來(lái)針對(duì)上述已經(jīng)訓(xùn)練好的網(wǎng)絡(luò),分別利用兩種方法介紹如何從網(wǎng)絡(luò)中獲取中間隱藏層的輸出,并對(duì)相關(guān)輸出進(jìn)行可視化。
1.使用中間層的輸出

? ? ? ?在上述定義的全連接網(wǎng)絡(luò)中,已經(jīng)輸出了隱藏層的輸出,可以直接使用mlpc()作用于測(cè)試集時(shí),輸出相關(guān)內(nèi)容,程序如下:

#計(jì)算最終模型在測(cè)試集上的第二個(gè)隱藏層的輸出

_,test_fc2,_ = mlpc(X_test_t)

print("test_fc2.shape:",test_fc2.shape)

Out[13]: test_fc2.shape: torch.Size([1151, 10])

? ? ? 在上述程序和輸出中,可以通過(guò)“_,test_fc2,_ = mlpc(X_test_t)”獲取mlpc網(wǎng)絡(luò),讓測(cè)試集在第二個(gè)隱藏層輸出test_fc2。test_fc2的尺寸為[1151,10],表明有1151個(gè)樣本,每個(gè)樣本包含10個(gè)特征輸出。

? ? ? 下面對(duì)10個(gè)特征進(jìn)行降維,然后使用散點(diǎn)圖進(jìn)行可視化,程序如下:

#對(duì)輸出進(jìn)行降維并可視化

test_fc2_tsne = TSNE(n_components = 2).fit_transform(test_fc2.data.numpy())

#將特征進(jìn)行可視化

plt.figure(figsize = (8,6))

#可視化前設(shè)置坐標(biāo)系的取值范圍

plt.xlim([min(test_fc2_tsne[:,0]-1),max(test_fc2_tsne[:,0])+1])

plt.ylim([min(test_fc2_tsne[:,1]-1),max(test_fc2_tsne[:,1])+1])

plt.plot(test_fc2_tsne[y_test == 0,0],test_fc2_tsne[y_test == 0,1],"bo",label = "0")

plt.plot(test_fc2_tsne[y_test == 1,0],test_fc2_tsne[y_test == 1,1],"rd",label = "1")

plt.legend()

plt.title("test_fc2_tsne")

plt.show()

? ? ? ? 上述程序隱形后,可得到如圖8所示的散點(diǎn)圖。

圖8

? ? ? ? ?在散點(diǎn)圖中,兩種點(diǎn)分別代表垃圾郵件和正常郵件在空間中的分布情況。

2.使用鉤子獲取中間層的輸出

? ? ? ? ?首先定義一個(gè)輔助函數(shù),以方便獲取和保存中間層的輸出。

?#定義一個(gè)輔助函數(shù),來(lái)獲取指定層名稱的特征

activation = {}? #保存不同層的輸出

def get_activation(name):

? ? def hook(model, input, output):

? ? ? ? activation[name] = output.detach()

? ? return hook

? ? ? ?在定義好get_activation()函數(shù)后,獲取中間層的輸出,以字典的形式保存在activation字典中。獲取分類層的輸出可以使用如下所示的代碼:

#全連接網(wǎng)絡(luò)獲取分類層的輸出

mlpc.classifica.register_forward_hook(get_activation("classifica"))

_,_,_ = mlpc(X_test_t)

classifica = activation["classifica"].data.numpy()

print("classifica.shape:",classifica.shape)

? ? ? ?上述程序先使用mlpc.classifica.register_forward_hook(get_activation("classifica"))操作(該操作主要用于獲取classifica層的輸出結(jié)果),然后將訓(xùn)練好的網(wǎng)絡(luò)mlpc作用于測(cè)試集X_test_t上,這樣在activation字典中,鍵值classifica對(duì)應(yīng)的結(jié)果即為想要獲取的中間層特征。從輸出中可以發(fā)現(xiàn),其每個(gè)樣本包含兩個(gè)特征輸出。下面同樣使用散點(diǎn)圖將其可視化。

#將特征進(jìn)行可視化

plt.figure(figsize = (8,6))

#可視化每類的散點(diǎn)圖

plt.plot(classifica[y_test == 0,0],classifica[y_test == 0,1],"bo",label = "0")

plt.plot(classifica[y_test == 1,0],classifica[y_test == 1,1],"rd",label = "1")

plt.legend()

plt.title("classifica")

plt.show()

? ? ? ?在散點(diǎn)圖9中,兩種點(diǎn)分別代表垃圾郵件和正常郵件在空間中的分布情況。

圖9


Pytorch教程——全連接神經(jīng)網(wǎng)絡(luò)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
灵山县| 奎屯市| 乃东县| 溆浦县| 策勒县| 巴南区| 宜兴市| 驻马店市| 罗山县| 沂源县| 镇雄县| 浙江省| 循化| 安龙县| 天等县| 昭通市| 满城县| 旬阳县| 娄底市| 平邑县| 香港| 白银市| 韶山市| 正安县| 炎陵县| 宁夏| 东海县| 乐山市| 休宁县| 平武县| 芒康县| 阜康市| 花莲市| 宜春市| 黑河市| 白山市| 墨江| 九龙坡区| 黔江区| 垣曲县| 邯郸市|