本篇文章中主要是介绍卷积神经网络
CNN


基本形式为wx+b,其中


上面神经网络的特点:
对图像(不同的窗口数据)和滤波矩阵(一组固定的权值的神经元)做內积(逐个元素相乘再相加)的操作就是卷积

举个例子:左边是原始数据,中间的是滤波器,右边是卷积结果
计算过程:4*0 + 0*0 + 0*0 + 0*0 + 0*1 + 0*1 + 0*0 + 0*1 + -4*2 = -8

如果输入是图像,不同的滤波器,得到不同的输出数据,比如颜色的深浅、轮廓灯

在CNN中,滤波器filter(带着一组固定权重的神经元)对局部输入数据进行卷积计算。每计算完一个数据窗口内的局部数据后,数据窗口不断平移滑动,直到计算完所有数据。几个重要参数:


zero_padding=1针对上面的动态卷积图的理解:
局部感知机制:左边数据在变化,每次滤波器都是针对某一局部的数据窗口进行卷积
参数(权重)共享机制:数据窗口滑动,导致输入在变化,但中间滤波器Filter w_0的权重(即每个神经元连接数据窗口的权重)是固定不变的。
**相乘再相加:**通过wx+b实现,每个输出和权重相乘再加上偏置b

下面是一个4层的全连接网络

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential, losses, optimizers, datasets
# 创建4层的全连接网络
model = keras.Sequential([
layers.Dense(256, activation='relu'),
layers.Dense(256, activation='relu'),
layers.Dense(256, activation='relu'),
layers.Dense(10),
])
model.build(input_shape(4,784))
model.summary() # 打印每层的参数量统计假设输入节点是n,输出节点是m
全连接层总的参数是n*m+m
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
try:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
local_gpus = tf.config.experimental.list_logical_devices('GPU')
print(len(gpus), len(logical_gpus))
except RuntimeError as e:
print(e)这段代码表示是tf.config.experimental.set_memory_growth(gpu, True)TF按需申请显存资源
全连接网络的参数集是非常大的
2D数据的处理demo

为空是,全连接层也称之为线性层Linear Layer。
输出节点和输入节点是否有必要全部连接在一起呢?
其中???(?,?,?)表示 I 层中对于J层中的?号节点重要性最高的前?个节点集合
节点数量从||I||||J||减少到k||J||个
重要性如何判断
认为与当前像素欧式距离小于等于\frac{k}{\sqrt{2}}的像素点的重要性是比较高的。
以实心网格为参考,表征的是每个像素对于中心像素的重要性分布情况:
高宽为k的窗口称之为感受野

基于这样的距离方式:只考虑距离自己较近的部分节点,忽略较远的节点,权连接层变成局部连接的方式

此时输入和输出的关系式为:
其中
通过感受野的方式将参数从全连接层的||I||||J||减少到了kk*||J||。通过权值共享的方式,每层使用相同的权值矩阵W,网络层的参数量总是k*k。k的取值一般是比较小的。

通过上面的权值共享矩阵W和感受野内部的像素相乘累加,得到了左上角像素的输出值

通过局部连接,权值共享的网络方式其实就是卷积神经网络。
在信号处理领域,使用的比较多的是离散卷积运算
比如g(x)经过翻转变成g(-x)再平移变成g(k-x)
离散卷积的累加运算
;两个函数在有效区内有值,其余是0
卷积核
图片函数f和卷积核函数g

2D函数只在i\in[-1,1]和j\in[-1,1]的时候存在值,其他位置是0,计算{f*g}


2D离散卷积核运算流程:每次通过移动卷积核窗口函数与图片对应位置处的像素进行累加,得到位置的输出值。

单通道输入c_{in}=1,单个卷积核c_{out}=1;输入时55的矩阵,卷积核是33


一个卷积核只能得到一个输出矩 阵,无论输入X的通道数量。需要注意的情况是




感受野密度是通过移动步长Strides来确定的。步长就是感受野每次移动的长度单位。

经过卷积运算后的输出O的高宽一般会小于输入X的高宽,有时希望输出O的高宽能够与输入X的高宽相同,从而方便网络参数的设计,残差连接等。
为了让输出O的高宽能够与输入X的相等,一般通过在原输入X的高和宽维度上面进行填充(Padding)若干无效元素操作,得到增大的输入X′。



,卷积核的大小 k,步长 s, 填充数 p(只考虑上下填充数量ph相同,左右填充数量pw相同的情况)以及输入X的高宽 h/w 共同决定
通过tf.nn.conv2d函数实现2D卷积运算
x = tf.random.normal([2,5,5,3]) # 模拟输入,3通道,高宽是5
w = tf.random.normal([3,3,3,4]) # 卷积核
out = tf.nn.conv2d(x, w, strides=1, padding=[[0,0], [0,0],[0,0],[0,0]]) # padding是设置参数的
# 如果padding参数为same,strides=1, 可以得到输入和输出同大小的卷积层
x = tf.random.normal([2,5,5,3])
w = tf.random.normal([3,3,3,4])
out = tf.nn.conv2d(x, w, strides=1, padding="SAME")通过卷积层类layers.Conv2D不需要手动定义卷积核W和偏置张量b,直接调用类实例即可完成卷积层的前向计算。
在新建卷积层类时,只需要指定卷积核数量filters,卷积核大小kernal_size,步长strides,填充padding
# 1. 创建卷积层类
layer = layers.Conv2D(4, kernel_size=3, strides=3, padding='SAME') # 步长行列和卷积核高宽相同
layer = layers.Conv2D(4, kernel_size=(3,4), strides=(3,1), padding='SAME') # 步长行列和卷积核高宽不同
# 2. 前向计算
out = layer(x)
out.shape()
# 3. 返回待优化的张量列表
layer.trainable_variables # 查看卷积核张量W和偏置张量bfrom tensorflow.keras import Sequential
network = Sequential([ # 创建容器
layers.Con2D(6, kernel_size=3, strides=1), # 第一个卷积层,6个3*3的卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 高宽各减半的池化层
layers.ReLU(), # 激活函数
layers.Con2D(16, kernel_size=3, strides=1), # 第一个卷积层,16个3*3的卷积核
layers.MaxPooling2D(pool_size=2, strides=2), # 高宽各减半的池化层
layers.ReLU(),
layers.Flatten(), # 打平层,方便连接层处理
layers.Dense(120, activation='relu'), # 全连接层,120个节点
layers.Dense(84, activation='relu'),
layers.Dense(10)
network.buidl(input_shape=(4,28,28,1))
network.summary() # 统计网络信息,每层的参数量
])from tensorflow.keras import losses, optimizers # 误差计算,优化器模块
criteon = losses.CategoricalCrossentropy(from_logits=True) # 创建损失函数,在实际计算中调用类实例
with tf.Gradientape() as tape: # 构建梯度计算环境
x = tf.expand_dims(x, axis=3) # 插入通道维度 ---> [b,28,28,1]
out = network(x) # 前向计算,获得10类别的概率分布, [b,784] ---> [b,10]
y_onehot = tf.one_hot(y, depth=10)
loss = criteon(y_onehot, out) # 计算损失函数,标量
grads = tape.gradient(loss, network.trainable_variables) # 自动更新梯度
optimizer.apply_gradients(zip(grads, network.trainable_variables)) # 自动更新参数
# 预测正确数量和样本总量
correct, total = 0,0
for x, y in db_test: # 遍历全部的样本
x = tf.expand_dims(x, axis=3) # 插入通道
out = network(x)
# 选取概率最大的元素所在的索引,作为当前x的预测类
pred = tf.argmax(out, axis=-1)
y = tf.cast(y, tf.int64) # y 是真实标签
corrext += float(tf.reduce_sum(tf.cast(tf.equal(pred, y), tf.float32)))
total += x.shape[0]
print('test acc:', corrext / total)

CNN在进行图片识别的过程中是将位置图片的局部和标准的图案的局部进行一个个的对比,这个对比计算的过程便是卷积操作。如果图片出现变形,如何处理?引入了features
正常情况下:

稍微变形:

对于CNN来说,它是一块一块地来进行比对。它拿来比对的这个“小块”我们称之为Features(特征)
在两幅图中大致相同的位置找到一些粗糙的特征进行匹配,CNN能够更好的看到两幅图的相似性,相比起传统的整幅图逐一比对的方法。
每一个feature就像是一个小图(就是一个比较小的有值的二维数组)。不同的Feature匹配图像中不同的特征。如果特征能够匹配上,则两幅图就是相似的。

每个features很有可能就是任何含有字母X的四个角和它的中心。其中一个角和中心


具体过程
