首页 > 其他分享 >卷积神经网络理解(5)

卷积神经网络理解(5)

时间:2024-02-04 14:24:00浏览次数:20  
标签:loss gt 卷积 id 神经网络 理解 model class

1、参数计算

               

 在一次卷积过程中,卷积核进行共享,即每个通道采用一个卷积核即可。

 在Pytorch的nn模块中,封装了nn.Conv2d()类作为二维卷积的实现。参数如下图所示

    

 

  • in_channels:输入张量的channels数
  • out_channels:输出张量的channels数
  • kernel_size:卷积核的大小。一般我们会使用3x3这种两个数相同的卷积核,这种情况只需要写kernel_size = 3就行了。如果左右两个数不同,比如3x5,那么写作kernel_size = (3, 5)
  • stride = 1:步长
  • padding:填充图像的上下左右,后面的常数代表填充的多少(行数、列数),默认为0。padding = 1时,若原始图像大小为32x32,则padding后的图像大小为34x34
  • dilation = 1:是否采用空洞卷积,默认为1(即不采用)
  • groups = 1:决定了是否采用分组卷积,默认为1可以参考一下:groups参数的理解
  • bias = True:是否要添加偏置参数作为可学习参数的一个,默认为True
  • padding_mode = ‘zeros’:padding的模式,默认采用零填充

前三个参数需要手动提供,后面的都有默认值

代码示例

 

 1 class Net(nn.Module):
 2     def __init__(self):
 3         nn.Module.__init__(self)
 4         self.conv2d = nn.Conv2d(in_channels=3,out_channels=64,kernel_size=4,stride=2,padding=1)
 5  
 6     def forward(self, x):
 7         print(x.requires_grad)
 8         x = self.conv2d(x)
 9         return x
10     
11 # 查看卷积层的权重和偏置
12 print(net.conv2d.weight)
13 print(net.conv2d.bias)

2、手写数字识别过程 

  1. 将手写数字图片转换为像素矩阵
  2. 对像素矩阵进行Padding不为0的卷积运算,目的是保留边缘特征,生成一个特征图
  3. 对这个特征图使用六个卷积核进行卷积运算,得到六个特征图
  4. 对每个特征图进行池化操作(也可称为下采样操作),在保留特征的同时缩小数据流,生成六个小图,这六个小图和上一层各自的特征图长得很像,但尺寸缩小了
  5. 对池化操作后得到的六个小图进行第二次卷积运算,生成了更多的特征图
  6. 对第二次卷积生成的特征图进行池化操作(下采样操作)
  7. 将第二次池化操作得到的特征进行第一次全连接
  8. 将第一次全连接的结果进行第二次全连接
  9. 将第二次全链接的结果进行最后一次运算,这种运算可能是线性的也可能是非线性的,最终每个位置(一共十个位置,从0到9)都有一个概率值,这个概率值就是将输入的手写数字识别为当前位置数字的概率,最后以概率最大的位置的值作为识别结果。可以看到,右侧上方是我的手写数字,右侧下方是模型(LeNet)的识别结果,最终的识别结果与我输入的手写数字是一致的,这一点从图片左边最上边也可以看到,说明此模型可以成功识别手写数字

可用代码:

  1 import torch
  2 from torch import nn
  3 import torchvision
  4 from torchvision import datasets
  5 from torchvision.transforms import ToTensor
  6 import matplotlib.pyplot as plt
  7 print(f"Pytorch version:{torch.__version__}\n torchvision version:{torchvision.__version__}")
  8 
  9 
 10 
 11 train_data=datasets.FashionMNIST(
 12     root="data",
 13     train=True,
 14     download=True,
 15     transform=ToTensor(),
 16     target_transform=None
 17 )
 18 
 19 test_data=datasets.FashionMNIST(
 20     root="data",
 21     train=False,
 22     download=True,
 23     transform=ToTensor(),
 24     target_transform=None
 25 )
 26 
 27 
 28 #数据集查看
 29 image, label = train_data[0]
 30 # print(image)
 31 # image, label #查看第一条训练数据
 32 # print(image.shape)  #查看数据的形状
 33 
 34 # 查看类别
 35 class_names = train_data.classes
 36 # print(class_names)
 37 
 38 
 39 # 图形可视化
 40 # print(f"Image shape: {image.shape}")
 41 plt.imshow(image.squeeze())
 42 plt.title(label)
 43 # plt.show()
 44 
 45 from torch.utils.data import DataLoader
 46 
 47 # 设置批处理大小超参数
 48 BATCH_SIZE = 32
 49 
 50 # 将数据集转换为可迭代的(批处理)
 51 train_dataloader = DataLoader(train_data,
 52     batch_size=BATCH_SIZE,  # 每个批次有多少样本?
 53     shuffle=True  # 是否随机打乱?
 54 )
 55 '''
 56 shuffle:指对数据集进行随机打乱,以便在训练模型时以随机顺序呈现数据。这样做有助于提高模型的泛化能力并减少模型对输入数据顺序的依赖性。
 57 相反,对于测试数据集通常被设置为False,因为在评估模型性能时,我们希望保持数据的原始顺序,以便能够正确评估模型在真实数据上的表现。
 58 '''
 59 test_dataloader = DataLoader(test_data,
 60     batch_size=BATCH_SIZE,
 61     shuffle=False  # 测试数据集不一定需要洗牌
 62 )
 63 
 64 # #打印结果
 65 # print(f"Dataloaders: {train_dataloader, test_dataloader}")
 66 # print(f"Length of train dataloader: {len(train_dataloader)} batches of {BATCH_SIZE}")
 67 # print(f"Length of test dataloader: {len(test_dataloader)} batches of {BATCH_SIZE}")
 68 
 69 
 70 device = "cuda" if torch.cuda.is_available() else "cpu"
 71 # print(device)
 72 
 73 
 74 # Create a convolutional neural network
 75 class FashionMNISTModelV2(nn.Module):
 76     def __init__(self, input_shape: int, hidden_units: int, output_shape: int):
 77         super().__init__()
 78         self.block_1 = nn.Sequential(
 79             nn.Conv2d(in_channels=input_shape,             # 传入的图片是几层的,灰色为1层,RGB为三层
 80                       out_channels=hidden_units,           # 输出的图片是几层
 81                       kernel_size=3,                       # 代表扫描的区域点为3*3
 82                       stride=1,                            # 就是每隔多少步跳一下
 83                       padding=1),                          # 边框补全,其计算公式 = (kernel - 1)/2 = (3-1)/2 = 1
 84             # -->-- (10,3,3)
 85             nn.ReLU(),
 86             nn.Conv2d(in_channels=hidden_units,            # 这里的输入是上层的输出为10层
 87                       out_channels=hidden_units,
 88                       kernel_size=3,
 89                       stride=1,
 90                       padding=1),
 91             nn.ReLU(),
 92             nn.MaxPool2d(kernel_size=2,
 93                          stride=2)
 94         )
 95         self.block_2 = nn.Sequential(
 96             nn.Conv2d(hidden_units, hidden_units, 3, padding=1),
 97             nn.ReLU(),
 98             nn.Conv2d(hidden_units, hidden_units, 3, padding=1),
 99             nn.ReLU(),
100             nn.MaxPool2d(2)
101         )
102         self.classifier = nn.Sequential(
103             nn.Flatten(),
104             nn.Linear(in_features=hidden_units*7*7,
105                       out_features=output_shape)
106         )
107 
108     def forward(self, x: torch.Tensor):
109         x = self.block_1(x)
110         # print(x.shape)
111         x = self.block_2(x)
112         # print(x.shape)
113         x = self.classifier(x)
114         # print(x.shape)
115         return x
116 
117 # 加入参数
118 torch.manual_seed(42)
119 model_2 = FashionMNISTModelV2(1, 10,len(class_names)).to(device)
120 # print(model_2)
121 
122 
123 # 设置loss和optimizer
124 # 导入accurcay_fn辅助函数文件
125 import requests
126 from pathlib import Path
127 
128 # 从Learn PyTorch存储库中下载辅助函数(如果尚未下载)
129 if Path("helper_functions.py").is_file():
130   print("helper_functions.py已存在,跳过下载")
131 else:
132   print("正在下载helper_functions.py")
133   # 注意:你需要使用"raw" GitHub URL才能使其工作
134   request = requests.get("https://raw.githubusercontent.com/mrdbourke/pytorch-deep-learning/main/helper_functions.py")
135   with open("helper_functions.py", "wb") as f:
136     f.write(request.content)
137 
138 # 创建loss、accuracy和optimizer
139 
140 from helper_functions import accuracy_fn
141 
142 # 设置loss和optimizer
143 loss_fn = nn.CrossEntropyLoss() # this is also called "criterion"/"cost function" in some places
144 optimizer = torch.optim.SGD(params=model_2.parameters(), lr=0.1)
145 
146 
147 # 创建一个计时器
148 from timeit import default_timer as timer
149 def print_train_time(start:float,end:float,device:torch.device=None):
150   total_time=end-start
151   print(f"Train time on {device}: {total_time:.3f} seconds")
152   return total_time
153 
154 
155 # 训练和测试循环
156 def train_step(model: torch.nn.Module,
157                data_loader: torch.utils.data.DataLoader,
158                loss_fn: torch.nn.Module,
159                optimizer: torch.optim.Optimizer,
160                accuracy_fn,
161                device: torch.device = device):
162     train_loss, train_acc = 0, 0
163     model.to(device)
164     for batch, (X, y) in enumerate(data_loader):
165         X, y = X.to(device), y.to(device)
166         y_pred = model(X)
167         loss = loss_fn(y_pred, y)
168         train_loss += loss
169         train_acc += accuracy_fn(y_true=y,
170                                  y_pred=y_pred.argmax(dim=1))
171         optimizer.zero_grad()
172         loss.backward()
173         optimizer.step()
174 
175     train_loss /= len(data_loader)
176     train_acc /= len(data_loader)
177     print(f"Train loss: {train_loss:.5f} | Train accuracy: {train_acc:.2f}%")
178 
179 def test_step(data_loader: torch.utils.data.DataLoader,
180               model: torch.nn.Module,
181               loss_fn: torch.nn.Module,
182               accuracy_fn,
183               device: torch.device = device):
184     test_loss, test_acc = 0, 0
185     model.to(device)
186     model.eval() # put model in eval mode
187     # Turn on inference context manager
188     with torch.inference_mode():
189         for X, y in data_loader:
190             X, y = X.to(device), y.to(device)
191             test_pred = model(X)
192 
193             test_loss += loss_fn(test_pred, y)
194             test_acc += accuracy_fn(y_true=y,
195                 y_pred=test_pred.argmax(dim=1) # Go from logits -> pred labels
196             )
197         test_loss /= len(data_loader)
198         test_acc /= len(data_loader)
199         print(f"Test loss: {test_loss:.5f} | Test accuracy: {test_acc:.2f}%\n")
200 
201 torch.manual_seed(42)
202 
203 from timeit import default_timer as timer
204 train_time_start_model_2 = timer()
205 from tqdm import tqdm
206 
207 epochs = 3
208 for epoch in tqdm(range(epochs)):
209     print(f"Epoch: {epoch}\n---------")
210     train_step(data_loader=train_dataloader,
211         model=model_2,
212         loss_fn=loss_fn,
213         optimizer=optimizer,
214         accuracy_fn=accuracy_fn,
215         device=device
216     )
217     test_step(data_loader=test_dataloader,
218         model=model_2,
219         loss_fn=loss_fn,
220         accuracy_fn=accuracy_fn,
221         device=device
222     )
223 
224 train_time_end_model_2 = timer()
225 total_train_time_model_2 = print_train_time(start=train_time_start_model_2,
226                                            end=train_time_end_model_2,
227                                            device=device)
228 
229 
230 # 模型评估和结果输出
231 torch.manual_seed(42)
232 
233 
234 def eval_model(model: torch.nn.Module,
235                data_loader: torch.utils.data.DataLoader,
236                loss_fn: torch.nn.Module,
237                accuracy_fn,
238                device: torch.device = device):  # 注意
239     loss, acc = 0, 0
240     model.eval()
241     with torch.inference_mode():
242         for X, y in data_loader:
243             # 注意设备转移
244             X, y = X.to(device), y.to(device)
245             y_pred = model(X)
246             loss += loss_fn(y_pred, y)
247             acc += accuracy_fn(y_true=y, y_pred=y_pred.argmax(dim=1))
248 
249         loss /= len(data_loader)
250         acc /= len(data_loader)
251     return {"model_name": model.__class__.__name__,
252             "model_loss": loss.item(),
253             "model_acc": acc}
254 
255 
256 model_2_results = eval_model(
257     model=model_2,
258     data_loader=test_dataloader,
259     loss_fn=loss_fn,
260     accuracy_fn=accuracy_fn
261 )
262 print(model_2_results)
手写字可用代码,点击加号扩展

 3、补充知识点

如果输入图像的size为(N, N)卷积核的size为(F, F),输出的特征图的size为 (N-F)/ stride +1

如下图,当步长=3时,输出特征图size是小数,应该避免这种情况,在原图的周围使用0填充,使输出特征图size是整数。

使用padding后,输出特征图size公式变为:(N+2P-F)/ stride +1

p 代表原图周围padding几圈。如果 p =(F-1)/ 2,且步长=1,那么输入和输出特征图的size相同

 

 1x1卷积是指 卷积核的长宽都等于1。如下图所示,输入特征图的shape为 [64, 64, 192],一个卷积核shape为 [1, 1, 192],对应元素相乘再相加,得到卷积输出特征图的shape为 [64, 64, 1]

可以使用不同个数的1x1卷积核来升维或降维,如果1x1卷积核个数是128个,就将输入图像的shape从 [64, 64, 192] 降为 [64, 64, 128]。

1x1卷积的作用:(1)降维或升维;(2)跨通道信息交融;(3)减少参数量;(4)增加模型深度,提高非线性表示能力。

如下图(a)所示,使用 5x5x256 的卷积核计算,参数量有二十万。操作数160.5M,可理解为每张输出特征图都是由一次卷积得来的,每一次卷积的运算量就是每个卷积核的size = 5*5

如图(b)所示,先用1x1卷积核降维,再用5x5卷积提取特征,每个1x1卷积核的参数量只有1*1*256。大大减少了计算量

 4、ReLU函数()

 激活函数的作用是把神经元的输入线性求和后,放入非线性的激活函数中激活,正因为有非线性的激活函数,神经网络才能拟合非线性的决策边界,解决非线性的分类和回归问题

                                                              

 

 ReLU函数又称为修正线性单元,将输入小于0的数都抹为0。

公式:<span id="MathJax-Span-68" class="mrow"><span id="MathJax-Span-69" class="mi">f<span id="MathJax-Span-70" class="mo">(<span id="MathJax-Span-71" class="mi">x<span id="MathJax-Span-72" class="mo">)<span id="MathJax-Span-73" class="mo">=<span id="MathJax-Span-74" class="mi">m<span id="MathJax-Span-75" class="mi">a<span id="MathJax-Span-76" class="mi">x<span id="MathJax-Span-77" class="mo">(<span id="MathJax-Span-78" class="mn">0<span id="MathJax-Span-79" class="mo">,<span id="MathJax-Span-80" class="mi">x<span id="MathJax-Span-81" class="mo">)

特点:   

(1)函数不会饱和,x大于0时,输入有多大,输出就有多大。

(2)计算简单,几乎不消耗计算资源。

(3)x大于0时梯度能得到保留,因此ReLU函数比sigmoid函数的收敛速度快6倍以上。

                                                                       

 缺陷: 

(1)输出不是关于0对称

(2)x小于0时,梯度为0。导致一些神经元是死的(dead ReLU),既不会产生正的输出,也不会产生正的梯度,也不会发生更新。

导致Dead ReLU原因是:① 初始化不良,随机初始化的权重,让神经元所有的输出都是0,梯度等于0;② 学习率太大,步子跨太大容易乱窜

为了解决Dead ReLU的问题,在ReLU的权重上加一个偏置项0.01,保证所有的神经元一开始都能输出一个正数,使所有的神经元都能获得梯度更新

 ReLU函数改进:

为了解决ReLU函数x小于0,梯度为0的情况,推出了Leaky ReLU 和 ELU函数。

Leaky ReLU函数给x小于0时,乘以一个非常小的权重

公式:<span id="MathJax-Span-83" class="mrow"><span id="MathJax-Span-84" class="mi">f<span id="MathJax-Span-85" class="mo">(<span id="MathJax-Span-86" class="mi">x<span id="MathJax-Span-87" class="mo">)<span id="MathJax-Span-88" class="mo">=<span id="MathJax-Span-89" class="mi">m<span id="MathJax-Span-90" class="mi">a<span id="MathJax-Span-91" class="mi">x<span id="MathJax-Span-92" class="mo">(<span id="MathJax-Span-93" class="mi">α<span id="MathJax-Span-94" class="mi">x<span id="MathJax-Span-95" class="mo">,<span id="MathJax-Span-96" class="mi">x<span id="MathJax-Span-97" class="mo">)

 x小于0时满足线性关系

                                                                              

ELU函数给x小于0时,使用指数函数 

 公式:equation

 改善了ReLU函数输出不关于0对称的问题,但是使用指数运算会带来更大的计算量 

                                                               

 

 

标签:loss,gt,卷积,id,神经网络,理解,model,class
From: https://www.cnblogs.com/Zhouce/p/18001180

相关文章

  • 神经网络优化篇:详解Softmax 回归(Softmax regression)
    Softmax回归有一种logistic回归的一般形式,叫做Softmax回归,能让在试图识别某一分类时做出预测,或者说是多种分类中的一个,不只是识别两个分类,来一起看一下。假设不单需要识别猫,而是想识别猫,狗和小鸡,把猫加做类1,狗为类2,小鸡是类3,如果不属于以上任何一类,就分到“其它”或者说“以上......
  • 每天1plus道算法题【学习->理解->创新】
    原则和步骤 2024/2/3原则动脑子,具体说就是揣摩背后的思路,思考并查询其在哪种场景下产生的,为了解决什么问题其实这个算是智力题游戏,既然是游戏就可以打怪升级越变越强的步骤step1读题+思考得出解法10min(一遍都是暴力解法)step2尝试写程序实现,可以先提交初版(在vscode里写或......
  • 读论文-基于图神经网络的会话推荐方法综述
    前言今天读的一篇文章是于2023年6月25日在《数据分析与知识发现》发表的一篇期刊论文,文章聚焦图神经网络技术,对会话推荐方法进行述评,为后续研究提供借鉴。以“SessionbasedRecommendation”、“GraphNeuralNetwork”、“会话推荐”、“图神经网络”为检索词,在“WebofScience......
  • synchronized【如何保证原子性、可见性、有序性】【如何实现原子性 原理解析】【什么
    @TOC转自极客时间如何解决可见性问题?同步原理剖析什么是Monitor?什么是锁优化?......
  • C语言“->“和“p->next“的理解
    这是对"->"的理解在c语言中,"->"是结构体指针的运算符.它允许你通过指向结构体的指针来访问结构体中的成员.例如,以下定义了一个student类的结构体,其中包含两个字符串类型的成员:"name","id",以及一个整形成员"age",可以使用如下方式访问结构体成员:structstudent{c......
  • 【随笔】我终于理解了退流!!!!!!!!
    我知道我看起来像个若至但这对我来说是历史性的一刻在我看来退流是NOI级基础算法图论中最难理解的,但是我看过的所有讲解都是一笔带过我不能理解虽然你晨星灵也只提了几句,但是我总算是彻底理解退流的原理和合理性了对于强迫症的我,不完全理解就去敲代码是不可接受的晨星灵我......
  • 数据库初步理解
    数据库的字段属性Unsinged: 无符号的整数不能声明为负数zerofill: 0填充的不足的位数,使用0来填充int(3),5----005自增:通常理解为自增,自动在上一条记录的基础上加+1(默认)通常用来设计唯一的主键~~~index,必须是整数类型可以自定义设计主键自增的......
  • 链表中,LNode和* LinkList的理解
    typedefstructLNode{ intdata; structLNode*next;}LNode,*LinkList;这是标准链表创建法。简单的说,其中LNode和*LinkList分别是LNode的不同名字,显而易见一个是指针一个不是指针。两者等价关系如下:typedefstructLNodeLNode;//将结构体重命名为LNodetypedefs......
  • 从TF-IDF 到BM25, BM25+,一文彻底理解文本相关度
    相关性描述的是⼀个⽂档和查询语句匹配的程度。我们从搜索引擎召回时,肯定希望召回相关性高的数据,那么如何来量化相关度呢。首先,我们定义,一个文档doc,由多个词语term组成。最早,通过最简单的TF-IDF来衡量。TF-IDF朴素的思想,相关度应该是词语权重、文档权重的融合。词频TF(Ter......
  • Python中用PyTorch机器学习神经网络分类预测银行客户流失模型|附代码数据
    阅读全文:http://tecdat.cn/?p=8522最近我们被客户要求撰写关于神经网络的研究报告,包括一些图形和统计输出。分类问题属于机器学习问题的类别,其中给定一组特征,任务是预测离散值。分类问题的一些常见示例是,预测肿瘤是否为癌症,或者学生是否可能通过考试在本文中,鉴于银行客户的某些......