python机器学习之神经网络(二)

本文介绍了为了解决Rosenblatt感知器在非线性分类上的局限性,如何构建多层感知器,并使用tanh函数作为激活函数。通过介绍网络结构、激活函数的作用以及权值更新法则,展示了使用Python实现的多层神经网络程序,以二维数据进行训练和测试,网络包含多个隐藏层和一个输出层。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于Rosenblatt感知器的局限性,对于非线性分类的效果不理想。为了对线性分类无法区分的数据进行分类,需要构建多层感知器结构对数据进行分类,多层感知器结构如下:


该网络由输入层,隐藏层,和输出层构成,能表示种类繁多的非线性曲面,每一个隐藏层都有一个激活函数,将该单元的输入数据与权值相乘后得到的值(即诱导局部域)经过激活函数,激活函数的输出值作为该单元的输出,激活函数类似与硬限幅函数,但硬限幅函数在阈值处是不可导的,而激活函数处处可导。本次程序中使用的激活函数是tanh函数,公式如下:



tanh函数的图像如下:


程序中具体的tanh函数形式如下:


就是神经元j的诱导局部域

它的局部梯度分两种情况:

(1)神经元j没有位于隐藏层:


(2)神经元j位于隐藏层:


其中k是单元j后面相连的所有的单元。

局部梯度得到之后,根据增量梯度下降法的权值更新法则

即可得到下一次的权值w,经过若干次迭代,设定误差条件,即可找到权值空间的最小值。

python程序如下,为了能够可视化,训练数据采用二维数据,每一个隐藏层有8个节点,设置了7个隐藏层,一个输出层,输出层有2个单元:

import numpy as np
import random
import copy
import matplotlib.pyplot as plt


#x和d样本初始化
train_x = [[1,6],[3,12],[3,9],[3,21],[2,16],[3,15]]
d =[[1,0],[1,0],[0,1],[0,1],[1,0],[0,1]]
warray_txn=len(train_x[0])
warray_n=warray_txn*4

#基本参数初始化
oldmse=10**100
fh=1
maxtrycount=500
mycount=0.0
if maxtrycount>=20:
        r=maxtrycount/5
else:
        r=maxtrycount/2
#sigmoid函数
ann_sigfun=None
ann_delta_sigfun=None
#总层数初始化,比非线性导数多一层线性层
alllevel_count=warray_txn*4
# 非线性层数初始化
hidelevel_count=alllevel_count-1

#学习率参数 
learn_r0=0.002  
learn_r=learn_r0      
#动量参数
train_a0=learn_r0*1.2
train_a=train_a0
expect_e=0.05
#对输入数据进行预处理
ann_max=[]
for m_ani in xrange(0,warray_txn):             #找出训练数据中每一项的最大值
    temp_x=np.array(train_x)
    ann_max.append(np.max(temp_x[:,m_ani]))
ann_max=np.array(ann_max)

def getnowsx(mysx,in_w):
        '''生成本次的扩维输入数据  '''
        '''mysx==>输入数据,in_w==>权值矩阵,每一列为一个神经元的权值向量'''
        global warray_n
        mysx=np.array(mysx)
        x_end=[]   
        for i in xrange(0,warray_n):
                x_end.append(np.dot(mysx,in_w[:,i]))
        return x_end

def get_inlw(my_train_max,w_count,myin_x):
        '''找出权值矩阵均值接近0,输出结果方差接近1的权值矩阵'''
        #对随机生成的多个权值进行优化选择,选择最优的权值
        global warray_txn
        global warray_n
        mylw=[]
        y_in=[]
        #生成测试权值
        mylw=np.random.rand(w_count,warray_txn,warray_n)
        for ii in xrange (0,warray_txn):
            mylw[:,ii,:]=mylw[:,ii,:]*1/float(my_train_max[ii])-1/float(my_train_max[ii])*0.5

        #计算输出
        for i in xrange(0,w_count):
                y_in.append([])
                for  xj in xrange(0,len(myin_x)):
                        y_in[i].append(getnowsx(myin_x[xj],mylw[i]))
        #计算均方差
        mymin=10**5
        mychoice=0
        for i in xrange(0,w_count):
                myvar=np.var(y_in[i])
                if abs(myvar-1)<mymin:
                        mymin=abs(myvar-1)
                        mychoice=i
        #返回数据整理的权值矩阵
        return mylw[mychoice]
mylnww=get_inlw(ann_max,300,train_x)

def get_inputx(mytrain_x,myin_w):
        '''将训练数据经过权值矩阵,形成扩维数据'''
        end_trainx=[]
        for i in xrange(0,len(mytrain_x)):
                end_trainx.append(getnowsx(mytrain_x[i],myin_w))        
        return end_trainx
        
x=get_inputx(train_x,mylnww)#用于输入的扩维数据
#对测试数据进行扩维
def get_siminx(sim_x):
        global mylnww
        myxx=np.array(sim_x)
        return get_inputx(myxx,mylnww)
#计算一层的初始化权值矩阵
def getlevelw(myin_x,wo_n,wi_n,w_count):
        mylw=[]
        y_in=[]
        #生成测试权值
        mylw=np.random.rand(w_count,wi_n,wo_n)
        mylw=mylw*2.-1

        #计算输出
        for i in xrange(0,w_count):
                y_in.append([])
                for  xj in xrange(0,len(myin_x)):
                    x_end=[]   
                    for myii in xrange(0,wo_n):
                        x_end.append(np.dot(myin_x[xj],mylw[i,:,myii]))
                    y_in[i].append(x_end)
        #计算均方差
        mymin=10**3
        mychoice=0
        for i in xrange(0,w_count):
                myvar=np.var(y_in[i])
                if abs(myvar-1)<mymin:
                        mymin=abs(myvar-1)
                        mychoice=i
        #返回数据整理的权值矩阵
        csmylw=mylw[mychoice]
        return csmylw,y_in[mychoice]        
ann_w=[]
def init_annw():
    global x
    global hidelevel_count
    global warray_n
    global d
    global ann_w
    ann_w=[]
 
    lwyii=np.array(x)
    #初始化每层的w矩阵
    for myn in xrange(0,hidelevel_count):               
        #层数
        ann_w.append([])  
        if myn==hidelevel_count-1:
            for iii in xrange(0,warray_n):
                ann_w[myn].append([])
                for jjj  in xrange(0,warray_n):
                                ann_w[myn][iii].append(0.0)
        elif myn==hidelevel_count-2:            
            templw,lwyii=getlevelw(lwyii,len(d[0]),warray_n,200)
            for xii in xrange(0,warray_n):
                ann_w[myn].append([])
                for xjj in xrange(0,len(d[0])): 
                    ann_w[myn][xii].append(templw[xii,xjj]) 
                for xjj in xrange(len(d[0]),warray_n):
                    ann_w[myn][xii].append(0.0)
        else: 
            templw,lwyii=getlevelw(lwyii,warray_n,warray_n,200)
            for xii in xrange(0,warray_n):
                ann_w[myn].append([])
                for xjj in xrange(0,warray_n): 
                    ann_w[myn][xii].append(templw[xii,xjj])                
    ann_w=np.array(ann_w)

def generate_lw(trycount):
    global ann_w
    print u"产生权值初始矩阵",             
    meanmin=1 
    myann_w=ann_w       
    alltry=30
    tryc=0
    while tryc<alltry:
        for i_i in range(trycount):
            print ".",
            init_annw()
            if abs(np.mean(np.array(ann_w)))<meanmin:
                meanmin=abs(np.mean(np.array(ann_w)))
                myann_w=ann_w
        tryc+=1
        if abs(np.mean(np.array(myann_w)))<0.008:break
        
    ann_w=myann_w
    print
    print u"权值矩阵平均:%f"%(np.mean(np.array(ann_w)))
    print u"权值矩阵方差:%f"%(np.var(np.array(ann_w)))      
generate_lw(15)

#前次训练的权值矩阵
ann_oldw=copy.deepcopy(ann_w)
#梯度初始化
#输入层即第一层隐藏层不需要,所以第一层的空间无用
ann_delta=[]
for i in xrange(0,hidelevel_count):
        ann_delta.append([])     
        for j in xrange(0,warray_n):
                ann_delta[i].append(0.0)
ann_delta=np.array(ann_delta)

#输出矩阵yi初始化
ann_yi=[]
for i in xrange(0,alllevel_count):
        #第一维是层数,从0开始
        ann_yi.append([])
        for j in xrange(0,warray_n):
                #第二维是神经元
                ann_yi[i].append(0.0)
ann_yi=np.array(ann_yi)     


      
#输出层函数        
def o_func(myy):
        myresult=[]
        mymean=np.mean(myy)
        for i in xrange(0,len(myy)):
                if myy[i]>=mymean:
                        myresult.append(1.0)
                else:
                        myresult.append(0.0)
        return np.array(myresult)
        
def get_e(myd,myo):
        return np.array(myd-myo)
def ann_atanh(myv):
        atanh_a=1.7159#>0
        atanh_b=2/float(3)#>0
        temp_rs=atanh_a*np.tanh(atanh_b*myv)
        return temp_rs
def ann_delta_atanh(myy,myd,nowlevel,level,n,mydelta,myw):
    anndelta=[]
    atanh_a=1.7159#>0
    atanh_b=2/float(3)#>0  
    if nowlevel==level:
       #输出层
        anndelta=(float(atanh_b)/atanh_a)*(myd-myy)*(atanh_a-myy)*(atanh_a+myy)
    else:
       #隐藏层
        anndelta=(float(atanh_b)/atanh_a)*(atanh_a-myy)*(atanh_a+myy)            
        temp_rs=[]
        for j in xrange(0,n):
                temp_rs.append(sum(myw[j]*mydelta))                
        anndelta=anndelta*temp_rs       
    return anndelta

def sample_train(myx,myd,n,sigmoid_func,delta_sigfun):
        '''一个样本的前向和后向计算'''
        global ann_yi
        global ann_delta
        global ann_w
        global ann_wj0
        global ann_y0
        global hidelevel_count
        global alllevel_count
        global learn_r
        global train_a
        global ann_oldw
        level=hidelevel_count
        allevel=alllevel_count
        
        #清空yi输出信号数组        
        hidelevel=hidelevel_count
        alllevel=alllevel_count
        for i in xrange(0,alllevel):
                #第一维是层数,从0开始
                for j in xrange(0,n):
                        #第二维是神经元
                        ann_yi[i][j]=0.0
        ann_yi=np.array(ann_yi)
        yi=ann_yi

        #清空delta矩阵
        for i in xrange(0,hidelevel-1):    
                for j in xrange(0,n):
                        ann_delta[i][j]=0.0
        delta=ann_delta     
        #保留W的拷贝,以便下一次迭代
        ann_oldw=copy.deepcopy(ann_w)
        oldw=ann_oldw
        #前向计算

        #对输入变量进行预处理              
        myo=np.array([])
        for nowlevel in xrange(0,alllevel):
                #一层层向前计算
                #计算诱导局部域
                my_y=[]
                myy=yi[nowlevel-1] 
                myw=ann_w[nowlevel-1]                
                if nowlevel==0:
                        #第一层隐藏层
                        my_y=myx
                        yi[nowlevel]=my_y                        
                elif nowlevel==(alllevel-1):
                        #输出层
                        my_y=o_func(yi[nowlevel-1,:len(myd)])
                        yi[nowlevel,:len(myd)]=my_y
                elif nowlevel==(hidelevel-1):
                        #最后一层输出层
                        for i in xrange(0,len(myd)):
                                temp_y=sigmoid_func(np.dot(myw[:,i],myy))
                                my_y.append(temp_y)                        
                        yi[nowlevel,:len(myd)]=my_y 
                else:
                        #中间隐藏层
                        for i in xrange(0,len(myy)):
                                temp_y=sigmoid_func(np.dot(myw[:,i],myy))
                                my_y.append(temp_y)
                        yi[nowlevel]=my_y

     
        #计算误差与均方误差
        myo=yi[hidelevel-1][:len(myd)]
        myo_end=yi[alllevel-1][:len(myd)]
        mymse=get_e(myd,myo_end)
 
        #反向计算
        #输入层不需要计算delta,输出层不需要计算W


        #计算delta
        for nowlevel in xrange(level-1,0,-1):
                if nowlevel==level-1:
                        mydelta=delta[nowlevel]
                        my_n=len(myd)
                else:
                        mydelta=delta[nowlevel+1]
                        my_n=n
                myw=ann_w[nowlevel]                
                if nowlevel==level-1:
                        #输出层
                        mydelta=delta_sigfun(myo,myd,None,None,None,None,None)
##                        mydelta=mymse*myo
                elif nowlevel==level-2:
                        #输出隐藏层的前一层,因为输出结果和前一层隐藏层的神经元数目可能存在不一致
                        #所以单独处理,传相当于输出隐藏层的神经元数目的数据
                        mydelta=delta_sigfun(yi[nowlevel],myd,nowlevel,level-1,my_n,mydelta[:len(myd)],myw[:,:len(myd)])
                else:
                        mydelta=delta_sigfun(yi[nowlevel],myd,nowlevel,level-1,my_n,mydelta,myw)
                        
                delta[nowlevel][:my_n]=mydelta
        #计算与更新权值W 
        for nowlevel in xrange(level-1,0,-1):
                #每个层的权值不一样
                if nowlevel==level-1:
                        #输出层
                        my_n=len(myd)
                        mylearn_r=learn_r*0.8
                        mytrain_a=train_a*1.6
                elif nowlevel==1:
                        #输入层
                        my_n=len(myd)
                        mylearn_r=learn_r*0.9
                        mytrain_a=train_a*0.8                       
                else:
                        #其它层
                        my_n=n
                        mylearn_r=learn_r
                        mytrain_a=train_a

                pre_level_myy=yi[nowlevel-1]
                pretrain_myww=oldw[nowlevel-1]
                pretrain_myw=pretrain_myww[:,:my_n]

                #第二个调整参数
                temp_i=[]                
                
                for i in xrange(0,n):
                        temp_i.append([])
                        for jj in xrange(0,my_n):
                                temp_i[i].append(mylearn_r*delta[nowlevel,jj]*pre_level_myy[i])
                temp_rs2=np.array(temp_i)
                temp_rs1=mytrain_a*pretrain_myw
                #总调整参数
                temp_change=temp_rs1+temp_rs2               
                my_ww=ann_w[nowlevel-1]                
                my_ww[:,:my_n]+=temp_change

        return mymse

def train_update(level,nowtraincount,sigmoid_func,delta_sigfun):
        '''一次读取所有样本,然后迭代一次进行训练'''
        #打乱样本顺序
        global learn_r
        global train_a
        global train_a0
        global learn_r0
        global r
        global x
        global d
        global maxtrycount
        global oldmse
        x_n=len(x)
        ids=range(0,x_n)
        train_ids=[]
        sample_x=[]
        sample_d=[]

        while len(ids)>0:
                myxz=random.randint(0,len(ids)-1)
                train_ids.append(ids[myxz])
                del ids[myxz]
                        
        for i in xrange(0,len(train_ids)):
                sample_x.append(x[train_ids[i]])
                sample_d.append(d[train_ids[i]])
        sample_x=np.array(sample_x)
        sample_d=np.array(sample_d)

                
        #读入x的每个样本,进行训练        
        totalmse=0.0
        mymse=float(10**-10)     
        for i in xrange(0,x_n):
                
                mymse=sample_train(sample_x[i],sample_d[i],warray_n,sigmoid_func,delta_sigfun)
                totalmse+=sum(mymse*mymse)
        totalmse=np.sqrt(totalmse/float(x_n))
        print u"误差为:%f"  %(totalmse)
        nowtraincount[0]+=1
        learn_r=learn_r0/(1+float(nowtraincount[0])/r)
        train_a=train_a0/(1+float(nowtraincount[0])/r)
        if nowtraincount[0]>=maxtrycount:
                return False,True,totalmse                 
        elif totalmse<expect_e:
        #(totalmse-oldmse)/oldmse>0.1 and (totalmse-oldmse)/oldmse<1:
                print u"训练成功,正在进行检验"
                totalmse=0.0
                for i in xrange(0,x_n):
                        mytemper=(sample_d[i]-simulate(sample_x[i],sigmoid_func,delta_sigfun))                       
                        totalmse+=sum(mytemper*mytemper)
                totalmse=np.sqrt(totalmse/float(x_n))
                if totalmse<expect_e:
                        return False,False,totalmse
        oldmse=totalmse
        return True,False,totalmse
                               
def train():
        '''训练样本,多次迭代'''
        global hidelevel_count
        nowtraincount=[]
        nowtraincount.append(0)
        #sigmoid函数指定
        delta_sigfun=ann_delta_atanh
        sigmoid_func=ann_atanh
        
        tryerr=0        
        while True:
                print u"-------开始第%d次训练---------"%(nowtraincount[0]+1),
                iscontinue,iscountout,mymse=train_update(hidelevel_count,nowtraincount,sigmoid_func,delta_sigfun)
                if not iscontinue:
                        if iscountout :
                                print u"训练次数已到,误差为:%f"%mymse 
                                tryerr+=1 
                                if tryerr>3:
                                    break
                                else:
                                    print u"训练失败,重新尝试第%d次"%tryerr
                                    nowtraincount[0]=0
                                    generate_lw(15+tryerr*2)                                                                                                           
                        else:
                                print u"训练成功,误差为:%f"%mymse                              
                                break
              
def simulate(myx,sigmoid_func,delta_sigfun):
        '''一个样本的仿真计算'''
        print u"仿真计算中"        
        global ann_yi
        global ann_w
        global ann_wj0
        global ann_y0
        global hidelevel_count
        global alllevel_count
        global d
        myd=d[0]

        myx=np.array(myx)
        n=len(myx)

        level=hidelevel_count
        allevel=alllevel_count
        
        #清空yi输出信号数组        
        hidelevel=hidelevel_count
        alllevel=alllevel_count
        for i in xrange(0,alllevel):
                #第一维是层数,从0开始
                for j in xrange(0,n):
                        #第二维是神经元
                        ann_yi[i][j]=0.0
        ann_yi=np.array(ann_yi)
        yi=ann_yi

        #前向计算
        myo=np.array([])
        myy=np.array([])
        for nowlevel in xrange(0,alllevel):
                #一层层向前计算
                #计算诱导局部域
                my_y=[]
                myy=yi[nowlevel-1]
                myw=ann_w[nowlevel-1]                
                if nowlevel==0:
                        #第一层隐藏层
                        my_y=myx
                        yi[nowlevel]=my_y                        
                elif nowlevel==(alllevel-1):
                        #线性输出层,使用线性激活
                        my_y=o_func(yi[nowlevel-1,:len(myd)])
                        yi[nowlevel,:len(myd)]=my_y                       
                elif nowlevel==(hidelevel-1):
                        #最后一层隐藏输出层,使用线性激活
                        for i in xrange(0,len(myd)):
                                temp_y=sigmoid_func(np.dot(myw[:,i],myy))
                                my_y.append(temp_y)                           
                        yi[nowlevel,:len(myd)]=my_y 
                else:
                        #中间隐藏层
                        #中间隐藏层需要加上偏置
                        for i in xrange(0,len(myy)):
                                temp_y=sigmoid_func(np.dot(myw[:,i],myy))
                                my_y.append(temp_y)
                        yi[nowlevel]=my_y

        return yi[alllevel-1,:len(myd)]        
train()

delta_sigfun=ann_delta_atanh
sigmoid_func=ann_atanh


for xn in xrange(0,len(x)):
        if simulate(x[xn],sigmoid_func,delta_sigfun)[0]>0:
                plt.plot(train_x[xn][0],train_x[xn][1],"bo")
        else:
                plt.plot(train_x[xn][0],train_x[xn][1],"b*")


                         
temp_x=np.random.rand(20)*10
temp_y=np.random.rand(20)*20+temp_x
myx=temp_x
myy=temp_y
plt.subplot(111)
x_max=np.max(myx)+5
x_min=np.min(myx)-5
y_max=np.max(myy)+5
y_min=np.min(myy)-5
plt.xlim(x_min,x_max)
plt.ylim(y_min,y_max)
for i in xrange(0,len(myx)):
        test=get_siminx([[myx[i],myy[i]]])
        if simulate(test,sigmoid_func,delta_sigfun)[0]>0:            
                plt.plot(myx[i],myy[i],"ro")
        else:
                plt.plot(myx[i],myy[i],"r*")  

plt.show()


图中蓝色是训练数据,红色是测试数据,圈圈代表类型[1,0],星星代表类型[0,1]




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值