写在前面
本篇文章算是作者在学习深度学习知识中的学习笔记,算是对于作者学习的相关论文、教材、视频的归纳总结,以后也会持续更新这个板块(如果上学了则会更新的慢一点)
什么是卷积神经网络
卷积神经网络(Convolutional Neural Networks, CNNs)是一种特别类型的深度前馈神经网络,在图像处理、视频分析、语音识别和自然语言处理等领域取得了显著的成功,尤其是在图像识别领域。CNN的设计灵感来源于生物视觉皮层的工作机制,特别是局部连接和层次结构。
它通过卷积层对输入数据进行卷积操作,提取局部特征;然后通过池化层对特征图进行降维;最后通过全连接层将学到的特征表示映射到样本的标记空间。在训练过程中,CNN使用反向传播算法来优化网络中的权重,以最小化损失函数。
CNN常用于图像识别、视频分析、医学影像处理等方面。
卷积神经网络的组成
总得来说,其结果包括了卷积层(Convolutional Layer)、激活函数(Activation Function)池化层(Pooling Layer)与全连接层(Fully Connected Layer)。
卷积层(Convolutional Layer)
定义:
卷积层(Convolutional Layer)是卷积神经网络(CNN)中的核心组件之一,负责执行卷积操作以从输入数据中提取特征。卷积层通过一组可学习的卷积核对输入数据进行滑动窗口式的点积运算,从而生成新的特征图。
每个卷积层都有若干卷积单元组成,每个卷积单元的参数都是通过反向传播算法最优化得到的。说到底,卷积层就是卷积运算。
作用与目的:
提取输入的不同特征。
运算过程:
在了解如何运算之前,需要了解什么是卷积核。
卷积核(Convolutional Kernel)是卷积神经网络(CNN)中的一个核心概念,它是进行卷积操作的小矩阵(或称为过滤器、特征检测器)。卷积核的作用是在输入数据(如图像)上滑动,通过元素间的乘法求和运算来提取特征。
它具有以下特点:
1、大小事先确定,即是3*3、5*5等。
2、在处理多维数据时(如彩色图像),卷积核的深度与输入数据的深度相匹配。即RGB图像升读为3,就需要卷积核的深度同样为3,才可以处理R、G、B三个通道。
3、卷积核中的每个元素都是一个可学习的权重。这些权重在训练过程中通过反向传播算法进行更新,以最小化网络的损失函数。
了解完以后,下面是计算过程:(如图1所示,图1源自 基于卷积神经网络的多模态图像自动分割方法研究_章德云)
(1)先用随机的方式来产生卷积核,其大小为3*3
(2)将要提取特征的图像从左至右、上至下,按序选取3*3的矩阵。
(3)图像中选取的矩阵与卷积核相乘,产生内积和。
图1
相应代码:
import cv2
import numpy as np
#卷积操作
img = cv2.imread('1.png')#读取图片,图片放在项目中
kernel=np.array([[1,0,-1],[1,0,-1],[1,0,-1]])#卷积核
res=cv2.filter2D(img,-1,kernel)#opencv的卷积操作,-1表示目标图像深度与输入图像的深度相同
结果对比:
如图2所示。
图2
激活函数(Activation Function)
激活函数又称激励函数。
定义:
它是神经网络中非常重要的一部分,它保留了“神经元”的特性,用于在神经元的输出上引入非线性因素,使得神经网络能够学习和模拟复杂的模式。
目的:
给定一个x,对其进行线性变换以后得到的结果仍然是线性的,只能用于解决线性的问题。而实际的问题通常是非线性的。因此我们需要引入一个非线性的激活函数,来解决非线性的问题。
常见的激活函数:
1、Sigmoid函数:(引自Sigmoid函数_百度百科)
sigmoid函数也叫Logistic函数,用于隐层神经元输出,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间,可以用来做二分类。在特征相差比较复杂或是相差不是特别大时效果比较好。Sigmoid函数为神经网络中的激励函数,是一种光滑且严格单调的饱和函数,其表达式为:
该饱和函数的上、下界为(0,1)。
具有以下优缺点:
优点:平滑、易于求导。
缺点:激活函数计算量大,反向传播求误差梯度时,求导涉及除法;反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。
其形状如图3
图3
2、ReLU函数:(引自ReLU 函数_百度百科)
线性整流函数(Linear rectification function),又称修正线性单元,是一种人工神经网络中常用的激活函数(activation function),通常指代以斜坡函数及其变种为代表的非线性函数。
通常意义下,线性整流函数指代数学中的斜坡函数,即
而在神经网络中,线性整流作为神经元的激活函数,定义了该神经元在线性变换
之后的非线性输出结果。换言之,对于进入神经元的来自上一层神经网络的输入向量
,使用线性整流激活函数的神经元会输出
至下一层神经元或作为整个神经网络的输出(取决现神经元在网络结构中所处位置)。
形状如图4所示
图4
相应代码:
#在前面的代码之外新增
import torch.nn.functional as nf
#加入一个relu激活函数
res_tensor = torch.from_numpy(res).float()#将numpy.ndarray 转换为 torch.Tensor,并将其转换为浮点类型。
x=nf.relu(res_tensor)#relu激活函数
x_np=x.numpy()#将 torch 张量转换回 numpy 数组
cv2.imshow('relu',x_np)
cv2.waitKey(0)
结果:
图5
池化层(Pooling Layer)
原理:
用于对图像进行重采样,减少计算量。常采用的池化操作有 最大池化(MaxPooling)、平均池化(AveragePooling)、随机池化(Random Pooling)。
图6
与卷积操作类似,池化操作也是通过滑动窗口来便利输入图像的特征。如图3所示,使用最大池化操作缩减采样,进行图像的转换,将原本的图像缩小了,但是不会改变图像的数量。
相应代码:
#池化操作,以最大池化为例
pool=nf.max_pool2d(x,kernel_size=2,stride=2)#最大池化,其中移动窗口大小为2,步长为2
pool_np=pool.numpy()#将 torch 张量转换回 numpy 数组
结果:
图7
全连接层(Fully Connected Layer):
原理:
全连接层的每一个节点都与上一层的所有节点相连接,用来把前面提取到的特征综合起来。
通常伴随着一个权重矩阵和一个偏置项。其计算公式为:
其中:为输出概率,为全连接层的输入,为权重,为偏置项(可有可无)。
作用:
在CNN的末尾,通常会有一到多个全连接层,用于将前面层提取的特征转换为最终的输出。
相应代码:
# 全连接层操作
# 确保 pool 张量在 CPU 上,并且形状正确
pool = pool.cpu()
pool_flat = pool.view(1, -1) # 展平张量
fc = nn.Linear(in_features=pool_flat.shape[1], out_features=1) # 定义一个全连接层,输入特征维度为展平后的特征维度,输出维度为1
fc.weight.data.fill_(1) # 初始化权重为1
fc.bias.data.fill_(0) # 初始化偏置为0
out = fc(pool_flat) # 全连接层输出
print('输出张量:',out) # 输出结果
附录:
全部代码:
import cv2
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as nf
import os
kernel = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]]) # 卷积核
print('请输入图片路径')
image_path = input()
def conv2d(name, kernel):
# 检查文件是否存在
if not os.path.exists(name):
print(f"文件 {name} 不存在")
return
# 卷积操作
img = cv2.imread(name) # 读取图片,图片放在项目中
if img is None:
print(f"无法读取图片: {name}")
return
res = cv2.filter2D(img, -1, kernel) # opencv的卷积操作,-1表示目标图像深度与输入图像的深度相同
cv2.imshow('original', img)
cv2.imshow('result', res)
cv2.waitKey(0)
# 加入一个relu激活函数
res_tensor = torch.from_numpy(res).float() # 将numpy.ndarray 转换为 torch.Tensor,并将其转换为浮点类型。
x = nf.relu(res_tensor) # relu激活函数
x_np = x.numpy() # 将 torch 张量转换回 numpy 数组
cv2.imshow('relu', x_np)
cv2.waitKey(0)
# 池化操作,以最大池化为例
pool = nf.max_pool2d(x, kernel_size=2, stride=2) # 最大池化
pool_np = pool.numpy() # 将 torch 张量转换回 numpy 数组
cv2.imshow('pool', pool_np)
cv2.waitKey(0)
# 全连接层操作
# 确保 pool 张量在 CPU 上,并且形状正确
pool = pool.cpu()
pool_flat = pool.view(1, -1) # 展平张量
fc = nn.Linear(in_features=pool_flat.shape[1], out_features=1) # 定义一个全连接层,输入特征维度为展平后的特征维度,输出维度为1
fc.weight.data.fill_(1) # 初始化权重为1
fc.bias.data.fill_(0) # 初始化偏置为0
out = fc(pool_flat) # 全连接层输出
print('输出张量:',out) # 输出结果
conv2d(image_path, kernel) # 调用函数