深度学习入门,看这一篇就够了
1.深度学习概述
1.1 深度学习的核心定义与特征
深度学习(Deep Learning,DL)是机器学习的分支,指基于深层神经网络架构,通过多层次非线性变换自动学习数据内在表征的算法范式。其本质是特征表征学习(Representation Learning),通过构建包含多个隐藏层(通常≥5层)的神经网络模型,逐层提取并组合数据的抽象特征,从而实现对复杂模式的识别与理解。
深度学习区别于传统机器学习依赖人工设计特征(Feature Engineering),深度学习的革命性在于:
自动特征提取:模型直接从原始数据(如图像像素、文本序列)中学习分层特征表示。例如,在图像识别中,底层网络学习边缘纹理,中层学习局部结构,高层则识别完整对象(如“猫”的概念)。
端到端学习:通过反向传播算法优化数百万至千亿级参数,将输入数据到输出结果的映射过程整合为单一训练流程,无需分阶段处理。
1.2 深度学习的起源与发展历程
深度学习,这一在当今科技领域掀起巨浪的前沿技术,其思想源头可追溯至 20 世纪中叶。当时,人工智能领域的先驱者们受人脑神经元结构的启发,提出了神经网络的雏形。这一创新性的理论基础为后续深度学习的发展奠定了基石。
随着计算机技术与数学理论的逐步进步,从早期简单的单层神经网络,到如今复杂的深度神经网络架构,深度学习经历了漫长而曲折的发展历程。在这一过程中,多个关键历史节点闪烁着创新的光芒。20 世纪 80 年代,反向传播算法的提出为神经网络的训练提供了有效的途径,使得网络能够通过误差反向传播自动调整参数,这一突破极大地推动了神经网络的发展。
进入 21 世纪,随着计算资源的显著提升,特别是 GPU 在深度学习中的广泛应用,深度学习迎来了蓬勃发展的新时期。2012 年,AlexNet 在 ImageNet 图像识别大赛中取得突破性胜利,这一标志性事件引发了深度学习在图像识别领域的广泛关注与深入研究。此后,深度学习在各个领域如自然语言处理、语音识别等不断取得令人瞩目的成果,其模型架构也日益复杂和多样化,如卷积神经网络(CNN)、循环神经网络(RNN)及其变体长短期记忆网络(LSTM)和门控循环单元(GRU)等,不断刷新着人工智能应用的边界。
1.3 深度学习的地位与作用
深度学习相较于传统机器学习算法,展现出独特的优势。它对复杂数据具有强大的拟合能力,能够自动从海量数据中提取深层次的特征,无需人工进行复杂的特征工程,这在处理诸如图像、语音、文本等复杂数据类型时尤为关键。例如,在图像识别任务中,深度学习模型能够自动学习到图像的边缘、纹理、形状等特征,从而实现对图像的精准分类和识别。
深度学习的强大威力使其成为推动人工智能发展的核心驱动力。在图像识别领域,深度学习模型的准确率不断攀升,甚至超越了人类水平,广泛应用于安防监控、自动驾驶、医疗影像诊断等多个重要领域。在自然语言处理领域,深度学习助力机器实现了对人类语言的深度理解和生成,使得机器翻译、语音助手、文本分析等应用取得了质的飞跃。在语音识别方面,深度学习模型能够精准地将语音信号转换为文字,推动了智能语音助手的普及与发展。
这些显著的成果不仅彰显了深度学习在人工智能领域的关键地位,也预示着它在未来将继续引领人工智能技术的创新与突破,为解决更加复杂和具有挑战性的问题提供可能,持续拓展人工智能的边界,为人类社会的发展带来更多的机遇与变革。
2.神经网络基础
2.1 标准神经网络
神经网络(Neural Network, NN)是由大量神经元相互连接而成的复杂系统。根据神经元的连接方式和网络结构,可以将神经网络分为不同的类型,每种类型都有其独特的特点和适用场景。
神经元是构成神经网络的基本单元,深入理解其结构和工作机制对于掌握神经网络的运行原理至关重要。在生物神经科学中,神经元通过接收、处理和传递信息实现大脑的功能。深度学习中的神经元模型抽象自生物神经元,但又有其独特的数学表达和信息处理方式(图1-1)。

图1-1人类的神经元结构
单层神经网络是最简单的神经网络形式,它由一个输入层和一个输出层组成,输入层接收外部数据,输出层直接产生结果。这种网络结构相对简单,适用于一些线性可分的问题,但在处理复杂的非线性问题时能力有限(图1-2)。

图1-2 单层神经网络
多层神经网络,也称为深度神经网络(DNN),它在输入层和输出层之间包含一个或多个隐藏层。隐藏层的存在使得网络能够学习数据中更复杂的模式和特征层次。随着隐藏层数量的增加,网络的深度也随之增加,从而能够捕捉到数据中更深层次的非线性关系,这正是“深度学习”名称的由来之一。多层神经网络在图像识别、语音识别等众多领域都取得了巨大的成功。
图1-3为一个多层全连接前馈神经网络,其数据流向呈现为:输入层A[0]接收包含多维特征的输入向量,随后通过三层节点数相同的隐藏层A[1]至A[3]进行特征抽象,每层均采用密集连接实现跨层信息交互,最终由输出层A[4]生成单个预测值。其核心架构优势包括:相邻层节点完全互连确保充分特征提取,三层隐藏层设计支持复杂非线性变换,单输出结构适配回归分析及二分类场景。(注意:神经网络的层数通常不包括输入层,因此下图是4层的网络。)

图1-3典型的多层神经网络模型结构
为什么需要深层神经网络?
深度学习的工作机制可以概括为表示学习与浅层学习的结合。其工作流程如图1-4:

图1-4 深度学习的工作机制
原始数据:深度学习的起点是原始数据,例如图像、文本、语音等。
底层特征:通过深度学习模型的第一层或几层,原始数据被转换为底层特征。这些特征通常是简单的、局部的模式,例如图像中的边缘和纹理。
中层特征:随着数据在模型中的进一步处理,底层特征被整合和抽象为中层特征。中层特征具有一定的语义意义,例如图像中的部分物体形状。
高层特征:在模型的更深层,中层特征被进一步抽象为高层特征。这些特征具有更丰富的语义信息,能够表示更复杂的模式,例如图像中的完整物体形状。
预测:基于高层特征,模型进行预测或分类。这一阶段相当于传统的浅层学习,利用提取到的特征进行最终的决策。
激活函数是神经元的核心部分,它决定了神经元的输出是否激活以及激活的程度,常见的激活函数有 Sigmoid、ReLU、Tanh 等,它们各自具有不同的数学特性和适用场景。通过激活函数,神经元能够引入非线性因素,使得神经网络具备对复杂函数的拟合能力。输出则是神经元经过激活函数处理后的结果,它可以传递给下一层神经元作为输入。通过类比生物神经元的信息传递过程,我们可以更直观地理解深度学习中神经元如何处理和传递信息,为后续构建复杂的神经网络提供基础认知。图1-5是常见的四种深度学习激活函数的图形。

图1-5 四种常见的深度学习激活函数
Sigmoid 函数:这是一种 S 型曲线函数,其定义域为全体实数,值域为(0,1)。当输入 小于0.5时,输出值趋近于 0;当输入大于等于0.5时,输出值趋近于 1。它常用于二分类问题中,将输出值映射到 0-1之间,表示某种概率。
Tanh 函数(双曲正切函数):同样是 S 型曲线,定义域为全体实数,值域为(-1,1)。与 Sigmoid 函数类似,当输入 小于0时,输出趋近于 - 1;当输入 大于等于时,输出趋近于 1。它在隐藏层神经元中使用较多,相较于 Sigmoid函 数,Tanh 函数能够提供更强烈的非线性特性,因此在深度学习中广泛采用。
ReLU 函数(修正线性单元):当输入大于等于 0 时,输出值等于输入值;当输入 小于 0 时,输出值为 0。它的计算简单,且在输入较大的时候不会出现梯度消失问题。ReLU函数在深度学习中被广泛应用,是默认的激活函数,尤其是在卷积神经网络中,能够加速模型收敛。
Leaky ReLU 函数:是对 ReLU 函数的一种改进,当输入 大于等于 0 时,输出值等于输入值;而当输入 小于 0 时,输出值为 0.01。这种改进解决了 ReLU函数在输入为负数时梯度为零的问题,使得网络在负数区域也能够学习到一些信息。
2.2 卷积神经网络
卷积神经网络(Convolutional Neural Network, CNN)是一类专门为处理具有网格结构数据(如图像、视频等)而设计的神经网络。图1-6是一个典型的识别手写数字的卷积神经网络结构。

图1-6 典型的卷积神经网络
CNN 的核心在于卷积层和池化层的使用。卷积层通过卷积核在输入数据上滑动进行卷积操作,能够自动提取数据中的局部特征,如图像的边缘、纹理等(图1-7).

图1-7 卷积操作
池化层则用于对卷积层的输出进行降维,减少计算量,同时增强模型对数据的平移不变性。这种结构使得 CNN 在图像识别、目标检测等计算机视觉任务中表现出色,能够高效地处理图像数据并捕捉其中的复杂模式。

图1-8 最大池化操作
图1-8是一个简单的最大池化操作,简单地说,根据图中的池化操作:使用2×2的池化核将原矩阵划分为4个区域,并抽取每个区域内的最大值(如1,1,5,6→取6);通过步长2的滑动,原4×4矩阵被压缩为2×2的新矩阵,尺寸减半但保留每个区域最显著的数值(结果矩阵为3,6,3,8),本质是信息的抽样提炼。
在图像分类任务中,CNN 可以通过多层卷积和池化操作逐步提取图像的深层次特征,最终实现高精度的分类结果。其中,有许多重要的卷积神经网络架构,如 LeNet、AlexNet、VGG、GoogLeNet、ResNet 等,它们在推动计算机视觉领域的发展中发挥了关键作用。这些经典网络架构不断演进,从简单的几层网络到拥有上百层的深层网络,每一次创新都极大地提升了 CNN 的性能和应用范围。
2.3 循环神经网络
循环神经网络(Recurrent Neural Network, RNN)也叫递归神经网络,具有循环结构,能够处理序列数据。如图1-9,在 RNN 中,神经元的输出不仅可以传递给下一层神经元,还可以反馈给自己或同一层的其他神经元,这种循环连接使得 RNN 能够记忆之前的信息,并将其用于当前的计算。因此,RNN 在自然语言处理、时间序列预测等需要考虑时间依赖关系的任务中具有独特的优势。

图1-9 典型的递归神经网络
LSTM(长短期记忆网络)是一种特殊的循环神经网络(RNN),专为解决传统RNN在处理长序列数据时的长期依赖问题(如梯度消失)而设计。如图1-10,RNN的核心通过门控机制动态管理信息流:遗忘门决定丢弃哪些旧信息(例如忽略句子中过时的细节);输入门筛选新信息加入记忆(如记住关键事件);输出门控制当前状态的对外输出(如生成翻译结果)。这种结构使LSTM能选择性保留长期重要信息(如对话主题),同时过滤噪声,广泛应用于机器翻译、语音识别和时间序列预测等领域。

图1-10 LSTM结构
2.4 Transformer
2017年Google的机器翻译团队在NIPS上发表了Attention is all you need的文章,开创性地提出了在序列转录领域,完全抛弃 CNN和 RNN,只依赖Attention-注意力结构的简单的网络架构,名为Transformer;论文实现的任务是机器翻译。其主要架构见图1-11,由编码器(左边部分)和解码器(右边部分)构成。

图1-11 Transformer结构
Transformer 模型摒弃了传统依赖人工标注数据集所带来的局限,相较于以往的模型,其在质量上更为优越,且具备更易于并行化的特性,这使得训练时间得以显著减少。Transformer 得以成功应用于拥有大量数据以及数据有限的分析场景,凭借出色的泛化能力,它能够顺利推广至其他各类任务。其完全依赖于注意力机制构建,摒弃了重复和卷积操作,正因如此,Transformer 在质量上更胜一筹,同时大幅提升了并行化的便捷性,极大地缩短了训练所需的时间。自Transformer 诞生以来,它迅速取代了 RNN 系列变种模型,凭借自身优势跻身主流模型架构的基础地位。RNN 的缺陷主要在于其流水线式的顺序计算模式,而 Transformer 的出现,使得大模型的基础模型架构基本得以确立,注意力机制也成功取代卷积神经网络,成为主流的基础模型组件。
Transformer 模型有利于向更大的参数量扩展,展现出强大的可扩展性。此外,它还拥有兼容多模态信息的天然优势,这一特性极大地拓宽了大模型的应用场景,使其应用范围更加广泛。
总的来说,Transformer 模型具有以下特点:参数量相对较少,训练速度更快,且在效果上更为出色。
3.深度学习的工作流程
深度学习的工作流程是深度学习项目实施的具体步骤和方法的总和,它为开发者提供了一套系统性的指导,有助于高效地构建和应用深度学习模型。以下是深度学习工作流程的主要阶段:
3.1 数据准备
数据是深度学习的基础,数据的质量和数量直接影响模型的性能。数据准备阶段主要包括以下几个步骤:
数据收集 :根据具体的应用场景和问题需求,收集大量的相关数据。数据来源可以是公开数据集、企业内部数据或通过网络爬取等合法途径获取的数据。
数据预处理 :对收集到的原始数据进行清洗和整理。这包括处理缺失值、异常值、噪声数据等,将数据转换为适合模型输入的格式。例如,对于图像数据,可能需要进行归一化、标准化、缩放等操作;对于文本数据,可能需要进行分词、词干提取、去除停用词等处理。
数据增强(可选):为了扩大训练数据的规模和多样性,可以采用数据增强技术。对于图像数据,常见的数据增强方法包括旋转、翻转、裁剪、调整亮度和对比度等;对于文本数据,可以通过同义词替换、句子重组等方式生成新的训练样本。
3.2 模型构建和训练
模型构建和训练是深度学习工作的核心部分,图1-12 模型构建和训练流程,主要包括以下几个方面:

图1-12 模型构建和训练流程
1.定义网络(模型构建)
这是训练流程的基石,需要精心设计神经网络的结构。具体包括:
架构设计 :确定网络层数(例如深度网络包含多个隐藏层)、每层的神经元数量、以及层与层之间的连接方式(全连接、卷积、循环连接等)。不同的网络类型适用于不同的任务,例如卷积神经网络(CNN)擅长图像处理,循环神经网络(RNN)则适用于序列数据。
激活函数选择 :为每一层选择合适的激活函数(如ReLU、Sigmoid、Tanh等),激活函数为网络引入非线性,使其能够学习复杂的模式。
参数初始化 :为网络的权重和偏置设定初始值,良好的初始化策略可以加速训练收敛。
网络定义的好坏直接决定了模型的表达能力上限,一个设计精良的网络架构是模型成功的关键。
2.定义损失函数
损失函数是衡量模型预测结果与真实情况之间差距的标尺,主要作用:
量化误差 :通过计算预测值与真实标签之间的差异,给出一个具体的数值来表示模型在当前任务上的表现。例如,回归任务常用均方误差(MSE),分类任务常用交叉熵损失。
指导优化 :损失函数为优化算法提供了明确的方向,优化算法的目标就是最小化损失函数的值。
任务导向 :损失函数的选择与具体任务密切相关,不同的任务需要不同的损失函数来准确评估模型性能。
3.优化(参数调整)
优化是模型训练的核心驱动环节,是通过训练数据来调整模型参数,使模型能够学习到数据中的模式和规律。主要步骤如下:
初始化模型参数:在训练开始前,需要对模型的权重和偏置等参数进行初始化。常见的初始化方法有随机初始化。合适的参数初始化可以加速模型的收敛,提高模型的性能。
选择训练方法和超参数:确定训练方法,如批量梯度下降、随机梯度下降、小批量梯度下降等。同时,设置超参数,如学习率、批量大小、训练轮数等。超参数的选择对模型的训练效果和收敛速度有重要影响,通常需要通过实验进行调整和优化。
前向传播与反向传播:在每次迭代中,将训练数据输入模型,进行前向传播计算,得到模型的输出和损失值。然后,通过反向传播算法计算损失函数对模型参数的梯度,并使用优化器更新模型参数。
监控训练过程:在训练过程中,需要监控模型的损失值、准确率等指标的变化情况,以判断模型是否正常收敛、是否出现过拟合或欠拟合等问题。可以通过绘制损失曲线、准确率曲线等可视化工具来辅助分析。
迁移学习是一种重要的深度学习技术,它允许我们将在一个任务中学习到的知识应用到另一个相关任务中。具体来说,迁移学习通常涉及以下几个步骤(图1-13):

图1-13迁移学习示例
(1)选择预训练模型 :选择一个在大规模数据集(如 ImageNet)上预训练好的模型。这些预训练模型已经在类似的任务上学习到了丰富的特征表示,可以作为新任务的良好起点。
(2)调整模型结构 :根据新任务的需求,对预训练模型的结构进行适当的调整。例如,更换模型的输出层以匹配新任务的类别数量,或者在模型中添加新的层以增强模型的表达能力。
(3)冻结部分参数 :在训练过程中,可以选择冻结预训练模型中的部分参数(通常是前面的层),只更新新添加的层或部分解冻的层的参数。这样可以保留预训练模型中已经学习到的通用特征表示,同时减少计算资源的消耗和过拟合的风险。
(4)微调模型 :使用新任务的训练数据对调整后的模型进行微调。在微调过程中,学习率通常设置得较低,以避免破坏预训练模型中已经学习到的有用信息。通过微调,模型可以适应新任务的具体特征和规律。
比如说,我们需要一个深度学习模型可以识别猫和狗,我们可以使用一个可以识别1000类物体(包含猫和狗)的模型进行迁移,最后只需要把输出层从1000类改成2类(猫和狗)即可。
迁移学习的优势在于它可以显著减少训练新模型所需的计算资源和数据量,同时提高模型的收敛速度和性能。它特别适用于新任务数据量有限或训练计算资源受限的情况。在实际应用中,迁移学习已被广泛应用于图像分类、目标检测、自然语言处理等多个领域,取得了显著的效果。
3.3 模型评估与优化
在模型训练完成后,需要对模型进行评估和优化,以确保模型具有良好的泛化能力和实际应用价值:
模型评估 :使用测试集对模型进行评估,计算模型在测试集上的性能指标,如准确率、召回率、F1 值、均方误差等。这些指标能够反映模型在未见数据上的表现,为模型的选择和优化提供依据。
模型优化 :如果模型的评估结果不理想,需要对模型进行优化。可以尝试调整超参数、改变模型架构、增加数据量、采用正则化方法(如 L1、L2 正则化、Dropout)等手段来提高模型的性能。此外,还可以采用模型集成方法,将多个模型的预测结果进行融合,以提高模型的稳定性和准确性。
3.4 模型部署与应用
当模型经过评估和优化,达到满意的性能后,可以将其部署到实际应用场景中。
模型部署 :将训练好的模型保存为文件(如 TensorFlow 的. ckpt 文件、PyTorch 的.pth 文件等),并使用相应的部署工具和框架(如 TensorFlow Serving、PyTorch Serve、ONNX 等)将模型部署到服务器、移动设备、边缘设备等不同的运行环境中。不同模型之间,可以用深度学习模型的交换格式,如ONNX格式(图1-14)。

图1-14 ONNX深度学习模型交换格式
ONNX(Open Neural Network Exchange)是一个开放的深度学习模型交换格式,旨在使不同深度学习框架之间能够更轻松地共享和使用模型。ONNX是由微软和Facebook联合开发的,现在已经得到了各大深度学习框架的支持,包括PyTorch、TensorFlow、Caffe等。
模型应用 :在实际应用中,接收用户输入的数据,对数据进行预处理,然后将其输入到部署好的模型中进行推理计算,得到模型的输出结果。根据具体的应用场景,将模型的输出结果进行相应的后处理和展示,为用户提供了一个完整的人工智能解决方案。
以上就是深度学习工作流程的主要阶段和步骤。在实际的深度学习项目中,这些阶段可能不是严格按照顺序进行的,可能需要根据实际情况进行迭代和调整。掌握深度学习的工作流程,有助于开发者更好地理解和应用深度学习技术,提高项目的开发效率和模型的性能。
4.深度学习框架及应用
4.1 主流深度学习框架
在深度学习领域,多种优秀的深度学习框架应运而生,它们为研究人员和开发者提供了强大的工具和支持,极大地推动了深度学习技术的发展和应用。其中,TensorFlow和PyTorch是目前最为流行的深度学习框架,各自具有独特的特点和优势。图1-15是主流的几种深度学习框架,其中最流行的是TensorFlow和PyTorch。

图1-15 主流的几种深度学习框架
TensorFlow 是由谷歌开发并开源的深度学习框架,它以其强大的计算图(Computation Graph)机制而闻名。TensorFlow 的计算图能够清晰地表示数据流动和计算过程,使得模型的构建、训练和部署具有高度的灵活性和可扩展性。此外,TensorFlow 还具备出色的分布式训练能力,可以在多台服务器和多个 GPU 上进行大规模的模型训练,适用于工业级应用和大规模数据处理场景。它拥有丰富的预训练模型和丰富的社区资源,为开发者提供了极大的便利。
PyTorch 是由 Facebook 推出的深度学习框架,近年来在学术界和工业界都获得了广泛的应用。PyTorch 的一大特色是其动态图(Dynamic Graph)机制,这使得网络结构可以动态调整,开发者能够根据需要在运行时修改模型结构,为模型的构建和调试提供了极大的灵活性。同时,PyTorch 的 API 设计简洁直观,易于上手,语法风格接近 Python 的原生代码,使得开发者能够快速构建和实验新的模型架构。此外,PyTorch 也提供了强大的 GPU 加速功能和高效的自动求导机制,能够满足复杂的模型训练需求。
在选择深度学习框架时,开发者应根据实际需求和项目特点进行综合考虑。如果项目需要强大的分布式训练能力和大规模的工业级应用支持,TensorFlow 可能是更合适的选择;如果更注重模型构建的灵活性和调试的便利性,PyTorch 则具有明显的优势。
4.2 深度学习框架的基本使用方法 —— 以 PyTorch 为例
接下来,我们将以 PyTorch 框架为例,实现一个具体的图像分类项目。
1.项目目标
本项目的目标是训练一个能够对 CIFAR10 数据集中的图像进行分类的卷积神经网络(CNN)。CIFAR10 数据集包含 10 个类别的彩色图像,每类有 6000 张图像,图像尺寸为 32x32 像素。具体目标如下:
成功加载和预处理数据 :从 torchvision 库中正确加载 CIFAR10 训练集和测试集,并对图像数据进行归一化处理,使其像素值范围在 [-1, 1] 之间,以便更好地输入神经网络进行训练。
构建有效的卷积神经网络模型 :设计一个包含卷积层、池化层和全连接层的 CNN 架构,能够提取图像特征并输出对应的类别概率。
实现模型的训练过程 :通过定义合适的损失函数(交叉熵损失)和优化器(带动量的随机梯度下降),在训练集上循环迭代,调整网络参数,使模型能够学习到图像与类别之间的映射关系。
对测试集进行准确分类 :在训练完成的模型上运行测试集数据,计算分类准确率,并分析模型在不同类别上的表现,验证模型是否具有良好的泛化能力和对未见数据的分类效果。
2. 项目意义
本项目实践了深度学习中卷积神经网络的基本构建和训练流程,有助于深入理解 CNN 的工作原理、网络结构设计以及如何通过优化算法来提升模型性能。同时,也展示了如何利用 torchvision 等库来便捷地处理图像数据集。图像分类是计算机视觉领域的核心任务之一,具有广泛的应用价值。如在安防监控中识别物体类别、在医疗影像诊断中对病变特征进行分类、在自动驾驶中对道路场景元素进行识别等。通过本项目,可为解决实际场景中的图像分类问题积累经验和技术储备。
3.项目实现
我们将按照下列顺序进行:
1)加载和归一化CIFAR10
import torch # 导入 PyTorch 库
import torchvision # 导入 torchvision 库,用于处理图像数据集等
import torchvision.transforms as transforms # 导入图像变换模块
import torch.nn as nn # 导入神经网络相关模块
import torch.nn.functional as F # 导入神经网络函数模块
import torch.optim as optim # 导入优化器模块
torchvision的输出是[0,1]的PILImage图像,我们把它转换为归一化范围为[-1,1]的张量。
# 定义图像变换操作,将图像转为张量并归一化
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
# 加载训练集
trainset = torchvision.datasets.CIFAR10(
root='./data', # 数据存储根目录
train=True, # 表示加载训练集
download=True, # 如果数据不存在则下载
transform=transform) # 应用图像变换
# 创建训练集数据加载器
trainloader = torch.utils.data.DataLoader(
trainset,
batch_size=4, # 每批加载 4 张图像
shuffle=True, # 随机打乱数据顺序
num_workers=2) # 使用 2 个子进程加载数据
# 加载测试集
testset = torchvision.datasets.CIFAR10(
root='./data',
train=False, # 表示加载测试集
download=True,
transform=transform)
# 创建测试集数据加载器
testloader = torch.utils.data.DataLoader(
testset,
batch_size=4,
shuffle=False, # 测试集不需要打乱顺序
num_workers=2)
# 定义 CIFAR10 数据集的类别名称
classes = ('飞机', '汽车', '鸟', '猫', '鹿', '狗', '青蛙', '马', '轮船', '卡车')
使用torchvision加载CIFAR10是非常容易的。CIFAR-10是一个更接近普适物体的彩色图像数据集。CIFAR-10 是由Hinton 的学生Alex Krizhevsky 和Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。一共包含10 个类别的RGB 彩色图片:飞机( airplane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。
每张图片的尺寸为32 × 32 ,每个类别有6000个图像,数据集中一共有50000 张训练图片和10000 张测试图片(图1-16)。

图1-16 CIFAR10图像数据集
2) 定义一个卷积神经网络
这个网络由两个卷积层、两个池化层和三个全连接层组成。第一个卷积层将输入的3通道RGB图像转换为6个通道的特征图,然后通过ReLU激活函数和池化层进行特征提取和降维。第二个卷积层进一步将特征图通道数增加到16,同样应用ReLU激活和池化。之后,特征图被展平为一维向量,输入到全连接层中。第一个全连接层将特征映射到120维,第二个全连接层映射到84维,最后第三个全连接层输出10个类别的概率,用于确定图像的类别。网络通过卷积层提取图像的局部特征,池化层减少特征图尺寸,全连接层整合特征进行分类,从而实现对CIFAR10数据集中10类图像的分类功能。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
3)定义损失函数和优化器
使用交叉熵作为损失函数,使用带动量的随机梯度下降。
# 使用交叉熵作为损失函数
criterion = nn.CrossEntropyLoss()
# 使用带动量的随机梯度下降优化器,学习率 0.001,动量 0.9
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
4)训练网络
在数据迭代器上循环,把数据输入给网络,并优化。
# 在训练集上循环多次进行训练
for epoch in range(2): # 进行 2 轮训练
running_loss = 0.0 # 初始化运行损失
for i, data in enumerate(trainloader, 0): # 遍历训练数据加载器
# 获取输入数据和标签
inputs, labels = data
# 梯度清零,避免梯度累积
optimizer.zero_grad()
# 前向传播 + 反向传播 + 优化
outputs = net(inputs) # 输入网络得到输出
loss = criterion(outputs, labels) # 计算损失
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新模型参数
# 统计和打印损失信息
running_loss += loss.item() # 累计损失
if i % 2000 == 1999: # 每 2000 个批次打印一次损失
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0 # 重置运行损失
print('训练结束!')
输出:
[1, 2000] loss: 2.208
[1, 4000] loss: 1.859
[1, 6000] loss: 1.678
[1, 8000] loss: 1.563
[1, 10000] loss: 1.515
[1, 12000] loss: 1.434
[2, 2000] loss: 1.389
[2, 4000] loss: 1.347
[2, 6000] loss: 1.314
[2, 8000] loss: 1.300
[2, 10000] loss: 1.286
[2, 12000] loss: 1.258
训练结束!
保存训练模型。
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
5)在测试集上测试网络
我们在整个训练集上训练了两次网络,但是我们还需要检查网络是否从数据集中学习到东西。所以通过预测神经网络输出的类别标签并根据实际情况进行检测,如果预测正确,我们把该样本添加到正确预测列表。
# 加载测试数据
dataiter = iter(testloader) # 创建测试数据迭代器
images, labels = next(dataiter) # 获取一批测试图像和标签
# 打印实际标签
print('实际标签: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
输出:
实际标签: 猫 轮船 轮船 飞机
接下来,重新加载保存的模型。
# 保存训练好的模型参数
PATH = './cifar_net.pth' # 指定保存路径
torch.save(net.state_dict(), PATH) # 保存模型参数
输出:
<All keys matched successfully>
输出是10个标签的概率。一个类别的概率越大,神经网络越认为它是这个类别。
# 对测试图像进行预测
outputs = net(images) # 输入模型得到预测输出
_, predicted = torch.max(outputs, 1) # 获取预测类别标签
print('预测结果: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
输出:
预测结果: 猫 轮船 轮船 轮船
接下让测试网络在整个测试集上的结果如何。
# 计算整体测试准确率
correct = 0 # 初始化正确预测数
total = 0 # 初始化总样本数
with torch.no_grad(): # 测试时不需要计算梯度,节省内存和计算资源
for data in testloader: # 遍历测试数据加载器
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0) # 累计总样本数
correct += (predicted == labels).sum().item() # 累计正确预测数
10000张图片上的测试准确率为: 55 %
那在什么类上预测较好,什么类预测结果不好呢?
# 计算各类别的准确率
class_correct = list(0. for i in range(10)) # 初始化每个类别的正确预测数
class_total = list(0. for i in range(10)) # 初始化每个类别的总样本数
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze() # 获取预测是否正确的布尔值
for i in range(4): # 每批次有 4 个样本
label = labels[i]
class_correct[label] += c[i].item() # 累计对应类别的正确预测数
class_total[label] += 1 # 累计对应类别的总样本数
# 打印每个类别的准确率
for i in range(10):
print('准确率- %5s : %2d %%' %
(classes[i], 100 * class_correct[i] / class_total[i]))
输出:
准确率 飞机 : 48 %
准确率 汽车 : 63 %
准确率 鸟 : 38 %
准确率 猫 : 24 %
准确率 鹿 : 59 %
准确率 狗 : 36 %
准确率 青蛙 : 66 %
准确率 马 : 72 %
准确率 轮船 : 74 %
准确率 卡车 : 71 %

