9.深层神经网络

一、异或门问题

1690441601353.png
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
andhat
andgate
andgate == andhat

考虑到与门的数据只有两维,我们可以通过python中的matplotlib代码将数据可视化,其中,特征为横坐标,特征为纵坐标,紫色点代表了类别0,红色点代表类别1。

1690454877637.png
1690454939330.png
1690455802986.png
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
1690455919686.png
1690456035102.png
#定义数据
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)
#绘制直线划分散点的图像
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)
#图像
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)
1690456212420.png
1690456306437.png
1690456536221.png
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)
    y_and = AND(input_2)
    #print("NANE:",y_nand)
    #print("OR:",y_or)
    return y_and

可以看到,最终输出的结果和异或门要求的结果是一致的。可见,拥有更多的”层“帮助我们解决了单层神经网络无法处理的非线性问题。叠加了多层的神经网络也被称为”多层神经网络“。多层神经网络是神经网络在深度学习中的基本形态,接下来,我们就来认识多层神经网络。

二、黑箱:深层神经网络的不可解释性

image.png

首先从结构上来看,多层神经网络比单层神经网络多出了“中间层”。中间层常常被称为隐藏层(hidden layer),理论上来说可以有无限层,所以在图像表示中经常被省略。层数越多,神经网络的模型复杂度越高,一般也认为更深的神经网络可以解决更加复杂的问题。在学习中,通常我们最多只会设置3~5个隐藏层,但在实际工业场景中会更多。还记得这张图吗?当数据量够大时,现代神经网络层数越深,效果越好。

1690457125837.png
1690457196997.png
1690457246239.png
1690457408331.png
image.png
1690459296058.png

三、探索多层神经网络:层 vs h(z)

1690459633788.png
#回忆一下XOR数据的真实标签
xorgate = torch.tensor([0,1,1,0],dtype=torch.float32)
def AND(X):
    w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)
    zhat = torch.mv(X,w)
    #下面这一行就是阶跃函数的表达式,注意AND函数是在输出层,所以保留输出层的阶跃函数g(z)
    andhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return andhat
def OR(X):
    w = torch.tensor([-0.08,0.15,0.15], dtype = torch.float32) #在这里我修改了b的数
值
    zhat = torch.mv(X,w)
    #注释掉阶跃函数,相当于h(z)是恒等函数
    #yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return zhat
def NAND(X):
    w = torch.tensor([0.23,-0.15,-0.15], dtype = torch.float32) 
    zhat = torch.mv(X,w)
    #注释掉阶跃函数,相当于h(z)是恒等函数
    #yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return zhat
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)
    y_and = AND(input_2)
    #print("NANE:",y_nand)
    #print("OR:",y_or)
    return y_and
XOR(X)
1690459782897.png
1690460099294.png
def AND(X):
    w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)
    zhat = torch.mv(X,w)
    #下面这一行就是阶跃函数的表达式,注意AND函数是在输出层,所以保留输出层的阶跃函数g(z)
    andhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return andhat
def OR(X):
    w = torch.tensor([-0.08,0.15,0.15], dtype = torch.float32) #在这里我修改了b的数
值
    zhat = torch.mv(X,w)
    #h(z), 使用sigmoid函数
    sigma = torch.sigmoid(zhat)
    return sigma
def NAND(X):
    w = torch.tensor([0.23,-0.15,-0.15], dtype = torch.float32) 
    zhat = torch.mv(X,w)
    #h(z), 使用sigmoid函数
    sigma = torch.sigmoid(zhat)
    return sigma
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)
    y_and = AND(input_2)
    #print("NANE:",y_nand)
    #print("OR:",y_or)
    return y_and
XOR(X)
1690462849099.png
1690463301073.jpg
1690463429857.png
#如果g(z)是sigmoid函数,而h(z)是阶跃函数
#输出层,以0.5为sigmoid的阈值
def AND(X):
    w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)
    zhat = torch.mv(X,w)
    sigma = torch.sigmoid(zhat)
    andhat = torch.tensor([int(x) for x in sigma >= 0.5],dtype=torch.float32)
    return andhat
#隐藏层,OR与NAND都使用阶跃函数作为h(z)
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
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
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)
    y_and = AND(input_2)
    #print("NANE:",y_nand)
    #print("OR:",y_or)
    return y_and
XOR(X)
1690463642912.png

五、从0实现深度神经网络的正向传播

image.png

学到这里,我们已经学完了一个普通深度神经网络全部的基本元素——用来构筑神经网络的结构的层与激活函数,输入神经网络的数据(特征、权重、截距),并且我们了解从左向右的过程是神经网络的正向传播(也叫做前向传播,或者向前传播)。还记得我们的架构图吗?在过去的课程中我们所学习的内容都是在torch.nn这个模块下,现在我们就使用封装好的torch.nn模块来实现一个完整、多层的神经网络的正向传播。
假设我们有500条数据,20个特征,标签为3分类。我们现在要实现一个三层神经网络,这个神经网络的架构如下:第一层有13个神经元,第二层有8个神经元,第三层是输出层。其中,第一层的激活函数是relu,第二层是sigmoid。我们要如何实现它呢?来看代码:

#继承nn.Module类完成正向传播
import torch
import torch.nn as nn
from torch.nn import functional as F
#确定数据
torch.manual_seed(420)
X = torch.rand((500,20),dtype=torch.float32)
y = torch.randint(low=0,high=3,size=(500,1),dtype=torch.float32)
#继承nn.Modules类来定义神经网路的架构
class Model(nn.Module):
    #init:定义类本身,__init__函数是在类被实例化的瞬间就会执行的函数
    def __init__(self,in_features,out_features):
        super(Model,self).__init__() #super(请查找这个类的父类,请使用找到的父类替换现在
的类)
        self.linear1 = nn.Linear(in_features,13,bias=True) #输入层不用写,这里是隐藏
层的第一层
        self.linear2 = nn.Linear(13,8,bias=True)
        self.output = nn.Linear(8,out_features,bias=True)
    
    #__init__之外的函数,是在__init__被执行完毕后,就可以被调用的函数
    def forward(self, x):
        z1 = self.linear1(x)
        sigma1 = torch.relu(z1)
        z2 = self.linear2(sigma1)
        sigma2 = torch.sigmoid(z2)
        z3 = self.output(sigma2)
        sigma3 = F.softmax(z3,dim=1)
        return sigma3
input_ = X.shape[1] #特征的数目
output_ = len(y.unique()) #分类的数目
#实例化神经网络类
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)
#在这一瞬间,所有的层就已经被实例化了,所有随机的w和b也都被建立好了
#前向传播
net(X)
net.forward(X)
#查看输出的标签
sigma = net.forward(X)
sigma.max(axis=1)
#查看每一层上的权重w和截距b
net.linear1.weight
net.linear1.bias

如果你是初次接触“类”,那这段代码中新内容可能会有点多。但如果你对python基础比较熟悉,你就会发现这个类其实非常简单。在神经网络的类中,我们以线性的顺序从左向右描绘神经网络的计算过程,并且无需考虑在这之间 的结构是如何,矩阵之间如何进行相互运算。所以只要你对自己要建立的神经网络的架构是熟悉的,pytorch代码就非常容易。在这里,特别需要强调一下的可能是super函数的用法。
super函数用于调用父类的一个函数,在这里我们使用super函数来帮助子类(我们建立的神经网络模型)继承一些通过类名调用无法被继承的属性和方法。谨防小伙伴们不熟悉super的用法,在这里我们来说明一下:

#建立一个父类
class FooParent(object):
    def __init__(self):
        self.parent = 'PARENT!!'
        print ('Running __init, I am parent')
    
    def bar(self,message):
        self.bar = "This is bar"
        print ("%s from Parent" % message)
FooParent() #父类实例化的瞬间,运行自己的__init__
FooParent().parent #父类运行自己的__init__中定义的属性
#建立一个子类,并通过类名调用让子类继承父类的方法与属性
class FooChild(FooParent):
    def __init__(self):
        print ('Running __init, I am child')
#查看子类是否继承了方法
FooChild().bar("HAHAHA")
FooChild().parent #子类没有继承到父类的__init__中定义的属性
#为了让子类能够继承到父类的__init__函数中的内容,我们使用super函数
#新建一个子类,并使用super函数
class FooChild(FooParent):
    def __init__(self):
        super(FooChild,self).__init__()
        print ('Child')
        print ("I am running the __init__")
#再次调用parent属性
FooChild() #执行自己的init功能的同时,也执行了父类的init函数定义的功能
FooChild().parent

通过使用super函数,我们的神经网络模型从nn.Module那里继承了哪些有用的属性和方法呢?首先,如果不加super函数,神经网络的向前传播是无法运行的:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容