機器學習動手做Lesson 18 — 用共識決來集成非監督式學習

施威銘研究室
9 min readFeb 21, 2022

--

這篇文章中,我們提到了集成式學習(Ensemble Learning)在處理非監督式學習(Unsupervised Learning)的難處,並且提出如何使用重設索引來解決問題,也提到了重設索引可能的問題。在這篇文章中,我們將要說明另一個比較強健的方法,可以進行非監督式學習的集成。

一、回顧分群(Clustering)問題

說到分群問題,我們最終想要知道的事情,就是「哪些資料應該要放在同一群」,這是一個比較巨觀的想法。

如果使用微觀的想法,那分群問題即是「第 1 筆資料跟第 2 筆資料要放在同一群嗎?第 1 筆資料跟第 3 筆資料要放在同一群嗎? ... 第 2 筆資料跟第 3 筆資料要放在同一群嗎? ... 第 N-1 筆資料跟第 N 筆資料要放在同一群嗎?」。只要有辦法回答上述全部問題,就做完了分群。

二、共識分群(Consensus Clustering)的概念

現在,我們考慮「第 1 筆資料跟第 2 筆資料要放在同一群嗎」這個問題。假設現在有 3 個非監督式演算法,A 跟 B 演算法將第 1 筆資料跟第 2 筆資料放在同一群,C 演算法將第 1 筆資料跟第 2 筆資料放在不同群。那麼集成這 3 個演算法之後,第 1 筆資料跟第 2 筆資料要放在同一群,比較合理吧!

因此,我們要做的事情是「對任 2 筆資料,看看是不是過半的非監督式演算法,認為這 2 筆資料是屬於同一群」。

共識分群比上週提到的重設索引,最大的好處在於共識分群只管資料是否放在同一群,至於索引是什麼數字,一點也不重要,也就避免了索引的重設、運算中會有的問題。

Photo by Aditya Joshi on Unsplash

三、共現矩陣(Co-occurrence Matrix)

用來表現「對任 2 筆資料,看看是不是過半的非監督式演算法,認為這 2 筆資料是屬於同一群」的方法之一,便是使用共現矩陣。

共現矩陣是一個 N 乘 N 的矩陣,N 的大小即為資料數,第 i 行、第 j 列代表「有多少基學習器將第 i 筆資料跟第 j 筆資料放在同一群」。建立好共現矩陣後,就能夠根據矩陣內容來找到最佳的集成結果。

以下是建立共現矩陣的範例程式。X 是一個 2 維的 list ,用來記錄所有基學習器(Base Learner)的分群結果。第 i 行代表第 i 個基學習器的輸出,第 j 列代表該基學習器給第 j 筆資料的索引。

程式中,我們抓出第 i 筆資料,將此資料跟第 j 筆資料比較,看看有多少基學習器將這 2 筆資料放在同一群。

def get_cooccurence_matrix(X):

n_sample = len(X[0])
n_ensemble = len(X)

matrix = np.zeros((n_sample, n_sample))

for i in range(n_sample):
for j in range(n_sample):
for k in range(n_ensemble):
if(X[k][i] == X[k][j]):
matrix[i][j] = matrix[i][j] + 1

return matrix

四、找到共識

有了共現矩陣之後,現在我們要進行集成。首先,我們找有哪些資料跟第 1 筆資料應該要放在同一群,然後換看第 2 筆、第 3 筆 ... 。

    for row in range(n_sample):

element = []

for column in range(n_sample):
if(matrix[row][column] > threshold):
element.append(column)

cluster.append(element)

接下來,我們要看交集。舉例來說,如果第 1、2、3 筆資料要放在一起,第 3、4、5 筆資料要放在一起。那集成之後則第 1、2、3、4、5 都要放在一起。

    for row in range(n_sample):
for column in range(n_sample):
if(intersection(cluster[row], cluster[column])):
cluster[row] = union(cluster[row], cluster[column])

接著,我們將刪去重複的資訊。

    for row in cluster:
if row not in ensemble:
ensemble.append(row)

最後指定索引,就是集成後結果囉。

    for row in range(len(ensemble)):
result[np.array(ensemble[row]).astype(int)] = row

事實上,共識分群已經有 Python OpenEnsembles 函式庫可以使用,此函式庫也提供了其他集成非監督式學習的方法。函式庫的使用範例,可以參考「集成式學習:Python 實踐!整合全部技術,打造最強模型

關於作者

Chia-Hao Li received the M.S. degree in computer science from Durham University, United Kingdom. He engages in computer algorithm, machine learning, and hardware/software codesign. He was former senior engineer in Mediatek, Taiwan. His currently research topic is the application of machine learning techniques for fault detection in the high-performance computing systems.

完整程式

import numpy as np
from sklearn import datasets
from sklearn.metrics import accuracy_score
def intersection(x, y):
return (len(set(x) & set(y)) > 0)
def union(x, y):
return list(set(x) | set(y))
def KMeans(X, n_clusters):

dist = np.zeros(n_clusters)
result = np.zeros(len(X))
center = X[np.random.randint(0, len(X), size = 3)]

for iteration in range(20):
for i in range(len(X)):
for j in range(n_clusters):
dist[j] = sum((np.array(X[i]) -
np.array(center[j])) ** 2)
result[i] = np.argmin(dist)

for j in range(n_clusters):
center[j] = np.mean([X[i] for i in range(len(X)) if
result[i] == j], axis = 0)

return result.astype(int)
def get_cooccurence_matrix(X):

n_sample = len(X[0])
n_ensemble = len(X)

matrix = np.zeros((n_sample, n_sample))

for i in range(n_sample):
for j in range(n_sample):
for k in range(n_ensemble):
if(X[k][i] == X[k][j]):
matrix[i][j] = matrix[i][j] + 1

return matrix
def get_ensemble_cluster(matrix, n_sample, n_ensemble):

threshold = n_ensemble / 2

ensemble = []

cluster = []

result = np.zeros(n_sample)

for row in range(n_sample):

element = []

for column in range(n_sample):
if(matrix[row][column] > threshold):
element.append(column)

cluster.append(element)

for row in range(n_sample):
for column in range(n_sample):
if(intersection(cluster[row], cluster[column])):
cluster[row] = union(cluster[row], cluster[column])

for row in cluster:
if row not in ensemble:
ensemble.append(row)

for row in range(len(ensemble)):
result[np.array(ensemble[row]).astype(int)] = row

return result
iris = datasets.load_iris()
X = iris.data
Y = iris.target
n_cluster = 3
n_ensemble = 11
p = []
for i in range(n_ensemble):

p.append(KMeans(X, n_cluster))

matrix = get_cooccurence_matrix(p)

ensemble = get_ensemble_cluster(matrix, len(Y), n_ensemble)
print("Ensemble", accuracy_score(Y, ensemble))

--

--

施威銘研究室
施威銘研究室

Written by 施威銘研究室

致力開發AI領域的圖書、創客、教具,希望培養更多的AI人才。整合各種人才,投入創客產品的開發,推廣「實作學習」,希望實踐學以致用的理想。

No responses yet