卷积神经网络

Posted by kalos Aner on January 4, 2025

卷积神经网络

K 紧邻算法:

1、计算已知类别数据集中的点与当前点的距离

2、按照距离依次排序

3、选取与当前点距离最小的 K 个点

4、确定前 K 个点所在的类别的出现频率

5、返回前 K 个点出现频率最高的类别作为当前点预测类别

推荐数据集:CIRAR-10

增加正则化惩罚可以减少过拟合。

反向传播用来降低损失,一般使用梯度下降。

激活函数:常用 Relu 作为激活函数

参数初始化:W = 0.01 * np.random.randn(D, H); D 和 H 表示行数和列数

卷积神经网络整体架构:

Snipaste_2025-01-07_16-20-09

  1. 输入层:输入层负责接收原始数据,这些数据通常以矩阵的形式存在。对于图像处理任务,输入层接收的矩阵大小为“高度 x 宽度 x 颜色通道”,其中颜色通道可能是 RGB(红绿蓝)或灰度等。
  2. 卷积层:卷积层是CNN的核心部分,它通过卷积运算提取输入数据中的局部特征。每个卷积层包含多个卷积核,每个卷积核独立对输入数据进行卷积运算,提取一种特定的局部特征。卷积运算的过程是将卷积核在输入数据上滑动,并对每个位置进行乘积累加的操作,从而得到该位置的特征值。最终得到一张特征图。
  3. 池化层:池化层通常位于卷积层之后,它的主要作用是进行下采样,减少数据的维度,同时保留重要特征。池化操作可以是最大池化、平均池化等。通过池化操作,CNN可以有效地降低计算复杂度和过拟合的风险。
  4. 全连接层:全连接层通常位于CNN的最后部分,它的作用是将前面卷积层和池化层提取到的特征进行整合,输出最终的分类结果。全连接层的神经元与前面层的所有神经元都进行连接,根据输入数据的特征和训练好的权重参数,计算输出结果。

可能会做多次卷积

Snipaste_2025-01-07_17-15-25

卷积层涉及参数:滑动窗口步长,卷积核尺寸,边缘填充,卷积核个数

卷积核就是图像处理时,给定输入图像,输入图像中一个小区域中像素加权平均后成为输出图像中的每个对应像素,其中权值由一个函数定义,这个函数称为卷积核。又称滤波器

卷积参数共享:如果不进行卷积参数共享,则每一个区域都用不同的卷积核。

池化:一般使用最大池化,用来压缩特征值

一些常见的CNN:

Alexnet:2012年夺冠的网络,性能不如现在的 CNN

Vgg:2014年的网络,有多个版本,常用16层的版本

残差网络 Resnet:如果某一层做的不好,可以把上一次的参数直接加到这一层后面。H(x) = F(x) + x。用来避免层数越高,效果越差。

感受野:当前特征中的某一个参数是由多少个原始参数决定的

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets,transforms 
import matplotlib.pyplot as plt
import numpy as np

### 下载读取数据
# 定义超参数 
input_size = 28  #图像的总尺寸28*28
num_classes = 10  #标签的种类数
num_epochs = 3  #训练的总循环周期
batch_size = 64  #一个撮(批次)的大小,64张图片

# 训练集
train_dataset = datasets.MNIST(root='./data',  
                            train=True,   
                            transform=transforms.ToTensor(),  
                            download=True) 

# 测试集
test_dataset = datasets.MNIST(root='./data', 
                           train=False, 
                           transform=transforms.ToTensor())

# 构建batch数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)
### 卷积网络模块构建
# 一般卷积层,relu层,池化层可以写成一个套餐
# 注意卷积最后结果还是一个特征图,需要把图转换成向量才能做分类或者回归任务
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(         # 输入大小 (1, 28, 28)
            nn.Conv2d(
                in_channels=1,              # 灰度图
                out_channels=16,            # 要得到几多少个特征图
                kernel_size=5,              # 卷积核大小
                stride=1,                   # 步长
                padding=2,                  # 如果希望卷积后大小跟原来一样,需要设置padding=(kernel_size-1)/2 if stride=1
            ),                              # 输出的特征图为 (16, 28, 28)
            nn.ReLU(),                      # relu层
            nn.MaxPool2d(kernel_size=2),    # 进行池化操作(2x2 区域), 输出结果为: (16, 14, 14)
        )
        self.conv2 = nn.Sequential(         # 下一个套餐的输入 (16, 14, 14)
            nn.Conv2d(16, 32, 5, 1, 2),     # 输出 (32, 14, 14)
            nn.ReLU(),                      # relu层
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),                # 输出 (32, 7, 7)
        )
        
        self.conv3 = nn.Sequential(         # 下一个套餐的输入 (16, 14, 14)
            nn.Conv2d(32, 64, 5, 1, 2),     # 输出 (32, 14, 14)
            nn.ReLU(),             # 输出 (32, 7, 7)
        )
        
        self.out = nn.Linear(64 * 7 * 7, 10)   # 全连接层得到的结果

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0), -1)           # flatten操作,结果为:(batch_size, 32 * 7 * 7)
        output = self.out(x)
        return output
### 计算准确率作为评估标准
def accuracy(predictions, labels):
    pred = torch.max(predictions.data, 1)[1] 
    rights = pred.eq(labels.data.view_as(pred)).sum() 
    return rights, len(labels) 
### 训练网络模型
# 实例化
net = CNN() 
#损失函数
criterion = nn.CrossEntropyLoss() 
#优化器
optimizer = optim.Adam(net.parameters(), lr=0.001) #定义优化器,普通的随机梯度下降算法

# 开始训练循环
for epoch in range(num_epochs):
    #当前epoch的结果保存下来
    train_rights = [] 
    
    for batch_idx, (data, target) in enumerate(train_loader):  #针对容器中的每一个批进行循环
        net.train()                             
        output = net(data) 
        loss = criterion(output, target) 
        optimizer.zero_grad() 
        loss.backward() 
        optimizer.step() 
        right = accuracy(output, target) 
        train_rights.append(right) 

    
        if batch_idx % 100 == 0: 
            
            net.eval() 
            val_rights = [] 
            
            for (data, target) in test_loader:
                output = net(data) 
                right = accuracy(output, target) 
                val_rights.append(right)
                
            #准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))

            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_loader.dataset),
                100. * batch_idx / len(train_loader), 
                loss.data, 
                100. * train_r[0].numpy() / train_r[1], 
                100. * val_r[0].numpy() / val_r[1]))