目录
2.1. Positive Attention Shifting Block in fd1
2.2. Negative Attention Shifting Block in fd2
4. Results and Discussion(结果和讨论)
4. torchsummary:计算神经网络模型各层输出特征图尺寸及参数量
本周完成的计划
- 阅读了半监督论文, Learning Morphological Feature Perturbations for Semi-Supervised Segmentation(半监督分割的形态学特征扰动学习)
- 回顾了弱监督论文,Multi-Layer Pseudo-Supervision for Histopathology Tissue Semantic Segmentation using Patch-level Classification Labels(利用Patch级分类标签进行组织病理学组织语义分割的多层伪监督法)
- 学习pytorch中容易搞混的几个知识点
- 跑老师的病理组织分割项目
论文阅读1
Learning Morphological Feature Perturbations for Semi-Supervised Segmentation(半监督分割的形态学特征扰动学习)
Abstract(摘要)
我们提出了MisMatch,一种新的一致性驱动的半监督分割框架,它产生对学习特征扰动不变的预测。MisMatch由一个编码器和一个双头解码器组成。另一个解码器在相同的未标记的图像上学习对前景的消极关注,从而产生被侵蚀的特征。然后,我们对成对的预测应用一致性正则,MisMatch在基于CT的肺血管分割任务和基于MRI的脑肿瘤分割任务中的表现优于最先进的半监督方法。此外,我们表明,MisMatch的有效性来自于比其监督学习对应的更好的模型校准。
1. Introduction(介绍)
半监督学习(SSL)旨在通过利用无标签的数据来解决标签稀缺的问题。SSL的成功建立在聚类假设上,即属于同一聚类的数据点更有可能属于同一类别(Cahpelle等人,2006)。将聚类假设与平滑度假设结合起来也是很常见的,平滑度假设认为数据点在聚类中心的密度更大。聚类假设的一个理想例子见下图1,其中一个聚类是圆形,另一个聚类是三角形,分别代表两个不同类别的数据点。当聚类假设成立时,最佳决策边界应位于两个聚类之间的低密度区域,以分离两个不同类别的数据点。我们用图1来解释一致性正则化在SSL分类中发挥作用的直观原因。
集群假设说,最佳决策边界位于低密度区域。此外,一致性正则化可以迫使模型在低密度区域设置决策边界。在这里,我们有两个圆形数据点和三角形数据点的群组。箭头:扰动。虚线:决策边界。
(a)当对位于高密度区域的红色数据点进行扰动时,两个红色扰动的数据点仍将被归类为同一圈层,因此一致性损失为零;
(b)当对位于低密度区域的蓝色数据点进行扰动时,两个蓝色扰动的数据点将有两个不同的预测值,这两个预测值之间的差异将对训练模型的一致性损失作出贡献。聚类边缘的数据点上的扰动会跨越低密度区域,导致错误的分类结果,这就制定了一个有效的一致性损失值来驱动决策边界向低密度区域移动。
一致性损失本质上是一种正则化,它将决策边界推向低密度区域。考虑到集群假设,网络因此可以正确地对未标记的数据点进行分类。
在本文中,我们介绍了MisMatch,这是一个使用注意力机制的深度框架,它可以学习在特征水平上应用形态学扰动,用于具有一致性正则化的半监督分割框架。
2. Methods(方法)
MisMatch背后的总体概念是利用不同的注意力机制来分别扩张和侵蚀前景特征,这些特征在一个一致性驱动的框架中被结合起来进行半监督性分割。如图2所示,MisMatch是一个框架,可以集成到任何基于编码器-解码器的分割架构中。
MisMatch学习对前景的信心不变的预测:解码器fd1扩张前景特征,解码器fd2侵蚀前景特征。最后的预测是fd1和fd2的输出之间的平均值。可以使用任何编码器-解码器分割网络。
Morphological Operations At Feature Level(在特征层面的形态学操作)
经典的形态学操作在图像层面上改变了前景的边界,而我们的网络拓扑结构被设计为影响特征层面。我们结合了两个概念。首先,研究结果(Wei等人,2018年;Chen等人,2017年;Luo等人,2016年;Xu等人,2020b)表明,Atrous卷积可以通过增加前景边界上的假阳性来扩大前景特征。 第二,(Luo等人,2016;Xu等人,2020b)的结果显示,跳跃连接可以缩小前景特征。结合起来,我们可以通过Atrous卷积和跳跃连接实现基于学习的特征扰动,从而实现一致性驱动的半监督分割。
Architecture of MisMatch(MisMatch的架构)
我们使用U-net(Ronneberger等人,2015)作为主干,因为它在医学成像领域很受欢迎。我们的MisMatch(图2)有两个组件:一个编码器(fe)和一个双头解码器(fd1和fd2)。
第一个解码器(fd1)包括一系列积极的注意力转移块,它扩张了前景。第二个解码器(fd2)包含一系列负面注意力转移块,它侵蚀了前景。第二个解码器(fd2)包含一系列负面注意力转移块,它侵蚀了前景。
2.1. Positive Attention Shifting Block in fd1
积极注意转移块(PASB)(图2中的粉色块)通过积极注意前景来扩张前景特征,每个PASB有两个平行的分支,即主分支和侧分支。
主分支用于处理视觉信息,它的结构与标准U-net中的解码器块相同,包括两个连续的卷积层,核大小为3,然后是ReLU和归一化层。侧枝被用来生成一个扩张的注意力掩码,以引导主枝放大其前景特征。为此,侧面分支使用了两个连续的Atrous卷积层,核大小为3,扩张率为5,每个卷积层后面都有ReLU和一个归一化层。为了学习每个像素的特征变化幅度,我们在侧枝的末端应用一个Sigmoid函数。我们使用侧枝的输出与主枝的输出进行元素明智的乘法,以扰乱主枝的特征。然后,我们在被扰动的主分支输出上应用一个跳过连接,以产生PASB的最终输出。
2.2. Negative Attention Shifting Block in fd2
负面注意力转移块(图2中的紫色块)利用对前景的负面注意力侵蚀前景特征。继PASB之后,我们再次将NASB设计成两个平行的分支。
主分支与PASB中的分支相同。侧枝与主枝相似,但在每个卷积层上有一个跳过连接。我们还在侧枝的输出上应用一个Sigmoid函数来学习每个像素的扰动幅度。我们还在侧枝的输出上应用一个Sigmoid函数来学习每个像素的扰动幅度。然后,我们把从侧面分支学到的侵蚀注意掩码与主分支的输出相乘,我们还在扰动的主分支输出上应用跳过连接,以产生NASB的最终输出。
2.3. Loss Functions(损失函数)
我们使用流式训练设置,以避免在有限的标记数据上过度拟合,因此模型在每个历时中不会重复看到标记数据。对于标记的数据,我们对每个解码器的输出应用标准的Dice损失(Milletari等人,2016)。对于无标签的数据,我们在两个解码器的输出之间应用了一个平均平方误差损失。这种一致性正则化是由介于(0.0005到0.004)之间的超参数α加权的。
3. Experiments(实验)
主干网是一个二维U型网(Ronneberger等人,2015),在第一个编码器中有24个通道。为了确保公平的比较,我们在所有基线上使用相同的U型网作为主干网。第一条基线在主干线上利用监督训练,用标记的数据进行训练,用翻转和高斯噪声进行增强,被称为 "Sup1"。为了研究无标签数据如何提高性能,我们的第二条基线 "Sup2 "利用MisMatch的监督训练,并进行同样的增强。因为MisMatch使用了一致性正则化,所以我们专注于与五种一致性正则化的SSL方法进行比较:
1)"平均教师"(MT)(Tarvainen和Valpola,2017),使用高斯噪声,它启发了目前大多数最先进的SSL方法。
2)目前最先进的模型称为 "FixMatch"(FM)(Sohn等人,2020)。为了使FixMatch适用于分割任务,我们使用高斯噪声作为弱增强,使用没有剪切的 "RandomAug"(Cubuk等人,2020)作为强增强。我们不使用剪切法进行扩增,因为它损害了成对密集输出的像素的空间对应关系。
3)采用多头解码器的最先进模型(Ouali等人,2020)进行分割(CCT),随机特征增强包括Dropout(Srivastava等人,2014)、V AT(Miyato等人,2017)和CutOut(DeVries和Taylor,2017)等,这一基线也与最近开发的模型相似(French等人,2020;Ke等人,2020)。
4)最近在医学成像方面又有一个模型(Chen等人,2019),使用图像重建作为额外的正则化(MTA),用高斯噪声增强。
5)一个带有两个标准解码器的U型网络,我们分别在每个解码器中直接对特征进行传统的侵蚀和扩张,并以高斯噪声(Morph)进行增强"。我们的MisMatch模型是在没有任何增强的情况下进行训练的。训练和实施的细节见附录A。
4. Results and Discussion(结果和讨论)
分割性能:
1)MisMatch的性能一直大大超过有监督的基线,例如在5个标记的切片上比Sup1提高24%,CARVE。
MisMatch(MM)与CAR VE上的基线对比。衡量标准是 "交叉联合"(IoU):10倍交叉验证下的平均值(std)。P值来自Mann-Whitney U-Test对MisMatch的测试。红色:最佳模型。蓝色:第二好的模型。
2)在表1中,MisMatch在不同的数据集上始终优于之前的SSL方法(Sohn等人,2020;Tarvainen和Valpola,2017;Chen等人,2019;Ouali等人,2020),例如在CARVE上使用6.25%的标签(100片与1600片全标签相比)时的统计差异(表1)。
3)训练数据的多样性对测试性能影响很大(图3),例如,在CAR VE的第3折中,MisMatch优于全标签训练,在第7和第8折中,MisMatch的表现与全标签训练相当,而在其他折中,MisMatch的表现比全标签训练差。
4)更多标记的训练数据始终产生较高的平均IU和较低的标准偏差(表2)。
消融研究 我们对MisMatch解码器的结构进行了消融研究(图4),对CAR VE的5个标记的切片进行了交叉验证。
1) “MM-a”是一个双头U型网络,在解码器中带有标准卷积块,该模型可以被视为没有特征扰动,但是,由于随机初始化,它们本质上略有不同,我们将U型网络的解码器表示为fd0
2) “MM-b”是U-net的标准译码器和负注意转移译码器fd2,这一译码器介于无扰动和扰动之间;
3) “MM-c”是U-net的标准译码器和正注意转移译码器fd1,该译码器介于无扰动和学习扩张扰动之间;
4) “MM”,fd1和fd2(我们的)。
如图4所示,我们的MisMatch("MM")在10个实验中的8个实验中优于其他组合,在其余2个实验中,它的表现与其他组合相当。我们还用同样的实验设置在0、0.0005、0.001、0.002、0.004测试了α。最佳的α出现在表4中的0.002。
MisMatch是更好的校准 我们认为MisMatch利用未标记的图像来改善模型校准(Guo等人,2017),导致更好的分割性能。模型校准反映了网络预测的可信度,这在临床应用中是至关重要的。按照(Guo等人,2017),我们将Bm设定为预测置信度在区间Im内的所有像素的子集。我们将准确性定义为在每个置信区间内有多少像素被正确分类。
Bm的精确度是:其中,ˆyi是预测的标签,yi是Bm中像素i处的地面真实标签。Bm内的平均置信度是用ˆpi来定义的,它是网络在每个像素的原始概率输出:
比较每个区间的准确度和置信度的图称为可靠性图(图5),准确度和置信度之间的差距称为预期校准误差,差距越小,网络的校准就越好。如图5所示,MisMatch产生了更好的校准预测结果。预期校准误差的分析见附录D。
5. Conclusion(结论)
我们提出了MisMatch,这是一个一致性驱动的SSL框架,具有基于注意力的特征增强功能,用于医学图像的半监督分割。MisMatch通过减少90%以上所需的训练标签的数量,保证了强大的临床效用:当只对10%的标签进行训练时,MisMatch达到了与使用所有可用标签训练的模型(IoU:77%)相似的性能(IoU:75%)。未来的工作将把MisMatch扩展到多类3D任务,因为很多医学图像都是体积型的。
论文阅读2
Multi-Layer Pseudo-Supervision for Histopathology Tissue Semantic Segmentation using Patch-level Classification Labels(利用Patch级分类标签进行组织病理学组织语义分割的多层伪监督法)
Abstract(摘要)
在千兆像素的整张WSI图像上绘制这种标签是非常昂贵和耗时的。在本文中,我们只使用patch级的分类标签来实现组织病理学图像上的组织语义分割,最终减少了注释的工作量。我们提出了一个包括分类和分割阶段的两步模型。在分类阶段,我们提出了一个基于CAM的模型,通过斑块级标签生成伪掩码。在分割阶段,我们通过我们提出的多层伪监督实现了组织语义分割。我们提出的模型优于两种最先进的WSSS方法。
1. INTRODUCTION(介绍)
在本文中,我们提出了一个简单而有效的CNN模型,用于仅使用patch级注释的组织病理学组织语义分割。我们提出的模型包含一个分类阶段和一个分割阶段。
在分类阶段,我们提出了一个基于CAM的分类模型来生成伪标签。为了避免discriminative区域萎缩问题,我们提出了渐进式Dropout注意力(PDA),逐步停用高亮区域,并推动分类网络通过非优势区域来区分组织类别。
在分割阶段,我们通过多层分类网络产生的伪掩码来训练一个语义分割模型,我们称之为多层伪监督(MLPS)。MLPS可以提供来自不同阶段的信息,以减少patch级和像素级标签之间的信息差距。此外我们提出了一个分类门机制,以减少非优势组织类别的假阳性率。
与完全监督模型相比,我们的模型显示了可比的定量和定性结果,对MIoU和FwIoU来说只有2%左右的差距。事实证明,所提出的模型比人工标注的速度快10倍。本文的主要贡献总结如下:
- 我们提出了一个只使用patch级分类标签的组织病理学图像的语义分割模型,这大大节省了病理学家的注释时间。
- 提出了具有渐进式Dropout关注的多层伪监督,以减少补丁级和像素级标签之间的信息差距。并引入了分类门机制以减少假阳性率。
- 我们提出的模型在两个数据集上与弱监督的语义分割模型相比,取得了最先进的性能,并与完全监督的基线性能相当。
- 第一个LUAD数据集被发布,用于弱监督的组织语义分割。
2.METHODOLOGY(方法)
在本文中,我们提出了一个组织语义分割模型,只使用patch级标签,以减轻注释工作。图2展示了我们提出的模型的系统设计。
第一阶段(分类):让我们把分类阶段的给定训练数据表示为,
是分类模型的多标签预测分类结果,这个阶段的目标是只使用patch级标签y来生成密集的像素级伪标签p。
第二阶段(分割):有了分类阶段产生的伪标签,我们可以为分割模型形成一个新的训练数据,即,其中P是X的伪标签集,具有参数φseg的分割模型fseg产生了最终的语义分割结果s。
A. Weakly-supervised Pseudo Mask Generation(弱监督下的伪标签生成)
在训练分类模型时,特征图(Class Activation Maps,CAM)提供了可用于物体定位和分割的 discriminative 物体位置线索。受此启发,我们提出了一个新的基于CAM的模型,首先训练一个分类模型。由于组织的分布在某种程度上是随机和分散的,它可能在一个patch中包含多个组织类型。因此,我们将组织分类定义为一个多标签分类问题。
1) 伪标签生成:如图2(a)所示,给定一个输入patch x,我们首先提取深层特征图,如下所示。
其中m表示从最后一层提取的特征图。为了提供更丰富、更全面的特征表示,我们提出了Progressive Dropout Attention ,以防止分类模型过度关注最具 discriminative 的区域。
其中A是Dropout注意力图。在Dropout Attention后,第k个组织类别的概率可以通过全局平均池和全连接层来计算。
其中GAP(-)表示全局平均池化。 Multi-label soft margin loss 被应用于分类网络中。
有了训练好的多标签分类模型,通过梯度加权类激活映射(Grad-CAM)为下一个分割模型生成像素级伪标签p。
2) Progressive Dropout Attention(渐进式Dropout 注意力)
虽然分类模型可以为分割任务提供空间位置线索,但这两个任务的目标仍然是不同的。随着训练过程的深入,普通的分类模型倾向于关注图像中最具鉴别力的部分/区域,而忽略一些无关紧要的区域。从patch级标签到像素级标签仍然存在巨大的信息差距。因此,如何最大化这种稀疏注释的价值以缩小差距仍然是一个极端的任务。为了克服上述两个挑战,我们提出了渐进式Dropout注意力。
Dropout Attention: 提出的Dropout Attention的想法是简单而直观的。我们希望神经网络能够从稀疏的标签中学习尽可能多的信息。在训练过程中,不允许分类模型只依靠最具鉴别力的区域来 "make easy money"。相反,CNN模型必须学习更完整和全面的空间信息。因此,我们将所有组织类别的类激活图中最significant的区域 deactivate,如图2(a)所示。
这样的策略会削弱最具discriminative的区域的贡献,迫使神经网络通过非优势区域进行多标签分类,在提取深层特征时可以有效扩大激活区域。根据这个想法,我们首先为每个类别生成一个类激活图(CAM),由特征图m的加权和组成。其中Mk表示第k类的CAM。
对于每个Mk,我们设置了一个dropout cutoff β,以deactivate最突出的区域,并刷新CAMs,如下所示。其中,i和j表示坐标,是有Dropout的CAM。
注意,β是一个相对值,它取决于类激活图的最大值,其中,μ是dropout系数。
最后,dropout注意力图A是所有deactivated CAM的平均值。
Progressive Dropout Attention:
正如我们上面提到的,当训练过程进一步进行时,被激活的区域会逐渐缩小到一个较小的区域。根据这一观察,我们提出了一种基于dropout注意力的反向操作,称为渐进式dropout注意力(PDA)。PDA逐步扩大deactivated的区域,以对抗这种萎缩的问题。我们将原来的dropout系数μ重新设计为渐进式dropout系数,它不再是一个常量值。当训练epoch增加时,渐进式dropout系数μ将自适应地减少,直到μ满足下限l。
其中t是正在进行的epoch,σ是衰减率,我们在实践中设定σ=0.985,l=0.65。为了更好地初始化分类模型,在前三个epoch中,初始μ被设置为1。在第3个epoch后,我们开始dropout,并逐步扩大dropout区域,以逐渐增加分类的难度。有了渐进式dropout attention,discriminative区域收缩问题得到了极大的缓解,分类模型可以学习到更丰富、更广泛的特征表示,可以生成更精确的伪掩码,如图4所示。
B. Pseudo-supervised Tissue Semantic Segmentation(伪监督下的组织语义分割)
在分割阶段,我们在伪掩码p的监督下训练一个语义分割模型fseg,以得到输入patch x的语义分割结果s。
在这一阶段,提出了多层伪监督和分类门机制这两个具体设计,以进一步提高语义分割的性能。
1) Multi-Layer Pseudo-Supervision
由于patch级标签和像素级标签之间的信息差距,从分类网络中学习到的空间信息仍然是不完整的,即使有渐进式dropout attention。为了减少差距,我们必须为分割模型带来更多的信息。由于CNN模型在不同阶段学习不同层次的语义特征,我们从三个不同的层中生成多层伪标签来丰富信息。然后我们计算语义分割结果和所有伪标签之间的交叉熵损失。
其中λi是超参数。我们在实践中设定λ1 = 0.2,λ2 = 0.2,λ3 = 0.6。
2)Classification Gate Mechanism
长尾问题在医学数据中很常见,特别是对于组织病理学图像。对于那些非优势组织类别,如坏死和淋巴细胞,它们将被优势组织类别所支配。为了克服长尾问题,减少非优势类别的假阳性率,我们提出了一个分类门机制。在我们提出的框架中,我们观察到,在patch图像中是否存在组织类别的问题上,分类结果的置信度通常高于分割结果,尤其是对于非优势类别。因为分类模型是由真实标签训练的,而分割模型是由伪标签训练的。基于这一观察,我们为每个输出通道引入一个门。让表示分割模型中第k个组织类别的输出概率图,其中n是类别的数量,d是概率图的维度。对于每个类别k,如果来自分类模型的组织类别的预测概率
小于阈值
,就意味着这个类别的存在率低。然后我们将通过归零来 ”close the gate” of the probability map 。
然后,通过对概率图o进行argmax操作,可以得到语义分割结果。在实验中我们设置
其中(i,j)表示坐标。
3) Semantic Segmentation for WSIs
我们上面定义的模型是patch级语义分割模型,接下来,我们将介绍如何实现整个WSI图像的语义分割。如图3所示,我们首先从整个WSI图像中裁剪出重叠区域超过50%的patch。
利用分割模型,可以为每个patch生成n个通道的probability maps (概率图),然后我们将概率图缝合到WSI级别。 对于重叠区域,我们计算了每个像素位置处每个类别的概率平均值。然后通过argmax运算得到整个WSI图像的语义分割结果。
3. EXPERIMENTS(实验)
与现有的两个模型相比,我们提出的模型可以生成更精确的组织边界。
4.CONCLUSION(结论)
在本文中,我们提出了一种用于癌症组织病理学图像的组织级语义分割模型。该模型的主要贡献是将像素级注释替换为patch级注释,这对于病理学家减少注释工作是一个重大进步。在方法上,我们提出了一些技术创新,以最小化patch级和像素级标注之间的信息差距,并实现了出色的语义分割性能。为了对计算病理学和癌症研究领域做出贡献,我们还介绍了一种新的肺腺癌弱监督语义分割数据集LUADHistoSeg。这是第一个针对肺癌的组织级语义分割数据集。通过应用我们提出的模型,我们还不断为不同的癌症类型生成更多的组织级语义分割数据。
pytorch代码学习
1.易混的模块和参数的几个函数
对pytorch中比较容易搞混的几个名称和内容进行对比主要包括下面几个内容:
- model.modules(), model.named_modules()
- model.children(), model.named_children()
- model.parameters(), model.named_parameters(),
- model.state_dict()
"""
本文通过一个例子实验来观察并讲解PyTorch中model.modules(), model.named_modules(), model.children(), model.named_children(),
model.parameters(), model.named_parameters(), model.state_dict()这些model实例方法的返回值。例子如下:
"""
import torch
from torch import nn
class Net(nn.Module):
def __init__(self,num_class=10):
super(Net, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(in_channels=3,out_channels=6,kernel_size=3),
nn.BatchNorm2d(6),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2,stride=2),
nn.Conv2d(in_channels=6,out_channels=9,kernel_size=3),
nn.BatchNorm2d(9),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2,stride=2)
)
self.classifier = nn.Sequential(
nn.Linear(9*8*8,128),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(128,num_class)
)
def forward(self,x):
output = self.features(x)
output = output.view(output.size()[0],-1)
output = self.classifier(output)
return output
# 创建模型
model = Net()
# 如上代码定义了一个由两层卷积层,两层全连接层组成的网络模型。
# 值得注意的是,这个Net由外到内有3个层次:
"""
model = Net(
(features): Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)
)
"""
# print("model = ",model)
"""
网络Net本身是一个nn.Module的子类,它又包含了features和classifier两个由Sequential容器组成的nn.Module子类,
features和classifier各自又包含众多的网络层,它们都属于nn.Module子类,所以从外到内共有3个层次。
"""
# 下面我们来看这几个实例方法的返回值都是什么。
# model.modules() = <generator object Module.modules at 0x0000020038D3B350>
# model.named_modules() = <generator object Module.named_modules at 0x0000020038D3B350>
print("model.modules() = ",model.modules())
print("model.named_modules() = ",model.named_modules())
# model.children() = <generator object Module.children at 0x000001E80786E2E0>
# model.named_children() = <generator object Module.named_children at 0x000001E80786E2E0>
print("model.children() = ",model.children())
print("model.named_children() = ",model.named_children())
# model.parameters() = <generator object Module.parameters at 0x000001D3A2D083C0>
# model.named_parameters() = <generator object Module.named_parameters at 0x000001D3A2D083C0>
print("model.parameters() = ",model.parameters())
print("model.named_parameters() = ",model.named_parameters())
# model.state_dict() = OrderedDict([('features.0.weight', tensor([[[[-0.0342, 0.1146, -0.0751],.....
# print("model.state_dict() = ",model.state_dict())
"""
可以看出,除了model.state_dict()返回的是一个字典,其他几个方法返回值都显示的是一个生成器,
是一个可迭代变量,我们通过列表推导式用for循环将返回值取出来进一步进行观察:
"""
model_modules = [x for x in model.modules()]
"""
model_modules = [Net(
(features): Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)
), Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1)), BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1)), BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True), ReLU(inplace=True), MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False), Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
), Linear(in_features=576, out_features=128, bias=True), ReLU(inplace=True), Dropout(p=0.5, inplace=False), Linear(in_features=128, out_features=10, bias=True)]
"""
# print("model_modules = ",model_modules)
"""
由结果可以看出:
model.modules()迭代遍历模型的所有子层,本例子中指,
Net(),features(),classifier(),以及nn.xxx构成的卷积,池化,ReLU,Linear,BN,Dropout等
都是nn.Module子类,也就是modle.modules()会迭代的遍历它们所有对象。
可以看出,model_modules列表中共有15个元素,首先是整个Net,然后遍历了Net下的features子层,进一步遍历了feature下的所有层,
然后又遍历了classifier子层以及其下的所有层。所以说model.modules()能够迭代地遍历模型的所有子层。
"""
"""
model_named_modules = [('', Net(
(features): Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)
)), ('features', Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)), ('features.0', Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))), ('features.1', BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)), ('features.2', ReLU(inplace=True)), ('features.3', MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)), ('features.4', Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))), ('features.5', BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)), ('features.6', ReLU(inplace=True)), ('features.7', MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)), ('classifier', Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)), ('classifier.0', Linear(in_features=576, out_features=128, bias=True)), ('classifier.1', ReLU(inplace=True)), ('classifier.2', Dropout(p=0.5, inplace=False)), ('classifier.3', Linear(in_features=128, out_features=10, bias=True))]
"""
model_named_modules = [x for x in model.named_modules()]
# print("model_named_modules = ",model_named_modules)
"""
顾名思义,它就是有名字的model.modules()。model.named_modules()不但
返回模型的所有子层,还会返回这些层的名字。
可以看出,model.named_modules()也遍历了15个元素,但每个元素都有了自己的名字,从名字可以看出,
除了在模型定义时有命名的features和classifier,其它层的名字都是PyTorch内部按一定规则自动命名的。
返回层以及层的名字的好处是可以按名字通过迭代的方法修改特定的层,如果在模型定义的时候就给每个层起了名字,
比如卷积层都是conv1,conv2...的形式,那么我们可以这样处理:
for name, layer in model.named_modules():
if 'conv' in name:
对layer进行处理
"""
"""
当然,在没有返回名字的情形中,采用isinstance()函数也可以完成上述操作:
for layer in model.modules():
if isinstance(layer, nn.Conv2d):
对layer进行处理
"""
model_children = [x for x in model.children()]
# print("model_children = ",model_children)
"""
model_children = [Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
), Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
)]
"""
"""
如果把这个网络模型Net按层次从外到内进行划分的话,features和classifier是Net的子层,而conv2d, ReLU, BatchNorm, Maxpool2d这些有时features的子层,
Linear, Dropout, ReLU等是classifier的子层,上面的model.modules()不但会遍历模型的子层,还会遍历子层的子层,以及所有子层。
而model.children()只会遍历模型的子层,这里即是features和classifier
可以看出,它只遍历了两个元素,即features和classifier.
"""
model_named_children = [x for x in model.named_children()]
# print("model_named_children = ",model_named_children)
"""
model_named_children = [('features', Sequential(
(0): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
(1): BatchNorm2d(6, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(4): Conv2d(6, 9, kernel_size=(3, 3), stride=(1, 1))
(5): BatchNorm2d(9, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(6): ReLU(inplace=True)
(7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)), ('classifier', Sequential(
(0): Linear(in_features=576, out_features=128, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=128, out_features=10, bias=True)
))]
"""
"""
model.named_children()就是带名字的model.children(), 相比model.children(),
model.named_children()不但迭代的遍历模型的子层,还会返回子层的名字:
对比上面的model.children(), 这里的model.named_children()
还返回了两个子层的名称:features 和 classifier .
"""
model_parameters = [x for x in model.parameters()]
# print("model_parameters = ",model_parameters)
"""
迭代地返回模型的所有参数。
"""
model_named_parameters = [x for x in model.named_parameters()]
# print("model_named_parameters = ",model_named_parameters)
"""
model_named_parameters = [('features.0.weight', Parameter containing:
tensor([[[[ 0.0350, 0.0103, 0.1016],
[-0.1339, 0.1515, 0.1680],
[-0.0698, -0.0406, 0.0864]],
[[-0.1830, -0.0817, 0.0382],
[-0.1305, -0.0943, 0.0407],
[ 0.0353, 0.1068, -0.1145]],
[[-0.1619, -0.0422, -0.0821],
[ 0.0567, -0.1495, 0.0950],
[ 0.0903, -0.1243, -0.1422]]],
[[[-0.0305, 0.0149, 0.0102],
[ 0.1704, -0.1404, 0.0244],
[ 0.0490, -0.0901, 0.1811]],
[[-0.0258, 0.1664, -0.0227],
[ 0.0043, -0.0295, -0.0112],
[-0.1794, 0.0269, 0.0688]],
[[ 0.1838, 0.0892, -0.1854],
[ 0.1217, -0.1055, 0.1548],
[-0.1136, 0.1164, -0.1127]]],
[[[-0.1000, -0.0226, 0.0203],
[ 0.1791, -0.0128, -0.1299],
[ 0.1420, 0.0108, -0.1067]],
[[ 0.1317, -0.1859, -0.0801],
[ 0.0392, -0.0918, -0.0631],
[ 0.0047, 0.0786, 0.0613]],
[[-0.0692, 0.0892, -0.1899],
[-0.0533, 0.1172, -0.1542],
[-0.0785, 0.0462, 0.0684]]],
[[[ 0.0124, 0.1793, -0.0063],
[-0.0957, -0.0683, 0.1323],
[-0.1145, 0.0799, 0.1016]],
[[-0.0175, 0.1211, 0.0306],
[-0.1402, 0.0101, -0.0253],
[-0.0217, 0.1320, 0.0575]],
[[ 0.0924, 0.0992, -0.1721],
[-0.1088, 0.0864, 0.0491],
[ 0.1400, 0.0665, 0.0087]]],
[[[-0.1451, -0.1380, 0.0535],
[-0.1722, 0.1474, 0.0659],
[ 0.0804, 0.1919, -0.1452]],
[[ 0.1174, 0.1427, -0.1740],
[-0.0788, -0.0937, 0.1732],
[-0.0168, 0.0534, 0.1858]],
[[ 0.1235, -0.1223, -0.1094],
[ 0.0097, -0.0980, 0.1445],
[-0.1352, 0.1720, -0.0109]]],
[[[-0.1285, -0.1427, 0.1380],
[-0.1085, -0.0668, -0.1830],
[-0.0872, -0.0166, 0.0485]],
[[-0.1093, -0.0880, -0.1125],
[-0.1451, 0.1625, -0.1856],
[-0.1510, 0.0299, 0.1619]],
[[-0.0407, 0.0116, -0.1543],
[ 0.1722, -0.1791, -0.0838],
[ 0.1276, 0.1317, -0.0685]]]], requires_grad=True)), ('features.0.bias', Parameter containing:
tensor([ 0.0677, 0.0136, -0.1695, 0.0557, -0.0463, -0.1873],
requires_grad=True)), ('features.1.weight', Parameter containing:
tensor([1., 1., 1., 1., 1., 1.], requires_grad=True)), ('features.1.bias', Parameter containing:
tensor([0., 0., 0., 0., 0., 0.], requires_grad=True)), ('features.4.weight', Parameter containing:
tensor([[[[-0.0724, -0.1141, 0.0105],
[-0.1128, -0.0751, 0.1208],
[ 0.0953, 0.1177, -0.0444]],
[[ 0.0957, -0.1216, 0.0293],
[ 0.1293, 0.0983, -0.0347],
[ 0.1343, 0.0386, 0.0112]],
[[-0.0560, 0.0995, 0.0643],
[-0.0586, -0.0472, 0.0290],
[-0.0611, -0.1101, 0.0827]],
[[ 0.0525, 0.1166, 0.0700],
[ 0.0142, -0.0692, -0.0835],
[ 0.0243, -0.1282, -0.0282]],
[[ 0.0162, -0.1156, -0.0433],
[-0.0868, -0.1142, 0.0250],
[-0.0299, -0.0039, -0.0724]],
[[-0.1259, -0.1108, -0.0686],
[ 0.0741, 0.0572, 0.0359],
[ 0.0952, 0.0263, -0.1288]]],
[[[ 0.0185, 0.0808, -0.1170],
[-0.0757, -0.0199, 0.1268],
[-0.0362, 0.0034, 0.0579]],
[[-0.0528, -0.1009, 0.0548],
[ 0.1096, 0.1081, 0.0112],
[ 0.0492, 0.0263, -0.1221]],
[[ 0.0851, 0.1073, 0.0807],
[ 0.0136, -0.0085, -0.0158],
[ 0.0051, 0.1234, -0.1120]],
[[ 0.0973, 0.0102, -0.0802],
[ 0.0484, -0.0545, -0.0322],
[-0.0933, -0.0746, -0.0595]],
[[ 0.1175, 0.0084, -0.0093],
[ 0.0529, -0.0679, -0.0656],
[-0.0310, 0.0018, 0.0773]],
[[-0.0662, 0.0908, 0.0016],
[ 0.0542, 0.0713, -0.0626],
[ 0.0094, -0.0947, 0.0817]]],
[[[-0.0301, 0.0898, -0.0228],
[-0.1076, -0.1124, 0.0680],
[-0.1019, 0.0581, 0.0945]],
[[ 0.0332, -0.0941, -0.0758],
[-0.1314, 0.0705, 0.0255],
[-0.0424, 0.0831, 0.1061]],
[[ 0.0841, -0.0150, -0.0069],
[ 0.1205, 0.1154, 0.1192],
[ 0.1314, -0.0572, -0.0537]],
[[ 0.0459, -0.1302, 0.0496],
[-0.1219, 0.0894, 0.1038],
[-0.1248, 0.0557, -0.0805]],
[[ 0.0088, 0.1010, -0.1095],
[-0.0311, -0.0550, 0.0131],
[-0.0652, 0.0236, -0.1046]],
[[ 0.0475, -0.0349, -0.1173],
[ 0.0346, -0.0733, -0.0914],
[-0.0344, 0.0186, 0.0769]]],
[[[ 0.1303, 0.0428, -0.0268],
[-0.0286, 0.0107, 0.0120],
[-0.0309, 0.1230, -0.0535]],
[[ 0.0120, -0.0188, 0.1058],
[ 0.0150, -0.0027, 0.0316],
[-0.1111, 0.1085, 0.0616]],
[[-0.1200, -0.0523, 0.1007],
[ 0.0953, 0.0342, -0.0743],
[-0.0097, 0.0769, 0.0222]],
[[-0.1126, 0.0587, -0.0308],
[-0.0166, -0.0589, -0.0744],
[-0.0695, -0.1091, 0.0833]],
[[ 0.0272, 0.0990, 0.1173],
[-0.0356, -0.0556, -0.0878],
[ 0.0579, -0.0674, 0.0777]],
[[ 0.1267, -0.1126, 0.0829],
[ 0.1207, 0.0637, 0.0447],
[ 0.0101, 0.0537, 0.0130]]],
[[[-0.0861, -0.1215, -0.0174],
[-0.0884, 0.0968, 0.1005],
[-0.0225, -0.1242, -0.0878]],
[[-0.0664, 0.0387, 0.0136],
[-0.1168, -0.1265, -0.1013],
[ 0.0073, -0.0823, -0.0221]],
[[ 0.0765, 0.0109, -0.0760],
[ 0.1172, -0.0953, -0.0571],
[ 0.0735, -0.0136, 0.1066]],
[[-0.1084, -0.0231, 0.0387],
[ 0.0644, 0.0232, 0.0378],
[-0.0725, -0.0773, 0.0961]],
[[ 0.0749, -0.1018, 0.0645],
[ 0.0643, -0.1131, 0.0713],
[ 0.1302, -0.0467, 0.1238]],
[[ 0.0891, 0.0968, -0.0872],
[-0.0176, -0.0625, 0.1247],
[ 0.0764, 0.0404, -0.0692]]],
[[[-0.0216, 0.0178, 0.0861],
[ 0.0194, -0.0809, 0.0016],
[-0.0975, 0.0677, 0.1178]],
[[ 0.0833, 0.0980, 0.0511],
[ 0.1035, -0.1361, -0.0866],
[-0.0882, -0.0723, -0.1111]],
[[-0.0383, -0.0263, -0.1000],
[ 0.1271, -0.0439, 0.0261],
[-0.0635, -0.0281, 0.0110]],
[[-0.0581, -0.0064, -0.1025],
[ 0.0285, -0.1292, -0.0303],
[ 0.0677, -0.0345, -0.1033]],
[[ 0.0643, -0.0275, -0.0674],
[ 0.0137, -0.0164, -0.1115],
[-0.0091, -0.0434, 0.0677]],
[[ 0.0144, 0.0092, 0.0296],
[ 0.1309, 0.0381, 0.0780],
[-0.0869, 0.1254, -0.1286]]],
[[[ 0.1236, -0.0180, -0.0362],
[ 0.0333, -0.0795, 0.0484],
[-0.0631, 0.0950, -0.0140]],
[[-0.1278, -0.0398, -0.1113],
[ 0.0188, 0.0812, -0.0135],
[-0.1257, -0.0858, 0.0594]],
[[ 0.1007, -0.0060, -0.1020],
[ 0.0128, -0.0452, 0.1216],
[-0.0290, 0.1182, -0.0886]],
[[ 0.1055, -0.0874, 0.0793],
[-0.1203, 0.0965, -0.0028],
[-0.0659, -0.0722, -0.1219]],
[[ 0.1211, 0.1321, 0.0527],
[ 0.0980, 0.1168, -0.0974],
[ 0.1002, 0.1068, -0.1127]],
[[ 0.1175, 0.0850, -0.0614],
[-0.0260, -0.1330, -0.0162],
[ 0.0337, -0.1211, 0.1081]]],
[[[-0.0436, -0.0912, -0.0291],
[-0.0602, -0.0715, -0.0935],
[ 0.0571, 0.0199, -0.1161]],
[[ 0.0630, -0.0065, -0.0129],
[ 0.0259, -0.0638, -0.1152],
[ 0.1032, -0.0629, 0.1348]],
[[ 0.0283, 0.0265, 0.0824],
[ 0.0419, 0.1153, 0.0783],
[-0.0305, -0.0472, 0.1295]],
[[-0.0471, 0.0886, 0.0463],
[-0.1360, 0.1348, -0.0625],
[ 0.0093, -0.1020, -0.0061]],
[[ 0.0762, -0.0925, 0.0034],
[-0.0250, 0.1321, 0.0676],
[ 0.0202, 0.0511, 0.0705]],
[[ 0.0154, 0.0837, -0.1195],
[ 0.0962, -0.1176, -0.0937],
[ 0.0501, -0.0946, 0.0148]]],
[[[ 0.1006, -0.0208, -0.0947],
[ 0.0609, 0.0735, 0.0179],
[-0.0887, 0.0736, 0.0908]],
[[ 0.0615, 0.0844, 0.1163],
[ 0.0101, 0.1171, 0.0063],
[ 0.0951, -0.0245, 0.0926]],
[[ 0.0617, -0.1190, 0.0921],
[-0.0887, 0.0842, 0.0813],
[ 0.0695, -0.0479, -0.1015]],
[[-0.0942, 0.0839, -0.0549],
[-0.0322, 0.0255, 0.0583],
[-0.0235, -0.0031, -0.0904]],
[[ 0.0492, -0.1012, -0.1180],
[ 0.1149, -0.1011, 0.1090],
[-0.1046, 0.0475, 0.0074]],
[[ 0.1330, -0.0620, -0.0341],
[ 0.1162, -0.0675, 0.1036],
[-0.0829, 0.1141, 0.1092]]]], requires_grad=True)), ('features.4.bias', Parameter containing:
tensor([-0.1039, -0.0869, -0.1233, -0.0459, -0.1086, 0.0432, -0.1133, -0.0032,
-0.0533], requires_grad=True)), ('features.5.weight', Parameter containing:
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.], requires_grad=True)), ('features.5.bias', Parameter containing:
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)), ('classifier.0.weight', Parameter containing:
tensor([[-0.0251, -0.0396, -0.0007, ..., -0.0369, 0.0107, -0.0136],
[ 0.0282, 0.0286, -0.0107, ..., -0.0259, -0.0243, 0.0128],
[-0.0040, -0.0160, 0.0199, ..., 0.0042, 0.0202, 0.0083],
...,
[-0.0053, -0.0384, 0.0213, ..., 0.0212, 0.0064, 0.0371],
[-0.0165, 0.0144, 0.0372, ..., 0.0067, 0.0081, 0.0246],
[ 0.0252, 0.0121, 0.0270, ..., 0.0198, 0.0189, 0.0359]],
requires_grad=True)), ('classifier.0.bias', Parameter containing:
tensor([ 0.0270, -0.0172, -0.0298, -0.0216, 0.0406, 0.0319, -0.0077, -0.0330,
0.0081, 0.0351, -0.0081, -0.0159, -0.0218, 0.0039, 0.0179, -0.0331,
0.0327, -0.0105, -0.0353, 0.0173, -0.0245, 0.0340, -0.0339, 0.0197,
-0.0213, 0.0108, 0.0151, 0.0069, 0.0055, 0.0140, -0.0325, 0.0326,
-0.0298, 0.0407, -0.0258, -0.0060, -0.0400, 0.0373, -0.0285, 0.0070,
-0.0244, 0.0277, 0.0181, 0.0407, 0.0009, -0.0053, 0.0084, -0.0228,
-0.0253, -0.0039, -0.0155, 0.0268, 0.0145, -0.0323, 0.0054, -0.0212,
0.0382, 0.0414, 0.0335, 0.0016, 0.0216, 0.0190, -0.0323, -0.0201,
-0.0303, 0.0161, -0.0102, -0.0151, 0.0376, -0.0008, -0.0278, 0.0340,
0.0245, 0.0333, -0.0357, 0.0360, -0.0164, -0.0272, -0.0353, -0.0341,
0.0313, 0.0314, -0.0316, -0.0152, 0.0298, -0.0136, 0.0370, -0.0074,
-0.0113, -0.0387, -0.0400, -0.0066, -0.0038, 0.0089, 0.0039, 0.0406,
-0.0034, -0.0166, -0.0147, 0.0264, -0.0278, -0.0152, 0.0165, -0.0356,
0.0098, 0.0388, -0.0060, 0.0240, 0.0119, -0.0141, -0.0204, 0.0157,
-0.0112, 0.0071, -0.0315, -0.0159, -0.0276, -0.0006, -0.0177, -0.0044,
0.0107, -0.0113, -0.0066, -0.0123, -0.0133, 0.0377, -0.0155, -0.0191],
requires_grad=True)), ('classifier.3.weight', Parameter containing:
tensor([[-0.0437, 0.0575, -0.0551, ..., 0.0620, 0.0798, 0.0225],
[ 0.0187, -0.0032, 0.0537, ..., 0.0614, 0.0683, -0.0728],
[-0.0069, -0.0273, -0.0102, ..., 0.0648, 0.0244, 0.0425],
...,
[ 0.0728, 0.0716, -0.0784, ..., 0.0721, -0.0252, 0.0415],
[ 0.0329, -0.0779, 0.0010, ..., -0.0502, -0.0827, -0.0699],
[ 0.0164, 0.0267, 0.0574, ..., 0.0434, -0.0673, -0.0557]],
requires_grad=True)), ('classifier.3.bias', Parameter containing:
tensor([ 0.0429, 0.0720, 0.0268, 0.0733, 0.0722, 0.0473, -0.0661, -0.0637,
-0.0498, -0.0619], requires_grad=True))]
"""
"""
如果你是从前面看过来的,就会知道,这里就是迭代的返回带有名字的参数,
会给每个参数加上带有 .weight或 .bias的名字以区分权重和偏置:
"""
state_dicts = model.state_dict()
# print("state_dicts = ",state_dicts)
for k in state_dicts:
print("k = ",k," v = ",state_dicts[k])
"""
state_dicts = OrderedDict([('features.0.weight', tensor([[[[-0.1120, 0.1490, 0.1126],
[-0.1723, 0.1112, -0.1769],
[ 0.1773, 0.1917, 0.0526]],
[[-0.1734, 0.1511, 0.1650],
[ 0.0675, -0.0678, 0.0216],
[ 0.1885, 0.1664, -0.1246]],
[[ 0.1417, 0.1417, 0.0409],
[-0.0858, -0.0144, -0.1439],
[ 0.0473, 0.0736, -0.0034]]],
[[[ 0.1588, -0.0245, 0.0427],
[ 0.0336, -0.0181, 0.1024],
[ 0.0696, -0.1315, -0.0653]],
[[-0.0196, -0.1345, -0.1009],
[-0.1496, -0.0704, 0.1776],
[ 0.0123, -0.0073, 0.0899]],
[[ 0.0317, -0.1059, -0.1572],
[-0.0766, -0.1680, 0.0808],
[-0.1485, 0.1683, 0.0383]]],
[[[-0.0897, -0.0250, 0.0031],
[ 0.0386, 0.1260, -0.0847],
[-0.0467, -0.0684, -0.1173]],
[[-0.1226, 0.1384, 0.0689],
[ 0.1758, -0.1891, -0.1873],
[ 0.1391, 0.0112, -0.1604]],
[[-0.0956, 0.1639, 0.1126],
[ 0.0309, -0.1016, -0.0393],
[-0.0270, -0.0333, -0.1562]]],
[[[ 0.0270, 0.0296, 0.0771],
[-0.0868, -0.1124, -0.0439],
[ 0.1779, -0.0512, 0.0108]],
[[ 0.1261, 0.0209, 0.1148],
[ 0.1882, -0.0144, -0.1624],
[-0.0143, 0.1711, 0.0430]],
[[ 0.1367, -0.0869, -0.1644],
[ 0.1404, -0.0255, 0.1486],
[-0.0415, -0.0261, 0.0015]]],
[[[-0.0011, 0.0968, 0.0957],
[-0.1461, -0.0302, -0.1816],
[-0.0779, 0.0440, -0.0650]],
[[ 0.1433, 0.1455, -0.0765],
[ 0.0740, 0.0872, -0.0867],
[-0.0946, -0.0586, -0.1586]],
[[-0.1641, -0.1063, 0.0597],
[ 0.0933, -0.1327, 0.1658],
[-0.0651, -0.0454, -0.0592]]],
[[[-0.1577, 0.0530, -0.1889],
[-0.0094, 0.0694, -0.0912],
[ 0.0916, 0.1582, 0.0685]],
[[-0.0960, 0.0386, 0.0987],
[ 0.0258, -0.1619, 0.0385],
[-0.0475, 0.0387, 0.0350]],
[[ 0.0571, -0.0815, -0.1121],
[ 0.0839, 0.1473, 0.0897],
[-0.0518, -0.0513, -0.1895]]]])), ('features.0.bias', tensor([ 0.1110, 0.1084, -0.1312, 0.0584, -0.0216, 0.0522])), ('features.1.weight', tensor([1., 1., 1., 1., 1., 1.])), ('features.1.bias', tensor([0., 0., 0., 0., 0., 0.])), ('features.1.running_mean', tensor([0., 0., 0., 0., 0., 0.])), ('features.1.running_var', tensor([1., 1., 1., 1., 1., 1.])), ('features.1.num_batches_tracked', tensor(0)), ('features.4.weight', tensor([[[[ 1.2339e-01, 3.6456e-02, -8.1264e-02],
[ 7.2834e-02, 7.7740e-02, -8.1412e-02],
[-1.3442e-01, -9.6639e-02, -8.1537e-02]],
[[-7.9242e-02, -1.6356e-02, -2.0175e-02],
[ 6.3926e-02, 4.6489e-02, -4.7286e-02],
[-4.3791e-02, 1.2156e-02, -3.7545e-02]],
[[ 8.3428e-02, -2.5146e-02, 1.3393e-01],
[-8.8177e-02, -9.2821e-02, 4.6915e-03],
[ 3.7843e-02, 1.1423e-01, 2.4096e-02]],
[[ 1.2078e-01, 6.6416e-02, 1.0651e-01],
[ 5.8561e-02, -9.9513e-02, -7.7436e-02],
[ 6.5392e-02, -3.4283e-02, -1.0200e-01]],
[[ 2.9786e-02, -6.0475e-02, -8.6477e-02],
[ 3.8416e-02, 4.0239e-02, 1.0487e-01],
[ 1.2980e-02, 4.3307e-02, -1.2445e-02]],
[[-1.0675e-01, -1.3109e-04, -1.1331e-01],
[-1.2819e-01, -6.0414e-02, -9.1673e-02],
[ 2.8632e-02, 4.8303e-02, -4.8784e-02]]],
[[[-2.0327e-02, -7.4646e-02, 1.1771e-01],
[-2.3229e-02, -1.3199e-01, 6.3986e-02],
[ 1.0935e-01, -7.1119e-02, -6.8069e-03]],
[[ 4.2655e-02, -4.0028e-02, 5.8079e-02],
[-2.8356e-02, -8.9192e-02, 5.8191e-02],
[-1.1162e-01, 1.3061e-01, -1.3425e-02]],
[[ 2.9063e-02, -1.1770e-01, -1.1862e-01],
[ 7.7019e-02, -1.1078e-01, -4.1114e-02],
[-6.0020e-02, -9.5343e-02, -9.3102e-02]],
[[-1.3583e-01, -8.4821e-02, 1.1639e-01],
[-4.4931e-02, -2.6818e-03, -1.3142e-01],
[-1.3248e-01, 3.5738e-02, 6.3704e-02]],
[[-9.5148e-02, -5.4758e-02, -8.5132e-02],
[ 5.9926e-02, -9.9971e-03, -1.2893e-01],
[-1.6311e-02, -1.6571e-02, -9.0908e-02]],
[[-1.2931e-01, -1.0908e-01, -8.0463e-02],
[-8.5217e-02, 9.4868e-02, -4.9266e-03],
[ 1.0161e-01, -1.2998e-01, -1.0456e-01]]],
[[[ 1.3086e-01, -1.0726e-01, -3.0973e-02],
[ 7.5602e-03, 4.0852e-03, -5.7365e-02],
[ 9.6861e-02, 5.1707e-03, 1.0884e-01]],
[[-6.5375e-02, 1.2043e-01, -6.0518e-02],
[ 9.7843e-02, -5.3584e-02, -4.3394e-02],
[-1.0804e-01, -7.3304e-02, -1.1025e-01]],
[[ 3.9323e-02, 4.3915e-02, -1.1270e-01],
[ 8.8957e-02, 3.9222e-02, 1.1955e-01],
[-5.8752e-02, -7.9406e-02, 8.1920e-02]],
[[-1.4651e-02, 1.2826e-01, -7.3560e-02],
[-6.7479e-03, 1.1927e-01, 9.5392e-03],
[-1.3190e-01, 9.2025e-02, 7.7550e-02]],
[[ 2.1855e-02, 2.6536e-02, -8.8161e-02],
[ 1.0861e-01, -1.8487e-02, 8.6501e-02],
[-8.3951e-02, -1.0361e-01, 4.6002e-03]],
[[-7.4299e-02, -6.3769e-02, 2.5605e-02],
[ 9.6181e-02, 1.2735e-01, 1.2609e-01],
[-7.3185e-02, -4.5585e-02, 6.2157e-02]]],
[[[ 2.6107e-02, -1.1989e-01, -8.6688e-02],
[ 8.6597e-02, -4.4664e-02, 3.2112e-02],
[-1.0814e-01, -7.2253e-02, -7.4629e-02]],
[[-3.9608e-02, -7.4466e-02, -1.2369e-01],
[ 6.2269e-02, -5.3591e-02, 1.0797e-01],
[ 5.8163e-02, -1.3221e-01, 1.0001e-01]],
[[ 9.5225e-02, 1.1649e-01, -7.3289e-03],
[-1.2376e-01, -2.4160e-02, -7.7053e-02],
[ 1.1293e-01, 3.0768e-02, -1.2418e-01]],
[[-5.3918e-02, -9.2364e-02, -1.0816e-02],
[-2.0389e-03, -1.0363e-01, 6.8401e-02],
[-6.7831e-02, 1.1953e-01, 4.0812e-02]],
[[-1.1983e-01, 8.5739e-02, -2.8247e-02],
[-8.8285e-03, -1.4419e-02, 6.0807e-02],
[-1.0024e-02, 1.0961e-01, -4.1463e-02]],
[[-6.1104e-02, 1.1612e-01, -1.0832e-01],
[-2.8441e-02, 3.5046e-02, 7.0129e-03],
[-1.4531e-02, 1.3536e-02, -1.2058e-01]]],
[[[ 5.5127e-02, -3.7674e-02, -3.0895e-03],
[ 7.4091e-02, 3.5885e-02, -3.1326e-03],
[ 9.0708e-02, -1.1664e-01, 3.1442e-02]],
[[-1.1080e-01, 1.2293e-01, 5.3925e-02],
[ 4.7981e-02, 1.1144e-01, -1.1489e-01],
[-1.2803e-01, 2.7881e-02, 1.3296e-01]],
[[-1.2624e-01, -6.5688e-02, 3.5976e-02],
[-3.0632e-02, -7.7391e-02, -6.7752e-02],
[ 5.9872e-02, 6.2385e-02, -4.8133e-02]],
[[-9.4431e-02, -6.3459e-02, 9.0059e-02],
[-3.1814e-02, -8.3559e-03, -1.9724e-02],
[ 7.4525e-02, 3.5408e-02, 1.3036e-01]],
[[ 1.1507e-01, 1.9809e-02, -1.1319e-01],
[-6.8824e-02, -9.7140e-02, 2.5071e-02],
[ 5.1618e-02, -1.3364e-01, 1.2377e-01]],
[[-1.6939e-03, 2.5055e-04, 1.6423e-02],
[ 1.1560e-01, 8.5741e-02, -9.4489e-02],
[-7.9593e-02, 4.4569e-02, -4.4471e-02]]],
[[[ 7.8615e-02, 4.1079e-02, -5.3803e-02],
[ 1.7017e-02, 5.2484e-02, -1.4643e-02],
[-5.3499e-02, 1.0117e-01, -1.2375e-01]],
[[-1.2176e-03, 2.8521e-02, -8.7684e-02],
[ 5.2779e-02, 1.2218e-01, 7.8400e-02],
[-1.0264e-01, 3.2854e-02, 1.0255e-01]],
[[-6.4508e-02, -4.0940e-02, 1.0328e-01],
[ 6.1570e-02, -4.1444e-02, 1.0093e-01],
[-1.1605e-01, 1.2389e-01, -1.3214e-02]],
[[-9.5283e-02, 2.7124e-02, 2.2692e-02],
[-1.2656e-01, 5.7397e-02, -1.3197e-01],
[ 9.7042e-02, -7.5878e-02, 5.2598e-02]],
[[-6.0347e-02, 3.4859e-02, 1.1488e-01],
[ 7.9926e-02, 6.6939e-02, 1.1487e-01],
[-3.0942e-03, 9.8462e-02, -3.0900e-02]],
[[ 9.5505e-02, -1.2781e-01, 4.7567e-02],
[-9.1119e-02, 9.7449e-02, 6.4211e-02],
[-6.0627e-02, -4.7606e-02, 1.0843e-01]]],
[[[-1.3378e-01, 9.7107e-02, -1.3604e-01],
[ 6.7992e-02, -4.7438e-02, 6.3659e-03],
[-1.1243e-01, -8.3477e-02, 5.0870e-02]],
[[-2.9983e-02, 9.8946e-03, 9.8673e-03],
[-4.2389e-02, 8.9317e-02, -7.8672e-02],
[-3.4808e-02, -9.8673e-02, -8.0076e-02]],
[[ 1.2528e-01, 9.5739e-02, 1.2877e-01],
[ 5.6588e-02, 4.7093e-02, 9.6125e-02],
[ 1.7334e-02, -7.8798e-02, 7.2877e-02]],
[[ 8.8860e-02, -1.2521e-01, 3.0678e-02],
[-9.4911e-02, -3.0096e-02, -1.2500e-01],
[-9.8543e-02, -6.1643e-02, -8.5940e-02]],
[[ 1.1547e-01, -7.9756e-02, 3.6319e-02],
[-5.8380e-02, 1.0619e-01, 9.0940e-02],
[-1.3119e-01, 4.8893e-02, -8.5568e-02]],
[[ 4.0550e-02, -8.9834e-02, 1.1359e-01],
[ 1.2870e-01, 1.3285e-01, -1.0320e-01],
[-3.2039e-02, -7.4832e-02, 3.7275e-02]]],
[[[-8.3612e-02, -1.1438e-01, -8.3008e-02],
[-3.7290e-02, -5.1211e-03, -1.6722e-02],
[-2.3919e-02, -8.4471e-02, 1.1062e-01]],
[[-4.9339e-02, -1.2613e-01, -2.4665e-02],
[-1.3429e-01, 7.2360e-02, 6.5418e-03],
[-4.8754e-02, -3.2074e-02, -6.0722e-02]],
[[-1.2371e-01, 1.1780e-01, -4.6866e-02],
[-1.3454e-01, -5.8887e-02, 6.1087e-02],
[ 7.6493e-02, -1.7035e-02, -6.2266e-02]],
[[-6.8178e-03, 3.6235e-02, -3.7017e-02],
[ 9.0602e-02, -1.5369e-02, -1.0495e-01],
[-7.2129e-02, -7.3497e-02, 7.6392e-02]],
[[ 2.3970e-02, 5.4974e-02, -1.3256e-01],
[-1.2958e-02, -2.9312e-02, -2.8393e-02],
[ 1.4545e-02, -5.8900e-02, 1.2764e-01]],
[[-3.1440e-02, 1.2754e-01, 1.2308e-01],
[ 3.4903e-02, -9.2762e-03, 9.8317e-02],
[-1.0416e-01, 3.6727e-02, 7.9707e-02]]],
[[[-2.8089e-02, 3.5733e-02, 1.0008e-01],
[ 9.6592e-02, 2.3322e-02, -8.9483e-02],
[ 9.1346e-02, -1.0593e-01, -1.2364e-01]],
[[-2.4829e-02, 2.0654e-02, 1.2911e-01],
[-5.9471e-02, -1.2641e-01, -1.1271e-01],
[ 6.1054e-02, 7.2971e-02, -1.0478e-02]],
[[ 1.1822e-01, 1.3285e-01, 1.8757e-02],
[ 3.6996e-02, 1.4597e-02, 8.0678e-03],
[-4.3496e-02, 1.0534e-01, -9.5338e-02]],
[[-1.0122e-01, -1.0457e-01, -8.6996e-02],
[-6.1046e-02, 3.1326e-02, -9.7862e-02],
[ 1.2714e-01, 8.0922e-03, -3.3908e-02]],
[[ 1.2773e-01, 9.3679e-02, 1.2142e-01],
[ 2.2865e-03, 1.4928e-02, 9.2772e-02],
[ 7.2978e-02, 7.3142e-02, 1.6641e-02]],
[[-4.9320e-02, -3.8339e-02, -2.3730e-02],
[ 1.0526e-02, -9.2967e-02, 7.3995e-02],
[ 7.5536e-02, -1.2523e-01, 7.5331e-02]]]])), ('features.4.bias', tensor([-0.0471, -0.1163, 0.0442, 0.0052, -0.1213, -0.0217, -0.1029, 0.1210,
0.1244])), ('features.5.weight', tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.])), ('features.5.bias', tensor([0., 0., 0., 0., 0., 0., 0., 0., 0.])), ('features.5.running_mean', tensor([0., 0., 0., 0., 0., 0., 0., 0., 0.])), ('features.5.running_var', tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.])), ('features.5.num_batches_tracked', tensor(0)), ('classifier.0.weight', tensor([[ 0.0054, -0.0252, -0.0105, ..., -0.0386, -0.0031, 0.0218],
[ 0.0310, 0.0202, -0.0034, ..., -0.0405, -0.0397, -0.0158],
[-0.0167, 0.0291, -0.0330, ..., -0.0380, -0.0029, -0.0250],
...,
[-0.0209, -0.0130, 0.0205, ..., 0.0202, -0.0303, 0.0271],
[ 0.0375, 0.0171, -0.0188, ..., -0.0071, -0.0027, -0.0409],
[-0.0195, 0.0331, -0.0208, ..., -0.0315, -0.0402, -0.0378]])), ('classifier.0.bias', tensor([-0.0357, 0.0414, 0.0168, 0.0020, -0.0358, 0.0363, -0.0172, 0.0374,
-0.0262, -0.0026, -0.0184, -0.0262, -0.0356, 0.0320, 0.0306, -0.0274,
0.0261, -0.0042, 0.0020, -0.0057, -0.0002, -0.0197, -0.0188, 0.0198,
0.0105, 0.0285, -0.0049, 0.0176, -0.0350, -0.0023, 0.0149, 0.0241,
-0.0196, -0.0148, -0.0288, -0.0099, -0.0151, 0.0315, 0.0246, -0.0105,
0.0301, -0.0217, -0.0007, 0.0135, 0.0186, -0.0270, 0.0318, 0.0026,
-0.0329, 0.0004, 0.0054, 0.0364, 0.0163, -0.0150, -0.0219, -0.0113,
-0.0397, 0.0017, -0.0066, 0.0045, -0.0307, 0.0153, 0.0167, 0.0280,
-0.0140, -0.0340, 0.0021, -0.0027, -0.0086, 0.0369, -0.0229, -0.0376,
-0.0089, -0.0384, -0.0181, 0.0335, -0.0268, 0.0130, 0.0240, -0.0388,
-0.0313, -0.0260, -0.0389, 0.0115, 0.0333, -0.0295, -0.0092, -0.0057,
0.0355, 0.0345, -0.0379, -0.0375, 0.0377, 0.0177, -0.0389, -0.0124,
-0.0376, -0.0093, -0.0270, 0.0220, -0.0022, -0.0386, -0.0203, 0.0345,
-0.0097, -0.0409, -0.0397, -0.0049, 0.0060, 0.0305, -0.0157, 0.0406,
-0.0403, -0.0235, -0.0398, -0.0116, -0.0246, 0.0041, -0.0411, 0.0082,
-0.0354, -0.0057, -0.0049, -0.0119, -0.0055, -0.0138, -0.0077, -0.0232])), ('classifier.3.weight', tensor([[ 0.0234, 0.0381, -0.0847, ..., 0.0811, -0.0137, 0.0406],
[ 0.0360, -0.0053, -0.0497, ..., 0.0074, 0.0563, 0.0491],
[ 0.0707, -0.0768, -0.0591, ..., -0.0193, -0.0729, 0.0814],
...,
[ 0.0557, -0.0453, 0.0463, ..., -0.0796, 0.0152, 0.0038],
[ 0.0278, 0.0081, -0.0659, ..., 0.0462, 0.0299, 0.0805],
[ 0.0032, 0.0169, 0.0535, ..., -0.0221, 0.0754, -0.0403]])), ('classifier.3.bias', tensor([-0.0858, 0.0056, -0.0493, -0.0573, 0.0795, -0.0510, -0.0379, -0.0851,
-0.0169, -0.0630]))])
"""
"""
model.state_dict()直接返回模型的字典,和前面几个方法不同的是这里不需要迭代,
它本身就是一个字典,可以直接通过修改state_dict来修改模型各层的参数,用于参数剪枝特别方便。
"""
print("finish..........")
2.PyTorch模型保存深入理解
"""
Pytorch模型保存与加载,并在加载的模型基础上继续训练
pytorch保存模型非常简单,主要有两种方法:
1.只保存参数(官方推荐)
2.保存整个模型(结构+参数)
由于保存整个模型将耗费大量的存储,故官方推荐只保存参数,然后在建好模型的基础上加载。
"""
import os
import torch
# 一、只保存参数
# 1.保存
# 采用一条语句即可保存参数
# torch.save(obj=model.state_dict(),f=path)
# 其中model指定义的模型实例变量,如 model=vgg16( ), path是保存参数的路径,
# 如 path='./model.pth' , path='./model.tar', path='./model.pkl', 保存参数的文件一定要有后缀扩展名。
# 保存参数的文件一定要有后缀扩展名!!!!!!!!!!!!
# 特别地,如果想保存某一次采用的优化器,epoch等信息
# 可以将这些信息组合起来构成一个字典,然后将字典保存起来
# state = {'model':model.state_dict(),'optimizer':optimizer.state_dict(),"epoch":epoch}
# torch.save(state,path)
# 2.加载
# 针对上述第一种情况,也需要一句即可加载模型:
# model.load_state_dict(torch.load(path))
# 针对上述第二种以字典形式保存的方法,加载方式 如下:
# checkpoint = torch.load(path)
# model.load_state_dict(checkpoint['model'])
# optimizer.load_state_dict(checkpoint['optimizer'])
# epoch = checkpoint(['epoch'])
"""
需要注意的是,只保存参数的方法在加载的时候要事先定义好跟原模型一致的模型,
并在该模型的实例对象(假设名为model)上进行加载,
即在使用上述加载语句前已经有定义了一个和原模型一样的Net, 并且进行了实例化 model=Net( ) 。
"""
"""
另外,如果每一个epoch或每n个epoch都要保存一次参数,可设置不同的path,
如 path='./model' + str(epoch) +'.pth',这样,不同epoch的参数就能保存在不同的文件中,
选择保存识别率最大的模型参数也一样,只需在保存模型语句前加个if判断语句即可。
"""
# 下面给出一个具体的例子程序,该程序只保存最新的参数:
import torch as torch
import torchvision as tv
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage
import torch.backends.cudnn as cudnn
import datetime
import argparse
# 参数声明
batch_size = 32
epochs = 10
WORKERS = 0 # dataloder线程数
test_flag = False # 测试标志,True时加载保存好的模型进行测试
ROOT = './cifar' # MNIST数据集保存路径
log_dir = './logs/cifar_model.pth' # 模型保存路径
# 加载MNIST数据集
transform = tv.transforms.Compose([
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
train_data = tv.datasets.CIFAR10(root=ROOT, train=True, download=True, transform=transform)
test_data = tv.datasets.CIFAR10(root=ROOT, train=False, download=False, transform=transform)
train_load = torch.utils.data.DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=WORKERS)
test_load = torch.utils.data.DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=WORKERS)
# 构造模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 64, 3, padding=1)
self.conv2 = nn.Conv2d(64, 128, 3, padding=1)
self.conv3 = nn.Conv2d(128, 256, 3, padding=1)
self.conv4 = nn.Conv2d(256, 256, 3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(256 * 8 * 8, 1024)
self.fc2 = nn.Linear(1024, 256)
self.fc3 = nn.Linear(256, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(F.relu(self.conv2(x)))
x = F.relu(self.conv3(x))
x = self.pool(F.relu(self.conv4(x)))
x = x.view(-1, x.size()[1] * x.size()[2] * x.size()[3])
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
model = Net().cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 模型训练
def train(model, train_loader, epoch):
model.train()
train_loss = 0
for i, data in enumerate(train_loader, 0):
x, y = data
x = x.cuda()
y = y.cuda()
optimizer.zero_grad()
y_hat = model(x)
loss = criterion(y_hat, y)
loss.backward()
optimizer.step()
train_loss += loss
loss_mean = train_loss / (i + 1)
print('Train Epoch: {}\t Loss: {:.6f}'.format(epoch, loss_mean.item()))
# 模型测试
def test(model, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for i, data in enumerate(test_loader, 0):
x, y = data
x = x.cuda()
y = y.cuda()
optimizer.zero_grad()
y_hat = model(x)
test_loss += criterion(y_hat, y).item()
pred = y_hat.max(1, keepdim=True)[1]
correct += pred.eq(y.view_as(pred)).sum().item()
test_loss /= (i + 1)
print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_data), 100. * correct / len(test_data)))
def main(epochs):
# 如果test_flag=True,则加载已保存的模型
if test_flag:
# 加载保存的模型直接进行测试机验证,不进行此模块以后的步骤
checkpoint = torch.load(log_dir)
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
epochs = checkpoint['epoch']
test(model, test_load)
return
for epoch in range(0, epochs):
train(model, train_load, epoch)
test(model, test_load)
# 保存模型
state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch}
torch.save(state, log_dir)
if __name__ == '__main__':
# main(epochs)
pass
"""
3.在加载的模型基础上继续训练
在训练模型的时候可能会因为一些问题导致程序中断,或者常常需要观察训练情况的变化来更改学习率等参数,这时候就需要加载中断前保存的模型,
并在此基础上继续训练,这时候只需要对上例中的 main() 函数做相应的修改即可,修改后的 main() 函数如下:
"""
def main():
# 如果test_flag=True,则加载已保存的模型
if test_flag:
# 加载保存的模型直接进行测试机验证,不进行此模块以后的步骤
checkpoint = torch.load(log_dir)
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']
test(model, test_load)
return
# 如果有保存的模型,则加载模型,并在其基础上继续训练
if os.path.exists(log_dir):
checkpoint = torch.load(log_dir)
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']
print('加载 epoch {} 成功!'.format(start_epoch))
else:
start_epoch = 0
print('无保存模型,将从头开始训练!')
for epoch in range(start_epoch+1, epochs):
train(model, train_load, epoch)
test(model, test_load)
# 保存模型
state = {'model':model.state_dict(), 'optimizer':optimizer.state_dict(), 'epoch':epoch}
torch.save(state, log_dir)
# 保存整个模型
# 1.保存
# torch.save(model,path)
# 2.加载
# model = torch.load(path)
# model.state_dict()
# 在PyTorch中,state_dict是一个从参数名称隐射到参数Tesnor的字典对象.
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.hidden = nn.Linear(3,2)
self.act = nn.ReLU()
self.output = nn.Linear(2,1)
def forward(self, x):
a = self.act(self.hidden(x))
return self.output(a)
net = MLP()
"""
net.state_dict() = OrderedDict([('hidden.weight', tensor([[ 0.4356, 0.2110, 0.1025],
[ 0.2040, -0.1028, 0.5749]])), ('hidden.bias', tensor([-0.1565, 0.1508])),
('output.weight', tensor([[ 0.4035, -0.2680]])), ('output.bias', tensor([-0.3705]))])
"""
print("net.state_dict() = ",net.state_dict())
"""
可以看出,state_dict()返回的是一个有序字典,
该字典的键即为模型定义中有参数的层的名称+weight或+bias,
值则对应相应的权重或偏差,无参数的层则不在其中。
"""
# model.load_state_dict()
"""
这是模型加载state_dict的语句,也就是说,它的输入是一个state_dict,
也就是一个字典。模型定义好并且实例化后会自动进行初始化,
上面的例子中我们定义的模型MLP在实例化以后显示的模型参数都是自动初始化后的随机数。
在训练模型或者迁移学习中我们会使用已经训练好的参数来加速训练过程,
这时候就用load_state_dict()语句加载训练好的参数并将其覆盖在初始化参数上,
也就是说执行过此语句后,加载的参数将代替原有的模型参数。
既然加载的是一个字典,那么需要注意的就是字典的键一定要相同才能进行覆盖,
比如加载的字典中的'hidden.weight'只能覆盖当前模型的'hidden.weight',如果键不同,
则不能实现有效覆盖操作。键相同而值的shape不同,则会将新的键值对覆盖原来的键值对,
这样在训练时会报错。所以我们在加载前一般会进行数据筛选,筛选是对字典的键进行对比来操作的:
"""
pretrained_dict = torch.load(log_dir) # 加载参数字典
model_state_dict = model.state_dict() # 加载模型当前状态字典
pretrained_dict_1 = {k:v for k,v in pretrained_dict.items() if k in model_state_dict} # 过滤出模型当前状态字典中没有的键值对
model_state_dict.update(pretrained_dict_1) # 用筛选出的参数键值对更新model_state_dict变量
model.load_state_dict(model_state_dict) # 将筛选出的参数键值对加载到模型当前状态字典中
"""
以上代码简单的对预训练参数进行了过滤和筛选,主要是通过第3条语句粗略的过滤了键值对信息,
进行筛选后要用Python更新字典的方法update()来对模型当前字典进行更新,
update()方法将pretrained_dict_1中的键值对添加到model_state_dict中,
若pretrained_dict_1中的键名和model_state_dict中的键名相同,则覆盖之;
若不同,则作为新增键值对添加到model_state_dict中。显然,
这里需要的是将pretrained_dict_1中的键值对覆盖model_state_dict的相应键值对,
所以对应的键的名称必须相同,所以第3条语句中按键名称进行筛选,过滤出当前模型字典中没有的键值对。否则会报错。
"""
3.学习率调整策略
"""
Pytorch中的学习率衰减及其用法
学习率衰减是一个非常有效的炼丹技巧之一,在神经网络的训练过程中,
当accuracy出现震荡或loss不再下降时,进行适当的学习率衰减是一个
行之有效的手段,很多时候明显提高accuracy。
"""
# Pytorch中两种学习率调整(衰减)方法:
# 1.使用库函数进行调整
# 2.手动调整
# 1.使用库函数进行调整
import torch
"""
Pytorch学习率调整策略通过torch.optim.lr_scheduler接口实现。
pytorch提供的学习率调整策略分为三大类别,分别是:
(1)有序调整:等间隔调整(Step),多间隔调整(MultiStep),指数衰减(Exponential),
余弦退火(CosineAnneanling)
(2)自适应调整:依训练状况伺机而变,通过监测某个指标的变化情况(loss、accuracy),
当该指标不怎么变化时,就是调整学习率的时机(ReduceLROnPlateau);
(3)自定义调整:通过自定义关于epoch的lambda函数调整学习率(LambdaLR)
在每个epoch的训练中,使用scheduler.step()语句进行学习率更新,此方法类似于
optimizer.step()更新模型参数,即一次epoch对应一次schedule.step()。但在mini-batch训练中,
每个mini-batch对应一个optimizer.step()。即用法如下:
"""
# optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
# scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=30,gamma=0.1)
# def train(...):
# for i,data in enumerate(train_dataloader):
# ......
# y_ = model(x)
# loss = criterion(y_,y)
# loss.backward()
# optimizer.step()
# ......
# for epoch in range(epochs):
# train(...)
# test(....)
# scheduler.step()
# (1) 等间隔调整学习率StepLR
# torch.optim.lr_scheduler.StepLR(optimizer,step_size,gamma=0.1,last_epoch=-1)
"""
每训练step_size个epoch,学习率调整为lr=lr*gamma
以下内容都将epoch和step对等,因为每个epoch中只进行一次scheduler.step(),实则
该step指scheduler.step()中的step,即step_size指scheduler.step()进行的次数。
函数中参数的含义:
optimizer:神经网络训练中使用的优化器,如optimizer = torch.optim.SGD(...)
step_size(int):学习率下降间隔,单位epoch,而不是iteration
gamma(float):学习率调整倍数,默认为0.1
last_epoch(int):上一个epoch数,这个变量用来指示学习率是否需要调整。
当last_epoch符合设定的间隔时,就会对学习率进行调整,当为-1时,学习率设置为初始值。
"""
等间隔调整学习率:step_size=30, gamma=0.1
# (4)余弦退火函数调整学习率
"""
学习率呈余弦函数型衰减,并以2*T_max为余弦函数周期,epoch=0对应余弦型学习率调整曲线的
x = 0,ymax = lr; epoch=T_max对应余弦型学习率调整曲线的x=π,ymin = eta_min处,
随着epoch > T_max,学习率随epoch增大逐渐上升,整个趋势同cos(x)。
torch.optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max,esta_min,last_epoch=-1)
参数:
T_max(int):学习率下降到最小值时的epoch数,即当epoch=T_max时,学习率降到余弦函数最小值,
当epoch > T_max时,学习率将增大
eta_min:学习率调整的最小值,即epoch=T_max时,lrmin = eta_min,默认为0
其它参数同上
"""
学习率余弦衰减:T_max=100
# 根据指标调整学习率ReduceLROnPlateau
"""
当某个指标(loss或accuracy)在最近几个epoch中都没有变化(下降或上升超过给定的阈值)时
调整学习率。
如当验证集的loss不再下降时,调整学习率,或验证集的accuracy不再升高时,调整学习率。
torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer,mode='min',factor=0.1,patience=10,
verbose=False,threshold=0.0001,threshold_mode='rel',
cooldown=0,min_lr=0,eps=1e-8)
参数:
mode(str): 模式选择,有min和max两种模式,min表示当指标不再降低(如监测loss),max表示当指标不再升高(如监测accuracy)。
factor(float): 学习率调整倍数,同前面的gamma,当监测指标达到要求时,lr=lr×factor。
patience(int): 忍受该指标多少个epoch不变化,当忍无可忍时,调整学习率。
verbose(bool): 是否打印学习率信息,print( 'Epoch {:5d} reducing learning rate of group {} to {:.4e}.'.format(epoch, i, new_lr), 默认为False, 即不打印该信息。
threshold_mode (str): 选择判断指标是否达最优的模式,有两种模式:rel 和 abs.
当threshold_mode == rel, 并且 mode == max时,dynamic_threshold = best * (1 + threshold);
当threshold_mode == rel, 并且 mode == min时,dynamic_threshold = best * (1 - threshold);
当threshold_mode == abs, 并且 mode == max时,dynamic_threshold = best + threshold;
当threshold_mode == abs, 并且 mode == min时,dynamic_threshold = best - threshold;
threshold(float): 配合threshold_mode使用。
cooldown(int): “冷却时间”,当调整学习率之后,让学习率调整策略冷静一下,让模型在训练一段时间,再重启监测模式。
min_lr(float or list): 学习率下限,可为float,或者list,当有多个参数组时,可用list进行设置。
eps(float): 学习率衰减的最小值,当学习率的变化值小于eps时,则不调整学习率。
"""
# optimizer = torch.optim.SGD(model.parameters(), args.lr,
# momentum=args.momentum, weight_decay=args.weight_decay)
#
# scheduler = ReducelROnPlateau(optimizer,'min')
# for epoch in range( args.start epoch, args.epochs ):
# train(train_loader , model, criterion, optimizer, epoch )
# result_avg, loss_val = validate(val_loader, model, criterion, epoch)
# # Note that step should be called after validate()
# scheduler.step(loss_val )
# (6)自定义学习率LambdaLR
# 为不同参数组设定不同学习率调整策略.调整规则为:
# lr = base_lr * lambda(self.last_epoch)
# 在fine-tune中特别有用,我们不仅可以为不同层设置不同学习率,
# 还可以为不同层设置不同学习率调整策略。
# torch.optim.lr_scheduler.LambdaLR(optimizer,lr_lambda,last_epoch=-1)
"""
lr_lambda(function or list): 自定义计算学习率调整倍数的函数,通常时epoch的函数,
当有多个参数组时,设为list.
其它参数同上。
例:
"""
"""
ignored_params = list(map(id, net.fc3.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params, net.parameters())
optimizer = optim.SGD([
{'params': base_params},
{'params': net.fc3.parameters(), 'lr': 0.001*100}], 0.001, momentum=0.9,weight_decay=1e-4)
# Assuming optimizer has two groups.
lambda1 = lambda epoch: epoch // 3
lambda2 = lambda epoch: 0.95 ** epoch
scheduler = LambdaLR(optimizer, lr_lambda=[lambda1, lambda2])
for epoch in range(100):
train(...)
validate(...)
scheduler.step()
print('epoch: ', i, 'lr: ', scheduler.get_lr())
输出:
epoch: 0 lr: [0.0, 0.1]
epoch: 1 lr: [0.0, 0.095]
epoch: 2 lr: [0.0, 0.09025]
epoch: 3 lr: [0.001, 0.0857375]
epoch: 4 lr: [0.001, 0.081450625]
epoch: 5 lr: [0.001, 0.07737809374999999]
epoch: 6 lr: [0.002, 0.07350918906249998]
epoch: 7 lr: [0.002, 0.06983372960937498]
epoch: 8 lr: [0.002, 0.06634204312890622]
epoch: 9 lr: [0.003, 0.0630249409724609]
为什么第一个参数组的学习率会是 0 呢? 来看看学习率是如何计算的。
第一个参数组的初始学习率设置为 0.001,
lambda1 = lambda epoch: epoch // 3,
第 1 个 epoch 时,由 lr = base_lr * lmbda(self.last_epoch),
可知道 lr = 0.001 *(0//3) ,又因为 1//3 等于 0,所以导致学习率为 0。
第二个参数组的学习率变化,就很容易看啦,初始为 0.1, lr = 0.1 * 0.95^epoch ,当
epoch 为 0 时, lr=0.1 , epoch 为 1 时, lr=0.1*0.95。
"""
# 给出画上述学习率变化图的程序
from torchvision.models import AlexNet
import matplotlib.pyplot as plt
# 模型
model = AlexNet(num_classes=2)
# 优化器
optimizer = torch.optim.SGD(params=model.parameters(),lr=0.1)
# 等间隔学习率,每训练step_size个epoch,lr*gamma
# scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=30,gamma=0.1)
# 多间隔调整学习率,每训练至milestones中的epoch,lr*gamma
# scheduler = lr_scheduler.MultiStepLR(optimizer,milestones=[10,30,80],gamma=0.1)
# 指数学习率衰减,lr*gamma**epoch
# scheduler = lr_scheduler.ExponentialLR(optimizer,gamma=0.9)
# 余弦退火学习率衰减,T_max表示半个周期,lr的初始值作为余弦函数0处的极大值逐渐开始下降,
# 在epoch=T_max时lr降至最小值,即pi/2处,然后进入后半个周期,lr增大
scheduler = lr_scheduler.CosineAnnealingLR(optimizer,T_max=100,eta_min=0.05)
plt.figure()
x = list(range(100))
y = []
for epoch in range(100):
scheduler.step()
y.append(scheduler.get_lr()[0])
plt.plot(x,y)
plt.show()
# 2.手动调整学习率
# 手动调整学习率,通常可以定义如下函数:
def adjust_learning_rate(optimizer,epoch):
"""Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
lr = args.lr * (0.1 ** (epoch // 30))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
# 又如
def adjust_learning_rate(epoch,lr):
if epoch <= 81:
return lr
elif epoch <= 122:
return lr / 10
else:
return lr / 100
# 该函数通过修改每个epoch下,各参数组中的lr来进行学习率手动调整,用法如下:
for epoch in range(epochs):
lr = adjust_learning_rate(optimizer,epoch) # 调整学习率
optimizer = optim.SGD(net.parameters(),lr=lr,momentum=0.9,weight_decay=5e-4)
.....
optimizer.step() #
# 什么是param_groups?
# optimizer通过param_group来管理参数组.param_group中保存了参数组以及
# 其对应的学习率,动量等等,所以我们可以通过更改param_group['lr']的值
# 来更改对应参数组的学习率。
# 例1.有两个'param_group'即len(optim.param_groups) == 2
optim.SGD([
{'params':model.base.parameters()},
{'params':model.classifier.parameters(),'lr':1e-3}
],lr=1e-2,momentum=0.9)
# 例子2:一个参数组
optim.SGD(model.parameters(),lr=1e-2,momentum=0.9)
"""
上面第一个例子,我们分别为model.base和model.classifier的参数设置了不同的学习率,
即此时optimizer.param_groups中有两个不同的param_group:
param_groups[0]:{'params':model.base.parameters()},
param_group[1]:{'params':model.classifier.parameters(),'lr':1e-3}
每一个param_group都是一个字典,它们共同构成了param_groups,所以此时
len(optimizer.param_groups) == 2,adjust_learning_rate()函数就是通过for循环遍历取出来每一个
param_group,然后修改其中的键"lr"的值,称之为手动调整学习率。
第二个例子中len(optimizer.param) == 1,利用for训练进行修改同样成立。
如果想要每次迭代都实时打印学习率,这样每次step都能知道更新的最新学习率,可以使用。
scheduler.get_lr()
它返回一个学习率列表,由参数组中的不同学习率组成,可通过列表索引来得到不同参数组中的学习率。
def adjust_learning_rate(optimizer, epoch):
Sets the learning rate to the initial LR decayed by 10 every 30 epochs
lr = args.lr * (0.1 ** (epoch // 30))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
"""
4. torchsummary:计算神经网络模型各层输出特征图尺寸及参数量
# torchsummary:计算神经网络模型各层
# 输出特征图尺寸及参数量
# 1.安装
# pip install torchsummary
# 2.导入和使用
# 【注意】:此工具是针对PyTorch的,需配合PyTorch使用!
"""
使用顺序可包括如下:
(1)导入torchsummary中summary对象;
(2)建立神经网络模型
(3)输入 模型(model)、输入尺寸(input_size)、批次大小(batch_size)、
运行平台(device)信息,运行后即可得到summary函数的返回值。
summary函数的接口信息如下:
summary(model,input_size,batch_size,device)
"""
# 3.使用的例子
import torch
import torchvision
# 导入torchsummary
from torchsummary import summary
# 需要使用device来指定网络在GPU还是CPU运行
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 建立神经网络模型,这里直接导入已有模型
model = torchvision.models.vgg11_bn().to(device)
# 使用summary,注意输入维度的顺序
summary(model,input_size=(3,224,224))
"""
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 64, 224, 224] 1,792
BatchNorm2d-2 [-1, 64, 224, 224] 128
ReLU-3 [-1, 64, 224, 224] 0
MaxPool2d-4 [-1, 64, 112, 112] 0
Conv2d-5 [-1, 128, 112, 112] 73,856
BatchNorm2d-6 [-1, 128, 112, 112] 256
ReLU-7 [-1, 128, 112, 112] 0
MaxPool2d-8 [-1, 128, 56, 56] 0
Conv2d-9 [-1, 256, 56, 56] 295,168
BatchNorm2d-10 [-1, 256, 56, 56] 512
ReLU-11 [-1, 256, 56, 56] 0
Conv2d-12 [-1, 256, 56, 56] 590,080
BatchNorm2d-13 [-1, 256, 56, 56] 512
ReLU-14 [-1, 256, 56, 56] 0
MaxPool2d-15 [-1, 256, 28, 28] 0
Conv2d-16 [-1, 512, 28, 28] 1,180,160
BatchNorm2d-17 [-1, 512, 28, 28] 1,024
ReLU-18 [-1, 512, 28, 28] 0
Conv2d-19 [-1, 512, 28, 28] 2,359,808
BatchNorm2d-20 [-1, 512, 28, 28] 1,024
ReLU-21 [-1, 512, 28, 28] 0
MaxPool2d-22 [-1, 512, 14, 14] 0
Conv2d-23 [-1, 512, 14, 14] 2,359,808
BatchNorm2d-24 [-1, 512, 14, 14] 1,024
ReLU-25 [-1, 512, 14, 14] 0
Conv2d-26 [-1, 512, 14, 14] 2,359,808
BatchNorm2d-27 [-1, 512, 14, 14] 1,024
ReLU-28 [-1, 512, 14, 14] 0
MaxPool2d-29 [-1, 512, 7, 7] 0
AdaptiveAvgPool2d-30 [-1, 512, 7, 7] 0
Linear-31 [-1, 4096] 102,764,544
ReLU-32 [-1, 4096] 0
Dropout-33 [-1, 4096] 0
Linear-34 [-1, 4096] 16,781,312
ReLU-35 [-1, 4096] 0
Dropout-36 [-1, 4096] 0
Linear-37 [-1, 1000] 4,097,000
================================================================
Total params: 132,868,840
Trainable params: 132,868,840
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 182.03
Params size (MB): 506.85
Estimated Total Size (MB): 689.46
----------------------------------------------------------------
"""
"""
可以看出,batch_size可以不指定,默认为-1。summary函数会对模型中的每层输出特征图尺寸进行计算,
并计算每层含有的参数量以及模型的参数总量等信息,对于逐层统计计算和分析非常直观和简洁。
"""