Now Loading ...
-
선착순 이벤트 시스템 개발하기
선착순 이벤트 시스템 개발하기
발생할 수 있는 문제
쿠폰을 100개만 발급해야 하는데, 쿠폰이 100개보다 많이 발급되었다
트래픽이 급증해 이벤트 페이지 접속이 안된다
이벤트랑 전혀 상관없는 페이지들도 느려졌다
해결책
트래픽이 몰렸을 때 대처하는 방법 적용
Redis를 사용해 쿠폰 발급 개수를 보장하기
Kafka를 활용해 다른 페이지에 미치는 영향을 줄이기
Redis로 Race Condition 해결하기
기존 락 활용의 문제
선착순 쿠폰 발행은 쿠폰 개수에 대한 정합성을 요구함
락 활용은 요구사항의 임계영역이 길어서 성능 불이익 발생
발급된 쿠폰 개수를 가져오는 것부터 쿠폰 생성까지 락을 걸면 임계영역이 길어져서 성능 불이익
해결책: 레디스는 싱글 스레드로 동작해 Race Condition 해결
애플리케이션의 모든 스레드는 언제나 최신 값을 가지게 됨
레디스 incr 명령어 활용
key의 value를 1씩 증가시킴
성능이 매우 빠른 명령어
남은 문제점
쿠폰 발급 개수가 많아질수록 RDB에 부담을 주어 서비스 지연 및 오류 발생
짧은 시간 내 많은 요청 -> DB 서버 리소스 과부하
쿠폰 전용 DB가 아니라면 다른 서비스에도 영향
e.g. MySQL이 1분에 100개 Insert가 가능하다고 가정
10:00
쿠폰 생성 10000개 요청
10:01
주문 생성 요청
10:02
회원가입 요청
-> 쿠폰 생성으로 인해 100분 이후에 주문 및 회원가입 요청이 처리됨
-> 심지어 보통은 타임아웃이 있으므로, 쿠폰 생성 일부분과 주문 및 회원가입 처리 실패
Kafka로 처리량 조절하기
데이터 정합성은 Redis로 이미 확인했으므로, 쿠폰 생성만 처리
Kafka
분산 이벤트 스트리밍 플랫폼
이벤트 스트리밍: 소스에서 목적지까지 이벤트를 실시간으로 스트리밍하는 것
Producer(소스) - Topic(큐) - Consumer(목적지)
장점
API에서 직접 생성하는 것에 비해 처리량 조절이 가능 -> DB 부하 감소
큐를 사용하므로 이벤트가 하나가 끝난 후 다음 이벤트가 처리되어 DB에 한 번에 쏠리지 않음
단점
이벤트 생산과 이벤트 처리는 약간의 텀이 발생
Producer의 이벤트 생산은 매우 빠르지만, Consumer는 이벤트를 처리하느라 시간차 발생
부록: 쿠폰 1인당 1개로 제한하기
DB 레벨 제한: Unique key 사용하기
userId, couponType에 유니크 제약 조건 걸기
문제점: 보통 서비스는 한 유저가 같은 타입의 쿠폰을 여러개 가질 수 있으므로, 실용적이지 않음
락 범위 넓혀서 쿠폰 발급 여부 조회해 판단하는 로직 추가하기
쿠폰 발급 여부 판단 로직
쿠폰 발급 여부 조회: select * from coupon where userId = ?
쿠폰이 이미 있다면 발급하지않고, 미지급일 때만 발급
락 범위: 쿠폰 발급 여부 조회 ~ Redis 동시성 체크 ~ 카프카 이벤트 생산
문제점
생산자와 소비자의 시간차 때문에 쿠폰이 여러 개 발급될 수 있음
소비자에서 아직 발급 중인데 유저의 쿠폰 발급 요청이 한 번 더 온다면?
API에서 쿠폰 생성까지 하더라도 락 범위가 너무 넓어 성능 저하 발생
Set 자료구조 사용하기 (권장)
userId를 Set에 저장하면 쿠폰 발급 여부를 바로 알 수 있음
Redis도 Set을 지원하므로 활용
부록: Consumer 예외 처리하기
문제: Consumer에서 예외가 발생하면, 발급된 쿠폰 개수는 올라갔는데 쿠폰은 발급되지 않은 상황 발생
즉, 100개보다 적은 쿠폰이 발생하는 상황 발생 가능
해결책: Consumer에서 예외 발생 시, 백업 데이터(FailedEvent 테이블)와 로그 남기기
추후 배치 프로그램으로 주기적으로 실패한 이벤트를 다시 처리해 쿠폰 발급
-
동시성 문제 해결 방법
동시성 문제 해결방법
멀티스레드 작업을 하다보면, 공유 자원에 대한 Race Condition으로 인해 동시성 이슈가 발생한다
이에 대한 다양한 해결방법을 정리해보자
최종 선택 기준
synchronized는 사용 X
비용적 여유가 없고 적정한 트래픽이라면, MySQL로 처리
비용적 여유가 있고 트래픽이 많다면 Redis로 처리
MySQL VS Redis
MySQL
이미 사용 중이라면 별도 비용 없이 사용 가능
Redis 보다 성능이 떨어짐 (어느정도 트래픽까지는 문제 없이 사용 가능)
Redis
사용 중인 Redis가 없다면, 인프라 구축 및 관리 비용 발생
MySQL보다 성능이 좋음
Synchronized (거의 사용 X)
데이터에 1개의 스레드만 접근 가능하도록 하기
문제점
여러 프로세스 동작 시, 여전히 Race Condition 발생
synchronized는 하나의 프로세스 안에서만 1개의 스레드 접근 보장
다른 프로세스의 스레드가 접근하면, 여전히 여러 스레드가 접근 가능해짐
서버가 1대일 때는 괜찮지만, 2~3대부터는 데이터 접근을 여러 곳에서 할 수 있음
실제 운영 중인 서비스는 대부분 2대 이상의 서버를 사용 -> synchronized는 거의 사용 X
추가로, @Transactional 사용 시 synchronized 적용이 어려움
@Transactional은 스프링 AOP 사용으로 트랜잭션 프록시 객체를 생성
내부 동작
startTransaction();
stockService.decrease(id, quantity); (target 객체 호출)
endTransaction();
실제 DB 업데이트(endTransaction()) 전에 다른 스레드가 decrease() 메서드 호출할 수 있음
이렇게 되면, 다른 스레드는 갱신되기 전 값을 가져가 여전히 동시성 문제 발생
즉, 서비스 객체 메서드가 아닌, AOP 객체 메서드에 synchronized를 걸어야 하는데 어려움
MySQL이 지원해주는 방법
선택 기준
충돌이 빈번하게 일어날 것 같다면 Pessimistic Lock
충돌이 별로 없을 것 같다면 Optimistic Lock
Pessimistic Lock
실제 데이터에 락을 걸어서 정합성을 맞추는 방법 (for update)
Exclusive Lock을 걸게되며, 다른 트랜잭션에서는 락이 해제되기전에 데이터를 가져갈 수 없음
e.g. 서버 1이 락을 가져가면, 다른 서버(2, 3, 4…)는 락 획득 대기
데드락을 주의해야 함
장점
충돌이 빈번하게 발생한다면, Optimistic Lock 보다 성능이 좋을 수 있음
락 덕분에 데이터 정합성 보장
단점
락 자체로 인한 성능 감소 발생
Optimistic Lock
버전을 이용해 정합성을 맞추는 방법
데이터를 읽은 후 update 쿼리를 수행할 때, 현재도 내가 읽은 버전이 맞는지 확인하며 업데이트
e.g.
서버 1과 2가 버전 1인 데이터를 읽고, 업데이트 쿼리를 날림
서버 1이 업데이트 쿼리를 수행하면 해당 데이터의 버전이 2가 됨
서버 2는 읽은 버전이 1이므로, 현재 데이터와 버전(버전 2)이 달라 쿼리 실패
내가 읽은 버전에서 수정사항이 생겼을 경우, application에서 다시 읽은 후에 작업을 수행
장점
별도의 락을 걸지 않으므로Pessimistic Lock 보다 성능이 좋음
단점
업데이트가 실패했을 때 재시도 로직을 개발자가 직접 작성해야 함
Named Lock
이름을 가진 Metadata Locking
이름을 가진 락을 획득한 후 해제할 때까지 다른 세션은 이 락을 획득할 수 없음
MySQL: select get_lock('1', 1000), select release_lock(‘1’)
락 해제는 별도의 명령어로 수행하거나 선점시간이 끝나야됨
트랙잭션이 종료될 때 락이 자동으로 해제되지 않음
장점
분산 락 구현에 적합
Pessimistic Lock 보다 타임아웃을 쉽게 구현할 수 있음
삽입 시 데이터 정합성 맞출 때도 좋음
단점
트랜잭션 종료 시 락 해제, 세션 관리를 잘해주어야 함
실제 사용 시 구현 방법이 복잡
참고: 실무에서는 데이터 소스를 분리해서 사용할 것 (커넥션 풀 고갈 예방)
Pessimistic Lock과의 차이점
Pessimistic Lock은 행, 테이블 단위로 락을 걸음 (e.g. Stock에 락을 걸음)
Named Lock은 메타 데이터에 락을 걸음 (e.g. Stock이 아닌 별도의 공간에 락을 걸음)
Redis를 이용한 방법
선택 기준
재시도가 필요하지 않은 락은 Lettuce 활용
재시도가 필요한 락은 Redisson 활용
Lettuce
setnx 명령어(set if not exist)를 활용해 분산 락 구현
데이터 set (=락 획득), 데이터 del (=락 해제)
MySQL의 Named Lock과 유사
스핀락(Spin Lock) 방식
장점
구현이 간단
세션 관리도 신경쓸 필요 X
별도 라이브러리 필요 X
spring-data-redis를 사용하면 Lettuce가 기본
단점
재시도 로직을 개발자가 직접 작성
스핀 락이므로 충돌이 잦으면 Redis 부하 상승
-> 실패 시 재시도 시간에 텀을 두어 보완 (Thread.sleep(100))
Redisson
Pub-Sub 기반으로 분산 락 제공
채널을 하나 만들어 락 점유 중인 스레드가 해제를 알리면 락 획득 대기 스레드는 락 획득 시도
장점
락 획득 재시도를 기본으로 제공
Pub-Sub 기반이므로 Lettuce에 비해 Redis 부하 적음
락 해제 후 알림으로 락 획득 시도는 1번 혹은 몇 번 정도만 진행함
단점
구현이 조금 복잡함
별도의 라이브러리 필요 O
-
정보처리기사 실기 요점 정리
이론 준비 체크리스트
필수 - 모르면 시험 들어가면 안됨
테스트 종류와 방식 (블랙/화이트)
라우팅 프로토콜
데이터베이스 이론 중 약술형 나올만한 것 (로킹, 상호배제 조건 등)
결합도/응집도
테스트 스텁, 드라이버
나머지
보안용어와 암호화 기법, OSI 7계층
운영체제
메모리 관리
배치 전략: 프로세스를 통으로 메모리에 넣음
최초 적합(First-fit): 가장 앞부분부터 탐색하다가 처음 만난 곳에 넣음
최적 적합(Best-fit): 최대한 낭비 없이 넣음 (남는 공간이 가장 적은 공간에 할당)
최악 적합(Worst-fit): 제일 낭비되는 곳에 넣음 (가장 큰 공간에 할당)
가상 메모리 관리 (페이징 기법): 프로세스를 페이지로 나누어 가상 메모리로 올리고 교체
페이징 교체 알고리즘
OPT(Optimal): 향후에 가장 안쓰는 것을 빼기(미래를 알고있다 가정)
FIFO(First-in First-out): 가장 먼저 온 것을 먼저 빼기
LRU(Least Recently Used): 사용한지 가장 오래된 것 빼기
LFU(Least Frequently Used): 참조 횟수가 가장 적은 페이지 교체 (참조횟수 같을 때는 가장 오래된것 빼는걸 기본으로 하자)
MFU(Most Frequently Used): 참조 횟수가 가장 많은 페이지 교체 (참조횟수 같을 때는 가장 오래된것 빼는걸 기본으로 하자)
NUR(Not Used Recently): 최근에 사용되지 않은 페이지 교체
CPU 스케줄링 알고리즘: 여러 프로세스를 CPU가 수행할 수 있게 연산 시간을 분배하는 것
선점형: 프로세스가 CPU 사용 중일 때, 더 높은 우선순위의 프로세스가 CPU 연산 빼앗아갈 수 있음
라운드로빈(Round-Robin): 모든 프로세스에 공평하게 시간 할당, 할당시간 실행 후 가장 뒤로 세움
SRT(Shortest Remaining Time First): 남은 시간이 가장 적은 프로세스를 우선 순위로 연산, 실행 중에 남은 시간이 더 적은 프로세스가 들어오면 그 프로세스를 먼저 해결
비선점형: 한 프로세스가 CPU를 차지하면, 끝날 때까지 다른 프로세스가 점유 불가
FCFS(First come, First Served): 먼저 온 프로세스 순으로 처리 (선착순)
SJF(Shortest Job First): 실행 시간이 가장 짧은 프로세스를 먼저 처리
우선순위(Priority): 프로세스가 가지고 있는 우선순위 별로 먼저 처리
운영체제 목적
처리능력(Throughput) 향상
반환시간 단축
사용 가능도 향상
신뢰도 향상
페이지 VS 프레임
페이지: 프로세스를 자른 것
프레임: 물리 메모리를 자른 것
페이지 폴트(page fault): 프로그램이 참조하려는 페이지가 현재 메모리에 없는 상황
스레싱(Thrashing): 프로세스 처리 시간보다 페이지 교체 시간이 더 많아지는 현상
e.g. 프로세스 처리시간 1초, 페이지 교체 시간 2초
프로세스 상태 종류: 생성, 준비, 실행, 대기, 완료, 끝
교착상태: 여러 개의 프로세스가 특정 자원 할당을 계속 대기하면서 서로 소비 못하는 상황
조건: 상호배제, 점유와 대기, 비선점, 환형대기
해결방법: 예방, 회피, 발견, 복구
뮤텍스: 하나의 프로세스가 공유자원에 접근하는 동안, 다른 프로세스가 해당 자원에 접근 못하게 막는 도구 (락을 걸어줌)
세마포어: 운영체제에서 여러 프로세스가 공유 자원을 사용할 때 문제를 해결하는 동기화 도구 (보통 정수값으로 관리)
데이터 입출력 구현
데이터 모델링 순서
데이터베이스 계획 -> 요구사항 분석
-> 개념적 데이터 모델링: 개체타입, 속성 등 명시해서 현실 세계 반영
-> 논리적 데이터 모델링: 개념적 구조를 정규화하고 규칙과 관계 완성 (엔터티, 속성, 관계 구조적 정의)
-> 물리적 데이터 모델링: 레코드 양식 순서, 경로 인덱싱, 클러스터링, 해싱
이상(Anomaly): DB 조작 시 비정상적으로 동작하는 현상 (삽입 이상, 갱신 이상, 삭제 이상)
정규화
제1 정규형 (1NF): 모든 속성이 원자값
제2 정규형 (2NF): 복합키일 때, 부분 함수 종속성 없어야 함
제3 정규형 (3NF): 이행 함수 종속성 없어야 함
보이스-코드 정규형 (BCNF): 모든 결정자가 후보키 되도록 해, 모든 함수적 종속성이 후보키에 의해 결정
제4 정규형 (4NF): 다치 종속성 없어야 함 (다치종속: 1개 속성에 여러 속성이 매핑)
제5 정규형 (5NF): 조인 종속성이 없어야 함 (조인종속: 여러 테이블 조합했을 때, 현재 결과 구성 가능)
샤딩: 대규모 데이터베이스 시스템에서 여러 개 독립적인 부분으로 분할하여 성능을 향상시키는 기술
인덱스: 추가적인 저장 공간을 활용해, 테이블의 검색 속도를 향상시키기 위한 자료구조
시스템 카탈로그: 데이터베이스에 저장되어 있는 데이터 개체들에 대한 정보가 수록되어 있는 시스템
분산 데이터베이스의 목표: 위치 투명성, 중복 투명성, 병행 투명성, 장애 투명성
데이터베이스 회복기법: 즉시갱신, 지연갱신, 검사시점, 그림자 페이징, 미디어 회복기법
서버 프로그램/인터페이스 구현
서버의 종류: 웹 서버, 웹 애플리케이션 서버, 데이터베이스 서버, 파일 서버
웹 서버: 정적 컨텐츠 처리, HTTP 요청 및 응답 처리
웹 애플리케이션 서버(WAS): 동적 컨텐츠, DB 연결 처리
응집도와 결합도 (매우 중요)
응집도 (강할수록 좋음) - 모듈 내부 코드들 간
기순교절시논우 (강한순서대로)
결합도 (강할수록 안좋음) - 모듈 간
내공외제스자 (강한순서대로)
공통 모듈 구현 절차
DTO/VO -> SQL -> DAO -> Service -> Controller -> View
매우 큰 소프트웨어의 분석
FAN-IN: 특정 모듈이 호출하는 모듈 (내가 부르는 것, 기준 모듈의 하위 모듈)
FAN-OUT: 특정 모듈을 호출하는 모듈 (나를 부르는 것, 기준 모듈의 상위 모듈)
미들웨어: 서로 다른 소프트웨어 사이를 연결하는 중간다리 소프트웨어
e.g. JDBC, RabbitMQ, Apache Tomcat
인터페이스 설계: 데이터 주고 받을 때 노드 구성 방법
EAI(Enterprise Application Integration) - 큰 규모 회사
Point-to-Point
Hub-and-Spoke
Message Bus
Hybrid
ESB(Enterprise Service Bus)
서비스 지향 아키텍처(SOA)를 지원하는 기업 애플리케이션 통합을 위한 아키텍처 패턴
EAI 보완
화면설계/애플리케이션 테스트
UI 유형: CLI, GUI, NUI(Natural, 말이나 행동), OUI (Organic, 모든 사물)
UI 설계원칙: 직관성, 유효성, 학습성, 유연성
UML 다이어그램 종류
구조적 다이어그램 (대표: 클래스 다이어그램, 객체 다이어그램)
행위적 다이어그램 (대표: 유스케이스 다이어그램, 순차 다이어그램, 상태 다이어그램)
애플리케이션 테스트
정적 테스트
동적 테스트 (중요)
화이트 박스 테스트: 코드를 오픈한 상태에서 논리적인 모든 경로 테스트
기초 경로 검사(Base Path Test): 모든 독립적인 실행 경로를 테스트하는 방법
제어 구조 검사: 조건 검사, 루프 검사, 데이터 흐름 검사
블랙 박스 테스트: 사용자 요구사항 명세서 보면서 동작 원리는 모르고 기능 작동해보며 테스트
동등 분할 검사 (Equivalence Partitioning)
입력 데이터를 유사한 특성을 가진 그룹으로 나누고, 각 그룹에서 대표값 선택해 테스트
경계값 분석 (Boundary Value Analysis)
입력 값의 경계 영역을 집중적으로 테스트
결정 테이블 테스트 (Decision Table Testing)
도표, 테이블을 만들어 입력에 따라 상태 변화 체크
상태 전이 테스트 (State Transition Testing)
입력에 따라 상태가 어떻게 변하는지 테스트
유스 케이스 테스트 (Use Case Testing)
사용자의 특정 행위 (유스 케이스) 따른 시스템 동작 테스트
오류 추정 (Error Guessing)
테스터의 경험과 직관을 바탕으로 오류 추정 및 테스트
테스트 평가 지표
구문 커버리지: 프로그램의 모든 구문이 한 번씩 실행될 수 있게 테스트 데이터 선정
결정 커버리지: 전체 결정문(조건문)을 테스트 하는 방법
조건 커버리지: 조건문 내에 참, 거짓을 적어도 한 번씩 결과가 나오도록 수행
조건/결정 커버리지: 전체 조건식과 개별 조건식이 참/거짓 한 번씩 나오게 (모든 결과 테스트)
변경조건/결정 커버리지: 각 개별 조건식이 독립적으로 영향주도록 테스트
다중조건/결정 커버리지: 모든 개별 조건식 모든 조합 다 커버리지
애플리케이션 테스트 기본원리
파레토 법칙: 애플리케이션의 20% 코드에서 전체 80% 결함이 발견됨
살충제 패러독스: 동일한 테스트 케이스 반복은 더이상 다른 결함 발견 못함
오류-부재의 궤변: 오류와 결함이 없더라도 요구사항을 만족하지 않으면 소프트웨어 품질은 낮은 것
완벽한 테스트 불가능: 테스트는 결함을 완전히 없애는 것이 아니라, 결함을 발견하는데 의의가 있음
테스트 하네스: 테스트 환경의 일부분으로 테스트를 지원하기 위해 생성된 코드나 데이터
테스트 드라이버
테스트 대상 모듈을 호출하는 더미 프로그램 (상향식 테스트 시, 임시로 만든 상위 모듈)
테스트 스텁
테스트 대상 모듈이 호출하는 프로그램 (하향식 테스트 시, 임시로 만든 하위 모듈)
관계대수
관계대수는 수학적 이론이고 이것을 구현해낸 것이 현재 컴퓨터과학의 데이터베이스 (RDBMS, SQL)
주로 나올 SQL
SELECT, UPDATE, DELETE, INSERT INTO
CREATE, ALTER, DROP
CREATE INDEX, DROP INDEX
관계 대수 기호
프로젝션은 중복값을 제거하고 릴레이션 만듦
합집합도 중복값 제거하고 릴레이션 만듦
합집합: SQL의 UNION
교집합: SQL의 INTERSECT
제약조건 키워드
PRIMARY KEY, FOREIGN KEY(+REFERENCES)
UNIQUE, NOT NULL, DEFAULT, CHECK, AUTOINCREMENT
ON DELETE CASCADE, ON UPDATE CASCADE
DCL
GRANT 권한 ON 테이블 TO 유저
REVOKE 권한 ON 테이블 FROM 유저
조인
세타조인
조인에 참여하는 두 릴레이션의 속성 값을 비교하고, 조건을 만족하는 튜플만 반환
조건의 종류: =, ≠, ≤, ≥, <, >
동등조인
세타조인에서 = (는) 연산자를 사용한 조인
가장 일반적으로 통용되는 “조인연산”
자연조인
동등 조인에서 조인에 참여한 속성이 두 번 나오지 않도록, 두 번째 속성을 제거한 결과를 반환
즉, 중복된 속성을 제거
세미조인
자연조인을 한 후에, 두 릴레이션 중에 한쪽 릴레이션의 결과만 반환
왼쪽과 오른쪽 중에 제거할 속성 쪽을 열어두는 형식으로 기호 작성
프로그래밍 코드 영역 외울 것
아스키 코드
A - 65
a- 97
문자 “0” - 48
완전수 (1~100 사이)
6, 28
문자열 상수풀
리터럴을 사용했을 때는 상수풀에 넣고 재사용 (같은 리터럴은 같은 참조객체를 쓴다)
new String()은 참조값이 다른 아얘 새로운 객체 생성, 힙 영역에 저장
Integer 캐싱 (Double 같은 자료형은 캐싱이 없음)
자바는 -128~127 범위안에 정수는 캐싱해 재사용 (참조값 동일) e.g. Integer num = 100
만일, int와 Integer를 == 비교하면, 언박싱으로 인해 true 나옴
new Integer(100)은 역시 참조값이 아얘 다른 새로운 객체 생성, 힙 영역 저장
(Boolean 등등…도 new 하면 참조값 아얘 다른 새로운 객체)
비트 연산자 종류
&(and), |(or), ^(xor), ~(not)
비트연산 XOR
XOR은 비트가 서로 다르면 1, 같으면 0
같은 XOR 연산을 3번하면 두 변수 값이 SWAP됨
e.g. a = a ^ b; b = a ^ b; a = a ^ b
비트연산 ~
양수에 not을 취하는 경우 결과값: -(해당 양수 + 1)
Reference
(2024) 일주일만에 합격하는 정보처리기사 실기
-
NFT overview
NFT란?
Non-Fungible Token의 약자로 다른 것과 대체 불가능한 토큰을 뜻합니다. 예를 들어, 돈의 경우 내가 가진 1000원은 다른 사람이 가진 1000원과 동일한 가치를 지니므로 대체 가능합니다. 반면, 나의 강아지와 다른 사람의 강아지는 동일한 경제적, 정서적 가치를 가지고 대체될 수 없습니다. 즉, NFT는 디지털 컨텐츠의 고유성 및 원본임을 증명해주는데 주 목적을 둡니다.
또한, NFT는 블록체인에 저장되어 있어서 누가 언제 해당 토큰을 소유했는지 전부 기록됩니다. 따라서, 이전에는 불가능했던 디지털 자산의 소유권을 입증하는 것도 가능해집니다.
NFT의 특징
NFT를 사는 것은 컨텐츠를 사는 것이 아니라 컨텐츠로 연결된 데이터를 사는 것입니다.
NFT는 소유자가 아니더라도 누구나 열람 가능합니다.
NFT는 소유자가 아니더라도 누구나 저장할 수 있습니다.
구매한 NFT는 재판매할 수 있습니다.
NFT의 장점과 단점
Pros
자신의 명망과 취향을 자랑하고 싶은 사람들의 Ego를 충족시킬 수 있습니다.
트위터 역시 NFT를 자랑할 수 있는 탭을 만들 예정입니다.
프로필 이미지도 NFT로 만들고 블록체인 검증 체크 마크를 보여줘서, 절대 유일한 프로필 사진을 가질 수 있게 해줄 계획입니다.
Provenance(프로비넌스)
해당 예술작품을 소장한 오너들의 기록을 말합니다.
명망있는 사람이 소유한 적이 있다면, 그 사실 자체가 예술작품의 가치 상승에 반영되기도 합니다.
NFT 덕분에 소유자의 기록이 모두 기록되어 누구나 확인이 가능합니다.
Cons
가치가 크게 변동되는 시장으로 인해, 투기꾼들이 몰리다보니 이미지 자체도 하락합니다.
막대한 전기를 소모하여 환경 오염을 촉발시킵니다.
NFT에 Contents를 삽입하는 방법
NFT 토큰을 만드는 큰 그림
두 가지 기능을 가진 Smart contract 만들기
돈을 받는 기능 (이더나 달러를 받으면)
토큰을 전송하는 기능 (1개의 토큰을 줄게)
해당 토큰은 1개의 유일한 토큰이 됩니다. 그리고 1개의 유일한 토큰에 이미지, 영상, 전세 계약 등을 심으면 NFT가 됩니다.
용어
ERC721: NFT의 스탠다드. ERC 20에 토큰 ID, 메타데이터 JSON 파일이 추가된 형태
토큰 ID: NFT에 붙는 개별 식별 번호
메타데이터 JSON: NFT에 넣을 정보 및 컨텐츠가 담기는 그릇
IPFS: 위변조가 불가능한 어찌보면 블록체인과 비슷한 분산 저장소 (모든 정보를 블록체인에 담기는 비싸므로 IPFS를 항상 함께 사용함)
실제 과정
원하는 컨텐츠(이미지, 영상 등)을 IPFS 올리고 hash 값을 받습니다.
메타데이터 JSON에 hash 값을 삽입합니다.
해당 메타데이터 JSON을 IPFS에 올리고 hash 값을 받습니다.
ERC721 민터 코드를 디플로이합니다. (코인을 내고 블록체인에 올립니다)
NFT를 민트합니다. (코인을 내고 NFT를 발행합니다, json hash값 주소와 wallet 주소도 적용)
NFT 전송 과정
NFT를 manually하게 구매 및 판매할 때
지갑을 설치합니다. (Ethereum 기준으로 MetaMask wallet을 설치합니다.)
MetaMask Mobile app을 엽니다. (NFT 거래는 아직 모바일에서만 가능합니다.)
NFT 탭에서 보내길 원하는 NFT를 고릅니다.
상대방의 Ethereum 주소를 입력합니다.
Transaction (gas) fee를 지불합니다.
Etherscan을 통해 Ethereum 블록체인에서 해당 transaction을 등록합니다.
NFT를 manually하게 구매할 때
판매자에게 MetaMask wallet 주소를 보냅니다.
판매자가 NFT를 전송했다면, transaction ID 전달도 함께 요청합니다.
Etherscan과 transaction ID를 통해 해당 transaction이 정상적으로 확인되면, 정식적으로 구매한 NFT의 소유주가 됩니다.
다만, NFT 마켓플레이스를 이용하는 것이 보다 안전하게 구매하는 방법일 것입니다.
Opensea에서 판매자는 입력폼에 컨텐츠를 첨부하고 가격을 정한 후, 트랜잭션 수수료를 내면 해당 컨텐츠를 NFT로 만들어 판매할 수 있습니다.
OpenSea에서 구매자는 원하는 NFT를 골라 MetaMask로 NFT 가격과 gas fee 지불하면 해당 NFT를 구매할 수 있습니다.
Reference
How to Transfer an NFT: Step by Step Guide to Do it Right
NFT 광풍? 혁신일까, 마케팅일까? 개발자가 정리해드림.
-
블록체인 Overview
블록체인(Blockchain)의 개념
공식적 정의
Blockchain Security Technology라고 하며, 데이터를 담은 블록을 체인 형태로 연결한 다음 동시에 수많은 컴퓨터에 복제와 저장을 하는 분산형 데이터 저장 기술을 말합니다. 중앙 서버에 거래 내역을 저장하지 않고 거래를 할 때마다 수많은 거래 참여자들에게 거래 내역을 공유하기 때문에 위조, 변조가 거의 불가능합니다.
특징
Append(추가): 블록체인에서 블록은 오직 추가만 할 수 있습니다.
Decentralization(탈중앙화): 특정 개인이 블록체인 DB를 관리할 수 없습니다.
쉬운 개념 설명
블록체인은 일종의 데이터베이스로, 각각의 블록은 데이터로 생각할 수 있습니다.
특별한 점은 데이터를 추가(Append)만 할 수 있다는 것입니다. 블록체인 DB에 연결된 블록 데이터는 편집 및 삭제가 불가능합니다. 대학교 학위, 정부 단위에서 운전면허증, 지원금 사용 내역, 혹은 개인의 전세 계약서 등 안전한 보관이 필요한 영역에서 데이터를 기록하는데 활용될 수 있습니다.
또한, 블록체인 참여자 모두가 DB의 복제본을 가지고 있어서(Decentralization) 특정 개인이 DB를 관리할 수 없습니다. 특정 개인이 갑자기 100억원어치 비트코인을 가지고 있다고 DB를 조작해도 다른 사람들의 블록체인 사본과 비교하면 쉽게 거짓이 감별됩니다. 이로 인해, 나아가 정부의 독단적인 감시나 통제에도 공동으로 대응할 수 있습니다. 이 검증이 확실한 동작하는 이유는 너무나 많은 사람들이 비트코인 노드를 돌리고 있어 모든 컴퓨터가 일제히 종료되어 있지 않는한 공동의 감시를 벗어나기 어렵기 때문입니다. 현실적으로 모두의 컴퓨터가 동시에 종료되는 일은 불가능합니다.
블록체인 생성 과정
블록의 형태
블록은 정보를 DB에 추가하는 방법입니다. 그리고 하나의 블록은 block hash, 이전 블록의 block hash, block data로 구성됩니다.
먼저 block data에는 해당 영역의 data가 담깁니다. 비트코인의 경우 거래 내역(transaction) 데이터가 해당됩니다. 그리고 현재 블록의 block hash에는 이전 block의 hash 값과 지금의 데이터를 함께 해시한 값이 담깁니다. 이렇게 해시를 통해 블록을 쌓아가면, 블록체인에 어떤 작은 조작만 가해도 결과로 나올 해시값이 크게 달라지기 때문에 보안이 안전하게 유지됩니다.
해시(Hash)란?
어떤 데이터를 특정 알고리즘을 사용해 고정된 길이를 가진 기묘한 모습의 데이터로 변환시킨 값을 말합니다. 해시를 사용하면 Input 데이터로 Output 데이터를 만들 수 있지만, 만들어진 Output data를 Input data로 되돌릴 수는 없습니다.
채굴자(Miner)의 역할
비트코인의 경우에는 10분마다 블록이 생성되어 블록체인에 추가됩니다. 그렇다면 블록에는 어떠한 데이터가 담겨야 하고, 이 블록을 생성하는 사람은 누구일까요?
우선 블록체인에 추가될 데이터는 상황에 따라 누구든지 올릴 수 있게 하기도, 아무나 올릴 수 없게 하기도 합니다. 아무나 올릴 수 없게 하는 경우는 안전과 정확함이 중요한 금융 관련 데이터, 정부 관련 데이터 등이 해당될 것입니다. 이러한 경우, 데이터에게 요구되는 속성은 진실성(Truth)입니다. 블록체인에 추가될 데이터는 거짓 없는 진실이어야 합니다. 그리고 데이터의 진실성 검증은 작업 증명(Proof of Work)을 통해 이뤄집니다. 작업 증명은 전 세계 채굴자(Miner)들에 의해 진행됩니다.
채굴(Mining)이란 주어진 데이터를 작업 증명하여 블록을 생성 및 추가한 후, 이에 대한 보상으로 해당 거래에 대한 수수료와 암호화폐(Cryptocurrency)를 받아가는 작업을 말합니다. 조금 더 자세히 얘기하면, 작업 증명이란 네트워크가 내는 어떠한 문제를 해결하는 것이고 이를 완료하면 이에 대한 보상이 지급됩니다. 채굴 작업은 전 세계 누구든지 참여할 수 있으나 해결해야 할 문제의 난이도는 시간이 지날수록 더욱 높아집니다.
채굴자의 작업은 단순합니다. 채굴자는 오직 Nonce라는 파라미터만 조작할 수 있고, 문제에서 원하는 해시값을 생성해내는 Nonce를 찾아 네트워크에게 알려주면 됩니다. 예를 들어, 해시값이 3개의 0으로 시작하기 위해서 어떠한 Nonce를 사용해야 하는지가 문제로 제시되면, 채굴자는 Nonce 값을 일일이 넣어보면서 해시값을 생성하고 비교해 완전 탐색으로 Nonce 값을 찾아냅니다.
문제를 푼 채굴자들은 블록을 생성해 해당 블록체인에 추가하고, 작업 증명에 대한 보상으로 해당 거래에 대한 수수료와 코인을 받아갑니다.
암호화폐(Cryptocurrency)
암호화폐는 채굴자들의 작업 증명 보상으로서 주어집니다. 이 때, 암호화폐는 채굴자의 작업 증명과 함께 새로 생성됩니다. 예를 들어 비트코인은 채굴자가 작업 증명을 함과 동시에 생성되어 채굴자의 지갑으로 지급됩니다.
암호화폐들은 생산량이 제한 되어 있는 경우에 가치가 상승합니다. 비트코인의 경우, 2100만개로 생산량이 한정되어 있어 희소성을 가집니다. 다만, 한정된 생산량에 너무 빨리 도달하지 않게끔 하기 위해 생산이 너무 빨리 진행되면 문제의 난이도를 올리는 등의 조정을 취합니다. (현재 난이도는 19이며, 생성하는 해시값이 19개의 0으로 시작해야 합니다.) 특히, 4년마다 한 번씩 반감기라는 것을 가지면서, 처음엔 작업 증명으로 50개씩 발급되었던 비트코인이 25, 12.5를 거쳐 현재는 6.25개씩 지급되고 있습니다.
그래픽카드(Graphic Card)
최근 그래픽 카드의 값이 급등하는 이유는 채굴 작업과 관련이 깊습니다. 그래픽 카드는 Nonce 탐색을 매우 빠르게 진행하기 때문입니다. 대략 6천만 Nonce를 1초만에 계산한다고 하니, 채굴자들은 그래픽 카드를 가능한한 총동원해 코인 채굴에 사용하길 원합니다.
Smart Contract
블록체인의 공유 네트워크를 활용해서 개인의 코드를 모두가 공유, 검증, 실행하지만 수정은 할 수 없는 백엔드에 올리는 작업을 말합니다. 예를 들어, Airbnb같이 집을 중개해주는 서비스 도움 없이 개인이 직접 도어록 같은 센서에 기반한 코드(봇)를 만들고, 이 코드를 통해 서로 금액을 교환할 수 있습니다. 덕분에 개인과 개인 사이에 중개인이 필요없어지고 정부와 단체에 조종당할 염려가 사라집니다. 다만, 네트워크 외부의 IoT 센서 혹은 무언가에게 의존해야하는 신뢰기반 거래가 형성되어 버려서, 센서가 해킹당했을 때의 위험성이 단점으로 존재합니다. 스마트 컨트랙트를 지원하는 블록체인은 Ethereum, Polcadot, Cardano, Kusama 등 다양합니다.
대체 불가능한 토큰 (NFT, Non Fungible Token)
Non Fungible이란 땅, 포켓몬 카드, 한정판 신발 등 대체 불가능한 것들을 말합니다. 즉, NFT는 어떠한 블록체인 기술을 기반으로 이미지, 영상 등의 콘텐츠에 고유한 표식을 부여하는 디지털 자산입니다. 이러한 표식은 해당 자산이 원본임을 증명해줍니다. 이로 인해, 디지털 재산권 개념과 함께 디지털 자산에 대한 소유권 주장이 가능해집니다.
DeFi
탈중앙화된 금융 서비스를 의미하며, 중간 거래자나 서드파티없이 스마트 컨트랙트로 모든 거래가 오고 가는 형태를 띕니다. 사람의 손을 전혀 거치지 않고 100% 코드로 돌아가기 때문에, 중간에서 중개 수수료가 지불되거나 관리자에 의해 계좌가 동결될 일이 없습니다.
Reference
블록체인. 개발자가 쉽게 설명해드림. - 노마드 코더
BlockChain 기초 개념 - 블록, 채굴, 작업증명, 난이도, 보상
미국 기자들 ‘교과서’ “암호화폐 ‘크립토’로 줄이지 말라”
-
Git 기초
Git 기초
Git 기본 이용 사이클
Git 저장소 선언하기 (Initialization & Repository)
Git 저장소를 흔히 Repo(레포)라고 부르며, Repo는 git으로 관리하는 하나의 메인 저장소를 뜻함
사용자가 변경한 모든 내용 추적 가능
현재 상태, 변경 시점, 변경한 사용자, 설명 텍스트 등
관리할 폴더에서 git init 을 통해 선언
주목할 특징
Git은 이제 Local에서 모든 것을 저장 및 버전 관리 가능하고 나중에 원격 서버에 올려도 상관 없음
Git은 데이터를 추가만 할 수 있음
파일 삭제 == 삭제 기록 추가 (물론 온전한 삭제도 가능하지만, 버전 관리에서는 삭제 기록도 매우 중요)
Git은 파일을 추적하지 않음
파일의 내용 단위로 각 문자와 줄을 추적
빈 디렉토리는 추적하지 않음
파일 추가/수정/삭제
변경 사항 선택
파일 상태
파일은 추적 여부로 구분해 tracked, untracked 파일로 나눌 수 있음
tracked 파일은 Unmodified(수정 없음), Modified(수정 있음), Staged(저장(커밋)을 위해 준비됨) 상태로 구분됨
스테이징(Staging)을 통해 커밋하고 싶은 파일 선택
스테이징이 필요한 이유?
여러 작업 중 일부분만 커밋해야 할 때
커밋 전 상태를 수정 또는 체크할 때 (안전하게 커밋하기 위함)
상태 업데이트
커밋(Commit)을 통해 새로운 버전으로 업로드
커밋을 하면, 각 버전마다 40자리의 숫자 + 알파벳 조합으로 이루어진 해시값이 존재
해시 값은 버전의 주소(Key)
해시값은 내용(파일 구조)을 사용해 생성됨 (파일이 어떤 폴더 밑에 있고, 어떤 내용이다 등의 상태를 40자리로 표현)
Commit Hash 값으로 Checkout하면 버전 이동 가능
Git Branch
소프트웨어 버전 넘버링 상식
보통 세가지 숫자로 표현되며, 위 그림과 같이 각각 의미가 다름
마지막에 알파벳이 들어가는 경우도 있지만, 보통은 모두 숫자로 사용
1.1, 2.1 같이 두 개로 표현된 경우, 1.0.1, 2.0.1을 뜻함
브랜치 (Branch)
버전 관리시 수많은 오류를 개발자들이 각각 따로 처리하는 상황이 발생하는데, 이를 위해 브랜치 개념이 등장
브랜치는 시간의 흐름의 축
Git 명령어
프로젝트 총 관리자 및 시작자 관점
프로젝트 시작 선언
git init
.git 파일이 생성됨 (처음엔 숨김 상태라 안보임)
모든 버전 관리 정보가 담겨있으므로 조심해야 함 (버전을 초기화하고 싶을 땐, 이 폴더를 통째로 삭제하면 됨)
로컬에서 Git 초기화 진행
시작 버전은 master branch에 기록
.gitignore 파일을 생성해 양식(정규표현식)에 맞춰 작성하면, 저장하고 싶지 않은 파일들을 무시할 수 있음
README.md 파일 작성
Repo의 메인 페이지 역할
프로젝트 설명 및 사용방법, 라이센스 등을 기술
유저 이름과 이메일 등록하기 (log에 남기는 용도)
git config --global user.name="[이름]" : 깃 설정 파일을 내 모든 컴퓨터에 적용, 그 중 이름 정보 입력하겠다.
git config --global user.email="[이메일]" : 깃 설정 파일을 내 모든 컴퓨터에 적용, 그 중 이메일 정보 입력하겠다.
파일 스테이지로 올리기
git add [file]
[file]을 스테이지로 올림 (폴더나 전체도 가능)
스테이지에서 내리기
git restore --staged [file]
파일 상태 체크하기 (습관적으로)
git status
git diff
스테이지에 있는 내용 커밋
git commit -m "add README.md"
간단한 설명과 함께 커밋
커밋 기록 살펴보기
git log
조금 더 시각적으로 편하게 살펴보는 방법
git log --all --decorate --graph --oneline
위 방법에 간단한 별명을 붙이는 방법
git config --global alias.adog "log --all --decorate --graph --oneline"
원격 저장소와 연결
git remote add origin [url]
origin이라는 이름으로 [url]과 연결 (origin은 원격 저장소에 관용 표현이므로 변경 가능)
연결 여부를 확인하는 명령어
git remote -v
원격 저장소로 올리기
git push origin master
원격 저장소 master branch에 업데이트
로컬과 원격 저장소가 동기화됨!
버전 이동하기
git log 로 원하는 버전의 해시값 확인
만일 과거로 돌아와 미래 로그가 안보인다면, git log --all
git checkout [40자리 해시값의 앞 7자리]
예시
git checkout a703380
git checkout master
협업하는 개발자 시점
원격 저장소 다운받기
git clone [url]
파일 구조, 로그를 포함한 모든 것이 다운로드됨
기능별로 개발하기
master 브랜치는 배포 버전이므로 함부로 커밋하기 어려움… 필요한 기능은 병렬적으로 가지쳐서 개발하자!
git branch [name]
처음에는 master를 가리키는 것처럼 보이지만 커밋하면 branch를 가리키는 것을 확인할 수 있음
[name] 없이 git branch를 쓰면, 현재 브랜치가 무엇인지 확인 가능
브랜치/버전 이동하기
git checkout [name]
브랜치 합치기
git merge [name]
[name] 브랜치를 현재 브랜치로 합치기
기능 완성 후, master와 합치는데 주로 이용
같은 파일만 건드리지 않았으면 문제 없이 병합 가능!
가지가 복잡한 브랜치 합치기
git rebase master - 자주 사용!!
base(기준점)를 master의 끝 점으로 re-base(재설정)해서 그래프를 한 줄로 만듬
브랜치 지우기
git branch -d [name]
완료했거나 필요가 없어진 브랜치를 삭제
프로젝트 리더 시점
다른 개발자가 원격에 메인 버전을 업데이트 하면, 최신 버전을 다운받아 오고 싶음
원격에서 기록 가져오기
git fetch - 자주 사용!!
원격 저장소와 동기화하지만 merge는 하지 않음
동일 파일을 건드리는 Conflict를 방지하기 위해 미리 체크할 수 있음
원격에서 기록 가져오고 합치기
git pull
원격 저장소와 동기화하고 merge까지 진행
충돌을 일으킨 개발자 시점
같은 파일의 같은 부분을 수정하고 합칠 때는 충돌(Conflict)이 발생함
커밋 되돌리기(reset, revert), 직접 충돌 부분 수정하기 등 다양한 해결법 존재
실수한 커밋을 RESET하기
git reset [option] [commit의 7자리 해시값]
해당 커밋 이후 기록을 없애기 (Hard, Mixed, Soft)
커밋으로 프로젝트가 망하면, 원하는 커밋으로 reset 가능
이미 원격 저장소에 올라가 있는 경우 사용해서는 안됨! 다른 개발자들과 버전이 달라져버린다…
실수한 커밋도 내 커밋, 기록하자!
git revert [commit의 7자리 해시값] - 가장 좋음!!
되돌릴 커밋이 여러개라면 범위를 주어 한번에 되돌릴 수 있음
git revert [commit의 7자리 해시값]..[commit의 7자리 해시값]
선택한 커밋 하나만 되돌리고 다른 커밋 내용은 그대로 둠, 수정한 기록도 남김
협업하는데 커밋 로그를 함부로 지우면 서로 버전이 이상해질 수 있으니 revert로 수정 기록 남기자!
다만, revert 쓰는 것 보다도 가장 좋은건 수정해서 그냥 커밋을 하는 것이 아닐까?
브랜치 바꿔야하는데 커밋은 하기 싫을 때
현재 무언가 작업 중일 때 브랜치를 바꾸면, 작업 중인 내용이 바뀐 브랜치로 따라옴
git stash
현재 작업하고 있는 작업물을 따로 추적하지 않는 저장파일에 저장하기
Reference
티아카데미 Git & GitHub page 블로그 만들기
초보용 git 되돌리기
-
Git의 발전 과정
Git의 발전 과정
Git 탄생 배경
Git은 분산형 버전 관리 시스템 (DVCS, Distributed Version Control System)
처음엔 리눅스 오픈 커뮤니티에서 BitKeeper(회사)의 DVCS를 사용했으나, 이익을 추구하는 기업과 오픈 커뮤니티와의 상충이 발생
리눅스 창시자 Linus Tovalds를 중심으로 2005년 리눅스 오픈 커뮤니티에서 자체 툴로서 제작
버전 관리 시스템 (VCS, Version Control System)
파일의 변경 사항을 저장하고, 원하는 시점의 버전을 다시 꺼내올 수 있는 시스템
CVCS(중앙집중식 VCS) VS DVCS(분산형 VCS)
중앙집중식 버전 관리 시스템 (CVCS, Central Version Control System)
하나의 중앙 서버를 두고, 해당 서버에서 버전을 관리
최신 버전으로 업그레이드하기 위해, 최상단의 버전을 다운받고 수정해 업데이트하는 방식
메인 서버에 접속하지 않으면 로컬에서 개발 불가
메인 서버가 폭파되면 큰일남
효율적이지만, 여전히 협업의 불편함이 있음
분산형 버전 관리 시스템 (DVCS, Distributed Version Control System)
메인 서버와 개발자들의 컴퓨터 각각에 모든 코드와 파일 변경 정보들이 분산되어 버전을 관리
메인 서버에 파일들이 어떻게 수정되었고, 누가 변경했는지 등의 정보가 저장됨
로컬에서 버전 관리하고 메인 서버에 올릴 수 있음
메인 서버가 폭파되어도 버전들 생존 가능
보다 효율적인 협업 가능
Subversion (SVN) - CVCS
CVCS의 대표적인 시스템 중 하나
파일의 모든 변경 사항을 저장
초기에 File A, B, C를 각각 만든다면, 각각의 파일마다 변경 사항 델타를 따로 저장
특정 버전을 다운 받을 때, 초깃값에서 해당 버전까지의 델타들을 합한 값인 파일을 다운받아 관리
Git - DVCS
특정 버전에 해당하는 모든 정보와 파일들을 스냅샷으로 찍어 관리
버전이 수정된 파일은 수정본을 올리고, 수정되지 않은 파일은 해당 파일이 존재하는 버전으로 연결되는 링크를 저장
최신 버전의 스냅샷만 유지하고 이전 버전은 델타로 관리
Reference
티아카데미 Git & GitHub page 블로그 만들기
-
-
모두를 위한 딥러닝 Part 2
딥러닝으로 XOR 문제 풀기
# XOR 문제
XOR 문제는 두 변수 x1, x2가 같다면 False를, 다르다면 True를 반환하는 문제이다. 많은 Neural Network 연구자들이 이 문제를 해결하기 위해 애썼지만, 이 문제는 하나의 유닛으로는 해결할 수 없다는 것이 수학적으로 증명될 정도로 해결하기 어려웠고 ‘Neural Network는 안된다’는 인식이 강해졌다. 이후 여러 개의 유닛을 사용하면 XOR 문제를 해결할 수 있음이 알려졌지만 어떻게 매개변수 w, b를 학습할 수 있을지에 대해 다시 한번 장벽에 부딪혔다. 하지만, 결국 Neural Network는 XOR 문제를 극복해냈는데 지금부터 어떻게 이것이 구현됐는지 살펴보고자 한다.
# Neural Network로 XOR 문제 해결하기
1. Forward Propagation
왼쪽 그림은 세 개의 유닛을 쌓아 만든 간단한 Neural Network 모델이다. 이 모델을 이용하면 XOR 문제를 해결할 수 있는데, 오른쪽 그림처럼 모델 유닛들의 가중치 w와 편향 b가 주어지면 XOR 문제에서 주어진 x1, x2를 모델에 입력하여 결과를 계산할 수 있다. 첫 번째 유닛은 행렬 연산을 수행하면 -8이라는 값이 나오고 이를 시그모이드(sigmoid) 함수에 넣으면 0에 매우 근접한 값 y1을 얻을 수 있다. 두 번째 유닛도 같은 방식으로 계산해 1에 매우 근접한 값 y2를 얻을 수 있으며, 계산을 통해 y1, y2를 세 번째 유닛에 넣고 계산하면 XOR 문제가 요구하는 답인 0을 얻을 수 있다.
나머지 XOR 문제도 같은 Neural Network로 연산하면 XOR 문제를 해결하기 위한 답을 도출할 수 있다. 이렇게 주어진 모델에 input을 넣고 output을 출력해내는 과정을 Forward Propagation이라고 한다.
2. Back Propagation
앞선 예시의 경우 Neural Network 모델의 w, b가 주어진 상태로 연산을 진행했다. 사실 단순한 얕은 Neural Network의 경우 XOR 문제를 해결할 수 있는 w, b를 사람이 직접 찾을 수 있다. 하지만 더욱 깊은 Neural Network의 경우 w, b를 직접 찾는 것은 불가능하다. 그렇다면 w, b가 주어지지 않았을 때는 어떻게 모델을 학습하여 w, b를 자동적으로 계산해낼 수 있을까?
가중치 w, 편향 b를 자동적으로 계산해내기 위해서는 Gradient Descent 알고리즘이 필요하다. 이를 위해 미분을 통해 기울기를 구하는 작업이 동반된다. 그러나 가중치를 조정하기 위해서는 네트워크 각 layer의 input들이 output y에 미치는 영향을 알아야 하는데, 깊은 네트워크일수록 x에 대한 미분을 구하기가 어려워 x가 y에 미치는 영향을 파악하기 힘들다. 심지어 MIT AI Lab의 Marvin Minsky 교수는 Perceptrons(1969)에서 아무도 이것을 학습시킬 수 없다고 말하며 당시 Neural Network의 암흑기가 도래했다.
이러한 학습의 난관을 Paul Werbos가 논문을 통해 Backpropagation 방법을 제시하며 해결했다. 당시에는 Neural Network가 주목받지 못해 Paul의 논문은 조명받지 못했지만, 사장됐던 Backpropagation을 Geoffrey Hinton 교수가 자신의 방법으로 다시 제안하며 Neural Network는 다시 관심을 받게 되었다.
그들이 제시한 Backpropagation은 예측값과 실제값 사이의 오류(Error)를 Cost 함수를 이용해 뒤에서부터 앞으로 보내 미분값을 구하고 w, b를 어떻게 수정할지 계산하는 방법이다. 여기에서는 미분의 공식 중 하나인 chain rule이 강력한 도구로 활용된다. 위 그림 같이 f 함수를 g 함수와 b에 대하여 미분한 값들과 g함수를 w와 x에 대해 미분한 값들을 단순히 계산해내면, chain rule을 통해 f 함수를 w와 x에 대해 미분한 값들을 쉽게 구해낼 수 있게 된다.
네트워크의 layer가 매우 많은 모델에 대해서도 chain rule을 통해 하나하나 미분값을 알아내가면 결국 위 그림처럼 f를 x, y에 대해서 각각 미분한 값들을 알아낼 수 있게 되고, 결국 w, b에 대해서도 미분값을 알아내어 Gradient Descent를 실행할 수 있게 된다. 이러한 Backpropagation은 매우 깊은 네트워크도 충분히 학습 가능하게 하는 딥러닝의 강력한 도구이다.
ReLU 함수
# Backpropagation에서 나타나는 Vanishing gradient 문제
모델의 층을 많이 쌓을수록 보다 다양한 문제를 풀 수 있지만, backpropagation을 하며 input이 output에 미치는 영향을 알아볼 때 chain rule을 통해 얻은 미분값이 너무 작아져 학습이 잘 안되는 문제가 생긴다.
위와 같이 곱셈에 대한 오차역전파를 구할 때 x에 대한 g(x)의 미분 값은 y가 되는데, y는 시그모이드 함수를 통과한 출력 값이라 범위가 0~1 사이로 고정된다. 만일 x = -10이어서 y가 0에 근접한 수를 갖게 된다면, x에 대한 f(x)의 미분값은 g(x)에 대한 f(x)의 미분값 곱하기 y이기 때문에 0에 근접한 값을 갖게 된다. 이러한 과정을 계속 반복해 더욱 0에 가까운 값으로 경사하강법을 수행하면 매개변수 w와 b의 학습이 제대로 이뤄지지 않게 된다. 이러한 문제를 경사도가 사라진다는 의미에서 Vanishing gradient라고 한다.
이 Vanishing gradient 문제로 인해 2~3단계를 넘어가는 층을 가진 Neural Network는 학습시킬 수 없다는 좌절에 빠지게 되고, Neural Net은 1986년~2006년까지 2차 겨울을 맞이 하게 된다.
# ReLU (Rectified Linear Unit) 함수
Geoffrey Hinton 교수는 Vanishing gradient의 원인을 시그모이드 함수에서 찾았다. (We used the wrong type of non-linearity) 시그모이드 함수는 항상 1 미만의 값을 갖는 한계점이 있기 때문에, 입력이 0 이하인 범위에서는 y = 0 함수를 갖고 입력이 0 초과인 범위에서는 y = x 함수를 갖는 ReLU 함수를 시그모이드 함수의 대안으로 제시했다.
Neural Net에서 Activation function으로 단순히 ReLU를 사용하면 해당 network의 학습을 원할하게 진행시킬 수 있다. 즉 층이 2~3 층정도로 적은 모델의 경우 기존의 시그모이드를 사용해도 괜찮지만, 이보다 더 많은 층수를 가진 모델은 ReLU를 사용해야 학습 진행이 가능하다.
실제로 Neural Net에 ReLU 함수를 적용 시, 미동이 없는 시그모이드에 비해 ReLU가 빠르게 Cost 함수를 줄여나가는 것을 관찰할 수 있다.
ReLU 함수에서 더 나아가 Leaky ReLU, tanh, Maxout, ELU 등의 다양한 activation 함수들이 존재하고 상황에 따라 효과적인 함수를 선택해 사용할 수 있다.
위의 activation 함수들을 각각 사용하여 CIFAR-10 이미지 데이터에 대한 정확도를 측정해본 결과, 실제로 시그모이드를 제외한 다른 activation 함수들은 큰 문제없이 높은 정확도를 기록하는 모습을 살펴볼 수 있다.
Weight initialization
# 가중치 초기화 (Weight initialization)
1. Zero initialization
Geoffrey Hinton 교수가 찾아낸 Neural Network가 제대로 동작하지 않는 이유들 중 하나는 가중치 초기화의 문제이다.
예를들어, 극단적으로 가중치를 0으로 초기화하면 backpropagation으로 구한 x에 대한 f(x)의 기울기 값이 0이 되어 기울기가 소실되는 현상이 발생한다. 이 경우 학습이 원활하게 이뤄지지 않기 때문에, 가중치 초기화의 첫번째 유의점은 ‘w를 모두 0으로 설정하지 말자.’이다.
2. RBM을 이용한 initialization
가중치를 어떻게 설정해야할 지에 대한 문제는 깊이 들어갈수록 어려워졌는데, Hinton 교수는 A Fast Learning Algorithm for Deep Belief Nets (2006)이라는 논문을 통해 RBM(Restricted Boatman Machine)으로 Neural Network의 가중치를 초기화하자고 제안했다. RBM을 사용해 가중치를 초기화한 Neural Net을 Deep Belief Nets이라고 한다.
RBM이란 Input을 모델에 넣어 Output을 구하고 (encoder), 구한 Output 값에 다시 가중치를 곱하여 Input 값을 예측해낸 뒤 (decoder), 원래의 Input 값과 예측한 Input 값의 차가 최저가 되도록 (두 값이 거의 동일해지도록) 가중치를 조정해나가며 초기값을 설정하는 방법이다.
전체 네트워크에 대해 RBM을 사용해 가중치를 학습시키는 것을 Pre-Training이라고 한다. 이것을 구현하기 위해 먼저 인접한 첫 번째와 두 번째 레이어에 대해 encoder와 decoder로 값을 비교하여 가중치를 학습시키고 다음 인접한 두 번째, 세 번째 레이어에 대해서도 똑같은 과정을 반복하며 네트워크의 끝까지 모든 레이어에 대해 가중치를 학습시킨다.
Pre-Training을 거쳐 가중치를 초기화한 모델을 원래대로 training data를 주고 학습시키는 것을 특별히 Fine Tunning한다고 말한다. Pre-Training을 통해 이미 가중치들이 생각보다 잘 학습되어서 좋은 초기값을 제공하므로 살짝의 튜닝만 한다는 의미에서 training 과정을 다르게 지칭한다.
3. 더욱 simple한 초기화
RBM을 사용해 가중치를 초기화하는 과정을 하나하나 거쳐가는 것은 복잡하다. 초기화 과정을 더욱 간단하게 진행하기 위해 Xavier initialization, He initialization 등의 초기화가 등장했다. 이 두 방법은 input 값의 개수와 output 값의 개수를 고려하여 초기화를 진행하는 방법인데, 위 식과 같이 초기화를 진행하면 RBM을 사용한 것과 동일한 혹은 더 나은 성능을 보여준다고 알려져 있다. 흥미로운 점은 He initialization이 Xavier initialization의 식에서 단순히 분모 루트 안의 값을 2로 나눠주는 것으로 Xavier initialization보다 더 좋은 성능을 보여준다는 사실이다.
이러한 가중치 초기화에 대한 연구는 아직도 활발히 진행 중이므로 여러가지 초기화 방법에 대해 알아두고 데이터마다 다르게 적용해볼 필요가 있다.
Dropout과 Ensemble
# Overfitting 문제
뉴럴 넷의 가중치 변수를 많이 생성하고, 레이어를 깊게 쌓을수록 해당 모델은 training data에 오버피팅될 가능성이 높다. 오버 피팅을 해결하기 위해서 1. training data를 더욱 늘리거나 2. feature(x 변수)를 줄이거나 (이 방법은 굳이 사용하지 않아도 괜찮다.) 3. Regularization(정규화)를 진행해 모델의 하이퍼플레인(선)을 보다 평탄하게 해주는 방법이 있다. (= 가중치에 너무 큰 값을 배정하지 않게 하자!)
# 드롭아웃 (Dropout)
그러나 뉴럴넷 모델이 복잡해지면 정규화만으로 오버피팅에 대응하기 어려워지는데, 이 때 Dropout 기법이 사용된다. Dropout: A Simple Way to Prevent Neural Networks from Overfitting (Nitish Srivastava et al. 2014) 논문에 따르면 Dropout은 랜덤하게 어떤 뉴런들의 연결을 끊고 해당 뉴런을 비활성화시켜 뉴럴넷을 보다 간소하게 만드는 방법이다. 힘들게 만든 뉴런들을 왜 없애는 지에 대한 강한 의문이 들 수 있지만, 이 방법은 생각보다 큰 성능 상승 효과를 가져온다.
각 뉴런들은 각자 한 분야의 전문가 역할을 한다. 예를 들어, 고양이인지 아닌지 판단하는 뉴럴 넷을 사용한다고 할 때, 어떤 뉴런은 귀를 갖고 있는지 아닌지 판단하는 전문가, 어떤 뉴런은 꼬리를 가지고 있는지 판별하는 전문가, 어떤 뉴런은 털이 있는지 없는지 판단하는 전문가이다. 이런 뉴런들 중 몇몇을 쉬게 하여 학습을 진행하는 것이 Dropout 방법이다.
Dropout을 텐서플로우에서 적용하는 방법은 단순히 활성화 함수까지 있는 레이어에 Dropout 레이어를 하나 추가해주는 것이다. 이 때 dropout_rate를 설정해줘야 하는데, 보통 0.5로 설정하여 매 레이어마다 랜덤하게 학습하는 뉴런을 다르게 한다. Dropout에서 주의할 점은 training할 때만 적용해야 한다는 점이다. 학습시킬 때는 Dropout으로 랜덤하게 뉴런들을 학습시키고 test할 때는 dropout없이 모든 뉴런을 사용해서 예측해야 한다.
# 앙상블 (Ensemble)
모델의 성능을 높이는 방법 중 하나로 앙상블(Ensemble) 기법이 있다. 예를들어, 위 그림과 유사하게 9개의 층을 가진 신경망 모델이 k개 있다고 하면, 이를 각각 모두 학습시킨 후 예측한 결과들을 마지막에 통합하여 최종적인 예측 결과를 도출할 수 있다. 마치 여러 전문가에게 의견을 물은 후 투표를 통해 최종적인 결론을 내리는 것과 유사한 이 방법은 적게는 2%에서 많게는 4~5%까지 모델의 성능을 향상시킨다.
CNN (Convolutional Neural Network)
# CNN (Convolutional Neural Network)
CNN의 시작은 고양이의 시각 인식에 관한 연구에서 비롯됐다. 고양이가 어떤 사진을 인식할 때, 각 뉴런이 각각 자신이 맡은 사진의 특정 부분에만 활성화되는 것이 확인되었고, 컴퓨터의 이미지 인식에도 같은 방식을 적용하는 시도가 이뤄졌다.
CNN은 Convolutional Layer와 ReLU, Pooling 계층의 반복적 조합으로 구성되며 끝에는 Fully Connected Layer로 결과를 분류한다.
실제로 CNN을 동작시킬 때, 행렬의 크기 단위로 설정한 filter를 사용해 전체 이미지를 부분으로 쪼개어 처리한다. 위 그림의 예는 3개의 RGB 컬러로 구성된 32 pixel X 32 pixel 이미지를 5 X 5 X 3의 크기를 가진 filter로 처리하는 모습이다.
Filter는 원래 딥러닝에서 사용하던 Wx + b 회귀식의 가중치 행렬 W로 표현된다. 따라서 이미지를 filter를 통해 처리하는 것은 쪼개어진 이미지를 회귀식을 통해 계산해 하나의 숫자 ouput으로 만드는 과정을 의미한다. 이 과정을 쪼개어진 이미지 모두에 적용하면, 우리는 보다 축소된 크기의 숫자값 행렬을 얻을 수 있다. 이러한 과정을 거쳐 얻은 행렬을 Feature map이라고 하고 이 피쳐맵에 활성화 함수를 적용한 것을 Activation map이라고 한다.
그렇다면 Feature map 하나의 크기는 어떻게 될까? 위 그림의 공식처럼 Output의 행 혹은 열 길이는 (N - F) / stride + 1로 구할 수 있다. 여기서 Stride는 filter를 몇 칸씩 이동하며 적용할지를 나타낸다. 예를들어, 위 그림에서 이미지의 가로, 세로 길이를 N이라 하고 filter의 가로, 세로 길이를 F라 한다면, N = 7, F = 3이다. 따라서 stride를 1로 설정해 filter를 한 칸씩 이동하며 적용하는 경우에는 (7 - 3) / 1 + 1 = 5를 통해 Ouput이 5 X 5 크기의 행렬이 되고, stride를 2로 설정해 filter를 두 칸씩 이동하며 적용하는 경우에는 (7 - 3) / 2 + 1 = 3을 통해 Output이 3 X 3 크기의 행렬이 된다. Stride가 3인 경우에는 공식에서 정수로 나누어 떨어지지 않기 때문에, CNN 적용이 불가능하다.
그러나 filter를 통해 이미지를 축소시키는 것은 다시말해 어떠한 정보를 잃어버리게 된다는 점을 의미한다. 따라서, 일반적으로 CNN을 사용할 때는 이미지 가장자리에 0을 둘러싸는 Zero padding 처리를 한 후 진행한다. Zero padding을 진행하면 1. 이미지의 과도한 축소를 막을 수 있고 2. 이미지 모서리 부분의 위치를 어떤 형태로든 network에 알려줄 수 있다. 위 그림처럼 7 X 7 크기의 이미지에 1 pixel만큼 Zero padding 처리를 해 9 X 9 크기의 행렬을 만들면, 1만큼 stride를 적용해 3 X 3 크기의 filter를 거쳐 나오는 output의 크기는 (9 - 3) / 1 + 1 = 7을 통해 원래대로 7 X 7 행렬이 된다.
지금까지 Convolutional Layer에서 하나의 Feature map을 만드는 과정을 살펴봤다. 이후에는 이 과정을 각 가중치가 서로 다른 여러 개의 filter에 대해 적용해 여러 개의 Activation map(활성화 함수가 적용된 Feature map)을 하나의 Convolutional Layer에서 만들 수 있다. 위와 같이 가중치가 서로 다른 6개의 5 X 5 X 3 filter를 사용한다면, 총 6개의 28 X 28 크기의 Activation map을 만들 수 있다.
앞의 과정을 반복하면 위와 같이 여러 개의 Convolutional layer들을 만들 수 있다. Zero padding 없이 32 X 32 X 3 크기의 이미지를 첫 번째 Convolutional Layer에 넣으면 6개의 5 X 5 X 3 filter에 의해 28 X 28 X 6 Activation Maps가 나오고, 이를 두 번째 Convolutional Layer에 넣으면 10개의 5 X 5 X 6 filter에 의해 24 X 24 X 10 Activation Maps가 나온다.
참고로 각각의 layer에서 사용되는 weight의 개수는 그 layer에 적용되는 필터의 크기와 갯수를 곱한 값과 같다. 즉, 첫 번째 layer의 경우 크기가 5 X 5 X 3인 filter가 6개 존재하므로 weight의 개수는 450개이다.
# Max pooling과 Fully Connnected Layer
Pooling Layer란 하나의 Convolutional Layer에서 1개씩 부분 layer를 뽑아서 sampling을 통해 resize하여 다시 축소된 Convolutional Layer로 재구성한 Layer이다.
Pooling은 filter를 이용해 진행하는데, 보통 Max pooling이 자주 이용된다. 위 그림처럼 stride가 2인 2 X 2 크기의 filter가 있다면, 그 filter에 대상이 된 4개의 값 중 가장 큰 값 하나를 선택해 Pooling Layer의 요소로 사용한다. 따라서, 위 그림의 Pooling Layer 요소는 6, 8, 3, 4로 구성된다.
그리고 CNN 모델의 마지막에는 기존 Neural Network에서 사용하던 모든 뉴런들을 연결해 연산하는 Fully Connected Layer를 위치시킨다. Convolutional Layer의 경우 모든 뉴런을 연결시키지 않고 정보를 전달했는데, CNN의 끝 부분에서는 Softmax를 사용해 이미지를 분류해야 하므로 기존의 Fully Connected된 적당한 깊이의 Neural Network를 사용한다.
RNN (Recurrent Neural Network)
# RNN (Recurrent Neural Network)
출처: https://heung-bae-lee.github.io/2020/01/12/deep_learning_08/
Data는 여러가지 종류가 존재하는데, 그 중 Sequential data의 처리는 기존의 Neural Net이나 CNN으로 해결할 수 없다. Sequential data란 순서에 의미가 있어서 순서가 달리지면 의미가 손상되는 데이터를 말한다. Sequential data에는 음성 인식에서의 소리 신호, 세계 기온 변화 추이와 사례가 있는데, 특히 언어의 경우 문장은 하나의 단어뿐만 아니라 그 앞뒤의 단어들까지 고려해야 이해가 가능하다. 이러한 Sequential data를 처리하는 기법으로 RNN(Recurrent Neural Network)이 사용된다.
RNN에서는 입력으로 계산한 값이 그 다음 것에 영향을 주도록 구조화한다. 위 그림에서 입력 X0를 받아 Cell이라고 부르는 A에서 계산된 상태(state)를 그 다음 입력 X1으로 계산된 A에 전달하는 것이 이에 해당한다. 즉 어떤 시점에서 무언가를 계산할 때, 이전 시점의 것들이 영향을 미친다는 점에서 Sequential data를 처리하는데 적합하다.
새로운 state를 연산하는 방법은 위의 공식과 같다. Input X와 이전 시점의 state에 가중치 W와 관련된 함수를 연산해주면 새로운 state의 값을 구할 수 있다. RNN에는 모든 state에 똑같은 공식이 적용되므로 위 그림의 모델처럼 순환 화살표 표시를한다.
# Vanila RNN
RNN의 가장 기본적인 형태는 위의 공식을 사용한다. 새로운 state를 연산하기 위해 이전 state와 input X에 각각 다른 가중치를 기존 회귀식같이 WX형태로 곱해주고 두 값을 더한다. 그리고 더한 값에 tanh 함수를 활성화함수로 사용해 새로운 state를 구한다. Output Y를 구할 때도 비슷하게 또 다른 가중치 W를 기존 WX 형태로 곱해주면 된다.
이 연산에서 사용하는 가중치 Wh, Wxh, Why는 어떤 state를 구할 때라도 항상 같다는 점을 주의하자.
단순한 신경망은 표현력에 한계가 있지만 RNN을 사용한다면 활용 범위가 크게 넓어진다.
· One to Many: Image Captioning (이미지를 인식하여 여러개의 문자열로 표현하는 분야)
· Many to One: Sentiment Anlaysis (여러 개의 문자열을 입력 받아 하나의 감성을 나타내는 문자열로 분석하는 분야)
· Many to Many: Machine Translation (여러 개의 문자열을 입력 받아 여러 개의 문자열로 번역하는 분야)
· Many to Many: Video Classification on frame level (여러 프레임을 입력 받아 여러 개의 문자열로 표현하는 분야)
이러한 방식으로 RNN도 다양한 형태를 띌 수 있는데, RNN이 보다 깊어지고 복잡해질수록 기존 신경망 모델과 마찬가지로 학습이 어려워지는 현상이 발생한다. 이를 극복하기 위한 Advanced Model로 조경현 교수님이 만든 GRU나 LSTM(Long Short Term Memory)이 존재한다.
# Hello의 예
RNN 모델에 단어 ‘hello’를 학습시키는 예를 살펴 보자. ‘hello’는 철자 단위로 모델을 학습시킬 수 있다. X0에 ‘h’, X1에 ‘e’, X2에 ‘l’, X3에 ‘l’을 입력으로 넣고 입력된 철자 다음으로 나올 철자를 예측하도록 설계할 수 있다. 이 경우 h0는 ‘e’, h1은 ‘l’, h2는 ‘l’, h3은 ‘o’를 output으로 예측한다.
이 때, 위 그림처럼 고정된 값 Whh, Wxh, Why를 가중치로 사용하여 각 input의 state를 연산하고 output Y를 출력한다. Input X의 경우 one-hot 인코딩을 통해 철자를 표현하는게 일반적이고 output은 softmax를 통해 철자 예측을 진행하면 되는데, 모델의 학습 역시 기존의 softmax에 대한 cost 함수를 사용해 진행하면 된다.
본 포스팅은 김성훈 교수님의 강의
‘모두를 위한 딥러닝’을 학습하고 정리한 내용을 담고 있습니다.
-
모두를 위한 딥러닝 Part 1
Machine Learning 개요
# 머신러닝이란?
Explicit(=many rules)한 프로그래밍을 지양하고, 프로그램에게 데이터를 보고 스스로 학습할 능력을 부여해서 어떠한 결과를 도출하게끔 하는 연구하는 분야
→ Field of study that gives computers the ability to learn without being explicitly programmed (Arthur Samuel, 1959)
# 학습 방법에 따른 유형
1. Supervised learning
: 컴퓨터에게 정답(label)이 무엇인지 알려주면서 학습시키는 방법 (label이 있는 data로 학습)
Regression
어떠한 연속된 값을 주어진 데이터들의 특징(feature)을 기준으로 예측하는 문제
ex) 시험공부에 투자한 시간에 대한 기말시험 ‘점수’ 예측
Binary Classification
주어진 데이터를 2개의 카테고리로 분류하는 문제
ex) 개와 고양이 구분
Multi-Class(=Multi-Lable) Classification : 주어진 데이터를 3개 이상의 카테고리로 분류하는 문제
ex) 시험공부에 투자한 시간에 대한 기말시험 ‘등급’ 예측
2. Unsupervised learning
: 정답(label)을 알려주지 않고 비슷한 데이터를 군집화하여 미래를 예측하는 학습 방법 (label이 없는 data로 학습)
ex) Google news grouping, Word clustering
Clustering
Dimensionality Reduction
etc…
Linear Regression
# Linear Regression
주어진 학습 데이터를 가장 잘 설명할 수 있는 선을 찾아 분석하는 방법이다. (Regression은 연속하는 값을 가지는 학습 데이터에 한해 사용한다.) 위 그림처럼 주어진 데이터를 그래프에 표현하고 여러가지 선을 긋다보면 파란선이 해당 데이터를 가장 잘 표현함을 알 수 있다. 이러한 선을 H(x) = Wx + b의 형태의 수식으로 찾아내는 것을 Linear Regression이라고 한다. 위 그림의 파란선은 H(x) = x로 나타낼 수 있다.
# Loss & Cost function
Cost function은 예상한 가설(선)이 데이터에 얼마나 잘 맞는지 확인하는 함수이다. 보통 예측값에서 실제값을 뺀 값의 제곱인 (H(x) - y)²을 Loss로 사용하여 Cost function을 구한다.
즉 이렇게 계산한 모든 Loss의 평균을 내면 Cost function을 구할 수 있다. 딥러닝에서는 주로 이 Cost function이 사용되고 이러한 Cost function을 최소화시키는 W, b를 찾는 것이 목표가 된다.
Multi-variable linear regression
# Mulit-variable linear regression
앞에서 공부했던 선형 회귀는 하나의 변수에 대하여 출력을 계산했다. 그러나 위 시험 점수 예측 사례의 퀴즈 1 점수, 퀴즈 2 점수, 중간고사 점수처럼 여러개의 변수를 고려하여 회귀를 진행할 땐 어떻게 해야할까?
기존의 선형 회귀 식은 H(x) = Wx + b였다. 다변량 선형 회귀는 위와 같이 기존 선형 회귀와 유사하게 새로운 가중치 w를 각각의 새로운 변수 x들에 곱해주면 된다.
다변량 선형 회귀의 비용함수 역시 선형 회귀의 비용함수 식을 그대로 가져오되 Hypothesis만 다변량 회귀식으로 적용하여 사용한다.
Hypothesis를 n개의 변수에 대하여 일반화하면 위와 같다. 그러나 n의 값이 커질수록 식이 길어서 이를 표현하기 어려워지는 문제가 생긴다.
식이 길어지는 문제는 행렬(Matrix)을 도입하는 방법(= Vectorization)으로 해결할 수 있다. 변수 x들에 대한 행렬 X와 각각의 변수에 대한 가중치 w들을 표현하는 행렬 W를 사용해 H(X) = XW라는 Hypothesis를 사용할 수 있다. 일반적으로 이론에 사용되는 식에서는 H(x) = Wx 처럼 W를 앞에 사용하지만, 실제로 구현할 때는 XW와 같이 X를 앞에 두고 사용한다. Vectorization은 n개의 변수에 대해 n번이나 수행되어야 하는 계산을 한번으로 줄여 효율적인 계산을 돕는 이점이 있다.
위는 기말 시험 점수를 예측하는 다변량 선형 회귀에 대한 예시이다. 왼쪽 상단의 표에는 3가지 시험 점수 변수와 기말 점수 변수에 대한 데이터가 5개 있다. 이러한 데이터 하나하나를 Instance라고 한다. 행렬로 다변량 선형 회귀를 수행할 때는 그 행과 열에 정보가 담겨 있는데, X의 행은 instance의 개수(data의 개수), 열은 독립변수의 개수를 나타낸다. W의 행은 독립변수의 개수를 나타내며 열은 출력 개수를 나타낸다. 그리고 두 행렬 X와 W를 계산한 결과를 담는 행렬은 행이 instance의 개수, 열이 출력의 개수를 나타낸다.
Logistic Regression
# 이진 분류 (Binary classification)
이진 분류(Binary Classification)는 어떤 문제에 대하여 두 가지 중 하나를 결정하는 문제이다. 메일이 스팸메일인지 아닌지, 페이스북 피드를 보여줄지 말지, 방금 진행한 신용카드 거래가 사기인지 아닌지 판단하는 것이 이진 분류의 예다. 일반적으로 결정해야할 두 가지 결과는 0, 1로 인코딩해 사용한다.
# 로지스틱 회귀 (Logistic Regression)
1. 로지스틱 회귀와 시그모이드(Sigmoid) 함수
이진 분류를 가장 잘 해결할 수 있는 방법으로 로지스틱 회귀(Logistic Regression)가 있다. 기존의 선형 회귀는 시험에 통과할 사람을 정확히 예측하는게 어렵고, 입력값이 커질수록 출력값이 0~1 범위를 크게 벗어나 결과를 두 가지로 분류하기 어렵다. 이러한 출력값을 0~1 범위로 압축하는 함수를 이용해 출력값을 분류하는 것이 로지스틱 회귀이다.
로지스틱 회귀에서는 0~1 범위로 출력값을 압축하는 함수로 시그모이드(Sigmoid) 함수를 사용한다. 시그모이드 함수는 모든 출력값이 0~1 사이에서 나오는 특징이 있다. 이를 통해, 기존 가설인 선형 회귀에 시그모이드 함수를 덧입혀 이진 분류에 적합한 새로운 가설 H(x)를 만들 수 있다.
2. 로지스틱 회귀의 Cost 함수
기존 선형 회귀의 cost 함수는 기울기가 0이 되는 값이 하나여서 쉽게 최솟값을 찾을 수 있었지만, 로지스틱 회귀의 경우 비선형 함수인 sigmoid 함수로 인해 cost 함수가 훨씬 구불구불한 형태를 띄게 된다. 이로 인해, 기울기가 0이 되는 지점이 많아져 시작점에 따라 경사하강법으로 찾는 최솟값의 지점이 달라진다. 즉, cost 함수의 진짜 최솟값을 찾는 것이 어렵다.
이를 극복하기 위해, 로지스틱 회귀에서는 위와 같은 cost 함수를 사용한다. 가장 왼쪽에 있는 그래프는 y = 1일 때의 cost 함수, 그 옆에 있는 그래프는 y = 0일 때의 cost 함수이다. 시그모이드 함수로 인해 생기는 지수함수적 특성을 log 함수를 사용해 중화한 덕분에 전체적으로 포물선과 비슷한 형태를 띈다. 따라서, 최솟값 찾기가 용이하다.
cost 함수 그래프를 살펴보자. y = 1일 때의 그래프에서 H(x)가 1에 가까울수록(예측값이 정답에 가까울수록) cost 함수가 작아지고 H(x)가 0에 가까울수록(예측값이 틀릴수록) cost 함수가 무한대로 커진다. 반대로 y = 0일 때의 그래프에서 H(x)가 1에 가까울수록(예측값이 틀릴수록) cost 함수가 무한대로 커지고 H(x)가 0에 가까울수록(예측값이 정답에 가까울수록) cost 함수가 작아진다. 로지스틱 회귀의 cost 함수가 비용함수의 역할을 정확히 수행함을 확인할 수 있다.
비용함수를 텐서플로우로 실제로 구현할 때는 C(H(x), y) = - ylog(H(x)) - (1 - y)log(1 - H(x)) 식을 사용한다. 위의 y = 1일 때와 y = 0일 때의 비용함수를 똑같이 표현한 같은 식이며 구현의 편의를 위해 사용한다.
Multi-Class Classification - Softmax
# 다중 클래스 분류 (Multi-Class Classification)
앞선 로지스틱 회귀에서는 두 가지 선택지만 결정했다. 만약 세 가지 이상의 클래스를 두고 결정해야 하는 상황이라면 다중 클래스 분류(Multi-Class Classification)를 한다. 위와 같이 시간과 출석 여부라는 두 가지 변수에 대하여 A, B, C 세 가지 성적을 매기는 상황을 가정해보자. 성적 분포의 그래프는 오른쪽 그래프와 같다.
성적이 표현된 그래프를 로지스틱 회귀를 사용한다고 생각하고 A에 대해, B에 대해, C에 대해 각각 이진 분류한다면 위와 같이 3가지 선을 그을 수 있다. A인지 아닌지, B인지 아닌지, C인지 아닌지를 구별하는 세 가지 선을 그은 것이다.
이 3가지 식을 행렬로 표현하면 왼쪽 그림과 같다. 그리고 계산의 편의를 위해 이 식들을 또 하나의 행렬로 통합하면 오른쪽 그림과 같아진다. 오른쪽 그림의 계산식의 3가지 출력이 각각 A, B, C에 대한 H(x) 값이 된다.
A, B, C에 대한 각각의 예측값이 0~1 범위에 있게 하고 모두 합해 1이 나오게 한다면 편리한 계산을 할 수 있다. 이를 위해 위 그림과 같은 Softmax 함수를 사용한다. Softmax 함수를 사용하면 A, B, C에 대한 세 가지 예측값을 확률로서 사용할 수 있게 된다.
Softmax로 도출된 각각의 확률은 One-Hot Encoding을 통해 1 혹은 0으로 분류되고 각각의 예측값이 결론적으로 가리키는 것이 A인지 B인지 C인지를 확인할 수 있게 된다.
※ Softmax의 Cost 함수
Learning rate, Overfitting and Regularization
# Learning rate (학습률)
Gradient Descent를 진행할 때, 각 step마다 어느 정도씩 진행할지 Learning rate(학습률)을 지정하여 설정할 수 있다. (위 그림에서 알파값이 학습률을 나타낸다.)
학습률을 너무 큰 값으로 설정하면 스텝마다 큰 폭으로 학습이 진행되어 왼쪽 그림처럼 w값이 발산해버리는 오버슈팅(Overshooting) 문제가 발생할 수 있다. 반대로 학습률을 너무 작은 값으로 설정하면 스텝마다 작은 폭으로 학습이 진행돼 오른쪽 그림처럼 학습이 더뎌지는 문제가 발생한다. 학습률 설정에 정답은 없지만 처음에 0.01의 학습률을 설정하고 양상에 따라 조절하는 것도 한 방법이 될 수 있다.
# 데이터 전처리 (Preprocessing)
데이터들을 다루다보면 x data에 해당하는 각각의 변수들의 값의 범위가 서로 크게 차이날 수 있다. 이러한 경우 적절한 학습률을 설정해도 오버피팅이나 언더피팅이 발생할 수 있는데, x data를 적절하게 전처리(Preprocessing)해주면 다시 정상적으로 학습시킬 수 있다. 이러한 전처리는 보통 zero-centered를 통해 원래의 데이터를 0을 중심으로 분포하게 만들거나, Normalization을 통해 변수 값의 범위를 특정 범위에 속하게 만드는 방법들이 있다.
이러한 normalize의 대표적인 예 중 하나가 표준화(Standardization)이다. 기존의 data에서 그 평균을 빼고 표준편차로 나눠주면 data는 표준정규분포를 따르게 되어 특정 범위 내에 분포하게 된다. 고등학교에서 통계 과목을 배울 때, 자주 봤던 이 개념을 사용해 data를 표준화시키면 정상적인 학습 진행에 큰 도움을 준다.
# 오버피팅 (Overfitting)
학습시킨 모델이 training data(학습 데이터)에서만 너무 잘 맞아서 test data나 실제 문제에서는 좋은 성능을 발휘하지 못하는 현상을 오버피팅(Overfitting)이라고 한다. 오른쪽 그림은 학습 데이터에서 +와 -를 완벽하게 가르지만 실제 문제를 다룰 때는 +와 -를 나누는 성능이 왼쪽 그림에 비해 더 떨어질 수 있다. 이 경우엔 오버피팅 문제가 없는 왼쪽 모델이 더 성능이 좋으므로 모델을 학습시킬 땐 항상 오버피팅에 대해 경계해야 한다.
오버피팅의 해결책으로는 1. training data를 더 많이 확보하는 것 2. feature의 개수를 줄이는 것(=x변수를 줄이는 것) 3. Regularization시키는 것 등이 있다.
# Regularization
Regularization이란 데이터를 가르는 모델의 구불구불한 선을 조금 더 평탄하게 만드는 것을 의미한다. 보통 가중치 w의 값이 커질수록 모델의 선이 구불구불해지고, w의 값이 작아질수록 모델의 선이 평탄하게 뻗게 된다. 가중치 w 값을 보다 작게 하여 모델의 선을 적당히 평탄하게 만드는 Regularization을 통해 오버피팅을 어느정도 줄일 수 있다.
Regularization은 cost 함수에 위 식을 더해주는 것으로서 구현하고 이를 L2 Regularization이라고 부른다. 맨 왼쪽의 람다 변수는 regularization strength라고 불리는데, 이 값이 0에 가까울수록 Regularization의 영향을 적게 한다는 의미고 이 값이 커질수록 Regularization의 영향력을 크게 한다는 의미이다. 이를 통해 가중치 값을 낮추고 오버피팅을 어느정도 극복할 수 있다.
본 포스팅은 김성훈 교수님의 강의
‘모두를 위한 딥러닝’을 학습하고 정리한 내용을 담고 있습니다.
Touch background to close