机器学习基础(五)—神经网络实现

神经网络代码实现

  1. 读取数据:

因为要求y为下图的形式,所以要对y进行处理
在这里插入图片描述

import numpy as np
import pandas as pd
from scipy.io import loadmat
from sklearn.preprocessing import OneHotEncoder
path ='E:/Data/Ng/Coursera-ML-AndrewNg-Notes-master/code/ex4-NN back propagation/ex4data1.mat'
data = loadmat(path)
X = data['X']
y = data['y']
encoder = OneHotEncoder(sparse=False)
y_encoder = encoder.fit_transform(y) #可以直接利用OneHotEncoder进行转换,比如说y当中一共有10个不同的元素,5000列,那么转化以后每一个数字都单独为1列,比如1就是[1,0,0,...,0],最后形状为(5000,10)
  1. 定义一个sigmoid函数和前向传播函数
    根据下列公式可进行编写
    在这里插入图片描述
def sigmoid(z):
    return 1/(1+np.exp(-z))

def forward(X,y_encoder,theta1,theta2):
	"""计算前向传播的值
		Params:
			X:特征值(5000*400)
			y_encoder:输出变量(5000*10)
			theta1;theta2:初始化的值
		return"""
    m = X.shape[0] #获取样本总数,用于插入列的时候使用
    a1 = np.insert(X,0,values=np.ones(m),axis=1) #插入一个x0=1的列,
    z2 = a1 * theta1.T 
    #因为 上图中可以看到当输入的样本数为1的时候z2= θ1*a1,即θ的每一行都要和样本值相乘。但是这里我们输入的样本值为5000,θ1为(25,401)的矩阵,也就是说我们的5000个样本都要单独和25行θ值对应相乘,所以这里的θ要进行转置。
    a2 = np.insert(sigmoid(z2),0,values=np.ones(m),axis=1)
    z3 = a2 *theta2.T
    h = sigmoid(z3)
    return a1,z2,a2,z3,h
  1. 根据公式编写代价函数:
    在这里插入图片描述
def cost(params,in_size,h_size,label_nums,X,y_encoder,l):
	"""代价函数的前面部分
		Params:
			params:存放所有初始化theta1,theta2值的ndarray
			in_size:特征值的数量
			h_szie:隐藏层的项数
			label_nums:总分类数
			X:特征值数据
			y_encoder:转换后的y
			l:lambda
		return
			J:代价函数"""
	
	m = X.shape[0] #获取样本总数
	X = np.mat(X)
	y_encoder = np.mat(y_encoder)
	theta1 = np.mat(np.reshape(params[:h_size*(in_size+1)],(h_size,in_size+1))) #(25,401)
	theta2 = np.mat(np.reshape(params[h_size*(in_size+1):],(label_nums,h_size+1))) #(10,26)
	a1,z2,a2,z3,h = forward(X,y_encoder,theta1,theta2) #获取h
	
	J= 0 #初始化代价函数
	for i in range(m): #一个样本一个样本的计算代价函数
		first_term = np.multiply(-y[i,:],np.log(h[i,:]))
		second_term = np.multiply((1-y)[i,:],np.log(1-h[i,:]))
		#注意神经网络的代价函数要求两次和,一次是m一次是k。这个循环是用来求m的,那么在循环里面要将k求好
		J += np.sum(first_term-second_term) #累加求m,sum是在求k
	
	J=J/m
	
	J = J + (l/(2*m)*(np.sum(np.power(theta1[:,1:],2))+np.sum(np.power(theta2[:,1:],2))) 
	#加上正则优化项
	return J	
  1. 后向传播的偏导数项
    后向传播比较难以理解,代码中使用的是一个一个样本计算的方法。
    求代价函数的偏导数。

    先假设对一个样本一个权重求偏导:
    假设神经网络有四层,中间两层为隐藏层
    在这里插入图片描述
    一般化可以这样表示:
    在这里插入图片描述

矢量化,因为a此时不再只是一个数,而是所有样本的数,所以要将其转置。
在这里插入图片描述

再求前一层的偏导
在这里插入图片描述

在这里插入图片描述

代码实现:

def sigmoid_gradient(z): #g的偏导数项
    return np.multiply(sigmoid(z),(1-sigmoid(z)))

def bp(params,in_size,h_size,label_nums,X,y_encoder,l):
	"""计算所有的代价函数对于θ的偏导数项
		Params:
			params:存放所有初始化theta1,theta2值的ndarray
			in_size:特征值的数量
			h_szie:隐藏层的项数
			label_nums:总分类数
			X:特征值数据
			y_encoder:转换后的y
			l:lambda
		return
			J:代价函数
			grad:梯度值""""""
    m = X.shape[0]
    X = np.mat(X)
    y_encoder = np.mat(y_encoder)
    theta1 = np.mat(np.reshape(params[:(in_size+1)*h_size],(h_size,in_size+1))) #(25,401)
    theta2 = np.mat(np.reshape(params[(in_size+1)*h_size:],(label_nums,h_size+1)))#(10,26)
    
    a1,z2,a2,z3,h=forward(X,y_encoder,theta1,theta2)
    
    J=0
    for i in range(m):
        first = np.multiply(-y_encoder[i,:],np.log(h[i,:]))
        second = np.multiply((1-y_encoder)[i,:],np.log(1-h[i,:]))
        J += np.sum(first-second)
    J = J/m
    
    reg_term = (np.sum(np.power(theta1[:,1:],2)) + np.sum(np.power(theta2[:,1:],2)))*(float(l)/(2*m))
    
    J += reg_term
    
    delta2 = np.zeros(theta2.shape) #(25,401) #初始化梯度2
    delta1 = np.zeros(theta1.shape) #(10,26) #初始化梯度1
    
    for t in range(m): #一个样本一个样本的进行计算
        a1t = a1[t,:] #(1,401) #拿出第一个样本的值
        z2t = z2[t,:] #(1,25)
        a2t = a2[t,:] #(1,26)
        z3t = z3[t,:] #(1,10)
        ht = h[t,:] #(1,10)
        yt = y_encoder[t,:] #(1,10)
        
        d3 = ht - yt #(1,10) #计算δ(3)
        z2t = np.insert(z2t,0,values=np.ones(1)) 
        d2 = np.multiply((theta2.T * d3.T).T,sigmoid_gradient(z2t) )
		#注意这里是用的点乘,所以用multiply        
        delta1 = delta1 + d2[:,1:].T*a1t #偏导向1,因为第一列不用计算
        delta2 = delta2 + d3.T*a2t #偏导项2
    delta1 = delta1/m
    delta2 = delta2/m
    
    delta1[:,1:] = delta1[:,1:] + (theta1[:,1:]*l)/m #正则优化
    delta2[:,1:] = delta2[:,1:] + (theta2[:,1:]*l)/m
    
    grad = np.concatenate((np.ravel(delta1),np.ravel(delta2)))
    return J,grad

if __name__ == '__main__':
	in_size = 400
	h_size=25
	label_nums=10
	l=1
	params = (np.random.random(size = h_size*(in_size+1)+label_nums*(h_size+1))-0.5)*0.25
	J,grad = bp(params,in_size,h_size,label_nums,X,y_encoder,l)
	print(grad.shape)
	

结果如下:
在这里插入图片描述

  1. 利用库去拟合最优参数
from scipy.optimize import minimize

fmin = minimize(fun=bp,x0=params,args=(in_size,h_size,label_nums,X,y_encoder,l),method='TNC',jac=True,options={'maxiter':250})
fmin

在这里插入图片描述

  1. 通过h值来检测准确度
X = np.mat(X)
theta1 = np.mat(np.reshape(fmin.x[:(in_size+1)*h_size],(h_size,in_size+1))) #(25,401)
theta2 = np.mat(np.reshape(fmin.x[(in_size+1)*h_size:],(label_nums,h_size+1)))#(10,26)
a1,z2,a2,z3,h=forward(X,y_encoder,theta1,theta2)
y_pred = np.array(np.argmax(h,axis=1)+1) #利用h来获得结果,输出可能性最大的值的索引,
#因为我们的数字是从1开始到10
y_pred

在这里插入图片描述

  1. 测试精准度
correct = [1 if a == b else 0 for (a, b) in zip(y_pred, y)]
accuracy = (sum(map(int, correct)) / float(len(correct)))
print ('accuracy = {0}%'.format(accuracy * 100))

在这里插入图片描述

每一个代价函数对于权重的偏导数由两个部分组成。
第一部分:z对于权重的偏导数
第二部分:代价函数对于z的偏导数
在这里插入图片描述

第一部分很好计算:
如果求z(i+1)对于权重 θ(i)的偏导,那就是a(i)
在这里插入图片描述

第二部分就比较难:
首先计算代价函数对于z的偏导可以利用链式求导法则,进行如下图的分解。
因此,又分为了两个部分。
第一部分:a对于z求偏导
第二部分:代价函数对于a求偏导

第一部分:a(i)对于z(i)求偏导,由sigmoid函数的特点可知=a(i) *(1-a(i))
在这里插入图片描述

第二部分:若知道了最后一层的h值,则可以算出δ=h-y
然后从后往前推
比如现在又一个三层的神经网络,我们通过前向传播求出了a(3)
因此δ(3)的值就等于a(3)-y
又通过下图可以知道,想要求出前面的一个代价函数关于z的偏导,就可以通过用δ(3) * θ(2)

在这里插入图片描述

然后将δ(3) * θ(2)和a(i) *(1-a(i))相乘就可以得出第二部分。然后再与第一部分相乘就可以得出第一个梯度。

然后令δ(3) * θ(2)*a(i) *(1-a(i))为δ(2) 按照同样的道理去计算出第二个梯度。

目前就只能理解到这里。。。

参考资料:

  1. Ng机器学习
  2. 黄博笔记
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值