最近在做乙個推薦系統的時候,我們採用的方法是基於svd的k-means聚類協同過濾演算法,其中在實現kmeans聚類演算法的時候參考了一篇文章,裡面給出了演算法**,並且很有新意的把最終的聚類結果以散點圖的形式展示了一下。於是昨天我突發奇想,可不可以把整個kmeans聚類過程視覺化出來,這樣還能更好的幫助我們理解kmeans具體的過程細節,聽起來很有意思,有了想法想盡快實現它,在午飯的時候還一直思考著該如何下手。由於很久沒用過d3了,有了想法還得一邊上網查d3的語法,看來還得好好學一下了。昨天在查閱了d3比例尺座標軸等資料後,終於在晚上實現了動態視覺化,真是太激動了!
k-means演算法是一種很常見的聚類演算法,它的基本思想是:通過迭代尋找k個聚類的一種劃分方案,使得用這k個聚類的均值來代表相應各類樣本時所得的總體誤差最小。
k-means演算法的基礎是最小誤差平方和準則。其代價函式是:
式中,μc(i)表示第i個聚類的均值。我們希望代價函式最小,直觀的來說,各類內的樣本越相似,其與該類均值間的誤差平方越小,對所有類所得到的誤差平方求和,即可驗證分為k類時,各聚類是否是最優的。
上式的代價函式無法用解析的方法最小化,只能有迭代的方法。k-means演算法是將樣本聚類成 k個簇(cluster),其中k是使用者給定的,其求解過程非常直觀簡單,具體演算法描述如下:
隨機選取 k個聚類質心點
重複下面過程直到收斂
下面用文字描述一下kmeans的偽**:
建立k個點作為初始的質心點(隨機選擇)
當任意乙個點的簇分配結果發生改變時
對資料集中的每乙個資料點
對每乙個質心
計算質心與資料點的距離
將資料點分配到距離最近的簇
對每乙個簇,計算簇中所有點的均值,並將均值作為質心
下面上kmeans聚類用js實現的**:
在kmeans聚類中,我們一般使用歐氏距離作為質心與資料點的誤差度量,因此我們在定義乙個距離函式:
function eucldistance(vector1, vector2) 按照第一步,我們首先要隨機選取k個質心,k就是我們要聚類的類的個數,data是資料點的陣列,會在下一節中提到。下面看**:
function initcentroids(data, k) else ;
node.x = data[index].x;
node.y = data[index].y;
node.index = index;
centroids.push(node);}}
console.log(centroids);
return centroids;
}這裡用到的getrandomnum函式是用來獲取0~length-1之間的隨機數的,**見下:
function getrandomnum(min, max) 並且為了保證在隨機選取質心的時候不會重複選擇,我們需要確保每次獲取的隨機數並沒使用過,於是我們用乙個資料儲存已經使用了的隨機數,然後每次檢查一下陣列是否包含了(contains)得到的新隨機數:
function contains(arr, obj)
}return false;
}第二步,重複計算知道收斂,即所有資料點所屬類別不再變化。**(****現的畫圖函式將會在下一節中描述):
function kmeans(data, k) 這樣我們得到的data就是乙個字典的陣列,例如data[0].x就是1.658985啦。
下面正式進入視覺化部分,開工!
建立svg,以及比例尺和座標軸:
var w = 1000;
var h = 600;
var xpadding = 200;
var ypadding = 50;
var xaxisheight = 500;
var xaxiswidth = 600;
var edge = 15;
var radius = 5;
var delay = 0;
var chart = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var colors = ["red", "blue", "yellow", "green"];
var xscale = d3.scale.linear()
.domain([0, d3.max(data, function(d) )])
.range([xpadding, xpadding + xaxiswidth]); //x軸
var yscale = d3.scale.linear()
.domain([0, d3.max(data, function(d) )])
.range([xaxisheight, ypadding]);//y軸
var xaxis = d3.svg.axis()
.scale(xscale)
.orient("bottom");
var yaxis = d3.svg.axis()
.scale(yscale)
.orient("left");
chart.append("g")
.attr("class","axis")
.attr("transform","translate("+0+","+xaxisheight+")")
.call(xaxis);
chart.append("g")
.attr("class","axis")
.attr("transform","translate("+xpadding+","+0+")")
.call(yaxis);
接下來我們需要做的就是繪製所有資料點以及初始質心,然後在發生變化的時候用動畫的形式重新繪製就可以啦。
畫資料點,初始預設都是黑色的小圓點:
function shownodes(data) )
.attr("cx", function(d) )
.attr("cy", function(d) )
.attr("r", function(d) );
}畫初始隨機的質心,不同質心用不同顏色區分,顏色陣列見上面變數宣告部分:
function showcentroids(centroids) )
.attr("fill", function(d, i) )
.attr("x", function(d) )
.attr("y", function(d) )
.attr("width", function(d) )
.attr("height", function(d) );
}本例子中最關鍵的一步,也就是在發生變化的時候如何動態的展示,先看**:
function changecluster(nodeid, clusterid)
function changecentroid(centroids, i) )
.attr("y", function(d) );
delay += 1000;
}其中最關鍵的乙個變數是delay,我們為每個動畫設定duration,然後為之後的動畫增加delay。
當乙個資料點所屬類別發生變化時,我們只需要改變這個點的顏色即可,工作量很小,所以我們為它設定duration為0.1s,相應的delay也要增加0.1s,這樣便使得之後的動畫開始的時間是在該動畫結束的那一刻。當乙個類的質心發生變化時,為了有更好的顯示效果,我們可以讓這個質心點在1s的時間間隔裡從原來的位置平移到新位置,同理,設定duration為1000,同時delay增加1000.
注:由於資料點順序隨機,這樣按照資料點的順序變換節點的顏色的時候會隨機變化,我們可以事先對資料點按照其距離原點的遠近進行排序,這樣再按照順序變化節點的顏色時就會出現從左下角逐漸到右上角的趨勢,利於觀察。
data.sort(sortbynorm);
function sortbynorm(vector1, vector2)
大功告成!我們終於可以看一下效果啦:
kmeans(data, 4);下面是視覺化效果,
初始時:
聚類完成後:
K Means聚類演算法
k means聚類演算法 intergret kmeans演算法的基本思想是初始隨機給定k個簇中心,按照最鄰近原則把待分類樣本點分到各個簇。然後按平均法重新計算各個簇的質心,從而確定新的簇心。一直迭代,直到簇心的移動距離小於某個給定的值。k means聚類演算法主要分為三個步驟 1 第一步是為待聚類...
聚類演算法 K means
演算法接受引數 k 然後將事先輸入的n個資料物件劃分為 k個聚類以便使得所獲得的聚類滿足 同一聚類中的物件相似度較高 而不同聚類中的物件相似度較小。聚類相似度是利用各聚類中物件的均值所獲得乙個 中心物件 引力中心 來進行計算的。k means演算法是最為經典的基於劃分的聚類方法,是十大經典資料探勘演...
模糊kmeans聚類
首先介紹乙個,fuzzykmeans演算法的主要思想 模糊均值聚類 fcm 即眾所周知的模糊isodata,是用隸屬度確定每個資料點屬於某個聚類的程度的一種聚類演算法。1973年,bezdek提出了該演算法,作為早期硬均值聚類 hcm 方法的一種改進。fcm把 n 個向量 xi i 1,2,n 分為...