1.全连接网络的基本流程
1.有一系列已经归一化的数据
2.一个网络模型y = fcNet(x)
其中
所以从第i层fc到第i+1层的递归公式
3.正向传播
先一个列向量
按照递推公式
输出到模型中得到label,也就是y
4.定义最普通的损失函数
5.bp反向传播
这里面我要解释一下有些数学表达式的写法以免误解A * B 或者AB的意思是:向量对应分量乘积,既不是矩阵乘法也不是向量内积,因为numpy就是这么定义的比如
A mul B的意思的:A,B矩阵乘积
训练网络最终目的是让y与label(y的真实值)的差距越小越好,也就是使得loss的值最小,所以把loss当成因变量,把fcNet中的所有参数和y的真实值当成自变量,loss关于它们的函数,就是这个样子
分开来就是
其中
流程大家大概都知道,让loss对每个w求导,设求出来的值是
这个就是多元函数
对某一个w的梯度了对吧然后设置一个学习率,learning = 0.01什么的
然后让每个w
就相当于是梯度更新
但是有三个问题不容易算清楚
1.越是前面的层,它的求导越麻烦,因为涉及到复合函数求导的链式法则
2.前向计算的时候正着来,一个接一个矩阵乘法,反向传播的时候反着往前求导,矩阵的递推关系相比正向计算要复杂一些
3.编程的时候不像我们在纸上面计算,所有的东西都相当于全局变量,随时可以取到它们的值,我编程的时候,一层作为一个fc类,那么这一层的全连接神经网络只能知道本层的
(输入向量值,W参数,输出向量值)
想要知道前一层或者后一层的数据,只能通过前向计算或者反向传播的时候,前层或者后层返回的数据才知道。那么前一层或者后一层传过来的数据,是什么维度的,表示什么含义,一定要统一,不然就乱套了,没有办法传递了
从任意多的全连接层扣出2层来看
考虑这样一个第i层和第i+1层的问题,假如要对第i层的W矩阵求导
那么一定是这样的
其中第一项,跟i+1层有关,第i层是不可能算出来的,所以它应该作为第i+1层传过来的参数
这边可以看出来,表达式(1)的后面两项是可以在本层内计算的,且并不复杂,那第一项呢?要考虑第一项是怎么样的,可以先考虑一下目前作为第i层,又该往i-1层传什么?先看第i-1层对W求导需要什么
很显然第一项,是需要第i层提供的,而这一项等于
在这个表达式中,第一项是第i+1层提供给第i层的,后两项是第i层自己可以计算到的,这样子,整个bp传播的递推就完整了,整理一下对于第i层来说
化简一下(4),(5)表达式
所以整个反向传播函数大概这样
def bp(self,last,learning_rate):
act = np.reshape(self.outx*(1-self.outx),[-1,1])
p=last*act
grad = np.matmul(p,np.reshape(self.inx,[1,self.input_dim]))
pass_next = np.matmul(np.reshape(p,[1,-1]),self.w)
self.w -= learning_rate*grad
return np.reshape(pn,[-1,1])
在整个对loss的前向计算过程中
对于这个图片所描述的网络
把y看成X5(in)
那么第一个last输入就是
也就是说,对于一个回归网络,且用方差作为损失函数的话,第一个last就是
然后就开始试着用自己写的网络训练了,以下为代码
2代码
1.定义自己的全连接层结构fc
默认激活函数为sigmoid
import numpy as np
class fc():
def __init__(self,input_dim,out_dim,name):
self.name = name
self.input_dim = input_dim
self.out_dim = out_dim
self.w = np.random.uniform(-0.2,0.2,(self.out_dim,self.input_dim))
self.b = np.random.uniform(-0.5,0.5,(self.out_dim))
self.disable = np.ones([self.out_dim,self.input_dim])
self.inx =np.zeros([self.input_dim])
self.outx =np.zeros([self.out_dim])
def forward(self,x):
self.inx = x
self.outx = self.sigmoid(np.matmul(self.w,self.inx))+self.b
return self.outx
def bp(self,last,learning_rate):
act = np.reshape(self.outx*(1-self.outx),[-1,1])
p=last*act
grad = np.matmul(p,np.reshape(self.inx,[1,self.input_dim]))
pn = np.matmul(np.reshape(p,[1,-1]),self.w)
self.w -= learning_rate*grad
return np.reshape(pn,[-1,1])
def sigmoid(self,x):
return 1/(1+np.exp(-x))
2.定义网络结构Net
这里定义了4层
class Net():
def __init__(self):
self.fc1 = fc(4,256,'fc1')
self.fc2 = fc(256,64,'fc2')
self.fc3 = fc(64,128,'fc3')
self.fc4 = fc(128,1,'fc3')
def forword(self,x):
x = self.fc1.forward(x)
x = self.fc2.forward(x)
x = self.fc3.forward(x)
x = self.fc4.forward(x)
return x
def bp(self,E,learning_rate):
E4 = self.fc4.bp(E,learning_rate)
E3 = self.fc3.bp(E4,learning_rate)
E2 = self.fc2.bp(E3,learning_rate)
E1 = self.fc1.bp(E2,learning_rate)
3.创建网络实例
fcNet = Net()
4.读取数据并查看了前4行
这边我随便找了一个aistudio上的数据集
股票数据集 因为这个代码可以处理的是回归问题,多个输入,一个输出
import pandas as pd
CSV_FILE_PATH = './work/AAPL.csv'
df = pd.read_csv(CSV_FILE_PATH)
df[:4]
5.简单处理数据,归一化,减均值除以标准差
x = df[['Open', 'High', 'Low', 'Close', 'Volume']]
x = np.array(x)
avg = x.sum(axis=0)/len(x)
avg
o =[0,0,0,0,0]
for i in x:
o[0]+=i[0]**2
o[1]+=i[1]**2
o[2]+=i[2]**2
o[3]+=i[3]**2
o[4]+=i[4]**2
xn = (x-avg)/(np.array(o)**0.5)
6.定义一次训练过程的函数
import random
def once(losses,learning_rate):
c =0
losssum =0
index = [i for i in range(len(xn))]
random.shuffle(index)
for i in index:
c+=1
d = xn[i][:4]
l = xn[i][4:5]
loss= abs(fcNet.forword(d) - l)
fcNet.bp(loss,learning_rate)
losssum+=loss**2
if(c%300==0):
print(losssum)
losses.append(losssum)
losssum=0
7.开始训练,用一个列表保存每300次更新后,损失函数的值,并打印输出
losses = []
learn_rate = 0.0003
for i in range(1,10):
print('now,learning_rate={}'.format(learn_rate))
once(losses,learn_rate)
8.画图,看一下训练过程中损失函数的减小趋势
import matplotlib.pyplot as plt
plt.plot([i for i in range(len(losses))],losses)