문제는 아래와 같습니다.

https://programmers.co.kr/learn/courses/30/lessons/77484

 

코딩테스트 연습 - 로또의 최고 순위와 최저 순위

로또 6/45(이하 '로또'로 표기)는 1부터 45까지의 숫자 중 6개를 찍어서 맞히는 대표적인 복권입니다. 아래는 로또의 순위를 정하는 방식입니다. 1 순위 당첨 내용 1 6개 번호가 모두 일치 2 5개 번호

programmers.co.kr

풀이

 

def solution(lottos, win_nums):
    count_same=0
    count_mask=0
    answer=[]
    for i in range(len(lottos)):
        if lottos[i] in win_nums:
            count_same+=1
        elif lottos[i]==0:
            count_mask+=1
    return [len(lottos)+1-(count_same+count_mask) if len(lottos)+1-(count_same+count_mask)<=6 else 6,
    len(lottos)+1-count_same if len(lottos)+1-count_same<=6 else 6]

 

1단계. 로또 점수와 일치하는 점수의 수를 구합니다.

2단계. 로또 점수에서 0의 개수를 구합니다.

3단계. 로또 총 개수에서 일치하는 점수, 0의 개수를 뺀 것이 최고의 순위가 됩니다.

4단계. 로또 총 개수에서 일치하는 점수를 뺀 것이 최저의 순위가 됩니다.

문제는 아래와 같습니다.

https://programmers.co.kr/learn/courses/30/lessons/72411

 

코딩테스트 연습 - 메뉴 리뉴얼

레스토랑을 운영하던 스카피는 코로나19로 인한 불경기를 극복하고자 메뉴를 새로 구성하려고 고민하고 있습니다. 기존에는 단품으로만 제공하던 메뉴를 조합해서 코스요리 형태로 재구성해서

programmers.co.kr

 

풀이

 

def solution(orders, course):
    
    from itertools import combinations 
    from collections import Counter

    total=[]
    result=[]
    value_1=[]
    
    for j in course:
    
        total=[]
        value_1=[]
        
        for i in orders:
            total+=list(combinations(i, j))
    
        for idx, value in enumerate(total):
            value_1.append(''.join(sorted(list(value))))
        
        for common in Counter(value_1).most_common():
            if common[1]==Counter(value_1).most_common()[0][1] & Counter(value_1).most_common()[0][1]>1:
                result.append(common[0])
    
    return sorted(list(set(result)))

 

1단계: itertools.combinations 함수로 음식 조합을 구성합니다.

 

2단계: 1단계의 결과가 튜플 형식으로 나옵니다. 그리고 ('A', 'B'), ('C', 'D') 형식이기 때문에 'AB', 'CD'로 변환합니다.

 

3단계: 두 명 이상이 주문한 음식 조합을 선택해야 됩니다. 또한 만약에 같은 개수의 음식 조합이 존재한다면, 주문한 수가 가장 많은 음식 조합을 선택해야 합니다. 예를 들면, 네오와 어피치가 'ABC' 음식 조합을 먹었고 네오, 어피치, 프로도가 'ACE' 음식 조합을 먹었다면 'ACE'를 선택해야 합니다. 주문한 수가 더 많았기 때문입니다. 

따라서 collections.Counter 함수를 이용해서 2번 이상 주문하고 가장 많이 주문한 조합 위주로 선택해야 합니다.

 

4단계: set 함수를 이용해 중복된 결과를 제거하고 list 변환한 후 sorted 함수로 알파벳순 정렬합니다. 

 

 

 

 

 

 

 

문제

카카오에 입사한 신입 개발자 네오는 "카카오계정개발팀"에 배치되어, 카카오 서비스에 가입하는 유저들의 아이디를 생성하는 업무를 담당하게 되었습니다. "네오"에게 주어진 첫 업무는 새로 가입하는 유저들이 카카오 아이디 규칙에 맞지 않는 아이디를 입력했을 때, 입력된 아이디와 유사하면서 규칙에 맞는 아이디를 추천해주는 프로그램을 개발하는 것입니다.
다음은 카카오 아이디의 규칙입니다.

  • 아이디의 길이는 3자 이상 15자 이하여야 합니다.
  • 아이디는 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.) 문자만 사용할 수 있습니다.
  • 단, 마침표(.)는 처음과 끝에 사용할 수 없으며 또한 연속으로 사용할 수 없습니다.

"네오"는 다음과 같이 7단계의 순차적인 처리 과정을 통해 신규 유저가 입력한 아이디가 카카오 아이디 규칙에 맞는 지 검사하고 규칙에 맞지 않은 경우 규칙에 맞는 새로운 아이디를 추천해 주려고 합니다.
신규 유저가 입력한 아이디가 new_id 라고 한다면,

1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.
6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
     만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.

 

풀이

[정규표현식을 이용했습니다]

def solution(new_id):
    import re
    new_id=''.join(list(re.sub('^[.]|[.]$','',re.sub('[.]{2,}','.',re.sub('[^0-9a-z._-]','',new_id.lower())))))   
    if len(new_id)==0: new_id='a' 
    elif len(new_id)>=16: new_id=re.sub('[.]$','',new_id[:15])  
    while len(new_id)<3: new_id=new_id+new_id[-1]
    return new_id

 

문제의 1~7단계 순서대로 설명드리겠습니다.

 

1단계: lower() 함수로 대문자->소문자 변경

 

2단계: re 의 sub() 함수로 숫자, 소문자, . , _ , -  제외한 나머지 문자는 공백처리

 

3단계:  . 이 두 개 이상인 경우 . 하나로 처리 re.sub('[.]{2,}','.', 문자열)

 

4단계:  ^[.] 이용,  . 이 앞에 있으면 공백처리, [.]$ 이용, . 이 끝에 있으면 공백처리

 

5단계: 문자열의 개수를 세어야 된다. 파이썬에서 길이를 재는 len함수는  '                 ' 를 0이 아닌, 칸 수만큼 개수를 내보낸다.

따라서 ''.join(list(...))를 이용해 문자열 -> 리스트 -> 공백이 없는 문자열로 변환한다.

 

6단계: 문자열의 개수가 16개 이상이면 15개까지 잘라낸다. 이후   [.]$ 이용, . 이 끝에 있으면 공백처리

 

7단계: 문자열의 개수가 3보다 작으면 문자열 개수가 3이 될때까지 마지막 문자열을 문자열 끝에 붙인다.

본 게시물은 리뷰기반 식당 추천 리스트를 생성하는 과정으로, 사용한 데이터는 망고플레이트의 제주도 맛집 데이터입니다. 리뷰 기반 감성분석으로 감성점수를 산출하고, [가격, 맛, 서비스, 분위기, 웨이팅] 카테고리별 음식점 추천 리스트를 생성했습니다.

 

*사용자의 위치를 반영하기 위해 Geocoding을 해서 반경 30Km이내 음식점으로 필터링했습니다.

Geocoding 은 네이버 Api를 이용했으며 R 코딩을 했습니다.

 

아이디어 구현 과정은 다음과 같습니다.

 

 

[목차]

1. 필요한 패키지 설치 및 임포트

2. 망고플레이트 제주도 맛집 링크 수집

3. 망고플레이트 제주도 맛집 식당명, 리뷰, 평점, 주소 등등 수집

4. 맛집 리뷰 형태소분석

5. 맛집 리뷰 단어 빈도 추출

6. Word2Vec과 단어-빈도 행렬을 이용한 리뷰와 카테고리별 연관성 점수 계산

7. 카테고리와 단어의 코사인 유사도 행렬 구하기

8. 단어=문서 행렬 (Term-Document Matrix)

9. 감성분석

10. 생성된 고객 A에 대한 추천리스트

 


 

1. 필요한 패키지 설치 및 임포트

 

!pip3 install beautifulsoup4

import csv
import time
import pandas as pd
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from selenium import webdriver
import time
import pandas as pd
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup

 

2. 망고플레이트 제주도 맛집 링크 수집

 

wd = webdriver.Chrome('chromedriver',chrome_options=chrome_options)
wd.get("https://www.mangoplate.com/top_lists/1095_summerguide_jeju")

post_list = []
title_list=[]

for i in range(1,11):
  ll = wd.find_element(By.XPATH, "//*[@id='contents_list']/ul/li["+str(i)+"]/div/figure/figcaption/div/span/a").get_attribute('href')
  post_list.append(ll)

pageNum = 1

while pageNum < 7:

    element=wd.find_element(By.XPATH, '//*[@id="contents_list"]/div/button')
    wd.execute_script("arguments[0].click();", element)
    time.sleep(1)
    for i in range(pageNum*10+1,pageNum*10+11):
      try:
        ll = wd.find_element(By.XPATH, "//*[@id='contents_list']/ul/li["+str(i)+"]/div/figure/figcaption/div/span/a").get_attribute('href')
        post_list.append(ll)
      except NoSuchElementException:
        print(i)  
    pageNum += 1
    
post_infos = pd.DataFrame({'link':post_list})

post_infos.to_csv('data_공모전_link.csv',index=False)

 

3. 망고플레이트 제주도 맛집 식당명, 리뷰, 평점, 주소 등등 수집

 
title=[]
review=[]
ID=[]
taste=[]
load=[]
t_1=[] #가고싶다수
t_2=[] #평점

for jj in range(len(post_infos)):
  pageNum=0

  wd = webdriver.Chrome('chromedriver',chrome_options=chrome_options)
  wd.get(post_infos.loc[jj,'link'])
  N=wd.find_element(By.XPATH,'/html/body/main/article/div[1]/div[1]/div/section[3]/header/ul/li[1]/button/span').text

  if int(N)<=5:  
    req = wd.page_source
    soup=BeautifulSoup(req, 'html.parser')
    aa = soup.find_all('p',"RestaurantReviewItem__ReviewText")
    bb= soup.find_all('span','RestaurantReviewItem__RatingText')
    cc=soup.find_all('span','RestaurantReviewItem__UserNickName')
    dd=soup.find('h1','restaurant_name').text
    ee=soup.find('div','Restaurant__InfoItemLabel--RoadAddressText').text
    ff=soup.find('span','cnt favorite').text
    gg=soup.find('strong','rate-point').text

    for i in range(len(aa)):
      review.append(aa[i].text)
      taste.append(bb[i].text)
      ID.append(cc[i].text) 
      title.append(dd)
      load.append(ee)
      t_1.append(ff)
      t_2.append(gg)

  if int(N)%5==0:
    N_1=int(N)//5-1
  else:
    N_1=int(N)//5

  for _ in range(N_1+1):
    element=wd.find_element(By.XPATH, '/html/body/main/article/div[1]/div[1]/div/section[3]/div[2]')
    wd.execute_script("arguments[0].click();", element)
    time.sleep(3)

  req = wd.page_source
  soup=BeautifulSoup(req, 'html.parser')
  aa = soup.find_all('p',"RestaurantReviewItem__ReviewText")
  bb= soup.find_all('span','RestaurantReviewItem__RatingText')
  cc=soup.find_all('span','RestaurantReviewItem__UserNickName')
  dd=soup.find('h1','restaurant_name').text
  ee=soup.find('div','Restaurant__InfoItemLabel--RoadAddressText').text
  ff=soup.find('span','cnt favorite').text
  gg=soup.find('strong','rate-point').text

  for i in range(len(aa)):
    review.append(aa[i].text)
    taste.append(bb[i].text)
    ID.append(cc[i].text) 
    title.append(dd)
    load.append(ee)
    t_1.append(ff)
    t_2.append(gg)

TOTAL = pd.DataFrame({'review':review, 'ID':ID, 'taste':taste, 'title':title,'load':load, '가고싶다':t_1, '평점':t_2})

for i in range(len(TOTAL['review'])-1):
  if TOTAL.loc[i,'review'] in TOTAL.loc[i+1:,'review'].tolist():
    print(TOTAL.loc[i,'review'])

TOTAL.to_csv('data_공모전_RAW.csv',index=False)
 
 

4. 맛집 리뷰 형태소분석

 

from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
TOTAL=pd.read_csv('/content/drive/MyDrive/data_공모전_RAW_1116.csv',sep=',',encoding="cp949")
import re
okt=Okt()
ii=[]
data_1=TOTAL.dropna(axis=0)
PP=[]
AA=[]
BB=[]
CC=[]
DD=[]
EE=[]
FF=[]
PP_1=[]

for i in range(len(data_1)):
  PP.append(data_1.iloc[i,0]) #review
  PP_1.append(data_1.iloc[i,0]) #raw
  AA.append(data_1.iloc[i,1]) #ID
  BB.append(data_1.iloc[i,2]) #title
  CC.append(data_1.iloc[i,3]) #taste
  DD.append(data_1.iloc[i,4]) #load
  EE.append(data_1.iloc[i,5])
  FF.append(data_1.iloc[i,6])

for i in range(len(PP)):
  PP[i]=re.sub("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]"," ", PP[i])
  PP[i]=okt.morphs(PP[i],norm=True, stem=True)
  PP[i]=[x for x in PP[i] if len(x)>1]
  PP[i]=' '.join(PP[i])

data_2=pd.DataFrame({'review':PP,'ID':AA,'title':BB,'taste':CC,'raw':PP_1,'load':DD,'가고싶다':EE,'평점':FF})

hangul = re.compile('[ㄱ-ㅣ가-힣]+')

for i in range(len(data_2)):
  if len(hangul.findall(data_2.iloc[i,0]))>1:
    if len(data_2.iloc[i,0])>1:
      ii.append(i)

data_3=data_2.iloc[ii,:]
data_3.to_csv('data_공모전_Noun_1116.csv',index=False)

 

5. 맛집 리뷰 단어 빈도 추출

 

##단어 빈도 추출 결과, 카테고리: 가격, 맛, 분위기, 서비스, 웨이팅 카테고리를 생성함
A=data_3.review.to_list()
B=[]
for i in range(len(A)):
  B+=A[i].split(' ')
words_count={}
for word in B:
    if word in words_count:
        words_count[word] += 1
    else:
        words_count[word] = 1
sorted_words = sorted([(k,v) for k,v in words_count.items()], key=lambda word_count: -word_count[1])
print([w[0] for w in sorted_words])

 

6. Word2Vec과 단어-빈도 행렬을 이용한 리뷰와 카테고리별 연관성 점수 계산

 

##Word2Vec 카테고리별 코사인 유사도가 높은 단어를 구할 수 있다.
X=[[]]*len(data_3)
for i in range(len(data_3)):
    X[i] = data_3.iloc[i,0].split(' ')

from gensim.models import Word2Vec
model_w = Word2Vec(sentences=X,size=500,window=5,min_count=2,workers=4,sg=1)
df1=pd.DataFrame(model_w.wv.most_similar('맛있다',topn=20),columns=['단어','맛있다'])
df2=pd.DataFrame(model_w.wv.most_similar('가격',topn=20),columns=['단어','가격'])
df3=pd.DataFrame(model_w.wv.most_similar('웨이팅',topn=20),columns=['단어','웨이팅'])
df4=pd.DataFrame(model_w.wv.most_similar('서비스',topn=20),columns=['단어','서비스'])
df5=pd.DataFrame(model_w.wv.most_similar('분위기',topn=20),columns=['단어','분위기'])

 

A=list(set(df1.단어.tolist()+df2.단어.tolist()+df3.단어.tolist()+df4.단어.tolist()+df5.단어.tolist()))
A=sorted(A)
pd.DataFrame(A).to_csv('data_공모전_단어리스트_ver4.csv',index=False)

 

7. 카테고리와 단어의 코사인 유사도 행렬 구하기

 

##카테고리와 단어의 코사인 유사도 행렬을 구할 수 있다.
from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
A=pd.read_csv('/content/drive/MyDrive/data_공모전_단어리스트_ver4_1.csv',sep=',',encoding="cp949")
A=A.iloc[:,0].tolist()
A=sorted(A)

B_맛있다=[]
B_가격=[]
B_웨이팅=[]
B_서비스=[]
B_분위기=[]
for i in range(len(A)):
  B_맛있다.append(model_w.wv.similarity('맛있다',A[i]))
  B_가격.append(model_w.wv.similarity('가격',A[i]))
  B_웨이팅.append(model_w.wv.similarity('웨이팅',A[i]))
  B_서비스.append(model_w.wv.similarity('서비스',A[i]))
  B_분위기.append(model_w.wv.similarity('분위기',A[i]))
B_total=pd.DataFrame({'맛있다':B_맛있다,'가격':B_가격,'웨이팅':B_웨이팅,'서비스':B_서비스,'분위기':B_분위기}).transpose()
B_total.columns=[A]
B_total[B_total<0.8]=0
B_total.to_csv('data_공모전_단어리스트_ver3_1.csv',index=False)

 

8. 단어=문서 행렬 (Term-Document Matrix)

 

from sklearn.feature_extraction.text import CountVectorizer
cv=CountVectorizer()
for i in range(len(X)):
  globals()['val_{}'.format(i)]=[]
B=[]
for i in range(len(X)):
  for j in X[i]:
    if j in A:
      globals()['val_{}'.format(i)].append(j)
      B.append(i)
C=[]
for j in range(len(X)):
  if j not in list(set(B)):
    C.append('')
  else:
    C.append(' '.join(globals()['val_{}'.format(j)]))
 DTM_array=cv.fit_transform(C).toarray()
 feature_names=cv.get_feature_names()
 DTM_DataFrame=pd.DataFrame(DTM_array,columns=feature_names)
##카테고리별 리뷰의 연관성을 알 수 있음
import numpy as np
Result=B_total.to_numpy() @ DTM_DataFrame.transpose().to_numpy()
round(pd.DataFrame(Result),4).to_csv('공모전_Relev_결과_1119_2.csv')
B_total.to_csv('공모전_Word2Vec_결과_1119_2.csv')
DTM_DataFrame.to_csv('공모전_DTM_결과_1119_2.csv')

 

9. 감성분석

 

##KNU 감성사전에서 팀원과 의논에 음식특화사전을 구축함, lexicon_1_최종사전
from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
lexicon=pd.read_csv('/content/drive/MyDrive/lexicon_1_최종사전.csv',sep=',',encoding="cp949")
X=[[]]*len(data_3)
for i in range(len(data_3)):
    X[i] = data_3.iloc[i,0].split(' ')
    
##감성점수를 부여하는 과정
A=[0]*len(data_3)
B=[0]*len(data_3)
C=[0]*len(data_3)
for i in range(len(data_3)):
  for j in range(len(X[i])):
    if X[i][j] in lexicon.word.tolist():
      A[i]+=lexicon.polarity.tolist()[lexicon.word.tolist().index(X[i][j])]
      B[i]+=1
for i in range(len(C)):
  if B[i]!=0:
    C[i]=round(int(A[i])/int(B[i]),2)
data_3[['감성점수']]=C
data_3.to_csv('data_공모전_Sent_1121_1.csv',index=False)

 

10. 생성된 고객A에 대한 추천리스트

추천점수 계산 방식: [(감성점수 * 연관성점수)+0.5(가고싶다수+평점)]

감성점수와 가고싶다수, 평점을 PCA분석한 결과, 감성점수 변수의 영향력이 컸습니다.

따라서 나머지 변수에 가중치를 0.5 곱했습니다.

 

제주도 맛집 추천리스트는 아래와 같습니다.

 순위 전체 가격 웨이팅 서비스 분위기
1 밀리우 스시호시카이 스시호시카이 밀리우 밀리우 밀리우
2 상춘재 상춘재 상춘재 상춘재 상춘재 상춘재
3 미영이네식당 밀리우 밀리우 스시호시카이 스시호시카이 스시호시카이
4 중문수두리보말칼국수 미영이네식당 숙성도 미영이네식당 미영이네식당 미영이네식당
5 숙성도 숙성도 오드랑베이커리 숙성도 숙성도 숙성도
6 스시호시카이 중문수두리보말칼국수 중문수두리보말칼국수 더스푼 중문수두리보말칼국수
7 진아떡집 더스푼 더스푼 더스푼 어우늘 더스푼
8 어우늘 우진해장국 어우늘 천짓골식당 중문수두리보말칼국수 어우늘
9 국수만찬 어우늘 천짓골식당 우진해장국 우진해장국 우진해장국
10 더스푼 천짓골식당 국수만찬 진아떡집 만선식당 진아떡집

 

 

* 데이콘(DACON)에서 주최한 '뉴스 토픽 분류 AI 경진대회' 모형을 공유하고자 게시물을 올렸습니다.

* 이 대회는 특정 모형으로 한국어 뉴스 헤드라인 데이터를 학습한 후 주제를 분류하는 것입니다.

 

Bi-LSTM 기법을 이용하여 한국어 뉴스 토픽 분류 모형을 만들었습니다. 임베딩은 Fasttext를 사용하였습니다.

 

먼저 Fasttext 임베딩 코드를 보여드리며 설명한 후 Bi-LSTM 기법을 적용하는 코드를 보여드리며 설명하겠습니다.

*코드는 Google Colaboratory 기준입니다

 

항목은 다음과 같습니다.

 

1. 필요한 패키지 설치

2. 데이터 설명 및 전처리

3. Fasttext 이용, 단어를 벡터로 변환

4. Bi-LSTM 기법 적용

 

그럼 시작하겠습니다.

 

1. 필요한 패키지 설치


[CODE]

!apt-get update 
!apt-get install g++ openjdk-8-jdk python-dev python3-dev 
!pip3 install JPype1-py3 
!pip3 install konlpy 
!JAVA_HOME="C:\Users\tyumi\Downloads"
from konlpy.tag import *  #한국어 형태소 분석
okt=Okt()
!pip3 install fasttext #fasttext 설치
import fasttext
import pandas as pd 
import re #정규표현식
from tqdm import tqdm_notebook
import numpy as np
from keras.layers import LSTM, Activation, Dropout, Dense, Input,Bidirectional
from keras.layers.embeddings import Embedding
from keras.models import Model
from keras import layers
from keras.callbacks import EarlyStopping
import keras
from keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

 

2. 데이터 설명 및 전처리


[CODE 2-1]

from google.colab import drive
drive.mount('/content/drive') 


test=pd.read_csv('/content/drive/MyDrive/test_data.csv')
train=pd.read_csv('/content/drive/MyDrive/train_data.csv')
sample_sub=pd.read_csv('/content/drive/MyDrive/sample_submission.csv')
topic_dict=pd.read_csv('/content/drive/MyDrive/topic_dict.csv')

 

[Explanation 2-1]

 

[CODE 2-1]은 구글 마운트를 이용해 데이터를 불러오는 과정입니다.

 

[그림 1] train data
[그림 2] 주제

- [그림 1]은 train data 입니다. 45654개의 관측값을 갖고 있는 것을 알 수 있습니다.

- title은 저희가 분석할 뉴스 헤드라인이고 topic_idx는 기사에 할당된 주제입니다.

- [그림 2]에 주제에 대한 설명이 있습니다.

- test data는 9131개의 관측값을 갖고 있습니다.

 

[CODE 2-2]

t_list=[]

for t in range(len(train['title'])):

  train['title'][t]=' '.join(re.compile('[가-힣]+').findall(train['title'][t]))



t_list=[]

for t in range(len(test['title'])):

  test['title'][t]=' '.join(re.compile('[가-힣]+').findall(test['title'][t]))



for i in range(len(train)):

  train['title'][i]=okt.nouns(train['title'][i])



for i in range(len(test)):

  test['title'][i]=okt.nouns(test['title'][i])



data_train = train['title']

data_test = test['title']

 

[Explanation 2-2]

 

- 정규표현식을 이용하여 train, test data의 title에서 한글을 제외한 나머지를 제거합니다.

- okt를 이용하여 처리된 train, test data의 title에서 명사만 추출합니다.

 

[그림 3] data_train

- data_train은 [그림 3]과 같습니다. 이로써 각 문장은 명사 단위로 구성된 리스트가 되었습니다.

 

3. Fasttext 이용, 단어를 벡터로 변환


[CODE 3-1]

with open('/content/drive/MyDrive/data_train.txt', "w") as f:
  for i in range(len(data_train)):
    f.write(' '.join(data_train.iloc[i])+'\n')
  f.close()
with open('/content/drive/MyDrive/data_test.txt', "w") as f:
  for i in range(len(data_test)):
    f.write(' '.join(data_test.iloc[i])+'\n')
  f.close()


model = fasttext.train_unsupervised(input='/content/drive/MyDrive/data_train.txt',model = 'skipgram', lr = 0.05,
dim = 100, ws = 5, epoch = 50,
minn = 1, word_ngrams = 6)

 

[Explanation 3-1]

 

[그림 4]data_train

- fasttext 모델의 입력값 형태로 만들기 위해 데이터를 메모장 파일로 저장합니다.([그림 4]와 같습니다.)

- 이후 data_train 파일로 fasttext 모델을 훈련시킵니다. (초모수 설정은 이명호님의 석사 학위 논문 '단어 임베딩과 LSTM을 활용한 비속어 판별 방법'을 참고하였습니다.)

 

[CODE 3-2]

#정수인덱싱과정
tokenizer = Tokenizer()
tokenizer.fit_on_texts(data_train)
vocab_size=len(tokenizer.word_index)+1
data_train = tokenizer.texts_to_sequences(data_train)
data_test = tokenizer.texts_to_sequences(data_test) 

#모든 문장에서 가장 긴 단어 벡터 길이 구하기
max_len=max(len(l) for l in data_train)

#max_len에 맞춰서 패딩하기
X_train = pad_sequences(data_train, maxlen = max_len)
X_test = pad_sequences(data_test, maxlen = max_len)

 

[Explanation 3-2]

 

- Tokenizer를 이용해서 data_train/data_test 에 있는 고유한 단어에 번호를 할당합니다. 

- 즉, data_train/data_test 를 정수 인덱싱 벡터로 변환하는 것입니다.

- 모든 문장의 단어 벡터 길이는 같아야 됩니다. 같지 않는 경우, 0으로 패딩해서 길이를 맞춰줍니다.

- 따라서 모든 문장에서 최대 단어 벡터 길이를 구한 후 패딩합니다.

 

[CODE 3-3]

#임베딩행렬 구하기
dd=list(tokenizer.word_index.keys())
embedding_matrix=[np.zeros(shape=(100,),dtype=float)]
for i in range(len(dd)):
  embedding_matrix.append(model.get_word_vector(dd[i]))
embedding_matrix=np.array(embedding_matrix)

 

[Explanation 3-3]

 

- 학습시킨 fasttext model로 data_train을 구성하는 각 단어의 벡터를 구합니다.

- 모두 합친 벡터를 embedding_matrix라고 합니다.

 

[그림 5] embedding_matrix

단어 단어벡터
대통령 [-0.04712268, -0.03183127, -0.24798341, ..., 0.10834852, -0.00924296, -0.08876684]
[-0.01681509, -0.02997315, -0.04271703, ..., 0.1574153 , 0.00186034, -0.07043125]

 

- 위의 표는 단어벡터를 생성한 예시입니다. 각 단어벡터는 15차원으로 구성됩니다.

- [그림 5]는 embedding_matrix로, (24418, 100) 차원을 가졌습니다. 24418은 data_train을 구성하는 고유한 단어의 수를

   나타냅니다. 100은 각 단어의 벡터 차원을 의미합니다.

 

4. Bi-LSTM  계층 구현


앞에서 구한 embedding_matrix를 이용해 Bi-LSTM 계층을 구현하겠습니다.

 

[CODE 4-1]

target = pd.get_dummies(train['topic_idx']).values

x_train=X_train
x_test=X_test
y_train=target

 

[Explanation 4-1]

 

- 뉴스 기사의 주제 분류를 목적으로 분석하기 때문에 'topic_idx'를 Y변수로 놓습니다.

- topic_idx를 더미 변수로 변환합니다.

- 앞에서 구한 embedding_matrix를 임베딩 계층의 가중치로 입력할 것입니다.

 

[CODE 4-2]

max_len=15
vocab_size=24418
embedding_dim=100
model_5=keras.Sequential([
keras.layers.Embedding(vocab_size, embedding_dim, weights=[embedding_matrix],input_length=max_len,trainable=True), 
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2,return_sequences=True)),
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2,return_sequences=True)),
keras.layers.Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2)),
keras.layers.Dense(7, activation='softmax')])

 

[Explanation 4-2]

 

- Keras는 인공 신경망 층을 구성하기 위해 Sequential()을 사용합니다.

- 첫번째 층은 Embedding 계층으로, 정수 인덱싱한 x_train 을 밀집 벡터로 맵핑합니다.

- 설명드리자면, 이 밀집 벡터는 신경망의 학습 과정에서 가중치가 학습되는 것과 같은 방식으로 훈련됩니다. 훈련 과정에서 단어는 모델이 풀고자하는 작업에 맞는 값으로 업데이트 됩니다.

- 두번째 층은 Bidirectional 계층이 감싼 LSTM 계층으로, 첫번째 요소값 100은 LSTM 계층을 거치며 출력될 차원값을 의미합니다.

- 세 개의 Bi-LSTM 계층을 쌓은 후 나오는 결과에 softmax함수를 적용합니다. 

- 이를 통해 각 관측값이 7개의 주제에 할당될 확률을 구할 수 있습니다.

 

[그림 6] model_5.summary() 결과

- [그림 6]은 model_5.summary() 결과로, 각 계층의 출력 차원을 확인하실 수 있습니다.

 

[CODE 4-3]

es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
mc = ModelCheckpoint('best_model.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)

from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.2, random_state=1000)

from tensorflow.keras.utils import to_categorical
model_5.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])
history = model_5.fit(X_train, y_train, epochs=15, callbacks=[es, mc], validation_data=(X_valid,y_valid),batch_size=60, validation_split=0.2)

predictions = model_5.predict(x_test)

pred_1=[]
for p in predictions:
  pred_1.append(np.argmax(p))

sample_sub['topic_idx']=pred_1
sample_sub.to_csv('Fasttext와3개BILSTM계층.csv',index=False)

[Explanation 4-3]

 

- 예측 결과 정확도가 81% 나왔습니다. 

제 게시물이 네이버/구글 검색에 뜨지 않는 것을 확인했습니다.. 이런 기운 빠지는 일이.. 손을 좀 봤는데 안 되면 고객 센터에 문의하기로 했습니다.

 

오늘은 연결 리스트를 활용한 알고리즘 문제를 풀어보겠습니다.

 

문제는 리트코드 #23. Merge k Sorted Lists 입니다.

https://leetcode.com/problems/merge-k-sorted-lists/

 

Merge k Sorted Lists - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

 

자 시작하겠습니다.

 

문제

 

문제: k개의 정렬된 리스트를 1개의 정렬된 리스트로 병합하라.

 

입력: [

1->4->5,

1->3->4,

2->6

]

 

출력: 1->1->2->3->4->4->5->6


이 문제에는 연결 리스트, 우선순위 큐 대한 개념이 필요합니다.

연결 리스트에 대한 개념은 https://byumm315.tistory.com/entry/%EB%A6%AC%ED%8A%B8%EC%BD%94%EB%93%9C-23-Merge-k-Sorted-Lists여기 있습니다!

 

(자료구조) 연결 리스트를 알아봅시다.

제가 요즘 코딩 테스트를 준비하면서 자료 구조도 함께 공부하고 있는데요. 연결 리스트라는 큰 난관에 부딪혀서 어려워하고 있습니다. 따라서 저처럼 연결 리스트를 어려워하시는 분들을 위해

byumm315.tistory.com

 

우선순위 큐에 대한 개념은 각자 알아봅시다..! 간단해요!

 

풀이

문제를 풀 때 연결 리스트와 우선순위 큐를 이용합니다. 우선순위 큐를 이용하면 내림차순/오름차순으로 데이터를 추출할 수 있기 때문입니다. 

 

답안 코드

 

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def mergeKLists(self,lists: List[ListNode]) -> ListNode:
        root=result=ListNode(None)
        heap=[]
        import heapq
    
        for i in range(len(lists)):
            if lists[i]: 
                heapq.heappush(heap, (lists[i].val,i,lists[i]))
        
        while heap: 
            node=heapq.heappop(heap) 
            idx=node[1] 
            result.next=node[2] 
            result=result.next 
            if result.next: 
                heapq.heappush(heap,(result.next.val,idx,result.next)) 
        return root.next

 

답안 코드 설명

 

# 연결 리스트 정의하기

class ListNode:


    def __init__(self, data=0, next=None):

        self.data = data
        self.next = next

 

class Solution:


    def mergeKLists(self,lists: List[ListNode]) -> ListNode:

    #lists에는 입력값이 들어갑니다.

    #전체적으로 리스트 형태이지만 각 원소는 연결 리스트 형태입니다.

    # 예) [1->3->4,  2->5,  6->1]

 

        root=head=ListNode(None)

         #ListNode(None)은 data와 next를 None으로 둔 연결 리스트입니다.

         #중요한 개념이 나옵니다. root와 head의 위치를 그림으로 표현하겠습니다.

         #[그림 1]과 같이 root 와 head는 같은 위치에 있는 None 인 연결 리스트입니다.

 

[그림 1] 빈 연결 리스트

 

        heap=[]

        import heapq

         #우선순위 큐를 사용하기 위해 모듈을 import 합니다.

 

        for i in range(len(lists)):

         #lists의 각 원소는 연결 리스트입니다.

 

            if lists[i]:   
                heapq.heappush(heap, (lists[i].data,i,lists[i]))

                  #heappush를 이용해서 (연결리스트의 가장 앞에 있는 데이터값, i, 연결리스트)를 heap에 저장합니다. 

                  #예) lists의 0번째 원소, 1->4->5의 경우 (1, 0, 1->4->5)가 heap에 저장됩니다.

 

       while heap:

       #heap이 비어있을 때까지 계속 while문을 돌립니다.

 

        

           #Step 1. while문의 첫번째 단계 (풀어서 설명하겠습니다.)

 

 

             node=heapq.heappop(heap)

               #heapq.heappop을 이용하면 가장 작은 원소부터 오름차순으로 heap의 원소가 추출됩니다.

               #추출된 원소는 heap에서 제거됩니다.

               #예) heap=[(1,0,1->4->5), (1,1,1->3->4), (2,2,2->6)]일 때 heappop을 이용하면 (1,0,1->4->5)가 추출됩니다.

               #붉은색 위치의 숫자끼리 비교하면 첫번째, 두번째 원소가 가장 작고 그 중에서 파란색 위치의 숫자끼리 비교하면 첫번째 원소가 가장 작기 때문입니다.


             idx=node[1] 
             head.next=node[2] 

              #head의 뒤쪽 포인터가 node[2] (1->4->5)를 참조하기 때문에 [그림 2]와 같이 변경됩니다.

 

[그림 2] 구조

 

            head=head.next

             #head의 뒤쪽 포인터가 1->4->5를 참조하기 때문에 head는 1->4->5의 맨 앞 노드 nodeA로 옮겨집니다.

[그림 3] 구조

 

            print(root.next) 

            print(head.next)
            #root.next 는 1->4->5 이고 head.next는 4->5 입니다.    

 

            if head.next: 
                heapq.heappush(heap,(head.next.data,idx,head.next))

            print(heap)

            #heap은 [(4,0,4->5),(1,1,1->3->4),(2,2,2->6)] 입니다.

 

        return root.next

 

 

           #Step 2. while문의 두번째 단계를 설명하겠습니다. 

 

 

            node=heapq.heappop(heap)

              #heappop을 이용하면 가장 작은 원소부터 오름차순으로 heap의 원소가 추출됩니다.

              #추출된 원소는 heap에서 제거됩니다.

              #예) heap=[(4,0,4->5), (1,1,1->3->4), (2,2,2->6)] 일 때 heappop을 이용하면 (1,1,1->3->4)가 추출됩니다.

              #붉은색 위치의 숫자끼리 비교하면 두번째 원소가 가장 작기 때문입니다.


            idx=node[1] 
            head.next=node[2] 

             #head의 뒤쪽 포인터가 node[2] (1->3->4)를 참조하기 때문에 [그림 4]와 같이 변경됩니다.

 

[그림 4] 구조

         head=head.next

          #head의 뒤쪽 포인터가 1->3->4를 참조하기 때문에 head는 1->3->4의 맨 앞 노드 nodeB로 옮겨집니다.

[그림 5] 구조

        print(root.next) 

        print(head.next)
        #root.next 는 1->1->3->4 이고 head.next는 3->4 입니다.

           
        if head.next: 
             heapq.heappush(heap,(head.next.data,idx,head.next))

        print(heap)

        #heap은 [(4,0,4->5),(3,1,3->4),(2,2,2->6)] 입니다.
       

   return root.next

 

이 과정을 반복하다보면 heap이 빈 값이 됩니다. 이때 root.next를 결과값으로 리턴하면 1->1->2->3->4->4->5->6 이 나옵니다.

 

감사합니다!!!

제가 요즘 코딩 테스트를 준비하면서 자료 구조도 함께 공부하고 있는데요. 연결 리스트라는 큰 난관에 부딪혀서 어려워하고 있습니다. 따라서 저처럼 연결 리스트를 어려워하시는 분들을 위해 게시물을 만들었습니다.

 

게시물을 읽으신 모든 분들....... 연결 리스트 타파합시다. 화이팅입니다.

 

'Do it 자료구조' 책의 도움을 많이 받았습니다. 감사합니다.

 

https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=246435695 

 

Do it! 자료구조와 함께 배우는 알고리즘 입문 : 파이썬 편

213개의 그림과 함께 저자의 친절한 설명으로 개념을 쉽게 이해할 수 있다. 이를 바탕으로 136개의 파이썬 실습 예제로 직접 코딩하며 개발 능력을 키울 수 있다. 눈으로 읽고, 코드를 한 줄씩 입

www.aladin.co.kr

 

 

오늘의 게시물은 아래 항목으로 나눠집니다.

 

1. 연결 리스트란?

2. 머리 노드와 꼬리 노드란?

3. 파이썬으로 연결 리스트를 만들어보기 

 

그럼 시작하겠습니다.

 

1. 연결 리스트란?

 

연결 리스트는 데이터를 사슬처럼 연결한 구조입니다. 연결 리스트에서는 각 원소들을 노드라고 부릅니다. 이 노드는 data와 next, pointer 로 구성됩니다. data는 노드의 data, next와 pointer는 뒤쪽 노드(바로 옆에 있는 노드)에 대한 참조를 의미합니다. [그림 1]은 연결 리스트의 기본적인 구조입니다. (연결 리스트 1->3->4 를 나타냈습니다) 

 

[그림 1] 연결 리스트의 구조

 

[그림 1]을 설명하자면 node A의 뒤쪽 포인터 next 가 node B를 참조하고 node B의 뒤쪽 포인터 next 가 node C를 참조합니다. 그럼 node C의 뒤쪽 포인터는 무엇을 참조할까요? 더이상 참조하는 대상이 없기 때문에 None을 참조합니다.

 

만약에 node C를 삭제하고 싶다면 어떻게 할가요? 우선 node A부터 node B까지 이동한 후 node B의 뒤쪽 포인터가 None을 참조하면 됩니다. 연결 리스트에서 특정 노드를 찾을 때 또는 특정 노드에 다가가고 싶을 때 처음 노드부터 연결된 순서로 이동해야 됩니다. 

 

2. 머리 노드와 꼬리 노드란?

 

리스트와 다르게 연결 리스트에는 머리 노드가 존재합니다. 머리 노드는 처음 시작하는 노드입니다.

 

[그림 1]에서는 머리 노드가 node A이겠죠? 이 머리 노드는 또다른 존재 head에 의해서 선택받은 자입니다.

head는 머리 노드를 참조하는 존재입니다.  [그림 1]에서 head는 node A를 참조했으므로 node A가 연결 리스트의 머리 노드입니다.

 

만약 머리 노드를 새로운 노드 node G로 변경하려면 어떻게 할까요? head가 node G를 참조하여 '얘가 머리 노드이다'라고 선언하면 됩니다. 그리고 node G의 뒤쪽 포인터는 기존의 머리 노드 node A를 참조합니다.

 

[그림 1] 연결 리스트의 구조

 

머리 노드가 있으면 꼬리 노드도 있겠죠? node C의 뒤쪽 포인터는 더이상 참조하는 대상이 없기 때문에 None을 참조합니다. 이렇게 뒤쪽 포인터가 더이상 참조하는 게 없는 경우, 해당 노드를 '꼬리 노드' 라고 합니다.

 

3. 파이썬으로 연결 리스트를 만들어보기 

 

이번에는 연결 리스트를 파이썬에 입력해봅시다.

우선 코드에 대한 설명을 한 후, 1->3->4->5를 입력해보겠습니다.

이후 연결 리스트를 구현하는 다른 방법을 알려드리고 마치겠습니다.

 

코드 설명

 

from __future__ import annotations
from typing import Any, Type

#연결 리스트용 노드 클래스
class Node:

 

#첫 번째로 head, data, next, current 초기값을 정의합니다.     
    def __init__(self, data:Any=None, next: Node=None):
        #초기화
        self.data=data #데이터에 대한 참조
        self.next=next  #뒤쪽 노드에 대한 참조
        self.head=None #머리 노드에 대한 참조
        self.current=None

        #현재 주목하고 있는 노드에 대한 참조(리스트에서 노드를 검색하여, 그 노드를 주목한 직후에 노드를 삭제하는 등의 용도로 사용됩니다.)
        

#두 번째로 머리 노드를 생성합니다. [그림 2]
    def add_first(self, data:Any)->None:
        ptr=self.head

        #삽입하기 전의 머리 노드, 저희는 머리 노드를 생성하는 단계이니 None이겠죠?
        self.head=self.current=Node(data,ptr)

         #삽입할 노드 node A를 Node(data,ptr)로 생성합니다.

         #해당 노드의 데이터가 data가 되고, 해당 노드의 뒤쪽 포인터가 참조하는 곳은 ptr이 됩니다.  

        return self.head

 

[그림 2] 머리 노드 생성 과정

        
#세 번째로 만들어진 연결 리스트의 끝 노드에 새로운 노드를 삽입합니다. [그림 3]

    def add_last(self, data=Any):
        if self.head is None:

            self.add_first(data)

            #만약에 head가 없는 경우, 앞에서 정의한 add_first함수로 head를 생성합니다.

        else:
            ptr=self.head

            #While문을 이용해서 꼬리 노드를 찾습니다.
            while ptr.next is not None:
                ptr=ptr.next

               #연결 리스트에서 특정 노드를 찾을 때 무조건 머리 노드부터 하나씩 지나면서 이동합니다.

            ptr.next=self.current=Node(data,None)
            #꼬리 노드를 찾았으면 꼬리 노드 옆에 새로운 node D Node(data,None)를 추가합니다. 

            #Node(data,None)에서 새로운 node D의 뒤쪽 포인터가 참조하는 것이 None인 이유는

            #node D가 곧 꼬리 노드가 되기 때문입니다.

            return ptr

 

[그림 3] node D 추가 과정

 

구현하기

A=Node() #클래스 할당
A.add_first(1) #머리 노드 지정 (node A)
A.add_last(3) #꼬리 노드에 새로운 노드 추가 (node B)
A.add_last(4) #꼬리 노드에 새로운 노드 추가 (node C)
A.add_last(5) #꼬리 노드에 새로운 노드 추가 (node D)

 

결과 확인하기 (긴가민가 합니다. 틀리면 바로 댓글 주세요!)

 

[그림 4] 노드 생성 후 결과

 

[그림 4]는 앞의 코드 구현한 후 object 항목에서 가져왔습니다. 1->3->4->5가 잘 들어갔는지 확인해보겠습니다.

 

- 우선 path의 A.head를 봅시다. A.head는 머리 노드 node A를 참조합니다. 따라서 A.head의 data (A.head.data)는 node A의 데이터 1 입니다.  

 

- path의 A.head.next 를 봅시다. A.head.next는 머리 노드 node A의 뒤쪽 포인터가 참조하는 노드입니다. [그림 3]에 따르면 node B겠죠? 마찬가지로 A.head.next의 data (A.head.next.data)는 node B의 데이터 3 입니다.

 

- path의 A.head.next.next 를 봅시다. A.head.next.next는 node B의 뒤쪽 포인터가 참조하는 노드입니다. [그림 3]에 따르면 node C겠죠? A.head.next.next의 data (A.head.next.next.data)는 node C의 데이터 4 입니다.

 

- path의 A.head.next.next.next 를 봅시다. A.head.next.next.next는 node C의 뒤쪽 포인터가 참조하는 노드입니다. [그림 3]에 따르면 node D겠죠? A.head.next.next.next의 data (A.head.next.next.next.data)는 node D의 데이터 5 입니다.

 

**연결 리스트를 구현하는 방법이 하나 더 있습니다.

https://lsjsj92.tistory.com/461 블로그에서 더 간단한 방법을 찾았습니다. 

근데 코드를 갖고오는게 저작권 침해 같고 걱정돼서 링크만 올렸습니다. 여기서 더 쉽게 구현할 수 있습니다!!

 

여러분, 사실 이 게시물은 빅픽처입니다. 다음 게시물에서는 리트코드 문제 풀이를 할건데요. 이 문제를 풀기 위해서 연결 리스트 개념이 꼭 필요하답니다. https://leetcode.com/problems/merge-k-sorted-lists/

 

Merge k Sorted Lists - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

그럼 곧 봐요!!

 

오늘은 역전파에 대하여 알아보겠습니다.

 

순서는 아래와 같습니다.

 

1. 로지스틱 회귀란?

2. 로지스틱 회귀를 단일층 신경망으로 구현하기

3. 로지스틱 손실 함수를 경사 하강법에 적용하기

 

입력층과 출력층으로만 구성된 단일층 신경망에서 역전파가 어떻게 진행되는지 알아보고자 합니다. 따라서 로지스틱 회귀를 구현하며 단일층 신경망에서의 역전파를 알아보겠습니다.

 

이 게시물은 Do it! 딥러닝 입문 책을 정리하였습니다.

 

http://www.yes24.com/Product/Goods/78896574

 

1. 로지스틱 회귀란?


로지스틱 회귀의 목적은 종속 변수(Y)와 독립 변수(X) 사이 관계를 구체적인 함수로 나타내서 예측 모델로 사용하는 것입니다. 독립 변수의 선형 결합으로 종속 변수를 설명한다는 관점에서는 선형 회귀 분석과 유사합니다.

*선형 결합: 일차식과 같습니다.

 

하지만 로지스틱 회귀는 종속 변수가 범주로 구성된 데이터를 대상으로 합니다. 입력 데이터가 주어졌을 때 로지스틱 회귀를 적용한 결과는 특정 분류로 나뉘기 때문에 일종의 분류 기법이라고도 합니다. 종속 변수의 범주가 2개인 경우 로지스틱 회귀라고 부르고 2개 이상인 경우 다항 로지스틱 회귀라고 부릅니다.

 

이번 시간에 저희는 종속 변수의 범주가 2개인 경우 사용하는 로지스틱 회귀를 공부합니다.

 

(출처: 위키백과 로지스틱 회귀)

 

로지스틱 회귀식은 [수식 1] 과 같습니다.

 

[수식 1] 로지스틱 회귀식

 

[수식 1]의 (1) 에 따르면 종속 변수 Y가 0과 1의 범주를 가질 때 Y가 1일 확률을 p라고 합니다.

(2)에 따르면 맨 오른쪽 항은 가중치 w1, w2와 편향 b를 입력값 x1, x2와 선형 결합한 일차식입니다.

그럼 확률 p는 어떻게 구할까요? (2)를 풀어낸 결과, 식 (3) 으로 구하는 것을 알 수 있습니다.

 

2. 로지스틱 회귀를 단일층 신경망으로 구현하기


[그림 1]은 로지스틱 회귀를 단일층 신경망으로 구현한 것입니다.

 

* 단일층 신경망: 은닉층이 없고 입력층과 출력층으로만 구성된 신경망입니다.

 

로지스틱 회귀에서 저희의 목적은 y가 0인지 1인지 예측하는 것입니다. [수식 1]의 (3)을 이용해서 y가 1인 확률을 구하고 임계 함수를 적용하여 y를 0 또는 1로 분류할 것입니다.

 

 

[그림 1] 로지스틱 회귀 단일층 신경망

 

[그림 1]을 통해 식으로 살펴보았던 로지스틱 회귀가 어떻게 신경망으로 구현되는지 단계별로 설명하겠습니다.

 

Step1. 입력층에 입력값 x1과 x2가 들어갑니다.

 

Step2. 출력층에서 입력값들과 가중치 w1, w2 그리고 편향 b을 선형 결합합니다. 그 결과를 z라고 합니다.

 

Step3.  z에 로지스틱 함수를 적용합니다. (이것은 시그모이드 함수라고도 합니다.)

          z와 같은 가중합을 출력값 형태인 a로 변환하는 함수를 활성화 함수라고 합니다. 활성화 함수에는 시그모이드

          함수, 소프트 맥스 함수 등이 해당됩니다.

 

* 가중합: 각 입력값과 가중치를 곱한 후 이 결과들과 절편을 합한 것을 의미합니다.

* 활성화 함수: 입력값들의 가중합을 출력값으로 변환하는 함수입니다.

 

Step4.  Step3의 결과로 y=1일 확률이 나오고 이것을 a라고 합니다. 마지막으로 a에 임계함수를 적용하여 0.5보다 크면 1, 작으면 0으로 분류합니다.

 

이로써 로지스틱 회귀 과정을 알아보았습니다. 그런데 궁금한 점이 하나 있습니다. 가중치와 절편은 어떻게 구할까요?

 

3번 항목에서 가중치와 절편을 업데이트하는 방법을 차근차근 배워봅시다.

 

3. 로지스틱 손실 함수를 경사 하강법에 적용하기


로지스틱 회귀와 같이 분류하는 알고리즘의 목표는 무엇일까요? 바로 올바르게 분류된 데이터의 비율을 높이는 것입니다. 그럼 알맞게 분류된 데이터의 비율을 높이는 방향으로 가중치와 절편을 업데이트 해야겠죠? 여기에 꼭 필요한 개념이 있는데요. 바로 손실 함수와 경사 하강법입니다.

 

* 손실 함수: 예측값과 실제값의 차이를 함수로 정의한 것을 말합니다. 손실 함수는 신경망의 가중치, 절편을 수정하는 기준이되는 함수입니다. 제곱 오차(예측값과 실제값 차이의 제곱합), 크로스 엔트로피 등이 손실 함수로 사용됩니다.

* 경사 하강법: 어떤 손실 함수가 정의되었을 때, 손실 함수의 값이 최소가 되는 지점을 찾아가는 방법입니다.

 

[그림 2]를 통해 로지스틱 회귀의 가중치와 절편을 업데이트 하는 과정을 단계별로 설명하겠습니다.

 

[그림 2] 로지스틱 회귀 역방향 계산

 

Step1. 로지스틱 회귀의 손실함수 L은 [수식 2]와 같습니다.  y는 실제값, a는 시그모이드 함수(활성화 함수)가 출력한 값 입니다. 

 

[수식 2] 로지스틱 회귀의 손실함수

 

Step2. Step1에서 정의한 손실함수(L)를 가중치(w)와 절편(b)에 대해 각각 미분한 기울기를 구할 것입니다. 이후 가중치와 절편을 업데이트 하겠습니다. 기울기를 직관적으로 구하기 어렵기 때문에 미분의 연쇄법칙을 이용하겠습니다. 

 

[수식 3] 미분의 연쇄법칙

 

[수식 3]의 왼쪽항은 손실함수를 가중치에 대해 미분한 기울기, 오른쪽항은 미분의 연쇄법칙으로 정리한 것입니다.

 

[수식 4] 기울기 식

 

[수식 4]는 [수식 3]의 오른쪽항을 구성하는 기울기를 구한 식입니다. 

 

[수식 5] 손실함수를 가중치, 절편에 대해 미분

 

[수식 5]는 미분의 연쇄법칙을 이용해서 기울기를 계산한 결과입니다. 

참고로 손실 함수에 대한 미분이 연쇄 법칙에 의해 진행되는 구조를 보고 그레디언트(기울기)가 역전파된다고 말합니다.

 

Step3.  로지스틱 회귀의 가중치 업데이트를 위해 손실 함수(L)를 가중치(w)에 대해 미분한 식을 가중치에서 뺍니다. 절편(b)도 마찬가지로 계산하면 됩니다. 계산식은 [수식 6]입니다.

 

[수식 6] 가중치 업데이트

이거 새 게시물을 추가하는 법이 독특하네요.. 한참 찾았습니다..ㅠㅠ

 

저의 두 번째 게시물은 "MNIST 데이터를 CNN에 적용하여 분류하기"입니다.

 

MNIST 데이터는 [그림 1]처럼 손으로 쓴 숫자 이미지로 구성되어 있습니다.

 

[그림 1] MNIST 데이터 시각화

 

저는 이 데이터를 이용해서 실제 숫자를 예측해보겠습니다. 

 

MNIST 데이터는 Kaggle에서 다운로드하였습니다.

 

링크: https://www.kaggle.com/c/digit-recognizer/data

 

Digit Recognizer | Kaggle

 

www.kaggle.com

 

train, test, sample_submission 데이터로 구성되어 있으며 sample_submission 데이터는 제출용이니 생략합니다.

 

우선 train 데이터의 관측값은 0-9 중 숫자 하나를 손으로 적은 이미지로, 42000개의 관측값을 갖습니다.

 

이 이미지는 높이 28, 너비 28 픽셀로 총 784 픽셀입니다. 각 픽셀에는 하나의 픽셀 값이 연결되어 해당 픽셀의 밝기 또는 어두움을 나타내며 숫자가 높을수록 어두워집니다. 이 픽셀 값은 0에서 255(포함) 사이의 정수입니다.

 

train 데이터의 맨 앞 열은 이미지에 있는 숫자를 나타내고 나머지 784개의 열은 해당 이미지가 갖고 있는 784개의 픽셀을 의미합니다.

 

따라서 맨 앞 열은 label 변수로 0-9 값을 갖고 나머지 784개의 열은 0에서 255 값을 갖습니다.

 

test 데이터는 label을 제외한 변수를 갖고 있으며 28000개의 관측값을 갖습니다.

 


MNIST 데이터 실습은 아래 항목으로 구성됩니다.

 

1. Colab으로 파일 불러오기

 

 1-1. 구글 마운트 하기

 1-2. 파일 불러오기

 

2. 데이터가 어떻게 생겼을까

 

3. 데이터를 전처리 하기

 

 3-1. train에서 label은 target, 나머지는 x_train으로 분리합니다.

 3-2. 데이터 표준화를 합니다.

 3-3. CNN모델 입력 형태로 차원을 변환합니다.

 

4. 모델 형성하기

 

5. 전체 코드

 


 

 

그럼 시작하겠습니다.

 

저는 Google Colaboratory를 이용하기 때문에 Colab 환경에 맞춰서 진행하겠습니다.

 

1. Colab으로 파일 불러오기

 

Colab으로 파일을 불러오는 방식은 다양합니다. 저는 Google Mount를 평소에 사용하기 때문에 이걸로 가겠습니다.

 

1-1. 구글 마운트 하기

 

 

위와 같이 코드를 치면 authorization code를 입력하는 란이 나옵니다. 위의 파란색 url에 접속하면 code를 받을 수 있습니다. code를 복사하여 입력란에 붙여 넣고 Enter를 치면 됩니다.

 

1-2. 파일 불러오기

 

위와 같이 입력하면 csv파일을 불러올 수 있습니다.

 

 

2. 데이터가 어떻게 생겼을까

 

train, test 데이터의 size를 알 수 있습니다. 

 

이번에는 train 데이터 42000개 중 0부터 3번째 데이터를 이미지로 직접 나타내 보겠습니다.

 

 

 

3. 데이터를 전처리하기

 

3-1. train에서 label은 target, 나머지는 x_train으로 분리합니다.

 

 

3-2. 데이터 표준화를 합니다.

 

 

픽셀이 0~255의 정수 값을 갖기 때문에 255로 나눠서 표준화하였습니다.

 

3-3. CNN모델 입력 형태로 차원을 변환합니다.

 

 

 

4. 모델 형성하기

 

[그림 2]에서는 이번 실습 때 사용하는 CNN 모델 프로세스를 자세히 보여줍니다.

 

[그림 2] CNN 프로세스

 

직접 코드로 구현해봅시다.

 

아래의 코드를 봅시다.

 

우선 모델 형성에 필요한 패키지를 설치하고요.

 

 

모델 층을 쌓는 코드에는 중간중간 설명을 적겠습니다. (하단에 정리한 코드 올려놓았습니다.)

 

model=keras.Sequential([

 

Sequential은 신경망 층을 쌓을 때 사용합니다.

앞으로 나오는 각 코드를 우리는 하나의 계층이라고 하겠습니다.

 

keras.layers.Conv2D(32, kernel_size=(3,3), input_shape=(28,28,1), padding='SAME', activation=tf.nn.relu)

 

첫 번째 층은 컨볼루션층입니다

맨 앞(32): Kernel(Filter) 수입니다.

kerel_size: Kernel의 크기를 지정합니다.

input_shape: 샘플 수를 제외한 입력 형태를 정의합니다. (행, 열, 채널 수)로 정의합니다.

                   (채널은 색상(R, G, B)을 의미하고 사용하는 데이터는 흑백 이미지이기 때문에 채널 수가 1입니다.)

Padding: padding이 'SAME'이면 출력 데이터 사이즈와 입력 데이터 사이즈가 동일합니다.

Activation: 활성화 함수를 설정합니다.

 

keras.layers.MaxPool2D(pool_size=(2,2), strides=(2,2), padding='SAME')

 

다음은 MaxPooling 계층입니다.

pool_size: Pooling 영역의 크기입니다.

strides: Pooling 영역이 이동하는 간격을 의미합니다.

 

keras.layers.Conv2D(64, kernel_size=(3,3), padding='SAME', activation=tf.nn.relu),

keras.layers.MaxPool2D(pool_size=(2,2), strides=(2,2), padding='SAME')

 

위의 코드를 반복합니다. (본 실습에서는 컨볼루션층을 두 번 거칩니다.)

 

keras.layers.Flatten()

 

앞에서는 Image data의 Feature Maps를 추출했습니다. 이번에는 실제 숫자로 분류하는 작업을 해보겠습니다.

Flatten 계층을 이용하여 3차원의 데이터를 1차원으로 변환합니다.

 

keras.layers.Dense(1024, activation='relu')

 

이후 완전 연결 계층 (Dense)를 적용합니다.

units: 1024는 완전 연결 계층을 거친 후 나오는 출력 데이터의 수를 의미합니다. 

 

keras.layers.Dense(10, activation='softmax')

 

마지막 완전 연결 계층은 units이 10이고 활성화 함수가 Softmax 입니다.

 

0에서 9 사이의 숫자를 예측하기 때문에 10개의 클래스로 분리됩니다.

따라서 이 계층을 지난 후 나오는 출력 데이터의 수가 10이 되어야 하구요.

 

활성화 함수로 Softmax를 쓰면 각 클래스로 예측될 확률을 알 수 있습니다.

예를 들면, 1로 분류될 확률이 0.1, 2로 분류될 확률이 0.3 등 (확률의 전체 합은 1이겠죠?!)

 

keras.layers.Dropout(0.25)

 

마지막으로 Dropout 계층입니다.

Dropout 계층은 과적합(over-fitting)을 막기 위한 방법으로 신경망이 학습 중일 때, 무작위로 신경망을 빼서 모델 학습을 방해함으로써, 과적합을 방지합니다.

 

[그림 3] CNN 모델 계층 정보

 

[그림 3]는 형성한 모델의 각 계층을 지나면서 출력되는 Output의 차원을 보여줍니다.

(Output Shape에서 확인 가능합니다.)

 

5. 전체 코드

 

다음과 같습니다.

 

from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
test=pd.read_csv('/content/drive/MyDrive/test.csv')
train=pd.read_csv('/content/drive/MyDrive/train.csv')
sample_sub=pd.read_csv('/content/drive/MyDrive/sample_submission.csv')

print("Train Data Size: ", train.shape)
print("Test Data Size: ", test.shape)

import matplotlib.pyplot as plt
import numpy as np
plt.style.use('seaborn')
fig, ax = plt.subplots(1,4, figsize=(10,10))
fig.tight_layout(pad=0.0)

for col in range(4):
    im_0 = np.array(train.drop("label", axis=1). iloc [col]). reshape(28,28)
    ax [col]. imshow(im_0, cmap="gray")
    ax [col]. axis("off")

target = train ['label']
x_train = train.drop('label', axis = 1)

x_train=x_train/255
test=test/255

x_train = np.array(x_train). reshape((-1,28,28,1)) 
x_train.shape

test = np.array(test). reshape((-1,28,28,1))
test.shape

from tensor flow.keras.utils import to_categorical
target = to_categorical(target, num_classes = 10)
target.shape

import tensorflow as tf
from tensorflow import keras

model=keras.Sequential([
keras.layers.Conv2D(32, kernel_size=(3,3), input_shape=(28,28,1), padding='SAME', activation=tf.nn.relu),
keras.layers.MaxPool2D(pool_size=(2,2), strides=(2,2), padding='SAME'),
keras.layers.Conv2D(64, kernel_size=(3,3), padding='SAME', activation=tf.nn.relu),
keras.layers.MaxPool2D(pool_size=(2,2), strides=(2,2), padding='SAME'),
keras.layers.Flatten(),
keras.layers.Dense(1024, activation='relu'),
keras.layers.Dense(10, activation='softmax'),
keras.layers.Dropout(0.25)])

model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

 

model.summary() #각 계층별 출력 데이터의 차원을 확인하실 수 있습니다.

 

model.fit(x_train, target, epochs=5) #모델 학습

 

pred=np.argmax(model.predict(x_train), axis=-1) #분류한 결과를 확인하실 수 있습니다.

 


 

출처 1: https://www.kaggle.com/mhmdsyed/digital-recognizer-simple-cnn-model

 

Digital Handwriting Recognizer ✍️ |

Explore and run machine learning code with Kaggle Notebooks | Using data from Digit Recognizer

www.kaggle.com

출처 2: https://dsbook.tistory.com/71?category=780563 

 

합성곱 신경망, Convolutional Neural Network (CNN)

완전 연결 계층, Fully connected layer (JY) Keras 사용해보기 1. What is keras? 케라스(Keras)는 텐서플로우 라이버러리 중 하나로, 딥러닝 모델 설계와 훈련을 위한 고수준 API이다. 사용자 친화적이고 모델의

dsbook.tistory.com

제가 블로그가 처음이라서 출처를 어떻게 남기는지, 직접 말씀드려야 하는지 잘 모르겠습니다.. 

만약 이 방법이 틀렸음 알려주신다면 감사드리겠습니다!

피드백, 질문 언제든지 환영입니다!!

 

다음은 RNN 이론을 올리겠습니다!

CNN은 Convolutional Neural Networks의 줄임말로 알다가도 모르는 기법이다.

그래서 이론을 확실히 하기 위해서 CNN 개념 및 원리를 올려본다.

 

최대한 알기 쉽게 설명하도록 하겠다. 피드백은 언제나 환영이다. 댓글도 Welcome이다.

 

이 글의 항목은 아래와 같다.

 

 

1. CNN의 NN, Neural Networks(신경망) 이란 무엇인가?

2. CNN이란 무엇인가?

 

 

 

1. CNN의 NN, Neural Networks(신경망) 이란 무엇인가?

 

 

  [그림 1]은 신경망의 원리를 잘 보여주는 예시로, 이 예시를 바탕으로 신경망을 설명하겠다.

 

[그림1] Neural Network 원리

 

  Step.1 Inputs은 데이터를 넣는 곳으로 만약 데이터 [1,2,3,4,5]를 신경망에 넣고 싶으면 Inputs에 [1,2,3,4,5]를 할당하면 된다. (x1=1, x2=2,..., x5=5) 

 

  Step.2 w1부터 w5는 가중치이다. 가중치와 Inputs의 각 원소를 곱한다.

     (x1*w1, x2*w2,..., x5*w5)

 

  Step.3 Step.2에서 구한 가중치와 원소의 곱을 모두 합한다.

     (x1*w1+x2*w2+...+x5*w5)

 

  Step.4 Step.3에 활성화 함수를 씌워서 출력한다. 활성화 함수는 Activation Function이며, 시그모이드, 렐루, 소프트 맥스 등이 사용된다.   

 

 

 

2. CNN이란 무엇인가?

 

 

2번 섹션은 세 개의 항목으로 나눴다.

 

2-1. 합성곱(Convolution)을 알아보자

2-2. CNN의 프로세스를 통째로 알아보자

2-3. CNN의 프로세스를 세부적으로 알아보자

 

1번 섹션에서는 1차원 데이터를 신경망에 적용했다. 그렇다면 이미지와 같은 2차원 데이터는 어떻게 신경망에 적용할까? 

 

정답은 지금부터 알아볼 CNN이다.

 

CNN은 Convolutional Neural Networks의 약자로, 핵심은 신경망에서 사용된 연산을 합성곱으로 교체한 것이다.

 

새로운 개념이 나왔다.

 

합성곱이 무엇일까? 2-1 섹션에서 차근차근 알아보자.

 

 

2-1. 합성곱(Convolution)을 알아보자

 

 

합성곱은 곱셈과 덧셈으로 이루어진 연산이다.

 

[그림 2]에서 행렬 A와 행렬 B를 합성곱 하여 행렬 C를 만들었다.

 

행렬 A와 행렬 B의 차원이 다른데 어떻게 곱셈이 가능할까?

 

정답: 행렬 A 위에서 행렬 B를 이동시키며 겹치는 원소들끼리 곱하고 더한다.

 

[그림2] 합성곱의 원리

 

[그림 2]를 단계별로 설명해보겠다.

 

Step 0. 행렬 A 위에 행렬 B를 올려놓는다.([그림 2]의 빨간색 네모 위에 행렬 B를 올려놓는다)

 

Step 1. 행렬 A의 빨간색 네모와 행렬 B의 각 원소를 곱하고 모두 더하면 행렬 C의 첫 번째 원소 7이 나온다.

(실제 계산: 1*1+0*0+2*0+0*2+2*1+1*3+0*0+2*0+1*1=7)

 

Step 2. 이제 행렬 B를 오른쪽으로 이동시킨다.

행렬 A와 겹치는 부분의 각 원소를 곱하고 모두 더한다. 행렬 C의 두 번째 원소 6이 나온다.

 

Step 3. 이번에는 행렬 B를 대각선 아래로 이동시킨다.

행렬 A와 겹치는 부분의 각 원소를 곱하고 모두 더한다. 행렬 C의 세 번째 원소 7이 나온다.

 

Step 4. 마지막으로 행렬 B를 왼쪽으로 이동시킨다.

[그림 2]에서 행렬 A의 파란색 네모와 행렬 B의 각 원소를 곱하고 모두 더하면 행렬 C의 네 번째 원소 10이 나온다.

 

Step 5. 이렇게 행렬 B가 행렬 A 위를 다 돌고 나면 행렬 C가 완성된다. 이 과정에서 사용된 연산이 바로 합성곱이다.

 

 

2-2. CNN의 프로세스를 통째로 알아보자

 

 

[그림 3]은 CNN의 원리로, 컴퓨터에서 CNN을 통해 이미지를 숫자 2로 분류하는 과정을 보여준다.

 

[그림 3]으로 CNN의 프로세스를 설명해보겠다.

 

우선 Input Data에 들어간 이미지 데이터가 가진 특징들을 추출하는 Feature Extraction 작업을 거친다.

이후 자동차 이미지면 자동차, 숫자 2 이미지이면 숫자 2 등으로 분류하는 Classification 작업을 거친다.

 

[그림 3]은 이미지를 0에서 9 사이의 숫자 중 하나로 분류하는 과정이다.

 

[그림 3] CNN의 원리

 

좀 더 알아보자.

 

Step 1. Image Data에 Padding을 하여 Input Data에 넣는다. 여기서 Padding은 '덧댄다'는 의미로, 합성곱 작업을 거치면 결과의 차원이 줄어드는 문제를 방지하기 위하여 Input Data의 모서리에 0이나 다른 숫자를 덧대는 작업을 한다.

(Padding 은 2-3 섹션에서 자세히 알아보자)

 

Step 2. 특성 맵(Feature map)을 추출하기 위하여 Input Data와 Filter를 합성곱(Convolution) 한 후 Relu라는 활성화 함수를 씌운다. [그림 3]에는 네 개의 Filter가 있다. Input Data와 네 개의 Filter를 각각 합성곱 한 후 활성화 함수를 적용하면 네 개의 특성 맵(Feature Maps)이 나온다.

 

Step 3. Feature Maps의 차원의 복잡성을 줄이면서 유의미한 정보는 유지하기 위해 Max Pooling을 이용한다.

(Pooling은 2-3 섹션에서 자세히 알아보자)

 

Step 4. Flatten을 통하여 3차원의 Feature Maps(Step 3의 결과)를 1차원의 형태로 변형시킨다.

 

Step 5. Step 4의 결과를 완전 연결 신경망에 적용한 후 0에서 9 사이의 숫자일 확률을 구하기 위해 Softmax 함수를 적용한다. CNN에 입력한 이미지 데이터가 0일 확률부터 9일 확률까지 알 수 있다.

 

Step 6. Step5의 결과에 최대의 확률을 가진 값을 구하는 argmax함수를 적용한다. [그림 3]의 Output에서 2로 분류된 것을 알 수 있다.

 

 

2-3. CNN의 프로세스를 세부적으로 알아보자

 

 

1) Padding

 

CNN에서 이미지의 특징을 추출하기 위하여 Filter와 합성곱 하는 과정을 거친다.

이후 차원이 축소되면서 정보가 손실되는 경향을 보이는데, 차원이 줄어드는 것을 예방하기 위해 사전 작업으로 이미지 데이터에 Padding(덧대기)를 한다. 

 

[그림 4]는 Padding 예시이다.

 

[그림4] Padding 예시

 

Padding은 두 가지 방법이 있다. 하나는 [그림 4]와 같이 데이터의 테두리에 0을 덧대는 방법, 다른 하나는 데이터의 테두리 값을 복사하여 덧대는 방법이 있다. 

 

[그림 4]처럼 테두리에 0을 덧대면 합성곱 이후에도 차원이 유지됨을 알 수 있다.

 

 

2) Filters

 

CNN에서 Input Data와 Filter를 합성곱 한 후 Input Data의 특성 맵(Feature Map)이 추출된다.

 

Filter가 어떤 값을 가지느냐에 따라서 추출되는 특성 맵이 달라지고 Input Data를 얼마큼의 간격으로 이동하느냐에 따라서도 달라지는데 이 간격을 'Stride'라고 한다. 또한 n개의 Filter를 이용하면 n개의 특성 맵이 추출된다. 

 

참고로 CNN에서 Filter와 Kernel은 같은 의미이다.

 

 

이해를 돕기 위해 [그림 5]를 보며 설명하겠다.  

 

[그림 5] Filter 적용 예시

 

[그림 5]는 Padding을 씌운 Input Data와 Filter의 합성곱을 나타낸다. 이때 Filter의 수는 한 개라서 하나의 특성 맵이 추출되고 Filter가 Input Data 위를 이동하는 간격은 2이다. 

 

[그림 5]에서 Input Data의 빨간색 네모와 Filter와 연산하여 Feature Map의 빨간 부분이 계산되고 2칸을 이동한 Input Data의 주황색 네모와 Filter와 연산하여 Feature Map의 주황색 부분이 계산된다. 이런 식으로 반복하면 하나의 Feature Map이 완성된다. 

 

 

3) Pooling

 

Pooling은 특성 맵을 스캔 하여 최댓값을 고르거나 평균값을 계산하는 것을 의미한다. 보통 연구자들은 최댓값을 고르는 Pooling 방식(Max Pooling)을 선호한다. 그 이유는 Input Data에서 각 Filter가 찾고자 하는 부분은 Feature Map의 가장 큰 값으로 활성화되는데 Max Pooling 방식은 Feature Map의 차원은 줄이면서 유의미한 정보이자, 가장 큰 특징은 유지시키는 성질이 있기 때문이다. 

 

[그림 6]은 Max Pooling의 예시이다.

 

[그림 6] Max Pooling 예시

 

하나의 Feature Map이 있으면 이 위를 풀링 영역이 지나가면서 최댓값을 가져온다. 풀링 영역은 사용자가 지정할 수 있으며 보통 2x2를 크기로 지정한다. 그리고 이동 간격(Stride)은 풀링 영역의 한 모서리 크기로 지정한다.

 

[그림 6]에서 첫 번째 풀링 영역에서 최댓값 5를 뽑아내고 두 칸 이동한 두 번째 풀링 영역에서 최댓값 8을, 왼쪽 대각선 아래로 이동한 세 번째 풀링 영역에서 최댓값 5를, 두 칸 이동한 네 번째 풀링 영역에서 최댓값 4를 뽑아낸다.

 

 

다음 게시물에서는 MNIST DATA가 CNN에서 어떻게 굴러가는지 Python 실습을 통해 알아보자

+ Recent posts