深度学习入门:从理论到实战,轻松开启 AI 之旅
而使用深度学习方法,我们只需将原始的图像像素数据输入到卷积神经网络(CNN)中,模型会自动学习图像的底层特征(如边缘)、中层特征(如纹理)和高层特征(如物体的形状),最终实现对图像的识别。深度学习(Deep Learning,DL)是机器学习(Machine Learning,ML)的一个重要分支,它基于人工神经网络(Artificial Neural Network,ANN),通过构建具有多层结
深度学习入门:从理论到实战,轻松开启 AI 之旅
一、引言:为什么要学习深度学习?
在这个 AI(人工智能)蓬勃发展的时代,深度学习作为 AI 领域的核心技术,正深刻改变着我们的生活。从手机里的人脸识别解锁、拍照时的智能美颜,到购物平台的精准推荐、自动驾驶汽车的环境感知,再到医疗领域的疾病诊断、教育领域的个性化学习指导,深度学习的应用无处不在。
对于初学者而言,学习深度学习不仅能让我们理解身边这些智能应用背后的原理,更能为未来的职业发展增添强大的竞争力。无论是想进入互联网大厂从事 AI 研发,还是在传统行业利用深度学习技术进行创新,掌握深度学习的基础知识和实战能力都是必不可少的。
那么,深度学习到底是什么?它和我们常听到的机器学习有什么区别?又该如何一步步入门并动手实践呢?接下来,这篇博客将为你一一解答,带您从理论到实战,轻松开启深度学习的学习之旅。
二、深度学习基础概念
2.1 深度学习的定义
深度学习(Deep Learning,DL)是机器学习(Machine Learning,ML)的一个重要分支,它基于人工神经网络(Artificial Neural Network,ANN),通过构建具有多层结构的网络模型,模拟人类大脑的学习过程,从大量数据中自动学习特征和规律,从而实现对数据的分析、预测和决策。
简单来说,深度学习就像一个 “智能大脑”,它不需要人类手动提取数据的特征,而是通过自身的多层网络结构,自主地从海量数据中挖掘有用信息,不断优化自身的模型参数,提高处理任务的准确性。
2.2 深度学习与机器学习的区别
很多初学者会混淆深度学习和机器学习,其实它们既有联系又有明显的区别,具体如下表所示:
对比维度 |
机器学习 |
深度学习 |
特征工程 |
依赖人工手动提取特征,需要领域专家根据经验选择和构建合适的特征 |
自动从数据中学习特征,无需人工干预,能处理高维、复杂的数据特征 |
数据依赖 |
对数据量要求相对较低,在小规模数据集上也能取得较好的效果 |
对数据量要求较高,需要大量的数据来训练模型,数据量越大,模型性能通常越好 |
模型结构 |
模型结构相对简单,通常包含输入层、特征处理层和输出层,层数较少 |
模型结构复杂,包含大量的隐藏层,层数可达几十层甚至上百层 |
计算资源 |
对计算资源要求较低,普通的 CPU 就能满足大部分训练需求 |
对计算资源要求较高,需要高性能的 GPU(图形处理器)来加速模型训练 |
应用场景 |
适用于传统的数据挖掘任务,如分类、回归、聚类等,例如客户流失预测、房价预测 |
适用于复杂的智能任务,如图像识别、自然语言处理、语音识别等,例如人脸识别、机器翻译 |
举个例子,在图像识别任务中,如果使用机器学习方法,我们需要人工提取图像的特征,如边缘、纹理、颜色等,然后将这些特征输入到分类模型(如支持向量机、决策树)中进行训练;而使用深度学习方法,我们只需将原始的图像像素数据输入到卷积神经网络(CNN)中,模型会自动学习图像的底层特征(如边缘)、中层特征(如纹理)和高层特征(如物体的形状),最终实现对图像的识别。
2.3 深度学习的核心:人工神经网络
人工神经网络是深度学习的基础,它的灵感来源于人类大脑的神经元结构。人类大脑由大量的神经元组成,神经元之间通过突触相互连接,传递和处理信息。人工神经网络就是模拟这一结构,由大量的人工神经元(简称 “神经元”)按照一定的规则连接而成。
2.3.1 单个神经元的结构与工作原理
单个神经元是人工神经网络的基本单元,它的结构如下图所示(此处为文字描述:一个神经元包含输入端、权重、偏置、激活函数和输出端):
- 输入端:接收来自其他神经元或外部数据的输入信号,用表示。
- 权重:每个输入信号都有一个对应的权重,权重表示该输入信号对神经元输出的重要程度,权重越大,对应的输入信号影响越大。
- 偏置:偏置是一个常数,用于调整神经元的激活阈值,避免输入信号全为 0 时神经元无法激活。
- 激活函数:激活函数用于对神经元的输入加权和进行非线性变换,将结果映射到一个特定的范围内,从而使神经网络能够处理非线性问题。常见的激活函数有 Sigmoid 函数、ReLU 函数、Tanh 函数等。
- 输出端:神经元的输出
y
由输入信号的加权和加上偏置后,经过激活函数变换得到,计算公式为:
举个简单的例子,假设一个神经元有两个输入
x1=2
,
x2=3
,对应的权重
w1=0.5
,
w2=0.3
,偏置
b=0.1
,激活函数采用 Sigmoid 函数(
f(z)=1+e−z1
)。那么:
首先计算输入的加权和加上偏置:
i=1∑2wixi+b=0.5×2+0.3×3+0.1=1+0.9+0.1=2
然后经过 Sigmoid 激活函数变换:
y=f(2)=1+e−21≈1+0.13531≈0.8808
所以,这个神经元的输出约为 0.8808。
2.3.2 神经网络的层次结构
单个神经元的能力有限,无法处理复杂的任务。因此,深度学习通过将大量的神经元按照一定的层次结构连接起来,形成人工神经网络。常见的神经网络层次结构包括输入层、隐藏层和输出层:
- 输入层:接收外部的原始数据,如图像的像素数据、文本的向量数据等,输入层的神经元数量等于数据的特征维度。例如,处理一张 28×28 的灰度图像(共 784 个像素)时,输入层的神经元数量为 784。
- 隐藏层:位于输入层和输出层之间,用于对输入数据进行复杂的特征提取和变换。隐藏层可以有一层或多层,层数越多,网络的深度越深,处理复杂问题的能力越强。例如,简单的多层感知机(MLP)可能有 1-2 层隐藏层,而复杂的卷积神经网络(CNN)或循环神经网络(RNN)可能有几十层甚至上百层隐藏层。
- 输出层:输出神经网络对输入数据的处理结果,输出层的神经元数量根据具体任务而定。例如,在二分类任务(如判断邮件是否为垃圾邮件)中,输出层通常有 1 个神经元,输出结果为 0 或 1;在多分类任务(如识别 10 种不同的手写数字)中,输出层通常有 10 个神经元,每个神经元对应一种数字的概率。
以手写数字识别任务为例,一个简单的神经网络结构可能如下:输入层有 784 个神经元(对应 28×28 的图像像素),隐藏层有 128 个神经元,输出层有 10 个神经元(对应 0-9 这 10 个数字)。当一张手写数字图像输入到网络中时,输入层将像素数据传递给隐藏层,隐藏层对数据进行特征提取和变换后传递给输出层,输出层最终输出每个数字的概率,概率最大的数字即为网络的识别结果。
三、深度学习核心原理
3.1 激活函数:让神经网络 “活” 起来
在前面的神经元结构中,我们提到了激活函数,它是神经网络中非常重要的组成部分。如果没有激活函数,神经网络的每一层输出都是输入的线性组合,无论网络有多少层,最终的输出仍然是输入的线性函数,无法处理复杂的非线性问题。而激活函数通过引入非线性变换,让神经网络能够学习和处理非线性关系,从而 “活” 起来。
下面介绍几种常用的激活函数:
3.1.1 Sigmoid 函数
Sigmoid 函数的公式为:
f(z)=1+e−z1
,它的取值范围在 (0, 1) 之间,形状呈 “S” 型。
优点:
- 输出值在 (0, 1) 之间,可以表示概率,适用于二分类任务的输出层。
- 函数连续可导,便于使用梯度下降算法进行模型训练。
缺点:
- 存在梯度消失问题:当
z
的值过大或过小时,Sigmoid 函数的导数趋近于 0,导致在反向传播过程中梯度无法有效传递,网络深层的参数难以更新。
- 输出不是以 0 为中心,可能导致训练过程中参数更新不稳定。
应用场景:主要用于二分类任务的输出层,在隐藏层中较少使用。
3.1.2 ReLU 函数
ReLU(Rectified Linear Unit,修正线性单元)函数的公式为:
f(z)=max(0,z)
,即当
z≥0
时,输出
z
;当
z<0
时,输出 0。
优点:
- 计算简单,只需要判断
z
是否大于 0,计算速度快。
- 有效缓解梯度消失问题:当
z>0
时,导数为 1,梯度可以有效传递,便于网络深层参数的更新。
- 稀疏激活:当
z<0
时,神经元输出为 0,处于 “休眠” 状态,减少了网络的计算量,提高了训练效率。
缺点:
- 存在 “死亡 ReLU” 问题:当
z
长期为负时,神经元会一直处于 “休眠” 状态,参数无法更新,导致该神经元永久失效。
应用场景:是目前深度学习中使用最广泛的激活函数之一,主要用于卷积神经网络(CNN)、多层感知机(MLP)等网络的隐藏层。
3.1.3 Tanh 函数
Tanh(双曲正切)函数的公式为:
f(z)=ez+e−zez−e−z
,它的取值范围在 (-1, 1) 之间,形状也呈 “S” 型,但以 0 为中心。
优点:
- 输出以 0 为中心,有利于模型的稳定训练。
- 函数连续可导,便于梯度下降算法的使用。
缺点:
- 同样存在梯度消失问题:当
z
的值过大或过小时,Tanh 函数的导数趋近于 0,影响深层参数的更新。
应用场景:在循环神经网络(RNN)的隐藏层中使用较多,也可用于一些需要输出以 0 为中心的场景。
3.2 损失函数:衡量模型的 “误差”
在深度学习模型训练过程中,我们需要知道模型的预测结果与真实结果之间的差距,以便调整模型的参数,使模型的预测更加准确。损失函数(Loss Function)就是用来衡量这种差距的函数,它的输出值称为损失值(Loss Value),损失值越小,说明模型的预测结果与真实结果越接近,模型的性能越好。
下面介绍几种常用的损失函数:
3.2.1 均方误差损失(MSE)
均方误差损失(Mean Squared Error,MSE)的公式为:
L=n1i=1∑n(yi−y^i)2
,其中
n
为样本数量,
yi
为第
i
个样本的真实值,
y^i
为第
i
个样本的预测值。
特点:
- 对误差的平方进行平均,惩罚较大的误差,对异常值比较敏感。
- 函数连续可导,便于使用梯度下降算法进行优化。
应用场景:主要用于回归任务,如房价预测、股票价格预测等,预测结果为连续值的任务。
例如,在房价预测任务中,假设我们有 3 个样本,真实房价分别为 100 万元、150 万元、200 万元,模型的预测房价分别为 95 万元、155 万元、190 万元。那么:
MSE 损失值 =
31[(100−95)2+(150−155)2+(200−190)2]=31[25+25+100]=3150=50
(万元 ²)
3.2.2 交叉熵损失(Cross-Entropy Loss)
交叉熵损失(Cross-Entropy Loss)主要用于分类任务,根据分类任务的类型(二分类、多分类),又分为二元交叉熵损失和多元交叉熵损失。
- 二元交叉熵损失:适用于二分类任务,公式为:
L=−n1i=1∑n[yilny^i+(1−yi)ln(1−y^i)]
,其中yi
为第i
个样本的真实标签(0 或 1),y^i
为第i
个样本预测为正类(标签为 1)的概率。
- 多元交叉熵损失:适用于多分类任务,公式为:
L=−n1i=1∑nj=1∑cyijlny^ij
,其中c
为类别数量,yij
为第i
个样本属于第j
类的真实标签(如果属于第j
类,则yij=1
,否则yij=0
),y^ij
为第i
个样本预测属于第j
类的概率。
特点:
- 直接衡量模型预测概率分布与真实概率分布之间的差距,对模型预测的错误惩罚力度较大,训练效果较好。
- 与 Softmax 激活函数配合使用时,可以有效解决多分类任务中的概率输出问题。
应用场景:二分类任务(如垃圾邮件识别、疾病诊断)和多分类任务(如手写数字识别、图像分类)。
例如,在手写数字识别任务(10 分类)中,假设我们有 1 个样本,真实标签为数字 “3”(对应的真实概率分布为 [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]),模型的预测概率分布为 [0.01, 0.02, 0.03, 0.8, 0.04, 0.02, 0.03, 0.02, 0.02, 0.01]。那么:
多元交叉熵损失值 =
−[0×ln0.01+0×ln0.02+0×ln0.03+1×ln0.8+0×ln0.04+0×ln0.02+0×ln0.03+0×ln0.02+0×ln0.02+0×ln0.01]=−ln0.8≈0.2231
3.3 优化算法:让模型 “学会” 调整参数
深度学习模型的训练过程,本质上是通过优化算法不断调整模型的参数(权重和偏置),使损失函数的值最小化的过程。常用的优化算法是梯度下降算法及其变种,下面详细介绍:
3.3.1 梯度下降算法(Gradient Descent)
梯度下降算法的核心思想是:沿着损失函数梯度的反方向更新模型参数,因为梯度方向是损失函数值增加最快的方向,反方向就是损失函数值减少最快的方向,这样可以快速找到使损失函数最小化的参数。
梯度下降算法的参数更新公式为:
θ=θ−η∇L(θ)
,其中
θ
为模型参数(权重或偏置),
η
为学习率(Learning Rate),
∇L(θ)
为损失函数
L(θ)
对参数
θ
的梯度。
- 学习率:学习率是梯度下降算法中的一个重要超参数,它决定了每次参数更新的幅度。学习率过大,可能会导致参数在最优值附近震荡,无法收敛;学习率过小,会导致参数更新速度过慢,训练时间过长。通常学习率的取值范围在 0.001-0.1 之间,需要根据具体任务和模型进行调整。
根据每次使用的样本数量,梯度下降算法又分为以下三种:
- 批量梯度下降(Batch Gradient Descent,BGD)
- 原理:每次使用全部训练样本计算损失函数的梯度,然后根据梯度更新参数。
- 优点:计算的梯度准确,收敛稳定,最终能找到全局最优解(在损失函数为凸函数的情况下)。
- 缺点:当训练样本数量较大时,每次计算梯度需要处理大量数据,计算量和内存消耗大,训练速度慢。
- 应用场景:适用于训练样本数量较小的场景。
- 随机梯度下降(Stochastic Gradient Descent,SGD)
- 原理:每次随机选择一个训练样本计算损失函数的梯度,然后根据梯度更新参数。
- 优点:每次计算梯度只需要处理一个样本,计算量小,训练速度快,能快速跳出局部最优解。
- 缺点:由于每次使用的样本随机,计算的梯度波动较大,收敛过程不稳定,可能在最优值附近震荡。
- 应用场景:适用于训练样本数量较大的场景,尤其是在模型训练的初期,需要快速迭代的情况。
- 小批量梯度下降(Mini-Batch Gradient Descent,MBGD)
- 原理:每次使用一小批训练样本(通常称为 Batch Size)计算损失函数的梯度,然后根据梯度更新参数。Batch Size 的取值通常为 32、64、128 等。
- 优点:综合了批量梯度下降和随机梯度下降的优点,既保证了梯度的稳定性,又提高了训练速度,是目前深度学习中使用最广泛的梯度下降算法。
- 缺点:需要选择合适的 Batch Size,Batch Size 过大可能导致训练速度慢,Batch Size 过小可能导致梯度波动大。
- 应用场景:适用于大多数深度学习任务,如图像识别、自然语言处理等。
3.3.2 梯度下降算法的变种
为了进一步提高梯度下降算法的收敛速度和稳定性,研究人员提出了许多梯度下降算法的变种,其中最常用的有动量法(Momentum)、自适应矩估计(Adam)等。
- 动量法(Momentum)
- 原理:模拟物理中的动量概念,在参数更新时不仅考虑当前的梯度,还考虑上一次参数更新的方向和幅度,从而加速收敛,减少震荡。
- 参数更新公式:
vt=γvt−1+η∇L(θt−1)
,θt=θt−1−vt
,其中vt
为第t
次更新的动量,γ
为动量系数(通常取值为 0.9),vt−1
为第t−1
次更新的动量。
- 优点:在梯度方向一致的情况下,能够加速参数更新;在梯度方向变化较大的情况下,能够减少参数震荡,使收敛更稳定。
- 自适应矩估计(Adam)
- 原理:结合了动量法和自适应学习率的思想,能够为不同的参数自适应地调整学习率,从而提高收敛速度和稳定性。
- 优点:不需要手动调整学习率,对不同类型的任务和数据适应性强,收敛速度快,是目前深度学习中最常用的优化算法之一。
- 应用场景:适用于各种深度学习任务,尤其是在大规模数据集和复杂模型的训练中。
四、深度学习常用框架
深度学习框架是用于构建、训练和部署深度学习模型的工具集合,它们提供了丰富的 API 和预实现的算法,能够大大简化深度学习模型的开发过程,提高开发效率。目前主流的深度学习框架有 TensorFlow、PyTorch 等,下面分别介绍:
4.1 TensorFlow
TensorFlow 是由 Google 开发的开源深度学习框架,于 2015 年首次发布,目前已经成为深度学习领域最流行的框架之一。
4.1.1 特点
- 灵活性高:支持构建各种类型的深度学习模型,如多层感知机、卷积神经网络、循环神经网络、Transformer 等,适用于图像识别、自然语言处理、语音识别、强化学习等多种任务。
- 分布式训练:支持分布式训练,能够将模型训练任务分配到多个 CPU 或 GPU 上,提高训练速度,适用于大规模数据集和复杂模型的训练。
- 部署方便:支持将训练好的模型部署到多种平台,如桌面端(Windows、Linux、macOS)、移动端(Android、iOS)、嵌入式设备(树莓派)等。
- 社区活跃:拥有庞大的用户社区和丰富的学习资源,包括官方文档、教程、开源项目等,遇到问题时容易找到解决方案。
4.1.2 基本使用流程
- 安装 TensorFlow:可以使用 pip 命令进行安装,在命令行中输入:pip install tensorflow(CPU 版本)或pip install tensorflow-gpu(GPU 版本,需要提前安装 CUDA 和 cuDNN)。
- 导入 TensorFlow 库:在 Python 代码中导入 TensorFlow 库,通常简称为 tf:import tensorflow as tf。
- 构建模型:使用 TensorFlow 提供的 API 构建深度学习模型,如使用tf.keras(TensorFlow 中的高级 API)构建模型。
- 编译模型:指定模型的优化算法、损失函数和评估指标,如:model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])。
- 训练模型:使用训练数据对模型进行训练,指定训练的轮数(Epoch)和批量大小(Batch Size),如:model.fit(x_train, y_train, epochs=10, batch_size=32)。
- 评估模型:使用测试数据评估模型的性能,如:test_loss, test_acc = model.evaluate(x_test, y_test)。
- 预测:使用训练好的模型对新数据进行预测,如:predictions = model.predict(x_new)。
4.2 PyTorch
PyTorch 是由 Facebook 开发的开源深度学习框架,于 2016 年首次发布,以其动态计算图和简洁的 API 而受到广大开发者的喜爱,尤其在学术界和研究领域使用广泛。
4.2.1 特点
- 动态计算图:PyTorch 采用动态计算图的方式,计算图在运行时动态构建,便于调试和修改模型,尤其适合开发和研究新的模型架构。
- API 简洁易懂:PyTorch 的 API 设计简洁直观,与 Python 的语法风格一致,容易上手,对初学者友好。
- 强大的生态系统:拥有丰富的扩展库,如 TorchVision(用于计算机视觉任务)、TorchText(用于自然语言处理任务)、TorchAudio(用于语音处理任务)等,能够满足不同领域的需求。
- 社区活跃:社区发展迅速,拥有大量的开源项目和学习资源,学术论文中的模型通常会首先在 PyTorch 中实现。
4.2.2 基本使用流程
- 安装 PyTorch:可以在 PyTorch 官方网站(PyTorch)根据自己的操作系统、CUDA 版本等选择合适的安装命令,例如:pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118(GPU 版本,CUDA 11.8)。
- 导入 PyTorch 库:在 Python 代码中导入 PyTorch 库,通常简称为 torch:import torch,同时导入相关的扩展库,如import torchvision。
- 构建模型:定义一个继承自torch.nn.Module的模型类,在__init__方法中定义模型的层,在forward方法中定义模型的前向传播过程。
- 定义损失函数和优化器:选择合适的损失函数和优化器,如:criterion = torch.nn.CrossEntropyLoss(),optimizer = torch.optim.Adam(model.parameters(), lr=0.001)。
- 训练模型:通过循环迭代训练数据,进行前向传播计算损失,反向传播计算梯度,然后使用优化器更新模型参数。
- 评估模型:将模型设置为评估模式(model.eval()),使用测试数据计算模型的损失和准确率,不进行梯度计算(with torch.no_grad():)。
- 预测:使用训练好的模型对新数据进行预测,输出预测结果。
五、深度学习实战实例
前面我们介绍了深度学习的基础概念、核心原理和常用框架,接下来通过两个实战实例,带大家动手构建和训练深度学习模型,分别是基于 TensorFlow 的手写数字识别(图像识别任务)和基于 PyTorch 的文本分类(自然语言处理任务)。
5.1 实例一:基于 TensorFlow 的手写数字识别(MNIST 数据集)
MNIST 数据集是深度学习领域中最经典的数据集之一,包含 60000 个训练样本和 10000 个测试样本,每个样本都是一张 28×28 的灰度手写数字图像,图像中的数字为 0-9 中的一个。我们将使用 TensorFlow 构建一个简单的卷积神经网络(CNN),实现对手写数字的识别。
5.1.1 环境准备
- 操作系统:Windows 10、Linux 或 macOS
- Python 版本:3.7-3.10
- TensorFlow 版本:2.x(本文使用 TensorFlow 2.10)
- 安装命令:pip install tensorflow==2.10
5.1.2 代码实现步骤
- 导入所需库
TypeScript取消自动换行复制
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt
- 加载并预处理 MNIST 数据集
MNIST 数据集已经包含在 TensorFlow 的datasets模块中,可以直接加载。加载后需要对数据进行预处理,包括将像素值归一化到 0-1 之间(原始像素值为 0-255),并将图像数据的形状从 (28, 28) 调整为 (28, 28, 1)(添加通道维度,因为 CNN 需要处理多通道图像,灰度图像的通道数为 1)。
TypeScript取消自动换行复制
# 加载MNIST数据集
(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()
# 查看数据集的形状
print("训练集图像形状:", x_train.shape) # 输出:(60000, 28, 28)
print("训练集标签形状:", y_train.shape) # 输出:(60000,)
print("测试集图像形状:", x_test.shape) # 输出:(10000, 28, 28)
print("测试集标签形状:", y_test.shape) # 输出:(10000,)
# 数据预处理:归一化和添加通道维度
x_train = x_train.reshape((60000, 28, 28, 1)).astype('float32') / 255.0
x_test = x_test.reshape((10000, 28, 28, 1)).astype('float32') / 255.0
# 查看预处理后的数据形状
print("预处理后训练集图像形状:", x_train.shape) # 输出:(60000, 28, 28, 1)
- 可视化数据集样本
为了更直观地了解 MNIST 数据集,我们可以可视化几个训练样本:
TypeScript取消自动换行复制
# 可视化前5个训练样本
plt.figure(figsize=(10, 10))
for i in range(5):
plt.subplot(1, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(x_train[i].reshape(28, 28), cmap=plt.cm.binary)
plt.xlabel(y_train[i])
plt.show()
运行上述代码后,会显示 5 张手写数字图像,每张图像下方标注了对应的真实数字。
- 构建卷积神经网络(CNN)模型
我们构建的 CNN 模型包含以下几层:
- 卷积层 1:32 个 3×3 的卷积核,激活函数为 ReLU,输入形状为 (28, 28, 1)
- 池化层 1:2×2 的最大池化层,步长为 2
- 卷积层 2:64 个 3×3 的卷积核,激活函数为 ReLU
- 池化层 2:2×2 的最大池化层,步长为 2
- 平坦层:将池化层 2 的输出展平为一维向量
- 全连接层 1:128 个神经元,激活函数为 ReLU
- 全连接层 2(输出层):10 个神经元,无激活函数(使用 Softmax 激活函数在损失函数中实现)
TypeScript取消自动换行复制
# 构建CNN模型
model = models.Sequential()
# 卷积层1
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
# 池化层1
model.add(layers.MaxPooling2D((2, 2)))
# 卷积层2
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
# 池化层2
model.add(layers.MaxPooling2D((2, 2)))
# 卷积层3(可选,增加模型复杂度)
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
# 平坦层
model.add(layers.Flatten())
# 全连接层1
model.add(layers.Dense(128, activation='relu'))
# 全连接层2(输出层)
model.add(layers.Dense(10))
运行上述代码后,会输出模型的详细结构,包括每一层的名称、输出形状和参数数量。
- 编译模型
指定模型的优化器、损失函数和评估指标:
- 优化器:Adam 优化器,学习率为 0.001
- 损失函数:稀疏分类交叉熵损失(SparseCategoricalCrossentropy),因为我们的标签是整数形式(如 0、1、2 等),而不是独热编码形式
- 评估指标:准确率(accuracy)
TypeScript取消自动换行复制
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
- 训练模型
使用训练集对模型进行训练,设置训练轮数(epochs)为 10,批量大小(batch_size)为 64,并使用 10% 的训练数据作为验证集(validation_split=0.1),用于在训练过程中评估模型的泛化能力:
TypeScript取消自动换行复制
history = model.fit(x_train, y_train, epochs=10, batch_size=64, validation_split=0.1)
训练过程中,会输出每一轮的训练损失、训练准确率、验证损失和验证准确率。随着训练轮数的增加,训练损失和验证损失会逐渐减小,训练准确率和验证准确率会逐渐提高。
- 评估模型
使用测试集评估训练好的模型的性能:
TypeScript取消自动换行复制
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print("\n测试集准确率:", test_acc)
运行上述代码后,会输出模型在测试集上的损失和准确率。通常情况下,经过 10 轮训练后,模型在测试集上的准确率可以达到 99% 左右。
- 模型预测
使用训练好的模型对测试集中的样本进行预测,并可视化预测结果:
TypeScript取消自动换行复制
# 为模型添加Softmax层,将输出转换为概率
probability_model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])
# 对测试集进行预测
predictions = probability_model.predict(x_test)
# 定义一个函数,用于可视化预测结果
def plot_image(i, predictions_array, true_label, img):
predictions_array, true_label, img = predictions_array, true_label[i], img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.imshow(img.reshape(28, 28), cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array)
if predicted_label == true_label:
color = 'blue'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(predicted_label,
100*np.max(predictions_array),
true_label),
color=color)
def plot_value_array(i, predictions_array, true_label):
predictions_array, true_label = predictions_array, true_label[i]
运行上述代码后,会显示 10 张测试样本图像,每张图像下方标注了模型的预测结果、预测概率和真实标签。其中,蓝色表示预测正确,红色表示预测错误;右侧的条形图表示模型对每个数字的预测概率,红色条形表示预测的数字,蓝色条形表示真实的数字。
5.2 实例二:基于 PyTorch 的文本分类(IMDB 影评数据集)
IMDB 影评数据集是一个用于情感分析的数据集,包含 50000 条影评,其中 25000 条为训练数据,25000 条为测试数据,每条影评都被标记为 “正面”(积极情感)或 “负面”(消极情感),是一个二分类任务。我们将使用 PyTorch 和 TorchText 构建一个基于循环神经网络(RNN)的文本分类模型,实现对影评情感的判断。
5.2.1 环境准备
- 操作系统:Windows 10、Linux 或 macOS
- Python 版本:3.7-3.10
- PyTorch 版本:1.10 及以上(本文使用 PyTorch 1.13)
- TorchText 版本:0.14 及以上(本文使用 TorchText 0.14)
- 安装命令:
- PyTorch:pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117(GPU 版本,CUDA 11.7)
- TorchText:pip install torchtext==0.14
5.2.2 代码实现步骤
- 导入所需库
TypeScript取消自动换行复制
import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.datasets import IMDB
from torchtext.data.utils import get_tokenizer
from torchtext.vocab import build_vocab_from_iterator
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import time
- 设置全局参数
TypeScript取消自动换行复制
# 设置设备:优先使用GPU,若无GPU则使用CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("使用的设备:", device)
# 文本处理参数
tokenizer = get_tokenizer('basic_english') # 使用基本的英语分词器
batch_size = 64 # 批量大小
embedding_dim = 128 # 词嵌入维度
hidden_dim = 256 # RNN隐藏层维度
num_layers = 2 # RNN层数
num_classes = 2 # 分类数量(正面、负面)
epochs = 10 # 训练轮数
learning_rate = 0.001 # 学习率
- 加载并预处理 IMDB 数据集
首先,我们需要加载 IMDB 数据集,并对文本数据进行预处理,包括分词、构建词汇表、将文本转换为数字序列等。
3.1 加载数据集
TypeScript取消自动换行复制
# 加载IMDB训练集和测试集
train_iter, test_iter = IMDB(split=('train', 'test'))
3.2 构建词汇表
词汇表将文本中的每个单词映射到一个唯一的索引,便于后续将文本转换为数字序列。我们使用训练集中的文本构建词汇表,并设置最小出现次数为 2(过滤掉出现次数过少的稀有单词)。
TypeScript取消自动换行复制
# 定义一个函数,用于生成文本的分词迭代器
def yield_tokens(data_iter):
for label, text in data_iter:
yield tokenizer(text)
# 构建词汇表
vocab = build_vocab_from_iterator(yield_tokens(train_iter), min_freq=2, specials=['<unk>', '<pad>'])
# 设置未知单词的索引为0,填充单词的索引为1
vocab.set_default_index(vocab['<unk>'])
# 查看词汇表大小
vocab_size = len(vocab)
print("词汇表大小:", vocab_size)
3.3 定义文本和标签的处理函数
- 文本处理函数:将文本分词后,转换为对应的索引序列,并根据指定的最大长度进行截断或填充(确保每个批次的文本序列长度相同)。
- 标签处理函数:将标签('pos' 或 'neg')转换为数字(1 或 0)。
TypeScript取消自动换行复制
# 定义最大序列长度(根据数据集的文本长度分布设置,此处设置为500)
max_seq_len = 500
# 文本处理函数
def text_pipeline(text):
tokens = tokenizer(text)
# 将分词后的文本转换为索引序列
indices = vocab(tokens)
# 截断或填充序列,使其长度为max_seq_len
if len(indices) < max_seq_len:
indices += [vocab['<pad>']] * (max_seq_len - len(indices))
else:
indices = indices[:max_seq_len]
return torch.tensor(indices, dtype=torch.int64)
# 标签处理函数
def label_pipeline(label):
return torch.tensor(1 if label == 'pos' else 0, dtype=torch.int64)
3.4 创建数据加载器
使用DataLoader将预处理后的数据转换为批量数据,便于模型训练和评估。
TypeScript取消自动换行复制
# 定义一个_collate_fn函数,用于将一个批次的样本转换为张量
def collate_batch(batch):
label_list, text_list = [], []
for (_label, _text) in batch:
label_list.append(label_pipeline(_label))
text_list.append(text_pipeline(_text))
# 将标签列表和文本列表转换为张量,并移动到指定设备
label_tensor = torch.stack(label_list).to(device)
text_tensor = torch.stack(text_list).to(device)
return text_tensor, label_tensor
# 创建训练集和测试集的数据加载器
train_loader = DataLoader(train_iter, batch_size=batch_size, shuffle=True, collate_fn=collate_batch)
test_loader = DataLoader(test_iter, batch_size=batch_size, shuffle=False, collate_fn=collate_batch)
# 查看数据加载器的批次数量
print("训练集批次数量:", len(train_loader))
print("测试集批次数量:", len(test_loader))
- 构建 RNN 文本分类模型
我们构建的 RNN 模型包含以下几层:
- 词嵌入层(Embedding):将文本的索引序列转换为词嵌入向量,维度为 (embedding_dim)。
- 双向 LSTM 层(BiLSTM):LSTM 是一种常用的 RNN 变体,能够有效处理长序列数据;双向 LSTM 可以同时从左到右和从右到左处理序列,捕捉更多的上下文信息。
- 全连接层(Linear):将 LSTM 层的输出映射到分类结果,输出维度为 (num_classes)。
TypeScript取消自动换行复制
class RNNTextClassifier(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim, num_layers, num_classes):
super(RNNTextClassifier, self).__init__()
# 词嵌入层
self.embedding = nn.Embedding(vocab_size, embedding_dim)
# 双向LSTM层
self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers, bidirectional=True, batch_first=True, dropout=0.5)
# 全连接层:双向LSTM的输出维度为2*hidden_dim(正向和反向各hidden_dim)
self.fc = nn.Linear(hidden_dim * 2, num_classes)
# Dropout层:防止过拟合
self.dropout = nn.Dropout(0.5)
def forward(self, x):
# x: (batch_size, max_seq_len)
# 词嵌入:(batch_size, max_seq_len, embedding_dim)
embedded = self.dropout(self.embedding(x))
# LSTM层:output: (batch_size, max_seq_len, 2*hidden_dim), (h_n, c_n): 隐藏状态和细胞状态
output, (h_n, c_n) = self.lstm(embedded)
# 取LSTM最后一个时间步的输出作为全连接层的输入:(batch_size, 2*hidden_dim)
# 因为是双向LSTM,所以需要将正向和反向的最后一个时间步的输出拼接起来
last_hidden = torch.cat((h_n[-2, :, :], h_n[-1, :, :]), dim=1)
last_hidden = self.dropout(last_hidden)
# 全连接层:(batch_size, num_classes)
logits = self.fc(last_hidden)
return logits
# 创建模型实例,并移动到指定设备
model = RNNTextClassifier(vocab_size, embedding_dim, hidden_dim, num_layers, num_classes).to(device)
- 定义损失函数和优化器
- 损失函数:使用交叉熵损失函数(CrossEntropyLoss),适用于分类任务。
- 优化器:使用 Adam 优化器,学习率为 0.001。
TypeScript取消自动换行复制
# 定义损失函数
criterion = nn.CrossEntropyLoss()
# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
- 训练模型
在训练过程中,我们会记录每一轮的训练损失、训练准确率、验证损失和验证准确率(此处使用测试集作为验证集,实际项目中建议单独划分验证集),以便后续分析模型的训练效果。
TypeScript取消自动换行复制
# 记录训练过程中的指标
train_losses = []
train_accs = []
test_losses = []
test_accs = []
# 开始训练
start_time = time.time()
for epoch in range(epochs):
# 训练模式
model.train()
train_loss = 0.0
train_correct = 0
train_total = 0
for batch_idx, (texts, labels) in enumerate(train_loader):
# 清零梯度
optimizer.zero_grad()
# 前向传播
outputs = model(texts)
loss = criterion(outputs, labels)
# 反向传播和参数更新
loss.backward()
optimizer.step()
# 计算训练损失和准确率
train_loss += loss.item() * texts.size(0)
_, predicted = torch.max(outputs.data, 1)
train_total += labels.size(0)
- 可视化训练过程
通过绘制训练损失、训练准确率、测试损失和测试准确率随训练轮数的变化曲线,直观地分析模型的训练效果:
TypeScript取消自动换行复制
ax2.set_xlabel('训练轮数(Epoch)')
ax2.set_ylabel('准确率(%)')
ax2.set_title('训练准确率和测试准确率随训练轮数的变化')
ax2.legend()
ax2.grid(True)
# 保存图形
plt.savefig('imdb_train_process.png', dpi=300, bbox_inches='tight')
plt.show()
运行上述代码后,会生成一张包含两条曲线的图片,分别展示了训练损失、测试损失和训练准确率、测试准确率随训练轮数的变化情况。如果训练损失和测试损失逐渐减小,训练准确率和测试准确率逐渐提高,并且测试准确率稳定在较高水平(如 85% 以上),说明模型训练效果良好。
- 模型预测
使用训练好的模型对新的影评文本进行情感预测:
TypeScript取消自动换行复制
text_tensor = text_pipeline(text).unsqueeze(0).to(device) # 添加批次维度
# 模型预测
model.eval()
with torch.no_grad():
outputs = model(text_tensor)
_, predicted = torch.max(outputs.data, 1)
probability = torch.softmax(outputs, dim=1)[0][predicted].item()
# 返回预测结果和概率
sentiment = '正面' if predicted.item() == 1 else '负面'
return sentiment, probability
# 测试几个新的影评文本
test_texts = [
"This movie is amazing! The plot is interesting and the acting is excellent. I really enjoyed watching it.",
"I don't like this movie at all. The story is boring and the characters are not well-developed. It's a waste of time.",
"The movie has some good parts, but overall it's just average. I wouldn't recommend it to my friends."
]
for text in test_texts:
sentiment, probability = predict_sentiment(text)
print(f"影评文本:{text}")
print(f"预测情感:{sentiment},置信度:{probability:.4f}\n")
运行上述代码后,会输出每个测试影评文本的预测情感(正面或负面)和对应的置信度(概率)。例如,对于第一条积极的影评,模型会预测为 “正面”,置信度较高;对于第二条消极的影评,模型会预测为 “负面”,置信度也较高。
六、深度学习学习资源推荐
深度学习的学习是一个持续的过程,除了本文介绍的内容外,还有很多优秀的学习资源可以帮助大家进一步深入学习,下面推荐一些常用的学习资源:
6.1 书籍
- 《深度学习》(Goodfellow 等著):被誉为 “深度学习圣经”,全面系统地介绍了深度学习的理论基础、核心算法和应用场景,适合有一定数学基础的学习者。
- 《深度学习入门:基于 Python 的理论与实现》(斋藤康毅著):从零基础开始,用通俗易懂的语言和丰富的实例讲解深度学习的基本概念和实现方法,适合初学者。
- 《Python 深度学习》(François Chollet 著):由 Keras 创始人编写,以 TensorFlow 和 Keras 为工具,详细介绍了深度学习在图像识别、自然语言处理等领域的应用,注重实战。
- 《PyTorch 深度学习实战》(Ian Pointer 著):以 PyTorch 为工具,通过大量的实战案例讲解深度学习的模型构建和训练方法,适合想要学习 PyTorch 的学习者。
6.2 在线课程
- Coursera《Deep Learning Specialization》(Andrew Ng 主讲):由深度学习领域的权威专家 Andrew Ng 主讲,包含 5 门课程,从基础到进阶,系统讲解深度学习的理论和应用,配套大量的编程作业,适合初学者。
- Stanford CS231n《Convolutional Neural Networks for Visual Recognition》:斯坦福大学的计算机视觉课程,详细介绍了卷积神经网络的原理和在图像识别、目标检测等领域的应用,课程内容深入,适合想要深入学习计算机视觉的学习者。
- Stanford CS224n《Natural Language Processing with Deep Learning》:斯坦福大学的自然语言处理课程,介绍了深度学习在自然语言处理领域的应用,如词嵌入、循环神经网络、Transformer 等,适合想要深入学习自然语言处理的学习者。
- B 站《PyTorch 深度学习实战》(刘二大人主讲):国内知名 UP 主刘二大人讲解的 PyTorch 课程,内容通俗易懂,实战性强,适合零基础学习 PyTorch 的学习者。
6.3 开源项目和社区
- TensorFlow 官方文档和教程:https://www.tensorflow.org/tutorials,提供了丰富的教程和实例,涵盖了各种深度学习任务和模型。
- PyTorch 官方文档和教程:Welcome to PyTorch Tutorials — PyTorch Tutorials 2.8.0+cu128 documentation,提供了详细的 API 文档和入门教程,帮助学习者快速掌握 PyTorch 的使用。
- GitHub:全球最大的开源代码托管平台,上面有大量的深度学习开源项目,如 TensorFlow Models、PyTorch Examples 等,可以通过学习和复现这些项目提高实战能力。
- Kaggle:数据科学和机器学习竞赛平台,上面有大量的数据集和竞赛项目,学习者可以通过参加竞赛锻炼自己的深度学习技能,同时学习其他优秀选手的解决方案。
七、总结
本文从深度学习的基础概念入手,详细介绍了深度学习与机器学习的区别、人工神经网络的结构与工作原理、深度学习的核心原理(激活函数、损失函数、优化算法)、常用的深度学习框架(TensorFlow、PyTorch),并通过两个实战实例(手写数字识别、文本分类)带大家动手构建和训练了深度学习模型。
深度学习是一个充满挑战和机遇的领域,学习过程中可能会遇到很多困难,比如数学理论的理解、模型参数的调优、计算资源的限制等。但只要坚持学习,多动手实践,不断积累经验,就一定能够逐步掌握深度学习的核心技能,为未来的学习和工作打下坚实的基础。
希望本文能够帮助大家顺利入门深度学习,开启自己的 AI 之旅!如果在学习过程中遇到问题,欢迎在评论区留言讨论,也可以参考本文推荐的学习资源进一步深入学习。
更多推荐
所有评论(0)