【Pytorch】Pytorch internal에 대한 탐구

  • Pytorch internal에 대한 탐구를 해본다.
  • Pytorch와 C와의 연결성에 대해 공부해본다.

1. where is torch.tensor.item() defined at the Github?

  • stackoverflow에 이와 같이 질문을 남겼다. stackoverflow LINK
  • 나는 Link에 나와 있는 것처럼 답변을 받았다.
  • 하지만 완벽히 이해를 하지 못해서 공부해서 다시 답변을 이해해 보려고 한다,
  • 그래서 나는 아래의 사이트를 공부하기로 하였다.
    1. https://pytorch.org/blog/a-tour-of-pytorch-internals-1/
    2. https://pytorch.org/blog/a-tour-of-pytorch-internals-2/
    3. https://medium.com/@andreiliphd/pytorch-internals-how-pytorch-start-211e0d57ad26

2. PyTorch internals. How PyTorch start?

  • torch/csrc/Module.cpp 이 python과 C++를 연결해준다.
  • Medium Link
    1. import torch를 하는 순간 torch/init.py를 읽고 from torch._C import *를 실행한다. 이 작업을 통해서 Pytorch 속 C 모듈을 실행한다.
    2. torch._C에는 pyi라는 python 파일들이 있고, 이것이 torch/csrc/Module.cpp와 연결되어 있다.
    3. torch/csrc/Module.cpp를 보면
    • THP란?
      • 간단하게, 리눅스의 메모리 관리 시스템
      • what THP? : https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/
      • TLB란 : https://ossian.tistory.com/38 0. PyObject 라는 자료형이 원래 어딘가 저장되어 있는 듯 하다. (나중에 VScode에 전체 페키지를 올리고 go to definition 해볼것.) 1. THPModule_initExtension라는 함수가 정의 되어 있다.
        - torch::utils::initializeLayouts(); // torch/csrc/utils/tensor_qschemes.h
        - torch::utils::initializeMemoryFormats();
        - torch::utils::initializeQSchemes();
        - torch::utils::initializeDtypes();
        - torch::tensors::initialize_python_bindings(); // torch/csrc/tensor/python_tensor.h
      
      • 이 함수는 파이썬 클래스의 추가적인 초기화(C를 사용한 tensor 맴버함수들을 사용할 수 있게끔 Ex.item())를 위해 사용된다.
    1. Tensor storage(tensor 저장공간)은 이 코드에 의해서 메모리 공간이 생성된다.

       auto module = THPObjectPtr(PyImport_ImportModule("torch"));
      
       THPDoubleStorage_postInit(module);
       THPFloatStorage_postInit(module);
       THPHalfStorage_postInit(module);
       THPLongStorage_postInit(module);
       ...
       ...
      
    2. THPModule_initNames 라는 함수에 의해서 메모리 공간 이름이 정의 된다.(?)
    3. PyObject* initModule() 이 함수에 의해서, torch._C의 python이 csrc 내부의 C++파일들을 사용할수 있게 만들어준다.
    4. static struct PyModuleDef torchmodule 이 부분에 의해서 torch._C 를 위한 메모리 공간을 잡아준다.

3. A Tour of PyTorch Internals (Part I)

  • document link
  • 우선 지금 이게 급한게 아니라서 나중에 하도록 하자.

4. PyTorch Internals Part II - The Build System

【캡스톤2】 자율주행 RC카 - 장애물 회피 및 곡선 주행

rplidar, IMU, 아두이노, 라즈베리파이를 이용한 자율주행 RC카 제작하기.

  • rplidar, IMU, 아두이노, 라즈베리파이를 이용한 자율주행 RC카 제작하기.
  • 코드는 현재 github에 private repository로 관리 중 입니다. 필요하신 분은 따로 연락주세요.

0. 주행 완성 동영상

  • 주행영상 1 (아래 이미지를 클릭!)

self-RCcar2

  • 주행영상 2 (아래 이미지를 클릭!)

self-RCcar

1. 큰그림 간결 손코딩

  • 주의사항
    1. 추후에 하나하나의 함수에 대한 손코딩 꼭 하고 코딩하기
    2. 추후에 어떤 식으로 모듈화하고 이용할 건지 정하고 코딩 하기

To Do list

  • PPT 발표준비하기
  • 11월 4일 이후에 팀원에게 코드 설명해주기
  • 11월 13일 이후에 아래의 내용들 시작하기 그 전까지 쉬자. 힘들어 죽겠다.
    • IMU ROS에 연동시키고 subscribe하는 line 추가하기
    • IMU값이 오차 없이 거의 일정한지 확인해보기.(정확하다면 두 직선의 기울기의 평균을 사용할 필요 없음)
    • 아래의 손코딩 주행 알고리즘 코딩하기
      1. 2개의 class에 대한 전처리 맴버변수 4개 정의하기. (팀원과 같이 하기)
      2. 주행 알고리즘 맴버변수 정의하기. (팀원과 같이 하기)
    • theta1, theta2 정확한 값 찾아서 적어놓기

0. 대표 상수 및 전역 벽수

  • theta1 : 첫 직선 기준 각도
  • theta2 : 곡선 후 직선 기준 각도
  • wall_c_l_count : wall_c_l가 발견된 갯수

1. Lidar Point Clouds를 2D 이미지로 변환하기.

  • 이때 중요한 점은 차가 회전해도 이미지는 회전하지 않도록 아래 중 하나 이용.
    1. 따로 이미지 변수를 만든다. 이것은 차가 회전하면 이미지도 회전하는 이미지. 여기서 감지되는 벽의 기울기가 theta1이 되도록, 회전하지 않는 이미지 만들기.
    2. theta1과 현재 IMU에서 나오는 차량 각도의 차이를 반영하기(IMU오차 걱정됨)

2. 변환된 이미지를 아래의 처리 순서대로 하기.

아래의 과정을 통해, 오차 없이 장애물 및 벽이 감지되도록 노력한다.

  1. 가오시안 필터
  2. Threshold로 binary 이미지로 변환
  3. canny edge 알고리즘 적용
  4. hough Transform으로 장애물, 벽 감지

3. 감지된 직선이 벽 VS 장애물 판단하기

  1. 장애물 : 50cm 이상 20cm 이상의 직선 만. 차량 뒤에 있는 장애물은 감지 하지 않기 위해, 장애물의 y축 좌표 중 큰 값이 차 앞에 있을 때 만 고려하기.
  2. 벽 : 1m 이상의 직선 만. theta1, theta2와 -5~5도 차이의 기울기를 가지는 직선
    • 위에서 탐지한 객체는 다음 중 하나로 labeling을 한다.
    1. obstacle_r : right
    2. obstacle_l : left
    3. wall_l : theta1
    4. wall_r : theta1
    5. wall_c_r : theta2 curve right
    6. wall_c_l : theta2 curve left
      • 한계점 : - 한 lebel에 대해서 여러개의 직선이 검출된다면? 하나만 선택? 평균값 이용? - left, right 벽이 잘 검출되지 않는다면? 직선이 겨우겨우 검출된다면? threshold 낮춰서 직선 검출하기. 꼭 필요한게 검출되지 않는다면 그 순간은 속도 0하기.(?)

4. 주행 알고리즘 설계하기

  • 경우1 : 장애물과의 거리가 충분히 멀 때
    • 점과 직선사이 거리 이용
    • 속도 上
    • 직선 주행 방법
      1. 두 직선과의 거리가 같도록, 두 직선의 기울기의 평균 기울기가 theta1이 되도록 주행하기. (X)
      2. 두 직선과의 거리가 같도록, 그리고 차량각이 theta1을 가지게 주행하기. (X)
      3. theta1(혹은 두 직선의 기울기의 평균 기울기)를 가지고, 두 직선 중심의 좌표점을 지나는 직선 (혹은 두 직선의 중앙 직선 구하는 방정식 찾기) 위의 한 점을 path point를 설정하고, 차량이 그 point를 향해 주행하도록 설정하기. (V)
  • 경우2 : 장애물과의 거리가 가까울 때
    • 점과 직선사이 거리 이용
    • 전면에 있을 때, 측면에 있을 때 모두 가능
    • 속도 下
    • 주행 방법
      1. 더 큰 벽을 선택한다? NO. 별로 좋은 방법 아닌듯.
      2. obstacle_l, obstacle_r를 선정하는 알고리즘에 두 직선의 중앙에 대해 어디에 있는가를 이용한다.
      3. 그리고 obstacle의 위치 반대편 벽을 따라서 주행한다.
      4. theta1(혹은 두 직선의 기울기의 평균 기울기)를 가지고, 한 벽에 대해 일정 거리의 좌표점을 지나는 직선 (혹은 일정 거리를 가지는 직선의 방정식 찾기) 위의 한 점을 path point로 설정하고, 차량이 그 point를 향해 주행하도록 설정하기
  • 경우3 : 곡선 주행 하기
    • 가까이 있는 wall_c_l(우회전일 경우)이 일정 count 이상 탐지가 됐다면.
    • bool 변수를 switch로써 on 하고 다른 함수 실행 시작하기.
    • wall_c_r이 감지되지 않으면, wall_c_l을 기준으로 경우2 알고리즘을 사용해서 곡선 주행.
    • wall_c_r이 감지되기 시작하면, 경우1 알고리즘을 사용해서 직선 주행.
  • 최종 조향에서! 차가 이미 많이 비틀어진 상태라면 반대방향으로 조향하도록 상수값의 steering 값 return.(경우1,2. 경우3에 대해 다른 상수값 적용)


2. PseudoCode in detail

0. To DO List

  1. 가상의 line이 그려져 있는 이미지 만들기
  2. c++ class 공부하기
  3. 그 이미지를 통해서 class 만들고 테스트 해보기

1. class의 종류와 맴버변수와 맴버함수

  1. class Line
    • 맴버변수
      1. x1
      2. y1
      3. x2
      4. y2
      5. length - cm
      6. dis_center - cm
      7. slope - degree
      8. label/class - 1(obstacle_l),2(obstacle_r),11(wall_l),12(wall_r),21(wall_c_r),22(wall_c_l)
      9. position - 1(back or left),2(front or right)
    • 맴버함수
      1. lengh 계산하기
      2. dis_center 계산하기
      3. slope 계산하기
  2. class Cal_lines
    • 맴버변수
      1. 현재 검출된 line의 갯수
      2. 검출되는 line 최대 18개
      3. lebel 별 갯수 - 6개의 객체
    • 맴버함수
      1. wall 검출하기
      2. obstacle 검출하기
      3. 갯수 변수에 값 저장해 두기
      • 경우1 주행알고리즘
      • 경우2 주행알고리즘
      • 경우3 주행알고리즘

Basic Code - step1

#include <opencv2/opencv.hpp>
#include <unistd.h>
#include <istream>

using namespace cv;
using namespace std;

// Class Line --------------------------------
class Line{
public:
	int x1,y1,x2,y2;
	float length, dis_center;
	double slope;
	int label;

	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	float cal_length();
	float cal_center();
	double cal_slope();
};

Line::Line(){
	x1 = 1;
	y1 = 1;
	x2 = 1;
	y2 = 1;
}

Line::Line(int _x1, int _y1, int _x2, int _y2){
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
	length = this->cal_length();
	dis_center = this->cal_center();
	slope = this->cal_slope();
}

float Line::cal_length(){
	return 1;
}

float Line::cal_center(){
	return 1;
}

double Line::cal_slope(){
	return 1;
}

// Class Cal_lines --------------------------------
class Cal_lines{
	public:
		int num_lines;
		Line line[18];
		int num_1, num_2, num_11, num_12, num_21, num_22; 

		void append(Line line_t);
		void labeling_wall();
		void labeling_obstacle();
		void howmany_num();
		Cal_lines();
};

Cal_lines::Cal_lines(){
	num_lines = 0;
	num_1 = num_2 = num_11 = num_12 = num_21 = num_22 = 0;
}

void Cal_lines::append(Line line_t){

}

void Cal_lines::labeling_wall(){

}
void Cal_lines::labeling_obstacle(){

}
void Cal_lines::howmany_num(){

}




// Main --------------------------------

int main()
{
	// 컬러 이미지를 저장할 Mat 개체를 생성합니다.

	cv::Mat img(1200, 1200, CV_8UC3, cv::Scalar(0,0,0));
	line(img, Point(450,400), Point(450,750), Scalar(255,255,255),1);
	line(img, Point(750,400), Point(750,750), Scalar(255,255,255),1);
	line(img, Point(630,450), Point(680,450), Scalar(255,255,255),1);
	line(img, Point(630,450), Point(630,420), Scalar(255,255,255),1);
	line(img, Point(450,300), Point(1100,300), Scalar(255,255,255),1);

	Line temp_line(450,400,450,750);
	cout << temp_line.slope << endl;

	Cal_lines Cal;
	cout << Cal.num_lines << endl;
	
	imshow("result", img);
	waitKey(0);
}

Basic Code - step2

// 실행하기 전에 꼭 아래의 문구 터미널에 치기!!
// $ export DISPLAY=:0.0
// circle : https://webnautes.tistory.com/1207
// opencv setup : https://webnautes.tistory.com/933
// shortcut : billd : Ctrl + b
// shortcut : excute : Ctrl + r
// 하지만 반드시 실행은 terminal 에서 하도록 해라!! ./opencv
// last sentence : waitKey(0); 필수이다!
// for is going to iterate only if there is keyboard input!


#include <opencv2/opencv.hpp>
#include <unistd.h>
#include <istream>
#include <math.h>
#define PI 3.14159265

using namespace cv;
using namespace std;

// Class Line --------------------------------
class Line{
public:
	int x1,y1,x2,y2;
	float length, dis_center;
	double slope;
	int label;
	int position;

	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	float cal_length();
	float cal_center();
	double cal_slope();
	int cal_position();
};

Line::Line(){
	x1 = 1;
	y1 = 1;
	x2 = 1;
	y2 = 1;
}

Line::Line(int _x1, int _y1, int _x2, int _y2){
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
	length = this->cal_length();
	slope = this->cal_slope();
	dis_center = this->cal_center();
	position = cal_position();
}

float Line::cal_length(){
	return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}

float Line::cal_center(){
	// By https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
	int center_x = 600;
	int center_y = 600;
	float dis = abs(((y2-y1)*center_x - (x2-x1)* center_y + x2*y1 - y2*x1) / (this->length));
	return dis;
}

double Line::cal_slope(){
	// return degree {-180 ~ +180}
	return atan2((y2 - y1),(x2 - x1)) * 180 / PI ; 
}

int Line::cal_position(){
	return 1;
}

// Class Cal_lines --------------------------------
class Cal_lines{
	public:
		int num_lines;
		Line line[18];
		int num_1, num_2, num_11, num_12, num_21, num_22; 

		Cal_lines();
		void append(Line line_t);
		void labeling_wall();
		void labeling_obstacle();
		void howmany_num();
};

Cal_lines::Cal_lines(){
	num_lines = 0;
	num_1 = num_2 = num_11 = num_12 = num_21 = num_22 = 0;
}

void Cal_lines::append(Line line_t){
	// Have to change IF condition 85~95...
	// if(line_t.slope == 90 || line_t.slope == 0){
		if(num_lines<18){
			line[num_lines] = line_t;
			num_lines++;	
	}
	// }
}

void Cal_lines::labeling_wall(){

}

void Cal_lines::labeling_obstacle(){

}

void Cal_lines::howmany_num(){

}




// Main --------------------------------
int main()
{
	// 컬러 이미지를 저장할 Mat 개체를 생성합니다.

	cv::Mat img(1200, 1200, CV_8UC3, cv::Scalar(0,0,0));
	int arr[5][4] = 
					{ {450,400,450,750}, //0
				    	{750,400,750,750}, //1
				    	{630,450,680,450}, //2
				    	{700,420,700,700}, //3
				    	{1100,300, 450,300} //4
					};
	
	line(img, Point(arr[0][0],arr[0][1]), Point(arr[0][2],arr[0][3]), Scalar(255,255,255),1);
	line(img, Point(arr[1][0],arr[1][1]), Point(arr[1][2],arr[1][3]), Scalar(255,255,255),1);
	line(img, Point(arr[2][0],arr[2][1]), Point(arr[2][2],arr[2][3]), Scalar(255,255,255),1);
	line(img, Point(arr[3][0],arr[3][1]), Point(arr[3][2],arr[3][3]), Scalar(255,255,255),1);
	line(img, Point(arr[4][0],arr[4][1]), Point(arr[4][2],arr[4][3]), Scalar(255,255,255),1);
	circle(img, Point(600,600),2,Scalar(255,0,0),2);
	

	Cal_lines lines;
	for(int i = 0; i < 5; i++ ){
		Line temp_line(arr[i][0],arr[i][1],arr[i][2],arr[i][3]);
		lines.append(temp_line);
	}

	cout << lines.num_lines << endl;
	cout << lines.line[0].dis_center << endl;



	
	imshow("result", img);
	waitKey(0);
	//waitKey(1);
}

Intermediate Code - step3

// 실행하기 전에 꼭 아래의 문구 터미널에 치기!!
// $ export DISPLAY=:0.0
// circle : https://webnautes.tistory.com/1207
// opencv setup : https://webnautes.tistory.com/933
// shortcut : billd : Ctrl + b
// shortcut : excute : Ctrl + r
// 하지만 반드시 실행은 terminal 에서 하도록 해라!! ./line_test
// last sentence : waitKey(0); 필수이다!
// For(including waitkeyy_function) is going to iterate only if there is keyboard input!


#include <opencv2/opencv.hpp>
#include <unistd.h>
#include <istream>
#include <math.h>
#define PI 3.14159265

using namespace cv;
using namespace std;

// 1. Class Line ----------------------------------------------------------------------------------------------------------------------------
class Line{
public:
	int x1,y1,x2,y2;
	float length, dis_center;
	double slope;
	int label; // 1,2,11,12,21,22
	int position;

	Line();
	Line(int _x1, int _y1, int _x2, int _y2);
	float cal_length();
	float cal_center();
	double cal_slope();
	int cal_position();
};

Line::Line(){
	x1 = 1;
	y1 = 1;
	x2 = 1;
	y2 = 1;
}

Line::Line(int _x1, int _y1, int _x2, int _y2){
	x1 = _x1;
	y1 = _y1;
	x2 = _x2;
	y2 = _y2;
	length = this->cal_length();
	slope = this->cal_slope();
	dis_center = this->cal_center();
	position = this->cal_position();
}

float Line::cal_length(){
	return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}

float Line::cal_center(){
	int center_x = 600;
	int center_y = 600;
	float dis = abs(((y2-y1)*center_x - (x2-x1)* center_y + x2*y1 - y2*x1) / (this->length));
	return dis;
}

double Line::cal_slope(){
	// return degree {-180 ~ +180}
	slope = atan2((y2 - y1),(x2 - x1)) * 180 / PI;  
	slope = (slope >= 0)? slope : 180 + slope; // 0~180 degree
	return slope; 
}

int Line::cal_position(){  // ***********
	/*
	front, right = return 2
	back, left = return 1
	*/
	if((slope > 90-3 && slope < 90+3 )){ // vertical
		return (x1>600)? 2:1; // 1: left,  2: right
	}
	else if(slope > (0-3)+180 || slope < 0+3){  // horizontal
		int y_min = min(y1,y2);
		return (y1>600)? 2:1; // 1: back,  2: front
	}
	else return 0; // not (vertical or horizontal)
	
} 

// 2. Class Calculate_lines ----------------------------------------------------------------------------------------------------------------------------
class Cal_lines{
	public:
		int num_lines;
		Line line[18];
		int num_1, num_2, num_11, num_12, num_21, num_22; 

		Cal_lines();
		void append(Line line_t);
		void labeling_wall();
		void labeling_obstacle();
		void howmany_num();

		// Driving Algorithm
		int what_case(); 
		void drive_case1(int *velocity, int *steering, int case_is);
		void drive_case2(int *velocity, int *steering, int case_is);
		void drive_case3(int *velocity, int *steering, int case_is);
};

Cal_lines::Cal_lines(){
	num_lines = 0;
	num_1 = num_2 = num_11 = num_12 = num_21 = num_22 = 0;
}

void Cal_lines::append(Line line_t){  
	// Have to change IF condition 85~95...
	if( (line_t.slope > 90-3 && line_t.slope < 90+3 ) || (line_t.slope > (0-3)+180 || line_t.slope < 0+3) ){
		if(num_lines<18){
			line[num_lines] = line_t;
			num_lines++;
		}
	}
}

void Cal_lines::labeling_wall(){ // ***********
	for (int i = 0; i < num_lines; i++)
	{
		line[i].label= 0; //??

	}
}

void Cal_lines::labeling_obstacle(){ // ***********
	for (int i = 0; i < num_lines; i++)
	{
		line[i].label= 0; //??

	}
}

void Cal_lines::howmany_num(){ 
	for (int i = 0; i < num_lines; i++)
	{
		if(line[i].label == 1)       num_1++;
		else if(line[i].label == 2)  num_2++;
		else if(line[i].label == 11) num_11++;
		else if(line[i].label == 12) num_12++;
		else if(line[i].label == 21) num_21++;
		else if(line[i].label == 22) num_22++;
		else cout << "line[i]'s label has to be 1,2,11,12,21,22";

	}
}

int Cal_lines::what_case(){ // ***********
	return 1;
}

void Cal_lines::drive_case1(int *velocity, int *steering, int case_is){ // ***********

}

void Cal_lines::drive_case2(int *velocity, int *steering, int case_is){ // ***********

}

void Cal_lines::drive_case3(int *velocity, int *steering, int case_is){ // ***********

}



// 3. Class detected_lines ------------------------------------------------------------------------------------------------------------------------
class Detected_lines{
	public:
		Line line_1[3];
		Line line_2[3];
		Line line_11[3];
		Line line_12[3];
		Line line_21[3];
		Line line_22[3];
		int counted_1, counted_2;
		int counted_11, counted_12;
		int counted_21, counted_22;
		void append(Line line_t);
		Detected_lines();

};

Detected_lines::Detected_lines(){
	counted_1 = counted_2 = counted_11 = counted_12 = counted_21 = counted_22 = 0;
}

void Detected_lines::append(Line line_t){
	switch (line_t.label){
		case 1:
			line_1[counted_1] = line_t;
			counted_1++;
			if (counted_1 == 3) counted_1 = 0;
			break;
		case 2:
			line_2[counted_2] = line_t;
			counted_2++;
			if (counted_2 == 3) counted_2 = 0;
			break;
		case 11:
			line_11[counted_11] = line_t;
			counted_11++;
			if (counted_11 == 3) counted_11 = 0;
			break;
		case 12:
			line_12[counted_12] = line_t;
			counted_12++;
			if (counted_12 == 3) counted_12 = 0;
			break;
		case 21:
			line_21[counted_21 %3] = line_t;
			counted_21++;
			if (counted_21 == std::numeric_limits<int>::max()) counted_21 = 300;
			break;
		case 22:
			line_22[counted_22 %3] = line_t;
			counted_22++;
			if (counted_22 == std::numeric_limits<int>::max()) counted_22 = 300;
			break;
		default:
			cout << "line's label must be one of 1,2,11,12,21,22" << endl;
	}
}

Detected_lines global_lines;

// Main -------------------------------------------------------------------------------------------------------------------------------------------------------------
int main()
{
	// 컬러 이미지를 저장할 Mat 개체를 생성합니다.

	cv::Mat img(1200, 1200, CV_8UC3, cv::Scalar(0,0,0));
	cv::Mat img_fliped(1200, 1200, CV_8UC3, cv::Scalar(0,0,0));
	int arr[5][4] = 
					{ {450,800,450,450}, //0
				    	{750,800,750,450}, //1
				    	{630,750,680,750}, //2
				    	{610,780,630,750}, //3
				    	{1100,900, 450,900} //4
					};
	
	line(img, Point(arr[0][0],arr[0][1]), Point(arr[0][2],arr[0][3]), Scalar(255,255,255),1); // 0
	line(img, Point(arr[1][0],arr[1][1]), Point(arr[1][2],arr[1][3]), Scalar(255,255,255),1); // 1
	line(img, Point(arr[2][0],arr[2][1]), Point(arr[2][2],arr[2][3]), Scalar(255,255,255),1); // 2
	line(img, Point(arr[3][0],arr[3][1]), Point(arr[3][2],arr[3][3]), Scalar(255,255,255),1); // 3
	line(img, Point(arr[4][0],arr[4][1]), Point(arr[4][2],arr[4][3]), Scalar(255,255,255),1); // 4
	circle(img, Point(600,600),2,Scalar(255,0,0),2);
	

	Cal_lines lines;
	for(int i = 0; i < 5; i++ ){
		Line temp_line(arr[i][0],arr[i][1],arr[i][2],arr[i][3]);
		lines.append(temp_line);
	}

	lines.labeling_wall();
	lines.labeling_obstacle();

	int velocity = 1111;
	int steering = 2222;
	int case_is = lines.what_case();
	if(case_is == 1)      lines.drive_case1(&velocity, &steering, case_is);
	else if(case_is == 2) lines.drive_case2(&velocity, &steering, case_is);
	else if(case_is == 3) lines.drive_case3(&velocity, &steering, case_is);
	else if(case_is == 4) lines.drive_case1(&velocity, &steering, case_is);
	else if(case_is == 5) lines.drive_case2(&velocity, &steering, case_is);
	else if(case_is == 6) lines.drive_case3(&velocity, &steering, case_is);
	else cout << "case_is is not valid";

	
	float data = float(velocity) + float(steering+1) / 10000;
	cout << fixed;
	cout.precision(5);
	cout << "Final ToArduData.data is :" << data << endl;

	cout << lines.num_lines << endl;
	cout << lines.line[0].position << endl;

	flip(img,img_fliped,0);
	imshow("result", img_fliped);
	waitKey(0);
	//waitKey(1);
}

【LinearAlgebra】선형대수 정리 1 (한글강의)

쑤튜브 선형대수 강의내용 정리 1강~22강

youtube 강의 링크 : 쓔튜브 선형대수 강의 링크

1강~5강까지는 매우 기초적인 내용이라 정리하지 않았습니다.

6강 Systems of linear equations

  1. 확장 행렬(Augmented matrix)이란? 선형연립방정식을 행렬로 표현한 것.

    image-20201111204926141

  2. reduced row echelon form (기약 행 사다리꼴)

    • 해가 이미 구해진 형태의 확장 행렬
    • x3 = 2라고 한다면 확장행렬의 한 row는 0 0 1 2가 될 것이다.
    • 아래의 4가지 규칙을 만족할 때 reduced row echelon form이라고 부를 수 있다.
      1. 영 행이 아니라면, 각 행에서 처음 등장하는 영이 아닌 수는 1이고, 이를 leading 1이라고 부른다.
      2. 영행은 항상 맨 아래에 모여있다.
      3. leading 1의 위치는 항상 한 칸 이상 뒤로 밀린다.
      4. leading 1이 포함된 열은 leading 1을 제외하면 모두 0이다.

Gauss Jordan Elimination (7강)

  • 일반적인 연립선형의 확장행렬을 reduced row echolon form(기약행사다리꼴)으로 바꿔주는 방법

    1. Elementary row operations(기본 행 연산)

      • 한 행에 상수배(=! 0) 하는 방법
      • 한 행의 상수배(=! 0)를 다른 행 더하기
      • 행 교환
    2. 가오스 소거법

      확장행렬을 기약 행 사다리꼴(row echelon = leading 1 아래로 0인 행렬)로 바꾸는 알고리즘

      • (1행부터 아래로) 기본 행 연산을 통해서 행 사다리꼴을 만들어 주는 과정
      • leading1 앞을 0으로 만들어주는 과정
      • 여기까지는 가오스 소거법이다.
    3. reduced (기약) 형태(leading1 뒤도 0인 행렬) 만들어 주기

      • 이게 가오스 조던 소거법이다.
      • (마지막행부터 위로 back substitution) 기본 행 연산을 통해서 기약(reduced) 행 사다리꼴을 만들어 주는 과정
  • 이 알고리즘을 통해서 컴퓨터로 해를 구하는 과정이 매우 쉬워졌다. 가감법 대입법을 사용해서 해를 구하면 엄청 오래 걸린다.

  • (8강 Gauss Jordan Elimination의 고찰) 가오스 조던 소거법은 왜 가능할까? 가오스 소거법이 연립 일차방정식의 가감법이다. 가오스 조던 소거법이 연립 일차방정식의 대입법이다.

행렬의 기본 연산과 이론

  • 13강 Identity matrix (항등행렬) - 행렬의 곱샘 항등원이다.

  • 14강 역행렬(inverse matrix, invertable (역행렬 존재하는 행렬=가역행렬))

    • 곱샘의 항등원에 대한 역원)
    • 귀류법을 이용한 증명에 의해, 한 행렬의 역행렬은 유일하다 (1개만 존재한다).
    • 영인자(zero divisor) - 0이 아닌 두 행렬을 곱해서 0이 나올 수 있다.
    • 가오스 조던 소거법을 이용한 역행렬 구하는 방법
      • image-20201113213128976
      • 파랑색 행렬에 대해서, 가오스 조던 소거법을 사용해 x,y,z,w를 구하면 우리에게 익숙한 2차 정사각행렬의 역행렬 공식을 구할 수 있다.
  • 15강 역행렬의 성질

    • invertable (역행렬 존재하는 행렬=가역행렬)을 판단하는 방법? determinent(행렬식)=0 인지를 판단
    • non-invertible한 행렬? singular matrix. determinent(행렬식)=0인 metrix
    • 역행렬의 지수곱. 역행렬의 역행렬은 자기자신. 스칼라배는 역수.
    • image-20201113222625212
  • 16강 전치행렬(transposed matrix)

    • 덧셈의 분배법칙 성립. (모든 증명은 동영상에 있다)
    • 곱셉의 분배법칙은 위와 같이 위치가 뒤바껴야 한다.
    • A가 invertable이면 transe(A)도 invertable이다.
    • inverse(trace(A)) = trace(inverse(A)) 이다.
  • 17강 대각합(trace)

    • n차 정사각행렬 A에 대해서, 대각성분들의 합을 대각합이라 한다.
    1. dot product와 행렬곱 사이의 관계
      • 열백터 -> 백터로 취급 가능하다.
      • dot product( • ) - 백터와 백터, 열백터와 열백터 (EX. (AB)ij => transe(Ai) • (Bj))
      • A : nxn, (n,v는 열백터) u : nx1, v : nx1 일때, Au • v = u • transe(A)v , u • Av = transe(A)u • v

기본 행렬과 가역행렬의 관계

  • 18강 기본행연산의 행렬화 (강의 꼭 한번 더 보기)

    • 열백터 -> 백터로 취급하기
      • 열백터의 행교환 = 백터의 좌표 뒤바꿈 -> 선형변환=선형사상(linear transformation) -> 이 변환식을 행렬로 표현(행렬화)할 수 있다.
        • 선형사상은 행렬로 표현할 수 있다.
        • 기본행 연산도 행렬로 표현할 수 있다.
      • y = f(x)의 함수로써 그 함수가 열백터의 행을 바꾸는 함수하면, 그 함수의 확장행렬은 아래와 같다.
      • image-20201113225645176
      • 즉 열백터의 행교환을 하고 싶으면 I의 행교환 시킨 행렬을 곱해주면 되고
      • 행렬 행백터의 열교환을 하고 싶으면 I의 열교환 시킨 행렬을 곱해주면 된다.
      • 스칼라곱을 하는 linear transformation으로, 한 행에 상수배를 해주고 싶으면, I의 i행에 상수배를 한 행렬이 변환행렬이다. (행렬화)
      • 이와 같은 기본 행연산(행변환, 열변환, 상수배, 행or열이 일차 결합)을 행렬화 할 수 있다. = 기본행렬
  • 19강 기본행렬(elementary matrix)과 기약행사다리꼴의 역행렬

    • 기본행렬 - 1. 행교환 2. 행의 상수배 3. 행의 상수배를 다른 행의 더하기. 이러한 역할을 하는 행렬을 의미한다. 이 3가지 역할 중 2개 이상을 수행하는 행렬은 기본행렬이라고 하지 않는다.(기본행렬 2개를 곱해주면 2개의 역할을 수행할 수 있긴 하지만. 그렇게 곱해서 나온 행렬은 기본행렬이라 하지 않는다.)
    • 기본행렬의 특징
      • n차 정사각행렬.
      • 선형사상이다(정의역과 치역의 Space가 동일하다. 다르거나 일부분이라면 선형사상이 아니다.) = 기본행렬은 항상 가역행렬이다. = 역행렬이 항상 존재 한다.
      • 항상 항등행렬을 이용해서 만든다(18강 처럼) = 그렇다면 그 행동과 완전히 반대대는 역할을 하는 행렬이 당연히 반드시 존재할 것이다. (ex, I의 i행과 j행을 행교환한 행렬 <-> I의 j행과 i행을 행교환한 행렬) 즉! 역행렬이 항상 존재한다.
    • 기약행사다리꼴 중 영행을 포함하는 행렬은 determinent=0 이다.(증명 나중에) 따라서 기약행사다리꼴 중 역행렬이 존재하는 행렬은 단위행렬 뿐이다.
    • 따라서! A가 가역행렬(역행렬이 존재하면)이면 A를 가오스 조던 소거법을 행하여 구한 기약행사다리꼴은 단위행렬이다!! = A에 가오스 조던 소거법을 적용하는 것이, “기본행렬 연산을 여러번 수행”하는 것이다. 따라서 A의 역행렬이 존재한다면, A에 가오스 조던 소거법을 취하면 단위행렬 형태가 되고, A의 역행렬은 위에서 적용한 “기본행렬 연산의 여러번 수행”이 역행렬 그 자체이다.
  • 20강 분할행렬과 역행렬 알고리즘

    • n차 정사각행렬 A에 대해서

    • 위의 명제의 역도 성립한다. A의 (가오스 조던 소거법을 적용해 구한) 기약행렬사다리꼴이 I라면, A는 가역행렬이다.

    • 이때 나오는 명제는 이것이다. A가 역행렬이 존재하면, A는 기본행렬들의 곱으로 표현가능하다.

    • 따라서 삼각형으로 연결된 3개의 명제는 서로서로 필요충분조건이다. 서로 동치(사실상 같은 말)이다.

    • image-20201115194429022

    • 행동치(row equivalent)를 이용한 A의 역행렬 구하기

      • Ek x … x E2 x E1 (기본 행 연산들) x A = B 에서 A와 B는 행동치이다.

        • = A에서 기본행 연산을 몇개 수행하면 B가 나온다
        • = B에서 기본행렬산을 몇개 수행하면 A가 나온다.
      • A와 I는 행동치이다.

        • = A에서 기본행 연산의 곱으로 표현가능하다.
        • = A는 가역이다.
        • 결론 : A와 I가 행동치이면, A를 항등행렬로 바꿔주는 “기본행 연산의 행렬들을” 그대로 항등행렬에 곱해주면, A의 역행렬이 나온다
        • A = Ek x … x E2 x E1 (기본 행 연산들) 일 때,
          • Ek x … x E2 x E1 (기본 행 연산들) x inverse(A) = I
          • I x inverse(A) = inverse( Ek x … x E2 x E1 (기본 행 연산들) ) x I 이므로, 아래와 같이 하면 된다.
        • 블록행렬(분할행렬) = A의 역행렬 구하기
          1. Ek x … x E2 x E1 (기본 행 연산들) = 가오스 조던 소거법을 위한 기본행 연산들
          2. 이 연산들을 I에다가도 적용해 주기
          3. 따로따로 계산하지 말고 블록으로 묶어서 한번에 연산하기
          4. image-20201120124739888

연립선형방정식과 행렬의 관계

  • 21강 연립선형방정식과 행렬의 관계1
    • A x = B (nxn) (nx1) (nx1)
    • consistent : 해가 적어도 한 개가 있는 경우
    • inconsistent : 해가 전혀 없는 경우
    • homogeneous : 동차 연립선형방정식 (B=0인 경우. A에 역행렬이 존재하면 자명해 만을 가진다.)
  • 22강 연립선형방정식과 행렬의 관계2
    • 선형함수/선형사상 - ‘선형적인 곱과 덧셈으로만 이루어진 식’ 말고 수학적 정의
      1. 가산성 f(x+y) = f(x) + f(y)
      2. 동차성 a * f(x) = a * f(x)
    • 행렬연산: A(x+y) = Ax+Ay , A(ax) = aAx -> 선형 사상이다!
    • 연립성형방정식과 역행렬과의 관계
      • A x = B (nxn) (nx1) (nx1)
      • A가 가역이면(b!=0), 방정식의 해는 존재하고 유일하다. (유일해를 가진다)
        • Y는 x의 유일해.
        • augmented matrix(A:b) –가오스조던소거법–> (I:Y)
        • (위에 참조) A와 I는 행동치이다 = A는 가역행렬이다.
    • AB가 가역행렬이면 A와 B도 각각 가역행렬이다. (증명은 22강 참조)
      • 내가 만든 명제 : AB가 가역행렬이면, AB를 기본행연산들의 곱으로 표현가능하다. 그 기본행 연산들을 나눠서 한쪽을 A, 다른 한쪽을 B라고 할 수 있을 것이다. A와 B 또한 기본행 연산들의 곱이므로, 가역행렬이다.

【기본수학】자연상수 e, 오일러 공식, 복소수의 곱셈과 덧셈, 테일러 급수, 퓨리에 급수(삼각합수)

수학의 기본적인 내용인 자연상수 e, 오일러 공식, 복소수의 곱셈과 덧셈, 테일러 급수, 퓨리에 급수(삼각합수) 와 같은 내용을 찾아서 공부한 내용들을 기록해 놓았습니다.

  • 공부한 내용: 자연상수 e, 오일러 공식, 복소수의 곱셈과 덧셈, 테일러 급수, 퓨리에 급수(삼각합수)
  • 이용한 테블릿 : 갤럭시 탭 s7+
  • 느낀점 : 고등수학은 잘했지만, 대학수학은 관심없었다. 하지만 생각보다 꿀잼이다. 근데 이거를 딥러닝에 쓸지는 모르겠지만 그래도 언젠간을 위해 알아두자.
  • 참조 좋은 사이트

1. 자연상수 e

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P1.png

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P4.png

2. 오일러 공식

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P2.png

3. 복소수의 곱셈과 덧셈

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P3.png

4. 테일러 급수

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P5.png

5. 퓨리에 급수(삼각합수)

공돌이의 수학 정리노트 & 쑤튜브 선형대수 P6.png

다음 공부

  • 복소 평면에서의 퓨리에 급수에 대해서 공부할 예정이다.

【기계학습】 기계학습 핵심정리 chap1, chap2, chap3, chap4

기계학습 책을 공부하고, 핵심만 기록해 놓는다. 구체적인 그림 및 설명은 최소화 하고, 꼭 필요한 설명은 1줄 이내로 적는다. 마음가짐 : ‘책 100번 볼거다. 또 보면 된다. 핵심만 정리해라’

기계학습 책 핵심 정리 chapter 1,2,3,4

Chapter 01 - 소개

  • 1.1절 - 기계학습이란

  • 1.2절 - feature(input) demention의 가시화 생각해보기

  • 1.3절 - 특징 공간 변환 공식 찾기 = 신경망 학습 알고리즘

  • 1.4절 - 기계 학습 알고리즘 - Θ = argmin_Θ loss(Θ)

  • 1.5절 - 모델선택
    1. 1d -> 1d로 이해하는 underfitting과 overfitting
    2. bias와 variance
      • Train Data set의 변화에 따른 모델들 간의 관계
    3. validation set과 cross-validation
      • validation 알고리즘 흐름 참조(50p 알고리즘1-4)
  • 1.6절 - 규제
    1. data augmentation
    2. weight decay
      • Loss = L1(x,y) + wights
  • 1.7절 - 기계 학습 유형
    1. 지도, 비지도, 준비도, 강화
    2. oneline/offline
      deterministic/stochastic
      discriminative/generative
  • 1.8절 - 기계학습의 과거, 현재, 미래
    • 다층퍼셉트론 -> SVM -> Deep learning
    • 쫄지말자. 복잡도만 다를 뿐 기본 원리는 같다.

Chapter 02 - 수학

2.1절 - 선형대수

  • Norm, 단위백터, Cosine similarity(코사인 유사도=단위백터화 후 내적)
  • 퍼셉트론과 행렬곱 Output(cx1) = Weight(cxd) x Input(dx1)
  • 행렬에서 행백터들(혹은 열백터들)이 선형 결합, 선형독립과 선형종속
  • mxn행렬에 대해 rank가 d(<= n)이면 행백터들에 대해, d차원 백터공간을 표현할 수 있다.
  • mxn행렬에 대해 최대 rank는 n(=행백터의 차원) 이다.
  • 역행렬이 없는 행렬 = singular matrix
  • 역행렬을 가진다 = 최대 계수 = 모든 열 선형 독립 = 고윳값은 모두 0이 아니다.
  • 모든 행렬은 (1)양의 정부호, (2)양의 준 정부호, (3)음의 정부호, (4)음의 준 정부호, (5)부정부호 행렬 5가지로 분류 할 수 있다. 분류 방법은 88p
  • 이 부호를 가지는 행렬은 각각 고윳값이 (1)모두 양수, (2)0이거나 양수, (3)음수, (4)0이거나 음수, (5)양수와 음수, 이다.
  • 고윳값과 고유백터, 그리고 고유윳값 분해(약수분해, 인수분해 처럼! 정사각행렬일때), 특잇값 분해(정사각행렬이 아니어도)
  • 모든 고유 백터는 orthogonal(직교)한다.

2.2절 - 확률과 통계

  • 확률분포, 합과 곱의 법칙, 조건부 확률, 두 사건의 독립
  • 베이즈 정리
    • 하얀 공이 나왔다는 사실만 알고 어느 병(a,b,c)에서 나왔는지 모르는데, 어느 병인지 추정해라.
    • 사후 확률(사건이 벌어진 후 확률) = P( Label | 데이터,Input Feature ) = 알기 어려움
  • 우도(likelihood) = P( 알고있음,데이터 | 추정해야함,모델/Label ) = 그나마 쉬움.
  • 최대 우도법(Maximum likelihood) = argmaxP( 데이터x | 모델 )
    데이터 x가 주어졌을 때, x를 발생시켰을 가능성을 최대로 하는 매개변수(모델)
  • 최대 로그 우도 추정
  • 평균과 분산 공식, d차원 feature에 대한, d개의 평균, dxd 차원 공분산 행렬(105p)
  • 가우시안 분포(=정규 분포), 베르누이 분포(1번 실험) -> 이항분포(m번 실험)
  • 정보이른 : 메세지가 가진 정보량을 수량화하여 ‘수’로 나타낼 수 있을까?
    발생활 확률이 적은 사건일 수록, 정보량이 크다!
  • 엔트로피 : 한(P) 확률분포의 무질서도, 불확실성 측정 = 클 수록 예측하기 어렵다.(윷놀이«주사위)
    • Cross 엔트로피 : 두(P,Q) 확률분포의 무질서도. -> 아래와 같이 분리가능 (교환법칙 성립)
    • P와 Q의 Cross 엔트로피 = P의 엔트로피(얼마나 예측하기 힘든지) + P와 Q사이의 KL diversence
    • KL diversence : 두 확률분포가 얼마나 다른지? 다를 수록 큰 수 (교환법칙 성립 안함)
    • cross 엔트로피 장점은 (Mean Squared제곱 Error 에서 처럼) 더 큰 오류에 더 낮은 벌점 주는 경우가 발생하지 않음(5장 추가 참조)

2.3절 - 최적화 이론

  • global optimal solution(전역 최적해)/ local optimal solution(지역 최적해)
  • 미분/ 편미분/ 독립변수(가중치)와 종속변수(Loss)/ 연쇄법칙
  • 아코비안(각 가중치에 대한 편미분 행렬)과 헤시안(각 가중치에 대한 2차 편미분 행렬)
  • 테일러 급수 - 함수는 모르고, 함수값과 미분값을 알 때.
  • BGD(batch gradient decent) : 전체 데이터에 대한(1 epoch 전체) ‘가중치의 편미분 값’을 평균하여 가중치 갱신
  • SGD(stochastic gradient decent) : 데이터 하나(1장) or 배치 묶음(1 epoch 전체 X)에 대해서 ‘가중치의 편미분 값’을 가지고 가중치 갱신 반복

chap3 - multi perceptron

  • 여기 내용은 차라리 밑바닥 시리즈로 공부하는게 낫겠다.
  • 3.1절 - 신경망 기초
    1. deterministic/stochastic 신경망 : 계산식에서 난수를 사용하나 안하나? 출력이 항상 같나 다르나?
  • 3.2절 - 퍼셉트론

  • 3.3절 - 다층 퍼셉트론
    1. 활성화 함수
      • 계단함수 - 1층 퍼셉트론
      • 로지스틱 시그모이드, 하이퍼볼릭 탄젠트 - Gradient Vanishing 문제
      • 소프트플러스, Relu
  • 3.4절 - 오류 역전파 알고리즘

  • 3.5절 - 미니배치 SGD
    1. 데이터 집합에서, t개의 샘플을 무작위로 뽑아 미니배치를 구성. 미니배치에 속한 샘플로 gredient 평균 후 가중치 갱신
  • 3.6절 - 다층 퍼셉트론에 의한 인식
  • 3.7절 - 다층 퍼셉트론의 특성
    1. 휴리스틱의 중요성
      • 아키텍처, 초깃값, 학습률

chap4 - Deep Learning

  • 넘어간 부분은, 궁금하면 나중에 다시 공부하자. 지금은 아니다.
  • 4.1절 - 딥러닝의 등장
  • 4.2절 - 깊은 다층 퍼셉트론
  • 4.3절 - 컨볼루션 신경망

  • 4.4절 - 컨볼류션 신경망 사례연구
  • 4.5절 - 생성 모델
  • 4.6절 - 딥러닝은 왜 강력한가? 1.

【Algorithm】[프머] DP - N으로 표현

1. 첫번째 코드 - DFS

알고리즘 프로그래머스 P2

  • first.py의 문제점 (44점)
    1. DFS 는 복잡하다. BFS 사용하자 -> 해결 NO
    2. 33 + 33 는 해결하지 못한다 -> 해결 YES ```python

      DFS 는 복잡하다. BFS 사용하자

      def solution(N, number): array = [10] * 32001 mark = [0] * 32001 temp = [1,11,111,1111,11111]

    def dynamic(checkN, count, i): if not(0<checkN <32001) : return if count > 8 : return # if mark[checkN] == i: # return if count > array[number]: return

      array[checkN] = count
      mark[checkN] = i
    
      if checkN == number:
          print(array[number], mark[checkN])
          return
      else:
          dynamic(checkN+N, count+1, i)
          dynamic(checkN-N, count+1, i)
          dynamic(checkN*N, count+1, i)
          dynamic(checkN//N, count+1, i)
    

    l = 5 if N < 3 else 4 for i in range(1,l): start = temp[i-1] * N dynamic(start, i, i)

    answer = array[number] if array[number] < 9 else -1 return answer

if name == “main”: print(solution(5,12))


# 2. 두번쨰 코드 - DFS
![image](https://user-images.githubusercontent.com/46951365/95305692-4be5e500-08c1-11eb-9629-ab6c6cc50af4.png)
- second.py의 문제점 (77점)
    1. 8을 return 해도 된는가               -> 7까지만 return 하도록 하면, 점수 떨어짐. 즉 8 return해야 함.
    2. 5, 26 = 5*5 + 5/5 로써 4가 답으로 나와야 함. But 나의 코드는 5가 나옴. ) "((5*5)+5)/5)"식으로 연산이 이뤄지기 떄문이다.
```python
def solution(N, number):
    array = [10] * 32001
    esses = [N*i for i in [1,11,111,1111,11111] if N*i < 32001]  # good
    print(esses)
    
    def dynamic(checkN, count):
        if not(0<checkN <32001) :
            return
        if count > 8 :  # 8도 return 가능
            return
        if count > array[number]:
            return
    
        array[checkN] = count

        if checkN == number:
            return
        else:
            for j, nu in enumerate(esses):
                dynamic(checkN+nu, count+j+1)
                dynamic(checkN-nu, count+j+1)
                dynamic(checkN*nu, count+j+1)
                dynamic(checkN//nu, count+j+1)

    for i, num in enumerate(esses):     # good
        dynamic(num, i+1)
    
    answer = array[number] if array[number]  < 9 else -1    # 8도 return 가능
    return answer


if __name__ == "__main__":
    print(solution(5,26))

3. 세번쨰 코드 - Danamic programming

  • 정답 참조 링크는 게시물 맨 위에 있음.
  • Danamic programming 문제를 풀기 위해서는 재귀식을 세우는게 가장 중요하다.
  • 핵심 재귀식은 다음과 같다.
      N을 n번 사용해서 만들 수 있는 수 :
          N을 n번 연달아서 사용할 수 있는 수 U(합집합)
          N을 1번 사용했을 때 SET 과 n-1번 사용했을 때 SET을 사칙연산한 수들의 집합 U
          N을 2번 사용했을 때 SET 과 n-2번 사용했을 때 SET을 사칙연산한 수들의 집합 U
          ... U
          N을 n-1번 사용했을 때 SET 과 1번 사용했을 때 SET을 사칙연산한 수들의 집합
    
  • 나의 손코딩

  • 나의 코드
def solution(N, number):
    S = [0]
    for i in range(1,9):
        S.append({int(str(N)*i)})
    for i in range(1,9):
        for j in range(1,i):
            print(i,j,i-j)
            # set은 순서가 없기 때문에 list로 바꿔서 for해야하는 줄 알았는데 
            # 이런 방법으로 set내부의 모든 원소에 쉽게 접근할 수 있다. dict도 마찬가지 이다. 
            for num1 in S[j]: 
                for num2 in S[i-j]:
                    S[i].add(num1+num2)
                    S[i].add(num1-num2)
                    S[i].add(num1*num2)
                    if num2 != 0:
                        S[i].add(num1//num2)
        if number in S[i]:
            return i
    return -1



if __name__ == "__main__":
    print(solution(5,))

"""
위의 print로 나오는 수
2 1 1
3 1 2
3 2 1
4 1 3
4 2 2
4 3 1
5 1 4
5 2 3
5 3 2
5 4 1
6 1 5
6 2 4
6 3 3
6 4 2
6 5 1
7 1 6
7 2 5
7 3 4
7 4 3
7 5 2
7 6 1
8 1 7
8 2 6
8 3 5
8 4 4
8 5 3
8 6 2
8 7 1
"""

4. 참고하면 좋은 정답 코드

  • 참조 사이트는 맨 위에 기제.
  • 참조 사이트의 손 코딩도 읽으면 매우 좋으니 참조 할 것.
      def solution(N, number):
          # 허뎝님의 수정 피드백 -> 테스트 케이스가 바뀌면서 예외 사항을 추가해야 함.
          if N == number:
              return 1
                
          # 1. [ SET x 8 ] 초기화
          s = [ set() for x in range(8) ] 
          # 2. 각 set마다 기본 수 "N" * i 수 초기화
          for i,x in enumerate(s, start=1):
              x.add( int( str(N) * i ) )
          # 3. n 일반화
          #   { 
          #       "n" * i U 
          #       1번 set 사칙연산 n-1번 set U
          #       2번 set 사칙연산 n-2번 set U
          #       ...
          #       n-1번 set 사칙연산 1번 set, 
          #    } 
          # number를 가장 최소로 만드는 수 구함.
          for i in range(1, 8):
              for j in range(i):
                  for op1 in s[j]:
                      for op2 in s[i-j-1]:
                          s[i].add(op1 + op2)
                          s[i].add(op1 - op2)
                          s[i].add(op1 * op2)
                          if op2 != 0:
                              s[i].add(op1 // op2)
              if  number in s[i]:
                  answer = i + 1
                  break
          else:
              answer = -1
          return answer
    

【Algorithm】[leetcode] linked-list - swap-nodes-in-pairs

문제 : https://leetcode.com/problems/swap-nodes-in-pairs/

이해 이미지

  • PS - 리스트를 전체로 바라보지 말아라. 결국 하나하나의 객체일 뿐이다.
  • 항상 1. 자리 바꾸기 2. 연결 바꾸기
  • note원본 P1

나의 코드

# https://leetcode.com/problems/swap-nodes-in-pairs/


class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(4)

print(head.val, head.next.val, head.next.next.val, head.next.next.next.val, head.next.next.next.next)


def swapPairs(head):
    """
    :type head: ListNode
    :rtype: ListNode
    """
    if not head or not head.next :
        return head
    
    first, second, third = head, head.next, head.next.next
    head = second
    head.next = first
    print(head.val, head.next.val)
    third = swapPairs(third)
    first.next = third
    return head

head = swapPairs(head)
print(head.val, head.next.val, head.next.next.val, head.next.next.next.val, head.next.next.next.next)

최적회 상대 코드

def swapPairs(self, head):

    if not head or not head.next:
        return head
  
    first,second = head, head.next
    
    # 자리 바꾸기 먼저
    third = second.next # 1
    head = second       # 2

    # next 연결 바꿔주기
    second.next = first # 3
    first.next = self.swapPairs(third) # 4
    
    return head

【GPU_Server】 Google Cloud 플랫폼 - VScode SSH, SSH 란?

VScode를 SSH를 사용하여 remote연결하기

VScode를 SSH를 사용하여 remote연결하기

PS

onedrive의 파일 wget으로 가져오기
stackoverflow 답변 링크

파일에 오른쪽 클릭하면 '임베드' 찾을 수 있음

<iframe src="https://onedrive.live.com/embed?cid=FBB2D8FB591497AB&resid=FBB2D8FB591497AB%2155221&authkey=AL6ELFsqIIuTp_s" width="98" height="120" frameborder="0" scrolling="no"></iframe>


https://onedrive.live.com/download?cid=FBB2D8FB591497AB&resid=FBB2D8FB591497AB%2155221&authkey=AL6ELFsqIIuTp_s

wget --no-check-certificate "https://onedrive.live.com/download?cid=FBB2D8FB591497AB&resid=FBB2D8FB591497AB%2155221&authkey=AL6ELFsqIIuTp_s"

0. Reference

  1. 참고 동영상 https://www.youtube.com/watch?v=7kum46SFIaY
  2. 참고 이전 포스트 : GCP의 instance에 Putty gen해서 생성한 key를 넣어주고 Putty로 SSH열기
  3. 참고 동영상 인프런 SSH
  4. 1번 reference로 안돼서 이 블로그 글 이용
    이 블로그 글에 대한 내용을 pdf로 저장해 두었다. 저장 공간은 다음과 같으니 나중에 필요하면 사용하기. 다운 링크

  5. 혹시 모르니 나중에 이것도 참조 https://evols-atirev.tistory.com/28

1. 설치

  1. SSH Gen 생성해서 vm instance에 집어넣어주기

    image

  2. VScode에 remote - SHH 설치하기

    image

  3. SSH target 설정하기

    image

  4. SSH + 하기

    image

  5. config 저장 공간 설정

    image

    맨 위에 있는 것을 선택헤서, SSH를 연결할 때마다 config가 삭제되지 않게 막아준다.

  6. Open 끝

    image

    image

2. 장점

  1. 인터넷 속도 개 빠르다. 파이썬 패키지 다운로드 속도 미쳤다.
  2. CPU도 개좋다. 나의 컴퓨터 CPU를 전혀 사용하지 않는다.
  3. 내 컴퓨터 메모리를 전혀 사용하지 않는다. WSL로 접속하면 Vmmeo로 컴퓨터 랩 1G를 잡아먹어서 짜증났는데, GCP를 사용하니 그런 일 없다.
  4. 파일 보기 및 terminal 다루기가 편하다.
  5. Jupyter Notebook보다 더 많은 작업을 쉽게 할 수 있다.
  6. 파일을 그냥 끌어와서 업로드, 다운로드 할 수 있다. 개미쳤다.

3. 단점

  1. 크레딧 다쓰면 진짜 내 돈나간다.
  2. 다운로드 했던 많은 파일들 설정들이, Instacne 문제가 발생하면 한꺼번에 날아간다.

4. 추신

  1. 앞으로 WSL에 들어가서 굳이 작업하지 말자. WSL에서 작업할 이유가 전혀 없다.
  2. WSL을 전체 삭제해도 지금 될 것 같다. 하지만 일단 놔두자. 추가 환경설정 ~/.zshrc 파일이 나중에 도움이 될지도 모른다
  3. zsh 설정한 것은 매우 좋지만, 굳이 더 이상 쓸 필요 없다. 만약 필요하면 다른 우분투 서버 환경을 zsh 설치해서 shell사용하면 된다.

5. SSH 란?

【Algorithm】 [프머] 코딩테스트연습/ 동적계획/ 등굣길

등교길 문제 링크

동적 계획법 문제로 매우 쉬운 문제지만,

문제 잘못 읽어서, 오류 찾느라 시간을 많이 썼다…

puddles 가 당연히 [행][열] 의 index로 주어지는 줄 알았는데, 아니었다.

1. 정답 코드

def solution(m, n, puddles):
    # 동적 메모리 미리 만들기
    path_num = [[0 for i in range(m+1)] for j in range(n+1)]
    path_num[0][0] = 1

    # puddle 계산하기 쉽게
    for temp in puddles:
        temp[0] = temp[0] - 1
        temp[1] = temp[1] - 1

    # 맨 윗라인
    for j in range(1, m):
        if [j, 0] not in puddles:
            path_num[0][j] = path_num[0][j-1]

    for i in range(1, n):
        if [0, i] not in puddles:
            path_num[i][0] = path_num[i-1][0]

    # 나머지 아래 라인들
    for i in range(1, n):
        for k in range(1, m):
            if [k, i] in puddles:  # 순서 조심,  m, n과 물이 잠긴 지역의 좌표를 담은 2차원 배열 puddles
                path_num[i][k] = 0
            else:    
                path_num[i][k] = path_num[i-1][k] + path_num[i][k-1]

    return path_num[n-1][m-1] % 1000000007

2. 참고하면 좋은 코드

  • 나의 코드와 흐름은 똑같다.
    def solution(m, n, puddles):
      # 문제에서는 1부터 시작하므로, 
      # 문제의 값과 일치할 수 있게 가로 m+1, 세로 n+1로 maps을 만들어준다.
      maps = [[0] * (m + 1) for _ in range(n+1)]
        
      # 시작점
      maps[1][1] = 1
      for i in range(1, n + 1):
          for j in range(1, m + 1):
              if i == 1 and j == 1:
                  continue
              if [j,i] in puddles:
                  maps[i][j] = 0
              else:
                  maps[i][j] = (maps[i-1][j] + maps[i][j-1])
      return (maps[-1][-1]) % 1000000007
    

【Keras】Keras기반 Mask-RCNN - Kaggle Necleus 데이터셋

Keras 기반 Mask-RCNN를 이용해 Kaggle Necleus 데이터셋을 학습시키고 추론해보자.

【Keras】Keras기반 Mask-RCNN - Kaggle Necleus 데이터셋

1. python 핵심 정리 모음(새롭게 안 사실)

python 새롭게 안 사실 및 핵심 내용들

  1. lines tab 처리하는 방법
    • ctrl + [
    • ctrl + ]
    • line drag + tab
    • line drag + shift + tab
  2. os package 유용한 함수들
    • os.listdir : 폴더 내부 파일이름들 list로 따옴
    • os.walk : sub directory를 iteration으로 반환 (검색해서 아래 활용 예시 보기)
    • os.path.dirname : 경로 중 이전 dir에 대한 정보까지만 얻기
  3. image_ids = list(set(image_ids) - set(val_indexs))
    • set과 list를 동시에 이용한다. 교집합을 제거하고, 남은 내용들만 list로 반환
  4. 문자열함수 endwith(문자열) : 문자열에 해당 문자가 있으면 True, 없으면 False
  5. skimage.io.imread(“bool 형식의 mask data 파일 이름”).astype(np.bool)
    • mask 데이터 뽑아 오는 하나의 방법
  6. masks = np.stack(mask_list, axis=-1)
    • 한장한장의 mask 데이터를 concatenation하는 방법
    • 공식 dacument
    • 만약 내가 묶고 싶은 list의 원소가 n개 일 때, axis가 명시하는 shape의 index가 n이 된다.
    • 2개의 list(1*3)를 합치고 axis가 -1이라면, 최종 result는 3x2가 된다.

2. Keras기반 Mask-RCNN - Kaggle Necleus 데이터셋 정의하고 학습하기

  • /DLCV/Segmentation/mask_rcnn/Kaggle_Nucleus_Segmentation_Challenge.ipynb 참고하기
  • 전체 흐름도 정리 : 이전 포스트 자료

1. Kaggle API로 데이터 다운로드 및 데이터 탐방

  • 이전에 배웠던 winSCP, 서버의 directory 구조 그대로 보고, 데이터를 주고 받을 수 있는 프로그램, 을 사용해도 좋다. 하지만 여기서는 kaggle API를 이용해서 data받는게 빠르고 편하다.
  • kaggle API 설치 및 데이터 다운로드 사용법 사이트
import os
import sys
import random
import math
import re
import time
import numpy as np
import tensorflow as tf
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as patches
%matplotlib inline 
from mrcnn.config import Config
from mrcnn import utils
from mrcnn import visualize
from mrcnn.visualize import display_images
import mrcnn.model as modellib
from mrcnn.model import log
## Kaggle에서 2018 Data science bowl Nucleus segmentation 데이터를 download 한 뒤 ./nucleus_data 디렉토리에 압축을 품
# stage1_train.zip 파일에 train용 image 데이터와 mask 데이터가 있음. stage1_train_zip 파일을 stage1_train 디렉토리에 압축 해제
# unzip stage1_train.zip -d stage1_train
# stage1_test.zip 파일에 test용 image 데이터와 mask 데이터가 있음. stage1_test_zip 파일을 stage1_test 디렉토리에 압축 해제
# unzip stage1_test.zip -d stage1_test
  • train용 데이터 세트에 들어있는 데이터 구조 확인
import os
from pathlib import Path

HOME_DIR = str(Path.home())

# 학습용, 테스트용 모두의 기준 디렉토리는 ~/DLCV/data/nucleus 임. 
DATASET_DIR = os.path.join(HOME_DIR, "DLCV/data/nucleus")
# print(DATASET_DIR)
# ~/DLCV/data/nucleus 디렉토리 밑에 학습용 디렉토리인 stage1_train이 만들어짐.stage1_train에 학습 이미지, mask 이미지 데이터 존재. 
subset_dir = 'stage1_train'
train_dataset_dir = os.path.join(DATASET_DIR, subset_dir)
# print(train_dataset_dir)
  • train 데이터 세트의 이미지 파일, mask 파일이 어떠한 디렉토리 구조 형태로 저장되어 있는지 확인.
  • /DLCV/data/nucleus/stage1_train/9a71a416f98971aa14f63ef91242654cc9191a1414ce8bbd38066fe94559aa4f$ ls
    >> images masks
  • 아래 코드 주의 os.walk 사용하는 중요한 코드!!
# 이미지 별로 고유한 이미지명을 가지는 이미지 디렉토리를 가지고 이 디렉토리에 하위 디렉토리로 images, masks를 가짐
# images 에는 하나의 이미지가 있으며 masks는 여러개의 mask 이미지 파일을 가지고 있음. 즉 하나의 이미지에 여러개의 mask 파일을 가지고 있는 형태임. 
# next(os.walk(directory))[1]은 sub directory를 iteration으로 반환 next(os.walk(directory))[2]는 해당 디렉토리 밑에 파일들을 iteration으로 반환
# 즉 [1]은 directory를 반환하고
#    [2]는 파일을 반환한다. 
index = 0 
for dir in next(os.walk(train_dataset_dir))[1]:
    print('┌',dir)
    subdirs = os.path.join(train_dataset_dir, dir)
    for subdir in next(os.walk(subdirs))[1]:
        print('└┬─'+subdir)
        sub_subdirs = os.path.join(subdirs, subdir)
        for sub_subdir in next(os.walk(sub_subdirs))[2]:
            print(' └── '+sub_subdir) # sub_subdir 에서는 png를 가져온 것을 확인할 수 있다.
            index += 1
            if index >1000:
                break

image

  • 하나의 dir에는 ‘하나의 이미지(images)’와 ‘그 이미지에 대한 새포핵 하나하나에 대한 mask 흑백이미지들’이 저장되어 있다.

2. Dataset 객체와 Data load 메소드 작성

  • 학습시 사용할 임의의 Validation용 IMAGE를 무엇으로 사용할지 랜덤 설정
def get_valid_image_ids(dataset_dir, valid_size):
    np.random.seed(0)
    dataset_dir = os.path.join(dataset_dir,'stage1_train')
    image_ids = next(os.walk(dataset_dir))[1] # stage1_train 딱 내부의 폴더들에 대한 iteration 가져옴. [1]은 '폴더'를 의미. 
    total_cnt = len(image_ids) # stage1_train 딱 내부의 폴더들의 갯수를 가져옴
    
    valid_cnt = int(total_cnt * valid_size) # 0 < valid_size < 1 을 명심하기
    valid_indexes = np.random.choice(total_cnt, valid_cnt, replace=False)
    
    return total_cnt, list(np.array(image_ids)[valid_indexes])  # stage1_train 내부 이미지 중, valid에 사용할 폴더의 index(몇번째 폴더)만 가져온다. 
total_cnt, val_indexs = get_valid_image_ids(DATASET_DIR, 0.1) 
print(total_cnt, len(val_indexs))
val_indexs[0:5]
670 67

['d7fc0d0a7339211f2433829c6553b762e2b9ef82cfe218d58ecae6643fa8e9c7',
 '6ab24e7e1f6c9fdd371c5edae1bbb20abeeb976811f8ab2375880b4483860f4d',
 '40b00d701695d8ea5d59f95ac39e18004040c96d17fbc1a539317c674eca084b',
 '1ec74a26e772966df764e063f1391109a60d803cff9d15680093641ed691bf72',
 '431b9b0c520a28375b5a0c18d0a5039dd62cbca7c4a0bcc25af3b763d4a81bec']
  • utils.Dataset 객체의 add_class(), add_image()를 이용하여, 개별 이미지를 Dataset 객체로 로딩하는 load_nucleus() 함수 정의하기.
from mrcnn import utils
import skimage
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline

_, val_indexs = get_valid_image_ids(DATASET_DIR, 0.1)

class NucleusDataset(utils.Dataset):
    
    # subset은 train, valid, stage1_test, stage2_test
    def load_nucleus(self, dataset_dir, subset):
        self.add_class(source='nucleus', class_id=1, class_name='nucleus')  
        # 이미 class로 등록되어 있으면, 지금 데이터셋의 class로 새로 넣지 않음
        # 새로운 데이터셋이라는 것을 명칭해주는 것이 source이다. "우리는 nucleus 라는 데이터셋을 추가로 등록할 것이다."를 의미함 
        
        subset_dir = 'stage1_train' if subset in ['train', 'val'] else subset
        dataset_dir = os.path.join(dataset_dir, subset_dir)
        
        if subset=='val':
            image_ids = val_indexs
        else:
            image_ids = next(os.walk(dataset_dir))[1]  # 폴더들 이름을 전체 list로 저장
            if subset=='train':
                image_ids = list(set(image_ids) - set(val_indexs)) # 

        for image_id in image_ids: # image_id라는 폴더 내부에 images_<image_id>.png 라는 이미지가 있다고 전제
            self.add_image('nucleus', image_id=image_id, path=os.path.join(dataset_dir, image_id, 'images/{}.png'.format(image_id)))   
    
    def load_mask(self, image_id):      ## 오버라이딩으로 정의해주지 않으면 오류나는 함수!  image_id는 숫자
        info = self.image_info[image_id]
        mask_dir = os.path.join(os.path.dirname(os.path.dirname(info['path'])), 'masks')
        mask_list=[]
        for mask_file in next(os.walk(mask_dir))[2]: # mask_dir에 있는 "파일" 이름들을 list로 가지고 있음.
            if mask_file.endswith('.png'):
                mask = skimage.io.imread(os.path.join(mask_dir, mask_file)).astype(np.bool)
                mask_list.append(mask)

                # test_mask = cv2.imread(os.path.join(mask_dir, mask_file))
                # print(type(test_mask), test_mask.shape) -> 하나의 파일은 <class 'numpy.ndarray'> (256, 320, 3)
                
        masks = np.stack(mask_list, axis=-1) 
        
        return masks, np.ones([masks.shape[-1]], dtype=np.int32)
nucleus_dataset = NucleusDataset(utils.Dataset)
nucleus_dataset.load_nucleus(DATASET_DIR, 'train')
#print('class info:', nucleus_dataset.class_info)
#print('image info:', nucleus_dataset.image_info)
nucleus_dataset.prepare()
print('class id:', nucleus_dataset.class_ids)
print('image id:', nucleus_dataset._image_ids)
class id: [0 1]
image id: [  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17
  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35
  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53
  54  55  56  57  58  59  60  ...  586 587 588 589 590 591 592 593
 594 595 596 597 598 599 600 601 602]
nucleus_dataset.image_info[0]

{
‘id’: ‘9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb’,
‘source’: ‘nucleus’,
‘path’: ‘/home/sb020518/DLCV/data/nucleus/stage1_train/9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb/images9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb.png’
}

masks, class_ids = nucleus_dataset.load_mask(0)
masks.shape, class_ids.shape
((256, 256, 18), (18,))
masks, class_ids = nucleus_dataset.load_mask(0)

sample_img = skimage.io.imread("/home/sb020518/DLCV/data/nucleus/stage1_train/9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb/images/9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb.png")
print(type(sample_img), sample_img.shape)
plt.imshow(sample_img)
plt.show()

print(masks[0]) 
<class 'numpy.ndarray'> (256, 256, 4)

[[False False False ... False False False]
 [False False False ... False False False]
 [False False False ... False False False]
 ...
 [False False False ... False False False]
 [False False False ... False False False]
 [False False False ... False False False]]

drawing

img_from_cv = cv2.imread("/home/sb020518/DLCV/data/nucleus/stage1_train/9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb/images/9f073db4acd7e634fd578af50d4e77218742f63a4d423a99808d6fd7cb0d3cdb.png")
print(type(img_from_cv), img_from_cv.shape)
plt.imshow(img_from_cv)
plt.show()

<class 'numpy.ndarray'> (256, 256, 3)

drawing

np.random.seed(0)
image_ids = np.random.choice(nucleus_dataset.image_ids, 4) # 506개의 이미지 중에 4개를 선택한다. 
print(image_ids)
for image_id in image_ids:
    image = nucleus_dataset.load_image(image_id)
    mask, class_ids = nucleus_dataset.load_mask(image_id)
    print('mask shape:', mask.shape, 'class_ids shape:', class_ids.shape)
    visualize.display_top_masks(image, mask, class_ids, nucleus_dataset.class_names, limit=1)
[559 192 359   9]
mask shape: (256, 256, 6) class_ids shape: (6,)
mask shape: (360, 360, 24) class_ids shape: (24,)
mask shape: (256, 256, 16) class_ids shape: (16,)
mask shape: (256, 256, 4) class_ids shape: (4,)

drawing

3. nucleus 데이터 세트 정의 및 pretrain 가져오기

  • Matterport 패키지에서 사용될 수 있도록 학습/검증/테스트 데이터 세트에 대한 각각의 객체 생성 필요!
  • 학습 또는 Inference를 위한 Config 설정
  • 학습과정 정리
dataset_train = NucleusDataset()
dataset_train.load_nucleus(DATASET_DIR, 'train')  # 위에서 NucleusDataset의 맴버 함수로 설정했었음
# dataset을 load한 뒤에는 반드시 prepare()메소드를 호출
dataset_train.prepare()

# Validation dataset
dataset_val = NucleusDataset()
dataset_val.load_nucleus(DATASET_DIR, "val")
dataset_val.prepare()
len(dataset_train.image_info), len(dataset_val.image_info)
# (603, 67)
  • Nucleus 학습을 위한 새로운 Config 객체 생성
from mrcnn.config import Config

train_image_cnt = len(dataset_train.image_info)
val_image_cnt = len(dataset_val.image_info)
print('train_image_cnt:',train_image_cnt, 'validation image count:',val_image_cnt)

class NucleusConfig(Config):
    """Configuration for training on the nucleus segmentation dataset."""
    # Give the configuration a recognizable name
    NAME = "nucleus"

    # Adjust depending on your GPU memory
    IMAGES_PER_GPU = 1

    # Number of classes (including background)
    NUM_CLASSES = 1 + 1  # Background + nucleus

    # Number of training and validation steps per epoch
    STEPS_PER_EPOCH = (train_image_cnt) // IMAGES_PER_GPU
    VALIDATION_STEPS = max(1, (val_image_cnt // IMAGES_PER_GPU))

    # Don't exclude based on confidence. Since we have two classes
    # then 0.5 is the minimum anyway as it picks between nucleus and BG
    DETECTION_MIN_CONFIDENCE = 0

    # Backbone network architecture
    # Supported values are: resnet50, resnet101
    BACKBONE = "resnet50"

    # Input image resizing
    # Random crops of size 512x512
    IMAGE_RESIZE_MODE = "crop"
    IMAGE_MIN_DIM = 512
    IMAGE_MAX_DIM = 512
    IMAGE_MIN_SCALE = 2.0

    # Length of square anchor side in pixels
    RPN_ANCHOR_SCALES = (8, 16, 32, 64, 128)

    # ROIs kept after non-maximum supression (training and inference)
    POST_NMS_ROIS_TRAINING = 1000
    POST_NMS_ROIS_INFERENCE = 2000

    # Non-max suppression threshold to filter RPN proposals.
    # You can increase this during training to generate more propsals.
    RPN_NMS_THRESHOLD = 0.9

    # How many anchors per image to use for RPN training
    RPN_TRAIN_ANCHORS_PER_IMAGE = 64

    # Image mean (RGB)
    MEAN_PIXEL = np.array([43.53, 39.56, 48.22])

    # If enabled, resizes instance masks to a smaller size to reduce
    # memory load. Recommended when using high-resolution images.
    USE_MINI_MASK = True
    MINI_MASK_SHAPE = (56, 56)  # (height, width) of the mini-mask

    # Number of ROIs per image to feed to classifier/mask heads
    # The Mask RCNN paper uses 512 but often the RPN doesn't generate
    # enough positive proposals to fill this and keep a positive:negative
    # ratio of 1:3. You can increase the number of proposals by adjusting
    # the RPN NMS threshold.
    TRAIN_ROIS_PER_IMAGE = 128

    # Maximum number of ground truth instances to use in one image
    MAX_GT_INSTANCES = 200

    # Max number of final detections per image
    DETECTION_MAX_INSTANCES = 400
train_image_cnt: 603 validation image count: 67
  • coco pretrained된 가중치 모델 가져오기.
  • 과거에 아래와 같은 코드로 패키지에 있는 pretrained된 가중치를 가져왔다
      COCO_MODEL_URL = "https://github.com/matterport/Mask_RCNN/releases/download/v2.0/mask_rcnn_coco.h5"
      with urllib.request.urlopen(COCO_MODEL_URL) as resp, open(coco_model_path, 'wb') as out:
          shutil.copyfileobj(resp, out)
    
ROOT_DIR = os.path.abspath("./")
# Path to trained weights file
COCO_WEIGHTS_PATH = os.path.join(ROOT_DIR, "./pretrained/mask_rcnn_coco.h5")

# Directory to save logs and model checkpoints, if not provided
# through the command line argument --logs
MODEL_DIR = os.path.join(ROOT_DIR, "./snapshots/nucleus")

4.학습을 위한 model 객체 생성 및 model.train()

  • Mask_RCNN 패키지는 modellib에 MaskRCNN 객체를 이용하여 Mask RCNN 모델을 생성함.
  • 그렇게 생성한 모델을 이용해서, model.train() model.detect() 를 사용하면 된다.

  • 필수 주의 사항 및 생성 인자
    • mode: training인지 inference인지 설정
    • config: training 또는 inference에 따라 다른 config 객체 사용. Config객체를 상속 받아 각 경우에 새로운 객체를 만들고 이를 이용. inference 시에는 Image를 하나씩 입력 받아야 하므로 Batch size를 1로 만들 수 있도록 설정
    • model_dir: 학습 진행 중에 Weight 모델이 저장되는 장소 지정.
from mrcnn import model as modellib

train_config = NucleusConfig()
# train_config.display()

model = modellib.MaskRCNN(mode="training", config=train_config,
                                  model_dir=MODEL_DIR)

# Exclude the last layers because they require a matching
# number of classes
model.load_weights(COCO_WEIGHTS_PATH, by_name=True, exclude=["mrcnn_class_logits", "mrcnn_bbox_fc","mrcnn_bbox", "mrcnn_mask"])
  • 학습 데이터 세트와 검증 데이터 세트를 NucleusDataset 객체에 로드하고 train 시작
    • augmentation은 imgaug를 사용.
from imgaug import augmenters as iaa

img_aug = iaa.SomeOf((0, 2), [
        iaa.Fliplr(0.5),
        iaa.Flipud(0.5),
        iaa.OneOf([iaa.Affine(rotate=90),
                   iaa.Affine(rotate=180),
                   iaa.Affine(rotate=270)]),
        iaa.Multiply((0.8, 1.5)),
        iaa.GaussianBlur(sigma=(0.0, 5.0))
    ])
  • warning ignore를 안해도 학습은 되지만, imgagu와 충돌하는동안 계속 warning이 발생해서 ignore처리를 하였다. 이런 방법도 있구나 알아두자.
import warnings 
warnings.filterwarnings('ignore')

print("Train all layers")
model.train(dataset_train, dataset_val,
            learning_rate=train_config.LEARNING_RATE,
            epochs=40, augmentation=img_aug,
            layers='all')
Train all layers

Starting at epoch 0. LR=0.001

Checkpoint Path: /home/sb020518/DLCV/Segmentation/mask_rcnn/./snapshots/nucleus/nucleus20200928T1014/mask_rcnn_nucleus_{epoch:04d}.h5
Selecting layers to train
conv1                  (Conv2D)
bn_conv1               (BatchNorm)
res2a_branch2a         (Conv2D)
bn2a_branch2a          (BatchNorm)
res2a_branch2b         (Conv2D)
bn2a_branch2b          (BatchNorm)
res2a_branch2c         (Conv2D)
res2a_branch1          (Conv2D)
bn2a_branch2c          (BatchNorm)
bn2a_branch1           (BatchNorm)
res2b_branch2a         (Conv2D)
bn2b_branch2a          (BatchNorm)
res2b_branch2b         (Conv2D)
bn2b_branch2b          (BatchNorm)
res2b_branch2c         (Conv2D)
bn2b_branch2c          (BatchNorm)
res2c_branch2a         (Conv2D)
bn2c_branch2a          (BatchNorm)
res2c_branch2b         (Conv2D)
bn2c_branch2b          (BatchNorm)
res2c_branch2c         (Conv2D)
bn2c_branch2c          (BatchNorm)
res3a_branch2a         (Conv2D)
bn3a_branch2a          (BatchNorm)
res3a_branch2b         (Conv2D)
bn3a_branch2b          (BatchNorm)
res3a_branch2c         (Conv2D)
res3a_branch1          (Conv2D)
bn3a_branch2c          (BatchNorm)
bn3a_branch1           (BatchNorm)
res3b_branch2a         (Conv2D)
bn3b_branch2a          (BatchNorm)
res3b_branch2b         (Conv2D)
bn3b_branch2b          (BatchNorm)
res3b_branch2c         (Conv2D)
bn3b_branch2c          (BatchNorm)
res3c_branch2a         (Conv2D)
bn3c_branch2a          (BatchNorm)
res3c_branch2b         (Conv2D)
bn3c_branch2b          (BatchNorm)
res3c_branch2c         (Conv2D)
bn3c_branch2c          (BatchNorm)
res3d_branch2a         (Conv2D)
bn3d_branch2a          (BatchNorm)
res3d_branch2b         (Conv2D)
bn3d_branch2b          (BatchNorm)
res3d_branch2c         (Conv2D)
bn3d_branch2c          (BatchNorm)
res4a_branch2a         (Conv2D)
bn4a_branch2a          (BatchNorm)
res4a_branch2b         (Conv2D)
bn4a_branch2b          (BatchNorm)
res4a_branch2c         (Conv2D)
res4a_branch1          (Conv2D)
bn4a_branch2c          (BatchNorm)
bn4a_branch1           (BatchNorm)
res4b_branch2a         (Conv2D)
bn4b_branch2a          (BatchNorm)
res4b_branch2b         (Conv2D)
bn4b_branch2b          (BatchNorm)
res4b_branch2c         (Conv2D)
bn4b_branch2c          (BatchNorm)
res4c_branch2a         (Conv2D)
bn4c_branch2a          (BatchNorm)
res4c_branch2b         (Conv2D)
bn4c_branch2b          (BatchNorm)
res4c_branch2c         (Conv2D)
bn4c_branch2c          (BatchNorm)
res4d_branch2a         (Conv2D)
bn4d_branch2a          (BatchNorm)
res4d_branch2b         (Conv2D)
bn4d_branch2b          (BatchNorm)
res4d_branch2c         (Conv2D)
bn4d_branch2c          (BatchNorm)
res4e_branch2a         (Conv2D)
bn4e_branch2a          (BatchNorm)
res4e_branch2b         (Conv2D)
bn4e_branch2b          (BatchNorm)
res4e_branch2c         (Conv2D)
bn4e_branch2c          (BatchNorm)
res4f_branch2a         (Conv2D)
bn4f_branch2a          (BatchNorm)
res4f_branch2b         (Conv2D)
bn4f_branch2b          (BatchNorm)
res4f_branch2c         (Conv2D)
bn4f_branch2c          (BatchNorm)
res5a_branch2a         (Conv2D)
bn5a_branch2a          (BatchNorm)
res5a_branch2b         (Conv2D)
bn5a_branch2b          (BatchNorm)
res5a_branch2c         (Conv2D)
res5a_branch1          (Conv2D)
bn5a_branch2c          (BatchNorm)
bn5a_branch1           (BatchNorm)
res5b_branch2a         (Conv2D)
bn5b_branch2a          (BatchNorm)
res5b_branch2b         (Conv2D)
bn5b_branch2b          (BatchNorm)
res5b_branch2c         (Conv2D)
bn5b_branch2c          (BatchNorm)
res5c_branch2a         (Conv2D)
bn5c_branch2a          (BatchNorm)
res5c_branch2b         (Conv2D)
bn5c_branch2b          (BatchNorm)
res5c_branch2c         (Conv2D)
bn5c_branch2c          (BatchNorm)
fpn_c5p5               (Conv2D)
fpn_c4p4               (Conv2D)
fpn_c3p3               (Conv2D)
fpn_c2p2               (Conv2D)
fpn_p5                 (Conv2D)
fpn_p2                 (Conv2D)
fpn_p3                 (Conv2D)
fpn_p4                 (Conv2D)
In model:  rpn_model
    rpn_conv_shared        (Conv2D)
    rpn_class_raw          (Conv2D)
    rpn_bbox_pred          (Conv2D)
mrcnn_mask_conv1       (TimeDistributed)
mrcnn_mask_bn1         (TimeDistributed)
mrcnn_mask_conv2       (TimeDistributed)
mrcnn_mask_bn2         (TimeDistributed)
mrcnn_class_conv1      (TimeDistributed)
mrcnn_class_bn1        (TimeDistributed)
mrcnn_mask_conv3       (TimeDistributed)
mrcnn_mask_bn3         (TimeDistributed)
mrcnn_class_conv2      (TimeDistributed)
mrcnn_class_bn2        (TimeDistributed)
mrcnn_mask_conv4       (TimeDistributed)
mrcnn_mask_bn4         (TimeDistributed)
mrcnn_bbox_fc          (TimeDistributed)
mrcnn_mask_deconv      (TimeDistributed)
mrcnn_class_logits     (TimeDistributed)
mrcnn_mask             (TimeDistributed)
WARNING:tensorflow:From /home/sb020518/anaconda3/envs/tf113/lib/python3.6/site-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Epoch 1/40
  1/603 [..............................] - ETA: 36:40:48 - loss: 5.2075 - rpn_class_loss: 1.6814 - rpn_bbox_loss: 1.6133 - mrcnn_class_loss: 1.9128 - mrcnn_bbox_loss: 0.0000e

---------------------------------------------------------------------------

5. 위에서 학습 시킨 모델로 Inference 수행

  • 예측용 모델을 로드. mode는 inference로 설정,config는 NucleusInferenceConfig()로 설정,
  • 예측용 모델에 위에서 찾은 학습 중 마지막 저장된 weight파일을 로딩함.
  • weight가져온_model.detect() 를 사용하면 쉽게 Inferece 결과를 추출할 수 있다.
class NucleusInferenceConfig(NucleusConfig):
    NAME='nucleus'   # 위의 train config와 같은 name을 가지도록 해야한다.
    # 이미지 한개씩 차례로 inference하므로 batch size를 1로 해야 하며 이를 위해 IMAGES_PER_GPU = 1
    GPU_COUNT = 1
    IMAGES_PER_GPU = 1
    # pad64는 64 scale로 이미지를 맞춰서 resize수행. 
    IMAGE_RESIZE_MODE = "pad64"
    # Non-max suppression threshold to filter RPN proposals.
    # You can increase this during training to generate more propsals.
    RPN_NMS_THRESHOLD = 0.7

infer_config = NucleusInferenceConfig()
inference_model = modellib.MaskRCNN(mode="inference", config=infer_config, model_dir=MODEL_DIR)
weights_path = model.find_last()  # model weight path를 직접 정해줘도 되지만, 이와 같은 방법으로, 가장 낮은 loss의 weight path를 찾아준다.
print('학습중 마지막으로 저장된 weight 파일:', weights_path)
inference_model.load_weights(weights_path, by_name=True)

- 테스트용 데이터 세트를 NucleusDataset으로 로딩. load_nucleus() 테스트 세트를 지정하는 'stage1_test' 입력. 
dataset_test = NucleusDataset()
dataset_test.load_nucleus(DATASET_DIR, 'stage1_test') # 위에서 load_nucleus 직접 정의했던거 잊지 말기
dataset_test.prepare()
dataset_test.image_ids
"""
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
           17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
           34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
           51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64])
"""
  • 우선 test 셋에 있는 데이터를 model에 통과시켜서, 모든 test data 64개에 대해서, Segmentation결과를 추출하고 visualize 해보자.
for image_id in dataset_test.image_ids:
        # Load image and run detection
        image = dataset_test.load_image(image_id)
        print(len(image))
        # Detect objects
        r = inference_model.detect([image], verbose=0)[0]
        # Save image with masks
        visualize.display_instances(
            image, r['rois'], r['masks'], r['class_ids'],
            dataset_test.class_names, r['scores'],
            show_bbox=False, show_mask=False,
            title="Predictions")

for image_id in dataset_test.image_ids:
        # Load image and run detection
        image = dataset_test.load_image(image_id)
        print(len(image))
       
        #plt.savefig("{}/{}.png".format(submit_dir, dataset.image_info[image_id]["id"]))

6. 위 5의 한장의 사진을 inference하는 능력을 가지고 전체사진 inference 수행 하는 함수 만들기

def detect(model, dataset_dir, subset):
    """Run detection on images in the given directory."""
    print("Running on {}".format(dataset_dir))

    # Create directory
    if not os.path.exists(RESULTS_DIR):
        os.makedirs(RESULTS_DIR)
    submit_dir = "submit_{:%Y%m%dT%H%M%S}".format(datetime.datetime.now())
    submit_dir = os.path.join(RESULTS_DIR, submit_dir)
    os.makedirs(submit_dir)

    # Read dataset
    dataset = NucleusDataset()
    dataset.load_nucleus(dataset_dir, subset)
    dataset.prepare()
    # Load over images
    submission = []
    for image_id in dataset.image_ids:
        # Load image and run detection
        image = dataset.load_image(image_id)
        # Detect objects
        r = model.detect([image], verbose=0)[0]
        # Encode image to RLE. Returns a string of multiple lines
        source_id = dataset.image_info[image_id]["id"]
        rle = mask_to_rle(source_id, r["masks"], r["scores"])
        submission.append(rle)
        # Save image with masks
        visualize.display_instances(
            image, r['rois'], r['masks'], r['class_ids'],
            dataset.class_names, r['scores'],
            show_bbox=False, show_mask=False,
            title="Predictions")
        plt.savefig("{}/{}.png".format(submit_dir, dataset.image_info[image_id]["id"]))

    # Save to csv file
    submission = "ImageId,EncodedPixels\n" + "\n".join(submission)
    file_path = os.path.join(submit_dir, "submit.csv")
    with open(file_path, "w") as f:
        f.write(submission)
    print("Saved to ", submit_dir)
detect(model, args.dataset, args.subset)

Pagination


© All rights reserved By Junha Song.