热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java实现梯度上升算法_机器学习之PCA与梯度上升法

主成分分析(PrincipleComponentAnalysis,简称:PCA)是一种非监督学习的机器算法,主要用于数据的降维。PCA基本

主成分分析(Principle Component Analysis,简称:PCA)是一种非监督学习的机器算法,主要用于数据的降维。

PCA 基本原理

以有2个特征的二维平面举例,如图:

30575dd36ebc160c1731c6fb129e5bcb.png

横轴表示特征1,纵轴表示特征2,其中4个点表示二维的特征样本。如果要对样本进行降维降到一维,可以将这些点映射到横轴、纵轴或者其它轴上如:

dfdc7f2cf18bf9377ddb2feb16389205.png

映射到不同的轴后样本间间距会不一样,而要找的就是让样本间间距最大的轴。假定这个轴方向为 $w = (w1, w2)$。

对于样本间间距可以用方差 $Var(x) = \frac{1}{m}\sum_{i=1}^m(x_i-\overline x)^2$ 来衡量,方差越大,样本间越稀疏。

接着进行均值归0(demean)处理,即 $\overline x = 0$,使得 $Var(x) = \frac{1}{m}\sum_{i=1}^m(x_i)^2$。均值归0处理使得原始样本发生了变化转换成了新的样本 $X$。将其映射到轴 $w$ 上又得到新的样本 $X_{pr}$,此时的方差为:

$$

Var(X_{pr}) = \frac{1}{m}\sum_{i=1}^m(X_{pr}^{(i)})^2

$$

因为 $X_{pr}^{(i)}$ 是一个向量 $(X_{pr1}^{(i)}, X_{pr2}^{(i)})$,所以实际的方差表示为:

$$

Var(X_{pr}) = \frac{1}{m}\sum_{i=1}^m||X_{pr}^{(i)}||^2

$$

最后计算 $||X_{pr}^{(i)}||$,如图所示:

b06ada8780537d8cb6a958ead45ccd48.png

$||X_{pr}^{(i)}|| = X^{(i)}·w$ ($w$ 为单位向量),如此得到了想要的目标函数:

求 $w$ 使得 $Var(X_{pr}) = \frac{1}{m}\sum_{i=1}^m(X^{(i)}·w)^2$ 最大;对于多维特征,即 $\frac{1}{m}\sum_{i=1}^m(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)^2$ 最大。

梯度上升法解决PAC问题

在梯度上升法中,$+\eta\nabla f$ 代表了 $f$ 增大的方向。

对于 PAC 的目标函数:求 $w$ 使得 $f(X) = \frac{1}{m}\sum_{i=1}^m(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)^2$ 最大,可以使用梯度上升法来解决。

$f(X)$ 的梯度为:

$$

\nabla f = \begin{pmatrix} \frac{\partial f}{\partial w_1} \\\ \frac{\partial f}{\partial w_2} \\\ \cdots \\\ \frac{\partial f}{\partial w_n} \end{pmatrix} = \frac{2}{m} \begin{pmatrix} \sum_{i=1}^{m}(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)X_1^{(i)} \\\ \sum_{i=1}^{m}(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)X_2^{(i)} \\\ \cdots \\\ \sum_{i=1}^{m}(X_1^{(i)}w_1+X_2^{(i)}w_2+...+X_n^{(i)}w_n)X_n^{(i)} \end{pmatrix} = \frac{2}{m} \begin{pmatrix} \sum_{i=1}^{m}(X^{(i)}w)X_1^{(i)} \\\ \sum_{i=1}^{m}(X^{(i)}w)X_2^{(i)} \\\ \cdots \\\ \sum_{i=1}^{m}(X^{(i)}w)X_n^{(i)} \end{pmatrix} = \frac{2}{m}·X^T(Xw)

$$

注:这里略去了转换过程。

利用梯度公式 $\nabla f = \frac{2}{m}·X^T(Xw)$ 的梯度上升法实际上是批量梯度上升法(还有随机梯度上升法和小批量梯度上升法,本文不涉及)。

梯度上升法求主成分

求第一主成分

首先定义一组有两个特征的数据集 $X$,共100个样本:

import numpy as np

X = np.empty((100, 2))

X[:, 0] = np.random.uniform(0., 100., size=100)

X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0, 10., size=100)

$X$ 可视化如图:

64eece2a408e2821df493d316d334a5c.png

demean() 方法对 $X$ 进行均值归0处理:

def demean(X):

return X - np.mean(X, axis=0)

X_demean = demean(X)

均值归0处理后的 $X\_demean$ 可视化如图:

8d8a8ad2b5e3d7fc4465847b7dd6ae34.png

f() 方法求函数 $f$ 的值:

def f(w, X):

return np.sum(X.dot(w) ** 2) / len(X)

df() 方法根据梯度公式 $\nabla f = \frac{2}{m}·X^T(Xw)$ 求梯度:

def df(w, X):

return X.T.dot(X.dot(w)) * 2 / len(X)

gradient_ascent() 方法为梯度上升法的过程,在 n_iters 次循环中,每次获得新的 $w$,如果新的 $w$ 和旧的 $w$ 对应的函数 $f$ 的值的差值满足精度 epsilon,则表示找到了合适的 $w$:

def gradient_ascent(X, initial_w, eta, n_iters=1e4, epsilon=1e-8):

w = direction(initial_w)

cur_iter = 0

while cur_iter

gradient = df(w, X)

last_w = w

w = w + eta * gradient

w = direction(w) # 将w转换成单位向量

if(abs(f(w, X) - f(last_w, X))

break

cur_iter += 1

return w

其中调用了一个方法 direction(),用于将 $w$ 转换成单位向量:

def direction(w):

return w / np.linalg.norm(w)

接着使用梯度上升法,先设定一个初始的 $w$ 和学习率 $\eta$:

initial_w = np.random.random(X.shape[1])

eta = 0.001

直接调用 gradient_ascent() 方法:

w = gradient_ascent(X_demean, initial_w, eta)

将得到的 $w$ 与样本可视化如图($w$ 以直线表示):

222e4b8488e7ad0c648d8b053d297a64.png

求前n个主成分

前面求出了第一主成分,如何求出下一个主成分呢?以二维特征为例,如图:

4e8d61001cdcf404b5a4e5766ccbd048.png

样本 $X^{(i)}$ 在第一主成分 $w$ 上的分量为 $(X_{pr1}^{(i)}, X_{pr2}^{(i)})$,下一个主成分上的分量 $X^{'(i)} = X^{(i)} - (X_{pr1}^{(i)}, X_{pr2}^{(i)})$,因此只需将数据在第一主成分上的分量去掉,再对新的数据求第一主成分即可。

first_n_component() 方法用于求 $X$ 的前 n 个主成分,在 for 循环中每次求主成分时都设定一个随机的 $w$(initial_w),得到一个主成分后就去掉在这个主成分上的分量 X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w:

def first_n_component(n, X, eta=0.001, n_iters=1e4, epsilon=1e-8):

X_pca = X.copy()

X_pca = demean(X_pca)

res = []

for i in range(n):

initial_w = np.random.random(X_pca.shape[1])

w = gradient_ascent(X_pca, initial_w, eta)

res.append(w)

X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w

return res

降维映射

得到了多个主成分后,就可以通过这些主成分将高维数据映射到低维数据。

对于有 m 个样本,n 个特征的高维数据 $X$(m*n),主成分分析后得到前 k(k

$$

X = \begin{pmatrix} X_1^{(1)} X_2^{(1)} \cdots X_n^{(1)} \\\ X_1^{(2)} X_2^{(2)} \cdots X_n^{(2)} \\\ \cdots \cdots \cdots \cdots \\\ X_1^{(m)} X_2^{(m)} ... X_n^{(m)} \end{pmatrix}, W_k= \begin{pmatrix} W_1^{(1)} W_2^{(1)} \cdots W_n^{(1)} \\\ W_1^{(2)} W_2^{(2)} \cdots W_n^{(2)} \\\ \cdots \cdots \cdots \cdots \\\ W_1^{(k)} W_2^{(k)} ... W_n^{(k)} \end{pmatrix}

$$

n 维降维到 k 维后的新数据为:$X_k = X·W_k^T$。

在这个降维的过程中可能会丢失信息。如果原先的数据中本身存在一些无用信息,降维也可能会有降燥效果。

也可以将降维后的新数据恢复到原来的维度:$X_m = X_k·W_k$。

实现 PCA

定义一个类 PCA,构造函数函数中 n_components 表示主成分个数即降维后的维数,components_ 表示主成分 $W_k$;

函数 fit() 与上面的 first_n_component() 方法一样,用于求出 $W_k$;

函数 transform() 将 $X$ 映射到各个主成分分量中,得到 $X_k$,即降维;

函数 transform() 将 $X_k$ 映射到原来的特征空间,得到 $X_m$。

import numpy as np

class PCA:

def __init__(self, n_components):

self.n_components = n_components

self.components_ = None

def fit(self, X, eta=0.001, n_iters=1e4):

def demean(X):

return X - np.mean(X, axis=0)

def f(w, X):

return np.sum(X.dot(w) ** 2) / len(X)

def df(w, X):

return X.T.dot(X.dot(w)) * 2 / len(X)

def direction(w):

return w / np.linalg.norm(w)

def first_component(X, initial_w, eta, n_iters, epsilon=1e-8):

w = direction(initial_w)

cur_iter = 0

while cur_iter

gradient = df(w, X)

last_w = w

w = w + eta * gradient

w = direction(w)

if(abs(f(w, X) - f(last_w, X))

break

cur_iter += 1

return w

X_pca = demean(X)

self.components_ = np.empty(shape=(self.n_components, X.shape[1]))

for i in range(self.n_components):

initial_w = np.random.random(X_pca.shape[1])

w = first_component(X_pca, initial_w, eta, n_iters)

self.components_[i:] = w

X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w

return self

def transform(self, X):

return X.dot(self.components_.T)

def inverse_transform(self, X):

return X.dot(self.components_)

Scikit Learn 中的 PCA

Scikit Learn 中的 PCA 使用的不是梯度上升法,而是使用的相应的数学方法。使用方式如下:

from sklearn.decomposition import PCA

pca = PCA(n_components=1)

参数 n_components 表示主成分个数。

由于 PCA 降维会丢失原数据的信息,Scikit Learn 中的 PCA 在 0

pca = PCA(0.95)

即保留原数据 95% 的信息,此时程序会自动得到一个 components_。

借助 kNN 看 PCA

现在借助于 kNN 分类算法来看一看 PCA 的优势。首先使用一个正规的手写数据集 MNIST 数据集,获取样本数据集的程序为:

import numpy as np

from sklearn.datasets import fetch_mldata

mnist = fetch_mldata("MNIST original")

X, y = mnist['data'], mnist['target']

# MNIST 数据集已经默认分配好了训练数据集和测试数据集,即前60000个数据为训练数据

X_train = np.array(X[:60000], dtype=float)

y_train = np.array(y[:60000], dtype=float)

X_test = np.array(X[60000:], dtype=float)

y_test = np.array(y[60000:], dtype=float)

先不使用 PCA 来看看 kNN 算法的效率:

from sklearn.neighbors import KNeighborsClassifier

knn_clf = KNeighborsClassifier()

%time knn_clf.fit(X_train, y_train)

此时 fit 的过程消耗的时间为:

CPU times: user 29.6 s, sys: 306 ms, total: 29.9 s

Wall time: 30.7 s

接着验证测试数据集的准确性:

%time knn_clf.score(X_test, y_test)

此过程消耗的时间为:

CPU times: user 10min 23s, sys: 2.05 s, total: 10min 25s

Wall time: 10min 31s

并且测试数据集的正确率为0.9688。

然后在来看看 PCA 的优势,使用 PCA 时,设定保留信息为 90%:

from sklearn.decomposition import PCA

pca = PCA(0.9)

pca.fit(X_train)

X_train_reduction = pca.transform(X_train)

knn_clf = KNeighborsClassifier()

%time knn_clf.fit(X_train_reduction, y_train)

此时 fit 的过程消耗的时间为:

CPU times: user 317 ms, sys: 5.31 ms, total: 323 ms

Wall time: 323 ms

接着验证测试数据集的准确性:

X_test_reduction = pca.transform(X_test)

%time knn_clf.score(X_test_reduction, y_test)

此过程消耗的时间为:

CPU times: user 1min 15s, sys: 469 ms, total: 1min 15s

Wall time: 1min 17s

可以看出使用 PCA 后时间的消耗大大减少,而测试数据集的正确率为0.9728,不仅正确率没有降低还增加了,这是因为这里的降维过程丢失的信息是无用的信息从而达到了降燥效果。

源码地址



推荐阅读
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了贝叶斯垃圾邮件分类的机器学习代码,代码来源于https://www.cnblogs.com/huangyc/p/10327209.html,并对代码进行了简介。朴素贝叶斯分类器训练函数包括求p(Ci)和基于词汇表的p(w|Ci)。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • #define_CRT_SECURE_NO_WARNINGS#includelist.h#includevoidSListInit(PNode*pHead ... [详细]
  • 本文整理了Java中org.apache.solr.common.SolrDocument.setField()方法的一些代码示例,展示了SolrDocum ... [详细]
  • 本文整理了常用的CSS属性及用法,包括背景属性、边框属性、尺寸属性、可伸缩框属性、字体属性和文本属性等,方便开发者查阅和使用。 ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
author-avatar
愛妳yi輩zi
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有