반응형

C++하면 가장 중요한 키워드가 하나 있다. 그건 바로 클래스(Class) 이다.

객체 지향 프로그래밍은 클래스에서 시작했다고 해도 과언이 아니며

오늘은 이 클래스란 것을 들여다볼 예정이다.

아시다시피 해당 내용은 다섯개 챕터로 나눠써도 부족한 만큼 크게크게 요약하여 작성하도록 하겠다.


1. 클래스(Class)?

클래스란 객체에서 불필요한 특성을 제거해 모델링해서 공통된 특성을 하나의 패키지안에 놓은 것이다.

맞다.. 이렇게 생각하면 어렵다. 쉽게 말해서 '구조체의 확장 버전'이라고 생각하면 편하다.

 

클래스는 구조체와 달리 각 타입별 변수의 집합 뿐만 아니라, 멤버 함수까지 포함되고

접근 제어 지정자를 선언하여 사용 가능하다.

 

여기서 클래스와 구조체에 차이점을 발견할 수 있는데 접근 제어 지정자를 따로 선언하지 않는다면

  • 구조체(struct)는 멤버변수가 public으로 선언되며,
  • 클래스(class)에선 멤버변수가 private으로 선언된다.

public과 private에 대해선 아래에서 설명하도록하겠다.

 

2. 클래스 선언

클래스 이용을 위해선 세가지 구역이 필요하다.

  • 클래스 선언 구역
  • 클래스 정의 구역
  • 클래스 사용 구역

클래스 사용을 위해선 클래스 선언부터 해주어야 한다.

 

우선 예제 코드를 보자.

class Friend {
//멤버 변수
private: //내부에서만 접근 가능(default라 생략 가능)
	int age;
	char *name;
//멤버 함수
public: //외부에서 접근 가능
	void printInfo(){}; 
	void printInfo2(); //내부 클래스 정의, 외부 클래스 정의 모두가능
};

void Friend::printInfo2() {
}

클래스는 다음과 같이 선언하게 된다. class <클래스명>으로 선언해준다.

이후 접근제어 지정자와 함께 멤버 변수나 멤버 함수를 사용해주면 된다.

 

멤버 함수는 위와 같이 내부 클래스에서 정의 가능하며 외부에서도 정의 가능하다.

여기에서 꼭 선언한 이후 세미콜론을 붙여주도록 한다.

 

잠깐, 접근제어 지정자에 대해 짚고 넘어가자.

  • public : 공개적 접근, 클래스 밖을 포함해 모든 곳에서 접근 가능하다.
  • protected : 클래스가 속한 클래스 내부나 해당 클래스로부터 파생한 클래스 내부에서만 접근 가능하다.
  • private (default) : 클래스가 속한 클래스의 내부만 접근 가능하며, 외부 접근 불가하다.

    *모든 멤버 변수는 꼭 필요한 경우가 아니면 protected나 private으로 외부 공개 안하는게 원칙이다.

    *일반적으로 멤버 변수는 private하며, 이에 안전하게 접근할 수 있는 멤버 함수는 public

 

 

3. 객체 생성

객체 생성을 위해선 다음과 같이 사용한다.

  • <클래스이름> <객체이름>;

 

멤버 사용을 위해선 다음과 같은 형태로 사용한다.

  • <객체이름>.<멤버변수>;
  • <객체이름>.<멤버함수()>;

 

당연히 초기화도 가능하며 예제로 살펴보자.

int main(void) {
	Friend kim;
	kim.age = 21; //멤버 변수 초기화, 해당 멤버 변수가 public이여야 직접 변경 가능
	kim.printInfo(); //멤버 함수 호출

	return 0;
}

 

Frient 클래스로 kim이란 객체를 생성하고, 안에 age를 초기화해주었다.

해당 부분처럼 age를 직접 초기화하기 위해선 클래스에서 int age가 public이어야 한다.

 

일반적으로 private에 선언하고 멤버함수로 setInfo()와 같은 함수를 만들어 변경해주는 것이 좋으며

본 예제에선 이해를 위해 위와 같이 작성하였다.

 

 

4. 생성자와 소멸자

생성자객체가 인스턴스화 될 때 컴파일러에 의해 호출되는 특수 메소드 함수이다.

디폴트 생성자, 매개변수 생성자, 복사 생성자가 존재하며 상황에 맞게 사용하게 된다.

특징은 다음과 같다.

  • 생성자는 클래스 이름과 반드시 같아야 함.
  • 생성자는 return을 가질 수 없음.

 

소멸자는 이와 반대로 객체 사용을 마쳤을 때 컴파일러에 의해 호출되는 특수 멤버 함수이다.

특징은 다음과 같다.

  • 객체가 유효 범위 벗어날 때 자동 호출
  • 클래스 이름 앞에 ~를 붙인다.

여기서 디폴트 생성자와 소멸자는 기본적으로 생성되며,

일반적으로 클래스 내부에서 동적할당 메모리를 할당, 해제할 때 사용된다.

 

예제를 보며 사용해보자.

#include <iostream>
using namespace std;

class Friend {
//멤버 변수
private:
	int age;
	const char* fname;
//멤버 함수
public:
	Friend(); //생성자
	~Friend(); //소멸자
	void printInfo(); //내부 클래스 정의, 외부 클래스 정의 모두가능
};
Friend::Friend() {
	cout << "생성자 호출" << endl;
	age = 24;
	fname = "KimJaeHo";
}
Friend::~Friend() {
	cout << "소멸자 호출" << endl;
}
void Friend::printInfo() {
	cout << "나이 : " << age << ", 이름 : " << fname << endl;
}

int main(void) {
	Friend kim;
	kim.printInfo(); //멤버 함수 호출

	return 0;
}

 

 

결과는 다음과 같다.


여기까지 클래스에 대한 공부를 마친다.

클래스에는 이외에도 오버로딩과 더불어 중요한 여러가지 개념들이 더 존재한다.

해당 포스팅에선 여기서 설명을 마치지만 함께 더 공부해봐야 할 것이다.

 

반응형

'Programming > Language' 카테고리의 다른 글

[Assembly] 파일 읽고 출력해보기  (0) 2020.06.06
[Assembly] 문자열 입력과 출력  (0) 2020.06.05
[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14
반응형

오늘은 윈도우 구조에 대해 공부해보고자 한다.

기존에 간단하게 짚고 넘어간 부분을 세세하게 다뤄보고자 한다.


1. 운영체제란?

운영체제(Operating System)란 하드웨어를 관리, 응용 소프트웨어를 효율적으로 실행/사용할 수 있도록 만든 시스템 소프트웨어이다.

사용자와 하드웨어 간의 인터페이스로 동작하며, 컴퓨터 자원들을 효율적으로 사용할 수 있도록 도와주는 역할을 한다.

기능과 목적은 다음과 같다.

 

  • 운영체제의 기능
    • 메모리 관리 : 프로세스에 메모리 할당 및 회수 방법 결정, 메모리 점검, 프로그램 절대주소 매핑하여 메인 메모리 저장
    • 프로세스 관리 : 프로세스 스케줄링 및 동기화 관리, 프로세스 생성과 제거 및 시작과 정지 등 기능 담당
    • I/O 장치 관리 : Buffer-Caching 기능 제공, 입출력 장치 스케줄링, 일반 및 특정 장치용 드라이버 제공
    • 파일 관리 : 파일의 생성 및 삭제, 변경 등을 관리
    • 등등

  • 운영체제의 목적
    • 처리 능력 향상 : 일정 시간 내에 시스템이 처리하는 일의 양 처리 효율 향상
    • 반환 시간 단축 : 시스템에 작업을 의뢰한 시간부터 처리가 완료될 때까지 걸리는 시간 단축
    • 사용 가능도 향상 : 시스템을 사용할 필요가 있을 때 즉시 사용 가능한 정도 향상
    • 신뢰도 향상 : 시스템이 주어진 문제를 정확하게 해결하는 신뢰 향상
    • 등등


2. 커널 & 시스템 호출

커널(Kernel)이란 운영체제의 중심에 위치하여 운영체제에 기본적인 기능을 제공하는 핵심 부분이다.
모든 작업은 커널의 동작에 의해 제어되며,

프로세스 관리, 메모리 관리, 파일 시스템 관리, 인터럽트 처리, 프로그래밍 인터페이스 제공 등의 기능을 제공한다.

 

커널에 사용자가 접근하기 위해선 어떡해야할까?

사용자가 커널에 접근하기 위해선 시스템 호출이 사용된다.

 

시스템 호출(System Calls)은 커널 영역의 기능을 사용자 모드가 사용 가능하게 해주며,

프로세스가 하드웨어에 접근하여 필요한 기능을 사용할 수 있게 해준다.

사용자, 혹은 응용 프로그램으로부터 컴퓨터의 자원을 보호하기 위해 직접 접근 차단 인터페이스라 볼 수 있다.

시스템 함수(write()과 같은 함수)에 의해 이루어진다.

 

아래 그림을 보면 시스템 콜을 이용하여 사용자 어플리케이션에서 API를 타고 커널 영역으로 들어가는 것을 볼 수있다.

좀 더 자세히 들여다보자면,

응용프로그램이 함수를 실행하면 Kernel32.dll의 API를 호출하고, 해당 API는 다시 ntdll.dll 함수를 호출한다.

그럼 ntdll.dll에선 커널에 요청해야 할 서비스 번호를 이용하여 특정 시스템 자원에 접근할 수 있게 된다.

 

*API(Application Programming Interface) : 운영체제가 응용 프로그램을 위해 제공하는 함수의 집합

 

3. 윈도우즈 구조

윈도우는 Microsoft 사가 개발하였으며 1981년에 1.0버전이 처음 등장하였다.

3.1버전부터 대중에게 널리 알려지기 시작하였다.

 

윈도우는 대표적으로 링 구조로 이루어져 있으며

 

하드웨어 <> HAL <> 마이크로 커널 <> 각종 관리자 <> 응용프로그램으로 이루어져 있다.

 

커널 모드는 직접적인 사용자 접근이 불가능 하며,

사용자 모드에선 윈도우 운영체제에서 사용자가 프로그램을 만들고 실행하는 모든 과정이 가능하다(완벽하게 구현은 X).

 

 


상세 구조는 다음과 같다.

  • 커널 모드

 간단하게 용어를 정리해보고자 한다.
HAL(Hardware Abstraction Layer)은 하드웨어와 소프트웨어 간의 원할한 통신을 가능토록 해준다.

마이크로 커널은 다양한 커널 종류 중 하나로 하드웨어와 통신만을 제어하는 최소한 역할만 하는 커널이다.

프로세스 스케줄링, 메모리 관리 등의 작업들은 각종 관리자에게 맡긴다.

(System32 폴더의 ntopskrnl.exe가 마이크로 커널)

 

각 관리자의 역할은 다음과 같다.

- 입출력 관리자 : 시스템의 입출력을 제어, 장치 드라이버 사이에서 메세지를 전달(하드웨어와 응용 프로그램이 바로 통신 가능)

- 그래픽 장치 관리자 : 그래픽으로 처리되는 화면에 표시되는 선, 폰트 등을 관리

- 객체 관리자 : 파일, 포트, 스레드, 프로세스 등 각 객체 정보를 제공

- 보안 참조 관리자 : 각 데이터나 시스템 자원 제어를 허가/거부, 시스템의 보안을 강제적으로 설정

- 프로세스 관리자 : 스레드 생성 및 처리

- 로컬 프로시저 호출 관리자 : 각 메모리에 할당받은 프로세스들 간의 통신을 담당

- 가상 메모리 관리자 : 응용 프로그램 요청따라 램 메모리 할당, 가상 메모리의 페이징 제어

 

이외에도 캐리 관리자, PNP 관리자, 전원 관리자 등 다양한 기타 관리자가 존재한다.

 

  • 사용자 모드

사용자 모드엔 다양한 응용 프로그램이 존재하며 이를 실행하는 서브 시스템이 존재한다.

서브 시스템여러 응용 프로그램을 실행하는 컨테이너라 불린다.

- Win32 서브 시스템 : 기본적인 서브 시스템이다. 32비트 응용 프로그램을 동작 가능, 윈도우 사용자 인터페이스를 제공

- OS/2 서브 시스템 : MS와 IBM에서 초기 제작한 운영체제로 현재는 지원 중단

- POSIX 서브 시스템 : 이식 가능 운영체제 인터페이스이며, 유닉스에서 사용되었음. 호환 이슈로 최근엔 사용 X

- NTVDM : 64비트의 OS를 제외한 16, 32비트를 위해 도입된 시스템 구성 요소로 32비트와 16비트 사이 호환위해 사용

*현재는 NTVDM을 사용안하는 것을 권장(개발 중단으로 인한 보안 위험 요소로 인해)


여기까지 윈도우 커널 공부를 마친다. 앞으로 개발에 대한 부분도 차근차근 다뤄나갈 예정이다.

반응형
반응형

오늘은 AI의 대표적인 데이터셋인 MNIST를 이용하여

손글씨 숫자 이미지를 인식하는 실습을 해보고자 한다.

 

스마트폰 앱에서 글씨를 캡쳐하면 실제 데이터로 변형되어 출력되는 것을 본적이 있을 것이다.

오늘은 이런 것이 어떻게 가능한 것인지 알아보고, 실습하는데 목적을 둔다.

 

*추후 모델과 기초 개념에 대해서도 다룰 예정이다.


0. 목표

우리는 위 숫자가 '8'이란 것을 안다. 어떻게 알게 되었는가?

 

어렸을 때 우린 1부터 9까지 숫자들의 모양을 학습하였고,

위 이미지와 우리가 알고있는 모양인 숫자 8과 일치시켜 답을 내놓을 수 있었을 것이다.

 

컴퓨터로는 어떻게 위 이미지를 인식하여 데이터를 처리할 수 있을까?

 

우리는 해당 포스팅을 통해 '손글씨 숫자 이미지를 인식하여 예측'해보는 모델을 만들어보고자 한다.

  • 1부터 9까지의 데이터를 학습하여 손글씨 이미지를 예측할 수 있는 '모델'을 만들고
  • 임의의 손글씨 이미지를 만들어 이 그림이 어떠한 숫자를 의미하는지 예측해 볼 것이다.

*참고로 이미지 데이터를 처리할때 높은 정확도를 도출하기 위해 CNN 방식을 많이 사용하지만 본 포스팅에선 CNN을 중점적으로 다루지 않는다. 추후 다룰 예정이다.

 


1. MNIST 데이터 셋

이번 포스팅에서도 다시 얘기하지만 Deep Learning의 핵심은 '좋은 데이터'이다.

 

이번은 AI에서 대표적인 손글씨 데이터 셋인 MNIST 데이터 셋에 대해 이야기해보겠다.

 

MNIST Dataset


MNIST는 다음과 같은 특징을 갖는다.

  • 대표 데이터 셋인 만큼 import하여 손쉽게 데이터를 불러올 수 있다.
  • 60,000개의 학습 데이터, 10,000개의 테스트 데이터로 나뉘어 있다.
  • 각 이미지는 28x28 크기로 784개의 픽셀을 가진다.

(1) 데이터를 불러오는 방법은 다음과 같다.

from keras.datasets import mnist


(2) 데이터 갯수는 다음과 같다.

(X_train, Y_class_train), (X_test, Y_class_test) = mnist.load_data()
print("학습셋 이미지 수 : %d 개" % (X_train.shape[0]))
print("테스트셋 이미지 수 : %d 개" % (X_test.shape[0]))

  • 첫번째 라인에서 X_train에는 학습할 60,000개의 각 784개 픽셀 정보가 배열 형태로 저장되게 되고
  • Y_class_train에는 60,000개 이미지 정답인 클래스 정보가 담기게 된다.
  • X_test와 Y_class_test는 테스트 데이터로 10,000개이고 형태는 위 학습데이터와 같다.

(3) 다음은 데이터 형태이다(X_train 데이터의 첫번째 원소).

# 이미지 형태 출력을 위한 pyplot 모듈 import
import matplotlib.pyplot as plt
# 위 60000개 데이터 배열에서 0번째 원소를 흑백으로 출력
plt.imshow(X_train[0], cmap='Greys')
plt.show()

################################

# 0번째 원소의 모든 데이터를 출력
for x in X_train[0]:
    for i in x:
        sys.stdout.write('%d  ' % i)
    sys.stdout.write('\n')

  • 위 그림을 보면 가로 28개 픽셀, 세로 28개 필셀의 집합으로 하나의 그림이 만들어 진 것을 볼 수 있다.
  • 흰색은 0, 검정색은 255 이렇게 각 픽셀의 색상을 데이터화하여 X_train의 원소에 담겨져 있는 것이다.

 

 


2. Training

# 딥러닝에 필요한 케라스 함수 호출
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense

# 필요 라이브러리 호출
import numpy
import tensorflow as tf

# 데이터 셋 호출
from keras.datasets import mnist

# 실행 시마다 같은 결과값 도출을 위한 시드 설정
numpy.random.seed(0)
tf.random.set_seed(0)

# 데이터를 불러와서 각 변수에 저장
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

# 학습에 적합한 형태로 데이터 가공
X_train = X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float32') / 255

# 클래스를 학습에 이용하기 위해 데이터 가공
Y_train = np_utils.to_categorical(Y_train, 10)
Y_test = np_utils.to_categorical(Y_test, 10)

# 딥러닝 모델 구조 설정(2개층, 512개의 뉴런 연결, 10개 클래스 출력 뉴런, 784개 픽셀 input 값, relu와 softmax 활성화 함수 이용)
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))

# 딥러닝 구조 설정(loss 옵션을 다중 클래스에 적합한 categorical_crossentropy, 옵티마이저는 adam 설정)
model.compile(loss='categorical_crossentropy', optimizer='adam',
              metrics=['accuracy'])

# 모델 실행(X_test, Y_test로 검증, 200개씩 30번 학습)
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), epochs=30, batch_size=200, verbose=2)

# 학습 정확도, 검증 정확도 출력
print('\nAccuracy: {:.4f}'.format(model.evaluate(X_train, Y_train)[1]))
print('\nVal_Accuracy: {:.4f}'.format(model.evaluate(X_test, Y_test)[1]))

# 모델 저장
model.save('Predict_Model.h5')

 

다음 부분에 설명이 필요할 것이다.

  • X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
  • 학습을 위해 각 데이터는 0-255에서 0-1사이의 숫자로 변환해야한다. 그래서 위 연산을 수행한다.
  • 위 함수를 아래 3줄로 풀어 출력하면 다음과 같다. 천천히 이해를 해보기 바란다.

  • np_utils.to_categorical(Y_train, 10)
  • 해당 클래스 정보도 0-10 값을 0, 1, 2, 3~~ 이렇게 저장할 수 있지만 앞서 말했듯이 0, 1값으로 저장해야 원할한 데이터 처리가 가능하다.
  • 5를 표현하기 위해 클래스 갯수를 가지는 리스트를 만들어 5번째 원소에만 1을 주는 방식으로 one-hot vector화하는 함수다.

 

이후 학습을 진행한다.

CNN을 사용하지 않았지만 높은 정확도를 보였다. 검증 정확도가 98%이다.

 

 

해당 모델은 다음과 같이 잘 저장되었다.

 

 

 


3. Testing

테스트를 위해 이미지를 하나 만들어보았다.

픽셀을 28x28로 만들고(어차피 resize해서 필요없긴하나 편의를 위해), 붓으로 숫자 8을 그려보았다.


이제 jupyter notebook에 업로드하고 다음과 같이 코딩한다.

# 딥러닝에 필요한 케라스 함수 호출
from keras.models import load_model
from keras.utils import np_utils

# 필요 라이브러리 호출(PIL은 이미지파일 처리위함)
from PIL import Image
import numpy as np

# test.png는 그림판에서 붓으로 숫자 8을 그린 이미지 파일
# test.png 파일 열어서 L(256단계 흑백이미지)로 변환
img = Image.open("test.png").convert("L")

# 이미지를 784개 흑백 픽셀로 사이즈 변환
img = np.resize(img, (1, 784))

# 데이터를 모델에 적용할 수 있도록 가공
test_data = ((np.array(img) / 255) - 1) * -1

# 모델 불러오기
model = load_model('Predict_Model.h5')

# 클래스 예측 함수에 가공된 테스트 데이터 넣어 결과 도출
res = model.predict_classes(test_data)
# 2021/10/02 수정 - 오류시 아래 명령어로 대체 가능합니다.
# res =(model.predict(test_data) > 0.5).astype("int32")

print(res)

 

출력 결과는 다음과 같다.

정확히 예측에 성공하였다.

학습 데이터가 좋아 쉽게 성공하였는데 이후에 CNN을 이용하여 실습해 보겠다.

 

 


4. 블랙박스 부수기

현재 우리는 블랙박스를 부수지 못하고있다.

인식, 예측엔 성공했지만 '어떻게' 이것이 가능한지는 원리적으로 알지 못한다.

아직 model.add(Dense(~~)) 부분도 상세히 설명하지 않았다.

 

해당 포스팅은 딥러닝이란 분야에 흥미를 불러일으키기 위한 목적을 가진다.

 

이후 글부터 '어떻게'에 초점을 맞추어 활성화 함수부터 모델에 대한 전반적인 개념에 대한 포스팅을 진행할 예정이다.

그리고 다시 해당 포스팅으로 돌아오면, 안보이던 코드(블랙 박스)가 하나하나 보이기 시작할 것이다.

 


같이 공부해요 ㅎㅎ

반응형
반응형

오늘은 공부한 것을 토대로 직접 테스트 데이터도 추가하여

폐암 환자의 수술 후 생존 여부를 예측해볼 것이다.

 

*추후 모델과 기초 개념에 대해서도 다룰 예정이다.


0. 목표

머신러닝은 '알고리즘을 이용하여 데이터를 분석하고, 학습하여 이러한 내용을 기반으로 판단, 예측하는 기술' 이다.

 

딥러닝도 머신러닝 범주에 속해있으며 우린 이를 이용해 미래의 일을 예측해보고자 한다.

 

이런 예측하는 기술은 어디에 사용할 수 있을까?

 

본 포스팅에서는 '폐암 환자의 수술 후 생존 여부'을 예측해보고자 한다.

  • 기존의 폐암 환자 데이터를 학습하여 수술 후 생존 여부를 예측할 수 있는 '모델'을 만들고
  • 가상의 환자 데이터(미래)를 만들어 수술 후 생존 여부를 예측해 볼 것이다.


 


1. 폐암 환자 데이터

AI에서 가장 중요한 부분이자 기반이 되야 하는 것이 '좋은 데이터'이다.

 

폐함 환자 생존률 데이터는 '모두의 딥러닝' 예제의 [ThoraricSurgery.csv]를 사용하였고,

직접 [test.csv]를 만들어 가상의 환자를 만들어 보았다.

* ThoraricSurgery.csv : 구글에 치면 다운받을 수 있는 깃허브 주소가 나온다.

 

[ThoraricSurgery.csv] 데이터의 구조를 살펴보자.

  • 데이터에는 470명의 환자 정보가 들어있다.
  • '속성' 정보는 [종양 유형, 폐활량, 기침, 흡연 여부 등] 각각의 17개의 환자의 정보가 담겨있다.
  • '클래스'에는 [수술 후 생존, 사망 여부] 정보가 담겨 있다.

[test.csv] 데이터 구조도 살펴보자.

  • 글쓴이가 만든 가상의 환자 정보이다.
  •  '속성' 정보는 [종양 유형, 폐활량, 기침, 흡연 여부 등] 위와 같이 17개의 환자의 정보가 담겨있다.
  • '클래스'[수술 후 생존, 사망 여부] 정보는 우리가 모델을 통해 예측해볼 것이다!


 


2. Training

# 딥러닝에 필요한 케라스 함수 호출
from keras.models import Sequential, load_model
from keras.layers import Dense

# 필요 라이브러리 호출
import numpy as np
import tensorflow as tf

# 실행 시마다 같은 결과값 도출을 위한 시드 설정
np.random.seed(0)
tf.random.set_seed(0)

# csv 파일을 읽어 ','기준으로 나눠 Dataset에 불러오기
Dataset = np.loadtxt("ThoraricSurgery.csv", delimiter=",")

# 환자 정보는 0-16번(17개)까지이므로 해당 부분까지 X에 담기
X = Dataset[:, 0:17]
# 수술 후 결과는 마지막 17번은 클래스로 Y에 담기
Y = Dataset[:, 17]

# 딥러닝 모델 구조 설정(3개층, 속성이 17개 input 값, relu와 sigmoid 활성화 함수 이용)
model = Sequential()
model.add(Dense(30, input_dim=17, activation='relu'))
model.add(Dense(15, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

# 딥러닝 실행(오차함수는 평균제곱오차, 최적화함수는 adam 이용)
model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])
model.fit(X, Y, epochs=30, batch_size=5)

# 만들어진 모델 저장
model.save('Predict_Model.h5')

 

중간에 Dataset을 출력한 사진이다.

다음과 같이 470명 환자의 17가지 정보가 들어간 이중 리스트가 만들어졌다.

 

중간 X와 Y를 출력한 사진이다.

X엔 환자 정보가, Y엔 마지막 컬럼인 생존여부부분이 들어간 것을 볼 수 있다.

 

모델의 정확도이다.

5개 단위(batch-size)로 30번(epochs) 학습시켜 85.74% 정확도를 가진 모델을 만들었다.

 

그리고 다음과 같이 잘 저장되었다.


3. Testing

# 딥러닝에 필요한 케라스 함수 호출
from keras.models import load_model
from keras.utils import np_utils

# 필요 라이브러리 호출
import numpy as np

# csv 파일을 읽어 ','기준으로 나눠 Dataset에 불러오기
Dataset = np.loadtxt("test.csv", delimiter=",")

# 환자 정보는 0-16번(17개)까지이므로 해당 부분까지 X에 담기
X = Dataset[:, 0:17]
# 수술 후 결과 정보인 예측값 변수 초기화
Y = []

# 모델 불러오기
model = load_model('Predict_Model.h5')

# X 값에 대한 predict_classes 함수 실행결과 Y에 저장
Y = model.predict_classes(X)

print('Predict : ', Y)

 

중간 Dataset 변수를 출력한 사진이다.

3명의 가상 환자 정보가 잘 들어간 것을 알 수 있다.

 

마지막 예측 결과 사진이다.

3명의 가상 환자에 대해 다음과 같은 결과를 도출했다.

  • 1번 가상 환자 : 수술 후 사망 예상
  • 2번 가상 환자 : 수술 후 사망 예상
  • 3번 가상 환자 : 수술 후 생존 예상

이라는 결과를 내놓았다.


4. 신뢰성

여기서 한가지 밝힐 사실이 있다.

이 가상 환자 세명은 사실 [ThoraricSurgery.csv] 데이터의 환자의 정보를 참고하여 만들었다.

두명은 수술 후 사망했던 환자 정보유사한 속성 정보를 집어넣었고

마지막 한명은 수술 후 생존했던 환자 정보유사하게 속성 정보를 집어넣었다.

 

보시다시피 결과는 예상과 같았다.

이러한 결과는 모델에 대한 정확도를 보여주는 또 한가지의 지표가 되었다.


 

AI를 이용하면 다음과 같이 기존 데이터를 이용해 미래의 일을 예측해볼 수 있다.

하지만 그만큼 어려운 분야인 만큼 많은 공부를 필요로 한다.

계속해서 공부해나가다 보면 우리 모두 좋은 성과를 도출해낼 수 있을 것이다.

반응형
반응형

당분간 딥러닝 관련 포스팅을 진행할 예정이다.

기초부터 원리, 실데이터를 이용한 실습까지 차근차근 진행해볼 예정이다.

딥러닝 공부를 위한 실습 환경 조성부터 진행해보자.


0. 환경 확인

 본인은 Windows 10 64bit 기준으로 설명한다.

실습시 GPU는 이용하지 않고 CPU를 이용할 예정이다.


1. 아나콘다 설치

  • 아나콘다3를 설치한다.

  • Add Anaconda3 to my PATH enviroment variable을 체크해줘서 환경변수 설정을 해주자.

  • 시작 - 검색에 anaconda를 검색하여 나타나면 설치가 완료된 것이다.

 


2. 텐서플로, 케라스 설치

  • Anaconda Prompt를 '관리자 권한으로 실행'한다.

  • 환경을 생성한다.
    > conda create -n [작업환경명] python=[버전] [필요라이브러리명], ...
    > ex:) conda create -n saynot python=3.8 numpy pandas matplotlib spyder 

  • 환경을 활성화한다.
    > activate [작업환경명]

  • 텐서플로를 설치한다.
    > pip install tensorflow

  • 텐서플로가 제대로 설치되었는지 확인한다.
    > python - import tensorflow as tf - print(tf.__version__) 입력 시 버전 출력되면 설치 확인

  • 케라스를 설치한다.
    > pip install keras

  • 케라스가 잘 설치 되었는지 확인한다.
    > python - import keras 입력 시 Using TensorFlow backend 문구나 에러문구 출력안되면 설치완료

 


3. 주피터 노트북 설치

마지막으로 주피터 노트북을 설치해서 코딩 환경을 조성한다.

  • 주피터 노트북 설치한다.
    > pip install jupyter

  • 주피터 노트북을 실행한다.
    > jupyter notebook
    > 메모장 or 페이지 or 도스상에 주피터 노트북 링크가 뜨게 되는데 해당 링크를 복사하여 크롬에 붙여넣기한다.

  • 주피터 노트북 페이지 접속하여 테스트해보기.
    > New - Python 3
    > 라인에서 파이썬 코드 입력후 Run 클릭

  • 저장 버튼은 항상 필수!!



4. 추후 접속 시

  • Anaconda Prompt (관리자 권한 실행) - activate [작업환경명] - jupyter notebook 순으로 접속하면 된다.

이제 실습환경 구축이 끝났으니 다음 챕터부터 딥러닝을 본격적으로 공부해 보겠다.

저작권 및 오타 지적 환영!

 

반응형
반응형

필자는 pcap 라이브러리를 사용하며 다양한 프로그램을 구현해본 경험이 있다.

최근 python으로도 pcap 라이브러리가 존재한다는 것을 알게되었다.

바로 PyPCAP인데, 자료가 많지 않더라. 공부해보자.


1. PyPCAP?

pypcap을 이야기하기 전에 pcap에 대해 먼저 알아야한다.

 

pcap이란 Packet Capture의 약자인데, 네트워크상의 패킷을 캡쳐하기 위한 API이다.

윈도우는 Winpcap, 리눅스에선 libpcap을 사용하여 이용 가능하다.

 

pypcap은 말그대로 python 버전의 pcap 라이브러리이다.

이를 이용하여 python 환경에서 네트워크 트래픽을 손쉽게 캡쳐하고, 분석 가능할 수 있다.

 

*공식 문서와 설명은 다음 링크에 있다.




2. 사용법

pypcap을 사용하기 위해선 pypcap을 먼저 설치해야한다.

이후 pcap을 import하여 사용하면 된다.

 

1. 설치

pip intall pypcap

  • python3의 경우 pip3 install pypcap
  • libpcap-dev, python-dev 패키지 의존

 

2. 사용법

import pcap




3. 실습/예제

이제 pypcap을 이용하여 간단한 코딩을 진행하자.

 

네트워크 패킷을 캡쳐하게 되면 호스트에선 Ethernet Header의 MAC Frame 부분부터 잡기 시작한다.

아래와 같이 첫 6바이트는 Destination의 MAC 주소를 가리키고, 다음 6바이트는 Source의 MAC 주소를 가리킨다.

 

다음은 Destination MAC주소와 Source MAC 주소를 출력하는 코드의 예문이다.

 

import pcap #pypcap 라이브러리

sniffer = pcap.pcap(name=None, promisc=True, immediate=True, timeout_ms=50)

for ts, pkt in sniffer:
    print('Dst MAC - ', end='', flush=True)
    print(':'.join('%02X' % i for i in pkt[0:6])) #패킷의 0번부터 5번 바이트까지 출력
    print('Src MAC - ', end='', flush=True)
    print(':'.join('%02X' % i for i in pkt[6:12])) #패킷의 6번부터 11번 바이트까지 출력
    print()

 

위와 같이 매우 짧은 코드로 잡힌 패킷의

출발지 맥주소와 도착지 맥주소를 출력할 수 있다.

 

코드를 잠시 살펴보자.

 

  • sniffer = pcap.pcap(name = None, promisc = True, immediate = True, timeout_ms = 50)
    • name = None > 디바이스 이름이다. None을 두면 디폴트값이 들어가게 되고, 작은따옴표 안에 직접 디바이스 명을 넣어줄 수 있다.
    • promisc = True > 프리큐어스(무차별) 모드이다. 해당 옵션을 True, 곧 사용하겠다는 뜻이다. 해당 옵션으로 모든 패킷을 캡쳐 가능하다.
    • immediate = True > immediate 옵션이다. 해당 옵션을 True로 설정하여 지연문제를 없앨 수 있다.
    • timeout_ms = 50 > 말그대로 타임아웃 시간이다. ms단위로 50이므로 0.05초로 설정하였다.
    • 해당 return 값을 sniffer에 반환한다.

print(sniffer)

  • for ts, pkt in sniffer
    • sniffer 객체의 첫번째 값은 timestamp, 두번째 값은 packet으로 반복문을 돌겠다는 의미이다.
    • 이후 pkt[idx] 형식으로 네트워크 패킷의 특정 위치 인덱스 값을 가져올 수 있다.



 

4. 응용

pypcap은 dpkt나 socket 모듈 등과 함께 편리하게 사용 가능하다.

다음은 dpkt 모듈을 이용하여 Ethernet의 정보를 파싱하여 출력하는 예문이다.

 

import pcap
import dpkt

sniffer = pcap.pcap(name='eth0', promisc=True, immediate=True, timeout_ms=50)
sniffer.setfilter('tcp and port 443')

def mac_addr(address):
    return ':'.join('%02X' % dpkt.compat.compat_ord(b) for b in address)

for ts, pkt in sniffer:
    eth = dpkt.ethernet.Ethernet(pkt)
    #ip = eth.data
    #tcp = ip.data
    print('Ethernet INFO - ', mac_addr(eth.dst), mac_addr(eth.src), eth.type)
  • eth0 디바이스를 이용하여 캡쳐를 진행하고,
  • setfilter() 함수를 통해 tcp 패킷과 443번 포트를 필터링
  • 이후 dpkt의 Ethernet 클래스로 ethernet 부분을 파싱
  • 해당 함수의 변수를 사용해 원하는 값을 출력

 

 

*eth.data, ip.data 등을 이용해 IP와 TCP Header에 대한 파싱도 손쉽게 가능하다.



4. 마무리

위와 같이 해당 모듈을 이용해 자신이 원하는 대로 네트워크 패킷을 캡쳐, 분석 가능하다.

 

  • 느낀점
    • C++보다 훨씬 짧은 코드로 원하는 결과를 표현 가능
    • python의 장점을 잘 살린 pcap 모듈
    • 하지만 관련 자료가 많지 않아 해당 라이브러리의 공식 문서를 찾는 수고가 필요

 

계속해서 공부하다보면 네트워크를 이용하는 프로그램을 손쉽게 만들 수 있게 될 것이다.


(이렇게 한글로 된 pypcap 자료 갯수에 이바지하였다.)

반응형

'Programming > Network' 카테고리의 다른 글

[소켓 프로그래밍] TCP Echo-Client, Server 구현  (0) 2020.06.09
반응형

오늘은 DFS를 사용해서 알고리즘 문제를 해결할 예정이다.

역시 차근차근 진행해보자.

*본인은 C++로 코딩할 예정이다.


1. 문제

주어진 항공권을 모두 이용하여 여행경로를 짜려고 합니다.

항상 ICN 공항에서 출발합니다.

항공권 정보가 담긴 2차원 배열 tickets가 매개변수로 주어질 때,

방문하는 공항 경로를 배열에 담아 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 모든 공항은 알파벳 대문자 3글자로 이루어집니다.
  • 주어진 공항 수는 3개 이상 10,000개 이하입니다.
  • tickets의 각 행 [a, b]는 a 공항에서 b 공항으로 가는 항공권이 있다는 의미입니다.
  • 주어진 항공권은 모두 사용해야 합니다.
  • 만일 가능한 경로가 2개 이상일 경우 알파벳 순서가 앞서는 경로를 return 합니다.
  • 모든 도시를 방문할 수 없는 경우는 주어지지 않습니다.

출저 - 프로그래머스



2. 사고 - DFS(깊이 우선 탐색)

1. 동선이 여러개 일 경우 알파벳순이므로, sort함수를 통해 내림차순으로 정렬해둔다.

3. unordered_map을 사용해 key는 출발지, value(여러개 일 수 있으므로 vector)엔 목적지 데이터를 넣어둔다.

4. 첫 방문지인 INC을 스택에 push해둔다.

5. 이후 스택이 빌때까지 반복문을 돌아가게된다.

6. 스택의 top부분을 map의 key에 넣어 도착지를 찾아 스택에 push한다.

7. 이후 map에선 해당 도착지를 pop하여 탐색대상에서 제외시킨다.

8. 방문하고자 하는 곳이 없거나, 도착지가 존재하지 않으면 해당 key를 answer에 push한다.

9. 이후 스택에서 pop한다.

10. 스택이 비게되면 answer을 reverse함수를 통해 역순으로 배열시킨다.

11. 결과를 도출



3. 코딩

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;

vector<string> solution(vector<vector<string>> tickets) {
	vector<string> answer;
	sort(tickets.begin(), tickets.end(), greater<vector<string>>());
	unordered_map<string, vector<string>> route;
	for (unsigned int i = 0; i < tickets.size(); i++) {
		route[tickets[i][0]].push_back(tickets[i][1]);
	}
	stack<string> stk;
	stk.push("ICN");
	while (!stk.empty()) {
		string tp = stk.top();
		if (route.find(tp) == route.end() || route[tp].size() == 0) {
			answer.push_back(tp);
			stk.pop();
		}
		else {
			stk.push(route[tp].back());
			route[tp].pop_back();
		}
	}
	reverse(answer.begin(), answer.end());
	return answer;
}

int main(void) {
	vector<vector<string>> tickets;
	vector<string> v;
	v.push_back("ICN");
	v.push_back("SFO");
	tickets.push_back(v);
	v.clear();
	v.push_back("ICN");
	v.push_back("ATL");
	tickets.push_back(v);
	v.clear();
	v.push_back("SFO");
	v.push_back("ATL");
	tickets.push_back(v);
	v.clear();
	v.push_back("ATL");
	v.push_back("ICN");
	tickets.push_back(v);
	v.clear();
	v.push_back("ATL");
	v.push_back("SFO");
	tickets.push_back(v);
	v.clear();
	
	vector<string> res;
	res = solution(tickets);
	for (auto &i : res) {
		cout << i << " ";
	}
	return 0;
}

- 티켓 : [[ICN, SFO], [ICN, ATL], [SFO, ATL], [ATL, ICN], [ATL,SFO]]

 

- 과정1(내림차순 정렬) : [[SFOATL], [ICNSFO], [ICNATL], [ATL,SFO], [ATLICN]]

 

- 과정2(unordered_map 대입)

SFO - ATL

ICN - SFO, ATL

ATL - SFO, ICN

 

- 과정3(while문)

stack - INC > ATL(else) > INC(else) > SFO(else) > ATL(else) > SFO(else) > pop*6번(if)

 

- 과정4(answer 역순으로 결과 도출)

answer - ICN > ATL > ICN > SFO > ATL > SFO

 



4. 알게된 것

백번 보는 것보다 한번 해보는게 낫다.

직접 문제만 볼땐 몰랐는데 코딩을 하며 하나씩 해결해나갈 수 있었다.

그리고 마지막엔 듣고 있는 수업의 강의를 보며 부족했던 코드를 하나씩 수정해나갔다.

BFS, DFS를 이용해야하는 문제가 많은데 계속해서 문제를 풀어봐야 적응이 되겠다는 생각이 들었다.


여행가고싶다.

반응형
반응형

오늘은 동적 계획법(일명 DP)을 사용해볼 예정이다.

'N으로 표현'이란 문제인데 문제만 보면 어려워 보인다.

차근차근 해결해나가자.

*본인은 C++로 코딩할 예정이다.


1. 문제

아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.

12 = 5 + 5 + (5 / 5) + (5 / 5)

12 = 55 / 5 + 5 / 5

12 = (55 + 5) / 5

5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.

이처럼 숫자 N과 number가 주어질 때,

N과 사칙연산만 사용해서 표현 할 수 있는 방법 중

N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.

 

출저 - 프로그래머스



2. 사고 - 동적 계획법(DP)

1. N을 사용한 횟수에 따라 만들 수 있는 수들이 생긴다(정해진다).

2. 직접 계산해보며 N에 따른 사용횟수를 일반화 해본다. > 3을 한번 사용하면 3, 두번 사용하면 33과 사칙연산 값인 6, 0, 9, 1을 산출 할 수 있다.

3. 일반화한 공식을 코딩으로 적용해본다.

4. 동적 계획법으로 탐색범위를 확실히 좁혀야 하는데에 집중하여야 한다.



3. 코딩

#include <iostream>
#include <unordered_set>
#define MAXN 8
using namespace std;

int solution(int N, int number) {
	int answer = -1, std = 0;
	unordered_set<int> us[MAXN];
	for (int i = 0; i < MAXN; i++) {
		std = 10 * std + 1;
		us[i].insert(std * N);
	}
	for (int i = 1; i < MAXN; i++) {
		for (int j = 0; j < i; j++) {
			for (auto& op1 : us[j]) {
				for (auto& op2 : us[i - j - 1]) {
					us[i].insert(op1 + op2);
					us[i].insert(op1 - op2);
					us[i].insert(op1 * op2);
					if (op2 != 0) {
						us[i].insert(op1 / op2);
					}
				}
			}
		}
		if (us[i].count(number) > 0) {
			answer = i + 1;
			break;
		}
	}
	return answer;
}

int main(void) {
	int N, number;
	N = 3;
	number = 12;
	cout << solution(N, number) << endl;

	return 0;
}

- N = 3, number = 12

 

- 과정1 : us[0] = 3, us[1] = 33, us[2] = 333, us[3] = 3333, us[4] = 33333, us[5] = 333333, us[6] = 3333333, us[7] = 33333333

- 과정2 : us[1] = {33, 6, 0, 9, 1}

- 과정3 : us[2] = {333, 4, 2, 3...12...}

- 과정4 : us[2] {...12...} 존재 확인, answer = i + 1

- answer = 3

- 3*3 + 3 = 12

 

- 결과 : 3

 



4. 알게된 것

다이나믹 프로그래밍이란?

문제의 답을 확인하기 위해서 확인하는 범위를 줄여가며 동적으로 결정해나가는 방식

 

동적 계획법을 사용하기 위해선 문제에 대한 충분한 이해를 필요로 한다.

문제를 잘 읽고 확인 범위를 줄여나가기 위한 설계를 철저히 진행해야 한다.

 

 


난이도도 다이나믹하다.

반응형

+ Recent posts