學習向量量化(Learning Vector Quantization,簡稱 LVQ)與 K 均值算法類似,也是試圖找到一組原型向量來刻畫聚類結構,但與一般聚類算法不同的是,LVQ 假設數據樣本帶有類別標記,學習過程利用樣本的這些監督信息來輔助聚類。
給定樣本集 ,每個樣本 是由 m 個屬性描述的特徵向量 是樣本 的類別標記。
【目標】:學得一組 m 維原型向量 ,每個原型向量代表一個聚類簇,簇標記 。
【算法描述】:
- 輸入:樣本集 ;原型向量個數 k,各原型向量預設的類別標記 ;學習率 。
- 輸出:原型向量 。
- 過程:
- 從樣本集 D 中隨機選取樣本作爲原型向量(初始均值向量);
- 在每一輪迭代中,隨機選取一個樣本,並計算該樣本與各個原型向量的距離,然後確定簇標記;
- 根據樣本和原型向量的類別標記是否一致來進行相應的更新:
- 若簇標記相等,則將原型向量向該樣本靠近;
- 若簇標記不相等,則將原型向量遠離該樣本;
- 若簇標記相等,則將原型向量向該樣本靠近;
- 若滿足算法的停止條件(指定的迭代次數),則將當前原型向量作爲最終結果返回。
在學得的一組原型向量 後,即可實現對樣本空間 X 的簇劃分。之後,對任意樣本 x,將其劃入與其距離最近的原型向量所代表的簇中。換言之,每個原型向量 定義了與之相關的一個區域 ,該區域中每個樣本與 的距離不大於它與其他原型向量 的距離,即
由此形成了對樣本空間 X 的簇劃分 ,該劃分通常稱爲 “Voronoi 剖分”(Voronoi tessellation)。若將 Ri 中樣本全用原型向量 pi 表示。則可實現數據的“有損壓縮”(lossy compression),這稱爲“向量量化”(vector quantization);LVQ 由此而得名。
案例說明
構造一個數據集用以說明 LVQ 的執行過程,數據集內容如下:
[3, 4, 1]
[3, 3, 1]
[1, 2, 0]
[1, 1, 0]
[2, 1, 0]
前兩列爲樣本的特徵數據 X,最後一列爲樣本的標籤值 Y。假設,此時我們將上述數據集劃分爲兩個簇,並隨機挑選 x1 和 x3 作爲初始原型向量 p1 和 p2,學習率 。
在第一輪迭代中隨機選取的樣本 x2,分別計算 x2 與 p1 和 p2 的距離爲 1 和 ,x2 與 p1 距離更近且具有相同的標籤值。
將 p1 更新爲 p1’ 之後,不斷重複上述過程,直到滿足終止條件。
【存在問題】:若挑選 x1 和 x2 或 x3 和 x4 作爲初始簇,那麼我們最後得到的兩個原型向量的標籤值都是相同的,例如都爲 0。這樣就會導致無論輸入的樣本與哪個原型向量更接近,其標籤值最終都爲 0,這顯然不是我們希望得到的結果。因此在挑選初始原型向量時需要將當前樣本集的所有標籤值都囊括在初始原型向量集中。
代碼實現
【所需包】:
- NumPy
【隨機挑選初始均值向量函數】:隨機確定起始位置,然後根據樣本集長度和挑選數量計算出步長。這麼做可以避免挑選出重複值,以及儘可能地從樣本集各個區域挑選數據,而不是聚集在某一區域。
def random_select(dataset, count):
# 獲取樣本集長度
length = dataset.shape[0]
# 計算步長和初始位置
step, start = length // count, np.random.randint(length)
data_select = []
# 按照起始位置和步長挑選數據
for i in range(count):
data_select.append(dataset[start])
start += step
start = start % length
return data_select
【LVP 函數實現】:
- 隨機挑選指定數量的原型向量。
prototypes = random_select(dataset, k)
- 開始迭代。
for i in range(n_iters):
# ...
- 迭代過程中,每次隨機從樣本集中選擇一個樣本,判斷該樣本所屬的簇。
data = dataset[numpy.random.randint(length)]
min_dist = numpy.inf
cluster_index = -1
for j in range(k):
dist = numpy.linalg.norm(data[:2] - prototypes[j, :2], 2)**2
if dist < min_dist:
min_dist = dist
cluster_index = j
- 接着,判斷當前樣本的標籤類別是否和所屬簇的類別標籤是否相同。根據不同的情況執行相應的操作。
if data[2] == prototypes[cluster_index, 2]:
prototypes[cluster_index, :2] += learning_rate * (data[:2] - prototypes[cluster_index, :2])
else:
prototypes[cluster_index, :2] -= learning_rate * (data[:2] - prototypes[cluster_index, :2])
- 迭代結束後,返回原型向量。
return prototypes
【完整代碼】:傳送門
def lvp(dataset, p, n_iters=100, learning_rate=1):
length = dataset.shape[0]
prototypes = random_select(dataset, p)
for i in range(n_iters):
# 隨機挑選樣本
data_random = dataset[np.random.randint(length)]
min_dist = np.inf
cluster_index = -1
# 判斷當前樣本所屬的簇
for j in range(p):
dist = np.linalg.norm(data_random[:2] - prototypes[j, :2], 2)
if dist < min_dist:
min_dist = dist
cluster_index = j
if data_random[2] == prototypes[cluster_index, 2]:
prototypes[cluster_index, :2] += learning_rate * min_dist
else:
prototypes[cluster_index, :2] -= learning_rate * min_dist
return prototypes
參考
- 《機器學習》周志華