Lesson 9.1异或門問題

Grateful_Dead424 2022-01-08 04:34:57 阅读数:347

lesson

在之前的介紹中,我們已經學習了三種單層神經網絡,分別為實現線性方程的回歸網絡,實現二分類的邏輯回歸(二分類網絡),以及實現多分類的softmax回歸(多分類網絡),我們已經基本了解了構成普通神經網絡的諸多元素。從本節課開始,我們將從單層神經網絡展開至深層神經網絡,並深入理解層、以及層上的各類函數計算對於神經網絡的意義。

從單層到多層是神經網絡發展史上的重大變化,層的增加徹底將神經網絡的性能提昇到了另一個高度, 正確理解層的意義對於我們自主構建神經網絡有很重要的作用,學會利用層是避免浪費計算資源以及提 昇神經網絡效果的關鍵。在本節的最後,我們還將繼承nn.Module類實現一個完整的深層神經網絡的前向傳播的過程。

一、异或門問題

還記得之前的課程中我們提到的數據“與門”(andgate)嗎?與門是一組只有兩個特征(x1, x2)的分類數據,當兩個特征下的取值都為1時,分類標簽為1,其他時候分類標簽為0。
在這裏插入圖片描述
之前我們使用tensor結構與nn.Linear類在“與門”數據上上實現了簡單的二分類神經網絡(邏輯回歸),其中使用tensor結構定義時,我們自定義了權重向量w,並巧妙的讓邏輯回歸輸出的結果與真實結果一 致,具體代碼如下:

import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
andgate = torch.tensor([0,0,0,1], dtype = torch.float32)
#定義w
w = torch.tensor([-0.2,0.15,0.15], dtype = torch.float32)
def LogisticR(X,w):
zhat = torch.mv(X,w) #首先執行線性回歸的過程,依然是mv函數,讓矩陣與向量相乘得到z
sigma = torch.sigmoid(zhat) #執行sigmoid函數,你可以調用torch中的sigmoid函數,也可 以自己用torch.exp來寫
andhat = torch.tensor([int(x) for x in sigma >= 0.5], dtype = torch.float32)
#設置閾值為0.5, 使用列錶推導式將值轉化為0和1
return sigma, andhat
sigma, andhat = LogisticR(X,w)
sigma
#tensor([0.4502, 0.4875, 0.4875, 0.5250])
andhat
#tensor([0., 0., 0., 1.])
andgate
#tensor([0., 0., 0., 1.])
andgate == andhat
#tensor([True, True, True, True])

考慮到與門的數據只有兩維,我們可以通過python中的matplotlib代碼將數據可視化,其中,特征 x 1 x_{1} x1為橫坐標,特征 x 2 x_{2} x2為縱坐標,紫色點代錶了類別0,紅色點代錶類別1。

import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn-whitegrid') #設置圖像的風格
sns.set_style("white")
plt.figure(figsize=(5,3)) #設置畫布大小
plt.title("AND GATE",fontsize=16) #設置圖像標題
plt.scatter(X[:,1],X[:,2],c=andgate,cmap="rainbow") #繪制散點圖
plt.xlim(-1,3) #設置橫縱坐標尺寸
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y") #顯示背景中的網格
plt.gca().spines["top"].set_alpha(.0) #讓上方和右側的坐標軸被隱藏
plt.gca().spines["right"].set_alpha(.0);

結果:
在這裏插入圖片描述
在機器學習中,存在一種對數據進行分類的簡單方式:就是在兩類數據中間繪制一條直線,並規定直線一側的點屬於一類標簽,下方另一側的點屬於另一類標簽。而在這個由 x 1 x_{1} x1 x 1 x_{1} x1構成的空間中,二維平面上的任意一條線可以被錶示為:
x 1 = a x 2 + b x_{1}=a x_{2}+b x1=ax2+b
我們將此錶達式變換一下:
0 = b − x 1 + a x 2 0 = [ 1 , x 1 , x 2 ] ∗ [ b − 1 a ] 0 = X w \begin{array}{l} 0=b-x_{1}+a x_{2} \\ 0=\left[1, x_{1}, x_{2}\right] *\left[\begin{array}{c} b \\ -1 \\ a \end{array}\right] \\ 0=\boldsymbol{X} \boldsymbol{w} \end{array} 0=bx1+ax20=[1,x1,x2]b1a0=Xw
其中[b, -1, a]就是我們的權重向量 w w w(默認列向量), X X X就是我們的特征張量, b b b是我們的截距。在一組數據下,給定固定的和,這個式子就可以是一條固定直線,在和不確定的狀况下,這個錶達式 X w = 0 Xw = 0 Xw=0就可以代錶平面上的任意一條直線。在直線上方的點為1類(紅色),在直線下方的點為0類(紫色),其顏色標注與剛才繪制的與門數據中的數據點一致。
在這裏插入圖片描述
如果在 w w w b b b和固定時,給定一個唯一的 X X X的取值,這個錶達式就可以錶示一個固定的點。如果任取一個紫色的點 X p X_{p} Xp就可以被錶示為:
X p w = p \boldsymbol{X}_{p} \boldsymbol{w}=p Xpw=p
由於紫色的點所代錶的標簽y是0,所以我們規定,p<=0。同樣的,對於任意一個紅色的點而言,我們可以將它錶示為:
X r w = r \boldsymbol{X}_{\boldsymbol{r}} \boldsymbol{w}=r Xrw=r
由於紅色點所錶示的標簽y是1,所以我們規定,r>0。由此,如果我們有新的測試數據 X t X_{t} Xt,則 X t X_{t} Xt的標簽就可以根據以下式子來判定:
y = { 1 ,  if  X t w > 0 0 ,  if  X t w ≤ 0 y=\left\{\begin{array}{ll} 1, & \text { if } \boldsymbol{X}_{\boldsymbol{t}} \boldsymbol{w}>0 \\ 0, & \text { if } \boldsymbol{X}_{\boldsymbol{t}} \boldsymbol{w} \leq 0 \end{array}\right. y={ 1,0, if Xtw>0 if Xtw0
在我們只有兩個特征的時候,實際上這個式子就是:
y = { 1 ,  if  w 1 x 1 + w 2 x 2 + b > 0 0 ,  if  w 1 x 1 + w 2 x 2 + b ≤ 0 y=\left\{\begin{array}{ll} 1, &\text { if } w_{1} x_{1}+w_{2} x_{2}+b>0 \\ 0,&\text { if } w_{1} x_{1}+w_{2} x_{2}+b \leq 0 \end{array}\right. y={ 1,0, if w1x1+w2x2+b>0 if w1x1+w2x2+b0
w 1 x 1 + w 2 x 2 + b w_{1} x_{1}+w_{2} x_{2}+b w1x1+w2x2+b就是我們在神經網絡中錶示 z z z的式子,所以這個式子實際上就是:
y = { 1  if  z > 0 0  if  z ≤ 0 y=\left\{\begin{array}{ll} 1 & \text { if } \boldsymbol{z}>0 \\ 0 & \text { if } \boldsymbol{z} \leq 0 \end{array}\right. y={ 10 if z>0 if z0
你發現了嗎?用線劃分點的數學錶達和我們之前學習的階躍函數的錶示方法一模一樣!在Lesson 8中,我們試著使用階躍函數作為聯系函數替代sigmoid來對與門數據進行分類,最終的結果發現階躍函數也可以輕松實現與sigmoid函數作為聯系函數時的分類效果。我們現在把代碼改寫一下:

import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
andgate = torch.tensor([0,0,0,1], dtype = torch.float32)
def AND(X):
w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)
zhat = torch.mv(X,w)
andhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return andhat
andhat = AND(X)
andgate
#tensor([0., 0., 0., 1.])

實際上,階躍函數的本質也就是用直線劃分點的過程,而這條具有分類功能直線被我們稱為“决策邊界”。在機器學習中,任意分類算法都可以繪制自己的决策邊界,且依賴决策邊界來進行分類。(邏輯回歸自然也有,不過因為sigmoid函數的决策邊界繪制起來沒有階躍函數那麼直接,因此我們沒有選擇邏輯回歸的决策邊界來進行繪制)。不難看出,既然决策邊界的方程就是 z z z的錶達式,那在相同的數據 X X X下(即在相同的數據空間中),决策邊界具體在哪裏就是由我們定義的 w w w决定的。

在之前的代碼中,我們定義了 w 1 w_{1} w1 = 0.15, w 2 w_{2} w2 = 0.15 , b b b = -0.23,這些參數對應的直線就是 0.15 x 1 + 0.15 x 2 − 0.23 0.15 x_{1}+0.15 x_{2}-0.23 0.15x1+0.15x20.23。我們可以用以下代碼,將這條線繪制到樣本點的圖像上:

import numpy as np
x = np.arange(-1,3,0.5)
plt.plot(x,(0.23-0.15*x)/0.15,color="k",linestyle="--"); #這裏是從直線的錶達式變型出的x2 = 的式子

在這裏插入圖片描述
可以看到,這是一條能够將樣本點完美分割的直線。這說明,我們所設置的權重和截距繪制出的直線,可以將與門數據中的兩類點完美分開。所以對於任意的數據,我們只需要找到適合的 w w w b b b就能够確認相應的决策邊界,也就可以自由進行分類了。

現在,讓我們使用階躍函數作為線性結果 z z z之後的函數,在其他典型數據上試試看使用决策邊界進行分類的方式。比如下面的“或門”(OR GATE),特征一或特征二為1的時候標簽就為1的數據。
在這裏插入圖片描述
以及”非與門“(NAND GATE),特征一和特征二都是1的時候標簽就為0,其他時候標簽則為1的數據。
在這裏插入圖片描述
用以下代碼,可以很容易就實現對這兩種數據的擬合:

#定義數據
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]], dtype = torch.float32)
#或門,或門的圖像
#定義或門的標簽
orgate = torch.tensor([0,1,1,1], dtype = torch.float32)
#或門的函數(基於階躍函數)
def OR(X):
w = torch.tensor([-0.08,0.15,0.15], dtype = torch.float32) #在這裏我修改了b的數值
zhat = torch.mv(X,w)
yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return yhat
OR(X)
#tensor([0., 1., 1., 1.])
#繪制直線劃分散點的圖像
x = np.arange(-1,3,0.5)
plt.figure(figsize=(5,3))
plt.title("OR GATE",fontsize=16)
plt.scatter(X[:,1],X[:,2],c=orgate,cmap="rainbow")
plt.plot(x,(0.08-0.15*x)/0.15,color="k",linestyle="--")
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)

在這裏插入圖片描述

#非與門、非與門的圖像
nandgate = torch.tensor([1,1,1,0], dtype = torch.float32)
def NAND(X):
w = torch.tensor([0.23,-0.15,-0.15], dtype = torch.float32) #和與門、或門都不同
的權重
zhat = torch.mv(X,w)
yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return yhat
NAND(X) #圖像
#tensor([1., 1., 1., 0.])
x = np.arange(-1,3,0.5)
plt.figure(figsize=(5,3))
plt.title("NAND GATE",fontsize=16)
plt.scatter(X[:,1],X[:,2],c=nandgate,cmap="rainbow")
plt.plot(x,(0.23-0.15*x)/0.15,color="k",linestyle="--")
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)

在這裏插入圖片描述
可以看到,或門和非與門的情况都可以被很簡單地解决。現在,來看看下面這一組數據:
在這裏插入圖片描述
和之前的數據相比,這組數據的特征沒有變化,不過標簽變成了[0,1,1,0]。這是一組被稱為“异或門”(XOR GATE)的數據,可以看出,當兩個特征的取值一致時,標簽為0,反之標簽則為1。我們同樣把這組數據可視化看看:

xorgate = torch.tensor([0,1,1,0], dtype = torch.float32)
plt.figure(figsize=(5,3))
plt.title("XOR GATE",fontsize=16)
plt.scatter(X[:,1],X[:,2],c=xorgate,cmap="rainbow")
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)

在這裏插入圖片描述
問題出現了——很容易注意到,現在沒有任意一條直線可以將兩類點完美分開,所以無論我們如何調整 w w w b b b的取值都無濟於事。這種情况在機器學習中非常常見,而現實中的大部分數據都是無法用直線完美分開的(相似的是,線性回歸可以擬合空間中的直線,但大部分變量之間的關系都無法用直線來擬合,這也是線性模型的局限性)。此時我們會需要類似如下的曲線來對數據進行劃分:
在這裏插入圖片描述
在神經網絡誕生初期,無法找出曲線的决策邊界是神經網絡的痛,但是現在的神經網絡已經可以輕松解决這個問題。那我們如何把直線的决策邊界變成曲線呢?答案是將單層神經網絡變成多層。來看下面的網格結構:
在這裏插入圖片描述
這是一個多層神經網絡,除了輸入層和輸出層,還多了一層“中間層”。在這個網絡中,數據依然是從左側的輸入層進入,特征會分別進入NAND和OR兩個中間層的神經元,分別獲得NAND函數的結果 y n a n d y_{nand} ynand和OR函數的結果 y o r y_{or} yor,接著, y n a n d y_{nand} ynand y o r y_{or} yor會繼續被輸入下一層的神經元AND,經過AND函數的處理,成為最終結果y。讓我們來使用代碼實現這個結構,來看看這樣的結構是否能够解决异或門的問題:

def XOR(X):
#輸入值:
input_1 = X
#中間層:
sigma_nand = NAND(input_1)
sigma_or = OR(input_1)
x0 = torch.tensor([[1],[1],[1],[1]],dtype=torch.float32)
#輸出層:
input_2 = torch.cat((x0,sigma_nand.view(4,1),sigma_or.view(4,1)),dim=1)
#input_2第一列x0 第二列sigma_nand 第三列sigma_or 按照列拼接
y_and = AND(input_2)
#print("NANE:",y_nand)
#print("OR:",y_or)
return y_and
XOR(X)
#tensor([0., 1., 1., 0.])
print(NAND(X))
OR(X)
#tensor([1., 1., 1., 0.])
#tensor([0., 1., 1., 1.])

可以看到,最終輸出的結果和异或門要求的結果是一致的。可見,擁有更多的”層“幫助我們解决了單層神經網絡無法處理的非線性問題。疊加了多層的神經網絡也被稱為”多層神經網絡“。多層神經網絡是神經網絡在深度學習中的基本形態,接下來,我們就來認識多層神經網絡。

版权声明:本文为[Grateful_Dead424]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201080434565345.html