其他分享
首页 > 其他分享> > BP神经网络:从Numpy到Tensorflow2实现

BP神经网络:从Numpy到Tensorflow2实现

作者:互联网

 用最简单的神经网络结构实现一幅灰度图像的彩色化。

BP网络即前馈神经网络,模型在完成一次训练后需要反向对训练过程中的参数进行优化调整,是最基础的神经网络,也是复杂网络结构的基础。

不做过多的原理性介绍,仅仅介绍如何进行结构实现。文章结尾放上Numpy实现与Keras实现。

目录

目标:实现从灰度图像(图1)到目标图像(图2)的变化

输入层

输出层

隐藏层

超参

过程1:输入层​隐藏层

过程2:隐藏层​输出层

学习率

激活函数

损失函数

优化器

推导

BP训练过程

1.将所有  整理为矩阵形式

2.随机生成

3.前向过程计算

4.反向传播

5.重复3、4知道达到指定步骤

代码

Numpy版本:

Tensorflow2 Keras版本:


目标:实现从灰度图像(图1)到目标图像(图2)的变化

图1 原始图像

图2 目标彩色图像

所谓神经网络,结构上需要三层结构:输入层、隐藏层、输出层

首先来解决输出层的问题。

输入层

首先需要拿到原始图像每个像素点P_x(i,j)的4邻域,即该像素点上下左右4个点:P_x(i,j-1)P_x(i-1,j)P_x(i+1,j)P_x(i,j+1) ,加上该点共5个点的灰度值作为输入x

P_x(i-1,j)
P_x(i,j-1)P_x(i,j)P_x(i,j+1)
P_x(i+1,j)

为了提高效果也可为8邻域,此时输入x为9个灰度值

P_x(i-1,j-1)P_x(i-1,j)P_x(i-1,j+1)
P_x(i,j-1)P_x(i,j)P_x(i,j+1)
P_x(i+1,j-1)P_x(i+1,j)P_x(i+1,j+1)

相信你也已经发现,取邻域的操作对于位于图像中部的点来说并无困难,而图像边缘点才是需要着重处理的地方,如果图像较大的话,取邻域操作也可以直接从非边缘点开始,到临界点结束,忽略边缘点信息,而本例中的图像较小,我们采取“补零填充”的方法。

000
0P_x(0,0)P_x(0,1)
0P_x(1,0)P_x(1,1)

以图1 中的(0,0)点为例,对于该点,x应为{0, 0, P_x(0,0)P_x(0,1)P_x(1,0)} 顺序无关,不过所有点的取值过程都需要按照一个固定的顺序。

遍历待处理图像中所有的像素点,将h*w个像素点的4邻域获取完毕,存储在一个

shape=[h*w,5]的矩阵中,后续过程中将每一行数据作为一次输入。

输出层

输入已经确定,在设计隐藏层之前首先确定输出层,本例的输出自然就是图2 中P_y(i,j)对应的RGB三通道值,即输出层结点数为3,分别代表着RGB三通道中的一个。

隐藏层

对本例最简单的神经网络来说,一层在输入与输出之间的全链接层即可完成任务,至于隐藏层的神经元数量,则需要根据后续的训练过程自行优化,神经网络是一个全随机的过程,参数的设置绝大多数情况下要根据经验。

结构设计完成,接下来需要对神经网路的参数进行定义

超参

过程1:输入层\rightarrow隐藏层

在本例中我们知道,每一次输入均有1*5个值,由输出层到隐藏层的过程可以定义为一个函数过程:f(x_{input})=v*x_{input}+b_1,式中,v代表权重,b_1则为偏置,这两个参数的初始化可以自行定义,可以在一定范围内选取随机数,也可以选取固定值,或者全部取0,在后续的反向传播过程中会根据「梯度」进行更新。

过程2:隐藏层\rightarrow输出层

类似于过程1,从隐藏层到输出层的过程也可以定义为一个函数过程:f_{out}(x_{hidden})=w*x_{hidden}+b_2,式中,w代表权重,b_2也为偏置,初始化过程类似于过程1。

学习率

关于学习率的定义这里不做赘述,感兴趣的可以查看其他博主介绍的比较详细的文章,或者西瓜书上面的介绍。在这里我们只需要知道这是一个神经网络中非常重要的参数,可以取固定值或根据训练结果更新,更新的方式有很多种,本例中选取固定学习率。

激活函数

某一网络层向一下网络层传输数据之前,该网络层产生的结果通常需要先经过一个函数转换为一个值,这个函数称为激活函数,根据网络结构、目标的不同有多种选择。

损失函数

神经网络产生一次输出之后,这个输出需要与目标结果进行对比,即f(y_{target},y_{output}),通过损失函数计算出一个这两者之间的差异度量,作为反向传播中更新参数的依据,通常使用sigmoid系列的函数作为损失函数,根据数据类型的差异也有不同的选择。

优化器

优化器:神经网络中用于梯度下降的工具,有多种选择。学习率也是在优化器中使用的。


推导

下面对神经网络过程做一个纯公式的推导,损失函数使用sigmoid,损失函数为MSE(均方误差)

i表示输出层第i个神经元编号

j表示隐藏层第j个神经元编号

k表示输出层第k 个神经元编号

D_1 D_2 D_3分别表示输入层、隐藏层、输出层神经元个数。

 v_{ij}表示第 i个输入层神经元与第j 个隐层神经元的连接权值  

w_{jk}表示第 j个隐层神经元与第 k个输出层神经元的连接权值  

b^1_j表示第j 个隐层神经元输入偏置 

b^2_k表示第 k个输出层神经元输入偏置

 x^l=[x^l_1 ,x^l_2, ... ,x^l_{D_1}] 表示第l 个输入样本

O^l_{out}=[O^l_{out_1} ,O^l_{out_2}, ... ,O^l_{out_{D_1}}] 表示第 l个样本经过神经网络的预测输出

y^l=[y^l_1,y^l_2,...,y^l_{D_3}]表示第 l个样本的真实输出

隐层第 j个神经元的输入h^l_{in} 可表示为

h^l_{in_j}=\sum_{i=1}^{D_1} v_{ij}x^l_i+b^1_j (1)

隐层第 j个神经元的输出h^l_{ou} 可表示为 

h^l_{ou_j}=g(h^l_{in_j})=\frac{1}{1+e^{-h^l_{in_j}}}   (2)

输出层第k 个神经元输入:

O^l_{in_k}=\sum_{j=1}^{D_2}w_{jk}h_{ou_j}^l+b_k^2​​​​​​​ (3)

输出层第k个神经元输出:

O_{ou_k}^l=g(O_{in_k}^l)=\frac{1}{1+e^{-O_{in_k}^l}} (4)

给定输出样本x^l=[x^l_1 ,x^l_2, ... ,x^l_{D_1}]

神经网络预测输出O^l_{out}=[O^l_{out_1} ,O^l_{out_2}, ... ,O^l_{out_{D_1}}]

可通过(1) \rightarrow(2)\rightarrow (3)\rightarrow (4) 计算

上述过程以矩阵形式可表示为:

v=\begin{bmatrix} v_{11} &v_{12} &... &v_{1D_2}\\ ...\\ v_{D_11}&v_{D_12}&...&v_{D_1D_2} \end{bmatrix} 

 b_1=\begin{bmatrix} b_{1} ^1&b_{2}^1 &... &b_{D_2}^1\\ \end{bmatrix}

w=\begin{bmatrix} w_{11} &w_{12} &... &w_{1D_3}\\ ...\\ w_{D_21}&w_{D_22}&...&w_{D_2D_3} \end{bmatrix}

b^2=\begin{bmatrix} b^2_{1} &b^2_{2} &... &b^2_{D_3}\\ \end{bmatrix}

h_{in}^l=[h_{in_1}^l,h_{in_2}^l,...,h_{in_D_2}^l]

h_{ou}^l=g(h_{in}^l)=\frac{1}{1+e^{-(x^lv+b^1)}}

O_{in}^l=[O_{in_1}^l,O_{in_2}^l,...,O_{in_{D_3}}^l]=h_{ou}^lw+b^2

O_{ou}^l=[g(O_{in_1}^l),g(O_{in_2}^l),...,g(O_{in_{D_3}}^l)]=\frac{1}{1+e^{-(h_{ou}^lw+b^2)}}

给定N个输入输出样本\{ x^l,y^l\} l=1,2,..,N

x=\begin{bmatrix} x^1\\x^2\\...\\x^N \end{bmatrix} \epsilon R^{N\times D_1}

经神经网络预测输出为:

O_{ou}=\begin{bmatrix}O_{ou}^1\\O_{ou}^2\\...\\O_{ou}^N \end{bmatrix}\epsilon R^{N\times D_3}

前馈项:

O_{ou}=g(g(xv+b^1)w+b^2)\\g(x)=\frac{1}{1+e^{-x}})

神经网络模型矩阵表示

以回归任务为例

神经网络参数评估采用均方差MSE

\theta =\sum_{l=1}^{N}\begin{Vmatrix} O_{ou}^l-y^l\end{Vmatrix}=\sum_{l=1}^{N}\sum_{k=1}^{D_3} (O_{ou_k}^l-y_{k}^l)^2

采用梯度下降方法求解网络最优参数

\frac{\partial \theta}{\partial w_{jk}}=\sum_{l=1}^{N}\frac{\partial}{\partial w_{jk}}\sum_{k=1}^{D_3}(O_{ou_k}^l-y_k^l)^2\\=2\sum_{l=1}^N(O_{ou_k}^l-y_k^l)\frac{\partial O_{ou_k}^l}{\partial w_{jk}}=2\sum_{l=1}^N (O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1)h_{ou_j}^l

\frac{\partial \theta}{\partial b_k^2}=\sum_{l=1}^N \frac{\partial}{\partial b_k^2}(\sum_{k=1}^{D_3}(O_{ou_k}^l-y_k^l)^2) =2\sum_{l=1}^N(O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1)

\frac{\partial \theta}{\partial v_{ij}}=\sum_{l=1}^N \frac{\partial}{\partial v_{ij}} (\sum_{k=1}^{D_3}(O_{ou_k}^l-y_k^l)^2)\\ =2\sum_{l=1}^N \sum_{k=1}^{D_3}(O_{ou_k}^l-y_k^l)\frac{\partial}{\partial v_{ij}}O_{ou_k}^l \\=2\sum_{l=1}^N \sum_{k=1}^{D_3}(O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1) \frac{\partial O_{in_k}^l}{\partial v_{ij}} \\= 2\sum_{l=1}^N \sum_{k=1}^{D_3} (O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1) w_{jk}\frac{\partial h_{ou_j}^l}{\partial v_{jk}} \\=2\sum_{l=1}^N \sum_{k=1}^{D_3} (O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1) w_{jk}h_{ou_j}^l(h_{ou_j}^l-1)\frac{\partial h_{in_j}^l}{\partial v_{ij}} \\=2\sum_{l=1}^N \sum_{k=1}^{D_3} (O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1)w_{jk}h_{ou_j}^l(h_{ou_j}^l-1)x_i^l

\frac{\partial \theta}{\partial b_j^1}=2\sum_{l=1}^N\sum_{k=1}^{D_3} (O_{ou_k}^l-y_k^l)O_{ou_k}^l(O_{ou_k}^l-1)w_{ij}h_{ou_j}^l(h_{ou_j}^l-1)

梯度求解采用矩阵形式可表示为

\frac{\partial \theta}{\partial w}= \begin{bmatrix} \frac{\partial \theta}{\partial w_{11}}&\frac{\partial \theta}{\partial w_{12}}&...&\frac{\partial \theta}{\partial w_{1D_3}}\\ ...\\ \frac{\partial \theta}{\partial w_{D_21}}&\frac{\partial \theta}{\partial w_{D_22}}&...&\frac{\partial \theta}{\partial w_{D_2D_3}} \end{bmatrix} \\=h_{ou}^T[(O_{ou}-y)\bigodot O_{ou}\bigodot (O_{ou}-1)]

符号表示矩阵的点乘

h_{ou}=\begin{bmatrix} h_{ou}^1\\h_{ou}^2\\...\\h_{ou}^N\end{bmatrix}\epsilon R^{N\times D_2}\\ O_{ou}=\begin{bmatrix} O_{ou}^1\\O_{ou}^2\\...\\O_{ou}^N\end{bmatrix}\epsilon R^{N\times D_3}\\ y=\begin{bmatrix} y^1\\y^2\\...\\y^N\end{bmatrix}\epsilon R^{N\times D_3}\\

\frac{\partial \theta}{\partial b^2}=\begin{bmatrix}\frac{\partial \theta}{\partial b_1^2}&\frac{\partial \theta}{\partial b_2^2}&...& \frac{\partial \theta}{\partial b_{D_3}^2}\end{bmatrix}\\= [1,1,...,1]_{1\times N}[(O_{ou}-y)\bigodot O_{ou}\bigodot (O_{ou}-1)]​​​​​​​

\frac{\partial \theta}{\partial v}=\begin{bmatrix} \frac{\partial \theta}{\partial v_{11}}& \frac{\partial \theta}{\partial v_{12}}& ...& \frac{\partial \theta}{\partial v_{1D_2}}\\ ...\\ \frac{\partial \theta}{\partial v_{D_11}}& \frac{\partial \theta}{\partial v_{D_12}}& ...& \frac{\partial \theta}{\partial v_{D_1D_2}} \end{bmatrix}\\= x^T[[(O_{ou}-y)\bigodot O_{ou}\bigodot (O_{ou}-1)w^T]\bigodot h_{ou}\bigodot (h_{ou}-1)]

(以上推导过程应该有一些顺序或者加减上的小问题,在numpy版本的代码中已经更正(大概))

BP训练过程

给定训练数据\{ x^l,y^l\} l=1,2,..,N

步骤:

1.将所有 x^l ,y^l 整理为矩阵形式

 x=\begin{bmatrix} x^1\\x^2\\...\\x^N \end{bmatrix}, y=\begin{bmatrix} y^1\\y^2\\...\\y^N \end{bmatrix}

2.随机生成w,v,b^1,b^2

3.前向过程计算

h_{ou}=g(xv+b^1)\\ O_{ou}=g(h_{ou}w+b^2)

4.反向传播

计算\frac{\partial \theta}{\partial w}, \frac{\partial \theta}{\partial b^2}, \frac{\partial \theta}{\partial v}, \frac{\partial \theta}{\partial b^1}

w=w-\eta \frac{\partial \theta}{\partial w}\\ b^2=b^2-\eta \frac{\partial \theta}{\partial b^2}\\ v=v-\eta \frac{\partial \theta}{\partial v}\\ b^1=b^1-\eta \frac{\partial \theta}{\partial b^1}

\eta是学习率)

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