之前提到过三种简单基础的监督学习算法,但是选择还有更多种:
- KNN(思路易于理解,熟悉其结合KD-tree来其优化算法的时间性能)
- ADAboost
- Random Forests
请尝试使用scikit-learn来得到各类算法的准确度,与运行时间!
KNN
KNN的思路很容易理解,简单地说就是,给定N个样本空间,一个询问点p,求距离询问点最近的K个点(最后根据这K个点的类标号中占比最大的标号,即为询问点的类标号)。
优化:使用KD-tree 这种数据结构来优化,主要过程是 建树 + 询问前k小。
用于理解的C++代码:
//-------------------------------------------------------------
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 55555, K = 5;
const int inf = 0x3f3f3f3f;
#define sqr(x) (x)*(x)
int k,n,idx; //k为维数,n为点数
struct point
{
int x[K];
bool operator < (const point &u) const
{
return x[idx] < u.x[idx];
}
}po[N];
typedef pair<double,point> tp;
priority_queue<tp> nq;
struct kdTree
{
point pt[N<<2];
int son[N<<2];
void build(int l, int r, int rt = 1, int dep = 0)
{
if(l > r) return;
son[rt] = r - l;
son[rt*2] = son[rt*2+1] = -1;
idx = dep % k;
int mid = (l + r) / 2;
nth_element(po + l, po + mid, po + r + 1);
pt[rt] = po[mid];
build(l, mid - 1, rt * 2, dep + 1);
build(mid + 1, r, rt * 2 + 1, dep + 1);
}
void query(point p, int m, int rt = 1,int dep = 0)
{
if(son[rt] == -1) return;
tp nd(0, pt[rt]);
for(int i = 0;i < k;i ++) nd.first += sqr(nd.second.x[i] - p.x[i]);
int dim = dep % k, x = rt * 2, y = rt * 2 + 1, fg = 0;
if(p.x[dim] >= pt[rt].x[dim]) swap(x, y);
if(~son[x]) query(p, m, x, dep + 1);
if(nq.size() < m) nq.push(nd), fg = 1;
else
{
if(nd.first < nq.top().first) nq.pop(), nq.push(nd);
if(sqr(p.x[dim] - pt[rt].x[dim]) < nq.top().first) fg = 1;
}
if(~son[y]&&fg) query(p, m, y, dep + 1);
}
}kd;
void print(point &p)
{
for(int j = 0;j < k;j ++) printf("%d%c",p.x[j], j == k-1 ? '\n' : ' ');
}
int main()
{
while(scanf("%d%d", &n, &k) != EOF)
{
for(int i = 0;i < n;i ++) for(int j = 0;j < k;j ++) scanf("%d", &po[i].x[j]);
kd.build(0, n-1);
int t, m;
for(scanf("%d", &t);t --;)
{
point ask;
for(int j = 0;j < k;j ++) scanf("%d", &ask.x[j]);
scanf("%d", &m); kd.query(ask, m);
printf("the closest %d points are:\n", m);
point pt[20];
for(int j = 0;!nq.empty();j ++) pt[j] = nq.top().second, nq.pop();
for(int j = m - 1;j >= 0;j --) print(pt[j]);
}
}
return 0;
}
ADAboost
ADAboost(Adaptive boost 自适应增强),是一个不断更新迭代的过程。其算法步骤为:
(1)、初始化各个样本点的权重为1/N
,N
为样本总数。训练得到初步的分类器: G1(x) ,计算出这个分类器的误差率:em=∑i=1i=NWi∗I(G1(xi)!=yi)(其中wi
指第i
个样本的权值),计算这个分类器的权重:am=12log1−emem。
可以看出,对于第一次的分类,如果误差率较高,那么这个分类器所占权重am
将会比较小。
(2)、进行第m+1(m>=1)
次迭代过程:
计算每个样本的权值:Wi,m+1=Wi,mZme−am⋅yi⋅Gm(xi)得到新的权值集合。接下来重新训练样本的得到分类器 Gm+1(x) ,同理按照上述的公式去计算这个费类其的误差率,以及这个分类器的权重。
(3)、组合各个分类器,得到最终的分类器:
f(x)=∑i=1i=MaM⋅GM(x)
G(x)=sign(f(x))具体详细的文章,参考:Adaboost详细过程及证明
Random Forests
随机森林(Random Forests),由多棵决策树来投票选择最终的分类结果,而每一颗决策树是怎样构成的呢?
(1)、如果训练集大小为N
,对于每颗决策树来说,随机且有放回的从训练集中抽取n
个样本,作为该树的训练集。
(2)、每个样本的特征维度为M
,随机地从M
个特征维度中选择m
个特征子集,构建决策树时,从这m
个特征子集进行划分。
(3)、每棵树都尽最大程度的生长,并且没有剪枝过程。思考:
一开始我们提到的随机森林中的“随机”就是指的这里的两个随机性。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)。随机森林分类效果(错误率)与两个因素有关:
(1)、森林中任意两棵树的相关性:相关性越大,错误率越大;
森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。
(2)、减小特征选择个数m,树的相关性和分类能力也会相应的降低;增大m,两者也会随之增大。所以关键问题是如何选择最优的m(或者是范围),这也是随机森林唯一的一个参数。结合实例详细的文章:讲解非常清晰的RF介绍