머신러닝과 필기체 숫자 인식

강형우·2022년 12월 12일
0

머신러닝

목록 보기
1/2

머신 러닝 개요

머신 러닝(machine learning)이란?

  • 머신 러닝(machine learning)이란?
    • 주어진 데이터를 분석하여 규칙성, 패턴 등을 찾고, 이를 이용하여 의미 있는 정보를 추출하는 과정

머신 러닝의 예

  • 붓꽃(IRIS) 품종 분류: Setosa vs. versicolor
  • 꽃잎(petal)과 꽃받침(sepal)의 길이에 의한 분류
  • 머신 러닝에 의한 붓꽃 분류(Con't)
    • Adaline: Adaptive Linear Neuron

머신 러닝 유형

  • 머신 러닝 종류
    • 분류를 많이 이용

머신 러닝 단계

  • 머신 러닝 단계
    • 훈련(Train): 훈련 데이터를 이용하여 모델을 학습하는 과정
    • 예측(Predict): 학습된 모델을 이용하여 새로운 데이터로부터 적절한 값을 예측하는 과정. 추론(Inference)
  • 머신 러닝 학습의 목적
    • 미래의 새로운 데이터를 더 정확하게 예측하기 위함
      • 미래의 새로운 데이터를 더 정확하게 예측하기 위함
        -> 모델의 일반화(generalization) 성능을 향상시키는 방향으로 학습해야함
    • under-fitting은 너무 Naive
    • Balanced 가 적절한 결과
    • Overfitting은 너무 과하다

머신 러닝 학습

  • 과적합(overfitting)

    • 훈련 데이터셋을 지나치게 정확하게 구분하도록 학습하여 모델의 일반화 성능이 떨어지게 되는 현상
  • 과적합 발생 원인

    • 훈련 데이터셋 문제
      • 훈련 데이터 셋이 너무 적은 경우
      • 훈련 데이터 셋이 전체 데이터셋의 특징/분포를 반영하지 않는 경우
    • 모델 문제
      • 모델이 복잡할수록 과적합 발생 확률이 높음
  • 훈련 데이터의 분할

    • 학습 가능한 데이터를 훈련, 검증, 테스트, 데이터 셋으로 분할하여 사용
  • K-폴드 교차 검증(k-fold cross-validation)

OpenCV 머신 러닝 알고리즘

OpenCV 머신 러닝 클래스

  • OpenCV머신 러닝 클래스 설명
    • 각각의 머신러닝 class는 ml 네임스페이스 안에 정의되어 있음
  • 머신러닝 알고리즘 훈련
  • 머신러닝 알고리즘 예측

SVM 알고리즘

서포트 벡터 머신(SVM) 알고리즘

  • 서포트 벡터 머신(SVM: Support Vector Machine) 알고리즘이란?
    • 기본적으로 두 개의 그룹(데이터)을 분리하는 방법으로 데이터들과 거리가 가장 먼 초평면(hyperplane)을 선택하여 분리하는 방법(maximum margin classifier)
    • 빨간 직선을 찾는 것. SVM
  • 최대 마진 초평면 구하기

  • 오분류 에러 허용하기
    • 주어진 샘플을 완벽하게 두 개의 그룹으로 선형 분리할 수 없을 경우, 오분류 에러를 허용(Soft margin, C-SVM)
  • 비선형 데이터 분리하기


  • 커널 트릭(kernel trick)
    • 매핑 함수를 직접 사용하는 대신 SVM 초평면 계산에서 사용되는 벡터 내적 연산을 대체하는 비선형 커널 함수(kernel function)를 정의하여 사용
  • 주요 커널 함수
  • SVM 객체 생성
  • SVM 타입 지정
    • 대부분 분류 용도로 SVM 사용
  • SVM 커널 지정
  • SVM 자동 훈련
    • C값, GAMMA값 어떻게 주어야?
      • C값, GAMMA값 그리고 그 외의 파라미터를 어떤식으로 설정할지를 자동으로 결정해서 학습을 진행하는 trainAuto 함수 제공
    • getC, getGamma를 통해 trainAuto가 설정한 C, Gamma값을 알아낼 수 있음
    • 권장방법: 샘플이 많을 경우 일부분만 선택을 하여 일부에 대해서만 trainAuto함수를 사용하여 학습을 진행한 후 거기서 결정된 C값과 Gamma값을 이용해서 전체데이터에 대한 학습을 다시 진행하면 학습이 빠르게 진행된다.
    • k-폴드 교차 검증을 통해 최선의 파라미터를 찾아 훈련
  • SVM을 이용한 2차원 점 분류 예제 프로그램
#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;
using namespace cv::ml;

int main()
{
	Mat train = Mat_<float>({ 8, 2 }, {
		150, 200, 200, 250, 100, 250, 150, 300,
		350, 100, 400, 200, 400, 300, 350, 400 });
	Mat label = Mat_<int>({ 8, 1 }, { 0, 0, 0, 0, 1, 1, 1, 1 });

	Ptr<SVM> svm = SVM::create();

#if 1
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::RBF);
	svm->trainAuto(train, ROW_SAMPLE, label);
#else
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->trainAuto(train, ROW_SAMPLE, label);
#endif

	cout << svm->getC() << endl;
	cout << svm->getGamma() << endl;

	Mat img = Mat::zeros(Size(500, 500), CV_8UC3);

	for (int y = 0; y < img.rows; y++) {
		for (int x = 0; x < img.cols; x++) {
			Mat test = Mat_<float>({ 1, 2 }, { (float)x, (float)y });
			int res = cvRound(svm->predict(test));

			if (res == 0)
				img.at<Vec3b>(y, x) = Vec3b(128, 128, 255); // R
			else
				img.at<Vec3b>(y, x) = Vec3b(128, 255, 128); // G
		}
	}

	for (int i = 0; i < train.rows; i++) {
		int x = cvRound(train.at<float>(i, 0));
		int y = cvRound(train.at<float>(i, 1));
		int l = label.at<int>(i, 0);

		if (l == 0)
			circle(img, Point(x, y), 5, Scalar(0, 0, 128), -1, LINE_AA); // R
		else
			circle(img, Point(x, y), 5, Scalar(0, 128, 0), -1, LINE_AA); // G
	}

	imshow("svm", img);
	waitKey();
}
  • 실행 결과

HOG 알고리즘

HOG 알고리즘

  • HOG(Histogram of Oriented Gradients)란?
    • 영상의 지역적 그래디언트 방향 정보를 특징 벡터로 사용
    • 2005년 CVPR 학회에서 보행자 검출 방법으로 소개되어 널리 사용되기 시작함
    • 이후 다양한 객체 인식에서 활용됨


    • 그래디언트의 크기 정보와 방향성 정보를 따로따로 저장
    • 10에는 4라는 값이 있는데 10이 중간 값이므로 2/2 나누어 계산하게된다.

HOG&SVM 필기체 숫자 인식

필기체 숫자 인식

  • 만약 정해진 폰트로 인쇄된 숫자라면 ?
    -> 템플릿 매칭으로도 가능
  • 필기체 인식?
    • 다수의 필기체 숫자 데이터가 필요
    • 70% Training 10% test
  • HOG 특징 벡터를 이용한 SVM학습

  • HOG&SVM 필기체 숫자 인식 예제 프로그램
#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;
using namespace cv::ml;

Mat img;
Point ptPrev(-1, -1);

void on_mouse(int event, int x, int y, int flags, void*)
{
	if (x < 0 || x >= img.cols || y < 0 || y >= img.rows)
		return;
	if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
		ptPrev = Point(-1, -1);
	else if (event == EVENT_LBUTTONDOWN)
		ptPrev = Point(x, y);
	else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
	{
		Point pt(x, y);
		if (ptPrev.x < 0)
			ptPrev = pt;
		line(img, ptPrev, pt, Scalar::all(255), 40, LINE_AA, 0);
		ptPrev = pt;

		imshow("img", img);
	}
}

Mat norm_digit(Mat& src)
{
	CV_Assert(!src.empty() && src.type() == CV_8UC1);

	Mat src_bin;
	threshold(src, src_bin, 0, 255, THRESH_BINARY | THRESH_OTSU);

	Mat labels, stats, centroids;
	int n = connectedComponentsWithStats(src_bin, labels, stats, centroids);

	Mat dst = Mat::zeros(src.rows, src.cols, src.type());
	for (int i = 1; i < n; i++) {
		if (stats.at<int>(i, 4) < 10) continue;

		int cx = cvRound(centroids.at<double>(i, 0));
		int cy = cvRound(centroids.at<double>(i, 1));

		double dx = 10 - cx;
		double dy = 10 - cy;

		Mat warpMat = (Mat_<double>(2, 3) << 1, 0, dx, 0, 1, dy);
		warpAffine(src, dst, warpMat, dst.size());
	}

	return dst;
}

int main()
{
	Mat digits = imread("digits.png", IMREAD_GRAYSCALE);

	if (digits.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

#if _DEBUG
	HOGDescriptor hog(Size(20, 20), // _winSize
		Size(8, 8),		// _blockSize
		Size(4, 4),		// _blockStride,
		Size(4, 4),		// _cellSize,
		9);				// _nbins,
#else
	HOGDescriptor hog(Size(20, 20), // _winSize
		Size(10, 10),	// _blockSize
		Size(5, 5),		// _blockStride,
		Size(5, 5),		// _cellSize,
		9);				// _nbins,
#endif

	size_t descriptor_size = hog.getDescriptorSize();
	cout << "Descriptor Size : " << descriptor_size << endl;

	Mat train_hog, train_labels;

	for (int j = 0; j < 50; j++) {
		for (int i = 0; i < 100; i++) {
			Mat roi = digits(Rect(i * 20, j * 20, 20, 20)).clone();

			vector<float> desc;
			hog.compute(roi, desc);

			Mat desc_mat(desc, true);
			train_hog.push_back(desc_mat.t());
			train_labels.push_back(j / 5);
		}
	}

	Ptr<SVM> svm = SVM::create();
//	Ptr<SVM> svm = SVM::load("svmdigits.yml");

	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::RBF);

#if 1
	svm->setGamma(0.50625);
	svm->setC(2.5);
	svm->train(train_hog, ROW_SAMPLE, train_labels);
#else
	svm->trainAuto(train_hog, ROW_SAMPLE, train_labels);
#endif
//	svm->save("svmdigits.yml");

	// 입력 이미지 생성
	img = Mat::zeros(400, 400, CV_8U);

	imshow("img", img);
	setMouseCallback("img", on_mouse);

	while (true) {
		int c = waitKey();

		if (c == 27) {
			break;
		} else if (c == ' ') {
			Mat img_blur, img_resize;
			GaussianBlur(img, img_blur, Size(), 1);
			resize(img_blur, img_resize, Size(20, 20), 0, 0, INTER_AREA);

			vector<float> desc;
			hog.compute(img_resize, desc);

			Mat desc_mat(desc, true);
			float res = svm->predict(desc_mat.t());
			cout << cvRound(res) << endl;

			img.setTo(0);
			imshow("img", img);
		} else if (c == 'c') {
			img.setTo(0);
			imshow("img", img);
		}
	}
}
  • 실행 결과
  • HOG & SVM 필기체 숫자 인식 예제 실행 결과
  • 숫자 영상 정규화
    • 훈련 데이터 영상과 테스트 데이터 영상의 위치, 크기, 회전 등의 요소를 정규화 하여 인식 성능을 향상시킬 수 있음
    • e.g) 숫자 영상의 바운딩 박스를 기준으로 중앙으로 위치 보정 또는 무게중심을 중앙에 위치하도록 위치 보정
    • 정규화를 거치지 않으면 성능이 기대에 미치지 않을 수 있다.

0개의 댓글