机器学习之无监督学习

概念

无监督学习算法包括没有已知的输出,在学习的过程中没有老师指导学习算法,只有依靠输入数据,并需要从这些数据中提取知识。

无监督学习包括了数据集变换与聚类。

什么是数据集变换?

数据集的无监督变换是一种算法,用于创建数据新的表示,这种表示更容易被人或其它机器学习算法所理解。无监督变换常用手段是降维,输入是具有许多特征的高维数据表示,通过算法用较少的特征就可以概括其重要性。常见应用将数据降为可视化的二维表示。

聚类算法是将数据划分为不同的组,每组包含相似的物项。

无监督学习的主要挑战是评估算法是否学习到了有用的东西。无监督学习算法所使用的数据一般不包含任何标签信息。因此不知道正确的输出应该是什么。那么判断一个模型是否 表现很好就很困难。通常来说,评估无监督算法结果的唯一方法是人工检查。

常见应用预处理

无监督算法的另一个常见应用是作为监督算法的预处理步骤,通过学习新的数据标识,有时可以提高监督算法的精度,或者减少内存的占用和时间开销。常见4种预处理:

1 StandardScaler 确保每个特征的平均值为0,方差为1, 使所有特征都位于同一量级。缺点是这种缩放不能保证特征任何特定的值的最大值与最小值。

2 RobustScaler 确保每个特征的统计属性都位于同一范围,与StandardScaler类似,但是使用的是中位数与四分位数,而不是平均值与方差。这种处理可以忽略一些异常值,可能会给其它缩放方法造成麻烦。

3 MinMaxScaler,通过移动数据,使所有特征都刚好位于0到1之间。对于二维数据集来说,所有的数据都包含在x轴0到1与y轴0到1组成的矩形中。

4 Normalizer 用到一种完全不同的缩放方法。通过对每个数据点进行缩放,使得特征向量的欧式长度等于1.也就是将数据点投射到半径为1的圆上(更高维度就是球面)这种处理在只有数据的方向是重要的,而特征向量的长度无关紧要时,就很适合这种归一化处理。

下面我们来看一下MinMaxScaler的预处理的效果,以及处理后的数据在svm算法应用上的前后对比效果:

scss 复制代码
def viewMinMaxScaleProcessDataChange():
    cancer = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=1)
    scaler = MinMaxScaler()
    scaler.fit(X_train)
    # 变换数据
    X_train_scaled = scaler.transform(X_train)
    # 在缩放之前和之后分别打印数据集属性
    print("transformed shape: {}".format(X_train_scaled.shape))
    print("train feature minimum before scaling:\b{}".format(X_train.min(axis=0)))
    print("train feature maximum before scaling:\b{}".format(X_train.max(axis=0)))
    print("train feature minimum after scaling:\b{}".format(X_train_scaled.min(axis=0)))
    print("train feature maximum after scaling:\b{}".format(X_train_scaled.max(axis=0)))

    # MinMaxScaler(以及其他所有缩放器)总是对训练集和测试集应用完全相同的变换。也就是说,transform方法总是减去训练集的最小值,然后除以训练集的范围,
    # 而这两个值可能与测试集的最小值和范围并不相同。
    X_test_scaled = scaler.transform(X_test)
    # 在缩放之后打印测试数据的属性
    print("test feature minimum before scaling:\b{}".format(X_test.min(axis=0)))
    print("test feature maximum before scaling:\b{}".format(X_test.max(axis=0)))
    print("test feature minimum after scaling:\b{}".format(X_test_scaled.min(axis=0)))
    print("test feature maximum after scaling:\b{}".format(X_test_scaled.max(axis=0)))


def MinMaxScaleOnSvm():
    cancer = load_breast_cancer()
    X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)
    svm = SVC(C=100, gamma='auto')
    svm.fit(X_train, y_train)
    print("Test accuracy:{:.2f}".format(svm.score(X_test, y_test)))

    scaler = MinMaxScaler()
    scaler.fit(X_train)
    X_train_scaled = scaler.transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    svm.fit(X_train_scaled, y_train)
    print("Scaled test set accuracy:{:.2f}".format(svm.score(X_test_scaled, y_test)))


if __name__ == '__main__':
    viewMinMaxScaleProcessDataChange()
    MinMaxScaleOnSvm()

下面是执行后的结果,可以看到原来很大的数据在经过处理后,值的范围在0到1之间. 此外,svm算法对数据的释放是敏感的,经过缩放后的数据,在svm的学习效果上从0.63提升到了0.97,效果非常显著。

css 复制代码
train feature minimum before scaling[6.981e+00 9.710e+00 4.379e+01 1.435e+02 5.263e-02 1.938e-02 0.000e+00 0.000e+00 1.060e-01 5.024e-02 1.153e-01 3.602e-01 7.570e-01 6.802e+00 1.713e-03 2.252e-03 0.000e+00 0.000e+00 9.539e-03 8.948e-04 7.930e+00 1.202e+01 5.041e+01 1.852e+02 7.117e-02 2.729e-02 0.000e+00 0.000e+00 1.566e-01 5.521e-02]
 
train feature maximum before scaling[2.811e+01 3.928e+01 1.885e+02 2.501e+03 1.634e-01 2.867e-01 4.268e-01 2.012e-01 3.040e-01 9.575e-02 2.873e+00 4.885e+00 2.198e+01 5.422e+02 3.113e-02 1.354e-01 3.960e-01 5.279e-02 6.146e-02 2.984e-02 3.604e+01 4.954e+01 2.512e+02 4.254e+03 2.226e-01 9.379e-01 1.170e+00 2.910e-01 5.774e-01 1.486e-01]
 
train feature minimum after scaling[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 
train feature maximum after scaling[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
 
test feature minimum before scaling[7.691e+00 1.038e+01 4.834e+01 1.704e+02 6.828e-02 3.116e-02 0.000e+00 0.000e+00 1.365e-01 4.996e-02 1.115e-01 3.871e-01 8.484e-01 7.228e+00 2.866e-03 3.746e-03 0.000e+00 0.000e+00 7.882e-03 1.087e-03 8.678e+00 1.420e+01 5.449e+01 2.236e+02 8.774e-02 5.131e-02 0.000e+00 0.000e+00 1.565e-01 5.504e-02]
 
test feature maximum before scaling[2.722e+01 3.381e+01 1.821e+02 2.250e+03 1.425e-01 3.454e-01 3.754e-01 1.878e-01 2.906e-01 9.744e-02 1.292e+00 2.612e+00 1.012e+01 1.587e+02 1.604e-02 1.006e-01 3.038e-01 3.322e-02 7.895e-02 1.220e-02 3.312e+01 4.178e+01 2.208e+02 3.216e+03 2.098e-01 1.058e+00 1.252e+00 2.688e-01 6.638e-01 2.075e-01]
 
test feature minimum after scaling[ 0.0336031   0.0226581   0.03144219  0.01141039  0.14128374  0.04406704  0.          0.          0.1540404  -0.00615249 -0.00137796  0.00594501  0.00430665  0.00079567  0.03919502  0.0112206   0.          0. -0.03191387  0.00664013  0.02660975  0.05810235  0.02031974  0.00943767  0.1094235   0.02637792  0.          0.         -0.00023764 -0.00182032]
  
test feature maximum after scaling[0.9578778  0.81501522 0.95577362 0.89353128 0.81132075 1.21958701 0.87956888 0.9333996  0.93232323 1.0371347  0.42669616 0.49765736 0.44117231 0.28371044 0.48703131 0.73863671 0.76717172 0.62928585 1.33685792 0.39057253 0.89612238 0.79317697 0.84859804 0.74488793 0.9154725  1.13188961 1.07008547 0.92371134 1.20532319 1.63068851]
 
Test accuracy:0.63
Scaled test set accuracy:0.97

PCA、NMF、t-SNE算法

利用无监督学习进行数据变换的最常见目的是可视化,压缩数据,以及寻找信息量更大的数据表示以用于进一步的处理。为了实现上述目的,最常用的算法是主成分分析(principal component analysis, PCA)。

PCA是一种旋转数据集的方法,旋转后的特征在统计上不相关,做完旋转之后,通常根据新特征对解释数据的重要性来选择一个子集。

算法首先找到方差最大的方向,将其标记为"成分1"(Component 1)。这是数据中包含最多信息的方向(或向量),换句话说,沿着这个方向的特征之间最为相关。然后,算法找到与第一个方向正交(成直角)且包含最多信息的方向. 利用这一过程找到的方向被称为主成分(principal component),因为它们是数据方差的主要方向。一般来说,主成分的个数与原始特征相同。

PCA的优缺点: 通过仅保留一部分主成分来进行降维。 也可以找到一种数据表示,比给定的原始表示更适合于分析,可以用于特征提取。缺点是通常不容易对图中的两个轴做出解释。主成分对应于原始数据中的方向,所以它们是原始特征的组合。但这些组合往往非常复杂。

非负矩阵分解:

非负矩阵分解(non-negative matrix factorization, NMF)是一种无监督学习算法. 其目的在于提取有用的特征,可用于降维。 原理是通过试图将每个数据点写成分量的加权求和,但与PCA不同的是,PCA想要的是正交分量,并且能够解释尽可能多的数据方差。但是NMF希望分量和系数都为非负。

为了说明非负矩阵分解算法的应用效果,以比较常见的人脸识别为例子进行说明,首先我们从人脸库中打印一些基本的信息:

python 复制代码
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_lfw_people
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.decomposition import PCA
from sklearn.decomposition import NMF
import warnings
warnings.filterwarnings("ignore")

def viewOriginalImage():
    people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
    fix, axes = plt.subplots(2, 5, figsize=(15, 8), subplot_kw={'xticks': (), 'yticks': ()})
    for target, image, ax in zip(people.target, people.images, axes.ravel()):
        ax.imshow(image)
        ax.set_title(people.target_names[target])
    # plt.show()
    print("people.images.shape:{}".format(people.images.shape))
    print("Number of classes:{}".format(len(people.target_names)))

    # 计算每个目标出现的次数
    counts = np.bincount(people.target)
    names = people.target_names
    for i, (count, name) in enumerate(zip(counts, names)):
        print("{0:25} {1:3}".format(name, count), end='   ')
        if (i + 1) % 3 == 0:
            print()

下面的打印信息显示了一共有3023张人脸特征图片,每张图片有87*65个特征点,这么多图片实际是62个人的照片集。 此外我们打印了每个人具体有多少张特征图片,例如George W Bush这个人有530张对应的照片,估计是个比较牛的人,采集了这么多张照片。

javascript 复制代码
people.images.shape:(3023, 87, 65)
Number of classes:62
Alejandro Toledo           39   Alvaro Uribe               35   Amelie Mauresmo            21   
Andre Agassi               36   Angelina Jolie             20   Ariel Sharon               77   
Arnold Schwarzenegger      42   Atal Bihari Vajpayee       24   Bill Clinton               29   
Carlos Menem               21   Colin Powell              236   David Beckham              31   
Donald Rumsfeld           121   George Robertson           22   George W Bush             530   
Gerhard Schroeder         109   Gloria Macapagal Arroyo    44   Gray Davis                 26   
Guillermo Coria            30   Hamid Karzai               22   Hans Blix                  39   
Hugo Chavez                71   Igor Ivanov                20   Jack Straw                 28   
Jacques Chirac             52   Jean Chretien              55   Jennifer Aniston           21   
Jennifer Capriati          42   Jennifer Lopez             21   Jeremy Greenstock          24   
Jiang Zemin                20   John Ashcroft              53   John Negroponte            31   
Jose Maria Aznar           23   Juan Carlos Ferrero        28   Junichiro Koizumi          60   
Kofi Annan                 32   Laura Bush                 41   Lindsay Davenport          22   
Lleyton Hewitt             41   Luiz Inacio Lula da Silva  48   Mahmoud Abbas              29   
Megawati Sukarnoputri      33   Michael Bloomberg          20   Naomi Watts                22   
Nestor Kirchner            37   Paul Bremer                20   Pete Sampras               22   
Recep Tayyip Erdogan       30   Ricardo Lagos              27   Roh Moo-hyun               32   
Rudolph Giuliani           26   Saddam Hussein             23   Serena Williams            52   
Silvio Berlusconi          33   Tiger Woods                23   Tom Daschle                25   
Tom Ridge                  33   Tony Blair                144   Vicente Fox                32   
Vladimir Putin             49   Winona Ryder               24

流形学习算法(manifold learning algorithm): 主要用于可视化很少用来生成两个以上的新特征. 其中特别有用的一个就是t-SNE算法, 该算法只能变换用于训练的数据, 不允许变换新数据,这意味着这些算法不能用于测试集, 因此流形学习对探索性数据分析是很有用的,但如果最终目标是监督学习的话,则很少使用。

t-SNE算法思想: 首先给出每个数据点的随机二维表示,然后尝试让在原始特征空间中距离较近的点更加靠近,原始特征空间中相距较远的点更加远离。 以此找到数据的一个二维表示,尽可能地保持数据点之间的距离。t-SNE重点关注距离较近的点,换句话说,它试图保存那些表示比较靠近的点的信息。

下main我们以数字分类为例,看一下pca与流行学习算法两种无监督学习的预处理算法,在数字分类上的一些效果:

ini 复制代码
def pcaProcessAndViewer():
    digits = load_digits()
    # 将64个成分降维到2个
    pca = PCA(n_components=2)
    pca.fit(digits.data)
    digits_pca = pca.transform(digits.data)
    # rgb 颜色对应的code
    colors = ["#476A2A", "#7851B8", "#BD3430", "#4A2D4E", "#875525", "#A83683", "#4E655E", "#853541", "#3A3120",
              "#535D8E"]
    plt.figure(figsize=(10, 10))
    # 分别设置x轴与y轴的取值范围
    plt.xlim(digits_pca[:, 0].min(), digits_pca[:, 0].max())
    plt.ylim(digits_pca[:, 1].min(), digits_pca[:, 1].max())

    for i in range(len(digits.data)):
        # digits_pca[i, 0]作为x轴坐标,digits_pca[i, 1]作为y轴坐标,定位到的这个点的值是digits.target[i],字体是粗体字号为9
        plt.text(digits_pca[i, 0], digits_pca[i, 1], str(digits.target[i]), color=colors[digits.target[i]],
                 fontdict={'weight': 'bold', 'size': 9})
    plt.xlabel("First principal component")
    plt.xlabel("Second principal component")
    plt.show()

从下图可以看出,pca处理后,将 0,6,4三个数字比较好的分类了。但是这三个数字之间相互还是有参杂。

下面将t-SNE应用于同一数据集,并对结果进行比较,可以发现t-SNE的结果非常棒。所有类别都被明确分开。数字1和9被分成几块,但大多数类别都形成一个密集的组。实际上这种方法并不知道类别标签:它完全是无监督的。但它能够找到数据的一种二维表示,仅根据原始空间中数据点之间的靠近程度就能够将各个类别明确分开。

ini 复制代码
def tSneProcessAndViewer():
    digits = load_digits()
    tsne = TSNE(random_state=42)
    digits_stne = tsne.fit_transform(digits.data)
    plt.figure(figsize=(10, 10))
    # 分别设置x轴与y轴的取值范围
    plt.xlim(digits_stne[:, 0].min(), digits_stne[:, 0].max())
    plt.ylim(digits_stne[:, 1].min(), digits_stne[:, 1].max())
    colors = ["#476A2A", "#7851B8", "#BD3430", "#4A2D4E", "#875525", "#A83683", "#4E655E", "#853541", "#3A3120",
              "#535D8E"]
    for i in range(len(digits.data)):
        # digits_pca[i, 0]作为x轴坐标,digits_pca[i, 1]作为y轴坐标,定位到的这个点的值是digits.target[i],字体是粗体字号为9
        plt.text(digits_stne[i, 0], digits_stne[i, 1], str(digits.target[i]), color=colors[digits.target[i]],
                 fontdict={'weight': 'bold', 'size': 9})
    plt.xlabel("t-SNE feature 0")
    plt.xlabel("t-SNE feature 1")
    plt.show()
相关推荐
莫叫石榴姐21 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
ChaseDreamRunner1 小时前
迁移学习理论与应用
人工智能·机器学习·迁移学习
谢眠2 小时前
深度学习day3-自动微分
python·深度学习·机器学习
搏博3 小时前
神经网络问题之一:梯度消失(Vanishing Gradient)
人工智能·机器学习
rellvera3 小时前
【强化学习的数学原理】第02课-贝尔曼公式-笔记
笔记·机器学习
我感觉。4 小时前
【机器学习chp4】特征工程
人工智能·机器学习·主成分分析·特征工程
DieYoung_Alive4 小时前
一篇文章了解机器学习(下)
人工智能·机器学习
幻风_huanfeng5 小时前
人工智能之数学基础:线性代数在人工智能中的地位
人工智能·深度学习·神经网络·线性代数·机器学习·自然语言处理
请你喝好果汁6415 小时前
单细胞|M3-4. 细胞聚类与轨迹推断
机器学习·数据挖掘·聚类
Chef_Chen7 小时前
从0开始学习机器学习--Day33--机器学习阶段总结
人工智能·学习·机器学习