Ver3.0 ๐ค ์บก์ฐจ ์๋ ์ธ์ ํ๋ก๊ทธ๋จ ์์ค ์์ ์ ๋ณต

๐ค ์บก์ฐจ ์๋ ์ธ์ ํ๋ก๊ทธ๋จ ์์ค ์์ ์ ๋ณต
์ด๋ฏธ์ง ์ฒ๋ฆฌ ๊ธฐ์ ๋ก ํ์ด๋ณด๋ ์บก์ฐจ ์ธ์์ ์ธ๊ณ ๐
๐ฏ ์บก์ฐจ๊ฐ ๋ญ๊ธธ๋ ์ด๋ ๊ฒ ๋ณต์กํด?
์ผ, ์ธํฐ๋ท ํ๋ค ๋ณด๋ฉด ์ง์ง ์ง์ฆ ๋๋ ๊ฒ ํ๋ ์์์? ๋ฐ๋ก ๊ทธ ์บก์ฐจ(CAPTCHA)๋ผ๋ ๊ฑฐ! ๐ค"์ ๋ ๋ก๋ด์ด ์๋๋๋ค" ์ฒดํฌํ๊ณ , ์ ํธ๋ฑ ์ฐพ๊ณ , ํก๋จ๋ณด๋ ์ฐพ๊ณ ... ๊ฐ๋์ "์ด๊ฒ ์ ํธ๋ฑ์ธ๊ฐ ํ์งํ์ธ๊ฐ?" ๊ณ ๋ฏผํ๋ค๊ฐ ํ๋ฆฌ๊ธฐ๋ ํ๊ณ ๋ง์ด์ผ. ๊ทผ๋ฐ ์ด๊ฒ ์ ์กด์ฌํ๋์ง ์์?
CAPTCHA๋ Completely Automated Public Turing test to tell Computers and Humans Apart์ ์ฝ์์ผ. ์ฝ๊ฒ ๋งํ๋ฉด "๋ ์ฌ๋์ด์ผ? ๋ก๋ด์ด์ผ?" ๊ตฌ๋ถํ๋ ํ ์คํธ์ง. ๐ค
์น์ฌ์ดํธ ์ ์ฅ์์๋ ๋ด(bot)๋ค์ด ์๋์ผ๋ก ํ์๊ฐ์ ํ๊ฑฐ๋, ํฐ์ผ์ ์น์ธ์ดํ๊ฑฐ๋, ์คํธ์ ๋ฟ๋ฆฌ๋ ๊ฑธ ๋ง์์ผ ํ๊ฑฐ๋ . ๊ทธ๋์ ์ฌ๋๋ง ํ ์ ์๋ ๋ฌธ์ ๋ฅผ ๋ด๋ ๊ฑฐ์ผ.
2. ์ด๋ฏธ์ง ๊ธฐ๋ฐ ์บก์ฐจ: ํน์ ๋ฌผ์ฒด๊ฐ ์๋ ์ด๋ฏธ์ง๋ฅผ ์ ํํ๋ ๋ฐฉ์ (๊ตฌ๊ธ reCAPTCHA)
3. ์ค๋์ค ์บก์ฐจ: ์์ฑ์ผ๋ก ๋ค๋ ค์ฃผ๋ ์ซ์๋ ๋ฌธ์๋ฅผ ์ ๋ ฅ
4. ํผ์ฆ ์บก์ฐจ: ํผ์ฆ ์กฐ๊ฐ์ ๋ง์ถ๋ ๋ฐฉ์
5. ํ๋ ๊ธฐ๋ฐ ์บก์ฐจ: ๋ง์ฐ์ค ์์ง์ ํจํด์ ๋ถ์ํ๋ ๋ฐฉ์
๊ทธ๋์ ์ค๋์ ์บก์ฐจ ์๋ ์ธ์ ํ๋ก๊ทธ๋จ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง, ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๊ธฐ์ ์ ์ด๋ป๊ฒ ํ์ฉ๋๋์ง ์์ ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๋ฌผ๋ก ์ด๊ฑด ๊ต์ก ๋ชฉ์ ์ด๊ณ , ์ค์ ๋ก ์ ์ฉํ๋ฉด ์ ๋ผ! โ ๏ธ
๐ฌ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ๊ธฐ๋ณธ ์๋ฆฌ
์บก์ฐจ๋ฅผ ์ธ์ํ๋ ค๋ฉด ๋จผ์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ(Image Processing)์ ๋ํด ์์์ผ ํด. ์ปดํจํฐ๊ฐ ์ด๋ฏธ์ง๋ฅผ ์ด๋ป๊ฒ "๋ณด๋์ง" ์ดํดํ๋ ๊ฒ ํต์ฌ์ด๊ฑฐ๋ .๐ธ ์ปดํจํฐ๋ ์ด๋ฏธ์ง๋ฅผ ์ด๋ป๊ฒ ๋ณผ๊น?
์ฐ๋ฆฌ๊ฐ ๋ณด๋ ์ด๋ฏธ์ง๋ ์ฌ์ค ์๋ง์ ํฝ์ (pixel)๋ค์ ์งํฉ์ด์ผ. ๊ฐ ํฝ์ ์ RGB(๋นจ๊ฐ, ์ด๋ก, ํ๋) ๊ฐ์ ๊ฐ์ง๊ณ ์๊ณ , ์ด ๊ฐ๋ค์ ์กฐํฉ์ผ๋ก ์์์ด ๋ง๋ค์ด์ง์ง.
์๋ฅผ ๋ค์ด๋ณผ๊ฒ:
โข ํฐ์ = RGB(255, 255, 255)
โข ๊ฒ์์ = RGB(0, 0, 0)
โข ๋นจ๊ฐ์ = RGB(255, 0, 0)
์บก์ฐจ ์ด๋ฏธ์ง๋ ๋ณดํต ์ด๋ฐ ํฝ์ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ํด์ ์ฌ๋์ ์ฝ์ ์ ์์ง๋ง ์ปดํจํฐ๋ ์ฝ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค์ด์ ธ ์์ด. ๊ธ์๋ฅผ ์๊ณก์ํค๊ฑฐ๋, ๋ฐฐ๊ฒฝ์ ๋ ธ์ด์ฆ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, ์ ์ ๊ทธ์ด๋๋ ์์ผ๋ก ๋ง์ด์ผ. ๐จ
์บก์ฐจ๋ฅผ ์ธ์ํ๊ธฐ ์ ์ ์ด๋ฏธ์ง๋ฅผ "๊นจ๋ํ๊ฒ" ๋ง๋๋ ์์ ์ด ํ์ํด. ์ด๊ฑธ ์ ์ฒ๋ฆฌ๋ผ๊ณ ํ๋๋ฐ, ๋ค์๊ณผ ๊ฐ์ ๊ณผ์ ๋ค์ด ํฌํจ๋ผ:
1๏ธโฃ ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ (Grayscale Conversion)
์ปฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ํ๋ฐฑ์ผ๋ก ๋ฐ๊พธ๋ ๊ฑฐ์ผ. ์๋๊ณ ? ์์ ์ ๋ณด๋ ์บก์ฐจ ์ธ์์ ๋ณ๋ก ์ค์ํ์ง ์๊ฑฐ๋ . ์คํ๋ ค ๋ฐ์ดํฐ๋ฅผ ๋จ์ํ์์ผ์ ์ฒ๋ฆฌ ์๋๋ฅผ ๋์ผ ์ ์์ด.
import cv2
import numpy as np
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
image = cv2.imread('captcha.png')
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# ๊ฒฐ๊ณผ ์ ์ฅ
cv2.imwrite('gray_captcha.png', gray)2๏ธโฃ ์ด์งํ (Binarization)
์ด๋ฏธ์ง๋ฅผ ์์ ํ ํ๋ฐฑ(0 ๋๋ 255)์ผ๋ก ๋ง๋๋ ๊ฑฐ์ผ. ๊ธ์๋ ๊ฒ์์, ๋ฐฐ๊ฒฝ์ ํฐ์ ์ด๋ฐ ์์ผ๋ก ๋ช ํํ๊ฒ ๊ตฌ๋ถํ๋ ๊ฑฐ์ง.
๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๋ฐฉ๋ฒ์ด Otsu's Binarization์ด์ผ. ์ด๊ฑด ์๋์ผ๋ก ์ต์ ์ ์๊ณ๊ฐ(threshold)์ ์ฐพ์์ ์ด์งํํด์ฃผ๋ ์๊ณ ๋ฆฌ์ฆ์ด์ผ. ๐
# Otsu's ์ด์งํ
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# ๋๋ ์ ์ํ ์ด์งํ (Adaptive Thresholding)
adaptive = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)3๏ธโฃ ๋ ธ์ด์ฆ ์ ๊ฑฐ (Noise Reduction)
์บก์ฐจ ์ด๋ฏธ์ง์๋ ์๋์ ์ผ๋ก ์ ์ด๋ ์ ๊ฐ์ ๋ ธ์ด์ฆ๊ฐ ๋ง์ด ๋ค์ด๊ฐ ์์ด. ์ด๊ฑธ ์ ๊ฑฐํด์ผ ๊ธ์๋ฅผ ์ ๋๋ก ์ธ์ํ ์ ์์ง.
์ฃผ๋ก ์ฌ์ฉํ๋ ๊ธฐ๋ฒ๋ค:
โข ๊ฐ์ฐ์์ ๋ธ๋ฌ(Gaussian Blur): ์ด๋ฏธ์ง๋ฅผ ๋ถ๋๋ฝ๊ฒ ๋ง๋ค์ด์ ๋ ธ์ด์ฆ ๊ฐ์
โข ๋ฏธ๋์ ํํฐ(Median Filter): ์๊ธ-ํ์ถ ๋ ธ์ด์ฆ ์ ๊ฑฐ์ ํจ๊ณผ์
โข ๋ชจํด๋ก์ง ์ฐ์ฐ(Morphology): ์นจ์(erosion)๊ณผ ํฝ์ฐฝ(dilation)์ผ๋ก ๋ ธ์ด์ฆ ์ ๊ฑฐ
# ๊ฐ์ฐ์์ ๋ธ๋ฌ ์ ์ฉ
blurred = cv2.GaussianBlur(binary, (5, 5), 0)
# ๋ฏธ๋์ ํํฐ
median = cv2.medianBlur(binary, 5)
# ๋ชจํด๋ก์ง ์ฐ์ฐ (Opening = Erosion + Dilation)
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1)
๐ฏ ๋ฌธ์ ๋ถ๋ฆฌ (Character Segmentation)
์ด๋ฏธ์ง๋ฅผ ๊นจ๋ํ๊ฒ ๋ง๋ค์์ผ๋ฉด, ์ด์ ๊ฐ ๊ธ์๋ฅผ ํ๋ํ๋ ๋ถ๋ฆฌํด์ผ ํด. ์ด ๊ณผ์ ์ ์ธ๊ทธ๋ฉํ ์ด์ (Segmentation)์ด๋ผ๊ณ ํ๋๋ฐ, ์๊ฐ๋ณด๋ค ๊น๋ค๋ก์ด ์์ ์ด์ผ. ๐๐ ์ค๊ณฝ์ ๊ฒ์ถ (Contour Detection)
OpenCV์ findContours ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฏธ์ง์์ ๊ฐ์ฒด์ ์ค๊ณฝ์ ์ ์ฐพ์ ์ ์์ด. ๊ฐ ๊ธ์๊ฐ ํ๋์ ์ค๊ณฝ์ ์ผ๋ก ์ธ์๋๋ ๊ฑฐ์ง.
# ์ค๊ณฝ์ ์ฐพ๊ธฐ
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# ๊ฐ ์ค๊ณฝ์ ์ ๋ฐ์ด๋ฉ ๋ฐ์ค๋ก ๋ณํ
char_boxes = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
# ๋๋ฌด ์๊ฑฐ๋ ํฐ ์ค๊ณฝ์ ์ ๋
ธ์ด์ฆ๋ก ํ๋จํ๊ณ ์ ์ธ
if w > 10 and h > 10 and w < 100 and h < 100:
char_boxes.append((x, y, w, h))
# x ์ขํ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌ (์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ ์์)
char_boxes = sorted(char_boxes, key=lambda box: box[0])๐ ๋ฌธ์ ์์ญ ์ถ์ถ
๋ฐ์ด๋ฉ ๋ฐ์ค ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ๊ฐ ๊ธ์๋ฅผ ๊ฐ๋ณ ์ด๋ฏธ์ง๋ก ์๋ผ๋ด๋ ๊ฑฐ์ผ. ์ด๋ ์ฃผ์ํ ์ ์ ๊ธ์๋ค์ด ๋ถ์ด์๊ฑฐ๋ ๊ฒน์ณ์์ ์ ์๋ค๋ ๊ฑฐ์ง.
characters = []
for i, (x, y, w, h) in enumerate(char_boxes):
# ๊ธ์ ์์ญ ์ถ์ถ
char_img = binary[y:y+h, x:x+w]
# ํฌ๊ธฐ ์ ๊ทํ (์: 28x28)
char_img = cv2.resize(char_img, (28, 28))
characters.append(char_img)
# ๋๋ฒ๊น
์ฉ ์ ์ฅ
cv2.imwrite(f'char_{i}.png', char_img)์ด๊ฑด ์ข ๋ ์ ๊ตํ ๋ฐฉ๋ฒ์ธ๋ฐ, ์ด๋ฏธ์ง์ ๊ฐ ํ๊ณผ ์ด์ ํฝ์ ํฉ์ ๊ณ์ฐํด์ ๊ธ์์ ๊ฒฝ๊ณ๋ฅผ ์ฐพ๋ ๊ฑฐ์ผ.
# ์์ง ํฌ์ (๊ฐ ์ด์ ๊ฒ์ ํฝ์
๊ฐ์ ํฉ์ฐ)
vertical_projection = np.sum(binary == 0, axis=0)
# ์ํ ํฌ์ (๊ฐ ํ์ ๊ฒ์ ํฝ์
๊ฐ์ ํฉ์ฐ)
horizontal_projection = np.sum(binary == 0, axis=1)
# ํฌ์๊ฐ์ด 0์ธ ์ง์ ์ ๊ธ์ ๊ฒฝ๊ณ๋ก ํ๋จ
# (์ค์ ๋ก๋ ์๊ณ๊ฐ์ ์ค์ ํด์ ์ฌ์ฉ)
import matplotlib.pyplot as plt
plt.plot(vertical_projection)
plt.title('Vertical Projection')
plt.show()์ด ๋ฐฉ๋ฒ์ ๊ธ์ ๊ฐ๊ฒฉ์ด ๋ช ํํ ๋ ํนํ ํจ๊ณผ์ ์ด์ผ. ํฌ์ ๊ทธ๋ํ์์ ๊ณจ์ง๊ธฐ(valley) ๋ถ๋ถ์ด ๊ธ์ ์ฌ์ด์ ๊ณต๋ฐฑ์ด๊ฑฐ๋ ! ๐
๐ค ๋ฌธ์ ์ธ์ (OCR - Optical Character Recognition)
์, ์ด์ ๋ถ๋ฆฌ๋ ๊ธ์๋ค์ ์ค์ ๋ก "์ฝ์ด์ผ" ํ๋ ๋จ๊ณ์ผ. ์ด๊ฒ ๋ฐ๋ก OCR(๊ดํ ๋ฌธ์ ์ธ์)์ด์ง! ๐ค๐ ์ ํต์ ์ธ OCR ๋ฐฉ๋ฒ๋ค
1๏ธโฃ Tesseract OCR
๊ตฌ๊ธ์ด ๊ฐ๋ฐํ ์คํ์์ค OCR ์์ง์ด์ผ. ๊ฐ์ฅ ์ ๋ช ํ๊ณ ๋ง์ด ์ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ํ๋์ง.
import pytesseract
from PIL import Image
# Tesseract ๊ฒฝ๋ก ์ค์ (Windows)
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
# ์ด๋ฏธ์ง์์ ํ
์คํธ ์ถ์ถ
image = Image.open('captcha.png')
text = pytesseract.image_to_string(image, config='--psm 7') # PSM 7: ๋จ์ผ ํ
์คํธ ๋ผ์ธ
print(f"์ธ์๋ ํ
์คํธ: {text}")๊ทผ๋ฐ ์์งํ ๋งํ๋ฉด, Tesseract๋ ์ผ๋ฐ ๋ฌธ์ ์ค์บ์๋ ์ข์ง๋ง ์๊ณก๋ ์บก์ฐจ ์ด๋ฏธ์ง์๋ ์ฑ๋ฅ์ด ๋ณ๋ก์ผ. ๐ ๊ทธ๋์ ์ ์ฒ๋ฆฌ๋ฅผ ์์ฒญ ์ ํด์ค์ผ ํ๊ฑฐ๋, ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์จ์ผ ํด.
2๏ธโฃ ํ ํ๋ฆฟ ๋งค์นญ (Template Matching)
๋ฏธ๋ฆฌ ์ค๋น๋ ๊ธ์ ํ ํ๋ฆฟ๊ณผ ๋น๊ตํด์ ๊ฐ์ฅ ์ ์ฌํ ๊ฑธ ์ฐพ๋ ๋ฐฉ์์ด์ผ. ๊ฐ๋จํ์ง๋ง ํจ๊ณผ์ ์ผ ์ ์์ด!
# ํ
ํ๋ฆฟ ์ด๋ฏธ์ง๋ค ๋ก๋
templates = {}
for char in '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ':
template = cv2.imread(f'templates/{char}.png', 0)
templates[char] = template
# ๊ฐ ๋ถ๋ฆฌ๋ ๊ธ์์ ํ
ํ๋ฆฟ ๋น๊ต
def match_template(char_img, templates):
best_match = None
best_score = -1
for char, template in templates.items():
# ํฌ๊ธฐ ๋ง์ถ๊ธฐ
resized_template = cv2.resize(template, (char_img.shape[1], char_img.shape[0]))
# ํ
ํ๋ฆฟ ๋งค์นญ
result = cv2.matchTemplate(char_img, resized_template, cv2.TM_CCOEFF_NORMED)
score = result[0][0]
if score > best_score:
best_score = score
best_match = char
return best_match, best_score
# ์ฌ์ฉ ์์
recognized_text = ""
for char_img in characters:
char, score = match_template(char_img, templates)
if score > 0.7: # ์๊ณ๊ฐ
recognized_text += char
print(f"์ธ์ ๊ฒฐ๊ณผ: {recognized_text}")์ด ๋ฐฉ๋ฒ์ ์ฅ์ ์ ๊ตฌํ์ด ๊ฐ๋จํ๊ณ , ํน์ ์บก์ฐจ ์คํ์ผ์ ํนํ์ํฌ ์ ์๋ค๋ ๊ฑฐ์ผ. ๋จ์ ์ ๊ธ์ ํฐํธ๋ ํฌ๊ธฐ๊ฐ ์กฐ๊ธ๋ง ๋ฌ๋ผ์ ธ๋ ์ธ์๋ฅ ์ด ๋จ์ด์ง๋ค๋ ๊ฑฐ์ง. ๐ญ
3๏ธโฃ ๋จธ์ ๋ฌ๋ ๊ธฐ๋ฐ ์ธ์ (KNN, SVM)
์ ํต์ ์ธ ๋จธ์ ๋ฌ๋ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด์ผ. ํน์ง(feature)์ ์ถ์ถํด์ ๋ถ๋ฅ๊ธฐ๋ฅผ ํ์ต์ํค๋ ๊ฑฐ์ง.
| ์๊ณ ๋ฆฌ์ฆ | ํน์ง | ์ฅ์ | ๋จ์ |
|---|---|---|---|
| KNN | K-์ต๊ทผ์ ์ด์ | ๊ตฌํ ๊ฐ๋จ, ํ์ต ๋น ๋ฆ | ์์ธก ๋๋ฆผ, ๋ฉ๋ชจ๋ฆฌ ๋ง์ด ์ฌ์ฉ |
| SVM | ์ํฌํธ ๋ฒกํฐ ๋จธ์ | ๊ณ ์ฐจ์ ๋ฐ์ดํฐ์ ๊ฐํจ | ๋์ฉ๋ ๋ฐ์ดํฐ์ ์ ๋๋ฆผ |
| Random Forest | ๋๋ค ํฌ๋ ์คํธ | ๊ณผ์ ํฉ ๋ฐฉ์ง, ์์ ์ | ํด์์ด ์ด๋ ค์ |
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
import numpy as np
# ๋ฐ์ดํฐ ์ค๋น (์์)
X_train = [] # ์ด๋ฏธ์ง ํน์ง ๋ฒกํฐ๋ค
y_train = [] # ๋ ์ด๋ธ (์ค์ ๊ธ์)
# ๊ฐ ๊ธ์ ์ด๋ฏธ์ง๋ฅผ 1์ฐจ์ ๋ฒกํฐ๋ก ๋ณํ
for char_img, label in training_data:
feature_vector = char_img.flatten() # 28x28 -> 784์ฐจ์ ๋ฒกํฐ
X_train.append(feature_vector)
y_train.append(label)
X_train = np.array(X_train)
y_train = np.array(y_train)
# KNN ๋ชจ๋ธ ํ์ต
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
# ์์ธก
test_char = characters[0].flatten().reshape(1, -1)
predicted_char = knn.predict(test_char)
print(f"์์ธก๋ ๊ธ์: {predicted_char[0]}")
๐ง ๋ฅ๋ฌ๋์ ํ์ฉํ ์บก์ฐจ ์ธ์
์์ฆ์ ๋ฅ๋ฌ๋์ด ๋์ธ์ง! ํนํ CNN(Convolutional Neural Network)์ ์ฌ์ฉํ๋ฉด ์บก์ฐจ ์ธ์ ์ ํ๋๋ฅผ ์์ฒญ๋๊ฒ ๋์ผ ์ ์์ด. ๐๐จ CNN์ด ๋ญ๊ธธ๋?
CNN์ ์ด๋ฏธ์ง ์ธ์์ ํนํ๋ ์ ๊ฒฝ๋ง์ด์ผ. ์ฌ๋์ ์๊ฐ ํผ์ง์ ๋ชจ๋ฐฉํด์ ๋ง๋ค์ด์ก๋๋ฐ, ์ด๋ฏธ์ง์ ํน์ง์ ์๋์ผ๋ก ํ์ตํ ์ ์์ด.
๊ธฐ๋ณธ ๊ตฌ์กฐ๋ ์ด๋:
โข Convolution Layer: ์ด๋ฏธ์ง์์ ํน์ง ์ถ์ถ (์ฃ์ง, ํ ์ค์ฒ ๋ฑ)
โข Pooling Layer: ํน์ง ๋งต ํฌ๊ธฐ ์ถ์ (์ค์ํ ์ ๋ณด๋ง ๋จ๊น)
โข Fully Connected Layer: ์ต์ข ๋ถ๋ฅ ์ํ
๐ฅ Keras/TensorFlow๋ก CNN ๊ตฌํํ๊ธฐ
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# CNN ๋ชจ๋ธ ์ ์
model = keras.Sequential([
# ์ฒซ ๋ฒ์งธ Convolution Block
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
# ๋ ๋ฒ์งธ Convolution Block
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
# ์ธ ๋ฒ์งธ Convolution Block
layers.Conv2D(64, (3, 3), activation='relu'),
# Flatten & Dense Layers
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dropout(0.5), # ๊ณผ์ ํฉ ๋ฐฉ์ง
layers.Dense(36, activation='softmax') # 0-9, A-Z = 36๊ฐ ํด๋์ค
])
# ๋ชจ๋ธ ์ปดํ์ผ
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# ๋ชจ๋ธ ์์ฝ
model.summary()โข MaxPooling2D(2,2): 2x2 ์์ญ์์ ์ต๋๊ฐ๋ง ์ ํ (ํฌ๊ธฐ ์ ๋ฐ์ผ๋ก ์ถ์)
โข Dropout(0.5): ํ์ต ์ 50% ๋ด๋ฐ์ ๋๋คํ๊ฒ ๋ (๊ณผ์ ํฉ ๋ฐฉ์ง)
โข Dense(36): 36๊ฐ ํด๋์ค(0-9, A-Z)๋ก ๋ถ๋ฅ
๐ ๋ฐ์ดํฐ์ ์ค๋น ๋ฐ ํ์ต
# ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ์ ์ฒ๋ฆฌ
import numpy as np
from sklearn.model_selection import train_test_split
# ์ด๋ฏธ์ง์ ๋ ์ด๋ธ ๋ก๋ (์์)
X = np.array([...]) # shape: (N, 28, 28)
y = np.array([...]) # shape: (N,)
# ์ ๊ทํ (0~255 -> 0~1)
X = X.astype('float32') / 255.0
# ์ฐจ์ ์ถ๊ฐ (CNN์ (height, width, channels) ํํ ํ์)
X = X.reshape(-1, 28, 28, 1)
# ํ์ต/๊ฒ์ฆ ๋ฐ์ดํฐ ๋ถ๋ฆฌ
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# ๋ชจ๋ธ ํ์ต
history = model.fit(
X_train, y_train,
epochs=20,
batch_size=32,
validation_data=(X_val, y_val),
callbacks=[
keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
keras.callbacks.ModelCheckpoint('best_model.h5', save_best_only=True)
]
)
# ํ์ต ๊ฒฐ๊ณผ ํ์ธ
print(f"์ต์ข
๊ฒ์ฆ ์ ํ๋: {max(history.history['val_accuracy']):.4f}")๐ฏ ์ค์ ์บก์ฐจ ์ธ์ ํ์ดํ๋ผ์ธ
์ด์ ๋ชจ๋ ๊ฑธ ํฉ์ณ์ ์์ ํ ์บก์ฐจ ์ธ์ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ๐ ๏ธ
class CaptchaSolver:
def __init__(self, model_path):
self.model = keras.models.load_model(model_path)
self.char_map = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def preprocess(self, image_path):
"""์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ"""
# ์ด๋ฏธ์ง ๋ก๋
img = cv2.imread(image_path)
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ด์งํ
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# ๋
ธ์ด์ฆ ์ ๊ฑฐ
kernel = np.ones((2,2), np.uint8)
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
return binary
def segment_characters(self, binary_img):
"""๋ฌธ์ ๋ถ๋ฆฌ"""
contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
char_boxes = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if 10 < w < 50 and 15 < h < 50: # ํฌ๊ธฐ ํํฐ๋ง
char_boxes.append((x, y, w, h))
# x ์ขํ ๊ธฐ์ค ์ ๋ ฌ
char_boxes = sorted(char_boxes, key=lambda b: b[0])
# ๋ฌธ์ ์ด๋ฏธ์ง ์ถ์ถ
characters = []
for x, y, w, h in char_boxes:
char_img = binary_img[y:y+h, x:x+w]
char_img = cv2.resize(char_img, (28, 28))
char_img = char_img.astype('float32') / 255.0
char_img = char_img.reshape(1, 28, 28, 1)
characters.append(char_img)
return characters
def recognize(self, image_path):
"""์บก์ฐจ ์ธ์ ๋ฉ์ธ ํจ์"""
# ์ ์ฒ๋ฆฌ
binary = self.preprocess(image_path)
# ๋ฌธ์ ๋ถ๋ฆฌ
characters = self.segment_characters(binary)
# ๊ฐ ๋ฌธ์ ์ธ์
result = ""
for char_img in characters:
prediction = self.model.predict(char_img, verbose=0)
char_idx = np.argmax(prediction)
confidence = prediction[0][char_idx]
if confidence > 0.8: # ์ ๋ขฐ๋ ์๊ณ๊ฐ
result += self.char_map[char_idx]
else:
result += "?" # ๋ถํ์คํ ๊ฒฝ์ฐ
return result
# ์ฌ์ฉ ์์
solver = CaptchaSolver('best_model.h5')
captcha_text = solver.recognize('captcha_sample.png')
print(f"์ธ์๋ ์บก์ฐจ: {captcha_text}")
โก ์ฑ๋ฅ ์ต์ ํ ๋ฐ ๊ณ ๊ธ ๊ธฐ๋ฒ
๊ธฐ๋ณธ์ ์ธ ์บก์ฐจ ์ธ์ ์์คํ ์ ๋ง๋ค์๋ค๋ฉด, ์ด์ ์ฑ๋ฅ์ ๋์ด์ฌ๋ฆด ์ฐจ๋ก์ผ! ๐ช๐ฏ ๋ฐ์ดํฐ ์ฆ๊ฐ (Data Augmentation)
ํ์ต ๋ฐ์ดํฐ๊ฐ ๋ถ์กฑํ๊ฑฐ๋ ๋ค์์ฑ์ด ๋จ์ด์ง ๋ ์ฌ์ฉํ๋ ๊ธฐ๋ฒ์ด์ผ. ๊ธฐ์กด ์ด๋ฏธ์ง๋ฅผ ๋ณํํด์ ์๋ก์ด ํ์ต ๋ฐ์ดํฐ๋ฅผ ๋ง๋๋ ๊ฑฐ์ง.
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# ๋ฐ์ดํฐ ์ฆ๊ฐ ์ค์
datagen = ImageDataGenerator(
rotation_range=10, # ๋๋ค ํ์ (-10๋ ~ +10๋)
width_shift_range=0.1, # ์ํ ์ด๋
height_shift_range=0.1, # ์์ง ์ด๋
shear_range=0.1, # ์ ๋จ ๋ณํ
zoom_range=0.1, # ํ๋/์ถ์
fill_mode='nearest' # ๋น ๊ณต๊ฐ ์ฑ์ฐ๊ธฐ
)
# ์ฆ๊ฐ๋ ๋ฐ์ดํฐ๋ก ํ์ต
model.fit(
datagen.flow(X_train, y_train, batch_size=32),
epochs=30,
validation_data=(X_val, y_val)
)๐ฅ ์์๋ธ (Ensemble) ๊ธฐ๋ฒ
์ฌ๋ฌ ๋ชจ๋ธ์ ์์ธก์ ๊ฒฐํฉํด์ ๋ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ป๋ ๋ฐฉ๋ฒ์ด์ผ. "๋ฐฑ์ง์ฅ๋ ๋ง๋ค๋ฉด ๋ซ๋ค"๋ ์๋ด์ฒ๋ผ! ๐
# ์ฌ๋ฌ ๋ชจ๋ธ ํ์ต
model1 = create_cnn_model() # CNN ๋ชจ๋ธ
model2 = create_resnet_model() # ResNet ๋ชจ๋ธ
model3 = create_vgg_model() # VGG ๋ชจ๋ธ
# ๊ฐ ๋ชจ๋ธ๋ก ์์ธก
pred1 = model1.predict(test_image)
pred2 = model2.predict(test_image)
pred3 = model3.predict(test_image)
# ํฌํ(Voting) ๋ฐฉ์์ผ๋ก ์ต์ข
์์ธก
final_pred = np.argmax(pred1 + pred2 + pred3, axis=1)
# ๋๋ ๊ฐ์ค ํ๊ท
weights = [0.4, 0.35, 0.25] # ๊ฐ ๋ชจ๋ธ์ ์ ๋ขฐ๋์ ๋ฐ๋ผ ๊ฐ์ค์น ๋ถ์ฌ
final_pred = np.argmax(weights[0]*pred1 + weights[1]*pred2 + weights[2]*pred3, axis=1)๐งช Transfer Learning (์ ์ด ํ์ต)
์ด๋ฏธ ํ์ต๋ ๋ชจ๋ธ์ ๊ฐ์ ธ์์ ์ฐ๋ฆฌ ๋ฌธ์ ์ ๋ง๊ฒ ๋ฏธ์ธ ์กฐ์ ํ๋ ๊ฑฐ์ผ. ์ฒ์๋ถํฐ ํ์ตํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๊ณ ํจ๊ณผ์ ์ด์ง!
from tensorflow.keras.applications import MobileNetV2
# ์ฌ์ ํ์ต๋ ๋ชจ๋ธ ๋ก๋ (ImageNet ๊ฐ์ค์น)
base_model = MobileNetV2(
input_shape=(28, 28, 3),
include_top=False, # ์ต์์ ๋ถ๋ฅ ๋ ์ด์ด ์ ์ธ
weights='imagenet'
)
# ๋ฒ ์ด์ค ๋ชจ๋ธ ๋๊ฒฐ (์ฒ์์๋ ํ์ตํ์ง ์์)
base_model.trainable = False
# ์๋ก์ด ๋ถ๋ฅ ๋ ์ด์ด ์ถ๊ฐ
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(36, activation='softmax')
])
# 1๋จ๊ณ: ์๋ก์ด ๋ ์ด์ด๋ง ํ์ต
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=10)
# 2๋จ๊ณ: ์ ์ฒด ๋ชจ๋ธ ๋ฏธ์ธ ์กฐ์
base_model.trainable = True
model.compile(optimizer=keras.optimizers.Adam(1e-5), loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=10)โ๏ธ ํ์ดํผํ๋ผ๋ฏธํฐ ํ๋
๋ชจ๋ธ ์ฑ๋ฅ์ ์ต๋๋ก ๋์ด์ฌ๋ฆฌ๋ ค๋ฉด ํ์ดํผํ๋ผ๋ฏธํฐ๋ฅผ ์ ์กฐ์ ํด์ผ ํด. ์ด๊ฑด ์ข ๋ ธ๊ฐ๋ค ์์ ์ด๊ธด ํ๋ฐ, ์๋ํ ๋๊ตฌ๋ฅผ ์ฐ๋ฉด ํธํด! ๐ ๏ธ
from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import GridSearchCV
def create_model(learning_rate=0.001, dropout_rate=0.5):
model = keras.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dropout(dropout_rate),
layers.Dense(36, activation='softmax')
])
model.compile(
optimizer=keras.optimizers.Adam(learning_rate),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
return model
# ๊ทธ๋ฆฌ๋ ์์น๋ก ์ต์ ํ๋ผ๋ฏธํฐ ์ฐพ๊ธฐ
model = KerasClassifier(build_fn=create_model, epochs=10, batch_size=32, verbose=0)
param_grid = {
'learning_rate': [0.001, 0.0001, 0.00001],
'dropout_rate': [0.3, 0.5, 0.7],
'batch_size': [16, 32, 64]
}
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3)
grid_result = grid.fit(X_train, y_train)
print(f"์ต์ ํ๋ผ๋ฏธํฐ: {grid_result.best_params_}")
print(f"์ต๊ณ ์ ํ๋: {grid_result.best_score_:.4f}")
๐ก๏ธ ์ค์ ๋ฌธ์ ํด๊ฒฐ ๋ฐ ๋๋ฒ๊น
์ค์ ๋ก ์บก์ฐจ ์ธ์ ํ๋ก๊ทธ๋จ์ ๋ง๋ค๋ค ๋ณด๋ฉด ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ์ ๋ถ๋ชํ๊ฒ ๋ผ. ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์์๋ณด์! ๐งโ ๋ฌธ์ 1: ๋ฌธ์ ๋ถ๋ฆฌ๊ฐ ์ ๋๋ก ์ ๋ผ์!
๊ธ์๋ค์ด ์๋ก ๋ถ์ด์๊ฑฐ๋ ๊ฒน์ณ์์ผ๋ฉด ์ธ๊ทธ๋ฉํ ์ด์ ์ด ์ด๋ ค์. ์ด๋ด ๋๋:
2. ์์ง ํฌ์ ๋ถ์: ํฝ์ ๋ฐ๋๊ฐ ๋ฎ์ ์ง์ ์ ๊ฒฝ๊ณ๋ก ํ๋จ
3. ๋ฅ๋ฌ๋ ๊ธฐ๋ฐ ์ธ๊ทธ๋ฉํ ์ด์ : YOLO๋ Mask R-CNN ๊ฐ์ ๊ฐ์ฒด ๊ฒ์ถ ๋ชจ๋ธ ์ฌ์ฉ
4. ์ฌ๋ผ์ด๋ฉ ์๋์ฐ: ๊ณ ์ ํฌ๊ธฐ ์๋์ฐ๋ฅผ ์ด๋์ํค๋ฉฐ ๊ธ์ ์ฐพ๊ธฐ
# ์นจ์ ์ฐ์ฐ์ผ๋ก ๊ธ์ ๋ถ๋ฆฌ ๊ฐ์
kernel = np.ones((2,2), np.uint8)
eroded = cv2.erode(binary, kernel, iterations=1)
# ์ดํ ์ค๊ณฝ์ ๊ฒ์ถ
contours, _ = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)โ ๋ฌธ์ 2: ์ธ์๋ฅ ์ด ๋๋ฌด ๋ฎ์์! (50% ์ดํ)
์ด๊ฑด ์ฌ๋ฌ ์์ธ์ด ์์ ์ ์์ด:
| ์์ธ | ์ฆ์ | ํด๊ฒฐ์ฑ |
|---|---|---|
| ํ์ต ๋ฐ์ดํฐ ๋ถ์กฑ | ๊ณผ์ ํฉ, ์ผ๋ฐํ ์คํจ | ๋ฐ์ดํฐ ์ฆ๊ฐ, ๋ ๋ง์ ๋ฐ์ดํฐ ์์ง |
| ์ ์ฒ๋ฆฌ ๋ถ๋ | ๋ ธ์ด์ฆ๊ฐ ๋ง์ด ๋จ์ | ์ ์ฒ๋ฆฌ ํ๋ผ๋ฏธํฐ ์กฐ์ , ๋ค์ํ ํํฐ ์๋ |
| ๋ชจ๋ธ ๋ณต์ก๋ ๋ถ์กฑ | ํ์ต ์ ํ๋๋ ๋ฎ์ | ๋ ๊น์ ๋คํธ์ํฌ, ๋ ๋ง์ ํํฐ |
| ํด๋์ค ๋ถ๊ท ํ | ํน์ ๊ธ์๋ง ์ ๋ชป ๋ง์ถค | ํด๋์ค ๊ฐ์ค์น ์กฐ์ , ์ค๋ฒ์ํ๋ง |
# ํด๋์ค ๋ถ๊ท ํ ํด๊ฒฐ ์์
from sklearn.utils.class_weight import compute_class_weight
# ํด๋์ค ๊ฐ์ค์น ๊ณ์ฐ
class_weights = compute_class_weight(
'balanced',
classes=np.unique(y_train),
y=y_train
)
class_weight_dict = dict(enumerate(class_weights))
# ํ์ต ์ ์ ์ฉ
model.fit(
X_train, y_train,
epochs=20,
class_weight=class_weight_dict # ๋ถ๊ท ํ ๋ณด์
)โ ๋ฌธ์ 3: ํ์ต์ ์ ๋๋๋ฐ ์ค์ ์์ ์ ๋ผ์!
์ด๊ฑด ์ ํ์ ์ธ ๊ณผ์ ํฉ(Overfitting) ๋ฌธ์ ์ผ. ํ์ต ๋ฐ์ดํฐ์๋ง ๋๋ฌด ์ต์ ํ๋์ด์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ชป ๋ง์ถ๋ ๊ฑฐ์ง. ๐ฐ
โข ํ์ต ์์ค์ ๊ณ์ ๊ฐ์ํ๋๋ฐ ๊ฒ์ฆ ์์ค์ ์ฆ๊ฐ
โข ํ์ต ๋ฐ์ดํฐ์ ์กฐ๊ธ๋ง ๋ค๋ฅธ ์ด๋ฏธ์ง๊ฐ ๋ค์ด์ค๋ฉด ์์ ํ ํ๋ฆผ
ํด๊ฒฐ ๋ฐฉ๋ฒ:
# 1. Dropout ๋น์จ ์ฆ๊ฐ
layers.Dropout(0.7) # 0.5์์ 0.7๋ก ์ฆ๊ฐ
# 2. L2 ์ ๊ทํ ์ถ๊ฐ
from tensorflow.keras import regularizers
layers.Dense(64, activation='relu',
kernel_regularizer=regularizers.l2(0.01))
# 3. Early Stopping ์ ์ฉ
early_stop = keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=5, # 5 ์ํฌํฌ ๋์ ๊ฐ์ ์์ผ๋ฉด ์ค๋จ
restore_best_weights=True
)
model.fit(X_train, y_train, epochs=100, callbacks=[early_stop])
# 4. ๋ฐฐ์น ์ ๊ทํ ์ถ๊ฐ
model = keras.Sequential([
layers.Conv2D(32, (3, 3), activation='relu'),
layers.BatchNormalization(), # ๋ฐฐ์น ์ ๊ทํ
layers.MaxPooling2D((2, 2)),
# ...
])๐ ๋๋ฒ๊น ํ
๊ฐ๋ฐํ๋ฉด์ ๋งํ ๋ ์ ์ฉํ ํ๋ค์ด์ผ! ๐
# 1. ์ค๊ฐ ๊ฒฐ๊ณผ ์๊ฐํ
import matplotlib.pyplot as plt
def visualize_preprocessing(image_path):
# ์๋ณธ
original = cv2.imread(image_path)
# ๊ทธ๋ ์ด์ค์ผ์ผ
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
# ์ด์งํ
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# ๋
ธ์ด์ฆ ์ ๊ฑฐ
kernel = np.ones((2,2), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# ์๊ฐํ
fig, axes = plt.subplots(1, 4, figsize=(15, 4))
axes[0].imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))
axes[0].set_title('Original')
axes[1].imshow(gray, cmap='gray')
axes[1].set_title('Grayscale')
axes[2].imshow(binary, cmap='gray')
axes[2].set_title('Binary')
axes[3].imshow(cleaned, cmap='gray')
axes[3].set_title('Cleaned')
plt.show()
# 2. ์์ธก ์ ๋ขฐ๋ ํ์ธ
predictions = model.predict(test_images)
for i, pred in enumerate(predictions):
top3_idx = np.argsort(pred)[-3:][::-1] # ์์ 3๊ฐ
print(f"์ด๋ฏธ์ง {i}:")
for idx in top3_idx:
print(f" {char_map[idx]}: {pred[idx]*100:.2f}%")
# 3. ํผ๋ ํ๋ ฌ(Confusion Matrix) ๋ถ์
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
y_pred = model.predict(X_test)
y_pred_classes = np.argmax(y_pred, axis=1)
cm = confusion_matrix(y_test, y_pred_classes)
plt.figure(figsize=(12, 10))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')
plt.show()
# ์ด๋ค ๊ธ์๋ฅผ ์์ฃผ ํ๋ฆฌ๋์ง ํ์ธ
print(classification_report(y_test, y_pred_classes, target_names=list(char_map)))
๐ ์ค์ ํ๋ก์ ํธ: ์์ ํ ์บก์ฐจ ์๋ฒ ๋ง๋ค๊ธฐ
์ด์ ๋ฐฐ์ด ๊ฑธ ๋ค ํฉ์ณ์ ์ค์ ์์ ์ธ ์ ์๋ ์์ ํ ์บก์ฐจ ์๋ฒ๋ฅผ ๋ง๋ค์ด๋ณด์! ๐๐ฆ ํ๋ก์ ํธ ๊ตฌ์กฐ
captcha_solver/
โ
โโโ data/
โ โโโ train/ # ํ์ต ๋ฐ์ดํฐ
โ โโโ test/ # ํ
์คํธ ๋ฐ์ดํฐ
โ โโโ samples/ # ์ํ ์บก์ฐจ ์ด๋ฏธ์ง
โ
โโโ models/
โ โโโ cnn_model.h5 # ํ์ต๋ ๋ชจ๋ธ
โ โโโ model_config.json
โ
โโโ src/
โ โโโ preprocessor.py # ์ ์ฒ๋ฆฌ ๋ชจ๋
โ โโโ segmenter.py # ๋ฌธ์ ๋ถ๋ฆฌ ๋ชจ๋
โ โโโ recognizer.py # ์ธ์ ๋ชจ๋
โ โโโ trainer.py # ํ์ต ๋ชจ๋
โ
โโโ utils/
โ โโโ data_generator.py # ๋ฐ์ดํฐ ์ฆ๊ฐ
โ โโโ visualizer.py # ์๊ฐํ ๋๊ตฌ
โ
โโโ main.py # ๋ฉ์ธ ์คํ ํ์ผ
โโโ requirements.txt # ์์กด์ฑ ํจํค์ง
โโโ README.md๐ง ํต์ฌ ๋ชจ๋ ๊ตฌํ
1. preprocessor.py - ์ ์ฒ๋ฆฌ ๋ชจ๋
import cv2
import numpy as np
class ImagePreprocessor:
def __init__(self, target_size=(28, 28)):
self.target_size = target_size
def preprocess(self, image):
"""์ ์ฒด ์ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ"""
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
if len(image.shape) == 3:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
else:
gray = image
# ๋
ธ์ด์ฆ ์ ๊ฑฐ (๊ฐ์ฐ์์ ๋ธ๋ฌ)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
# ์ด์งํ (Otsu)
_, binary = cv2.threshold(blurred, 0, 255,
cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# ๋ชจํด๋ก์ง ์ฐ์ฐ (๋
ธ์ด์ฆ ์ ๊ฑฐ)
kernel = np.ones((2, 2), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_OPEN, kernel)
return cleaned
def normalize(self, image):
"""์ ๊ทํ (0~1 ๋ฒ์)"""
return image.astype('float32') / 255.0
def resize(self, image):
"""ํฌ๊ธฐ ์กฐ์ """
return cv2.resize(image, self.target_size)2. segmenter.py - ๋ฌธ์ ๋ถ๋ฆฌ ๋ชจ๋
import cv2
import numpy as np
class CharacterSegmenter:
def __init__(self, min_width=10, max_width=50, min_height=15, max_height=50):
self.min_width = min_width
self.max_width = max_width
self.min_height = min_height
self.max_height = max_height
def segment(self, binary_image):
"""๋ฌธ์ ๋ถ๋ฆฌ ์ํ"""
# ์ค๊ณฝ์ ๊ฒ์ถ
contours, _ = cv2.findContours(binary_image,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# ๋ฐ์ด๋ฉ ๋ฐ์ค ์ถ์ถ ๋ฐ ํํฐ๋ง
char_boxes = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
# ํฌ๊ธฐ ํํฐ๋ง
if (self.min_width < w < self.max_width and
self.min_height < h < self.max_height):
char_boxes.append((x, y, w, h))
# x ์ขํ ๊ธฐ์ค ์ ๋ ฌ (์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ)
char_boxes = sorted(char_boxes, key=lambda b: b[0])
# ๋ฌธ์ ์ด๋ฏธ์ง ์ถ์ถ
characters = []
for x, y, w, h in char_boxes:
char_img = binary_image[y:y+h, x:x+w]
characters.append(char_img)
return characters, char_boxes
def merge_overlapping(self, char_boxes, overlap_threshold=0.5):
"""๊ฒน์น๋ ๋ฐ์ค ๋ณํฉ"""
merged = []
used = set()
for i, box1 in enumerate(char_boxes):
if i in used:
continue
x1, y1, w1, h1 = box1
merged_box = list(box1)
for j, box2 in enumerate(char_boxes[i+1:], start=i+1):
if j in used:
continue
x2, y2, w2, h2 = box2
# ๊ฒน์นจ ๋น์จ ๊ณ์ฐ
overlap_x = max(0, min(x1+w1, x2+w2) - max(x1, x2))
overlap_area = overlap_x * min(h1, h2)
box1_area = w1 * h1
if overlap_area / box1_area > overlap_threshold:
# ๋ณํฉ
new_x = min(x1, x2)
new_y = min(y1, y2)
new_w = max(x1+w1, x2+w2) - new_x
new_h = max(y1+h1, y2+h2) - new_y
merged_box = [new_x, new_y, new_w, new_h]
used.add(j)
merged.append(tuple(merged_box))
used.add(i)
return merged3. recognizer.py - ์ธ์ ๋ชจ๋
import numpy as np
from tensorflow import keras
class CaptchaRecognizer:
def __init__(self, model_path, char_map='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
self.model = keras.models.load_model(model_path)
self.char_map = char_map
self.preprocessor = ImagePreprocessor()
def recognize_character(self, char_image, confidence_threshold=0.7):
"""๋จ์ผ ๋ฌธ์ ์ธ์"""
# ์ ์ฒ๋ฆฌ
processed = self.preprocessor.resize(char_image)
processed = self.preprocessor.normalize(processed)
processed = processed.reshape(1, 28, 28, 1)
# ์์ธก
prediction = self.model.predict(processed, verbose=0)[0]
# ๊ฐ์ฅ ๋์ ํ๋ฅ ์ ํด๋์ค
char_idx = np.argmax(prediction)
confidence = prediction[char_idx]
if confidence >= confidence_threshold:
return self.char_map[char_idx], confidence
else:
return '?', confidence
def recognize_captcha(self, captcha_image, return_confidence=False):
"""์ ์ฒด ์บก์ฐจ ์ธ์"""
# ์ ์ฒ๋ฆฌ
preprocessor = ImagePreprocessor()
binary = preprocessor.preprocess(captcha_image)
# ๋ฌธ์ ๋ถ๋ฆฌ
segmenter = CharacterSegmenter()
characters, boxes = segmenter.segment(binary)
# ๊ฐ ๋ฌธ์ ์ธ์
result = ""
confidences = []
for char_img in characters:
char, conf = self.recognize_character(char_img)
result += char
confidences.append(conf)
if return_confidence:
avg_confidence = np.mean(confidences) if confidences else 0
return result, avg_confidence
else:
return result
def batch_recognize(self, image_paths):
"""์ฌ๋ฌ ์บก์ฐจ ์ผ๊ด ์ธ์"""
results = []
for path in image_paths:
image = cv2.imread(path)
text, conf = self.recognize_captcha(image, return_confidence=True)
results.append({
'path': path,
'text': text,
'confidence': conf
})
return results4. main.py - ๋ฉ์ธ ์คํ ํ์ผ
import argparse
import cv2
from src.recognizer import CaptchaRecognizer
def main():
parser = argparse.ArgumentParser(description='CAPTCHA Solver')
parser.add_argument('--image', type=str, required=True, help='์บก์ฐจ ์ด๋ฏธ์ง ๊ฒฝ๋ก')
parser.add_argument('--model', type=str, default='models/cnn_model.h5', help='๋ชจ๋ธ ๊ฒฝ๋ก')
parser.add_argument('--visualize', action='store_true', help='๊ฒฐ๊ณผ ์๊ฐํ')
args = parser.parse_args()
# ์ธ์๊ธฐ ์ด๊ธฐํ
recognizer = CaptchaRecognizer(args.model)
# ์ด๋ฏธ์ง ๋ก๋
image = cv2.imread(args.image)
if image is None:
print(f"์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค: {args.image}")
return
# ์บก์ฐจ ์ธ์
result, confidence = recognizer.recognize_captcha(image, return_confidence=True)
print(f"์ธ์ ๊ฒฐ๊ณผ: {result}")
print(f"์ ๋ขฐ๋: {confidence*100:.2f}%")
# ์๊ฐํ
if args.visualize:
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.text(0.5, 0.5, result, fontsize=40, ha='center', va='center')
plt.title(f'Result (Confidence: {confidence*100:.1f}%)')
plt.axis('off')
plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()๐ฎ ์ฌ์ฉ ๋ฐฉ๋ฒ
# 1. ์์กด์ฑ ์ค์น
pip install -r requirements.txt
# 2. ๋จ์ผ ์ด๋ฏธ์ง ์ธ์
python main.py --image samples/captcha1.png --model models/cnn_model.h5
# 3. ์๊ฐํ ํฌํจ
python main.py --image samples/captcha1.png --visualize
# 4. ๋ฐฐ์น ์ฒ๋ฆฌ
python batch_process.py --input_dir samples/ --output results.csvโ๏ธ ๋ฒ์ ยท์ค๋ฆฌ์ ๊ณ ๋ ค์ฌํญ
์ฌ๊ธฐ๊น์ง ์ฝ์์ผ๋ฉด ์บก์ฐจ ์ธ์ ํ๋ก๊ทธ๋จ์ ๋ง๋ค ์ ์์ ๊ฑฐ์ผ. ํ์ง๋ง ์ ๋ง ์ค์ํ ์๊ธฐ๋ฅผ ํด์ผ ํด. ๐โข ์ปดํจํฐ ์ฌ์ฉ ์ฌ๊ธฐ ๋ฐ ๋จ์ฉ๋ฒ ์๋ฐ (๋ฏธ๊ตญ CFAA)
โข ์ ๋ณดํต์ ๋ง๋ฒ ์๋ฐ (ํ๊ตญ)
โข ์๋น์ค ์ด์ฉ์ฝ๊ด ์๋ฐ
โข ์ ์๊ถ๋ฒ ์๋ฐ (์ผ๋ถ ์บก์ฐจ๋ ์ ์๊ถ ๋ณดํธ ๋์)
โ ํฉ๋ฒ์ ์ธ ์ฌ์ฉ ์ฌ๋ก
๊ทธ๋ผ ์ธ์ ์บก์ฐจ ์ธ์ ๊ธฐ์ ์ ์ฌ์ฉํด๋ ๋๋ ๊ฑธ๊น?
1. ๊ต์ก ๋ฐ ์ฐ๊ตฌ ๋ชฉ์
โข ๋ํ ํ๋ก์ ํธ๋ ๋ ผ๋ฌธ ์ฐ๊ตฌ
โข ๋ณด์ ์ทจ์ฝ์ ๋ถ์ (ํ์ดํธํ ํดํน)
โข ๋จธ์ ๋ฌ๋ ํ์ต ๋ชฉ์
2. ์ ๊ทผ์ฑ ํฅ์
โข ์๊ฐ ์ฅ์ ์ธ์ ์ํ ๋ณด์กฐ ๋๊ตฌ ๊ฐ๋ฐ
โข ์๋ํ๋ ์ ๊ทผ์ฑ ํ ์คํธ
3. ์์ฒด ์๋น์ค ๊ฐ์
โข ์์ ์ด ์ด์ํ๋ ์น์ฌ์ดํธ์ ์บก์ฐจ ๊ฐ๋ ํ ์คํธ
โข ๋ ๋์ ์บก์ฐจ ์์คํ ๊ฐ๋ฐ
4. ๋ช ์์ ํ๊ฐ๊ฐ ์๋ ๊ฒฝ์ฐ
โข API๋ฅผ ํตํ ๊ณต์ ์ง์
โข ์น์ฌ์ดํธ ์ด์์์ ์๋ฉด ๋์
๐ค ์ค๋ฆฌ์ ๊ฐ์ด๋๋ผ์ธ
๊ธฐ์ ์ ๋ฐฐ์ฐ๋ ๊ฑด ์ข์ง๋ง, ์ฑ ์๊ฐ ์๊ฒ ์ฌ์ฉํด์ผ ํด:
โข ์์ ์ ์๋น์ค์์๋ง ํ ์คํธํ๊ธฐ
โข ๋ณด์ ์ทจ์ฝ์ ์ ๋ฐ๊ฒฌํ๋ฉด ์ฑ ์๊ฐ ์๊ฒ ์ ๊ณ ํ๊ธฐ
โข ์คํ์์ค ๊ธฐ์ฌ ์ ๋ช ํํ ๋ผ์ด์ ์ค ๋ช ์
DON'T โ
โข ํฐ์ผํ ๋งคํฌ๋ก ์ ์
โข ์คํธ ๋ด ๊ฐ๋ฐ
โข ๋ฌด๋จ ํฌ๋กค๋ง/์คํฌ๋ํ
โข DDoS ๊ณต๊ฒฉ ๋๊ตฌ๋ก ํ์ฉ
โข ํ์ธ์ ๊ณ์ ๋ฌด๋จ ์ ๊ทผ
๐ ๋์์ ์ ๊ทผ ๋ฐฉ๋ฒ
์บก์ฐจ๋ฅผ ์ฐํํ๋ ๋์ ์ด๋ฐ ๋ฐฉ๋ฒ๋ค์ ๊ณ ๋ คํด๋ด:
1. ๊ณต์ API ์ฌ์ฉ
๋ง์ ์๋น์ค๋ค์ด ๊ฐ๋ฐ์๋ฅผ ์ํ API๋ฅผ ์ ๊ณตํด. ์ด๊ฑธ ์ฌ์ฉํ๋ฉด ํฉ๋ฒ์ ์ด๊ณ ์์ ์ ์ผ๋ก ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์์ด.
2. ์น์ฌ์ดํธ ์ด์์์ ํ์
์ ๋นํ ๋ชฉ์ ์ด ์๋ค๋ฉด ์ง์ ์ฐ๋ฝํด์ ํ๊ฐ๋ฅผ ๋ฐ๋ ๊ฒ ์ต์ ์ด์ผ.
3. ์บก์ฐจ ์๋ฃจ์ ์๋น์ค ์ด์ฉ
2Captcha, Anti-Captcha ๊ฐ์ ํฉ๋ฒ์ ์ธ ์๋น์ค๋ค์ด ์์ด. (๋ฌผ๋ก ์ด๊ฒ๋ ์ฉ๋์ ๋ฐ๋ผ ๋ฌธ์ ๊ฐ ๋ ์ ์์ผ๋ ์ฃผ์!)
4. ์ ๊ทผ์ฑ ๊ธฐ๋ฅ ํ์ฉ
๋ง์ ์บก์ฐจ ์์คํ ์ด ์ค๋์ค ์บก์ฐจ๋ ๋์ฒด ์ธ์ฆ ๋ฐฉ๋ฒ์ ์ ๊ณตํด. ์ด๋ฐ ๊ธฐ๋ฅ์ ํ์ฉํ๋ ๊ฒ๋ ๋ฐฉ๋ฒ์ด์ผ.
๐ฎ ๋ฏธ๋ ์ ๋ง ๋ฐ ์ต์ ํธ๋ ๋
์บก์ฐจ์ ์บก์ฐจ ์ธ์ ๊ธฐ์ ์ ๊ณ์ ์งํํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ป๊ฒ ๋ฐ์ ํ ์ง ์ดํด๋ณด์! ๐๐ค AI vs AI์ ์ ์
์ฌ๋ฏธ์๋ ๊ฑด, ์บก์ฐจ๋ฅผ ๋ง๋๋ ์ชฝ๋ AI๋ฅผ ์ฐ๊ณ , ์บก์ฐจ๋ฅผ ํธ๋ ์ชฝ๋ AI๋ฅผ ์ด๋ค๋ ๊ฑฐ์ผ. ์ผ์ข ์ ๊ตฐ๋น ๊ฒฝ์์ด์ง! ๐
โข GAN (Generative Adversarial Network): ๋ ์ด๋ ค์ด ์บก์ฐจ ์์ฑ
โข Transformer ๋ชจ๋ธ: ๋ณต์กํ ํจํด๋ ์ธ์ ๊ฐ๋ฅ
โข Few-shot Learning: ์ ์ ๋ฐ์ดํฐ๋ก๋ ํ์ต ๊ฐ๋ฅ
โข Meta-Learning: ์๋ก์ด ์บก์ฐจ ์คํ์ผ์ ๋น ๋ฅด๊ฒ ์ ์
๐ ์ฐจ์ธ๋ ์บก์ฐจ ๊ธฐ์
1. ํ๋ ๊ธฐ๋ฐ ์ธ์ฆ (Behavioral Biometrics)
๋ง์ฐ์ค ์์ง์, ํ์ดํ ํจํด, ํฐ์น ์ ์ค์ฒ ๋ฑ์ ๋ถ์ํด์ ์ฌ๋์ธ์ง ํ๋จํด. ๊ตฌ๊ธ์ reCAPTCHA v3๊ฐ ์ด ๋ฐฉ์์ ์ฌ์ฉํ์ง.
2. ์์ฒด ์ธ์ฆ ํตํฉ
์ง๋ฌธ, ์ผ๊ตด ์ธ์, ํ์ฑ ์ค์บ ๋ฑ์ ์บก์ฐจ์ ๊ฒฐํฉํ๋ ์ถ์ธ์ผ.
3. ๊ฒ์ํ๋ ์บก์ฐจ (Gamified CAPTCHA)
ํผ์ฆ ๋ง์ถ๊ธฐ, ๋ฏธ๋ ๊ฒ์ ๊ฐ์ ์ฌ๋ฏธ์๋ ๋ฐฉ์์ผ๋ก ์งํ ์ค์ด์ผ.
4. ๋ธ๋ก์ฒด์ธ ๊ธฐ๋ฐ ์ธ์ฆ
๋ถ์ฐ ์์ฅ ๊ธฐ์ ์ ํ์ฉํ ์๋ก์ด ์ธ์ฆ ๋ฐฉ์๋ ์ฐ๊ตฌ๋๊ณ ์์ด.
๐ ์ฐ์ ๋ํฅ
| ๊ธฐ์ | ํ์ฌ ์ํ | ๋ฏธ๋ ์ ๋ง |
|---|---|---|
| ํ ์คํธ ์บก์ฐจ | ์ ์ ์ฌ์ฉ ๊ฐ์ | ๊ฑฐ์ ์ฌ๋ผ์ง ์ ๋ง |
| ์ด๋ฏธ์ง ์บก์ฐจ | ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋จ | ๋ ๋ณต์กํด์ง ๊ฒ |
| ํ๋ ๋ถ์ | ๋น ๋ฅด๊ฒ ์ฑ์ฅ ์ค | ์ฃผ๋ฅ๊ฐ ๋ ์ ๋ง |
| ์์ฒด ์ธ์ฆ | ์ด๊ธฐ ๋จ๊ณ | ๋ชจ๋ฐ์ผ์์ ํ๋ |
๐ ํ์ต ๋ฆฌ์์ค ๋ฐ ์ปค๋ฎค๋ํฐ
๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ์๋ฃ๋ค์ ์ถ์ฒํด:
์จ๋ผ์ธ ๊ฐ์:
โข Coursera - Deep Learning Specialization (Andrew Ng)
โข Fast.ai - Practical Deep Learning for Coders
โข Udacity - Computer Vision Nanodegree
๋ ผ๋ฌธ ๋ฐ ์๋ฃ:
โข "Breaking Text-based CAPTCHAs with Variable Word Length" (Goodfellow et al.)
โข "I'm not a human: Breaking the Google reCAPTCHA" (Sivakorn et al.)
โข OpenCV ๊ณต์ ๋ฌธ์
โข TensorFlow ํํ ๋ฆฌ์ผ
์คํ์์ค ํ๋ก์ ํธ:
โข captcha-tensorflow (GitHub)
โข simple-captcha-solver (GitHub)
โข keras-ocr (GitHub)
์ปค๋ฎค๋ํฐ:
โข Stack Overflow - [computer-vision] ํ๊ทธ
โข Reddit - r/MachineLearning, r/computervision
โข Kaggle - ์บก์ฐจ ๊ด๋ จ ๋ํ ๋ฐ ๋ ธํธ๋ถ
โข Papers with Code - ์ต์ ์ฐ๊ตฌ ๋ํฅ
๐ก ๋ง์น๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์บก์ฐจ ์๋ ์ธ์ ํ๋ก๊ทธ๋จ์ ๋ํด ์์ ํ ํํค์ณ๋ดค์ด! ๐์ฐ๋ฆฌ๊ฐ ๋ค๋ฃฌ ๋ด์ฉ์ ์ ๋ฆฌํ๋ฉด:
โจ ํต์ฌ ์์ฝ
1. ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๊ธฐ์ด
โข ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ, ์ด์งํ, ๋ ธ์ด์ฆ ์ ๊ฑฐ
โข OpenCV๋ฅผ ํ์ฉํ ์ ์ฒ๋ฆฌ ๊ธฐ๋ฒ
โข ๋ชจํด๋ก์ง ์ฐ์ฐ๊ณผ ํํฐ๋ง
2. ๋ฌธ์ ๋ถ๋ฆฌ (Segmentation)
โข ์ค๊ณฝ์ ๊ฒ์ถ๊ณผ ๋ฐ์ด๋ฉ ๋ฐ์ค
โข ์์ง/์ํ ํฌ์ ๋ถ์
โข ๊ฒน์น ๊ธ์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
3. ๋ฌธ์ ์ธ์ (OCR)
โข Tesseract OCR
โข ํ ํ๋ฆฟ ๋งค์นญ
โข ๋จธ์ ๋ฌ๋ ๊ธฐ๋ฐ ๋ถ๋ฅ (KNN, SVM)
โข ๋ฅ๋ฌ๋ (CNN) ํ์ฉ
4. ์ฑ๋ฅ ์ต์ ํ
โข ๋ฐ์ดํฐ ์ฆ๊ฐ
โข ์์๋ธ ๊ธฐ๋ฒ
โข ์ ์ด ํ์ต
โข ํ์ดํผํ๋ผ๋ฏธํฐ ํ๋
5. ์ค์ ๊ตฌํ
โข ๋ชจ๋ํ๋ ํ๋ก์ ํธ ๊ตฌ์กฐ
โข ๋๋ฒ๊น ๋ฐ ๋ฌธ์ ํด๊ฒฐ
โข ๋ฐฐ์น ์ฒ๋ฆฌ ์์คํ
๐ฏ ์ค๋ฌด ์ ์ฉ ์ ์ฒดํฌ๋ฆฌ์คํธ
ํ๋ก์ ํธ๋ฅผ ์์ํ๊ธฐ ์ ์ ์ด๊ฒ๋ค์ ํ์ธํด๋ด:
โ๏ธ ๋ฒ์ ๋ฌธ์ ๋ ์๋๊ฐ? (๊ฐ์ฅ ์ค์!)
โ๏ธ ์ถฉ๋ถํ ํ์ต ๋ฐ์ดํฐ๊ฐ ์๋๊ฐ?
โ๏ธ ์ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ด ์ ์๋ํ๋๊ฐ?
โ๏ธ ๋ชจ๋ธ ์ฑ๋ฅ์ด ๋ชฉํ์น๋ฅผ ๋ฌ์ฑํ๋๊ฐ?
โ๏ธ ์ค์ ๋ฐ์ดํฐ์์๋ ์ ์๋ํ๋๊ฐ?
โ๏ธ ์๋ฌ ํธ๋ค๋ง์ด ์ ๋๋ก ๋์ด ์๋๊ฐ?
โ๏ธ ์ฝ๋๊ฐ ์ ์ง๋ณด์ ๊ฐ๋ฅํ ๊ตฌ์กฐ์ธ๊ฐ?
๐ ๋ค์ ๋จ๊ณ
์ด์ ๊ธฐ๋ณธ์ ๋ง์คํฐํ์ผ๋, ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๋ณด์:
์ด๊ธ โ ์ค๊ธ:
โข ๋ค์ํ ์บก์ฐจ ์คํ์ผ์ ๋์ํ๊ธฐ
โข ์ค์๊ฐ ์ฒ๋ฆฌ ์ฑ๋ฅ ๊ฐ์
โข ์น ์๋น์ค๋ก ๋ฐฐํฌํ๊ธฐ
์ค๊ธ โ ๊ณ ๊ธ:
โข Transformer ๋ชจ๋ธ ์ ์ฉ (BERT, ViT)
โข GAN์ ํ์ฉํ ๋ฐ์ดํฐ ์์ฑ
โข ๊ฐํํ์ต ๊ธฐ๋ฐ ์ต์ ํ
โข ๋ถ์ฐ ์ฒ๋ฆฌ ์์คํ ๊ตฌ์ถ
๊ณ ๊ธ โ ์ ๋ฌธ๊ฐ:
โข ์๋ก์ด ์บก์ฐจ ๋ฐฉ์ด ๊ธฐ๋ฒ ์ฐ๊ตฌ
โข ๋ ผ๋ฌธ ์์ฑ ๋ฐ ํํ ๋ฐํ
โข ์คํ์์ค ํ๋ก์ ํธ ๊ธฐ์ฌ
โข ๋ณด์ ์ปจ์คํ
ํ๋ก๊ทธ๋๋ฐ์ ๋์์๋ ํ์ต์ ์ฐ์์ด์ผ. ๋งํ๋ ๋ถ๋ถ์ด ์์ด๋ ํฌ๊ธฐํ์ง ๋ง๊ณ , ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ณ , ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์ผ๋ฉด์ ์ฑ์ฅํด๋๊ฐ์. ํ์ดํ ! ๐ฅ
ํน์ ์ด ๊ธ์ด ๋์์ด ๋๋ค๋ฉด, ์ฃผ๋ณ ๊ฐ๋ฐ์ ์น๊ตฌ๋ค์๊ฒ๋ ๊ณต์ ํด์ค! ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์๋ก์ด ์ธ์ฌ์ดํธ๋ฅผ ์ป๊ฒ ๋๋ฉด, ์ปค๋ฎค๋ํฐ์ ๊ณต์ ํ๋ ๊ฒ๋ ์์ง ๋ง! ๐
๋ง์ง๋ง์ผ๋ก, ์ด ๋ถ์ผ๋ ์ ๋ง ๋น ๋ฅด๊ฒ ๋ฐ์ ํ๊ณ ์์ด. ํญ์ ์ต์ ํธ๋ ๋๋ฅผ ๋ฐ๋ผ๊ฐ๊ณ , ์๋ก์ด ๊ธฐ์ ์ ๋ฐฐ์ฐ๋ ์์ธ๋ฅผ ์ ์งํ๋ ๊ฒ ์ค์ํด. ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ด ๋ด์ผ์ ๊ตฌ์์ด ๋ ์๋ ์๊ฑฐ๋ ! ๐
๊ทธ๋ผ ๋ชจ๋ ์ฆ๊ฑฐ์ด ์ฝ๋ฉ ๋๊ธธ ๋ฐ๋ผ! Happy Coding! ๐ฎ๐ปโจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.