Skip to content
CatBus

165 POSTS · 2022.02 — 2025.10

막힌 걸 풀고,
왜 그렇게 풀렸는지 적어 둡니다.

알고리즘 문제 풀이와 삽질 기록. 백준·프로그래머스 풀이가 대부분이고, PyTorch 스터디와 인프라 트러블슈팅이 섞여 있습니다.

BOJ1430GOLD 4
def distance(x1, y1, x2, y2, r_squared):
    dist_sq = (x2 - x1) ** 2 + (y2 - y1) ** 2
    return dist_sq <= r_squared

def solve():

    N, R, D, X, Y = map(int, input().split())

    graph = [[0, 0]]
    for _ in range(N):
        graph.append(list(map(float, input().split())))

    v = [False] * (N + 1)
    
    q = deque([(X, Y, 0)])
    
    result = 0.0
    
    r_sq = R * R

    while q:
        cur_x, cur_y, count = q.popleft()

        for i in range(1, N + 1):
            target_x, target_y = graph[i]

            if not v[i] and distance(cur_x, cur_y, target_x, target_y, r_sq):
                v[i] = True

                result += (D / (2 ** count))
                
                q.append((target_x, target_y, count + 1))

    print(result)
solve()

공격

백준 1430번 '공격' (골드 4) 문제 풀이. math, graph theory, graph traversal 로 접근했다.

2025.10.26·2분·math
BOJ1303SILVER 1
N, M = map(int, input().split())
grid = [list(input().strip()) for _ in range(M)]
visited = set()

dxy = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def bfs(x, y, team):
    q = deque([(x, y)])
    visited.add((x, y))
    count = 1
    
    while q:
        x, y = q.popleft()
        
        for dx, dy in dxy:
            nx, ny = x + dx, y + dy
            
            if not(0 <= nx < M and 0 <= ny < N):
                continue
            if (nx, ny) in visited:
                continue
            if grid[nx][ny] != team:
                continue
            visited.add((nx, ny))
            q.append((nx, ny))
            count += 1
    return count

white_power = 0
blue_power = 0

for i in range(M):
    for j in range(N):
        if (i, j) in visited:
            continue
        team = grid[i][j]
        count = bfs(i, j, team)
        if team == 'W':
            white_power += count ** 2
        else:
            blue_power += count ** 2

print(white_power, blue_power)

전쟁 - 전투

백준 1303번 '전쟁 - 전투' (실버 1) 문제 풀이. graph theory, graph traversal, bfs 로 접근했다.

2025.10.26·3분·graph theory
BOJ4994GOLD 3
def bfs(n):
    q = deque([1])
    while q:
        cur = q.popleft()
        for n_num in (cur * 10 + 0, cur * 10 + 1):
            if len(str(n_num)) > 100:
                continue
            
            if n_num % n == 0:
                return n_num
            q.append(n_num)

while True:
    n = int(input())
    if n == 0:
        break

    print(bfs(n))

O(2^100) (worst case, but pruned by modulo check)

배수 찾기

백준 4994번 '배수 찾기' (골드 3) 문제 풀이. math, graph theory, graph traversal 로 접근했다.

2025.10.26·1분·math
BOJ25401GOLD 5
n = int(input())
cards = list(map(int, input().split()))

ans = n - 2

# 모든 가능한 두 카드 조합 (i, j)에 대해 확인
for i in range(n):
    for j in range(i + 1, n):
        if (cards[j] - cards[i]) % (j - i) != 0:
            continue
        d = (cards[j] - cards[i]) // (j - i)
        cnt = 0
        
        for k in range(n):
            expected = cards[i] + (k - i) * d
            if cards[k] != expected:
                cnt += 1
        
        ans = min(ans, cnt)

print(ans)

카드 바꾸기

백준 25401번 '카드 바꾸기' (골드 5) 문제 풀이. math, implementation, bruteforcing 로 접근했다.

2025.10.12·1분·math
BOJ24446SILVER 2
input = sys.stdin.readline

N, M, R = map(int, input().split())

graph = [[] for _ in range(N + 1)]
for _ in range(M):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)

def bfs(start):
    q = deque([(start, 0)])
    visited = [-1] * (N + 1)
    visited[start] = 0
    
    while q:
        cur_node, d = q.popleft()
        for n_node in graph[cur_node]:
            if visited[n_node] != -1:
                continue
            visited[n_node] = d + 1
            q.append((n_node, d + 1))

    return visited[1:]

print(*bfs(R), sep='\n')

알고리즘 수업 - 너비 우선 탐색 3

백준 24446번 '알고리즘 수업 - 너비 우선 탐색 3' (실버 2) 문제 풀이. graph theory, graph traversal, bfs 로 접근했다.

2025.10.12·2분·graph theory
BOJ1756GOLD 5
D, N = map(int, input().split())
oven = list(map(int, input().split()))
doughs = list(map(int, input().split()))

min_oven = oven[0]
for i in range(1, D):
    min_oven = min(min_oven, oven[i])
    oven[i] = min(oven[i], min_oven)

oven_i = D - 1
dough_i = 0

while dough_i < N:
    if oven[oven_i] < doughs[dough_i]:
        # 못들어감
        oven_i -= 1
        if oven_i < 0:
            # 다 들어갈 수 없음
            print(0)
            break
    else:
        dough_i += 1
        oven_i -= 1

else:
    print(oven_i + 2)

피자 굽기

백준 1756번 '피자 굽기' (골드 5) 문제 풀이. implementation 로 접근했다.

2025.10.11·2분·implementation
BOJ2458GOLD 4
from collections import defaultdict, deque

N, M = map(int, input().split())

taller = defaultdict(list)
shorter = defaultdict(list)

for _ in range(M):
    a, b = map(int, input().split())
    taller[a].append(b)
    shorter[b].append(a)

def bfs(graph, start):
    visited = set()
    q = deque([start])
    while q:
        node = q.popleft()
        for n in graph[node]:
            if n not in visited:
                visited.add(n)
                q.append(n)
    return visited

result = 0
for i in range(1, N+1):
    visited_taller = bfs(taller, i)
    visited_shorter = bfs(shorter, i)
    # 앞 뒤의 키를 모두 탐색 가능할 경우
    if len(visited_taller) + len(visited_shorter) == N - 1:
        result += 1

print(result)

키 순서

백준 2458번 '키 순서' (골드 4) 문제 풀이. graph theory, graph traversal, shortest path 로 접근했다.

2025.09.28·2분·graph theory
BOJ13335SILVER 1
n, w, L = map(int, input().split())
trucks = list(map(int, input().split()))

bridge = deque([0] * w)
t = 0
cur_w = 0
i = 0

while i < n:
    t += 1
    cur_w -= bridge.popleft()
    if cur_w + trucks[i] <= L:
        bridge.append(trucks[i])
        cur_w += trucks[i]
        i += 1
    else:
        bridge.append(0)

t += w
print(t)

트럭

백준 13335번 '트럭' (실버 1) 문제 풀이. implementation, data structures, simulation 로 접근했다.

2025.09.28·1분·implementation
BOJ11559GOLD 4
field = list(map(list, [input() for _ in range(12)]))

dyx = [(1, 0), (0, 1), (-1, 0), (0, -1)]

def bfs(sy, sx):
    visited = set()
    q = [(sy, sx)]
    visited.add((sy, sx))
    while q:
        y, x = q.pop(0)
        for dy, dx in dyx:
            ny, nx = y + dy, x + dx
            if not(0 <= ny < 12 and 0 <= nx < 6):
                continue
            if (ny, nx) in visited:
                continue
            
            if field[ny][nx] == field[sy][sx]:
                visited.add((ny, nx))
                q.append((ny, nx))
    
    if len(visited) >= 4:
        for y, x in visited:
            field[y][x] = '.'
        return True
    return False

cnt = 0
while True:
    is_remove = False
    for i in range(12):
        for j in range(6):
            if field[i][j] == '.':
                continue

            if bfs(i, j):
                is_remove = True

    if not is_remove:
        break

    # 블록 내리기
    for j in range(6):
        stack = []
        for i in range(11, -1, -1):
            if field[i][j] == '.':
                continue
            
            stack.append(field[i][j])
            field[i][j] = '.'
        
        i = 11
        while stack:
            field[i][j] = stack.pop(0)
            i -= 1

    cnt += 1

print(cnt)

Puyo Puyo

백준 11559번 'Puyo Puyo' (골드 4) 문제 풀이. implementation, graph theory, graph traversal 로 접근했다.

2025.09.28·3분·implementation
BOJ27211GOLD 5
N, M = map(int, input().split())

grid = [list(map(int, input().split())) for _ in range(N)]

dxy = ((0, 1), (0, -1), (1, 0), (-1, 0))


def bfs(x, y):
    q = deque([(x, y)])

    while q:
        x, y = q.popleft()

        for dx, dy in dxy:
            nx, ny = x + dx, y + dy

            nx %= N
            ny %= M

            if grid[nx][ny] == 1:
                continue

            grid[nx][ny] = 1
            q.append((nx, ny))

    return 1


result = 0

for x in range(N):
    for y in range(M):
        if grid[x][y] == 1:
            continue

        result += bfs(x, y)

print(result)

도넛 행성

백준 27211번 '도넛 행성' (골드 5) 문제 풀이. graph theory, graph traversal, bfs 로 접근했다.

2025.09.28·2분·graph theory
BOJ13903SILVER 1
R, C = map(int, input().split())
grid = [list(map(int, input().split())) for _ in range(R)]

N = int(input())
dxy = [list(map(int, input().split())) for _ in range(N)]

visited = [[False] * C for _ in range(R)]

q = deque()
for i, floor in enumerate(grid[0]):
    if floor == 1:
        q.append([0, i, 0])
        visited[0][i] = True

result = -1
while q:
    x, y, t = q.popleft()
    
    if x == R - 1:
        result = t
        break
    
    for dx, dy in dxy:
        nx, ny = x + dx, y + dy
        if not(0 <= nx < R and 0 <= ny < C):
            continue
        if grid[nx][ny] == 0 or visited[nx][ny]:
            continue
        
        q.append([nx, ny, t + 1])
        visited[nx][ny] = True

print(result)

출근

백준 13903번 '출근' (실버 1) 문제 풀이. graph theory, graph traversal, bfs 로 접근했다.

2025.09.14·2분·graph theory
BOJ1091GOLD 4
P = list(map(int, input().split()))
S = list(map(int, input().split()))

def shuffle_card(cards, S):
    new_cards = [0] * (N)
    for i, n_i in enumerate(S):
        new_cards[n_i] = cards[i]
    return new_cards

cards = P[:]
answer = [0, 1, 2] * (N // 3)
cnt = 0

while answer != cards:
    cards = shuffle_card(cards, S)
    cnt += 1

    if cards == P:
        print(-1)
        break

else:
    print(cnt)

카드 섞기

백준 1091번 '카드 섞기' (골드 4) 문제 풀이. implementation, simulation 로 접근했다.

2025.09.14·2분·implementation
PROGRAMMERS164671SQL
SELECT CONCAT('/home/grep/src/', b.board_id, '/', f.file_id, f.file_name, f.file_ext) as file_path
FROM used_goods_board AS b
    JOIN
    used_goods_file AS f
    ON b.board_id = f.board_id
WHERE b.views = (SELECT MAX(views) FROM used_goods_board)
ORDER BY f.file_id DESC;

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 1회 JOIN 으로 두 테이블을 연결
  • WHERE 로 조건에 맞는 행만 남김
  • ORDER BY 로 정렬 (내림차순 포함)
  • 서브쿼리를 사용
  • 사용한 함수: CONCAT(), MAX()

조회수가 가장 많은 중고거래 게시판의 첨부파일 조회하기

조회수 최댓값을 서브쿼리로 구해 그 게시글의 첨부파일만 남기고, CONCAT 으로 파일 경로 문자열을 만들어 낸다.

2025.07.17·1분·sql
PROGRAMMERS284531SQL
SELECT route,
    CONCAT(ROUND(SUM(d_between_dist), 1), 'km') AS total_distance,
    CONCAT(ROUND(AVG(d_between_dist), 2), 'km') AS average_distance
FROM subway_distance
GROUP BY route
ORDER BY ROUND(SUM(d_between_dist), 1) DESC;

-- SELECT route,
--     CONCAT(ROUND(SUM(d_between_dist), 1), 'km') AS total_distance,
--     CONCAT(ROUND(AVG(d_between_dist), 2), 'km') AS average_distance
-- FROM subway_distance
-- GROUP BY route
-- ORDER BY total_distance DESC;

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

노선별 평균 역 사이 거리 조회하기

노선별로 SUM 과 AVG 를 각각 구해 ROUND 자릿수를 다르게 주고, CONCAT 으로 'km' 를 붙여 문자열로 만든다.

2025.07.17·1분·sql
PROGRAMMERS157339SQL
WITH cars_with_fee AS (
    SELECT
        c.car_id,
        c.car_type,
        FLOOR(c.daily_fee * 30 * (100 - p.discount_rate) / 100) AS fee
    FROM
        car_rental_company_car AS c
    JOIN
        car_rental_company_discount_plan AS p ON c.car_type = p.car_type
    WHERE
        c.car_type IN ('세단', 'SUV')
        AND p.duration_type = '30일 이상'
)
SELECT
    car_id,
    car_type,
    fee
FROM
    cars_with_fee
WHERE
    car_id NOT IN (
        SELECT
            car_id
        FROM
            car_rental_company_rental_history
        WHERE
            end_date >= '2022-11-01' AND start_date <= '2022-11-30'
    )
    AND fee >= 500000 AND fee < 2000000
ORDER BY
    fee DESC,
    car_type ASC,
    car_id DESC;

특정 기간동안 대여 가능한 자동차들의 대여비용 구하기

CTE 에서 30일 요금제 할인율을 FLOOR 로 적용해 두고, 해당 기간에 대여 기록이 겹치는 차를 NOT IN 으로 걸러 낸다.

2025.07.10·2분·sql
PROGRAMMERS131533SQL
SELECT pd.product_code, SUM(os.sales_amount) * pd.price as sales
FROM product as pd
    JOIN
    offline_sale as os
    ON pd.product_id = os.product_id
GROUP BY pd.product_id
ORDER BY sales DESC, pd.product_code ASC

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 1회 JOIN 으로 두 테이블을 연결
  • GROUP BY 로 묶어서 집계
  • ORDER BY 로 정렬 (내림차순 포함)
  • 사용한 함수: SUM()

상품 별 오프라인 매출 구하기

상품과 오프라인 판매를 조인해 SUM(수량) × 단가로 매출을 만든다. GROUP BY 는 product_id 로 하고 정렬만 product_code 를 쓴다.

2025.07.10·1분·sql
PROGRAMMERS284528SQL
-- 사번, 성명, 평가 등급, 성과금
-- 부서 정보
-- 사원 정보
-- 사원 평가 정보
WITH avg_emp AS (
    SELECT he.emp_no, he.emp_name, he.sal,
        CASE
            WHEN AVG(hg.score) >= 96 THEN 'S'
            WHEN AVG(hg.score) >= 90 THEN 'A'
            WHEN AVG(hg.score) >= 80 THEN 'B'
            ELSE 'C'
    END AS grade
    FROM hr_employees AS he
        LEFT JOIN
        hr_grade AS hg
        ON he.emp_no = hg.emp_no
    GROUP BY he.emp_no
)

SELECT emp_no, emp_name, grade,
    CASE
        WHEN grade = 'S' THEN sal * 0.2
        WHEN grade = 'A' THEN sal * 0.15
        WHEN grade = 'B' THEN sal * 0.1
        WHEN grade = 'C' THEN 0
        ELSE NULL
    END AS bonus
FROM avg_emp
ORDER BY emp_no

연간 평가점수에 해당하는 평가 등급 및 성과금 조회하기

CTE 에서 사원별 평균 점수를 CASE 로 S·A·B·C 등급으로 바꾸고, 바깥에서 등급별 지급률을 다시 CASE 로 적용해 성과금을 구한다.

2025.07.03·2분·sql
PROGRAMMERS299308SQL
WITH quarter AS (
    SELECT 
        CASE
            WHEN(QUARTER(differentiation_date)) = 1 THEN '1Q'
            WHEN(QUARTER(differentiation_date)) = 2 THEN '2Q'
            WHEN(QUARTER(differentiation_date)) = 3 THEN '3Q'
            WHEN(QUARTER(differentiation_date)) = 4 THEN '4Q'
            ELSE NULL
    END AS quarter
    FROM ecoli_data
)

SELECT quarter, COUNT(*) AS ecoli_count
FROM quarter
GROUP BY quarter
ORDER BY quarter

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

분기별 분화된 대장균의 개체 수 구하기

QUARTER() 로 분화일을 분기 문자열로 바꾸는 CTE 를 만들고, 바깥에서 그 값으로 GROUP BY 해 분기별 개체 수를 센다.

2025.07.03·1분·sql
PROGRAMMERS131118SQL
-- 코드를 입력하세요
WITH rm AS (SELECT ri.rest_id,
            ri.rest_name,
            ri.food_type,
            ri.favorites,
            ri.address,
            ROUND(AVG(review_score), 2) AS score
    FROM rest_info AS ri
        JOIN
        rest_review AS rr
        ON ri.rest_id = rr.rest_id
    WHERE ri.address LIKE "서울%"
    GROUP BY ri.rest_id, RI.REST_NAME, RI.FOOD_TYPE, RI.FAVORITES, RI.ADDRESS)

SELECT *
FROM rm
ORDER BY score DESC, favorites DESC;

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

서울에 위치한 식당 목록 출력하기

CTE 안에서 식당과 리뷰를 조인해 평점을 ROUND(AVG(), 2) 로 묶고, 주소가 '서울' 로 시작하는 곳만 남긴 뒤 평점·즐겨찾기 순으로 정렬한다.

2025.06.26·1분·sql
PROGRAMMERS273711SQL
WITH rare_item AS (
    SELECT it.item_id
    FROM item_info AS ii
        JOIN
        item_tree AS it
        ON ii.item_id = it.parent_item_id
    WHERE rarity = "RARE"
)

# SELECT *
# FROM rare_item

SELECT ii.item_id, ii.item_name, ii.rarity
FROM rare_item AS ri
    JOIN
    item_info AS ii
    ON ri.item_id = ii.item_id
ORDER BY ii.item_id DESC

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 2회 JOIN 으로 두 테이블을 연결
  • WHERE 로 조건에 맞는 행만 남김
  • ORDER BY 로 정렬 (내림차순 포함)
  • 서브쿼리를 사용

업그레이드 된 아이템 구하기

item_tree 에서 부모로 등장하는 아이템 중 rarity 가 RARE 인 것을 CTE 로 모은 뒤, 아이템 정보와 조인한다.

2025.06.26·1분·sql
PROGRAMMERS293261SQL
WITH max_fish AS (
    SELECT fish_type, MAX(length) AS max_length
    FROM fish_info
    GROUP BY fish_type
)

SELECT fi.id, fni.fish_name, fi.length
FROM fish_info fi
JOIN 
    max_fish mf
    ON fi.fish_type = mf.fish_type AND fi.length = mf.max_length
JOIN 
    fish_name_info fni
    ON fi.fish_type = fni.fish_type
ORDER BY 
    fi.id;

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 2회 JOIN 으로 두 테이블을 연결
  • GROUP BY 로 묶어서 집계
  • ORDER BY 로 정렬
  • 서브쿼리를 사용
  • 사용한 함수: MAX()

물고기 종류 별 대어 찾기

어종별 MAX(length) 를 CTE 로 만든 뒤 어종과 길이를 둘 다 조건에 걸어 조인해, 종마다 가장 큰 개체만 남긴다.

2025.06.12·1분·sql
PROGRAMMERS301650SQL
SELECT e1.id
FROM ecoli_data AS e1
    JOIN ecoli_data AS e2 ON e1.parent_id = e2.id
    JOIN ecoli_data AS e3 ON e2.parent_id = e3.id
WHERE e3.parent_id IS NULL
ORDER BY e1.id ASC

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 2회 JOIN 으로 두 테이블을 연결
  • WHERE 로 조건에 맞는 행만 남김
  • ORDER BY 로 정렬

특정 세대의 대장균 찾기

ecoli_data 를 세 번 조인해 부모의 부모까지 거슬러 올라가고, 그 위가 NULL 인 경우만 남겨 3세대를 찾는다.

2025.05.07·1분·sql
PROGRAMMERS301649SQL
WITH per AS (
    SELECT id, PERCENT_RANK() OVER (ORDER BY size_of_colony DESC) as per_rank
    FROM ECOLI_DATA
)
SELECT id,
CASE
    WHEN per_rank < 0.25 THEN 'CRITICAL'
    WHEN per_rank < 0.5 THEN 'HIGH'
    WHEN per_rank < 0.75 THEN 'MEDIUM'
    ELSE 'LOW'
END AS colony_name
FROM per
ORDER BY id;

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • ORDER BY 로 정렬 (내림차순 포함)
  • 서브쿼리를 사용
  • CASE WHEN 으로 조건에 따라 값을 분기

대장균의 크기에 따라 분류하기 2

PERCENT_RANK() 윈도 함수로 크기 백분위를 매기고, CASE 로 25%·50%·75% 구간을 잘라 등급 이름을 붙인다.

2025.05.07·1분·sql
PROGRAMMERS273712SQL
WITH null_item as (SELECT t1.item_id
    FROM item_tree as t1
        LEFT JOIN
        item_tree as t2
        ON t1.item_id = t2.parent_item_id
    WHERE t2.item_id IS NULL
)

SELECT ii.item_id, ii.item_name, ii.rarity
FROM null_item as ni
    JOIN
    item_info as ii
    ON ni.item_id = ii.item_id
ORDER BY ii.item_id DESC

문제에서 요구한 결과를 만들기 위해 쿼리에서 실제로 쓴 것들이다.

  • 테이블 2회 LEFT JOIN 으로 두 테이블을 연결
  • WHERE 로 조건에 맞는 행만 남김
  • ORDER BY 로 정렬 (내림차순 포함)
  • 서브쿼리를 사용

업그레이드 할 수 없는 아이템 구하기

item_tree 를 자기 자신과 LEFT JOIN 해서 자식이 없는(NULL 인) 아이템만 남긴다 — 더 업그레이드할 수 없는 아이템이다.

2025.05.05·1분·sql
TROUBLESHOOTINGIndexError
  • 위 현상이 나타날 수 있는 이유
    • 토크나이저 설정 오류:
      • 문제: 사용하는 토크나이저가 예상과 다르게 token_type_ids를 생성하고 있을 수 있습니다. 예를 들어, 토크나이저가 질의-응답 모델처럼 두 개 이상의 세그먼트를 처리하도록 설정되어 있거나, 잘못된 매개변수가 사용되었을 수 있습니다.
      • 해결 방법:
        • 토크나이저의 설정과 사용법을 다시 확인합니다. 특히 encode_plus 또는 __call__ 메서드의 매개변수를 주의 깊게 살펴보아야 합니다.
        • 단일 문장 분류와 같은 작업에서는 token_type_ids가 실제로 필요하지 않으므로, 토크나이저에서 이를 생성하지 않도록 설정하거나, 생성된 값을 무시해야 합니다.
        • encodings['token_type_ids'] = torch.zeros_like(encodings['input_ids']) 이 코드를 통해 모든 토큰 id를 0으로 설정하면, 위 문제를 회피할 수 있습니다.
    • 입력 데이터 형태 오류:
      • 문제: 입력 데이터가 토크나이저가 예상하는 형태와 다를 수 있습니다. 예를 들어, 질의-응답 쌍과 같은 형태의 데이터가 단일 문장으로 처리되고 있을 수 있습니다.
      • 해결 방법:
        • 입력 데이터의 형태를 확인하고, 토크나이저가 데이터를 올바르게 처리할 수 있도록 데이터를 조정합니다.
        • 문제가 되는 특정 데이터의 형태를 확인하는 것이 중요합니다.
    • 모델 또는 라이브러리 문제:
      • 문제: 드물게 모델 자체 또는 사용하는 라이브러리에 오류가 있을 수 있습니다.
      • 해결 방법:
        • 사용하는 모델과 라이브러리의 버전을 확인하고, 최신 버전으로 업데이트하거나 다른 버전으로 시도해 봅니다.
        • 허깅페이스와 같은 유명한 모델은, 버전 업데이트가 빠른 편입니다. 가능한 최신버전을 이용하는게 좋습니다.
    • 패딩의 문제점:
      • 문제: 패딩 과정에서 일부 토큰 타입 ID가 원하지 않는 값으로 설정될 수 있습니다.
      • 해결 방법:
        • 패딩 설정과 방식을 확인하고, 필요에 따라 패딩 마스크를 사용하여 패딩된 토큰을 모델이 무시하도록 합니다.

pytorch의 IndexError: index out of range in self 에러

BERT 로 예측할 때 뜬 IndexError. 토크나이저가 token_type_ids 에 2 이상을 넣어 임베딩 레이어의 허용 범위를 벗어난 것이 원인이었다.

2025.03.28·8분·pytorch
TROUBLESHOOTINGjenkins
    ...
    		stages {
            stage('Checkout Code') {
                steps {
                    // 내장 checkout 단계를 사용합니다. Jenkins가 모든 것을 처리하도록 합니다.
                    checkout([$class: 'GitSCM',
                              branches: [[name: '*/release']], // 또는 '*/main' 등
                              extensions: [],
                              userRemoteConfigs: [[credentialsId: 'gitlab-token',
                                                   url: 'gitlab-url']]])
                }
            }
    ...
    ```

jenkins에서 git pull 사용시 발생하는 문제

Jenkins 의 sh 단계에서 부른 git pull 이 checkout 이 설정한 자격 증명을 상속하지 못해 Access denied. 내장 checkout 으로 대체했다.

2025.03.24·2분·jenkins
TROUBLESHOOTINGConnectionError
...
dev-backend   |     connection.connect()
dev-backend   |     ~~~~~~~~~~~~~~~~~~^^
dev-backend   |   File "/usr/local/lib/python3.13/site-packages/redis/connection.py", line 363, in connect
dev-backend   |     raise ConnectionError(self._error_message(e))
dev-backend   | redis.exceptions.ConnectionError: Error 111 connecting to 127.0.0.1:6379. Connection refused.
  • redis가 docker에 제대로 연결되지 않아 발생하는 문제
    • 127.0.0.1은 local에서 사용하는 것이므로 docker에 맞게 바꿔줄 필요가 있음

docker 상에서 redis가 정상적으로 연결되지 않는 문제

컨테이너의 redis 에 127.0.0.1 로 붙으려 해 연결이 안 됐다. host 를 redis 컨테이너 이름으로 바꿨다.

2025.02.16·1분·docker
TROUBLESHOOTINGdocker
    ...
    CMD ["cp", "-r", "/app/dist", "/app/frontend_build"]
    ```

- volume mount를 local의 상태가 덮어 쓰기 된다는 것을 명심하자
---

# 📚 Reference

docker에서 volume을 연결해도 파일이 보이지 않는 문제

이미지 안에서 만든 build 산출물이 볼륨 마운트로 비어 있는 로컬 디렉터리에 덮여 사라졌다. Dockerfile 에서 build 파일을 복사하도록 고쳤다.

2025.02.16·1분·docker
TROUBLESHOOTINGgitlab ci
    ...
    build_backend:
      tags:
        - backend-runner
      script:
        - cd Backend
        - docker build -t $IMAGE_BACKEND:$TAG -f Dockerfile.dev .
        - docker push $IMAGE_BACKEND:$TAG
      only:
        - develop
        - master
    
    build_frontend:
      tags:
        - frontend-runner
      script:
        - cd Frontend
        - docker build -t $IMAGE_FRONTEND:$TAG -f Dockerfile.dev .
        - docker push $IMAGE_FRONTEND:$TAG
      only:
        - develop
        - master
    ...
    ```

gitlab ci 상에서 permission denied가 발생하는 문제

같은 stage 의 두 job 이 runner 하나를 두고 다퉈 뒤늦은 쪽이 docker daemon 권한을 얻지 못했다. tags 로 job 마다 runner 를 나눠 지정했다.

2025.02.15·4분·gitlab-ci-cd
TROUBLESHOOTINGconda
  • 분명 conda activate 이후 가상환경 안에서 pip install을 통해 모듈을 설치했음에도 불구하고 not found module이 발생하는 경우가 있다.

  • pip의 경로를 확인해보면 conda 환경의 경로가 아님을 확인할 수 있다. 이 때문에 global 환경에 설치가 되어 가상환경 내에서 사용할 수 없었던 것
    $ which pip
    /home/user/.local/bin/pip
    ```

- pip를 현재 가상환경의 것으로 사용하도록 명시한다.

```bash
    python -m pip install <module_name>
    ```

# 📚 Reference

conda 가상환경 상에서 pip install로 설치한 모듈을 찾을 수 없는 경우

conda 환경에서 pip install 한 모듈을 찾지 못했다. pip 경로가 전역을 가리켜 전역에 설치되고 있었던 것이라 가상환경의 pip 를 명시했다.

2025.02.12·1분·python
TROUBLESHOOTINGSSL
    server {
        listen 80;
        server_name momoso106.duckdns.org;
    
        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }
    
        location / {
            return 301 https://$host$request_uri;
        }
    }
    
    server {
        listen 443 ssl; // 이게 문제임
        server_name momoso106.duckdns.org;
    
        location / {
            root /app/frontend/build;
            index index.html;
            try_files $uri /index.html;
        }
    
        location /api/ {
            proxy_pass http://backend:8000/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
        }
    
        location /openvidu/ {
            proxy_pass https://openvidu:4443/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_ssl_verify off;
        }
    }
    
    ```

SSL 발급 억까 탐방기

인증서를 받기도 전에 nginx conf 가 listen 443 ssl 을 쓰고 있어 nginx 가 죽었고, 그 탓에 certbot 인증 경로에도 못 붙었다. ssl 지시문을 빼고 먼저 발급받았다.

2025.02.08·5분·ssl
TROUBLESHOOTINGdocker
  • webpack@4.47.0webpack-cli@5.1.4 간의 의존성 충돌 문제이다. webpack-cli@5.x.xwebpack@5.x.x를 필요로 하지만, 현재 프로젝트에서는 webpack@4.47.0이 사용되고 있기 때문.

  • 일단 webpack의 버전을 5로 올려줬다.
    npm install webpack@5 --save-dev
    ```

- 추후에 front 담당자와 확실히 정하면 같다.
---

# 📚 Reference

docker 빌드 중 npm install 의존성 충돌

컨테이너에서 npm install 이 실패. webpack-cli 5 는 webpack 5 를 요구하는데 프로젝트가 webpack 4 를 쓰고 있던 의존성 충돌이라 webpack 을 5 로 올렸다.

2025.02.05·2분·gitlab-ci-cd
TROUBLESHOOTINGdocker
    ...
      frontend:
        build: ./Frontend
        environment:
          - CHOKIDAR_USEPOLLING=true  # 파일 변경 감지를 위한 설정
        ports:
          - "3000:3000"
        volumes:
          - ./Frontend:/app  # 로컬 파일 시스템을 컨테이너에 마운트
          - /app/node_modules  # 로컬의 node_modules가 container 내에 적용되지 않도록
        networks:
          - app_network
    ...
    ```

- 해결하기 까지 정말 오래 걸렸다. node_modules를 지우고 하더라고 한 번만 잘 되고 계속 안됐었는데 mount된 volume에 local 파일이 영향을 줘서 생기는 문제라고는 생각을 못했다.
- 어찌 보면 기본적인 compose 작성법일 수 있는데 기초 공부가 조금 부족하지 않았나 반성하게 되는 계기가 되었다.
---

docker에서 react-scripts를 찾지 못하는 문제

compose 로 react 를 띄우면 로컬 node_modules 가 마운트로 컨테이너 쪽을 덮어써 react-scripts 를 못 찾았다. node_modules 를 별도 볼륨으로 떼어 냈다.

2025.02.03·3분·docker
TROUBLESHOOTINGpem
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    Permissions 0644 for 'amazonec2.pem' are too open.
    It is recommended that your private key files are NOT accessible by others.
    This private key will be ignored.
    bad permissions: ignore key: amazonec2.pem
    Permission denied (publickey).
    ```

- pem 파일에 너무 많은 권한이 부여되어 보안 AWS에서 거부한
---

pem 키로 ssh 접근 시 권한 문제가 발생하는 경우

pem 키에 권한이 너무 열려 있어 AWS 가 ssh 접근을 거부했다. chmod 400 으로 읽기 전용으로 바꿨다.

2025.01.31·1분·ssh
TROUBLESHOOTINGdocker
/ # gitlab-runner exec shell build
Runtime platform                                    arch=amd64 os=linux pid=139 revision=66a723c3 version=17.5.0
FATAL: Command exec not found.
  • gitlab-runner의 버전이 v17 이상인 경우 exec가 안되는 경우가 있다고 한다.

🛠 해결책

  • gitlab-runner의 버전을 v16.10.0으로 내렸다.
    services:
      gitlab-runner:
        image: gitlab/gitlab-runner:v16.10.0
        container_name: gitlab_runner
        restart: always
        volumes:
          - ./gitlab-runner/config:/etc/gitlab-runner
          - /var/run/docker.sock:/var/run/docker.sock
          - ./entrypoint.sh:/entrypoint.sh
        environment:
          - RUNNER_NAME=my-runner
          - TZ=Asia/Seoul
          - CI_SERVER_URL=${CI_SERVER_URL}
          - REGISTRATION_TOKEN=${REGISTRATION_TOKEN}
          - RUNNER_EXECUTOR=${RUNNER_EXECUTOR}
        ports:
          - "9252:9252"
        entrypoint: ["tail", "-f", "dev/null"]
    ```

docker 안에서 gitlab-runner exec가 작동하지 않는 문제

gitlab-runner v17 이상에서 exec 가 동작하지 않는다. v16.10.0 으로 내려서 해결했다.

2025.01.23·3분·gitlab-ci-cd
TROUBLESHOOTINGgitlab-runner

🛠 해결책

  • 명령어 변경
    gitlab-runner register \
      --url "$CI_SERVER_URL"\
      --token "$REGISTRATION_TOKEN"\
      --listen-address ":9252" \
      --executor "docker"\
      --docker-image "alpine:latest"
    ```

- `config.toml`파일을 제거하고 다시 등록하니 403 발생 안됨

## 1. **`--registration-token`**

- *Runner를 등록(Register)** 사용되는 **일회성 토큰**입니다.
- GitLab에서 Runner를 프로젝트, 그룹, 또는 인스턴스에 연결하기 위해 사용합니다.
- 토큰은 Runner를 등록한 뒤에는 이상 사용되지 않습니다.

- **프로젝트** **그룹** **Settings > CI/CD > Runners** 섹션에서 확인 가능합니다.
- 예: `Settings > CI/CD > Specific Runners`에서 `registration-token`으로 표시됩니다.

gitlab-runner가 연결하려 할 때 403 error

register 는 되는데 Checking for jobs 에서 403. 예전에 등록해 둔 runner 들이 config.toml 에 남아 함께 실행되던 것이 원인이라 파일을 지우고 다시 등록했다.

2025.01.22·7분·gitlab-ci-cd
DOCKERCH6
인스트럭션내용
FROM토대가 되는 이미지를 지정
ADD이미지에 파일이나 폴더를 추가
COPY이미지에 파일이나 폴더를 복사
RUN이미지를 빌드할 때 실행할 명령어 지정
CMD컨테이너를 실행할 때 실행할 명령어 지정
ENTRYPOINT컨테이너를 실행할 때 실행할 명령어 강제 지정
ONBUILD이 이미지를 기반으로 다른 이미지를 빌드할 때 실행할 명령어를 지정
EXPOSE이미지가 통신에 사용할 포트를 명시적으로 지정
VOLUMEpersistency data를 저장할 경로를 명시적으로 지정
ENV환경변수 정의
WORKDIRRUN, CMD, ENTRYPOINT, ADD, COPY에 정의된 명령어를 실행하는 작업 디렉터리를 지정
SHELL빌드 시 사용할 shell을 변경
LABEL이름이나 버전, 저작자 정보를 설정
USERRUN, CMD, ENTRYPOINT에 정의된 명령어를 실행하는 사용자 또는 그룹을 지정
ARGdocker build 커맨드를 사용할 때 입력받을 수 있는 인자를 선언
STOPSIGNALdocker build 커맨드를 사용할 때 컨테이너 안에서 실행 중인 프로그램에 전달되는 시그널을 변경
HEALTHCHECK컨테이너 health check 방법을 커스터마이징

컨테이너와 호스트 간에 파일 복사하기

프로그램만으로 구성된 시스템은 그리 많지 않다. 5장에서도 나왔듯이 프로그램 외에도 프로그래밍 언어의 런타임이나 웹 서버, 데이터베이스 등이 함께 시스템을 구성한다.

2024.12.12·12분·docker
DOCKERCH7
# 베이스 이미지 선택
FROM kwon0528/postgres_backend:0.5

# 필요한 패키지 설치 (bash 설치)
RUN apt-get update && apt-get install -y bash

# 작업 디렉토리 설정
WORKDIR /home

# back.sh 파일 복사
COPY back.sh /home/back.sh

# 실행 권한 부여
RUN chmod +x /home/back.sh

# 실행
CMD ["bash", "/home/back.sh"]

Dockerfile.vue

# 베이스 이미지 선택
FROM kwon0528/vue_frontend:0.3.2

# 작업 디렉토리 설정
WORKDIR /app

# # front.sh 파일 복사
# COPY front.sh /app/chuibot/front.sh

# # 실행 권한 부여
# RUN chmod +x /app/chuibot/front.sh

# # 실행
# CMD ["sh", "/app/front.sh"]
# CMD cd ~/chuibot && git pull

도커 컴포즈를 익히자

시스템 구축과 관련된 명령어를 하나의 텍스트 파일(정의 파일)에 기재해 명령어 한번에 시스템 전체를 실행하고 종료와 폐기까지 한번에 하도록 도와주는 도구이다.

2024.12.12·15분·docker
DOCKERCH8
항목내용
kube-let마스터노드에 있는 kube-scheduler와 연동하며 워커 노드에 파드를 배치하고 실행한다. 또 실행 중인 파드의 상태를 정기적으로 모니터링하며 kube-scheduler에 통지한다
kube-proxy네트워크 통신의 라우팅 메커니즘

쿠버네티스를 익히자

Kubernetes는 컨테이너 오케스트레이션 도구이다. 컨테이너 오케스트레이션이란 시스템 전체를 통괄하고 여러 개의 컨테이너를 관리하는 일을 말한다.

2024.12.12·12분·docker
DOCKERCH5
C:\Users\Kwon>docker run --name mysql000ex11 -dit --net=wp -e MYSQL_ROOT_PASSWORD=1234 -e MYSQL_DATABASE=wordpress---db -e MYSQL_USER=wordpress000user -e MYSQL_PASSWORD=1234 mysql --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-authentication-plugin=mysql_native_password
853dd040ade1fef5532d8429504d7a1b2ab3f0b3cb6f8ed635918d4df773b1b6

C:\Users\Kwon>docker run --name wordpress000ex12 -dit --net=wp -p 8085:80 -e WORDPRESSS_DB_HOST=mysql000ex11 -e WORDPRES
S_DB_NAME=wordpress000db -e WORDPRESS_DB_USER=wordpress000user -e WORDPRESS_DB_PASSWORD=1234 wordpress
aef41f135fbdba00fe389a0f882e52aaa05f24f638fbf7ce3277dfc964ea5e79

C:\Users\Kwon>docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                  NAMES
aef41f135fbd   wordpress   "docker-entrypoint.s…"   24 seconds ago   Up 21 seconds   0.0.0.0:8085->80/tcp   wordpress000ex12

여러 개의 컨테이너를 연동해 실행해보자

워드프레스는 웹 사이트를 만들기 위한 소프트웨어로, 아파치나 데이터베이스, PHP 런타임 등을 필요로 하기 때문에 구축을 위한 연습 소재로 좋다.

2024.12.09·7분·docker
DOCKERCH4
이미지 이름컨테이너의 내용컨테이너 실행에 주로 사용되는 옵션 및 인자
openjdkjava 런타임-d를 사용하지 않고 인자로 java 명령 등을 지정해 도구 형태로 사용한다.
pythonpython 런타임-d를 사용하지 않고 인자로 python 명령 등을 지정해 도구 형태로 사용한다.
phpPHP 런타임웹 서버가 포함된 것과 실행 명령만 포함된 것으로 나위어 제공된다.
rubyruby 런타임웹 서버가 포함된 것과 실행 명령만 포함된 것으로 나위어 제공된다.
perlperl 런타임-d를 사용하지 않고 인자로 perl 명령 등을 지정해 도구 형태로 사용한다
gccC/C++ 컴파일러-d를 사용하지 않고 인자로 gcc 명령 등을 지정해 도구 형태로 사용한다
nodeNode.js-d를 사용하지 않고 인자로 app 명령 등을 지정해 도구 형태로 사용한다
registry도커 레지스트리-d옵션을 사용해 백그라운드로 실행한다. -p 옵션으로 포트 번호를 지정한다.
wordpressWordPress-d옵션을 사용해 백그라운드로 실행한다. -p 옵션으로 포트 번호를 지정한다. MySQL 또는 MariaDB가 필요하다. 접속에 필요한 패스워드는 -e 옵션으로 지정한다.
nextcloudNextCloud-d옵션을 사용해 백그라운드로 실행한다. -p 옵션으로 포트 번호를 지정한다.
redmineRedmine-d옵션을 사용해 백그라운드로 실행한다. -p 옵션으로 포트 번호를 지정한다. PostgreSQL 또는 MySQL이 필요하다

컨테이너를 실행해 보자

컨테이너를 다루는 모든 명령은 docker 명령어로 시작한다 docker 명령어 뒤에 '무엇을', '어떻게', '대상' 순으로 지정하여 명령어를 작성한다.

2024.11.17·37분·docker
DOCKERCH2

2-1. OS는 뭘 하는가?

소프트웨어나 프로그램의 명령을 하드웨어에 전달하는 역할을 한다.

하드웨어는 자신의 판단으로 여러 의미 중 적절한 것을 고르거나 적당히 일을 할 수 없고, 지시받은 대로만 수행할 수 있으므로 동작 하나하나를 지정해 주지 않으면 안 된다.

OS는 이런 일들을 하드웨어가 알아들을 수 있게 번역하여 전달하게 된다.

2-2. 도커의 리눅스 OS 비슷한 것

본래 OS는 ‘커널’이라는 부분과 ‘그 이외의 주변 부분’으로 구성된다. 주변 부분이 프로그램의 연락 내용을 커널에 전달하고 커널이 하드웨어를 다룬다.

여기서 도커의 컨테이너는 운영체제의 주변 부분을 가지고 있다. 이를 통해 프로그램의 명령을 전달받고, 이를 밑바탕이 되는 커널에 전달하는 구조로 되어 있다.

이 덕분에 OS 전체를 컨테이너에 넣지 않을 수 있어, 도커는 가장 큰 특징인 ‘가벼움’을 얻을 수 있다.

2-3. 도커는 기본적으로 Linux 용이다.

도커의 동작 원리

위 그림은 도커의 간단한 구조를 나타낸 것이다. 물리 서버가 있고, 여기서 동작하는 리눅스 운영체제가 있다.

2024.11.07·8분·docker
DOCKERCH1

2-1. 컨테이너

컴퓨터(서버) 상의 환경을 작게 분할한 공간

이렇게 나누어진 컨테이너에 데이터나 프로그램을 두어 격리한다. 이를 격리하는 기능을 제공하는 소프트웨어가 도커다. 도커는 도커 소프트웨어 본체인 도커 엔진을 설치해 사용한다. 이 도커 엔진을 사용하여 컨테이너를 생성하고 구동할 수 있다.

컨테이너를 만드는 데에는 도커 엔진 외에도 이미지가 필요하다

이미지는 컨테이너의 빵틀과도 같은 역할을 하는 것

이미지에는 많은 종류가 있다. 아파치 컨테이너를 만들려면 아파치 이미지를 사용하고, MySQL 컨테이너를 만들려면 MySQL 이미지를 사용한다.

용량이 허락하는 한, 하나의 도커에서 여러 개의 컨테이너를 만들 수 있다.

2-2. 도커의 작동

도커는 리눅스 상에서 동작한다.

Windows나 MacOS에서도 동작하지만 결국 내부적으로 리눅스가 개입한다. 또한, 컨테이너에서 동작시킨 프로그램도 리눅스용 프로그램이다.

도커란 무엇인가?

도커는 '데이터 또는 프로그램을 격리시키는 기능'을 제공하는 소프트웨어다. 주로 서버에 사용되며, 다양한 프로그램과 데이터를 각각 독립된 환경에 격리하는 기능을 제공한다. 운영체제(비슷한 것) 통째로 격리하는 기능이다.

2024.11.06·5분·docker
SWEA2112모의역량
test_case = int(input())

def chk_test():
    chk_a_list = [0] * k
    chk_b_list = [1] * k

    for w_i in range(w):
        is_success = False

        for d_i in range(d - k + 1):
            cur_chk = [film[tmp_i][w_i] for tmp_i in range(d_i, d_i + k)]
            if cur_chk == chk_a_list or cur_chk == chk_b_list:
                is_success = True
                break
        if not is_success:
            return False
    return True


def test_film(film, depth=0, cnt_inject=0, chk_list=[]):
    global min_inject
    
    if cnt_inject >= min_inject:
        return

    if chk_test():
        min_inject = min(min_inject, cnt_inject)
        return

    if depth >= d:
        return
    
    origin_membrane = film[depth][:]

    # 현재 층을 그대로
    test_film(film, depth + 1, cnt_inject)

    # 현재 층을 a로
    film[depth] = inject_a
    test_film(film, depth + 1, cnt_inject + 1)
    film[depth] = origin_membrane

    # 현재 층을 b로
    film[depth] = inject_b
    test_film(film, depth + 1, cnt_inject + 1)
    film[depth] = origin_membrane

for t in range(test_case):
    d, w, k = map(int, input().split())

    film = [list(map(int, input().split())) for _ in range(d)]
    
    inject_a = [0] * w
    inject_b = [1] * w

    min_inject = float('inf')

    test_film(film)
    print(f"#{t + 1} {min_inject}")

보호 필름

SWEA 2112번 '보호 필름' (모의 역량 테스트) 문제 풀이. dfs, backtracking 로 접근했다.

2024.08.14·7분·dfs
SWEA2115모의역량
def max_subset_sum(arr):
    dp = [[0, 0] for _ in range(c + 1)]

    for num in arr:
        for j in range(c, num - 1, -1):
            if dp[j - num][0] + num > c:
                continue
            next_sq_value = dp[j - num][1] + num ** 2
            if next_sq_value > dp[j][1]:
                dp[j][0] = dp[j - num][0] + num
                dp[j][1] = next_sq_value
    _, max_sum = max(dp, key=lambda x: x[1])
    return max_sum

test_case = int(input())

for t in range(test_case):
    n, m, c = map(int, input().split())
    honey_map = [list(map(int, input().split())) for _ in range(n)]
    total_max = 0

    for fst_i in range(n):
        for fst_j in range(n - m + 1):

            fst_max = max_subset_sum(honey_map[fst_i][fst_j:fst_j + m])

            for snd_i in range(n):
                start = 0
                if snd_i == fst_i:
                    start = fst_j + m
                for snd_j in range(start, n - m + 1):
                    snd_max = max_subset_sum(honey_map[snd_i][snd_j:snd_j + m])

                    total_max = max(total_max, fst_max + snd_max)

    print(f"#{t + 1} {total_max}")

벌꿀 채취

SWEA 2115번 '벌꿀 채취' (모의 역량 테스트) 문제 풀이. dfs, subset, dynamic programming 로 접근했다.

2024.08.10·8분·dfs
SWEA4008모의역량
# 계산

def calculate(num1, num2, operator):

    if operator == '+':
        num1 += num2
    elif operator == '-':
        num1 -= num2
    elif operator == '*':
        num1 *= num2
    elif operator == '/':
        num1 = int(num1 / num2)
    return num1

# 수식 완성
def search_expression(i, result):
    if i == n:
        global max_num, min_num
        max_num = max(max_num, result)
        min_num = min(min_num, result)
        return

    for operator in operators:
        if operator_dict[operator] > 0:
            operator_dict[operator] -= 1
            search_expression(i + 1, calculate(result, nums[i+1], operator))
            operator_dict[operator] += 1



test_case = int(input())

for t in range(test_case):
    n = int(input()) - 1
    operators = ['+', '-', '*', '/']
    operator_dict = {operator: cnt for operator, cnt in zip(operators, map(int, input().split()))}

    nums = list(map(int, input().split()))

    max_num = float('-inf')
    min_num = float('inf')
    result_dict = {}
    visited = []

    search_expression(0, nums[0])

    print(f"#{t + 1} {max_num - min_num}")

숫자 만들기

SWEA 4008번 '숫자 만들기' (모의 역량 테스트) 문제 풀이. dfs 로 접근했다.

2024.08.09·6분·dfs
SWEA4012모의역량
test_case = int(input())

def search_recipe(index_list, n):
    if n == 1 :
        return [[i] for i in index_list]
    result = []
    for i in range(len(index_list) - 1):
        for j in search_recipe(index_list[i+1:], n - 1):
            result.append([index_list[i]] + j)
    
    return result


for t in range(test_case):
    n = int(input())
    min_diff = float('inf')

    recipe = [list(map(int, input().split())) for _ in range(n)]
    
    index_set = set(range(n))

    comb_list = [[0] + c for c in search_recipe(list(range(1, n)), n // 2 - 1)]

    for comb in comb_list:
        comb2 = list(index_set - set(comb))
        food1, food2 = 0, 0

        for i_idx, (i1, i2) in enumerate(zip(comb, comb2)):
            for j1, j2 in zip(comb[i_idx + 1:], comb2[i_idx + 1:]):
                food1 += recipe[i1][j1] + recipe[j1][i1]
                food2 += recipe[i2][j2] + recipe[j2][i2]
        min_diff = min(min_diff, abs(food1 - food2))

    print(f"#{t + 1} {min_diff}")

요리사

SWEA 4012번 '요리사' (모의 역량 테스트) 문제 풀이. combinatorics, backtracking 로 접근했다.

2024.08.06·6분·combinatorics
SWEA5215D3
test_case = int(input())


# 제한 칼로리 내에서 최대의 맛
def search_best(hamburgers, sum_cal=0, sum_score=0):
    global max_score
    max_score = max(max_score, sum_score)

    for i, (score, cal) in enumerate(hamburgers):
        if sum_cal + cal > l:
            continue
        search_best(hamburgers[i + 1:], sum_cal + cal, sum_score + score)


for t in range(test_case):
    n, l = map(int, input().split())

    hamburgers = [list(map(int, input().split())) for _ in range(n)]

    max_score = 0

    search_best(hamburgers)

    print(f"#{t + 1} {max_score}")

햄버거 다이어트

SWEA 5215번 '햄버거 다이어트' (D3) 문제 풀이. dfs, greedy algorithm 로 접근했다.

2024.07.31·9분·dfs
BACKGROUNDEuclidean

a,bZa, b \in \mathbb{Z}이고 aabb로 나눈 나머지를 rr이라 하자. (ba,0rbb \leq a, 0 \leq r \leq b)

a,ba, b의 최대 공약수를 (a,b)(a, b)라고 하면, 다음이 성립한다.

(a,b)=(b,r)(a, b)=(b, r)

출처: wikipidia

rr이 0이 될 때 알고리즘을 멈추며, 이 때의 bb가 최대공약수가 된다.

예를 들어 1460과 1037에 대해 알고리즘을 진행해보면 다음과 같다.

\begin{flalign*} (1460, 1037)\\=(1037, 323)\\=(323, 68)\\=(68, 52)\\=(52, 16)\\=(16, 4)\\=(4,0) \end{flalign*}

rr이 0일때 bb가 4이므로 1460과 1037의 최대공약수는 4이다.

유클리드 호제법

유클리드 호제법은 2개의 자연수에 대해 최대공약수를 구하는 알고리즘이며 다음과 같은 성질을 통해 알고리즘을 진행한다.

2022.11.22·1분·math
BACKGROUNDAttention
종류점수 함수Q의 출처K, V의 출처출처 논문
Bahdanau (additive)vatanh(Wast1+Uahj)v_a^\top \tanh(W_a s_{t-1} + U_a h_j)decoder 직전 상태encoder 전체Bahdanau et al. (2015)
Luong doththˉsh_t^\top \bar{h}_sdecoder 현재 상태encoder 전체Luong et al. (2015)
Luong generalhtWahˉsh_t^\top W_a \bar{h}_sdecoder 현재 상태encoder 전체Luong et al. (2015)
Luong concatvatanh(Wa[ht;hˉs])v_a^\top \tanh(W_a[h_t ; \bar{h}_s])decoder 현재 상태encoder 전체Luong et al. (2015)
Encoder self-attentionQK/dkQK^\top / \sqrt{d_k}encoder 이전 층encoder 이전 층Vaswani et al. (2017)
Masked self-attentionQK/dkQK^\top / \sqrt{d_k} (뒤쪽 -\infty)decoder 이전 층decoder 이전 층Vaswani et al. (2017)
Encoder-decoder attentionQK/dkQK^\top / \sqrt{d_k}decoder 이전 층encoder 출력Vaswani et al. (2017)

Attention 메커니즘 정리 - Seq2Seq에서 Transformer까지

Lab11-5에서 Seq2Seq model을 공부하면서 입력 문장 전체를 vector 하나로 압축한다는 점이 계속 걸렸다.

2022.06.10·19분·attention
PYTORCHLAB 11-5
def evaluate(pairs, source_vocab, target_vocab, encoder, decoder, target_max_length):
    for pair in pairs:
        print(">", pair[0])
        print("=", pair[1])
        source_tensor = tensorize(source_vocab, pair[0])
        source_length = source_tensor.size()[0]
        encoder_hidden = torch.zeros([1, 1, encoder.hidden_size]).to(device)

        for ei in range(source_length):
            _, encoder_hidden = encoder(source_tensor[ei], encoder_hidden)

        decoder_input = torch.Tensor([[SOS_token]]).long().to(device) # 수정해야 작동
        decoder_hidden = encoder_hidden
        decoded_words = []

        for di in range(target_max_length):
            decoder_output, decoder_hidden = decoder(decoder_input, decoder_hidden)
            _, top_index = decoder_output.data.topk(1) # 1개의 가장 큰 요소를 반환
            if top_index.item() == EOS_token:
                decoded_words.append("<EOS>")
                break
            else:
                decoded_words.append(target_vocab.index2vocab[top_index.item()])

            decoder_input = top_index.squeeze().detach()

        predict_words = decoded_words
        predict_sentence = " ".join(predict_words)
        print("<", predict_sentence)
        print("")

Seq2Seq

Seq2Seq model은 아래와 같은 구조를 가지고 있다. 일종의 Encoder-Decoder 구조라고도 할 수 있는데 모든 입력을 다 받은 후에 출력을 생성하는 구조이다.

2022.06.09·15분·rnn
PYTORCHLAB 11-4
# load data
xy = np.loadtxt("data-02-stock_daily.csv", delimiter=",")
xy = xy[::-1]  # reverse order

# split train-test set
train_size = int(len(xy) * 0.7)
train_set = xy[0:train_size]
test_set = xy[train_size - seq_length:]

앞서 언급한대로 scaling을 하고 학습하기 좋은 형태로 data를 가공해야 한다.

def minmax_scaler(data):
    numerator = data - np.min(data, 0)
    denominator = np.max(data, 0) - np.min(data, 0)
    return numerator / (denominator + 1e-7)

train_set = minmax_scaler(train_set)
test_set = minmax_scaler(test_set)

Timeseries

timeseries(시게열) data는 일정 시간 간격으로 배치된 data를 말한다. 매장의 시간별 매출, 요일별 주식 시가/종가 등이 여기에 속할 수 있다.

2022.06.09·8분·rnn
PYTORCHLAB 11-3
# data setting
x_data = []
y_data = []

# window를 오른쪽으로 움직이면서 자름
for i in range(0, len(sentence) - sequence_length):
    x_str = sentence[i:i + sequence_length]
    y_str = sentence[i + 1: i + sequence_length + 1]
    print(i, x_str, '->', y_str)

    x_data.append([char_dic[c] for c in x_str])  # x str to index (dict 사용)
    y_data.append([char_dic[c] for c in y_str])  # y str to index

x_one_hot = [np.eye(dic_size)[x] for x in x_data]

X = torch.FloatTensor(x_one_hot)
Y = torch.LongTensor(y_data)

'''output
0 if you wan -> f you want
1 f you want ->  you want 
2  you want  -> you want t
3 you want t -> ou want to
4 ou want to -> u want to 
...
166 ty of the  -> y of the s
167 y of the s ->  of the se
168  of the se -> of the sea
169 of the sea -> f the sea.
'''

RNN - longseq

앞서 살펴보았던 RNN 예제들은 모두 한 단어나 짧은 문장에 대해 RNN을 학습시키는 내용들이었다. 하지만 우리가 다루고 싶은 데이터는 더 긴 문장이거나 내용을 가질 가능성이 높다.

2022.06.06·8분·rnn
PYTORCHLAB 11-2
char_set = ['h', 'i', 'e', 'l', 'o']

# hyper parameters
input_size = len(char_set)
hidden_size = len(char_set)
learning_rate = 0.1

# data setting
x_data = [[0, 1, 0, 2, 3, 3]]
x_one_hot = [[[1, 0, 0, 0, 0],
              [0, 1, 0, 0, 0],
              [1, 0, 0, 0, 0],
              [0, 0, 1, 0, 0],
              [0, 0, 0, 1, 0],
              [0, 0, 0, 1, 0]]]
y_data = [[1, 0, 2, 3, 3, 4]]

X = torch.FloatTensor(x_one_hot)
Y = torch.LongTensor(y_data)

마찬가지로 one-hot encoding하여 Tensor로 바꾼다. 다만 각 알파벳 변수에 배열을 저장하는 방식이 아니라 char_set에 저장된 알파벳을 x_data의 값을 인덱스로 불러오는 방식이다. one-hot encoding은 x_data에 적용하여 학습한다.

RNN - hihello / charseq

hihello 문제는 같은 문자들이 다음 문자가 다른 경우 이를 예측하는 문제를 말한다. hihello에서 'h'와 'l'은 2번씩 등장하지만 어디에 문자가 위치하느냐에 따라 다음에 올 문자가 달라진다.

2022.06.05·8분·rnn
PYTORCHLAB 11-1
rnn = torch.nn.RNN(input_size, hidden_size)

outputs, _status = rnn(input_data)
print(outputs)
print(outputs.size())

'''output
tensor([[[-0.7497, -0.6135],
         [-0.5282, -0.2473],
         [-0.9136, -0.4269],
         [-0.9136, -0.4269],
         [-0.9028,  0.1180]],

        [[-0.5753, -0.0070],
         [-0.9052,  0.2597],
         [-0.9173, -0.1989],
         [-0.9173, -0.1989],
         [-0.8996, -0.2725]],

        [[-0.9077, -0.3205],
         [-0.8944, -0.2902],
         [-0.5134, -0.0288],
         [-0.5134, -0.0288],
         [-0.9127, -0.2222]]], grad_fn=<StackBackward>)
torch.Size([3, 5, 2])
'''

RNN Basics

PyTorch에서 RNN은 in/output size만 잘 맞춰주면 바로 사용이 가능하다. "h, e, l, o" 4개의 알파벳으로 이루어진 데이터셋을 통해 2차원의 output(class가 2개)을 내는 RNN을 만들어볼 것이다.

2022.06.05·5분·rnn
PYTORCHLAB 11-0
ht=f(ht1,xt)h_t=f(h_{t-1}, x_t)

activation과 weight를 명시하여 표현하면 다음과 같다.

ht=tanh(Whht1,Wxxt)h_t=tanh(W_h h_{t-1}, W_x x_t)

Usages of RNN

이런 RNN의 구조를 응용하여 다음과 같은 구조들로 사용할 수 있다.

Usages of RNN

  • one to many : 하나의 입력을 받아 여러 출력을 내는 구조이다. 하나의 이미지를 받아 그에 대한 설명을 문장(여러개의 단어)으로 출력하는 것을 예로 들 수 있다.

  • many to one : 여러 입력을 받아 하나의 출럭을 내는 구조이다. 문장을 입력받아 그 문장이 나타내는 감정의 label을 출력하는 것을 예로 들 수 있다.

  • many to many : 2가지의 구조가 있는 것을 볼 수 있다.

    • 입력이 다 끝나는 지점부터 여러 출력을 내는 구조로, 문장을 입력받아 번역하는 모델을 예로 들 수 있다. 이 경우 문장의 중간에 번역을 진행하면 다 끝나고 나서 문장의 의미가 달라질 수 있기 때문에 먼저 입력 문장을 다 듣고 번역을 진행하게 된다.
    • 입력 하나하나를 받으면서 그때마다 모델의 출력을 내는 구조이다. 영상을 처리할 때 frame 단위의 이미지로 나눠 입력을 받은 후 각 frame을 입력 받을 때마다 처리하는 것을 예로 들 수 있다.

RNN intro

RNN은 sequential data를 잘 학습하기 위해 고안된 모델이다. Sequential data란 단어, 문장이나 시게열 데이터와 같이 데이터의 순서도 데이터의 일부인 데이터들을 말한다.

2022.06.03·3분·rnn
PYTORCHLAB 10-6
    # self.inplanes = 64
    # self.layer1 = self._make_layer(block=Bottleneck, 64, layers[0]=3)
    def _make_layer(self, block, planes, blocks, stride=1):
        
        downsample = None
        
        # identity 값을 낮춰서 shape을 맞춰주기 위함. channel도 맞춰주기.
        if stride != 1 or self.inplanes != planes * block.expansion: # 64 != 64 * 4
            
            downsample = nn.Sequential(
                conv1x1(self.inplanes, planes * block.expansion, stride), #conv1x1(256, 512, 2) #conv1x1(64, 256, 2)
                nn.BatchNorm2d(planes * block.expansion), #batchnrom2d(512) #batchnrom2d(256)
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample))
        # layers.append(Bottleneck(64, 64, 1, downsample))
        
        self.inplanes = planes * block.expansion #self.inplanes = 128 * 4
        
        for _ in range(1, blocks): 
            layers.append(block(self.inplanes, planes)) # * 3

        return nn.Sequential(*layers)

ResNet

Plain network는 skip connection을 사용하지 않은 일반적인 CNN 신경망을 의미한다. 이러한 plain net이 깊어지면 깊어질수록 backpropagation을 할 때 기울기 소실이나 폭발이 발생할 확률이 높아진다.

2022.05.26·25분·cnn
PYTORCHLAB 10-5
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(vgg16.parameters(), lr = 0.005,momentum=0.9)

# 학습이 진행됨에 따라 lr 조절
lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.9) # optimizer의 step이 5번 진행될 때마다 gamma만큼 곱함

기존과 다른 점은 학습된 정도에 따라 learning rate를 줄이는 코드가 추가되었다는 것이다.

Train 설명 그림

lr_sche.step()이 추가된 것 말고 크게 다른 점은 없다.

correct = 0
total = 0

with torch.no_grad():
    for data in testloader:
        images, labels = data
        images = images.to(device)
        labels = labels.to(device)
        outputs = vgg16(images)
        
        _, predicted = torch.max(outputs.data, 1)
        
        total += labels.size(0)
        
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

Accuracy of the network on the 10000 test images: 75 %

VGG

VGG-net(이하 VGG)은 14년도 ILSVRC(Imagenet 이미지 인식 대회)에 나온 네트워크로 옥스포드의 Visual Geometry Group에서 만든 모델이다.

2022.05.25·20분·cnn
PYTORCHLAB 10-4
import torchvision
from torchvision import transforms

from torch.utils.data import DataLoader

from matplotlib.pyplot import imshow
%matplotlib inline

trans = transforms.Compose([
    transforms.Resize((64,128))
])

train_data = torchvision.datasets.ImageFolder(root='custom_data/origin_data', transform=trans)

원본 데이터가 있는 곳을 root로 잡고 Compose를 통해 적용할 transforms들을 묶어 넣어준다. 원본 데이터가 265x512로 너무 커서 64x128로 바꾸어주는 과정을 거친다. 여기서는 하나의 transforms을 사용하지만 어러개를 사용해야할 때 Compose로 묶어 사용할 수 있다.

ImageFolder

torchvision.datasets에 있는 ImageFolder는 directory에 따라 category를 자동으로 labeling 하여 데이터로 만들어 준다. 우리가 찍은 사진을 학습하는데 사용할 때 아주 좋은 기능이다.

2022.05.22·10분·imagefolder
PYTORCHLAB 10-3
MNIST = dsets.MNIST(root="./MNIST_data",train = True,transform=torchvision.transforms.ToTensor(), download=True)
cifar10 = dsets.CIFAR10(root="./cifar10",train = True, transform=torchvision.transforms.ToTensor(),download=True)

#CIFAR10
data = cifar10.__getitem__(0)
print(data[0].shape)
vis.images(data[0],env="main")

# MNIST
data = MNIST.__getitem__(0)
print(data[0].shape)
vis.images(data[0],env="main")

MNIST 설명 그림

두꺼비(?)와 숫자 5가 잘 나온다. 또한 이런 이미지들도 당연히 vis.images()를 통해 한번에 많은 이미지도 출력할 수 있다.

Visdom

Visdom은 Meta 사(facebook)에서 제공하는 PyTorch에서 사용할 수 있는 시각화 도구이다. 실시간으로 데이터를 시각화하면서 바뀌는 점을 확인할 수 있다는 장점이 있다.

2022.05.22·8분·visdom
PYTORCHLAB 10-1
class CNN(torch.nn.Module):

    def __init__(self):
        super(CNN, self).__init__()
        # L1 ImgIn shape=(?, 1, 28, 28)
        #    Conv     -> (?, 32, 28, 28)
        #    Pool     -> (?, 32, 14, 14)
        self.layer1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))
        # L2 ImgIn shape=(?, 32, 14, 14)
        #    Conv      ->(?, 64, 14, 14)
        #    Pool      ->(?, 64, 7, 7)
        self.layer2 = torch.nn.Sequential(
            torch.nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2))
        # Final FC 7x7x64 inputs -> 10 outputs
        self.fc = torch.nn.Linear(7 * 7 * 64, 10, bias=True)
        torch.nn.init.xavier_uniform_(self.fc.weight)

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.view(out.size(0), -1)   # Flatten them for FC
        out = self.fc(out)
        return out

model = CNN().to(device)

Convolution

강의 자료에서는 '이미지(2차원 매트릭스) 위에서 stride 만큼 filter(kernel)을 이동시키면서 겹쳐지는 부분의 각 원소의 값을 곱해서 더한 값을 출력으로 하는 연산'이라고 나와있다. 자세히 어떤 과정의 연산인지 확인해 보자.

2022.05.21·10분·convolution
PYTORCHLAB 9-4
# nn layers
linear1 = torch.nn.Linear(784, 32, bias=True)
linear2 = torch.nn.Linear(32, 32, bias=True)
linear3 = torch.nn.Linear(32, 10, bias=True)
relu = torch.nn.ReLU()
bn1 = torch.nn.BatchNorm1d(32)
bn2 = torch.nn.BatchNorm1d(32)

nn_linear1 = torch.nn.Linear(784, 32, bias=True)
nn_linear2 = torch.nn.Linear(32, 32, bias=True)
nn_linear3 = torch.nn.Linear(32, 10, bias=True)

# model with Batch normalization
bn_model = torch.nn.Sequential(linear1, bn1, relu,
                            linear2, bn2, relu,
                            linear3).to(device)

# model without Batch normalization
nn_model = torch.nn.Sequential(nn_linear1, relu,
                               nn_linear2, relu,
                               nn_linear3).to(device)

# define cost/loss & optimizer
criterion = torch.nn.CrossEntropyLoss().to(device)    # Softmax is internally computed.
bn_optimizer = torch.optim.Adam(bn_model.parameters(), lr=learning_rate)
nn_optimizer = torch.optim.Adam(nn_model.parameters(), lr=learning_rate)

Batch Normalization

Gradient Vanishing(기울기 소실)과 Gradient Exploding(기울기 폭주)는 정상적인 학습을 할 수 없게 만드는 요인들이다.

2022.05.14·6분·batch-normalization
BACKGROUNDBackpropagation
w3=costW3=costo1o1y1y1W3\nabla w_3=\frac{\partial cost}{\partial W_3}=\frac{\partial cost}{\partial o_1}\frac{\partial o_1}{\partial y_1}\frac{\partial y_1}{\partial W_3}

W3\nabla W_3는 chain rule을 통해 위와 같이 미분이 바로 되는 형식으로 표현할 수 있다. 한 번 더 거슬러 올라가 보자.

이번에는 W1\nabla W_1W2\nabla W_2를 구할 차례이다. 먼저 X1\nabla X_1을 구해보자.

costW1=costy1y1hz2hz2z2z2W1\frac{\partial cost}{\partial W_1}=\frac{\partial cost}{\partial y_1}\frac{\partial y_1}{\partial h_{z_2}}\frac{\partial h_{z_2}}{\partial z_2}\frac{\partial z_2}{\partial W_1}

Backpropagation

데이터를 레이어의 노드들을 통과시키면서 설정된 weight에 따라 예측 결과값을 계산하는 것을 forward pass라고 한다.

2022.05.12·3분·backpropagation
GENERATIVE-MODEL오토인코더의 모든 것

Introduction

Autoencoder

오토인코더는 인풋과 아웃풋이 같은 네트워크를 의미한다. Auto-associators, Diabolo networks, Sandglass-shaped net 등의 이명으로 불리기도 하며 가장 많이 불리는 이름은 역시 Autoencoder이다.

Autoencoder의 모습과 비슷한 Diabolo의 모습

오토인코더는 다음과 같이 중간의 은닉층이 잘록한 모습의 네트워크인데, 이때 중간 은닉층을 ZZ라고 부르며 Code, Latent Variable, Feature, Hidden representation 등으로 불린다. 그래서 ZZ를 어떻게 생각하냐에 따라 오토인코더에서 학습하는 과정을 Representation Learning, Efficient Code Learning 등으로 부르기도 하지만, 결국 이들은 모두 ZZ 노드를 배우는 학습을 이르는 말들이다.

오토인코더의 모든 것 - 3. Autoencoders

오토인코더는 인풋과 아웃풋이 같은 네트워크를 의미한다. Auto-associators, Diabolo networks, Sandglass-shaped net 등의 이명으로 불리기도 하며 가장 많이 불리는 이름은 역시 Autoencoder이다.

2022.05.03·7분·autoencoder
BACKGROUNDCross
KL(PQ)=H(P,Q)=i=1np(xi)logq(xi)KL\left( P\|\|Q \right) = H\left( P, Q \right) = -\sum^n_{i=1} p\left( x_i \right) \log{ q\left( x_i\right ) }

이런 이유 때문에 교차 엔트로피 자체가 loss로 기능할 수 있는 것이다.

Cross Entropy(교차 엔트로피)

교차 엔트로피의 의미는 이름에서 찾아볼 수 있다. 먼저 교차 엔트로피의 식을 한번 보자. 엔트로피 식에 와 의 밀도함수들이 교차해서 들어가 있다. 그런 의미에서 교차 엔트로피라는 이름이 붙은 것이다.

2022.04.27·2분·entropy
BACKGROUNDKL

KL(PQ)=H(P,Q)H(P)KL\left( P\|\|Q \right) = H\left( P, Q \right) - H\left( P \right) =(i=1np(xi)logq(xi))(i=1np(xi)logp(xi))= \left( -\sum^n_{i=1} p\left( x_i \right) \log{ q\left( x_i\right ) } \right) - \left( -\sum^n_{i=1} p\left( x_i \right) \log{ p\left( x_i\right ) } \right)

=i=1np(xi)logq(xi)p(xi)= -\sum^n_{i=1} p\left( x_i \right) \log{ \frac{ q\left( x_i\right )}{ p\left( x_i\right ) } }

이때 이산확률분포의 쿨백-라이블러 발산은 위에서 표현한 것과 같이 총 합으로 나타낼 수 있으며 다음과 같고

KL(PQ)=iP(i)logP(i)Q(i)KL\left( P\|\|Q \right) = \sum_i P\left( i \right) \log{ \frac{ P\left( i \right )}{ Q\left( i \right ) } }

KL divergence(쿨백-라이블러 발산)

이번에는 엔트로피에 이어 쿨백-라이블러 발산(Kullback-Leibler divergence)에 대해 알아보려 한다.

2022.04.26·3분·entropy
BACKGROUNDEntropy
H(x)=E(I(x))=i=1np(xi)logbp(xi)H\left(x\right) = E\left( I\left ( x\right ) \right) = -\sum^n_{i=1} p\left( x_i \right) \log_b{ p\left( x_i\right ) }

엔트로피는 정보량이 클수록 커지는 성질을 가진다. 다른 정보이론에서는 모르겠지만 머신러닝 분야에서는 예측을 하는 것이 목적이므로 예측에 맞춰 다시 말을 정리하면, 예측이 힘들수록 더 크다는 것이 된다.

이것이 앞서 예측하기 어려운 정도가 가장 와닿았다고 하는 이유였다.

다음 포스팅에서는 이 엔트로피를 이용한 대표적인 loss인 cross entropy를 알아보기 위해 KL divergence에 대해 먼저 알아보려 한다.

Entropy(엔트로피)란?

오토인코더에 대한 공부를 하다가 entropy, cross entropy, KL divergence 등 모르는 용어들이 있어서 더 이상 미루지 않고 공부를 해 보기로 했다.

2022.04.26·3분·entropy
전체 165개 보기 →