BP神经网络:从Numpy到Tensorflow2实现
作者:互联网
用最简单的神经网络结构实现一幅灰度图像的彩色化。
BP网络即前馈神经网络,模型在完成一次训练后需要反向对训练过程中的参数进行优化调整,是最基础的神经网络,也是复杂网络结构的基础。
不做过多的原理性介绍,仅仅介绍如何进行结构实现。文章结尾放上Numpy实现与Keras实现。
目录
目标:实现从灰度图像(图1)到目标图像(图2)的变化
图1 原始图像
图2 目标彩色图像
所谓神经网络,结构上需要三层结构:输入层、隐藏层、输出层
首先来解决输出层的问题。
输入层
首先需要拿到原始图像每个像素点的4邻域,即该像素点上下左右4个点:、、、 ,加上该点共5个点的灰度值作为输入x
为了提高效果也可为8邻域,此时输入x为9个灰度值
相信你也已经发现,取邻域的操作对于位于图像中部的点来说并无困难,而图像边缘点才是需要着重处理的地方,如果图像较大的话,取邻域操作也可以直接从非边缘点开始,到临界点结束,忽略边缘点信息,而本例中的图像较小,我们采取“补零填充”的方法。
0 | 0 | 0 |
0 | ||
0 |
以图1 中的(0,0)点为例,对于该点,x应为{0, 0, , , } 顺序无关,不过所有点的取值过程都需要按照一个固定的顺序。
遍历待处理图像中所有的像素点,将个像素点的4邻域获取完毕,存储在一个
shape=[,5]的矩阵中,后续过程中将每一行数据作为一次输入。
输出层
输入已经确定,在设计隐藏层之前首先确定输出层,本例的输出自然就是图2 中对应的RGB三通道值,即输出层结点数为3,分别代表着RGB三通道中的一个。
隐藏层
对本例最简单的神经网络来说,一层在输入与输出之间的全链接层即可完成任务,至于隐藏层的神经元数量,则需要根据后续的训练过程自行优化,神经网络是一个全随机的过程,参数的设置绝大多数情况下要根据经验。
结构设计完成,接下来需要对神经网路的参数进行定义
超参
过程1:输入层隐藏层
在本例中我们知道,每一次输入均有1*5个值,由输出层到隐藏层的过程可以定义为一个函数过程:,式中,v代表权重,则为偏置,这两个参数的初始化可以自行定义,可以在一定范围内选取随机数,也可以选取固定值,或者全部取0,在后续的反向传播过程中会根据「梯度」进行更新。
过程2:隐藏层输出层
类似于过程1,从隐藏层到输出层的过程也可以定义为一个函数过程:,式中,w代表权重,也为偏置,初始化过程类似于过程1。
学习率
关于学习率的定义这里不做赘述,感兴趣的可以查看其他博主介绍的比较详细的文章,或者西瓜书上面的介绍。在这里我们只需要知道这是一个神经网络中非常重要的参数,可以取固定值或根据训练结果更新,更新的方式有很多种,本例中选取固定学习率。
激活函数
某一网络层向一下网络层传输数据之前,该网络层产生的结果通常需要先经过一个函数转换为一个值,这个函数称为激活函数,根据网络结构、目标的不同有多种选择。
损失函数
神经网络产生一次输出之后,这个输出需要与目标结果进行对比,即,通过损失函数计算出一个这两者之间的差异度量,作为反向传播中更新参数的依据,通常使用sigmoid系列的函数作为损失函数,根据数据类型的差异也有不同的选择。
优化器
优化器:神经网络中用于梯度下降的工具,有多种选择。学习率也是在优化器中使用的。
推导
下面对神经网络过程做一个纯公式的推导,损失函数使用sigmoid,损失函数为MSE(均方误差)
表示输出层第个神经元编号
表示隐藏层第个神经元编号
表示输出层第 个神经元编号
分别表示输入层、隐藏层、输出层神经元个数。
表示第 个输入层神经元与第 个隐层神经元的连接权值
表示第 个隐层神经元与第 个输出层神经元的连接权值
表示第 个隐层神经元输入偏置
表示第 个输出层神经元输入偏置
表示第 个输入样本
表示第 个样本经过神经网络的预测输出
表示第 个样本的真实输出
隐层第 个神经元的输入 可表示为
(1)
隐层第 个神经元的输出 可表示为
(2)
输出层第 个神经元输入:
(3)
输出层第个神经元输出:
(4)
给定输出样本
神经网络预测输出
可通过(1) (2) (3) (4) 计算
上述过程以矩阵形式可表示为:
给定N个输入输出样本
经神经网络预测输出为:
前馈项:
神经网络模型矩阵表示
以回归任务为例
神经网络参数评估采用均方差MSE
采用梯度下降方法求解网络最优参数
梯度求解采用矩阵形式可表示为
符号表示矩阵的点乘
(以上推导过程应该有一些顺序或者加减上的小问题,在numpy版本的代码中已经更正(大概))
BP训练过程
给定训练数据
步骤:
1.将所有 整理为矩阵形式
2.随机生成
3.前向过程计算
4.反向传播
计算
(是学习率)
5.重复3、4知道达到指定步骤
代码
下面上代码
Numpy版本:
import cv2
import numpy as np
import time
ori_path='ori.png'
res_path='res.png'
output_path='output_numpy_version.jpg'
#循环次数(即遍历整张图的次数)
ecp_num=10
def sigmoid(x):
return 1/(1+np.exp(0-x))
#本例使用5*5大小的块作为输入
def get5pixel(arr):
arr=np.pad(arr,2,'constant')
ers=np.zeros(shape=(151*144,25))
t=0
for i in range(2,153):
for j in range(2,146):
tmp=arr[i-2:i+3,j-2:j+3]
ers[t]=np.reshape(tmp,(1,25))
t=t+1
return ers
#规定输出
def getColor(arr):
res=np.zeros(shape=(151*144,3))
t=0
for i in range(151):
for j in range(144):
res[t]=arr[i,j]
t=t+1
return res
img_ori=cv2.imread(ori_path)
img_res=cv2.imread(res_path)
#转为灰度图像
img_ori=cv2.cvtColor(img_ori,cv2.COLOR_BGR2GRAY)
#归一化
img_ori=img_ori/255
img_res=img_res/255
x_pixel=get5pixel(img_ori)
y_color=getColor(img_res)
v=np.random.random_sample(size=(25,64))
b1=np.zeros(shape=(64,1))
w=np.random.random_sample(size=(64,3))
b2=np.zeros(shape=(1,3))
#创建用于存放结果的空矩阵
data_pre=np.zeros(shape=(151*144,3))
learn_rate=1
def update(new,old):
return old-learn_rate*new
def backword(t,h_out):
global v,b1,w,b2,y_color,data_pre
ow=w
ob2=b2
ob1=b1
ov=v
b2=(data_pre[t]-y_color[t,:])*data_pre[t]*(np.full((1,3),1)-data_pre[t])
w=np.dot(h_out,b2)
b1=(np.dot(b2,ow.T)*h_out.T*(np.full(64,1)-h_out).T).T
v=np.reshape(x_pixel[t,:],(25,1))*b1.T
b2=update(b2,ob2)
w=update(w,ow)
b1=update(b1,ob1)
v=update(v,ov)
def forword():
for t in range(151*144):
hidden_in=np.reshape((np.dot(x_pixel[t,:],v)).T,(64,1))
hidden_out=hidden_in+b1
out_in=np.zeros(shape=(64,1))
for i in range(64):
out_in[i,:]=sigmoid(hidden_out[i,:])
out_out=np.dot(out_in.T,w)+b2
for i in range(3):
out_out[0,i]=sigmoid(out_out[0,i])
data_pre[t]=out_out
backword(t,out_in)
for i in range(ecp_num):
a=time.time()
forword()
b=time.time()
print('rang',i,'cost_sc',b-a)
result=np.reshape(data_pre,(155,144,3))
cv2.imwrite(output_path,result*255)
print('done')
Tensorflow2 Keras版本:
keras是tensorflow 的高级封装,大大简化了神经网络的设计过程。
import cv2
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
#与numpy版本数据处理相同
def get_piexl_5(arr):
arr = np.pad(arr, 2, 'constant')
ers = np.zeros(shape=(151*144, 25))
t = 0
for i in range(2, 153):
for j in range(2, 146):
tmp = arr[i - 2:i + 3, j - 2:j + 3]
tmp = np.reshape(tmp, (1, 25))
ers[t] = tmp
t = t + 1
return ers
def get_pixel_color(arr):
res = np.zeros(shape=(151 * 144, 3))
t = 0
for i in range(151):
for j in range(144):
res[t] = arr[i, j]
t = t + 1
return res
ori_img = cv2.imread('.\\ori.png')
res_img = cv2.imread('.\\res.png')
ori_img=cv2.cvtColor(ori_img,cv2.COLOR_BGR2GRAY)
ori_img = ori_img / 255
ori_img = get_piexl_5(ori_img)
#转张量
t = tf.convert_to_tensor(ori_img)
res_img = res_img / 255
res_data = get_pixel_color(res_img)
#以下为使用keras函数式API构建网络结构
#输入层,(25,)的张量
img_imputs = keras.Input(shape=(25,),name='Input')
# initializer = keras.initializers.RandomUniform(minval=0., maxval=1.)
#偏置初始化
initializer_2 = keras.initializers.TruncatedNormal(mean=0.5,stddev=0.5)
initializer_1 = keras.initializers.Zeros()
#64个神经元的全链接层,激活函数为sigmoid,使用偏置,使用权重(kernel)
dense = layers.Dense(64, activation='sigmoid', use_bias=True, kernel_initializer=initializer_2,
bias_initializer=initializer_1, name='Dense')(img_imputs)
#输出层,3个神经元
outputs = layers.Dense(3, activation='sigmoid', use_bias=True, kernel_initializer=initializer_2,
bias_initializer=initializer_1, name='OutPut')(dense)
#build模型
model = keras.Model(inputs=img_imputs, outputs=outputs, name="m_model")
#打印网络结构
print(model.summary())
#此行会生成一张网络结构图,需要的依赖较多,可以删去
keras.utils.plot_model(model, '.\\model_info.png', show_shapes=True)
#确定损失函数、优化器、评价标准等
model.compile(
loss=keras.losses.MeanSquaredError(),
# loss=keras.losses.BinaryCrossentropy(),
optimizer=keras.optimizers.SGD(learning_rate=1),
metrics=["accuracy"]
)
x_train = t
y_train = tf.convert_to_tensor(res_data)
x_test = t
y_test = y_train
#训练
history = model.fit(x_train, y_train,batch_size=64 ,epochs=50)
#查看训练中间过程
test_scores = model.evaluate(x_test, y_test, verbose=0)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])
#保存整个模型,下次可以直接读取文件进行调用
# model.save('.\\model')
#预测
res=model.predict(x_test)
res = np.reshape(res, (151, 144, 3))
res = res * 255
cv2.imwrite(".\\ult_32.jpg", res)
不能说是毫无关系,只能说是一模一样。
(numpy版本完成时间过久,已经忘记了是否能够跑通,如果报错可以尝试修改反向传播过程中的维度)
有问题欢迎留言
标签:Tensorflow2,输出,img,res,BP,ori,np,Numpy,out 来源: https://blog.csdn.net/weixin_43297448/article/details/118789423