Press "Enter" to skip to content

机器学习之无监督学习 一、什幺是无监督学习所谓无监督学习,指的并不是现实中没人看管的学习方式,…

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

一、什幺是无监督学习

 

所谓无监督学习,指的并不是现实中没人看管的学习方式,而是指利用无标签的数据学习数据的分布或者数据之间的关系。这个定义的关键在于理解什幺是标签,理解无监督学习不如先看一下有监督学习,有监督学习最典型的就是辨别动物,想要辨别是一条狗,就需要利用大量的狗的图片进行学习,得到狗的特点,而我们学习的图片是已经知道是狗,这就相当于给图片加了一个标签,所以有监督学习和无监督学习的区别就在于学习的数据是不是带标签,也就是我们是否知道学习的数据是什幺。这个解释可能还有点牵强,结合下面的具体内容可能会容易理解一些。

 

无监督学习有两个最常使用的场景,即聚类和降维。

 

聚类在大三数据挖掘的课程里面学习过不少,简单来说聚类就是将一堆零散的数据根据某些标准分为几个类别,一般来说最常使用的标准是距离,距离也分为好几类,比如欧式距离(空间中两点的直线距离)、曼哈顿距离(城市街区距离)、马氏距离(数据的协方差距离)和夹角余弦。

 

降维指的是在保证数据所具有的代表性特性或者分布的情况下,将高维数据转化为低维数据的过程,这个过程包括数据的可视化和数据的精简。简单来说,我们拿到的一些数据存在很多字段,但是一些字段的数据对于结果是没有意义或者意义极小,但是在做机器学习的过程中也会参与计算,对我们最终分析结果造成不利影响,所以为了方便计算分析,要想办法将这部分字段去掉,这就是降维。

 

降维和聚类都是无监督学习的典型任务,任务之间存在关联,比如高维数据的聚类可以通过降维处理更好地进行。

 

二、聚类

 

聚类一般使用的是sklearn库,常用的聚类算法函数都在这个sklearn.cluster这个模块中,根据各种算法的特点,得到分类的结果以及耗费的时间也是不同的,这是由算法的特性决定的。

聚类的这部分因为在数据挖掘的课程里学习过,所以这里的理解就比较方便了。网课主要是介绍了两个聚类常用的算法,下面展开介绍一下这两个算法在Python中的使用。

 

①K-means聚类算法

 

这里直接上我准备复试的时候写的笔记,比较官方的过程就不再赘述。

 

这个算法需要指定参数k,用于将n个数据分成k个簇,其中划分后得到的k个簇内部需要有较高的相似度,其实就是将n个个体根据一定的标准分成k个簇。

 

这个算法在处理时是以一个不断循环的过程进行的,第一个循环中会随机选择k个点作为初始的聚类中心,之后根据标准,将n个个体分给这k个聚类中心,标准是距离的情况时,就是将每个点划分到距离自己最近的聚类中心代表的簇中。一轮划分后需要更新聚类中心,标准如果是距离,那幺更新的方法是取平均值,也就是对簇中所有的点的坐标取平均,得到新的聚类中心。不断重复这个过程,直到聚类中心不再发生变化为止。

 

网课里面没有给出运行算法的数据,这里将备注后的代码挂一下:

 

import numpy as np
from sklearn.cluster import KMeans
# 引入sklearn.cluster库
def loadData(filePath):
    fr = open(filePath, 'r+')
    # 以读写形式打开路径上的文件
    lines = fr.readlines()
    retData = []
    retCityName = []
    # 准备读取数据用的数据结构
    for line in lines:
        items = line.strip().split(",")
        # 去除一行中首尾的空格,之后根据逗号进行划分
        retCityName.append(items[0])
        # 获得城市名
        retData.append([float(items[i]) for i in range(1, len(items))])
        # 获得城市数据
    return retData, retCityName
if __name__ == '__main__':
    data, cityName = loadData('city.txt')
    # 读取数据
    km = KMeans(n_clusters=4)
    # 调用KMeans函数,参数表明k的值
    label = km.fit_predict(data)
    # 将数据导入函数
    expenses = np.sum(km.cluster_centers_, axis=1)
    # 计算每个簇中城市的数目
    CityCluster = [[], [], [], []]
    for i in range(len(cityName)):
        CityCluster[label[i]].append(cityName[i])
    # 存储分类结果
    for i in range(len(CityCluster)):
        print("Expenses:%.2f" % expenses[i])
        print(CityCluster[i])
    # 输出每个簇的城市以及平均花费输出

 

这段代码可以看出,利用sklearn运行Kmeans算法的关键,在于实例化一个Kmeans实例,也就是代码中的km对象,之后指定运行的参数,需要的参数包括三个:n_clusters(指定聚类中心的个数)、init(初试聚类中心的初始化方法)、max_iter(最大的迭代次数),一般调用只用给出第一个参数即可,后面的基本都是默认的。

 

初始化之后,利用fit_predict()传入数据并且计算簇中心以及为簇分配序号。

 

②DBSCAN算法

 

这个算法也是聚类中很经典的算法,与Kmeans不同的是,DBSCAN是一种基于密度的聚类算法,不需要指定簇的个数而且最终簇的个数也是不确定的。

 

DBSCAN算法将数据点分为下面的三类:

算法的流程都是针对于这三种点进行的,首先将所有点进行划分,划分后删除噪音点,将所有Eps内部的核心点之间连接起来,所有连接的核心点形成了一个簇,将核心点范围内的边界点也划分入相应的簇中。简单来说就是就是一个不断利用密度可达来扩大簇的过程。这个算法的流程也决定了,DBSCAN算法对Eps和MinPts是很敏感的。

 

import numpy as np
import sklearn.cluster as skc
from sklearn import metrics
import matplotlib.pyplot as plt
mac2id = dict()
onlinetimes = []
f = open('TestData.txt', encoding='utf-8')
# 读取数据
for line in f:
    mac = line.split(',')[2]
    onlinetime = int(line.split(',')[6])
    starttime = int(line.split(',')[4].split(' ')[1].split(':')[0])
    # 读取每一行的开始上网时间和上网时长
    if mac not in mac2id:
        mac2id[mac] = len(onlinetimes)
        onlinetimes.append((starttime, onlinetime))
    else:
        onlinetimes[mac2id[mac]] = [(starttime, onlinetime)]
    # 将上网信息存入字典 
real_X = np.array(onlinetimes).reshape((-1, 2))
X = real_X[:, 0:1]
db = skc.DBSCAN(eps=0.01, min_samples=20).fit(X)
# 调用DBSCAN,指定两个参数,并且传入数据
labels = db.labels_
# 得到划分的标签簇
print('Labels:')
print(labels)
# 打印划分
raito = len(labels[labels[:] == -1]) / len(labels)
print('Noise raito:', format(raito, '.2%'))
# 打印噪声率
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
print('Estimated number of clusters: %d' % n_clusters_)
print("Silhouette Coefficient: %0.3f" % metrics.silhouette_score(X, labels))
# 打印结果评价
for i in range(n_clusters_):
    print('Cluster ', i, ':')
    print(list(X[labels == i].flatten()))
# 打印每个簇的数据
plt.hist(X, 24)
# 以直方图形式打印结果

 

这里是加上注解以后的源代码,和Kmeans类似,都是实例化一个对象然后调用方法,因为DBSCAN有两个关键参数,所以需要在实例化时给出eps和min_samples,之后就是传入数据和打印数据。

 

总的来说这一部分的两个聚类算法各有利弊,Kmeans很简单,但是对于噪声和孤立点数据很敏感,也就是说少量的数据就可能对最终结果产生很大的影响,DBSCAN可以在存在噪声的数据环境中进行聚类,但是对与半径和最小数目的设置十分敏感。

 

聚类算法并不止这些,这里顺便放一下复试准备的其它聚类算法简介:

三、降维

 

前面已经简单介绍了一下什幺是降维,这里总结一下网课里面介绍的Python中的降维算法。

所谓降维,就是将原数据中用处不大或者没有用处的部分去掉,从而简化数据,让数据在判断的过程中少进行一些操作。同聚类一样,由于我们使用的是Python的库文件,所以不需要去了解降维算法的具体原理,这个是机器学习的内容而不是机器学习应用的内容,我们需要的是会用给定的库文件。网课主要介绍了降维算法中的PCA和NMF,下面展开介绍这两种降维算法。

 

①PCA算法

 

PCA中文名称主成分分析,是一种常用的降维方法,可以用于高维数据集的探索与可视化,也可以用作数据的预处理和压缩。PCA可以将具有相关性的高维变量合成为线性无关的低位变量,而主成分则尽可能保留原始数据的信息。

 

简单介绍一下PCA的原理,PCA主要就是选择样本协方差矩阵的前k个特征值对应的特征向量作为简化后的内容。所以除了数据,还需要给定一个低维空间维数,即要简化为多少个特征向量。

由于我们使用了Python的库,所以这个过程只需要简单了解一下,使用库时就快很多了。在sklearn库中,可以使用sklearn.decomposition.PCA加载PCA然后进行降维,最主要的参数有两个:n_components(主成分的个数,即降维后数据的维度)和svd_solver(特征值分解的方法)。

 

下面用鸢尾花数据集的降维来尝试一下,已知鸢尾花数据集是一个四维的数据集,这里使用PCA对鸢尾花数据进行降维,从而实现数据在二维平面上的可视化。

 

import matplotlib.pyplot as plt
# 数据可视化需要用到matplotlib库
from sklearn.decomposition import PCA
# 使用PCA需要用decomposition库
from sklearn.datasets import load_iris
# 使用鸢尾花数据集需要引入datasets库
data = load_iris()
# 加载鸢尾花数据集
y = data.target
# 取出数据中的标签,即鸢尾花的分类结果
X = data.data
# 取出数据中的数值,即待降维的四维数据
pca = PCA(n_components=2)
# 加载PCA算法,指定n_components的值为2,即降维为2维数据
reduced_X = pca.fit_transform(X)
# 传入数据,对数据进行降维,结果返回并保存
red_x, red_y = [], []
blue_x, blue_y = [], []
green_x, green_y = [], []
for i in range(len(reduced_X)):
    if y[i] == 0:
        red_x.append(reduced_X[i][0])
        red_y.append(reduced_X[i][1])
    elif y[i] == 1:
        blue_x.append(reduced_X[i][0])
        blue_y.append(reduced_X[i][1])
    else:
        green_x.append(reduced_X[i][0])
        green_y.append(reduced_X[i][1])
# 将结果分为三类显示在图表中
plt.scatter(red_x, red_y, c='r', marker='x')
plt.scatter(blue_x, blue_y, c='b', marker='D')
plt.scatter(green_x, green_y, c='g', marker='.')
plt.show()
# 打印

 

实际运行结果如下,所有的数据被分为了三类,而且由于进行了降维,所以原本的四维数据现在可以显示在二维的坐标系中,并且分类的结果并没有受到影响。

对比降维前后的数据的数值情况,可以发现降维前能够理解的数据现在已经无法理解了,这应该是降维对数据含义造成的影响,原数据的含义已经无法理解,但是并不影响分类等操作。

 

②NMF算法

 

NMF中文名称非负矩阵分解,是在矩阵中所有元素均为非负数的条件下的矩阵分解方法。基本的思想是给定一个非负矩阵V,找到一个非负矩阵W和一个非负矩阵H,使得两个矩阵的乘积近似等于矩阵V中的值。

其中W矩阵是基础图像矩阵,相当于V中抽取的特征,H为系数矩阵。可见这种抽取特征的方式能够极大简化内容,所以NMF能够广泛用于图像分析、文本挖掘以及语音处理等方面。

 

在Python中,我们使用sklearn.decomposition.NMF加载NMF算法,需要指定的参数有两个:n_components(指定分解后矩阵的单个维度)和init(W矩阵和H矩阵的初始化方式)。

 

降维的一个很大用途就是用于图片的特征提取,下面的代码是网课中演示的图片特征提取。

 

from numpy.random import RandomState
import matplotlib.pyplot as plt
# 用于数据的可视化
from sklearn.datasets import fetch_olivetti_faces
# 加载人脸数据集
from sklearn import decomposition
# 加载算法包
n_row, n_col = 2, 3
n_components = n_row * n_col
# 打印时的排布
image_shape = (64, 64)
# 图片分辨率
dataset = fetch_olivetti_faces(shuffle=True, random_state=RandomState(0))
# 加载数据
faces = dataset.data
# 提取出数据中的数值部分
def plot_gallery(title, images, n_col=n_col, n_row=n_row):
# 打印函数
    plt.figure(figsize=(2. * n_col, 2.26 * n_row))
    # 创建并设置好图片大小
    plt.suptitle(title, size=16)
    # 副标题设置
    for i, comp in enumerate(images):
        plt.subplot(n_row, n_col, i + 1)
        # 将图片添加到对应的框中
        vmax = max(comp.max(), -comp.min())
        plt.imshow(comp.reshape(image_shape), cmap=plt.cm.gray,
                   interpolation='nearest', vmin=-vmax, vmax=vmax)
        # 对数值进行归一化,并且以灰度形式显示
        plt.xticks(())
        plt.yticks(())
        # 去除坐标轴
    plt.subplots_adjust(0.01, 0.05, 0.99, 0.94, 0.04, 0.)
    # 调整间隔位置
plot_gallery("First centered Olivetti faces", faces[:n_components])
# 打印原来的图像
estimators = [
    ('Eigenfaces - PCA using randomized SVD',
     decomposition.PCA(n_components=6, whiten=True)),
    # 调用PCA实例并打印
    ('Non-negative components - NMF',
     decomposition.NMF(n_components=6, init='nndsvda', tol=5e-3))
    # 调用NMF实例并打印
]
for name, estimator in estimators:
    print("Extracting the top %d %s..." % (n_components, name))
    print(faces.shape)
    estimator.fit(faces)
    # 传入数据并进行特征提取
    components_ = estimator.components_
    # 得到返回结果
    plot_gallery(name, components_[:n_components])
    # 打印当次图像
plt.show()

 

运行结果如下(大晚上看差点给我带走):

两种算法都可以进行特征提取,但是有所区别,需要根据实际需要选择对应的算法。

 

四、小实验:基于聚类的图像分割

 

图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。采用聚类,可以进行简单的图像分割,这里采用Kmeans算法,让同一类的点都显示同一个颜色。

 

基本的思路是先导入包,加载图片并进行预处理,将预处理后的数据送入Kmeans,最后输出,代码如下:

 

import numpy as np
import PIL.Image as image
from sklearn.cluster import KMeans
# 引入需要的库文件
def loadData(filePath):
    f = open(filePath, 'rb')
    # 打开文件
    data = []
    img = image.open(f)
    # 以列表形式返回图片像素值
    m, n = img.size
    # 获得图片的大小
    
    for i in range(m):
        for j in range(n):
            x, y, z = img.getpixel((i, j))
            data.append([x / 256.0, y / 256.0, z / 256.0])
    # 将每个像素的颜色值处理到0-1
    
    f.close()
    return np.mat(data), m, n
    # 以矩阵形式返回处理的图像以及图片的大小
loc = 'C:\\Users\\Binary\\Pictures\\401.jpg'
# 选择一张照片
imgData, row, col = loadData(loc)
label = KMeans(n_clusters=4).fit_predict(imgData)
# 加载Kmeans算法并进行处理
label = label.reshape([row, col])
pic_new = image.new("L", (row, col))
# 创建一张新照片
for i in range(row):
    for j in range(col):
        pic_new.putpixel((i, j), int(256 / (label[i][j] + 1)))
# 向内部添加像素的值
filename = 'C:\\Users\\Binary\\Desktop\\'+loc.split('\\')[-1]
# 在桌面以原文件名保存
pic_new.save(filename, "JPEG")

 

这里选用了86里面的蕾娜的萌照做测试,对比结果如下:

总的来说,这个小程序就是用Kmeans算法,预处理是将图片原本的像素转换到0-1的范围内,之后将图片根据像素转换后的值进行聚类,聚类后的结果再填入一张新的图片。

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注