참조 : https://wikidocs.net/book/4542

Flask는 대표적인 Micro 웹 프레임워크이다. 최소한의 구성요소와 요구사항을 제공하여 시작하기 쉽고, 확장하기도 쉽다.

짧은 코드만으로 웹 프로그램을 만들 수 있는 것이다.

그러나. 한개의 페이지만을 만들어서 사용하려는 것이 아니기에, DB처리, Routing, form관리 등등

데이터베이스를 처리하는 models.py 파일

파이보 프로젝트는 ORM(object relational mapping)을 지원하는 파이썬 데이터베이스 도구인 SQLAlchemy를 사용한다. SQLAlchemy는 모델 기반으로 데이터베이스를 처리한다. 지금은 모델 기반으로 데이터베이스를 처리한다는 말이 이해되지 않겠지만, 이후 프로젝트를 진행하면 잘 알 수 있을 것이다. 아무튼 지금 여러분이 알아야 할 내용은 파이보 프로젝트에는 ‘모델 클래스들을 정의할 models.py 파일이 필요하다’는 것이다.

서버로 전송된 폼을 처리하는 forms.py 파일

파이보 프로젝트는 웹 브라우저에서 서버로 전송된 폼을 처리할 때 WTForms라는 라이브러리를 사용한다. WTForms 역시 모델 기반으로 폼을 처리한다. 그래서 폼 클래스를 정의할 forms.py 파일이 필요하다.

화면을 구성하는 views 디렉터리

pybo.py 파일에 작성했던 hello_pybo 함수의 역할은 화면 구성이었다. views 디렉터리에는 바로 이런 함수들로 구성된 뷰 파일들을 저장한다. 파이보 프로젝트에는 기능에 따라 main_views.py, question_views.py, answer_views.py 등 여러 가지 뷰 파일을 만들 것이다.

CSS, 자바스크립트, 이미지 파일을 저장하는 static 디렉터리

static 디렉터리는 파이보 프로젝트의 스타일시트(.css), 자바스크립트(.js) 그리고 이미지 파일(.jpg, .png) 등을 저장한다.

HTML 파일을 저장하는 templates 디렉터리

templates 디렉터리에는 파이보의 질문 목록, 질문 상세 등의 HTML 파일을 저장한다. 앞에서 살펴본 파이보 프로젝트 구조에는 index.html 파일만 있다. 하지만 파이보 프로젝트가 진행되면서 question_list.html, question_detail.html과 같은 템플릿 파일을 계속 추가할 것이다.

파이보 프로젝트를 설정하는 config.py 파일

config.py 파일은 파이보 프로젝트를 설정한다. 파이보 프로젝트의 환경변수, 데이터베이스 등의 설정을 이 파일에 저장한다.

Application Factory

Circular Import 문제를 피해야한다.

# __init__.py
from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.route('/'
    def hello():
        return 'Hello'

    return app

flask에서 pybo라는 폴더를 만든다음, init.py를 만들어서 그 안에 create_app()함수를 세팅해준다.
flask에서는 FLASK_APP = pybo flask run 이라는 명령어를 쓸 수 있는데, 이는 FLASK_APP을 pybo라는 폴더로 실행하라! 라고 받아들인다고 생각하면 된다. 그럼 그 폴더의 init.py에서 create_app()이라는 함수를 찾아 실행시키면서 flask서버가 구동된다고 보면 된다.

Routing

어떤 URL로 들어왔을 때 이에 대하여 어떤 기능을 통해서 어떤 화면을 보여줄것인가? 라는 것을 생각하면된다.
결국은 주어진 input에 대한, 주어진 output을 만드는것처럼 간단하게 생각해도 된다.

하지만 route해줘야하는곳이 많아지기 시작하면 기능별로 함수를 분리해주는것이 좋다.
너무 길어져버리니까 한눈에 잘 들어오지도 않고, 이후 유지보수적인 측면에서 생각해도 기능별로 파일을 관리해주는것이 훨~~씬 시간을 아낄 수 있을테니까!
이때 쓰는것이 app.register_blueprint() 와 Blueprint라는 객체이다.
Blueprint 청사진이라는 표현처럼, 이쪽에 대한 입력들을 한번에 모아서 관리할때 사용하기편하며, 이후 url_for함수에서 blueprint별로 처리를 해줄 수 있으니 더더욱 좋다!

# main_route.py
bp = Blueprint('main', __name__)

@bp.route('/')
def index():
    return render_template('index.html')
## create_app(): 
    # 위에 app = Flask(__name__)부터 각종 config설정, db_init이 실행되고 난 후
    from kokoa.routes import main_route

    app.register_blueprint(main_route.bp)

이렇게 circular import를 피하며 blueprint를 통하여 라우팅을 관리할 수 있다!

단순히 하나의 python 파일만 실행하는것이 아닌 여러개의 파일과 그 안의 코드들이 자신들의 역할에 맞게 작동하면서 Application이 작동한다.
이렇게 작동한다는 것 자체가, 서로서로 import를 해주며 엮인다는 뜻이며, 이중 자칫하면 Circular Import문제로 App의 실행이 중단될 수 있다.

Circular Import란?

두개 이상의 python파일이 서로를 import하면서, 서로 불러오는것이 반복되어 결국 무한루프에 빠지게 되는것.

# appdirectory.__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

def create_app(config=None):
    app = Flask(__name__)

    db.init_app(app)
    migrate.init_app(app, db)

    from kokoa.routes import main_route
    app.register_blueprint(main_route.bp)

    return app

if __name__ == "__main__":
    app = create_app()
    app.run(debug=True)

위와같이 create_app을 통해, app을 실행시키도록하며, flask실행보다 먼저 db와 migrate를 생성하므로, 향후 다른곳에서 appdirectory에서 db 또는 app을 참조할 때 순환임포트 문제가 발생하지 않도록 해준다!

'AI_BootCamp' 카테고리의 다른 글

Section 2 회고  (0) 2021.03.02

서로 다른 테이블을 연결하여, 큼직~하게 테이블 하나 만든다고 생각하면된다.
그럼 여기서 어떻게 테이블을 연결할까?
그 방법에 대하여 Inner , Outer, Self 등이 존재한다.

Different Types of SQL JOINs

Here are the different types of the JOINs in SQL:

  • (INNER) JOIN: Returns records that have matching values in both tables
    둘 다 있는 것만 조인됨
  • LEFT (OUTER) JOIN: Returns all records from the left table, and the matched records from the right table
    왼쪽 테이블에 오른쪽 테이블을 붙여서 조인(왼쪽 테이블 행만큼)
  • RIGHT (OUTER) JOIN: Returns all records from the right table, and the matched records from the left table
    오른쪽테이블에 왼쪽 테이블을 붙여서 조인 (오른쪽 테이블 행만큼)
  • FULL (OUTER) JOIN: Returns all records when there is a match in either left or right table
    양쪽에 서로 다 붙임 → 없는거 Null 쭈르륵 생김

SQL INNER JOIN

The INNER JOIN keyword selects records that have matching values in both tables.

양쪽 테이블 모두 갖고있는 것들만 JOIN 한다

SQL LEFT JOIN Keyword

The LEFT JOIN keyword returns all records from the left table (table1), and the matched records from the right table (table2). The result is NULL from the right side, if there is no match.

왼쪽테이블의 모든 칸에 오른쪽테이블의 JOIN이 실행되며, 오른쪽에 결과값이 없는것은 NULL이 매핑된다
Note: In some databases LEFT JOIN is called LEFT OUTER JOIN.

SQL FULL OUTER JOIN Keyword

The FULL OUTER JOIN keyword returns all records when there is a match in left (table1) or right (table2) table records.

양쪽의 모든 테이블이 JOIN되며, 없는것들은 모두 NULL

Note: FULL OUTER JOIN can potentially return very large result-sets!

Tip: FULL OUTER JOIN and FULL JOIN are the same.

SQL Self JOIN

A self JOIN is a regular join, but the table is joined with itself.

스스로 조인하는것으로, 활용하는 법에 대하여 조금 난이도있다고 생각된다.

가장 대표적인 것으로, 어떠한 인적정보 테이블에서 , 자신과 같은 도시에 사는 다른사람 쌍을 구해보는 쿼리를 짠다고 생각하면

select e1.FirstName ,e2.FirstName, e1.city    
from employees e1, employees e2  
where e1.EmployeeId <> e2.EmployeeId and e1.city=e2.City

where e1.EmployeeId <> e2.EmployeeId 의 부분은 자기 스스로와 매핑되지 않도록 하기위함이고,

and e1.city=e2.City 이 부분은 city가 같은 다른사람을 조인하기 위한 부분이다.

 

참조 : SQL Tutorial (w3schools.com)

 

'SQL' 카테고리의 다른 글

SQL기본 select, from, where / insert,update..  (0) 2021.04.01

FROM

~~ FROM Table 어떤 테이블로부터 가져온다는 뜻

SELECT

말 그대로 선택 →

select * 하게되면 전부 가져오는것

SELECT column1, column2, ...
FROM table_name;

위와 같은 방식으로 작성하여 사용

DISTINCT

The SELECT DISTINCT statement is used to return only distinct (different) values.

유일한 값으로 가져온다는 뜻으로, 중복 없이 가져오게된다.

SELECT DISTINCT column1, column2, ...
FROM table_name;

Example

-- Write a statement that will select the City column from the Customers table.
SELECT City FROM Customers;

-- Select all the different values from the Country column in the Customers table.
SELECT DISTINCT Country FROM Customers;

WHERE

필터링 하는 것

The WHERE clause is used to filter records.

The WHERE clause is used to extract only those records that fulfill a specified condition.

LIKE or NOT LIKE

특정 문자 또는 문자열을 포함하고 있는 값을 검색하고 싶을 때 사용.

% : 0개 이상의 문자열과 대치

_ : 임의의 한 개의 문자와 대치

%,_ 를 검색하고싶을땐? → @% , @_

--Genres에서 name이 B로 시작하는것 가져오기
select * from genres g
where g.Name like 'B%';

AND, OR and NOT Operators

생각대로 and , or , not 연산자를 사용하여 조건을 추가할 수 있다.

SELECT column1, column2, ...
FROM table_name
WHERE condition1 AND condition2 AND condition3 ...;

SELECT column1, column2, ...
FROM table_name
WHERE condition1 OR condition2 OR condition3 ...;

SELECT column1, column2, ...
FROM table_name
WHERE NOT condition;

-- Customers 테이블에서 Country는 Germany이고, City는 Berlin이나 Munchen인 records를 골라라
SELECT * FROM Customers
WHERE Country='Germany' AND (City='Berlin' OR City='München');
--Select all records where the City column has the value "Berlin".
SELECT * FROM Customers
WHERE City = Berlin;

--The following SQL statement selects all the customers from the country "Mexico", in the "Customers" table:
SELECT * FROM Customers
WHERE Country='Mexico';

--#Select all records where the City column has the value 'Berlin' and the PostalCode column has the value 12209.
Select * From Customers
Where City = 'Berlin' and PostalCode = 12209

SELECT * FROM Customers
Where 

INSERT

The INSERT INTO statement is used to insert new records in a table.

새로운 레코드(행)을 추가할 때 사용한다.

INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);

INTO의 ( ... ) 와 VALUES의 ( ... )의 개수가 당연히 같아야한다.
해당 테이블의 column을 전부 채우지 않을 경우 null이 입력된다

NULL Values

A field with a NULL value is a field with no value.

SELECT column_names
FROM table_name
WHERE column_name IS NULL;

SELECT column_names
FROM table_name
WHERE column_name IS NOT NULL;

UPDATE

The UPDATE statement is used to modify the existing records in a table.

현재 있는 records를 업데이트한다

UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

Customers테이블에서, Country가 Norway인 레코드들의 City를 Oslo로 바꾼다

Set the value of the City columns to 'Oslo', but only the ones where the Country column has the value "Norway".

update Customers
set City = 'Oslo'
where Country = 'Norway';

DELETE

삭제하는것

말그대로 삭제이며, from으로 어디서 삭제할지 명시해줘야한다

DELETE FROM table_name WHERE condition;

MIN(), MAX()

최대 최소 뽑기

The MIN() function returns the smallest value of the selected column.

The MAX() function returns the largest value of the selected column.

SELECT MIN(column_name)
FROM table_name
WHERE condition;

SELECT MAX(column_name)
FROM table_name
WHERE condition;

COUNT(), AVG() and SUM()

개수새기, 평균, 합계 함수

SELECT COUNT(column_name)
FROM table_name
WHERE condition;

SELECT AVG(column_name)
FROM table_name
WHERE condition;

SELECT SUM(column_name)
FROM table_name
WHERE condition;

Wildcard 와일드카드

A wildcard character is used to substitute one or more characters in a string.

Wildcard characters are used with the SQL LIKE operator. The LIKE operator is used in a WHERE clause to search for a specified pattern in a column.

Symbol Description Example
% Represents zero or more characters bl% finds bl, black, blue, and blob
_ Represents a single character h\t finds hot, hat, and hit
[] Represents any single character within the brackets h[oa]t finds hot and hat, but not hit
^ Represents any character not in the brackets h[^oa]t finds hit, but not hot and hat
- Represents a range of characters c[a-b]t finds cat and cbt
--Select all records where the first letter of the City starts with anything from an "a" to an "f".
SELECT * FROM Customers
WHERE City LIKE '[a-f]%';

Select all records where the first letter of the City is NOT an "a" or a "c" or an "f".

SELECT * FROM Customers
WHERE City LIKE '[^acf]%';

IN, BETWEEN

The IN operator allows you to specify multiple values in a WHERE clause.

The IN operator is a shorthand for multiple OR conditions.

SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);

SELECT column_name(s)
FROM table_name
WHERE column_name IN (SELECT STATEMENT);


The BETWEEN operator selects values within a given range. The values can be numbers, text, or dates.

The BETWEEN operator is inclusive: begin and end values are included.

SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;

'SQL' 카테고리의 다른 글

SQL JOIN  (0) 2021.04.01

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

여러 도커 Application을 정의하고 실행하는 툴로, YAML파일을 통해 각각의 서비스를 설정할수있다.

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

(공식문서 Docker-compose 예시)

도커 엔진 1.13.1 버전 이상, 도커컴포즈 버전 1.6.0 이상에서 사용할 수 있는 기능으로,
컨테이너 실행에 필요한 옵션을 docker-compose.yml 이라는 파일에 담아서 일괄적으로 실행시켜준다

version

첫 줄에는 version 을 명시해준다.

version : '3'

3으로 시작하는 최신버전을 사용한다는 뜻

버전 3부터는 links: 를 통하여 연결해줄 필요가 없다

services

실행하려는 컨테이너들을 정의한다.
서비스의 이름을 지정해주고, 그 안에 이제 도커이미지명, 환경변수 등등 다양한 것들을 입력해 넣어서 구성해준다.

서비스 이름 (지정)

사용하려는 컨테이너를 지정하는 이름과같다

services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 

postgre 서비스 이름을 postgresql로 지정하였다

image

사용하려는 이미지

services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 
    image: postgres:12 #그 서비스의 이미지는 이것과 같고

postgresql에서 사용할 도커 이미지를 적는다.

postgres (docker.com) 설명 사진 (hub.docker.com)

volumes

volumes:
      - ./servers.json:/pgadmin4/servers.json

docker run 으로 db컨테이너 실행시 --volume 옵션으로 연결하던 로컨의 데이터와 같다.
위 과정을 통해 프로젝트 루트 아래의 pgadmin4 에 servers.json 을 저장하였다

environment

services:
  postgresql:
    environment:
      POSTGRES_DB : Chinook
      POSTGRES_USER: postgres_admin
      POSTGRES_PASSWORD: password

Chinook이라는 Database로, User와 비밀번호를 지정하였다.

restart

restart 공식문서
재실행 여부를 설정한다.

Restarts all stopped and running services.
If you make changes to your docker-compose.yml configuration these changes are not reflected after running this command.
For example, changes to environment variables (which are added after a container is built, but before the container’s command is executed) are not updated after restarting.
If you are looking to configure a service’s restart policy, please refer to restart in Compose file v3 and restart in Compose v2. Note that if you are deploying a stack in swarm mode, you should use restart_policy, instead.

어떠한 구성을 변경했을 때 변경사항의 즉각적인 반영 여부를 나타내는 것 같다.


services: #서비스부터 어떤 컨테이너를 이용할 것인지 작성
  postgresql: #db이라는 서비스를 이용할것인데 
    restart: unless-stopped

윗 부분에서 이제 Postgre를 설정했으면, 이제 Admin부분을 만들어준다

  pgadmin:
    image: dpage/pgadmin4:4
    container_name : pgadmin
    ports:
      - 5050:80 #외부에서 5050으로 들어왔을 때 내부에서 80번을 보여주겠다
    environment:
      # PGADMIN_DEFAULT_HOST: postgres_admin
      PGADMIN_DEFAULT_EMAIL: user1@pgadmin.com
      PGADMIN_DEFAULT_PASSWORD: secret
    depends_on:
      - postgresql
    volumes:
      - ./servers.json:/pgadmin4/servers.json
    restart: unless-stopped  

image는 pgadmin4의 4번태그 이미지를 활용하며, 서비스이름은 pgadmin, container_name또한 동일하게 설정.
ports부분이 있는데, 이 부분을 이제 들어온곳과 연결할 곳을 입력해줘야한다. 여기서는 5050번 포트로 들어왔을때, 내부의 80번 포트와 연결한다는 것이다.
그리고 이후 pgadmin페이지 (localhost:5050)에서 로그인할 이메일,비밀번호를 설정하였다.
depends_on 부분이 있다.
이 부분은 앞서 작성한 postgresql이 실행되고 나서, pgadmin이 실행된다는 뜻이다


이제 실제로 compose만들고, up해서 연결해보자

version: '3.8'
services:
  db:
    container_name: pg_container # pgadmin에서 서버연결할 때 연결할 
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: root
      POSTGRES_PASSWORD: root
    ports:
      - "5432:5432"
  pgadmin:
    container_name: pgadmin4_container
    image: dpage/pgadmin4
    restart: always
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@admin.com
      PGADMIN_DEFAULT_PASSWORD: root
    ports:
      - "5050:80"
    depends_on: 
      - db

docker-composet up --force-recreate 명령어를 통해 up을 하면서, 혹시 이전에 저장했다가 테스트용으로 했던게 있어도, 새로 반영하도록한다. 

이제 pgadmin4 사이트에서

yml파일에 적어놓은, DEFAULT_EMAIL , DEFAULT_PASSWORD를 통하여 로그인한다 

로그인을 하면  browser페이지로 넘어가는데, 이곳에서 서버를 클릭해도 아직 연결되어있지않다, server - create

서버생성(연결) connection에 기존에 yml파일에서 세팅했던것들을 입력하여 연결!

서버연결에 성공하고나면, 내가 yml에서 만들었던 db의 POSTGRES_DB가 생성되어있다.

 

이후, db와 pgadmin을 띄우고난 뒤 DBeaver를 통해 연결해보자 

정말 간단하다! 그냥 연결하면서 POSTGRES 설정해주고, localhost의 5432포트와, 
Database이름은 내가 기존에 생성한 db로 하고, user와 password를 입력하면 연결완료!

참조 :
도커(Docker) 컴포즈를 활용하여 완벽한 개발 환경 구성하기 | 44BITS
docker-compose공식문서

'Python > Docker' 카테고리의 다른 글

[Docker] 도커를 알기위해 알아야하는 것들  (0) 2021.08.31
Docker  (0) 2021.03.09

드디어 만났다 도커!

너무도 많은 엔지니어들이 애용하고있는 도커다!

도커는 어플리케이션의 실행환경을 코드로 작성할 수 있고, OS와 격리시켜 관리할 수 있는 환경을 제공한다.
이게 얼마나 강력한것이냐?!!
Application들은 운영체제(Linux, Windows, MacOS 등) 에 따라, 각각의 환경을 구분하여 개발해야 한다. 왜냐면 각각 os마다 파일시스템, 텍스트의 문법 등 사소한것부터 큰것까지 무지하게 다르기 때문에, 이것들을 하나하나 다 맞춰주면서 개발을 해줘야 했다는 뜻이다. 얼마나 귀찮았을까..
그리고, 그렇게 개발할 때 환경변수도 맞춰줘야하고, 사용자권한, 폴더들, 방화벽, 포트 등등 수많은 설정이 있기에, 이것들을 다 하나하나 관리해줘야했다는 것을 의미하며 굉장히 번거로운 작업일수밖에 없었다.

도커 이전에는 가상머신을 이용했다고한다. 하나의 컴퓨터에 다른 컴퓨터를 통째로 만드는것이다.
맥에서 인터넷뱅킹을 위하여 페러렐즈를 통해 윈도우를 사용하던것과 같다.
그런데, 가상머신이 생각보다 성능 손실이 상당하다는 것이 큰 단점이었다.

리눅스 컨테이너

위의 가상머신의 성능문제를 해결하기 위해 생겨난 기술이 리눅스컨테이너이다.
컨테이너 안에 가상 공간을 만들어, 실행파일을 호스트 컴퓨터에서 직접 실행하는 것이다.
리눅스의 cgroup, namespaces가 제공하는 기술이다.
필요한 라이브러리와, 어플리케이션을 모다, 하나의 서버처럼 구성한 것으로, 컨테이너 내의 네트워크 설정, 환경변수 등 시스템 자원이 독립적으로 구성된다.

  1. 프로세스의 구획화
  • 특정 컨테이너에서 작동하는 프로세스는 기본적으로 그 컨테이너 안에서만 엑세스 가능
  • 컨테이너 안에서 실행되는 프로세스는 다른 컨테이너의 프로세스에 영향을 줄 수 없음
  1. 네트워크의 구획화
  • 하나의 컨테이너에 하나의 IP가 할당
  1. 파일 시스템의 구획화
  • 컨테이너 안에서 사용되는 파일시스템은 구획화되어있음
  • 해당 컨테이너의 명령이나 파일 등의 엑세스를 제한할 수 있음

왼쪽 : 도커                                 오른쪽 : 가상머신

도커

리눅스 컨테이너 기술을 활용, App 실행환경을 코드로 작성할 수 있고, OS와 격리화 하여 관리하는 기술.
환경표준화, 수작업으로 일치시키는 환경 구성,


도커 컨테이너를 만들 때, 환경변수를 넣어주는 부분에서 자꾸 에러가 났다. 여러 방법을 찾아보니, txt파일로 직접 만들어서
docker container run 이후 인자로 --env-file = [filename]를 통하여, txt파일 내의 환경변수를 자동 매핑시켜 실행할 수 있었다. 

vi env_list.txt
#---
S3S1N2_Name=장형준
github_username=jun1116 
#---입력 후 Esc, :wq 로 종료
##### Vi 에디터를 통하여 env_list.txt 에 환경변수를 작성
 
docker container run -it --name first --env-file=env_list.txt aibcontents/n312:1.0 bash
# --실행, 후 ls로 내부 파일 확인, python 명령어를 통해 해당 파이썬 파일 실행 

docker inspect를 통해 확인해보니, 내가 입력한 환경변수가 성공적으로 등록되었다

'Python > Docker' 카테고리의 다른 글

[Docker] 도커를 알기위해 알아야하는 것들  (0) 2021.08.31
docker-compose  (0) 2021.03.10

이전 글 : [Tree Based Models/Boosting] - XGBoost_1_트리만들기, Similarity Score, Gain, Pruning(Gamma)

이제 각 Leaf의 Output Value에 대하여 공부해보자. 

$${Output\,Value} = { Sum\,of\,Residuals \over {Number\,of\,Residuals + \lambda} }$$

$${Similarity\,Score} = { Sum\,of\,Residuals,\,Squared \over {Number\,of\,Residuals + \lambda} }$$

분자(잔차 합)의 제곱을 하느냐 마느냐 차이가 있다.

Output Value에서 $\lambda$값이 커진다는것은 Output Value가 작아지는것을 의미하고, 이는 Prediction값을 줄인다는 뜻이다. 

다른 Gradient Boost처럼, XGBoost도 값을 예측하는데에 Initial Prediction (Default = 0.5) 와  Learning Rate , 그리고 해당 리프의Output Value를 사용한다.

$ Prediction = Initial\, Prediction + Learning\,Rate * Output\,Value$

Learning Rate를 eta라고 하는데, 각 Output Value에 이를 곱하여 사용한다. 

Prediction

시리즈 1번에서의 예시자료를 그대로 사용하면,

이로써, 초기의 예측 0.5에서 -2.65로 한층 더 가까워졌다. 

나머지 값들도 예측을 진행

2,3,4번의 값들도 각각의 Leaf에 맞는 output Value를 사용하여 새로운 예측을 하였고, 이로써 Initial Prediction보다 좀 더 나아졌다. 

이제 이렇게 만들어진 Prediction을 통하여 또 잔차를 계산, 그 잔차들을 통해 Root에 넣고, SS계산
Max_Depth가 허용하는 만큼 or leaf에 하나남을때 까지 Split 지점을 찾아서 (최대 Gain을 기준으로) Split 진행, 
최종 Split 후 Gamma 값에 따라서 Gain과 비교하여 Pruning진행, 
Pruning 까지 진행한 후 Output Value를 계산,
Initial Prediction부터 이전 트리까지의 Output Value와 Learning Rate를 사용하여 새로운 Prediction 을 한다.

위 과정을 Maximum Number에 닿을때 까지 or 더이상 진전이 없을때 까지 반복하면 점차점차 각각의 값들에 맞는 Tree들이 만들어지고, 그렇게 모델이 학습된다! 

Gain이 최대가 되는 Split지점을 찾아서 트리만들고 SS계산 
Gain과 Gamma값의 비교를 통하여 Pruning
Leaf의 Output Value를 계산

아래는 StatQuest의 XGBoost Mathmatical Detail을 들으며,,,, 열심히 정리하며 이해하려 노력한 흔적이다!

테일러 근사를 통해 Loss Function을 쉽게 근사하고, Gradient와 Gession을 통해 식을 편하게 바꾸고나서 풀고 정리하고 풀고 정리하면, 
어떻게 Output Value 식이 만들어졌는지 알 수 있다.!

 

 

 

'Machine Learning > Boosting' 카테고리의 다른 글

XGBoost_1_트리만들기, Similarity Score, Gain, Pruning(Gamma)  (0) 2021.03.03
LightGBM  (0) 2021.03.01

출처 :
XGBoost: A Scalable Tree Boosting System / Tianqi Chen , University of Washington
www.youtube.com/watch?v=ZVFeW798-2I&list=RDCMUCtYLUTtgS3k1Fg4y5tAhLbw&start_radio=1&t=900 

Ensemble 모델 중 Boosting의 Gradient 부스팅의 상위호환버전과도 같은 XGBOOST이다. 

강력한 성능을 가진 XGBoost의 논문과, StatQuest의 강의를 열심히 공부하여 아래와 같이 정리해본다. 

초기 prediction 을 만들고 (default=0.5) 시작하는 것은 기존의 부스팅과 동일하다. 그러고 나서 잔차를 먼저 하나의 리프에 넣어준다. 

그리고, 해당 leaf의 Similarity Score를 계산하는데,

$$ {Similarity.Score} = { Sum. of. Residuals, Squared \over{Number. of. Residuals + \lambda }} = { (\Sigma_{i=1}^{n}{Residual})^2 \over { n +\lambda}  }$$
n=해당 leaf의 Residual 수

여기서 $\lambda$는 Regularization Parameter로 우리가 지정해주는 것이다. 
StatQuest에서는 간단하게 설명하기위해 4개의 row를 가진 데이터를 예를 들었다.

최 상위 4개가 들어있을때의 SS는 $(-10.5+6.5+7.5-7.5)\over{4 + \lambda}$이고, 람다를 0이라 가정했을때 (계산을 간편히하기위해)
SS=4이다. 

SS(Similarity Score) 계산이 끝나면 이제 이 리프를 분기시켜서 트리를 만들어본다.
4개의 데이터 사이에 분기점을 만들면 총 3개를 만들 수 있고, 이렇게 분기되는 때마다 SS를 계산하여, Gain을 계산한다.
각 트리를 만들때마다 왼쪽, 오른쪽 리프의 SS계산을 하여 기록하면 이제 분기 전후에 대한 비교가 가능해진다. 
분기 전에는 7.5와 -7.5가 서로 상쇄시켜서 ss가 낮았던 반면, 분기 후엔, 왼쪽 오른쪽 리프들 각각 상쇄작용이 약해서 ss점수가 더 크다. 

이제 각 분기점에 대한 Gain을 계산하는데, Gain의 식은 이러하다. $$Gain = Left_{Similarity} + Right_{Similarity} - ROOT_{Similarity}$$

분기점을 1,2번 데이터 사이로 했을 때 : Gain=120.33

분기점을 1번과 2번데이터 사이(Dosage < 15)로 했을때 Gain은 120.33이다.

22.5를 기준으로 분기하면 왼쪽에 잔차는 -10.5 , 6.5 이고, 오른쪽 잔차는 7.5 , -7.5로

$\lambda=0$으로 설정하였으니 각각의 SS는 $왼쪽 = {{(-10.5 + 6.5)}^2 \over {2 + \lambda}}= {4^2 \over {2}} = 8$ , $오른쪽 = {({7.5-7.5})^2 \over{2+\lambda}}=0$에서 왼쪽이 8 오른쪽이 0이고, Gain은 $8+0 -4 = 4$ 이다.

분기점을 2,3번 데이터 사이(Dosage<22.5)로 했을 때 Gain = 8 + 0 - 4 = 4

이제 분기점을 3,4번 데이터 사이 (Dosage < 30)으로 했을 때를 계산하면 

분기점을 3,4번 데이터 사이(Dosage < 30)으로 했을 때 Gain = 56.33

더이상 관측치 사이에 Split 할 것이 없다. 따라서 각 분기점의 Gain을 비교하면, 1,2번 사이의 Gain (120.33)이 가장 크므로, Dosage < 15을 선택.
왼쪽 리프에는 -10.5 하나만 들어와있고, 오른쪽엔 6.5, 7.5, -7.5가 들어와있다. 

이제 오른쪽 리프에 3개를 분할해보는데, 맨 처음 루트에서 SS와 각각 split지점에 대한 SS, Gain을 계산했던 것과 동일하게 진행한다.
2,3번 사이에 split을 하면, 아래 그림과 같이 나눠지고

그때의 SS와 Gain은 
$SS_{(Dosage<22.5)} = {  {(6.5+7.5-7.5)^2}\over{3+0} } = 14.08  $, $SS_{left}= {{(6.5)^2}\over{1+0} }= 42.25$ , $SS_{right}= { { (7.5-7.5)^2 } \over {2+0} } = 0$이고,
${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } = {42.25 +0 - 14.08} = 28.17$ 이다.

이후, 

3,4번 사이에 split을 하면, 아래 그림과 같은 분기가 이루어지고, 이 떄의 SS를 계산하면 각각 
$SS_{(Dosage<30)} = {  {(6.5+7.5-7.5)^2}\over{3+0} } = 14.08  $ ,  $SS_{left}= {{(6.5+7.5)^2}\over{2+0} }= 98$ , $SS_{right}= { { (-7.5)^2 } \over {2+0} } = 56.25$이고, 
${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } = {98 + 56.25 - 14.08} = 140.17$ 이다. 

22.5로 나눴을 때 보다, 30으로 나눴을 때 더욱 Gain이 크므로, Dosage < 30 을 선택한다. 
이후, max_depth (default = 6) 이 허용하는 만큼 한번 더 왼쪽 리프를 분기할 수 있지만, 계산을 간편히 하기위해 max_depth = 2로 제한하여 분기종료. 

이로써, SS Score와 Gain 을 통하여 트리를 만들었다. 
이제 트리를 Prune해야한다. Prune에서는 Gamma($\gamma$)를 사용한다. 
Gain보다 $\gamma$가 크면 가지친것을 삭제하는 것이다.

$\gamma = 130$이라면, 오른쪽 분기점의 Gain ( 140.17 )이 더 크므로 오른쪽 brunch가 살아남는다, 이에따라, 루트도 함께 살아남게된다.

 

만약 $\gamma=150$이라면?

먼저 오른쪽 brunch의 gain이 150보다 작으므로, 삭제하고, 루트또한 gain이 120.33으로 150보다 작으므로 이번에 만든 트리 자체를 날려버리는 것이다. 

$\lambda$의 역할은?

람다가 어떤 정규화 파라미터로 작용한다는 것은 앞서 설명했다. 하지만 어떻게 작용하길래 정규화 파라미터역할을 하는 것일까?
이는 Similarity Score를 확인하면 알 수 있다.

$$ {Similarity Score} = { { { Sum.Of.Residuals  }^2} \over {Num.Of.Residuals + \lambda }  }$$

$${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } $$

에서, $\lambda$가 커질수록, SS는 작아지고, 이에따라 Gain도 같이 작아지게된다. XGBoost에서 Gain에 따라 Pruning을 진행하는데, $\gamma$값보다 Gain이 작으면, 그 트리(or Brunch)를 삭제하므로, 트리가 자주 지워지게되고, 이는 결국 Overfitting을 방지하는 역할을 하게된다!

 λ 값에 따른동일 트리에 대한 Gain 차이

λ 값에 따른 Gain의 차이가 위와같다. SS를 계산할 때,  λ가 분모에 들어있으므로,  λ가 커지면 -> SS가 작아지고, 이에따라 Gain도 작아지는 것이다.

하나의 리프 내 샘플의 수에 따라서 λ가 어떤 영향을 주는가? 

SS와 Gain 모두 Number of Residual로, 한 리프에 몇개의 샘플(잔차)이 들어있는지에 영향을 받는다.
λ는 모두 분모에 있으며, 이에 대한 영향은 λ가 1만큼 커짐에 따라, Residual이 1개있는 리프는 SS가 $1\over2$만큼 작아진다.
2개있을경우 분모가 2->3으로 변화하므로 한 leaf에 잔차 수가 많으면 많을 수록 λ의 영향이 작다는것이고, 이는 한 leaf의 샘플 수가 많을 수록 λ의 영향이 적어지므로, 향후 파라미터 튜닝에서 λ의 값과, min_samples_leaf를 함께 생각해야 할 것이며 이 둘의 상호 영향또한 염려해야한다는 것으로 이해된다.

위의 내용에 대한 정리

$$ {Similarity.Score} = { Sum. of. Residuals, Squared \over{Number. of. Residuals + \lambda }} = { (\Sigma_{i=1}^{n}{Residual})^2 \over { n +\lambda}  }$$
$${Gain} = { SS_{left} + SS_{right} - SS_{ROOT} } $$
n=해당 leaf의 Residual 수

λ가 0보다 커진다 = Gain이 작아진다 = Leaves를 Prune하기 쉬워진다 = OverFitting을 방지한다 
$\gamma$가 커진다 = Gain-$\gamma$가 음수가 될 확률이 높아진다 =  Brunch를 Prune하기 쉬워진다 = OverFitting을 방지한다.

 

 

'Machine Learning > Boosting' 카테고리의 다른 글

XGBoost_2_Output Value  (0) 2021.03.03
LightGBM  (0) 2021.03.01

드디어.. 머신러닝을 시작했다. 
이전에는 sklearn을 사용하는게 너무 어렵고 낯설었는데, 이제는 이렇게 깔끔한 라이브러리가 있을까....싶다.

Linear Regression부터 시작하여, tree, ensemble 모델들을 배우며 매일매일 머리가 따끈해질정도로 어지러웠지만, 돌이켜보니 이번한달도 참 열심히 배웠구나.. 라는 생각을 하게된다. 

학부시절, 공업수학이랑, 통계학을 배울때 썼던 책을 다시 펴보며, 부족한 부분을 찾아 매꾸려 노력하였으며,, 역시 통계학개론책은 안물려주고 꾹 갖고있길 잘했다는 생각에 뿌듯했다 ㅎ;;

그땐, 통계학이 너무도 어려웠는데, 이젠 또 돌이켜보니 만만하다..; 

매번 큰 산을 하나씩 넘으며, 골짜기에 빠지기도 하지만, 다시 엉금엉금 올라와서 다음산을 향해 가다보면, 언젠가 판교에 갈 수 있겠지? 라는 굳은 다짐을 하며, 이번 Section을 마무리한다.

'AI_BootCamp' 카테고리의 다른 글

Section3 회고  (0) 2021.04.02

+ Recent posts