当前位置: 首页 > 编程日记 > 正文

K-均值聚类(K-Means) C++代码实现

K-均值聚类(K-Means)简介可以参考: http://blog.csdn.net/fengbingchun/article/details/79276668

以下是K-Means的C++实现,code参考OpenCV 3.3中的cv::kmeans函数,均值点初始化的方法仅支持KMEANS_RANDOM_CENTERS。

以下是从数据集MNIST中提取的40幅图像,0,1,2,3四类各20张,如下图:


关于MNIST的介绍可以参考: http://blog.csdn.net/fengbingchun/article/details/49611549

实现及测试代码如下:

kmeans.hpp:

#ifndef FBC_SRC_NN_KMEANS_HPP_
#define FBC_SRC_NN_KMEANS_HPP_#include <vector>namespace ANN {typedef enum KmeansFlags {// Select random initial centers in each attemptKMEANS_RANDOM_CENTERS = 0,// Use kmeans++ center initialization by Arthur and Vassilvitskii [Arthur2007]//KMEANS_PP_CENTERS = 2,// During the first (and possibly the only) attempt, use the//user-supplied labels instead of computing them from the initial centers. For the second and//further attempts, use the random or semi-random centers. Use one of KMEANS_\*_CENTERS flag//to specify the exact method.//KMEANS_USE_INITIAL_LABELS = 1
} KmeansFlags;template<typename T>
int kmeans(const std::vector<std::vector<T>>& data, int K, std::vector<int>& best_labels, std::vector<std::vector<T>>& centers, double& compactness_measure,int max_iter_count = 100, double epsilon = 0.001, int attempts = 3, int flags = KMEANS_RANDOM_CENTERS);} // namespace ANN#endif // FBC_SRC_NN_KMEANS_HPP_

kmeans.cpp:

#include "kmeans.hpp"
#include <algorithm>
#include <limits>
#include<random>
#include "common.hpp"namespace ANN {namespace {template<typename T>
void generate_random_center(const std::vector<std::vector<T>>& box, std::vector<T>& center)
{std::random_device rd;std::mt19937 generator(rd());std::uniform_real_distribution<T> distribution((T)0, (T)0.0001);int dims = box.size();T margin = 1.f / dims;for (int j = 0; j < dims; j++) {center[j] = (distribution(generator) * (1. + margin * 2.) - margin) * (box[j][1] - box[j][0]) + box[j][0];}
}template<typename T>
inline T norm_L2_Sqr(const T* a, const T* b, int n)
{double s = 0.f;for (int i = 0; i < n; i++) {double v = double(a[i] - b[i]);s += v*v;}return s;
}template<typename T>
void distance_computer(std::vector<double>& distances, std::vector<int>& labels, const std::vector<std::vector<T>>& data,const std::vector<std::vector<T>>& centers, bool only_distance = false)
{const int K = centers.size();const int dims = centers[0].size();for (int i = 0; i < distances.size(); ++i) {const std::vector<T> sample = data[i];if (only_distance) {const std::vector<T> center = centers[labels[i]];distances[i] = norm_L2_Sqr(sample.data(), center.data(), dims);continue;}int k_best = 0;double min_dist = std::numeric_limits<double>::max(); // DBL_MAXfor (int k = 0; k < K; ++k) {const std::vector<T> center = centers[k];const double dist = norm_L2_Sqr(sample.data(), center.data(), dims);if (min_dist > dist) {min_dist = dist;k_best = k;}}distances[i] = min_dist;labels[i] = k_best;}
}} // namespacetemplate<typename T>
int kmeans(const std::vector<std::vector<T>>& data, int K, std::vector<int>& best_labels, std::vector<std::vector<T>>& centers, double& compactness_measure,int max_iter_count, double epsilon, int attempts, int flags)
{CHECK(flags == KMEANS_RANDOM_CENTERS);int N = data.size();CHECK(K > 0 && N >= K);int dims = data[0].size();attempts = std::max(attempts, 1);best_labels.resize(N);std::vector<int> labels(N);centers.resize(K);std::vector<std::vector<T>> centers_(K), old_centers(K);std::vector<T> temp(dims, (T)0.);for (int i = 0; i < K; ++i) {centers[i].resize(dims);centers_[i].resize(dims);old_centers[i].resize(dims);}compactness_measure = std::numeric_limits<double>::max(); // DBL_MAXdouble compactness = 0.;epsilon = std::max(epsilon, (double)0.);epsilon *= epsilon;max_iter_count = std::min(std::max(max_iter_count, 2), 100);if (K == 1) {attempts = 1;max_iter_count = 2;}std::vector<std::vector<T>> box(dims);for (int i = 0; i < dims; ++i) {box[i].resize(2);}std::vector<double> dists(N, 0.);std::vector<int> counters(K);const T* sample = data[0].data();for (int i = 0; i < dims; ++i) {box[i][0] = sample[i];box[i][1] = sample[i];}for (int i = 1; i < N; ++i) {sample = data[i].data();for (int j = 0; j < dims; ++j) {T v = sample[j];box[j][0] = std::min(box[j][0], v);box[j][1] = std::max(box[j][1], v);}}for (int a = 0; a < attempts; ++a) {double max_center_shift = std::numeric_limits<double>::max(); // DBL_MAXfor (int iter = 0;;) {centers_.swap(old_centers);if (iter == 0 && (a > 0 || true)) {for (int k = 0; k < K; ++k) {generate_random_center(box, centers_[k]);}} else {// compute centersfor (auto& center : centers_) {std::for_each(center.begin(), center.end(), [](T& v){v = (T)0; });}std::for_each(counters.begin(), counters.end(), [](int& v) {v = 0; });for (int i = 0; i < N; ++i) {sample = data[i].data();int k = labels[i];auto& center = centers_[k];for (int j = 0; j < dims; ++j) {center[j] += sample[j];}counters[k]++;}if (iter > 0) max_center_shift = 0;for (int k = 0; k < K; ++k) {if (counters[k] != 0) continue;// if some cluster appeared to be empty then://   1. find the biggest cluster//   2. find the farthest from the center point in the biggest cluster//   3. exclude the farthest point from the biggest cluster and form a new 1-point cluster.int max_k = 0;for (int k1 = 1; k1 < K; ++k1) {if (counters[max_k] < counters[k1])max_k = k1;}double max_dist = 0;int farthest_i = -1;auto& new_center = centers_[k];auto& old_center = centers_[max_k];auto& _old_center = temp; // normalizedT scale = (T)1.f / counters[max_k];for (int j = 0; j < dims; j++) {_old_center[j] = old_center[j] * scale;}for (int i = 0; i < N; ++i) {if (labels[i] != max_k)continue;sample = data[i].data();double dist = norm_L2_Sqr(sample, _old_center.data(), dims);if (max_dist <= dist) {max_dist = dist;farthest_i = i;}}counters[max_k]--;counters[k]++;labels[farthest_i] = k;sample = data[farthest_i].data();for (int j = 0; j < dims; ++j) {old_center[j] -= sample[j];new_center[j] += sample[j];}}for (int k = 0; k < K; ++k) {auto& center = centers_[k];CHECK(counters[k] != 0);T scale = (T)1.f / counters[k];for (int j = 0; j < dims; ++j) {center[j] *= scale;}if (iter > 0) {double dist = 0;const auto old_center = old_centers[k];for (int j = 0; j < dims; j++) {T t = center[j] - old_center[j];dist += t*t;}max_center_shift = std::max(max_center_shift, dist);}}}bool isLastIter = (++iter == std::max(max_iter_count, 2) || max_center_shift <= epsilon);// assign labelsstd::for_each(dists.begin(), dists.end(), [](double& v){v = 0; });distance_computer(dists, labels, data, centers_, isLastIter);std::for_each(dists.cbegin(), dists.cend(), [&compactness](double v) { compactness += v; });if (isLastIter) break;}if (compactness < compactness_measure) {compactness_measure = compactness;for (int i = 0; i < K; ++i) {memcpy(centers[i].data(), centers_[i].data(), sizeof(T)* dims);}memcpy(best_labels.data(), labels.data(), sizeof(int)* N);}}return 0;
}template int kmeans<float>(const std::vector<std::vector<float>>&, int K, std::vector<int>&, std::vector<std::vector<float>>&, double&,int max_iter_count, double epsilon, int attempts, int flags);
template int kmeans<double>(const std::vector<std::vector<double>>&, int K, std::vector<int>&, std::vector<std::vector<double>>&, double&,int max_iter_count, double epsilon, int attempts, int flags);} // namespace ANN

main.cpp:

#include "funset.hpp"
#include <iostream>
#include <opencv2/opencv.hpp>#include "perceptron.hpp"
#include "BP.hpp""
#include "CNN.hpp"
#include "linear_regression.hpp"
#include "naive_bayes_classifier.hpp"
#include "logistic_regression.hpp"
#include "common.hpp"
#include "knn.hpp"
#include "decision_tree.hpp"
#include "pca.hpp"
#include "logistic_regression2.hpp"
#include "single_hidden_layer.hpp"
#include "kmeans.hpp"// ================================= K-Means ===============================
int test_kmeans()
{const std::string image_path{ "E:/GitCode/NN_Test/data/images/digit/handwriting_0_and_1/" };cv::Mat tmp = cv::imread(image_path + "0_1.jpg", 0);CHECK(tmp.data != nullptr && tmp.channels() == 1);const int samples_number{ 80 }, every_class_number{ 20 }, categories_number{ samples_number / every_class_number };cv::Mat samples_data(samples_number, tmp.rows * tmp.cols, CV_32FC1);cv::Mat labels(samples_number, 1, CV_32FC1);float* p1 = reinterpret_cast<float*>(labels.data);for (int i = 1; i <= every_class_number; ++i) {static const std::vector<std::string> digit{ "0_", "1_", "2_", "3_" };CHECK(digit.size() == categories_number);static const std::string suffix{ ".jpg" };for (int j = 0; j < categories_number; ++j) {std::string image_name = image_path + digit[j] + std::to_string(i) + suffix;cv::Mat image = cv::imread(image_name, 0);CHECK(!image.empty() && image.channels() == 1);image.convertTo(image, CV_32FC1);image = image.reshape(0, 1);tmp = samples_data.rowRange((i - 1) * categories_number + j, (i - 1) * categories_number + j + 1);image.copyTo(tmp);p1[(i - 1) * categories_number + j] = j;}}std::vector<std::vector<float>> data(samples_data.rows);for (int i = 0; i < samples_data.rows; ++i) {data[i].resize(samples_data.cols);const float* p = (const float*)samples_data.ptr(i);memcpy(data[i].data(), p, sizeof(float)* samples_data.cols);}const int K{ 4 }, attemps{ 100 }, max_iter_count{ 100 };const double epsilon{ 0.001 };const int flags = ANN::KMEANS_RANDOM_CENTERS;std::vector<int> best_labels;double compactness_measure{ 0. };std::vector<std::vector<float>> centers;ANN::kmeans<float>(data, K, best_labels, centers, compactness_measure, max_iter_count, epsilon, attemps, flags);fprintf(stdout, "K = %d, attemps = %d, iter count = %d, compactness measure =  %f\n",K, attemps, max_iter_count, compactness_measure);CHECK(best_labels.size() == samples_number);const auto* p2 = best_labels.data();for (int i = 1; i <= every_class_number; ++i) {for (int j = 0; j < categories_number; ++j) {fprintf(stdout, "  %d  ", *p2++);}fprintf(stdout, "\n");}return 0;
}

执行结果如下,下图中每一列表示期望是同一个label。


GitHub: https://github.com/fengbingchun/NN_Test 

相关文章:

让学生网络相互学习,为什么深度相互学习优于传统蒸馏模型?| 论文精读

作者 | Ying Zhang&#xff0c;Tao Xiang等译者 | 李杰出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;蒸馏模型是一种将知识从教师网络&#xff08;teacher&#xff09;传递到学生网络&#xff08;student&#xff09;的有效且广泛使用的技术。通常来说&#xff0c;…

mac apache 配置

mac系统自带apache这无疑给广大的开发朋友提供了便利&#xff0c;接下来是针对其中的一些说明 一、自带apache相关命令 1. sudo apachectl start 启动服务&#xff0c;需要权限&#xff0c;就是你计算机的password 2. sudo apachectl stop 终止服务 ####3. sudo apachectl rest…

jQuery学习---------认识事件处理

3种事件模型&#xff1a;原始事件模型DOM事件模型IE事件模型原始事件模型&#xff08;0级事件模型&#xff09;1、事件处理程序被定义为函数实例&#xff0c;然后绑定到DOM元素事件对象上&#xff0c;实现事件的注册。例子&#xff1a;var btn document.getElementsByTagName(…

C++中的虚函数表介绍

在C语言中,当我们使用基类的引用或指针调用一个虚成员函数时会执行动态绑定。因为我们直到运行时才能知道到底调用了哪个版本的虚函数&#xff0c;所以所有虚函数都必须有定义。通常情况下&#xff0c;如果我们不使用某个函数&#xff0c;则无须为该函数提供定义。但是我们必须…

AI如何赋能金融行业?百度、图灵深视等同台分享技术实践

近日&#xff0c;由BTCMEX举办的金融技术创新研讨会在北京举办。BTCMEX投资人李笑来&#xff0c;AI技术公司TuringPass、百度、美国Apache基金会项目Pulsar、区块链安全公司SlowMist等相关专家参加了此次会议&#xff0c;共同探讨了金融技术在创新方面的现状。 图灵深视副总裁许…

【Win32 API学习]打开可执行文件

在MFC中打开其他可执行文件常用到的方法有&#xff1a;WinExec、ShellExecute、CreatProcess。 1.WinExec WinExec 主要运行EXE文件&#xff0c;用法简单&#xff0c;只有两个参数&#xff0c;前一个指定命令路径&#xff0c;后一个指定窗口显示方式&#xff1a; UINT WinExec(…

支付宝接口使用文档说明 支付宝异步通知

支付宝接口使用文档说明 支付宝异步通知(notify_url)与return_url. 现支付宝的通知有两类。 A服务器通知&#xff0c;对应的参数为notify_url&#xff0c;支付宝通知使用POST方式 B页面跳转通知&#xff0c;对应的参数为return_url&#xff0c;支付宝通知使用GET方式 &#xff…

完全隐藏Master Page Site Actions菜单只有管理员才可以看见

1. 在Master Page Head 增加下面的Style <style type"text/css"> .ms-cui-tt{visibility:hidden;} </style> 2. 增加SPSecurityTrimmedControl <SharePoint:SPRibbonPeripheralContent runat"server" Location"TabRowLeft&qu…

深度学习中的随机梯度下降(SGD)简介

随机梯度下降(Stochastic Gradient Descent, SGD)是梯度下降算法的一个扩展。机器学习中反复出现的一个问题是好的泛化需要大的训练集&#xff0c;但大的训练集的计算代价也更大。机器学习算法中的代价函数通常可以分解成每个样本的代价函数的总和。随着训练集规模增长为数十亿…

推荐系统中的前沿技术研究与落地:深度学习、AutoML与强化学习 | AI ProCon 2019...

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;个性化推荐算法滥觞于互联网的急速发展&#xff0c;随着国内外互联网公司&#xff0c;如 Netflix 在电影领域&#xff0c;亚马逊、淘宝、京东等在电商领域&#xff0c;今日头条在内容领域的采用和推动&…

运维日志管理系统

因公司数据安全和分析的需要&#xff0c;故调研了一下 GlusterFS lagstash elasticsearch kibana 3 redis 整合在一起的日志管理应用&#xff1a;安装&#xff0c;配置过程&#xff0c;使用情况等续一&#xff0c;glusterfs分布式文件系统部署&#xff1a;说明&#xf…

NLP学习思维导图,非常的全面和清晰

作者 | Tae Hwan Jung & Kyung Hee编译 | ronghuaiyang【导读】Github上有人整理了NLP的学习路线图&#xff08;思维导图&#xff09;&#xff0c;非常的全面和清晰&#xff0c;分享给大家。先奉上GitHub地址&#xff1a;https://github.com/graykode/nlp-roadmapnlp-roadm…

Go在windows10 64位上安装过程

1. 从 https://golang.org/dl/ 下载最新的发布版本go1.10即go1.10.windows-amd64.msi; 2. 双击go1.10.windows-amd64.msi ,使用默认选项&#xff0c;默认会安装到C:\Go目录下&#xff1b; 3. 将C:\Go\bin目录添加到系统环境变量中(默认已自动添加)&#xff0c;此目录下有go.exe…

Windows SharePoint Services 3.0 应用程序模板

微软发布的一些WSS模板&#xff0c;看了一下&#xff0c;跟以前看到的模板好像不同模板分两类&#xff0c;一类是站点管理模板&#xff0c;一类是服务器管理模板站点管理模板&#xff1a;董事会、业务绩效报告、政府机构案例管理、课堂管理、临床试验启动和管理、竞争性分析站点…

HAProxy+Keepalived高可用负载均衡配置

一、系统环境&#xff1a;系统版本&#xff1a;CentOS5.5 x86_64master_ip:172.20.27.40backup_ip:172.20.27.50 vip:172.20.27.200web_1: 172.20.27.90web_2:172.20.27.100二、haproxy安装&#xff1a;1.首先172.20.27.40安装上安装&#xff1a;1.1安装 tar zxvf haproxy-1.3.…

Go在Ubuntu 14.04 64位上的安装过程

1. 从 https://golang.org/dl/ 或 https://studygolang.com/dl 下载最新的发布版本go1.10即go1.10.linux-amd64.tar.gz&#xff1b; 2. 将下载的tar包解压缩到/usr/local目录下&#xff0c;执行以下命令&#xff0c;结果如下&#xff1a; $ sudo tar -C /usr/local -xzf go1.…

毕业就拿阿里offer,你和他比差在哪?

我在大学的时候&#xff0c;真的遇到一个神人&#xff0c;叫他小马吧。超前学习。1024&#xff0c;是程序员的节日&#xff0c;恰逢CSDN的20周年&#xff0c;我们准备为你做件大事&#xff01;我们与AI博士唐宇迪、畅销书作家、北大硕士阿甘等4位老师&#xff0c;共同为大家带来…

04号团队-团队任务5:项目总结会

1.团队信息 团队序号&#xff1a;04 开发项目&#xff1a;北软毕设管理系统 整理人&#xff1a;丛云聪 学号&#xff1a;2017035107185 在团队中的职务&#xff1a;项目经理兼产品经理 2.代码仓库地址 主仓库&#xff1a;https://gitee.com/The_Old_Cousin/StuInfoManage…

微软职位内部推荐-Sr SDE for Win Apps Ecosystem

微软近期Open的职位:Job posting title: Senior Software Design EngineerLocation: China, BeijingLevel: 63Division: Operations System Group EngineeringGroup OverviewOSG is delivering flagship products in Microsoft. China is a second largest economy in the worl…

C# Winform 启动和停止进程

启动和停止进程 一、启动进程 方法1&#xff1a; &#xff08;1&#xff09; 创建一个Process组件的实例&#xff0c;例如&#xff1a; Process myProcess new Process(); &#xff08;2&#xff09; 设置其对应的StartInfo属性&#xff0c;指定要运行的应用程序名…

在Windows/Ubuntu上使用Visual Studio Code作为Go语言编辑器操作步骤

下面以在Windows10上操作为例&#xff0c;在Ubuntu上操作步骤与windows一致&#xff1a; 1. 从 https://code.visualstudio.com/ 下载windows上的最新发布版本1.21.1&#xff0c;即VSCodeSetup-x64-1.21.1.exe&#xff1b; 2. 以管理员身份运行VSCodeSetup-x64-1.21.1.exe&…

实战:基于tensorflow 的中文语音识别模型 | CSDN博文精选

作者 | Pelhans来源 | CSDN博客目前网上关于tensorflow 的中文语音识别实现较少&#xff0c;而且结构功能较为简单。而百度在PaddlePaddle上的 Deepspeech2 实现功能却很强大&#xff0c;因此就做了一次大自然的搬运工把框架转为tensorflow….简介百度开源的基于PaddlePaddle的…

js获取Html元素的实际宽度高度

第一种情况就是宽高都写在样式表里&#xff0c;就比如#div1{width:120px;}。这中情况通过#div1.style.width拿不到宽度&#xff0c;而通过#div1.offsetWidth才可以获取到宽度。第二种情况就是宽和高是写在行内中&#xff0c;比如style"width:120px;"&#xff0c;这中…

新框架ES-MAML:基于进化策略、简易的元学习方法

作者 | Xingyou Song、Wenbo Gao、Yuxiang Yang、Krzysztof Choromanski、Aldo Pacchiano、Yunhao Tang译者 | TroyChang编辑 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】现有的MAML算法都是基于策略梯度的&#xff0c;在试图利用随机策…

Tesseract-OCR 3.04简单使用举例(读入图像输出识别结果)

下面code是对Tesseract-OCR 3.04版本进行简单使用的举例&#xff1a;包括两段&#xff0c;一个是读入带有中文字符的图像&#xff0c;一个是读入仅有英文字符的图像&#xff1a; #include "funset.hpp"#include <iostream> #include <string> #include &…

坑爹的微软官方文档:SQL无人值守安装

我在部署项目的时候&#xff0c;需要用批处理无人值守安装SQLserver,.Net等组件。 于是查了微软官方文档&#xff0c;其中一项内容如下&#xff1a; http://msdn.microsoft.com/zh-cn/library/ms144259.aspx SQL Server 安装程序控件 /IACCEPTSQLSERVERLICEN…

各种 django 静态文件的配置总结【待续】

2019独角兽企业重金招聘Python工程师标准>>> 最近在学习django框架的使用&#xff0c;想引用静态css文件&#xff0c;怎么都引用不到&#xff0c;从网搜了好多&#xff0c;大多因为版本问题&#xff0c;和我现在的使用的dango1.1配置不同&#xff0c;根据资料和公司…

实战:人脸识别的Arcface实现 | CSDN博文精选

来源 | CSDN博客本文将简单讲述arcface从训练到部署的整个过程&#xff0c;主要包括前期的数据筛选和准备&#xff0c;模型训练以及模型部署。此文参考的arcface的代码地址&#xff1a;https://github.com/ronghuaiyang/arcface-pytorch数据集准备1. 首先准备需要训练的人脸数据…

Windows7/10上快速搭建Tesseract-OCR开发环境操作步骤

之前在https://blog.csdn.net/fengbingchun/article/details/51628957 中描述过如何在Windows上搭建Tesseract-OCR开发环境&#xff0c;那时除了需要clone https://github.com/fengbingchun/OCR_Test 工程外&#xff0c;还需要依赖 https://github.com/fengbingchun/Liblept_T…

C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)

反射以及Attribute在ORM中的应用 一、 反射什么是反射&#xff1f;简单点吧&#xff0c;反射就是在运行时动态获取对象信息的方法&#xff0c;比如运行时知道对象有哪些属性&#xff0c;方法&#xff0c;委托等等等等。反射有什么用呢&#xff1f;反射不但让你在运行是获取对象…