Sign In
AI

데이터 전처리와 정규표현식

최윤진
이번 글에서는 딥러닝 학습에서 데이터가 가지는 중요성을 살펴보고, 데이터 전처리에 사용되는 정규표현식에 대해 살펴보겠습니다.

1. 딥러닝 성공 배경

딥러닝이 성공할 수 있었던 이유는 크게 3가지 입니다.
1.
Algorithms
AlexNet, CNN, RNN, Transformer, BERT, GPT ..
1.
Computation
V100, A100 ..
1.
Data
MNIST, CIFAR, WikiText ..
Data - Model - Cuda
먼저, Backpropagation, ReLU, Dropout, CNN 과 같은 기술들을 통해 AlexNet 이 만들어졌습니다. 시계열 데이터의 경우 RNN-LSTM-Transformer-BERT/GPT 로 이어지는 모델 계보가 있습니다. 이러한 모델 아키텍쳐가 있었기 때문에 딥러닝이 성공할 수 있었습니다.
다음 Computation 능력의 경우 병렬 처리가 가능한 좋은 GPU가 덕분에 효과적으로 학습과 추론을 할 수 있었고, 이 때문에 딥러닝이 성공할 수 있었습니다.
마지막으로 데이터입니다. 딥러닝 모델과 GPU 모두 양질의 데이터가 있을 때 비로소 의미가 있습니다. MNIST, CIFAR, WikiText 와 같은 품질 좋은 거대 데이터셋이 있었기 때문에 오차 계산의 재료가 충분했습니다. 소프트웨어 개발에 있어서 코드(Code)는 딥러닝 개발에 있어서 데이터와 같습니다.
GPT-3 의 경우 753GB 의 데이터셋으로 학습시켰다. 출처 : https://devocean.sk.com/blog/techBoardDetail.do?ID=164626&boardType=techBlog

2. 데이터 & 데이터 전처리 필요성

세 가지 모두 중요한 요소이지만 이번 글에서는 데이터를 집중 조명해보고자 합니다. 세상엔 많은 것이 데이터입니다. 이미지도, 언어도, 소리도, 글씨도 모두 데이터입니다. 풀고 싶은 문제에 따라 문제의 정답율도, 광고의 클릭률도 데이터가 될 수 있습니다. 당연히도 아날로그로 존재하는 데이터는 디지털로 변환되고, 디지털 상에서 태어난 데이터는 일반적으로 크롤러에 의해 수집되고 데이터베이스에 저장 관리하는 방식으로 사용할 수 있는 데이터가 됩니다.
많은 것이 데이터지만 무한하지는 않습니다. 인터넷/웹 이후로 디지털 공간에서 만들어진 데이터는 빠르게 증가해왔습니다. 특히 모바일의 등장은 가파른 데이터 생산에 큰 기여를 했습니다. 하지만 학습에 사용하고 있는 데이터가 새롭게 공급되는 데이터를 추월하고 있는 상황입니다. 모델 학습에 필요한 데이터가 고갈 될거라는 우려가 존재합니다. 때문에 한정된 데이터를 효율적으로 사용하는 게 중요합니다. 특히 데이터셋 자체를 구하기 어려운 분야의 경우에는 더욱 그러합니다.
낮은 품질의 텍스트 데이터는 약 2040년에, 높은 품질의 텍스트 데이터는 약 2024년에, 이미지 데이터는 약 2035년에 중앙값 데이터가 고갈될 것으로 추정된다. 출처 : https://zdnet.co.kr/view/?no=20230110071207
수집된 원본(raw) 데이터는 노이즈를 포함하고 있습니다. 불필요한 정보들이 입력 되었을 때 모델의 성능은 나쁜 영향을 받습니다. 원본 데이터 3만개 보다 품질 좋은 데이터 300개가 더 좋을 수 있습니다. 수집된 원본 데이터가 전처리를 거쳐야 하는 이유입니다.
노이즈 데이터는 모델 학습에 악영향을 끼친다. 출처 : https://sci2s.ugr.es/noisydata
데이터 전처리는 데이터를 의도에 맞게 학습하고 싶은 방향으로 처리하는 과정입니다.
이미지 데이터의 경우 RGB로 표현되는 값을 정규화 시키거나 openCV 라이브러리를 활용하여 중요한 정보를 부각시키는 방식(크기 조정, 회전, 반전, 경계 검출, 색상 분할)으로 전처리를 할 수 있습니다.
텍스트 데이터의 경우 전처리하는 다양한 방법들이 존재하며 정규 표현식은 텍스트 전처리에 과정에서 사용되는 대표적인 문자열 처리 방법입니다.
텍스트 데이터 전처리 로직, 출처 : https://www.researchgate.net/figure/Text-pre-processing-method-flowchart_fig1_341937237
Remove Stop words (불용어 제거)
불용어(stop words)는 자주 등장하지만 실제 의미 분석에는 크게 도움이 되지 않는 단어
예를 들어, "a", "the", "and" 등이 있음.
Stemming (어간 추출)
어간 추출은 단어를 어간(stem)으로 변환하는 과정.
어간은 단어의 기본 형태로, 접사 등이 제거된 형태.
예를 들어, "running", "runs", "ran"은 모두 "run"이라는 어간으로 변환.
Remove generic words (일반적인 단어 제거):
일반적인 단어는 해당 도메인에서 자주 등장하지만 실제 분석에는 큰 도움이 되지 않는 단어들을 말함.
예를 들어, 의료 도메인에서는 "patient", "doctor" 등의 단어가 일반적인 단어에 해당할 수 있음.
Remove tagged names (태그된 이름 제거):
태그된 이름은 데이터에 포함된 개인정보나 식별 가능한 정보를 말함.
예를 들어, 사람 이름, 지명, 이메일, URL, 휴대폰 번호
개인정보 보호와 데이터 익명화를 위해 중요한 단계

3. 텍스트 데이터 전처리 방법 : 정규 표현식

출처 : https://hamait.tistory.com/342
정규표현식(Regular Expression)이란 문자열에서 특정 패턴을 찾기 위해 사용되는 문자와 메타문자의 조합입니다. 1951년 수학자 스티븐 클리니에 의해 처음 만들어졌으며, 고전적으로 컴파일러 개발에 많이 활용이 되었습니다. 현재는 그 범위가 확장되어 웹 개발과 텍스트 데이터 전처리에 많이 활용 되고 있습니다.
정규 표현식을 이용하면 간단하게 이메일 주소, 전화번호, 로그 데이터 등과 같은 다양한 패턴을 검증하거나 치환할 수 있습니다. Python, Javascript, Java 등 다양한 언어에서 내장 라이브러리를 기본적으로 제공합니다. Python 에서는 re 모듈을 통해 정규표현식을 사용할 수 있습니다.

3.1 re 메서드

python 에서 re 사용법을 간단하게 살펴봅니다.
re.search(pattern, string)
문자열에서 패턴과 첫 번째로 일치하는 부분을 찾아 Match 객체를 반환합니다.
일치하는 부분이 없으면 None을 반환합니다.
import re text = "Hello, World!" pattern = r"World" match = re.search(pattern, text) if match: print("패턴과 일치하는 부분:", match.group()) else: print("일치하는 부분이 없습니다.") # 패턴과 일치하는 부분: World
re.findall(pattern, string)
문자열에서 패턴과 일치하는 모든 부분 문자열을 리스트로 반환
import re text = "Hello, World! Hello, Python!" pattern = r"Hello" matches = re.findall(pattern, text) print("일치하는 부분 문자열:", matches) # 일치하는 부분 문자열: ['Hello', 'Hello']
re.sub(pattern, repl, string)
문자열에서 패턴과 일치하는 부분을 repl로 대체한 새로운 문자열을 반환
import re text = "Hello, World! Hello, Python!" pattern = r"Hello" replaced = re.sub(pattern, "Hi", text) print("대체된 문자열:", replaced) # 대체된 문자열: Hi, World! Hi, Python!

3.2 정규 표현식 문법

📌 . ? * +

특수 문자
설명
.
줄바꿈 문자를 제외한 모든 문자와 매치
?
앞의 문자가 0번 또는 1번 나타나는 패턴과 매치
*
앞의 문자가 0번 이상 반복되는 패턴과 매치
+
앞의 문자가 1번 이상 반복되는 패턴과 매치
1.
. (줄바꿈을 제외한 모든 문자)
import re text = "Hello, World!\nThis is a new line." pattern = r"." matches = re.findall(pattern, text) print(matches) # 출력: ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'n', 'e', 'w', ' ', 'l', 'i', 'n', 'e', '.']
2.
? (앞의 문자가 0번 또는 1번 나타나는 패턴)
import re text = "a aa aaa abaa" pattern = r"a?" matches = re.findall(pattern, text) print(matches) # 출력: ['a', '', 'a', 'a', '', 'a', 'a', 'a', '', 'a', '', 'a', 'a', '']
3.
* (앞의 문자가 0번 이상 반복되는 패턴)
import re text = "a aa aaa abaa" pattern = r"a*" matches = re.findall(pattern, text) print(matches) # 출력: ['a', '', 'aa', '', 'aaa', '', 'a', '', 'aa', '']
+ (앞의 문자가 1번 이상 반복되는 패턴)
import re text = "a aa aaa abaa" pattern = r"a+" matches = re.findall(pattern, text) print(matches) # 출력: ['a', 'aa', 'aaa', 'a', 'aa']

📌 ^ $ {number}

특수 문자
설명
^
뒤의 문자열로 문자열이 시작됩니다.
$
앞의 문자열로 문자열이 끝납니다.
{숫자}
숫자만큼 반복합니다.
{숫자1, 숫자2}
숫자1 이상 숫자2 이하만큼 반복합니다. 
?, *, +를 이것으로 대체할 수 있습니다.
{숫자,}
숫자 이상만큼 반복합니다.
1.
^ (문자열의 시작)
import re text = "Hello, World!" pattern = r"^Hello" match = re.search(pattern, text) if match: print("문자열이 'Hello'로 시작합니다.") else: print("문자열이 'Hello'로 시작하지 않습니다.") #문자열이 'Hello'로 시작합니다.
2.
$ (문자열의 끝)
import re text = "Hello, World!" pattern = r"World!$" match = re.search(pattern, text) if match: print("문자열이 'World!'로 끝납니다.") else: print("문자열이 'World!'로 끝나지 않습니다.") # 문자열이 'World!'로 끝납니다.
3.
{숫자} (숫자만큼 반복):
import re text = "Hello, World!" pattern = r"l{2}" matches = re.findall(pattern, text) print("'l'이 2번 반복되는 부분:", matches) # 'l'이 2번 반복되는 부분: ['ll']
4.
{숫자1, 숫자2} (숫자1 이상 숫자2 이하만큼 반복)
import re text = "Hello, World! Look at that cooing dove!" pattern = r"o{1,2}" matches = re.findall(pattern, text) print("'o'가 1번 또는 2번 반복되는 부분:", matches) # 'o'가 1번 또는 2번 반복되는 부분: ['o', 'o', 'oo', 'oo', 'o']
5.
{숫자,} (숫자 이상만큼 반복)
import re text = "Hello, World! Look at that cooing dove!" pattern = r"o{1,}" matches = re.findall(pattern, text) print("'o'이 1번 이상 반복되는 부분:", matches) # 'o'이 1번 이상 반복되는 부분: ['o', 'o', 'oo', 'oo', 'o']

📌 [ ] [^문자] | \d \D \s \S \w \W

특수 문자
설명
[ ]
대괄호 안의 문자들 중 한 개의 문자와 매치합니다.
[amk]라고 한다면 a 또는 m 또는 k 중 하나라도 존재하면 매치를 의미합니다. 
[a-z]와 같이 범위를 지정할 수도 있습니다. 
[a-zA-Z]는 알파벳 전체를 의미하는 범위이며, 문자열에 알파벳이 존재하면 매치를 의미합니다.
[^문자]
해당 문자를 제외한 문자를 매치합니다
l
AlB와 같이 쓰이며 A 또는 B의 의미를 가집니다.
\\
역 슬래쉬 문자 자체를 의미합니다
\d
모든 숫자를 의미합니다. [0-9]와 의미가 동일합니다.
\D
숫자를 제외한 모든 문자를 의미합니다. [^0-9]와 의미가 동일합니다.
\s
공백을 의미합니다. [ \t\n\r\f\v]와 의미가 동일합니다.
space character 의 단축어
• 스페이스( ``)
• 탭(
t)
• 라인 피드 또는 새 줄(
n)
• 캐리지 리턴(
r)
• 폼 피드(
f)
• 수직 탭(
v)
\S
공백을 제외한 문자를 의미합니다. [^ \t\n\r\f\v]와 의미가 동일합니다.
\w
문자 또는 숫자를 의미합니다. [a-zA-Z0-9]와 의미가 동일합니다.
word character 의 단축어
\W
문자 또는 숫자가 아닌 문자를 의미합니다. [^a-zA-Z0-9]와 의미가 동일합니다.
1.
[] : 대괄호 안의 문자들 중 한 개의 문자와 매치
import re text = "Hello, World!" pattern = r"[aeiou]" matches = re.findall(pattern, text) print("모음:", matches)# 출력: ['e', 'o', 'o']
2.
[^문자] : 해당 문자를 제외한 문자를 매치
import re text = "Hello, World!" pattern = r"[^aeiou]" matches = re.findall(pattern, text) print("모음을 제외한 문자:", matches) # 출력: ['H', 'l', 'l', ',', ' ', 'W', 'r', 'l', 'd', '!']
3.
| (파이프): A 또는 B의 의미
import re text = "Hello, World!" pattern = r"Hello|World" matches = re.findall(pattern, text) print("'Hello' 또는 'World':", matches)# 출력: ['Hello', 'World']
4.
\\ (역슬래시): 역슬래시
import re text = "Hello, \\World!" pattern = r"\\" matches = re.findall(pattern, text) print("역슬래시 문자:", matches)# 출력: ['\\']
5.
\d : 숫자
import re text = "Hello, World! 123" pattern = r"\d" matches = re.findall(pattern, text) print("숫자:", matches)# 출력: ['1', '2', '3']
6.
\D : 숫자가 아닌 문자
import re text = "Hello, World! 123" pattern = r"\D" matches = re.findall(pattern, text) print("숫자가 아닌 문자:", matches) # 출력: ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', ' ']
7.
\s : 공백
import re text = "Hello, World! \t123\n" pattern = r"\s" matches = re.findall(pattern, text) print("공백 문자:", matches)# 출력: [' ', ' ', '\t', '\n']
8.
\S : 공백이 아닌 문자
import re text = "Hello, World! \t123\n" pattern = r"\S" matches = re.findall(pattern, text) print("공백이 아닌 문자:", matches)# 출력: ['H', 'e', 'l', 'l', 'o', ',', 'W', 'o', 'r', 'l', 'd', '!', '1', '2', '3']
9.
\w : 문자 또는 숫자
import re text = "Hello, World! 123" pattern = r"\w" matches = re.findall(pattern, text) print("문자 또는 숫자:", matches)# 출력: ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '1', '2', '3']
10.
\W : 문자 또는 숫자가 아닌 문자
import re text = "Hello, World! 123" pattern = r"\W" matches = re.findall(pattern, text) print("문자 또는 숫자가 아닌 문자:", matches)# 출력: [',', ' ', '!', ' ']

3.3 정규표현식을 통한 이메일 주소, URL 검증

📌 이메일 검증

💬
regex = r'^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$'
^[0-9a-zA-Z]
'시작을'  0~9 사이 숫자 or a-z A-Z 알바펫 아무거나로 시작하고
([-_\.]?[0-9a-zA-Z])*
[-_\.]?
중간에 - _  . 같은 문자가 있을수도 있고 없을수도 있으며
[0-9a-zA-Z]
그 후에 0~9 사이 숫자 or a-z A-Z 알바펫중 하나의 문자가 있어야함
→ 그러한 패턴이 0 번 이상 반복 (. 으로 끝날 수는 없다)
@
@ 가 반드시 존재하고
[0-9a-zA-Z]
0-9a-zA-Z 여기서 하나가 반드시 있어야 함.
([-_\.]?[0-9a-zA-Z])*
[-_\.]?
중간에 - _  . 같은 문자가 있을수도 있고 없을수도 있으며
[0-9a-zA-Z]
그 후에 0~9 사이 숫자 or a-z A-Z 알바펫중 하나의 문자가 있어야함
→ 그러한 패턴이 0 번 이상 반복 (. 으로 끝날 수는 없다)
\.
반드시 . 이 있어야 함.
[a-zA-Z]{2,3}$
[a-zA-Z] 의 문자가 2개나 3개가 존재 (대소문자 구분안함)
import re def valid_email(email): regex = r'^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$' valid = re.search(regex, email) if valid: print("Valid email") else: print("Invalid email") email = "mike@korea.co.kr" valid_email(email) # 제대로 된 이메일 email = "mike@daum.net" valid_email(email) # 제대로 된 이메일 email = "mike.j@korea.co.kr" valid_email(email) # 제대로 된 이메일 email = "mysite.com" valid_email(email) # 제대로 된 이메일이 아님 email = "mike@good" valid_email(email) # 제대로 된 이메일이 아님 # Valid email # Valid email # Valid email # Invalid email # Invalid email

📌 URL 검증

url 일반적인 구성 요소
스킴(Scheme): 웹페이지 (http), 보안 웹페이지 (https) 등과 같은 프로토콜을 나타냅니다.
호스트(Host): 도메인 이름 (예: www.example.com) 또는 IP 주소를 포함합니다.
포트(Port): 서버에서 특정 서비스를 제공하는 통신 포트를 나타냅니다 (예: :80, :443 등).
경로(Path): 서버의 파일 경로를 나타냅니다 (예: /path/to/myfile.html).
쿼리(Query): 서버에 제공하는 추가 파라미터 (예: ?key1=value1&key2=value2).
프래그먼트(Fragment): 페이지 내의 특정 부분으로 바로 가기 위한 앵커 (예: #section1).
💬
regex = r'^(https?):\/\/([^:\/\s]+)(:([^\/]*))?((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$'
^(https?)
URL의 시작 부분에서 "http" 또는 "https"가 있어야 함.
s? 는 s 가 있거나 없거나 를 의미
:\/\/
:// 가 반드시 존재
([^:\/\s]+)
프로토콜 이후에 /:, 공백이 아닌 문자가 하나 이상 있어야 함
(:([^\/]*))?
선택적으로 포트 번호가 올 수 있습니다.
:로 시작하고 /가 아닌 문자가 0개 이상 올 수 있음.
→ 이러한 패턴이 0번 혹은 1번
((\/[^\s/\/]+)*)?
선택적으로 경로(path)가 올 수 있음.
/로 시작하고
공백(\s), /가 아닌 문자가 하나 이상 올 수 있으며, 이는 0번 이상 반복될 수 있음.
매치될 부분: /path/to/page
\/?
/ 가 0 번 혹은 1번 나올 수 있습니다.
경로의 마지막 슬래시(/) 매치
**http://example.com**과 http://example.com/ 둘 다 유효한 URL 임.
([^#\s\?]*)
쿼리 문자열 또는 프래그먼트 이전에 #, 공백, ?가 아닌 문자가 0개 이상 올 수 있음.
(\?([^#\s]*))?
?로 시작하고 #, 공백이 아닌 문자가 0개 이상 올 수 있음.
선택적으로 쿼리 스트링이 올 수 있음.
?key=value&anotherKey=anotherValue
→ 이러한 패턴이 0번 혹은 1번
(#(\w*))?$
선택적으로 프래그먼트가 올 수 있음.
#로 시작하고 단어 문자(알파벳, 숫자, 언더스코어)가 0개 이상 올 수 있음.
$ : 이것으로 문자열이 끝나야함.
→ 이러한 패턴이 0 번 혹은 1번
import re def valid_url(url): regex = r'^(https?):\/\/([^:\/\s]+)(:([^\/]*))?((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$' valid = re.search(regex, url) if valid: print("Valid URL") else: print("Invalid URL") url = "https://www.example.com" valid_url(url) # 유효한 URL url = "http://example.com/path/to/page" valid_url(url) # 유효한 URL url = "https://example.com/path/to/page?query=param#fragment" valid_url(url) # 유효한 URL url = "http://example.com:8080" valid_url(url) # 유효한 URL (포트 번호 포함) url = "http://example.com/path/to/page?query=param&invalid=char" valid_url(url) # 유효한 URL url = "http://example.com/path/to/page/index.html?query=param&invalid=char" valid_url(url) # 유효한 URL url = "ftp://example.com" valid_url(url) # 유효하지 않은 URL (http 또는 https가 아님) url = "http://example.com/path/to/page#invalid fragment" valid_url(url) # 유효하지 않은 URL (프래그먼트에 유효하지 않은 문자 포함) # Valid URL # Valid URL # Valid URL # Valid URL # Valid URL # Invalid URL # Invalid URL

3.4 정규표현식을 통한 크롤링 텍스트 데이터 전처리

import re def preprocess_text(text): # HTML 태그 제거 text = re.sub(r'<[^>]+>', '', text) # 이메일 주소 추출 emails = re.findall(r'[\w\.-]+@[\w\.-]+', text) print("Extracted Emails:", emails) # 웹 주소(URL) 추출 urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', text) # [!*\\(\\),] -> # (?:%[0-9a-fA-F][0-9a-fA-F]) : URL 인코딩 값 (예: %20) print("Extracted URLs:", urls) # 전화번호 형식 추출 (기본적인 형식) phone_numbers = re.findall(r'\d{2,3}-\d{3,4}-\d{4}', text) print("Extracted Phone Numbers:", phone_numbers) # 공백 정리 (연속된 공백을 하나로) text = re.sub(r'\s+', ' ', text) # 한글만 추출 text = ''.join(re.findall(r'[\u3131-\u3163\uac00-\ud7a3]+', text)) return text # 예제 텍스트에 적용 sample_text = """<html>이것은 예시 <b>텍스트</b>입니다! 여기에는 다양한 정보가 있습니다: 이메일 주소 john.doe@example.com, 웹사이트 http://example.com, 전화번호 010-1234-5678이 포함되어 있습니다.</html>""" processed_text = preprocess_text(sample_text) print("Processed Text:", processed_text) # Extracted Emails: ['john.doe@example.com'] # Extracted URLs: ['http://example.com,'] # Extracted Phone Numbers: [] # Processed Text: 이것은예시텍스트입니다여기에는다양한정보가있습니다이메일주소웹사이트전화번호이포함되어있습니다
정규표현식 치트시트 출처: https://cheatography.com/davechild/cheat-sheets/regular-expressions/

4. 마치며

이상으로 딥러닝에서의 데이터의 중요성과 대표적인 전처리 방법인 정규표현식에 대해서 살펴보았습니다. 전처리에 있어서 정답은 없습니다. 모델을 통해 어떤 문제를 해결하고자 하는지에 따라서 최적의 전처리 방법은 달라질 수 있습니다.
언어 모델이 잘못된 정보를 학습하지 않기 위해 관련 지식을 배제하는 것이 올바른 방법일까요? 예를 들어, 폭탄 제조 방법에 대한 정보 유출을 막기 위해 관련 내용을 가르치지 않는게 맞을까요? 맞춤법은 항상 교정하여 학습시키는게 좋을까요?
이러한 질문들에 대한 명확한 답은 없습니다. 분명한 것은 모델의 성능은 데이터의 품질에 크게 좌우된다는 점입니다. 모델이 배우는 대상은 오직 데이터이기 때문입니다. 그러한 이유로 데이터의 특성을 잘 파악하는 것이 무엇보다 중요합니다. 이를 위해 탐색적 데이터 분석(Exploratory Data Analysis, EDA)을 하고, 다양한 전처리 방법들을 테스트하며 결과를 분석하는 과정은 딥러닝 개발 과정에서 필수적이라고 할 수 있습니다.
이상으로 마치도록 하겠습니다. 감사합니다.

참고자료

Kp
Subscribe to 'KPMG Lighthouse'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'KPMG Lighthouse'!
Subscribe
👍🏻
1
😀
1
😍
1