From 53973b6604259595b2e3608d196b1803ec9c10b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=BE=E5=B3=B6=E9=A3=8E=E8=B5=B7=E6=99=82?= Date: Thu, 25 Sep 2025 04:05:18 +0800 Subject: [PATCH] Add files via upload --- Chapter27/27-1.md | 46 +++++++++++++++++++++++++++++ Chapter27/27-10.md | 45 +++++++++++++++++++++++++++++ Chapter27/27-11.md | 40 ++++++++++++++++++++++++++ Chapter27/27-2.md | 36 +++++++++++++++++++++++ Chapter27/27-3.md | 60 ++++++++++++++++++++++++++++++++++++++ Chapter27/27-4.md | 48 +++++++++++++++++++++++++++++++ Chapter27/27-5.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++ Chapter27/27-6.md | 47 ++++++++++++++++++++++++++++++ Chapter27/27-7.md | 68 +++++++++++++++++++++++++++++++++++++++++++ Chapter27/27-8.md | 58 +++++++++++++++++++++++++++++++++++++ Chapter27/27-9.md | 45 +++++++++++++++++++++++++++++ 11 files changed, 565 insertions(+) create mode 100644 Chapter27/27-1.md create mode 100644 Chapter27/27-10.md create mode 100644 Chapter27/27-11.md create mode 100644 Chapter27/27-2.md create mode 100644 Chapter27/27-3.md create mode 100644 Chapter27/27-4.md create mode 100644 Chapter27/27-5.md create mode 100644 Chapter27/27-6.md create mode 100644 Chapter27/27-7.md create mode 100644 Chapter27/27-8.md create mode 100644 Chapter27/27-9.md diff --git a/Chapter27/27-1.md b/Chapter27/27-1.md new file mode 100644 index 0000000..624b275 --- /dev/null +++ b/Chapter27/27-1.md @@ -0,0 +1,46 @@ +### 介绍下 SVM + +**SVM 的核心思想:寻找最佳分隔超平面** + +想象一下,你有一堆红色的球和蓝色的球,你想用一根线把它们分开。你可以画出无数条线,但哪条线是最好的呢? + +SVM 的答案是:那条离**最近**的红色球和蓝色的球都最远的线 + +在二维空间中,这个“线”就是一个**分隔平面**(Separating Plane)。在三维空间中,它是一个平面。在更高维度的空间中,我们称它为**超平面**(Hyperplane) + +- **支持向量(Support Vectors)**:这些“最近”的数据点就是**支持向量**。它们是距离超平面最近的那些训练样本点,它们决定了超平面的位置。如果移除或移动这些支持向量,超平面的位置可能会发生变化。但如果移动那些离超平面很远的数据点,超平面则保持不变 +- **最大间隔(Maximal Margin)**:超平面到最近支持向量的距离被称为**间隔**(Margin)。SVM 的目标就是找到一个超平面,使得这个间隔最大化。这个最大间隔的超平面就是最优超平面 + +**线性可分与非线性可分** + +**1. 线性可分(Linear SVM)** + +当数据可以被一条直线(或一个超平面)完美分开时,我们称其为**线性可分**。在这种情况下,我们可以直接使用上面提到的最大间隔方法来找到最优超平面。 + +**2. 非线性可分(Kernel SVM)** + +大多数情况下,数据点并非线性可分。例如,红色球和蓝色球可能混合在一起,你无法用一条直线将它们完全分开 + +这就是 SVM 强大之处的体现:**核技巧**(Kernel Trick) + +核技巧的核心思想是,将原始的低维数据**映射**到一个更高维度的空间,在这个高维空间中,数据变得**线性可分**了 + +举个例子:一个二维的圆环,内圈是蓝色,外圈是红色。在二维平面上,你无法用一条直线将它们分开。但是,如果我们将这些点映射到三维空间,它们可能就会在一个平面上呈“碗状”分布,这时我们就可以用一个平面将它们分开了 + +常用的核函数有: + +- **线性核(Linear Kernel)**:当数据本身线性可分时使用 +- **多项式核(Polynomial Kernel)**:可以将低维数据映射到高维 +- **径向基函数核(RBF Kernel)**:也叫高斯核,这是最常用、最强大的核函数之一,它能够将样本映射到无穷维度的空间,从而处理非常复杂的数据集 + +**软间隔与惩罚参数 C** + +在现实世界中,完美的分隔几乎不存在,数据集中往往会有一些“异常值”或“噪声”点,它们会混入另一个类别。如果我们坚持找到一个完美的超平面,模型可能会**过拟合** + +为了解决这个问题,SVM 引入了**软间隔(Soft Margin)**的概念。它允许一些数据点“越界”,即它们可以出现在间隔内部,甚至位于错误的一侧 + +- **惩罚参数 C(Penalty Parameter C)**:这个参数用来控制**容忍度** + - **C 值很小**:模型容忍度高,允许更多数据点越界。这会使间隔更大,但可能会导致一些误分类,因此更不容易过拟合 + - **C 值很大**:模型容忍度低,对错误分类的惩罚很高。这会使间隔变小,模型试图精确地分离所有训练数据,因此更容易过拟合 + +选择合适的 C 值是一个重要的调优步骤 \ No newline at end of file diff --git a/Chapter27/27-10.md b/Chapter27/27-10.md new file mode 100644 index 0000000..75fa422 --- /dev/null +++ b/Chapter27/27-10.md @@ -0,0 +1,45 @@ +### 如何降低模型的误报率 + +**1. 调整决策阈值** + +这是最直接、最常用的方法 + +- **原理**:分类模型通常会输出一个概率值,例如,预测一个邮件是垃圾邮件的概率为0.8。我们设定一个**决策阈值**(比如0.5),如果概率高于这个阈值,就预测为“垃圾邮件” +- **如何操作**:为了降低误报率,我们可以**提高这个决策阈值**。如果我们将阈值从0.5提高到0.7,模型只有在对一个邮件有更高的信心时,才会将其标记为垃圾邮件。这样做会减少误报,但代价是可能会增加漏报(False Negative),即遗漏掉一些真正的垃圾邮件 +- **适用场景**:当你更看重准确性(Precision)而不是召回率(Recall)时,例如在金融诈骗检测中,宁可漏掉一些诈骗,也不想错误地阻止用户的正常交易 + +**2. 收集更多高质量的负样本** + +**负样本**(Negative Samples)指的是不属于你所关注的类别的样本 + +- **原理**:模型之所以误报,是因为它可能没有见过足够多的负样本,或者负样本的种类太少,导致它对“负类别”的理解不够全面。当一个负样本的特征与正样本(你关注的类别)相似时,模型就很容易误判 +- **如何操作**: + - **增加负样本数量**:尽可能收集更多不属于目标类别的数据,让模型有更多机会去学习真正的“负类”是什么样的 + - **增加负样本多样性**:特别关注那些容易被误报的**“困难负样本”**(Hard Negative Samples),并将它们加入训练集。这能强制模型学习更细粒度的边界,从而更好地区分正负样本 +- **适用场景**:当你的数据集存在严重的类别不平衡问题时 + +**3. 重新设计特征** + +模型的性能很大程度上取决于你给它什么样的数据 + +- **原理**:如果模型总是误报某个特定类型的样本,很可能是因为当前的特征无法很好地区分这个样本和目标类别。 +- **如何操作**: + - **添加新特征**:思考并提取一些能更好地区分正负样本的新特征。例如,在垃圾邮件识别中,除了关键词,还可以加入发件人信誉度、邮件格式异常等特征 + - **特征选择**:移除那些与目标无关或有误导性的特征,这可以帮助模型更专注于重要的信息 +- **适用场景**:当你发现模型的误报不是随机的,而是集中在某一类特定数据上时 + +**4. 调整损失函数或使用惩罚项** + +- **原理**:标准的交叉熵损失函数对正负样本的错误预测给予相同的惩罚。为了降低误报,我们可以对误报(FP)给予更大的惩罚 +- **如何操作**: + - **自定义损失函数**:设计一个**加权的损失函数**,给误报(将负样本预测为正样本)赋予更高的权重,让模型在训练过程中更努力地避免这种错误 + - **Focal Loss**:这是一种专门用于处理类别不平衡问题的损失函数,它能降低对那些易于分类的样本的权重,而更关注那些难以分类的样本,从而迫使模型去学习区分那些“困难”的负样本 +- **适用场景**:在类别极度不平衡,且误报代价极高的场景 + +**5. 集成学习** + +- **原理**:集成学习通过结合多个模型的预测结果来提高整体性能。这可以减少单个模型因过拟合或对数据敏感而产生的误报 +- **如何操作**: + - **投票法**:训练多个不同的模型,让它们对同一个样本进行预测,最终由多数模型投票决定结果。这可以有效减少单个模型出错带来的影响 + - **提升法(Boosting)**:如 **LightGBM** 或 **XGBoost**。这些算法会顺序地训练一系列弱分类器,每个后续分类器都会重点关注前一个分类器错误分类的样本,尤其是那些容易被误报的样本 +- **适用场景**:当你拥有足够的数据和计算资源来训练多个模型时 \ No newline at end of file diff --git a/Chapter27/27-11.md b/Chapter27/27-11.md new file mode 100644 index 0000000..dfe95cc --- /dev/null +++ b/Chapter27/27-11.md @@ -0,0 +1,40 @@ +### 如何找攻击样本 + +**1. 基于梯度的攻击** + +这是最常见、最基础的一类攻击方法,利用模型的梯度信息来生成扰动 + +- **原理**:利用反向传播,计算损失函数相对于输入图像的梯度。这个梯度表示了如果对图像的像素进行微小改变,会如何影响模型的预测结果。通过沿着这个梯度方向对图像进行调整,可以最大化模型的损失,从而导致模型分类错误 +- **代表算法**: + - **快速梯度符号法(FGSM, Fast Gradient Sign Method)**:一种非常高效的单步攻击。它计算损失函数对输入图像的梯度,然后沿着梯度的正负号方向给图像加上一个微小的扰动 + - **基本迭代方法(BIM, Basic Iterative Method)/FGSM 迭代版**:FGSM 的多次迭代版本。它在每一步迭代中都计算梯度并进行小幅调整,以更精确地将图像推向模型的决策边界之外 +- **优点**:计算效率高,生成速度快 +- **缺点**:生成的对抗样本可能不够隐蔽,有时会留下人眼可见的痕迹 + +**2. 基于优化的攻击** + +这类方法将寻找对抗样本看作一个优化问题,旨在找到最小的扰动,同时确保模型分类失败 + +- **原理**:设定一个优化目标,比如最小化扰动的大小(通常用L2或L∞范数来衡量),同时满足模型对新样本的预测结果是错误的 +- **代表算法**: + - **Carlini & Wagner(C&W)攻击**:这是最强大的白盒攻击之一。它通过一个精心设计的损失函数,可以生成非常小且难以被防御方法检测到的对抗样本。这种攻击的成功率非常高,但计算成本也相对较高 +- **优点**:生成的对抗样本扰动非常小,视觉效果极佳,难以被发现 +- **缺点**:计算复杂,生成速度慢 + +**3. 基于生成模型的攻击** + +这类方法利用生成对抗网络(GAN)来生成对抗样本,而非直接基于梯度或优化 + +- **原理**:训练一个生成器网络,它可以将随机噪声或正常样本作为输入,直接生成对抗性扰动。这个生成器被训练来欺骗一个分类器(判别器) +- **优点**:一旦生成器训练好,生成对抗样本的速度非常快,几乎可以实时生成 +- **缺点**:生成器的训练过程复杂且不稳定,可能需要大量数据和计算资源 + +**4. 黑盒攻击** + +以上方法都是**白盒攻击**,即需要知道模型的内部结构和参数。而**黑盒攻击**则更具挑战性,它不需要知道模型的内部信息,只能通过输入和输出来寻找攻击样本 + +- **原理**: + - **基于迁移性(Transferability)**:在白盒模型(通常是公开的、结构相似的模型)上生成对抗样本,然后将这些样本用于攻击目标黑盒模型。研究表明,对抗样本在不同模型之间具有一定的**迁移性** + - **基于查询(Query-based)**:通过向目标模型发送大量查询,观察其输出,并利用这些信息来估计模型的决策边界,从而生成攻击样本。这种方法通常需要大量的查询次数,但效果也更精确 +- **优点**:适用于无法获取模型参数的实际应用场景 +- **缺点**:通常比白盒攻击效率低,成功率也不如白盒攻击高 \ No newline at end of file diff --git a/Chapter27/27-2.md b/Chapter27/27-2.md new file mode 100644 index 0000000..3582c15 --- /dev/null +++ b/Chapter27/27-2.md @@ -0,0 +1,36 @@ +### 介绍下 KNN + +**KNN 的核心思想:近朱者赤,近墨者黑** + +KNN 算法的原理非常直观,可以比喻成“物以类聚,人以群分” + +想象一下,你有一张地图,上面标注了各种餐馆的类型(中餐、西餐、日料)。现在,你想在地图上给一个新的、未标注的餐馆进行分类 + +KNN 的做法是: + +1. **找到距离最近的邻居**:首先,找到离这个新餐馆最近的 K 个已知的餐馆 +2. **统计邻居的类别**:然后,统计这 K 个最近邻居中,哪种餐馆类型出现的次数最多 +3. **做出预测**:将出现次数最多的那个类别,作为新餐馆的预测类别 + +在这个例子中,**K** 就是你选择的“邻居”数量。如果 K=3,你就会考察离新餐馆最近的 3 个餐馆的类型;如果 K=5,你就会考察 5 个 + +**KNN 算法的步骤** + +1. **确定 K 值**:选择一个合适的整数 K。这个 K 值是算法中唯一也是最重要的参数 +2. **计算距离**:对于每一个待分类的样本,计算它与所有训练集中样本的距离。常见的距离度量有: + - **欧氏距离(Euclidean Distance)**:两点之间直线距离,最常用 + - **曼哈顿距离(Manhattan Distance)**:两点在坐标轴上移动的距离之和 +3. **排序**:将计算出的所有距离进行升序排序 +4. **选择 K 个最近邻**:选取距离最小的前 K 个样本 +5. **投票或取均值**: + - **分类任务**:统计这 K 个样本所属类别中出现频率最高的那个类别,作为最终的预测类别 + - **回归任务**:计算这 K 个样本的标签值的平均值,作为最终的预测值 + +**如何选择 K 值?** + +K 值的选择对 KNN 算法的性能影响很大: + +- **K 值太小**:模型会变得非常复杂,对噪声数据和异常点非常敏感,容易导致**过拟合** +- **K 值太大**:模型会变得过于简单,忽略了局部数据的特点,可能导致**欠拟合** + +通常情况下,K 的值会根据数据的具体情况进行选择,常用的方法是通过交叉验证(Cross-validation)来寻找最优的 K 值 \ No newline at end of file diff --git a/Chapter27/27-3.md b/Chapter27/27-3.md new file mode 100644 index 0000000..5c8d5aa --- /dev/null +++ b/Chapter27/27-3.md @@ -0,0 +1,60 @@ +### 卷积神经网络介绍 + +**CNN 是什么?** + +**卷积神经网络**是一种专门用于处理具有类似网格结构数据(如图像)的**深度学习**模型。它的灵感来源于人脑的**视觉皮层**,该皮层中的神经元只对视野中的特定区域敏感 + +简单来说,CNN 的核心思想是:**与其让模型去一次性地看一张完整的图片,不如让它通过一个“小窗口”去扫描图片,逐块地提取特征,然后再把这些局部特征组合起来,形成对整个图像的理解。** 这种“局部到整体”的处理方式,使得 CNN 在图像识别、目标检测等领域取得了巨大成功 + +**CNN 的核心构成** + +一个典型的 CNN 主要由以下几个核心层组成: + +**1. 卷积层** + +这是 CNN 最核心的部分。它通过一个被称为**卷积核**(Kernel)或**滤波器**(Filter)的小窗口,在输入图像上进行滑动扫描 + +- **卷积核**:这个小窗口是一个矩阵,里面装着一系列权重 +- **滑动**:卷积核从图像的左上角开始,以一定的步长(Stride)向右和向下移动 +- **计算**:在每一次滑动到新位置时,卷积核会与它覆盖的图像区域进行**点乘**(Element-wise Multiplication),然后将结果相加,得到一个新的数值。这个新的数值代表了该区域的特征 +- **特征图**(Feature Map):卷积核在整个图像上滑动完成后,会生成一个由这些新数值组成的矩阵,我们称之为**特征图**。这个特征图捕捉了图像中某种特定的局部特征,比如边缘、纹理或颜色 + +通过使用多个不同的卷积核,我们可以提取出图像中多种多样的特征 + +**2. 激活层** + +在卷积层计算出结果后,通常会紧跟一个**激活函数**,例如 **ReLU**(Rectified Linear Unit) + +- **作用**:激活函数引入了**非线性**。如果没有激活函数,神经网络无论有多少层,最终都只会是线性的,无法学习和表达复杂的模式 +- **ReLU**:它的工作原理非常简单:`ReLU(x) = max(0, x)`。如果输入值大于0,它就保持不变;如果小于等于0,它就变为0。这使得模型能够更好地学习复杂的、非线性的特征 + +**3. 池化层** + +池化层的作用是**压缩特征图**,减少数据维度,从而: + +- **减少参数数量**:降低计算量,防止**过拟合** +- **增强特征的鲁棒性**:使得模型对图像中的小幅平移、旋转或缩放变化不那么敏感 + +最常用的池化方法是**最大池化**(Max Pooling):在一个小区域内(比如2x2),只保留最大的那个值,而舍弃其他值 + +**4. 全连接层** + +在经过多层卷积和池化之后,图像的特征被抽象和压缩。这时,我们通常会使用一个或多个**全连接层** + +- **作用**:全连接层将前面所有提取到的局部特征**整合**起来,进行更高层次的抽象 +- **工作方式**:全连接层中的每个神经元都与前一层的所有神经元相连,就像传统的神经网络一样。 + +**5. 输出层** + +全连接层的最后一层通常是**输出层**,它负责给出最终的预测结果 + +- **分类问题**:如果是一个图像分类任务,输出层会使用 **Softmax** 激活函数来输出每个类别的概率 + +**CNN 的工作流程概览** + +1. **输入**:一张原始图像 +2. **多层卷积和激活**:通过多层**卷积层**和**激活层**,从图像中逐步提取出越来越抽象的特征,比如从边缘、纹理到物体的局部、再到完整的物体 +3. **多层池化**:在卷积层之间插入**池化层**,对特征图进行下采样,减少计算量,并增强模型对图像变化的鲁棒性 +4. **展平(Flatten)**:将最终的特征图**展平**成一个一维的向量 +5. **全连接层**:将这个向量输入到**全连接层**中,进行分类或回归 +6. **输出**:输出最终的预测结果 \ No newline at end of file diff --git a/Chapter27/27-4.md b/Chapter27/27-4.md new file mode 100644 index 0000000..2f380de --- /dev/null +++ b/Chapter27/27-4.md @@ -0,0 +1,48 @@ +### 莱文斯坦距离 + +**莱文斯坦距离是什么?** + +莱文斯坦距离,又被称为**编辑距离**,是衡量两个字符串之间相似度的一种度量方法。它定义了将一个字符串**A**转换成另一个字符串**B**所需的最少**单字符编辑操作**的次数 + +这些允许的单字符编辑操作有三种: + +1. **插入(Insertion)**:在字符串中插入一个字符 +2. **删除(Deletion)**:从字符串中删除一个字符 +3. **替换(Substitution)**:将字符串中的一个字符替换成另一个字符 + +莱文斯坦距离越小,说明两个字符串越相似。反之,距离越大,相似度越低 + +**一个简单的例子** + +让我们用一个例子来直观地理解它 + +计算字符串 **"kitten"** 和 **"sitting"** 的莱文斯坦距离 + +1. **"kitten"** -> **"sitten"**(将 'k' **替换**为 's') +2. **"sitten"** -> **"sittin"**(将 'e' **替换**为 'i') +3. **"sittin"** -> **"sitting"**(在末尾**插入** 'g') + +总共需要 **3** 次编辑操作,所以 "kitten" 和 "sitting" 的莱文斯坦距离是 **3** + +**如何计算?** + +莱文斯坦距离通常使用**动态规划**(Dynamic Programming)算法来计算。这个方法的核心思想是构建一个二维矩阵,其中 `dp[i][j]` 存储的是字符串 A 的前 `i` 个字符与字符串 B 的前 `j` 个字符之间的编辑距离 + +矩阵的填充规则如下: + +- **初始化**: + + - `dp[i][0]` 等于 `i`(将 A 的前 `i` 个字符全部删除) + - `dp[0][j]` 等于 `j`(将 B 的前 `j` 个字符全部插入) + +- **递推关系**:对于矩阵中的每一个 `(i, j)`,我们比较 A 的第 `i` 个字符和 B 的第 `j` 个字符: + + - 如果 `A[i-1]` 等于 `B[j-1]`,说明这两个字符相同,不需要编辑。此时 `dp[i][j] = dp[i-1][j-1]` + - 如果 `A[i-1]` 不等于 `B[j-1]`,我们需要考虑三种操作的最小代价: + - **插入**:`dp[i][j-1] + 1` + - **删除**:`dp[i-1][j] + 1` + - **替换**:`dp[i-1][j-1] + 1` + + 最终,`dp[i][j]` 取这三个值的最小值:`dp[i][j] = min(dp[i][j-1] + 1, dp[i-1][j] + 1, dp[i-1][j-1] + 1)` + +最终,矩阵右下角的 `dp[len(A)][len(B)]` 就是两个字符串的莱文斯坦距离 \ No newline at end of file diff --git a/Chapter27/27-5.md b/Chapter27/27-5.md new file mode 100644 index 0000000..dc0fbe9 --- /dev/null +++ b/Chapter27/27-5.md @@ -0,0 +1,72 @@ +### 倒排索引 + +**倒排索引是什么?** + +倒排索引是一种非常重要且应用广泛的数据结构,它是现代**搜索引擎**的核心技术之一 + +它的基本思想是:**将文档中的每个词语与包含该词语的文档列表关联起来** + +你可以把它想象成一本书后面的**索引**。在这本书的索引里,你不会看到“第10页有...,第11页有...”,而是会看到“**人工智能**:第10页、第11页、第25页...” + +倒排索引就是这样一种结构,它将词语(或术语)作为**键**(Key),将包含该词语的文档列表作为**值**(Value)。这种“词语到文档”的映射关系与传统的“文档到词语”的映射(即正向索引)正好相反,因此得名“倒排” + +**倒排索引的核心构成** + +一个典型的倒排索引通常包含两个部分: + +**1. 词典** + +- **作用**:存储所有文档中出现过的所有**不重复**的词语 +- **结构**:通常是一个经过排序的列表或哈希表。它能让你快速定位到某个词语在倒排列表中的位置。 + +**2. 倒排列表** + +- **作用**:存储每个词语出现的**文档ID**列表,以及该词语在文档中的**位置**、**频率**等信息 +- **结构**:与词典中的每个词语相对应,包含以下信息: + - **文档ID(Document ID)**:包含该词语的文档的唯一标识符 + - **词频(Term Frequency)**:该词语在特定文档中出现的次数 + - **位置(Position)**:该词语在文档中出现的位置(如第几个词)。这对于处理短语查询(比如"人工智能")非常重要 + +**倒排索引的工作原理** + +让我们通过一个简单的例子来理解它的工作流程: + +假设我们有三个文档: + +- **文档1**:`人工智能是未来。` +- **文档2**:`学习人工智能。` +- **文档3**:`未来是美好的。` + +**1. 分词(Tokenization)** + +首先,对每个文档进行分词,得到词语列表 + +- **文档1**:`[“人工智能”, “是”, “未来”]` +- **文档2**:`[“学习”, “人工智能”]` +- **文档3**:`[“未来”, “是”, “美好”]` + +**2. 构建倒排索引** + +然后,我们构建倒排索引,将每个词语与包含它的文档关联起来 + +| 词语 | 倒排列表(文档ID,词频,位置) | +| -------- | ---------------------------------------------- | +| 人工智能 | [ (文档1, tf:1, pos:0), (文档2, tf:1, pos:1) ] | +| 是 | [ (文档1, tf:1, pos:1), (文档3, tf:1, pos:1) ] | +| 未来 | [ (文档1, tf:1, pos:2), (文档3, tf:1, pos:0) ] | +| 学习 | [ (文档2, tf:1, pos:0) ] | +| 美好 | [ (文档3, tf:1, pos:2) ] | + +**3. 查询过程** + +现在,如果你搜索关键词“**人工智能**”,搜索引擎会怎么做呢? + +1. **查询词典**:它会直接在倒排词典中找到“人工智能”这个词 +2. **获取倒排列表**:获取与“人工智能”对应的倒排列表,即 `[ (文档1, ...), (文档2, ...) ]` +3. **返回结果**:根据这个列表,搜索引擎可以立即知道文档1和文档2包含了“人工智能”这个词,并迅速将它们作为搜索结果返回 + +如果你搜索“**人工智能 未来**”,搜索引擎会: + +1. 分别找到“人工智能”和“未来”的倒排列表 +2. 对这两个列表进行**求交集**(Intersection),找到同时包含这两个词的文档ID +3. 最终得到结果是**文档1** \ No newline at end of file diff --git a/Chapter27/27-6.md b/Chapter27/27-6.md new file mode 100644 index 0000000..7e545ec --- /dev/null +++ b/Chapter27/27-6.md @@ -0,0 +1,47 @@ +### 搜索引擎的算法有哪些 + +**1. 爬虫与数据抓取** + +这是整个搜索引擎系统的起点。爬虫是一个自动化程序,它会模拟人浏览网页的行为,从一个起始页面开始,沿着页面中的链接不断地爬取新的网页,并将它们存储到搜索引擎的数据库中 + +- **工作原理**:爬虫会周期性地访问网站,检查是否有新的内容或更新。为了提高效率,它会遵循一些规则,比如不爬取某些禁止访问的页面(通过 `robots.txt` 文件指定),或者优先抓取热门网站和经常更新的页面 + +**2. 索引** + +抓取到的网页原始数据是无法直接用于搜索的。索引过程就是将这些网页数据进行**预处理**和**结构化**,使其能够被快速检索 + +- **分词(Tokenization)**:将网页内容分解成一个个独立的词语(或称为“词项”)。例如,将句子“深度学习算法”分解为“深度”、“学习”、“算法” +- **倒排索引(Inverted Index)**:这是索引的核心。它将词语与包含该词语的文档列表进行关联。这种结构使得搜索引擎可以迅速找到包含特定关键词的所有文档,而不是遍历所有文档 + +**3. 排名算法** + +这是搜索引擎算法中最核心、最复杂的部分,它决定了哪些搜索结果会排在前面。排名算法的目标是根据用户查询,评估每个网页的**相关性(Relevance)\**和\**重要性(Importance)**,并进行排序 + +**a. 文本相关性算法** + +这些算法主要评估查询词和网页内容之间的匹配程度 + +- **TF-IDF(词频-逆文档频率)**:这是一种经典的相关性算法 + - **TF(Term Frequency)**:一个词在文档中出现的频率。如果一个词出现得越多,说明该文档与这个词越相关 + - **IDF(Inverse Document Frequency)**:一个词在所有文档中出现的频率。如果一个词在越少文档中出现,说明它越能区分文档,其权重越高 + - TF-IDF 的核心思想是:一个词在一个文档中出现频率高,但在所有文档中出现频率低,那么这个词对该文档的代表性就越强。 + +**b. 网页重要性算法** + +这些算法旨在评估网页的整体权威性 + +- **PageRank**:这是 Google 早期最著名的排名算法。它的核心思想是:如果一个网页被越多重要的网页链接,那么它本身也越重要。可以把链接看作是一种“投票”。PageRank 会递归地计算每个网页的“重要性得分”,并将其作为排名的一个重要指标。 + +**c. 用户行为算法** + +现代搜索引擎还会考虑用户的行为数据来优化排名 + +- **点击率(CTR)**:如果一个网页在搜索结果中的点击率很高,说明用户认为它很相关,这会提升它的排名 +- **停留时间**:用户在点击某个搜索结果后,在该页面停留的时间长短。如果用户很快就返回搜索结果页,可能说明这个页面不是他们想要的,这会降低它的排名 + +**4. 知识图谱与语义理解** + +现代搜索引擎已经超越了简单的关键词匹配。它们能够理解查询背后的**意图**和**实体** + +- **知识图谱(Knowledge Graph)**:它将实体(如“巴黎”、“埃菲尔铁塔”)及其相互关系组织成一个巨大的网络。当用户搜索“埃菲尔铁塔高度”时,搜索引擎可以直接从知识图谱中返回答案,而无需跳转到网页 +- **自然语言处理(NLP)**:搜索引擎利用 NLP 技术来理解查询的语义。例如,它能理解“苹果”这个词在“苹果公司”和“苹果手机”中的不同含义,从而给出更精准的结果 \ No newline at end of file diff --git a/Chapter27/27-7.md b/Chapter27/27-7.md new file mode 100644 index 0000000..c4f9548 --- /dev/null +++ b/Chapter27/27-7.md @@ -0,0 +1,68 @@ +### 了解 TF-IDF 文档匹配算法吗 + +**TF-IDF 的核心思想** + +TF-IDF 的核心思想可以用一句话概括:**一个词语在一个文档中出现得越多,并且在所有文档中出现得越少,那么它对于该文档的区分度就越高,也就越重要** + +这个算法将一个词语的重要性分为两个部分来计算: + +**1. 词频** + +**TF** 表示一个词语在一个文档中出现的频率。计算公式通常是: + +TF(t,d)=文档 d 中所有词语的总数词语 t 在文档 d 中出现的次数 + +- **作用**:衡量一个词语在**当前文档**中的重要性。一个词在文档中出现得越多,TF 值就越大,表明该词与该文档的相关性可能更高。 + +**2. 逆文档频率(IDF, Inverse Document Frequency)** + +**IDF** 表示一个词语在整个文档集中出现的稀有程度。计算公式通常是: + +IDF(t,D)=log(包含词语 t 的文档数+1文档总数 N) + +这里的 +1 是为了防止分母为零,以避免对未出现的词语产生错误计算。 + +- **作用**:衡量一个词语在**所有文档**中的重要性。 + - 如果一个词语在很多文档中都出现,说明它是一个**通用词**(如“的”、“是”、“了”),它的 IDF 值就会很低,接近于0。 + - 如果一个词语只在很少的文档中出现,说明它是一个**稀有词**,它的 IDF 值就会很高。 + +**TF-IDF 的计算** + +最终,一个词语在一个文档中的 **TF-IDF 值**是 **TF** 和 **IDF** 的乘积: + +TF−IDF(t,d,D)=TF(t,d)×IDF(t,D) + +**举个例子** + +假设我们有一个包含3个文档的文档集: + +- **文档1**:`“我 喜欢 吃 苹果。苹果 很甜。”` +- **文档2**:`“我 喜欢 吃 香蕉。”` +- **文档3**:`“我 喜欢 玩 电脑。”` + +现在,我们来计算“**苹果**”这个词在**文档1**中的 TF-IDF 值 + +1. **计算 TF (苹果, 文档1)** + - “苹果”在文档1中出现了2次 + - 文档1中总共有7个词语 + - TF=72≈0.286 +2. **计算 IDF (苹果, 所有文档)** + - 文档总数 N=3 + - “苹果”只出现在文档1中,所以包含“苹果”的文档数是1 + - IDF=log(1+13)=log(1.5)≈0.176 +3. **计算 TF-IDF (苹果, 文档1)** + - TF−IDF=0.286×0.176≈0.0503 + +如果再计算“**我**”这个词在**文档1**中的 TF-IDF 值: + +1. **计算 TF (我, 文档1)** + - “我”出现了1次 + - TF=71≈0.143 +2. **计算 IDF (我, 所有文档)** + - 文档总数 N=3 + - “我”出现在了所有3个文档中 + - IDF=log(3+13)=log(0.75)≈−0.125 +3. **计算 TF-IDF (我, 文档1)** + - TF−IDF=0.143×(−0.125)≈−0.0178 + +这个负值表明“我”这个词的通用性太高,几乎不具备区分度。而“苹果”这个词的 TF-IDF 值更高,因为它在文档1中频繁出现,但在整个文档集中又相对稀有,因此更能代表文档 1 的主题 \ No newline at end of file diff --git a/Chapter27/27-8.md b/Chapter27/27-8.md new file mode 100644 index 0000000..f3a2b80 --- /dev/null +++ b/Chapter27/27-8.md @@ -0,0 +1,58 @@ +### SGD 和 Adam 的区别 + +**什么是优化算法?** + +在深入了解这两种算法之前,我们先明确一下什么是**优化算法** + +在训练神经网络时,我们的目标是最小化**损失函数**(Loss Function)。损失函数衡量了模型预测结果与真实值之间的差距。优化算法就是一种方法,它告诉我们**如何调整模型的参数**(即权重和偏置),以使损失函数的值越来越小,从而让模型变得越来越准确 + +可以把优化算法想象成在下山时,如何选择每一步的方向和步长,才能最快地到达山谷(即损失函数的最小值) + +**1. 随机梯度下降(SGD, Stochastic Gradient Descent)** + +**基本原理** + +**SGD** 是最基础、最原始的优化算法。它的“随机”体现在:在每一次迭代中,它**随机选择一个样本**(或一小批样本,即 **Mini-Batch SGD**),然后计算这一个(或一小批)样本的损失,并根据这个损失来更新模型的参数 + +这与传统的**批量梯度下降**(Batch Gradient Descent)不同,后者会计算所有训练样本的损失来更新一次参数。 + +**优点** + +- **计算效率高**:由于每次只处理一小批样本,计算量大大减少,尤其是在面对海量数据时 +- **跳出局部最优**:SGD 的“随机性”使得它有机会跳出一些浅的局部最优解,找到更好的全局最优解。 + +**缺点** + +- **收敛速度慢且不稳定**:由于每次的梯度只代表了一小部分数据,更新方向可能会非常“抖动”和不稳定,导致损失函数在下降时像锯齿一样波动 +- **需要手动设置学习率**:如果学习率(步长)太大,可能无法收敛;如果太小,收敛速度会非常慢。而且,整个训练过程都使用同一个固定的学习率,无法根据参数的重要性进行调整 + +**2. Adam(Adaptive Moment Estimation)** + +**基本原理** + +**Adam** 是一种**自适应学习率**的优化算法。它结合了**RMSprop** 和 **Adagrad** 的优点,是目前最常用、效果最好的优化器之一。它的核心思想是:**为每个参数都动态地调整学习率** + +Adam 主要通过计算梯度的**一阶矩**(均值)和**二阶矩**(方差)的指数移动平均值来调整学习率 + +- **一阶矩(mt)**:可以看作是梯度的加权平均,它决定了更新方向 +- **二阶矩(vt)**:可以看作是梯度平方的加权平均,它决定了更新的步长大小 + +Adam 会根据这些动态计算的矩,为不同的参数提供不同的学习率。对于梯度大的参数,它会减小学习率;对于梯度小的参数,它会增加学习率 + +**优点** + +- **收敛速度快**:由于它能够自适应地调整学习率,Adam 在多数情况下比 SGD 收敛得更快 +- **无需手动调优学习率**:大多数情况下,使用 Adam 的默认参数就能取得很好的效果,大大简化了调参过程 +- **能处理稀疏梯度**:对于有稀疏梯度的任务(如自然语言处理),Adam 表现出色 + +**缺点** + +- **可能会收敛到局部最优**:一些研究表明,Adam 在某些特定场景下可能会收敛到比 SGD 差的局部最优解,这是因为它自适应的学习率可能导致它在训练后期学习率过低,无法跳出局部最优 + +| 特性 | SGD | Adam | +| -------- | ------------------------------------------------ | ---------------------------------------------- | +| 学习率 | 固定,需要手动设置 | 自适应,为每个参数动态调整 | +| 更新方式 | 根据单个样本或 mini-batch 的梯度直接更新 | 结合一阶矩和二阶矩,动态调整更新方向和步长 | +| 收敛性 | 波动较大,可能收敛到全局最优 | 平稳且快速收敛,但可能陷入较差的局部最优 | +| 适用性 | 在大型模型或特定任务上需要精细调参,效果可能更好 | 绝大多数场景下都能快速获得不错的效果,易于使用 | +| 调参难度 | 高,需要仔细调整学习率 | 低,默认参数通常表现良好 | \ No newline at end of file diff --git a/Chapter27/27-9.md b/Chapter27/27-9.md new file mode 100644 index 0000000..cfbb8d3 --- /dev/null +++ b/Chapter27/27-9.md @@ -0,0 +1,45 @@ +### 如何缩减模型的检测时延 + +**1. 模型量化** + +**模型量化**是减少模型大小和计算量最直接有效的方法。它将模型参数从浮点数(如32位浮点数)转换为低精度的数据类型(如8位整数) + +- **原理**:浮点运算比整数运算耗时更多。通过将权重和激活值量化为整数,可以利用专门的整数计算单元,从而大幅提高推理速度 +- **优点**: + - 显著减少模型大小,便于部署在移动设备和边缘设备上 + - 大幅降低计算时延,尤其是对于 CPU 和 DSP 等处理器 +- **缺点**:可能会损失一定的模型精度。不过,在许多应用中,这种精度损失是可以接受的。 + +**2. 模型剪枝** + +**模型剪枝**是移除模型中不重要或冗余的连接和神经元,以减小模型体积和计算量 + +- **原理**:在训练好的模型中,很多权重值可能接近于零,对模型的贡献很小。剪枝就是识别并移除这些不重要的权重或神经元 +- **方法**: + - **非结构化剪枝**:移除单个权重,但会使得模型变得稀疏,需要特殊的硬件或库才能加速 + - **结构化剪枝**:移除整个神经元、通道或层,生成的模型结构更紧凑,可以直接在现有硬件上加速 +- **优点**:减少模型大小和计算量,提高推理速度,同时可以保持较高的精度 + +**3. 知识蒸馏** + +**知识蒸馏**是一种训练技巧,它利用一个已经训练好的大型模型(**教师模型**)来指导一个较小的模型(**学生模型**)的学习过程 + +- **原理**:学生模型不仅学习真实标签,还学习教师模型的软目标(Soft Labels,即教师模型输出的概率分布)。这种方法让学生模型在学习过程中获得额外的“知识”,从而在模型大小显著减小的情况下,也能达到接近教师模型的性能 +- **优点**:能够在保持较高精度的前提下,将一个复杂模型的知识转移到一个更小、推理更快的新模型上 + +**4. 优化推理框架** + +使用高效的推理框架可以最大化硬件性能,从而缩短时延 + +- **TensorRT**:NVIDIA 推出的高性能深度学习推理引擎。它可以对模型进行一系列的优化,如量化、层融合(Layer Fusion)等,并为 GPU 生成高度优化的代码,从而大幅提升推理速度 +- **ONNX Runtime**:一个跨平台的推理引擎,支持多种硬件和框架。它能够优化模型图,并选择最佳的执行路径,以提高推理性能 +- **OpenVINO**:英特尔推出的工具套件,专门用于在英特尔硬件(如 CPU、集成显卡、VPU)上进行高效的推理部署 + +**5. 硬件加速** + +选择合适的硬件平台是缩减时延的根本 + +- **GPU(图形处理器)**:对于大规模并行计算有天然优势。深度学习模型中的矩阵乘法和卷积操作都可以高效地在 GPU 上执行 +- **TPU(张量处理器)**:谷歌专门为机器学习工作负载设计的专用集成电路(ASIC),在执行矩阵运算方面比 GPU 更高效 +- **FPGA(现场可编程门阵列)**:可以根据模型结构进行定制化硬件设计,从而达到极高的性能和能效比 +- **移动端 AI 芯片**:许多移动设备都集成了专门的神经处理单元(NPU),如苹果的 Neural Engine、高通的 Hexagon DSP,这些芯片专门为神经网络推理设计,具有低功耗和高效率的特点 \ No newline at end of file