对核函数(kernel)最通俗易懂的理解

本文介绍了核函数的概念及其在机器学习中的应用。重点讲述了核函数如何帮助解决非线性问题,通过映射到高维空间实现数据的线性可分。并通过实例展示了如何使用核函数提升模型的表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

机器学习算法中,不论是感知机还是支持向量机,在面对非线性问题时,往往都会用到一个名为“核函数”的技巧。那么到底什么是核函数呢?是否真的如听起来这样难以理解呢?

核函数:是映射关系 的内积,映射函数本身仅仅是一种映射关系,并没有增加维度的特性,不过可以利用核函数的特性,构造可以增加维度的核函数,这通常是我们希望的。

二维映射到三维,区分就更容易了,这是聚类、分类常用核函数的原因。为什么PCA这样一个降维算法也用核函数呢?

左图为原数据,右图为映射到三维的数据,可以看出:同样是降到1维,先通过Kernel映射到(Kernel是映射的内积,不要弄乱了)三维,再投影到1维,就容易分离开,这就是Kernel在PCA降维中的应用,本质还是对原有数据增加维度。

下面是李航的《统计学习方法》中对于核函数的定义:

要注意,核函数和映射没有关系。核函数只是用来计算映射到高维空间之后的内积的一种简便方法。

一般英文文献对Kernel有两种提法,一是Kernel Function,二是Kernel Trick。从Trick一词中就可以看出,这只是一种运算技巧而已,不涉及什么高深莫测的东西。

具体巧在哪里呢?我们如果想进行原本就线性不可分的数据集进行分割,那么选项一是容忍错误分类,即引入Soft Margin;选项二是我们可以对Input Space做Feature Expansion,把数据集映射到高维中去,形成了Feature Space。我们几乎可以认为(引用Caltech的课堂用语“We are safe but not certain”)原本在低维中线性不可分的数据集在足够高的维度中存在线性可分的超平面。

 

举一个知乎上霍华德的例子。假设我们的任务是要预测那些微博可以上微博热搜榜。有两个离散特征,一个代表某个微博里有“鹿晗”,一个代表某个微博里有“关晓彤”。

两个特征单独看热度都一般,此时我们用二阶多项式核方法:

K(鹿晗,关晓彤) = <鹿晗,关晓彤>^2

这个核函数可以把二维空间投射到三维空间,展开之后是:

K(鹿晗,关晓彤) =(鹿晗^2,\sqrt{2}鹿晗\times 关晓彤,关晓彤^2)

这样就把二维特征变成了三维,多了一维 鹿晗\times 关晓彤 ,代表着某条微博里鹿晗和关晓彤同时出现。

结果大家都知道了,鹿晗关晓彤同时出现的那条微博超级火,把新浪服务器都挤爆了。

现实生活中有很多非线性非常强的特征 而核方法能够捕捉它们。核技巧(kernel trick)的作用,一句话概括的话,就是降低计算的复杂度,甚至把不可能的计算变为可能

在机器学习中常用的核函数,一般有这么几类,也就是LibSVM中自带的这几类:

1) 线性:K(v_1,v_2)=<v_1,v_2>
2) 多项式:K(v_1,v_2)=(\gamma<v_1,v_2>+c)^n
3) Radial basis function:K(v_1,v_2)=\exp(-\gamma||v_1-v_2||^2)
4) Sigmoid:K(v_1,v_2)=\tanh(\gamma<v_1,v_2>+c)

我举的例子是多项式核函数中\gamma=1, c=0, n=2的情况。

在实用中,很多使用者都是盲目地试验各种核函数,并扫描其中的参数,选择效果最好的。至于什么样的核函数适用于什么样的问题,大多数人都不懂。很不幸,我也属于这大多数人,所以如果有人对这个问题有理论性的理解,还请指教。

参考文章:

知乎:核函数的定义与作用

<think>嗯,用户想了解Linux内核中的initcall机制,我得先回忆一下相关的知识。initcall是内核用来管理初始化函数的一种机制,对吧?我记得在系统启动时,内核需要执行大量的初始化函数,比如设备驱动、子系统初始化这些。如果没有一个统一的机制来管理,这些初始化函数的执行顺序可能会混乱,导致系统启动出现问题。 那initcall机制具体是怎么工作的呢?可能涉及到将不同的初始化函数分类,然后按照优先级顺序执行。好像在内核代码中,这些初始化函数会被标记为不同的级别,比如early、core、postcore等等。每个级别对应不同的初始化阶段,这样内核在启动时就能按照正确的顺序执行它们。 我记得在代码中,这些初始化函数是通过宏来定义的,比如module_init或者其他的宏,这些宏会把函数指针放到特定的段(section)里。然后链接器在编译时会把这些段按照顺序排列,内核启动时再依次调用这些函数。这样做的好处是开发者只需要用宏标记函数,而不必手动管理执行顺序,减少了出错的可能。 不过用户可能还不太明白段的概念,需要解释一下。段是ELF文件中的一个部分,链接脚本会控制这些段的顺序。不同的initcall级别对应不同的段名,比如".initcall1.init"、".initcall2.init"等等。这样在链接时,这些段会按照数字顺序排列,内核在启动时按顺序遍历这些段,执行里面的函数。 可能用户还会想知道initcall的七个级别是什么,每个级别的作用。比如early initcall用于非常早期的初始化,而late initcall可能是在所有基础服务就绪后才执行。此外,还有设备模型相关的初始化,比如device_initcall和late_initcall的区别。 另外,用户可能对module_init和subsys_initcall这样的宏之间的关系感兴趣。这些宏其实都是将函数指针放到不同的段里,对应不同的初始化阶段。比如subsys_initcall用于子系统初始化,比device_initcall早执行吗?或者反过来?需要确认各个宏对应的级别顺序。 还有,内核启动过程中,比如start_kernel函数之后,会调用rest_init,然后启动init进程。在这个过程中,什么时候执行这些initcall函数呢?可能是在kernel_init这个函数里,或者是在其他阶段?需要回忆一下启动流程。 另外,用户可能想知道这个机制如何保证正确的执行顺序,尤其是在模块动态加载的时候。比如,当模块被insmod加载时,它的初始化函数会被插入到相应的段里,然后内核执行时调用。不过如果是静态编译进内核的模块,它们的初始化函数也会被放在对应的段里,在启动时执行。 可能还要举一个例子,比如设备驱动的初始化函数使用__init宏和module_init宏,这样在编译时会被放到特定的段里。当内核启动时,会按照段的顺序依次执行这些函数,完成驱动的初始化。 需要确保解释足够通俗,避免太技术化的术语,但又要准确。比如,把段比作一个容器,里面装满了初始化函数,内核按照容器的顺序一个个执行里面的函数。这样用户可能更容易理解。 还要注意引用相关的参考资料,比如引用链接中的设备驱动模型或者启动流程的内容,但用户提供的引用中有关于设备驱动模型和进程通信的内容,可能和initcall相关的是引用1和引用4,但需要确认引用是否正确。比如引用1提到了platform driver模型,可能涉及初始化过程,而引用4是启动流程,可能提到initcall的执行阶段。 后,生成的相关问题需要围绕initcall机制展开,比如执行顺序如何控制,和模块加载的关系,以及不同initcall级别的区别等等。</think>### Linux内核initcall机制解析 #### 一、核心概念 initcall是Linux内核用于管理初始化函数的机制,它将不同阶段的初始化函数分类为多个优先级,并按照固定顺序执行。这种机制解决了内核启动时数百个初始化函数执行顺序难以管理的问题[^1]。 #### 二、工作原理 1. **分级定义** 内核定义了7个初始化级别(从0到6),例如: - `early_initcall`:早期初始化(如内存管理) - `core_initcall`:核心子系统初始化 - `postcore_initcall`:核心后续初始化 - `device_initcall`:设备驱动初始化 - `late_initcall`:终阶段初始化 2. **宏实现机制** 开发者通过宏声明初始化函数: ```c static int __init my_init(void) { /* ... */ } device_initcall(my_init); // 将函数注册到设备初始化阶段 ``` 宏展开后会将函数指针存入特定ELF段(如`.initcall4.init`)。 3. **链接脚本控制** 内核链接脚本`vmlinux.lds`明确规定了各.initcall段的排列顺序: ``` .initcall0.init : { *(.initcall0.init) } .initcall1.init : { *(.initcall1.init) } ... .initcall6.init : { *(.initcall6.init) } ``` 4. **执行过程** 在`start_kernel()`→`rest_init()`→`kernel_init()`启动流程中,通过`do_initcalls()`遍历执行所有级别的初始化函数[^4]。 #### 三、关键特点 1. **顺序保障** 编译器按照链接脚本顺序将函数指针写入二进制文件,确保执行时严格按优先级调用。 2. **内存优化** 初始化函数使用`__init`宏标记,执行完毕后相关代码段内存会被释放。 3. **模块支持** 动态加载模块时,其初始化函数会通过类似机制插入执行队列。 #### 四、应用示例 设备驱动初始化典型模式: ```c static int __init my_driver_init(void) { // 注册设备操作 return platform_driver_register(&my_driver); } module_init(my_driver_init); // 等价于device_initcall ``` 此函数会被放入`.initcall4.init`段,在设备初始化阶段执行[^1]。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图灵的猫.

小二,给客官上酒!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值