파이썬(Python)은 1991년 네덜란드 CWI의 귀도 반 로섬(Guido van Rossum)이 발표한 고급 프로그래밍 언어입니다.
파이썬이라는 이름은 자신이 좋아하던 코미디 쇼 "몬티 파이썬의 날아다니는 서커스"에서 따왔다고 합니다.
파이썬은 TIOBE Index에서 꾸준히 1위를 차지하며, 전 세계에서 가장 인기 있는 프로그래밍 언어로 자리 잡았습니다.
웹 개발(Django, Flask), 데이터 과학(NumPy, Pandas), 인공지능/머신러닝(TensorFlow, PyTorch), 자동화 스크립팅, 게임 개발, IoT 등
거의 모든 소프트웨어 분야에서 활용됩니다. 특히 간결하고 읽기 쉬운 문법은 프로그래밍 입문자부터 숙련 개발자까지 폭넓은 사용자층을 형성하는 핵심 이유입니다.
파이썬의 주요 특징
파이썬은 다른 프로그래밍 언어와 비교하여 독보적인 특징을 가지고 있습니다. 인터프리터 언어로서 컴파일 과정 없이 코드를 한 줄씩 즉시 실행할 수 있고, 동적 타이핑(Dynamic Typing)으로 변수 선언 시 자료형을 명시하지 않아도 됩니다. 또한 멀티 패러다임을 지원하여 절차적, 객체지향, 함수형 프로그래밍 스타일을 모두 사용할 수 있습니다. 특히 크로스 플랫폼 지원으로 Windows, macOS, Linux 등 어디서나 동일한 코드를 실행할 수 있다는 점이 큰 장점입니다.
인터프리터 언어
소스 코드를 기계어로 미리 컴파일하지 않고, 인터프리터가 한 줄씩 해석하여 실행합니다. 코드를 작성하고 즉시 실행 결과를 확인할 수 있어 개발 속도가 빠릅니다. 대화형 셸(REPL)에서 실시간으로 코드를 테스트할 수 있는 것도 이 특징 덕분입니다.
동적 타이핑
변수 선언 시 자료형을 명시하지 않아도 됩니다. x = 10이라고 쓰면 자동으로 정수형으로 인식하고, x = "hello"로 바꾸면 문자열형으로 변환됩니다. 이로 인해 코드가 간결해지지만, 런타임 타입 에러에 주의해야 합니다.
멀티 패러다임
절차적 프로그래밍(함수 단위 순차 실행), 객체지향 프로그래밍(클래스와 상속), 함수형 프로그래밍(lambda, map, filter, reduce)을 모두 지원합니다. 문제의 성격에 따라 가장 적합한 스타일을 선택할 수 있습니다.
풍부한 표준 라이브러리
"배터리가 포함된(Batteries Included)" 철학에 따라, 파일 I/O, 네트워킹, 정규식, JSON 처리, 데이터베이스 연동, 멀티스레딩 등을 별도 설치 없이 표준 라이브러리만으로 처리할 수 있습니다. 200개 이상의 모듈이 기본 제공됩니다.
크로스 플랫폼
Windows, macOS, Linux, Unix 등 주요 운영체제에서 동일한 코드를 수정 없이 실행할 수 있습니다. "Write once, run anywhere" 원칙을 실현하여 개발 환경에 구애받지 않습니다.
거대한 생태계
PyPI(Python Package Index)에 50만 개 이상의 서드파티 패키지가 등록되어 있으며, pip install 한 줄로 설치할 수 있습니다. 웹, AI, 데이터, 보안, 게임 등 거의 모든 분야의 라이브러리가 존재합니다.
파이썬의 핵심 철학 (PEP 20)
파이썬의 설계 원칙은 PEP 20 (The Zen of Python)에 담겨 있으며, 인터프리터에서 import this를 실행하면 확인할 수 있습니다. 이 철학은 코드 작성 시 가독성과 단순함을 최우선으로 두라는 메시지를 전달합니다.
"아름다운 게 추한 것보다 낫다." (Beautiful is better than ugly)
"명시적인 것이 암시적인 것보다 낫다." (Explicit is better than implicit)
"단순함이 복잡함보다 낫다." (Simple is better than complex)
"복잡함이 난해한 것보다 낫다." (Complex is better than complicated)
"가독성은 중요하다." (Readability counts)
"인생은 너무 짧다. 그래서 파이썬이 필요하다." ("Life is short, You need Python.")
파이썬의 역사
파이썬은 1989년 크리스마스 주간에 귀도 반 로섬이 취미 프로젝트로 시작하여, 30년 이상의 역사를 거치며 세계에서 가장 영향력 있는 프로그래밍 언어로 성장했습니다. 각 주요 버전은 언어의 방향성을 결정짓는 중요한 이정표가 되었습니다.
파이썬 실행 구조
파이썬 코드가 실행되는 내부 과정을 이해하면 성능 최적화와 디버깅에 큰 도움이 됩니다. 파이썬은 순수 인터프리터 언어가 아니라, 먼저 소스 코드를 바이트코드로 컴파일한 뒤 가상 머신(PVM)에서 실행하는 하이브리드 방식을 사용합니다. .pyc 파일은 이 바이트코드가 캐싱된 것이며, __pycache__ 디렉터리에 저장됩니다.
파이썬 설치 가이드
파이썬은 python.org에서 공식 배포판을 다운로드하거나, 운영체제별 패키지 관리자를 통해 설치할 수 있습니다. 데이터 과학 용도라면 Anaconda 또는 Miniconda가 편리합니다. 설치 후 터미널에서 python --version을 입력하여 정상 설치를 확인하세요.
# Windows — python.org에서 다운로드 후 설치# 설치 시 "Add Python to PATH" 체크 필수!# macOS — Homebrew 사용
brew install python
# Ubuntu / Debian
sudo apt update
sudo apt install python3 python3-pip python3-venv
# 설치 확인
python3 --version # Python 3.x.x
pip3 --version # pip xx.x.x
가상 환경 권장: 프로젝트마다 독립된 패키지 환경을 만들어야 의존성 충돌을 방지할 수 있습니다. python -m venv myenv로 가상 환경을 생성하고, source myenv/bin/activate(Linux/Mac) 또는 myenv\Scripts\activate(Windows)로 활성화합니다. 최근에는 uv나 poetry 같은 최신 패키지 매니저도 많이 사용됩니다.
파이썬 생태계와 커뮤니티
파이썬의 강력함은 언어 자체뿐만 아니라 거대한 생태계와 활발한 커뮤니티에서 비롯됩니다. PyPI(Python Package Index)에는 50만 개 이상의 패키지가 등록되어 있으며, GitHub에서 가장 많이 사용되는 언어 중 하나입니다. PyCon, DjangoCon 등의 컨퍼런스가 전 세계에서 열리고 있으며, Stack Overflow에서도 파이썬 관련 질문이 가장 활발합니다.
PyPI (Python Package Index)
파이썬의 공식 서드파티 패키지 저장소입니다. pip install 패키지명 한 줄로 전 세계 개발자가 만든 50만 개 이상의 패키지를 즉시 설치할 수 있습니다. 웹 프레임워크, AI 라이브러리, 이미지 처리, 암호화 등 거의 모든 기능의 패키지가 존재합니다.
Jupyter Notebook
코드, 시각화, 설명 텍스트를 하나의 문서에 결합할 수 있는 대화형 개발 환경입니다. 데이터 과학, 머신러닝 연구, 교육 분야에서 사실상 표준 도구로 사용됩니다. Google Colab은 클라우드에서 무료로 Jupyter 환경을 제공합니다.
커뮤니티와 거버넌스
PSF(Python Software Foundation)가 언어 발전을 관리하며, PEP(Python Enhancement Proposals) 프로세스를 통해 새로운 기능이 제안되고 채택됩니다. 귀도 반 로섬 은퇴 후 Steering Council(5인 위원회)이 최종 결정을 내립니다.
주요 IDE / 에디터
VS Code(가장 인기, Python 확장 제공), PyCharm(JetBrains, 전문 IDE), Jupyter(대화형 분석), Vim/Neovim(터미널 기반). 각 도구는 자동 완성, 디버깅, 린팅, 가상환경 관리 등을 지원합니다.
초급자 가이드 - 파이썬을 왜 배워야 할까?
파이썬은 프로그래밍을 처음 접하는 분에게 가장 권장되는 언어입니다. 그 이유는:
쉬운 문법: 영어 문장을 읽듯 코드를 읽을 수 있습니다. 예를 들어 if age >= 18: print("성인")은 "만약 나이가 18 이상이면 '성인'을 출력하라"와 같습니다.
설치 후 바로 시작: python.org에서 설치 후 터미널에 python을 입력하면 바로 코드를 실행할 수 있습니다.
방대한 학습 자료: 한국어 강좌, 책, 유튜브 영상 등 풍부한 자료가 있어 독학이 용이합니다.
높은 취업 수요: 데이터 분석, AI, 웹 개발, 자동화 등 거의 모든 IT 분야에서 파이썬 개발자를 찾고 있습니다.
처음 시작하는 팁: Python을 설치한 후 터미널(또는 명령 프롬프트)에서 python을 입력하면 대화형 모드(REPL)가 시작됩니다. 여기서 print("Hello!")를 입력해보세요. 이것이 여러분의 첫 번째 파이썬 프로그램입니다!
데이터 엔지니어링: Apache Airflow(워크플로 자동화), PySpark(대규모 데이터 처리)로 데이터 파이프라인을 구축합니다.
DevOps/자동화: Ansible, Fabric으로 서버 관리를 자동화하고, Selenium으로 웹 테스트를 자동화합니다.
패키지 관리: 실무에서는 venv 대신 poetry나 uv를 사용하여 의존성을 더 체계적으로 관리하는 추세입니다.
고급자 가이드 - 파이썬의 내부 구조와 한계
파이썬의 내부 동작 원리를 이해하면 더 효율적인 코드를 작성할 수 있습니다:
CPython 바이트코드: Python 소스코드는 먼저 바이트코드(.pyc)로 컴파일되고, 이후 CPython VM이 이를 해석 실행합니다. dis 모듈로 바이트코드를 확인할 수 있습니다. (import dis; dis.dis(함수명))
GIL의 실제 영향: GIL은 CPU-bound 작업에서만 병목이 됩니다. I/O-bound 작업(네트워크, 파일)에서는 GIL이 자동 해제되어 멀티스레딩이 효과적입니다. CPU-bound는 multiprocessing이나 C 확장 모듈을 사용하세요.
메모리 관리: CPython은 참조 카운팅(Reference Counting)과 세대별 가비지 컬렉터(Generational GC)를 사용합니다. 순환 참조는 GC가 처리하지만, __del__ 메서드가 있으면 수집이 지연될 수 있습니다.
성능 한계 극복: 성능이 중요한 부분은 Cython, ctypes, pybind11로 C/C++ 확장을 만들거나, Numba JIT 컴파일러를 사용하여 수십~수백 배 속도 향상이 가능합니다.
파이썬 인터프리터 종류
파이썬은 언어 사양(specification)과 구현체(implementation)가 분리되어 있습니다. 동일한 파이썬 코드를 다양한 인터프리터에서 실행할 수 있으며, 각 인터프리터는 다른 플랫폼이나 성능 요구사항에 최적화되어 있습니다. 이 분리 덕분에 다양한 실행 환경(JVM, .NET, 브라우저 등)에서 파이썬 코드를 재사용할 수 있습니다.
CPython
C로 작성된 공식 인터프리터. Reference 구현체로 사실상 표준이며, python.org에서 다운로드하는 것이 바로 CPython입니다. GIL(Global Interpreter Lock)을 사용하는데, 이는 한 번에 하나의 스레드만 Python 바이트코드를 실행하도록 제한하는 뮤텍스입니다. 메모리 관리의 안전성을 보장하지만, CPU 집약적 작업에서 멀티스레드 병렬 실행이 제한되는 단점이 있습니다.
PyPy
JIT(Just-In-Time) 컴파일러를 내장한 고성능 인터프리터. 반복 연산이 많은 코드에서 CPython 대비 수~수십 배 빠른 성능을 제공합니다.
Jython
JVM(Java Virtual Machine) 위에서 실행되는 인터프리터. Java 라이브러리를 직접 호출할 수 있어 Java 생태계와의 통합에 유용합니다.
IronPython
.NET CLR(Common Language Runtime) 위에서 실행되며, C#이나 VB.NET 등 .NET 라이브러리와 상호 운용이 가능합니다.
Brython
JavaScript로 구현된 브라우저용 인터프리터. HTML 페이지 내에서 Python 코드를 직접 실행할 수 있어 웹 프론트엔드 개발에 활용됩니다.
Python 2.x vs 3.x 차이점
권장: 이제 Python 3.x 이상을 기준으로 학습하세요. Python 2.x는 2020년 1월 1일에 공식 지원이 종료(EOL)되었으며, 보안 패치도 더 이상 제공되지 않습니다. 모든 주요 라이브러리(Django, NumPy, Pandas 등)가 Python 2 지원을 중단했습니다.
Python 3.14 새로운 기능 (2025년 10월 출시)
Python 3.14는 성능 개선, 새로운 문법, 디버깅 도구 등 다양한 혁신을 포함합니다. 특히 GIL 제거를 향한 Free-threaded 모드와 Template Strings는 파이썬 생태계에 큰 변화를 가져올 핵심 기능입니다.
PEP 750: Template Strings
t"..." 접두사로 템플릿 문자열 생성. f-string과 달리 즉시 평가되지 않아 SQL 인젝션 방지 등 보안이 필요한 곳에 활용됩니다.
PEP 734: Multiple Interpreters
하나의 프로세스 내에서 여러 독립적인 인터프리터를 실행하여 GIL 제약 없이 진정한 병렬 처리를 구현할 수 있습니다.
PEP 649: Deferred Annotations
타입 어노테이션이 런타임에 필요할 때까지 평가를 지연시켜 순환 참조 문제를 해결하고 모듈 로딩 속도를 개선합니다.
PEP 768: Debugger Interface
외부 프로세스에서 안전하게 디버거를 연결할 수 있는 표준 인터페이스를 제공하여 프로덕션 환경의 디버깅을 용이하게 합니다.
PEP 784: Zstandard
Facebook이 개발한 고성능 Zstandard 압축 알고리즘을 표준 라이브러리에 포함하여 별도 설치 없이 빠른 데이터 압축/해제가 가능합니다.
Free-threaded Mode
GIL(Global Interpreter Lock)을 비활성화하여 멀티코어 CPU를 최대한 활용하는 진정한 멀티스레딩 실행이 가능합니다. 실험적 기능으로 점진적 안정화 중입니다.
주요 CLI 옵션
파이썬 인터프리터는 명령줄에서 다양한 옵션을 지원합니다. 이 옵션들을 활용하면 디버깅, 최적화, 스크립트 실행 방식을 세밀하게 제어할 수 있습니다.
옵션
설명
python
인자 없이 실행: 대화형 인터프리터
-d
디버그 출력
-O
최적화 모드 (assert 제거, __pycache__에 최적화된 .pyc 생성)
-v
자세한 출력 (import 추적)
-c "cmd"
명령어 실행
-S
시작 시 import site 건너뛰기
파이썬 환경변수
환경변수를 통해 파이썬 인터프리터의 동작을 전역적으로 설정할 수 있습니다. 모듈 검색 경로, 인코딩 설정 등을 OS 수준에서 지정합니다.
변수
설명
PYTHONPATH
모듈 파일 위치
PYTHONSTARTUP
인터프리터 시작 시 실행 파일
PYTHONHOME
모듈 검색 경로
PYTHONCASEOK
대소문자 구분 안 함 (Windows)
예약어 (Keywords)
다음 예약어는 변수나 식별자로 사용할 수 없습니다 (Python 3.10+ 기준):
False, None, True, and, as, assert, async, await, break, class,
continue, def, del, elif, else, except, finally, for, from, global,
if, import, in, is, lambda, match, case, nonlocal, not, or, pass,
raise, return, try, while, with, yield
연산자 우선순위
여러 연산자가 하나의 표현식에 함께 사용될 때, 어떤 연산이 먼저 수행되는지를 결정하는 규칙입니다. 숫자가 작을수록 우선순위가 높습니다. 괄호 ()를 사용하면 우선순위를 명시적으로 지정할 수 있습니다. 할당 연산자(=, += 등)는 표현식이 아닌 문(statement)이므로 이 표에 포함되지 않습니다.
우선순위
연산자
설명
1 (최고)
(expr), [list], {dict}, {set}
괄호, 리터럴
2
x[i], x.attr, x()
인덱싱, 속성 참조, 호출
3
await x
Await 표현식
4
**
지수 (거듭제곱)
5
+x, -x, ~x
단항 양수, 음수, 비트 NOT
6
*, /, //, %, @
곱셈, 나눗셈, 나머지, 행렬곱
7
+, -
덧셈, 뺄셈
8
<<, >>
비트 시프트
9
&
비트 AND
10
^
비트 XOR
11
|
비트 OR
12
==, !=, <, <=, >, >=, is, is not, in, not in
비교, 식별, 멤버십
13
not
논리 NOT
14
and
논리 AND
15
or
논리 OR
16
if – else
조건 표현식
17
lambda
람다 표현식
18 (최저)
:=
Walrus 연산자
2. 기초 문법
초급
파이썬의 기초 문법은 다른 언어에 비해 직관적이고 자연어에 가깝게 설계되어 있습니다.
중괄호({}) 대신 들여쓰기로 코드 블록을 구분하고, 세미콜론(;) 없이도 한 줄이 하나의 문장이 됩니다.
이런 설계 덕분에 파이썬 코드는 마치 영어 문장을 읽는 것처럼 읽히며, 초보자도 빠르게 프로그래밍의 핵심 개념을 익힐 수 있습니다.
초급자 가이드 - 기초 문법의 핵심
파이썬 기초 문법에서 가장 중요한 3가지를 기억하세요:
들여쓰기가 곧 문법: C, Java에서는 중괄호 {}로 코드 블록을 구분하지만, 파이썬은 들여쓰기(보통 스페이스 4칸)로 구분합니다. 탭과 스페이스를 섞으면 에러가 납니다!
변수 선언이 필요 없다: int x = 5;처럼 타입을 명시하지 않고, 그냥 x = 5로 작성하면 됩니다. 파이썬이 자동으로 타입을 결정합니다.
세미콜론이 필요 없다: 한 줄이 하나의 문장입니다. 줄바꿈 자체가 구분자 역할을 합니다.
흔한 실수:if문 뒤에 콜론(:)을 빼먹는 것입니다. if x > 0:처럼 반드시 콜론을 붙여야 합니다.
중급자 가이드 - PEP 8 코딩 스타일
실무에서는 PEP 8 스타일 가이드를 준수하는 것이 중요합니다. 팀 협업 시 코드 일관성을 유지하는 핵심 규칙:
한 줄 길이: 최대 79자(PEP 8 권장). 현대적 프로젝트에서는 100~120자까지 허용하기도 합니다.
import 순서: 표준 라이브러리 → 서드파티 → 로컬 모듈 순서로 작성하고, 그룹 사이에 빈 줄을 넣습니다.
자동 도구: black(코드 포매터), isort(import 정렬), flake8(린터), ruff(올인원 린터)를 사용하면 스타일을 자동으로 맞출 수 있습니다.
고급자 가이드 - 파이썬 실행 모델의 이해
파이썬의 실행 과정을 깊이 이해하면 디버깅과 최적화에 도움이 됩니다:
모든 것이 객체: 정수 42도, 함수 print도, 모듈 os도 모두 객체입니다. id()로 메모리 주소를, type()으로 타입 객체를 확인할 수 있습니다.
변수는 이름표(label): 파이썬 변수는 C의 변수와 다릅니다. 값을 담는 "상자"가 아니라 객체를 가리키는 "이름표"입니다. a = [1,2,3]; b = a에서 a와 b는 같은 리스트 객체를 가리키며, id(a) == id(b)입니다.
정수 캐싱(Integer Interning): CPython은 -5~256 범위의 정수를 미리 캐싱합니다. 이 범위의 정수는 is 비교가 True이지만, 그 밖의 정수는 False일 수 있습니다. 값 비교에는 항상 ==를 사용하세요.
문자열 인터닝(String Interning): 식별자처럼 보이는 문자열(영문/숫자/밑줄로만 구성)은 자동으로 인터닝되어 동일 객체를 재사용합니다.
Hello, World! 출력하기
프로그래밍의 첫걸음은 언제나 "Hello, World!"를 출력하는 것입니다.
print("Hello, World!")
# 결과: Hello, World!
💡 팁: 파이썬에서 print()는 함수의 일종입니다. 괄호 안에 넣은 내용을 화면에 출력해줍니다.
변수와 주석
변수는 데이터를 저장하는 공간입니다. 주석은 코드에 설명을 추가하는 데 사용됩니다.
# 변수 선언의 예
name = "민준"
age = 25
height = 175.5print(name) # 민준print(age) # 25
주석 (Comments)과 독스트링 (Docstrings)
#으로 시작하는 주석(comment)은 인터프리터가 완전히 무시하는 메모입니다. 반면, 삼중 따옴표(""" 또는 ''')로 작성하는 독스트링(docstring)은 실제로는 문자열 리터럴이며, 함수·클래스·모듈의 첫 줄에 오면 __doc__ 속성에 저장되어 help()로 조회할 수 있습니다. 독스트링은 주석처럼 보이지만 런타임에 존재하는 문자열이라는 점이 핵심 차이입니다.
# 한 줄 주석입니다 (인터프리터가 무시)defgreet(name):
"""인사말을 반환합니다.
이것은 독스트링(docstring)으로, 함수의 __doc__ 속성에 저장됩니다.
help(greet)로 확인할 수 있습니다.
"""returnf"안녕, {name}!"print(greet.__doc__) # 독스트링 출력
여러 줄로 구문 작성
긴 코드를 여러 줄에 걸쳐 작성할 수 있습니다. 백슬래시(\)를 사용하거나 괄호([], {}, ()) 안에서는 자동으로 줄이 연결됩니다. 세미콜론(;)으로 한 줄에 여러 구문을 작성할 수도 있지만, PEP 8에서는 권장하지 않습니다.
# \(백슬래시)로 줄 연결
a = 1 + \
2 + \
3# [], {}, ()는 자동으로 연결
numbers = [1, 2, 3,
4, 5]
# 한 줄에 여러 구문 (세미콜론)
x = 1; y = 2; print(x + y)
식별자 (Identifiers)
식별자는 변수, 함수, 클래스 등에 붙이는 이름입니다. 파이썬의 명명 규칙은 PEP 8을 따르며, 언더스코어의 개수에 따라 특별한 의미를 가집니다.
영문자, 숫자, _(밑줄) 사용 가능 (숫자로 시작 불가)
대소문자 엄격히 구분
Python 3.x부터 한글 변수명 사용 가능
_var : 관례적으로 내부 사용(private)을 의미하며, from module import *에서 제외됩니다.
__var : 이름 맹글링(name mangling) 적용. 클래스 내에서 _ClassName__var로 변환되어 하위 클래스와의 이름 충돌을 방지합니다.
__var__ : 던더(dunder, double underscore) 또는 매직 메서드/속성이라 불립니다. __init__, __str__, __len__ 등 Python이 특별한 용도로 예약한 이름입니다. 직접 새로 정의하지 않는 것이 좋습니다.
var_ : Python 예약어와의 충돌을 피하기 위한 관례 (예: class_, type_)
들여쓰기 (Indentation)
중요: 파이썬은 들여쓰기로 블록을 구분합니다. 콜론(:) 뒤에 들여쓰기를 하세요.
if x > 0:
print("양수") # 4칸 들여쓰기if x > 10:
print("큰 양수") # 8칸 들여쓰기
첫 줄 Shebang과 인코딩
Unix/Linux 환경에서 스크립트를 직접 실행하려면 첫 줄에 Shebang(#!)을 추가합니다. 인코딩 선언(# -*- coding: utf-8 -*-)은 Python 3에서는 기본이 UTF-8이므로 생략 가능하지만, Python 2 호환이나 명시적 선언이 필요할 때 사용합니다.
Python 3.8부터 도입된 Walrus 연산자(:=)는 표현식 내에서 변수에 값을 할당하면서 동시에 그 값을 반환합니다. := 기호가 바다코끼리(walrus)의 눈과 엄니를 닮았다고 하여 이 이름이 붙었습니다. 조건문이나 반복문에서 중복 호출을 줄이는 데 유용합니다.
# 기존 방식
result = some_function()
if result:
print(result)
# Walrus 연산자 사용if (result := some_function()):
print(result)
# 리스트 comprehension에서의 사용if (n := int("42")) > 40:
print(f"숫자는 {n}입니다")
3. 자료형 (Data Types)
초급
파이썬은 동적 타이핑(Dynamic Typing) 언어로, 변수를 선언할 때 자료형을 명시하지 않아도 됩니다.
인터프리터가 대입된 값에 따라 자동으로 자료형을 결정합니다. 파이썬의 모든 데이터는 객체(object)이며, type() 함수로 자료형을 확인할 수 있습니다.
기본 자료형은 크게 숫자형(int, float, complex), 시퀀스형(str, list, tuple), 매핑형(dict), 집합형(set, frozenset), 불리언형(bool), None으로 분류됩니다.
숫자 (Numbers)
파이썬은 정수(int), 실수(float), 복소수(complex) 세 가지 숫자 자료형을 지원합니다. 정수는 크기 제한이 없어 아무리 큰 수도 표현할 수 있으며(메모리가 허용하는 한), 2진수(0b), 8진수(0o), 16진수(0x) 리터럴도 사용 가능합니다.
문자열은 작은따옴표('...') 또는 큰따옴표("...")로 생성하며, 삼중 따옴표("""...""")로 여러 줄 문자열을 만들 수 있습니다. 파이썬 3에서 문자열은 기본적으로 유니코드(Unicode)이므로 한글 등 다국어 처리가 자연스럽습니다. f-string(Python 3.6+)을 사용하면 변수를 문자열에 직접 삽입할 수 있습니다.
single = '작은따옴표'
double = "큰따옴표"
multi_line = """여러 줄
문자열"""# 문자열 포맷팅
name = "철수"print(f"안녕하세요, {name}님!") # 안녕하세요, 철수님!
문자열 주요 메서드
파이썬 문자열은 불변(immutable) 객체이므로 모든 메서드는 원본을 수정하지 않고 새로운 문자열을 반환합니다. 다음은 가장 자주 사용되는 문자열 메서드들입니다.
메서드
설명
.upper()
대문자 변환
.lower()
소문자 변환
.strip()
좌우 공백 제거
.split()
분할 (리스트 반환)
.join()
합치기
.replace()
치환
.find()
위치 찾기 (-1 if not found)
.index()
위치 찾기 (오류 if not found)
.count()
개수 세기
.startswith()
시작 문자열 확인
.endswith()
끝 문자열 확인
.isalpha()
영문자 여부
.isdigit()
숫자 여부
.splitlines()
줄바꿈으로 분할
.zfill()
0으로 패딩
문자열 슬라이싱
슬라이싱은 [시작:끝:간격] 문법으로 문자열의 일부를 추출합니다. 인덱스는 0부터 시작하며, 음수 인덱스는 끝에서부터 역순으로 셉니다. 끝 인덱스는 해당 위치를 포함하지 않습니다.
이스케이프 문자는 백슬래시(\)와 함께 사용되어 줄바꿈, 탭 등 특수 문자를 표현합니다. 이스케이프 시퀀스를 무시하려면 문자열 앞에 r(raw string)을 붙입니다. 예: r"C:\new"
코드
설명
\n
줄바꿈
\t
탭
\\
백슬래시
\'
작은따옴표
\"
큰따옴표
\r
캐리지 리턴
\b
백스페이스
리스트와 튜플
리스트(list)는 순서가 있고 수정 가능한(mutable) 시퀀스로 []로 생성합니다. 튜플(tuple)은 순서가 있지만 수정 불가능한(immutable) 시퀀스로 ()로 생성합니다. 튜플은 변경되면 안 되는 데이터(좌표, DB 레코드 등)에 사용하며, 딕셔너리의 키로도 활용할 수 있습니다.
리스트는 가변(mutable) 객체이므로 메서드를 통해 원본을 직접 수정할 수 있습니다. append(), insert() 등은 리스트 자체를 변경하고 None을 반환한다는 점에 주의하세요.
메서드
설명
예시
.append(x)
끝에 추가
[1,2].append(3) → [1,2,3]
.extend(iterable)
iterable 추가
[1].extend([2,3]) → [1,2,3]
.insert(i, x)
위치에 삽입
[1,3].insert(1,2) → [1,2,3]
.remove(x)
첫 번째 x 제거
[1,2,3].remove(2) → [1,3]
.pop([i])
위치 값 제거/반환
[1,2,3].pop() → 3
.clear()
모두 제거
[1,2].clear() → []
.index(x)
첫 번째 x 인덱스
[1,2,3].index(2) → 1
.count(x)
x 개수
[1,2,2].count(2) → 2
.sort(*, key, reverse)
정렬
[3,1,2].sort() → [1,2,3]
.reverse()
역순
[1,2,3].reverse() → [3,2,1]
.copy()
shallow 복사
[1,2].copy() → [1,2]
List Comprehension (리스트 내포)
리스트 컴프리헨션은 한 줄로 리스트를 생성하는 파이썬의 강력한 문법입니다. [표현식 for 변수 in 반복가능객체 if 조건] 형식으로 작성하며, for 루프보다 간결하고 일반적으로 더 빠릅니다. 중첩 컴프리헨션으로 다차원 리스트도 생성할 수 있습니다.
# 기본
squares = [x**2for x inrange(5)]
# [0, 1, 4, 9, 16]# 조건 포함
evens = [x for x inrange(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]# 중첩
matrix = [[j for j inrange(3)] for i inrange(3)]
딕셔너리 (Dictionary)
딕셔너리는 키-값(key-value) 쌍으로 데이터를 저장하는 자료형입니다. {}로 생성하며, 키를 통해 O(1) 시간복잡도로 값에 접근할 수 있습니다. Python 3.7부터 삽입 순서가 보장되며, 키는 불변(hashable) 객체만 사용 가능합니다.
# 딕셔너리 생성 (키-값 쌍)
person = {"이름": "민준", "나이": 25, "도시": "서울"}
# 값 접근print(person["이름"]) # 민준print(person.get("나이")) # 25 (키 없을 때 None 반환)print(person.get("직업", "미정")) # 미정 (기본값 지정)# 추가 / 수정
person["직업"] = "개발자"
person.update({"나이": 26, "취미": "독서"})
# 삭제del person["취미"]
person.pop("직업", None) # 키 없어도 오류 없음# 순회for key in person.keys():
print(key)
for value in person.values():
print(value)
for key, value in person.items():
print(f"{key}: {value}")
# 딕셔너리 컴프리헨션
squares = {x: x**2for x inrange(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}# 딕셔너리 병합 (Python 3.9+)
d1 = {"a": 1}
d2 = {"b": 2}
merged = d1 | d2 # {"a": 1, "b": 2}
집합 컴프리헨션 (Set Comprehension)
리스트 컴프리헨션과 동일한 문법이지만 {}를 사용하여 중복 없는 집합을 생성합니다. 소괄호 ()를 사용하면 제너레이터 표현식이 되어 한 번에 모든 값을 메모리에 저장하지 않고 필요할 때 하나씩 생성하므로 메모리 효율적입니다.
# 집합 컴프리헨션
unique_squares = {x**2for x in [-2, -1, 0, 1, 2]}
# {0, 1, 4}# 제너레이터 표현식 (메모리 효율)
gen = (x**2for x inrange(10))
# next(gen)으로 하나씩 꺼냄
집합 (Set)
집합(set)은 중복을 허용하지 않고 순서가 없는 자료형입니다. 수학의 집합 연산(합집합, 교집합, 차집합, 대칭차집합)을 지원하며, 멤버십 테스트(in)가 리스트보다 훨씬 빠릅니다(O(1)). 빈 집합은 set()으로 생성합니다({}는 빈 딕셔너리).
# 집합 (중복 불가, 순서 없음)
s = {1, 2, 3}
s.add(4) # 추가
s.remove(2) # 제거# 집합 연산
a = {1, 2, 3}
b = {2, 3, 4}
print(a | b) # 합집합: {1, 2, 3, 4}print(a & b) # 교집합: {2, 3}print(a - b) # 차집합: {1}
None (Null)
None은 파이썬의 '값이 없음'을 나타내는 특별한 싱글턴 객체입니다. 다른 언어의 null에 해당하며, 함수가 명시적으로 값을 반환하지 않을 때도 None이 반환됩니다. None 비교는 == 대신 is를 사용하는 것이 권장됩니다.
파이썬 객체는 생성 후 값을 변경할 수 있는지에 따라 가변(mutable)과 불변(immutable)으로 나뉩니다. 불변 객체를 수정하려 하면 새로운 객체가 생성되며, 가변 객체는 원본이 직접 변경됩니다. 이 차이는 함수 인자 전달, 복사, 딕셔너리 키 사용 등에서 중요합니다.
불변: int, float, str, tuple, frozenset
가변: list, dict, set
초급자 가이드 - 자료형 선택 기준
어떤 자료형을 사용해야 할지 고민될 때 참고하세요:
숫자를 다룰 때 → int(정수), float(소수점). 대부분의 경우 이 두 가지로 충분합니다.
글자/문장을 다룰 때 → str(문자열). 작은따옴표든 큰따옴표든 상관없습니다.
여러 값을 순서대로 저장할 때 → list(리스트). 가장 자주 사용하는 자료형입니다.
이름표(키)로 값을 찾을 때 → dict(딕셔너리). 전화번호부처럼 "이름 → 번호"의 관계를 저장합니다.
참/거짓을 나타낼 때 → bool. True 또는 False 값만 가집니다.
타입 변환:int("42")는 문자열을 정수로, str(42)는 정수를 문자열로, float("3.14")는 문자열을 실수로 변환합니다.
중급자 가이드 - 자료형의 성능과 실무 패턴
자료형마다 연산별 시간복잡도가 다릅니다. 올바른 자료형 선택이 성능에 큰 영향을 미칩니다:
리스트 vs 집합: in 연산에서 리스트는 O(n), 집합(set)은 O(1)입니다. 멤버십 검사가 빈번하면 set을 사용하세요.
딕셔너리 컴프리헨션: {k: v for k, v in pairs}로 딕셔너리를 간결하게 생성할 수 있습니다.
문자열 인코딩: CPython 3.3+에서 문자열은 내용에 따라 Latin-1(1바이트), UCS-2(2바이트), UCS-4(4바이트) 중 가장 효율적인 인코딩을 자동 선택합니다(PEP 393 Flexible String Representation).
__slots__: 클래스에 __slots__를 정의하면 __dict__ 대신 고정 크기 배열을 사용하여 인스턴스당 수십 바이트의 메모리를 절약합니다. 수백만 개의 객체를 생성할 때 유용합니다.
array 모듈: 동일 타입의 숫자만 저장할 때 array.array를 사용하면 리스트 대비 메모리를 4-8배 절약합니다.
4. 제어문
초급중급
제어문은 프로그램의 실행 흐름을 제어하는 구문입니다.
조건문(if-elif-else)으로 조건에 따라 다른 코드를 실행하고,
반복문(for, while)으로 코드를 반복 실행합니다.
Python 3.10부터는 Match-Case(구조적 패턴 매칭)를 통해 복잡한 데이터 구조의 형태를 매칭하여 분기할 수 있습니다.
break, continue, pass 등의 키워드로 반복 흐름을 세밀하게 제어합니다.
조건문 (if-elif-else)
조건문은 주어진 조건의 참/거짓에 따라 코드 실행 흐름을 분기합니다. if로 시작하고, 추가 조건은 elif(else if의 축약), 모든 조건에 해당하지 않는 경우 else를 사용합니다. 파이썬에는 C/Java의 삼항 연산자 대신 값1 if 조건 else 값2 형태의 조건부 표현식이 있습니다.
for 문은 시퀀스(리스트, 문자열, range 등)의 각 요소를 순회하며 실행합니다. while 문은 조건이 참인 동안 반복 실행합니다. range(n)은 0부터 n-1까지의 정수 시퀀스를 생성하며, range(start, stop, step)으로 시작값, 끝값, 간격을 지정할 수 있습니다.
# for 반복문for i inrange(5):
print(i) # 0, 1, 2, 3, 4# while 반복문
count = 0while count < 3:
print(f"카운트: {count}")
count += 1
Match-Case (Structural Pattern Matching)
Python 3.10에서 도입된 구조적 패턴 매칭은 C/Java의 switch-case보다 훨씬 강력합니다. 단순 값 비교뿐 아니라 튜플, 리스트, 클래스 인스턴스 등 데이터 구조의 형태를 매칭하고, 매칭된 부분을 변수에 바인딩할 수 있습니다. 와일드카드 _는 기본(default) 케이스에 사용됩니다.
defhttp_status(status):
match status:
case200:
return"OK"case404:
return"Not Found"case500:
return"Server Error"case _:
return"Unknown"# 패턴 매칭 with tuple/listdeflocation(point):
match point:
case (0, 0):
return"원점"case (x, 0):
returnf"x축 위: {x}"case (x, y):
returnf"점 ({x}, {y})"
Break, Continue, Else (반복문)
break는 반복문을 즉시 종료하고, continue는 현재 반복을 건너뛰고 다음 반복으로 진행합니다. 파이썬의 독특한 기능으로, for/while 뒤에 else 블록을 붙일 수 있으며, 이는 break 없이 반복이 정상 완료되었을 때만 실행됩니다.
for i inrange(10):
if i == 3:
continue# 다음 반복으로if i == 7:
break# 반복 종료print(i)
else:
print("완료") # break로 나가면 실행 안됨
💡 팁: range() 함수는 숫자 시퀀스를 생성합니다. range(5)는 0부터 4까지입니다.
초급자 가이드 - 제어문의 일상적 비유
제어문을 일상생활에 비유하면 쉽게 이해할 수 있습니다:
if문 = 교차로: "비가 오면 우산을 챙기고, 아니면 그냥 나간다" → if 비: 우산() else: 외출()
for문 = 체크리스트: "장바구니의 각 물건에 대해 가격을 확인한다" → for 물건 in 장바구니: 가격확인(물건)
while문 = 반복 알람: "목표에 도달할 때까지 계속 시도한다" → while not 목표달성: 시도()
주의:while True:는 무한 루프를 만듭니다. 반드시 break로 탈출 조건을 설정하세요. 무한 루프에 빠지면 Ctrl+C로 중단할 수 있습니다.
중급자 가이드 - 파이썬다운(Pythonic) 반복 패턴
인덱스 기반 반복 대신 파이썬다운 반복 패턴을 사용하세요:
enumerate(): 인덱스가 필요할 때 for i, v in enumerate(리스트):를 사용합니다. for i in range(len(리스트)):는 비-파이썬적(unpythonic)입니다.
zip(): 두 리스트를 동시에 순회할 때 for a, b in zip(리스트1, 리스트2):를 사용합니다.
삼항 연산자: result = "짝수" if x % 2 == 0 else "홀수"로 간결하게 조건 분기합니다.
any() / all(): if any(x > 10 for x in numbers):로 "하나라도 만족하면", if all(x > 0 for x in numbers):로 "모두 만족하면"을 표현합니다.
for-else 실전 활용: 소수 판별 시 for i in range(2, n): 반복 후 else에서 "소수임"을 확인하는 패턴이 대표적입니다.
고급자 가이드 - 반복 성능 최적화와 Match-Case 심화
대규모 데이터 처리 시 반복문 성능을 최적화하는 기법과 패턴 매칭 고급 활용법:
컴프리헨션 vs for 루프: 리스트 컴프리헨션은 일반 for 루프보다 약 20-30% 빠릅니다. C 레벨에서 최적화된 LIST_APPEND 바이트코드를 사용하기 때문입니다.
제너레이터 표현식: 메모리 절약이 필요하면 [...] 대신 (...)를 사용하세요. sum(x**2 for x in range(10**7))은 리스트를 생성하지 않아 메모리를 절약합니다.
itertools 활용: itertools.chain()으로 여러 이터러블을 연결하고, itertools.islice()로 슬라이싱하면 대용량 데이터를 메모리 효율적으로 처리할 수 있습니다.
Match-Case 클래스 패턴: case Point(x=0, y=y):처럼 클래스 인스턴스의 속성을 매칭하고 추출할 수 있습니다. __match_args__를 정의하면 위치 기반 매칭도 가능합니다.
Match-Case 가드(Guard): case x if x > 0:처럼 if 가드를 사용하여 패턴 매칭에 추가 조건을 걸 수 있습니다.
5. 함수 (Functions)
초급중급
함수는 특정 작업을 수행하는 코드 블록을 하나로 묶어 이름을 부여한 것입니다. 함수를 사용하면 동일한 코드를 여러 번 작성할 필요 없이 호출만으로 재사용할 수 있고,
프로그램을 논리적 단위로 분리하여 가독성과 유지보수성을 크게 높일 수 있습니다.
파이썬에서 함수는 일급 객체(First-class Object)로, 변수에 할당하거나 다른 함수의 인자로 전달하는 등 유연하게 활용할 수 있습니다.
또한 lambda, 클로저(closure), 데코레이터(decorator), 제너레이터(generator) 등 다양한 고급 기법을 지원합니다.
기본 함수 정의
함수는 def 키워드로 정의하며, 함수명 뒤 괄호 안에 매개변수를 선언합니다. 함수 본문 첫 줄에 문자열을 작성하면 docstring(문서 문자열)이 되어 help() 함수로 확인할 수 있습니다. return으로 값을 반환하며, 생략 시 None이 반환됩니다.
매개변수에 기본값(default value)을 지정하면 호출 시 해당 인자를 생략할 수 있습니다. 기본값이 있는 매개변수는 반드시 기본값이 없는 매개변수 뒤에 위치해야 합니다. 여러 값을 반환하려면 튜플로 반환하고 언패킹하여 받을 수 있습니다.
defadd_numbers(a, b=0):
"""두 수를 더하는 함수 (기본값 지원)"""return a + b
add_numbers(5, 3) # 8add_numbers(5) # 5 (b의 기본값 0 사용)
*args와 **kwargs
*args는 위치 인자를 튜플로 묶어서 받고, **kwargs는 키워드 인자를 딕셔너리로 묶어서 받습니다. 이를 통해 함수가 임의 개수의 인자를 처리할 수 있습니다. 반대로, 호출 시 *리스트나 **딕셔너리를 사용하면 인자를 풀어서(unpacking) 전달할 수 있습니다.
클로저(Closure)는 외부 함수의 변수를 기억하는 내부 함수이며, 상태를 유지하는 함수를 만들 때 유용합니다. 데코레이터(Decorator)는 @ 문법으로 함수를 감싸서 기존 기능을 변경하지 않으면서 새로운 동작을 추가하는 패턴입니다. (더 실전적인 데코레이터 활용은 아래 데코레이터 (Decorators) 섹션에서 다룹니다.)
파이썬에서 함수는 일급 객체(First-class Object)입니다. 이는 함수를 변수에 할당하고, 다른 함수의 인자로 전달하고, 함수의 반환값으로 사용할 수 있다는 의미입니다. 이 특성이 데코레이터, 콜백, 함수형 프로그래밍 등 고급 패턴의 기반이 됩니다.
# 함수를 변수에 할당
my_func = print
my_func("Hello")
# 함수를 인자로 전달defapply(func, x):
return func(x)
defdouble(x): return x * 2print(apply(double, 5)) # 10# 함수 반환defget_adder(n):
returnlambda x: x + n
add5 = get_adder(5)
print(add5(3)) # 8
함수 Annotations
함수 어노테이션은 매개변수와 반환값의 타입 정보를 메타데이터로 기록합니다. 런타임에는 강제되지 않지만, mypy와 같은 정적 분석 도구가 타입 오류를 사전에 검출하는 데 활용합니다. __annotations__ 속성으로 어노테이션 정보를 확인할 수 있습니다.
defgreet(name: str, times: int = 1) -> str:
return"Hello! " * times + name
# Annotations 확인print(greet.__annotations__)
# {'name': , 'times': , 'return': }
제너레이터 (Generator) - 함수에서
제너레이터는 yield 키워드를 사용하여 값을 하나씩 느리게(lazy) 생성하는 특수 함수입니다. 모든 값을 메모리에 한꺼번에 저장하지 않으므로 대용량 데이터 처리에 매우 효율적입니다. next()로 다음 값을 수동으로 가져오거나, for 문에서 자동으로 순회할 수 있습니다.
deffibonacci(n):
a, b = 0, 1for _ inrange(n):
yield a
a, b = b, a + b
for num infibonacci(10):
print(num) # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34# next()로 수동 가져오기
gen = fibonacci(5)
print(next(gen)) # 0print(next(gen)) # 1
데코레이터 (Decorators)
데코레이터는 기존 함수를 감싸서(wrapping) 원본 코드를 수정하지 않고 로깅, 인증, 성능 측정 등 횡단 관심사(Cross-cutting Concerns)를 추가하는 디자인 패턴입니다. @decorator_name 문법은 func = decorator_name(func)의 축약형입니다. functools.wraps를 사용하면 원본 함수의 메타데이터가 보존됩니다.
deftimer_decorator(func):
defwrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 실행 시간: {end-start:.2f}초")
return result
return wrapper
@timer_decoratordefslow_function():
import time
time.sleep(1)
return"완료"slow_function()
컨텍스트 매니저 (Context Manager)
with 문은 리소스(파일, 네트워크 연결, 락 등)의 획득과 해제를 자동으로 관리합니다. 예외가 발생하더라도 반드시 리소스가 정리되므로, try-finally보다 안전하고 간결합니다. __enter__와 __exit__ 메서드를 구현하거나 contextlib.contextmanager 데코레이터로 커스텀 컨텍스트 매니저를 만들 수 있습니다.
withopen("test.txt", "w") as f:
f.write("안녕하세요!")
# 파일이 자동으로 닫힘
async/await (비동기 프로그래밍)
비동기 프로그래밍은 I/O 대기 시간(네트워크 요청, 파일 읽기 등)에 다른 작업을 수행하여 효율성을 극대화합니다. async def로 코루틴(coroutine) 함수를 정의하고, await로 비동기 작업의 완료를 기다립니다. asyncio는 Python 표준 라이브러리의 비동기 I/O 프레임워크로, asyncio.run()으로 이벤트 루프를 시작하며, asyncio.gather()로 여러 코루틴을 동시에 실행할 수 있습니다.
함수 = 레시피: "재료(매개변수)를 넣으면 요리(결과)가 나오는 레시피"입니다. def 볶음밥(밥, 계란): return 완성된_볶음밥
왜 함수를 만드나?: 같은 코드를 10번 반복해서 쓰면 수정할 때 10곳을 다 고쳐야 합니다. 함수로 만들면 1곳만 고치면 됩니다.
return vs print: return은 값을 "돌려주는 것"이고, print는 화면에 "보여주는 것"입니다. return한 값은 변수에 저장하거나 다른 연산에 사용할 수 있습니다.
초보자 팁: 함수 이름은 동사로 시작하세요. calculate_total(), get_user_name(), send_email()처럼 "무엇을 하는지" 이름에 담으면 읽기 좋습니다.
중급자 가이드 - 함수 설계 원칙과 실무 패턴
좋은 함수를 작성하기 위한 실무 원칙:
단일 책임 원칙: 하나의 함수는 하나의 작업만 수행해야 합니다. 함수 설명에 "그리고"가 들어가면 분리를 고려하세요.
가변 기본값 함정: def f(items=[])는 모든 호출이 같은 리스트를 공유합니다! def f(items=None): items = items or []로 작성하세요.
키워드 전용 인자: def f(*, name, age)에서 * 뒤의 인자는 반드시 키워드로 전달해야 합니다. API 설계 시 실수를 방지합니다.
위치 전용 인자(3.8+): def f(x, y, /)에서 / 앞의 인자는 위치로만 전달할 수 있습니다.
functools.wraps: 데코레이터 작성 시 반드시 @functools.wraps(func)를 사용하여 원본 함수의 __name__, __doc__ 등 메타데이터를 보존하세요.
고급자 가이드 - 함수의 내부 동작과 고급 패턴
함수 객체의 내부 구조와 고급 활용 패턴:
함수 객체 속성: 함수는 __code__(바이트코드), __closure__(클로저 변수), __defaults__(기본값), __globals__(전역 네임스페이스) 등의 속성을 가집니다.
LEGB 규칙: 변수 탐색 순서는 Local → Enclosing → Global → Built-in입니다. nonlocal은 Enclosing 스코프의 변수를 수정할 때, global은 Global 스코프의 변수를 수정할 때 사용합니다.
제너레이터 send(): gen.send(value)로 제너레이터에 값을 전달할 수 있어 양방향 통신이 가능합니다. 이는 코루틴(coroutine)의 기초입니다.
yield from: yield from iterable은 서브 제너레이터에게 제어권을 위임합니다. 재귀적 제너레이터나 코루틴 체이닝에 사용됩니다.
데코레이터 팩토리: 인자를 받는 데코레이터는 3단 중첩 함수입니다. @retry(max_attempts=3)처럼 설정 가능한 데코레이터를 만들 수 있습니다.
asyncio 동시성: asyncio.gather()로 여러 코루틴을 동시에 실행하고, asyncio.Semaphore로 동시 실행 수를 제한할 수 있습니다.
6. 객체지향 프로그래밍 (OOP)
중급고급
객체지향 프로그래밍(OOP)은 관련 데이터와 동작을 하나의 객체(Object)로 묶어 프로그램을 설계하는 패러다임입니다.
캡슐화(Encapsulation)로 데이터를 보호하고, 상속(Inheritance)으로 코드를 재사용하며,
다형성(Polymorphism)으로 같은 인터페이스에 다른 구현을 제공합니다.
파이썬은 모든 것이 객체이며, 클래스 기반 OOP를 class 키워드, 특수 메서드(dunder methods), @property, ABC, Protocol 등으로 풍부하게 지원합니다.
클래스와 객체
클래스는 객체를 만들기 위한 설계도(blueprint)이고, 객체는 클래스로부터 생성된 실체(instance)입니다. __init__ 메서드는 생성자로, 객체 초기화 시 자동 호출됩니다. self는 인스턴스 자신을 가리키며, 모든 인스턴스 메서드의 첫 번째 매개변수입니다.
classDog:
"""개를 나타내는 클래스"""def__init__(self, name, age):
self.name = name
self.age = age
defbark(self):
returnf"{self.name}가 멍멍짖습니다!"# 객체 생성
my_dog = Dog("멍이", 3)
print(my_dog.bark()) # 멍이가 멍멍짖습니다!
상속 (Inheritance)
상속은 기존 클래스(부모/기반 클래스)의 속성과 메서드를 새로운 클래스(자식/파생 클래스)가 물려받는 메커니즘입니다. 코드 재사용을 촉진하고, 자식 클래스에서 부모 메서드를 오버라이드(재정의)하여 다형성을 구현할 수 있습니다.
파이썬은 하나의 클래스가 여러 부모 클래스를 동시에 상속받는 다중 상속을 지원합니다. 이를 통해 여러 기능을 조합한 클래스를 만들 수 있으며, 이런 패턴을 믹스인(Mixin)이라 합니다. 다중 상속 시 메서드 충돌은 MRO(Method Resolution Order) 규칙에 따라 해결됩니다.
MRO는 다중 상속에서 메서드를 찾는 순서를 결정하는 알고리즘입니다. 파이썬은 C3 선형화(C3 Linearization) 알고리즘을 사용하며, 클래스.__mro__ 또는 클래스.mro()로 순서를 확인할 수 있습니다. super()는 이 MRO 순서에 따라 다음 클래스의 메서드를 호출합니다.
print(Duck.__mro__)
# (, , ...)
super()와 메서드 오버라이드
super()는 부모 클래스의 메서드를 호출할 때 사용합니다. 자식 클래스에서 부모와 같은 이름의 메서드를 정의하면 오버라이드(재정의)되며, 부모의 기능을 확장하려면 super().메서드()를 호출한 후 추가 로직을 작성합니다.
특수 메서드(매직 메서드)는 이름 양쪽에 이중 밑줄(__)이 있어 던더(Dunder) 메서드라 불립니다. 연산자 오버로딩(__add__, __eq__), 문자열 표현(__repr__, __str__), 컨테이너 프로토콜(__len__, __getitem__) 등 파이썬의 내장 동작을 커스터마이즈할 수 있습니다.
@property 데코레이터를 사용하면 메서드를 속성처럼 접근할 수 있으며, getter/setter/deleter를 통해 값의 유효성 검증이나 계산된 속성을 구현할 수 있습니다. 이는 Java의 getter/setter 패턴보다 훨씬 파이썬다운(Pythonic) 방식입니다.
클래스 변수는 클래스 본문에 직접 선언되어 모든 인스턴스가 공유하는 변수입니다. 인스턴스 변수는 self를 통해 선언되어 각 인스턴스마다 독립적인 값을 가집니다. 클래스 변수에 가변 객체(리스트 등)를 사용할 때는 의도치 않은 공유에 주의해야 합니다.
classDog:
species = "Canis familiaris"# 클래스 변수def__init__(self, name):
self.name = name # 인스턴스 변수print(Dog.species) # Canis familiaris
dog1 = Dog("Max")
dog2 = Dog("Buddy")
print(dog1.name, dog2.name) # Max Buddy
정적 메서드와 클래스 메서드
@staticmethod는 인스턴스(self)나 클래스(cls)에 접근하지 않는 유틸리티 함수를 클래스 내에 정의할 때 사용합니다. @classmethod는 첫 번째 인자로 클래스 자체(cls)를 받아 팩토리 메서드나 대안 생성자를 만들 때 활용됩니다.
classMyClass:
value = 10@staticmethoddefstatic_method():
return"정적 메서드"@classmethoddefclass_method(cls):
return f"클래스 메서드: {cls.value}"print(MyClass.static_method()) # 정적 메서드print(MyClass.class_method()) # 클래스 메서드: 10
Private 멤버 (Encapsulation)
파이썬에는 진정한 private 접근 제한이 없지만, 언더스코어 관례로 접근을 제어합니다. _변수는 '내부용'이라는 관례적 표시이고, __변수(이중 밑줄)는 네임 맹글링(Name Mangling)이 적용되어 _클래스명__변수로 변환됩니다. 이를 통해 하위 클래스와의 이름 충돌을 방지합니다.
Python 3.5에서 도입된 타입 힌트는 함수와 변수의 예상 타입을 명시적으로 선언합니다. 런타임에 강제되지 않지만, IDE의 자동완성 지원, mypy를 통한 정적 타입 검사, 코드 문서화 역할을 합니다. typing 모듈의 List, Dict, Optional, Union 등으로 복잡한 타입을 표현할 수 있습니다.
Python 3.7에서 도입된 @dataclass 데코레이터는 __init__, __repr__, __eq__ 등을 자동 생성하여 데이터 저장용 클래스를 간결하게 정의합니다. field()로 기본값, 비교 제외 등 세밀한 설정이 가능하며, frozen=True 옵션으로 불변 데이터 클래스를 만들 수 있습니다.
from dataclasses import dataclass, field
@dataclassclassUser:
name: str
age: int
email: str = "unknown@example.com"
active: bool = field(default=True)
# 사용
user = User("민준", 25)
print(user) # User(name='민준', age=25, ...)
NamedTuple
NamedTuple은 일반 튜플의 불변성과 인덱스 접근을 유지하면서, 필드에 이름으로 접근할 수 있는 자료형입니다. 간단한 데이터 레코드를 표현할 때 유용하며, 메모리 효율이 딕셔너리보다 좋습니다. typing.NamedTuple을 사용하면 타입 힌트도 추가할 수 있습니다.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y) # 10 20print(p[0], p[1]) # 10 20
Enum (열거형)
Enum은 관련된 상수들을 하나의 클래스로 그룹화하여 타입 안전한 상수 집합을 만듭니다. 매직 넘버 대신 의미 있는 이름을 사용하여 코드 가독성을 높이고, 잘못된 값 사용을 방지합니다. auto()로 값을 자동 할당할 수도 있습니다.
from enum import Enum, auto
classColor(Enum):
RED = 1
GREEN = 2
BLUE = 3print(Color.RED) # Color.REDprint(Color.RED.name) # REDprint(Color.RED.value) # 1
ABC (추상 베이스 클래스)
추상 베이스 클래스(ABC)는 @abstractmethod로 선언된 메서드를 반드시 구현하도록 계약(contract)을 강제합니다. ABC를 직접 인스턴스화하면 에러가 발생하며, 상속받은 클래스가 추상 메서드를 구현하지 않으면 역시 인스턴스화할 수 없습니다. 인터페이스 역할을 합니다.
Python 3.8에서 도입된 Protocol은 ABC와 달리 명시적 상속 없이 특정 메서드/속성을 가진 객체를 타입으로 인식합니다. 이를 구조적 타이핑(Structural Typing) 또는 덕 타이핑(Duck Typing)의 정적 버전이라 합니다. Go 언어의 인터페이스와 유사한 개념입니다.
클래스 = 붕어빵 틀: 틀(클래스)로 붕어빵(객체)을 찍어냅니다. 틀 하나로 여러 개의 붕어빵을 만들 수 있습니다.
언제 클래스를 만드나?: 관련된 데이터(속성)와 기능(메서드)을 하나로 묶고 싶을 때입니다. 예: "학생" 데이터(이름, 성적)와 기능(성적 계산, 학년 올리기).
self란?: 클래스 안에서 "나 자신"을 가리키는 참조입니다. self.name은 "이 객체의 이름"이라는 뜻입니다.
__init__이란?: 객체가 생성될 때 자동으로 호출되는 초기화 함수입니다. 생성자(constructor)라고도 합니다.
팁: 처음에는 클래스 없이 함수만으로 프로그래밍해도 됩니다. 코드가 복잡해지고 관련 데이터와 함수가 많아질 때 클래스 도입을 고려하세요.
중급자 가이드 - OOP 설계 원칙(SOLID)
유지보수 가능한 코드를 위한 SOLID 원칙:
S - 단일 책임(SRP): 클래스는 하나의 이유로만 변경되어야 합니다. "사용자 인증"과 "이메일 전송"을 같은 클래스에 넣지 마세요.
O - 개방-폐쇄(OCP): 확장에는 열려있고, 수정에는 닫혀있어야 합니다. 새 기능 추가 시 기존 코드를 수정하지 않고 상속/합성으로 확장하세요.
L - 리스코프 치환(LSP): 자식 클래스는 부모 클래스를 대체할 수 있어야 합니다.
I - 인터페이스 분리(ISP): 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리하세요.
D - 의존성 역전(DIP): 구체 클래스가 아닌 추상(ABC/Protocol)에 의존하세요.
실무 팁: 파이썬에서는 "상속보다 합성(Composition over Inheritance)"을 선호합니다. 다중 상속의 복잡성을 피하고 Mixin 패턴을 활용하세요.
고급자 가이드 - 메타클래스와 디스크립터
파이썬 OOP의 깊은 내부 메커니즘:
메타클래스(Metaclass): 클래스의 클래스입니다. class MyMeta(type):로 정의하며, 클래스 생성 과정을 제어합니다. __new__에서 클래스를 생성하고, __init__에서 초기화하며, __init_subclass__는 3.6+에서 더 간단한 대안을 제공합니다.
디스크립터(Descriptor): __get__, __set__, __delete__를 구현한 객체로, 속성 접근을 가로챕니다. @property, @classmethod, @staticmethod가 모두 디스크립터로 구현되어 있습니다.
__new__ vs __init__: __new__는 인스턴스를 생성(메모리 할당)하고, __init__은 생성된 인스턴스를 초기화합니다. 불변 객체(tuple, str 등)를 커스터마이즈할 때 __new__를 오버라이드해야 합니다.
__init_subclass__(3.6+): 메타클래스 없이 서브클래스 생성을 후킹할 수 있습니다. 플러그인 시스템, 자동 등록 등에 유용합니다.
__class_getitem__(3.7+): MyClass[int]처럼 클래스를 제네릭하게 사용할 수 있게 해주며, typing.Generic의 기반입니다.
7. 예외 처리 (Exception Handling)
초급중급
예외(Exception)는 프로그램 실행 중 발생하는 오류 상황을 나타내는 객체입니다.
파이썬의 예외 처리 메커니즘을 사용하면 오류가 발생하더라도 프로그램이 비정상적으로 종료되지 않고, 적절한 복구 로직을 수행한 뒤 계속 실행할 수 있습니다.
파이썬은 ZeroDivisionError, TypeError, ValueError, FileNotFoundError 등 60개 이상의 내장 예외 클래스를 제공하며,
이들은 모두 BaseException을 최상위 부모로 하는 계층 구조를 이룹니다.
try-except-else-finally 구문으로 예외를 처리하고, raise로 의도적으로 예외를 발생시킬 수 있습니다.
try-except 구조
try 블록에서 예외가 발생하면 해당 예외 타입과 일치하는 except 블록이 실행됩니다. 예외를 잡지 않으면 프로그램이 비정상 종료됩니다. except Exception as e로 예외 객체를 변수에 바인딩하여 에러 메시지를 확인할 수 있습니다.
try:
result = 10 / 0except ZeroDivisionError:
print("0으로 나눌 수 없습니다!")
여러 예외 처리
여러 except 절을 사용하여 예외 타입별로 다른 처리를 할 수 있습니다. else 블록은 예외가 발생하지 않았을 때만 실행되고, finally 블록은 예외 발생 여부와 관계없이 항상 실행됩니다. finally는 파일 닫기, 리소스 해제 등 정리 작업에 사용합니다.
예외 계층 설계: 라이브러리를 설계할 때 기본 예외 클래스를 만들고(class MyLibError(Exception)), 세부 예외를 파생시킵니다. 사용자가 except MyLibError:로 모든 라이브러리 예외를 잡을 수 있습니다.
__traceback__ 관리: 예외 객체의 __traceback__ 속성은 트레이스백 객체를 참조합니다. 메모리 누수를 방지하려면 del e.__traceback__ 또는 raise e from None을 사용하세요.
8. 모듈과 패키지 (Modules & Packages)
초급중급
모듈(Module)은 파이썬 코드(함수, 클래스, 변수 등)가 담긴 .py 파일 하나를 의미합니다.
패키지(Package)는 관련 모듈들을 디렉터리 구조로 묶어 체계적으로 관리하는 방법입니다.
파이썬은 "배터리 포함(Batteries Included)" 철학에 따라 os, sys, json, datetime, pathlib 등
300개 이상의 표준 라이브러리 모듈을 제공하며, PyPI(Python Package Index)를 통해 50만 개 이상의 서드파티 패키지를 pip로 쉽게 설치할 수 있습니다.
이러한 방대한 생태계가 파이썬의 강력한 경쟁력입니다.
모듈 가져오기
import 문으로 모듈을 가져옵니다. import 모듈은 전체 모듈을 가져오고, from 모듈 import 함수는 특정 항목만 가져옵니다. as를 사용하면 별칭을 지정할 수 있어 코드를 간결하게 만듭니다. from 모듈 import *는 이름 충돌 위험이 있으므로 일반적으로 권장되지 않습니다.
# 전체 모듈 가져오기import math
print(math.pi) # 3.141592653589793print(math.sqrt(16)) # 4.0# 특정 함수만 가져오기from math import pi, sqrt
print(sqrt(25)) # 5.0# 별칭 사용import numpy as np
import pandas as pd
표준 라이브러리 모듈
파이썬은 "배터리 포함(Batteries Included)" 철학에 따라 방대한 표준 라이브러리를 제공합니다. 별도 설치 없이 바로 사용할 수 있는 주요 모듈들입니다.
📅 datetime
날짜와 시간 처리
🔢 random
랜덤 숫자 생성
📊 json
JSON 데이터 처리
🌐 urllib
네트워크 요청
주요 내장 함수 (Built-in Functions)
파이썬은 별도 import 없이 어디서든 사용 가능한 내장 함수(built-in functions)를 약 70개 이상 제공합니다. 데이터 변환, 입출력, 반복 처리 등 프로그래밍의 기본 작업을 담당하는 핵심 도구입니다.
함수
설명
print()
출력
input()
입력
len()
길이
range()
범위 생성
enumerate()
인덱스와 값
zip()
여러 시퀀스 병합
map()
함수 적용
filter()
조건 필터링
sorted()
정렬
reversed()
역순
sum(), min(), max()
합계, 최소, 최대
abs()
절대값
round()
반올림
type()
타입 확인
isinstance()
타입 확인
dir()
속성 목록
help()
도움말
사용자 정의 모듈
모든 .py 파일은 모듈로 사용할 수 있습니다. 다른 파일에서 import하면 해당 파일의 함수, 클래스, 변수를 사용할 수 있습니다. if __name__ == "__main__": 구문을 사용하면 모듈이 직접 실행될 때만 특정 코드가 실행되도록 할 수 있습니다.
pip는 파이썬의 공식 패키지 관리자로, PyPI(Python Package Index)에서 30만 개 이상의 패키지를 설치할 수 있습니다. requirements.txt 파일로 프로젝트의 의존성을 관리하며, pip freeze > requirements.txt로 현재 설치된 패키지 목록을 내보낼 수 있습니다.
# 패키지 설치
pip install numpy pandas matplotlib
# 특정 버전
pip install numpy==1.24.0
#requirements.txt 사용
pip install -r requirements.txt
# 패키지 목록
pip list
# 패키지 검색
pip search "keyword"
# 패키지 제거
pip uninstall package_name
가상 환경 (Virtual Environment)
가상 환경은 프로젝트마다 독립된 파이썬 패키지 공간을 만들어 의존성 충돌을 방지합니다. 프로젝트 A가 Django 3.x, 프로젝트 B가 Django 4.x를 사용하더라도 각각의 가상 환경에서 충돌 없이 실행할 수 있습니다. venv는 파이썬 3.3부터 표준 라이브러리에 포함되어 있습니다.
# venv 생성
python -m venv myenv
# 활성화 (Windows)
myenv\Scripts\activate
# 활성화 (Linux/Mac)
source myenv/bin/activate
# 비활성화
deactivate
주요 표준 라이브러리
파이썬의 표준 라이브러리는 시스템 상호작용, 병렬 처리, 파일 관리, 로깅 등 실무에서 자주 필요한 기능을 포괄합니다. 다음은 가장 많이 사용되는 표준 라이브러리 모듈입니다.
sys
시스템 매개변수 및 함수. 인터프리터와 직접 상호작용합니다.
os
운영체제 상호작용
subprocess
프로세스 실행
threading
스레드 기반 병렬처리
multiprocessing
프로세스 기반 병렬처리
pathlib
경로 객체 지향 처리
argparse
명령행 인자 파싱
logging
로깅 프레임워크
tempfile
임시 파일/디렉토리
shutil
고수준 파일 operations
초급자 가이드 - 모듈과 패키지를 쉽게 이해하기
모듈과 패키지를 일상에 비유하면:
모듈 = 도구 상자: 다른 사람이 만든 기능(도구)을 가져다 쓰는 것입니다. import math하면 수학 관련 함수들을 모두 사용할 수 있습니다.
pip = 앱 스토어: 전 세계 개발자들이 만든 패키지를 무료로 설치할 수 있는 저장소입니다.
가상 환경 = 작업 폴더: 프로젝트마다 독립된 공간을 만들어 패키지 버전 충돌을 방지합니다.
if __name__ == "__main__": 이해하기: 이 코드는 "이 파일이 직접 실행될 때만 아래 코드를 실행해라"라는 뜻입니다. 다른 파일에서 import할 때는 실행되지 않습니다. 모든 스크립트의 실행 진입점에 사용합니다.
상대 vs 절대 import: 패키지 내에서 from . import module(상대 import)을 사용하면 패키지 이름 변경 시에도 안전합니다. 실행 스크립트에서는 절대 import를 사용합니다.
고급자 가이드 - import 시스템의 내부 동작
파이썬 import 시스템의 내부 메커니즘:
import 과정: (1) sys.modules 캐시 확인 → (2) sys.meta_path의 finder 검색 → (3) sys.path 경로에서 모듈 찾기 → (4) 모듈 로딩 및 실행 → (5) sys.modules에 캐싱
커스텀 Importer: importlib.abc.MetaPathFinder와 importlib.abc.Loader를 구현하면 데이터베이스, 네트워크, ZIP 파일 등에서 모듈을 로드할 수 있습니다.
순환 import: A가 B를 import하고 B가 A를 import하면 순환 참조가 발생합니다. 해결법: (1) import를 함수 내부로 이동, (2) 공통 모듈 분리, (3) 구조 재설계
__all__: 모듈에 __all__ = ["func1", "Class1"]을 정의하면 from module import *에서 내보낼 항목을 제한합니다.
namespace package: __init__.py 없이도 패키지를 만들 수 있습니다(PEP 420). 여러 디렉토리에 분산된 패키지를 하나로 합칠 때 사용합니다.
9. 파일 입출력 (File I/O)
초급중급
파일 입출력(File I/O)은 프로그램의 데이터를 디스크에 영구적으로 저장하고, 저장된 데이터를 다시 읽어오는 핵심 기능입니다.
프로그램이 종료되면 메모리의 데이터는 사라지지만, 파일에 기록한 데이터는 유지됩니다.
파이썬은 open() 내장 함수와 with 컨텍스트 매니저를 통해 안전하고 간결한 파일 처리를 지원하며,
텍스트 파일뿐 아니라 JSON, CSV, 바이너리 파일, pickle 직렬화 등 다양한 형식을 다룰 수 있습니다.
Python 3.4 이후에는 pathlib 모듈로 경로를 객체 지향적으로 다루는 방법도 제공합니다.
파일 읽기
파일 읽기는 open() 함수로 파일을 열고 read(), readline(), readlines() 메서드로 내용을 가져옵니다. with 문을 사용하면 파일이 자동으로 닫히므로 리소스 누수를 방지할 수 있습니다. 한글 파일은 encoding="utf-8"을 명시하는 것이 안전합니다.
# 전체 읽기withopen("file.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
# 줄 단위 읽기withopen("file.txt", "r") as f:
for line in f:
print(line.strip())
# readlines()로 목록 가져오기withopen("file.txt") as f:
lines = f.readlines()
# seek()로 위치 이동withopen("file.txt") as f:
f.seek(0) # 처음으로print(f.read(10)) # 첫 10자
파일 쓰기
"w" 모드는 파일을 새로 생성하거나 기존 내용을 덮어쓰고, "a" 모드는 기존 내용 끝에 추가합니다. write()는 문자열 하나를 쓰고, writelines()는 문자열 리스트를 한꺼번에 씁니다. 줄바꿈은 자동 추가되지 않으므로 필요 시 \n을 직접 포함해야 합니다.
# 새 파일 작성 (w: overwrite, a: append)withopen("output.txt", "w", encoding="utf-8") as f:
f.write("첫 번째 줄\n")
f.write("두 번째 줄\n")
# 여러 줄 쓰기
lines = ["줄 1", "줄 2", "줄 3"]
withopen("output.txt", "w") as f:
f.writelines("\n".join(lines))
파일 모드 (Modes)
open() 함수의 두 번째 인자로 파일 모드를 지정합니다. 모드를 조합하여 사용할 수 있으며(예: "rb"는 바이너리 읽기), 기본값은 텍스트 읽기("rt")입니다.
모드
설명
r
읽기 (기본)
w
쓰기 (덮어쓰기)
a
추가 (끝에)
r+
읽기+쓰기
w+
읽기+쓰기 (덮어쓰기)
a+
읽기+추가
b
바이너리 모드
t
텍스트 모드 (기본)
pickle (객체 직렬화)
pickle은 파이썬 객체를 바이트 스트림으로 변환(직렬화)하여 파일에 저장하고, 나중에 다시 파이썬 객체로 복원(역직렬화)합니다. 리스트, 딕셔너리, 클래스 인스턴스 등 거의 모든 파이썬 객체를 저장할 수 있지만, 신뢰할 수 없는 출처의 pickle 파일은 보안 위험이 있으므로 주의해야 합니다.
import pickle
# 객체 저장
data = {"name": "민준", "age": 25}
withopen("data.pkl", "wb") as f:
pickle.dump(data, f)
# 객체 읽기withopen("data.pkl", "rb") as f:
loaded_data = pickle.load(f)
pathlib (객체 지향 경로)
Python 3.4에서 도입된 pathlib은 파일 경로를 객체 지향적으로 다룹니다. / 연산자로 경로를 결합하고, 메서드 체이닝으로 파일 존재 확인, 읽기/쓰기, 패턴 검색 등을 간결하게 수행합니다. os.path보다 직관적이며 현대 파이썬에서 권장됩니다.
from pathlib import Path
p = Path("myproject")
# 경로 생성
(p / "src" / "main.py")
# 파일 존재 확인
p.exists()
p.is_file()
p.is_dir()
#.glob()로 파일 찾기for f in Path(".").glob("*.py"):
print(f)
# 파일 읽기
content = Path("file.txt").read_text()
# 파일 쓰기
Path("new.txt").write_text("Hello")
tempfile (임시 파일)
tempfile 모듈은 시스템의 임시 디렉토리에 안전하게 임시 파일/디렉토리를 생성합니다. with 문과 함께 사용하면 블록 종료 시 자동으로 삭제되어 정리 걱정이 없습니다. 파일명 충돌을 자동으로 방지합니다.
import tempfile
# 임시 파일with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
f.write("임시 내용")
temp_name = f.name
# 임시 디렉토리with tempfile.TemporaryDirectory() as tmpdir:
print(tmpdir)
JSON 파일 처리
JSON(JavaScript Object Notation)은 웹 API, 설정 파일 등에서 가장 널리 사용되는 데이터 교환 형식입니다. json.dump()로 파일에 쓰고, json.load()로 읽습니다. ensure_ascii=False를 지정하면 한글이 유니코드 이스케이프 없이 그대로 저장됩니다.
import json
# 쓰기
data = {"name": "민준", "age": 25}
withopen("data.json", "w") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 읽기withopen("data.json", "r") as f:
data = json.load(f)
CSV 파일 처리
CSV(Comma-Separated Values)는 표 형태의 데이터를 텍스트로 저장하는 형식으로, 엑셀이나 데이터베이스와의 데이터 교환에 널리 사용됩니다. csv.writer/csv.reader로 기본 읽기/쓰기를, csv.DictWriter/csv.DictReader로 헤더 기반 처리를 할 수 있습니다.
import csv
# CSV 쓰기withopen("data.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["이름", "나이"])
writer.writerow(["민준", 25])
writer.writerow(["서연", 23])
# CSV 읽기withopen("data.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
💡 팁: 항상 with문을 사용하여 파일을 다루세요. 파일이 자동으로 닫힙니다.
초급자 가이드 - 파일 다루기의 기본 패턴
파일 작업에서 가장 중요한 3가지 패턴:
반드시 with문 사용: with open("파일") as f: 패턴은 파일을 안전하게 열고 닫습니다. with 없이 f = open("파일")로 열면, 예외 발생 시 파일이 닫히지 않아 데이터 손실이 생길 수 있습니다.
인코딩 명시: 한글이 포함된 파일은 반드시 encoding="utf-8"을 지정하세요. 지정하지 않으면 운영체제 기본 인코딩이 사용되어 Windows에서 한글 깨짐이 발생합니다.
파일 모드 구분: "r"(읽기), "w"(쓰기, 기존 내용 삭제!), "a"(추가, 기존 내용 유지). "w" 모드는 기존 파일을 완전히 덮어쓰므로 주의하세요!
중급자 가이드 - 실무 파일 처리 패턴
실무에서 자주 사용하는 파일 처리 패턴과 주의사항:
대용량 파일 처리: for line in f:는 한 줄씩 읽어 메모리 효율적입니다. f.read()는 전체 내용을 메모리에 올리므로 대용량 파일에서는 피하세요.
pathlib 우선 사용: os.path 대신 pathlib.Path를 사용하세요. Path("dir") / "file.txt"로 경로를 결합하고, .read_text(), .write_text()로 간결하게 읽기/쓰기할 수 있습니다.
원자적 쓰기: 파일 쓰기 중 프로그램이 중단되면 데이터가 손상될 수 있습니다. 임시 파일에 먼저 쓴 후 os.replace()로 이름을 바꾸면 안전합니다.
구조화된 데이터: 단순 텍스트보다 JSON(설정 파일, API), CSV(표 데이터), YAML(설정 파일), TOML(프로젝트 설정)을 목적에 맞게 선택하세요.
고급자 가이드 - 고성능 파일 I/O
대규모 데이터를 효율적으로 처리하기 위한 고급 I/O 기법:
mmap (메모리 매핑): mmap.mmap()은 파일을 메모리에 매핑하여 랜덤 접근 성능을 극대화합니다. 수 GB 파일에서 특정 위치의 데이터를 빠르게 읽을 때 유용합니다.
io.BufferedReader/Writer: 버퍼 크기를 조절하여 I/O 성능을 최적화합니다. SSD에서는 더 큰 버퍼(예: 1MB)가 효율적입니다.
aiofiles: asyncio와 함께 비동기 파일 I/O를 수행합니다. 많은 파일을 동시에 처리할 때 이벤트 루프를 블로킹하지 않습니다.
orjson / ujson: 표준 json 모듈보다 3-10배 빠른 JSON 처리를 제공합니다. 대량의 JSON 데이터를 다룰 때 유용합니다.
Parquet / HDF5: 대규모 데이터 분석에서는 CSV 대신 Apache Parquet(컬럼 기반, 압축)이나 HDF5(계층적 데이터)를 사용하면 10-100배 빠른 읽기/쓰기가 가능합니다.
10. 정규표현식 (Regular Expressions)
중급고급
정규표현식(regex)은 문자열 패턴을 정의하는 특수한 표기법으로, 데이터 검증, 텍스트 검색/치환, 로그 파싱 등에 널리 사용됩니다. 파이썬의 re 모듈이 정규표현식 기능을 제공하며, 패턴 문자열은 r"..."(raw string)로 작성하여 백슬래시 이스케이프 문제를 방지합니다.
기본 매칭
re.search()는 문자열 전체에서 패턴을 검색하고, re.match()는 문자열의 시작 부분에서만 매칭합니다. 매칭에 성공하면 Match 객체를 반환하고, 실패하면 None을 반환합니다.
import re
# 매칭 여부 확인
pattern = r"\d+"
text = "내 번호는 010-1234-5678입니다"if re.search(pattern, text):
print("숫자 발견!")
패턴 매칭 함수
re 모듈은 다양한 매칭 함수를 제공합니다. findall()은 모든 매칭을 리스트로 반환하고, sub()은 매칭된 부분을 대체하며, split()은 패턴을 기준으로 문자열을 분할합니다. compile()로 패턴을 미리 컴파일하면 반복 사용 시 성능이 향상됩니다.
# findall: 모든 매칭 찾기
result = re.findall(r"\d+", "123 abc 456 def 789")
print(result) # ['123', '456', '789']# sub: 대체
new_text = re.sub(r"\d+", "X", "123 abc 456")
print(new_text) # X abc X# split: 분리
words = re.split(r"\s+", "hello world python")
print(words) # ['hello', 'world', 'python']
자주 사용하는 패턴
정규표현식의 핵심 메타문자들입니다. 소문자(\d, \w, \s)는 해당 문자를 매칭하고, 대문자(\D, \W, \S)는 그 반대를 매칭합니다. {n,m}으로 반복 횟수를 정밀하게 지정할 수 있습니다.
패턴
의미
예시
\d
숫자
0-9
\w
단어 문자
a-z, A-Z, 0-9, _
\s
공백
스페이스, 탭, 줄바꿈
.
모든 문자
줄바꿈 제외
*
0회 이상
ab* = a, ab, abb...
+
1회 이상
ab+ = ab, abb...
?
0회 또는 1회
ab? = a, ab
^
시작
^hello
$
끝
world$
이메일 검증 예제
정규표현식의 실전 활용 예시입니다. ^와 $로 문자열 전체가 패턴에 맞는지 확인합니다. 실제 서비스에서는 email-validator 같은 전용 라이브러리 사용이 더 안정적입니다.
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
emails = ["test@example.com", "invalid-email", "user@domain.co.kr"]
for email in emails:
if re.match(email_pattern, email):
print(f"{email}: 유효함")
else:
print(f"{email}: 무효함")
초급자 가이드 - 정규표현식 첫걸음
정규표현식이 어렵게 느껴진다면, 가장 자주 사용하는 3가지만 먼저 익히세요:
\d = 숫자 한 글자 (0-9). \d+는 "숫자 1개 이상", \d{3}은 "숫자 정확히 3개"
\w = 글자 한 글자 (영문, 숫자, _). \w+는 "단어 1개 이상"
. = 아무 글자 1개 (줄바꿈 제외). .*는 "아무 글자 0개 이상"
실전 예시: 전화번호 찾기는 r"\d{3}-\d{4}-\d{4}", 이메일 찾기는 r"\w+@\w+\.\w+"로 시작할 수 있습니다.
팁:r"..."(raw string)을 반드시 사용하세요! r을 빼면 \n이 줄바꿈으로 해석됩니다.
중급자 가이드 - 정규표현식 실전 패턴
실무에서 빈번히 사용되는 정규표현식 패턴과 기법:
그룹 캡처: r"(\d{3})-(\d{4})-(\d{4})"에서 괄호 안 내용을 match.group(1), match.group(2)로 개별 추출합니다.
이름 붙은 그룹: r"(?P<area>\d{3})-(?P<mid>\d{4})"로 이름을 부여하면 match.group("area")로 접근할 수 있습니다.
비탐욕적(non-greedy) 매칭: .*는 최대한 많이 매칭(탐욕적)하지만, .*?는 최소한만 매칭합니다. HTML 태그 추출 시 중요합니다.
전방/후방 탐색: (?=패턴)(전방 긍정), (?<=패턴)(후방 긍정)으로 패턴 주변을 조건으로 사용하되 결과에 포함하지 않습니다.
re.compile(): 같은 패턴을 반복 사용할 때 pattern = re.compile(r"...")으로 미리 컴파일하면 약 30% 성능이 향상됩니다.
원자적 그룹(3rd party): regex 모듈(pip install regex)은 원자적 그룹 (?>...), 유니코드 프로퍼티 \p{Han}, 중첩 집합 등 표준 re보다 강력한 기능을 제공합니다.
정규표현식 대안: 단순 문자열 작업은 str.startswith(), str.endswith(), in 연산자가 정규표현식보다 10-100배 빠릅니다. 항상 정규표현식이 필요한지 먼저 검토하세요.
re.VERBOSE 플래그: re.VERBOSE(또는 re.X)를 사용하면 패턴에 줄바꿈과 주석을 넣을 수 있어 복잡한 패턴의 가독성이 크게 향상됩니다.
11. 테스트 (Testing)
중급고급
테스트는 코드가 의도한 대로 동작하는지 자동으로 검증하는 코드입니다. 단위 테스트(Unit Test)는 개별 함수/메서드를 독립적으로 검증하고, 통합 테스트는 여러 컴포넌트 간의 상호작용을 검증합니다. TDD(Test-Driven Development) 방법론에서는 테스트를 먼저 작성한 후 구현 코드를 작성합니다.
단위 테스트 (unittest)
unittest는 파이썬 표준 라이브러리에 포함된 테스트 프레임워크로, TestCase 클래스를 상속하여 테스트를 작성합니다. assertEqual, assertTrue, assertRaises 등의 단언(assert) 메서드를 제공하며, setUp/tearDown으로 테스트 전후 초기화/정리를 수행합니다.
pytest는 파이썬에서 가장 인기 있는 서드파티 테스트 프레임워크입니다. unittest보다 간결한 문법(단순 assert 사용), 강력한 fixture 시스템, 풍부한 플러그인 생태계를 제공합니다. 테스트 파일은 test_로 시작하는 이름을 사용합니다.
# pytest 설치
pip install pytest
# 테스트 파일 작성 (test_*.py)# test_calculator.pydeftest_addition():
assert 1 + 1 == 2deftest_subtraction():
assert 5 - 3 == 2# 실행
pytest test_calculator.py -v
테스트.raises()
pytest.raises()는 특정 예외가 발생하는지 검증하는 컨텍스트 매니저입니다. 예외가 발생하지 않거나 다른 타입의 예외가 발생하면 테스트가 실패합니다. 예외 처리 로직을 테스트할 때 필수적인 도구입니다.
import pytest
deftest_divide_by_zero():
with pytest.raises(ZeroDivisionError):
1 / 0deftest_value_error():
with pytest.raises(ValueError):
int("not a number")
Fixture와 Mock
Fixture는 테스트 실행 전에 필요한 데이터나 환경을 준비하는 함수입니다. @pytest.fixture로 정의하고 테스트 함수의 매개변수로 받습니다. Mock은 외부 의존성(DB, API 등)을 가짜 객체로 대체하여 테스트를 독립적으로 실행할 수 있게 합니다. unittest.mock의 @patch 데코레이터가 자주 사용됩니다.
import pytest
from unittest.mock import Mock, patch
@pytest.fixturedefsample_data():
return {"name": "테스트", "value": 100}
deftest_with_fixture(sample_data):
assert sample_data["value"] == 100@patch("module.function")
deftest_mock(mock_func):
mock_func.return_value = "mocked"
result = mock_func()
assert result == "mocked"
💡 팁: TDD(Test-Driven Development) 방식: 테스트를 먼저 작성하고, 그 다음에 실제 코드를 구현하세요.
초급자 가이드 - 왜 테스트를 작성해야 할까?
테스트 코드는 처음에 "추가 작업"처럼 느껴지지만, 장기적으로 시간을 절약해줍니다:
자동 검증: 코드를 수정할 때마다 수동으로 확인하지 않아도 됩니다. 테스트를 실행하면 모든 기능이 정상인지 자동으로 확인됩니다.
버그 방지: 한 곳을 고치면서 다른 곳이 깨지는 "회귀 버그"를 방지합니다.
문서 역할: 테스트 코드를 보면 함수가 어떻게 사용되는지, 어떤 결과를 기대하는지 알 수 있습니다.
시작 팁: pytest로 시작하세요. assert 1 + 1 == 2처럼 간단한 assert 문만으로 테스트를 작성할 수 있습니다. 파일 이름을 test_로 시작하면 pytest가 자동으로 찾아 실행합니다.
중급자 가이드 - 테스트 전략과 커버리지
효과적인 테스트를 위한 실무 전략:
테스트 피라미드: 단위 테스트(많이) → 통합 테스트(적당히) → E2E 테스트(적게) 비율로 작성합니다.
AAA 패턴: 테스트는 Arrange(준비) → Act(실행) → Assert(검증) 구조로 작성합니다.
pytest-cov: pytest --cov=mypackage로 테스트 커버리지를 측정합니다. 80% 이상이 일반적인 목표입니다.
Parametrize: @pytest.mark.parametrize("input,expected", [(1,1), (2,4)])로 여러 입력에 대해 같은 테스트를 반복합니다.
conftest.py: 여러 테스트 파일에서 공유하는 fixture를 conftest.py에 정의하면 자동으로 사용 가능합니다.
고급자 가이드 - 테스트 자동화와 고급 기법
대규모 프로젝트를 위한 고급 테스트 기법:
Property-based Testing: hypothesis 라이브러리로 무작위 입력을 자동 생성하여 엣지 케이스를 발견합니다. @given(st.integers())로 정수 범위의 모든 값에 대해 속성을 검증합니다.
Snapshot Testing: syrupy 또는 pytest-snapshot으로 복잡한 출력(JSON, HTML 등)의 스냅샷을 저장하고, 변경 사항을 자동 감지합니다.
Mutation Testing: mutmut으로 소스코드를 의도적으로 변형(mutation)하여 테스트가 이를 감지하는지 확인합니다. 테스트의 품질을 측정하는 방법입니다.
Test Doubles 구분: Stub(정해진 값 반환), Mock(호출 검증), Fake(간단한 구현), Spy(호출 기록)를 목적에 맞게 사용합니다.
12. 데이터 과학 도구
중급고급
파이썬은 데이터 과학 분야에서 가장 널리 사용되는 언어입니다. NumPy로 고성능 수치 연산을, Pandas로 데이터 조작을, Matplotlib/Seaborn으로 시각화를 수행합니다. 이 생태계는 R, MATLAB 등의 대안보다 범용성과 확장성이 뛰어나 산업 표준으로 자리잡았습니다.
AI 개발 환경 설정 & 설치
데이터 과학과 AI 개발을 위해 필요한 핵심 패키지들을 설치합니다. GPU를 활용한 딥러닝 학습에는 CUDA 호환 GPU와 드라이버가 필요하며, 가상 환경에서 패키지를 관리하는 것이 권장됩니다.
# 필수 패키지 설치
pip install numpy pandas matplotlib seaborn
# scikit-learn
pip install scikit-learn
# TensorFlow (GPU 자동 포함)
pip install tensorflow
# PyTorch (https://pytorch.org 에서 플랫폼별 명령어 확인)
pip install torch torchvision torchaudio
# Hugging Face Transformers
pip install transformers datasets
# 기타 유용한 패키지
pip install opencv-python pillow albumentations
NumPy(Numerical Python)는 파이썬 과학 계산의 핵심 라이브러리입니다.
C로 구현된 다차원 배열 객체 ndarray를 제공하며,
반복문 없이 벡터화 연산으로 수십~수백 배의 성능을 냅니다.
Pandas, scikit-learn, TensorFlow, PyTorch 등 거의 모든 AI/데이터 라이브러리의 기반입니다.
import numpy as np
# 리스트/튜플에서 생성
a1 = np.array([1, 2, 3]) # 1차원 (ndim=1)
a2 = np.array([[1, 2], [3, 4]]) # 2차원 (ndim=2)
a3 = np.array([[[1, 2], [3, 4]]]) # 3차원 (ndim=3)# 특수 배열 생성
np.zeros((3, 4)) # 0으로 채운 3×4 행렬
np.ones((2, 3)) # 1로 채운 2×3 행렬
np.full((3, 3), 7) # 7로 채운 3×3 행렬
np.eye(4) # 4×4 단위 행렬(항등행렬)
np.empty((2, 2)) # 초기화 없이 메모리 할당 (속도 빠름)# 수열 배열
np.arange(0, 10, 2) # [0 2 4 6 8] — range와 유사, 실수 step 가능
np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1.] — 균등 간격 n개
np.logspace(0, 3, 4) # [1. 10. 100. 1000.] — 로그 균등# 타입 지정
np.array([1, 2, 3], dtype=np.float32) # float32 (메모리 절반)
np.zeros(5, dtype=np.int64) # int64
np.array([True, False], dtype=np.bool_) # bool
배열 속성 (ndarray Attributes)
a = np.array([[1, 2, 3],
[4, 5, 6]])
a.ndim # 2 — 차원 수
a.shape # (2, 3) — (행, 열) 크기
a.size # 6 — 전체 요소 수
a.dtype # dtype('int64') — 요소 데이터 타입
a.itemsize # 8 — 요소 하나의 바이트 크기
a.nbytes # 48 — 전체 바이트 크기 (size × itemsize)
a.T # 전치 행렬 shape=(3, 2)
a.real # 실수부 (복소수 배열일 때)
a.imag # 허수부
인덱싱 & 슬라이싱 (Indexing & Slicing)
a = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
# 기본 인덱싱
a[0, 1] # 20 — (0행, 1열)
a[-1, -1] # 90 — 마지막 요소# 슬라이싱 [행 start:stop:step, 열 start:stop:step]
a[0:2, 1:3] # [[20,30],[50,60]]
a[:, 0] # [10 40 70] — 0번 열 전체
a[1, :] # [40 50 60] — 1번 행 전체
a[::2, ::2] # [[10,30],[70,90]] — 2칸 간격# 팬시 인덱싱 (Fancy Indexing) — 인덱스 목록으로 선택
idx = [0, 2]
a[idx] # [[10,20,30],[70,80,90]] — 0행, 2행
a[:, idx] # 0열, 2열 선택# 불리언 인덱싱 (Boolean Indexing)
mask = a > 50
a[mask] # [60 70 80 90] — 조건 만족 요소
a[a % 20 == 0] # 20의 배수만# 인덱스 위치 찾기
np.where(a > 50) # 조건 위치 (행 배열, 열 배열) 반환
np.argmax(a), np.argmin(a) # 최대/최소 위치 (flatten 기준)
np.argmax(a, axis=0) # 열별 최대값 위치
형태 변환 (Reshaping)
a = np.arange(12) # [0 1 2 ... 11]# reshape — 데이터 공유(뷰), 변경 시 원본도 변경
a.reshape(3, 4) # 3행 4열
a.reshape(2, -1) # -1은 자동 계산 → (2, 6)
a.reshape(-1, 1) # 열 벡터 (12, 1)
a.reshape(1, -1) # 행 벡터 (1, 12)# 차원 추가/제거
a[np.newaxis, :] # (1, 12) — 앞에 차원 추가
a[:, np.newaxis] # (12, 1) — 뒤에 차원 추가
np.expand_dims(a, axis=0) # (1, 12)
np.squeeze(a.reshape(1,12)) # 크기 1인 차원 제거 → (12,)# 평탄화
b = a.reshape(3, 4)
b.ravel() # 1차원 뷰 반환 (원본 공유)
b.flatten() # 1차원 복사본 반환 (독립)# 전치 (Transpose)
b.T # shape (3,4) → (4,3)
np.transpose(b, (1, 0)) # 축 순서 지정
브로드캐스팅 (Broadcasting)
브로드캐스팅 규칙: 두 배열의 shape를 오른쪽에서 비교해서,
① 같거나 ② 한쪽이 1이면 연산 가능합니다. 크기 1인 차원이 자동으로 확장됩니다.
# 스칼라 브로드캐스팅
a = np.array([1, 2, 3])
a + 10# [11 12 13]
a * 2# [2 4 6]# 2D ↔ 1D 브로드캐스팅
m = np.array([[1, 2, 3], # shape (2, 3)
[4, 5, 6]])
v = np.array([10, 20, 30]) # shape (3,) → 자동으로 (1,3)→(2,3)
m + v # [[11,22,33],[14,25,36]]# 열 벡터 ↔ 행 벡터
col = np.array([[1], [2], [3]]) # shape (3, 1)
row = np.array([10, 20]) # shape (2,) → (1, 2)
col + row # shape (3, 2) — 외적처럼 확장# [[11,21],[12,22],[13,23]]# 실용: 정규화 (각 열을 평균 0으로)
data = np.random.randn(100, 5)
data -= data.mean(axis=0) # 열별 평균 빼기
data /= data.std(axis=0) # 열별 표준편차 나누기
A = np.array([[3, 1],
[1, 2]])
b = np.array([9, 8])
# 행렬 곱
np.dot(A, A) # 행렬 곱셈 (2D 권장)
A @ A # @ 연산자 (Python 3.5+, 권장)
np.matmul(A, A) # matmul (배치 연산 지원)# np.linalg 함수
np.linalg.det(A) # 행렬식(determinant)
np.linalg.inv(A) # 역행렬
np.linalg.solve(A, b) # Ax=b 연립방정식 풀기 (inv 대신 권장)
np.linalg.norm(b) # 벡터 노름(크기) — L2 norm
np.linalg.norm(b, 1) # L1 norm
np.linalg.norm(A, "fro") # Frobenius norm# 고유값 분해 (Eigendecomposition)
eigenvalues, eigenvectors = np.linalg.eig(A)
# 특이값 분해 (SVD)
U, S, Vt = np.linalg.svd(A)
# QR 분해 / LU 분해
Q, R = np.linalg.qr(A)
# 랭크 / 대각 / 대각합
np.linalg.matrix_rank(A) # 행렬 랭크
np.diag(A) # 대각 요소 추출
np.diag([1, 2, 3]) # 대각 행렬 생성
np.trace(A) # 대각합 (trace)
난수 생성 (Random — np.random)
rng = np.random.default_rng(seed=42) # 권장: 새 Generator API# 연속 분포
rng.random((3, 4)) # [0, 1) 균등 분포
rng.uniform(-1, 1, 5) # [-1, 1) 균등 분포
rng.normal(0, 1, (3, 3)) # 정규 분포 (평균=0, 표준편차=1)
rng.exponential(2, 10) # 지수 분포
rng.beta(2, 5, 10) # 베타 분포
rng.poisson(3, 10) # 포아송 분포# 이산 분포
rng.integers(0, 10, 5) # [0, 10) 정수 (구 randint)
rng.choice([10, 20, 30], 5, replace=True) # 복원 추출
rng.choice([10, 20, 30], 2, replace=False) # 비복원 추출# 셔플 / 순열
arr = np.arange(10)
rng.shuffle(arr) # 제자리 셔플 (in-place)
rng.permutation(arr) # 셔플된 새 배열 반환# 레거시 API (구버전 호환)
np.random.seed(42) # 재현성 고정 (전역 상태 — 비권장)
np.random.rand(3, 3) # [0,1) 균등
np.random.randn(3, 3) # 표준 정규 분포
배열 결합 & 분할 (Concatenation & Splitting)
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 결합 (concatenate)
np.concatenate([a, b], axis=0) # 행 방향 → (4,2)
np.concatenate([a, b], axis=1) # 열 방향 → (2,4)
np.vstack([a, b]) # 행 쌓기 (axis=0), shape 달라도 열수 같으면 OK
np.hstack([a, b]) # 열 쌓기 (axis=1)
np.dstack([a, b]) # 깊이 쌓기 (axis=2)# 새 축으로 쌓기
np.stack([a, b], axis=0) # → (2,2,2): 새 축(0) 생성
np.stack([a, b], axis=2) # → (2,2,2): 새 축(2) 생성# 분할 (split)
arr = np.arange(12).reshape(4, 3)
np.split(arr, 2, axis=0) # 행 방향 2등분 → [shape(2,3), shape(2,3)]
np.vsplit(arr, 2) # vsplit = split(..., axis=0)
np.hsplit(arr, 3) # 열 방향 3등분 → [shape(4,1), ...]
np.array_split(arr, 3, axis=0) # 불균등 분할도 가능
정렬 & 탐색 (Sorting & Searching)
a = np.array([3, 1, 4, 1, 5, 9, 2, 6])
# 정렬
np.sort(a) # [1 1 2 3 4 5 6 9] — 새 배열
a.sort() # in-place 정렬
np.sort(a)[:-1] # 내림차순 슬라이싱# 간접 정렬 (인덱스 반환)
np.argsort(a) # 정렬 시 원래 인덱스 순서
a[np.argsort(a)] # 정렬된 배열 (argsort 활용)# 2D 정렬
m = np.array([[3,1],[4,2]])
np.sort(m, axis=0) # 열별 정렬
np.sort(m, axis=1) # 행별 정렬# 탐색
np.searchsorted([1,3,5], 4) # 삽입 위치 → 2 (이진 탐색)
np.nonzero(a) # 0이 아닌 요소 위치
np.where(a > 4, a, 0) # 조건 기반 대체# 집합 연산
np.unique(a) # 중복 제거 + 정렬
np.unique(a, return_counts=True) # 각 값의 빈도
np.intersect1d([1,2,3], [2,3,4]) # 교집합
np.union1d([1,2], [2,3]) # 합집합
np.setdiff1d([1,2,3], [2,3]) # 차집합
np.isin(a, [1,3,5]) # 멤버십 검사 (bool 배열)
파일 I/O
arr = np.arange(12).reshape(3, 4)
# 이진 형식 (.npy / .npz) — 빠르고 정확, dtype 보존
np.save("array.npy", arr) # 단일 배열 저장
loaded = np.load("array.npy") # 불러오기
np.savez("data.npz", x=arr, y=arr*2) # 여러 배열
f = np.load("data.npz")
f["x"], f["y"] # 키로 접근
np.savez_compressed("data.npz", arr=arr) # 압축 저장# 텍스트 형식 (.txt / .csv) — 사람이 읽을 수 있음
np.savetxt("data.csv", arr, delimiter=",", fmt="%.4f")
loaded_txt = np.loadtxt("data.csv", delimiter=",")
# genfromtxt — 결측치/헤더 처리 가능
data = np.genfromtxt("data.csv", delimiter=",",
skip_header=1, filling_values=0)
벡터화 & 성능 최적화
# np.vectorize — 스칼라 함수를 배열 함수로 래핑defmy_func(x):
return x ** 2 + 1if x > 0else0
vfunc = np.vectorize(my_func)
vfunc(np.array([-1, 0, 2, 3])) # [0, 0, 5, 10]# np.where — 조건 분기 (벡터화된 if-else)
a = np.array([-3, -1, 0, 2, 4])
np.where(a > 0, a, 0) # ReLU: [0 0 0 2 4]
np.where(a > 0, "pos", "non-pos") # 문자열도 가능# np.select — 다중 조건
conditions = [a < 0, a == 0, a > 0]
choices = ["neg", "zero", "pos"]
np.select(conditions, choices, default="?")
# 메모리 레이아웃 — C order(행 우선) vs F order(열 우선)
c_arr = np.ascontiguousarray(arr) # C-contiguous (행 연속)
f_arr = np.asfortranarray(arr) # F-contiguous (열 연속)
arr.flags # 메모리 레이아웃 정보# 뷰 vs 복사 — 메모리 공유 여부 확인
view = arr[0:2] # 뷰 (메모리 공유)
copy = arr[0:2].copy() # 독립 복사본
view.base is arr # True (뷰 확인)
NumPy vs 순수 Python 성능 비교
작업
순수 Python
NumPy
속도 차이
1백만 요소 합산
sum(list)
np.sum(arr)
~10–50배 빠름
요소별 곱셈
list comprehension
arr * arr
~20–100배 빠름
행렬 곱
이중 for 루프
A @ B
~100배+ 빠름
메모리 사용
int 객체 ~28B
int64 8B
~3배 적음
조건 필터
list comprehension
arr[mask]
~10배 빠름
주의: 반복문 안에서 NumPy 배열을 요소 하나씩 접근하면 오히려 느립니다.
NumPy는 벡터화 연산으로 사용할 때 최고 성능을 발휘합니다.
루프 대신 브로드캐스팅, np.where, np.vectorize, ufunc를 활용하세요.
Pandas (데이터 처리)
Pandas는 표 형태의 데이터를 다루는 파이썬의 핵심 라이브러리입니다.
Series(1차원)와 DataFrame(2차원) 두 가지 자료구조를 중심으로,
데이터 로딩·정제·변환·집계·시각화까지 데이터 분석의 전 과정을 지원합니다.
df.shape # (행수, 열수)
df.dtypes # 각 열의 타입
df.index # 행 인덱스
df.columns # 열 이름 목록
df.values # numpy 배열
df.size # 전체 요소 수
df.memory_usage(deep=True) # 열별 메모리 사용량# 데이터 미리보기
df.head(5) # 상위 5행 (기본값)
df.tail(3) # 하위 3행
df.sample(5) # 무작위 5행
df.sample(frac=0.1) # 10% 무작위# 요약 정보
df.info() # 타입·결측치·메모리
df.describe() # 수치형 통계 요약 (count/mean/std/min/Q/max)
df.describe(include="all") # 범주형 포함
df.describe(percentiles=[.1, .5, .9]) # 퍼센타일 지정# 열 탐색
df["age"].value_counts() # 값별 빈도
df["age"].value_counts(normalize=True) # 비율
df["age"].nunique() # 고유값 개수
df["age"].unique() # 고유값 배열
인덱싱 & 선택 (loc / iloc / query)
방법
기준
예시
특징
df["col"]
열 이름
df["age"]
Series 반환
df[["c1","c2"]]
열 이름 목록
df[["name","age"]]
DataFrame 반환
.loc[행, 열]
레이블 기반
df.loc[0, "age"]
슬라이스 끝 포함
.iloc[행, 열]
위치(정수) 기반
df.iloc[0:3, 1:3]
슬라이스 끝 미포함
.at[행, 열]
단일 레이블
df.at[0, "age"]
스칼라, 빠름
.iat[행, 열]
단일 위치
df.iat[0, 1]
스칼라, 가장 빠름
.query()
문자열 표현식
df.query("age > 25")
가독성 좋음
# loc — 레이블 기반 (끝 인덱스 포함)
df.loc[0] # 인덱스 0인 행
df.loc[0:3] # 인덱스 0~3행 (3 포함)
df.loc[0, "age"] # 스칼라
df.loc[0:3, ["name", "score"]] # 슬라이스 + 여러 열
df.loc[df["age"] > 25] # 불리언 마스크
df.loc[df["age"] > 25, "name"] # 조건 행 + 특정 열# iloc — 위치 기반 (끝 인덱스 미포함)
df.iloc[0] # 첫 번째 행
df.iloc[0:3] # 0, 1, 2행
df.iloc[0:3, 1:3] # 0~2행, 1~2열
df.iloc[[0, 2, 4], [1, 3]] # 팬시 인덱싱
df.iloc[:, :-1] # 마지막 열 제외 전체# query — SQL 스타일 (변수는 @var)
threshold = 80
df.query("age > 25 and score >= @threshold")
df.query("name in ['Alice', 'Bob']")
df.query("score.between(80, 95)")
# 불리언 마스크 조합
mask = (df["age"] > 25) & (df["score"] >= 80)
df[mask]
df[~mask] # 반전
데이터 타입 변환
# astype — 기본 타입 변환
df["age"] = df["age"].astype("int32") # 메모리 절반
df["score"] = df["score"].astype("float32")
df["flag"] = df["flag"].astype(bool)
# 수치 변환 — 오류 처리
pd.to_numeric(df["col"], errors="coerce") # 변환 실패 → NaN
pd.to_numeric(df["col"], errors="ignore") # 실패 시 원본 유지
pd.to_numeric(df["col"], downcast="integer") # 가능한 작은 int로# 날짜 변환
df["date"] = pd.to_datetime(df["date"])
df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d")
df["date"] = pd.to_datetime(df["date"], errors="coerce")
# 카테고리 타입 — 반복 문자열에 효율적
df["grade"] = df["grade"].astype("category")
df["grade"].cat.categories # 카테고리 목록
df["grade"].cat.codes # 정수 코드
pd.Categorical(df["grade"],
categories=["C","B","A"], ordered=True) # 순서 있는 카테고리# 일괄 타입 최적화
df = df.convert_dtypes() # Pandas 2.0+: 최적 타입 자동 추론
결측치 처리 (Missing Data)
# 결측치 확인
df.isnull() # 요소별 bool
df.isnull().sum() # 열별 결측치 수
df.isnull().sum() / len(df) # 결측 비율
df.notnull() # 반대# 결측치 제거
df.dropna() # 결측 있는 행 전체 제거
df.dropna(axis=1) # 결측 있는 열 제거
df.dropna(subset=["name", "age"]) # 특정 열 기준
df.dropna(thresh=3) # 유효값이 3개 미만인 행 제거
df.dropna(how="all") # 모두 NaN인 행만 제거# 결측치 채우기
df.fillna(0) # 모든 NaN → 0
df["score"].fillna(df["score"].mean()) # 평균으로
df["score"].fillna(df["score"].median()) # 중앙값으로
df["grade"].fillna(df["grade"].mode()[0]) # 최빈값으로
df.fillna({"age": 0, "name": "unknown"}) # 열별 다른 값
df["col"].ffill() # 앞 값으로 전파 (forward fill)
df["col"].bfill() # 뒤 값으로 전파 (backward fill)# 보간 (Interpolation)
df["price"].interpolate(method="linear") # 선형 보간
df["price"].interpolate(method="spline", order=2) # 스플라인
df["price"].interpolate(limit=2) # 최대 2개만
데이터 정제 & 변환
# 열/행 이름 변경
df.rename(columns={"name": "Name", "age": "Age"})
df.rename(index={0: "first"})
df.columns = df.columns.str.lower().str.replace(" ", "_") # 일괄 소문자+언더스코어# 열/행 추가·삭제
df["bonus"] = df["score"] * 0.1# 새 열 추가
df.insert(2, "rank", df["score"].rank()) # 위치 지정 삽입
df.drop(columns=["bonus"]) # 열 삭제
df.drop(index=[0, 2]) # 행 삭제
df.pop("bonus") # 열 제거 후 반환# 중복 처리
df.duplicated() # 중복 행 여부 bool
df.duplicated(subset=["name"]) # 특정 열 기준
df.drop_duplicates() # 중복 제거
df.drop_duplicates(subset=["name"], keep="last") # 마지막 남기기# 값 치환
df["grade"].replace("A", "Excellent")
df["grade"].replace({"A": 4, "B": 3, "C": 2})
df.replace(np.nan, 0) # 전체 NaN → 0# apply — 행/열 단위 함수 적용
df["score_scaled"] = df["score"].apply(lambda x: (x - 50) / 50)
df.apply(np.sum, axis=0) # 열별 합
df.apply(np.sum, axis=1) # 행별 합
df[["age","score"]].apply(lambda row: row["age"] * row["score"], axis=1)
# map — Series 요소별 매핑
df["grade"].map({"A": 4, "B": 3, "C": 2})
# 구간 분류
pd.cut(df["score"], bins=[0,60,80,100], labels=["F","B","A"])
pd.qcut(df["score"], q=4) # 4분위로 자동 분류# 인덱스 재설정
df.reset_index(drop=True) # 0,1,2... 재번호
df.set_index("name") # 열 → 인덱스
df.set_index(["year", "month"]) # 멀티 인덱스
정렬 & 순위
# 값 기준 정렬
df.sort_values("age") # 오름차순
df.sort_values("age", ascending=False) # 내림차순
df.sort_values(["grade", "score"],
ascending=[True, False]) # 복합 정렬
df.sort_values("score", na_position="first") # NaN 앞에 배치# 인덱스 기준 정렬
df.sort_index()
df.sort_index(ascending=False)
# 순위 (rank)
df["rank"] = df["score"].rank(ascending=False) # 내림차순 순위
df["score"].rank(method="dense") # 동점 시 건너뜀 없음
df["score"].rank(method="min") # 동점 시 최소 순위
df["score"].rank(pct=True) # 백분위 순위 (0~1)# 상위/하위 N개
df.nlargest(5, "score") # 상위 5개
df.nsmallest(3, "age") # 하위 3개
그룹화 & 집계 (GroupBy)
# 기본 groupby
grp = df.groupby("grade")
grp["score"].mean() # 학점별 평균
grp["score"].agg(["mean", "std", "count"]) # 여러 집계# 다중 키 그룹화
df.groupby(["dept", "grade"])["score"].mean()
# agg — 열별로 다른 집계 함수
df.groupby("grade").agg(
avg_score=("score", "mean"),
max_age =("age", "max"),
count =("name", "count")
)
# transform — 원래 shape 유지 (그룹 통계 → 각 행에 브로드캐스트)
df["grade_avg"] = df.groupby("grade")["score"].transform("mean")
df["score_z"] = df.groupby("grade")["score"].transform(
lambda x: (x - x.mean()) / x.std() # 그룹 내 z-score
)
# filter — 조건 만족 그룹만 남기기
df.groupby("grade").filter(lambda g: g["score"].mean() >= 80)
# pivot_table — 엑셀 피벗과 동일
pd.pivot_table(df,
values="score",
index="dept",
columns="grade",
aggfunc="mean",
fill_value=0)
# crosstab — 빈도 교차표
pd.crosstab(df["dept"], df["grade"], margins=True)
pd.crosstab(df["dept"], df["grade"],
values=df["score"], aggfunc="mean")
데이터 결합 (Merge / Join / Concat)
# concat — 같은 구조의 DataFrame 이어 붙이기
pd.concat([df1, df2]) # 행 방향 (axis=0, 기본)
pd.concat([df1, df2], ignore_index=True) # 인덱스 재번호
pd.concat([df1, df2], axis=1) # 열 방향
pd.concat([df1, df2], join="inner") # 공통 열만# merge — SQL JOIN과 동일 개념
pd.merge(df_left, df_right, on="id") # INNER JOIN
pd.merge(df_left, df_right, on="id", how="left") # LEFT JOIN
pd.merge(df_left, df_right, on="id", how="right") # RIGHT JOIN
pd.merge(df_left, df_right, on="id", how="outer") # FULL OUTER JOIN
pd.merge(df_left, df_right,
left_on="user_id", right_on="id") # 다른 열 이름
pd.merge(df_left, df_right, on=["year", "month"]) # 복합 키
pd.merge(df_left, df_right, on="id",
suffixes=("_left", "_right")) # 중복 열명 접미사# join — 인덱스 기준 병합 (merge의 편의 래퍼)
df1.join(df2, how="left")
df1.join(df2, lsuffix="_l", rsuffix="_r")
시계열 (Time Series)
# DatetimeIndex 생성
dates = pd.date_range("2024-01-01", periods=12, freq="ME") # 월말
dates = pd.date_range("2024-01-01", "2024-12-31", freq="D") # 일별
dates = pd.bdate_range("2024-01-01", periods=10) # 영업일# dt 접근자 — datetime 열 속성
df["date"] = pd.to_datetime(df["date"])
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["weekday"] = df["date"].dt.day_name()
df["quarter"] = df["date"].dt.quarter
df["is_month_end"] = df["date"].dt.is_month_end
# resample — 시간 단위 리샘플링
ts = df.set_index("date")["price"]
ts.resample("ME").mean() # 월별 평균 (다운샘플링)
ts.resample("D").ffill() # 일별로 확장 (업샘플링, 앞 값으로)
ts.resample("QE").agg({"price": ["mean", "max"]}) # 분기별 집계# rolling / expanding — 이동 통계
ts.rolling(window=7).mean() # 7일 이동 평균
ts.rolling(window=7, min_periods=1).std() # 최소 1개부터 계산
ts.expanding().mean() # 누적 평균
ts.ewm(span=7).mean() # 지수 가중 이동 평균# shift / diff — 시차 & 변화량
ts.shift(1) # 1기간 뒤로 (lag)
ts.shift(-1) # 1기간 앞으로 (lead)
ts.diff(1) # 1기간 차분
ts.pct_change() # 전기 대비 변화율
문자열 처리 (str 접근자)
s = df["name"] # 문자열 Series# 검색 & 판별
s.str.contains("li", case=False) # 대소문자 무시 포함 여부
s.str.startswith("A") # 시작 여부
s.str.endswith("e") # 끝 여부
s.str.match(r"^[A-Z]") # 정규식 매치
s.str.fullmatch(r"[A-Za-z]+") # 전체 일치# 변환
s.str.upper() # 대문자
s.str.lower() # 소문자
s.str.strip() # 양쪽 공백 제거
s.str.replace(" ", "_", regex=False) # 치환
s.str.replace(r"\s+", "_", regex=True) # 정규식 치환# 분리 & 추출
s.str.split("_") # 분리 → 리스트
s.str.split("_", expand=True) # DataFrame으로 확장
s.str.split("_").str[0] # 첫 번째 요소
s.str.extract(r"(\d+)") # 정규식 캡처 그룹# 길이 & 카운트
s.str.len() # 문자열 길이
s.str.count("a") # 특정 문자 개수
s.str.zfill(5) # 앞에 0 채우기
파일 I/O
# CSV
df = pd.read_csv("data.csv",
encoding="utf-8",
index_col="id",
parse_dates=["date"],
dtype={"age": "int32"},
na_values=["-", "N/A", "?"],
chunksize=10_000) # 대용량: 청크 단위 읽기
df.to_csv("out.csv", index=False, encoding="utf-8-sig") # BOM for Excel# Excel (openpyxl 필요)
df = pd.read_excel("data.xlsx", sheet_name="Sheet1", header=1)
df.to_excel("out.xlsx", index=False, sheet_name="Result")
# 여러 시트를 한 파일에 쓰기with pd.ExcelWriter("report.xlsx", engine="openpyxl") as writer:
df1.to_excel(writer, sheet_name="Summary", index=False)
df2.to_excel(writer, sheet_name="Detail", index=False)
# JSON
df = pd.read_json("data.json", orient="records")
df.to_json("out.json", orient="records", force_ascii=False)
# Parquet — 컬럼형 이진, 대용량에 권장 (pyarrow 필요)
df = pd.read_parquet("data.parquet")
df.to_parquet("out.parquet", compression="snappy")
# SQL (SQLAlchemy)from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydb.db")
df = pd.read_sql("SELECT * FROM users WHERE age > 20", engine)
df.to_sql("result", engine, if_exists="replace", index=False)
# 대용량 CSV 청크 처리
chunks = pd.read_csv("big.csv", chunksize=50_000)
result = pd.concat(
chunk[chunk["age"] > 25] for chunk in chunks
)
성능 최적화
# 1. dtype 다운캐스팅 — 메모리 대폭 절감
df["age"] = pd.to_numeric(df["age"], downcast="integer") # int64→int8
df["score"] = pd.to_numeric(df["score"], downcast="float") # float64→float32
df["grade"] = df["grade"].astype("category") # 반복 문자열# 2. iterrows 대신 벡터화# 느림 (절대 금지):for idx, row in df.iterrows():
df.at[idx, "bonus"] = row["score"] * 0.1# 빠름:
df["bonus"] = df["score"] * 0.1# 3. inplace=True — Pandas 2.0+ 에서 더 이상 성능 이점 없음, 지양 권장# 구버전 관용:
df.dropna(inplace=True)
# 권장 (재할당):
df = df.dropna()
# 4. copy vs view — SettingWithCopyWarning 방지
subset = df[df["age"] > 25].copy() # 명시적 복사
subset["bonus"] = 100# 안전하게 수정 가능# 5. pipe — 메서드 체인 가독성
result = (
df
.dropna(subset=["score"])
.query("age >= 20")
.assign(bonus=lambda x: x["score"] * 0.1)
.groupby("grade")
.agg(avg=("score", "mean"))
.reset_index()
)
데이터 전처리는 머신러닝 파이프라인에서 가장 중요한 단계로, 모델 성능의 80% 이상을 좌우합니다. 표준화(Z-score)는 평균 0, 표준편차 1로 변환하고, 정규화(Min-Max)는 0~1 범위로 스케일링합니다. 원핫 인코딩은 범주형 변수를 수치로 변환하며, 데이터 증강(Data Augmentation)은 학습 데이터를 인위적으로 확장하여 과적합을 방지합니다.
메모리 최적화: df.astype({"col": "int32"})로 타입을 다운캐스트하면 메모리를 50-80% 절약합니다. category 타입은 반복 문자열에 효과적입니다.
고급자 가이드 - 대규모 데이터 처리
Pandas의 한계를 넘어서는 대규모 데이터 처리 기법:
Polars: Rust로 작성된 차세대 DataFrame 라이브러리로, Pandas보다 5-50배 빠릅니다. Lazy evaluation과 멀티코어 병렬 처리가 기본 내장되어 있습니다.
Dask: Pandas와 동일한 API로 코어 수를 넘는 대용량 데이터를 분산 처리합니다. dask.dataframe은 메모리를 초과하는 데이터도 처리 가능합니다.
NumPy 벡터화: for 루프 대신 배열 연산을 사용하세요. np.where(arr > 0, arr, 0)처럼 조건부 연산도 벡터화합니다. ufunc을 직접 만들려면 np.vectorize() 또는 Numba @vectorize를 사용합니다.
GPU 가속: CuPy(NumPy 호환)와 cuDF(Pandas 호환)로 NVIDIA GPU에서 연산을 수행하면 100배 이상의 속도 향상이 가능합니다.
Apache Arrow: 언어 간 데이터 교환의 표준 메모리 포맷입니다. Pandas 2.0+에서 Arrow 백엔드를 사용하면 문자열 연산이 크게 빨라집니다.
13. 머신러닝
중급고급
머신러닝(Machine Learning)은 데이터로부터 패턴을 학습하여 예측이나 의사결정을 자동화하는 AI의 핵심 분야입니다. 지도 학습(분류, 회귀), 비지도 학습(군집화, 차원 축소), 강화 학습 세 가지 범주로 나뉘며, 파이썬에서는 scikit-learn이 전통적 ML의 표준 라이브러리입니다.
scikit-learn (머신러닝)
scikit-learn은 파이썬의 대표적인 머신러닝 라이브러리로, 분류·회귀·클러스터링·차원 축소·전처리·모델 선택 등
전통적 ML 알고리즘을 일관된 Estimator API로 제공합니다.
fit / transform / predict 세 메서드 패턴만 익히면 모든 모델을 같은 방식으로 사용할 수 있습니다.
설치:pip install scikit-learn |
버전 확인: sklearn.__version__
Estimator API 핵심 패턴
# 모든 scikit-learn 객체는 동일한 인터페이스# fit(X, y) — 학습 (파라미터 추정)# transform(X) — 변환 (전처리기)# fit_transform(X) — 학습 + 변환 (훈련 데이터에만)# predict(X) — 예측 (지도학습 모델)# predict_proba(X) — 클래스 확률 반환# score(X, y) — 기본 평가 지표 (분류: accuracy, 회귀: R²)# 중요 규칙: fit은 반드시 훈련 데이터로만, transform은 전체에 적용from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_sc = scaler.fit_transform(X_train) # 훈련: 평균·표준편차 학습 + 변환
X_test_sc = scaler.transform(X_test) # 테스트: 학습된 값으로만 변환# ❌ X_test_sc = scaler.fit_transform(X_test) # 데이터 누수(leakage)!
내장 데이터셋 & 데이터 분할
from sklearn.datasets import (
load_iris, # 붓꽃 분류 (150×4, 3클래스)
load_digits, # 손글씨 숫자 (1797×64, 10클래스)
load_breast_cancer, # 유방암 (569×30, 이진분류)
load_boston, # 보스턴 집값 — deprecated, 아래 대체
fetch_california_housing, # 캘리포니아 주택가 (회귀)
make_classification, # 합성 분류 데이터
make_regression, # 합성 회귀 데이터
make_blobs, # 클러스터링용 합성 데이터
make_moons, make_circles, # 비선형 경계 데이터
)
# 데이터 로드
iris = load_iris()
X, y = iris.data, iris.target # numpy 배열
iris.feature_names # ['sepal length (cm)', ...]
iris.target_names # ['setosa', 'versicolor', 'virginica']# 합성 데이터 생성
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10, # 실제 유효 특성 수
n_redundant=5,
n_classes=3,
random_state=42
)
# 데이터 분할from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2, # 테스트 비율 (0~1)
random_state=42, # 재현성
stratify=y # 클래스 비율 유지 (분류에서 권장)
)
# 3-way 분할 (훈련 / 검증 / 테스트)
X_tr, X_tmp, y_tr, y_tmp = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_tmp, y_tmp, test_size=0.5, stratify=y_tmp, random_state=42)
from sklearn.linear_model import (
LinearRegression,
Ridge, Lasso, ElasticNet, # 정규화 선형 모델
HuberRegressor, # 이상치에 강건
BayesianRidge,
)
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import (
RandomForestRegressor,
GradientBoostingRegressor,
HistGradientBoostingRegressor, # 대용량·결측치 허용
AdaBoostRegressor,
)
from sklearn.svm import SVR
from sklearn.neighbors import KNeighborsRegressor
# 선형 회귀
lr = LinearRegression(fit_intercept=True)
lr.fit(X_train, y_train)
lr.coef_ # 계수
lr.intercept_ # 절편# Ridge (L2 정규화) — 다중공선성, 과적합 완화
ridge = Ridge(alpha=1.0) # alpha ↑ → 정규화 강도 ↑# Lasso (L1 정규화) — 특성 선택 효과 (계수 → 0)
lasso = Lasso(alpha=0.1, max_iter=5000)
# ElasticNet (L1+L2) — Lasso와 Ridge 혼합
en = ElasticNet(alpha=0.1, l1_ratio=0.5)
# GradientBoosting — 순차 부스팅, 높은 정확도
gbr = GradientBoostingRegressor(
n_estimators=200,
learning_rate=0.05,
max_depth=4,
subsample=0.8,
random_state=42
)
gbr.fit(X_train, y_train)
지도학습 — 분류 (Classification)
from sklearn.linear_model import LogisticRegression, SGDClassifier
from sklearn.svm import SVC, LinearSVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import (
RandomForestClassifier,
GradientBoostingClassifier,
HistGradientBoostingClassifier,
AdaBoostClassifier,
ExtraTreesClassifier,
)
from sklearn.naive_bayes import GaussianNB, MultinomialNB
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# 로지스틱 회귀
lr = LogisticRegression(C=1.0, # C=1/alpha (C↑ → 정규화 약해짐)
penalty="l2", # "l1","l2","elasticnet"
solver="lbfgs", # 다중분류: "lbfgs","saga"
multi_class="auto",
max_iter=1000)
# SVM (Support Vector Machine)
svc = SVC(C=1.0,
kernel="rbf", # "linear","poly","rbf","sigmoid"
gamma="scale", # "scale"=1/(n_features*X.var())
probability=True) # predict_proba 활성화 (속도 저하)# K-최근접 이웃
knn = KNeighborsClassifier(n_neighbors=5,
weights="distance", # "uniform" or "distance"
metric="minkowski") # "euclidean","manhattan"# 랜덤 포레스트
rf = RandomForestClassifier(
n_estimators=200, # 트리 수 (많을수록 안정, 느려짐)
max_depth=None, # None이면 완전 성장
max_features="sqrt", # 분기 시 고려 특성 수
min_samples_leaf=1,
class_weight="balanced", # 클래스 불균형 처리
n_jobs=-1, # 모든 CPU 코어 사용
random_state=42
)
rf.fit(X_train, y_train)
rf.feature_importances_ # 특성 중요도# Gradient Boosting
hgb = HistGradientBoostingClassifier(
max_iter=200,
learning_rate=0.05,
max_leaf_nodes=31,
min_samples_leaf=20
)
# HistGradientBoosting은 결측치(NaN)를 자체 처리
비지도학습 — 클러스터링
from sklearn.cluster import (
KMeans, MiniBatchKMeans,
DBSCAN, HDBSCAN,
AgglomerativeClustering,
SpectralClustering,
)
# K-Means
km = KMeans(n_clusters=3,
init="k-means++", # 초기 중심 선택 (수렴 빠름)
n_init=10, # 시작 횟수 (최선 결과 반환)
max_iter=300,
random_state=42)
km.fit(X)
km.labels_ # 각 샘플의 클러스터 번호
km.cluster_centers_ # 클러스터 중심 좌표
km.inertia_ # Within-cluster sum of squares (낮을수록 좋음)# 최적 K 찾기 — 엘보우 방법
inertias = []
for k in range(1, 11):
inertias.append(KMeans(n_clusters=k, random_state=42, n_init=10)
.fit(X).inertia_)
# DBSCAN — 밀도 기반, 이상치 자동 감지
db = DBSCAN(eps=0.5, # 이웃 반경
min_samples=5, # 핵심 포인트 최소 이웃 수
metric="euclidean")
db.fit(X)
db.labels_ # -1 = 이상치(noise)# 계층적 클러스터링
agg = AgglomerativeClustering(n_clusters=3,
linkage="ward") # "average","complete","single"
agg.fit(X)
비지도학습 — 차원 축소
from sklearn.decomposition import PCA, TruncatedSVD, NMF
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
# PCA (Principal Component Analysis)
pca = PCA(n_components=2) # 고정 차원 수
pca = PCA(n_components=0.95) # 분산 95% 보존에 필요한 차원 수
pca = PCA(n_components="mle") # MLE로 자동 결정
X_pca = pca.fit_transform(StandardScaler().fit_transform(X))
pca.explained_variance_ratio_ # 각 PC의 설명 분산 비율
pca.components_ # 주성분 벡터 (로딩)print(f"보존 분산: {pca.explained_variance_ratio_.sum():.3f}")
# TruncatedSVD — 희소 행렬에 적합 (PCA 대안)
svd = TruncatedSVD(n_components=50)
X_svd = svd.fit_transform(X_sparse)
# t-SNE — 시각화 전용 (2D/3D), 비볼록 최적화
tsne = TSNE(n_components=2,
perplexity=30, # 이웃 수 목표 (5~50)
n_iter=1000,
learning_rate="auto",
init="pca", # PCA 초기화 (안정적)
random_state=42)
X_tsne = tsne.fit_transform(X) # fit_transform만 제공 (transform 없음)
from sklearn.feature_selection import (
SelectKBest, SelectPercentile,
f_classif, chi2, mutual_info_classif, # 분류 점수 함수
f_regression, mutual_info_regression, # 회귀 점수 함수
RFE, RFECV, # 재귀적 특성 제거
SelectFromModel, # 모델 기반 선택
)
# 통계 기반 — 상위 K개 특성 선택
skb = SelectKBest(score_func=f_classif, k=10)
X_new = skb.fit_transform(X_train, y_train)
skb.get_support() # 선택된 특성 bool 마스크
skb.scores_ # 각 특성의 점수
skb.get_feature_names_out() # 선택된 특성 이름 (sklearn 1.0+)# RFE — 재귀적 특성 제거
rfe = RFE(estimator=LogisticRegression(), n_features_to_select=10, step=1)
rfe.fit(X_train, y_train)
rfe.ranking_ # 특성 순위 (1 = 선택됨)
rfe.support_ # 선택된 특성 bool# RFECV — 교차 검증으로 최적 특성 수 자동 결정
rfecv = RFECV(LogisticRegression(), cv=5, scoring="accuracy", n_jobs=-1)
rfecv.fit(X_train, y_train)
rfecv.n_features_ # 선택된 최적 특성 수# 모델 기반 — 트리/선형모델 feature_importances_ 활용
sfm = SelectFromModel(RandomForestClassifier(n_estimators=100, random_state=42),
threshold="median") # 중앙값 이상 중요도만 선택
sfm.fit(X_train, y_train)
X_selected = sfm.transform(X_train)
앙상블 (Ensemble)
from sklearn.ensemble import (
VotingClassifier, VotingRegressor,
StackingClassifier, StackingRegressor,
BaggingClassifier,
)
# VotingClassifier — 여러 모델의 다수결 / 평균
voting = VotingClassifier(estimators=[
("lr", LogisticRegression(max_iter=1000)),
("svc", SVC(probability=True)),
("rf", RandomForestClassifier(n_estimators=100, random_state=42)),
],
voting="soft", # "hard"=다수결, "soft"=확률 평균 (일반적으로 성능 우수)
n_jobs=-1)
voting.fit(X_train, y_train)
# StackingClassifier — 메타 학습기 (2단계)
estimators = [
("lr", LogisticRegression(max_iter=1000)),
("rf", RandomForestClassifier(n_estimators=100, random_state=42)),
("svc", SVC(probability=True)),
]
stacking = StackingClassifier(
estimators=estimators,
final_estimator=LogisticRegression(), # 메타 학습기
cv=5,
passthrough=False# True이면 원본 특성도 메타 학습기에 전달
)
# BaggingClassifier — 부트스트랩 앙상블
bag = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=100,
max_samples=0.8, # 훈련 샘플 비율
max_features=0.8, # 특성 비율
bootstrap=True,
n_jobs=-1,
random_state=42
)
모델 저장 & 불러오기
import joblib
import pickle
# joblib — sklearn 모델에 권장 (numpy 배열 효율적)
joblib.dump(model, "model.joblib") # 저장
loaded_model = joblib.load("model.joblib") # 불러오기
loaded_model.predict(X_test)
# 압축 저장
joblib.dump(model, "model.joblib.gz", compress=3)
# pickle — 표준 라이브러리withopen("model.pkl", "wb") as f:
pickle.dump(model, f)
withopen("model.pkl", "rb") as f:
loaded = pickle.load(f)
# 전처리기 + 모델 파이프라인 통째로 저장
joblib.dump(full_pipe, "pipeline.joblib")
# 모델 메타 정보 확인print(loaded_model.get_params()) # 파라미터 딕셔너리
주요 알고리즘 선택 가이드
알고리즘
유형
장점
단점 / 주의
LinearRegression / Logistic
선형
빠름, 해석 쉬움
비선형 관계 취약
Ridge / Lasso / ElasticNet
정규화 선형
과적합 완화, 특성 선택(Lasso)
alpha 튜닝 필요
DecisionTree
트리
해석 쉬움, 전처리 불필요
과적합 심함
RandomForest
배깅 앙상블
안정적, 중요도 제공, 병렬화
메모리 사용량, 느린 예측
GradientBoosting / HistGB
부스팅
높은 정확도, 결측치 허용(Hist)
튜닝 파라미터 많음
SVM / SVC
커널
고차원 강점, 마진 최적화
대용량 느림, 확률 느림
KNN
거리 기반
구현 간단, 파라미터 적음
예측 느림, 고차원 취약
K-Means
클러스터링
빠름, 확장성
K 사전 지정, 구형 군집만
DBSCAN
밀도 클러스터링
이상치 감지, K 불필요
고차원·대용량 어려움
PCA
차원 축소
빠름, 노이즈 제거
비선형 관계 포착 불가
모델 평가 및 검증
모델의 성능을 객관적으로 측정하는 것은 ML 파이프라인의 필수 과정입니다. 정확도(Accuracy)는 전체 정답률, 정밀도(Precision)는 양성 예측의 정확성, 재현율(Recall)은 실제 양성의 검출률을 나타냅니다. F1 Score는 정밀도와 재현율의 조화 평균이며, 교차 검증(Cross Validation)은 과적합을 방지하기 위해 데이터를 여러 폴드로 나누어 반복 평가합니다.
파이프라인: Pipeline([("scaler", StandardScaler()), ("model", SVC())])로 전처리와 모델을 하나로 묶으면 데이터 누출(data leakage)을 방지합니다.
특성 중요도: 트리 기반 모델의 feature_importances_나 SHAP 값으로 어떤 특성이 예측에 영향을 주는지 분석합니다.
고급자 가이드 - MLOps와 프로덕션 배포
머신러닝 모델을 프로덕션에 배포하고 운영하기 위한 고급 전략:
실험 추적: MLflow, Weights & Biases(W&B)로 하이퍼파라미터, 메트릭, 모델을 체계적으로 기록하고 비교합니다.
모델 서빙: FastAPI + uvicorn으로 REST API를 구축하거나, TorchServe, TensorFlow Serving으로 모델을 배포합니다. ONNX Runtime으로 프레임워크 독립적인 추론도 가능합니다.
피처 스토어: Feast 등의 피처 스토어로 특성 엔지니어링을 재사용하고 학습/서빙 간 일관성을 보장합니다.
모델 모니터링: 데이터 드리프트, 개념 드리프트를 감지하여 모델 성능 저하를 조기에 발견합니다. Evidently, Whylogs 등의 도구를 활용합니다.
앙상블 기법: Gradient Boosting(XGBoost, LightGBM, CatBoost)은 대부분의 정형 데이터 문제에서 최고 성능을 제공합니다. 스태킹(Stacking)으로 여러 모델을 결합하면 추가 성능 향상이 가능합니다.
14. 딥러닝
고급전문가
딥러닝(Deep Learning)은 다층 신경망을 사용하여 데이터의 복잡한 패턴을 학습하는 머신러닝의 하위 분야입니다. 이미지 인식, 자연어 처리, 음성 인식, 생성 AI 등에서 혁신적인 성과를 이루고 있으며, TensorFlow와 PyTorch가 양대 프레임워크입니다.
TensorFlow/Keras (딥러닝)
TensorFlow는 Google이 개발한 오픈소스 딥러닝 프레임워크이며,
Keras는 TensorFlow의 고수준 API로 모델을 직관적으로 구축·학습·배포할 수 있습니다.
TensorFlow 2.x부터 Keras가 기본 통합되어 tf.keras로 사용합니다.
import tensorflow as tf
import numpy as np
# 텐서 생성
a = tf.constant([[1, 2], [3, 4]], dtype=tf.float32) # 불변 텐서
b = tf.Variable([[5, 6], [7, 8]], dtype=tf.float32) # 변경 가능 (학습 가중치용)
z = tf.zeros([3, 4]) # 영행렬
o = tf.ones([2, 3]) # 1행렬
r = tf.random.normal([3, 3], mean=0.0, stddev=1.0) # 정규분포 난수
e = tf.eye(3) # 단위행렬# 기본 수학 연산 (요소별)
c = a + b # tf.add(a, b)
c = a * b # tf.multiply(a, b) — 요소별 곱
c = a @ b # tf.matmul(a, b) — 행렬 곱
c = tf.reduce_sum(a) # 전체 합 → 스칼라 10
c = tf.reduce_mean(a, axis=0) # 열 평균 → [2. 3.]# Shape 조작
t = tf.constant([[1, 2, 3], [4, 5, 6]])
t.shape # TensorShape([2, 3])
r = tf.reshape(t, [3, 2]) # (2,3) → (3,2)
r = tf.reshape(t, [-1]) # 평탄화 → (6,)
r = tf.expand_dims(t, axis=0) # (2,3) → (1,2,3) — 배치 차원 추가
r = tf.squeeze(r) # 크기 1인 차원 제거
r = tf.transpose(t) # 전치 (2,3) → (3,2)# 타입 변환 & NumPy 호환
f = tf.cast(t, tf.float32) # int → float
n = t.numpy() # 텐서 → NumPy 배열
t = tf.convert_to_tensor(n) # NumPy → 텐서
Sequential API
from tensorflow import keras
from tensorflow.keras import layers
# Sequential 모델 — 레이어를 순서대로 쌓는 가장 간단한 방식
model = keras.Sequential([
layers.Dense(64, activation="relu", input_shape=(784,)),
layers.Dense(32, activation="relu"),
layers.Dropout(0.3), # 과적합 방지: 30% 뉴런 비활성화
layers.Dense(10, activation="softmax")
])
model.summary() # 모델 구조 및 파라미터 수 확인# add()로 레이어 추가 (동적 구성)
model = keras.Sequential()
model.add(layers.Dense(128, activation="relu", input_shape=(784,)))
model.add(layers.BatchNormalization()) # 배치 정규화
model.add(layers.Dense(64, activation="relu"))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation="softmax"))
# 모델 컴파일
model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"]
)
# 학습
history = model.fit(
X_train, y_train,
epochs=10,
batch_size=32,
validation_split=0.2,
verbose=1# 0=무출력, 1=진행바, 2=에포크 요약
)
# 학습 기록 활용
history.history["loss"] # 에포크별 손실값 리스트
history.history["val_accuracy"] # 에포크별 검증 정확도# 평가 및 예측
test_loss, test_acc = model.evaluate(X_test, y_test)
predictions = model.predict(X_test) # (N, 10) 확률 배열
predicted_classes = tf.argmax(predictions, axis=1) # 최대 확률 클래스
Functional API
# Functional API — 다중 입력/출력, 잔차 연결 등 복잡한 구조 지원# 기본 사용
inputs = keras.Input(shape=(784,), name="input")
x = layers.Dense(64, activation="relu")(inputs)
x = layers.Dropout(0.3)(x)
outputs = layers.Dense(10, activation="softmax", name="output")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
# 잔차 연결 (Residual / Skip Connection)
inputs = keras.Input(shape=(256,))
x = layers.Dense(256, activation="relu")(inputs)
x = layers.BatchNormalization()(x)
x = layers.Dense(256, activation="relu")(x)
x = layers.Add()([x, inputs]) # 잔차 연결: 입력을 출력에 더함
outputs = layers.Dense(10, activation="softmax")(x)
res_model = keras.Model(inputs, outputs)
# 다중 입력 / 다중 출력
text_input = keras.Input(shape=(100,), name="text")
meta_input = keras.Input(shape=(5,), name="meta")
x1 = layers.Dense(64, activation="relu")(text_input)
x2 = layers.Dense(16, activation="relu")(meta_input)
merged = layers.Concatenate()([x1, x2]) # 특성 결합
shared = layers.Dense(32, activation="relu")(merged)
category_out = layers.Dense(5, activation="softmax", name="category")(shared)
score_out = layers.Dense(1, name="score")(shared)
multi_model = keras.Model(
inputs=[text_input, meta_input],
outputs=[category_out, score_out]
)
multi_model.compile(
optimizer="adam",
loss={"category": "sparse_categorical_crossentropy", "score": "mse"},
loss_weights={"category": 1.0, "score": 0.5}
)
Model Subclassing
# 커스텀 모델 클래스 — 최대 유연성, 연구용에 적합classMyModel(keras.Model):
def__init__(self, num_classes=10):
super().__init__()
self.dense1 = layers.Dense(128, activation="relu")
self.bn = layers.BatchNormalization()
self.dropout = layers.Dropout(0.3)
self.dense2 = layers.Dense(num_classes, activation="softmax")
defcall(self, inputs, training=False):
x = self.dense1(inputs)
x = self.bn(x, training=training) # training 플래그 전달 중요
x = self.dropout(x, training=training)
return self.dense2(x)
model = MyModel(num_classes=10)
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
# 커스텀 레이어classScaledDense(layers.Layer):
def__init__(self, units, scale=1.0):
super().__init__()
self.units = units
self.scale = scale
defbuild(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer="random_normal")
self.b = self.add_weight(shape=(self.units,), initializer="zeros")
defcall(self, inputs):
return tf.matmul(inputs, self.w) * self.scale + self.b
주요 레이어 비교
레이어
용도
주요 파라미터
입력 shape
Dense
완전연결 (MLP)
units, activation
(batch, features)
Conv2D
이미지 특징 추출
filters, kernel_size, strides, padding
(batch, H, W, C)
MaxPooling2D
공간 다운샘플링
pool_size, strides
(batch, H, W, C)
LSTM
시계열/순차 데이터
units, return_sequences, dropout
(batch, timesteps, features)
GRU
경량 순환 레이어
units, return_sequences
(batch, timesteps, features)
Embedding
정수→밀집 벡터 변환
input_dim, output_dim
(batch, sequence_length)
BatchNormalization
학습 안정화, 가속
momentum, epsilon
임의
Dropout
과적합 방지
rate (0~1)
임의
Flatten
다차원→1차원 변환
없음
(batch, ...)
GlobalAveragePooling2D
특성맵 평균 풀링
없음
(batch, H, W, C)
옵티마이저(Optimizer)
# 문자열 지정 (기본 하이퍼파라미터)
model.compile(optimizer="adam", loss="mse")
# 객체로 지정 (하이퍼파라미터 커스터마이징)
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=1e-3, weight_decay=1e-4),
loss="mse"
)
# 주요 옵티마이저 비교
keras.optimizers.SGD(learning_rate=0.01, momentum=0.9) # 안정적, 일반화 우수
keras.optimizers.Adam(learning_rate=1e-3) # 가장 범용, 빠른 수렴
keras.optimizers.AdamW(learning_rate=1e-3, weight_decay=1e-4) # Adam + 가중치 감쇠
keras.optimizers.RMSprop(learning_rate=1e-3) # RNN에 적합# 학습률 스케줄러
lr_schedule = keras.optimizers.schedules.CosineDecay(
initial_learning_rate=1e-3,
decay_steps=10000,
alpha=1e-5# 최소 학습률
)
optimizer = keras.optimizers.Adam(learning_rate=lr_schedule)
손실 함수(Loss Function)
작업
손실 함수
출력 활성화
레이블 형태
이진 분류
binary_crossentropy
sigmoid
0 또는 1
다중 분류 (정수 레이블)
sparse_categorical_crossentropy
softmax
0, 1, 2, ...
다중 분류 (원핫 레이블)
categorical_crossentropy
softmax
[0,1,0,...] 원핫
회귀
mse (mean_squared_error)
없음 (linear)
연속값
회귀 (이상치 강건)
huber
없음
연속값
다중 레이블
binary_crossentropy
sigmoid
[1,0,1,...] 다중
# 커스텀 손실 함수defcustom_mse(y_true, y_pred):
return tf.reduce_mean(tf.square(y_true - y_pred))
model.compile(optimizer="adam", loss=custom_mse)
# 클래스 가중치 (불균형 데이터)
model.fit(X_train, y_train, class_weight={0: 1.0, 1: 5.0}) # 소수 클래스에 높은 가중치
# 주요 콜백 — 학습 과정을 자동으로 제어
callbacks = [
# 검증 손실이 5 에포크 동안 개선되지 않으면 학습 중단
keras.callbacks.EarlyStopping(
monitor="val_loss", patience=5, restore_best_weights=True
),
# 최적 모델 자동 저장
keras.callbacks.ModelCheckpoint(
"best_model.keras", monitor="val_loss", save_best_only=True
),
# 검증 손실 정체 시 학습률 감소
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=3, min_lr=1e-6
),
# TensorBoard 로그
keras.callbacks.TensorBoard(log_dir="./logs", histogram_freq=1),
]
model.fit(X_train, y_train, epochs=100, callbacks=callbacks, validation_split=0.2)
# 커스텀 콜백classPrintLR(keras.callbacks.Callback):
defon_epoch_end(self, epoch, logs=None):
lr = self.model.optimizer.learning_rate
print(f"\n에포크 {epoch+1} 학습률: {lr:.6f}")
커스텀 학습 루프 (GradientTape)
# tf.GradientTape — 학습 과정을 완전히 제어
model = MyModel()
optimizer = keras.optimizers.Adam(1e-3)
loss_fn = keras.losses.SparseCategoricalCrossentropy()
train_acc = keras.metrics.SparseCategoricalAccuracy()
@tf.function# 그래프 모드로 컴파일 → 성능 향상deftrain_step(x, y):
with tf.GradientTape() as tape:
predictions = model(x, training=True)
loss = loss_fn(y, predictions)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
train_acc.update_state(y, predictions)
return loss
# 학습 루프for epoch inrange(10):
train_acc.reset_state()
for x_batch, y_batch in train_dataset:
loss = train_step(x_batch, y_batch)
print(f"에포크 {epoch+1}, 손실: {loss:.4f}, 정확도: {train_acc.result():.4f}")
모델 저장 및 로드
# ① Keras 형식 (권장) — 모델 구조 + 가중치 + 옵티마이저 상태
model.save("my_model.keras")
loaded_model = keras.models.load_model("my_model.keras")
# ② SavedModel 형식 (TF Serving 배포용)
model.save("saved_model_dir") # 디렉토리로 저장
loaded_model = keras.models.load_model("saved_model_dir")
# ③ 가중치만 저장/로드 (모델 구조는 코드로 재생성)
model.save_weights("weights.weights.h5")
model.load_weights("weights.weights.h5")
# ④ TFLite 변환 (모바일/엣지 배포)
converter = tf.lite.TFLiteConverter.from_saved_model("saved_model_dir")
converter.optimizations = [tf.lite.Optimize.DEFAULT] # 양자화
tflite_model = converter.convert()
withopen("model.tflite", "wb") as f:
f.write(tflite_model)
# ⑤ ONNX 변환# pip install tf2onnx# python -m tf2onnx.convert --saved-model saved_model_dir --output model.onnx
tf.data 입력 파이프라인
# tf.data.Dataset — 효율적인 데이터 로딩 & 전처리 파이프라인# NumPy 배열에서 생성
dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train))
# 파이프라인 구성 (체이닝)
train_ds = (
dataset
.shuffle(buffer_size=10000) # 데이터 섞기
.batch(32) # 배치 크기
.map(lambda x, y: (x / 255.0, y), # 전처리 (정규화)
num_parallel_calls=tf.data.AUTOTUNE)
.prefetch(tf.data.AUTOTUNE) # GPU 연산 중 다음 배치 준비
.cache() # 첫 에포크 후 메모리 캐시
)
# 이미지 디렉토리에서 데이터셋 생성
train_ds = keras.utils.image_dataset_from_directory(
"data/train",
image_size=(224, 224),
batch_size=32,
label_mode="categorical", # "int", "categorical", "binary"
validation_split=0.2,
subset="training",
seed=42
)
# CSV 파일에서 생성
csv_ds = tf.data.experimental.make_csv_dataset(
"data.csv",
batch_size=32,
label_name="target",
num_epochs=1
)
# 커스텀 map 함수 (데이터 증강)defaugment(image, label):
image = tf.image.random_flip_left_right(image)
image = tf.image.random_brightness(image, max_delta=0.2)
return image, label
train_ds = train_ds.map(augment, num_parallel_calls=tf.data.AUTOTUNE)
GPU 메모리 관리
# GPU 메모리 점진적 할당 (필수 설정 — OOM 방지)
gpus = tf.config.list_physical_devices("GPU")
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
# 또는 메모리 한도 설정
tf.config.set_logical_device_configuration(
gpus[0],
[tf.config.LogicalDeviceConfiguration(memory_limit=4096)] # 4GB
)
# 혼합 정밀도 학습 (float16 + float32) — 속도 2배, 메모리 절감
keras.mixed_precision.set_global_policy("mixed_float16")
# 멀티 GPU 분산 학습
strategy = tf.distribute.MirroredStrategy() # 단일 머신, 다중 GPUwith strategy.scope():
model = keras.Sequential([...])
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
주의사항:set_memory_growth는 프로그램 시작 시 GPU 사용 전에 호출해야 합니다.
혼합 정밀도 사용 시 모델의 마지막 출력 레이어는 반드시 dtype="float32"로 지정하세요.
model(x, training=True/False)에서 training 플래그를 정확히 전달해야
Dropout과 BatchNormalization이 올바르게 동작합니다.
TensorFlow/Keras 핵심 정리
작업
API / 메서드
비고
간단한 모델 구축
keras.Sequential
선형 스택 구조
복잡한 모델 구축
keras.Model (Functional)
다중 입출력, 잔차 연결
최대 유연성 모델
Model Subclassing
동적 로직, 연구용
학습
model.fit()
콜백으로 제어
세밀한 학습 제어
tf.GradientTape
GAN, 강화학습 등
데이터 파이프라인
tf.data.Dataset
shuffle→batch→prefetch
모델 저장
model.save(".keras")
Keras 형식 권장
배포 변환
TFLiteConverter
모바일/엣지용
성능 향상
@tf.function
그래프 모드 컴파일
메모리 절감
mixed_float16
Ampere+ GPU 권장
분산 학습
MirroredStrategy
멀티 GPU 자동 분배
학습 모니터링
TensorBoard 콜백
tensorboard --logdir ./logs
PyTorch (딥러닝)
PyTorch는 Meta(Facebook)가 개발한 오픈소스 딥러닝 프레임워크로,
동적 계산 그래프(Define-by-Run) 방식으로 직관적인 디버깅과 유연한 모델 설계가 가능합니다.
연구 커뮤니티에서 가장 널리 사용되며, TorchScript/ONNX를 통한 프로덕션 배포도 지원합니다.
설치:pip install torch torchvision torchaudio |
CUDA: pytorch.org에서 플랫폼별 명령어 확인 |
확인: python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
텐서(Tensor) 기본 연산
import torch
import numpy as np
# 텐서 생성
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32) # 리스트에서
b = torch.zeros(3, 4) # 영행렬
c = torch.ones(2, 3) # 1행렬
d = torch.randn(3, 3) # 표준정규분포 난수
e = torch.eye(3) # 단위행렬
f = torch.arange(0, 10, 2) # [0, 2, 4, 6, 8]
g = torch.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1]
h = torch.empty(2, 3).uniform_(0, 1) # 균일분포 난수 (in-place)# 기본 수학 연산 (요소별)
x = a + a # torch.add(a, a)
x = a * a # torch.mul(a, a) — 요소별 곱
x = a @ a # torch.matmul(a, a) — 행렬 곱
x = a.sum() # 전체 합 → 스칼라 tensor(10.)
x = a.mean(dim=0) # 열 평균 → tensor([2., 3.])
x = a.max(dim=1) # 행 최대 → values, indices 반환
x = torch.clamp(a, 0, 3) # 값 범위 제한 [0, 3]# Shape 조작
t = torch.randn(2, 3, 4)
t.shape # torch.Size([2, 3, 4])
r = t.view(6, 4) # reshape (연속 메모리 필요)
r = t.reshape(6, 4) # reshape (자동 복사)
r = t.view(-1) # 평탄화 → (24,)
r = t.unsqueeze(0) # (2,3,4) → (1,2,3,4) — 차원 추가
r = r.squeeze(0) # (1,2,3,4) → (2,3,4) — 차원 제거
r = t.permute(2, 0, 1) # 축 재배열 (2,3,4) → (4,2,3)
r = t.transpose(0, 1) # 두 축 교환 (2,3,4) → (3,2,4)# 결합 & 분할
x = torch.cat([a, a], dim=0) # 행 방향 결합 (4,2)
x = torch.stack([a, a], dim=0) # 새 차원으로 쌓기 (2,2,2)
chunks = t.chunk(3, dim=1) # dim=1 기준 3등분# 타입 변환 & NumPy 호환
f = a.to(torch.float64) # float32 → float64
f = a.int() # → int32 (단축형)
n = a.numpy() # 텐서 → NumPy (CPU, 메모리 공유)
t = torch.from_numpy(n) # NumPy → 텐서 (메모리 공유)
t = a.clone().detach() # 독립 복사본 (그래프 분리)
Autograd (자동 미분)
# requires_grad=True → 연산 추적, 자동 미분 가능
x = torch.tensor([2.0, 3.0], requires_grad=True)
y = x ** 2 + 3 * x + 1# y = x² + 3x + 1
loss = y.sum()
loss.backward() # 역전파 — 그래디언트 계산print(x.grad) # dy/dx = 2x + 3 → tensor([7., 9.])# 그래디언트 제어
x.grad.zero_() # 그래디언트 초기화 (축적 방지)# 그래디언트 추적 비활성화 (추론/전처리 시)with torch.no_grad():
y = x * 2# 연산 추적 안 함 → 메모리 절약
y = x.detach() # 그래프에서 분리된 새 텐서# 고차 미분
x = torch.tensor([1.0], requires_grad=True)
y = x ** 3
grad1 = torch.autograd.grad(y, x, create_graph=True)[0] # 3x² = 3
grad2 = torch.autograd.grad(grad1, x)[0] # 6x = 6
nn.Module 모델 구축
import torch.nn as nn
# 기본 모델 정의classNet(nn.Module):
def__init__(self, num_classes=10):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.bn1 = nn.BatchNorm1d(256)
self.fc2 = nn.Linear(256, 128)
self.dropout = nn.Dropout(0.3)
self.fc3 = nn.Linear(128, num_classes)
defforward(self, x):
x = torch.relu(self.bn1(self.fc1(x)))
x = torch.relu(self.fc2(x))
x = self.dropout(x) # training 모드에서만 활성화return self.fc3(x) # 분류: CrossEntropyLoss가 softmax 포함
model = Net(num_classes=10)
print(model) # 모델 구조 출력# 파라미터 확인
total = sum(p.numel() for p in model.parameters())
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"전체: {total:,} / 학습 가능: {trainable:,}")
# 파라미터 순회for name, param in model.named_parameters():
print(f"{name}: {param.shape}")
nn.Sequential & ModuleList
# nn.Sequential — 레이어를 순서대로 쌓기
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.BatchNorm1d(256),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
# OrderedDict로 이름 부여from collections import OrderedDict
model = nn.Sequential(OrderedDict([
("fc1", nn.Linear(784, 256)),
("relu1", nn.ReLU()),
("fc2", nn.Linear(256, 10)),
]))
model.fc1 # 이름으로 레이어 접근# nn.ModuleList — 동적 레이어 구성classDynamicNet(nn.Module):
def__init__(self, layers_sizes):
super().__init__()
self.layers = nn.ModuleList([
nn.Linear(in_f, out_f)
for in_f, out_f inzip(layers_sizes[:-1], layers_sizes[1:])
])
defforward(self, x):
for layer in self.layers[:-1]:
x = torch.relu(layer(x))
return self.layers[-1](x)
# nn.ModuleDict — 조건부 레이어 선택classFlexibleNet(nn.Module):
def__init__(self, activation="relu"):
super().__init__()
self.fc = nn.Linear(784, 10)
self.activations = nn.ModuleDict({
"relu": nn.ReLU(),
"gelu": nn.GELU(),
"silu": nn.SiLU(),
})
self.act = self.activations[activation]
TensorFlow vs PyTorch 채널 순서:
TensorFlow/Keras는 (batch, H, W, C) — channels-last,
PyTorch는 (batch, C, H, W) — channels-first가 기본입니다.
PyTorch에서 .permute(0, 3, 1, 2)로 변환하세요.
손실 함수(Loss Function)
작업
손실 함수
출력 형태
레이블 형태
다중 분류
nn.CrossEntropyLoss()
raw logits (N, C)
정수 (N,)
이진 분류
nn.BCEWithLogitsLoss()
raw logits (N, 1)
float 0/1 (N, 1)
이진 분류 (sigmoid 후)
nn.BCELoss()
확률 (N, 1)
float 0/1 (N, 1)
회귀
nn.MSELoss()
(N, *)
(N, *)
회귀 (이상치 강건)
nn.SmoothL1Loss()
(N, *)
(N, *)
회귀 (L1)
nn.L1Loss()
(N, *)
(N, *)
다중 레이블
nn.BCEWithLogitsLoss()
raw logits (N, C)
multi-hot (N, C)
# CrossEntropyLoss는 내부에 softmax를 포함 — 모델 출력에 softmax 적용하지 말 것!
criterion = nn.CrossEntropyLoss()
logits = model(x) # raw logits, softmax 적용 안 함
loss = criterion(logits, labels) # labels: 정수 텐서 (LongTensor)# 클래스 불균형 처리
weights = torch.tensor([1.0, 5.0, 1.0]) # 클래스별 가중치
criterion = nn.CrossEntropyLoss(weight=weights.to(device))
# 커스텀 손실 함수classFocalLoss(nn.Module):
def__init__(self, alpha=1.0, gamma=2.0):
super().__init__()
self.alpha = alpha
self.gamma = gamma
defforward(self, inputs, targets):
ce_loss = nn.functional.cross_entropy(inputs, targets, reduction="none")
pt = torch.exp(-ce_loss)
return (self.alpha * (1 - pt) ** self.gamma * ce_loss).mean()
옵티마이저(Optimizer) & 스케줄러
import torch.optim as optim
# 주요 옵티마이저
optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)
optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)
optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2) # 분리된 가중치 감쇠
optim.RMSprop(model.parameters(), lr=1e-3)
# 파라미터 그룹별 학습률 설정
optimizer = optim.Adam([
{"params": model.fc1.parameters(), "lr": 1e-4}, # 사전학습 레이어 (낮은 lr)
{"params": model.fc3.parameters(), "lr": 1e-3}, # 새 레이어 (높은 lr)
], weight_decay=1e-4)
# 학습률 스케줄러
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) # 10 에포크마다 ×0.1
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50) # 코사인 감쇠
scheduler = optim.lr_scheduler.ReduceLROnPlateau( # 정체 시 감소
optimizer, mode="min", factor=0.5, patience=5
)
scheduler = optim.lr_scheduler.OneCycleLR( # 1-Cycle 정책
optimizer, max_lr=0.01, total_steps=1000
)
# 스케줄러 사용 (학습 루프 내에서)# scheduler.step() — 에포크 기반 (StepLR, CosineAnnealing)# scheduler.step(val_loss) — 메트릭 기반 (ReduceLROnPlateau)# scheduler.step() — 배치 기반 (OneCycleLR) — 매 배치 후 호출
표준 학습 루프
import torch
import torch.nn as nn
import torch.optim as optim
device = torch.device("cuda"if torch.cuda.is_available() else"cpu")
model = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)
# 학습 함수deftrain_one_epoch(model, loader, criterion, optimizer, device):
model.train() # Dropout, BatchNorm 학습 모드
running_loss = 0.0
correct = 0
total = 0for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad() # ① 그래디언트 초기화
outputs = model(inputs) # ② 순전파
loss = criterion(outputs, labels)
loss.backward() # ③ 역전파
optimizer.step() # ④ 가중치 갱신
running_loss += loss.item() * inputs.size(0)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
return running_loss / total, correct / total
# 검증 함수@torch.no_grad()# 그래디언트 비활성화defevaluate(model, loader, criterion, device):
model.eval() # Dropout 비활성화, BatchNorm 고정
running_loss = 0.0
correct = 0
total = 0for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
loss = criterion(outputs, labels)
running_loss += loss.item() * inputs.size(0)
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
return running_loss / total, correct / total
# 학습 루프 (EarlyStopping 포함)
best_val_loss = float("inf")
patience_counter = 0for epoch inrange(100):
train_loss, train_acc = train_one_epoch(model, train_loader, criterion, optimizer, device)
val_loss, val_acc = evaluate(model, val_loader, criterion, device)
scheduler.step()
print(f"[{epoch+1:3d}] train_loss={train_loss:.4f} acc={train_acc:.4f} | val_loss={val_loss:.4f} acc={val_acc:.4f}")
if val_loss < best_val_loss:
best_val_loss = val_loss
patience_counter = 0
torch.save(model.state_dict(), "best_model.pt") # 최적 모델 저장else:
patience_counter += 1if patience_counter >= 10:
print("Early stopping!")
break
model.load_state_dict(torch.load("best_model.pt")) # 최적 가중치 복원
DataLoader & Dataset
from torch.utils.data import Dataset, DataLoader, TensorDataset, random_split
# ① TensorDataset — NumPy/텐서에서 바로 생성
dataset = TensorDataset(
torch.tensor(X_train, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.long)
)
# ② 커스텀 DatasetclassMyDataset(Dataset):
def__init__(self, data, labels, transform=None):
self.data = data
self.labels = labels
self.transform = transform
def__len__(self):
returnlen(self.data)
def__getitem__(self, idx):
sample = self.data[idx]
label = self.labels[idx]
if self.transform:
sample = self.transform(sample)
return sample, label
# DataLoader — 배치, 셔플, 병렬 로딩
train_loader = DataLoader(
dataset,
batch_size=64,
shuffle=True, # 학습 데이터 셔플
num_workers=4, # 병렬 데이터 로딩 프로세스
pin_memory=True, # GPU 전송 가속 (CUDA)
drop_last=True, # 마지막 불완전 배치 제거
)
val_loader = DataLoader(dataset, batch_size=128, shuffle=False)
# 데이터 분할
train_set, val_set = random_split(dataset, [0.8, 0.2])
# 이미지 데이터: torchvision transformsfrom torchvision import transforms, datasets
transform_train = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
transform_val = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 내장 데이터셋 (CIFAR-10, MNIST, ImageNet 등)
train_ds = datasets.CIFAR10("./data", train=True, download=True, transform=transform_train)
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True, num_workers=4)
# ImageFolder — 디렉토리 구조에서 자동 로딩 (data/train/cat/*.jpg, data/train/dog/*.jpg)
train_ds = datasets.ImageFolder("data/train", transform=transform_train)
모델 저장 및 로드
# ① state_dict 저장/로드 (권장 — 가중치만, 코드 유지)
torch.save(model.state_dict(), "model_weights.pt")
model = Net() # 모델 구조 재생성 필요
model.load_state_dict(torch.load("model_weights.pt", weights_only=True))
model.eval()
# ② 전체 모델 저장 (구조 + 가중치 — pickle 의존)
torch.save(model, "full_model.pt")
model = torch.load("full_model.pt", weights_only=False)
# ③ 체크포인트 저장 (학습 재개용)
torch.save({
"epoch": epoch,
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict(),
"scheduler_state_dict": scheduler.state_dict(),
"best_val_loss": best_val_loss,
}, "checkpoint.pt")
# 체크포인트 복원
ckpt = torch.load("checkpoint.pt", weights_only=False)
model.load_state_dict(ckpt["model_state_dict"])
optimizer.load_state_dict(ckpt["optimizer_state_dict"])
scheduler.load_state_dict(ckpt["scheduler_state_dict"])
start_epoch = ckpt["epoch"] + 1# ④ TorchScript 변환 (C++/프로덕션 배포)
scripted = torch.jit.script(model) # Python 제어흐름 포함 가능
scripted.save("model_scripted.pt")
traced = torch.jit.trace(model, torch.randn(1, 784)) # 입력 기반 추적
traced.save("model_traced.pt")
loaded = torch.jit.load("model_scripted.pt")
# ⑤ ONNX 변환
dummy = torch.randn(1, 784)
torch.onnx.export(model, dummy, "model.onnx",
input_names=["input"], output_names=["output"],
dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}})
GPU & 디바이스 관리
# 디바이스 설정
device = torch.device("cuda"if torch.cuda.is_available() else"cpu")
device = torch.device("cuda:0") # 특정 GPU 지정# 텐서/모델을 디바이스로 이동
x = x.to(device) # 텐서 이동
model = model.to(device) # 모델 이동 (파라미터 + 버퍼)# GPU 정보 확인
torch.cuda.is_available() # GPU 사용 가능 여부
torch.cuda.device_count() # GPU 개수
torch.cuda.get_device_name(0) # GPU 이름
torch.cuda.memory_allocated() # 현재 메모리 사용량
torch.cuda.memory_reserved() # 예약된 메모리
torch.cuda.empty_cache() # 미사용 캐시 해제# 혼합 정밀도 학습 (AMP) — 속도 2배↑, 메모리 절감
scaler = torch.amp.GradScaler()
for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
with torch.amp.autocast(device_type="cuda"): # float16 자동 변환
outputs = model(inputs)
loss = criterion(outputs, labels)
scaler.scale(loss).backward() # 스케일링된 역전파
scaler.step(optimizer) # 가중치 갱신
scaler.update() # 스케일 조정# 멀티 GPU (DataParallel — 간단하지만 비효율)if torch.cuda.device_count() > 1:
model = nn.DataParallel(model) # 배치를 GPU에 분배
model = model.to(device)
# 멀티 GPU (DistributedDataParallel — 권장, 고성능)# torchrun --nproc_per_node=4 train.pyimport torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
dist.init_process_group(backend="nccl")
local_rank = int(os.environ["LOCAL_RANK"])
model = DDP(model.to(local_rank), device_ids=[local_rank])
주의사항:model.train()과 model.eval()을 학습/추론 전에 반드시 호출하세요 —
Dropout과 BatchNorm의 동작이 달라집니다.
CrossEntropyLoss는 softmax를 내장하므로 모델 출력에 softmax를 중복 적용하면 안 됩니다.
loss.backward() 전에 optimizer.zero_grad()를 호출하세요 — PyTorch는 그래디언트를 기본으로 누적합니다.
추론 & 배포
# 추론 모드 — 메모리 최적화, autograd 완전 비활성화
model.eval()
with torch.inference_mode(): # torch.no_grad()보다 빠름
predictions = model(x.to(device))
probs = torch.softmax(predictions, dim=1)
classes = probs.argmax(dim=1)
# 배치 추론 (대량 데이터)
all_preds = []
model.eval()
with torch.inference_mode():
for batch in test_loader:
inputs = batch[0].to(device)
preds = model(inputs).argmax(dim=1)
all_preds.append(preds.cpu())
all_preds = torch.cat(all_preds)
# torch.compile — PyTorch 2.0+ 자동 최적화 (JIT 컴파일)
model = torch.compile(model) # 첫 실행 시 컴파일, 이후 가속
model = torch.compile(model, mode="reduce-overhead") # 오버헤드 최소화
그래디언트 클리핑 & 정규화
# 그래디언트 클리핑 — 그래디언트 폭발 방지 (RNN, Transformer 필수)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # L2 norm 기준
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5) # 절대값 기준
optimizer.step()
# 가중치 초기화definit_weights(m):
ifisinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
if m.bias isnotNone:
nn.init.zeros_(m.bias)
elifisinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode="fan_out")
model.apply(init_weights) # 모든 하위 모듈에 재귀 적용# 레이어 동결 (전이 학습)for param in model.fc1.parameters():
param.requires_grad = False# fc1 레이어 가중치 고정# 동결된 파라미터 제외한 옵티마이저
optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-3)
PyTorch 핵심 정리
작업
API / 메서드
비고
모델 정의
nn.Module 상속
__init__ + forward
간단한 모델
nn.Sequential
이름 부여 시 OrderedDict
학습 모드
model.train()
Dropout/BN 활성화
추론 모드
model.eval() + inference_mode()
메모리 최적화
역전파
loss.backward()
zero_grad → forward → backward → step
데이터 로딩
DataLoader
pin_memory=True (GPU)
모델 저장
torch.save(state_dict)
가중치만 저장 권장
체크포인트
dict로 epoch/optimizer 포함 저장
학습 재개용
프로덕션 배포
torch.jit.script/trace
TorchScript C++ 런타임
성능 향상
torch.compile()
PyTorch 2.0+ JIT 컴파일
혼합 정밀도
torch.amp.autocast
GradScaler 필수
멀티 GPU
DistributedDataParallel
torchrun으로 실행
그래디언트 클리핑
clip_grad_norm_
RNN/Transformer 필수
전이 학습
requires_grad = False
레이어 동결
CNN (합성곱 신경망)
합성곱 신경망(Convolutional Neural Network)은 이미지 인식, 객체 탐지, 세그멘테이션 등
시각적 패턴 인식에 최적화된 아키텍처입니다. 합성곱 레이어가 공간적 특징을 계층적으로 추출하고,
풀링으로 차원을 줄인 뒤 완전연결층에서 분류합니다.
합성곱 연산 이해
# Conv2D 핵심 파라미터 이해## 입력: (batch, H, W, C) — Keras / (batch, C, H, W) — PyTorch## filters/out_channels : 출력 특성맵 수 (학습할 필터 개수)# kernel_size : 필터 크기 (3×3이 가장 보편적)# strides : 필터 이동 간격 (2이면 출력 크기 절반)# padding : "same" → 입출력 크기 동일, "valid" → 패딩 없음## 출력 크기 계산: O = (I - K + 2P) / S + 1# I=입력크기, K=커널크기, P=패딩, S=스트라이드# 예: 입력 28×28, kernel=3, padding=0, stride=1 → 26×26# 예: 입력 28×28, kernel=3, padding=1, stride=1 → 28×28 (same)# 예: 입력 28×28, kernel=3, padding=0, stride=2 → 13×13
CNN — Keras 구현
from tensorflow import keras
from tensorflow.keras import layers
# 기본 CNN (MNIST 분류)
model = keras.Sequential([
# 블록 1: 합성곱 → 정규화 → 활성화 → 풀링
layers.Conv2D(32, (3, 3), padding="same", input_shape=(28, 28, 1)),
layers.BatchNormalization(),
layers.ReLU(),
layers.MaxPooling2D((2, 2)), # 28×28 → 14×14# 블록 2
layers.Conv2D(64, (3, 3), padding="same"),
layers.BatchNormalization(),
layers.ReLU(),
layers.MaxPooling2D((2, 2)), # 14×14 → 7×7# 블록 3
layers.Conv2D(128, (3, 3), padding="same"),
layers.BatchNormalization(),
layers.ReLU(),
layers.GlobalAveragePooling2D(), # 7×7×128 → 128 (Flatten 대신 권장)# 분류 헤드
layers.Dropout(0.5),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
# 1D 합성곱 (텍스트, 시계열)
text_cnn = keras.Sequential([
layers.Embedding(10000, 128, input_length=200),
layers.Conv1D(64, 5, activation="relu"), # 5-gram 특징 추출
layers.GlobalMaxPooling1D(),
layers.Dense(1, activation="sigmoid")
])
CNN — PyTorch 구현
import torch
import torch.nn as nn
classCNN(nn.Module):
def__init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
# 블록 1
nn.Conv2d(1, 32, kernel_size=3, padding=1), # (1,28,28)→(32,28,28)
nn.BatchNorm2d(32),
nn.ReLU(inplace=True),
nn.MaxPool2d(2), # →(32,14,14)# 블록 2
nn.Conv2d(32, 64, kernel_size=3, padding=1), # →(64,14,14)
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(2), # →(64,7,7)# 블록 3
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.AdaptiveAvgPool2d((1, 1)), # →(128,1,1) 어떤 입력 크기든 OK
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Dropout(0.5),
nn.Linear(128, num_classes),
)
defforward(self, x):
x = self.features(x)
return self.classifier(x)
# Depthwise Separable Convolution (MobileNet 스타일 — 경량화)
depthwise_sep = nn.Sequential(
nn.Conv2d(64, 64, 3, padding=1, groups=64), # depthwise: 채널별 독립 합성곱
nn.Conv2d(64, 128, 1), # pointwise: 1×1 합성곱으로 채널 결합
)
# Dilated (Atrous) Convolution — 수용 영역 확장
dilated = nn.Conv2d(64, 64, 3, padding=2, dilation=2) # 3×3 필터가 5×5 영역 커버# Transposed Convolution — 업샘플링 (세그멘테이션, GAN 생성기)
upsample = nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1) # 크기 2배
주요 CNN 아키텍처 비교
아키텍처
연도
핵심 기법
파라미터
특징
LeNet-5
1998
Conv→Pool→FC
60K
CNN의 시초, 손글씨 인식
AlexNet
2012
ReLU, Dropout, GPU
60M
딥러닝 붐의 시작
VGGNet
2014
3×3 필터 반복
138M
단순한 구조, 깊은 네트워크
GoogLeNet
2014
Inception 모듈
6.8M
다양한 필터 크기 병렬 적용
ResNet
2015
잔차 연결 (Skip)
25M (50)
100+층 학습 가능, 가장 범용
DenseNet
2017
밀집 연결
8M (121)
모든 이전 층과 연결
MobileNet
2017
Depthwise Separable
3.4M
모바일/엣지 배포용 경량화
EfficientNet
2019
복합 스케일링
5.3M (B0)
깊이×너비×해상도 최적 균형
ConvNeXt
2022
Transformer 기법 적용
29M
현대화된 순수 CNN, ViT 경쟁
데이터 증강 (Data Augmentation)
# Keras — 레이어 기반 증강 (GPU 가속, 모델에 포함)
data_augmentation = keras.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1), # ±36도
layers.RandomZoom(0.2), # ±20% 확대/축소
layers.RandomTranslation(0.1, 0.1), # ±10% 이동
layers.RandomContrast(0.2),
])
model = keras.Sequential([
data_augmentation, # 학습 시에만 적용 (추론 시 자동 비활성화)
layers.Conv2D(32, 3, activation="relu"),
# ...
])
# PyTorch — torchvision.transformsfrom torchvision import transforms
transform_train = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(15),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
transforms.RandomErasing(p=0.3), # 랜덤 영역 마스킹 (CutOut)
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
# albumentations — 고급 증강 라이브러리 (pip install albumentations)import albumentations as A
from albumentations.pytorch import ToTensorV2
aug = A.Compose([
A.RandomCrop(224, 224),
A.HorizontalFlip(p=0.5),
A.OneOf([ # 셋 중 하나만 적용
A.GaussNoise(var_limit=(10, 50)),
A.GaussianBlur(blur_limit=3),
A.MotionBlur(blur_limit=3),
], p=0.3),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2(),
])
GlobalAveragePooling vs Flatten:Flatten은 특성맵을 1차원으로 펴서 파라미터가 폭증하고 과적합 위험이 큽니다.
GlobalAveragePooling2D는 각 특성맵의 평균을 내어 파라미터 수를 대폭 줄이며,
현대 CNN에서 표준으로 사용합니다.
CNN 핵심 정리
개념
설명
권장 설정
커널 크기
필터 크기, 작을수록 깊이로 보상
3×3 (가장 범용)
패딩
입력 테두리 0 채움
"same" / padding=1 (크기 유지)
스트라이드
필터 이동 간격
1 (특징 추출), 2 (다운샘플링)
풀링
공간 다운샘플링
MaxPool 2×2 또는 stride=2 Conv
정규화
학습 안정화
BatchNorm (Conv 직후)
활성화
비선형성 도입
ReLU (기본), GELU (Transformer계열)
출력 풀링
특성맵→벡터 변환
GlobalAveragePooling (Flatten 대신)
필터 수 패턴
깊어질수록 증가
32→64→128→256 (2배씩)
RNN/LSTM (시퀀스 데이터)
순환 신경망(Recurrent Neural Network)은 시퀀스 데이터(텍스트, 시계열, 음성)를 처리하는
아키텍처입니다. LSTM과 GRU는 장기 의존성 문제(기울기 소실)를 해결한 발전된 RNN 셀입니다.
RNN/LSTM/GRU 비교
셀 타입
게이트
파라미터
장점
단점
SimpleRNN
없음
적음
간단, 빠름
기울기 소실 심함, 장기 의존성 불가
LSTM
forget, input, output (3개)
많음
장기 의존성 학습, 가장 범용
느림, 메모리 사용 높음
GRU
reset, update (2개)
중간
LSTM과 유사 성능, 더 빠름
매우 긴 시퀀스에서 LSTM보다 약간 열세
LSTM/GRU — Keras 구현
from tensorflow import keras
from tensorflow.keras import layers
# 텍스트 분류 (감정 분석)
model = keras.Sequential([
layers.Embedding(10000, 128), # 어휘 10000개 → 128차원 벡터
layers.LSTM(128, dropout=0.2), # 마지막 타임스텝 출력만 반환
layers.Dense(1, activation="sigmoid") # 이진 분류
])
# 양방향 (Bidirectional) — 과거+미래 문맥 모두 활용
model = keras.Sequential([
layers.Embedding(10000, 128),
layers.Bidirectional(layers.LSTM(64)), # 출력 차원: 64×2 = 128
layers.Dense(1, activation="sigmoid")
])
# 다층 (Stacked) LSTM — return_sequences=True로 모든 타임스텝 전달
model = keras.Sequential([
layers.Embedding(10000, 128),
layers.LSTM(128, return_sequences=True, dropout=0.2), # (batch, seq, 128)
layers.LSTM(64, dropout=0.2), # (batch, 64)
layers.Dense(1, activation="sigmoid")
])
# GRU — LSTM보다 경량, 비슷한 성능
model = keras.Sequential([
layers.Embedding(10000, 128),
layers.GRU(64, return_sequences=True),
layers.GRU(32),
layers.Dense(1, activation="sigmoid")
])
# 시계열 예측 (Sequence-to-One)
model = keras.Sequential([
layers.LSTM(64, input_shape=(30, 5)), # 30 타임스텝, 5 특성
layers.Dense(1) # 다음 값 예측 (회귀)
])
# 시계열 다중 스텝 예측 (Sequence-to-Sequence)
model = keras.Sequential([
layers.LSTM(64, return_sequences=True, input_shape=(30, 5)),
layers.LSTM(32, return_sequences=True),
layers.TimeDistributed(layers.Dense(1)) # 각 타임스텝에 Dense 적용
])
주의:return_sequences=True(Keras) / output(PyTorch)는 모든 타임스텝의 출력을 반환합니다.
다층 RNN에서 중간 레이어는 반드시 모든 타임스텝을 다음 레이어에 전달해야 합니다.
PyTorch LSTM의 기본 입력은 (seq, batch, features)이며, batch_first=True로
(batch, seq, features)로 변경하는 것을 권장합니다.
RNN/LSTM 핵심 정리
작업
권장 구조
비고
텍스트 분류
Embedding → BiLSTM → Dense
양방향이 단방향보다 우수
감성 분석
Embedding → BiGRU → Dense
GRU가 더 빠르고 비슷한 성능
시계열 예측 (단일)
LSTM → Dense(1)
마지막 타임스텝 사용
시계열 예측 (다중)
LSTM(return_seq) → TimeDistributed
모든 타임스텝 예측
기계 번역
Encoder-Decoder + Attention
Transformer로 대체 추세
음성 인식
BiLSTM + CTC Loss
가변 길이 정렬 불필요
일반 권장
Transformer
긴 시퀀스, 병렬화 우수
Transformer (자연어 처리)
Transformer는 Self-Attention 메커니즘 기반으로 시퀀스를 병렬 처리하는 아키텍처입니다.
RNN의 순차 처리 한계를 극복하여 NLP(BERT, GPT), 비전(ViT), 음성 등 거의 모든 분야에서
최고 성능을 달성하고 있습니다. "Attention Is All You Need" (2017) 논문에서 제안되었습니다.
Self-Attention 메커니즘
# Scaled Dot-Product Attention## Attention(Q, K, V) = softmax(Q·K^T / √d_k) · V## Q (Query) : "내가 찾는 것" — 각 토큰이 다른 토큰에게 묻는 질의# K (Key) : "내가 가진 것" — 각 토큰이 제공하는 키# V (Value) : "내 내용" — 실제 전달할 정보# √d_k : 스케일링 — 내적 값이 커지면 softmax 기울기 소실 방지import torch
import torch.nn as nn
import torch.nn.functional as F
import math
defscaled_dot_product_attention(Q, K, V, mask=None):
d_k = Q.size(-1)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k) # (batch, heads, seq, seq)if mask is notNone:
scores = scores.masked_fill(mask == 0, float("-inf")) # 마스킹 위치 -∞
attn_weights = F.softmax(scores, dim=-1) # 어텐션 가중치
output = torch.matmul(attn_weights, V) # 가중 합return output, attn_weights
# pip install transformers datasets# ① Pipeline — 가장 간단한 사용법 (추론 전용)from transformers import pipeline
# 감성 분석
classifier = pipeline("sentiment-analysis")
result = classifier("This movie is amazing!")
# [{'label': 'POSITIVE', 'score': 0.9998}]# 텍스트 생성
generator = pipeline("text-generation", model="gpt2")
output = generator("The future of AI", max_length=50)
# 질의응답
qa = pipeline("question-answering")
result = qa(question="What is PyTorch?", context="PyTorch is a deep learning framework.")
# 요약
summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
summary = summarizer(long_text, max_length=130, min_length=30)
# ② 직접 모델 로드 — 토크나이저 + 모델from transformers import AutoTokenizer, AutoModel, AutoModelForSequenceClassification
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
# 토크나이징
inputs = tokenizer(
"Hello, how are you?",
padding=True,
truncation=True,
max_length=128,
return_tensors="pt"# "pt"=PyTorch, "tf"=TensorFlow, "np"=NumPy
)
# {'input_ids': tensor([[101, 7592, ...]]), 'attention_mask': tensor([[1, 1, ...]])}
outputs = model(**inputs)
logits = outputs.logits # (batch, num_labels)# ③ 파인튜닝 (Hugging Face Trainer)from transformers import TrainingArguments, Trainer
from datasets import load_dataset
dataset = load_dataset("imdb")
deftokenize_fn(examples):
return tokenizer(examples["text"], truncation=True, max_length=256)
tokenized = dataset.map(tokenize_fn, batched=True)
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
learning_rate=2e-5,
weight_decay=0.01,
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
fp16=True, # 혼합 정밀도
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized["train"],
eval_dataset=tokenized["test"],
)
trainer.train()
주요 Transformer 모델 비교
모델
구조
특징
주요 용도
BERT
Encoder
양방향, MLM+NSP 사전학습
분류, QA, NER
GPT-2/3/4
Decoder
단방향, 자기회귀 생성
텍스트 생성, 대화
T5
Encoder-Decoder
모든 NLP를 text-to-text로 통합
번역, 요약, QA
BART
Encoder-Decoder
노이즈 제거 자기부호화기
요약, 번역
RoBERTa
Encoder
BERT 학습 전략 최적화
분류, NER (BERT 대체)
DeBERTa
Encoder
분리된 어텐션 + 향상된 디코딩
SuperGLUE SOTA
ViT
Encoder
이미지를 패치로 분할하여 처리
이미지 분류
Whisper
Encoder-Decoder
음성→텍스트 범용 모델
음성 인식, 번역
LLaMA
Decoder
효율적 오픈소스 LLM
텍스트 생성, 추론
Transformer 하이퍼파라미터 가이드:d_model = 임베딩 차원 (256~1024),
num_heads = 어텐션 헤드 수 (d_model의 약수, 보통 8~16),
d_ff = FFN 히든 차원 (보통 d_model × 4),
num_layers = 블록 수 (6~12).
학습률은 워밍업(warmup) 후 감소하는 스케줄이 필수적입니다.
Transformer 핵심 정리
구성 요소
역할
핵심 포인트
Self-Attention
토큰 간 관계 모델링
O(n²) 복잡도, Flash Attention으로 개선
Multi-Head
다양한 관계 패턴 포착
헤드별 독립 어텐션 후 결합
Positional Encoding
순서 정보 부여
sin/cos (고정) 또는 학습 가능
Feed-Forward
비선형 변환
각 위치에 독립 적용 (MLP)
LayerNorm
학습 안정화
Pre-LN (블록 앞) 이 더 안정적
잔차 연결
그래디언트 흐름 보존
모든 서브레이어에 적용
Causal Mask
미래 토큰 가리기
디코더/자기회귀 모델 필수
Cross-Attention
인코더-디코더 연결
Q=디코더, K/V=인코더
전이 학습 (Transfer Learning)
전이 학습은 대규모 데이터로 사전학습된 모델의 지식을 새로운 작업에 재활용하는 기법입니다.
적은 데이터와 짧은 학습 시간으로 높은 성능을 달성할 수 있어, 실무에서 모델을 처음부터
학습하는 경우보다 전이 학습을 사용하는 경우가 훨씬 더 많습니다.
전이 학습 전략
전략
방법
데이터 양
적합한 경우
Feature Extraction
사전학습 레이어 동결, 헤드만 학습
적음 (<1000)
새 데이터가 원본과 유사
Fine-tuning (전체)
모든 레이어를 낮은 lr로 학습
많음 (>10000)
데이터 충분, 도메인 다름
Gradual Unfreezing
뒤쪽 레이어부터 순차적 해동
중간
안정적인 파인튜닝
Discriminative LR
레이어별 다른 학습률
중간~많음
앞쪽=낮은lr, 뒤쪽=높은lr
이미지 전이 학습 — Keras
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.applications import EfficientNetV2B0, ResNet50V2
# ① Feature Extraction (특성 추출) — 사전학습 레이어 동결
base_model = EfficientNetV2B0(weights="imagenet", include_top=False, input_shape=(224, 224, 3))
base_model.trainable = False# 모든 레이어 동결
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dropout(0.3),
layers.Dense(128, activation="relu"),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer=keras.optimizers.Adam(1e-3), # 높은 lr OK (헤드만 학습)
loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(train_ds, epochs=10, validation_data=val_ds)
# ② Fine-tuning — 헤드 학습 후 일부 레이어 해동
base_model.trainable = Truefor layer in base_model.layers[:-30]: # 앞쪽 레이어는 동결 유지
layer.trainable = False
model.compile(optimizer=keras.optimizers.Adam(1e-5), # 매우 낮은 lr (기존 가중치 보호)
loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(train_ds, epochs=10, validation_data=val_ds)
# 사용 가능한 사전학습 모델# keras.applications.EfficientNetV2B0~L — 효율적, 최신 권장# keras.applications.ResNet50V2 — 범용, 검증된 성능# keras.applications.MobileNetV3Large — 모바일/엣지용# keras.applications.ConvNeXtTiny — 최신 순수 CNN
이미지 전이 학습 — PyTorch
import torch
import torch.nn as nn
from torchvision import models
# ① Feature Extraction
model = models.efficientnet_v2_s(weights="IMAGENET1K_V1")
# 모든 레이어 동결for param in model.parameters():
param.requires_grad = False# 분류 헤드 교체
model.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(model.classifier[1].in_features, 10)
)
# 교체된 헤드만 학습됨 (requires_grad=True 기본)
optimizer = torch.optim.Adam(model.classifier.parameters(), lr=1e-3)
# ② Fine-tuning — Discriminative Learning Ratefor param in model.parameters():
param.requires_grad = True# 전체 해동
optimizer = torch.optim.AdamW([
{"params": model.features[:5].parameters(), "lr": 1e-6}, # 앞쪽: 아주 낮은 lr
{"params": model.features[5:].parameters(), "lr": 1e-5}, # 중간: 낮은 lr
{"params": model.classifier.parameters(), "lr": 1e-3}, # 헤드: 높은 lr
], weight_decay=0.01)
# ResNet 헤드 교체
resnet = models.resnet50(weights="IMAGENET1K_V2")
resnet.fc = nn.Linear(resnet.fc.in_features, 10) # 마지막 FC 교체# ViT (Vision Transformer) 전이 학습
vit = models.vit_b_16(weights="IMAGENET1K_V1")
vit.heads = nn.Linear(vit.heads[0].in_features, 10)
NLP 전이 학습 — Hugging Face
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# 사전학습 모델 + 분류 헤드
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=3)
# Layer-wise Learning Rate Decay (LLRD)from transformers import AdamW
no_decay = ["bias", "LayerNorm.weight"]
optimizer_grouped = []
lr = 2e-5
decay_rate = 0.9for i inrange(12, -1, -1): # BERT 12층 → 0층
layer_lr = lr * (decay_rate ** (12 - i))
layer_params = [(n, p) for n, p in model.named_parameters() if f"layer.{i}."in n]
optimizer_grouped.append({
"params": [p for n, p in layer_params ifnotany(nd in n for nd in no_decay)],
"lr": layer_lr, "weight_decay": 0.01
})
# 한국어 모델 예시# tokenizer = AutoTokenizer.from_pretrained("klue/bert-base")# model = AutoModelForSequenceClassification.from_pretrained("klue/bert-base", num_labels=5)
사전학습 모델 비교 (이미지)
모델
파라미터
ImageNet Top-1
추론 속도
적합한 용도
MobileNetV3-Small
2.5M
67.5%
매우 빠름
모바일, 엣지
EfficientNet-B0
5.3M
77.1%
빠름
경량 서버
ResNet-50
25M
80.9%
보통
범용 베이스라인
EfficientNetV2-S
21M
84.2%
보통
서버 권장
ConvNeXt-Tiny
29M
82.1%
보통
현대 CNN
ViT-B/16
86M
84.5%
느림
대규모 데이터
EfficientNetV2-L
118M
85.7%
느림
최고 정확도
전이 학습 주의사항:
Feature extraction 단계에서는 높은 학습률(1e-3)을 사용해도 되지만,
fine-tuning 단계에서는 반드시 낮은 학습률(1e-5~1e-4)을 사용하세요 — 사전학습 가중치가 파괴됩니다.
입력 이미지의 전처리(정규화 값)는 사전학습 시 사용된 것과 동일해야 합니다.
데이터가 매우 적으면(<500) fine-tuning 대신 feature extraction만 사용하세요.
전이 학습 핵심 정리
단계
동결 범위
학습률
에포크
1. Feature Extraction
사전학습 전체 동결
1e-3
5~10
2. Fine-tuning (뒤쪽)
앞쪽 동결, 뒤쪽 30% 해동
1e-5
5~15
3. Fine-tuning (전체)
전체 해동 (선택)
1e-5~1e-6
3~5
NLP Fine-tuning
전체 해동 + LLRD
2e-5 (상위) ~2e-6 (하위)
3~5
초급자 가이드 - 딥러닝이란 무엇인가?
딥러닝을 일상적인 비유로 이해해보세요:
신경망 = 뇌의 모방: 인간의 뇌처럼 여러 층(layer)의 뉴런이 연결되어 정보를 처리합니다. "깊은(deep)" 학습이라는 이름은 이 층이 여러 개라는 뜻입니다.
학습 과정: (1) 데이터를 보여줌 → (2) 예측함 → (3) 정답과 비교 → (4) 틀린 만큼 조정 → 반복. 이것이 "역전파(Backpropagation)"의 핵심입니다.
CNN = 이미지 전문가: 사진에서 특징(모서리, 패턴, 형태)을 단계적으로 인식합니다.
RNN/LSTM = 시퀀스 전문가: 문장의 앞뒤 문맥을 기억하며 처리합니다.
Transformer = 현대 AI의 핵심: ChatGPT, DALL-E 등 최신 AI의 기반 구조입니다.
시작 팁: 수학이 부족해도 걱정하지 마세요. Keras는 model.add(Dense(64, activation="relu"))처럼 블록 쌓기 방식으로 모델을 만들 수 있습니다.
중급자 가이드 - 딥러닝 실전 개발 전략
효과적인 딥러닝 모델 개발을 위한 실전 가이드:
전이 학습 우선: 처음부터 모델을 훈련하지 마세요. ResNet, BERT, GPT 등 사전 훈련된 모델을 Fine-tuning하는 것이 일반적으로 더 빠르고 좋은 성능을 냅니다.
데이터 증강(Augmentation): 이미지 회전, 반전, 크롭 등으로 훈련 데이터를 인위적으로 늘립니다. torchvision.transforms나 albumentations를 활용합니다.
학습률(Learning Rate): 가장 중요한 하이퍼파라미터입니다. Learning Rate Finder로 최적값을 찾고, Cosine Annealing이나 OneCycleLR 스케줄러를 사용합니다.
과적합 방지: Dropout, Early Stopping, Weight Decay(L2 정규화), 데이터 증강을 조합하여 과적합을 방지합니다.
Mixed Precision Training: torch.cuda.amp로 FP16/FP32 혼합 정밀도를 사용하면 메모리 50% 절약, 학습 속도 2-3배 향상이 가능합니다.
고급자 가이드 - 대규모 모델 학습과 최적화
프로덕션 수준의 딥러닝 시스템을 위한 고급 기법:
분산 학습: DistributedDataParallel(DDP)로 멀티 GPU 학습을 수행합니다. DeepSpeed나 FSDP(Fully Sharded Data Parallel)로 수백억 파라미터 모델도 학습 가능합니다.
모델 압축: 양자화(Quantization, INT8/INT4), 지식 증류(Knowledge Distillation), 가지치기(Pruning)로 모델 크기를 줄이고 추론 속도를 높입니다.
LLM Fine-tuning: LoRA(Low-Rank Adaptation), QLoRA로 대규모 언어 모델을 효율적으로 미세조정합니다. PEFT(Parameter-Efficient Fine-Tuning) 라이브러리가 이를 쉽게 만들어줍니다.
추론 최적화: TensorRT, ONNX Runtime, vLLM으로 추론 속도를 최적화합니다. KV Cache, Flash Attention, Speculative Decoding 등의 기법이 사용됩니다.
RAG(Retrieval Augmented Generation): LLM에 외부 지식을 결합하여 환각(hallucination)을 줄이고 최신 정보를 반영합니다. LangChain, LlamaIndex 등의 프레임워크를 활용합니다.
15. 참고 자료 & 다음 단계
초급중급고급
추천 학습 리소스
파이썬 학습의 핵심은 공식 문서를 기준으로 삼고, 실습 프로젝트를 통해 지식을 체화하는 것입니다. 다음은 신뢰할 수 있는 주요 학습 리소스입니다.
functools는 함수형 프로그래밍을 지원하는 고차 함수 모듈입니다. @lru_cache는 함수 결과를 캐싱하여 반복 호출을 최적화하고, partial은 함수의 일부 인자를 미리 고정하며, reduce는 시퀀스를 하나의 값으로 누적 계산합니다.
from functools import lru_cache, partial, reduce
# 메모이제이션@lru_cachedeffib(n):
if n < 2: return n
return fib(n-1) + fib(n-2)
# 부분 함수
add5 = partial(lambda x, y: x + y, 5)
add5(3) # 8# 누적 계산
reduce(lambda x, y: x + y, [1,2,3,4]) # 10
collections 예제
collections는 내장 컨테이너(dict, list, set, tuple)를 확장한 특수 자료구조를 제공합니다. Counter는 요소 빈도를 세고, defaultdict는 존재하지 않는 키에 기본값을 자동 생성하며, deque는 양쪽 끝에서 O(1) 삽입/삭제가 가능한 큐입니다.
from collections import Counter, defaultdict, OrderedDict
# 카운터
Counter("hello") # Counter({'l':2, 'h':1, 'e':1, 'o':1})# 기본값 딕셔너리
d = defaultdict(int)
d["count"] += 1# 오류 없이 1
PEP 주요 규칙
PEP(Python Enhancement Proposal)는 파이썬의 새로운 기능, 코딩 스타일, 프로세스 등을 제안하는 공식 문서입니다. 특히 PEP 8(코딩 스타일 가이드)은 파이썬 커뮤니티의 사실상 표준이며, 모든 파이썬 개발자가 숙지해야 할 핵심 문서입니다.
PEP 8: Python 스타일 가이드
PEP 20: Python 철학 (import this)
PEP 257: Docstring 규칙
PEP 484: 타입 힌트
PEP 498: f-string
PEP 572: Walrus 연산자 (:=)
PEP 622: Structural Pattern Matching
초급자 학습 전략 - 파이썬 시작하기
프로그래밍이 처음이라면 다음 순서로 학습하세요:
1주차: 변수, 자료형, print() 익히기. 간단한 계산기 프로그램 만들기.
2주차: if/else 조건문, for/while 반복문. 구구단, 가위바위보 게임 만들기.
3주차: 리스트, 딕셔너리 다루기. 학생 성적 관리 프로그램 만들기.
4주차: 함수 정의와 호출. 기존 코드를 함수로 리팩토링하기.
5-6주차: 파일 입출력, 예외 처리. 메모장 프로그램, 주소록 프로그램 만들기.
핵심 조언: "이론 30% + 실습 70%" 비율을 유지하세요. 강의를 듣고 끝나는 것이 아니라, 직접 코드를 타이핑하고 수정해보는 것이 가장 효과적입니다. 에러 메시지를 두려워하지 마세요 - 에러가 곧 학습의 기회입니다!
중급자 학습 전략 - 실무 역량 키우기
기초 문법을 넘어 실무 개발자로 성장하기 위한 전략:
프로젝트 기반 학습: 웹 스크래퍼, REST API 서버, 데이터 분석 대시보드, CLI 도구 등 실제 동작하는 프로젝트를 완성하세요.
오픈소스 기여: GitHub에서 관심 있는 프로젝트의 이슈를 해결하며 실전 경험을 쌓으세요. "good first issue" 태그를 찾아보세요.
코드 품질 도구: black(포매터), ruff(린터), mypy(타입 체커), pytest(테스트)를 프로젝트에 도입하세요.
디자인 패턴: 싱글턴, 팩토리, 옵저버, 데코레이터 패턴 등을 파이썬에 맞게 구현해보세요.