바로 정답 공개하도록 하겠습니다. 그 이후에 시간초과가 걸리는 코드와 정답으로 통과한 코드를 비교해 보겠습니다. 먼저, 사람들이 가장 많이 채택한 정답입니다.
def solution(ingredient):
s = []
cnt = 0
for i in ingredient:
s.append(i)
if s[-4:] == [1, 2, 3, 1]:
cnt += 1
for _ in range(4):
s.pop()
return cnt
다음으로 시간초과로 통과할 수 없는 코드를 보여드리겠습니다. 위의 코드와 동일하나, 딱 한 줄이 다릅니다.
def solution(ingredient):
s = []
cnt = 0
for i in ingredient:
s.append(i)
if s[-4:] == [1, 2, 3, 1]:
cnt += 1
s = s[:-4] # 해당 부분으로 인해 시간 초과 발생
return cnt
최종적으로 해결하고자 하는 작업을 의미합니다. Downstream이라는 말은 영어 그대로 직역 하면 '하류(下流)' 입니다.
물은 위에서 흘러 아래로 흘러갑니다. 즉, 상류(Upstream)에서 하류(downstream)로 흘러가는 순서가 존재합니다.
우리가 하는 작업에도 순서가 있는데, 먼저 해결해야 할 작업을 Upsteam task라 부르고 다음에 최종적으로 해결하고자 하는 작업을 Downstream task라고 합니다. 그렇다면 딥러닝에서의 Downstream task은 무엇을 의미할까요? 이는 전이학습 (Transfer Leanring)과 깊은 관련이 있습니다.
전이학습(Transfer learing) 이란, 모델을 처음부터 끝까지 (from scratch) 새로 훈련하는 것이 아니라, 미리 훈련된 (pre-trained) 모델로부터 학습을 시작하는 것을 의미합니다.
이를 up/down stream의 개념에 비유해보면, upstream은 미리 훈련된 pre-trained model이 될 것이고, downstream은 내가 최종적으로 만들고자 하는 모델이 될 것입니다.
마치 직장에서 후배가 선배의 보고서를 참조하는 것과 유사한데요, 선배가 보고서가 좋은 보고서라면 (good upstream), 후배가 보고서를 쓰기 훨씬 수월할 것입니다. (good down stream)
늑대와 호랑이를 분류하는 Downstream task를 생각해 볼까요? 개와 고양이를 분류하는 모델이 이미 학습되었다면, 이를 늑대와 호랑이를 분류하는 모델에 활용할 수 있을 것입니다. 늑대와 개 / 호랑이와 고양이는 서로 비슷한 특징을 가지고 있기 때문입니다!
딥러닝 분야에서 downstream이라는 말은 다양한 분야에서 쓰입니다. 자연어처리(NLP), 이미지 분류(image classification)등 많은 포스팅과 논문에서 Downstream이라는 단어가 등장합니다.
하지만 분야에 상관없이 모두 '내가 최종적으로 해결하고 싶은 문제', 'upstream을 거쳐 단계적으로 해결하는 것이 보다 좋은 문제'라는 점에서는 일맥상통하는 것 같습니다.
먼저 입력 데이터를 살펴봅시다. 위와 같이 split()함수를 이용하면, 공백으로 구분된 문자열을 리스트에 이쁘게 담아줍니다. 출력을 해보면 위와 같이 프린트가 됩니다.
이제 값들을 숫자와 숫자가 아닌 것으로 구분해줘야 합니다. 그 전에 data의 값이 음수가 올 수도 있다는 사실에 주목해야 합니다. 보통 문자열 데이터에서 숫자를 구분하기 위해 isdigit(), isnumeric() 함수를 많이 사용하는데, 이 함수들은 음수값들을 필터링하지 못 합니다.
조건문에 문자열 'Z'만 따로 구분하는 방법도 있겠지만, 저는 좀 더 pythonic한 방법을 사용해보려 합니다.
input_data = "1 -2 Z 3"
for data in input_data.split():
try:
num = int(data)
print('num:', num)
except:
print('str:', data)
output
--------------------------------
num: 1
num: -2
str: Z
num: 3
try ~ except 구문을 이용하였고, 음수에도 동작한다는 것을 보여드리기 위해 두 번째 입력값을 2 -> -2로 바꾸었습니다.
try ~ except는 파이썬에서 예외처리를 하기 위해 쓰이는 문법입니다. try 쪽을 먼저 시도해보고, 에러가 안 난다면 그대로 excpet를 건너뛰고 통과합니다. 만약 try 쪽에서 에러가 난다면, except 쪽으로 빠지게 되는 구조입니다.
위의 코드에서 문자열 'Z'에 int 함수를 씌우려 하니 에러가 발생합니다. (Exception: invalid literal for int() with base 10: 'Z') 그래서 except 구문이 실행이 되는 원리 입니다.
3. Stack 활용하여 자료 넣고 꺼내기
input_data = "1 -2 Z 3"
stack = []
for data in input_data.split():
try:
num = int(data)
stack.append(num)
except:
if stack:
pop_data = stack.pop()
print('stack:', stack)
Python List 자료구조를 Stack처럼 활용하였습니다. 데이터가 숫자라면 차례대로 stack에 append 함수를 이용하여 쌓아줍니다. 데이터 'Z'가 들어오면 stack에서 pop 함수를 이용하여, 마지막 것 부터 꺼냅니다. (pop 함수는 리스트의 맨 뒤 요소를 뽑아내는 기능 수행)
(+) if stack 부분 설명: stack에 데이터가 있으면 실행한다는 뜻 입니다. stack이 비어있으면 if문이 실행되지 않습니다.
코드가 실행되는 것을 슬라이드로 표현해 보았습니다. 코드의 output을 이해하시는데 도움이 될 것입니다.
4. 최종 결과 값 계산하기
result라는 변수를 하나 만들고 결과들을 하나씩 더해주거나 빼주도록 하겠습니다.
숫자가 나온다면? -> result에 차례로 더해주고 stack에 쌓으면 됩니다. 'Z'가 나온다면? -> stack에서 하나를 pop한 후, result에서 빼주면 됩니다.
input_data = "1 -2 Z 3"
stack = []
result = 0
for data in input_data.split():
try:
num = int(data)
result += num
stack.append(num)
except:
if stack:
pop_data = stack.pop()
result -= pop_data
print('result:', result)
print('--------------')
print('result2:', sum(stack))
result 결과값은 최종적으로 4가 됩니다. 이는 stack에 남아있는 숫자들을 모두 더한 값과 동일합니다. (result2: 4) 저도 나중에 알았네요 ㅋㅋ result 변수를 안 써도 될 듯 합니다. 최종 풀이는 해당 방법으로 교체하고 result 변수 부분들을 삭제하도록 하겠습니다.
5. 프로그래머스 제출용 정답 및 핵심정리
def solution(s):
stack = []
for num in s.split():
try:
stack.append(int(num))
except:
if stack:
stack.pop()
return sum(stack)
★핵심 정리★
1. Stack 자료구조 이해 (python list 이용) 2. 음수값 처리 방법 -> python try ~ except 구문 이용 3. append와 pop의 용법 이해하기
아이디와 비밀번호가 각각 for문의 id, pw라는 변수에 담겼습니다. 먼저 db에 담긴 내용을 살펴보겠습니다. 현재 test case1에 대해서만 출력을 해보았는데, 결과는 위와 같습니다.
2. if 문으로 분기 나누기
그 다음으로 생각해 봐야 하는 것은 if 문으로 분기를 나누는 것입니다. id와 pw를 비교해야 하는데, 어떤 것을 더 상위의 if문으로 둘 것인지 생각해봐야 합니다.
만약 id가 일치하는 회원이 없다면? 그냥 "fail"을 return해주고 종료하면 됩니다. id가 틀렸는데 pw를 굳이 또 체크할 필요가 있을까요? 그럴 필요는 없습니다 ㅎㅎ
id가 일치하는 회원이 있다면, 여기서 또 분기를 2개로 나눠야 합니다. ① pw가 일치할 경우 -> "login"을 return ② pw가 일치하지 않을 경우 -> "wrong pw"를 return
def solution(id_pw, db):
for id, pw in db:
if id_pw[0] == id:
if id_pw[1] == pw:
return "login"
else:
return "wrong pw"
return "fail"
★ 체크포인트① ★ list indexing을 이용한 id_pw 접근 id_pw는 대괄호로 묶여있는 자료구조로, 이러한 것을 python에선 list라고 부릅니다. list의 각 요소에 접근하기 위한 방법을 indexing이라고 부르는데요, 대괄호 안의 숫자로 각 요소에 접근할 수 있습니다. (ex- id_pw[0])
+ Python index의 시작 번호는 1번 부터가 아닌 0번 부터입니다. 그러므로 id_pw[0]이라고 하는 것은, 리스트의 첫 번째 요소에 접근한다는 뜻 입니다.
id_pw = ["meosseugi", "1234"]
현재 id_pw라는 리스트는 위와 같이 구성되어 있습니다. 그러므로 id_pw[0]을 사용하면, "meosseugi"라는 id값에 접근하게 됩니다. 마찬가지 원리로 id_pw[1]는 리스트의 두 번째 요소를 가리키며, "1234"라는 값을 반환합니다.
def solution(id_pw, db):
for id, pw in db:
if id_pw[0] == id:
if id_pw[1] == pw:
return "login"
else:
return "wrong pw"
return "fail"
★ 체크포인트② ★ return 명령어를 활용한 함수 즉시 종료 return 명령어는 함수를 즉시 종료합니다. 해당 함수는 solution이라는 이름을 갖고 있습니다.
그러므로 위 코드를 풀이해보면, 하나라도 id와 pw가 맞는 경우가 있다면 바로 "login"을 return하고 함수가 종료된다는 뜻입니다.
만약 for문이 종료될 동안 하나도 if문에 걸리는게 없다면 어떨까요? 맨 마지막 구문인 return "fail"까지 도달하게 됩니다. 이 경우, 입력한 id와 일치하는 것이 db에 없다는 말이 되겠습니다.
3. 핵심 정리
해당 문제를 풀기위해 필요한 개념입니다.
ⓐ 자료구조 list에 대한 이해 (indexing 개념 포함) ⓑ for 반복문, ⓒ if 분기문 + return을 활용한 함수 즉시 종료
해당 문제는 파이썬의 기초을 잘 알고 계신 분이라면, 무난하게 풀 수 있었던 문제라고 생각됩니다. 기초를 탄탄히 다지는 것은 언제나 중요합니다!
안녕하세요. 은공지능 공작소의 파이찬입니다! 정말 오랜만에 포스팅을 올리네요. 9월달은 정말 바쁘게 지낸 것 같습니다. 오늘은 tf-idf vectorizer를 생성할 때 필요한 여러가지 파라미터들에 대해 알아보겠습니다.
min_idf, analyzer, sublinear_tf, ngram_range, max_features 이렇게 5가지 파라미터들에 대해서 알아볼 것입니다.
1. 사전준비
from sklearn.feature_extraction.text import TfidfVectorizer
text = ['I go to my home my home is very large',
'I went out my home I go to the market',
'I bought a yellow lemon I go back to home']
tfidf_vectorizer = TfidfVectorizer()
우선 위와같이 데이터를 생성해 줍니다. 저번 시간에 썼던 데이터와 동일한 데이터 입니다. 오늘 사용할 파라미터는 TfidfVectorizer()의 괄호 안에 들어가는 것들입니다.
sublinear_tf를 구현해보는 간단한 함수를 만들어 보았습니다. 결과를 보시면 100은 5.6 정도로, 10000은 10.2 정도로 값이 확 줄어든 것을 확인하실 수 있습니다. 이처럼 sublinear_tf는 높은 TF값을 완만하게 처리하는 효과를 가지고 있습니다.
TF의 아웃라이어가 너무 심한 데이터의 경우, 이런 서브리니어 파라미터를 True로 바꿔주면 어느정도 효과를 기대할 수 있습니다. 간단히 코드 실습 후 넘어가겠습니다.
vectorizer = TfidfVectorizer(sublinear_tf = True) # defalut는 False입니다.
vectorizer.fit(text)
a = vectorizer.transform(text).toarray()
a
위와 같이 sublinear_tf를 True로 설정하면, 높은 TF값들에 대해서 스무딩 처리를 해줍니다. 하지만 저희 예제 데이터는 너무 작기 때문에, 해당 파라리터의 효과가 별로 없는 것 같네요. 'TF 값에 대해 아웃라이어 처리를 해준다'라는 효과 정도만 기억하고 넘어가시면 되겠습니다.
5. ngram_range 파라미터 이해하기
n-gram이라는 것은 단어의 묶음을 말합니다. 이 단어의 묶음을 범위를 설정하는 것이 ngram_range 파라미터입니다.
예를들어, ngram_range = (1, 1)이라면 단어의 묶음을 1개부터 1개까지 설정하라는 의미입니다. 단어사전에는 1개 단어묶음에 인덱스가 할당되겠죠. 이는 기존의 벡터라이저와 별 차이가 없는 결과를 내놓습니다.
만약 ngram_range = (1, 2)라고 한다면, 단어의 묶음을 1개부터 2개까지 설정하라는 뜻입니다. 단어사전에는 1개 단어묶음도 있고, 2개 단어묶음도 존재하게 되겠죠. 가령, 'go home', 'very good'과 같은 2개 짜리 묶음도 인덱스를 받게 되는 것입니다.
이번에는 ngram_range를 (1, 2)로 설정해보았습니다. 이는 단어묶음을 1개부터 2개까지로 세팅하겠다는 뜻입니다. 단어사전을 출력해보니, 단어 2개 묶음도 인덱스를 받은 것을 볼 수 있습니다.
사실 이번 포스팅은 이 ngram을 설명할고 하다가, 아예 기본적인 파라미터들을 다 설명해기로 하고 만든 것입니다 ^^
그럼 이 ngram_range를 사용하는 이유는 무엇일까요? 단어가 묶여야 비로소 의미를 가지는 것들이 있기 때문입니다. 예를들어 'very'라는 단어 하나만으로는 의미를 잘 알 수 없지만 'very good', 'very bad' 등과 같이 뒤에 오는 단어가 같이 오면 정확한 의미를 알 수 있습니다. 이렇게 묶여야 의미가 살아나는 녀석들 때문에, ngram_range를 사용한다고 볼 수 있습니다.
6. max_features 파라미터 알아보기
max_feature는 tf-idf vector의 최대 feature를 설정해주는 파라미터입니다. 해당 파라미터를 이해하려면, feature의 개념에 대해 아셔야 합니다.
머신러닝에서 feature란, 테이블의 컬럼에 해당하는 개념입니다. 또한 행렬의 열에 해당하는 것이기도 합니다.
TF-IDF 벡터는 단어사전의 인덱스만큼 feature를 부여받습니다. 그럼 구체적인 예시를 코드로 한 번 살펴보겠습니다.
text
output:
['I go to my home my home is very large',
'I went out my home I go to the market',
'I bought a yellow lemon I go back to home']
저희의 데이터는 위와 같이 생겼습니다. 모두 15 종류의 단어가 사용되었는데요. 그러므로 feature값은 15가 할당이 됩니다.
vectorizer = TfidfVectorizer()
a = vectorizer.fit_transform(text).toarray()
a.shape
output:
(3, 15)
벡터의 shape를 출력해보면 (3, 15)가 나옵니다. 이 15라는 값이 feature에 해당하는 것입니다. 이해가 되셨죠?
이제 max_features 파라미터를 통해, 이 feature값을 10으로 제한해보겠습니다.
vectorizer = TfidfVectorizer(max_features = 10)
a = vectorizer.fit_transform(text).toarray()
a.shape
output:
(3, 10)
feature의 값을 10으로 제한하니, 벡터의 모양이 변했습니다. (3, 10)으로 shape가 바뀐 것을 확인하실 수 있을 겁니다.
해당 파라미터는 어느 상황에 사용하게 될까요? 자연어처리 데이터를 다루다보면, 엄청 긴 데이터를 만날 수 있습니다.
예를 들어서 다른 데이터의 단어들은 10정도를 가지는데, 어떤 데이터만 단어 종류가 100이 넘어간다고 하면, 이 100에 맞추어 feature의 수가 엄청 늘어나게 됩니다. 그럴 경우, 모델 성능이 저하될 수도 있는 것이죠.
그렇기 때문에 사전에 EDA를 통해서 데이터의 length를 확인하시는 것이 중요합니다. 그렇게 되면 통계적으로 엄청 정확한 기준은 아닐지라도, 대략 해당 파라미터의 스타트 지점을 유추해 볼 수 있을 것입니다.
7. 마무리
네 이렇게 오늘은 tf-idf의 중요한 파라미터 5가지에 대해서 알아보았습니다. 도움이 되셨다면 포스팅 하단에 하트 버튼 한 번씩 눌러주시는 것 잊지 마세요~!