일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Github
- conda
- 파이썬
- ai 캠프
- django
- ollama
- pytorch
- 머신러닝
- mifare
- lightsail
- finpilot
- sLLM
- pandas
- 티스토리챌린지
- streamlit
- aws
- Jupyterlab
- 로컬 런타임
- EC2
- ai_캠프
- djangorestframework
- chromeextention
- seaborn
- team_project
- ai캠프
- Python
- 오블완
- mysql
- ML
- 정치기 필기
- Today
- Total
greatsangho의 이야기
캠프58일차 - 합성공(CNN) 신경망 개요(CNN 개요, 합성곱 계층, 풀링 계층) 본문
CNN(Convolutional Neural Networ, 합성곱 신경망)은 이미지나 영상처리에 사용되는 딥러닝 모델이다. 입력 데이터의 공간적 구조를 보존하면서 특징을 추출하고 학습한다. 주요 구성 요소는 합성곱 계층(Convolutional Layer), 풀링 계층(Pooling Layer) 그리고 완전 연결 계층(Fully Connected Layer)이다.
Convolutional Layer는 이미지의 특징을 추출하는 역할을 한다. 합성곱 연산은 필터나 커널을 사용하여 입력 데이터와 연산을 수행하여 특징 맵(feature map)을 생성한다.
필터(Filter)는 작은 행렬로 이미지에 적용되어 패턴을 추출한다.
스트라이드(Stride)는 필터가 이미지를 스캔할 때 한번에 이동하는 픽셀 수로 스트라이드가 클수록 출력이 작아진다.
패딩(Padding)은 입력 데이터의 경계 부분을 처리하기 위해 추가하는 픽셀로 출력 크기를 유지하기 위해 사용한다.
Pooling Layer는 합성곱 계층에서 생성된 특징 맵의 크기를 줄여주는 역할을 한다. 계산양을 줄이고 모델이 중요한 특징에 집중하도록 한다. 풀링에는 가장 큰 값을 선택하는 Max Pooling과 평균값을 출력하는 Average Pooling이 있다.
CNN은 여러개의 합성곱 계층과 풀링 계층이 반복적으로 쌓이고 최종적으로 완전 연결 계층을 통해 분류하는 것이 특징이다.
CTC 손실(Connectionist Temporal Classification Loss)
시퀀스 데이터를 학습할 때 입력 시퀀스와 출력 시퀀스의 길이가 다를 수 있는 문제를 해결하기 위해 설계된 손실함수, 정렬 정보 없이도 모델을 학습할 수 있도록 한다.
- 음성인식, OCR 등에서 널리 사용
- 입력 'hello'
- 'h-e-l-l-o'
- 'hh-e-ll-o' 여기서 - 는 블랙크 토큰, 입력을 스킵하는 역할
- 출력을 정답시퀀스로 매핑 가능한 모든 경로의 확률의 합을 최대화
CTC는 blank 토큰을 사용하여 각 타입 스탭에 아무런 출력을 하지 않을 수 있음을 허용한다. 이를 통해 다양하고 유연한 정렬이 가능하며, 출력 간 길이 차이를 해결한다.
CTC 손실은 forward-backward 알고리즘을 사용하며 모든 경로에 대한 확률을 계산한다.
import kagglehub
path = kagglehub.dataset_download("fournierp/captcha-version-2-images")
print("Path to dataset files:", path)
캐글에서 캡챠 이미지 데이터를 불러온다.
# 갭차 이미지 시각화
import matplotlib.pyplot as plt
img_data = plt.imread(images[0])
plt.imshow(img_data), img_data.shape, images[0] # 파일명이 정답
png 형태의 이미지를 보기 위해서는 plt.imread()를 이용하여 png를 리스트 형태로 바꾼 뒤 plt.imshow()를 이용한다.
# 데이터 셋 정의 : 소문자와 숫자만 정답으로 이용 Dataset
# __init__, __len__ __getitem__
from PIL import Image
class Capcha(Dataset): # 학습용과 평가용을 나누는 기준 train 매개변수
def __init__(self,imgPath, train=True):
# 소문자와 숫자만 정답으로 이용
# 불러올 이미지 파일의 경로
# 문자와 숫자를 고유번호로 치환
def get_seq(self, line):
# train, test의 길이 반환
def __len__(self):
def __getitem__(self, idx):
# png파일을 RGB파일로 변환
# 파일이름에서 확장자를 제거
# 정답 문자열을 BOW의 순열로 변환
# 파이토치는 채널이 가장 앞에 와야 함
Capcha 클래스를 정의하고 다음과 같이 __init__, get_seq, __len__, __getitem__에 대해 정의한다.
앞서 정의 한 get_BOW를 이용하여 ascii_lowercase + digits를 정답 데이터로 저장한다. self.BOW = get_BOW(self.corpus) 형태로 저장한다.
이미지를 경로에서 불러오기 위해서는 glob을 이용한다.
from glob import glob
images = glob('/root/.cache/kagglehub/datasets/fournierp/captcha-version-2-images/versions/2/**/*.png', recursive=True)
images[0]
# /root/.cache/kagglehub/datasets/fournierp/captcha-version-2-images/versions/2/samples/w75w8.png
images[0].split('/')[-1].split('.')[0]
/**/*.png, recursive=True 옵션을 통해 하위 폴더 내에서 png로 끝나는 파일 경로를 불러온다. 그 다음 필요한 파일명만 추출하기 위해서 split()을 이용한다. 이제 이미지 파일의 개수를 len(self.imgfiles)를 이용해 구한 다음 이를 list slicing을 이용해 나누어 준다.
def get_seq(self, line):
# BOW (bag of word)
import numpy as np
from torch.utils.data.dataset import Dataset
def get_BOW(corpus): # 말뭉치
# 공백문자를 <pad> ->0
BOW = {
'<pad>':0
}
for letter in corpus:
if letter not in BOW:
BOW[letter] = len(BOW)
return BOW
get_BOW('hello') # {'<pad>': 0, 'h': 1, 'e': 2, 'l': 3, 'o': 4}
캡챠 이미지에 속하는 문자들을 저장할 말뭉치를 정의한다. hello를 넣으면 다음과 같이 딕셔너리로 저장된 변환되는 것을 확인할 수 있다.
import string
string.punctuation, string.ascii_letters, string.ascii_lowercase, string.digits
string에는 다양한 문자 조합을 가지고 있다. punctuation은 특수문자를, ascii_letters는 영어 대소문자, ascii_lowercase는 영어 소문자, 그리고 string.digits는 0에서9까지의 숫자를 가진다.
이제 label 리스트를 준비하고 숫자와 문자를 고유한 딕셔너리 번호로 치환해준다.
def __len__(self): 부분에서는 trainset과 testset의 길이를 반환한다.
def __getitem__(self, idx)에서는 이미지를 RGB로 변환한다. png는 4개의 레이어로 이루어져 있기 때문에 ResNet과 같이 RGB 기준으로 3개 레이어로 이루어진 모델에 바로 적용할 수 없다. 이는 import PIL import Image에서 .convert('RGB')를 이용해 RGB 형식으로 변경하여 해결할 수 있다.
class BasicBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=(3, 5), stride=(2, 1)):
...
BasicBlock을 정의한다. ResNet에서 Conv2D-BatchNorm2d-ReLU-Conv2D-BatchNorm2D와 처음 입력 값의 Conv2D 레이어가 합쳐지는 residual connection 뒤 다시 ReLU를 진행하는 구조가 반복된다. 이 반복구조를 BasicBlock 클래스로 만들어준다.
class CRNN(nn.Module):
def __init__(self, output_size):
super(CRNN, self).__init__()
...
CRNN이란 CNN과 RNN을 결합한 구조로 이미지에서 시계열 정보를 추출하고 문자를 예측하는 모델이다. 캡챠에서 앞 부분부터 차례대로 이미지를 자른다음 이를 인식하여 순서대로 반환하기 때문에 시계열 정보를 추출한다.
CNN 층은 이미지의 공간적 특징을, GRU(Gated Recurrent Unit)층은 시계열 정보를 학습한다. 마지막 FC(Fully Connected) 층에서 문자를 분류하고, CTC 손실은 log_softmax를 적용한다. CTC 손실함수가 로그 확률값을 사용하기 때문이다. 로그 확률값을 사용하는 이유는 각 클래스에 대한 확률값이 매우 작은 값들을 곱할 때 모델이 불안정해지는 점과, 이를 해결하면서 덧셈으로 연산이 가능해 더 안정적이기 때문이다.
이와 같은 이유로 torch.nn의 CTCLoss 함수는, 입력값이 로그 확률값이다.
for epoch in range(200):
iterator = tqdm.tqdm(loader)
for data, label in iterator:
optim.zero_grad()
preds = model(data.to(device))
preds_size = torch.IntTensor([preds.size(0)] * 8).to(device)
target_len = torch.IntTensor([len(txt) for txt in label]).to(device)
loss = nn.CTCLoss(blank=0)(preds, label.to(device), preds_size, target_len)
loss.backward()
optim.step()
CTC 손실 함수를 이용하여 예측과 정답의 차이를 계산하고 연전파를 통해 모델을 업데이트 한다.
torch.save(model.state_dict(), "/content/drive/MyDrive/model/CRNN2.pth")
# 모델 로드
import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = CRNN(len(dataset.BOW)).to(device)
model.load_state_dict(torch.load('/content/drive/MyDrive/model/CRNN2.pth'))
모델은 다음과 같이 학습된 가중치를 저장할 수 있다. 다시 모델을 로드하기 위해서는 다음과 같이 미리 정의된 모델을 다시 로드하고, 가중치를 불러온다. 모델 자체를 불러오는 것보다 가볍고 빠르게 불러올 수 있다.
# 중복글자는제거
def extract_text(pred):
prev,curr,result = '','',''
bow_lists = list(dataset.BOW.keys())
for id, num in enumerate([ i.item() for i in pred]):
if num != 0:
if id == 0:
prev = curr = bow_lists[num]
else:
curr = bow_lists[num]
if curr != prev :
result += curr
prev = curr
print(result)
나머지 인식되지 않은 문자들은 0으로 저장되므로 0을 제외한 글자를 순서대로 저장하는 함수를 만들어준다.
with torch.no_grad():
testDataset = Capcha(imeages,train=False)
testloader = DataLoader(testDataset,batch_size=1)
for idx,(input_tensor, label) in enumerate(testloader):
if idx == 4:
break
preds = model(input_tensor.to(device))
# 가장높은 확률을 가지는 글자를 추출
pred = torch.argmax(preds, dim=-1)
extract_text(pred)
# 시각화
import matplotlib.pyplot as plt
for idx,(input_tensor, label) in enumerate(testloader):
if idx == 4:
break
plt.subplot(2,2,idx+1)
plt.title(list(label)[0])
plt.imshow(input_tensor.squeeze(dim=0).permute(1,2,0))
가져온 데이터를 시각화하면 다음과 같으며, 잘 예측하는 것을 볼 수 있다.
'프로그래밍 > SK AI 캠프' 카테고리의 다른 글
캠프60일차 - 이미지 딥러닝 응용(Style Transfer Learning, GAN) (2) | 2024.11.20 |
---|---|
캠프59일차 - 3. 합성곱(CNN) 신경망(CNN 개요, 합성곱 계층, 풀링 계층) U-Net (1) | 2024.11.19 |
SK AI 캠프 13주차 후기 (4) | 2024.11.16 |
캠프57일차 - 특정 LLM 모델 (BERT)을 Foundation 모델로 선택하여 도메인 특화 데이터로 파인튜닝 (1) | 2024.11.15 |
캠프56일차 - 파인튜닝된 LLM 모델의 성능 평가 방법 (1) | 2024.11.14 |