机器学习序言:A machine learning sampler

*声明:本系列博文均参考英文原版书籍《Machine Learning》(Peter Flach)。

        其实机器学习已经深入我们的生活,就比如邮件过滤。早期的垃圾邮件过滤器依赖于手工编码的模式匹配技术,如正则表达式(即单纯的匹配关键字),但是这种技术的弊端日益明显,随着邮件种类、内容的丰富,它很难灵活有效的分类。人们利用机器学习进行垃圾邮件分类,获得了更高的适应性和灵活性。

SpamAssassin就是这样一个应用广泛的开源邮件过滤器。它的思路就是:对接收到的邮件进行不同项的测试,每个单项测试会有一个分数,将这些单项分数加和获得总分数, 当这些分数超过阈值5就判定该邮件为垃圾邮件。如下图,为一个邮件单项测试分数结果列表。


        图中从左到右分别是每个单项测试获得的分数(为负数表明更像友好邮件而不是垃圾邮件)、测试名称、测试结果说明。该邮件分数加和大于阈值5,所以将会被分类为垃圾邮件。

        那么SpamAssassin是如何确定每个单项的分数和这个阈值5的呢?这个是机器学习要解决的问题。分数规则和阈值设定是通过对大量的已经知道其类别的训练数据集(training set)进行测试的结果。就是我已经知道这些邮件的类别,然后我去计算分数,不断更改我的分数规则,最后获得一个阈值,使垃圾与非垃圾邮件区分度最好。

        为了便于理解,举一个简单的例子:


        表格中有四封邮件编号为1、2、3、4,x1和x2分别表示两个不同的单项测试,测试通过表示1,未通过表示0;“Spam?”表示已知邮件类型是否为垃圾邮件,如果是则为1,不是为0;最后一列为该邮件的总分数。这个例子中,每个单项测试通过得分均为4分,未通过均得0分,这是一个简单的例子。我们可以看出分数其实就是权重,不同的单项测试可以有不同的权重,也就是分数。继续看这个例子,我们发现垃圾邮件分数为8大于4,而非垃圾邮件分数小于等于4,所以可以设定阈值为4到8中的一个数,例如5,就可以将垃圾邮件区分。


        总结一下邮件过滤的整体思路:如上图。接收到邮件;进行测试获得分数;经过分类器,同经过机器学习后的分数阈值作比较;区分出垃圾邮件。

        下面我们再举一个例子,说明一下机器学习中存在的过拟合问题(Overfitting)。所谓过拟合就是过度追求在训练数据集下的良好表现(performance),

而更换其他数据集时则表现较差,简而言之就是不具一般性(Generalisation)。

        假设你准备你的第101次考试,老师把之前的考试题目和答案发给你让你复习,但是你只是把之前题目的标准答案背了下来,这样会有一个问题,

就是如果考试大部分都是原来的题目,那么你会有一个很好的成绩,如果都是新的题目,之前没有的题目,那么你背的标准答案就用不上,你将会获得

很低的分数。这种情况就称你对之前的考试过拟合,获得的知识不具一般性。在之后的博文中我们会陆续讲解如何克服过拟合问题。

        SpamAssassin的线性分类很经典,但是机器学习不只是测试的分数,我们还要讲测试本身。

        邮件内容也被很多过滤器应用于垃圾邮件的识别,这些分类器都包含一个词汇、短语表,这个表里是一些垃圾邮件常用的词汇、短语。假如

单词‘Viagra’在4个垃圾邮件出现,在1个友好邮件出现,那么我们认为当一个邮件含有‘Viagra’这个单词时,它为垃圾邮件的o为4:1

o= p/(1−p),p为事件发生的概率)。我们还需要考虑垃圾邮件的流行程度,假设流行程度为平均每隔6个友好邮件收到一封垃圾邮件,则其为垃圾

邮件的o=1:6,然后我收到一封含有‘Viagra’的邮件,那么它是垃圾邮件的概率为多少呢?我们肯定要综合考虑两个o(4:1和1:6),假设两个事件是

独立的,贝叶斯定律告诉我们可以直接将两个o相乘得到该邮件为垃圾邮件的o是4:6,即概率为0.4。这件事情厉害的地方在于将不相关的两个事件

的概率结合了起来,一个是流行程度,一个是是否包含单词'Viagra'。如果还有第三个事件,就是包含'Blue pill',它跟前两个事件也是独立的,它为

垃圾邮件的o为3:1,那么就将三者的o结合起来就是2:1。我们发现假设为独立时,没有求联合概率,这也使我们可以处理大量的变量,

但是实际上’Blue pill‘和'viagra'可能不是独立出现的,实际上他们的联合o要小于12:1,但在实践中,求联合概率运算量非常大,不好实现,

那么如何确定一个最佳的特征组合?我们之后会介绍很多有效的方法。

SpamAssassin这个例子是一个二分类(binary classification)。如下图,我们进一步来描述一下机器学习。机器学习主要有三个部分,

tasks(任务)、models(模型)和features(特征),Model是特征和分类的联系,Task是根据特征数据由Model的映射关系得到输出。

我们在图中还可以看到learning problem,它跟Task是不一样的,它是根据学习算法产生Model,这个也是很多书上写的'learning task',这要同Task相区分。


总而言之,机器学习关注如何用正确的特征(Features)得到正确的模型(Models)并能正确的完成任务(Tasks)。


<think>好的,我现在要帮用户了解torch::grid_sampler的用法。首先,我需要回忆一下PyTorch中这个函数的作用。记得grid_sampler是用来对输入张量进行采样和插值的,通常用在空间变换网络中,比如图像变形、旋转等操作。不过用户提到的是torch::grid_sampler,可能是指C++ API中的函数,不过基本概念应该和Python版的一致。 接下来,我需要确认这个函数的基本参数。Python版的torch.nn.functional.grid_sample有几个关键参数:input,grid,mode,padding_mode,align_corners。C++的接口应该类似,但可能命名稍有不同。需要确保参数的正确性,比如输入是4D或5D张量,grid的形状要和输入匹配,并且值的范围可能根据align_corners的不同而变化。 然后,用户可能需要知道如何构造grid。比如,在2D情况下,grid的每个位置对应输入的一个采样点坐标。对于normalized坐标,通常范围是[-1, 1],比如左上角是(-1,-1),右下角是(1,1)。这时候需要注意align_corners的设置会影响坐标的计算方式。如果align_corners为True,那么-1和1对应的是像素的中心,否则可能对应像素的边界。这部分需要解释清楚,避免用户在使用时坐标错误。 接下来,不同的插值模式,比如'mode'参数有nearest和bilinear,在C++中可能用枚举类型表示,比如torch::kNearest或torch::kBilinear。同样,padding_mode控制当采样点超出输入范围时的处理方式,比如用zeros或者border,或者reflection。需要举例说明不同模式的效果。 然后,应用示例非常重要。用户可能想知道如何在C++中实际调用这个函数。比如,构造一个输入张量和一个grid,然后调用grid_sampler函数。要注意输入和grid的维度匹配,比如4D输入(batch, channels, height, width)对应的grid是(batch, H_out, W_out, 2),其中最后一个维度是x和y坐标。 还要注意可能的错误,比如输入和grid的device是否一致(CPU或CUDA),数据类型是否正确。此外,需要提醒用户关于梯度的问题,因为在反向传播时,grid_sampler的梯度会传播到输入和grid,这在训练中使用时很重要。 最后,可能的应用场景,比如图像变形、数据增强、空间变换网络等,可以举一些实际例子,比如旋转图像、仿射变换等,帮助用户理解如何结合grid生成和grid_sampler来实现这些功能。 检查一下是否有遗漏的部分,比如是否提到align_corners的具体影响,或者不同版本的PyTorch是否有差异。确保示例代码正确,并且参数解释清晰。可能还需要对比Python和C++ API的差异,如果有的话,但假设用户主要关注C++的用法,所以重点放在那里。 总结步骤:先介绍函数的作用,参数详解,grid的构造方法,插值和padding模式,示例代码,注意事项,应用场景。这样结构清晰,用户能逐步理解如何使用这个函数。</think>`torch::grid_sampler` 是 PyTorch C++ API 中用于对输入张量进行 **网格采样** 的函数,常用于实现图像变形、仿射变换、空间变换网络(STN)等任务。以下是其核心用法详解: --- ### **函数原型** ```cpp torch::Tensor grid_sampler( const torch::Tensor& input, // 输入张量 const torch::Tensor& grid, // 采样网格 int64_t interpolation_mode, // 插值模式 int64_t padding_mode, // 边界填充模式 bool align_corners // 对齐方式 ); ``` --- ### **参数详解** 1. **`input`** - 输入张量,支持 4D (N, C, H, W) 或 5D (N, C, D, H, W),分别对应图像或体积数据。 - 示例:`torch::randn({1, 3, 256, 256})` 表示一张 256x256 的 RGB 图像。 2. **`grid`** - 采样网格,形状为 `(N, H_out, W_out, 2)`(2D)或 `(N, D_out, H_out, W_out, 3)`(3D)。 - **坐标范围**: - 若 `align_corners = True`,坐标范围为 `[-1, 1]`(角点对齐)。 - 若 `align_corners = False`,坐标范围为 `[-1 + 1/(H-1), 1 - 1/(H-1)]`(像素中心对齐)。 3. **`interpolation_mode`** - 插值方式,可选: - `0` 或 `torch::kNearest`: 最近邻插值。 - `1` 或 `torch::kBilinear`: 双线性插值(默认)。 - `2` 或 `torch::kBicubic`: 双三次插值(仅部分版本支持)。 4. **`padding_mode`** - 越界填充方式,可选: - `0` 或 `torch::kZeros`: 填充 0。 - `1` 或 `torch::kBorder`: 填充边界值。 - `2` 或 `torch::kReflection`: 镜像填充。 5. **`align_corners`** - 是否对齐输入和输出的角点像素中心,影响坐标映射计算。 --- ### **使用示例(2D 图像变形)** ```cpp // 输入:1 张 3 通道 256x256 图像 auto input = torch::randn({1, 3, 256, 256}); // 构造网格:输出尺寸 128x128 auto grid = torch::empty({1, 128, 128, 2}); // 生成归一化坐标(示例:简单缩放) for (int h = 0; h < 128; ++h) { for (int w = 0; w < 128; ++w) { // 将坐标从 [-1, 1] 映射到输入尺寸 float x = (w / 127.0f) * 2 - 1; // align_corners=True float y = (h / 127.0f) * 2 - 1; grid[0][h][w][0] = x; grid[0][h][w][1] = y; } } // 执行采样 auto output = torch::grid_sampler( input, grid, torch::kBilinear, // 双线性插值 torch::kZeros, // 越界填充 0 true // align_corners=True ); ``` --- ### **关键注意事项** 1. **网格坐标方向** - 网格的 `(x, y)` 对应输入张量的 `(width, height)`,即最后一个维度为 `[x, y]`。 2. **梯度传播** - `grid_sampler` 支持自动求导,梯度会传播到 `input` 和 `grid`,适用于可变形卷积等可训练任务。 3. **性能优化** - 对于固定网格(如仿射变换),可预先计算 `grid` 并复用,避免重复生成。 --- ### **典型应用场景** 1. **仿射变换** - 通过 `grid` 实现旋转、平移、缩放等操作。 2. **可变形卷积** - 根据学习到的偏移量动态调整采样位置。 3. **数据增强** - 随机扭曲图像生成训练样本。 4. **3D 体积采样** - 处理医学影像或点云数据时,扩展至 5D 输入。 --- 若需进一步了解如何生成复杂网格(如仿射/透视变换),可结合 `torch::affine_grid` 或自定义坐标计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值