這幾天導師安排寫一個微博簽到數據依據地理座標的分類,所以有一段時間沒有更新博客了,不過正好也在這次代碼的寫作中學習了一些新的姿勢,寫這篇博客跟大家分享下心得。
優先隊列
定義
所謂的優先隊列,就是在普通隊列的基礎上,對於每個點維護一個“優先”值,這個優先值決定了優先隊列的出隊順序。優先隊列內的元素不能夠是空的
JAVA中的優先隊列
在JAVA的優先隊列(java.util.priorityQueue)內部維護了一個優先堆(priority heap),他的優先值比較是通過實現一個Comparator< T>接口進行實現的,這個接口決定了你打算怎麼衡量優先度對出隊的順序,是打算先出大的,還是先出小的,或者先出優先值接近某個值的…Comparator< T>接口的實現代碼如下:
static Comparator<priorityQueue> OrderIsdn = new Comparator<priorityQueue>(){
public int compare(priorityQueue o1, priorityQueue o2) {
// TODO Auto-generated method stub
double numbera = o1.getPopulation();
double numberb = o2.getPopulation();
if(numberb > numbera)
{
return 1; //優於比較值
}
else if(numberb<numbera)
{
return -1; //劣於比較值
}
else
{
return 0; //相等
}
}
};
關於這個comparator我們可以玩出花來,這裏就先不探討了。
優先隊列的構造方法如下所示
PriorityQueue() //Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.
PriorityQueue(Collection<? extends E> c) //Creates a PriorityQueue containing the elements in the specified collection.
PriorityQueue(Comparator<? super E> comparator) //Creates a PriorityQueue with the default initial capacity and whose elements are ordered according to the specified comparator.
PriorityQueue(int initialCapacity) //Creates a PriorityQueue with the specified initial capacity that orders its elements according to their natural ordering.
PriorityQueue(int initialCapacity, Comparator<? super E> comparator) //Creates a PriorityQueue with the specified initial capacity that orders its elements according to the specified comparator.
PriorityQueue(PriorityQueue<? extends E> c) //Creates a PriorityQueue containing the elements in the specified priority queue.
PriorityQueue(SortedSet<? extends E> c) //Creates a PriorityQueue containing the elements in the specified sorted set.
優先隊列通過兩個方法進行取值,第一個是
peek();
也就是偷偷看一眼要出來的值,這個行爲不會導致這個值從優先隊列中刪除
第二個是
poll();
這個行爲會導致優先隊列中的值被彈出
好了優先隊列是一個很簡單的概念,我們來看看應用:
poi點簽到與優先隊列
問題的提出
做微博簽到點分類的重要思想就是根據該座標點的位置訊息以及周圍poi點的訊息進行一個微博簽到數據的“打標籤”。想到這裏很容易就能夠引入一個東西–也就是KNN算法。
KNN算法能夠在給定的一個訓練集中,每次輸入一個測試集,返回在邏輯上距離這個輸入的測試集最近的k個推薦點,如下所示:
//以k=4爲例
(x1 ,x2) a
(x3 ,x4) b
(x5 ,x6) c
(x7 ,x8) a
然後,再依據這k個推薦點內的標籤數量進行排序。過程是真的有點mapreduce的味道~
//以k=4爲例
a 2
b 1
c 1
可是這樣分類會有一個什麼問題呢?
那就是分類的依據可以說單單依據座標,如果想加入個“時間”維、“性別”維、“這個poi點簽到數量”維的話會很困難,因爲你很難去將座標反應的距離與其他的維度進行一個歸一化。
同時如果在KNN中引入這些維度的話,可能某地會有一個極端火熱的poi點,這個點對男女、不同時間上都有大量的簽到數量(比如說“武漢市”這個大的poi點),那麼在KNN內進行這種權值計算的話,就會導致不論輸入的點的請況,都會被分到這個極端點。
那應該怎麼解決呢
解決思路
我們先依據距離先對預選點進行限制(這也是poi對微博進行分類的前提思想)
首先還是之前的流程,我們通過向KNN算法內輸入一個座標,先得到距離這個座標最近的k個點
然後我們對這k個點入優先隊列,並在隊列內依據距離、簽到數量、性別、時間等維度計算每個點的權值,以這個權值爲優先隊列的優先值。
這樣的思路就能夠解決“點王”出現的可能。
總結
之前使用純距離分類,準確率能達到89%左右,但是這個結論在理論上是很難站住腳的,因爲參數太少了,而且我們一開始只設置了11個標籤進行poi點分類,這11個標籤裏面有個“51”類佔全部標籤的70%,幾乎所有的分類結果都成了“51”標籤,所以才能達到這個準確度。
之後我們在實驗中引入了更多的變量,並對這些更多的變量進行KNN,發現實驗效果更差了,退到了20%~30%,甚至後面坐的哥們搞出了個9%。
所以這次我改進了下代碼,使用更多的標籤(幾十到幾百個小標籤),並且使用了前面說到的思想對KNN算法進行改進。
結果嘛~嘿嘿…91.45%(對簽到數據取一萬次隨機樣本,取四次實驗平均值,k值取10)