机器学习之 K 均值聚类

一、引言——非监督学习 之前介绍的算法都是监督学习算法。需要样本中包含各特征信息以及结果标签来让模型学习特征与结果的对应关系。但是,如果我们此时只有特征信息,而不指明具体结果是什么,又需要什么样的算法和模型才能得知哪些样本会有同样的标签呢? 这种从原始数据中探索样本间联系的学习方式称为非监督学习。其中非监督意指,模型不需要人来告知他 “什么是什么”,而能自己从数据中挖掘信息。 二、聚类算法 非监督学习中的一种算法为聚类算法。它的目的是将数据按照一定特征进行分组。 最广泛使用的聚类算法称为 K 均值(K-means)算法。 三、K均值算法 假设我们要将所有数据分成 k 组,并称在一组中的数据为一个簇。那么对于每一簇内的数据,我们就希望他们之间的距离相对较小,而他们与其他簇中的数据的距离较大。直观上讲,就是同簇的数据点形成一个较为密集的区域,不同簇之间则有较大的空隙(这或许就是聚类的含义)。 我们用簇内的数据的平均作为对簇的描述,相当于指示了簇在特征空间中的位置。这一平均后得到的点称为簇质心。假设我们现在已经得到了一种分组,那么这种分组现在是否有优化的可能呢?要回答这个问题就要查看每一数据点相对于各个簇质心的距离,如果某个数据点相对于其他簇质心的距离要小于其相对于当前所在的簇的质心的距离,就说明该点被错分类了。我们就要将其重新划分为与其距离最小的簇质心所对应的簇内。 但是,这种重新划分又会使得簇质心的位置发生变化,这又导致一些之前是距离该质心位置最小的数据点不再是距离该质心位置最小,又需要再次调整分类。就这样,不断地调整,直到最终所有的点距离当前所在簇的质心距离都为最小,就代表了分类完成。 经过了上面的分析,我们现在给出 K 均值算法的流程: 初始化 k 个簇质心位置,分别对应 k 个簇 对每个数据点,选择与其距离最近的簇质心对应的簇作为其当前分组 对所有同一簇的数据点,计算其平均值,作为新的当前簇的簇质心 不断重复 2、3,直到所有簇的簇质心不再发生变化,即代表分组完成 可以看出,该算法具有很简单的流程,且没有复杂的数据结构。 算法中还需要明确两个点: 如何初始化簇质心: 一般是随机选取点作为质心,或者在样本数据中随机选取 k 个点 怎么计算数据点到簇质心的距离 该算法使用二范数,或者说欧氏距离作为距离 四、K均值算法的损失函数 这里我们要更理论化地解释为什么这样的算法可以将数据成功分类 假设总共有 m 个样本数据 $x^{(1)}, x^{(2)}, ..., x^{(m)}$,需要将其分为 k 类(k < m)。我们用 $c^{(i)}$ 表示第 i 个数据点选择第几个簇。用 $\mu_{j}$ 表示第 j 个簇的簇质心。 要衡量当前分组,按照之前所说的 “同簇的数据点形成一个较为密集的区域,不同簇之间则有较大的空隙”,很容易采用簇中数据点相对于质心的距离的平均作为标准。这里直接给出损失函数 $$ J(c^{(1)}, ..., c^{(m)}, \mu_{1}, ..., \mu_{k}) = \frac{1}{m}\sum_{i=1}^m \Vert x^{(i)} - \mu_{c^{(i)}} \Vert^2 $$ 该损失函数在这里又叫做失真函数。很明显,失真函数是所有数据点到其对应的簇质心的距离的平方的平均。距离取平方是因为这样并不影响损失函数的衡量效果,而又方便计算。...

十月 23, 2022 · 1 分钟 · 102 字 · Wokron

机器学习之决策树

一、引言 决策树是另一种十分有效的机器学习模型。该模型采取树状分支结构,能够很快地进行模型训练,并具有较高的准确率。 本文将以决策树的最简单形式开始,即特征只包括真值特征,或只有两种选择的特征,且结果也只有两种的情况。 二、决策树基本模型 决策树是一个二叉树,树的非叶节点存储需要区分的特征,叶节点存储预测的分类。对于每一个要进行预测的数据,从根节点开始,根据当前节点所对应的特征,选择移动到左子节点或右子节点。直到最终移动到一个叶节点,返回预测结果。 可以认为决策树的预测过程就是通过不同特征将当前数据分组归类的过程。决策树的构建过程,或者更一般地说,该模型的学习过程,也就是不断确定如何分类的过程。 三、决策树的构建 总体上讲,对于我们已有的样本数据,构建决策树的过程时这样的。 选择一个特征,将该特征作为当前节点的特征 根据该特征将样本数据分为两组 对于两组被分类的数据,分别递归执行如上操作,从而确定当前节点的左右子节点。直到当前数据已经全部是要预测的某个结果了、或者所有特征都被用完、又或者递归进行到了最大层数。 可以看到,决策树构建的过程中大部分过程都可以用简单的程序逻辑实现。现在唯一要解决的就是在每个节点时,如何选择一个特征了。 当然,对于神经网络来说也是这样的。唯一要解决的是如何选择权重。 四、特征的选择 我们要衡量选择不同特征对训练样本的区分效果。这点类似于损失函数。 我们区分的目标是使得所有当前组的样本属于同一个预测结果。那么我们就要找到一种衡量纯度的指标,数据越纯指标越小,数据越不纯指标越大。这种指标就是熵。假设某一种类在数据总体中的占比为 $p_1$,则熵 $H(p_1)$ 为 $$ p_0 = 1 - p_1 \\ H(p_1) = -p_1log_2(p_1) - p_0 log_2(p_0) $$ 即 $$ H(p_1) = -p_1log_2(p_1) - (1-p_1) log_2(1 - p_1) $$ 当 $p_1 = 0$ 或 $p_1 = 1$ 时 $$ H(0) = H(0+0) = 0 \\ H(1) = H(1-0) = 0 $$ 可以看出熵与交叉熵损失函数之间有联系。确实,交叉熵损失函数也是用来衡量预测的纯度的。对于一个预测,我们总希望它接近 0 或者 1,也就是纯度最大的比例。...

十月 17, 2022 · 1 分钟 · 163 字 · Wokron

机器学习之模型评估与优化

一、引言 对于一个训练好的模型,我们需要知道它是否能很好地适应现实应用。也就是说,我们希望得知该模型对于训练样本之外的数据,是否也能有较好的预测效果。 模型的这种能力称为泛化能力 这一问题本质上还是我们之前讨论过的拟合问题。只不过对于一个较为复杂的模型来说,常常不能将其拟合或分类的结果用图像直观表示。如果我们不能较为清晰地评估模型,那就无法进一步进行优化调整(如调整正则化系数、增加或减少特征、增加样本数量等)。因此我们需要方法,来一般化地评估模型的质量。 二、泛化能力的衡量指标 正如前一节所说,泛化能力的衡量就是衡量模型对训练样本集之外的数据集的预测效果。我们知道衡量模型对训练样本预测效果的指标是损失函数,那么对于其他数据,我们同样可以把损失函数的概念迁移到这里来。 我们可以将已经获得的数据划分为两部分,一部分用于训练模型,称为训练集;另一部分用于测试模型的泛化能力,称为测试集。对于用训练集训练后的数据,我们再求该模型对测试集数据的损失函数。 举个例子,如果我们采用简单的平方误差损失函数,则我们就是要求 $$ J_{test}(\vec{w},b) = \frac{1}{2m_{test}}\sum_{i=0}^{m_{test}-1} ( f_{\vec{w},b}(\vec{x}^{(i)}_{test}) - y^{(i)}_{test} )^2 $$ $J_{test}(\vec{w},b)$ 就是我们对该模型泛化能力的衡量指标,称为测试误差。 我们可以将该指标与训练集的损失函数值 $J_{train}(\vec{w},b)$(注意这里没有正则项,称为训练误差)进行比较。一般的情况是 $J_{test}(\vec{w},b)$ 大于 $J_{train}(\vec{w},b)$。 我们还可以将该模型的泛化能力与其他模型的泛化能力,或者与人类的误差进行比较。这样我们就可以评价我们所训练的模型的优劣了。 三、交叉验证集 在我们继续评估模型之前,我们要先来考虑一些我们究竟要如何进行优化。你可能会想,我们既然有了评价模型泛化能力的指标 $J_{test}$,那么只要试图让该指标减小不就可以了吗?这是一个很直接的想法。 但问题是,如果我们向着使 $J_{test}$ 减小的目标优化,那么测试集与训练集有什么区别呢?按照这种方法,测试集就相当于变成了训练集的一部分,而不再能成为衡量训练集之外泛化能力的样本了。 因此,我们不能将训练集作为优化的方向,而只能将其作为评价的指标。我们需要再分出一部分数据,作为优化时的参考。这就是交叉验证(Cross-validation)集。对应的衡量指标为 $J_{cv}$ 四、模型评价 我们用偏差和方差来评价一个模型。 偏差表示模型对训练集的预测相对于真实情况的误差,如果模型对训练样本都不能很好地拟合或分类,就称其为高偏差的。 $J_{train}$ 可衡量模型的偏差。 方差表示模型对测试集的预测相对于真实情况的误差(也就是泛化能力本身),如果模型不能很好地预测测试集中数据,就称其为高方差的。$J_{cv}$ 可衡量模型的方差。 一般来说,模型的方差会大于偏差。因此高偏差的模型都是高方差的。 随着模型复杂度的提升,模型的偏差会逐渐降低,但偏差会先降低再升高;随着训练样本量的提升,偏差会逐渐减小,方差会逐渐升高,直到最后方差和偏差程度接近。 五、倾斜数据集的误差指标 对于正面例子和负面例子比例差距较大的训练样本(也就是说,被标记为某一结果的样本数量占样本总数的比例很大或很小),根据全概率分布可知,可能就算全部预测同一结果,其正确的概率也很大。因此我们需要能消除样本本身概率分布导致的正确率偏差的指标。 有两种误差指标用于评价这种情况。分别为准确率(Precision)和召回率(Recall)。 准确率衡量的是,模型预测为某一结果的情况中,有多少是预测正确的。 召回率衡量的是,对所有真正为某一结果的情况,有多少是模型预测到的。 也就是说,设 TP表示将正类预测为正类的数量 FN表示将正类预测为负类的数量 FP表示将负类预测为正类的数量 TN表示将负类预测为负类的数量 则准确率为 $$ P = \frac{TP}{TP + FP} $$ 召回率为 $$ R = \frac{TP}{TP + FN} $$ 可以这样理解,把预测的过程当做选择的过程,准确率高就是“不重”,不过多的选择,以致把许多不属于的也包括在内;召回率高就是“不漏”,不过少地选择,把一些属于的漏掉了。...

十月 16, 2022 · 1 分钟 · 93 字 · Wokron

机器学习之神经网络基础

一、神经元模型 在研究人工智能的过程中,模拟生物的神经是一条很显然的道路。神经网络模型最初便是以模拟生物的神经网络为目的的。但是在早期算力不足的情况下,神经网络的效果并不怎么好。直到硬件基础成熟的最近一段时间,神经网络才发挥出超常的能力。 当然,此时经过改进、优化后的神经网络也在一定程度上偏离了最初模拟生物神经系统的初衷。当前的,以统计为基础的神经网络模型是否是实现强人工智能的有效方法也不得而知。但是我们还是有必要对神经网络模型,特别是作为神经网络的最小单元——神经元模型,做充分的介绍。因为事实证明了,这是一条有效的创造 AI 工具的途径。 单个神经元可以看做有多个输入 $x_j$,单个输出 $a$ 的函数。神经元模型具有如下属性: 权重 $\vec{w}, b$ 激发函数 $g(z)$ 所对应的函数为 $$ f_{\vec{w}, b, g(z)}(\vec{x}) = g(\vec{w} \cdot \vec{x} + b) $$ 取 $g(z) = \frac{1}{1 + e^{-z}}$, 与我们之前学习过的逻辑回归没有差别。 通过对这样的神经元按一定规则进行组合,便可以构造用于完成某种功能的神经网络。 二、神经网络层 对于一组输入,我们可以将其依次通过不同的神经元进行计算,计算的结果可以按顺序组成向量,作为一组新的输入。这种不断迭代的能力是神经网络的基础。我们称同一次计算使用的所有神经元为同一层的神经元。这些神经元组成了神经网络上的一层。不同层的组合构成了整个神经网络。 注意,同一层的神经元需要满足输入个数相同、激发函数相同。 从数学上讲,设输入为 $\vec{x} = (x_1, x_2, ..., x_n)$,输出为 $\vec{a} = (a_1, a_2, ..., a_m)$,神经元函数为 $f_1(\vec{x}), f_2(\vec{x}), ... f_m(\vec{x})$ 则 $$ \vec{a} = (f_1(\vec{x}), f_2(\vec{x}), ... f_m(\vec{x})) $$ 对于不同层上的属性或参数 x,我们加上上标 $x^{[i]}$ 表示该属性或参数属于第 i 层(第几层指示神经元计算的顺序);对于同一层的不同神经元,加上下标 $x^{[i]}_j$ 表示第 i 层的第 j 个神经元的属性或参数。...

十月 14, 2022 · 2 分钟 · 254 字 · Wokron

系统编程之 Shell 编程

一、前言 本文将简单探索shell脚本编程,介绍shell的基本语法。 二、shell简介 shell是一个命令解释器,可以用来启动、停止、编写程序;是用户和UNIX/Linux操作系统内核程序间的一个接口。 而shell编程则是将linux命令与shell的各种流程控制和条件判断来组合成命令与变量,形成可以进行自动处理的脚本程序。 三、前期准备 创建脚本 shell脚本是一个文本文件,可用文本编辑器如vi、vim编辑保存。创建shell脚本只需按照创建文本文件的方式创建。如 vi c1.sh vim c2.sh > c3.sh shell脚本一般以.sh为后缀,但没有后缀依旧可以执行。 创建的shell脚本,一定要在开头第一行加上如下语句: #!/bin/bash 这一行将指明该脚本执行所需要的命令解释器。 执行脚本 shell脚本的执行方法有 sh <scriptname> bash <scriptname> 或者使用chmod命令修改脚本为可执行,再直接使用 ./<scriptname> 运行。 四、基本语法 变量 shell中的变量分为环境变量、用户定义变量、内部变量。 其中环境变量是操作系统的一部分,但可以利用shell脚本进行修改;用户变量即在脚本中声明的变量;而内部变量则用来指示脚本运行中出现的一些变量。 声明 只有用户变量可以声明。和其他语言一样,使用等号进行声明。但要注意的是,shell脚本是弱类型的,因此变量名前不需要加上类型名。 var=hello_world 注意shell对空格敏感,声明时等号两边不能有空格 另外可以在变量名前添加 readonly关键字设为只读 readonly constVar shell中声明数组同样直接写出数组名称 arr[0]=1 arr[1]=5 arr[10]=20 未赋值的部分默认为NULL。 注意 ubuntu 默认使用 dash 而非 bash shell。dash 并不支持数组。要使用数组可以用bash运行脚本,即运行命令 bash <scriptname> 赋值 shell变量有类似左值右值的区别。在用其他变量进行赋值时,需要对变量使用${ } 进行取值。 var1=hi var2=${var1} 引号 shell脚本是为了自动化处理命令而设计的。因此语法中有很大一部分关注于字符串和命令的相关操作。在变量上体现在,shell中所有变量默认以字符串形式存在。 并且,为了满足命令处理的需要,shell设计出了引号变量值。 shell中的引号包括单引号、双引号和倒引号。 单引号中的字符均作为普通字符出现。(可以包括空格) var1=hello_world # var2=hello world # 不合法 var3='hello world' 双引号中的字符大部分作为普通字符对待。除了$\’和双引号,这些变量依旧用于对字符串内容进行变量替换。...

十月 8, 2022 · 2 分钟 · 321 字 · Wokron

机器学习笔记之正则化

一、前言——过拟合 我们知道,经过一组点可以有无数条曲线。这些曲线对于这组样本点的损失函数同为 0。但是对于预测来说,这些曲线产生的结果却并不相同。这就意味着,进行梯度下降到达某一最低点时,依旧不一定能得到“最好的”预测(拟合/分类)效果。甚至可能对于一些情况,此时的(预测/分类)效果反而更差了。这样的情况就称为过拟合。 过拟合的存在是很合理的。从感性上讲,将机器学习的过程类比人类的认知,一个观念的形成不能超出经验之外,认知的结果永远是片面而非客观的。那么在与更广泛的客观现实接触之前,我们必然无法得知已经形成的认知是否是依旧可以应用的。 这是一个很休谟的观点。但却无法解决现实问题。我们依旧需要找到减少过拟合的方法。 二、惩罚 我们的思想中存在着一种先验观念,它规范天地万物,在冥冥中告诉我们什么是“合理的”。对于机器学习模型来说也是一样的,它应当具有这样的机制,告诉它什么情况是不可能的。 就比如说,对于房价,我们知道一些特征是更加重要的,而另一些是更加不重要的。很显然,那些更重要的特征对应的权重应该较不重要的特征对应的权重大。那么我们就需要对那些不重要的权重进行“惩罚”,以避免这些权重过大,从而导致模型过拟合。这样的“惩罚”在损失函数中体现。即,当这些权重过大时,损失函数也会相应增大。 具体而言,对如下的表达式 $$ y = w_1x_1 + w_2x_2 + w_3x_3 + b $$ 假设要使第二、三个权重相对较小,则可以在损失函数中加上惩罚项 $\lambda_2 w_2 + \lambda_3 w_3$。其中 $\lambda_2, \lambda_3$ 取较大值。则损失函数变为 $$ J_{new}(\vec{w}, b) = J(\vec{w}, b) + \lambda_2 w_2 + \lambda_3 w_3 $$ 具体地比如说 $$ J_{new}(\vec{w}, b) = J(\vec{w}, b) + 1000 w_2 + 2000 w_3 $$ 那么此时很显然,当 $w_2, w_3$ 较大时,损失函数也会相应更大。 可是对于大多数情况,我们无法事先知晓权重的重要程度。对于这些一般化的问题,还需要有一般化的解决办法。 三、正则化 正则化是惩罚的一种。该方法在损失函数中增加了正则项: $$ \frac{\lambda}{2m} \sum_{j=1}^n w^2_j $$ 其中 $\lambda$ 称为正则化参数。...

十月 8, 2022 · 1 分钟 · 143 字 · Wokron