算法入门

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法入门相关的知识,希望对你有一定的参考价值。

参考技术A ROC曲线(受试者工作特征曲线)的横坐标为假阳性率(分错的负样本占所有负样本比率),纵坐标为真阳性率(分对的正样本占所有正样本比率)。通过动态地调整分类模型的分类阈值,可以在ROC图上绘制出每一个分类阈值对应的两个坐标值,再连接所有点绘制出模型的ROC曲线。AUC指ROC曲线下面积的大小,该指标能够量化地反映基于ROC曲线的模型性能,AUC的取值一般都在0.5~1之间,值越大,说明分类器越可能把真正的正样本排在前面,分类性能越好。相比较P-R曲线,ROC曲线在正负样本的分布发生变化时,形状能够基本保持不变,而P-R曲线一般会发生较剧烈的变化,这个特点可以使得ROC曲线能够尽量降低不同测试集带来的干扰,更加客观地衡量模型本身的性能。在实际中,正负样本数量往往不均衡,因此ROC曲线的适用场景更广泛。

对于任意线性可分的两组点,它们在SVM分类的超平面上的投影都是线性不可分的。由于SVM的分类超平面仅由支持向量决定,可以考虑只含有支持向量的场景:假设存在一个SVM超平面满足投影线性可分,则样本中分属两类的支持向量之间的中垂线所组成的超平面是相较于SVM超平面更优的解,这与SVM超平面为最优分类超平面的假设相违背。

SVM的KKT条件:

结合(3)和(4),当 时,必有 ,将这一结果与拉格朗日对偶优化问题的公式相比较: ,其中 。除了支持向量之外,其他系数均为0,因此SVM的分类结果仅依赖于支持向量,SVM的分类结果与仅使用支持向量的分类结果一致。

该问题也可以通过凸优化理论中的超平面分离定理解决。

高斯核SVM的预测公式为: ,固定 ,则有 。由于不存在两个点在同一位置,则对于任意点 ,有 .

对于任意 ,取 ,有

所以,对于任意 ,预测结果 与真实标签的距离小于1,所有样本的类别都被正确预测,训练误差为0.

本题等价于找到使训练误差为0的参数,且是SVM模型的一个解。上述所找到的参数可以满足 ,若想成为SVM的解,还需要满足 。

仍然固定 ,则有 . 此时可以把每个 都选择一个很大的值,同时取一个非常小的 ,使得核映射项 非常小,就可以满足题意。

不一定能得到训练误差为0的模型,因为此时优化的目标改变了,当松弛变量模型目标函数参数C选取较小的值时,正则项将占据优化的较大比重,此时一个带有训练误差但是参数较小的点将成为更优的结果。

如果把一个事件的几率定义为该事件发生与该事件不发生的概率比值,根据逻辑回归的公式 ,逻辑回归可以看作是对于事件"y=1|x"的对数几率的线性回归,所以有回归的名称。但是逻辑回归的因变量是离散的,处理的是分类问题;线性回归中的因变量是连续的,处理的是回归问题。逻辑回归与线性回归的相似处是:都使用了极大似然估计,线性回归的最小二乘实际上是自变量和超参数确定、因变量服从正态分布的假设下使用极大似然估计的一个化简,逻辑回归中通过对似然函数的学习来得到最佳超参数;二者在求解超参数的过程中,都可以使用梯度下降法。

如果一个样本只对应于一个标签,可以假设每个样本属于不同标签的概率服从于几何分布,使用多项逻辑回归(Softmax Regression)来进行分类:
当存在样本可能属于多个标签的情况时,可以训练k个二分类的逻辑回归分类器,第i个分类器用于区分每个样本是否可以归为第i类。

ID3会倾向于选取取值较多的特征,因为信息增益反应的是给定条件以后不确定性减少的程度,特征取值越多就意味着确定性更高,也就是条件熵越小,信息增益越大,C4.5通过引入信息增益比,一定程度对取值比较多的特征进行惩罚,避免ID3出现过拟合的特性,提升模型的泛化能力;ID3只能处理离散变量,而C4.5和CART都可以处理连续变量;ID3和C4.5只能用于分类任务,CART不仅可以分类也可以用于回归;ID3和C4.5可以在每个结点上产生出多叉分支,且每个特征在层级之间不会复用,而CART每个结点只会产生两个分支,且每个特征可以被重复使用;ID3和C4.5通过剪枝来权衡树的准确性与泛化能力,而CART直接利用全部数据发现所有可能的树结构进行对比。

对于给定的一组数据点 ,中心化后表示为 ,其中 ,目标是找到一个投影方向 (单位方向向量)使数据点在其上的投影方差尽可能大。投影之后的均值: 投影之后的方差(均值为0,直接平方): 其中, 是样本的协方差矩阵,将其写作 ,则有求解最大化问题: 引入拉格朗日乘子,并对 求导令其等于0,可以推出 ,此时 该值为协方差矩阵的最大特征值

LDA的最大化目标: 其中 分别表示两类投影后的方差: 则目标函数可以写成: 定义类间散度矩阵 ,类内散度矩阵 ,最大化 即是对 求偏导且令其等于零: 可以得出 在简化的二分类问题中,可以令 ,则有 这里LDA最大化的目标对应了矩阵 的特征值,而投影方向就是这个特征值对应的特征向量。

PCA为无监督降维算法,LDA为有监督降维算法,两种降维算法的求解过程有很大的相似性,但是对应的原理却有所区别:PCA选择投影后数据方差最大的方向,由于算法无监督,PCA假设方差越大信息量越多,用主成分来表示原始数据可以去除冗余的维度,达到降维;LDA用到了类别标签的信息,选择投影后类内方差小、类间方差大的方向,使得原始数据在这些方向上投影后不同类别尽可能区分开。应用的原则是无监督任务使用PCA,有监督任务使用LDA。

优点:对于大数据集,K均值聚类算法相对是可伸缩和高效的,它的计算复杂度是 接近于线性,其中 是数据对象的数目, 是聚类的簇数, 是迭代的轮数;尽管算法经常以局部最优结束,但一般情况下达到局部最优已经可以满足聚类的需求

缺点:需要人工预先确定初始K值,且该值和真实的数据分布未必吻合;受初值和离群点的影响,每次的结果不稳定;结果通常不是全局最优而是局部最优解,效果受到初始值影响;无法良好地解决数据簇分布差别比较大的情况(比如一类是另一类样本数量的100倍);不太适用于离散分类;样本点只能被划分到单一的类中

SOM本质上是一个两层的神经网络,包含模拟感知的输入层和模拟大脑皮层的输出层,输出层中神经元的个数通常是聚类的个数。具有保序映射的特点,可以将任意维输入模式在输出层映射为一维或者二维图形,并保持拓扑结构不变,使得输出层神经元的空间位置对应于输入空间的特定域或特征。在SOM中,以获胜神经元为中心,近邻者相互激励,远邻者相互抑制,这种交互作用的方式以曲线可视化则类似于“墨西哥帽”。

输出层神经元数量:和样本的类别数相关。若不清楚样本的类别,则尽可能地设定较多的节点数,以便更好地映射样本的拓扑结构,如果分类过细再酌情减少输出节点。这样可能会带来少量从未更新过权重的“死节点”,但一般可通过重新初始化权重来解决

输出层节点排列:排列形式应尽量直观地反映出实际问题的物理意义。例如,对于一般的分类问题,一个输出节点能代表一个模式类,使用一维线阵;对于颜色空间或者旅行路径问题,二维平面则比较直观

初始化权重:可以随机初始化,但尽量使权值的初始位置与输入样本的大概分布区域充分重合,避免出现大量初始"死节点"。可以从训练集中随机抽取m个输入样本作为初始权重

拓扑领域:设计原则是使领域不断缩小,这样输出平面上相邻神经元对应的权向量之间既有区别又有相当的相似性,从而保证当获胜节点对某一类模式产生最大响应时,其领域节点也能产生较大响应。领域的形状可以是正方形、六边形或者菱形。优势领域的大小用领域的半径表示,通常凭借经验来选择

学习率:学习率为递减函数,训练开始时,学习率可以选取较大的值,之后以较快的速度下降,有利于很快地捕捉到输入向量的大致结构,然后学习率在较小的值上缓降为0,这样可以精细地调整权值使之符合输入空间的样本分布结构。

如果数据基本随机,那么聚类的结果毫无意义。可以用霍普金斯统计量来判断数据在空间上的随机性:从样本中随机找 个点 ,对每一个 ,都在样本空间中找到一个离它最近的点并计算它们之间的距离 ,从而得到距离向量 ;从样本可能取值范围内随机生成 个点 ,使用同样的原则得到距离向量 ,则霍普金斯统计量 可表示为: 。如果样本接近随机分布,则 的值接近于0.5,如果聚类趋势明显,随机生成的样本点距离应该远大于实际样本点距离,则 的值接近于1。

Java入门算法(动态规划篇1:初识动规)

本专栏已参加蓄力计划,感谢读者支持❤

往期文章

一. Java入门算法(贪心篇)丨蓄力计划
二. Java入门算法(暴力篇)丨蓄力计划
三. Java入门算法(排序篇)丨蓄力计划
四. Java入门算法(递归篇)丨蓄力计划
五. Java入门算法(双指针篇)丨蓄力计划
六. Java入门算法(数据结构篇)丨蓄力计划
七. Java入门算法(滑动窗口篇)丨蓄力计划
八. Java入门算法(动态规划篇1:初识动规)
九. Java入门算法(动态规划篇2:01背包精讲)


你好,我是Ayingzz,Ayi是我的名字,ing进行时代表我很有动力,zz提醒我按时睡觉 ~


本篇内容

    前言

    在递归篇中,我们用递归求解原问题分解出来的每个子问题,一步步回代最终得到原问题的解。而在计算的过程中,往往存在有很多重复计算的值,导致运算时间的怠慢。动态规划要解决的,就是避免这些重复的计算。

    通常,我们会用表格的形式将某些计算结果记录下来,以供后面的计算直接使用,这个过程即为“填表”,称这个表为“dp表”。在计算表格的某个值时,我们会归纳出一种特定的、具体分析出来的公式,告诉计算机应该根据此公式完成表格的填写,通常称此式子为“状态转移方程”,它规定了之前的结果应该怎样复用并得到新的结果。

    自底向上求解子问题得到原问题的解是动态规划具有的性质,但不是关键。真正关键的地方是子问题的解可不可以重复使用、怎样重复使用(设计状态转移方程),很多程序员都会卡在这里,所以动态规划算法的设计往往比其他入门算法更加困难。


    泰波那契

    LeetCode题目描述:1137. 第 N 个泰波那契数 (Easy)

    • 泰波那契序列 Tn 定义如下:

    T0 = 0,T1 = 1,T2 = 1,Tn+3 = Tn + Tn+1 + Tn+2

    给你整数 n,请返回第 n 个泰波那契数 Tn 的值。
    (n >= 0)

    输入:n = 4
    输出:4
    解释:
    T_3 = 0 + 1 + 1 = 2
    T_4 = 1 + 1 + 2 = 4
    

    纯递归的办法在这就稍显愚笨,因为它的复杂度达到了O(3n) !

    class Solution {
        public int tribonacci(int n) {
            if (n == 0)return 0;
            if (n == 1 || n == 2)return 1;
            return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3);
        }
    }
    

    像求解T6时需要用到T5、T4、T3,计算T5需要用到T4、T3、T2,计算T4需要用到T3、T2、T1,但T4其实在T5的求解中已经被计算过一遍了,所以此类递归存在重复的计算,消耗了时间与空间,当数据量达到一定时,重复子问题的规模不可小觑。

    此时可以用dp数组,存放类似T4这些重复计算的值。

    • 时间上的优化:
    class Solution {
        public int tribonacci(int n) {
            if (n < 3) {
                return n == 0 ? 0 : 1;
            }
            int[] dp = new int[n + 1];
            dp[0] = 0;
            dp[1] = dp[2] = 1;
            for (int i = 3; i < n + 1; ++i) {
                dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
            }
            return dp[n];
        }
    }
    

    因为结果不需要保存整一个数组,而每一个Tn也只需要用到前三个数,所以可以将一维dp表降维成为滚动数组,边计算边刷新空间值,此时只需用到3 + 1个存储空间。

    • 继续在空间上进行优化:
    class Solution {
        public int tribonacci(int n) {
            int x = 0, y = 1, z = 1;
            for (int i = 3; i <= n; ++i) {
                int temp = x + y + z;
                x = y;
                y = z;
                z = temp;
            }
            return n == 0 ? 0 : z;
        }
    }
    

    注意:两种方法都需要特判n < 3时的返回结果。


    不同路径

    LeetCode题目描述:62. 不同路径(Medium)

    • 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

    机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?

    在这里插入图片描述
    示例

    输入:m = 3, n = 7
    输出:28
    解释:...
    -------------------
    输入:m = 3, n = 2
    输出:3
    解释:
    从左上角开始,总共有 3 条路径可以到达右下角。
    1. 向右 -> 向下 -> 向下
    2. 向下 -> 向下 -> 向右
    3. 向下 -> 向右 -> 向下
    

    正着推,规定机器人的走位方式(向下和向右)、以及走位不能超过网格的边界,一步一步递归+回溯,可以得出正确答案,但是太慢了!

    反着推,想象这个网格就是一张dp表,每个格子的值代表从出发位置到达这个格子的不同的路径总数,那么状态转移方程应该怎样设计?因为机器人只能向下或者向右走,所以机器人如果要走到dp[m][n]这个格子,那么它只能从dp[m - 1][n]往下走一格或者从dp[m][n - 1]往右走一格。

    所以此处的状态转移就是到达dp[m][n]处的路径总数 等于 到达dp[m - 1][n]处的路径总数 + 到达dp[m][n - 1]处的路径总数。

    还需要注意的是问题的边界条件,因为第一行的格子只能通过向右走到达,固dp表第一行的值恒为1,同理,第一列的格子只能通过向下走到达,dp表第一列的值恒为1。

    到此易得出状态转移方程:

    • dp[ i ][ j ] = 1,( i = 0 或 j = 0 )
    • dp[ i ][ j ] = dp[ i -1][ j ] + dp[ i ][ j - 1],(其他)
    class Solution {
        public int uniquePaths(int m, int n) {
            int[][] dp = new int[m][n];
            for (int i = 0; i < m; ++i) {// 首行
                dp[i][0] = 1;
            }
            for (int i = 0; i < n; ++i) {// 首列
                dp[0][i] = 1;
            }
            for (int i = 1; i < m; ++i) {// 填表
                for (int j = 1; j < n; ++j) {
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
            }
            return dp[m - 1][n - 1];
        }
    }
    

    打家劫舍

    LeetCode题目描述:198. 打家劫舍(Medium)

    • 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。
    • 影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

    给定一个代表每个房屋存放金额的非负整数数组,计算在 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

    示例

    输入:[2,7,9,3,1]
    输出:12
    解释:
    偷窃 1 号房屋 (金额 = 2), 
    偷窃 3 号房屋 (金额 = 9),
    接着偷窃 5 号房屋 (金额 = 1)。
    偷窃到的最高金额 = 2 + 9 + 1 = 12 。
    

    和上一题相似的点是,我们不会想到会有什么重复的计算,也似乎并不是为了避免重复的计算而在这类题目中使用动态规划,仅仅是从最优子问题出发,通过状态转移将问题规模扩大,每次使用dp表前面的关联值计算当前规模的最优解。最后我们会发现有一个特点在这类题目中十分明显,即最优子问题的解会导致问题的整体最优解。固可用动态规划求解出原问题。

    要设计状态转移方程,首先考虑边界问题,即最小的子问题。当我们要盗窃的房子只有一间时,我们当然要选择这间房子进行偷窃;当我们要盗窃的房子有两间时,我们就要选择金额大的房子进行偷窃。

    那么有三间房子呢?

    当然,聪明的我们会在三间房子间做出比较。因为不能偷相邻的房子,所以要么选择偷第一间房子 + 第三间房子,要么就选择偷第二间房子,然后比较这两种做法的优劣(金额的大小)。易知此时的状态转移为dp[3] = max (dp[3] + dp[1],dp[2]),进而推出题目的状态转移方程:

    • dp[ 0 ] = nums[ 0 ] (只有一间房子)
    • dp[ 1 ] = max ( nums[ 0 ],nums[ 1 ] ) (只有两间房子) ​
    • dp[ i ] = max ( dp[ i − 2 ] + nums[ i ],dp[ i − 1] ) (多于两间房子)

    也可以像第二题一样降成滚动数组,此处不作实现。

    class Solution {
        public int rob(int[] nums) {
            if (nums.length <= 1) {
                return nums.length == 0 ? 0 : nums[0];
            }
    
            int[] dp = new int[nums.length];
    
            // 初始化盗窃开头一间房子的金额
            dp[0] = nums[0];
            
            // 初始化盗窃开头两间房子的最高金额
            dp[1] = Math.max(nums[0],nums[1]);
    
            // dp填表,计算盗窃N间房子的最高金额
            for (int i = 2; i < nums.length; ++i) {
                dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
            }
    
            return dp[nums.length - 1];
        }
    }
    

    推荐练习

    70. 爬楼梯(斐波那契数的变形题)

    • 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
    • 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

    213. 打家劫舍 II(房屋头尾相连)

    你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。
    这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。


    本专栏持续更新,预计7月结束。感谢读者支持❤

    以上是关于算法入门的主要内容,如果未能解决你的问题,请参考以下文章

    Java入门算法(树篇)

    Java入门算法(树篇)

    Java入门算法(动态规划篇1:初识动规)

    LDA算法入门

    Java入门算法(动态规划篇1:初识动规)

    Java入门算法(动态规划篇2:01背包精讲)