python
uv
- uv init : 프로젝트 생성 (디렉토리 안에서)
- uv python install 3.12 : ~/.local/share/uv/python/ 디렉토리에 파이썬 저장 (venv 사용하는 방향으로 진행)
- uv venv --python 3.12 : 폴더 내에서 가상환경 파이썬 설치 (~/.local/share/uv/python/ 에 base python 저장)
- uv export -o requirements.txt : export
- uv add -r requirements.txt : 라이브러리 설치
- uv sync : 가상환경 동기화 (pyproject.toml 과 uv.lock 파일을 기준)
- uv run <파일명.py>
트러블슈팅
- fastAPI의 uvicorn 실행 시 host를 0,0,0,0 으로 설정해야 아웃바운드 가능 (localhost/127,0,0,0은 불가)
컨벤션
- file명은 스네이크
- class는 파스칼
- def(function)은 스네이크
- 언더스코어(
_)- 언더스코어 변수는 private로 외부에서 접근하지 말라는 권유 제한자(직접 가져다 쓰는 것은 가능)
- 더블 언더스코어(
__)- 더블 언더스코어는 private 접근 제한자
- @staticmethod
@staticmethod는 클래스와 관련된 기능을 논리적으로 묶어두면서도, 해당 기능이 인스턴스나 클래스의 상태에 의존하지 않는 경우에 사용하는 방식이며, 클래스의 상속과 오버이딩 사용가능
- @classmethod
- @abstractmethod
프로세스 관리
- python 코드 내에서 프로세스 생성
- 에러와 같은 예외처리 가능 (process.join() 모든 프로세스가 종료될 때까지 대기)
subprocess
- 별개의 프로세스 생성
- capture_output=True: 표준 출력(stdout)과 표준 오류(stderr)를 캡처합니다.)
- check=True: 실행 중 오류가 발생하면 CalledProcessError 예외를 발생시킵니다
데코레이터
- wrapper를 통해 데코레이터 기능 사용 :: role 확인, 캐싱(호출결과 상수화), log
생성자/반복자
- 데이터 스트림 처리
컨텍스트 관리자 및 with
- 파일 스트림과 같은 리소스를 관리
메타클래스
- 인스턴스가 생성되지 전 생성 시점에 클래스 동작 변경
몽키 패칭
- 소스 코드를 직접 변경하지 않고 런타임 시 모듈이나 클래스를 동적으로 변경하며, 타 라이브러리를 테스트하거나 수정하는 데 사용
유형 힌트 및 정적 분석 (PEP 484)
- 타입 힌트
from typing import Optional, Union
def divide(dividend: float, divisor: float) -> Optional[float]:
if divisor == 0.0:
return None
return dividend / divisor
def log(level: Union[str, int], message: str) -> None:
if isinstance(level, int):
print(f"Level {level}: {message}")
else:
print(f"{level.upper()}: {message}")
- 정적분석
- mypy : 유형 주석을 기반으로 Python 코드 분석
asyncio : 비동기
메모리관리
- 참조 카운팅은 순환 참조시를 감지 못하여 메모리 누수 발생
- gc를 사용하여 gabage collector 동작 제어
- gc.collect(generation=2){컬렉션에서 살아남은 개체는 전 세대로 이동}/ gc.set_threshold()
descriptors
- 액세스, 설정 또는 삭제될 때 사용자 정의 동작을 허용
- 클래스 속성이 엑세스, 설정 또는 삭제 될 때 사용자 정의 동작을 허용
__get__(self, obj, type=None): 소유자 클래스(type) 또는 인스턴스(obj)의 속성을 가져오기 위해 호출__set__(self, obj, value):obj인스턴스의 속성을 새 값으로 설정하기 위해 호출__delete__(self, obj): 인스턴스에서 속성을 삭제하기 위해 호출- 데코레이터 : @property(속성 객체를 생성하고 반환), @classmethod(클래스 전용), @staticmethod
기본
- closures : 영역 밖에서 호출된 함수의 변값과 레퍼런스를 복사하고 값을에 엑세스
def outer_func():
message = 'Hi'
def inner_func():
print(message)
return inner_func
my_func = outer_func()
my_func() # Hi 가 출력됨
- Iterator : list, tuple 등 객체들이 갖는 iter() 함수를 통해 Iterator 구현 가능
mytuple = (1,2,3)
myit = iter(mytuple)
print(next(myit)) #1
print(next(myit)) #2
print(next(myit)) #3
- generator : iterator로 미리 요소를 리스트에 만들어 놓고 꺼내는 것이 아니라 yield 및 next() 함수를 통해 필요할 때마다 접근/생성하여 요소를 가져옴 (메모리 효율)
def yield_test():
for i in range(5):
yield i
print(i,'번째 호출!')
print(type(yield_test())) # <class 'generator'> yield 가 사용되면 generator 가 되나 봄
t = yield_test()
print(t.__next__()) # 0
print(t.__next__()) # 0 번째 호출! 1
print(t.__next__()) # 1 번째 호출! 2
print(t.__next__()) # 2 번째 호출! 3
print(t.__next__()) # 3 번째 호출! 4
#print(t.__next__()) #Error
fastAPI
- async function은 즉시 실행되지 않고, coroutine 객체를 반환. coroutine 객체는 await 키워드를 사용하여 실제로 실행
import asyncio
async def fetch_data():
print("Fetching data...")
await asyncio.sleep(2) # 대기
print("Data fetched")
return {"data": "some data"}
async def main():
print("Main function start")
data = await fetch_data()
print(data)
print("Main function end")
- chunk 값 단위 설정
- 메모리 크기에 따라 스위칭 될 chuck 단위 설정
- 전송 형식에 따라 단위 조정 (이미지일 경우 큰 청크가 유리/ log는 라인단위 청크가 유리)
- 네트워크 대역폭에 따라 전송효율을 높힐 수 있으며, 낮은 대역폭에는 청크 단위를 축소하여 지연율 낮춤
- CPU 성능에 따라 I/O작업이라면 청크를 크게 잡아서 효율적으로, 연산작업이라면 청크 단위를 작게 잡아서 병렬처리 특화로 설정
import time
def process_data_in_chunks(file_path, chunk_size):
start_time = time.time()
with open(file_path, 'rb') as file:
while chunk := file.read(chunk_size):
# 청크 데이터를 처리하는 로직
process_chunk(chunk)
end_time = time.time()
return end_time - start_time
def process_chunk(chunk):
# 청크 데이터를 처리 코드
pass
file_path = 'large_data_file.csv'
chunk_sizes = [1024 * 1024, 2 * 1024 * 1024, 5 * 1024 * 1024] # 1MB, 2MB, 5MB
best_chunk_size = None
best_time = float('inf')
for size in chunk_sizes:
elapsed_time = process_data_in_chunks(file_path, size)
print(f'Chunk size: {size} bytes, Time taken: {elapsed_time} seconds')
if elapsed_time < best_time:
best_time = elapsed_time
best_chunk_size = size
print(f'Optimal chunk size: {best_chunk_size} bytes')
pytorch
- 모든 신경망 모델은 nn.Module 하위 클래스
- super(Net, self).init():
- 부모 클래스 nn.Module의 생성자를 호출하여 사위 클래스의 초기화 코드 실행
- self.conv1 = nn.Conv2d(1, 32, 3, 1):
- 첫번째 합성곱 레이어(conv1)은 1개의 입력채널(흑백이미지)를 받아들이고, 32개의 출력 채널을 생성하며, 3x3 크기의 커널을 사용, 스트라이드는 1
- 합성곱 레이어의 역할은 입력 이미지를 여러 개의 필터(또는 커널)을 사용하여 여러개의 특징 맵으로 변환
- 3x3 크기의 32개 필터를 사용하여 입력 이미지를 스캔
- 28x28 크기의 흑백이미지를 26x26 크기의 출력 채널 생성(패딩 없는 경우, 입력크기(28) - 커널크기(3) + 1 = 26)
- 흑백은 하양, 검정으로 입력채널이 1개, RGB는 파랑, 노랑, 빨강으로 입력채널이 3개
- 3x3 커널의 사이즈 기준으로 이미지를 검색
- 1 스트라이드는 1픽셀씩 이동하여 검출
- self.conv2 = nn.Conv2d(32, 64, 3, 1):
- 두 번째 합성곱 레이어(conv2)는 32개의 채널을 받아들이고, 64개의 출력채널을 생성하며, 3x3 크기의 커널을 사용하며, 스트라이드는 1
- 첫 번째 합성곱 레이어의 출력이 32개 채널이므로 두 번째 합성곱 레이어의 입력 채널은 32개
- 64개의 필터를 사용하여 입력 채넣을 스캔하고, 64개의 출력 채널을 생성
- self.dropout1 = nn.Dropout2d(0.25):
- 2D 드롭아웃 레이어로 확률은 0.25로써 과적합을 방지하기 위해 일부 뉴런을 출력을 0으로 만듬
- 학습 중 뉴런의 출력을 무작위로 0으로 설정하는 기법 :: 특정 뉴런에 과도하게 의존하지 않도록 과적합 방지
- 예를 들어 64개의 채널 중에 무작위로 25% 뉴런을 비활성화하여 다른 뉴런 선택을 통해 다양한 조합으로 학습하고, 특정 뉴런에 의존되는 현상을 방지, 무작위로 선택된 뉴런 집합은 서로 다른 서브 네트워크를 형성하여 앙상블 효과로 일반화 능력이 향상
- self.fc1 = nn.Linear(9216, 128):
- 입력크기는 9216이고, 출력크기는 128
- nn.Linear는 완전연결(fully connected) 계층을 정의하는 클래스로 입력과 출력 사이의 모든 뉴런이 연결되는 계층, 이는 주로 합성곱 계층에서 추출된 특징을 결합하여 최종적으로 분류하거나 회귀 수행에 사용
- nn.Linear(9216, 128) : 입력 뉴런 수 9216은 완전 연결 계층이 받을 입력(입력 뉴런 또는 특징) 크기 :: 이전 계층 출력이 (batch_size, 64, 12, 12)라면 (batch_size, 64x12x12 = 9216)으로 평탄화
- 이 계층의 입력 뉴런과 출력 뉴런 사이의 가중치(weight)와 편향(bias)를 학습
- 신경망 후반부로 합성곱 계층에서 추출한 저수준 특징을 결합하여 고수준의 정보를 생성하는 역할
- 가중치와 편향
import torch
import torch.nn as nn
# 완전 연결 계층 정의
fc1 = nn.Linear(9216, 128)
# 가중치와 편향 확인
print(f'가중치 행렬 크기: {fc1.weight.shape}') # (128, 9216)
print(f'편향 벡터 크기: {fc1.bias.shape}') # (128)
# 가중치와 편향 초기화 (기본적으로 PyTorch는 Xavier 초기화를 사용함)
nn.init.xavier_uniform_(fc1.weight)
nn.init.zeros_(fc1.bias)
# 가중치와 편향 값 출력
print(f'초기화된 가중치: {fc1.weight}')
print(f'초기화된 편향: {fc1.bias}')
- 가중치 : 입력 뉴런의 값을 특정 비율로 조정하여 출력 뉴런에 영향을 미침. 가중치들을 조정하여 입력 데이터에서 중요한 특징을 추출하고 패턴을 학습
- 가중치 행렬 : (128(출력뉴런 수), 9216(입력 뉴런 수)수
- 편향 : 각 출력 뉴런에 더해지는 추가적인 파라미터, 신경망이 특정 입력 패턴에 대해 더 유연하게 반응 가능, 입력 값이 모두 0일 때도 출력이 특정값을 가질 수 있도록하여 모델이 데이터를 일반화 하는데 도움
- 평향 벡터 : (128(출력 뉴런의 수))
-
역전파(backpropagation) : 학습 과정에서 신경망은 손실 함수(loss function)을 최소화하기 위해 가중치와 편향 조절. 이를 위해 역전파 알고리즘을 사용하여 손실 함수의 기울기(gradient)를 계산하고, 기울기에 따라 가중치와 편향 업데이트. 순전파(forward propagation)로 예측결과와 실제 값을 얻고 손실(loss) 계산, 손실 함수를 각 가중치에 대해 미분하여 기울기(gradient) 구함, 역전파 알고리즘을 사용하여 출력 층에서 입력 층으로 기울기를 전파(이 과정에서 체인 룰(chain rule)을 사용하여 기울기 계산), 계산된 기울기를 사용하여 가중치를 업데이트(주로 경사하강법)
-
경사하강법
- 배치 경사 하강법 : 전체 데이터 셋을 사용하여 기울기 계산(비용 많이 듬)
- 확률적 경사 하강법 : 각 포인트에 대해 가중치 업데이트
- 미니 배치 경사 하강법 : 데이터를 작은 배치로 나누어 각 배치에 대해 가중치를 업데이트
-
최적화 알고리즘
- 모멘텀(momentum) : 기울기 계산에 이전 기울기의 영향도를 고려하여 업데이트 :: 지수 가중 편균(데이터의 이동 평균을 구할 때 오랜된 데이터가 미치는 영향을 지수적으로 감쇠)
- 아다그라드(adagrad) : 학습률을 각 파라미터에 대해 개별적으로 조정, 자주 업데이트 되는 파라미터에 대해서는 학습률이 줄어듬
- RMSProp: 아다그라드의 단점을 보완하여 기울기의 제곱으로 이동 평균을 계로
- adam(adaptive moment estimation) : 모멘텀과 RMSProp을 결합한 방법
-
진화 알고리즘
- 유전 알고리즘 (genetic algorithms) : 선택, 교차, 돌연변이 과정을 통해 최적의 가중치를 찾음
- 진화 전략(evolution strategies) : 파라미터 공간에서 무작위 샘플링과 선택을 통해 최적화
-
준 뉴턴 방법 (quasi-newton methods)
- BFGS(broyden-fletcher-goldfarb-shanno) : 이차 미분 행렬(헤시안 행렬)의 근사값을 사용하여 최적화 수행
- L-BFGS(lmited-memory BFGS) : 대규모 문제에 적합한 BGFS 변형 버전
-
def forward(self, x):
- forward pass(순전파) 정의로 입력 x가 네트워크를 통과하는 과정
-
x = self.conv1(x) :
- 입력 x가 첫 번째 합성곱 레이어 통과
-
x = F.relu(x) :
- ReLU 활성화 함수를 적용하여 비선형성을 추가
- 입력 텐서 x에 비선형 변환을 적용
- 입력이 양수이면 그 값을 그대로 반환, 음수이면 0을 반환
-
ReLU (rectified linear unit) : active function
- 비선형 함수로 신경망에 비선형성을 추가하여 복잡한 함수와 패턴을 학습할 수 있게 함. 이는 다층 퍼셉트론(MLP)와 같은 깊은 신경망에서 중요
- 단순히 최대값을 계산하는 함수로 빠르고 효율적
- 음수 입력을 0으로 만들면 신경망의 희소성을 증가시켜 모델이 간결하고 일반화되도록 도움을 주나 음수일 경우 기울기가 0이 되어 뉴런이 죽을 수 있다는 단점(단점을 보완한 leaky ReLU, parametic ReLU(PReLU) 변형 함수 존재
- leaky ReLU : 음수 임력에 대해 작은 기울기를 부여하는 죽는 뉴런 문제 완화
- 비선형성의 필요성
- 선형 활성화 함수를 사용하는 신경망은 입력과 출력 사이의 관계를 선형 함수로만 학습할 수 있으므로 출력은 여전히 입력의 선형 조합일 뿐이므로 비선형성이 필요(복잡한 함수와 패턴 학습)
-
시그모이드/ 탄젠트 함수
import torch
import torch.nn.functional as F
# 임의의 입력 텐서 생성
x = torch.tensor([-1.0, 0.0, 1.0, 2.0])
# 시그모이드 활성화 함수 적용
sigmoid_y = torch.sigmoid(x)
# tanh 활성화 함수 적용
tanh_y = torch.tanh(x)
print(f'입력 텐서: {x}')
print(f'시그모이드 적용 후 텐서: {sigmoid_y}')
print(f'tanh 적용 후 텐서: {tanh_y}')
- 시그모이드 함수 : 입력 값을 0과 1사이의 값으로 맵핑하는 s자 형태의 비선형 함수(확률 표현할 때 유용)로 미분이 가능하여 기울기 계산에 사용될 수 있음, 극단적인 값에서 매우 평평해지기 때문에 역전파 과정에서 기울기가 매우 작아져서 학습이 거의 이루어지지 않음, 항상 양수라서 학습을 어렵게 만들 수 있음
- 탄젠트(하이퍼볼릭 탄젠트; tanh function) : 1과 1 사이의 값으로 맵핑하는 비선형 함수 :: 시그모이드 함수보다 범위가 크고 0을 중심으로 대칭적이라 학습을 쉽게 만듬
- x = F.max_pool2d(x, 2) : 2x2 크기의 최대 풀링을 적용하여 특징 맵의 공간 차원을 줄임
- 최대 풀링 이유
- 공간 차원 축소(spatial dimension reduction) : 입력 데이터 공간 차원을 줄임으로 입력 데이터 크기가 반으로 줄어들어, 신경망을 더 깊어질 수 있게 하여 더 많은 계층을 쌓아 복잡한 패턴을 학습할 수 있음
- 특징 맵의 불변성 (invariance) : 최대 풀링은 특징 맵의 이동(translation)이나 회정(rorations)에 대한 불변성 제공. 이동, 회전을 해도 중요한 특징 유지
- 과적합 방지(overfitting reduction) : 파라미터 수를 줄여 과적합 방지 도움. 공간 차원을 줄이면서도 중요한 정보는 유지하므로 모델의 복잡도를 줄이고 일반화 성능을 향상
- 특징 강조(feature emphasis) : 최대 풀링은 각 풀링 창에서 가장 강한 활성화를 선택하므로, 가장 중요한 특징 강조. 중요한 패턴이 다음 계층에서 더 학습 잘되도록 도움
- 최대 풀링 이유
import torch
import torch.nn.functional as F
# 임의의 입력 텐서 생성 (배치 크기: 1, 채널 수: 1, 높이: 4, 너비: 4)
x = torch.tensor([[[[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0],
[13.0, 14.0, 15.0, 16.0]]]])
print(f'입력 텐서: \n{x}')
# 2x2 최대 풀링 적용
y = F.max_pool2d(x, 2)
print(f'최대 풀링 적용 후 텐서: \n{y}')
- 입력 텐서의 크기는 (1,1,4,4)이며 이는 배치 크기 1, 채널 수 1, 높이 4, 너비 4를 의미
- 2x2 최대 풀링을 적용하면, 각 2x2 영역에서 최대값이 선택(첫 번째 2x2 영역: {1,2,5,6}-> 6, 두번째 2x2 영역 : {3,4,7,8}->8)
- x = torch.flatten(x, 1): 데이터를 평탄화하여 1차원 벡터로 변환. 이는 완전 연결 레이어에 입력하기 위함
- output = F.log_softmax(x, dim=1) :
- log softmax 함수를 적용하여 출력이 확률분포가 되도록 설정
- 주어진 입력 벡터의 요소들을 0과 1사이의 확률 값으로 변환하고 이들의 합이 1이 되도록하는 소프트맥스 함수의 출력을 log변환
- 로그를 취함으로써 입력값이 크거나 작을 때 수치적 불안정을 완화
- 크로스 엔트로피 손실 함수
- 확률 분포 간의 차이를 측정
dataset과 batch_size
import torch
from torch.utils.data import DataLoader, TensorDataset
# 예제 데이터셋 생성
data = torch.randn(100, 1, 28, 28) # 100개의 1x28x28 이미지
targets = torch.randint(0, 10, (100,)) # 100개의 레이블 (0에서 9 사이의 정수)
# 텐서 데이터셋 생성
dataset = TensorDataset(data, targets)
# 배치 크기를 32로 설정한 데이터 로더 생성
batch_size = 32
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
# 데이터 로더를 통해 배치 단위로 데이터 순회
for batch_data, batch_targets in dataloader:
# 여기서 batch_data와 batch_targets는 각각 batch_size만큼의 데이터를 포함
print(f'Batch data shape: {batch_data.shape}, Batch targets shape: {batch_targets.shape}')
-
sample model
- ANN : 인공신경망(Artificial Neural Network) :: 사람의 신경망 구조와 원리 모방 :: 어떠한 임계값(threshold)을 넘어서면 결과 신호를 전달하는 과정
- CNN : 합성곱신경망 convolution neural network :: 합성곱 신경망 :: 이미지 특징점을 찾아냄
- RNN : 순환신경망 recurrent neural network :: 시계열, 데이터 강도 데이터를 받아서 순차적으로 과거와 현재의 학습을 연결 :: 순차적으로 처리하므로 앞의 데이터와 뒤의 데이터가 멀리 떨어져 있을시 오역 발생 (재귀적으로 활용, 언어, 음성인식, 주식 동향)
- transformer : self-attention :: 병렬처리가 어려워 연산속도가 느린 RNN 아키텍쳐 극복
- DNN : deep neural network :: 분류 잘함
- GAN : 상대적 적대 신경망 :: 주어진 데이터의 확률 분포를 예측하는 모델
-
sample parameter
- optimization : 모델의 파라미터 조정을 통해 loss가 작은 점을 찾는 것
• Preprocessor: 입력 데이터를 모델에 적합한 형태로 변환. (예: 스케일링, 형태 변환, 데이터 증강 등)
• Postprocessor: 모델의 출력을 해석 가능한 형태로 변환. (예: 소프트맥스, 클래스 레이블 변환, 임계값 적용 등)
캐싱
- @lru_cache 데코레이터
- 함수 호출 결과를 캐싱하여 반복적인 계산 방지
- f-string
- 문자열 포맷팅 최적화
수치 데이터 처리
- if n > 20: return math.factorial(n) :: 큰 숫자의 경우 python 내장 함수를 사용하여 더 효율적으로 계산
파이썬 가상환경 라이브러리 삭제
- pip freeze | xargs pip uninstall -y