感谢Jack-Cui大佬的知识分享

机器学习专栏点击这里

目录

  • 感谢Jack-Cui大佬的知识分享
  • 0. 什么是SVM?
    • 概述
  • 1. 线性SVM
    • 1.1 相关概念:决策面,分类间隔,最优决策面(最优解),支持向量(支持样本点)
    • 1.2 数学建模
      • 1.2.1 决策面方程(超平面方程)
      • 1.2.2 “分类间隔”方程,即优化目标函数
      • 1.2.3 约束条件
      • 1.2.4 线性SVM优化问题基本描述
      • 1.2.5 求解准备:知识概述
    • 1.3 求解最优化问题准备:拉格朗日函数
      • 1.3.1 第一步:构造无约束的拉格朗日目标函数
      • 1.3.2 第二步:将不易求解的优化问题转化为易求解的优化问题
    • 1.4 求解最优化问题准备:KKT条件
    • 1.5 对偶问题求解:公式推导
    • 1.6 SMO(Sequential Minimal Optimizaion)算法
      • 1.6.1 概述
      • 1.6.2 SMO算法的解法
      • 1.6.3 算法整体步骤
  • 传送门

0. 什么是SVM?

支持向量机(support vector machine, SVM) 是一类通用有效的机器学习方法,它被广泛地应用于模式识别(分类),函数估计(回归),时间序列预测等数据挖掘问题,其本质是基于统计学习理论的一种机器学习方法。

概述

当一个分类问题,数据是线性可分的,也就是用一根棍就可以将两种小球分开的时候,我们只要将棍的位置放在让小球距离棍的距离最大化的位置即可,寻找这个最大间隔的过程,就叫做最优化。但是,现实往往是很残酷的, 一般的数据是线性不可分 的,也就是找不到一个棍将两种小球很好的分类。这个时候,我们就需要像大侠一样,将小球拍起,用一张纸代替小棍将小球进行分类。想要让数据飞起,我们需要的东西就是 核函数(kernel),用于切分小球的纸,就是超平面

也许这个时候,你还是似懂非懂,没关系。根据刚才的描述,可以看出,问题是从线性可分延伸到线性不可分的。那么,我们就按照这个思路,进行原理性的剖析。

1. 线性SVM

1.1 相关概念:决策面,分类间隔,最优决策面(最优解),支持向量(支持样本点)

先看下线性可分的二分类问题

  • 上图中的(a)是已有的数据,红色和蓝色分别代表两个不同的类别。数据显然是线性可分的,但是将两类数据点分开的直线显然不止一条

  • 上图的(b)和©分别给出了B、C两种不同的分类方案,其中 黑色实线为分界线,术语称为“决策面” ,每个决策面对应了一个线性分类器。虽然从分类结果上看,分类器A和分类器B的效果是相同的。但是他们的性能是有差距的,看下图:


在"决策面"不变的情况下,我又添加了一个红点。可以看到,分类器B依然能很好的分类结果,而分类器C则出现了分类错误。显然分类器B的"决策面"放置的位置优于分类器C的"决策面"放置的位置,SVM算法也是这么认为的,它的依据就是分类器B的分类间隔比分类器C的分类间隔大

这里涉及到第一个 SVM独有的概念"分类间隔"。在保证决策面方向不变且不会出现错分样本的情况下移动决策面,会在原来的决策面两侧找到 两个极限位置(越过该位置就会产生错分现象),如虚线所示。虚线的位置由决策面的方向和距离原决策面最近的几个样本的位置决定。而这两条平行虚线正中间的分界线就是在保持当前决策面方向不变的前提下的最优决策面。两条虚线之间的垂直距离就是这个最优决策面对应的分类间隔。 显然每一个可能把数据集正确分开的方向都有一个最优决策面(有些方向无论如何移动决策面的位置也不可能将两类样本完全分开),而不同方向的最优决策面的分类间隔通常是不同的,那个具有“最大间隔”的决策面就是SVM要寻找的最优解。而这个真正的最优解对应的两侧虚线所穿过的样本点,就是SVM中的支持样本点,称为"支持向量"。

1.2 数学建模

求解这个"决策面"的过程,就是最优化

一个最优化问题通常有两个基本的因素:目标函数,优化对象

  • 1)目标函数,也就是你希望什么东西的什么指标达到最好;
  • 2)优化对象,你期望通过改变哪些因素来使你的目标函数达到最优。

在线性SVM算法中,目标函数显然就是那个"分类间隔",而优化对象则是决策面。所以要对SVM问题进行数学建模,首先要对上述两个对象(“分类间隔"和"决策面”)进行数学描述。按照一般的思维习惯,我们先描述决策面。

数学建模的时候,先在二维空间建模,然后再推广到多维。

1.2.1 决策面方程(超平面方程)


向量化后,向量w代表与该直线垂直的法向量,标量γ的作用没变,依然决定直线的截距。(可类比空间平面的求解公式:A(x-x0)+B(y-y0)+Z(z-z0)=0)

1.2.2 “分类间隔”方程,即优化目标函数



我们目的是为了找出一个分类效果好的超平面作为分类器。分类器的好坏的评定依据是分类间隔W=2d的大小,即分类间隔w越大,我们认为这个超平面的分类效果越好。此时,求解超平面的问题就变成了求解分类间隔W最大化的问题。W的最大化也就是d最大化的。

1.2.3 约束条件

看起来,我们已经顺利获得了目标函数的数学形式。但是为了求解w的最大值。我们不得不面对如下问题:

  • 我们如何判断超平面是否将样本点正确分类?

  • 我们知道要求距离d的最大值,我们首先需要找到支持向量上的点,怎么在众多的点中选出支持向量上的点呢?

上述我们需要面对的问题就是约束条件,也就是说我们优化的变量d的取值范围受到了限制和约束。事实上 约束条件一直是最优化问题里最让人头疼的东西。但既然我们已经知道了这些约束条件确实存在,就不得不用数学语言对他们进行描述。

但SVM算法通过一些巧妙的小技巧,将这些约束条件融合到一个不等式里面。

这个二维平面上有两种点,我们分别对它们进行标记:

  • 红颜色的圆点标记为1,我们人为规定其为 正样本(决策面下方的数据)

  • 蓝颜色的五角星标记为-1,我们人为规定其为 负样本(决策面上方的数据)


    因为||w||和d都是标量。所以上述公式的两个矢量,依然描述一条直线的法向量和截距。

1.2.4 线性SVM优化问题基本描述

现在整合一下思路,我们已经得到我们的 目标函数


我们的 优化目标是是d最大化。我们已经说过,我们是 用支持向量上的样本点求解d的最大化的问题的。那么支持向量上的样本点有什么特点呢?

有疑问的回看约束条件的推导过程

为什么要做这样的等效呢?这是为了在进行最优化的过程中对目标函数求导时比较方便,但这绝对不影响最优化问题最后的求解。我们将最终的目标函数和约束条件放在一起进行描述:

这里 n是样本点的总个数,缩写s.t.表示"Subject to",是"服从某某条件"的意思。上述公式描述的是一个典型的不等式约束条件下的 二次型函数优化问题,同时也是支持向量机的基本数学模型。

1.2.5 求解准备:知识概述

我们已经得到支持向量机的基本数学模型,接下来的问题就是 如何根据数学模型,求得我们想要的最优解

在学习求解方法之前,我们得知道一点,想用我下面讲述的求解方法有一个前提,就是我们的目标函数必须是凸函数。理解凸函数,我们还要先明确另一个概念,凸集。在凸几何中,凸集(convex set)是在凸组合下闭合的放射空间的子集。

看一幅图可能更容易理解:

左右两图都是一个集合。如果集合中任意2个元素连线上的点也在集合中,那么这个集合就是凸集。显然,上图中的左图是一个凸集,上图中的右图是一个非凸集

凸函数的定义也是如此,其几何意义表示为函数任意两点连线上的值大于对应自变量处的函数值。若这里凸集C即某个区间L,那么,设函数f为定义在区间L上的函数,若对L上的任意两点x1,x2和任意的实数λ ,λ属于(0,1),总有:

函数f称为L上的凸函数, 当且仅当其上镜图(在函数图像上方的点集)为一个凸集。再看一幅图,也许更容易理解:

像上图这样的函数,它整体就是一个非凸函数,我们无法获得全局最优解的,只能获得局部最优解。比如红框内的部分,如果单独拿出来,它就是一个凸函数。

对于我们的目标函数:

很显然,它是一个凸函数。具体可以看看这篇文章:机器学习必知必会:凸优化。所以,可以使用我接下来讲述的方法求取最优解。

通常我们需要求解的最优化问题有如下几类:

  • 无约束优化 问题,可以写为:
  • 等式约束 的优化问题,可以写为:
  • 不等式约束 的优化问题,可以写为:
  • 对于第(a)类的优化问题(无约束优化),尝试使用的方法就是费马大定理(Fermat),即使用求取函数f(x)的导数,然后令其为零,可以求得候选最优值,再在这些候选值中验证;如果是凸函数,可以保证是最优解。这也就是我们高中经常使用的求函数的极值的方法。
  • 对于第(b)类的优化问题(等式约束优化),常常使用的方法就是 拉格朗日乘子法(Lagrange Multiplier) ,即把等式约束h_i(x)用一个系数与f(x)写为一个式子,称为拉格朗日函数,而系数称为拉格朗日乘子 。通过拉格朗日函数对各个变量求导,令其为零,可以求得候选值集合,然后验证求得最优值。
  • 对于第( c)类的优化问题(不等式约束优化),常常使用的方法就是 KKT条件。同样地,我们把所有的等式、不等式约束与f(x)写为一个式子,也叫拉格朗日函数,系数也称拉格朗日乘子,通过一些条件,可以求出最优值的必要条件,这个条件称为KKT条件。(KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数,求其在指定作用域上的全局最小值)

必要条件和充要条件 如果不理解,可以看下面这句话:

  • A的必要条件就是A可以推出的结论
  • A的充分条件就是可以推出A的前提

了解到这些,现在让我们再看一下我们的最优化问题

现在,我们的这个对优化问题属于哪一类?很显然,它属于第( c)类问题。在学习求解最优化问题之前,我们还要学习两个东西:拉格朗日函数和KKT条件。

1.3 求解最优化问题准备:拉格朗日函数

首先,我们先要从宏观的视野上了解一下 拉格朗日对偶问题 出现的原因和背景。

我们知道我们要求解的是最小化问题,所以一个直观的想法是如果我能够构造一个函数,使得该函数在可行解区域内与原目标函数完全一致,而在可行解区域外的数值非常大,甚至是无穷大,那么这个没有约束条件的新目标函数的优化问题就与原来有约束条件的原始目标函数的优化问题是等价的问题。这就是使用拉格朗日方程的目的,它将约束条件放到目标函数中,从而将有约束优化问题转换为无约束优化问题

随后,人们又发现,使用拉格朗日获得的函数,使用求导的方法求解依然困难。进而,需要对问题再进行一次转换,即使用一个数学技巧:拉格朗日对偶

所以,显而易见的是,我们在拉格朗日优化我们的问题这个道路上,需要进行下面二个步骤:

  • 第一步:将有约束的原始目标函数转换为无约束的新构造的拉格朗日目标函数
  • 第二步:使用拉格朗日对偶性,将不易求解的优化问题转化为易求解的优化

1.3.1 第一步:构造无约束的拉格朗日目标函数

根据不等式约束的优化问题标准格式:

则我们定义不等式约束下的拉格朗日函数L,则L表达式为:

类比我们的最优化问题:

则公式变形为:

其中 αi是拉格朗日乘子,αi大于等于0,是我们构造新目标函数时引入的系数变量(我们自己设置)。

现在我们令:

原因如下:详细可参照这里

判别新函数是否符合期望要求:一个在可行解区域内与原目标函数相同,在可行解区域外函数值趋近于无穷大的新函数

此时,再看我们的初衷,就是为了建立一个在可行解区域内与原目标函数相同,在可行解区域外函数值趋近于无穷大的新函数,现在我们做到了。

1.3.2 第二步:将不易求解的优化问题转化为易求解的优化问题

我们看一下我们的新目标函数,先求最大值,再求最小值。这样的话,我们 首先就要面对带有需要求解的参数w和b的方程,而αi又是不等式约束,这个求解过程不好做。所以,我们需要使 用拉格朗日函数对偶性,将最小和最大的位置交换一下,这样就变成了:

交换以后的新问题是原始问题的对偶问题,这个新问题的最优值用d来表示,而且d<=p*。我们关心的是d=p的时候(强对偶性),这才是我们要的解

拉格朗日对偶性的详细信息点击这里

需要什么条件才能让d*=p*呢?

  • 必须满足这个优化问题是 凸优化问题+Slater条件=》强对偶关系

凸优化问题的定义是:求取最小值目标函数为凸函数的一类优化问题。目标函数是凸函数我们已经知道,这个优化问题又是求最小值。所以我们的最优化问题就是凸优化问题。只要是凸优化问题一定满足弱对偶性

接下里,就是探讨KKT条件了,当满足强对偶性,就可以通过KKT条件求解对应最优值。

1.4 求解最优化问题准备:KKT条件

我们已经使用拉格朗日函数对我们的目标函数进行了处理,生成了一个新的目标函数。通过一些条件,可以求出最优值的必要条件,这个条件就是接下来要说的KKT条件。

一个最优化模型能够表示成下列标准形式:

KKT条件的全称是Karush-Kuhn-Tucker条件,KKT条件是说最优值条件必须满足以下条件:


  • 对于我们的优化问题:

    显然,条件二已经满足了。另外两个条件为啥也满足呢?

    详细信息点击这里

现在,凸优化问题和KKT都满足了,问题转换成了对偶问题。而求解这个对偶学习问题,可以分为三个步骤:

  • 首先要让L(w,b,α)关于w和b最小化
  • 然后求对α的极大
  • 最后利用 SMO算法 求解对偶问题中的拉格朗日乘子。
    现在,我们继续推导。

1.5 对偶问题求解:公式推导



1.6 SMO(Sequential Minimal Optimizaion)算法

1.6.1 概述

1996年,John Platt发布了一个称为SMO的强大算法,用于训练SVMSM表示序列最小化(Sequential Minimal Optimizaion)。Platt的 SMO算法是将大优化问题分解为多个小优化问题来求解的。这些小优化问题往往很容易求解,并且对它们进行顺序求解的结果与将它们作为整体来求解的结果完全一致的。在结果完全相同的同时,SMO算法的求解时间短很多。

SMO算法的目标是求出一系列alpha和b,一旦求出了这些alpha,就很容易计算出权重向量w并得到分隔超平面。

SMO算法的工作原理是:

  • 每次循环中选择两个alpha进行优化处理。一旦找到了一对合适的alpha,那么就增大其中一个同时减小另一个。

这里所谓的"合适"就是指两个alpha必须符合以下两个条件:

  • 条件之一就是两个alpha必须要在间隔边界之外,
  • 而且第二个条件则是这两个alpha还没有进行过区间化处理或者不在边界上

1.6.2 SMO算法的解法

SMO处的惩罚项详细了解点击这里


                 max(f(x))=min(-f(x))

实际上,对于上述目标函数,是存在一个假设的,即数据100%线性可分。但是,目前为止,我们知道几乎所有数据都不那么"干净"。这时我们就可以通过引入所谓的 松弛变量ξ(slack variable)和惩罚参数C,来允许有些数据点可以处于超平面的错误的一侧

考虑到松弛变量ξ和惩罚参数C目标函数变为

此时我们的 约束条件有所改变

则原始问题的拉格朗日函数变为:

             αi>=0,μi>=0,为拉格朗日算子


将根据偏导数=0得到的参数关系带入L(w,b,α,ξ,μ),则对偶问题拉格朗日函数的极大极小问题,得到以下等价优化问题:

则,原始问题的解对偶问题的解相同 需要满足KKT对偶互补条件,即:(根据原始问题的约束条件得出)可行条件
对样本点xi,记SVM的输出结果为:

Platt在序列最小优化(SMO)方法中提到,对正定二次优化问题(a positive definite QP problem)的优化点的充分必要条件为KKT条件(Karush-Kuhn-Tucker conditions)。
对于所有的i,若满足以下条件,QP问题可解。KKT条件如下:

其中 yiui就是每个样本点的函数间隔

KKT条件推导如下:


因此:

最优解需要满足KKT条件,即上述3个条件都得满足,如果存在不能满足KKT条件的αi,那么需要更新这些αi,这是第一个约束条件。此外,更新的同时还要受到第二个约束条件的限制,即:

因为这个条件,我们同时更新两个α值,因为只有成对更新,才能保证更新之后的值仍然满足和为0的约束,假设我们选择的两个乘子为α1和α2:

其中, ksi为常数。因为两个因子不好同时求解,所以可以先求第二个乘子α2的解(α2 new),得到α2的解(α2 new)之后,再用α2的解(α2 new)表示α1的解(α1 new )。为了求解α2 new ,得先确定α2 new的取值范围。

假设它的上下边界分别为H和L,那么有:

接下来,综合下面两个条件:

当y1不等于y2时,即一个为正1,一个为负1的时候,可以得到:


当y1等于y2时,即两个都为正1或者都为负1,可以得到:


如此,根据y1和y2异号或同号,可以得出α2 new的上下界分别为:

这个界限就是编程的时候需要用到的。

已经确定了边界,接下来,就是推导迭代式,用于更新 α值。
我们已经知道,更新α的边界,接下来就是讨论如何更新α值
我们依然假设选择的两个乘子为α1和α2。固定这两个乘子,进行推导。于是目标函数变成了:

为了描述方便,我们定义如下符号:

最终目标函数变为:

我们 不关心constant的部分,因为对于α1和α2来说,它们都是常数项,在求导的时候,直接变为0。对于这个目标函数,如果对其求导,还有个未知数α1,所以要推导出α1和α2的关系,然后用α2代替α1,这样目标函数就剩一个未知数了,我们就可以求导了,推导出迭代公式。所以现在继续推导α1和α2的关系。

注意第一个约束条件:

我们在求α1和α2的时候,可以将α3,α4,…,αn和y3,y4,…,yn看作常数项。因此有:

我们不必关心常数B的大小,现在将上述等式两边同时乘以y1,得到(y1*y1=1)

其中γ为常数B*y1,我们不关心这个值,s=y1*y2

接下来,我们将得到的α1带入W(α2)公式得:

这样目标函数中就只剩下α2了,我们对其求偏导(注意:s=y1*y2,所以s的平方为1,y1的平方和y2的平方均为1):

令: Ei为误差项,η为学习速率。

再根据我们已知的公式:

将α2 new继续化简得:

这样,我们就得到了最终需要的迭代公式.
这个是没有经过剪辑是的解,需要考虑约束

根据之前推导的α取值范围,我们得到最终的解析解为:

又因为:

这样,我们就知道了怎样计算α1和α2了,也就是如何对选择的α进行更新

当我们更新了α1和α2之后,需要重新计算阈值b,因为b关系到了我们f(x)的计算,也就关系到了误差Ei的计算

我们要根据 α的取值范围,去更正b的值,使间隔最大化

当α1_new在0和C之间的时候,根据KKT条件可知,这个点是支持向量上的点。因此,满足下列公式:

公式两边同时乘以y1得(y1*y1=1):

因为我们是根据α1和α2的值去更新b,所以单独提出i=1和i=2的时候,整理可得:



当b1和b2都有效的时候,它们是相等的,即:

两个乘子都在边界上,则b阈值和KKT条件一致。当不满足的时候,SMO算法选择他们的中点作为新的阈值:

最后,更新所有的α和b,这样模型就出来了,从而即可求出我们的分类函数。

1.6.3 算法整体步骤

现在,让我们梳理下SMO算法的步骤:

传送门

支持向量机原理篇之手撕线性SVM
【机器学习】【白板推导系列】【合集 1~23】
SMO算法最通俗易懂的解释
SVM基础
SVM原理介绍
向量范数与矩阵范数
SVM推导步骤
如何理解二次型?
二次型的意义是什么?有什么应用?
二次型求导
我的凸优化学习之路
凸优化学习(二)——凸集
机器学习必知必会:凸优化
优先:深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
【整理】深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件
简易解说拉格朗日对偶(Lagrange duality)
支持向量机:Duality

凸优化问题,凸二次规划问题QP,凸函数
svm惩罚因子c的理解
KKT条件介绍

机器学习算法之手撕SVM-线性(理论)相关推荐

  1. 机器学习算法(九): 基于线性判别LDA模型的分类(基于LDA手写数字分类实践)

    机器学习算法(九): 基于线性判别模型的分类 1.前言:LDA算法简介和应用 1.1.算法简介 线性判别模型(LDA)在模式识别领域(比如人脸识别等图形图像识别领域)中有非常广泛的应用.LDA是一种监 ...

  2. SVM原理篇之手撕SVM

    转载自:https://blog.csdn.net/c406495762/article/details/78072313 转载请注明作者和出处: https://zhuanlan.zhihu.com ...

  3. 机器学习算法(一)SVM

    文章目录 1. SVM 1.1 SVM 基本概念 1.2 hard-margin SVM 1.3 SVM 应用实例 2. 核方法 使用核方法的动机 常用的核函数(kernel functions) 核 ...

  4. 机器学习算法:支持向量机(SVM)

    参考书籍: Solem<python计算机视觉编程>.李航<统计学习方法>.周志华<机器学习> 要理解好支持向量机需要较好的数学功底,且能不被公式以及文字绕晕,这里 ...

  5. 深度学习笔记——基于传统机器学习算法(LR、SVM、GBDT、RandomForest)的句子对匹配方法

    句子对匹配(Sentence Pair Matching)问题是NLP中非常常见的一类问题,所谓"句子对匹配",就是说给定两个句子S1和S2,任务目标是判断这两个句子是否具备某种类 ...

  6. 【机器学习算法-python实现】svm支持向量机(1)—理论知识介绍

    (转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景      强烈推荐阅读(http://www.cnblogs.com/jerrylead/archiv ...

  7. 机器学习算法(4)——SVM(以及拉格朗日对偶问题)

    假设在桌子上似乎有规律放了两种颜色的球,你用一根棍分开它们?要求:尽量在放更多球之后,仍然适用." 于是我们这样分了: 然后我们往在桌上放了更多的球,似乎有一个球站错了阵营:SVM就是试图把 ...

  8. 机器学习算法系列(十)-线性判别分析算法(一)(Linear Discriminant Analysis Algorithm)

    阅读本文需要的背景知识点:拉格朗日乘数法.一丢丢编程知识 一.引言   前面学习了一种用回归的方式来做分类的算法--对数几率回归算法,下面再来学习另一种分类算法--线性判别分析算法1(Linear D ...

  9. 手撕SVM公式——硬间隔、软间隔、核技巧

    1.SVM作用 对于给定的训练样本集D={(x1,y1), (x2,y2),- (xn,yn)},yi属于{-1,+1},希望能找出一个超平面,把不同类别的数据集分开,对于线性可分的数据集来说,这样的 ...

最新文章

  1. Leangoo敏捷项目管理软件 6.3.2卡片关联支持链接
  2. Hierarchy-Viewer架构
  3. 芒果TV崩了?项目组在家办公效率不行呀!
  4. java退出不报异常_如何优雅的处理异常(java)?
  5. qt样式表中背景图片的使用
  6. Python——LOL官方商城皮肤信息爬取(一次练手)
  7. mini_Xception架构
  8. 微软最有价值专家大中华峰会开幕视频
  9. wpf 设置滑动条不能划到头_剪力墙结构设计中遇到的纠结问题8条归纳
  10. 8s 配置session保持_分布式session共享解决方案
  11. 分贝噪音测试软件文件存储,手机分贝噪音测试软件
  12. Linux下基于LDAP统一用户认证的研究
  13. C++ pthread库
  14. vue中点击高德地图获取经纬度
  15. 263企业邮箱:客户端授权码登录
  16. 项目开发成本高周期长 数维图低代码平台解决行业痛点
  17. cf 1450 F The Struggling Contestant
  18. 编码:隐匿在计算机软硬件背后的语言(2)--二进制
  19. Big O, Big Omega, Big Theta的含义
  20. 算法创作|龟兔赛跑问题解决方法

热门文章

  1. 集备二Linux部署之FTP
  2. 软件项目经理新手上路10 - 要的是计划,还是?
  3. Sparkstreaming实时开发详解
  4. Kernel(核函数)
  5. 关于C++的建议,仅仅为了规范代码(二)
  6. java学习(六)多线程 中
  7. php开启filtervar_PHP如何使用filter_var()函数?(代码示例)
  8. java调取对方接口_java中如何调用对方接口
  9. Spark内核解析之七:核心组件解析
  10. 新工具︱微软Microsoft Visual Studio的R语言模块下载试用Ing...(尝鲜)