퍼셉트론(perceptron) 알고리즘에 관한 내용이다. 퍼셉트론은 프랑크 로젠블라트가 1957년에 고안한 알고리즘이다. 흔히 딥러닝을 시작할 때 퍼셉트론(perceptron)의 개념에 대해서 알고 공부를 한다. 이 고대 화석같은 알고리즘을 왜 배우는가 하면 이는 퍼셉트론이 신경망(딥러닝)의 기원이기 때문이다. 

 

 

1. 퍼셉트론이란?

퍼셉트론은 다수의 신호를 입력으로 받아 하나의 신호를 출력한다. 이 책에서는 흔히 신호를 전류나 강물처럼 흐름이 있는 것으로 상상하는게 좋다고 하나, 본인은 그런 상상은 잘 안든다..

 

우선 퍼셉트론은 입력이 0과 1로 두가지 값을 갖을 수 있다.

 

 위 그림에서 

-x1 과 x2는 입력 신호, y는 출력 신호, w1과 w2는 가중치를 의미한다.

-원을 뉴런 또는 노드라고 부른다.

-입력 신호가 뉴런에 보내질 때는 각각 고유한 가중치가 곱해진다.(x1*w1,x2*w2).

-뉴런에서 전달 받은 신호의 총합이 임계값 theta를 넘을 때만 1을 출력한다.

이를 수식으로 나타내면,

 

퍼셉트론은 복수의 입력 신호 각각에 고유한 가중치를 부여한다. 가중치는 각 신호가 결과에 주는 영향력을 조절하는 요소로 작용하며, 가중치가 클수록 신호가 그만큼 더 중요함을 뜻한다.

 

 

2. 단순한 논리 회로

AND 게이트

AND Gate 진리표

이 그림은 AND게이트의 진리표로, 두 입력이 모두 1일 때만 1을 출력하고 그 외에는 0을 출력한다.

 

NAND 게이트와 OR 게이트

 

NAND 게이트는 Not AND를 의미하며, 그 동작은 AND게이트의 출력을 뒤집은 것이다. 

진리표로 확인해보면, 

NAND Gate 진리표

위 그림처럼 x1과 x2가 모두 1일 때만 0을 출력, 그 외에는 모두 1을 출력한다.

 

OR 게이트는 입력 신호 중 하나 이상이 1이면 출력이 1이 되는 논리 회로이다. 

진리표를 확인해보면,

 

OR gate 진리표

3. 퍼셉트론 구현하기

3.1 간단한 구현.

위에서 배운 논리 회로를 간단하게 파이썬으로 구현해보자.

def AND_Func(x1,x2):
    w1,w2,theta=.5,.5,.7
    tmp=x1*w1+x2*w2
    if tmp<=theta:
        return 0
    elif tmp>theta:
        return 1

위 함수는 x1과 x2를 인수로 받는 AND_Func함수이다. 매개변수 w1,w2,theta는 함수 안에서 초기화하고, 가중치를 곱한 입력의 총합이 임계값을 넘으면 1을 반환하고 그 외에는 0을 반환한다.

 

inputs=[(0,0),(1,0),(0,1),(1,1)]

for x1,x2 in inputs:
    y=AND_Func(x1,x2)
    print('({x1},{x2})->{y}'.format(x1=x1,x2=x2,y=y))
    
(0,0)->0
(1,0)->0
(0,1)->0
(1,1)->1

다음 과 같이 input튜플리스트를 만든 후, 이 값을 차례로 AND_Func에 집어넣으면 위와 같은 출력값이 나온다.

 

3.2 가중치와 편향 도입

앞에서 구현한 AND게이트는 직관적이고 알기 쉽지만, 다른 방식으로 수정해보자. 

식 2.1

위 식에서 theta를 -b로 치환하면 퍼셉트론의 동작은 다음과 같다.

식 2.2

기호 표기만 변경되었지, 그 의미는 같다. 여기서는 b를 편향이라고 하며 w1과 w2는 그대로 가중치이다.

해석해보면, 퍼셉트론은 입력 신호에 가중치를 곱한 값과 편향을 합하여, 그 값이 0을 넘으면 1을 출력, 그렇지 않으면 0

을 출력한다. 파이썬으로 확인해보자.

 

 

가중치와 편향을 도입한 AND gate

import numpy as np
def AND_Func_Bias(x1,x2):
    x=np.array([x1,x2])
    w=np.array([.5,.5])
    b=-.7
    tmp=np.sum(w*x)+b
    if tmp<=0:
        return 0
    else:
        return 1

위 함수는 theta가 -b로 치환된 함수이다. 또한, 편향은 가중치와 다르다는 사실에 주의하자. 구체적으로 설명하자면 w1과 w2는 각 입력 신호가 결과에 주는 영향력(중요도)을 조절하는 매개변수고, 편향은 뉴런이 얼마나 쉽게 활성화(결과로 1을 출력)하느냐를 조절하는 매개변수이다. 추가적으로 과녁의 예를 들 수있다. 이는 통계학 카테고리에 따로 설명을 해놓겠다.

inputs=[(0,0),(1,0),(0,1),(1,1)]

for x1,x2 in inputs:
    y=AND_Func_Bias(x1,x2)
    print('({x1},{x2}=>{y})'.format(x1=x1,x2=x2,y=y))
    
    
(0,0=>0)
(1,0=>0)
(0,1=>0)
(1,1=>1)

 

가중치와 편향을 도입한 NAND gate & OR gate

def NAND_Bias_Func(x1,x2):
    x=np.array([x1,x2])
    w=np.array([-.5,-.5])
    b=.7
    tmp=np.sum(w*x)+b
    if tmp<=0:
        return 0
    else:
        return 1
    
def OR_Bias_Func(x1,x2):
    x=np.array([x1,x2])
    w=np.array([.5,.5])
    b=-.2
    tmp=np.sum(w*x)+b
    if tmp<=0:
        return 0
    else:
        return 1
    

 

#NAND 
for x1,x2 in inputs:
    y=NAND_Bias_Func(x1,x2)
    print('({x1},{x2}=>{y})'.format(x1=x1,x2=x2,y=y))    
    
(0,0=>1)
(1,0=>1)
(0,1=>1)
(1,1=>0)

#OR
for x1,x2 in inputs:
    y=OR_Bias_Func(x1,x2)
    print('({x1},{x2}=>{y})'.format(x1=x1,x2=x2,y=y))

(0,0=>0)
(1,0=>1)
(0,1=>1)
(1,1=>1)

 

4. 퍼셉트론의 한계

XOR 게이트

XOR 게이트는 배타적 논리합이라는 논리 회로이다. x1과 x2 중 한쪽이 1일 때만 1을 출력한다. 즉 자기 외에는 거부한다는 의미이다.

XOR 게이트 진리표

이 XOR게이트로 우리는 퍼셉트론을 구현하려면 가중치 매개변수 값을 어떻게 설정할까? 사실 지금까지 본 퍼셉트론으로는 이 XOR게이트를 구현할 수 없다. 

 

즉, 단층 퍼셉트론으로 AND,NAND,OR 게이트는 구현 가능하지만, XOR게이트는 구현할 수 없다. 퍼셉트론은 위와 같이 직선으로 나뉜 두 영역을 만든다. 하지만 XOR은 직선으로 두 영역을 나눌 수 없다.

 

선형과 비선형

퍼셉트론은 직선 하나로 나눈 영역만 표현할 수 있다는 한계가 있다. 위 그림과 같은 곡선은 표현할 수 없다는 의미이다. 위 그림과 같은 곡선의 영역을 비선형 영역, 직선의 영역을 선형 영역이라고 한다. 선형, 비선형이라는 말은 기계학습 분야에서 자주 쓰이는 용어이다.

 

다층 퍼셉트론

퍼셉트론의 아름다움은 '층을 쌓아' 다층 퍼셉트론을 만들 수 있다는데 있다. 우선, XOR 게이트 문제를 다른관점에서 생각해보자.

 

-기존 게이트 조합하기

XOR 게이트를 만든느 방법은 다양하다. 그중 하나는 앞서 만든 AND,NAND,OR 게이트를 조합하는 방법이다. 

AND,NAND,OR 게이트 기호
AND,NAND,OR 게이트를 조합해 구현한 XOR게이트

위 조합이 정말 XOR를 구현하는지 살펴보자. NAND의 출력을 s1, OR의 출력을 s2로 해서 진리표를 만들면 밑에 그림처럼 된다. 

XOR게이트의 진리표

-XOR 게이트 구현하기

def XOR(x1,x2):
    s1=NAND_Bias_Func(x1,x2)
    s2=OR_Bias_Func(x1,x2)
    y=AND_Func_Bias(s1,s2)
    return y
for x1,x2 in inputs:
    y=XOR(x1,x2)
    print('({x1},{x2}=>{y})'.format(x1=x1,x2=x2,y=y))
    
(0,0=>0)
(1,0=>1)
(0,1=>1)
(1,1=>0)

이로써 XOR 게이트를 완성했다. 지금 구현한 XOR를 뉴런을 이용한 퍼셉트론으로 표현하면 다음과 같다.

XOR의 퍼셉트론

 

이전 포스팅에서 판다스 자료구조인 Series에 대해 알아보았다. 이번에는 DataFrame(데이터프레임)에 대해 알아보자.

시리즈가 엑셀의 한 열과 동일한 이해구조를 본다면, 데이터프레임은 그 열을 merge한 엑셀 시트라고 볼 수 있다.

컴퓨터 공학도에게는 DB를 알기에 데이터베이스와 동일하다고도 볼 수 있다.

 

DataFrame의 정의:

DataFrame(데이터프레임)은 numpy의 ndArray를 기반으로 한 행과 열로 이루어진 자료구조이다.

 

판다스 데이터프레임 속성은 다음과 같다.

 

https://dsbook.tistory.com/12

 

데이터프레임 생성법부터 알아보자.

생성법은 크게 3가지가 있다. list를 이용한 생성, Dictionary를 이용한 생성, Series를 이용한 생성

각각 한번 알아보자.

list를 이용한 생성법.

이 list생성도 두가지로 분류 할 수 있다. 1자원 리스트와 멀티 리스트로 볼 수 있다.

-1차원 리스트

single_list=['a','b,','c','d']
df=pd.DataFrame(single_list)
df


	0
0	a
1	b
2	c
3	d

본인도 데이터를 다루며 이런식의 데이터프레임은 잘 사용하지 않으므로 설명은 생략하겠다.

-멀티 리스트

multi_list=[['kwandoll',27],['suuuuuk',27],['heeeee',30]]
df=pd.DataFrame(multi_list)
df


0	1
0	kwandoll 27
1	suuuuuk	27
2	heeeee	30

위 리스트를 이용한 생성을 보면 칼럼들에 대한 변수명은 0 과 1이다. 만일 dimension을 늘리면 늘릴수록 칼럼명은 0,1,2,3,4~~~식으로 간다는 건데, 일반적으로 이해하는게 어렵다.

 

그렇기에, 카럼명을 지정해줘야 한다.

multi_list=[[['kwandoll',27],['suuuuuk',27]],[['heeeee',30]]]
df=pd.DataFrame(multi_list,columns=['name','age'])
df


name	age
0	kwandoll 27
1	suuuuuk	27
2	heeeee	30

이러한 작업을 피하기 위해, 본인은 파이썬에 꽃인 Dictionary자료구조를 제일 많이 사용한다. 실제 데이터 분석을 할경우 같은 의미를 뽑아야하는 경우가 있으며, 데이터프레임 내에 처리가 안될 경우에는 value들을 직접 뽑아서 처리하고 DataFrame에 다시 저장하는 경우가 비일비재하다. 그러므로 이번에 설명하는 Dictionary를 이용한 생성을 유심히 보길 바란다.

 

Dictionary를 이용한 생성

딕셔너리 자료구조로 생성할때 두가지가 있다. 단순 Dictionary로 생성하기, Dictionary로 구성된 리스트로 생성하기.

우선 단순 Dictionary로 생성하는 법을 알아보자.

 

-단순 Dictionary로 생성

Dictionary.keys()는 column명을 생성하고, Dictionary.values()는 data역할을 한다.

단, 각 딕셔너리의 길이는 동일해야한다. 

data={'lee':[1,2,3],'kwan':[2,3,4],'doll':[3,4,5]}
df=pd.DataFrame(data)
df


	lee	kwan	doll
0	1	2	3
1	2	3	4
2	3	4	5

그럼 만일 dictionary.values()의 길이가 다르다면 어떨까

data={'lee':[1,2,3],'kwan':[2,3],'doll':[3,4,5]}
df=pd.DataFrame(data)
df

ValueError: arrays must all be same length

 

다음과같은 에러가 뜬다. 즉 이말은 values에 있는 리스트의 길이는 동일해야한다는 뜻이다.

 

다음은 Dictionary로 구성된 리스트로 생성하는 법을 알아보자.

 

-Dictionary로 구성된 리스트 생성

data=[{'lee':1,'kwan':2},{'lee':3,'doll':4},{'king':5,'laa':6}]
df=pd.DataFrame(data)
df

	lee	kwan	doll	king	laa
0	1.0	2.0	NaN	NaN	NaN
1	3.0	NaN	4.0	NaN	NaN
2	NaN	NaN	NaN	5.0	6.0

data=[]에 들어있는 리스트의 순서대로 각 인덱스가 인덱스로, 즉, dict1, dict2, dict3이 각각 0,1,2가 되는것이다.

또한 Dictionary.keys()는 column명으로, dictionary.values()는 data로 저장된다. 또한, 비어있는 부분은 모두 NaN값으로 처리한다.

 

여기서 만약 인덱스와 칼럼명을 지정해준다면 다음과 같이 변형된다.

data=[{'lee':1,'kwan':2},{'lee':3,'doll':4},{'king':5,'laa':6}]
df_1=pd.DataFrame(data,index=['zero','first','second'],columns=['lee','kwan','doll','king','laa'])
df_1

	lee	kwan	doll	king	laa
zero	1.0	2.0	NaN	NaN	NaN
first	3.0	NaN	4.0	NaN	NaN
second	NaN	NaN	NaN	5.0	6.0

위 데이터에서 key인 'lee'는 value를 2개 갖고있다. 그러므로 이것이 데이터프레임으로 처리할때는 순서대로 저장되는 것이다.

 

마지막으로 Sereis를 이용한 생성을 살펴보자.

 

Series를 이용한 생성

 

앞선 포스트처럼 시리즈는 액셀의 한 열이다. 그렇다면? 병합한다는 개념을 생각해보자.

series 또한 생성할때 리스트로 생성하는 법과 딕셔너리로 생성하는 법이 있었다. 

두가지를 각각 살펴보자.

 

-리스트로 생성된 Series를 이용한 생성

 

a=pd.Series([11,22,33],['a','b','c'])
b=pd.Series([44,55,66],['d','e','f'])
c=pd.Series([77,88,99],['i','j','k'])
df=pd.DataFrame([a,b,c])
df

	a	b	c	d	e	f	i	j	k
0	11.0	22.0	33.0	NaN	NaN	NaN	NaN	NaN	NaN
1	NaN	NaN	NaN	44.0	55.0	66.0	NaN	NaN	NaN
2	NaN	NaN	NaN	NaN	NaN	NaN	77.0	88.0	99.0

어떤 형식인지 이해해보자. 각 시리즈는 이차원 리스트로 되어있으며, Data와 Column으로 구성되어있다.

즉 이것을 모두 병합하여 처리를한다면, 위 결고와 같이 각각의 Column명으로 구성된 abcdefijk와 각각의 

데이터로 구성된 11,22,33,44,55,66,77,88,99로 된다. 

만일 예를들어, a라는 칼럼이 중복되어 11,33으로 되어있다면, a칼럼의 0번째 인덱스는 11,1번째 인덱스는 33으로 되는 것이다.

 

-딕셔너리로 생성된 Series를 이용한 생성

 

example={'lee':pd.Series([85,180],['weight','height']),'park':pd.Series([52,168],['weight','height'])}
df=pd.DataFrame(example)
df


	lee	park
weight	85	52
height	180	168

위 코드를 살펴보면 'lee'라는 칼럼명에는 시리즈 첫번째 리스트에 있는 값들이 그 다음에 지정해놓은 인덱스로 각각 처리되어 저장되는 모습이다. 실제 이런 것들은 직접 코딩을 해보고 눈으로 확인을 해봐야한다.

 

지금까지 크게 3가지로 구성된 자료구조로 어떻게 데이터프레임을 만들지는 상황마다 다르며 각자 쓰는 방법마다 다르다. 그러므로 이 세가지를 잘 숙지하는 것도 데이터를 다루는 능력에 절반은 차지할것이라고 생각한다(개인적 생각).

 

 

'Data analysis > Pandas' 카테고리의 다른 글

pandas.DataFrame.apply  (0) 2021.03.16
[Pandas] Series  (0) 2021.01.15
[Pandas] DataFrame 특정 칼럼 혹은 인덱스 선택  (0) 2021.01.13
[Pandas] DataFrame(개념)  (0) 2021.01.13

판다스는 소프트웨어 라이브러리 용으로 작성된 파이썬 프로그래밍 언어 데이터 조작 및 분석을 위한 패키지라고 볼 수 있다. 특히, 수치표와 시계열을 조작하기위한 데이터 구조와 연산을 제공해준다. 

 

판다스의 Series

판다스의 시리즈를 이해하기에 앞서 우리가 자주 접하는 엑셀을 한번 생각해보자. 

엑셀은 칼럽이 있고, 열들이 있다. 그 구조를 이해한 상태에서 시리즈를 직관적으로 이해한다면, 시트의 열 하나를 떠올리면된다. 

시리즈의 속성은 index와 values가 있다. 이는 아래 예시를 통해 한번 확인해보자.

시리즈를 생성하는 방법은 list형식, dictionary형식이 있다. 

우선 list형식으로 생성해보자.

example1=pd.Series(["undead","Kwandoll","JJang"])
example1

0      undead
1    Kwandoll
2       JJang
dtype: object

그 다음은 dictionary형식으로 생성해보자.

example2=pd.Series({"undead":33,"Kwandoll":22,"JJang":11})
example2

undead      33
Kwandoll    22
JJang       11
dtype: int64

차이점은 무엇일까?

1. 리스트 형식과 딕셔너리 형식의 생성 기호는 다르다. 즉 리스트는 [] 딕셔너리는 {} 이다. 이는 파이썬 기본을 공부한다면 누구나 아는 얘기지만, 모를수도 있기에 적어본다.

2. 인덱스가 다르다. 즉, 리스트형식으로 생성한다면 인덱스라인에 있는 숫자는 리스트 원래 인덱스넘버가 그대로 생성된다. 반대로 딕셔너리로 생성을 한다면, key,value형태이기에 key가 인덱스 value가 value로 들어가는 것이다.

이 차이를 정확하게 집어가야지만 데이터프레임을 이해하고, 그 데이터프레임을 다룰때 정확하게 처리할 수 있다.

 

시리즈 인덱싱

example1['undead']
33
example2[1]
'kwandoll'

말로 설명하는 것보다 예시가 이해에 더 빠르기에, 이렇게 작성해본다. 

해석) example1이라는 Series자료구조에서 인덱스가 'undead'인 value를 출력하라. 결과) 33

해석) example2라는 Series자료구조에서 인덱스가 1인 value를 출력하라. 결과) kwandoll

 

 

 

'Data analysis > Pandas' 카테고리의 다른 글

pandas.DataFrame.apply  (0) 2021.03.16
[Pandas] DataFrame  (0) 2021.01.15
[Pandas] DataFrame 특정 칼럼 혹은 인덱스 선택  (0) 2021.01.13
[Pandas] DataFrame(개념)  (0) 2021.01.13

과연 실무에서 전공자가 아닌 비전공자가 분석 내용을 확인하는 방법중에 가장 효과적인 방법은 무엇일까?

바로 그래프이다. 그래프를 통해 알기 어려운 insight를 한눈에 알아차릴 수 있다. 하지만, 그 과정을 만들어 잘 보여지게 하는게 analysist 의 일이다.

 

시작 데이터는 항상 연습용으로 사용하는 리뷰데이터를 통해 시작하겠다. 이유는 간단하다. 전처리 하기에도 가장 훌륭한 연습용 데이터이기 때문이다.

먼저 필요한 라이브러리 함수를 불러온다.

 

import numpy as np
import pandas as pd
from konlpy.tag import Okt
from collections import Counter
import re
from konlpy.tag import Twitter
from pykospacing import spacing
import matplotlib.pyplot as plt
twitter=Twitter()
data=pd.read_csv('c:/data/thisweek_selected_classified.csv')
stopwords = ['도', '는', '다', '의', '가', '이', '은', '한', '에', '하', '고', '을', '를', '인', '듯', '과', '와', '네', '들', '듯', '지', '임', '게','요','거','로','으로',
            '것','수','할','하는','제','에서','그','데','번','해도','죠','된','건',
             '바','구','세','랑','시','저','만']

data는 작성일전으로부터 1주일 데이터를 크롤링한 데이터이다. 한번 데이터를 확인해보자.

데이터는 10개만 확인해본다.

data=pd.read_csv('c:/data/thisweek_selected_classified.csv')
data[:10]

본인이 생각하기에 실무자가 아닌 일반 비전공자들은 이 데이터르 보면 '아 1주일데이터가 이렇구나'까지이다. 우리의 임무는 어떤 방식으로 어떻게 상대방에게 결과 즉, insight를 꺼내주면 된다.

content 칼럼의 리뷰 문장들을 한번 간단하게라도 전처리 및 토큰화를 해보자.

twitter=Twitter()
data=pd.read_csv('c:/data/thisweek_selected_classified.csv')
#불용어 단어
stopwords = ['도', '는', '다', '의', '가', '이', '은', '한', '에', '하', '고', '을', '를', '인', '듯', '과', '와', '네', '들', '듯', '지', '임', '게','요','거','로','으로',
            '것','수','할','하는','제','에서','그','데','번','해도','죠','된','건',
             '바','구','세','랑','시','저','만']

#단어의 길이가 2개 이상인 경우만 선택 및 저장
data['content']=data['content'].str.replace('[^ㄱ-ㅎㅏ-ㅣ|가-힣]','') #전처리 
data['tokenized']=data['content'].apply(twitter.morphs) #명사마나ㅏ 토큰화
data['tokenized']=data['tokenized'].apply(lambda x: [item for item in x if item not in stopwords])
#불용어 처리
x=data['tokenized']
for line in x:
    for word in line:
        if len(word)<2:
            line.remove(word)
data['tokenized']=x 

*참고) Twitter 전처리 라이브러리는 Konlpy에서 만든 것인데, KoNLPy v0.4.5 버전 이후로는 Okt 전처리 라이브러리와 통합되었다.

아직까지 Konlpy에서 만든 토큰화 라이브러리는 단어가 조금만 오타가 나도 오류를 범하기에 가장 빠르게 할 수 있는 방법은 단어 길이가 1인 경우는 지워주는 것이다. 본인 기준이다.... 

다시 데이터 10개를 확인해보면 잘 처리가 되었다. 이후 우리는 numpy에서 제공하는 단어 카운트 라이브러리를 사용할 것이다. 

우선 긍정 및 부정으로 나누어 주고, 각 부분 별 단어의 빈도 최고 10개를 확인해 보자.

negative_count=np.hstack(data[data.grade<3]['tokenized'].values)
positive_count=np.hstack(data[data.grade>2]['tokenized'].values)
negative_word_count=Counter(negative_count)
positive_word_count=Counter(positive_count)

print(negative_word_count.most_common(10))
print(positive_word_count.most_common(10))

[('영상', 9), ('너무', 7), ('화면', 7), ('업데이트', 6), ('라이브', 6), ('버전', 6), ('기능', 5), ('따로', 4), ('채널', 4), ('계속', 3)]
[('영상', 12), ('브이', 8), ('너무', 7), ('기록', 5), ('삭제', 5), ('업데이트', 4), ('라이브', 4), ('까지', 4), ('설정', 4), ('좋은데', 3)]

긍정과 부정에 최빈 단어는 '영상' 이다. 수치로도 확인할 수 있지만, 본래 취지는 그래프를 통한 insight이기에 그래프로 확인해보자.

import matplotlib.font_manager as fm
def bar_plot(dict1,dict2):

    fl = fm.FontProperties(fname='c:/Windows/Fonts/malgun.ttf').get_name()
    plt.rc('font', family=fl)
    plt.bar(dict1.keys(), dict1.values(), color='b')
    plt.bar(dict2.keys(), dict2.values(), color='r')
    plt.legend()
    plt.title('Bar Chart')
    plt.show()
    
ne_data=dict(negative_word_count.most_common(10))
po_data=dict(positive_word_count.most_common(10))

bar_plot(po_data,ne_data)

위 그래프를보면 까지, 너무, 이런 단어들은 굳이 있을 필요가 없는 단어들이다. 그러므로 다시 불용어 처리를 하며 그래프를 더 정교화 시키면 된다. 연습용 데이터가 작기에 이 단어 빈도수가 작지만 데이터를 늘린다면 더 괜찮은 insight를 도출해 낼 수 있을 것이다. 

 

모델을 만든 후 학습시키는 시간 또한 데이터가 크면 클수록 오래걸린다. 짧게는 수 분에서 길게는 몇일이 걸릴 정도이다. 이를 통해 결과를 얻는다면 다음에 이걸 적용해 볼때마다 학습시키고 사용해야 하는가? 

본인도 처음 모델을 만들고 학습시킨 후, 저장 및 불러오는 방법을 몰라 30분가량을 기다리고 사용해보고 했다. 비효율 적인 상황에서 구글링을 하였고 해결책을 얻었다.

 

크게 세가지로 구분하자.

1. 모델을 저장하는 코드

model_save

2. 모델을 불러오는 코드

model_load

3. 불러온 모델을 평가하는 코드

model.compile,model.evaluate

 

1. 모델을 저장하는 코드

모델을 저장할 경우 두가지를 저장하면 된다. 

Model 을 저장하는 json 파일, Weight을 저장하는 h5 파일

 

코드는 다음과 같다.

#모델 저장
json_model=model.to_json()
with open("C:/data/model.json","w") as json_file:  
#쌍따옴표 안은 절대경로에 저장하면 파일 이름.josn
#임의 경로를 원한다면 경로 설정
	json_file.write(model_json)

#가중치 저장
model.save_weights("C:/data/weight_model.h5")
#쌍따옴표 안은 절대경로에 저장하면 파일 이름.josn
#임의 경로를 원한다면 경로 설정

2. 모델을 불러오는 코드

모데를 불러오려면 똑같이 model과 weight을 불러와야한다. 그렇다면 json 파일 및 h5를 불러오면 되는것이다.

from keras.models import model_from_json
#모델 불러오기
json_model=open("C:/data/model.json","r")
loadded_json_model=json_model.read()
json_model.close()
loadded_json_model=model_from_json(loadded_json_model)

#가중치 불러오기
model.load_weight("C:/data/weight_model.h5")

 

3. 모델을 평가하기. 

본인은 이것이 가장 중요하다 생각한다. 실제 내가 훈련시킨 데이터를 저장해 놓지 않았다면, 위 작업은 의미가 없다. pkl형식이든 csv형식이든 훈련데이터는 존재해야 한다. 꼭!

 

#평가하기
load_model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])
print("\n 테스트 정확도: %.4f" % (loaded_model.evaluate(X_test, Y_test)[1]))

 

평가 코드는 간단하다. 하지만 위에서 언급한 것처럼 X_test와 Y_test가 없으면 아무 소용이 없다.

 

예시를 통해 확인해보자 불러오고 컴파일시큰 것까지 정리된 것이다.

from nltk.corpus import stopwords
import pandas as pd
from konlpy.tag import Okt
okt=Okt()
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from keras.models import model_from_json
stopwords = ['도', '는', '다', '의', '가', '이', '은', '한', '에', '하', '고', '을', '를', '인', '듯', '과', '와', '네', '들', '듯', '지', '임', '게','요','거','로','으로',
            '것','수','할','하는','제','에서','그','데','번','해도','죠','된','건','바','구','세']

train_data=pd.read_pickle("c:/data/Train_data.pkl")
test_data=pd.read_pickle("c:/data/Test_data.pkl")
X_Train=train_data['tokenized'].values
Y_Train=train_data['label'].values
X_Test=test_data['tokenized'].values
Y_Test=test_data['label'].values
tokenizer=Tokenizer()
tokenizer.fit_on_texts(X_Train)
vocab_size=19249
tokenizer=Tokenizer(vocab_size,oov_token='OOV')
tokenizer.fit_on_texts(X_Train)
X_Train=tokenizer.texts_to_sequences(X_Train)
X_Test=tokenizer.texts_to_sequences(X_Test)
max_len=100
X_Train=pad_sequences(X_Train,maxlen=max_len)
X_Test=pad_sequences(X_Test,maxlen=max_len)

json_file=open("c:/data/model.json","r")
loaded_model_json=json_file.read()
json_file.close()
loaded_model=model_from_json(loaded_model_json)
loaded_model.load_weights("c:/data/best_model_GRU.h5")
loaded_model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])

def GRU_predict(new_sentence):
    max_len=90
    new_sentence=okt.morphs(new_sentence)
    new_words=[word for word in new_sentence if not word in stopwords]
    encoded=tokenizer.texts_to_sequences([new_sentence])
    pad_new=pad_sequences(encoded,maxlen=max_len)
    score=float(loaded_model.predict(pad_new))
    percentage=score*100
    return percentage,new_words

위 작업은 GRU 기법을 활용한 감성분석 시 사용한 모델이다. 모델 저장은 크게 어렵지 않지만, 불러오는 것 또한 준비할게 있다는 걸 명심해두길 바란다.

본인이 만든 함수들은 각 ipynb로 분류해 놓았다. 그러므로 이를 따라하는 것을 추천하지는 않는다..

import import_ipynb
import send_email
import Main_1
import call_Graph
import pandas as pd


importing Jupyter notebook from send_email.ipynb
importing Jupyter notebook from Main_1.ipynb
importing Jupyter notebook from call_url.ipynb
importing Jupyter notebook from call_all_company.ipynb
importing Jupyter notebook from call_GRU_predict.ipynb
Epoch 1/15
375/375 [==============================] - ETA: 0s - loss: 0.4081 - acc: 0.8263
Epoch 00001: val_acc improved from -inf to 0.84589, saving model to best_model_GRU.h5
375/375 [==============================] - 55s 145ms/step - loss: 0.4081 - acc: 0.8263 - val_loss: 0.3822 - val_acc: 0.8459
Epoch 2/15
375/375 [==============================] - ETA: 0s - loss: 0.3341 - acc: 0.8630
Epoch 00002: val_acc improved from 0.84589 to 0.85187, saving model to best_model_GRU.h5
375/375 [==============================] - 55s 145ms/step - loss: 0.3341 - acc: 0.8630 - val_loss: 0.3605 - val_acc: 0.8519
Epoch 3/15
375/375 [==============================] - ETA: 0s - loss: 0.3128 - acc: 0.8725
Epoch 00003: val_acc did not improve from 0.85187
375/375 [==============================] - 55s 146ms/step - loss: 0.3128 - acc: 0.8725 - val_loss: 0.3565 - val_acc: 0.8499
Epoch 4/15
375/375 [==============================] - ETA: 0s - loss: 0.2961 - acc: 0.8803
Epoch 00004: val_acc did not improve from 0.85187
375/375 [==============================] - 55s 146ms/step - loss: 0.2961 - acc: 0.8803 - val_loss: 0.3612 - val_acc: 0.8492
Epoch 5/15
375/375 [==============================] - ETA: 0s - loss: 0.2810 - acc: 0.8872
Epoch 00005: val_acc did not improve from 0.85187
375/375 [==============================] - 53s 142ms/step - loss: 0.2810 - acc: 0.8872 - val_loss: 0.3755 - val_acc: 0.8447
Epoch 6/15
375/375 [==============================] - ETA: 0s - loss: 0.2659 - acc: 0.8948
Epoch 00006: val_acc did not improve from 0.85187
375/375 [==============================] - 54s 143ms/step - loss: 0.2659 - acc: 0.8948 - val_loss: 0.3756 - val_acc: 0.8431
Epoch 7/15
375/375 [==============================] - ETA: 0s - loss: 0.2504 - acc: 0.9023
Epoch 00007: val_acc did not improve from 0.85187
375/375 [==============================] - 58s 155ms/step - loss: 0.2504 - acc: 0.9023 - val_loss: 0.3964 - val_acc: 0.8342
Epoch 00007: early stopping
492/492 [==============================] - 10s 20ms/step - loss: 0.3621 - acc: 0.8509

 테스트 정확도: 0.8509
importing Jupyter notebook from call_Graph.ipynb

 

 

def kwandoll():
    check1=int(input("어떤 작업을 수행하시겠습니까?\n"+"[1]:경쟁회사 전체 데이터\n"+"[2]:경쟁회사 및 자사 데이터"))
    check2=int(input("분류된 데이터를 확인하시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
    check3=int(input("이메일로 받으시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
    check4=int(input("그래프로 확인해보시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
    if(check1==1):
        
        if(check2==1 and check4==1):
            
            Main_1.classified_all_data()
            df=pd.read_csv("C:\\Data\\thisweek_classified.csv")
            call_Graph.wordcloud(df)
            if(check3==1):
                send_email.sending_classified_all_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")
        elif(check2==1 and check4==2):
            if(check3==1):
                send_email.sending_classified_all_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")
        elif(check2==2 and check4==1):
            Main_1.none_classified_all_data()
            df=pd.read_csv("C:\\Data\\thisweek_unclassified.csv")
            call_Graph.wordcloud(df)
            if(check3==1):
                send_email.sending_unclassified_all_data()
            else:
                print("지정된 디렉터리에 저장되었습니다.")
        elif(check2==2 and check4==2):
            Main_1.none_classified_all_data()
            if(check3==1):
                send_email.sending_unclassified_all_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")

    elif(check1==2):
        if(check2==1 and check4==1):
            Main_1.classified_selected_data()
            df=pd.read_csv("C:\\Data\\thisweek_selected_classified.csv")
            call_Graph.wordcloud(df)
            if(check3==1):
                send_email.sending_classified_selected_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")
        elif(check2==1 and check4==2):
            if(check3==1):
                send_email.sending_classified_selected_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")
        elif(check2==2 and check4==1):
            Main_1.none_classified_selected_data()
            df=pd.read_csv("C:\\Data\\thisweek_selected_unclassified.csv")
            call_Graph.wordcloud(df)
            if(check3==1):
                send_email.sending_unclassified_selected_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")
        elif(check2==2 and check4==2):
            Main_1.none_classified_selected_data()
            if(check3==1):
                send_email.sending_unclassified_selected_data()
            else:
                print("지정된 디렉토리에 저장되었습니다.")

 

 

이후, kwandoll() 함수를 호출하면

어떤 작업을 수행하시겠습니까?
[1]:경쟁회사 전체 데이터
[2]:경쟁회사 및 자사 데이터1
분류된 데이터를 확인하시겠습니까?
[1]:예[2]:아니오1
이메일로 받으시겠습니까?
[1]:예[2]:아니오1
그래프로 확인해보시겠습니까?
[1]:예[2]:아니오1
시작 년도를 적으시오: 2021
시작 월을 적으시오: 01
시작 일을 적으시오: 01
종료 년도를 적으시오: 2021
종료 월을 적으시오: 01
종료 일을 적으시오: 13
퀄리티 벨류의 최댓값을 지정하세요: 90
퀄리티 벨류의 최솟값을 지정하세요: 10

 

이름을 적으세요이관형
보낼 이메일 주소를 적으세요: asd95101@naver.com
Successfully sent the mail!!!

이로써 내가 원하는 데이터는 지정 이메일로 전송이되고, 워드클라우드를 통해 그래프로 보여진다. 

이제 시작이라 말주변도없고 이 데이터도 전처리를 더 해야하지만, 인턴을 통해 이 정도까지 왔다는 것도 만족한다. 계속 공부하며 글을 올려 실력을 올려보자.

+ Recent posts