始まりました、「~したい」シリーズ。
今回はHKM≪階層的K平均法≫です。
話は変わるんですけど、ワタクシ大変なことに気づいてしまいました・・・。
「~したい」シリーズのタイトルの後ろについてる「@その~」っていうバックナンバー、
そのいち、そのにぐらいまでしか続かない(^p^)
バックナンバーつける意味あるのかってね。
BBCPマコトの紹介記事とかだと結構続いたんですけどね。
飽きっぽい性格の私は一つの物事に集中して取り組めないのですよ(^p^)
いっそバックナンバー制廃止にしようかな。
まあ、それはさておいて、今回のタイトル「HKM」についてです。
HKMとは本来ならクラスタ分析をする為に使われる手法です。
今回はこれを最近傍探索に使います。
最近傍探索にはいろいろな手法があるのですが、これはKDtreeに似ているかもしれません。
KDtreeという手法は簡単に言えば
- 空間を分割する。
- クエリが分割された空間のどこに属するか調べる。
- クエリが属する空間の中に最近傍がある。
という手法です。
それに対してHKMは
- データを2つのクラスタ(データの集合)に分ける。
- 分けられたクラスタの中のデータをさらに2つのクラスタに分ける。
- クラスタの中のデータが1つになるまで1.2.を繰り返す。
- クエリがどのクラスタに属するか調べる。
- クエリが属するクラスタがの中に最近傍がある。
詳しいことは 知 り ま せ ん 。
詳しい人は私にご教授ください。
とりあえず今回HKMを使うために使ったアルゴリズムとその紹介。
KNN(k近傍法)
クエリの近傍をk個求める手法。アルゴリズム
- クエリとデータベース全点の距離を計算。
- 距離を昇順にソート。
- 上からk個をとる。
Kmeans(k平均法)
k個のクラスタを作る手法。
アルゴリズム
- 空のクラスタをk個作る。
- それぞれのクラスタにランダムにデータを割り当てる。
- クラスタに含まれるデータの座標の平均をクラスタの中心とする。
- 各データと各クラスタ中心の距離を計算し、各データを最近傍となる各クラスタに割り当てなおす。
- 割り当ての変化量が一定の閾値以内になるまで3.4.を繰り返す。
HKM(階層的k平均法)
階層的にk個のクラスタを作る手法。アルゴリズム
- k個のクラスタを作る。
- クラスタの中でさらにk個のクラスタを作る。
- クラスタの中のデータが1個になるまで2.を繰り返す。
こんな感じです。
最初にKNNを作って、次にKNNを使ってKmeansを作って、次にKmeansを使ってHKMを作ります。
こういうの楽しいよね。
弱い武器を強化していって最強の武器を作るみたいな感じ。
テンションが上がりますね(^p^)
それでは以下ソース
量がやばい。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include"KMeansClass.h" | |
class HKMClass :public KMeansClass | |
{ | |
public: | |
static struct vertex | |
{ | |
vertex* left; | |
vertex* right; | |
vector<CLASTER> claster; | |
}; | |
static enum CONST | |
{ | |
left, | |
right, | |
hk = 2, | |
}; | |
vertex *node; | |
HKMClass() :KMeansClass() | |
{} | |
HKMClass(const int &t) :KMeansClass(hk,t) | |
{ | |
} | |
HKMClass(const vector<DATA> &data, const int &t) :KMeansClass(hk,t) | |
{ | |
this->node = MakeHKM(data); | |
} | |
~HKMClass(){} | |
vertex* R_MakeHKM(const vector<DATA> &data) | |
{ | |
vertex *node = new vertex(); | |
node->claster = MakeClaster(data, hk); | |
if (node->claster[left].data.size() < hk) | |
{ | |
node->left = NULL; | |
} | |
else | |
{ | |
node->left = R_MakeHKM(node->claster[left].data); | |
} | |
if (node->claster[right].data.size() < hk) | |
{ | |
node->right = NULL; | |
} | |
else | |
{ | |
node->right = R_MakeHKM(node->claster[right].data); | |
} | |
return node; | |
} | |
vertex* MakeHKM(const vector<DATA> &data) | |
{ | |
this->node = R_MakeHKM(data); | |
return this->node; | |
} | |
vector<DATA> R_Search(const DATA &query,const vertex* node) | |
{ | |
size_t claster_size = node->claster.size(); | |
if (claster_size < hk) | |
{ | |
return node->claster[0].data; | |
} | |
vector<DATA> center(claster_size); | |
for (int i = 0; i < claster_size;++i) | |
{ | |
center[i] = node->claster[i].center; | |
} | |
KNNClass knn_obj(center, 1); | |
int neighbor=knn_obj.KNN(query)[0].second; | |
if ((node->left!=NULL)&&(neighbor == left || node->right==NULL)) | |
{ | |
return R_Search(query, node->left); | |
} | |
else if ((node->right != NULL) && (neighbor == right || node->left == NULL)) | |
{ | |
return R_Search(query, node->right); | |
} | |
else if (node->left == NULL&&node->right == NULL) | |
{ | |
return node->claster[0].data; | |
} | |
else | |
{ | |
} | |
return vector<DATA>(); | |
} | |
DATA Search(const DATA &query,const vertex* node) | |
{ | |
return R_Search(query, node)[0]; | |
} | |
DATA Search(const DATA &query) | |
{ | |
return Search(query, this->node); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include<vector> | |
#include"KNNClass.h" | |
#include"../../../Useful/useful.h" | |
using namespace std; | |
class KMeansClass | |
{ | |
public: | |
typedef vector<double> DATA; | |
static struct CLASTER | |
{ | |
vector<DATA> data; | |
DATA center; | |
}; | |
protected: | |
int k; | |
vector<CLASTER> claster; | |
int threshold; | |
void CalcCenter(vector<CLASTER> *claster) | |
{ | |
size_t feature_size = (*claster)[0].data[0].size(); | |
for (auto &i : *claster) | |
{ | |
i.center = VectorAverage(i.data); | |
} | |
} | |
int ReassignData(vector<CLASTER> *claster, const int &k) | |
{ | |
vector<DATA> center(k); | |
for (int i = 0; i < k; ++i) | |
{ | |
center[i] = (*claster)[i].center; | |
} | |
KNNClass knn_obj(center, 1); | |
int difference = 0; | |
for (int i = 0; i < k; i++) | |
{ | |
size_t data_size = (*claster)[i].data.size(); | |
for (int j=0;j<data_size;++j) | |
{ | |
DATA* temp = &(*claster)[i].data[j]; | |
int neighbor = knn_obj.KNN(*temp)[0].second; | |
if (i != neighbor) | |
{ | |
++difference; | |
(*claster)[neighbor].data.push_back(*temp); | |
(*claster)[i].data.erase | |
( | |
remove | |
( | |
(*claster)[i].data.begin(), (*claster)[i].data.end(), *temp | |
) | |
); | |
data_size = (*claster)[i].data.size(); | |
} | |
} | |
} | |
return difference; | |
} | |
void InitClasterData(vector<CLASTER> *claster) | |
{ | |
for (auto &i : *claster) | |
{ | |
i.data.clear(); | |
i.center.clear(); | |
} | |
} | |
void AllocateForNormal(vector<CLASTER> *claster, const vector<DATA> &data, const int &k) | |
{ | |
int i = 0; | |
for (auto &j : data) | |
{ | |
(*claster)[++i%k].data.push_back(j); | |
} | |
} | |
public: | |
KMeansClass() | |
{ | |
SetK(1); | |
SetThreshold(100); | |
} | |
KMeansClass(const int &k) | |
{ | |
SetK(k); | |
SetThreshold(100); | |
} | |
KMeansClass(const int &k, const int &t) | |
{ | |
SetK(k); | |
SetThreshold(t); | |
} | |
KMeansClass(const vector<DATA> &data,const int &k, const int &t) | |
{ | |
SetK(k); | |
SetThreshold(t); | |
MakeClaster(data); | |
} | |
~KMeansClass(){} | |
vector<CLASTER> MakeClaster(const vector<DATA> &data, const int &k) | |
{ | |
auto claster_size = k; | |
vector<CLASTER> claster(k); | |
AllocateForNormal(&claster, data, k); | |
CalcCenter(&claster); | |
while (threshold<ReassignData(&claster, k)); | |
this->claster = claster; | |
return claster; | |
} | |
vector<DATA> Search(const DATA &query) | |
{ | |
int claster_size = this->claster.size(); | |
vector<DATA> center(claster_size); | |
for (int i = 0; i < claster_size; ++i) | |
{ | |
center[i] = this->claster[i].center; | |
} | |
KNNClass knn_obj(center, 1); | |
int neighbor = knn_obj.KNN(query)[0].second; | |
return this->claster[neighbor].data; | |
} | |
vector<CLASTER> MakeClaster(const vector<DATA> &data) | |
{ | |
return MakeClaster(data, this->k); | |
} | |
void InitClasterData() | |
{ | |
InitClasterData(&this->claster); | |
} | |
void SetK(const int &k) | |
{ | |
this->k = k; | |
} | |
void SetThreshold(const int &t) | |
{ | |
this->threshold = t; | |
} | |
vector<CLASTER> GetClaster() | |
{ | |
return this->claster; | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include<vector> | |
#include<utility> | |
#include<algorithm> | |
using namespace std; | |
typedef vector<double> DATA; | |
typedef vector<pair<double, int>> SOLVE; | |
class KNNClass | |
{ | |
private: | |
vector<DATA> data_base; | |
int k; | |
double Sqare(const double &x) | |
{ | |
return x*x; | |
} | |
double Distance(const DATA &a, const DATA &b) | |
{ | |
auto size = a.size(); | |
double sum = 0.; | |
for (auto i = 0; i < size; ++i) | |
{ | |
sum += Sqare(a[i] - b[i]); | |
} | |
return sum; | |
} | |
public: | |
KNNClass() | |
{ | |
SetK(1); | |
InitDataBase(); | |
} | |
KNNClass(const int &k) | |
{ | |
SetK(k); | |
InitDataBase(); | |
} | |
KNNClass(const vector<DATA> &data_base) | |
{ | |
SetK(1); | |
SetDataBase(data_base); | |
} | |
KNNClass(const vector<DATA> &data_base, const int &k) | |
{ | |
SetK(k); | |
SetDataBase(data_base); | |
} | |
~KNNClass(){} | |
vector<pair<double, int>> KNN(const DATA &query, const vector<DATA> &data_base, const int &k) | |
{ | |
auto size = data_base.size(); | |
vector<pair<double, int>> distance(size); | |
for (auto i = 0; i < size; ++i) | |
{ | |
distance[i] = make_pair(Distance(query, data_base[i]), i); | |
} | |
sort(distance.begin(), distance.end()); | |
return vector<pair<double, int>>(distance.begin(), distance.begin() + k); | |
} | |
vector<pair<double, int>> KNN(const DATA &query, const vector<DATA> &data_base) | |
{ | |
return KNN(query, data_base, this->k); | |
} | |
vector<pair<double, int>> KNN(const DATA &query, const int &k) | |
{ | |
return KNN(query, this->data_base, k); | |
} | |
vector<pair<double, int>> KNN(const DATA &query) | |
{ | |
return KNN(query, this->data_base, this->k); | |
} | |
void SetK(const int &k) | |
{ | |
this->k = k; | |
} | |
void SetDataBase(const vector<DATA> &data_base) | |
{ | |
this->data_base = data_base; | |
} | |
void InitDataBase() | |
{ | |
this->data_base.clear(); | |
} | |
}; |
アルゴリズムの紹介してるしね?
未来の俺はこう言う。
「コメント文ぐらい書けよ(#^p^)」
ってか、あれ、これ・・・
そのに、そのさんに分ければよかったんじゃね??