深层神经网络

1. 异或门问题

在第一篇的博客中,我们使用代码实现了与门

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 = torch.tensor([-0.2,0.15,0.15], dtype = torch.float32)

def LogisticR(X,w):
zhat = torch.mv(X,w) 
sigma = torch.sigmoid(zhat) 
andhat = torch.tensor([int(x) for x in sigma >= 0.5], dtype = torch.float32)

return sigma, andhat

sigma, andhat = LogisticR(X,w)

    考虑到与门的数据只有两维,我们可以将数据可视化,其中,特征x为横坐标,特征x为纵坐标,紫色点代表了类别0,红色点代表类别1,绘制出来的图如下所示:
从零开始的深度学习之旅(2)-LMLPHP
我们可以使用一条直线将紫色和红色的圆点分开,如下图所示:
从零开始的深度学习之旅(2)-LMLPHP

    这条具有分类功能直线被我们称为“决策边界”。
 但并不是所有的图像都可以用直线来进行分类,如下图的异或门所示:

从零开始的深度学习之旅(2)-LMLPHP
   上图没有任意一条直线可以将两类点完美分开,此时我们会需要类似如下的曲线来对数据进行划分.
从零开始的深度学习之旅(2)-LMLPHP


**如何把决策边界从直线转换为曲线,就需要把单层神经网络变成多层**

从零开始的深度学习之旅(2)-LMLPHP
    这是一个多层神经网络,除了输入层和输出层,还多了一层“中间层”。
 在这个网络中,数据从左侧的输入层进入,特征会分别进入NAND和OR两个中间层的神经元,分别获得NAND函数的结果和OR函数的结果,接着, y和y 会继续被输入下一层的神经元AND,经过AND函数的处理,成为最终结果y.

1.1 异或代码实现

import torch
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)
    zhat = torch.mv(X,w)
    yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return yhat

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
    
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)
	return y_and

2.神经网络的层

      在神经网络的隐藏层中,存在两个关键的元素,一个是加和函数,另一个是h(z)(激活函数) 。

    在人工神经网络的神经元上,根据一组输入 定义 该神经元的输出结果的函数,就是激活函数。激活函数一般都是非线性函数,它出现在神经网络中除了输入层以外的每层的每个神经元上.

  神经元有“多进单出”的性质,可以一次性输入多个信号,但是输出只能有一个,因此输入神经元的信息必须以某种方式进行整合,否则神经元就无法将信息传递下去,而最容易的整合方式就是加和。

  我们可以认为加和是神经元自带的性质,只要增加更多的层,就会有更多的加和。但是激活函数的存在却不是如此,假设隐藏层上没有激活函数的话,输出的结果都会是线性的

2.1 去除激活函数的异或门

import torch
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)
    zhat = torch.mv(X,w)
    # yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
    return yhat

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
    
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)
	return y_and
y_and=XOR(X)

输出结果:
从零开始的深度学习之旅(2)-LMLPHP
    可见,神经网络的层数本身不是神经网络解决非线性问题的关键,隐藏层上的激活函数才是,如果 h(z)是线性函数,或不存在,那增加再多的层也没有用。

    如果换成sigmoid函数,神经网络同样会失效,可见,即便是使用了,也不一定能够解决曲线分类的问题。

    在不适合的非线性函数加持下神经网络的层数再多也无法起效。所以,真正能够让神经网络算法“活起来”的关键,是神经网络中的激活函数.

2.2 使用sigmoid函数的异或门

def OR(X):
    w = torch.tensor([-0.08,0.15,0.15], dtype = torch.float32)
    zhat = torch.mv(X,w)
    sigma = torch.sigmoid(zhat)
    return sigma

def AND(X):
    w = torch.tensor([-0.2,0.15, 0.15], dtype = torch.float32)
    zhat = torch.mv(X,w)
    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)
    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)
	return y_and

输出结果:
从零开始的深度学习之旅(2)-LMLPHP

3.从0实现深度神经网络的正向传播

   假设我们有500条数据,20个特征,标签为3分类。我们现在要实现一个三层神经网络.
 这个神经网络的架构如下:第一层有13个神经元,第二层有8个神经元,第三层是输出层。其中,第一层的激活函数是relu,第二层是sigmoid。

import torch
import torch.nn as nn
from torch.nn import functional as F

torch.manual_seed(100)
X=torch.rand((500,20),dtype=torch.float32)
y=torch.randint(low=0,high=3,size=(500,1),dtype=torch.float32)

class Model(nn.Module):
    def __init__(self,in_features=10,out_features=2):
        super(Model,self).__init__()
        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)
    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_)
net(X)

从零开始的深度学习之旅(2)-LMLPHP

11-23 14:16