Commit 0620f2cb authored by insun park's avatar insun park
Browse files

docs(courses): 강의 문서 내용 보강

parent ff4183e6
......@@ -178,7 +178,69 @@ print(f"5 + 3 = {sum_result}")
---
## 9. 직접 해보기 (Hands-on Lab)
## 9. 심화 학습: Pythonic 코드 탐구 (Decorators & Generators)
숙련된 개발자를 위해, 코드의 효율성과 표현력을 극대화하는 파이썬의 강력한 기능인 데코레이터와 제너레이터를 소개합니다.
### 9.1. 데코레이터(Decorators): 함수를 포장하는 기술
**데코레이터**는 기존 함수의 코드를 수정하지 않고도, 그 함수에 새로운 기능을 덧붙이거나 감싸는(wrapping) '포장지' 같은 역할을 합니다. 로깅, 실행 시간 측정, 접근 제어 등 다양한 [횡단 관심사(Cross-cutting concerns)](./glossary.md#횡단-관심사-cross-cutting-concern)를 우아하게 처리할 수 있습니다.
```python
import time
def timing_decorator(func):
"""함수의 실행 시간을 측정하는 데코레이터"""
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs) # 원래 함수 실행
end_time = time.time()
print(f"'{func.__name__}' 함수 실행 시간: {end_time - start_time:.4f}초")
return result
return wrapper
@timing_decorator
def slow_function(delay_time):
"""시간이 오래 걸리는 가상 함수"""
print("느린 함수 시작...")
time.sleep(delay_time)
print("느린 함수 종료.")
# 데코레이터가 적용된 함수 호출
slow_function(2)
```
위 예제에서 `@timing_decorator``slow_function`에 실행 시간 측정 기능을 '장식'합니다.
### 9.2. 제너레이터(Generators): 메모리 효율적인 이터레이터
**제너레이터**는 모든 값을 메모리에 올려두고 시작하는 리스트와 달리, 필요할 때마다 값을 '생성(yield)'하여 전달하는 특별한 종류의 [이터레이터](./glossary.md#이터레이터-iterator)입니다. 이 방식은 대용량 데이터 스트림을 처리할 때 메모리 사용량을 획기적으로 줄여줍니다.
- **`yield` 키워드**: 함수가 제너레이터임을 나타냅니다. 값을 반환하고 함수의 상태를 일시 정지시킵니다. 다음에 호출될 때 정지된 지점부터 다시 시작합니다.
- **제너레이터 표현식(Generator Expression)**: 리스트 컴프리헨션과 유사하지만, `[]` 대신 `()`를 사용합니다. 즉시 리스트를 생성하는 대신 제너레이터 객체를 반환합니다.
```python
# 제너레이터 함수
def count_up_to(max_num):
count = 1
while count <= max_num:
yield count # 값을 생성하고 멈춤
count += 1
# 제너레이터 사용
counter = count_up_to(5)
for num in counter:
print(num) # 1, 2, 3, 4, 5 출력
# 제너레이터 표현식
large_squares = (x**2 for x in range(1_000_000))
# sum(large_squares) # 메모리 문제 없이 거대한 시퀀스의 합계를 계산 가능
```
이러한 기능들은 AI/ML 파이프라인에서 대규모 데이터셋을 효율적으로 다루거나, 모델의 행동을 로깅하는 등 실제 개발 현장에서 매우 유용하게 사용됩니다.
---
## 10. 직접 해보기 (Hands-on Lab)
이번 주에 배운 내용을 바탕으로 다음 함수들을 직접 완성해보세요.
......@@ -192,19 +254,143 @@ print(f"5 + 3 = {sum_result}")
**문제 3: 구구단 출력 함수**
- 숫자를 하나 매개변수로 받아 해당 숫자의 구구단을 `while`문을 사용하여 출력하는 `multiplication_table` 함수를 만들어보세요. (이 함수는 값을 반환할 필요가 없습니다.)
## 10. 되짚어보기 (Summary)
모범 답안은 [`part_2_python_core_syntax.py`](../../source_code/02_python_core_syntax/part_2_python_core_syntax.py) 파일에서 확인할 수 있습니다.
---
## 11. 트러블슈팅 (Troubleshooting)
코드를 작성하다 보면 예상치 못한 문제들을 마주하게 됩니다. 괜찮습니다! 모든 개발자가 겪는 과정이며, 문제 해결 능력은 뛰어난 개발자의 핵심 역량입니다.
이번 주차에 우리는 파이썬의 가장 기본적인 구성 요소들을 배웠습니다.
- **`IndentationError`가 발생했나요?**
- 파이썬은 들여쓰기로 코드 블록을 구분합니다. `if`, `for`, `def` 아래의 코드가 정확히 스페이스 4칸으로 들여쓰기 되었는지 확인하세요.
- **`SyntaxError`는 무엇인가요?**
- 문법이 틀렸다는 의미입니다. 괄호 `()`, 따옴표 `""`, 콜론 `:` 등을 빠뜨리지 않았는지 확인해보세요.
- **코드가 예상대로 동작하지 않나요?**
- `print()` 함수를 사용해 중간중간 변수의 값이 어떻게 변하는지 출력해보면 문제의 원인을 찾는 데 큰 도움이 됩니다.
- **변수와 자료형**: 데이터를 저장하고(변수), 데이터의 종류(숫자, 문자열, 불리언)를 구분했습니다.
- **자료구조**: 여러 데이터를 담는 그릇인 리스트, 튜플, 딕셔너리, 셋을 익혔습니다.
- **제어문**: `if`, `for`, `while`을 통해 코드의 흐름을 제어하는 방법을 배웠습니다.
- **함수**: `def`를 사용해 반복되는 코드를 재사용 가능한 블록으로 만들었습니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
이 핵심 문법들은 앞으로 우리가 만들 모든 AI 서비스의 뼈대가 될 것입니다.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
## 11. 더 깊이 알아보기 (Further Reading)
---
## 되짚어보기 (Recap)
- **변수와 자료형**: 모든 데이터는 변수에 담기고, 각 데이터는 고유한 자료형(정수, 문자열 등)을 가집니다.
- **자료구조**: 리스트(변경 가능, 순서 O), 튜플(변경 불가, 순서 O), 딕셔너리(Key-Value, 순서 X), 셋(중복 불가, 순서 X)의 특징을 이해하고 상황에 맞게 사용하는 것이 중요합니다.
- **제어문**: `if`문으로 조건을, `for`/`while`문으로 반복을 제어하며 코드의 흐름을 만듭니다.
- **함수**: `def`를 사용하여 반복되는 코드를 재사용 가능한 '레시피'로 만들어 코드의 구조를 개선합니다.
## 더 깊이 알아보기 (Further Reading)
- [Official Python 3.10.12 documentation](https://docs.python.org/3/)
- [A Byte of Python (한국어)](https://python.bytebank.org/)
- [점프 투 파이썬](https://wikidocs.net/book/1)
---
## 🚀 캡스톤 미니 프로젝트: 간단한 로그 분석기 만들기
지금까지 배운 Python 핵심 문법을 모두 활용하여, 앞으로 진행할 최종 캡스톤 프로젝트의 기반을 다지는 작은 실습을 진행해봅시다. 이번 미니 프로젝트의 목표는 여러 줄의 로그(log) 텍스트 파일을 읽고, 분석하여, 의미 있는 요약 정보를 출력하는 간단한 프로그램을 만드는 것입니다.
이 과정을 통해 여러분은 파일을 읽고, 문자열을 다루고, 배운 자료구조(리스트, 딕셔너리)와 제어문, 함수를 실제 문제에 적용하는 경험을 하게 됩니다.
### 프로젝트 목표
주어진 형식의 로그 파일을 분석하여 다음과 같은 요약 리포트를 출력하는 Python 스크립트(`log_analyzer.py`)를 완성합니다.
**샘플 로그 파일 (`sample.log`):**
```
2023-10-27 10:00:00 INFO: User 'alice' logged in.
2023-10-27 10:01:30 WARNING: Disk space is running low.
2023-10-27 10:02:15 INFO: Data processing started.
2023-10-27 10:05:00 ERROR: Failed to connect to database.
2023-10-27 10:05:05 INFO: User 'bob' logged in.
2023-10-27 10:06:00 ERROR: File not found: 'data.csv'.
```
**예상 출력 결과:**
```
Log Analysis Report
--------------------
Total Logs: 6
Log Levels Count:
- INFO: 3
- WARNING: 1
- ERROR: 2
```
### 단계별 구현 가이드
아래 단계에 따라 함수를 하나씩 완성해보세요.
**1. 로그 파일 읽기 함수**
- `read_logs(file_path)` 라는 이름의 함수를 만드세요.
- 이 함수는 파일 경로를 입력받아, 파일의 각 줄을 리스트의 요소로 담아 반환해야 합니다.
- (힌트) 파일 처리는 다음과 같은 `with open(...)` 구문을 사용하면 편리합니다.
```python
def read_logs(file_path):
"""파일을 읽어 모든 줄을 리스트로 반환합니다."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.readlines()
except FileNotFoundError:
print(f"Error: 파일 '{file_path}'를 찾을 수 없습니다.")
return []
```
**2. 로그 파싱(Parsing) 함수**
- `parse_log(log_line)` 라는 이름의 함수를 만드세요.
- 이 함수는 로그 한 줄(문자열)을 입력받아, 로그 레벨(e.g., 'INFO', 'ERROR')을 추출하여 반환해야 합니다.
- (힌트) 문자열의 `split()` 메소드를 사용하면 특정 문자를 기준으로 문자열을 나눌 수 있습니다. 로그 레벨은 보통 두 번째 단어입니다.
```python
def parse_log(log_line):
"""로그 한 줄에서 로그 레벨을 파싱하여 반환합니다."""
parts = log_line.split()
if len(parts) > 2:
# 로그 레벨은 보통 'INFO:', 'WARNING:' 처럼 ':'가 붙어있으므로 제거합니다.
return parts[2].replace(':', '')
return None
```
**3. 로그 분석 및 리포트 출력 함수**
- `analyze_logs(logs)` 라는 이름의 함수를 만드세요.
- 이 함수는 로그 줄들의 리스트를 입력받아, 전체 로그 수와 각 로그 레벨(INFO, WARNING, ERROR)의 개수를 계산하고, 위 예시와 같은 형식으로 리포트를 출력해야 합니다.
- (힌트) 로그 레벨의 개수를 세기 위해 딕셔너리를 사용하면 매우 효율적입니다.
```python
def analyze_logs(logs):
"""로그 리스트를 분석하여 요약 리포트를 출력합니다."""
level_counts = {}
for log_line in logs:
level = parse_log(log_line)
if level:
level_counts[level] = level_counts.get(level, 0) + 1
print("Log Analysis Report")
print("--------------------")
print(f"Total Logs: {len(logs)}")
print("Log Levels Count:")
for level, count in level_counts.items():
print(f" - {level}: {count}")
```
**4. 메인 로직 구현**
- 위에서 만든 함수들을 조합하여 스크립트가 실행될 때 전체 로직이 동작하도록 만드세요.
- `sample.log` 파일을 생성하고 샘플 내용을 채운 뒤, `log_analyzer.py`가 이 파일을 읽도록 경로를 지정해주세요.
```python
# log_analyzer.py 의 메인 부분
if __name__ == "__main__":
log_file = "sample.log"
# 1. 로그 파일 읽기
logs = read_logs(log_file)
# 2. 분석 및 리포트 출력
if logs:
analyze_logs(logs)
```
- [파이썬 공식 튜토리얼: 자료구조 (리스트, 튜플, 셋, 딕셔너리)](https://docs.python.org/ko/3/tutorial/datastructures.html)
- [파이썬 공식 튜토리얼: 제어 흐름 도구 (if, for, while)](https://docs.python.org/ko/3/tutorial/controlflow.html)
- [파이썬 공식 튜토리얼: 함수 정의하기](https://docs.python.org/ko/3/tutorial/controlflow.html#defining-functions)
\ No newline at end of file
이 미니 프로젝트는 앞으로 여러분이 만들 복잡한 AI 애플리케이션의 기본이 되는 데이터 처리 능력을 기르는 좋은 출발점이 될 것입니다.
\ No newline at end of file
......@@ -186,7 +186,22 @@ playlist.show_recent()
---
## 8. 되짚어보기 (Summary)
## 8. 트러블슈팅 (Troubleshooting)
- **`AttributeError: 'list' object has no attribute 'appendleft'`가 발생했나요?**
- `appendleft``deque` 객체의 메서드입니다. 일반 리스트에서는 사용할 수 없습니다. `from collections import deque`를 통해 `deque`를 생성했는지 확인하세요.
- **`KeyError`가 자꾸 발생하나요?**
- `dict`에서 존재하지 않는 키를 조회할 때 발생합니다. 키가 항상 존재한다고 보장할 수 없다면 `my_dict.get('key', 'default_value')`를 사용하거나, 데이터를 그룹핑하는 경우라면 `defaultdict`를 사용하는 것이 좋습니다.
- **어떤 자료구조를 써야 할지 모르겠나요?**
- 아래 '컬렉션 선택 가이드' 표를 다시 한번 살펴보세요. 데이터의 특징(순서, 중복, 변경 가능성)과 내가 하려는 작업(검색, 추가/삭제)을 기준으로 가장 적합한 것을 선택하는 연습이 중요합니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 되짚어보기 (Summary)
### 컬렉션 선택 가이드: 어떤 공구를 언제 쓸까?
어떤 상황에 어떤 [자료구조](./glossary.md#자료구조-data-structure)를 선택하는 것이 가장 효율적인지 한눈에 정리해봅시다.
......@@ -204,7 +219,132 @@ playlist.show_recent()
이러한 자료구조의 특징을 잘 이해하고 적재적소에 사용하면, 여러분의 파이썬 코드는 훨씬 더 효율적이고 읽기 좋아질 것입니다.
## 9. 더 깊이 알아보기 (Further Reading)
---
## 10. 더 깊이 알아보기 (Further Reading)
- [파이썬 공식 문서: `collections` — 컨테이너 데이터형](https://docs.python.org/ko/3/library/collections.html)
- [파이썬 공식 튜토리얼: 자료구조 심화](https://docs.python.org/ko/3/tutorial/datastructures.html)
---
## 🚀 캡스톤 미니 프로젝트: 전자상거래 주문 데이터 분석
이번 파트에서 배운 전문가용 컬렉션들을 활용하여, 좀 더 현실적인 데이터 분석 문제를 해결해봅시다. 여러 건의 전자상거래 주문 데이터를 분석하여, 제품별 매출 현황과 인기 상품을 파악하는 스크립트를 작성하는 것이 목표입니다.
이 프로젝트를 통해 `namedtuple`로 가독성 높은 데이터 구조를 만들고, `defaultdict``Counter`로 복잡한 데이터를 효율적으로 집계하는 실전 감각을 익힐 수 있습니다.
### 프로젝트 목표
주어진 주문 데이터(튜플의 리스트)를 처리하여 다음과 같은 분석 리포트를 출력하는 Python 스크립트(`order_analyzer.py`)를 완성합니다.
**샘플 주문 데이터:**
```python
# (주문 ID, 제품명, 수량, 개당 가격)
orders_data = [
(1, "Laptop", 1, 1500000),
(2, "Mouse", 2, 25000),
(1, "Keyboard", 1, 45000),
(3, "Monitor", 1, 300000),
(2, "Keyboard", 1, 45000),
(3, "Mouse", 1, 25000),
(4, "Laptop", 1, 1500000),
]
```
**예상 출력 결과:**
```
E-commerce Order Analysis Report
---------------------------------
Total Revenue: 3,440,000 KRW
Product Sales Summary:
- Laptop: Total Items: 2, Total Revenue: 3,000,000 KRW
- Mouse: Total Items: 3, Total Revenue: 75,000 KRW
- Keyboard: Total Items: 2, Total Revenue: 90,000 KRW
- Monitor: Total Items: 1, Total Revenue: 300,000 KRW
Top 3 Most Sold Products:
1. Mouse (3 items)
2. Laptop (2 items)
3. Keyboard (2 items)
```
### 단계별 구현 가이드
**1. `namedtuple`로 주문 구조 정의하기**
- `collections.namedtuple`을 사용하여 'Order'라는 이름의 튜플을 만드세요.
- 필드는 `order_id`, `product_name`, `quantity`, `price_per_item` 으로 구성합니다.
**2. 데이터 분석 함수 구현하기**
- `analyze_orders(orders)` 라는 함수를 정의하고, 그 안에 다음 분석 로직을 구현하세요.
**2-1. 제품별 매출 및 판매량 집계**
- `collections.defaultdict`를 사용하여 제품별 총 매출액(`total_revenue_per_product`)을 계산하세요. 키는 제품명, 값은 총 매출액입니다.
- `collections.Counter`를 사용하여 제품별 총 판매 수량(`items_sold_per_product`)을 집계하세요.
- (힌트) `Counter``counter[key] += value` 형식으로 값을 누적할 수 있습니다.
**2-2. 전체 총 매출 계산**
- `defaultdict`에 저장된 제품별 매출액을 모두 더하여 전체 총 매출(`total_revenue`)을 계산합니다.
**2-3. 가장 많이 팔린 상품 Top 3 찾기**
- `Counter``most_common(3)` 메서드를 사용하여 가장 많이 팔린 상품 3개를 찾습니다.
**3. 리포트 출력 함수 구현하기**
- `print_report(total_revenue, revenue_per_product, items_per_product)` 함수를 만들어, 위 '예상 출력 결과'와 같은 형식으로 분석 내용을 깔끔하게 출력하세요.
- 숫자는 세 자리마다 콤마(,)를 넣어주면 가독성이 더 좋습니다. (`f"{value:,}"` 포맷팅 사용)
**4. 전체 로직 통합**
아래는 위 기능들을 통합한 전체 코드의 뼈대입니다. `...` 부분을 채워 완성해보세요.
```python
from collections import namedtuple, defaultdict, Counter
# 1. namedtuple 정의
Order = namedtuple('Order', ['order_id', 'product_name', 'quantity', 'price_per_item'])
# 샘플 데이터
orders_data = [
(1, "Laptop", 1, 1500000), (2, "Mouse", 2, 25000), (1, "Keyboard", 1, 45000),
(3, "Monitor", 1, 300000), (2, "Keyboard", 1, 45000), (3, "Mouse", 1, 25000),
(4, "Laptop", 1, 1500000),
]
# Order namedtuple 리스트로 변환
orders = [Order(*o) for o in orders_data]
def analyze_orders(order_list):
# 2-1. 제품별 매출 및 판매량 집계
revenue_per_product = defaultdict(float)
items_per_product = Counter()
for order in order_list:
revenue_per_product[order.product_name] += order.quantity * order.price_per_item
items_per_product[order.product_name] += order.quantity
# 2-2. 전체 총 매출 계산
total_revenue = sum(revenue_per_product.values())
# 2-3. 가장 많이 팔린 상품 Top 3 찾기
top_3_products = items_per_product.most_common(3)
return total_revenue, revenue_per_product, items_per_product, top_3_products
def print_report(total_revenue, revenue_per_product, items_per_product, top_3_products):
print("E-commerce Order Analysis Report")
print("---------------------------------")
print(f"Total Revenue: {total_revenue:,.0f} KRW\n")
print("Product Sales Summary:")
for product, total_rev in revenue_per_product.items():
total_items = items_per_product[product]
print(f"- {product}: Total Items: {total_items}, Total Revenue: {total_rev:,.0f} KRW")
print("\nTop 3 Most Sold Products:")
for i, (product, count) in enumerate(top_3_products, 1):
print(f"{i}. {product} ({count} items)")
# 4. 분석 및 리포트 출력
if __name__ == "__main__":
total_revenue, revenue_per_product, items_per_product, top_3_products = analyze_orders(orders)
print_report(total_revenue, revenue_per_product, items_per_product, top_3_products)
```
\ No newline at end of file
......@@ -174,9 +174,75 @@ print(bpp1 == bpp2) # __eq__ 호출 -> True
- **`Warrior`**: `attack` 메서드를 오버라이딩하여, 20% 확률로 2배의 데미지를 입히도록 수정
- **`Wizard`**: `attack` 메서드를 오버라이딩하고, `super()`를 사용하여 부모의 공격을 먼저 실행한 뒤, 자신의 `hp`를 회복하는 기능 추가
### 문제 3: AI 라이브러리 스타일의 클래스 설계 (Custom Dataset)
PyTorch, TensorFlow와 같은 딥러닝 라이브러리들은 상속과 다형성을 적극적으로 활용합니다. PyTorch의 `Dataset` 클래스를 모방하여, 우리만의 커스텀 데이터셋 클래스를 만들어보는 실습을 통해 라이브러리 내부 동작 원리를 이해해봅시다.
**요구사항:**
1. `__len__``__getitem__` 두 개의 추상 메서드를 가진 추상 기본 클래스 `BaseDataset``abc` 모듈을 사용해 정의하세요.
2. `BaseDataset`를 상속받는 `CustomTextDataset` 클래스를 구현하세요.
3. `CustomTextDataset`는 생성자(`__init__`)에서 텍스트 데이터 리스트를 입력받아 저장합니다.
4. 상속받은 추상 메서드 `__len__`(데이터셋의 총 길이 반환)와 `__getitem__`(주어진 인덱스(i)에 해당하는 데이터 반환)을 구현해야 합니다.
5. 객체를 생성하고, `len()` 함수와 `[]` (인덱싱)을 사용하여 다형성이 어떻게 동작하는지 확인해보세요.
**예시 코드 구조:**
```python
from abc import ABC, abstractmethod
# 1. 추상 기본 클래스 정의
class BaseDataset(ABC):
@abstractmethod
def __len__(self):
pass
@abstractmethod
def __getitem__(self, idx):
pass
# 2. 상속 및 구현
class CustomTextDataset(BaseDataset):
def __init__(self, texts):
self.texts = texts
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
return self.texts[idx]
# 5. 다형성 동작 확인
my_texts = ["Hello AI", "Learn OOP", "PyTorch Style"]
dataset = CustomTextDataset(my_texts)
print(f"데이터셋 길이: {len(dataset)}") # __len__ 호출
print(f"첫 번째 데이터: {dataset[0]}") # __getitem__ 호출
print(f"마지막 데이터: {dataset[-1]}") # __getitem__ 호출
# for-loop도 가능해집니다.
for item in dataset:
print(item)
```
이러한 구조는 PyTorch에서 모든 데이터셋이 공통된 인터페이스(`__len__`, `__getitem__`)를 갖도록 강제하여, `DataLoader`와 같은 다른 구성요소들이 어떤 종류의 데이터셋이든 동일한 방식으로 처리할 수 있게 하는 다형성의 핵심적인 예시입니다.
---
## 9. 되짚어보기 (Summary)
## 9. 트러블슈팅 (Troubleshooting)
- **`TypeError: __init__() missing 1 required positional argument: '...'` 오류가 발생했나요?**
- 클래스의 객체를 생성할 때, `__init__` 메서드가 필요로 하는 모든 인자를 전달하지 않았다는 의미입니다. `my_object = MyClass(arg1, arg2)` 처럼 필수 인자가 모두 포함되었는지 확인하세요. `@dataclass`를 사용했다면, 기본값이 없는 모든 필드가 필수 인자입니다.
- **`self`는 왜 항상 첫 번째 매개변수로 써야 하나요?**
- 파이썬 클래스에서 메서드가 호출될 때, 그 메서드를 호출한 객체 자신이 첫 번째 인자로 암묵적으로 전달됩니다. `self`는 이 객체 자신을 가리키는 관례적인 이름입니다. 이를 통해 메서드는 객체의 다른 속성이나 메서드(`self.attribute`, `self.method()`)에 접근할 수 있습니다.
- **`super()`는 언제 사용해야 하나요?**
- 자식 클래스에서 부모 클래스의 메서드를 오버라이딩했지만, 부모의 원래 기능도 함께 호출하고 싶을 때 사용합니다. 예를 들어, 자식 클래스의 `__init__`에서 부모의 `__init__`을 먼저 호출하여 기본 속성을 설정한 뒤, 자식만의 속성을 추가하는 경우가 대표적입니다.
- **추상 클래스는 객체를 만들 수 없나요?**
- 네, 없습니다. `abc.ABC`를 상속하고 `@abstractmethod`가 하나라도 있는 추상 클래스는 '미완성 설계도'와 같아서 직접 객체로 만들 수 없습니다. 반드시 이 클래스를 상"속받아 모든 추상 메서드를 구현한 자식 클래스만이 객체를 생성할 수 있습니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 10. 되짚어보기 (Summary)
이번 주차에는 '붕어빵 비즈니스'를 통해 객체 지향 프로그래밍(OOP)을 배웠습니다.
......@@ -187,6 +253,168 @@ print(bpp1 == bpp2) # __eq__ 호출 -> True
OOP는 복잡한 AI 라이브러리의 구조를 이해하고, 더 크고 체계적인 프로그램을 만드는 핵심 열쇠입니다.
## 10. 더 깊이 알아보기 (Further Reading)
## 11. 더 깊이 알아보기 (Further Reading)
- [파이썬 공식 튜토리얼: 클래스](https://docs.python.org/ko/3/tutorial/classes.html)
- [Real Python: Object-Oriented Programming (OOP) in Python 3](https://realpython.com/python3-object-oriented-programming/)
---
## 🚀 캡스톤 미니 프로젝트: 나만의 미니 AI 모델 프레임워크 구축
이번 장에서 배운 객체 지향 프로그래밍(OOP)의 꽃, 상속과 다형성을 활용하여 실제 AI 라이브러리의 구조를 모방한 미니 프레임워크를 만들어 봅니다. 이 프로젝트의 목표는 다양한 종류의 AI 모델들을 공통된 방식으로 관리하고 실행할 수 있는 체계를 설계하는 것입니다.
실제 모델의 복잡한 수학 대신, 각 모델이 어떻게 '객체'로서 추상화되고, '공통된 인터페이스'를 통해 유연하게 동작하는지에 집중합니다. 이 경험은 PyTorch, Scikit-learn과 같은 라이브러리의 내부 구조를 이해하는 훌륭한 기반이 될 것입니다.
### 프로젝트 목표
모든 AI 모델이 따라야 하는 규칙(추상 기본 클래스)을 정의하고, 이를 상속받는 여러 종류의 구체적인 모델(간단한 추천 모델, 감성 분석 모델)을 만든 뒤, 이 모델들을 일관된 방식으로 실행하는 파이프라인을 구축합니다.
**예상 실행 시나리오 및 출력:**
```
--- Running SimpleRecommender ---
[Train] 'SimpleRecommender' is training with ['songA', 'songB', 'songA']...
- Model learned most popular items: ['songA']
[Predict] 'SimpleRecommender' is predicting...
- Prediction result: Based on popularity, we recommend ['songA'].
--- Running SentimentClassifier ---
[Train] 'SentimentClassifier' is training with ['this is good', 'so bad', 'really great']...
- Model learned keywords: {'positive': {'good', 'great'}, 'negative': {'bad'}}
[Predict] 'SentimentClassifier' is predicting for input: 'what a great movie'
- Prediction result: positive
```
### 단계별 구현 가이드
**1. 모든 모델의 '설계도', `BaseModel` 추상 클래스 정의**
- `abc` 모듈을 사용하여 `BaseModel`이라는 이름의 추상 기본 클래스를 만드세요.
- `__init__` 에서는 `model_name`을 받아 속성으로 저장합니다.
- 모든 자식 클래스가 반드시 구현해야 할 두 개의 추상 메서드를 정의하세요.
- `train(self, train_data)`
- `predict(self, input_data)`
**2. `BaseModel`을 상속받는 구체적인 모델 클래스 구현**
**2-1. `SimpleRecommender` (인기 아이템 추천 모델)**
- `BaseModel`을 상속받습니다.
- `__init__`: 부모 클래스의 `__init__``super()`로 호출합니다.
- `train`: 학습 데이터(e.g., 아이템 리스트)에서 가장 인기 있는 아이템(최빈값)을 찾아 인스턴스 변수(`self.popular_items`)에 저장하는 로직을 구현합니다. (`collections.Counter` 사용 추천)
- `predict`: `self.popular_items`에 저장된 아이템을 추천 결과로 반환합니다.
**2-2. `SentimentClassifier` (키워드 기반 감성 분석 모델)**
- `BaseModel`을 상속받습니다.
- `__init__`: 부모 클래스의 `__init__``super()`로 호출합니다.
- `train`: 학습 데이터(e.g., 문장 리스트)를 받아, 'positive' 키워드('good', 'great' 등)와 'negative' 키워드('bad', 'terrible' 등)를 학습하여 인스턴스 변수(`self.keywords`)에 저장합니다.
- `predict`: 입력 문장에 학습된 키워드가 포함되어 있는지 확인하여 'positive', 'negative', 'neutral' 중 하나를 반환합니다.
**3. 모델 실행 파이프라인 구축 (다형성 활용)**
- `run_pipeline(models, train_data_dict, predict_data_dict)` 함수를 만드세요.
- 이 함수는 모델 객체의 리스트와 각 모델에 맞는 학습/예측 데이터를 받아 동작합니다.
- 리스트에 담긴 모델들을 하나씩 순회하며, 각 모델의 `train``predict` 메서드를 **동일한 형태로 호출**합니다.
- `isinstance()`를 사용하여 각 모델 객체가 어떤 클래스의 인스턴스인지 확인하고, 그에 맞는 데이터를 전달해줍니다.
**4. 전체 로직 통합**
```python
from abc import ABC, abstractmethod
from collections import Counter
# 1. BaseModel 추상 클래스
class BaseModel(ABC):
def __init__(self, model_name):
self.model_name = model_name
@abstractmethod
def train(self, train_data):
pass
@abstractmethod
def predict(self, input_data):
pass
# 2-1. SimpleRecommender
class SimpleRecommender(BaseModel):
def __init__(self, model_name="Popularity Recommender"):
super().__init__(model_name)
self.popular_items = None
def train(self, train_data):
print(f"[Train] '{self.model_name}' is training with {train_data}...")
item_counts = Counter(train_data)
self.popular_items = [item for item, count in item_counts.most_common(1)]
print(f" - Model learned most popular items: {self.popular_items}")
def predict(self, input_data=None):
print(f"[Predict] '{self.model_name}' is predicting...")
if not self.popular_items:
return "Model not trained yet."
return f"Based on popularity, we recommend {self.popular_items}."
# 2-2. SentimentClassifier
class SentimentClassifier(BaseModel):
def __init__(self, model_name="Keyword-based Classifier"):
super().__init__(model_name)
self.keywords = {'positive': set(), 'negative': set()}
def train(self, train_data):
print(f"[Train] '{self.model_name}' is training with {train_data}...")
for sentence in train_data:
if 'good' in sentence or 'great' in sentence:
self.keywords['positive'].update(sentence.split())
if 'bad' in sentence or 'terrible' in sentence:
self.keywords['negative'].update(sentence.split())
# 간단한 구현을 위해 불용어 처리 등은 생략
self.keywords['positive'] &= {'good', 'great'} # 학습된 키워드만 남김
self.keywords['negative'] &= {'bad', 'terrible'}
print(f" - Model learned keywords: {self.keywords}")
def predict(self, input_data):
print(f"[Predict] '{self.model_name}' is predicting for input: '{input_data}'")
input_words = set(input_data.split())
if self.keywords['positive'] & input_words:
return "positive"
elif self.keywords['negative'] & input_words:
return "negative"
return "neutral"
# 3. 모델 실행 파이프라인
def run_pipeline(models, train_data_dict, predict_data_dict):
for model in models:
model_type = type(model).__name__
print(f"\n--- Running {model_type} ---")
# 모델 타입에 맞는 데이터로 학습 및 예측
train_data = train_data_dict.get(model_type)
predict_data = predict_data_dict.get(model_type)
if train_data:
model.train(train_data)
if predict_data:
prediction = model.predict(predict_data)
print(f" - Prediction result: {prediction}")
# 4. 전체 로직 실행
if __name__ == "__main__":
# 모델 객체 생성
recommender = SimpleRecommender()
classifier = SentimentClassifier()
# 모델들을 리스트에 담기
my_models = [recommender, classifier]
# 각 모델에 맞는 데이터 준비
train_data = {
"SimpleRecommender": ['songA', 'songB', 'songA', 'songC', 'songA'],
"SentimentClassifier": ['this is good movie', 'what a bad day', 'he is great person']
}
predict_data = {
"SimpleRecommender": None, # 입력이 필요 없는 모델
"SentimentClassifier": "the movie was really great"
}
# 파이프라인 실행
run_pipeline(my_models, train_data, predict_data)
```
이 구조를 이해하면, 왜 AI 라이브러리들이 `model.fit(data)` 이나 `model.predict(new_data)` 와 같은 일관된 API를 제공하는지, 그리고 그것이 어떻게 다양한 종류의 모델에 적용될 수 있는지(다형성) 깊이 있게 파악할 수 있게 됩니다.
\ No newline at end of file
......@@ -233,7 +233,135 @@ stock_df = pd.DataFrame(stock_data)
---
## 7. 되짚어보기 (Summary)
## 7. 트러블슈팅 (Troubleshooting)
- **`ValueError: operands could not be broadcast together with shapes (...) (...)`가 발생했나요?**
- NumPy 배열 간의 연산에서 발생하며, 브로드캐스팅 규칙에 맞지 않는다는 의미입니다. 두 배열의 차원 수나 각 차원의 크기가 호환되는지 확인하세요. 예를 들어, `(3, 4)` 배열과 `(4,)` 벡터는 더할 수 있지만, `(3, 4)` 배열과 `(3,)` 벡터는 기본적으로 더할 수 없습니다.
- **`KeyError: '...'`가 발생하며 특정 열을 찾을 수 없나요?**
- DataFrame에 해당 이름의 열이 없는 경우입니다. `df.columns`를 출력하여 실제 열 이름에 오타나 불필요한 공백이 있는지 확인해보세요.
- **`SettingWithCopyWarning` 경고가 계속 나타나나요?**
- 이 경고는 Pandas가 원본을 수정하려는 것인지, 사본을 수정하려는 것인지 헷갈릴 때 발생합니다. `df[df['A'] > 0]['B'] = 1` 과 같이 여러 단계로 접근하여 값을 할당하는 대신, `df.loc[df['A'] > 0, 'B'] = 1` 처럼 `.loc`를 사용하여 한 번에 명확하게 위치를 지정하여 값을 할당하면 경고를 피할 수 있습니다.
- **`merge`를 했는데 데이터가 예상보다 적거나 많게 나오나요?**
- `how` 매개변수(`'left'`, `'right'`, `'inner'`, `'outer'`)의 동작을 다시 한번 확인해보세요. 기본값인 `'inner'`는 양쪽 DataFrame에 모두 존재하는 키만 남기므로 데이터가 줄어들 수 있습니다. 또한, 병합하려는 키에 중복된 값이 있다면 결과 행이 예상보다 많아질 수 있습니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 🚀 캡스톤 미니 프로젝트: 타이타닉 생존자 데이터 분석 (EDA)
이번 장에서 배운 NumPy와 Pandas는 모든 AI 프로젝트의 출발점인 '탐색적 데이터 분석(Exploratory Data Analysis, EDA)'의 핵심 도구입니다. 이번 미니 프로젝트에서는 데이터 과학 입문용으로 가장 유명한 '타이타닉 호 생존자 데이터'를 사용하여, 실제 데이터 분석 프로젝트의 축소판을 경험해 봅니다.
데이터를 불러오고, 정제하고, 새로운 정보를 파생시키고, 그룹별로 분석하여 의미 있는 인사이트를 발견하고, 시각화하는 전체 과정을 직접 수행하게 됩니다.
### 프로젝트 목표
주어진 타이타닉 승객 데이터를 Pandas DataFrame으로 분석하여, "어떤 승객이 더 많이 살아남았을까?"라는 질문에 대한 답을 데이터에 기반하여 찾아내고, 분석 결과를 시각화하여 보고합니다.
**샘플 데이터 (CSV 형식):**
```csv
PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Fare,Embarked
1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,7.25,S
2,1,1,"Cumings, Mrs. John Bradley (Florence Briggs Thayer)",female,38,1,0,71.2833,C
3,1,3,"Heikkinen, Miss. Laina",female,26,0,0,7.925,S
4,1,1,"Futrelle, Mrs. Jacques Heath (Lily May Peel)",female,35,1,0,53.1,S
5,0,3,"Allen, Mr. William Henry",male,35,0,0,8.05,S
6,0,3,"Moran, Mr. James",male,,0,0,8.4583,Q
7,0,1,"McCarthy, Mr. Timothy J",male,54,0,0,51.8625,S
8,0,3,"Palsson, Master. Gosta Leonard",male,2,3,1,21.075,S
9,1,3,"Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)",female,27,0,2,11.1333,S
10,1,2,"Nasser, Mrs. Nicholas (Adele Achem)",female,14,1,0,30.0708,C
```
**최종 분석 리포트 (예시):**
```
*** Titanic Survival Analysis Report ***
[Basic Statistics]
Total Passengers: 10
Survival Rate: 40.0%
[Survival Rate by Gender]
Sex
female 1.000000
male 0.000000
Name: Survived, dtype: float64
[Survival Rate by Pclass]
Pclass
1 0.500000
2 1.000000
3 0.400000
Name: Survived, dtype: float64
(여기에 Matplotlib/Seaborn을 이용한 시각화 차트가 추가될 것입니다)
```
### 단계별 구현 가이드
**1. 데이터 로드 및 기본 탐색**
- 위 CSV 텍스트를 `io.StringIO`를 사용하여 Pandas DataFrame으로 불러오세요. (실제 파일이 없어도 문자열을 파일처럼 읽게 해줍니다)
- `df.info()`, `df.describe()`를 사용해 데이터의 기본 정보를 파악하세요.
```python
import pandas as pd
import numpy as np
import io
csv_data = """PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Fare,Embarked
1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,7.25,S
2,1,1,"Cumings, Mrs. John Bradley (Florence Briggs Thayer)",female,38,1,0,71.2833,C
3,1,3,"Heikkinen, Miss. Laina",female,26,0,0,7.925,S
4,1,1,"Futrelle, Mrs. Jacques Heath (Lily May Peel)",female,35,1,0,53.1,S
5,0,3,"Allen, Mr. William Henry",male,35,0,0,8.05,S
6,0,3,"Moran, Mr. James",male,,0,0,8.4583,Q
7,0,1,"McCarthy, Mr. Timothy J",male,54,0,0,51.8625,S
8,0,3,"Palsson, Master. Gosta Leonard",male,2,3,1,21.075,S
9,1,3,"Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)",female,27,0,2,11.1333,S
10,1,2,"Nasser, Mrs. Nicholas (Adele Achem)",female,14,1,0,30.0708,C
"""
df = pd.read_csv(io.StringIO(csv_data))
```
**2. 데이터 정제 및 피처 엔지니어링**
- `Age` 열의 결측치(NaN)를 전체 승객의 **중앙값(median)**으로 채워 넣으세요.
- `SibSp` (형제/배우자 수)와 `Parch` (부모/자녀 수)를 더하고 1을 추가하여 `FamilySize` (가족 총원)라는 새로운 열을 만드세요.
**3. 그룹 분석 (Group Analysis)**
- `groupby()`를 사용하여 다음을 계산하고 출력하세요.
- 성별(`Sex`)에 따른 생존율(`Survived`의 평균)
- 객실 등급(`Pclass`)에 따른 생존율
- 객실 등급(`Pclass`)별 운임(`Fare`)의 평균
- `FamilySize`별 생존율
**4. 데이터 시각화 (Matplotlib/Seaborn 소개)**
- 데이터 분석 결과를 직관적으로 이해하기 위해 시각화는 필수입니다.
- `matplotlib``seaborn` 라이브러리를 사용하여, 위에서 분석한 '객실 등급별 생존율'과 '성별 생존율'을 막대그래프로 그려보세요.
```python
# !pip install matplotlib seaborn
import matplotlib.pyplot as plt
import seaborn as sns
# 성별에 따른 생존율 시각화
sns.barplot(x='Sex', y='Survived', data=df)
plt.title('Survival Rate by Gender')
plt.show()
# 객실 등급에 따른 생존율 시각화
sns.barplot(x='Pclass', y='Survived', data=df)
plt.title('Survival Rate by Pclass')
plt.show()
```
이 EDA 과정을 통해 얻은 인사이트("여성의 생존율이 높다", "1등석 승객의 생존율이 높다" 등)는 나중에 머신러닝 모델을 만들 때 어떤 정보를 더 중요하게 고려해야 할지 결정하는 중요한 근거가 됩니다.
---
## 8. 되짚어보기 (Summary)
이번 주차에는 데이터 과학의 양대 산맥인 **NumPy****Pandas**를 마스터했습니다.
......@@ -243,7 +371,7 @@ stock_df = pd.DataFrame(stock_data)
이제 여러분은 대부분의 정형 데이터를 원하는 형태로 가공하고, 기본적인 통계를 파악할 수 있는 강력한 무기를 갖추게 되었습니다.
## 8. 더 깊이 알아보기 (Further Reading)
## 9. 더 깊이 알아보기 (Further Reading)
- [Pandas 공식 10분 완성 튜토리얼](https://pandas.pydata.org/docs/user_guide/10min.html)
- [NumPy 공식 시작하기 가이드](https://numpy.org/doc/stable/user/absolute_beginners.html)
- [Chris Albon's Notes On pandas and NumPy](https://chrisalbon.com/): 특정 상황에 필요한 코드 스니펫을 찾기에 매우 유용한 사이트
......
......@@ -145,6 +145,40 @@ scaled_minmax = scaler_minmax.fit_transform(data)
print("\nMinMaxScaler 결과:\n", scaled_minmax)
```
### 5-4. 심화: 어떤 모델을 언제 사용해야 할까? (Model Selection Guide)
Scikit-learn에는 수많은 머신러닝 모델이 존재합니다. 문제의 종류, 데이터의 특성, 그리고 우리가 추구하는 목표(성능, 해석 가능성, 속도 등)에 따라 최적의 모델은 달라집니다. 여기서는 가장 대표적인 **분류(Classification)** 모델 몇 가지를 비교하고, 언제 어떤 모델을 선택하는 것이 좋은지에 대한 가이드라인을 제시합니다.
| 모델 | 핵심 아이디어 | 장점 | 단점 | 언제 사용하면 좋을까? |
| :--- | :--- | :--- | :--- | :--- |
| **로지스틱 회귀<br>(Logistic Regression)** | 데이터가 특정 클래스에 속할 확률을 선형 결정 경계로 예측 | 빠르고, 간단하며, **해석이 용이함**. 모델의 계수가 각 특성의 중요도를 나타냄. | 복잡한 비선형 관계를 학습하기 어려움. | **모델의 해석**이 중요할 때 (e.g., 금융, 의료), 베이스라인 모델로 사용하기 좋음. |
| **결정 트리<br>(Decision Tree)** | 데이터를 '예/아니오' 질문(규칙)의 연속으로 나누어 예측 | **결과를 이해하기 쉬움 (White Box)**. 시각화가 가능함. 스케일링이 필요 없음. | **과적합(Overfitting) 위험이 높음**. 데이터의 작은 변화에도 모델이 크게 변동될 수 있음. | 규칙 기반으로 데이터를 설명하고 싶을 때. (e.g., 고객 이탈 원인 분석) |
| **랜덤 포레스트<br>(Random Forest)** | 여러 개의 작은 결정 트리들을 만들어, 그 결과를 **투표(Voting)**하여 종합. (앙상블 기법) | **성능이 매우 우수함**. 과적합에 강하고 안정적임. | 결정 트리보다 **해석이 어려움 (Black Box)**. 훈련 속도가 느리고 메모리를 더 많이 사용. | **높은 예측 성능**이 가장 중요할 때. 대부분의 분류 문제에서 좋은 성능을 보임. |
| **서포트 벡터 머신<br>(SVM)** | 클래스 간의 **마진(Margin)을 최대화**하는 결정 경계를 찾음. | 고차원 데이터에서 잘 작동하며, 과적합에 강함. | 데이터가 많을 때 훈련 속도가 매우 느림. 파라미터 튜닝에 민감함. | 특성(Feature)이 매우 많은 복잡한 데이터셋 (e.g., 이미지, 바이오인포매틱스)에 효과적. |
> **💡 핵심 질문: 해석 가능성(Interpretability) vs. 성능(Performance)**
> - **해석이 중요하다면?** -> Logistic Regression, Decision Tree
> - **성능이 중요하다면?** -> Random Forest, Gradient Boosting (더 발전된 앙상블), SVM
>
> 실무에서는 먼저 Logistic Regression이나 Decision Tree 같은 간단한 모델로 베이스라인을 설정하고, 점차 Random Forest나 Gradient Boosting과 같은 복잡한 모델로 성능을 높여가는 방식을 많이 사용합니다.
이 가이드는 시작점일 뿐이며, 최고의 모델을 찾는 유일한 방법은 **직접 여러 모델을 실험해보고, 교차 검증(Cross-validation)을 통해 성능을 객관적으로 평가하는 것**입니다.
### 5-5. 실제 기업 적용 사례 (Real-world Enterprise Use Cases)
우리가 배운 머신러닝 기술은 단순히 학문적인 개념에 머무르지 않고, 오늘날 수많은 글로벌 기업들의 핵심 서비스를 구동하는 엔진이 되고 있습니다. 몇 가지 대표적인 사례를 통해 머신러닝이 실제로 어떻게 가치를 창출하는지 살펴보겠습니다.
| 기업 | 적용 분야 | 사용 기술 및 모델 (추정) | 비즈니스 가치 |
| :--- | :--- | :--- | :--- |
| **Netflix** | **영화/드라마 추천 시스템** | 협업 필터링, 콘텐츠 기반 필터링, 랭킹 알고리즘 (e.g., Logistic Regression, Gradient Boosting) | 개인화된 콘텐츠 추천을 통해 **고객 이탈률을 낮추고**, 사용자 만족도를 극대화. (매년 1조 원 이상의 가치 창출) |
| **Google** | **Gmail 스팸 필터** | 지도 학습 분류기 (e.g., Naive Bayes, Logistic Regression), 딥러닝(RNN, Transformer) | 매일 수십억 개의 스팸 메일을 99.9% 이상의 정확도로 차단하여, **사용자 경험을 보호**하고 보안 위협을 감소시킴. |
| **Google** | **검색 결과 랭킹 (RankBrain)** | 학습-투-랭크(Learning to Rank) 알고리즘 | 이전에는 없었던 새로운 검색어(전체 검색의 15%)에 대해 **가장 관련성 높은 결과를 추론**하여 제시. 검색 품질 향상. |
| **Meta (Facebook)** | **뉴스피드 랭킹** | 랭킹 알고리즘, 대규모 추천 시스템 | 수천 개의 잠재적 게시물 중 사용자에게 가장 흥미로울 만한 콘텐츠를 예측하여 보여줌으로써, **플랫폼 체류 시간을 늘리고** 광고 효율을 높임. |
| **Amazon** | **상품 추천 (Customers who bought this item also bought...)** | 협업 필터링 (Item-to-Item), 개인화 랭킹 | 사용자의 구매 패턴을 분석하여 관련 상품을 추천함으로써, **객단가(Average Order Value)를 높이고** 새로운 상품을 발견할 기회를 제공. |
> **💡 시사점: 문제 정의의 중요성**
> 위 사례들의 공통점은 기술 자체가 목적이 아니라, '고객 이탈 방지', '사용자 경험 보호', '관련성 높은 정보 제공'과 같은 **명확한 비즈니스 문제를 해결하기 위해 머신러닝을 '도구'로 사용했다는 점**입니다. 성공적인 머신러닝 프로젝트를 위해서는 "어떤 모델을 쓸까?" 보다 "**어떤 문제를 풀고 싶은가?**"를 먼저 정의하는 것이 중요합니다.
---
## 6. 직접 해보기 (Hands-on Lab): 타이타닉 생존자 예측
......@@ -164,6 +198,35 @@ print("\nMinMaxScaler 결과:\n", scaled_minmax)
---
## ⚠️ What Could Go Wrong? (토론 주제)
Scikit-learn을 사용하면 몇 줄의 코드로 쉽게 모델을 만들 수 있지만, 그 과정에는 수많은 함정이 존재합니다. "동작하는" 코드를 넘어 "올바른" 코드를 작성하기 위해 다음 주제들에 대해 함께 토론해보세요.
1. **전처리 순서가 초래한 데이터 누수(Data Leakage)**
- **상황**: 한 동료가 효율성을 위해 전체 데이터셋 `X`에 대해 `StandardScaler`를 먼저 적용(`fit_transform`)한 후, 그 결과를 `train_test_split`으로 나누어 모델을 학습시켰습니다. 코드는 오류 없이 잘 동작했고, 테스트 정확도도 꽤 높게 나왔습니다.
- **토론**:
- 이 접근 방식의 무엇이 치명적인 실수일까요? 테스트셋의 정보가 훈련 과정에 어떻게 "누수"되었을까요?
- 피처 스케일링과 같은 전처리 단계를 올바르게 적용하는 파이프라인은 어떤 순서로 구성되어야 할까요? 왜 그 순서가 중요한가요?
2. **99% 정확도의 함정: 불균형 데이터 문제**
- **상황**: 어떤 희귀병(전체 환자의 1%)을 예측하는 분류 모델을 만들었습니다. 테스트셋에서 99%의 정확도(Accuracy)를 달성했다고 팀에 보고하자, 한 선임 개발자가 결과에 우려를 표했습니다.
- **토론**:
- 왜 99%의 정확도가 이 상황에서는 매우 나쁜 성능 지표일 수 있을까요? 만약 모델이 무조건 "정상"이라고만 예측한다면, 그 모델의 정확도는 몇 %가 될까요?
- 이처럼 데이터 클래스가 심하게 불균형할 때, 정확도 대신 사용해야 할 더 나은 평가 지표는 무엇이 있을까요? 혼동 행렬(Confusion Matrix), 정밀도(Precision), 재현율(Recall), F1-점수(F1-score)는 각각 어떤 정보를 우리에게 알려주나요?
3. **"완벽한" 테스트 점수에 대한 의심**
- **상황**: 타이타닉 생존자 예측 문제를 해결하기 위해 여러 모델과 특성을 조합해본 결과, 마침내 제공된 테스트셋에 대해 100%의 정확도를 달성했습니다.
- **토론**:
- 이 결과를 기뻐해야 할까요, 아니면 의심해야 할까요? "완벽한" 점수가 나올 수 있는 잠재적인 원인들은 무엇일까요? (예: 데이터 누수, 너무 작거나 대표성이 없는 테스트셋, 특정 테스트셋에 대한 과적합 등)
- 실제 서비스를 만든다고 가정할 때, 왜 최종 성능 검증을 위해 단 한 번만 사용하는 별도의 "검증(validation) 데이터셋"을 따로 떼어놓는 것이 중요한지 토론해보세요.
4. **특성 중요도(Feature Importance)의 섣부른 해석**
- **상황**: 고객 이탈 예측을 위해 만든 랜덤 포레스트 모델이 '월 청구요금'을 가장 중요한 특성으로 지목했습니다. 이 결과를 보고, 마케팅팀은 모든 고객의 요금을 인하하는 캠페인을 기획하려 합니다.
- **토론**:
- 이러한 의사 결정이 왜 섣부른 판단일 수 있을까요? 랜덤 포레스트는 특성 중요도를 어떤 방식으로 계산하나요?
- 높은 특성 중요도가 '인과 관계'를 의미할까요, 아니면 단순한 '상관 관계'일 가능성이 높을까요? 이처럼 중요한 비즈니스 결정을 내리기 전에, 어떤 추가적인 분석이나 실험을 수행해야 할까요?
## 7. 되짚어보기 (Summary)
이번 주 우리는 데이터에서 패턴을 찾아 미래를 예측하는 머신러닝의 세계를 여행했습니다.
......@@ -174,11 +237,177 @@ print("\nMinMaxScaler 결과:\n", scaled_minmax)
이제 여러분은 데이터를 분석하고 예측 모델을 만들 수 있는 기본적인 역량을 갖추었습니다.
## 8. 더 깊이 알아보기 (Further Reading)
---
## 8. 트러블슈팅 (Troubleshooting)
- **그래프에 한글이 깨져서 `□□□` 형태로 나오나요?**
- Matplotlib가 기본적으로 한글 폰트를 지원하지 않기 때문입니다. 아래 코드를 실행하여 시스템에 맞는 한글 폰트를 설정해주어야 합니다.
```python
import matplotlib.pyplot as plt
# For Windows
# plt.rcParams['font.family'] = 'Malgun Gothic'
# For macOS
# plt.rcParams['font.family'] = 'AppleGothic'
```
- **`fit_transform()`은 훈련 데이터에만 사용하고, `transform()`은 테스트 데이터에 사용하라는 말이 무슨 뜻인가요?**
- `fit()`은 데이터로부터 스케일링에 필요한 정보(e.g., 평균, 표준편차, 최소/최대값)를 '학습'하는 과정입니다. `transform()`은 학습된 정보를 바탕으로 데이터를 변환합니다. `fit_transform()`은 이 두 과정을 한 번에 수행합니다. 테스트 데이터는 '미래의 데이터'를 의미하므로, 훈련 데이터에서 학습한 '규칙'을 그대로 적용해야 합니다. 따라서 테스트 데이터에는 `fit()`을 다시 사용하지 않고, 훈련 데이터로 `fit()`한 스케일러를 사용하여 `transform()`만 적용해야 합니다.
- **모델의 성능이 너무 낮게 나오나요?**
- 여러 가능성이 있습니다.
- **데이터 전처리**: 결측치를 적절히 처리했나요? 범주형 변수(e.g., 'sex')를 숫자로 변환했나요? 피처 스케일링이 필요한 모델인데 적용했나요?
- **특성 선택**: 모델에 너무 적은 특성을 사용했거나, 반대로 예측에 도움이 되지 않는 불필요한 특성을 너무 많이 사용했을 수 있습니다.
- **모델 선택**: 현재 문제에 적합하지 않은 모델을 사용했을 수 있습니다. 'Model Selection Guide'를 참고하여 다른 모델을 시도해보세요.
- **과적합/과소적합**: 훈련 데이터의 점수는 높은데 테스트 데이터의 점수가 현저히 낮다면 과적합(Overfitting)을, 둘 다 낮다면 과소적합(Underfitting)을 의심해볼 수 있습니다.
- **`ValueError: Input contains NaN, infinity or a value too large for dtype('float64').` 오류가 발생했나요?**
- 모델의 입력 데이터(`X_train`)에 결측치(NaN), 무한대(infinity) 값이 포함되어 있다는 의미입니다. 모델을 학습시키기 전, `X_train.isnull().sum()` 과 같은 코드로 결측치를 확인하고, `fillna()` 등을 사용하여 적절히 처리해야 합니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 더 깊이 알아보기 (Further Reading)
### 실전 코드 & 튜토리얼
- [Scikit-learn 공식 예제 갤러리](https://scikit-learn.org/stable/auto_examples/index.html): 다양한 모델과 데이터셋에 대한 예제 코드의 보고
- [Seaborn 공식 튜토리얼](https://seaborn.pydata.org/tutorial.html): 더 아름답고 전문적인 시각화를 위한 가이드
- [Kaggle](https://www.kaggle.com/): 전 세계 데이터 과학자들이 모여 경쟁하는 플랫폼. 'Titanic'과 같은 다양한 입문용 프로젝트를 체험할 수 있습니다.
### 이론 및 개념 심화
- **[An Introduction to Statistical Learning (ISLR)](https://www.statlearning.com/)**: 머신러닝의 바이블로 불리는 명저. 통계적 이론 배경을 탄탄히 다지고 싶다면 필독을 권합니다. (PDF 무료 제공)
- **[StatQuest 유튜브 채널](https://www.youtube.com/c/statquest)**: 결정 트리, 로지스틱 회귀 등 머신러닝의 핵심 개념들을 가장 직관적이고 명쾌하게 설명해주는 채널입니다.
- **[Scikit-learn: 모델 평가 지표 가이드](https://scikit-learn.org/stable/modules/model_evaluation.html)**: 본문에서 다룬 정확도 외에 정밀도, 재현율, F1-score 등 다양한 평가 지표에 대한 상세한 설명과 사용법을 제공합니다.
- **[Seeing-Theory](https://seeing-theory.brown.edu/index.html)**: 확률 및 통계의 핵심 개념들을 인터랙티브 시각화 자료를 통해 체험하며 배울 수 있는 사이트입니다.
---
## 🚀 캡스톤 미니 프로젝트: 모델 성능 비교 및 파이프라인 구축
이번 장에서는 머신러닝의 전체 흐름을 배웠습니다. 하지만 실제 프로젝트에서는 단 하나의 모델만 만들어보는 경우는 거의 없습니다. 어떤 모델이 이 문제에 가장 적합한지, 그리고 데이터 전처리가 성능에 어떤 영향을 미치는지 체계적으로 '실험'하고 '비교'하는 과정이 필수적입니다.
이번 미니 프로젝트의 목표는 Scikit-learn의 `Pipeline`을 사용하여 데이터 전처리(스케일링)와 모델 훈련을 하나의 파이프라인으로 묶고, 여러 다른 모델의 성능을 체계적으로 비교하여 최적의 모델을 찾는 과정을 경험하는 것입니다.
### 프로젝트 목표
Scikit-learn의 내장 데이터셋인 붓꽃(Iris) 데이터를 사용하여, 다음 세 가지 모델의 성능을 '전처리 적용 전/후'로 나누어 비교하고, 그 결과를 보고서로 작성합니다.
1. Logistic Regression
2. Decision Tree Classifier
3. Random Forest Classifier
**최종 분석 리포트 (예시):**
```
*** Model Performance Comparison Report ***
[Without Scaling]
- LogisticRegression Accuracy: 0.9333
- DecisionTreeClassifier Accuracy: 1.0000
- RandomForestClassifier Accuracy: 1.0000
[With StandardScaler]
- LogisticRegression Accuracy: 1.0000
- DecisionTreeClassifier Accuracy: 1.0000
- RandomForestClassifier Accuracy: 1.0000
[Analysis]
- 스케일링을 적용했을 때 Logistic Regression의 성능이 향상되었습니다.
- 트리 기반 모델인 Decision Tree와 Random Forest는 스케일링의 영향을 받지 않았습니다.
- ...
```
### 단계별 구현 가이드
**1. 데이터 준비**
- `sklearn.datasets`에서 `load_iris` 함수를 사용해 데이터를 불러오세요.
- `train_test_split`을 사용하여 데이터를 훈련셋과 테스트셋으로 분리합니다.
**2. 모델 비교 실험 (스케일링 미적용)**
- 비교할 모델들을 리스트에 담으세요: `[LogisticRegression(), DecisionTreeClassifier(), RandomForestClassifier()]`
- `for` 루프를 사용해 각 모델을 순회하며 다음을 반복합니다.
- 모델을 훈련 데이터로 학습 (`fit`)
- 테스트 데이터로 예측 (`predict`)
- 정확도(`accuracy_score`)를 계산하고 결과를 저장
**3. Scikit-learn `Pipeline`으로 전처리 및 모델링 자동화**
- 데이터 전처리와 모델 학습을 하나의 객체로 묶어주는 `Pipeline`을 사용해 봅시다. 이는 데이터 누수(Data Leakage)를 방지하는 매우 중요한 실무 기법입니다.
- `from sklearn.pipeline import Pipeline`
- `from sklearn.preprocessing import StandardScaler`
- 다시 모델 리스트를 순회하며, 각 모델에 대해 `StandardScaler`와 모델을 포함하는 파이프라인을 생성합니다.
- `pipe = Pipeline([('scaler', StandardScaler()), ('classifier', model)])`
- 전체 파이프라인을 훈련 데이터로 학습시키고(`pipe.fit`), 테스트 데이터로 정확도를 평가합니다.
**4. 결과 보고 및 분석**
- 위 두 실험(스케일링 전/후)의 결과를 보기 좋게 출력하세요.
- 결과에 대한 분석을 1~2줄로 덧붙여 보세요.
- 왜 Logistic Regression 모델의 성능은 스케일링 후 변했는데, 트리 기반 모델들은 변하지 않았을까요? (힌트: 각 모델이 데이터를 어떻게 바라보는지 생각해보세요. 거리 기반 vs 규칙 기반)
**전체 코드 구조:**
```python
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.pipeline import Pipeline
# 1. 데이터 준비
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 2. 비교할 모델 리스트
models = [
LogisticRegression(max_iter=1000),
DecisionTreeClassifier(random_state=42),
RandomForestClassifier(random_state=42)
]
# 3. 실험: 스케일링 전/후 성능 비교
results_no_scaling = {}
results_with_scaling = {}
print("--- Starting Model Evaluation ---")
for model in models:
model_name = model.__class__.__name__
# 스케일링 없이 학습 및 평가
model.fit(X_train, y_train)
pred_no_scaling = model.predict(X_test)
results_no_scaling[model_name] = accuracy_score(y_test, pred_no_scaling)
# 파이프라인으로 스케일링 적용 후 학습 및 평가
pipe = Pipeline([
('scaler', StandardScaler()),
('classifier', model)
])
pipe.fit(X_train, y_train)
pred_with_scaling = pipe.predict(X_test)
results_with_scaling[model_name] = accuracy_score(y_test, pred_with_scaling)
# 4. 결과 출력
print("\n*** Model Performance Comparison Report ***\n")
print("[Without Scaling]")
for model_name, acc in results_no_scaling.items():
print(f"- {model_name} Accuracy: {acc:.4f}")
print("\n[With StandardScaler]")
for model_name, acc in results_with_scaling.items():
print(f"- {model_name} Accuracy: {acc:.4f}")
print("\n[Analysis]")
print("로지스틱 회귀와 같은 거리 기반 알고리즘은 피처 스케일링에 영향을 받지만,")
print("결정 트리 기반 알고리즘은 각 피처를 독립적으로 보기 때문에 스케일링의 영향을 거의 받지 않습니다.")
print("---------------------------------------")
```
이러한 체계적인 실험 과정을 통해, 여러분은 단순히 하나의 모델을 만드는 것을 넘어, 주어진 문제와 데이터에 가장 적합한 해결책을 '찾아내는' 데이터 과학자의 역량을 기르게 될 것입니다.
---
**➡️ 다음 시간: [Part 7: 딥러닝 기초와 PyTorch](../07_deep_learning/part_7_deep_learning.md)**
......@@ -181,6 +181,35 @@ print(f'\nFinal prediction after training: "hell" -> "{predicted_char}"')
> [!TIP]
> 위 코드를 직접 실행해보면서 `hidden_size`, `learning_rate`, `epoch` 등의 하이퍼파라미터를 바꿔보세요. `nn.RNN`을 `nn.LSTM`으로 바꾸고 실행해보는 것도 좋습니다. (LSTM은 `forward`에서 `hidden`이 `(h_0, c_0)` 튜플 형태여야 합니다)
---
## ⚠️ What Could Go Wrong? (토론 주제)
RNN과 LSTM은 순차 데이터 처리의 혁신이었지만, 실제 적용 과정에서는 여러 가지 한계와 어려움에 부딪힙니다. 다음 시나리오들에 대해 함께 토론해보세요.
1. **훈련 중 손실(Loss)이 `NaN`으로 폭발하는 현상**
- **상황**: 시계열 예측을 위한 RNN 모델을 훈련하던 중, 손실(loss) 값이 갑자기 `NaN` (Not a Number)으로 바뀌면서 훈련이 멈춰버렸습니다.
- **토론**:
- 이 현상의 가장 유력한 원인은 무엇일까요? 본문에서 배운 '기울기 소실' 문제와 '기울기 폭발' 문제 중 어느 쪽에 해당하며, 그 이유는 무엇일까요?
- 이 문제를 해결하기 위한 가장 대표적인 기법인 **그래디언트 클리핑(Gradient Clipping)**이란 무엇이며, 어떤 원리로 동작하는지 설명해보세요. (PyTorch의 `torch.nn.utils.clip_grad_norm_` 함수를 찾아보는 것을 추천합니다.)
2. **순차 처리의 본질적인 병목 현상**
- **상황**: 아주 긴 문서(예: 책 한 권)를 LSTM으로 번역하는 모델을 학습시키려 합니다. 강력한 GPU를 사용함에도 불구하고, 이전 챕터에서 배운 CNN 모델에 비해 학습 속도가 매우 느리고, GPU 메모리 문제로 배치 사이즈(batch size)를 키우기도 어렵습니다.
- **토론**:
- RNN/LSTM 계열의 모델들이 CNN에 비해 병렬화가 어려운 근본적인 아키텍처상의 이유는 무엇일까요? `h_t``h_t-1`에 의존해야만 하는 순차적 계산 방식이 왜 성능의 병목이 될까요?
- 이러한 한계점이 이후에 배울 Transformer와 같은 새로운 아키텍처의 등장을 어떻게 촉진시켰을지 토론해보세요.
3. **"잊는 것"은 기능이지만, 완벽하지는 않다**
- **상황**: LSTM 기반의 챗봇을 만들었습니다. 대화 초반에 사용자가 자신의 이름을 알려주면, 챗봇은 대화 내내 사용자의 이름을 잘 기억하고 사용합니다. 하지만 대화가 길어진 후 "내가 처음에 했던 질문이 뭐였지?"라고 물어보면, 챗봇은 그 내용을 기억하지 못합니다.
- **토론**:
- LSTM은 장기 의존성 문제를 해결하기 위해 고안되었지만, 여전히 위와 같은 한계를 보입니다. 일반적인 문맥(사용자 이름)은 잘 유지하면서도, 구체적이고 덜 중요한 과거의 정보를 놓치는 이유는 무엇일까요?
- LSTM의 게이트들이 0 또는 1의 이산적인 값이 아닌 0과 1 사이의 연속적인 값을 갖는다는 점이, 어떻게 정보의 '완벽한 보존'이 아닌 점진적인 '희미해짐'에 기여할 수 있을지 토론해보세요.
4. **양방향 RNN: 미래를 보는 것이 항상 정답일까?**
- **상황**: 영화 리뷰의 긍정/부정을 판단하는 감성 분석 모델을 만들 때, 단방향(unidirectional) LSTM보다 양방향(Bidirectional) LSTM이 훨씬 좋은 성능을 보였습니다. 한 동료가 "이 모델은 문장의 미래를 보고 예측하는 것이니, 반칙이 아닌가?"라고 질문했습니다.
- **토론**:
- 양방향 LSTM의 개념을 비전공자에게 어떻게 설명할 수 있을까요? 왜 감성 분석이나 기계 번역 같은 Task에서는 문장의 전체 문맥(과거+미래)을 보는 것이 유리할까요?
- 반대로, 실시간 주가 예측이나 일기예보와 같은 Task에 양방향 모델을 사용하는 것이 왜 부적절하거나 불가능한지 토론해보세요.
## 8. 되짚어보기 (Summary)
......@@ -195,4 +224,146 @@ print(f'\nFinal prediction after training: "hell" -> "{predicted_char}"')
---
## 🚀 캡스톤 미니 프로젝트: 주가 예측 (LSTM)
RNN과 LSTM의 가장 대표적인 활용 사례 중 하나는 주가, 날씨, 판매량과 같은 시계열(Time-series) 데이터를 예측하는 것입니다. 이번 미니 프로젝트에서는 LSTM 모델을 사용하여, 과거의 주가 데이터 패턴을 학습하고 미래의 주가를 예측하는 간단한 주가 예측기를 만들어 봅니다.
이 프로젝트의 핵심은 **연속된 시계열 데이터를 딥러닝 모델의 입력 형식(sequence, target)으로 가공**하는 방법을 배우고, 회귀(Regression) 문제에 맞는 손실 함수를 사용하여 모델을 훈련시키는 것입니다.
### 프로젝트 목표
1. 연속된 시계열 데이터를 정규화(Normalization)하고, 지도 학습을 위한 입력 시퀀스와 타겟 값으로 분리합니다.
2. PyTorch `nn.LSTM` 레이어를 사용하여 시계열 예측 모델을 설계합니다.
3. 회귀 문제에 적합한 손실 함수(`nn.MSELoss`)를 사용하여 모델을 훈련합니다.
4. 예측된 값을 실제 값과 함께 시각화하여 모델의 성능을 직관적으로 평가합니다.
**최종 분석 리포트 (예시):**
```
Epoch [20/200], Loss: 0.0451
Epoch [40/200], Loss: 0.0218
...
Epoch [200/200], Loss: 0.0015
```
(여기에 실제 주가와 예측 주가를 비교하는 Matplotlib 그래프가 추가됩니다)
![Stock Prediction Chart](https://i.imgur.com/r8O0e2b.png)
### 단계별 구현 가이드
**1. 데이터 준비**
- `numpy`를 사용하여 가상의 일일 주가 데이터를 생성합니다.
- `MinMaxScaler`를 사용하여 데이터 값을 0과 1 사이로 정규화합니다. 이는 LSTM의 안정적인 학습을 위해 매우 중요합니다.
- 정규화된 데이터를 `(입력 시퀀스, 타겟 값)` 쌍으로 만듭니다. 예를 들어, `sequence_length=5` 라면, `[0일차~4일차]` 데이터로 `5일차` 데이터를 예측하는 쌍을 만듭니다.
- 데이터를 훈련용과 테스트용으로 분리하고, PyTorch `Tensor`로 변환합니다.
**2. LSTM 모델 정의**
- `nn.Module`을 상속받는 `StockLSTM` 클래스를 만드세요.
- `__init__`: `nn.LSTM` 레이어와, LSTM의 출력을 받아 최종 예측값(1개)을 만드는 `nn.Linear` 레이어를 정의합니다.
- `forward`: 데이터가 LSTM과 Linear 레이어를 순서대로 통과하는 흐름을 정의합니다.
**3. 학습 및 평가**
- 모델, 손실 함수(`nn.MSELoss`), 옵티마이저를 초기화합니다.
- 학습 루프를 통해 모델을 훈련시킵니다.
- **(중요)** 평가 시에는 모델이 예측한 값과 실제 정답 값을 `scaler.inverse_transform`을 사용하여 원래의 주가 스케일로 되돌려 비교하고 시각화합니다.
**전체 코드 구조:**
```python
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
# 1. 데이터 준비
# 가상의 일일 주가 데이터 (20일치)
data = np.array([
100, 102, 105, 103, 108, 110, 112, 115, 118, 120,
122, 125, 123, 128, 130, 132, 135, 133, 138, 140
], dtype=np.float32).reshape(-1, 1)
# 데이터 정규화
scaler = MinMaxScaler(feature_range=(0, 1))
data_scaled = scaler.fit_transform(data)
# 입력 시퀀스, 타겟 쌍 생성
def create_sequences(data, seq_length):
xs, ys = [], []
for i in range(len(data) - seq_length):
x = data[i:i+seq_length]
y = data[i+seq_length]
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
SEQ_LENGTH = 5
X, y = create_sequences(data_scaled, SEQ_LENGTH)
# 훈련/테스트 데이터 분리 (뒤의 20%를 테스트용으로 사용)
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
# PyTorch 텐서로 변환
X_train_tensor = torch.from_numpy(X_train).float()
y_train_tensor = torch.from_numpy(y_train).float()
X_test_tensor = torch.from_numpy(X_test).float()
y_test_tensor = torch.from_numpy(y_test).float()
# 2. LSTM 모델 정의
class StockLSTM(nn.Module):
def __init__(self, input_size=1, hidden_size=50, output_size=1):
super(StockLSTM, self).__init__()
self.hidden_size = hidden_size
self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
h0 = torch.zeros(1, x.size(0), self.hidden_size).to(x.device)
c0 = torch.zeros(1, x.size(0), self.hidden_size).to(x.device)
out, _ = self.lstm(x, (h0, c0))
out = self.fc(out[:, -1, :]) # 마지막 타임스텝의 출력만 사용
return out
# 3. 학습
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = StockLSTM().to(device)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
num_epochs = 200
for epoch in range(num_epochs):
model.train()
# 전체 데이터를 한번에 학습 (배치로더는 생략)
outputs = model(X_train_tensor.to(device))
optimizer.zero_grad()
loss = criterion(outputs, y_train_tensor.to(device))
loss.backward()
optimizer.step()
if (epoch+1) % 20 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 4. 평가 및 시각화
model.eval()
with torch.no_grad():
test_predict = model(X_test_tensor.to(device))
# 원래 스케일로 되돌리기
test_predict_inv = scaler.inverse_transform(test_predict.cpu().numpy())
y_test_inv = scaler.inverse_transform(y_test)
# 시각화
plt.figure(figsize=(10, 6))
plt.plot(y_test_inv, label='Actual Price')
plt.plot(test_predict_inv, label='Predicted Price')
plt.title('Stock Price Prediction')
plt.xlabel('Time (days)')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.show()
```
이 프로젝트는 시계열 데이터 전처리의 핵심적인 과정을 이해하고, 회귀 문제에 딥러닝을 적용하는 실질적인 경험을 제공할 것입니다.
---
**➡️ 다음 시간: [Part 7.2: Transformer와 LLM의 핵심 원리](./part_7.2_transformer_and_llm_principles.md)**
\ No newline at end of file
......@@ -102,6 +102,34 @@ Transformer 아키텍처는 현대 LLM의 근간이 되었습니다. 대표적
- **Transformer의 구조**: RNN을 완전히 배제하고, 인코더-디코더 구조와 Self-Attention만으로 문장의 문맥을 파악하는 방법을 이해했습니다.
- **BERT와 GPT**: Transformer의 인코더와 디코더가 각각 어떻게 문맥 이해와 텍스트 생성이라는 두 가지 큰 흐름을 만들어냈는지 확인했습니다.
## ⚠️ What Could Go Wrong? (토론 주제)
Transformer는 현대 AI의 혁신을 이끌었지만, 그 강력함만큼 새로운 종류의 문제와 한계를 드러냈습니다. 다음 시나리오들에 대해 함께 토론해보세요.
1. **Self-Attention의 연산량 병목(Quadratic Bottleneck)**
- **상황**: 한 연구팀이 고해상도 이미지를 처리하기 위해, 각 픽셀을 하나의 토큰으로 간주하여 표준 Transformer 모델에 입력했습니다. 최고 사양의 GPU를 사용했음에도 불구하고, 시퀀스 길이가 조금만 길어져도 메모리 부족(Out of Memory) 오류가 발생하며 모델을 훈련시킬 수 없었습니다.
- **토론**:
- Self-Attention은 RNN의 순차 처리 병목을 해결했지만, 대신 어떤 새로운 병목을 만들었을까요? 왜 Self-Attention의 연산량과 메모리 사용량은 시퀀스 길이(n)에 따라 제곱(O(n²))으로 증가할까요?
- 이러한 한계점 때문에 표준 Transformer를 매우 긴 시퀀스(긴 문서, 고화질 이미지, 음성 등)에 적용하기 어려운 이유는 무엇이며, 이를 해결하기 위해 어떤 아이디어(예: Sparse Attention, Longformer, Linformer 등)들이 제시되었는지 조사하고 토론해보세요.
2. **위치 정보의 한계와 일반화 문제**
- **상황**: 최대 512 토큰 길이의 문장으로 학습된 Transformer 모델이 있습니다. 추론 시점에 600 토큰 길이의 문장을 입력하자, 모델의 성능이 급격히 저하되고 문법에 맞지 않는 문장을 생성하기 시작했습니다.
- **토론**:
- 왜 기본적인 Positional Encoding 방식은 학습 시 사용된 최대 길이를 넘어선 위치에 대해 잘 일반화하지 못할까요? 모델이 한 번도 본 적 없는 위치 값(예: 513번째 위치)을 마주했을 때 어떤 일이 벌어질까요?
- 이러한 문제를 해결하기 위한 대안적인 위치 인코딩 방식들에는 어떤 것들이 있을까요? (예: Relative Positional Encoding, RoPE 등)
3. **LLM의 환각(Hallucination) 현상**
- **상황**: GPT 기반의 역사 Q&A 봇에게 잘 알려지지 않은 역사적 인물에 대해 질문하자, 매우 유창하고 그럴듯하지만 완전히 허구인 사실을 자신감 있게 생성해냈습니다.
- **토론**:
- 이러한 '환각' 현상은 왜 발생할까요? 모델이 '거짓말'을 하는 것일까요, 아니면 다른 원인이 있을까요? 이 현상이 모델의 근본적인 학습 목표("정답을 말하기"가 아닌 "다음에 올 가장 그럴듯한 단어 예측하기")와 어떻게 연결되는지 토론해보세요.
- 이러한 환각 현상을 완화하기 위한 기술적인 전략들에는 어떤 것들이 있을까요? (예: Retrieval-Augmented Generation (RAG), Grounding, Fact-Checking 등)
4. **"이해" 모델 vs. "생성" 모델: 올바른 도구 선택하기**
- **상황**: 한 개발자가 문장의 긍정/부정을 판단하는 감성 분류(Classification) Task에 GPT(Decoder-only) 모델을 사용했습니다. 모델이 작동은 하지만, 비슷한 크기의 BERT(Encoder-only) 모델에 비해 추론 속도가 훨씬 느리고 정확도도 낮았습니다.
- **토론**:
- 왜 GPT와 같은 생성 모델이 BERT와 같은 이해 모델에 비해 분류 Task에서 비효율적일까요? Decoder 아키텍처의 단방향(Causal) 어텐션 마스크가 어떻게 전체 문맥을 파악하는 능력에 한계를 주는지, BERT의 양방향(Bidirectional) 어텐션과 비교하여 설명해보세요.
- 풀고자 하는 문제(Downstream Task)의 성격에 맞게 모델의 아키텍처와 사전학습 방식을 선택하는 것이 왜 중요한지 토론해보세요.
## 9. 더 깊이 알아보기 (Further Reading)
- [Attention Is All You Need (원문 논문)](https://arxiv.org/abs/1706.03762): 모든 것의 시작이 된 논문.
- [The Illustrated Transformer (영문 블로그)](http://jalammar.github.io/illustrated-transformer/): 그림과 함께 Transformer를 쉽게 설명한 최고의 자료 중 하나.
......@@ -109,4 +137,23 @@ Transformer 아키텍처는 현대 LLM의 근간이 되었습니다. 대표적
---
### 💡 Capstone Project 연계 미니 프로젝트: "From Scratch: Self-Attention 계산해보기"
이론으로 배운 Self-Attention 메커니즘이 실제 코드 레벨에서 어떻게 동작하는지 직접 구현해보며 Transformer의 심장을 파헤쳐봅니다. 이 과정을 통해 캡스톤 프로젝트에서 Transformer 기반 모델을 다룰 때 내부 동작에 대한 깊은 직관을 얻을 수 있습니다.
- **목표**: PyTorch를 사용하여 Self-Attention의 핵심 계산 과정을 단계별로 직접 구현합니다.
- **핵심 단계**:
1. **입력 준비**: 간단한 문장을 토큰화하고, 각 토큰을 표현하는 임베딩 벡터(여기서는 랜덤 텐서)를 생성합니다.
2. **Q, K, V 생성**: 각 임베딩 벡터로부터 Query, Key, Value 벡터를 만들어내는 가중치 행렬(`nn.Linear`)을 정의하고, 행렬 곱을 통해 Q, K, V 행렬을 계산합니다.
3. **Attention Score 계산**: `(Q @ K.transpose) / sqrt(d_k)` 공식을 그대로 코드로 옮겨 어텐션 스코어를 계산합니다.
4. **Attention Distribution**: 스코어에 `Softmax` 함수를 적용하여 특정 단어가 다른 모든 단어에 얼마나 '집중'할지를 나타내는 확률 분포를 구합니다.
5. **최종 결과**: 계산된 어텐션 가중치를 Value 행렬에 곱하여 문맥이 풍부하게 반영된 최종 결과 벡터를 얻습니다.
- **기대 효과**:
- `Q`, `K`, `V`의 역할과 텐서의 차원 변화를 명확하게 이해합니다.
- Self-Attention의 복잡한 수식이 실제로는 몇 줄의 행렬 연산 코드로 구현됨을 확인하며 자신감을 얻습니다.
- Hugging Face 라이브러리의 Transformer 모델 내부를 상상할 수 있는 능력을 기릅니다.
- **구현 가이드**: [live_coding_transformer_block.md](./live_coding_transformer_block.md) 파일의 단계별 가이드를 참고하여 직접 코드를 작성해보세요.
---
**➡️ 다음 시간: [Part 7.3: LangChain으로 LLM 애플리케이션 개발 맛보기](./part_7.3_llm_application_development_with_langchain.md)**
\ No newline at end of file
......@@ -133,6 +133,25 @@ print(response["result"])
---
### 💡 Capstone Project 연계 미니 프로젝트: "회사 내부 문서 Q&A 봇 만들기 (실전 RAG)"
앞서 배운 RAG 파이프라인을 활용하여, 단순 텍스트가 아닌 실제 파일(`.md`)을 기반으로 질문에 답변하는 실용적인 Q&A 봇을 구축합니다. 이 과제는 최종 캡스톤 프로젝트에서 특정 도메인(예: 법률, 의료, 금융)의 문서를 다루는 LLM 애플리케이션을 구현할 때 훌륭한 기반이 됩니다.
- **목표**: LangChain의 `DocumentLoader`를 사용하여 외부 마크다운 파일을 로드하고, 해당 문서의 내용에 대해서만 정확하게 답변하는 RAG 시스템을 완성합니다.
- **과제**:
1. **샘플 문서 확인**: `../../source_code/data/rag_sample_company_rules.md` 파일의 내용을 확인합니다. 이 문서는 가상의 회사 보안 규정에 대한 내용을 담고 있습니다.
2. **Document Loader 사용**: 기존의 `my_document` 변수 대신, LangChain의 `TextLoader`를 사용하여 위 마크다운 파일을 로드하도록 코드를 수정하세요.
3. **성능 테스트**:
- **문서 기반 질문**: "업무용 노트북에서 개인 이메일 사용이 가능한가요?" 와 같이 문서에 명시된 내용에 대해 질문하고, RAG 시스템이 정확한 답변을 생성하는지 확인합니다.
- **문서 외 질문 (Grounding 테스트)**: "회사의 연봉 정책에 대해 알려줘" 와 같이 문서에 없는 내용에 대해 질문했을 때, 모델이 "문서에서 관련 정보를 찾을 수 없습니다" 또는 "알 수 없습니다" 와 같이 솔직하게 답변하는지(Grounding) 확인합니다.
- **기대 효과**:
- `DocumentLoader`의 역할을 이해하고, 다양한 포맷의 문서를 LLM 애플리케이션에 통합하는 방법을 학습합니다.
- RAG 시스템의 핵심 역량인 'Grounding'(제공된 정보에 기반하여 답변하는 능력)의 중요성을 체감합니다.
- 환각(Hallucination) 현상을 줄이고 LLM의 신뢰도를 높이는 실용적인 방법을 터득합니다.
- **구현 가이드**: `../../source_code/part_7_5_llm_application_development.py` 파일의 주석 가이드를 따라 코드를 완성해보세요.
---
## 6. 되짚어보기 (Summary)
이번 시간에는 LLM의 한계를 넘어, 외부 세계와 소통하는 AI를 만드는 강력한 기술인 RAG를 배웠습니다.
......@@ -150,4 +169,29 @@ print(response["result"])
---
### ⚠️ What Could Go Wrong? (토론 주제)
LangChain은 LLM 기반 애플리케이션 개발을 가속화하는 강력한 도구이지만, 실제 프로덕션 환경에 적용할 때는 여러 잠재적 위험과 한계를 고려해야 합니다.
1. **지나친 추상화의 함정 (The Pitfall of Over-abstraction)**
* LangChain의 고수준 추상화가 내부 동작을 이해하기 어렵게 만들어, 특정 요구사항에 맞게 커스터마이징하거나 디버깅할 때 오히려 더 복잡해지는 상황이 발생할 수 있습니다. "간단한 일은 더 간단하게, 복잡한 일은 불가능하게" 만드는 경우가 될 수 있을까요?
* 특정 컴포넌트(예: Vectorstore)를 교체하거나 세부 동작을 수정하려 할 때, LangChain의 추상화 계층 때문에 예상보다 많은 노력이 드는 경우를 겪어본 적이 있나요?
2. **프롬프트 주입 및 보안 취약점 (Prompt Injection & Security Vulnerabilities)**
* RAG(Retrieval-Augmented Generation) 파이프라인에서 사용자가 악의적으로 조작한 문서를 데이터 소스에 포함시켜, 시스템 프롬프트를 탈취하거나 의도치 않은 결과를 유도하는 '프롬프트 주입' 공격에 어떻게 대비해야 할까요?
* LangChain 에이전트가 파일 시스템 접근이나 API 호출과 같은 위험한 도구(Tool)를 사용할 때, 권한 상승이나 데이터 유출과 같은 보안 사고를 방지하기 위한 안전장치는 무엇이 있을까요?
3. **에이전트의 예측 불가능성과 제어의 어려움 (Unpredictability and Controllability of Agents)**
* ReAct와 같은 복잡한 에이전트가 잘못된 추론 경로에 빠져 무한 루프를 돌거나, 의도치 않게 비싼 유료 API를 계속 호출하여 막대한 비용을 발생시키는 상황을 어떻게 방지할 수 있을까요?
* "목표를 줄 테니 알아서 해줘"라는 에이전트의 방식이, 실제 비즈니스 로직처럼 엄격한 제어가 필요한 경우에 적합할까요? 에이전트의 자율성과 시스템의 안정성 사이의 균형점은 어디일까요?
4. **디버깅과 투명성 부족 (Lack of Debuggability and Transparency)**
* 여러 체인과 에이전트, 도구가 복잡하게 얽혀 있을 때, 최종 결과가 어떤 중간 단계를 거쳐 나왔는지 추적하기 어려운 경험을 한 적이 있나요? (e.g., "왜 이 답변이 나왔지?")
* LangSmith와 같은 추적 도구가 이러한 문제를 어느 정도 해결해주지만, 근본적인 복잡성 자체는 여전히 높은 진입 장벽이 될 수 있지 않을까요?
5. **프레임워크 종속성 문제 (Framework Lock-in)**
* 애플리케이션의 핵심 로직을 LangChain의 특정 구조와 기능에 깊이 의존하여 개발했을 때, 나중에 다른 프레임워크나 순수 API 호출 기반의 코드로 전환하기 어려워지는 '기술 부채'가 될 수 있습니다. 이러한 종속성을 최소화하며 LangChain을 현명하게 활용하는 전략은 무엇일까요?
---
**➡️ 다음 시간: [Part 7.4: 그래프 신경망 (GNN)](./part_7.4_graph_neural_networks.md)**
\ No newline at end of file
......@@ -64,3 +64,59 @@ PyTorch Geometric 라이브러리를 사용하여 소셜 네트워크의 사용
---
**➡️ 다음 시간: [Part 7.5: 강화학습 (Reinforcement Learning)](./part_7.5_reinforcement_learning.md)**
### 참고 자료
* [A Gentle Introduction to Graph Neural Networks](https://distill.pub/2021/gnn-intro/): GNN에 대한 시각적이고 직관적인 설명이 담긴 distill.pub 아티클
* [PyTorch Geometric 라이브러리](https://pytorch-geometric.readthedocs.io/en/latest/): PyTorch 기반의 GNN 라이브러리
---
### ⚠️ What Could Go Wrong? (토론 주제)
GNN은 관계형 데이터를 다루는 강력한 패러다임이지만, 실제 적용 시에는 고유한 기술적 난제들을 마주하게 됩니다.
1. **과잉 평탄화 문제 (Over-smoothing Problem)**
* GNN 레이어를 깊게 쌓았을 때, 여러 홉 떨어진 노드들의 정보가 섞이면서 결국 모든 노드 임베딩이 서로 유사해져 버리는 '과잉 평탄화' 현상이 발생합니다. 이 때문에 깊은 GNN 모델의 성능이 얕은 모델보다 오히려 떨어질 수 있는데, 왜 이런 현상이 발생할까요?
* 이를 해결하기 위해 제안된 방법들(예: Jumping Knowledge, GCNII, Skip-connection)은 어떤 원리로 이 문제를 완화하며, 각각 어떤 장단점을 가질까요?
2. **이웃 폭발 및 확장성 문제 (Neighbor Explosion & Scalability Issues)**
* 하나의 노드 임베딩을 계산하기 위해 이웃 노드들을 재귀적으로 참조하다 보면, 몇 홉만 거쳐도 관련 노드의 수가 기하급수적으로 증가하여 '이웃 폭발'이 발생합니다. 이는 대규모 그래프에서 메모리 부족(Out-of-Memory)과 연산량 폭증의 원인이 됩니다.
* GraphSAGE에서 제안한 '이웃 샘플링(Neighbor Sampling)' 방식은 이 문제를 어떻게 해결하나요? 샘플링이 가져오는 성능과 연산량 간의 트레이드오프는 무엇일까요?
3. **동적 그래프 처리의 어려움 (Difficulty with Dynamic Graphs)**
* 실세계의 많은 그래프(소셜 네트워크, 금융 거래망)는 시간이 지남에 따라 노드와 엣지가 끊임없이 변하는 '동적 그래프'입니다. 새로운 노드가 추가될 때마다 전체 모델을 다시 학습시켜야 할까요?
* 시간의 흐름에 따른 변화를 GNN 모델이 학습하게 하려면 어떤 접근법(예: Temporal GNNs)이 필요하며, 기존 GNN과 비교하여 어떤 점이 더 복잡할까요?
4. **비관계형 데이터에 대한 부적절한 적용 (Inappropriate Application to Non-relational Data)**
* 모든 데이터를 그래프 형태로 표현할 수는 있지만, 그것이 항상 유용한 것은 아닙니다. 노드 간의 '관계'가 명확하지 않거나 의미 없는 데이터를 억지로 그래프로 구성하여 GNN을 적용하면 어떤 문제가 발생할 수 있을까요?
* GNN이 다른 모델(예: CNN, RNN, Transformer)에 비해 확실한 강점을 가지는 데이터의 특징은 무엇이라고 생각하시나요?
5. **그래프 구조 자체의 편향 (Bias in Graph Structure)**
* 소셜 네트워크에서 특정 인구 집단이 다른 집단보다 더 촘촘하게 연결된 '에코 체임버'나 '필터 버블'이 존재할 수 있습니다. 이런 편향된 구조를 학습한 GNN 모델이 소수 집단에 대해 불공정한 예측을 내릴 위험은 없을까요? 이러한 구조적 편향을 어떻게 탐지하고 완화할 수 있을까요?
---
## 캡스톤 프로젝트 연계 미니 프로젝트: 소셜 네트워크 기반 친구 추천 시스템
이 챕터에서 배운 GNN의 개념을 활용하여, 15장 캡스톤 프로젝트의 '추천 시스템' 아이디어에 적용해볼 수 있는 미니 프로젝트를 진행합니다. 사용자의 연결 관계(그래프)를 기반으로 새로운 친구를 추천하는 모델을 직접 만들어보면서, GNN의 실용성을 체감하는 것을 목표로 합니다.
### 프로젝트 목표
- `PyTorch Geometric`을 사용하여 소셜 네트워크 데이터를 로드하고 시각화합니다.
- 간단한 GCN(Graph Convolutional Network) 모델을 구축하여, 그래프의 '링크 예측(Link Prediction)' 문제를 해결합니다.
- 특정 사용자가 주어졌을 때, 연결될 가능성이 가장 높은 다른 사용자(새로운 친구)를 추천하는 API의 프로토타입을 구상합니다.
### 개발 과정
1. **데이터셋 선정**: `PyTorch Geometric`에 내장된 `Cora` 또는 `CiteSeer`와 같은 인용 네트워크 데이터셋을 소셜 네트워크라고 가정하고 사용합니다. 각 논문은 '사용자', 인용 관계는 '친구 관계'로 간주합니다.
2. **모델링**:
- 노드(사용자)의 특징을 입력으로 받아, GCN 레이어를 통과시켜 임베딩을 생성합니다.
- 두 노드 임베딩의 내적(dot product)을 계산하여, 두 노드 사이에 링크(친구 관계)가 존재할 확률을 예측하는 모델을 만듭니다.
3. **학습 및 평가**:
- 기존의 링크 중 일부를 일부러 숨기고(train/test split), 모델이 이 숨겨진 링크를 얼마나 잘 예측하는지 평가합니다. (AUC, Accuracy 등)
4. **결과 해석 및 활용**:
- 학습된 모델을 사용하여, 현재 연결되지 않은 노드 쌍 중에서 연결 확률이 가장 높은 쌍을 찾습니다.
- 이를 "사용자 A에게 사용자 B, C, D를 추천합니다"와 같은 기능으로 연결하여, 캡스톤 프로젝트에서 만들 최종 서비스의 그림을 그려봅니다.
### 캡스톤 프로젝트 연계 방안
- **추천 시스템 고도화**: 실제 캡스톤 프로젝트에서는 단순 친구 관계를 넘어, '사용자-상품' 관계, '영화-배우-감독' 관계 등 더 복잡한 그래프를 구축하여 GNN 기반 추천 모델을 만들 수 있습니다.
- **FastAPI 연동**: 이 미니 프로젝트에서 개발한 모델을 `part_8_model_serving_with_fastapi.md`에서 배울 FastAPI를 사용해 API로 서빙하는 기능을 구현해볼 수 있습니다. 예를 들어, `GET /users/{user_id}/recommendations` 와 같은 엔드포인트를 설계할 수 있습니다.
\ No newline at end of file
......@@ -69,3 +69,63 @@
---
**➡️ 다음 시간: [Part 8: FastAPI를 이용한 모델 서빙](../08_model_serving_with_fastapi/part_8_model_serving_with_fastapi.md)**
### 참고 자료
* [OpenAI Spinning Up in Deep RL](https://spinningup.openai.com/en/latest/): 심층 강화학습에 대한 체계적인 가이드
* [Hugging Face Deep RL Course](https://huggingface.co/learn/deep-rl-course/unit0/introduction): 허깅페이스에서 제공하는 실습 중심의 강화학습 코스
---
### ⚠️ What Could Go Wrong? (토론 주제)
강화학습은 에이전트가 스스로 최적의 전략을 학습한다는 점에서 매력적이지만, 실제 문제에 적용하기까지는 수많은 난관을 극복해야 합니다.
1. **탐험과 활용의 딜레마 (Exploration vs. Exploitation Dilemma)**
* 에이전트가 현재까지 알아낸 최선의 방법(활용)만 고집하면 더 좋은 해결책을 찾을 기회를 놓치고, 새로운 시도(탐험)에만 집중하면 보상을 극대화하지 못합니다. 이 근본적인 딜레마를 해결하기 위한 전략(e.g., Epsilon-Greedy, UCB)들은 각각 어떤 상황에서 유리하고 불리할까요?
* 실제 비즈니스 문제(예: 추천 시스템)에서, 사용자의 만족도를 떨어뜨릴 수 있는 '탐험'을 어느 수준까지 허용해야 할까요? 탐험의 비용과 잠재적 이득을 어떻게 정량화할 수 있을까요?
2. **희소한 보상 문제 (Sparse Reward Problem)**
* 체스나 바둑처럼 게임이 끝날 때만 승패라는 보상이 주어지는 경우, 수많은 중간 행동들 중 어떤 것이 승리에 기여했는지 알기 어렵습니다. 이러한 '희소한 보상' 환경에서 에이전트를 효과적으로 학습시키기 어려운 이유는 무엇일까요?
* 이를 해결하기 위해 인위적으로 중간 보상을 설계('Reward Shaping')해주는 것은 어떤 부작용을 낳을 수 있을까요? (예: 에이전트가 보상의 허점을 파고들어 의도치 않은 행동을 학습하는 경우)
3. **시뮬레이션과 현실의 차이 (Sim2Real Gap)**
* 시뮬레이션 환경에서 완벽하게 작동하도록 학습된 로봇 팔이, 실제 공장 라인에서는 미묘한 마찰력이나 조명 변화 때문에 처참하게 실패하는 'Sim2Real Gap'은 왜 발생할까요?
* 이 간극을 줄이기 위해 시뮬레이션 환경을 최대한 현실과 비슷하게 만들려는 노력과, 오히려 환경을 무작위로 계속 바꾸어 에이전트의 강인함(Robustness)을 키우려는 'Domain Randomization' 전략 중 어느 것이 더 효과적일까요?
4. **안전성과 예측 불가능성 (Safety and Unpredictability)**
* 자율주행차가 보상을 최대로 하기 위해 위험한 추월을 학습하거나, 청소 로봇이 특정 구역을 피하도록 음(-)의 보상을 주었더니 아예 움직이지 않는 방법을 터득하는 등, RL 에이전트는 명시적으로 금지하지 않은 모든 행동을 할 수 있습니다.
* '절대로 해서는 안 되는 행동'을 어떻게 정의하고 학습시킬 수 있을까요? 시스템의 안전을 보장하기 위한 강화학습 설계는 어떻게 접근해야 할까요?
5. **샘플 비효율성 (Sample Inefficiency)**
* 많은 강화학습 알고리즘, 특히 Model-Free 방식은 최적 정책을 배우기 위해 수백만, 수천만 번의 시행착오를 거쳐야 합니다. 현실 세계에서 이 정도의 데이터를 수집하는 것이 불가능한 경우가 많습니다. (예: 로봇이 수백만 번 넘어져야 걷는 법을 배울 수는 없음)
* '환경 모델'을 학습하여 상호작용을 시뮬레이션하는 Model-Based RL은 이 문제를 어떻게 완화하며, 그 접근법의 근본적인 한계(예: 모델이 부정확할 경우의 성능 저하)는 무엇일까요?
---
## 캡스톤 프로젝트 연계 미니 프로젝트: 게임 AI 에이전트 훈련시키기
강화학습의 핵심은 '최적의 의사결정'을 찾는 것입니다. 15장 캡스톤 프로젝트에서 자원 관리, 추천 순서 최적화, 자율 에이전트 등 동적인 시스템을 구상하고 있다면, 강화학습은 강력한 무기가 될 수 있습니다. 이번 미니 프로젝트에서는 `Gymnasium` 라이브러리를 사용해 간단한 게임 환경 속에서 스스로 학습하고 성장하는 AI 에이전트를 만들며 강화학습의 원리를 체득합니다.
### 프로젝트 목표
- `Gymnasium` 라이브러리를 사용하여 'LunarLander-v2' 환경을 설정하고 이해합니다.
- DQN(Deep Q-Network) 알고리즘의 기본 아이디어를 이해하고, `stable-baselines3`와 같은 라이브러리를 사용해 에이전트를 학습시킵니다.
- 학습된 에이전트가 게임을 플레이하는 과정을 시각화하고, 학습 전후의 성능을 '누적 보상'으로 비교 분석합니다.
### 개발 과정
1. **환경 설정**: `pip install gymnasium[box2d] stable-baselines3[extra]` 명령어로 필요한 라이브러리를 설치합니다.
2. **에이전트 학습**:
- `stable-baselines3` 라이브러리의 `DQN` 모델을 불러옵니다.
- 'LunarLander-v2' 환경을 생성하고, 모델에 환경을 지정하여 학습을 시작합니다. (`model.learn(total_timesteps=100000)`)
- 학습된 모델을 저장합니다.
3. **성능 평가 및 시각화**:
- 학습된 모델을 불러와, 렌더링 모드로 환경을 실행하여 에이전트가 어떻게 우주선을 착륙시키는지 직접 관찰합니다.
- 학습하지 않은 무작위 에이전트의 평균 누적 보상과, 학습된 에이전트의 평균 누적 보상을 비교하여 성능 향상을 수치적으로 확인합니다.
4. **결과 해석 및 활용**:
- 에이전트는 어떤 전략(Policy)을 학습했을까요? (예: 특정 상황에서 어떤 엔진을 분사하는가)
- 학습의 '보상'을 어떻게 설계하는지에 따라 에이전트의 행동이 어떻게 달라질지 토론해봅니다.
### 캡스톤 프로젝트 연계 방안
- **커스텀 환경 제작**: 캡스톤 프로젝트의 주제에 맞춰 `Gymnasium``Env` 클래스를 상속받아 '나만의 강화학습 환경'을 직접 만들어볼 수 있습니다. 예를 들어, '웹사이트 광고 배치 최적화' 환경을 만들고, '광고 클릭 수'를 보상으로 설정하여 최적의 광고 배치 전략을 학습하는 에이전트를 개발할 수 있습니다.
- **금융 트레이딩 봇**: 주가 데이터를 상태로, '매수/매도/보유'를 행동으로, '수익률'을 보상으로 하는 환경을 설계하여, 시장 상황에 맞춰 최적의 투자를 수행하는 트레이딩 봇을 강화학습으로 개발할 수 있습니다.
- **자율 에이전트**: `LangChain`의 'Agent' 개념과 강화학습을 결합하여, 특정 목표(예: '최신 AI 논문 3개 요약하기')를 달성하기 위해 스스로 웹 검색, 코드 실행 등의 도구를 선택하고 사용하는 고차원적인 자율 에이전트를 구상해볼 수 있습니다.
\ No newline at end of file
......@@ -126,196 +126,346 @@ print(model)
> - 예측 B (못 맞춤): `[0.1, 0.8, 0.1]` -> 높은 손실(Loss)
>
> ### 5-2. 핵심 프로세스: 학습 루프(Training Loop)
딥러닝 모델 학습은 '에포크(Epoch)'라는 단위로 반복됩니다. 1 에포크는 전체 훈련 데이터를 한 번 모두 사용하는 것을 의미하며, 학습은 다음 4단계로 이루어집니다.
> 딥러닝 모델 학습은 '에포크(Epoch)'라는 단위로 반복됩니다. 1 에포크는 전체 훈련 데이터를 한 번 모두 사용하는 것을 의미하며, 학습은 다음 4단계로 이루어집니다.
>
> 1. **`model(inputs)`**: 모델에 입력을 넣어 예측값(`outputs`)을 계산합니다 (**순전파**).
> 2. **`loss = criterion(outputs, labels)`**: 예측값과 실제 정답을 비교하여 오차(`loss`)를 계산합니다.
> 3. **`loss.backward()`**: 계산된 오차를 기반으로, 각 파라미터가 오차에 얼마나 기여했는지 미분값을 계산합니다 (**역전파**).
> 4. **`optimizer.step()`**: 계산된 미분값을 바탕으로 모델의 파라미터를 업데이트하여 오차를 줄이는 방향으로 나아갑니다.
>
> > **💡 순전파와 역전파**
> > - **순전파 (Forward)**: 내가 만든 레시피로 요리를 해서(모델 예측) 손님에게 내놓는 과정.
> > - **역전파 (Backward)**: 손님의 피드백("너무 짜요!")을 듣고, 소금을 얼마나 넣었는지, 간장을 얼마나 넣었는지 원인을 거슬러 올라가 분석하는 과정. `optimizer.step()`은 이 분석을 바탕으로 다음 요리에서는 소금을 덜 넣는 행동입니다.
>
> ### 5-3. 학습, 검증, 그리고 추론 (Training, Validation, and Inference)
>
> 딥러닝 모델의 생명주기는 크게 '학습', '검증', '추론' 세 단계로 나뉩니다.
>
> - **학습 (Training)**: `train_loader` 와 같이 훈련용 데이터를 사용하여 모델의 파라미터(가중치)를 최적화하는 과정입니다. 위에서 설명한 **학습 루프**가 바로 이 단계에 해당합니다. 모델은 정답을 보면서 오차를 줄여나갑니다.
> - **검증 (Validation)**: 학습 과정 중에 모델이 얼마나 잘 학습되고 있는지, 과적합(Overfitting)은 발생하지 않는지 확인하기 위한 단계입니다. 학습에 사용되지 않은 `validation_loader` 데이터로 모델의 성능을 주기적으로 평가합니다. 이 평가 결과를 바탕으로 학습률(Learning Rate)을 조절하거나 학습을 조기 종료하는 등의 결정을 내립니다.
> - **추론 (Inference)**: 학습이 완료된 모델을 사용하여 새로운 데이터에 대한 예측을 수행하는 단계입니다. 이 단계에서는 더 이상 모델의 가중치를 업데이트하지 않으므로, 역전파나 옵티마이저가 필요 없습니다. `torch.no_grad()` 컨텍스트 안에서 실행하여 메모리 사용량을 줄이고 계산 속도를 높이는 것이 일반적입니다. 서비스 배포 환경에서 모델이 사용되는 단계가 바로 추론입니다.
>
> ---
>
> ## 6. 직접 해보기 (Hands-on Lab): MNIST 손글씨 분류
> > **🎯 5일차 목표:** PyTorch를 사용하여 딥러닝의 "Hello, World!"인 MNIST 손글씨 분류기를 직접 만듭니다.
>
> ### 문제:
> `torchvision`을 사용하여 MNIST 데이터셋을 불러오고, 간단한 CNN(합성곱 신경망) 모델을 구축하여 손글씨 숫자를 분류하는 전체 코드를 완성하세요.
>
> ```python
> import torch
> import torch.nn as nn
> import torchvision
> import torchvision.transforms as transforms
> from torch.utils.data import DataLoader
>
> # 1. 데이터셋 및 로더 준비
> transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
> train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
> test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transform)
> train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
> test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
>
> # 2. CNN 모델 정의
> class ConvNet(nn.Module):
> def __init__(self):
> super(ConvNet, self).__init__()
> self.layer1 = nn.Sequential(
> nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),
> nn.ReLU(),
> nn.MaxPool2d(kernel_size=2, stride=2))
> self.layer2 = nn.Sequential(
> nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
> nn.ReLU(),
> nn.MaxPool2d(kernel_size=2, stride=2))
> self.fc = nn.Linear(7*7*64, 10)
>
> def forward(self, x):
> out = self.layer1(x)
> out = self.layer2(out)
> out = out.reshape(out.size(0), -1) # Flatten
> out = self.fc(out)
> return out
>
> # 3. 모델, 손실함수, 옵티마이저 설정
> device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
> model = ConvNet().to(device)
> criterion = nn.CrossEntropyLoss()
> optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
>
> # 4. 학습 루프 구현
> # for epoch ...
> # for i, (images, labels) in enumerate(train_loader):
> # - images, labels를 device로 이동
> # - 순전파, 손실 계산, 역전파, 파라미터 업데이트 코드 작성
>
> ### 6-1. 심화: 주요 CNN 아키텍처의 발전사
> 간단한 CNN 모델을 직접 만들어본 지금, 딥러닝 역사에 한 획을 그은 주요 CNN 아키텍처들이 어떻게 발전해왔는지 살펴보는 것은 매우 의미 있는 일입니다. 각 모델이 어떤 문제를 해결하려 했고, 어떤 아이디어를 제시했는지 이해하면 더 깊이 있는 모델을 설계하는 데 도움이 될 것입니다.
>
> | 모델명 | 발표 연도 | 주요 특징 및 기여 | 주요 사용처 및 선택 가이드 |
> |---|---|---|---|
> | **LeNet-5** | 1998 | LeCun 교수가 제안. 최초의 성공적인 CNN. `Conv`-`Pool`-`Conv`-`Pool`-`FC` 구조의 기반을 마련. | **학습용/기본 CNN 구조 이해**: CNN의 기본 동작 원리를 학습하고 간단한 분류 문제를 해결할 때 좋습니다. 현대적인 문제에 직접 사용하기에는 성능이 부족합니다. |
> | **AlexNet** | 2012 | 이미지넷(ILSVRC) 챌린지에서 압도적 성능으로 우승하며 **딥러닝의 부흥**을 이끌었다. **ReLU**, **Dropout**, **GPU 활용** 등 현재까지도 널리 쓰이는 기법들을 대거 도입했다. | **성능 개선의 출발점**: LeNet보다 복잡한 문제에 적용 가능. 딥러닝 모델의 성능을 어떻게 개선하는지에 대한 아이디어(활성화 함수, 규제)를 얻고 싶을 때 분석해볼 만합니다. |
> | **VGGNet** | 2014 | **3x3의 작은 커널**을 여러 겹 깊게 쌓는 단순하지만 효과적인 구조를 제시. 깊이(Depth)가 성능에 미치는 영향을 증명했다. | **전이 학습(Transfer Learning)의 베이스라인**: 구조가 단순하고 특징 추출 능력이 우수하여, 사전 훈련된 모델을 다른 이미지 문제에 적용하는 전이 학습의 베이스라인으로 널리 사용됩니다. |
> | **GoogLeNet** | 2014 | **인셉션 모듈(Inception Module)**을 도입하여, 여러 크기의 커널을 병렬로 사용함으로써 연산 효율성과 성능을 동시에 잡았다. **파라미터 수를 줄이는 데** 집중했다. | **연산량/메모리가 제한된 환경**: 모바일 기기나 임베디드 시스템처럼 계산 비용에 민감한 환경에서 높은 성능을 내야 할 때 우선적으로 고려됩니다. |
> | **ResNet** | 2015 | **잔차 학습(Residual Learning)**과 **스킵 연결(Skip Connection)**이라는 혁신적인 아이디어로 100층이 넘는 **매우 깊은 신경망**의 학습을 가능하게 했다. **Vanishing Gradient 문제를 해결**하며 딥러닝의 한계를 돌파했다. | **대부분의 컴퓨터 비전 문제의 표준**: 현재까지도 이미지 분류, 객체 탐지, 세그멘테이션 등 다양한 컴퓨터 비전 문제에서 매우 강력한 '기본/표준' 모델로 사용됩니다. |
>
> #### 1. LeNet-5: CNN의 서막
> - Yann LeCun 교수가 제안한 모델로, 현대적인 CNN 구조(합성곱 층과 풀링 층의 반복, 마지막에 완전 연결 계층)의 청사진을 제시했습니다. 당시에는 손글씨 숫자 인식과 같은 제한된 문제에서 큰 성공을 거두었습니다.
>
> #### 2. AlexNet: 딥러닝의 폭발
> - 2012년 이미지넷 분류 챌린지에서 전통적인 컴퓨터 비전 기법들을 압도적인 성능 차이로 이기고 우승하며, AI 커뮤니티에 엄청난 충격을 주었습니다. 딥러닝이 학계를 넘어 산업계의 주류 기술로 발돋움하는 결정적인 계기가 되었습니다.
> - **주요 기여**:
> - **ReLU 활성화 함수**: Sigmoid/Tanh의 Vanishing Gradient 문제를 해결하고 학습 속도를 크게 향상시켰습니다.
> - **Dropout**: 학습 시 일부 뉴런을 무작위로 비활성화하여 과적합을 방지하는 효과적인 규제(Regularization) 기법을 제시했습니다.
> - **GPU 활용**: 2개의 GPU를 병렬로 사용하여 대규모 연산을 효율적으로 처리하며 딥러닝의 대중화를 이끌었습니다.
>
> #### 3. VGGNet: 깊고 단순함의 미학
> - "더 깊게 쌓으면 성능이 좋아질까?"라는 단순한 질문에서 출발했습니다.
> - **주요 기여**:
> - **3x3 합성곱 필터**: 큰 크기(예: 5x5, 7x7)의 필터를 사용하는 대신, 3x3의 작은 필터를 여러 개 연속으로 사용하여 동일한 Receptive Field를 가지면서도 파라미터 수를 줄이고 비선형성을 증가시켰습니다.
> - **균일한 구조**: VGG-16, VGG-19 등 깊이만 다른 균일한 구조를 제시하여 모델 설계의 단순함을 보여주었습니다.
>
> #### 4. GoogLeNet (Inception): 효율적인 깊이의 추구
> - VGGNet처럼 무작정 깊게 쌓는 것이 아니라, "어떻게 하면 연산 자원을 효율적으로 사용하면서 성능을 높일 수 있을까?"를 고민했습니다.
> - **주요 기여**:
> - **인셉션 모듈(Inception Module)**: 1x1, 3x3, 5x5 합성곱과 3x3 풀링을 하나의 블록 안에서 병렬로 처리하고 그 결과를 합칩니다. 이를 통해 모델이 다양한 스케일의 특징을 동시에 학습할 수 있게 됩니다.
> - **1x1 Convolution 활용**: 채널(Channel) 수를 줄여(BottleNeck) 전체 연산량을 획기적으로 감소시켜 성능은 유지하거나 향상시켰습니다.
>
> #### 5. ResNet: 깊이의 한계를 넘어서
> - 네트워크가 깊어질수록 오히려 학습이 더 어려워지고 성능이 저하되는 **퇴화(Degradation) 문제**가 발생했습니다. 이는 Vanishing/Exploding Gradient와는 다른 현상으로, 깊은 망이 얕은 망보다 학습하기 더 어렵다는 문제였습니다.
> - **주요 기여**:
> - **잔차 학습(Residual Learning)**: 층이 입력값 `x`를 `H(x)`로 직접 변환하도록 학습하는 대신, **변화량(Residual), 즉 `F(x) = H(x) - x`**를 학습하도록 구조를 바꿨습니다.
> - **스킵 연결(Skip Connection)**: 입력 `x`를 몇 개의 층을 건너뛰어 출력에 그대로 더해주는 `H(x) = F(x) + x` 구조입니다. 이 간단한 '지름길'을 통해 그래디언트가 깊은 층까지 소실되지 않고 잘 흘러갈 수 있게 되어, 152층이라는 전례 없는 깊이의 네트워크 학습을 성공시켰습니다. ResNet의 등장은 이후 수많은 딥러닝 아키텍처에 지대한 영향을 미쳤습니다.
>
> ### 6-2. 실제 기업 적용 사례 (Real-world Enterprise Use Cases)
>
> 앞서 살펴본 CNN 아키텍처들의 발전은 오늘날 다양한 산업 분야에서 AI 혁신을 이끄는 밑거름이 되었습니다. 딥러닝 기술이 실제 비즈니스 문제를 어떻게 해결하고 있는지 최신 사례를 통해 살펴보겠습니다.
1. **`model(inputs)`**: 모델에 입력을 넣어 예측값(`outputs`)을 계산합니다 (**순전파**).
2. **`loss = criterion(outputs, labels)`**: 예측값과 실제 정답을 비교하여 오차(`loss`)를 계산합니다.
3. **`loss.backward()`**: 계산된 오차를 기반으로, 각 파라미터가 오차에 얼마나 기여했는지 미분값을 계산합니다 (**역전파**).
4. **`optimizer.step()`**: 계산된 미분값을 바탕으로 모델의 파라미터를 업데이트하여 오차를 줄이는 방향으로 나아갑니다.
---
> **💡 순전파와 역전파**
> - **순전파 (Forward)**: 내가 만든 레시피로 요리를 해서(모델 예측) 손님에게 내놓는 과정.
> - **역전파 (Backward)**: 손님의 피드백("너무 짜요!")을 듣고, 소금을 얼마나 많이 넣었는지, 간장을 얼마나 넣었는지 원인을 거슬러 올라가 분석하는 과정. `optimizer.step()`은 이 분석을 바탕으로 다음 요리에서는 소금을 덜 넣는 행동입니다.
## 🚀 캡스톤 미니 프로젝트: 와인 품질 예측 (PyTorch MLP)
### 5-3. 학습, 검증, 그리고 추론 (Training, Validation, and Inference)
이번 장의 MNIST 실습을 통해 이미지 데이터에 대한 딥러닝(CNN)의 맛을 보았습니다. 이번 미니 프로젝트에서는 우리가 더 익숙한 **표(tabular) 형식의 데이터**에 딥러닝을 적용해 봅니다. 이 과정을 통해 딥러닝의 기본 원리(모델 정의, 학습 루프)가 데이터의 종류와 상관없이 동일하게 적용된다는 중요한 사실을 체득하게 됩니다.
딥러닝 모델의 생명주기는 크게 '학습', '검증', '추론' 세 단계로 나뉩니다.
Scikit-learn에 내장된 '와인 품질' 데이터셋을 사용하여, 와인의 화학적 특성으로 품질 등급('좋음'/'나쁨')을 예측하는 다층 퍼셉트론(MLP) 모델을 PyTorch로 직접 구축, 훈련, 평가하는 전체 과정을 경험합니다.
- **학습 (Training)**: `train_loader` 와 같이 훈련용 데이터를 사용하여 모델의 파라미터(가중치)를 최적화하는 과정입니다. 위에서 설명한 **학습 루프**가 바로 이 단계에 해당합니다. 모델은 정답을 보면서 오차를 줄여나갑니다.
- **검증 (Validation)**: 학습 과정 중에 모델이 얼마나 잘 학습되고 있는지, 과적합(Overfitting)은 발생하지 않는지 확인하기 위한 단계입니다. 학습에 사용되지 않은 `validation_loader` 데이터로 모델의 성능을 주기적으로 평가합니다. 이 평가 결과를 바탕으로 학습률(Learning Rate)을 조절하거나 학습을 조기 종료하는 등의 결정을 내립니다.
- **추론 (Inference)**: 학습이 완료된 모델을 사용하여 새로운 데이터에 대한 예측을 수행하는 단계입니다. 이 단계에서는 더 이상 모델의 가중치를 업데이트하지 않으므로, 역전파나 옵티마이저가 필요 없습니다. `torch.no_grad()` 컨텍스트 안에서 실행하여 메모리 사용량을 줄이고 계산 속도를 높이는 것이 일반적입니다. 서비스 배포 환경에서 모델이 사용되는 단계가 바로 추론입니다.
### 프로젝트 목표
---
1. Scikit-learn의 데이터를 PyTorch `Tensor`로 변환하고, `DataLoader`를 구성합니다.
2. `nn.Module`을 상속받아 1개 이상의 은닉층을 가진 MLP 모델을 설계합니다.
3. 이진 분류(Binary Classification) 문제에 맞는 손실 함수(`BCEWithLogitsLoss`)와 옵티마이저를 선택합니다.
4. 모델을 훈련시키고, 테스트 데이터에 대한 정확도를 측정하여 성능을 평가합니다.
## 6. 직접 해보기 (Hands-on Lab): MNIST 손글씨 분류
> **🎯 5일차 목표:** PyTorch를 사용하여 딥러닝의 "Hello, World!"인 MNIST 손글씨 분류기를 직접 만듭니다.
**최종 분석 리포트 (예시):**
```
Epoch [10/50], Loss: 0.6123
Epoch [20/50], Loss: 0.5248
Epoch [30/50], Loss: 0.4581
Epoch [40/50], Loss: 0.4132
Epoch [50/50], Loss: 0.3887
### 문제:
`torchvision`을 사용하여 MNIST 데이터셋을 불러오고, 간단한 CNN(합성곱 신경망) 모델을 구축하여 손글씨 숫자를 분류하는 전체 코드를 완성하세요.
Test Accuracy: 0.8611
```
### 단계별 구현 가이드
**1. 데이터 준비 (Scikit-learn -> PyTorch)**
- `sklearn.datasets.load_wine`을 사용해 데이터를 불러옵니다.
- 데이터를 Feature와 Target으로 나누고, `train_test_split`으로 훈련/테스트셋을 분리합니다.
- **(중요)** MLP는 스케일링에 민감하므로, `StandardScaler`를 사용해 Feature 데이터를 표준화합니다.
- 모든 데이터를 NumPy 배열에서 PyTorch `Tensor`로 변환합니다.
- `TensorDataset``DataLoader`를 사용하여 PyTorch가 배치(batch) 단위로 데이터를 처리할 수 있도록 준비합니다.
**2. MLP 모델 정의**
- `nn.Module`을 상속받는 `WineNet` 클래스를 만드세요.
- `__init__`: 입력층, 1개 이상의 은닉층(`nn.Linear` + `nn.ReLU`), 출력층(`nn.Linear`)을 정의합니다. 이진 분류이므로 출력층의 뉴런 수는 1개입니다.
- `forward`: 데이터가 각 층을 순서대로 통과하는 흐름을 정의합니다.
**3. 학습 및 평가 루프 구현**
- 모델, 손실 함수(`nn.BCEWithLogitsLoss`), 옵티마이저(`torch.optim.Adam`)를 초기화합니다.
- **학습 루프**: `for epoch in ...`:
- `for inputs, labels in train_loader:`:
- `optimizer.zero_grad()`: 이전 배치의 기울기 초기화
- `outputs = model(inputs)`: 순전파
- `loss = criterion(outputs, labels.unsqueeze(1))`: 손실 계산
- `loss.backward()`: 역전파
- `optimizer.step()`: 가중치 업데이트
- **평가 루프**:
- `model.eval()` 모드로 전환하고, `with torch.no_grad():` 블록 안에서 실행합니다.
- 테스트 로더에서 예측을 수행하고, `torch.sigmoid`와 임계값(0.5)을 사용해 로짓(logit)을 0 또는 1의 예측값으로 변환한 뒤, 정확도를 계산합니다.
**전체 코드 구조:**
```python
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 1. 데이터셋 및 로더 준비
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transform)
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
# 2. CNN 모델 정의
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2))
self.layer2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2))
self.fc = nn.Linear(7*7*64, 10)
from torch.utils.data import TensorDataset, DataLoader
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 1. 데이터 준비
# 데이터 로드 및 DataFrame 변환
# 와인 데이터셋은 13개의 feature와 3개의 클래스(0, 1, 2)를 가짐
X, y = load_wine(return_X_y=True)
# 이진 분류 문제로 변환: class 0을 1(good)로, 나머지(1, 2)를 0(bad)으로
y_binary = (y == 0).astype(int)
# 데이터 분리 및 스케일링
X_train, X_test, y_train, y_test = train_test_split(X, y_binary, test_size=0.2, random_state=42, stratify=y_binary)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# PyTorch 텐서로 변환
X_train_tensor = torch.FloatTensor(X_train_scaled)
y_train_tensor = torch.FloatTensor(y_train)
X_test_tensor = torch.FloatTensor(X_test_scaled)
y_test_tensor = torch.FloatTensor(y_test)
# DataLoader 생성
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
# 2. MLP 모델 정의
class WineNet(nn.Module):
def __init__(self, input_features):
super(WineNet, self).__init__()
self.layer1 = nn.Linear(input_features, 64)
self.layer2 = nn.Linear(64, 32)
self.output_layer = nn.Linear(32, 1)
self.relu = nn.ReLU()
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.reshape(out.size(0), -1) # Flatten
out = self.fc(out)
return out
x = self.relu(self.layer1(x))
x = self.relu(self.layer2(x))
x = self.output_layer(x)
return x
# 3. 모델, 손실함수, 옵티마이저 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = ConvNet().to(device)
criterion = nn.CrossEntropyLoss()
input_features = X_train.shape[1]
model = WineNet(input_features).to(device)
criterion = nn.BCEWithLogitsLoss() # Sigmoid + BCELoss
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 4. 학습 루프 구현
# for epoch ...
# for i, (images, labels) in enumerate(train_loader):
# - images, labels를 device로 이동
# - 순전파, 손실 계산, 역전파, 파라미터 업데이트 코드 작성
# 5. 평가 루프 구현
# with torch.no_grad():
# - test_loader에서 데이터를 가져와 예측
# - 전체 테스트 정확도 계산 및 출력 코드 작성
### 6-1. 심화: 주요 CNN 아키텍처의 발전사
간단한 CNN 모델을 직접 만들어본 지금, 딥러닝 역사에 획을 그은 주요 CNN 아키텍처들이 어떻게 발전해왔는지 살펴보는 것은 매우 의미 있는 일입니다. 모델이 어떤 문제를 해결하려 했고, 어떤 아이디어를 제시했는지 이해하면 깊이 있는 모델을 설계하는 도움이 것입니다.
| 모델명 | 발표 연도 | 주요 특징 기여 |
|---|---|---|
| **LeNet-5** | 1998 | LeCun 교수가 제안. 최초의 성공적인 CNN. `Conv`-`Pool`-`Conv`-`Pool`-`FC` 구조의 기반을 마련. |
| **AlexNet** | 2012 | 이미지넷(ILSVRC) 챌린지에서 압도적 성능으로 우승하며 **딥러닝의 부흥** 이끌었다. **ReLU**, **Dropout**, **GPU 활용** 현재까지도 널리 쓰이는 기법들을 대거 도입했다. |
| **VGGNet** | 2014 | **3x3의 작은 커널** 여러 깊게 쌓는 단순하지만 효과적인 구조를 제시. 깊이(Depth) 성능에 미치는 영향을 증명했다. |
| **GoogLeNet** | 2014 | **인셉션 모듈(Inception Module)** 도입하여, 여러 크기의 커널을 병렬로 사용함으로써 연산 효율성과 성능을 동시에 잡았다. **파라미터 수를 줄이는 ** 집중했다. |
| **ResNet** | 2015 | **잔차 학습(Residual Learning)** **스킵 연결(Skip Connection)**이라는 혁신적인 아이디어로 100층이 넘는 **매우 깊은 신경망** 학습을 가능하게 했다. **Vanishing Gradient 문제를 해결**하며 딥러닝의 한계를 돌파했다. |
#### 1. LeNet-5: CNN의 서막
- Yann LeCun 교수가 제안한 모델로, 현대적인 CNN 구조(합성곱 층과 풀링 층의 반복, 마지막에 완전 연결 계층) 청사진을 제시했습니다. 당시에는 손글씨 숫자 인식과 같은 제한된 문제에서 성공을 거두었습니다.
#### 2. AlexNet: 딥러닝의 폭발
- 2012 이미지넷 분류 챌린지에서 전통적인 컴퓨터 비전 기법들을 압도적인 성능 차이로 이기고 우승하며, AI 커뮤니티에 엄청난 충격을 주었습니다. 딥러닝이 학계를 넘어 산업계의 주류 기술로 발돋움하는 결정적인 계기가 되었습니다.
- **주요 기여**:
- **ReLU 활성화 함수**: Sigmoid/Tanh의 Vanishing Gradient 문제를 해결하고 학습 속도를 크게 향상시켰습니다.
- **Dropout**: 학습 일부 뉴런을 무작위로 비활성화하여 과적합을 방지하는 효과적인 규제(Regularization) 기법을 제시했습니다.
- **GPU 활용**: 2개의 GPU를 병렬로 사용하여 대규모 연산을 효율적으로 처리하며 딥러닝의 대중화를 이끌었습니다.
#### 3. VGGNet: 깊고 단순함의 미학
- "더 깊게 쌓으면 성능이 좋아질까?"라는 단순한 질문에서 출발했습니다.
- **주요 기여**:
- **3x3 합성곱 필터**: 크기(: 5x5, 7x7) 필터를 사용하는 대신, 3x3의 작은 필터를 여러 연속으로 사용하여 동일한 Receptive Field를 가지면서도 파라미터 수를 줄이고 비선형성을 증가시켰습니다.
- **균일한 구조**: VGG-16, VGG-19 깊이만 다른 균일한 구조를 제시하여 모델 설계의 단순함을 보여주었습니다.
#### 4. GoogLeNet (Inception): 효율적인 깊이의 추구
- VGGNet처럼 무작정 깊게 쌓는 것이 아니라, "어떻게 하면 연산 자원을 효율적으로 사용하면서 성능을 높일 수 있을까?" 고민했습니다.
- **주요 기여**:
- **인셉션 모듈(Inception Module)**: 1x1, 3x3, 5x5 합성곱과 3x3 풀링을 하나의 블록 안에서 병렬로 처리하고 결과를 합칩니다. 이를 통해 모델이 다양한 스케일의 특징을 동시에 학습할 있게 됩니다.
- **1x1 Convolution 활용**: 채널(Channel) 수를 줄여(BottleNeck) 전체 연산량을 획기적으로 감소시키면서도 성능은 유지하거나 향상시켰습니다.
#### 5. ResNet: 깊이의 한계를 넘어서
- 네트워크가 깊어질수록 오히려 학습이 어려워지고 성능이 저하되는 **퇴화(Degradation) 문제** 발생했습니다. 이는 Vanishing/Exploding Gradient와는 다른 현상으로, 깊은 망이 얕은 망보다 학습하기 어렵다는 문제였습니다.
- **주요 기여**:
- **잔차 학습(Residual Learning)**: 층이 입력값 `x` `H(x)` 직접 변환하도록 학습하는 대신, **변화량(Residual), `F(x) = H(x) - x`** 학습하도록 구조를 바꿨습니다.
- **스킵 연결(Skip Connection)**: 입력 `x` 개의 층을 건너뛰어 출력에 그대로 더해주는 `H(x) = F(x) + x` 구조입니다. 간단한 '지름길' 통해 그래디언트가 깊은 층까지 소실되지 않고 흘러갈 있게 되어, 152층이라는 전례 없는 깊이의 네트워크 학습을 성공시켰습니다. ResNet의 등장은 이후 수많은 딥러닝 아키텍처에 지대한 영향을 미쳤습니다.
# 4. 학습 루프
num_epochs = 50
for epoch in range(num_epochs):
model.train()
for inputs, labels in train_loader:
inputs, labels = inputs.to(device), labels.to(device)
```python
# ResNet의 기본 빌딩 블록 (PyTorch 예시)
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
# 입력과 출력의 차원(채널 수, 크기)이 다를 경우를 위한 다운샘플링 레이어
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels.unsqueeze(1))
loss.backward()
optimizer.step()
def forward(self, x):
identity = self.shortcut(x) # 스킵 연결을 위한 입력값 저장
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
# 5. 평가 루프
model.eval()
with torch.no_grad():
X_test_tensor, y_test_tensor = X_test_tensor.to(device), y_test_tensor.to(device)
out = self.conv2(out)
out = self.bn2(out)
y_pred_logits = model(X_test_tensor)
y_pred_prob = torch.sigmoid(y_pred_logits)
y_pred = (y_pred_prob > 0.5).float()
accuracy = (y_pred.squeeze() == y_test_tensor).float().mean()
print(f'\nTest Accuracy: {accuracy.item():.4f}')
out += identity # ★ 핵심: Conv Layer를 통과한 결과에 입력값을 더해준다.
out = self.relu(out)
return out
```
이 프로젝트를 통해 여러분은 어떤 종류의 데이터(이미지, 텍스트, 정형 데이터)를 다루든, PyTorch의 일관된 설계 철학 안에서 문제를 해결하는 능력을 갖추게 될 것입니다.
---
## 7. 되짚어보기 (Summary)
## ⚠️ What Could Go Wrong? (토론 주제)
번 주 우리는 PyTorch라는 '레고 블록'으로 딥러닝 모델을 직접 조립하는 방법을 배웠습니다.
론을 배우고 코드를 실행하는 것을 넘어, 실제 딥러닝 프로젝트에서 마주할 수 있는 문제들에 대해 미리 고민해보는 것은 매우 중요합니다. 다음 주제들에 대해 함께 토론해보세요.
- **PyTorch의 구성요소**: GPU 연산이 가능한 `Tensor`와, `nn.Module`을 상속받아 만드는 우리만의 모델 구조를 이해했습니다.
- **딥러닝 학습 과정**: '오차 측정기'인 **손실 함수**와 '조율사'인 **옵티마이저**를 사용하여, **순전파 → 손실 계산 → 역전파 → 파라미터 업데이트**로 이어지는 학습 루프의 원리를 파악했습니다.
- **실전 경험**: MNIST 손글씨 분류 실습을 통해 이미지 데이터를 다루는 CNN 모델을 직접 구현하고 학습시키는 전 과정을 체험했습니다.
1. **"Dying ReLU" 문제의 심각성**
- **상황**: 학습 과정 모니터링 중, 특정 은닉층의 뉴런 중 상당수가 항상 0만을 출력하는 'Dying ReLU' 현상을 발견했습니다.
- **토론**:
- 이 현상이 모델 학습에 구체적으로 어떤 악영향을 미칠까요? (그래디언트 흐름, 파라미터 업데이트 등)
- 이론 섹션에서 배운 Leaky ReLU 외에, 이 문제를 진단하고 해결하기 위해 시도해볼 수 있는 방법에는 어떤 것들이 있을까요? (가중치 초기화 기법, 학습률 조정 등)
2. **교과서적인 과적합(Overfitting) 발생**
- **상황**: 우리가 만든 MNIST 분류기의 훈련(train) 정확도는 99%에 달하지만, 검증(validation) 정확도는 90%에서 정체되고 있습니다. 훈련 손실은 계속 감소하지만, 검증 손실은 오히려 증가하기 시작합니다.
- **토론**:
- 이는 전형적인 과적합 신호입니다. 현재 `ConvNet` 코드 구조를 기준으로, 과적합을 완화하기 위해 시도해볼 수 있는 구체적인 방법 3가지를 논의하고, 각 방법의 장단점을 설명해보세요. (예: `nn.Dropout` 추가, `torchvision.transforms`를 이용한 데이터 증강, 모델 구조 변경 등)
3. **옵티마이저 선택의 중요성**
- **상황**: 실습 코드에서는 `Adam` 옵티마이저를 기본으로 사용했습니다. 만약 `SGD` with momentum으로 옵티마이저를 변경하여 처음부터 다시 학습시킨다면, 학습 곡선(learning curve)은 어떻게 달라질 것으로 예상되나요?
- **토론**:
- `Adam``SGD`의 핵심적인 차이점은 무엇인가요? (파라미터별 학습률 조정, 모멘텀 등)
- 어떤 종류의 문제나 데이터셋에서 `Adam`이, 또 어떤 상황에서 `SGD`가 더 나은 선택이 될 수 있을지 각자의 경험과 근거를 들어 토론해보세요.
4. **배치 사이즈(Batch Size)는 클수록 좋을까?**
- **상황**: 더 빠른 학습을 위해 배치 사이즈를 64에서 1024로 크게 늘렸더니, GPU 메모리 부족(Out of Memory) 오류 없이 학습은 완료되었지만 최종 검증 정확도가 오히려 약간 떨어졌습니다.
- **토론**:
- 배치 사이즈를 크게 하는 것의 장점(학습 속도, 안정적인 그래디언트)과 단점(메모리 사용량, 일반화 성능 저하 가능성)은 무엇일까요?
- '큰 배치 사이즈가 일반화 성능을 해칠 수 있다'는 주장의 배경이 되는 'Sharp Minimum'과 'Flat Minimum'의 개념에 대해 조사하고 설명해보세요.
---
## 7. 트러블슈팅 (Troubleshooting)
- **`RuntimeError: mat1 and mat2 shapes cannot be multiplied (...)` 오류가 발생했나요?**
- 신경망의 연속된 두 `nn.Linear` 계층 간에 입력과 출력의 크기가 맞지 않는다는 의미입니다. 예를 들어, `nn.Linear(in_features=10, out_features=20)` 다음에는 `nn.Linear(in_features=20, ...)`가 와야 합니다. 특히 CNN에서 `nn.Linear` 계층으로 넘어갈 때 Flatten 이후의 크기를 정확히 계산했는지 확인해야 합니다.
- **손실(Loss)이 전혀 줄어들지 않나요?**
- **학습률(Learning Rate)**이 너무 높거나 낮을 수 있습니다. 너무 높으면 최적점을 지나쳐 발산하고, 너무 낮으면 학습이 매우 느리거나 지역 최적점(Local Minima)에 갇힐 수 있습니다. 옵티마이저의 `lr` 값을 조절해보세요.
- 데이터에 문제가 있을 수도 있습니다. 입력 데이터와 레이블이 제대로 매칭되었는지, 데이터 정규화가 올바르게 적용되었는지 확인해보세요.
- **GPU를 사용하고 있는데도 `CUDA out of memory` 오류가 발생하나요?**
- GPU 메모리보다 큰 모델이나 데이터를 올리려고 할 때 발생합니다. **배치 사이즈(Batch Size)**를 줄이는 것이 가장 일반적인 해결책입니다. 그래도 문제가 해결되지 않는다면, 모델의 파라미터 수를 줄이거나(더 얕은 모델 사용), 이미지의 해상도를 낮추는 방법을 고려할 수 있습니다.
- **`model.eval()`과 `torch.no_grad()`는 왜 사용해야 하나요?**
- **`model.eval()`**: 모델을 평가(evaluation/inference) 모드로 전환합니다. 이는 드롭아웃(Dropout)이나 배치 정규화(Batch Normalization)와 같이 훈련 때와 평가 때 다르게 동작해야 하는 레이어들을 올바르게 설정해주는 역할을 합니다.
- **`torch.no_grad()`**: 이 컨텍스트 블록 안에서는 그래디언트 계산을 수행하지 않도록 하여, 불필요한 메모리 사용을 막고 계산 속도를 높여줍니다. 모델 평가나 추론 시에는 역전파가 필요 없으므로 반드시 사용해주는 것이 좋습니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 8. 더 깊이 알아보기 (Further Reading)
- [PyTorch 공식 60분 튜토리얼](https://tutorials.pytorch.kr/beginner/deep_learning_60min_blitz.html): PyTorch의 핵심 기능을 빠르게 훑어볼 수 있는 최고의 가이드
- [cs231n: Convolutional Neural Networks for Visual Recognition](https://cs231n.github.io/): 스탠포드 대학의 전설적인 딥러닝 강의. CNN의 원리를 깊이 있게 이해하고 싶다면 필독.
- [A Comprehensive Introduction to Torch.nn for Deep Learning](https://www.assemblyai.com/blog/a-comprehensive-introduction-to-torch-nn-for-deep-learning/): `torch.nn` 모듈에 대한 상세한 설명
| 기업 | 적용 분야 | 사용 기술 및 모델 (추정) | 비즈니스 가치 |
| :--- | :--- | :--- | :--- |
| **Tesla** | **자율 주행 (Autopilot / FSD)** | 다중 카메라 영상 기반의 **컴퓨터 비전**, Transformer 기반의 End-to-End 딥러닝 모델 (e.g., HydraNets) | 주변 환경(차선, 차량, 신호등, 보행자)을 3D 공간으로 인식하고, 차량의 행동을 결정. **운전자 보조 및 완전 자율 주행**을 목표로 함. |
| **Google (DeepMind)** | **단백질 구조 예측 (AlphaFold)** | 어텐션 기반의 딥러닝 네트워크 (Transformer와 유사) | 수십 년간 난제였던 단백질 3차원 구조 예측 문제를 해결. **신약 개발, 질병 연구** 등 생명 과학 분야에 혁신적인 발전을 가져옴. |
| **OpenAI** | **대규모 언어 모델 / 생성형 AI (GPT-4, DALL-E, Sora)** | **Transformer** 아키텍처 기반의 GPT(Generative Pre-trained Transformer) | 인간과 유사한 수준의 **텍스트 생성, 코드 작성, 이미지/영상 제작** 등 복잡한 창작 및 지능적 작업 수행. 생산성의 패러다임을 바꾸고 있음. |
| **Meta (FAIR)** | **콘텐츠 이해 및 추천, AR/VR** | CNN, RNN, Transformer 등 다양한 모델 활용 | 뉴스피드/릴스 추천, 유해 콘텐츠 자동 감지, 번역 등 핵심 서비스를 고도화. 차세대 플랫폼인 **AR 글래스, VR 헤드셋**의 객체 인식 및 상호작용에 활용. |
| **NVIDIA** | **AI 기반 신약/소재 개발 (BioNeMo)** | 생성형 AI, 분자 역학 시뮬레이션 | 신약 개발 후보 물질을 탐색하고 설계하는 데 드는 막대한 시간과 비용을 획기적으로 절감. **디지털 트윈** 기술과 결합하여 신소재 개발 가속화. |
---
## 9. 다양한 딥러닝 아키텍처 맛보기
## 8. 되짚어보기 (Summary)
지금까지 우리는 딥러닝의 기본적인 구성 요소와 MLP, CNN 모델을 살펴보았습니다. 딥러닝의 세계는 훨씬 더 넓고 다양하며, 해결하려는 문제의 종류에 따라 특화된 여러 아키텍처가 존재합니다. 앞으로 이어질 파트에서는 다음과 같은 모델들을 탐험하게 됩니다.
이번 파트에서는 머신러닝의 한계를 넘어, 이미지와 텍스트 같은 비정형 데이터까지 처리할 수 있는 딥러닝의 세계로 들어섰습니다.
- **[Part 7.1: 순환 신경망 (RNN)과 LSTM](./part_7.1_recurrent_neural_networks.md)**
- **무엇을 위한 모델인가?**: 텍스트, 시계열 데이터와 같이 **순서가 있는 데이터(Sequential Data)**를 처리하는 데 특화되어 있습니다.
- **활용 분야**: 자연어 처리(번역, 챗봇), 주가 예측, 음성 인식 등
- **PyTorch는 레고 블록**: 우리는 `nn.Module`이라는 '설명서'와 `nn.Linear`, `nn.Conv2d` 같은 '블록'으로 직접 신경망을 조립하는 법을 배웠습니다.
- **학습은 레시피 수정 과정**: 모델이 예측(순전파)을 하면, 손실 함수로 오차를 계산하고, 옵티마이저를 통해 오차를 줄이는 방향으로 파라미터를 수정(역전파)하는 학습 루프의 원리를 이해했습니다.
- **CNN은 이미지 전문가**: 합성곱(Convolution)과 풀링(Pooling) 연산을 통해 이미지의 공간적 특징을 효과적으로 추출하는 CNN의 원리를 배우고, MNIST 분류 실습을 통해 직접 구현해보았습니다.
- **거인의 어깨 위에서**: LeNet부터 ResNet에 이르기까지, 딥러닝 아키텍처의 발전사를 통해 문제 해결을 위한 다양한 아이디어를 엿보았고, Tesla, OpenAI 등 실제 기업들이 어떻게 딥러닝으로 세상을 바꾸고 있는지 확인했습니다.
- **[Part 7.2: Transformer와 LLM의 핵심 원리](./part_7.2_transformer_and_llm_principles.md)**
- **무엇을 위한 모델인가?**: RNN의 한계를 극복하고 병렬 처리를 통해 문장 전체의 문맥을 파악합니다. 현대 거대 언어 모델(LLM)의 기반이 됩니다.
- **활용 분야**: 기계 번역(ChatGPT), 텍스트 생성 및 요약 등
이제 여러분은 복잡한 딥러닝 모델의 구조를 이해하고, PyTorch를 사용하여 직접 구현할 수 있는 탄탄한 기초를 갖추게 되었습니다.
- **[Part 7.3: LangChain으로 LLM 애플리케이션 개발 맛보기](./part_7.3_llm_application_development_with_langchain.md)**
- **무엇을 위한 모델인가?**: Transformer 기반의 LLM을 외부 데이터 소스나 다른 도구와 연결하여 더 강력한 애플리케이션을 만듭니다.
- **활용 분야**: RAG(검색 증강 생성) 챗봇, AI 에이전트 개발 등
## 9. 더 깊이 알아보기 (Further Reading)
- **[Part 7.4: 그래프 신경망 (GNN)](./part_7.4_graph_neural_networks.md)** (예정)
- **무엇을 위한 모델인가?**: 소셜 네트워크, 분자 구조와 같이 **관계와 연결의 구조를 가진 데이터(Graph Data)**를 처리합니다.
- **활용 분야**: 친구 추천 시스템, 신약 개발, 이상 거래 탐지 등
### 실전 코드 & 튜토리얼
- [PyTorch 공식 튜토리얼 (한국어)](https://tutorials.pytorch.kr/): "60분 만에 끝내는 딥러닝"부터 분야별 심화 예제까지, PyTorch를 마스터하기 위한 최고의 자료입니다.
- [PyTorch Lightning](https://www.pytorchlightning.ai/): `trainer.fit(model)`과 같이 PyTorch 코드를 더 구조화하고, 보일러플레이트를 획기적으로 줄여주는 상위 프레임워크입니다.
- [Hugging Face](https://huggingface.co/): 최신 Transformer 모델들을 쉽게 다운로드하고 사용할 수 있는 플랫폼. 자연어 처리를 한다면 반드시 알아야 할 필수 라이브러리입니다.
- **[Part 7.5: 강화학습 (Reinforcement Learning)](./part_7.5_reinforcement_learning.md)** (예정)
- **무엇을 위한 모델인가?**: 에이전트가 환경과 상호작용하며 **보상을 최대화하는 행동**을 학습합니다.
- **활용 분야**: 게임 AI(알파고), 로봇 제어, 자율 주행 등
### 이론 및 개념 심화
- **[Dive into Deep Learning](https://d2l.ai/)**: 딥러닝의 거의 모든 주제를 다루는 포괄적인 온라인 교재. 수학적 설명과 PyTorch 코드가 함께 제공됩니다.
- **[CS231n: Convolutional Neural Networks for Visual Recognition](https://cs231n.github.io/)**: 스탠포드 대학의 전설적인 컴퓨터 비전 강의. CNN의 원리와 응용에 대해 가장 깊이 있게 배우고 싶다면 강력히 추천합니다.
- **[Attention Is All You Need (논문)](https://arxiv.org/abs/1706.03762)**: 현대 딥러닝의 흐름을 바꾼 Transformer 모델을 제안한 논문. `part_7.2`를 학습하기 전에 한번쯤 읽어보는 것을 권장합니다.
- **[3Blue1Brown: 신경망 영상 시리즈](https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi)**: 역전파(Backpropagation)와 같은 딥러닝의 핵심 수학 개념을 세상에서 가장 직관적인 시각 자료로 설명해줍니다.
- **[A Comprehensive Guide to Convolutional Neural Networks](https://towardsdatascience.com/a-comprehensive-guide-to-convolutional-neural-networks-the-eli5-way-3bd2b1164a53)**: CNN의 기본 개념부터 주요 아키텍처까지 시각 자료와 함께 친절하게 설명하는 아티클입니다.
---
......
......@@ -156,27 +156,95 @@ def predict_iris(data: IrisFeatures):
---
## 6. 직접 해보기 (Hands-on Lab): 스마트 메뉴판으로 시식하기
## 6. 캡스톤 프로젝트 연계 미니 프로젝트: 나만의 AI 모델 API로 서빙하기
이론은 충분합니다. 이제 직접 'AI 레스토랑'의 오너가 되어, 여러분이 직접 만든 모델을 세상에 선보일 시간입니다. 이번 미니 프로젝트는 6장, 7장에서 직접 만들었던 모델 중 하나를 골라, FastAPI를 사용해 실제 호출 가능한 API로 만드는 전체 과정을 경험합니다.
### 프로젝트 목표
- 이전 챕터에서 직접 학습시킨 나만의 모델(Scikit-learn 또는 PyTorch)을 API로 서빙할 수 있습니다.
- 모델의 입력과 출력에 맞는 Pydantic 모델을 직접 설계하고 정의할 수 있습니다.
- `lifespan` 이벤트를 사용하여, Scikit-learn 모델(.pkl)과 PyTorch 모델(.pth)을 각각의 방식에 맞게 로드하는 코드를 작성할 수 있습니다.
- FastAPI의 자동 문서(`/docs`)를 사용하여, 직접 만든 API를 테스트하고 다른 사람에게 사용법을 공유할 수 있습니다.
### 선택 가능한 모델 (아래 중 1개 선택)
1. **[6장] 붓꽃 품종 분류 모델 (`iris_model.pkl`)**: 가장 간단한 옵션. 본문 예제와 거의 유사합니다.
2. **[7장] IMDB 영화 리뷰 감성 분석 모델 (`sentiment_model.pth`)**: PyTorch 모델을 서빙하는 실전 경험을 쌓을 수 있습니다.
3. **[7.4장] GNN 친구 추천 모델 (GCN 모델)**: 조금 더 도전적인 과제. 그래프 데이터를 입력으로 받아 추천 목록을 반환하는 API를 설계해봅니다.
### 개발 과정 (IMDB 감성 분석 모델 기준)
1. **프로젝트 구조 설정**:
- `my_api/` 폴더를 만들고, 7장에서 학습시킨 `sentiment_model.pth`와 토크나이저(Tokenizer) 관련 파일(`vocab.pkl` 등)을 복사해 넣습니다.
- `main.py` 파일을 생성합니다.
2. **Pydantic 모델 정의 (`main.py`)**:
```python
from pydantic import BaseModel
class ReviewRequest(BaseModel):
text: str
class Config:
json_schema_extra = {"example": {"text": "This movie was fantastic!"}}
class SentimentResponse(BaseModel):
sentiment: str
score: float
```
3. **PyTorch 모델 로딩 로직 구현 (`main.py`)**:
- 7장에서 사용했던 PyTorch 모델 클래스(`SentimentClassifier`)와 토크나이저 로딩 코드를 가져옵니다.
- `lifespan` 컨텍스트 매니저 안에 모델과 토크나이저를 전역 변수로 로드하는 코드를 작성합니다. `model.load_state_dict()`, `model.eval()`을 사용하는 것을 잊지 마세요.
4. **예측 엔드포인트 구현 (`main.py`)**:
```python
@app.post("/predict", response_model=SentimentResponse)
def predict_sentiment(request: ReviewRequest):
# 1. 입력 텍스트를 토크나이저로 전처리
# 2. 전처리된 데이터를 PyTorch 텐서로 변환
# 3. 모델에 입력하여 예측 수행 (with torch.no_grad():)
# 4. 모델의 출력(logit)을 확률(softmax)로 변환하고, 긍정/부정 레이블 결정
# 5. SentimentResponse 형식에 맞춰 결과 반환
...
return SentimentResponse(sentiment=label, score=prob)
```
5. **서버 실행 및 테스트**:
- `uvicorn main:app --reload`로 서버를 실행합니다.
- 브라우저에서 `http://127.0.0.1:8000/docs`에 접속합니다.
- `/predict` 엔드포인트의 `Try it out` 기능을 사용하여, 여러 가지 영화 리뷰 텍스트를 입력하고 모델이 감성을 잘 예측하는지 실시간으로 테스트합니다.
### 캡스톤 프로젝트 연계 방안
이 미니 프로젝트는 15장 캡스톤 프로젝트의 2단계, **'핵심 기능 개발'**의 축소판입니다. 여기서 작성한 코드는 여러분의 최종 프로젝트에서 그대로 재사용되거나 확장될 수 있습니다. 예를 들어, 지금 만든 감성 분석 API에 사용자 인증 기능을 추가하거나, 데이터베이스와 연동하여 예측 결과를 저장하는 식으로 발전시킬 수 있습니다.
> **🎯 5일차 목표:** FastAPI의 자동 API 문서를 사용하여, 코드를 건드리지 않고 브라우저에서 직접 API를 테스트합니다.
---
FastAPI의 가장 강력한 기능, **스마트 메뉴판(Swagger UI)**을 이용해봅시다.
## ⚠️ What Could Go Wrong? (토론 주제)
1. `uvicorn main:app --reload` 명령으로 서버가 실행 중인지 확인합니다.
2. 웹 브라우저에서 **`http://127.0.0.1:8000/docs`** 로 접속합니다.
FastAPI를 사용하면 매우 빠르게 API를 만들 수 있지만, 실제 프로덕션 환경에서는 간단한 예제 코드에서 보이지 않던 문제들이 발생합니다. 다음 상황들에 대해 함께 토론해보세요.
> **💡 비유: '스마트 메뉴판' Swagger UI**
> `/docs`는 단순한 메뉴판이 아닙니다.
> - **모든 메뉴(엔드포인트)가 목록으로** 보입니다.
> - 각 메뉴를 클릭하면 **필요한 재료(요청 형식)와 나오는 요리(응답 형식)**에 대한 상세한 설명이 나옵니다.
> - 가장 멋진 기능! **`Try it out`** 버튼을 누르면 메뉴판에서 바로 **'맛보기 주문'**을 넣어볼 수 있습니다. 주방에 직접 가지 않아도 그 자리에서 바로 요리 결과를 확인할 수 있습니다.
1. **`async` 키워드의 환상**
- **상황**: 한 개발자가 무거운 머신러닝 모델의 예측 속도를 높이기 위해, 기존 `def predict(...):``async def predict(...):`로 바꿨습니다. 그는 `async` 키워드만 붙이면 모델 예측이 자동으로 비동기 처리되어 서버의 다른 요청을 막지 않을 것이라고 기대했습니다. 하지만 여러 요청이 동시에 들어오자 서버는 여전히 응답이 느려졌습니다.
- **토론**:
-`async` 키워드를 붙이는 것만으로는 CPU를 많이 사용하는 작업(CPU-bound, 예: `model.predict()`)이 빨라지지 않을까요? I/O-bound 작업과 CPU-bound 작업의 근본적인 차이는 무엇일까요?
- 무거운 모델 예측이 전체 서버를 차단(blocking)하는 것을 막기 위한 현실적인 해결책은 무엇이 있을까요? (예: `FastAPI.run_in_threadpool`의 역할, Celery와 같은 별도 작업 큐(Task Queue)의 필요성)
### 문제:
1. `/docs` 페이지에서 `POST /predict` 메뉴를 클릭하여 펼치고 `Try it out` 버튼을 누르세요.
2. **Request body**에 다른 붓꽃 데이터를 넣어보세요. (예: `versicolor` 품종인 `[6.0, 2.2, 4.0, 1.0]` 또는 `virginica` 품종인 `[6.7, 3.0, 5.2, 2.3]`)
3. `Execute` 버튼을 눌러 예측 결과를 확인하고, 모델이 올바르게 예측하는지 검증해보세요.
2. **모델 로딩 시점의 딜레마**
- **상황**: 새로운 대용량 언어 모델(LLM)을 `lifespan` 이벤트에 추가했더니, 서버가 시작되는 데 2분이 넘게 걸리기 시작했습니다. 이로 인해 개발 환경에서 코드를 수정할 때마다 재시작을 기다리는 시간이 길어졌고, 클라우드 배포 시스템의 헬스 체크(health check) 시간 초과로 배포가 실패하는 문제가 발생했습니다.
- **토론**:
- 서버 시작 시 모델을 로드하는 방식(`lifespan`)의 장점과 위와 같은 상황에서의 단점은 무엇일까요?
- 긴 모델 로딩 시간을 완화할 수 있는 전략들에는 어떤 것들이 있을까요? (예: 모델 최적화/양자화, 첫 요청 시 모델을 로드하는 'Lazy Loading' 패턴, Triton 같은 전문 추론 서버의 역할)
---
3. **전역 변수와 동시성 문제**
- **상황**: 여러 버전의 모델을 서빙하기 위해, 실행 중에 모델을 교체하는 `/load_model`이라는 API 엔드포인트를 새로 만들었습니다. 이 엔드포인트는 전역 변수인 `MODELS` 딕셔너리의 모델 객체를 새로운 것으로 바꿉니다. 테스트 시에는 잘 동작했지만, 실제 서비스에서는 가끔 사용자들이 엉뚱한 버전의 모델로부터 예측 결과를 받는 문제가 발생했습니다.
- **토론**:
- 웹 서버처럼 여러 요청이 동시에 처리되는 환경에서 `MODELS`와 같은 전역 변수를 직접 수정하는 것은 어떤 위험을 초래할까요? '경쟁 상태(Race Condition)'란 무엇일까요?
- 여러 모델을 안전하게 관리하고 서빙하기 위한 더 나은 설계 패턴은 무엇이 있을까요? (예: `lifespan`에서 모든 모델을 불러두고 경로 매개변수로 선택, 모델별로 별도의 서버/컨테이너로 배포)
4. **Pydantic을 생략했을 때의 비용**
- **상황**: 개발 속도를 높이기 위해, 한 팀원이 새로운 엔드포인트를 만들면서 Pydantic 모델 정의를 생략하고, 대신 `await request.json()`으로 원시(raw) 데이터를 직접 다루기로 결정했습니다. 얼마 후, 이 엔드포인트는 간헐적으로 `KeyError``TypeError`를 발생시키며 프로덕션 환경에서 실패하기 시작했습니다.
- **토론**:
- Pydantic은 단순히 데이터 타입을 변환해주는 것 외에, 우리를 위해 어떤 '보이지 않는' 중요한 일들을 해주고 있었을까요? (예: 필수 필드 검사, 타입 강제, 상세한 오류 메시지 리포팅 등)
- Pydantic을 사용하여 명확한 데이터 계약(Data Contract)을 정의하는 것이 프론트엔드와 백엔드 개발자 간의 협업에 어떻게 도움이 될까요?
## 7. 되짚어보기 (Summary)
......@@ -187,7 +255,28 @@ FastAPI의 가장 강력한 기능, **스마트 메뉴판(Swagger UI)**을 이
- **Pydantic과 lifespan**: API의 데이터 형식을 강제하고, 서버 시작 시 모델을 효율적으로 로드하는 방법을 익혔습니다.
- **Swagger UI**: `/docs`의 '스마트 메뉴판'을 통해 API를 쉽고 빠르게 테스트하는 강력한 기능을 체험했습니다.
## 8. 더 깊이 알아보기 (Further Reading)
---
## 8. 트러블슈팅 (Troubleshooting)
- **`uvicorn` 실행 시 `ModuleNotFoundError: No module named 'main'` 오류가 발생하나요?**
- `uvicorn main:app` 명령은 현재 터미널이 위치한 디렉토리에서 `main.py` 파일을 찾으라는 의미입니다. `main.py` 파일이 있는 디렉토리로 이동한 후 다시 명령을 실행해보세요.
- **`/docs` 접속 시 `{"detail":"Not Found"}`가 표시되나요?**
- 이는 주로 URL 주소를 잘못 입력했을 때 발생합니다. `http://12.0.0.1:8000/docs` 가 맞는지, 끝에 `/`가 빠지지는 않았는지 다시 한번 확인해보세요.
- **`Try it out` 실행 시 `422 Unprocessable Entity` 오류가 발생하나요?**
- FastAPI가 Pydantic 모델을 통해 요청 본문을 검증했지만, 형식이 맞지 않아 처리할 수 없다는 의미입니다. 예를 들어, `IrisFeatures` 모델은 `{"features": [1.0, 2.0, 3.0, 4.0]}` 형태의 JSON을 기대하는데, `{"feature": ...}` 와 같이 키 이름을 잘못 입력했거나 리스트의 요소 개수가 맞지 않는 경우 발생합니다.
- **모델을 로드할 때 `FileNotFoundError: [Errno 2] No such file or directory: 'iris_model.pkl'` 오류가 발생하나요?**
- `joblib.load()``iris_model.pkl` 파일을 찾지 못한 경우입니다. `main.py`를 실행하는 위치 기준으로 `iris_model.pkl` 파일이 같은 폴더에 있는지 확인하세요. 만약 다른 위치에 있다면, 절대 경로 또는 올바른 상대 경로를 지정해주어야 합니다.
- **서버 로그에 `ERROR: Traceback (most recent call last):` 와 함께 예상치 못한 오류가 표시되나요?**
- `uvicorn`을 실행한 터미널에 출력되는 로그는 최고의 디버깅 도구입니다. 오류 메시지의 가장 마지막 줄과 그 위의 몇 줄을 자세히 읽어보면, 어떤 코드 라인에서 어떤 종류의 예외(e.g., `AttributeError`, `ValueError`)가 발생했는지에 대한 단서를 얻을 수 있습니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 더 깊이 알아보기 (Further Reading)
- [FastAPI 공식 튜토리얼](https://fastapi.tiangolo.com/tutorial/): FastAPI의 모든 것을 배울 수 있는 최고의 가이드
- [Pydantic 공식 문서](https://docs.pydantic.dev/latest/): 데이터 유효성 검사를 더 깊이 있게 활용하는 방법
- [Serving Machine Learning Models](https://www.manning.com/books/serving-machine-learning-models): 모델 서빙에 대한 전반적인 내용을 다루는 전문 서적
......
......@@ -167,18 +167,90 @@ services:
---
## 6. 직접 해보기 (Hands-on Lab): Docker로 레스토랑 오픈하기
## 6. 캡스톤 프로젝트 연계 미니 프로젝트: 나만의 AI API '밀키트' 만들기
지난 8장 미니 프로젝트에서 여러분은 '1인 식당'처럼 빠르게 동작하는 API를 만들었습니다. 이제 이 식당을 '프랜차이즈 본사'처럼 체계적으로 리팩터링하고, 어디서든 동일한 품질을 보장하는 '밀키트(Docker 이미지)'로 포장하는 작업을 진행합니다. 이 과정은 캡스톤 프로젝트의 코드를 체계적으로 관리하고, 안정적으로 배포하는 핵심 역량을 길러줍니다.
### 프로젝트 목표
- 8장 미니 프로젝트에서 만든 '나만의 AI 모델 API'를 '관심사 분리' 원칙에 따라 체계적인 폴더 구조로 리팩터링할 수 있습니다.
- `APIRouter`를 사용하여 API 엔드포인트 로직을 `main.py`에서 완전히 분리할 수 있습니다.
- `Dockerfile`을 작성하여, 내 모델과 API 코드가 포함된 '나만의 Docker 이미지'를 성공적으로 빌드할 수 있습니다.
- `docker-compose up` 명령어로, 직접 만든 AI API 컨테이너를 실행하고 API가 정상적으로 동작하는 것을 확인할 수 있습니다.
### 개발 과정
1. **프로젝트 구조 리팩터링**:
- 8장에서 만든 `my_api` 프로젝트를 아래와 같은 구조로 변경합니다.
```
/my_prod_api
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── schemas.py # Pydantic 모델 분리
│ └── routers/
│ ├── __init__.py
│ └── predict.py # 예측 로직 분리
├── models/ # 모델/토크나이저 등은 별도 폴더로
│ └── sentiment_model.pth
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
```
- `schemas.py`: `ReviewRequest`, `SentimentResponse` 등 Pydantic 모델들을 이 파일로 옮깁니다.
- `routers/predict.py`: `APIRouter`를 생성하고, 모델 로딩과 예측 로직을 이 파일로 옮깁니다. (모델 파일 경로는 `../models/sentiment_model.pth`와 같이 수정되어야 합니다.)
2. **`main.py` 재작성**:
- `main.py`는 이제 '총지배인' 역할만 합니다. FastAPI 앱을 생성하고, `predict.py`에 정의된 라우터를 `app.include_router()`로 포함시키는 코드만 남깁니다.
3. **`requirements.txt` 생성**:
- 8장 프로젝트에 필요했던 모든 라이브러리(`fastapi`, `uvicorn`, `torch`, `pydantic` 등)를 `requirements.txt`에 명시합니다.
4. **`Dockerfile` 작성**:
- 본문 예제를 참고하여 `Dockerfile`을 작성합니다.
- `COPY` 명령어를 사용하여 `app/` 폴더 전체와 `models/` 폴더 전체를 컨테이너의 작업 디렉토리로 복사해야 합니다.
```dockerfile
# ...
COPY ./app /code/app
COPY ./models /code/models
# ...
```
5. **`docker-compose.yml` 작성 및 실행**:
- 본문 예제를 참고하여 `docker-compose.yml` 파일을 작성합니다.
- 터미널에서 `docker-compose up --build` 명령을 실행하여 이미지를 빌드하고 컨테이너를 실행합니다.
- `http://localhost:8000/docs`에 접속하여, 리팩터링되고 Docker 컨테이너 안에서 실행 중인 여러분의 API가 이전과 똑같이 잘 동작하는지 확인합니다.
### 캡스톤 프로젝트 연계 방안
이번 미니 프로젝트에서 완성한 **프로젝트 구조****Dockerfile**, **docker-compose.yml**은 여러분의 캡스톤 프로젝트의 '뼈대'가 됩니다. 새로운 기능(예: 사용자 인증, 결과 저장)이 필요할 때마다, 이 구조에 따라 `routers`에 새로운 파일을 추가하고, DB 컨테이너를 `docker-compose.yml`에 추가하는 방식으로 체계적인 확장이 가능합니다. 이는 최종 프로젝트의 기술적 완성도와 협업 효율성을 크게 높여줄 것입니다.
> **🎯 5일차 목표:** Dockerfile과 docker-compose.yml을 사용하여, 단 한 줄의 명령어로 FastAPI 서버를 실행합니다.
---
### 문제:
1. **`requirements.txt` 파일 생성**: 프로젝트에 필요한 라이브러리(`fastapi`, `uvicorn`, `scikit-learn`, `joblib`, `numpy`) 목록을 `requirements.txt` 파일에 작성하세요.
2. **`Dockerfile` 작성**: 위 템플릿을 참고하여 프로젝트 최상위 경로에 `Dockerfile`을 완성하세요.
3. **`docker-compose.yml` 작성**: 위 템플릿을 참고하여 프로젝트 최상위 경로에 `docker-compose.yml`을 완성하세요.
4. **Docker로 서버 실행**: 터미널에서 `docker-compose up --build` 명령어를 실행하여 Docker 이미지를 빌드하고 컨테이너를 실행하세요.
5. **결과 확인**: 빌드가 성공적으로 완료되고 서버가 실행되면, 웹 브라우저에서 `http://localhost:8000/docs`에 접속하여 API 문서가 이전과 동일하게 잘 보이는지 확인하세요.
## ⚠️ What Could Go Wrong? (토론 주제)
---
이제 우리는 Docker를 사용하여 어디서든 동일하게 실행되는 애플리케이션을 만들 수 있게 되었습니다. 하지만 실제 프로덕션 환경은 '실행되는 것' 이상의 것을 요구합니다. 다음 시나리오들에 대해 토론해보세요.
1. **점점 비대해지는 Docker 이미지**
- **상황**: 처음에는 200MB였던 우리 팀의 Docker 이미지가, 여러 기능이 추가되면서 이제는 2GB를 넘어섰습니다. 이미지를 빌드하고 레지스트리에 푸시하는 시간이 너무 오래 걸리고, 서버에 배포하는 시간도 길어졌습니다.
- **토론**:
- Docker 이미지 크기가 커지는 일반적인 원인들은 무엇일까요? (예: `python:3.9` 같은 큰 베이스 이미지 사용, `.dockerignore` 파일의 부재, 빌드 캐시나 불필요한 도구가 이미지에 남는 경우 등)
- **다단계 빌드(Multi-stage build)**란 무엇이며, 이 기법이 어떻게 더 작고 안전한 프로덕션 이미지를 만드는 데 도움이 될 수 있을까요?
2. **환경 설정의 대혼란**
- **상황**: 한 개발자가 로컬 DB 접속 주소(`"localhost:5432"`)를 소스 코드에 하드코딩했습니다. 그의 컴퓨터에서는 앱이 완벽하게 동작했지만, 다른 DB를 사용하는 테스트 환경에 배포하자마자 앱이 실패했습니다.
- **토론**:
- 소스 코드에 설정값(DB 주소, API 키 등)을 하드코딩하는 것이 왜 나쁜 습관일까요?
- 개발, 테스트, 프로덕션 환경별로 달라지는 설정들을 관리하는 더 나은 방법들은 무엇이 있을까요? (예: 환경 변수, `.env` 파일, `docker-compose.yml``env_file` 옵션 활용)
3. **"죽었는데 살아있다"고 말하는 헬스 체크(Health Check)**
- **상황**: 우리 서비스의 헬스 체크는 단순히 컨테이너가 '실행 중'인지만 확인합니다. 어느 날 코드의 버그로 인해 `/predict` 엔드포인트가 계속 `500 Internal Server Error`를 반환하기 시작했지만, 컨테이너 자체는 정상적으로 실행 중이었습니다. 그 결과, 오케스트레이션 시스템(예: Kubernetes, Docker Swarm)은 서비스가 건강하다고 판단하여 계속해서 고장난 컨테이너로 트래픽을 보냈고, 사용자들은 장애를 겪었습니다.
- **토론**:
- 단순히 '프로세스가 실행 중인지'만 확인하는 헬스 체크의 문제점은 무엇일까요?
- 의미 있는 헬스 체크란 무엇을 확인해야 할까요? 단순히 `/health` 엔드포인트에서 `200 OK`를 반환하는 것으로 충분할까요, 아니면 DB 연결이나 모델 가용성 같은 핵심 의존성을 함께 확인해야 할까요?
4. **무심코 `root`로 실행한 컨테이너**
- **상황**: 빠른 개발을 위해, `Dockerfile`에서 별다른 사용자 설정 없이 컨테이너를 `root` 사용자로 실행해왔습니다. 최근 실시한 보안 점검에서 이 부분이 주요 취약점으로 지적되었습니다.
- **토론**:
- 컨테이너를 `root` 권한으로 실행하는 것이 왜 보안상 위험할까요? 만약 공격자가 우리 애플리케이션의 취약점을 통해 쉘(shell)을 획득한다면 어떤 일이 벌어질 수 있을까요?
- `Dockerfile`에서 `root`가 아닌 일반 사용자(non-root user)를 생성하고, 그 사용자로 애플리케이션을 실행하도록 만드는 구체적인 단계를 설명해보세요.
## 7. 되짚어보기 (Summary)
......@@ -188,7 +260,31 @@ services:
- **Docker 컨테이너화**: '밀키트' 비유를 통해 Docker의 필요성을 이해하고, `Dockerfile`을 작성하여 우리 앱을 재현 가능한 이미지로 만들었습니다.
- **Docker Compose**: `docker-compose.yml`을 통해 여러 서비스를 명령어 하나로 관리하는 방법을 익혀, 복잡한 애플리케이션 배포의 기초를 다졌습니다.
## 8. 더 깊이 알아보기 (Further Reading)
---
## 8. 트러블슈팅 (Troubleshooting)
- **`docker-compose up` 실행 시 `Cannot connect to the Docker daemon...` 오류가 발생하나요?**
- Docker 데몬(백그라운드 서비스)이 실행 중이 아니라는 의미입니다.
- **Mac/Windows**: Docker Desktop 앱이 실행 중인지 확인하고, 아니라면 실행해주세요.
- **Linux**: `sudo systemctl start docker` 명령으로 Docker 데몬을 시작하거나, `docker-compose` 앞에 `sudo`를 붙여 실행해보세요 (`sudo docker-compose up`).
- **`docker-compose up` 실행 시 `build path ... does not exist` 오류가 발생하나요?**
- `docker-compose.yml`이 있는 위치에서 `Dockerfile`을 찾을 수 없다는 의미입니다. `docker-compose.yml``Dockerfile`이 프로젝트 최상위 폴더에 함께 있는지 확인하세요.
- **`CMD ["uvicorn", "app.main:app", ...]` 실행 중 `ModuleNotFoundError: No module named 'app'` 오류가 발생하나요?**
- Docker 컨테이너 안에서 `app`이라는 폴더/모듈을 찾지 못하는 경우입니다. `Dockerfile``COPY ./app /code/app` 부분이 올바르게 작성되었는지, 그리고 실제 프로젝트 구조와 일치하는지 확인하세요. 작업 디렉토리(`WORKDIR`)가 `/code`로 설정되어 있으므로, 파이썬은 `/code/app`을 찾게 됩니다.
- **`/docs` 접속은 되는데, `Try it out` 실행 시 모델 파일을 찾지 못한다는 오류(`FileNotFoundError`)가 발생하나요?**
- `Dockerfile``COPY ./iris_model.pkl /code/iris_model.pkl` 라인을 추가하여 모델 파일을 이미지 안으로 복사했는지 확인하세요. 컨테이너는 호스트 머신(여러분의 컴퓨터)의 파일 시스템을 직접 볼 수 없으므로, 필요한 모든 파일을 이미지에 포함시켜야 합니다.
- **`docker-compose up` 후 `http://localhost:8000` 접속이 안 되나요?**
- `docker-compose.yml``ports` 설정이 ` "8000:8000" `으로 올바르게 되어 있는지 확인하세요. 이는 여러분 컴퓨터의 `8000`번 포트를 컨테이너의 `8000`번 포트로 연결하는 설정입니다.
- `Dockerfile``CMD` 명령어에 `"--host", "0.0.0.0"` 옵션이 포함되어 있는지 확인하세요. 컨테이너 내부에서 실행되는 서버는 `0.0.0.0` 주소로 바인딩되어야만 외부(호스트 머신)에서 접속할 수 있습니다. `127.0.0.1`은 컨테이너 자기 자신만을 의미합니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 더 깊이 알아보기 (Further Reading)
- [TestDriven.io: FastAPI and Docker](https://testdriven.io/blog/dockerizing-fastapi/): Docker를 사용한 FastAPI 배포에 대한 상세한 튜토리얼
- [Docker 공식 문서: Get Started](https://docs.docker.com/get-started/): Docker의 기본 개념부터 차근차근 배울 수 있는 최고의 자료
- [『코어 자바스크립트』 저자의 Docker/k8s 강의](https://www.inflearn.com/course/%EB%8F%84%EC%BB%A4-k8s-%ED%95%B5%EC%8B%AC%EA%B0%9C%EB%념-%EC%9D%B5%ED%9E%88%EA%B8%B0): Docker와 쿠버네티스의 핵심 원리를 비유를 통해 쉽게 설명하는 강의
......
......@@ -93,112 +93,42 @@ graph TD
---
## 6. 직접 해보기 (Hands-on Lab)
## 6. 캡스톤 프로젝트 연계 미니 프로젝트: '나만의 AI 서비스' 기획서 작성하기
> **🎯 3-4일차 목표:** MLOps 도구와 AI 네이티브 IDE를 직접 사용하여, '전문가처럼' 문제를 해결하는 경험을 합니다.
지금까지 배운 모든 지식과 기술을 집약하여, 여러분의 마지막 여정인 15장 캡스톤 프로젝트의 '청사진'을 그릴 시간입니다. 이번 미니 프로젝트는 코딩이 아닌, '전문가처럼 생각하고 기획하는' 훈련입니다. 여러분이 만들고 싶은 AI 서비스에 대한 구체적인 기획서를 작성하며, 아이디어를 현실적인 계획으로 바꾸는 능력을 기릅니다.
### Lab 1: MLOps 도구 맛보기 (MLflow & DVC)
### 프로젝트 목표
- 15장 캡스톤 프로젝트에서 만들고 싶은 '나만의 AI 서비스' 주제를 명확하게 정의하고, 해결하고자 하는 문제를 구체화할 수 있습니다.
- 서비스의 전체적인 시스템 아키텍처를 다이어그램으로 설계하고, 핵심 기술 스택(AI 모델, 프레임워크 등)을 근거와 함께 선정할 수 있습니다.
- MLOps의 관점에서, 만들게 될 서비스의 데이터와 모델을 어떻게 관리하고 실험을 추적할지 초기 계획을 수립할 수 있습니다.
- 프로젝트의 잠재적 리스크와 AI 윤리 측면에서 고려해야 할 사항을 미리 식별하고 고민해볼 수 있습니다.
#### 1-1. MLflow로 실험 기록 추적하기
[MLflow](./glossary.md#mlflow)는 모델 개발 과정의 모든 실험(파라미터, 성능, 결과물)을 체계적으로 기록하고 관리하여 최고의 결과를 재현하고 모델을 개선하는 데 도움을 줍니다.
### 기획서 필수 포함 항목
아래 항목들을 포함하는 마크다운(`MY_CAPSTONE_PROPOSAL.md`) 파일을 작성하여, 캡스톤 프로젝트의 '이정표'로 삼으세요.
1. **MLflow 설치**:
```bash
pip install mlflow
```
1. **프로젝트 개요 (Project Overview)**
- **서비스 이름**: (예: AI 기반 여행 코스 추천 서비스, 'TripGenius')
- **문제 정의**: 이 서비스가 해결하고자 하는 구체적인 문제는 무엇인가? (예: "여행객들은 여러 웹사이트를 참고하며 여행 계획을 짜는 데 너무 많은 시간을 소모한다.")
- **핵심 기능 (MVP)**: 3주 안에 구현할 핵심 기능은 무엇인가? (예: "사용자가 원하는 도시와 기간, 관심사(휴양/맛집/역사)를 입력하면, 최적화된 일자별 여행 코스와 예상 비용을 제안하는 API")
2. **실험 코드 작성 (`mlflow_test.py`)**:
- 6주차 로지스틱 회귀 모델 학습 코드에 `mlflow` 로깅 기능을 추가합니다.
2. **시스템 아키텍처 (System Architecture)**
- `Mermaid` 또는 다른 다이어그램 도구를 사용하여, 사용자 요청부터 최종 응답까지 데이터가 어떻게 흐르는지 시각화하세요. (참고: 9장의 아키텍처 다이어그램)
```python
import mlflow
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
3. **기술 스택 및 선정 이유 (Tech Stack & Justification)**
- **AI/ML 모델**: 어떤 종류의 모델을 사용할 계획인가요? (예: `KoGPT-2` 파인튜닝, `GNN`, `YOLOv8` 등) 그 모델을 선택한 이유는 무엇인가요?
- **프레임워크 및 라이브러리**: `PyTorch` vs `TensorFlow`? `FastAPI`? `LangChain`? `stable-baselines3`? 선택한 도구와 그 이유를 설명하세요.
- **데이터 소스**: 필요한 데이터는 어디서, 어떻게 확보할 계획인가요? (예: "한국관광공사 TourAPI 활용", "관련 커뮤니티 사이트 웹 크롤링")
mlflow.set_experiment("Iris_Logistic_Regression")
4. **MLOps 전략 (MLOps Strategy)**
- **실험 관리**: 모델의 하이퍼파라미터와 성능은 어떻게 기록하고 관리할 것인가요? (`MLflow` 사용 계획)
- **데이터/모델 버전 관리**: 대용량 데이터나 모델 파일의 버전은 어떻게 관리할 것인가요? (`DVC` 또는 `Git-LFS` 사용 계획)
with mlflow.start_run():
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=42)
5. **리스크 및 윤리적 고려사항 (Risks & Ethical Considerations)**
- **예상되는 기술적 난관**: (예: "외부 API의 호출량 제한 문제", "크롤링 시 특정 웹사이트의 차단 가능성")
- **AI 윤리 문제**: 만들려는 서비스가 초래할 수 있는 잠재적인 편향이나 프라이버시 문제는 무엇인가? (예: "추천 모델이 특정 지역이나 고비용 상품에 편중될 위험", "사용자 데이터 수집 시 익명화 처리 방안")
C = 1.0
solver = 'lbfgs'
mlflow.log_param("C", C)
mlflow.log_param("solver", solver)
model = LogisticRegression(C=C, solver=solver, max_iter=200)
model.fit(X_train, y_train)
accuracy = accuracy_score(y_test, model.predict(X_test))
mlflow.log_metric("accuracy", accuracy)
mlflow.sklearn.log_model(model, "iris_model")
print(f"Accuracy: {accuracy}")
print("MLflow Run ID:", mlflow.active_run().info.run_id)
```
3. **MLflow UI 실행 및 확인**:
- 터미널에서 `mlflow ui`를 실행하고, 웹 브라우저에서 `http://127.0.0.1:5000`로 접속하여 실험 결과를 확인하세요.
#### 1-2. DVC로 데이터 버전 관리하기
[DVC(Data Version Control)](./glossary.md#dvc)는 Git처럼 대용량 데이터나 모델 파일의 버전을 관리합니다.
1. **DVC 및 Git 초기화**:
```bash
git init
pip install dvc
dvc init
```
2. **데이터 생성 및 DVC 추적**:
```bash
mkdir data
echo "feature1,feature2,label" > data/data.csv
echo "1,2,A" >> data/data.csv
dvc add data
```
3. **Git에 커밋**:
- 데이터의 '포인터' 역할을 하는 `data.dvc` 파일을 Git에 커밋하여 데이터 버전을 기록합니다.
```bash
git add data.dvc .gitignore
git commit -m "Add initial dataset v1"
```
### Lab 2: AI 네이티브 IDE로 코드 리팩터링하기
아래의 비효율적인 코드를 [GitHub Copilot](./glossary.md#github-copilot)과 같은 AI 네이티브 IDE를 사용하여 리팩터링하세요.
**미션**:
1. 각 기능을 별도의 함수로 분리하세요.
2. 변수 이름을 명확하게 변경하세요.
3. 각 함수에 Docstring과 타입 힌트(Type Hint)를 추가하세요.
**원본 코드 (`refactor_challenge.py`)**:
```python
# 데이터 리스트
d = [{"name": "Alice", "score": 85, "city": "New York"}, {"name": "Bob", "score": 92, "city": "Los Angeles"}, {"name": "Charlie", "score": 78, "city": "New York"}]
# 점수 80점 이상인 사람 필터링
f_d = [item['name'] for item in d if item['score'] > 80]
print("80점 이상인 학생:", f_d)
# 도시별 사람 수 계산
c_c = {}
for item in d:
c_c[item['city']] = c_c.get(item['city'], 0) + 1
print("도시별 학생 수:", c_c)
# 평균 점수 계산
avg_s = sum(item['score'] for item in d) / len(d)
print("평균 점수:", avg_s)
```
**AI에게 이렇게 요청해보세요**:
> "이 파이썬 코드를 리팩터링해줘. 각 기능(고득점자 필터링, 도시별 인원 수 계산, 평균 점수 계산)을 별도의 함수로 분리하고, 변수 이름을 더 명확하게 바꿔줘. 마지막으로 각 함수에 Docstring과 타입 힌트를 추가해줘."
### 캡스톤 프로젝트 연계 방안
이 기획서는 여러분의 **캡스톤 프로젝트 그 자체를 위한 0단계**입니다. 잘 작성된 기획서는 15장 프로젝트를 진행하는 동안 방향을 잃지 않게 해주는 '지도'가 될 것입니다. 팀원들과 이 기획서를 기반으로 토론하며, 더 현실적이고 성공 가능성 높은 프로젝트 계획을 수립하세요.
---
......@@ -240,7 +170,25 @@ AI의 세계는 광활하며 빠르게 변화합니다. 아래 로드맵을 참
---
## 8. 전문가의 자세: 기술을 넘어선 지혜
## 8. 트러블슈팅 (Troubleshooting)
- **`mlflow ui` 실행 후 웹페이지에 `This site can't be reached`가 표시되나요?**
- `mlflow ui`를 실행한 터미널이 정상적으로 작동 중인지 확인하세요. 터미널을 닫으면 서버도 함께 종료됩니다.
- 기본 포트인 `5000`번이 다른 프로세스에 의해 사용 중일 수 있습니다. `mlflow ui --port 5001`과 같이 다른 포트를 지정하여 실행해보세요.
- **`dvc add data` 실행 시 `ERROR: failed to add... Did you mean to use 'dvc add data/'?` 오류가 발생하나요?**
- DVC는 기본적으로 폴더(directory)를 추적 대상으로 인식합니다. `dvc add data``data`라는 '파일'을 찾으라는 명령인데, 실제로는 `data`라는 '폴더'이므로 오류가 발생합니다. DVC의 제안대로 `dvc add data/` (끝에 `/` 추가) 또는 `dvc add data/data.csv` 와 같이 명확한 파일 경로를 지정해주세요.
- **AI 네이티브 IDE에서 코드를 생성했지만, `ModuleNotFoundError` 또는 `NameError`가 발생하나요?**
- AI는 현재 열린 파일의 맥락을 중심으로 코드를 생성하며, 전체 프로젝트 구조나 다른 파일의 존재를 항상 알지는 못합니다. 생성된 코드에 필요한 `import` 구문이 누락되지 않았는지, 또는 다른 파일에 정의된 함수/클래스를 사용한다면 해당 파일이 올바르게 참조되었는지 확인하고 수정해야 합니다.
- **AI가 생성한 코드가 비효율적이거나 버그가 있는 것 같나요?**
- AI는 '확률적으로 가장 그럴듯한' 코드를 생성할 뿐, 항상 최적의 해답을 주지는 않습니다. 생성된 코드는 초안일 뿐, 최종 책임은 개발자에게 있습니다. 코드를 비판적으로 검토하고, "이 로직을 더 파이썬스럽게(pythonic) 바꿔줘" 또는 "이 함수의 시간 복잡도를 줄일 수 있는 방법은?" 과 같이 구체적인 개선을 요구하며 여러 번 다듬는 과정이 필수적입니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 전문가의 자세: 기술을 넘어선 지혜
진정한 전문가는 단순히 코딩만 잘하는 사람이 아닙니다. 끊임없이 배우는 자세, 명확하게 소통하는 능력, 그리고 기술에 대한 윤리적 책임감을 갖춘 사람입니다.
......@@ -248,7 +196,7 @@ AI의 세계는 광활하며 빠르게 변화합니다. 아래 로드맵을 참
- **AI 윤리 고민하기**: AI는 강력한 만큼 위험성도 큽니다. '보상 해킹(Reward Hacking)', '데이터 편향성(Bias)'과 같은 문제를 항상 경계하고, 내가 만드는 서비스가 사회에 미칠 영향을 항상 고민하는 책임감 있는 개발자가 되어야 합니다.
- **포기하지 않고 계속하기**: 이 과정을 마친 여러분은 이미 상위 10%의 잠재력을 증명했습니다. 앞으로 마주할 문제들은 훨씬 더 어렵고 복잡할 것입니다. 하지만 기억하세요. 모든 전문가는 수많은 버그와 씨름하고, 이해할 수 없는 에러 메시지 앞에서 좌절했던 시간을 거쳐왔습니다. 포기하지 않는다면, 여러분은 분명 원하는 목표를 이룰 수 있습니다.
## 9. 되짚어보기 (Summary)
## 10. 되짚어보기 (Summary)
지난 10주간, 우리는 파이썬 기초부터 시작해 AI 모델을 만들고, 이를 세상에 서비스하기 위한 모든 과정을 압축적으로 경험했습니다.
......@@ -259,7 +207,7 @@ AI의 세계는 광활하며 빠르게 변화합니다. 아래 로드맵을 참
이 과정이 여러분의 커리어에 단단한 발판이 되기를 진심으로 바랍니다. 여러분의 성장을 항상 응원하겠습니다.
## 10. 더 깊이 알아보기 (Further Reading)
## 11. 더 깊이 알아보기 (Further Reading)
- **MLOps**:
- [MLOps Community](https://mlops.community/): MLOps 관련 최신 아티클, 팟캐스트, 슬랙 커뮤니티
......@@ -269,3 +217,31 @@ AI의 세계는 광활하며 빠르게 변화합니다. 아래 로드맵을 참
- [The Batch (DeepLearning.AI)](https://www.deeplearning.ai/the-batch/): 앤드류 응 교수가 매주 발행하는 AI 뉴스레터
- **Papers**:
- [Papers with Code](https://paperswithcode.com/): 최신 AI 논문과 관련 코드를 함께 볼 수 있는 최고의 사이트
여러분의 성공적인 AI 전문가 커리어를 응원합니다!
---
### ⚠️ What Could Go Wrong? (토론 주제)
자신에게 맞는 전문가 경로를 선택하고 성장하는 과정은 간단하지 않습니다. 많은 개발자들이 비슷한 고민과 시행착오를 겪습니다.
1. **지나치게 빠른 전문화의 함정 (The Pitfall of Specializing Too Early)**
* AI 분야는 매우 빠르게 변화합니다. 커리어 초기에 특정 도메인(예: 의료 영상 분석)이나 특정 기술(예: 특정 GNN 아키텍처)에만 '올인'했을 때, 기술 트렌드가 바뀌거나 해당 분야의 수요가 줄어들면 어떻게 될까요?
* 'T자형 인재'가 되기 위해, 넓은 지식(수평 바)과 깊은 전문성(수직 바) 사이의 균형을 어떻게 맞춰나가는 것이 현명할까요? 언제쯤 깊이를 파기 시작하는 것이 적절할까요?
2. **'핫한' 분야만 좇는 것의 위험성 (The Danger of Chasing "Hype")**
* 현재 가장 주목받는 분야(예: LLM, 생성형 AI)에만 집중하다가, 기반이 되는 전통적인 머신러닝이나 데이터 엔지니어링 역량을 소홀히 할 경우 어떤 문제가 발생할 수 있을까요?
* 시장의 유행과 나의 장기적인 흥미/강점이 일치하지 않을 때, 어떤 기준으로 커리어 방향을 설정해야 할까요? 유행을 좇는 것과 기회를 포착하는 것의 차이는 무엇일까요?
3. **이론과 실제의 괴리 무시하기 (Ignoring the Gap Between Theory and Practice)**
* 최신 논문에 나오는 SOTA(State-of-the-art) 모델의 성능 수치에만 매료되어, 실제 기업 환경에서 발생하는 데이터 정합성 문제, 모델 배포, 운영, 비용 최적화와 같은 엔지니어링의 현실을 등한시하는 경향이 있습니다.
* '리서처'와 'ML 엔지니어'의 역할이 나뉘는 이유는 무엇이며, 두 역할 모두에게 공통적으로 요구되는 역량은 무엇이라고 생각하시나요?
4. **소프트 스킬의 중요성 간과 (Overlooking the Importance of Soft Skills)**
* 뛰어난 기술력을 갖추었음에도 불구하고, 비즈니스 문제를 이해하고 정의하는 능력, 다른 팀(기획, 백엔드)과의 협업 능력, 자신의 연구/개발 결과를 비전문가에게 설명하고 설득하는 능력이 부족하여 성장하지 못하는 경우가 많습니다.
* 기술 전문가가 조직 내에서 더 큰 영향력을 발휘하고 리더로 성장하기 위해, 기술 외적으로 어떤 역량을 갖춰야 할까요?
5. **특정 회사/도구에 대한 과도한 의존 (Over-reliance on a Specific Company/Tool)**
* 하나의 회사에서 사용하는 특정 플랫폼(예: AWS SageMaker, Databricks)이나 내부 도구에만 익숙해지면, 이직 시 새로운 환경에 적응하는 데 어려움을 겪을 수 있습니다.
* 특정 도구의 '사용법'을 아는 것을 넘어, 그 도구가 해결하려는 근본적인 '문제'와 '원리'를 이해하는 것이 왜 중요할까요? 이직 시장에서 나의 가치를 높이기 위한 학습 전략은 무엇이 있을까요?
......@@ -184,7 +184,108 @@ Feast는 크게 두 가지 데이터 흐름을 관리합니다.
---
## 8. 개념 확인 퀴즈 (Concept Check Quiz)
## 캡스톤 프로젝트 연계 미니 프로젝트: 나만의 AI API를 위한 CI 파이프라인 구축하기
지금까지 우리는 훌륭한 AI 모델을 만들고, API로 서빙하며, Docker로 포장하는 방법을 배웠습니다. 이제 '전문가처럼' 이 과정을 자동화할 시간입니다. 이번 미니 프로젝트에서는 9장에서 만든 '나만의 AI API 밀키트' 프로젝트에 CI(Continuous Integration, 지속적 통합) 파이프라인을 구축합니다. 코드가 변경될 때마다 자동으로 테스트를 실행하여, 우리 서비스의 안정성을 한 단계 끌어올리는 것을 목표로 합니다.
### 프로젝트 목표
- `pytest`를 사용하여 FastAPI 애플리케이션의 기본적인 동작을 검증하는 테스트 코드를 작성할 수 있습니다.
- GitHub 리포지토리에 코드를 푸시(push)할 때, 특정 작업이 자동으로 실행되도록 GitHub Actions 워크플로우를 설정할 수 있습니다.
- CI 파이프라인에 테스트 자동화, Docker 이미지 빌드 등 ML 프로젝트에 필수적인 단계들을 포함시키고, 실행 결과를 GitHub에서 직접 확인할 수 있습니다.
### 개발 과정
1. **테스트 코드 작성 (`tests/test_api.py`)**:
- 9장에서 만든 프로덕션 API 프로젝트(`my_prod_api`)에 `tests` 폴더를 추가하고, `pytest``httpx` 라이브러리를 설치합니다. (`pip install pytest httpx`)
- API의 상태와 예측 엔드포인트가 정상적으로 동작하는지 확인하는 간단한 테스트 코드를 작성합니다.
```python
from fastapi.testclient import TestClient
from app.main import app # 여러분의 FastAPI 앱을 임포트합니다.
import pytest
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "AI 레스토랑에 오신 것을 환영합니다."}
def test_predict_sentiment_positive():
# 8장에서 만든 감성분석 API를 테스트하는 예시
response = client.post("/predict", json={"text": "This movie is fantastic!"})
assert response.status_code == 200
data = response.json()
assert data["sentiment"] == "positive"
assert data["score"] > 0.5
```
2. **GitHub 리포지토리 생성 및 코드 푸시**:
- GitHub에 새로운 리포지토리를 생성합니다.
- 9장에서 수정한 `my_prod_api` 프로젝트를 이 리포지토리에 푸시합니다.
3. **GitHub Actions 워크플로우 파일 생성 (`.github/workflows/ci.yml`)**:
- 프로젝트 루트에 `.github/workflows` 폴더를 생성하고, 그 안에 `ci.yml` 파일을 작성합니다.
```yaml
name: CI Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest httpx # 테스트에 필요한 라이브러리 설치
- name: Run tests with pytest
run: pytest tests/
- name: Build Docker image
run: docker build -t my-awesome-api .
```
4. **CI 파이프라인 실행 및 결과 확인**:
- `ci.yml` 파일을 포함한 변경사항을 `main` 브랜치에 푸시합니다.
- GitHub 리포지토리의 'Actions' 탭으로 이동하여, 방금 푸시한 커밋에 대해 워크플로우가 자동으로 실행되는 것을 확인합니다.
- 각 단계(코드 체크아웃, 의존성 설치, 테스트 실행, 도커 빌드)가 성공적으로 완료되어 녹색 체크 표시가 뜨는지 확인합니다. 만약 실패한다면, 로그를 통해 원인을 분석하고 코드를 수정하여 다시 푸시합니다.
### 캡스톤 프로젝트 연계 방안
이번에 구축한 CI 파이프라인은 캡스톤 프로젝트의 '품질 관리 시스템'입니다. 팀원들이 새로운 코드를 추가하거나 수정할 때마다, 이 파이프라인이 자동으로 코드의 안정성을 검증해줍니다. 이를 통해 "제 컴퓨터에선 됐는데..."와 같은 문제를 예방하고, 여러 명이 협업하더라도 항상 안정적인 상태의 프로젝트를 유지할 수 있습니다. 캡스톤 프로젝트에서는 이 파이프라인을 확장하여, 테스트를 통과한 모델을 자동으로 스테이징 서버에 배포(CD)하는 단계까지 구현해볼 수 있습니다.
---
## 8. 트러블슈팅 (Troubleshooting)
- **GitHub Actions 워크플로우 실행 시 `command not found: pytest` 또는 `No module named 'pandas'` 오류가 발생하나요?**
- 이는 워크플로우의 `runner`(실행 환경)에 필요한 라이브러리가 설치되지 않았기 때문입니다.
- **Evidently AI로 리포트 생성 시 `ValueError: Reference data has ... features, but current data has ... features` 오류가 발생하나요?**
- 참조(reference) 데이터와 현재(current) 데이터의 컬럼(피처) 수가 일치하지 않는 경우입니다. 데이터 전처리 파이프라인이 두 데이터셋에 동일하게 적용되었는지, 특정 컬럼이 누락되거나 추가되지는 않았는지 스키마를 확인해야 합니다.
- **Feast `get_historical_features()` 실행이 매우 느리거나 메모리 부족 오류가 발생하나요?**
- 오프라인 스토어에서 대규모 데이터를 처리할 때 발생할 수 있는 문제입니다. `entity_df`에 포함된 엔티티의 수나 조회 기간이 너무 크지 않은지 확인해보세요. 필요한 피처만 선택적으로 조회하거나, Spark와 같은 분산 처리 엔진을 데이터 소스로 사용하여 성능을 개선할 수 있습니다.
- **Feast `materialize` 실행 후 온라인 스토어에서 피처를 조회해도 예전 값만 보이나요?**
- `materialize` 명령은 특정 시간 범위의 데이터를 오프라인 스토어에서 온라인 스토어로 '밀어넣는' 작업입니다. `materialize-incremental`을 사용하여 주기적으로 최신 데이터를 증분(incremental) 반영하도록 스케줄링했는지 확인하세요. 또는, `feast materialize <end-date> --view <feature-view-name>` 와 같이 시간 범위를 명시적으로 지정하여 최신 데이터가 포함되도록 실행했는지 확인해야 합니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 개념 확인 퀴즈 (Concept Check Quiz)
1. Kubeflow Pipelines의 주요 역할은 무엇이며, ML 워크플로우를 어떻게 구성하나요?
2. ML을 위한 CI/CD 파이프라인에서 '데이터 유효성 검사'와 '모델 품질 검증'이 왜 중요한가요?
......@@ -192,14 +293,14 @@ Feast는 크게 두 가지 데이터 흐름을 관리합니다.
---
## 9. 과제 (Assignment)
## 10. 과제 (Assignment)
1. **모니터링 시스템 설계**: Prometheus와 Grafana를 사용하여 간단한 FastAPI 애플리케이션의 API 요청 수와 응답 시간을 모니터링하는 대시보드를 구축하는 과정을 순서대로 설명해 보세요. (실제 구축이 아닌, 개념적 순서 설명)
2. **Feature Store 도입 설득**: 여러분이 데이터 사이언티스트 팀의 리드라고 상상해 보세요. 동료들에게 Feature Store 도입의 필요성을 설득하기 위한 3가지 핵심 논거를 제시해 보세요.
---
## 10. 되짚어보기 (Summary)
## 11. 되짚어보기 (Summary)
이번 파트에서는 모델 개발 이후, 프로덕션 환경에서 AI 모델을 안정적으로 운영하기 위한 MLOps 기술들을 심도 있게 다루었습니다.
......@@ -210,7 +311,7 @@ Feast는 크게 두 가지 데이터 흐름을 관리합니다.
---
## 11. 더 깊이 알아보기 (Further Reading)
## 12. 더 깊이 알아보기 (Further Reading)
- [Kubeflow 공식 문서](https://www.kubeflow.org/docs/): Kubeflow의 다양한 컴포넌트에 대한 상세한 가이드
- [GitHub Actions 문서](https://docs.github.com/en/actions): 워크플로우 작성법 및 다양한 예제
- [Evidently AI 공식 문서](https://docs.evidentlyai.com/): 데이터 드리프트 및 모델 성능 모니터링 리포트 생성 가이드
......@@ -219,3 +320,34 @@ Feast는 크게 두 가지 데이터 흐름을 관리합니다.
---
**➡️ 다음 시간: [Part 12: 대규모 AI 모델 최적화 및 서빙](../12_model_optimization/part_12_model_optimization.md)**
### 참고 자료
* [MLOps Community](https://mlops.community/): MLOps 관련 최신 정보, 아티클, 팟캐스트를 얻을 수 있는 커뮤니티
* [Google Cloud의 MLOps 가이드](https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning?hl=ko): MLOps의 레벨별 정의와 구성 요소를 체계적으로 설명한 문서
---
### ⚠️ What Could Go Wrong? (토론 주제)
MLOps는 단순히 도구를 도입하는 것을 넘어, 조직의 문화와 프로세스를 바꾸는 복잡한 과정입니다. 많은 조직이 MLOps 도입 과정에서 어려움을 겪습니다.
1. **'모든 것을 자동화'하려는 함정 (The "Automate Everything" Trap)**
* MLOps의 궁극적인 목표가 자동화는 맞지만, 아직 프로토타입 단계에 있거나 비즈니스 가치가 불확실한 모델에 대해 처음부터 너무 거대하고 복잡한 CI/CD/CT 파이프라인을 구축하는 것은 '과도한 엔지니어링(Over-engineering)'일 수 있습니다.
* 프로젝트의 성숙도(초기 연구 vs 프로덕션)에 따라 MLOps의 적용 수준을 어떻게 조절하는 것이 현명할까요? "좋은 MLOps"와 "나쁜 MLOps"를 가르는 기준은 무엇일까요?
2. **데이터 중심 원칙의 부재 (Lack of Data-centric Principles)**
* 멋진 파이프라인을 구축하고 모델 버전을 관리하는 데 집중하느라, 정작 그 파이프라인에 입력되는 데이터의 품질, 버전, 통계적 분포 변화(Data Drift)를 추적하고 관리하는 데 소홀히 하는 경우가 많습니다.
* "쓰레기가 들어가면 쓰레기가 나온다(Garbage In, Garbage Out)"는 원칙이, 지속적으로 재학습되는 ML 시스템에서는 왜 더욱 치명적일까요? 데이터 중심(Data-centric) MLOps를 위해 어떤 요소(e.g., Feature Stores, Data Validation)가 중요할까요?
3. **사일로화된 MLOps 팀 (Siloed MLOps Team)**
* MLOps를 위한 플랫폼을 구축하는 엔지니어링 팀이 모델을 개발하는 데이터 사이언티스트 팀과 긴밀하게 협업하지 않고, "그들만의" 인프라를 구축하는 경우가 있습니다. 이로 인해 실제 모델 개발 및 배포의 어려움이 전혀 반영되지 않은, 사용하기 어려운 MLOps 플랫폼이 탄생하게 됩니다.
* DevOps와 마찬가지로 MLOps가 특정 팀의 업무가 아닌 '문화'로 자리 잡기 위해, 조직 내에서 어떤 노력이 필요할까요?
4. **보안 및 규제 준수 간과 (Overlooking Security and Compliance)**
* 학습에 사용된 민감 데이터, 모델 아티팩트, 예측 API 엔드포인트에 대한 접근 제어 및 보안을 소홀히 하여, 모델의 가중치나 내부 데이터가 외부에 유출될 수 있습니다.
* GDPR, HIPAA 등 데이터 규제가 엄격한 산업에서, "이 모델이 왜 이런 예측을 했는가?"를 설명하고 감사(Audit)에 대비하기 위해 MLOps 파이프라인에 어떤 기능(예: 예측 결과 및 근거 로깅, 모델 카드)이 포함되어야 할까요?
5. **비용 관리의 실패 (Failure in Cost Management)**
* 코드가 커밋될 때마다 전체 학습 파이프라인이 자동으로 실행되도록 설정하여 막대한 클라우드 비용이 발생하거나, 실험 추적을 위해 모든 중간 결과물을 저장하다 스토리지 비용이 폭증하는 등 비용 관리에 실패할 수 있습니다.
* 효율적인 MLOps를 위해, 성능과 비용 사이의 균형을 맞출 수 있는 전략(예: 캐싱, GPU 자원 관리, 경량 모델 실험)은 무엇이 있을까요?
\ No newline at end of file
......@@ -208,7 +208,73 @@ deepspeed --num_gpus=4 training.py --deepspeed --deepspeed_config ds_config.json
---
## 6. 고성능 추론 서버 활용
## 6. 실습: BERT 모델 양자화 및 성능 벤치마킹
> **🎯 실습 목표:** Hugging Face의 BERT 모델에 8-bit 양자화를 적용하고, 최적화 전후의 모델 크기, 추론 속도(지연 시간), 처리량(Throughput)을 직접 측정하여 경량화의 효과를 눈으로 확인합니다.
이론으로 배운 양자화가 실제 NLP 모델에서 어떤 효과를 가져오는지, `transformers``bitsandbytes` 라이브러리를 사용하여 직접 확인해보겠습니다. 이 실습을 통해 우리는 원본 FP32 모델과 INT8로 양자화된 모델의 성능을 구체적인 수치로 비교 분석합니다.
**실습 환경:**
- **라이브러리:** `torch`, `transformers`, `bitsandbytes`, `accelerate`, `numpy`
- **모델:** `bert-base-uncased`
- **측정 지표:**
- **모델 크기 (Model Size)**: 모델이 메모리에서 차지하는 용량 (MB)
- **지연 시간 (Latency)**: 단일 추론 요청을 처리하는 데 걸리는 평균 시간 (ms)
- **처리량 (Throughput)**: 1초당 처리할 수 있는 추론 요청의 수 (inferences/sec)
### 실습 코드 (`source_code/12_model_optimization/bert_quantization.py`)
실습에 사용할 전체 코드는 위 경로에서 확인하실 수 있습니다. 코드의 핵심 로직은 다음과 같습니다.
1. **모델 로딩**: 원본 FP32 `bert-base-uncased` 모델을 로드하고, `load_in_8bit=True` 옵션을 사용하여 동일한 모델을 8-bit 정수형으로 양자화하여 로드합니다.
2. **크기 측정**: 각 모델이 실제 메모리에서 차지하는 크기를 측정하여 비교합니다.
3. **성능 벤치마킹 함수 (`benchmark_performance`)**:
- 정확한 측정을 위해 GPU 예열(warm-up) 과정을 거칩니다.
- 지정된 횟수(예: 100회)만큼 추론을 반복 실행하여 평균 지연 시간과 초당 처리량을 계산합니다.
4. **결과 비교**: 원본 모델과 양자화된 모델의 크기, 지연 시간, 처리량을 일목요연한 표로 정리하여 보여주고, 개선율을 계산하여 출력합니다.
### 실습 실행 및 결과 분석
아래 명령어를 사용하여 터미널에서 벤치마킹 스크립트를 실행합니다. (CUDA 환경에서 실행 권장)
```bash
python source_code/12_model_optimization/bert_quantization.py
```
**예상 실행 결과:**
스크립트를 실행하면 먼저 각 모델을 로드하고, 벤치마킹을 수행한 후 다음과 같은 최종 성능 비교표를 출력합니다.
```
--- 5. 최종 성능 비교 ---
==================================================
모델 | 모델 크기(MB) | 평균 지연시간(ms) | 처리량(inf/sec)
--------------------------------------------------------------------------------------------------
FP32 | 417.82 | 15.82 | 63.21
INT8 | 134.78 | 9.55 | 104.71
--------------------------------------------------------------------------------------------------
📊 모델 크기 감소율: 67.74%
⏱️ 지연 시간 개선율: 39.63%
🚀 처리량 향상률: 65.65%
==================================================
```
*(참고: 위 결과는 실행 환경의 하드웨어(GPU 종류, 시스템 사양)에 따라 달라질 수 있습니다.)*
**결과 분석 (가이드라인):**
- **모델 크기**: 양자화를 통해 모델의 메모리 사용량이 약 **68%** 감소했습니다. 이는 FP32(32-bit) 가중치를 INT8(8-bit)로 표현하면서 모델 크기가 이론적으로 1/4 수준으로 줄어들기 때문입니다. (실제 감소율은 레이어 구성 등에 따라 다를 수 있습니다.) 이로 인해 리소스가 제한된 환경에 모델을 배포하기 훨씬 용이해집니다.
- **지연 시간 및 처리량**: 추론 속도 또한 크게 향상되었습니다. 평균 지연 시간은 약 **40%** 감소했고, 초당 처리량은 **65%** 이상 증가했습니다. 이는 8-bit 정수 연산이 32-bit 부동소수점 연산보다 하드웨어에서 훨씬 효율적으로 처리되기 때문입니다. 더 빠른 응답 속도와 더 많은 사용자 요청 처리가 가능해져 서빙 비용 효율이 높아집니다.
- **추론 정확도**: 스크립트의 마지막 부분을 보면, 두 모델의 추론 결과(레이블 및 점수)가 거의 동일하게 나타나는 것을 확인할 수 있습니다. 이는 `bitsandbytes` 라이브러리가 성능 저하를 최소화하면서 양자화를 효과적으로 수행했음을 보여줍니다.
이처럼 양자화는 모델의 정확도에 거의 영향을 주지 않으면서, 모델의 크기와 추론 속도를 극적으로 개선할 수 있는 매우 효과적인 최적화 기법입니다.
> **💬 심화: 가지치기(Pruning)와 지식 증류(Knowledge Distillation)**
>
> 이번 실습에서는 양자화에 집중했지만, 앞서 이론 섹션에서 배운 **가지치기(Pruning)**와 **지식 증류(Knowledge Distillation)** 역시 중요한 경량화 기법입니다. 가지치기는 모델의 불필요한 파라미터를 제거하는 방식이며, 지식 증류는 크고 복잡한 모델의 지식을 작고 효율적인 모델에 전달하는 방식입니다. 실제 프로젝트에서는 모델의 특성과 목표 성능에 따라 이러한 기법들을 단독 혹은 조합하여 사용하게 됩니다.
---
## 7. 고성능 추론 서버 (Inference Server)
모델을 경량화하는 것만큼, 학습된 모델을 효율적으로 서빙하는 것 또한 중요합니다. Python 기반의 웹 프레임워크(예: FastAPI, Flask)는 모델 서빙 API를 빠르고 간단하게 만들 수 있다는 장점이 있지만, 대규모 트래픽 처리나 복잡한 서빙 요구사항에는 한계가 있습니다.
......@@ -248,67 +314,154 @@ Triton은 클라이언트/서버 모델로 작동합니다. 클라이언트는 H
| | | | - PyTorch | |
| | | | - ONNX | |
| | | | - TensorRT | |
| | | | - etc. | |
+----------------+ +---------------------+
^
|
+---------------------+
| Model Repository |
| (Local/Cloud Storage) |
+---------------------+
| | | +-----------------+ |
| | | | Model Repository | |
+----------------+---------------------+---------------------+
```
### TorchServe
TorchServe는 PyTorch 커뮤니티에서 직접 개발하고 지원하는 PyTorch 모델 서빙을 위한 공식 도구입니다.
TorchServe는 PyTorch 에서 직접 개발하고 관리하는 공식 모델 서빙 라이브러리입니다. PyTorch 모델을 프로덕션 환경에 배포하는 과정을 간소화하는 데 중점을 둡니다.
**주요 특징:**
- **PyTorch 최적화**: PyTorch 모델 서빙에 특화되어 있으며, `torch-model-archiver`를 통해 모델과 핸들러를 `.mar` 파일로 쉽게 패키징할 수 있습니다.
- **기본 서빙 기능**: REST 및 gRPC API, 로깅, 메트릭 수집, 모델 버전 관리 등 모델 서빙에 필요한 기본적인 기능들을 제공합니다.
- **간단한 사용법**: 상대적으로 가볍고 설정이 간단하여 PyTorch 모델을 빠르게 서빙하고자 할 때 좋은 선택지입니다.
- **PyTorch 최적화**: PyTorch 모델 서빙에 가장 최적화되어 있으며, `torch.jit` 스크립트 모델이나 eager 모드 모델을 쉽게 배포할 수 있습니다.
- **간편한 사용법**: `torch-model-archiver` CLI를 사용하여 모델과 핸들러를 `.mar` 파일로 아카이빙하고, TorchServe에 등록하여 바로 서빙할 수 있습니다.
- **기본 기능 제공**: 로깅, 메트릭 수집, RESTful API 엔드포인트 등 모델 서빙에 필요한 기본적인 기능들을 충실하게 제공합니다.
- **동적 배치**: Triton과 마찬가지로 동적 배치 기능을 지원하여 처리량을 향상시킬 수 있습니다.
**Triton vs TorchServe:**
### Triton vs TorchServe
- **범용성**: Triton은 거의 모든 프레임워크를 지원하는 반면, TorchServe는 PyTorch에 집중합니다.
- **성능/기능**: Triton이 동적 배치, 모델 앙상블 등 더 진보되고 고성능을 위한 다양한 기능을 제공합니다.
- **사용 편의성**: TorchServe가 더 가볍고 빠르게 시작할 수 있습니다.
| 구분 | NVIDIA Triton | PyTorch TorchServe |
|---|---|---|
| **범용성** | **다양한 프레임워크 지원** (TF, ONNX, TRT 등) | **PyTorch에 집중** 및 최적화 |
| **성능** | GPU 최적화, TensorRT 통합 등 **최고 수준의 성능** | 준수한 성능을 제공하지만 Triton 대비 기능은 적음 |
| **기능** | 모델 앙상블, 스트리밍, C++ 백엔드 등 고급 기능 풍부 | 모델 서빙의 핵심 기능에 집중 |
| **사용 편의성**| 설정이 다소 복잡할 수 있음 | PyTorch 사용자에게 매우 친숙하고 간편함 |
따라서, 다양한 종류의 모델을 복잡한 서빙 로직과 함께 운영해야 하는 환경이라면 Triton이, 오직 PyTorch 모델만을 빠르고 간하게 서빙하는 것이 목표라면 TorchServe가 좋은 선택이 될 수 있습니다.
**결론적으로,** 다양한 종류의 모델을 하나의 서버에서 서빙하거나, 최고의 성능 최적화가 필요하다면 **Triton**이 좋은 선택입니다. 반면, 프로젝트가 **PyTorch 중심으로 이루어져 있고**, 빠르고 간하게 모델을 배포하고 싶다면 **TorchServe**가 효율적인 대안이 될 수 있습니다.
---
## 7. 개념 확인 퀴즈 (Concept Check Quiz)
## 8. 캡스톤 프로젝트 연계 미니 프로젝트: 나만의 AI 모델 최적화 및 서빙 전략 수립
1. 데이터 병렬 처리(Data Parallelism)와 모델 병렬 처리(Model Parallelism)의 가장 큰 차이점은 무엇인가요? 어떤 상황에서 각각 사용되나요?
2. 모델 경량화 기법 중 양자화(Quantization)의 기본 원리는 무엇이며, PTQ와 QAT 방식의 차이점은 무엇인가요?
3. NVIDIA Triton 추론 서버의 '동적 배치(Dynamic Batching)' 기능이 왜 GPU 활용률을 높이는 데 효과적인가요?
대규모 모델을 서비스하는 것은 성능뿐만 아니라 '비용'과의 싸움입니다. 이번 미니 프로젝트에서는 이전 챕터에서 만들었던 나만의 모델에 최적화 기법을 직접 적용하여 '가성비'를 높이고, 전문 추론 서버를 사용한 서빙 전략을 수립하여 15장 캡스톤 프로젝트의 기술적 깊이를 더합니다.
### 프로젝트 목표
- 이전에 학습시킨 모델(예: 7장의 IMDB 감성 분석 PyTorch 모델)에 양자화, 가지치기 등 최소 1개 이상의 경량화 기법을 적용할 수 있습니다.
- 최적화 전후 모델의 크기, 추론 지연 시간(Latency), 처리량(Throughput)을 정량적으로 측정하고 비교 분석하여 최적화의 효과를 입증할 수 있습니다.
- NVIDIA Triton 또는 TorchServe와 같은 고성능 추론 서버의 역할을 이해하고, 최적화된 모델을 해당 서버에서 운영하기 위한 배포 전략을 문서로 설계할 수 있습니다.
### 개발 과정
1. **최적화 대상 모델 선정**:
- 이전 과제에서 만들었던 모델 중 하나를 선택합니다. (예: 7장의 IMDB 감성 분석 PyTorch 모델)
2. **모델 경량화 적용**:
- **(필수)** PyTorch의 `torch.quantization.quantize_dynamic`을 사용하여 모델에 Post-Training Dynamic Quantization을 적용합니다.
- **(도전 과제)** `torch.nn.utils.prune`을 사용하여 가중치 일부를 제거하는 비구조적 가지치기(Unstructured Pruning)를 적용해보고, 양자화와 중복 적용했을 때의 효과를 비교합니다.
3. **성능 벤치마킹**:
- 이 챕터의 `bert_quantization.py` 실습 코드를 참고하여, 여러분의 모델에 맞는 벤치마킹 스크립트를 작성합니다.
- **원본 모델****최적화된 모델** 각각에 대해 아래 지표를 측정하고 표로 정리합니다.
- 모델 파일 크기 (MB)
- 평균 추론 지연 시간 (ms)
- 초당 처리량 (inferences/sec)
4. **고성능 서빙 전략 설계 (문서 작성)**:
- `MY_SERVING_STRATEGY.md` 파일을 생성하고, **Triton****TorchServe** 중 하나를 선택하여 서빙 전략을 수립합니다.
- **모델 리포지토리 구성**: 선택한 추론 서버가 요구하는 모델 저장소의 디렉토리 구조를 설명합니다. (예: `model_repository/my_sentiment_model/1/model.pt`)
- **서버 환경 설정 (`config.pbtxt` 등)**: 모델을 효율적으로 서빙하기 위한 핵심 설정을 어떻게 구성할지 구상합니다.
- **동적 배치(Dynamic Batching)**: 여러 요청을 묶어 처리하기 위한 배치 크기와 지연 시간 설정의 트레이드오프를 설명합니다.
- **인스턴스 그룹(Instance Group)**: 단일 GPU에서 모델을 몇 개나 동시에 실행하여 처리량을 극대화할지 계획을 세웁니다.
### 캡스톤 프로젝트 연계 방안
- 이 미니 프로젝트에서 최적화한 모델 파일과 서빙 전략 문서는 15장 캡스톤 프로젝트의 **'성능 고도화 및 배포'** 단계에서 그대로 활용될 수 있습니다.
- FastAPI 기본 서빙과 전문 추론 서버의 성능 차이를 직접 고민하고 비교해본 경험은, 여러분의 최종 프로젝트가 '프로토타입'을 넘어 '프로덕션 레벨' 서비스로 나아가는 중요한 밑거름이 될 것입니다.
- 벤치마킹 결과와 서빙 전략은 최종 발표에서 여러분의 프로젝트가 가진 기술적 깊이와 완성도를 보여주는 강력한 근거 자료가 됩니다.
---
## 8. 과제 (Assignment)
## 9. 트러블슈팅 (Troubleshooting)
- **DeepSpeed/FSDP 실행 시 `CUDA out of memory` 오류가 계속 발생하나요?**
- **원인 분석:** 배치 사이즈, 모델 크기 대비 GPU 메모리가 부족한 경우입니다. ZeRO-3나 FSDP를 사용하더라도, 중간 연산 결과물이나 활성화 함수 출력값이 메모리를 차지하기 때문입니다.
- **해결 방안:**
- `per_device_train_batch_size`를 더 줄여보세요.
- `gradient_accumulation_steps`를 늘려서, 작은 배치로 여러 번 학습한 그래디언트를 모아 한 번에 파라미터를 업데이트하세요. (배치 사이즈를 늘리는 것과 유사한 효과)
- CPU 오프로딩(offloading) 기능을 활성화하여 GPU 메모리가 부족할 때 일부 텐서를 CPU 메모리로 옮기는 옵션을 사용해보세요. (학습 속도는 느려질 수 있습니다.)
- **양자화(Quantization) 후 모델 정확도가 너무 많이 떨어지나요?**
- **원인 분석:** PTQ(Post-Training Quantization)는 학습 없이 양자화를 수행하므로, 모델의 특정 레이어가 양자화에 민감하게 반응하여 오차가 크게 발생할 수 있습니다.
- **해결 방안:**
- **QAT (Quantization-Aware Training):** 양자화를 고려하여 추가로 모델을 미세 조정(fine-tuning)하면 정확도 손실을 크게 줄일 수 있습니다.
- **부분 양자화 (Partial Quantization):** 전체 모델이 아닌, 특정 레이어(주로 Conv, Linear)에만 양자화를 적용하거나, 성능 하락에 민감한 레이어는 양자화에서 제외해보세요.
- **Triton 서버에서 모델 로드에 실패하나요?**
- **원인 분석:** 모델 저장소의 디렉토리 구조가 잘못되었거나, `config.pbtxt` 파일의 설정이 올바르지 않은 경우가 대부분입니다.
- **해결 방안:**
- [Triton 모델 저장소 문서](https://github.com/triton-inference-server/server/blob/main/docs/user_guide/model_repository.md)를 참고하여 `config.pbtxt``name`, `platform`, `input`, `output` 설정이 정확한지 다시 한 번 확인하세요.
- 모델 파일의 버전 디렉토리(예: `1/model.pt`)가 제대로 생성되었는지 확인하세요.
1. **분산 학습 전략 선택**: 매우 큰 언어 모델(LLM)을 학습시켜야 하는 상황입니다. PyTorch FSDP와 Microsoft DeepSpeed 중 하나를 선택해야 한다면, 어떤 기준(최소 2가지)으로 기술을 선택할 것이며 그 이유는 무엇인가요?
2. **모델 경량화 전략 수립**: 여러분이 개발한 이미지 분류 모델을 스마트폰 앱에 탑재해야 합니다. 모델의 성능 저하를 최소화하면서 용량과 추론 속도를 개선하기 위한 경량화 전략을 2가지 이상 조합하여 제시해 보세요. (예: 지식 증류 후 양자화 적용)
---
## 10. 개념 확인 퀴즈 (Concept Check Quiz)
1. DeepSpeed의 ZeRO-2와 ZeRO-3 최적화 레벨의 가장 큰 차이점은 무엇인가요?
2. 모델의 가중치를 FP32에서 INT8로 바꾸는 경량화 기법의 이름은 무엇이며, 이로 인해 얻을 수 있는 가장 큰 장점 두 가지는 무엇인가요?
3. 동적 배치(Dynamic Batching)가 추론 서버의 처리량(Throughput)을 높이는 원리를 간략히 설명하세요.
---
## 9. 되짚어보기 (Summary)
## 11. 과제 (Assignment)
1. **지식 증류 시나리오**: 여러분이 매우 정확하지만, 너무 크고 느려서 모바일 앱에 탑재할 수 없는 이미지 분류 모델(선생님 모델)을 가지고 있다고 가정해 보세요. 이 지식을 가볍고 빠른 MobileNetV2(학생 모델)에 전달하는 '지식 증류' 학습 과정을 단계별로 설명해 보세요.
2. **벤치마킹 계획 수립**: "실시간 동영상 배경 제거 API"를 개발해야 하는 상황입니다. 모델 최적화 전후 성능을 비교하기 위한 벤치마킹 계획을 수립하세요. (측정할 핵심 지표, 사용할 도구, 테스트 시나리오 포함)
3. **Triton `config.pbtxt` 작성**: `ResNet-50` PyTorch 모델을 Triton 서버에 배포하려고 합니다. 이 모델은 224x224 크기의 3채널 이미지(`FP32` 타입)를 입력받아, 1000개의 클래스에 대한 로짓(`FP32` 타입)을 출력합니다. 이 모델을 위한 최소한의 `config.pbtxt` 파일을 작성해보세요.
---
이번 파트에서는 대규모 AI 모델을 현실적인 제약 속에서 효율적으로 다루기 위한 핵심 기술들을 배웠습니다.
## 12. 되짚어보기 (Summary)
- **효율적 분산 학습**: 단일 GPU의 한계를 넘어 거대 모델을 학습시키기 위한 데이터 병렬 처리와 모델 병렬 처리의 개념을 이해하고, PyTorch FSDP와 Microsoft DeepSpeed와 같은 최신 분산 학습 프레임워크의 작동 방식과 특징을 비교 분석했습니다.
- **모델 경량화 기법**: 학습된 모델을 더 빠르고 가볍게 만들기 위한 양자화(Quantization), 가지치기(Pruning), 지식 증류(Knowledge Distillation)의 원리와 장단점을 파악했습니다.
- **고성능 추론 서버**: 대규모 트래픽 환경에서 모델을 안정적으로 서빙하기 위한 NVIDIA Triton과 TorchServe의 필요성을 이해하고, 동적 배치와 같은 핵심 기능들을 학습했습니다.
이번 파트에서는 대규모 AI 모델을 다루기 위한 분산 학습, 모델 경량화, 고성능 서빙 기술들을 학습했습니다. FSDP와 DeepSpeed를 통해 단일 GPU의 한계를 넘어 거대한 모델을 효율적으로 학습시키는 방법을 알아보았고, 양자화, 가지치기, 지식 증류와 같은 경량화 기법으로 모델의 서빙 효율을 높이는 전략을 배웠습니다. 마지막으로, Triton과 TorchServe 같은 전문 추론 서버를 활용하여 프로덕션 환경에서 모델을 안정적이고 빠르게 서빙하는 방법을 살펴보았습니다. 이 모든 기술은 제한된 리소스 안에서 AI의 가능성을 최대로 끌어내기 위한 핵심적인 도구입니다.
---
## 10. 더 깊이 알아보기 (Further Reading)
- [PyTorch FSDP 공식 튜토리얼](https://pytorch.org/tutorials/intermediate/FSDP_tutorial.html): FSDP를 시작하는 상세한 가이드
- [DeepSpeed 공식 문서](https://www.deepspeed.ai/): ZeRO를 포함한 다양한 최적화 기능 소개
- [NVIDIA Triton Inference Server 문서](https://developer.nvidia.com/triton-inference-server): Triton의 아키텍처 및 사용법
- [The Illustrated Guide to Model Quantization (블로그)](https://arxiv.org/abs/2403.15933): 모델 양자화의 개념을 쉽게 설명한 자료
## 13. 더 깊이 알아보기 (Further Reading)
- [PyTorch FSDP 공식 튜토리얼](https://pytorch.org/tutorials/intermediate/FSDP_tutorial.html)
- [DeepSpeed 공식 튜토리얼](https://www.deepspeed.ai/tutorials/)
- [NVIDIA Triton Inference Server 공식 문서](https://developer.nvidia.com/triton-inference-server)
- [PyTorch Quantization 공식 문서](https://pytorch.org/docs/stable/quantization.html)
---
**➡️ 다음 시간: [Part 13: 생성형 AI 및 AI 에이전트 심화](../13_generative_ai/part_13_generative_ai.md)**
### 참고 자료
* [Hugging Face Optimum](https://huggingface.co/docs/optimum/index): 다양한 하드웨어 가속기에서 트랜스포머 모델을 효율적으로 학습하고 추론하기 위한 라이브러리
* [NVIDIA TensorRT](https://developer.nvidia.com/tensorrt): NVIDIA GPU에서 딥러닝 추론 성능을 극대화하기 위한 SDK
---
### ⚠️ What Could Go Wrong? (토론 주제)
모델 최적화는 단순히 모델을 작고 빠르게 만드는 기술을 넘어, 비즈니스 목표와 긴밀하게 연결된 복잡한 트레이드오프 과정입니다.
1. **성능과 정확도의 트레이드오프 실패 (Failure in Performance-Accuracy Trade-off)**
* 모델을 경량화(양자화, 가지치기)하여 서빙 지연 시간(Latency)을 줄이는 데만 집중하다가, 비즈니스 핵심 지표(예: 추천 시스템의 클릭률, 사기 탐지 모델의 재현율)에 심각한 영향을 미칠 정도로 모델 정확도가 떨어지는 경우가 있습니다.
* '이 정도 정확도 하락은 괜찮다'와 '이 정도 속도 향상은 의미 있다'의 기준은 어떻게 설정해야 할까요? 이 트레이드오프를 검증하기 위해 어떤 A/B 테스트를 설계해야 할까요?
2. **벤치마크 환경의 함정 (The Pitfall of the Benchmarking Environment)**
* 내 로컬 개발 장비(예: 고성능 GPU)에서 측정한 "10배 성능 향상!"이라는 결과가, 실제 프로덕션 환경(예: CPU 기반의 오토스케일링 인스턴스, 수많은 동시 요청이 몰리는 상황)에서는 전혀 재현되지 않는 경우가 많습니다.
* 신뢰할 수 있는 성능 벤치마킹을 위해, 테스트 환경이 실제 서빙 환경의 어떤 특성(예: 하드웨어, 배치 사이즈, 동시성)을 반영해야 할까요?
3. **특정 하드웨어에 대한 과잉 최적화 (Over-optimization for Specific Hardware)**
* NVIDIA의 TensorRT나 AWS의 Inferentia와 같이 특정 벤더의 하드웨어와 라이브러리에 맞춰 모델을 깊게 최적화하면, 다른 종류의 하드웨어(예: 다른 클라우드, 온프레미스 서버, 엣지 디바이스)에서는 모델을 실행할 수 없거나 성능이 오히려 떨어지는 '벤더 종속성' 문제가 발생합니다.
* 이식성(Portability)을 유지하면서 성능을 최적화할 수 있는 중간 지점(예: ONNX)은 어디이며, 어떤 장단점을 가질까요?
4. **복잡성 증가로 인한 유지보수 비용 (Increased Maintenance Cost due to Complexity)**
* 원본 FP32 모델뿐만 아니라, 양자화된 INT8 모델, 프루닝된 모델, ONNX 변환 모델 등 관리해야 할 모델 아티팩트가 여러 개로 늘어납니다. 이로 인해 MLOps 파이프라인이 훨씬 복잡해지고, 디버깅 및 유지보수 비용이 증가합니다.
* 모델 최적화가 오히려 전체 시스템의 안정성을 해치고 기술 부채를 늘리는 상황을 어떻게 피할 수 있을까요?
5. **"최적화는 필요 없다"는 착각 (The Illusion that "Optimization is Not Needed")**
* "요즘 하드웨어는 워낙 좋고 클라우드는 무한 확장 가능하니, 모델 최적화에 드는 노력보다 그냥 더 좋은 인스턴스를 쓰는 게 낫다"는 주장이 있습니다. 이 주장의 맹점은 무엇일까요?
* 모델 최적화가 단순히 '속도 개선'을 넘어, '서빙 비용 절감', '전력 효율성 증대(ESG)', '온디바이스(On-device) AI 구현' 등 더 넓은 비즈니스 가치를 가질 수 있다는 점을 어떻게 설득할 수 있을까요?
\ No newline at end of file
......@@ -259,38 +259,127 @@ LangSmith는 개발 초기 단계의 디버깅부터 프로덕션 환경의 모
---
## 7. 개념 확인 퀴즈 (Concept Check Quiz)
## 7. 캡스톤 프로젝트 연계 미니 프로젝트: 고급 RAG 기술을 적용한 'AI 논문 Q&A 봇' 만들기
단순한 챗봇을 넘어, 특정 전문 분야의 지식을 깊이 있게 이해하고 답변하는 AI를 만드는 것은 생성형 AI의 핵심적인 활용 사례입니다. 이번 미니 프로젝트에서는 여러 편의 AI 논문을 학습하여, 사용자의 복잡한 질문에 대해 근거를 찾아 답변하는 'AI 논문 Q&A 봇'을 만듭니다. 이 과정에서 고급 RAG 기술을 직접 구현하고 LangSmith로 내부 동작을 추적하며, 캡스톤 프로젝트의 기술적 완성도를 한 단계 끌어올립니다.
### 프로젝트 목표
- 특정 PDF 문서들을 기반으로 질문에 답변하는 RAG 기반의 Q&A 챗봇을 구축할 수 있습니다.
- 쿼리 변환, 재순위화 등 최소 1개 이상의 고급 RAG 기술을 적용하여 답변의 정확성과 관련성을 향상시킬 수 있습니다.
- LangSmith를 사용하여 챗봇의 전체 실행 과정을 추적하고 디버깅하며, 각 컴포넌트의 상호작용을 분석할 수 있습니다.
### 개발 과정
1. **데이터 준비**:
- `ArXiv` 등에서 관심 있는 최신 AI 논문 3~5편의 PDF 파일을 다운로드합니다.
- LangChain의 `PyMuPDFLoader`와 같은 문서 로더를 사용하여 PDF를 텍스트로 변환하고, 적절한 크기의 청크(Chunk)로 분할합니다.
- 분할된 텍스트 청크를 임베딩하여 로컬 벡터 저장소(예: `FAISS`, `ChromaDB`)에 저장합니다.
2. **고급 RAG 파이프라인 구현**:
- **(기본)** 먼저 저장된 벡터 저장소를 기반으로 기본적인 Retriever를 구현합니다.
- **(택 1)** 아래의 고급 RAG 기법 중 하나를 선택하여 파이프라인을 개선합니다.
- **옵션 A (쿼리 확장)**: `MultiQueryRetriever`를 사용하여, 사용자의 질문을 LLM이 여러 개의 다른 질문으로 확장하고, 각 질문에 대한 검색 결과를 종합하여 더 풍부한 컨텍스트를 확보합니다.
- **옵션 B (재순위화)**: 기본적인 Retriever가 1차로 검색해 온 문서 목록을, `CrossEncoder` 기반의 재순위화 모델(`CohereRerank` 또는 `bge-reranker-base` 등)을 사용하여 질문과 가장 관련성이 높은 순서로 재정렬합니다.
3. **LCEL을 이용한 체인 구성**:
- LangChain Expression Language (LCEL)를 사용하여 전체 파이프라인을 구성합니다.
- `(사용자 질문) -> (고급 RAG Retriever) -> (검색된 문서 컨텍스트 + 질문을 프롬프트에 삽입) -> (LLM) -> (최종 답변)`의 흐름으로 체인을 설계합니다.
4. **LangSmith 연동 및 분석**:
- LangSmith 사용을 위한 환경 변수를 설정하고, 챗봇을 실행하여 여러 질문을 테스트합니다.
- LangSmith 대시보드에서 생성된 트레이스(Trace)를 확인합니다.
- `MultiQueryRetriever`를 사용했다면, 원본 질문이 어떻게 여러 질문으로 확장되었는지 확인합니다.
- `Re-ranker`를 사용했다면, 초기 검색 결과와 재순위화된 후의 문서 순서가 어떻게 바뀌었는지 비교 분석합니다.
- 최종적으로 LLM에 전달된 전체 프롬프트(컨텍스트 포함) 내용을 확인하며 챗봇의 답변 생성 과정을 디버깅합니다.
### 캡스톤 프로젝트 연계 방안
- 이 Q&A 봇은 '사내 규정 안내 봇', '법률 문서 검색 어시스턴트', '개인화된 학습 자료 추천 봇' 등 여러분의 캡스톤 프로젝트에서 핵심 기능으로 활용될 수 있습니다.
- 고급 RAG 기법의 구현 및 디버깅 경험은 현재 가장 수요가 높은 LLM 애플리케이션 개발 역량을 증명하는 좋은 사례가 될 것입니다.
- LangSmith를 활용한 추적 및 평가는 단순한 기능 구현을 넘어, 서비스의 신뢰성과 운영 효율성까지 고려하는 프로페셔널한 개발 접근법을 보여줍니다.
1. LLM을 파인튜닝할 때 전체 파라미터를 학습시키는 것과 비교하여 LoRA 방식이 갖는 가장 큰 장점은 무엇인가요?
2. 고급 RAG 파이프라인에서 '재순위화(Re-ranking)' 단계가 필요한 이유는 무엇인가요?
3. AI 에이전트의 ReAct 프레임워크에서 'Thought', 'Action', 'Observation'의 순환 구조는 어떤 역할을 하나요?
---
## 8. 트러블슈팅 (Troubleshooting)
- **LoRA 파인튜닝 후 모델 성능이 오히려 떨어지거나, 이상한 답변을 생성하나요?**
- **학습률(Learning Rate)이 너무 높을 수 있습니다.** 파인튜닝은 매우 낮은 학습률(e.g., `1e-5` ~ `5e-4`)로 시작하는 것이 일반적입니다. 학습률이 너무 높으면 사전 학습된 지식이 손상될 수 있습니다.
- **LoRA 설정값을 조정해보세요.** `r` (rank)과 `lora_alpha`는 중요한 하이퍼파라미터입니다. `alpha``r`의 2배 정도로 설정하는 것이 일반적인 출발점입니다. (`lora_alpha=32`, `r=16`)
- **학습 데이터의 품질을 확인하세요.** 적은 양이라도 품질이 낮은 데이터(e.g., 잘못된 형식, 관련 없는 내용)가 포함되면 모델 성능에 악영향을 줍니다.
- **RAG의 재순위화(Re-ranking) 단계에서 관련 없는 문서가 더 높은 점수를 받나요?**
- **Cross-Encoder 모델이 현재 도메인에 적합하지 않을 수 있습니다.** 일반적인 목적으로 학습된 Cross-Encoder는 특정 전문 분야의 미묘한 의미 차이를 잘 잡아내지 못할 수 있습니다. 해당 도메인의 데이터로 파인튜닝된 Cross-Encoder를 사용하거나, 직접 파인튜닝하는 것을 고려해볼 수 있습니다.
- **AI 에이전트가 의도와 다른 도구(Tool)를 사용하거나, 무한 루프에 빠지나요?**
- **프롬프트가 명확하지 않기 때문일 가능성이 높습니다.** ReAct 프롬프트에서 각 도구의 역할과 사용법(인풋 형식 등)을 매우 명확하고 상세하게 설명해야 합니다. "이 도구는 이런 상황에서, 이런 형식의 입력을 받아, 이런 결과를 반환한다" 와 같이 구체적으로 명시해주세요.
- **생각(Thought) 단계를 강제하세요.** LLM이 충분히 생각하지 않고 성급하게 행동을 결정하는 것을 방지하기 위해, 프롬프트에 "먼저 다음 행동 계획을 신중하게 생각하고, 그 다음에 행동을 결정하라"는 지시를 명시적으로 추가하는 것이 효과적입니다.
- **LangGraph 실행 시 `RecursionError`가 발생하나요?**
- 이는 그래프의 조건부 엣지(conditional edge) 로직이 잘못되어 두 개 이상의 노드를 계속해서 오가는 무한 루프가 발생했음을 의미합니다. 각 노드의 실행 후 상태(State)를 면밀히 추적하고, 조건부 엣지의 분기 조건이 명확하게 종료(END) 상태로 이어질 수 있도록 로직을 수정해야 합니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 8. 과제 (Assignment)
## 9. 개념 확인 퀴즈 (Concept Check Quiz)
1. **고급 RAG 파이프라인 설계**: 여러분 회사의 내부 문서를 기반으로 답변하는 Q&A 챗봇을 만들려고 합니다. 사용자의 질문이 모호하거나 너무 구체적인 경우에도 정확한 답변을 찾을 수 있도록, 본문에서 배운 '쿼리 변환' 기법 중 2가지 이상을 조합하여 RAG 파이프라인을 어떻게 개선할지 구체적인 예시와 함께 설명해 주세요.
2. **AI 에이전트 설계**: LangGraph를 사용하여 '오늘 날씨를 확인하고, 날씨에 맞는 옷차림을 추천해 주는' 간단한 AI 에이전트를 설계해 보세요. 어떤 노드(Node)들이 필요하고, 노드 간의 엣지(Edge)는 어떻게 연결되어야 할지 순서도나 의사 코드로 표현해 보세요.
1. PEFT, 특히 LoRA 방식이 전체 파인튜닝(Full Fine-tuning)에 비해 가지는 3가지 주요 이점은 무엇인가요?
2. 고급 RAG 파이프라인에서 '재순위화(Re-ranking)' 단계가 필요한 이유는 무엇이며, 이는 기존의 벡터 검색(Retrieval)과 어떻게 다른가요?
3. LangGraph가 기존의 LangChain Expression Language (LCEL)과 비교하여 AI 에이전트 구축에 더 적합한 이유는 무엇인가요?
---
## 9. 되짚어보기 (Summary)
## 10. 과제 (Assignment)
이번 파트에서는 생성형 AI의 최전선에 있는 기술들과, 스스로 문제를 해결하는 자율 AI 에이전트를 구축하는 방법을 탐험했습니다.
1. **LoRA 파인튜닝 설계**: 여러분이 Llama 3 8B 모델을 '법률 자문' 챗봇으로 파인튜닝하려고 합니다. Hugging Face의 `peft` 라이브러리를 사용한다고 가정할 때, 어떤 `LoraConfig`를 사용할지 예시를 작성하고, 왜 `target_modules`를 특정 레이어(예: `q_proj`, `k_proj`, `v_proj`)에 주로 적용하는지 이유를 설명해 보세요.
2. **AI 에이전트 프롬프트 작성**: '오늘 서울의 날씨를 확인하고, 날씨가 비가 오면 우산을 챙기라는 메시지를, 맑으면 좋은 하루 보내라는 메시지를 반환하는' AI 에이전트를 만든다고 가정해 보세요. 이 에이전트를 위한 ReAct 프롬프트의 '생각(Thought)' 부분을 어떻게 작성할지 예시를 보여주세요. (사용 가능한 도구: `search_weather(city: str)`)
- **효율적 LLM 파인튜닝**: PEFT의 개념을 이해하고, 대표적인 기법인 LoRA를 사용하여 적은 리소스로 LLM을 특정 작업에 맞게 효율적으로 튜닝하는 방법을 배웠습니다.
- **RAG 심화**: 기본적인 RAG를 넘어 쿼리 변환, 재순위화 등의 고급 기술을 통해 검색 증강 생성의 정확도를 한 단계 끌어올리는 방법을 확인했습니다.
- **자율 AI 에이전트**: LLM을 두뇌로 사용하는 AI 에이전트의 개념을 이해하고, ReAct 프레임워크와 LangGraph를 사용하여 복잡한 작업을 수행하는 에이전트를 설계하고 구축하는 방법을 학습했습니다.
- **LLMOps & AgentOps**: LLM 기반 애플리케이션의 안정적인 운영을 위한 LLMOps의 개념과 LangSmith를 활용한 추적, 모니터링, 평가 방법을 이해했습니다.
---
## 11. 되짚어보기 (Summary)
이번 파트에서는 생성형 AI와 자율 에이전트의 최신 기술들을 깊이 있게 탐구했습니다.
- **효율적 LLM 파인튜닝**: PEFT와 LoRA의 원리를 이해하고, 적은 리소스로 LLM을 특정 작업에 맞게 효율적으로 튜닝하는 방법을 배웠습니다.
- **고급 RAG 기술**: 쿼리 변환과 재순위화 기법을 통해 RAG 시스템의 검색 정확도를 한 단계 높여, 더 신뢰도 높은 답변을 생성하는 방법을 확인했습니다.
- **자율 AI 에이전트**: ReAct 프레임워크를 통해 에이전트의 행동 원리를 이해하고, LangGraph를 사용하여 여러 에이전트가 협력하는 복잡하고 순환적인 워크플로우를 구축하는 방법을 탐구했습니다.
- **LLMOps/AgentOps**: LLM과 에이전트 기반 애플리케이션의 운영 및 평가를 위한 LLMOps의 개념과 LangSmith와 같은 전문 도구의 필요성을 이해했습니다.
---
## 10. 더 깊이 알아보기 (Further Reading)
- [Hugging Face PEFT 라이브러리](https://huggingface.co/docs/peft/index): LoRA 등 다양한 PEFT 기법 구현체
- [LangChain RAG 문서](https://python.langchain.com/v0.2/docs/concepts/#retrieval-augmented-generation-rag): 쿼리 변환, 재순위화 등 다양한 RAG 관련 기능 소개
- [LangGraph 공식 문서](https://langchain-ai.github.io/langgraph/): 순환적인 에이전트 워크플로우 구축 가이드
- [LangSmith 공식 문서](https://docs.smith.langchain.com/): LLM 애플리케이션 추적 및 평가 플랫폼
## 12. 더 깊이 알아보기 (Further Reading)
- [Hugging Face PEFT 라이브러리 공식 문서](https://huggingface.co/docs/peft)
- [Pinecone: RAG-Fusion 설명 블로그](https://www.pinecone.io/learn/rag-fusion/)
- [LangChain: ReAct 프레임워크 설명](https://python.langchain.com/docs/modules/agents/concepts/react)
- [LangGraph 공식 문서](https://langchain-ai.github.io/langgraph/): 다양한 에이전트 아키텍처 예제 포함
---
**➡️ 다음 시간: [Part 14: AI 윤리 및 거버넌스 실무](../14_ai_ethics/part_14_ai_ethics.md)**
### 참고 자료
* [LangChain 공식 문서](https://python.langchain.com/docs/get_started/introduction): LangChain 라이브러리의 공식 문서
* [Awesome Agents](https://github.com/kyrolabs/awesome-agents): AI 에이전트 관련 최신 논문, 리소스 모음
---
### ⚠️ What Could Go Wrong? (토론 주제)
생성형 AI와 에이전트 기술은 엄청난 가능성을 보여주지만, 동시에 통제 불가능성, 비용, 저작권 등 수많은 잠재적 위험을 안고 있습니다.
1. **환각 현상의 통제 불가능성 (Uncontrollability of Hallucinations)**
* RAG(Retrieval-Augmented Generation)나 정교한 프롬프트 엔지니어링을 통해 LLM이 사실이 아닌 내용을 그럴듯하게 지어내는 '환각'을 줄일 수는 있지만, 완전히 없애는 것은 현재 기술로는 거의 불가능합니다.
* 환각 현상이 치명적인 오류로 이어질 수 있는 서비스(예: 법률 자문 챗봇, 의료 정보 제공)에 생성형 AI를 어떻게 '안전하게' 적용할 수 있을까요? '사실 기반' 응답과 '창의적' 응답의 경계를 어떻게 제어해야 할까요?
2. **데이터 오염과 저작권 문제 (Data Contamination and Copyright Issues)**
* LLM이 인터넷의 저작권 있는 자료(코드, 기사, 이미지)를 학습하여 매우 유사한 결과물을 생성했을 때, 이는 저작권 침해에 해당할까요? 서비스 제공자는 어떤 법적 책임을 져야 할까요?
* AI가 대량으로 생성한 저품질 콘텐츠가 다시 인터넷에 확산되고, 다음 세대 AI가 그 오염된 데이터를 학습하게 되는 '모델 자가 중독(Model Autophagy)' 현상은 AI 생태계에 어떤 장기적인 위험을 초래할까요?
3. **예측 불가능한 비용 문제 (Unpredictable Cost Issues)**
* 복잡한 에이전트 시스템에서, 사용자의 간단한 질문 하나가 내부적으로 수십, 수백 번의 LLM API 호출로 이어져 막대한 비용을 발생시킬 수 있습니다. (예: "A와 B를 비교 분석하고, 그 결과를 바탕으로 C에 대한 리포트를 써줘.")
* 프로덕션 환경에서 생성형 AI 서비스의 운영 비용을 어떻게 안정적으로 예측하고 통제할 수 있을까요? 사용자별로 토큰 사용량을 제한하거나, 비용 효율적인 모델(오픈소스 소형 모델)을 혼합하는 전략의 장단점은 무엇일까요?
4. **사용자의 악의적인 이용 (Malicious Use by Users)**
* 사용자가 생성형 AI를 악용하여 가짜 뉴스, 스팸 메일, 피싱 공격 스크립트, 딥페이크 등 유해하거나 불법적인 콘텐츠를 대량으로 생산하는 문제를 어떻게 막을 수 있을까요?
* 서비스 제공자 입장에서, 이러한 악용을 방지하기 위해 어떤 기술적, 정책적 안전장치(예: 입력/출력 필터링, 워터마킹, 사용자 행위 모니터링)를 마련해야 할까요? 자유로운 활용과 통제 사이의 균형점은 어디일까요?
5. **고정된 지식과 최신성 부족 (Fixed Knowledge and Lack of Timeliness)**
* 대부분의 LLM은 특정 시점(Knowledge Cutoff)까지의 데이터로 학습되어, 그 이후의 최신 정보를 알지 못합니다. RAG로 이를 보완할 수 있지만, 실시간으로 빠르게 변하는 정보(예: 주식 시세, 뉴스 속보)를 다뤄야 하는 서비스에 LLM을 적용할 때의 근본적인 한계는 무엇일까요?
\ No newline at end of file
......@@ -172,7 +172,60 @@ EU AI Act는 유럽 시장에 AI 제품이나 서비스를 제공하는 모든
---
## 7. 개념 확인 퀴즈 (Concept Check Quiz)
## 7. 캡스톤 프로젝트 연계 미니 프로젝트: "대출 심사 모델을 위한 편향성 분석 및 완화 보고서" 작성
> Part 14에서 학습한 AI 윤리 개념을 실제 시나리오에 적용하여, 모델의 편향성을 탐지하고 완화하며 그 과정을 문서화하는 미니 프로젝트를 진행합니다.
**프로젝트 목표:**
가상의 '대출 심사 AI 모델'의 예측 결과를 분석하여, 특정 인구 통계 그룹(예: 성별, 연령대)에 대한 편향성이 존재하는지 Fairlearn을 사용해 탐지하고, 완화 기법을 적용해 본 후, 결과를 '모델 카드' 형식의 보고서로 작성하여 AI 시스템의 투명성을 확보하는 경험을 쌓습니다.
**주요 구현 단계:**
1. **시나리오 설정 및 데이터 준비**
* 가상의 대출 신청자 데이터(나이, 소득, 부채, 신용 점수, 성별 등)와 모델의 예측 결과(대출 승인/거절)를 생성하거나 다운로드합니다. (예: `scikit-learn``make_classification`으로 데이터 생성 후, 특정 그룹에 의도적인 편향 주입)
* '성별' 또는 '연령대'를 민감 특성(sensitive feature)으로 지정합니다.
2. **편향성 탐지 및 측정 (Fairlearn `MetricFrame`)**
* `fairlearn.metrics``MetricFrame`을 사용하여 모델의 전반적인 성능(예: `accuracy_score`)과 민감 특성 그룹별 성능을 비교 분석합니다.
* 주요 공정성 지표(Demographic Parity, Equalized Odds 등)를 계산하고, 그룹 간 차이가 통계적으로 유의미한지 확인합니다.
* Fairlearn의 시각화 기능을 사용하여 그룹 간 성능 불균형을 대시보드 형태로 시각화하고, 어떤 그룹이 불이익을 받는지 직관적으로 파악합니다.
3. **편향성 완화 알고리즘 적용**
* Fairlearn에서 제공하는 후처리(Post-processing) 기법인 `ThresholdOptimizer`를 적용해 봅니다.
* 이 기법은 원본 모델을 수정하지 않고, 각 그룹에 대한 예측 임계값을 조정하여 공정성 지표를 개선합니다.
* 완화 기법 적용 전과 후의 정확도 및 공정성 지표를 비교하여, '정확도-공정성 트레이드오프'를 확인하고 분석합니다.
4. **모델 카드 초안 작성**
* 분석 및 완화 과정의 결과를 바탕으로 모델 카드의 일부를 작성합니다.
* **[용도]**: 모델의 사용 목적과 범위를 명시합니다.
* **[성능 지표]**: 전체 및 그룹별 정확도, 그리고 완화 전후의 공정성 지표 변화를 명확하게 기술합니다.
* **[윤리적 고려사항]**: 발견된 편향의 종류, 잠재적 위험, 그리고 편향 완화를 위해 어떤 노력을 기울였는지 서술합니다. 이 과정을 통해 모델의 한계점을 투명하게 공유합니다.
**캡스톤 프로젝트 연계 방안:**
이 미니 프로젝트는 여러분의 캡스톤 프로젝트에서 모델의 '신뢰성'과 '책임성' 파트를 강화하는 데 직접적으로 활용될 수 있습니다. 최종 모델에 대해 편향성 분석을 수행하고, 그 결과를 모델 카드 형식으로 문서화하여 발표 자료에 포함시키세요. 이는 단순한 모델 성능 경쟁을 넘어, 실제 산업 현장에서 요구되는 '책임감 있는 AI 개발자'로서의 역량을 보여주는 강력한 차별화 포인트가 될 것입니다.
---
## 8. 트러블슈팅 (Troubleshooting)
- **SHAP 계산이 너무 오래 걸리거나 메모리가 부족한가요?**
- SHAP은 정확한 계산을 위해 많은 연산을 필요로 합니다. 특히 `KernelExplainer`는 모든 피처 조합을 고려하므로 느릴 수 있습니다.
- **데이터 샘플링**: 설명할 데이터의 양이 많다면, 대표성을 가질 수 있는 일부 데이터(e.g., 100~1000개)를 샘플링하여 계산 시간을 줄일 수 있습니다.
- **Tree-based 모델**: 설명하려는 모델이 XGBoost, LightGBM, CatBoost와 같은 트리 기반 모델이라면, 이에 최적화된 `TreeExplainer`를 사용하세요. `KernelExplainer`보다 훨씬 빠르고 정확한 SHAP 값을 계산합니다.
- **Fairlearn의 완화 알고리즘을 적용한 후 모델 정확도가 너무 많이 떨어졌나요?**
- 공정성과 정확도는 종종 **트레이드오프(trade-off)** 관계에 있습니다. 편향성을 강하게 보정할수록 모델의 전반적인 성능은 떨어질 수 있습니다. `GridSearch`와 같은 인프로세싱 알고리즘을 사용하여, 허용 가능한 수준의 정확도 감소 범위 내에서 최적의 공정성-정확도 균형점을 찾는 것이 중요합니다.
- **모델 카드를 작성하려고 하니, 어떤 내용을 채워야 할지 막막한가요?**
- 처음부터 완벽한 문서를 만들려고 하기보다, 채울 수 있는 항목부터 시작하는 것이 좋습니다. Hugging Face Hub에 올라와 있는 다른 모델들의 `Model Card`를 참고하는 것이 가장 좋은 방법입니다. 특히 [Google의 모델 카드 템플릿](https://modelcards.withgoogle.com/model-card-toolkit)이나 [Hugging Face의 템플릿](https://huggingface.co/docs/hub/model-cards)을 살펴보면 어떤 내용을 어떤 수준으로 기술해야 하는지 감을 잡는 데 도움이 됩니다.
- **LIME 설명 결과가 실행할 때마다 조금씩 바뀌나요?**
- LIME은 설명할 데이터 포인트 주변을 **무작위로 샘플링**하여 지역적인 모델을 학습시키기 때문에, 실행 시마다 결과가 조금씩 달라질 수 있습니다. 이는 LIME의 본질적인 특성입니다. 결과의 안정성을 높이려면 `num_samples` 파라미터를 늘려 더 많은 샘플을 사용하는 것을 고려해볼 수 있지만, 계산 시간도 함께 늘어납니다.
더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.
- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**
---
## 9. 개념 확인 퀴즈 (Concept Check Quiz)
1. XAI 기법인 LIME과 SHAP의 가장 큰 차이점은 무엇인가요? (설명의 범위 관점에서)
2. AI의 공정성 지표 중 'Demographic Parity'와 'Equalized Odds'는 각각 무엇을 의미하며, 어떤 상황에서 서로 다른 결론을 내릴 수 있을까요?
......@@ -180,14 +233,14 @@ EU AI Act는 유럽 시장에 AI 제품이나 서비스를 제공하는 모든
---
## 8. 과제 (Assignment)
## 10. 과제 (Assignment)
1. **공정성 문제 해결 시나리오**: 여러분이 은행의 대출 심사 AI 모델 개발자라고 가정해 봅시다. 모델이 특정 그룹에게 불리한 결정을 내린다는 의심이 제기되었습니다. Fairlearn 라이브러리를 사용하여 이 문제를 (1)탐지하고, (2)측정하고, (3)완화하기 위한 단계를 구체적으로 설명해 보세요.
2. **모델 카드 작성 연습**: 여러분이 이전에 만들었던 프로젝트(또는 가상의 이미지 분류 모델)에 대한 '모델 카드'의 주요 항목(용도, 성능 지표, 학습 데이터, 윤리적 고려사항)을 간략하게 채워보는 연습을 해보세요.
---
## 9. 되짚어보기 (Summary)
## 11. 되짚어보기 (Summary)
이번 파트에서는 AI를 더 똑똑하게 만드는 것을 넘어, 더 올바르고 공정하게 만들기 위한 AI 윤리와 거버넌스 실무에 대해 학습했습니다.
......@@ -197,7 +250,7 @@ EU AI Act는 유럽 시장에 AI 제품이나 서비스를 제공하는 모든
---
## 10. 더 깊이 알아보기 (Further Reading)
## 12. 더 깊이 알아보기 (Further Reading)
- [LIME 원문 논문 (KDD 2016)](https://arxiv.org/abs/1602.04938)
- [SHAP 원문 논문 (NeurIPS 2017)](https://arxiv.org/abs/1705.07874)
- [Fairlearn 공식 문서](https://fairlearn.org/): 공정성 측정 및 완화 알고리즘 가이드
......@@ -206,4 +259,37 @@ EU AI Act는 유럽 시장에 AI 제품이나 서비스를 제공하는 모든
---
### 참고 자료
* [Partnership on AI](https://partnershiponai.org/): AI의 미래에 대한 연구와 논의를 선도하는 비영리 단체
* [AI 윤리 정책 동향](https://www.nia.or.kr/nia/re/sub/re_15_1.do?boardId=10406): 한국지능정보사회진흥원(NIA)에서 제공하는 국내외 AI 윤리 정책 동향
---
### ⚠️ What Could Go Wrong? (토론 주제)
AI 윤리 원칙을 선언하는 것은 쉽지만, 그것을 실제 제품과 조직 문화에 내재화하는 과정은 수많은 난관과 딜레마로 가득 차 있습니다.
1. **'윤리 워싱'의 함정 (The "Ethics Washing" Trap)**
* 기업이 실질적인 기술적/조직적 변화 없이, 마케팅이나 PR 목적으로만 'AI 윤리 위원회'를 만들거나 '책임감 있는 AI 원칙'을 발표하는 경우가 있습니다. 진정성 있는 AI 윤리 실천과 '보여주기식' 활동을 어떻게 구분할 수 있을까요?
* 개발자 입장에서, 우리 회사의 AI 윤리 활동이 '워싱'이 아니라고 자신 있게 말하려면 어떤 조건이 충족되어야 할까요?
2. **편향 탐지 및 완화의 한계 (Limitations of Bias Detection and Mitigation)**
* AIF360, Fairlearn과 같은 도구를 사용하여 데이터나 모델의 통계적 편향을 측정하고 완화할 수 있지만, 이는 사회에 존재하는 복잡하고 미묘한 편견(예: 특정 직업에 대한 성 역할 고정관념)을 완벽하게 포착하지 못합니다.
* 편향을 완화하려는 기술적 시도가 오히려 또 다른 형태의 편향을 만들어내거나('편향-정확도 트레이드오프'), 문제를 지나치게 단순화할 위험은 없을까요? 기술적인 해결책만으로 사회적 편견 문제를 다룰 수 있을까요?
3. **설명가능성(XAI)의 역설 (The Paradox of Explainability)**
* 모델의 예측 결과를 해석하기 위해 LIME이나 SHAP 같은 XAI 기술을 사용했지만, 그 설명 자체가 너무 복잡하여 비전문가가 이해하기 어렵거나, 때로는 사용자를 오도(misleading)할 수 있습니다.
* 또한, 설명가능성을 높이기 위해 일부러 성능이 더 낮은 단순한 모델(예: 로지스틱 회귀)을 사용해야 하는 딜레마에 빠질 수 있습니다. '설명을 위한 설명'이 되는 것을 어떻게 경계하고, 진정으로 유용한 설명을 제공할 수 있을까요?
4. **책임 소재의 불분명성 (Ambiguity of Accountability)**
* 자율주행차가 사고를 냈을 때, 그 책임은 자동차 소유주, 자동차 제조사, AI 모델을 개발한 엔지니어, 학습 데이터를 제공한 회사 중 누구에게 있을까요? AI 시스템의 결정으로 인해 피해가 발생했을 때, 법적/윤리적 책임을 어떻게 분배해야 하는가에 대한 사회적 합의가 아직 부족합니다.
* 이러한 책임의 공백 상태가 AI 기술 발전과 도입에 어떤 영향을 미칠까요?
5. **'규제 준수'가 곧 '윤리'라는 착각 (The Misconception that "Compliance" Equals "Ethics")**
* GDPR과 같은 데이터 보호 규정을 지키는 것을 AI 윤리를 전부 실천한 것이라고 착각하는 경우가 있습니다. 하지만 법은 사회가 용납하는 최소한의 기준일 뿐입니다.
* 법이 아직 정의하지 못하는 회색지대(예: 딥페이크 기술의 오용, AI를 이용한 감정 조작)에서, 개발자와 기업은 어떤 윤리적 책임을 져야 할까요? '법을 어기진 않았다'는 변명이 충분할까요?
---
**➡️ 다음 시간: [Part 15: 나만의 AI 서비스 만들기 (캡스톤 프로젝트 가이드)](../15_capstone_project/part_15_capstone_project.md)**
......@@ -181,3 +181,79 @@ uvicorn app.main:app --reload
---
캡스톤 프로젝트는 여러분의 가능성을 세상에 보여주는 첫걸음입니다. 과정 자체를 즐기며, 마음껏 도전하고, 마음껏 성장하시길 바랍니다!
---
### 🚨 디버깅 및 트러블슈팅 가이드 (Debugging & Troubleshooting)
프로젝트를 진행하다 보면 예상치 못한 문제들이 발생하기 마련입니다. 다음은 캡스톤 프로젝트 중 흔히 겪는 문제와 해결을 위한 실마리입니다.
<details>
<summary><b>🔹 문제: 모델 학습이 너무 느리거나, GPU 메모리가 부족해요.</b></summary>
<ul>
<li><b>원인 분석:</b> 데이터셋 크기, 모델의 복잡도, 배치 사이즈(Batch Size) 등이 원인일 수 있습니다.</li>
<li><b>해결 방안:</b>
<ul>
<li><b>배치 사이즈 줄이기:</b> <code>batch_size</code>를 줄여서 한 번에 처리하는 데이터 양을 줄여보세요.</li>
<li><b>데이터 증강(Augmentation) 줄이기:</b> 실시간으로 데이터를 변형하는 과정이 많으면 병목이 될 수 있습니다.</li>
<li><b>모델 경량화:</b> 더 작은 모델(e.g., ResNet18 대신 MobileNet)을 사용하거나, 모델의 레이어 수를 줄여보세요.</li>
<li><b>GPU 자원 확인:</b> <code>nvidia-smi</code> 명령어로 다른 프로세스가 GPU를 점유하고 있는지 확인하세요.</li>
</ul>
</li>
</ul>
</details>
<details>
<summary><b>🔹 문제: API 서버(FastAPI)를 실행했는데, 'Connection refused' 오류가 발생해요.</b></summary>
<ul>
<li><b>원인 분석:</b> 서버가 정상적으로 실행되지 않았거나, 방화벽 문제, 혹은 잘못된 주소/포트로 접속을 시도하고 있을 가능성이 높습니다.</li>
<li><b>해결 방안:</b>
<ul>
<li><b>서버 로그 확인:</b> Uvicorn 실행 로그에 에러 메시지가 없는지 꼼꼼히 확인하세요.</li>
<li><b>포트 확인:</b> <code>lsof -i :8000</code> (macOS/Linux) 또는 <code>netstat -ano | findstr :8000</code> (Windows) 명령어로 해당 포트를 다른 프로그램이 사용하고 있는지 확인하세요.</li>
<li><b>호스트 주소 확인:</b> <code>uvicorn.run(app, host="0.0.0.0", port=8000)</code>과 같이 <code>host</code><code>0.0.0.0</code>으로 설정해야 외부에서 접속이 가능합니다.</li>
</ul>
</li>
</ul>
</details>
<details>
<summary><b>🔹 문제: Git 충돌(Conflict)이 발생해서 Pull/Merge가 안 돼요.</b></summary>
<ul>
<li><b>원인 분석:</b> 여러 명이 동시에 같은 파일의 같은 부분을 수정했을 때 발생합니다.</li>
<li><b>해결 방안:</b>
<ul>
<li><b>침착하게 충돌 해결하기:</b> Git이 알려주는 충돌 파일(e.g., <code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</code>, <code>=======</code>, <code>&gt;&gt;&gt;&gt;&gt;&gt;&gt; branch-name</code>)을 열어, 팀원과 상의하여 남길 코드를 결정하고 충돌 마커를 직접 수정하세요.</li>
<li><b>예방이 최선:</b> 작업 시작 전 항상 <code>git pull origin main</code>으로 최신 코드를 받고 시작하는 습관을 들이고, 서로 작업하는 파일이 겹치지 않도록 사전에 소통하는 것이 중요합니다.</li>
</ul>
</li>
</ul>
</details>
여러분의 성공적인 캡스톤 프로젝트를 통해, 이 과정에서 배운 모든 것을 종합하고 한 단계 더 성장하는 경험을 하시기를 바랍니다!
---
### ⚠️ What Could Go Wrong? (토론 주제)
캡스톤 프로젝트는 실제 현업의 미니 버전과 같습니다. 기술적인 문제뿐만 아니라, 기획, 소통, 일정 관리 등 다양한 부분에서 예상치 못한 어려움이 발생할 수 있습니다.
1. **지나치게 야심 찬 목표 설정 (Setting Overly Ambitious Goals)**
* 제한된 시간과 리소스 안에서 달성 불가능한 거창한 주제(예: "사람처럼 대화하는 AI 챗봇 만들기", "알파고 수준의 바둑 AI 개발")를 선정하여, 결국 아무것도 제대로 완성하지 못하고 이도 저도 아닌 결과물을 내는 경우가 많습니다.
* 현실적인 프로젝트 범위(Scope)를 설정하는 것이 왜 중요할까요? '핵심 기능(MVP, Minimum Viable Product)'을 먼저 정의하고 점진적으로 확장해나가는 방식의 장점은 무엇일까요?
2. **'데이터' 없이 '모델'부터 생각하는 함정 (The "Model-first, Data-later" Trap)**
* 최신 유행하는 멋진 모델(예: LLM, Diffusion Model)을 사용하겠다는 생각에 사로잡혀, 정작 그 모델을 학습시키거나 파인튜닝할 양질의 데이터를 어떻게 확보할지에 대한 계획 없이 프로젝트를 시작하는 경우.
* '데이터 가용성'과 '데이터 품질'이 프로젝트 성패에 미치는 영향을 간과하면 어떤 문제가 발생할까요? 프로젝트 기획 단계에서 데이터 확보 계획이 왜 가장 먼저 검토되어야 할까요?
3. **문제 정의의 실패 (Failure in Problem Definition)**
* "무엇을(WHAT)" 만들고 "왜(WHY)" 만드는지에 대한 명확한 고민 없이, 단순히 '어떻게(HOW)' 만들지에만 집중하는 경우. 이로 인해 프로젝트의 목표와 성공의 기준이 모호해지고, 팀원들이 각자 다른 방향으로 헤맬 수 있습니다.
* 성공적인 프로젝트가 되기 위해, "우리가 해결하려는 문제는 무엇인가?" 와 "프로젝트의 성공 여부를 어떻게 측정할 것인가?" 라는 질문에 먼저 답해야 하는 이유는 무엇일까요?
4. **팀원 간의 역할 및 책임 불균형 (Imbalance of Roles and Responsibilities)**
* 팀 프로젝트에서 일부 팀원에게만 업무가 과중되거나, 누구도 책임지지 않는 업무 공백(예: 문서 정리, 최종 발표 자료 제작)이 발생하는 경우가 빈번합니다. 이는 팀의 사기를 저하시키고 갈등을 유발하는 주요 원인이 됩니다.
* 성공적인 협업을 위해 프로젝트 초기에 어떤 것들(예: 역할 분담(R&R), 소통 규칙, 정기 회의, 일정 관리 도구 사용)을 명확히 정의해야 할까요?
5. **'완성'에 대한 집착으로 학습 기회 상실 (Losing Learning Opportunities by Obsessing over "Completion")**
* 최종 결과물의 '완성도'와 '발표'에만 집착한 나머지, 과정에서 겪는 수많은 실패와 디버깅, 다양한 실험과 그로부터 얻은 교훈의 중요성을 간과할 수 있습니다.
* 캡스톤 프로젝트의 진정한 목표가 '완벽한 제품 출시'가 아니라 '과정을 통한 실용적 학습'에 있다는 것을 어떻게 상기할 수 있을까요? 실패 기록과 회고(Retrospective)가 왜 중요할까요?
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment