Jump to content
Compvision.ru
Sign in to follow this  
mrgloom

kmeans и MNIST dataset

Recommended Posts

ну с учителем как то прозаично, тем более что внутри класса цифра сильно варьируется и можно некоторые примеры посчитать за неправильные.

именно хочется найти какие то взаимосвязи.

кстати если я вектор длины N проецирую на пространство размерности N, то я вроде как получаю результат отличный от того если бы я не проецировал, т.е. у PCA видимо есть какое то еще свойство кроме уменьшения размерности.

потом интересно посмотреть на центр кластеров, это как я понимаю усредненное изображение по кластеру, наверно можно ввести еще такое понятие как дисперсия внутри кластера, чтобы посмотреть сильно ли различаются сэмплы в кластере.

вот такой результат получился если взять максимальный размер пространсва 28*28.

это уже спроецированные векторы, слева видно что всё в таких шумных точках, это видимо важные данные, а если смещаться в право, то всё одно и то же, видимо не важные, единственное меня смущает что полоски получились какого то разного цвета, возможно это не верно, т.к. я нормировал для вывода каждую полоску, а может быть надо было нормировать всё изображение.

кстати можно найти базис размерности n, а проецировать на размерность k (k<n), т.е. на k первых максимальных?

image.png

Share this post


Link to post
Share on other sites

вообще если выбрать не целевое кол-во кластеров, а большее, то получается что то более осмысленное, как бы подкластеры.

на самом деле ведь по такому подходу можно наглядно оценивать хорошо ли выбран вектор признаков.

единственное пока не очень понятно как сливать подкластеры в целевые кластеры и как можно скажем так отфильтровать шумные сэмплы?

image.png

Share this post


Link to post
Share on other sites

Что касается проецирования, то Вы же всегда находите все собственные числа и векторы.

Смысл PCA именно в том и есть, чтобы делать репроекцию на векторы с максимальными собственными числами, это и есть уменьшение размерности.

По поводу k-means и тому подобного:

http://charlotte.ucsd.edu/users/elkan/cikm02.pdf

Share this post


Link to post
Share on other sites

попробовал на 10к данных knn, потом решил на тех же данных проверить как работает и мне выдало все лишь 963 правильных ответа из 10к.

это нормально?

void do_knn()

{

	//первая часть тренировка

	//читаем данные

    vector<Mat> db;

	read_data("C:/MNIST/10k_images.bin",db);

    int rows=db[0].rows;

    int cols=db[0].cols;


	//читаем лэйблы

	vector<unsigned char> vec_labels;

	read_labels("C:/MNIST/10k_labels.bin",vec_labels);


	//приводим исходные данные к нужным матрицам

	int total = rows*cols;

	Mat trainData(total, db.size(), CV_32FC1);

    for(int i = 0; i < db.size(); i++)

    {

        Mat X = trainData.col(i);

        db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);

    }

	Mat trainClasses(vec_labels);

	trainClasses.convertTo(trainClasses,CV_32FC1, 1/255.);


	int k=32;//можно варьировать

	Mat t= trainData.t();

	CvMat trainData_= t;

	CvMat trainClasses_= trainClasses;

	CvKNearest knn(&trainData_,&trainClasses_, 0, false, k);


	int c_tottal=0;

	int c_good=0;

	for(int i = 0; i < db.size(); ++i)

    {

		Mat t=db[i].reshape(1,1);

		t.convertTo(t,CV_32FC1, 1/255.);

		CvMat sample= t;


		CvMat* currentLabel= cvCreateMat( 1, 1, CV_32FC1);

		knn.find_nearest(&sample, k, currentLabel);


		if(255*currentLabel->data.fl[0]==vec_labels[i])

			c_good++;


		c_tottal++;

		int g=0;

	}


	cout<<c_good;

	cout<<c_tottal;

}

Share this post


Link to post
Share on other sites

Такой же результат был бы если просто вслепую выбирать ответ. (вероятность 1/10)

Share this post


Link to post
Share on other sites

дело было в параметре k в find_nearest, почему то если его поставить таким же как при обучении =32, то получается плохой результат, попробовал =5 и норм ~97%(правда на той же самой выборке).Если выбрать =1 то на той же самой выборке получим 100%.

какое k оптимально для обучения и поиска опять же непонятно.

Share this post


Link to post
Share on other sites

кстати я тут подумал вот о чём приминительно к нахождению лиц.

если мы находим лица с помощью хаара, то нам надо иметь примеры положительные и отрицательные.

что если мы имеем довольно много фотографий людей(один вариант простой как на паспорт или другой вариант сложнее в "натуральной среде обитания")

и пытаемся найти какую либо зависимость в данных, выделяя из изображения какие либо высоуровневые или низкоуровневые признаки.

т.е. алгоритм нам как бы сам найдет "примеры" через похожести в данных, а мы уже на последней стадии сможем проверить эти данные на адекватность.

имеет ли такой подход право на существование?

Share this post


Link to post
Share on other sites

Я думаю тут что то близкое к проблеме обнаружения аномалий. (Anomaly Detection).

Share this post


Link to post
Share on other sites

как можно отобразить это на 2д? (т.е. чтобы похожие элементы были близко, а не похожие не близко)

я попробовал спроецировать через PCA на 2 компоненты, но это получилось что то не то.

Share this post


Link to post
Share on other sites

Вообще PCA должен работать, и его для таких вещей часто применяют.

Share this post


Link to post
Share on other sites

Мне не дают посмотреть что у Вас получилось, у меня выдает по первым векторам такое:

Вот три вида (проекции на плоскости, задаваемые парами векторов (v0,v1),(v0,v2) и (v1,v2)):

post-1-0-21837500-1340400567_thumb.png post-1-0-90373700-1340400582_thumb.png post-1-0-69661200-1340400593_thumb.png

#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;

inline void endian_swap(unsigned short& x)
{
x = (x>>8) |
(x<<8);
}

inline void endian_swap(unsigned int& x)
{
x = (x>>24) |
((x<<8) & 0x00FF0000) |
((x>>8) & 0x0000FF00) |
(x<<24);
}

// __int64 for MSVC, "long long" for gcc
inline void endian_swap(unsigned __int64& x)
{
x = (x>>56) |
((x<<40) & 0x00FF000000000000) |
((x<<24) & 0x0000FF0000000000) |
((x<<8) & 0x000000FF00000000) |
((x>>8) & 0x00000000FF000000) |
((x>>24) & 0x0000000000FF0000) |
((x>>40) & 0x000000000000FF00) |
(x<<56);
}

void read_mnist_labels(vector<unsigned char>& vec_lbl)
{
//vector<Mat> vec_img;
ifstream file;

file.open("C:/MNIST/train-labels.idx1-ubyte",ifstream::in | ifstream::binary);
if (file.is_open())
{
unsigned int magic_number=0;
unsigned int number_of_labels=0;
file.read((char*)&magic_number,sizeof(magic_number)); //если перевернуть то будет 2051
endian_swap(magic_number);
file.read((char*)&number_of_labels,sizeof(number_of_labels));//если перевернуть будет 10к
endian_swap(number_of_labels);

cout << "magic_number=" << magic_number << endl;
cout << "number_of_labels=" << number_of_labels << endl;

for(int i=0;i<number_of_labels;++i)
{
unsigned char t_ch=0;
file.read((char*)&t_ch,sizeof(t_ch));
vec_lbl.push_back(t_ch);
}
}
}

void read_mnist(vector<Mat>& vec_img)
{
//vector<Mat> vec_img;
ifstream file;

file.open("C:/MNIST/train-images.idx3-ubyte",ifstream::in | ifstream::binary);
//file.open("C:/MNIST/t10k-images.idx3-ubyte",ifstream::in | ifstream::binary);
if (file.is_open())
{
unsigned int magic_number=0;
unsigned int number_of_images=0;
unsigned int n_rows=0;
unsigned int n_cols=0;
file.read((char*)&magic_number,sizeof(magic_number)); //если перевернуть то будет 2051
endian_swap(magic_number);
file.read((char*)&number_of_images,sizeof(number_of_images));//если перевернуть будет 10к
endian_swap(number_of_images);
file.read((char*)&n_rows,sizeof(n_rows));
endian_swap(n_rows);
file.read((char*)&n_cols,sizeof(n_cols));
endian_swap(n_cols);
cout << "magic_number=" << magic_number << endl;
cout << "n_rows=" << n_rows << endl;
cout << "n_cols=" << n_cols << endl;
cout << "number_of_images=" << number_of_images << endl;

for(int i=0;i<number_of_images;++i)
{
Mat temp(n_rows,n_cols,CV_8UC1);
for(int r=0;r<n_rows;++r)
{
for(int c=0;c<n_cols;++c)
{
unsigned char t_ch=0;
file.read((char*)&t_ch,sizeof(t_ch));
//тут идет запись матрицы 28х28 в вектор
temp.at<unsigned char>(r,c)= t_ch; //возможно можно как то быстрее копировать построчно?
}
}
vec_img.push_back(temp);
}
}

}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void calc_PCA(vector<Mat>& db,int numPrincipalComponents,Mat& EigVec,Mat& Mean)
{
int total = db[0].rows * db[0].cols;

int rows=db[0].rows;
int cols=db[0].cols;

Mat mat(total, db.size(), CV_32FC1);

for(int i = 0; i < db.size(); i++)
{
Mat X = mat.col(i);
db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);
}

EigVec=Mat(numPrincipalComponents,total,CV_32FC1);
// do pca
PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, numPrincipalComponents);

Mat imgDst;
imgDst.create( rows, cols*numPrincipalComponents, CV_32FC1 );
imgDst=0;

pca.mean.copyTo(Mean);

for(int i=0;i<numPrincipalComponents;++i)
{
Mat temp=(pca.eigenvectors.row(i));
normalize(temp,temp); // Это и есть собственный вектор
temp.copyTo(EigVec.row(i));
// ---------------------------------------------------
// Этот кусок нужен для того, чтобы были видны картинки
// ---------------------------------------------------
double m,M;
minMaxLoc(temp,&m,&M);
if((M-m)!=0)
{
temp-=m;
temp/=(M-m);
temp*=255.0;
}
temp=temp.reshape(1, rows);
temp.copyTo(imgDst(Rect(i*cols,0,cols,rows)));
// ---------------------------------------------------
}
imwrite("result.png", imgDst);
int f=0;
}


CvScalar random_color(CvRNG* rng)
{
int color = cvRandInt(rng);
return CV_RGB(color&255, (color>>8)&255, (color>>16)&255);
}


//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
const int NumVectors=3;
vector<Mat> MNIST;
read_mnist(MNIST);

vector<unsigned char> MNIST_L;
read_mnist_labels(MNIST_L);

int rows=MNIST[0].rows;
int cols=MNIST[0].cols;
// ---------------------------------------------------
// Составили картинку из 100 первых изображений
// ---------------------------------------------------
Mat imgDst;
imgDst.create( 10*rows, 10*cols, CV_8UC1 );
imgDst=0;
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
MNIST[i+j*10].copyTo(imgDst(Rect(i*cols,j*rows,cols,rows)));
}
}
imshow("result",imgDst);
// ---------------------------------------------------
// Посчитаем собственные векторы возьмем NumVectors с
// максимальными собственными числами
// ---------------------------------------------------
Mat EigVec,mean,result,repared;
calc_PCA(MNIST,NumVectors,EigVec,mean);
// ---------------------------------------------------
// Спроецируем 10 первых цифр на собственные векторы
// получаем координаты этих цифр в NumVectors мерном пространстве
// а затем восстановим их по этим проекциям.
// ---------------------------------------------------
for(int i=0; i<10; i++)
{
// Спроецируем на собственные векторы
cv::PCAProject(MNIST[i].reshape(1, rows*cols),mean,EigVec,result);
cout << result << endl;

// Восстановим по их NumVectors проекциям на собственные оси.
cv::PCABackProject(result,mean,EigVec,repared);

// переведем обратно в прямоугольный вид
// и выведем в файл
repared=repared.reshape(1, rows);
char str[100];
sprintf(str,"repared_%d.png",i);
imwrite(str,repared);

}
vector<float> x;
vector<float> y;
for(int i=0; i<60000; i++)
{
// Спроецируем на собственные векторы
cv::PCAProject(MNIST[i].reshape(1, rows*cols),mean,EigVec,result);
y.push_back(result.at<float>(1));
x.push_back(result.at<float>(2));
}
pair<vector<float>::iterator,vector<float>::iterator> ylimits = minmax_element(y.begin(),y.end());
pair<vector<float>::iterator,vector<float>::iterator> xlimits = minmax_element(x.begin(),x.end());

float w=*xlimits.second-*xlimits.first;
float h=*ylimits.second-*ylimits.first;
Mat imacl(Size(w/5,h/5),CV_8UC3);
cout<< "h=" << h << " w=" << w << endl;
imacl=0;

CvRNG rng(35435345);

CvScalar Colors[256];
for (int i=0;i<256;i++)
{
Colors[i]=random_color(&rng);
}

for(int i=0; i<10; i++)
{
rectangle(imacl,Point(i*10,0),Point(i*10+10,10),Colors[i],-1);
}

for(int i=0; i<x.size(); i++)
{
int X=(x[i]-*xlimits.first)/5;
int Y=(y[i]-*ylimits.first)/5;
imacl.at<Vec3b>(Y,X)[0]=Colors[MNIST_L[i]].val[0];
imacl.at<Vec3b>(Y,X)[1]=Colors[MNIST_L[i]].val[1];
imacl.at<Vec3b>(Y,X)[2]=Colors[MNIST_L[i]].val[2];
}
imshow("imacl",imacl);
x.clear();
y.clear();
waitKey(0);
return 0;
}[/code]

  • Like 1

Share this post


Link to post
Share on other sites

Кстати, вот здесь точно то, что использовано для получения той картинки :

http://homepage.tudelft.nl/19j49/t-SNE.html

Плюс к тому Isomap:

http://isomap.stanford.edu/

И хорошая статейка:

http://axon.cs.byu.edu/Dan/678/miscellaneous/Manifold.example.pdf

Share this post


Link to post
Share on other sites
Вот три вида (проекции на плоскости, задаваемые парами векторов (v0,v1),(v0,v2) и (v1,v2)):

не понял это вы 3Д проецировали на 3 разных плоскости в 2Д?

Кстати, вот здесь точно то, что использовано для получения той картинки :

http://homepage.tude...9j49/t-SNE.html

Плюс к тому Isomap:

http://isomap.stanford.edu/

это то все понятно, но боюсь если не знать как этим пользоваться ничего не выйдет.

пока наверно можно хотя бы в матлабе поиграться с PCA и проецировать в 3Д, там кстати его крутить можно?

Share this post


Link to post
Share on other sites

Это opencv-шное 2Д, просто разные пары осей беру по X и Y.

Можно и в 3д построить (OpenGL) или записать координаты и в матлаб закинуть, там крутить можно.

Иногда строю такие вещи в Asymptote качественный вывод (много разных форматов) получается, причем можно 3д в pdf встраивать.

Вот в этом куске цифры менял:

y.push_back(result.at<float>(1));
x.push_back(result.at<float>(2));[/code]

Share this post


Link to post
Share on other sites

пытаюсь воспользоваться этой программой(которая похоже оказалась довольно глючной)

http://mldemos.epfl.ch/

в хелпе написано

Importing data

Generating data in MLDemos is done in three different ways: by manually drawing samples, by projecting image data through PCA (via the Projection panel), or by loading external data.

The data format used by the software is ascii-based and contains on the first line the # of samples followed by # of dimensions (only the first 2 dimensions are used in the software). The file then contains #samples lines with the coordinates of each sample followed by a class number (0 ... 255) and a flag (0-3) that allows to determine whether a sample is unused or in the training, validation or test sets.

a concrete example would be

4 2

0.10 0.11 0 0

0.14 0.91 0 0

0.43 0.74 1 0

0.28 0.34 1 0

which presents 4 two-dimensional samples, two from class 0 and one from class 1.

When the file is saved from MLDemos, the software adds the current algorithm parameters (provided an algorithm was selected), which can be useful for demonstration purposes. If no such information is present, the default algorithm parameters are selected. You should be able to convert your own data to this format with any script.

пробую простейший пример

4 2

0.10 0.11 0 0

0.14 0.91 0 0

0.43 0.74 1 0

0.28 0.34 1 0

thumb.png

thumb.png

thumb.png

thumb.png

пробую пример где размерность >2, хотя даже для простейшего примера =2 нормально не работает.

thumb.png

thumb.png

thumb.png

даже название классов отображается как то не так

thumb.png

вообщем такое ощущение, что либо я что то не так делаю, либо программа вообще не пригодна для данных размерности >2

п.с. качал бинарную версию для винды, возможно сборка из сорцов и их просмотр прояснят что либо, но пока туда лезть не охота, ибо там много зависимостей судя по всему.

Share this post


Link to post
Share on other sites

Вообще - то она для размерности 2 и есть.

Если больше, то попробуйте язык R (ML-сообщество почему то его очень любит).

http://www.r-project.org/

IDE здесь:

http://rstudio.org/

Share this post


Link to post
Share on other sites

ну вопервых там написано

by projecting image data through PCA (via the Projection panel)
откуда же там предполагается брать многомерные данные? а во вторых он даже для 2-мерного случая неправильно загружает(во всяком случае 1 лишнюю строку+ неправильно отображение названий классов)

Share this post


Link to post
Share on other sites

Насчет строки, там нет лишнего cr lf в конце файла ?

Share this post


Link to post
Share on other sites

нет я так тоже пробовал, может там должно быть не cr lf, а что то другое?

Share this post


Link to post
Share on other sites

А, ну так там же вначале количество строк и размерность данных указывается (можно попробовать поменять 8 на 7).

Share this post


Link to post
Share on other sites

попробовал, такое ощущение, что программе всё равно какое первое число, грузит всё так же, хоть 0 ставь.

хотя может быть что то в реестре что то прописалось и теперь из-за этого глюки.

Share this post


Link to post
Share on other sites

я кстати ваш код так до конца и не понял переписал по своему

Вот в этом куске цифры менял:

y.push_back(result.at<float>(1));

x.push_back(result.at<float>(2));

ну так numPrincipalComponents было ровно 3?

Mat imacl(Size(w/5,h/5),CV_8UC3);

почему делим на 5? я наоборот умножал.

for(int i=0; i<10; i++)

{

rectangle(imacl,Point(i*10,0),Point(i*10+10,10),Colors,-1);

}

не понял что делает.

void project_to_2D()

{

	//читаем данные

    vector<Mat> db;

	read_data("C:/ICP/MNIST/60k_images.bin",db);


	//читаем лэйблы (нужно для окраски точек)

	vector<unsigned char> MNIST_L;

	read_labels("C:/ICP/MNIST/60k_labels.bin",MNIST_L);


	int total = db[0].rows * db[0].cols;

	int numPrincipalComponents=2; //проецируем на 2Д

    int rows=db[0].rows;

    int cols=db[0].cols;


    Mat mat(total, db.size(), CV_32FC1);


	//составляем всё в общию матрицу

    for(int i = 0; i < db.size(); i++)

    {

        Mat X = mat.col(i);

        db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);

    }


    // do pca

    PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, numPrincipalComponents);


	//теперь должны спроецировать наши входные данные на наш новый базис

	//проецируем

	vector<float> x;

    vector<float> y;

	for(int i=0;i<db.size();++i)

	{

		Mat temp;

		Mat res;

		db[i].reshape(1, rows*cols).convertTo(temp,CV_32FC1,1./255);

		pca.project(temp,res);

		x.push_back(res.at<float>(0));

		y.push_back(res.at<float>(1));

	}


	//подготавливаем изображение под отрисовку

	float min_x,max_x,min_y,max_y;

	minmax_element(min_x, max_x, x);

	minmax_element(min_y, max_y, y);

	float w=max_x-min_x;

    float h=max_y-min_y;

    Mat imacl(Size(w*200,h*200),CV_8UC3);

    imacl=0;


	CvRNG rng(35435345);


    CvScalar Colors[256];

    for (int i=0;i<256;i++)

    {

            Colors[i]=random_color(&rng);

    }


    for(int i=0; i<x.size(); i++) 

    {

            int X=(x[i]-min_x)*200;

            int Y=(y[i]-min_y)*200;

			if(X>=(int)(w*200))

				X= w*200-1;

			if(Y>=(int)(h*200)) 

				Y= h*200-1;

            imacl.at<Vec3b>(Y,X)[0]=Colors[MNIST_L[i]].val[0];

            imacl.at<Vec3b>(Y,X)[1]=Colors[MNIST_L[i]].val[1];

            imacl.at<Vec3b>(Y,X)[2]=Colors[MNIST_L[i]].val[2];

    }

	imwrite("clusters_label_data.png",imacl); 

}

получилось вот что, вроде то же самое, но всё таки хочется куда то это выгружать, чтобы можно было увеличивать уменьшать в риалтайме и были написаны легенды или даже сами знаки.

thumb.png

thumb.png

по улучшению можно придумать только поменять вектор признаков или поменять PCA на что либо более сложное.

но с другой стороны как это всё оценивать? только на глаз?

Share this post


Link to post
Share on other sites

y.push_back(result.at<float>(1));
x.push_back(result.at<float>(2));[/code] Здесь второй собственный вектор совмещается с осью Y, а третий с X. На 5 делил, потому что в экран не помещалось.
[code]for(int i=0; i<10; i++)
{
rectangle(imacl,Point(i*10,0),Point(i*10+10,10),Colors[i],-1);
}

Легенду рисует в верхнем углу :)

Share this post


Link to post
Share on other sites

//преобразуем через LBP (надо не просто преобразовать, а гистограммы посчитать)

	vector<Mat> db_lbp(db.size());

	for(int i=0;i<db.size();++i)

	{

		lbp::OLBP(db[i],db_lbp[i]);

	}

	db= db_lbp;//

попробовал Local Binary Patterns, но ничего хорошего не выдало, видимо надо посчитать гистограммы, но я пока не понял как.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×