4. 외국어 문장 판별


파이썬을 이용한 머신러닝, 딥러닝 실전개발 입문

파이썬을 이용한 머신러닝, 딥러닝 실전개발 입문 책과 강의를 바탕으로한 코드 리뷰 및 정리입니다. 자세한 내용은 책과 강의를 참고해주세요.


4. 머신러닝

4-4. 외국어 문장 판별


판정 방법

언어가 다르면 알파벳의 출현 빈도가 다르다는 성질을 이용한다. Input vector로 각 알파벳의 출현 빈도의 비율을 이용한다.


샘플 데이터 수집

위키피디아에서 무작위로 기사를 추출하고 각 언어의 식별자에 맞게 저장한다. 간단하게 영어(en), 프랑스어(fr), 인도네시아어(id), 타갈로그어(tl)로 테스트데이터를 만들어 사용하였다.


언어 판별 프로그램
from sklearn import svm, metrics
import glob, os.path, re, json

def check_freq(fname):
    basename=os.path.basename(fname)
    lang=basename.split("-")[0]
    
    #텍스트 추출하기
    with open(fname, "r", encoding="utf-8") as f:
        text=f.read()
        text=text.lower() #소문자 변환
      
    #알파벳 출현 빈도 구하기
    code_a=ord("a")
    code_z=ord("z")
    cnt=[0 for n in range(0,26)] #26개의 0
    
    #알파벳 출현 횟수 구하기
    for char in text:
        code_current=ord(char)
        if code_a <= code_current <= code_z: #a와 z 사이에 있을 때
            cnt[code_current-code_a]+=1 
    #정규화하기
    total=sum(cnt)
    freq=list(map(lambda n: n/total, cnt))
    return (freq, lang)

#각 파일 처리하기
def load_files(path):
    freqs=[]
    labels=[]
    file_list=glob.glob(path)

    for fname in file_list:
        r=check_freq(fname)
        freqs.append(r[0])
        labels.append(r[1])
    return {"freqs":freqs, "labels":labels}

data=load_files("./language/train/*.txt")
test=load_files("./language/test/*.txt")

#JSON으로 결과 저장
with open ("./language/freq.json","w",encoding="utf-8") as fp:
    json.dump([data, test],fp)
    
#학습하기
clf=svm.SVC()
clf.fit(data['freqs'], data['labels'])

#예측하기
result=clf.predict(test['freqs'])

#결과테스트하기
score=metrics.accuracy_score(test["labels"],predict)
report=metrics.classification_report(test["labels"],predict)
print("정답률=",score)
print("리포트=",report)
    
정답률= 0.875
리포트=              precision    recall  f1-score   support

         en       0.67      1.00      0.80         2
         fr       1.00      1.00      1.00         2
         id       1.00      0.50      0.67         2
         tl       1.00      1.00      1.00         2

avg / total       0.92      0.88      0.87         8

각 언어당 5개의 데이터로도 꽤 괜찮은 정답률이 나온다. 각 언어에 따라 알파벳 출현 빈도가 다르다는 것을 알 수 있다. 88%가 된 주범인 인도네시아어의 데이터를 확인해보니 영어가 굉장히 많이 섞여 있어 잘못 인식했던 것으로 파악했다. 또한 그 나라 특유의 알파벳 (é와 같이 악센트가 붙은 알파벳)을 제외하여 정확도에 아쉬움을 줬을 수도 있다고 생각한다.


데이터분포를 그래프로 확인하기
#데이터 그래프 그리기
import matplotlib.pyplot as plt
import pandas as pd
import glob, os.path

files=glob.glob("./language/train/*.txt") #폴더 내 텍스트 파일 추출
train_data=[]
train_label=[]

for fname in files:
    #레이블 구하기
    basename=os.path.basename(fname)
    lang=basename.split("-")[0]
    
    #텍스트 추출하기
    with open(fname, "r", encoding="utf-8") as f:
        text=f.read()
        text=text.lower() #소문자 변환
    
      
    #알파벳 출현 빈도 구하기
    code_a=ord("a")
    code_z=ord("z")
    cnt=[0 for n in range(0,26)] #26개의 0
    for char in text:
        code_current=ord(char)
        if code_a <= code_current <= code_z:
            cnt[code_current-code_a]+=1 
    #정규화하기
    total=sum(cnt)
    freq=list(map(lambda n: n/total, cnt))
    
    
    #리스트에 넣기
    train_label.append(lang)
    train_data.append(freq)

    
#그래프 준비하기
graph_dict={}
for i in range(0,len(train_label)):
    label=train_label[i]
    data=train_data[i]
    if not (label in graph_dict):
        graph_dict[label]=data
                
asclist=[[chr(n) for n in range(97,97+26)]]
df=pd.DataFrame(graph_dict, index=asclist)

print(df)

#바그래프
plt.style.use('ggplot')
df.plot(kind='bar', subplots=True, ylim=(0,0.15))
plt.savefig('lang-plot.png')

#라인그래프
plt.style.use('ggplot')
df.plot(kind='line')
plt.show()
         tl        en        fr        id
a  0.202369  0.073806  0.072599  0.180053
b  0.022730  0.020368  0.015761  0.025664
c  0.019562  0.031099  0.039836  0.008246
d  0.037057  0.039641  0.051129  0.046101
e  0.064196  0.141261  0.157111  0.083582
f  0.005786  0.020368  0.013031  0.005113
g  0.033062  0.020368  0.013527  0.034121
h  0.006750  0.056943  0.014396  0.020827
i  0.075492  0.065046  0.085629  0.088696
j  0.002342  0.003285  0.004344  0.009058
k  0.014740  0.005913  0.001862  0.035923
l  0.050558  0.039641  0.062050  0.035680
m  0.023832  0.028909  0.029039  0.033975
n  0.114892  0.062199  0.073964  0.099912
o  0.067640  0.066141  0.056217  0.034819
p  0.030858  0.018835  0.023455  0.029722
q  0.001791  0.000438  0.005833  0.000179
r  0.039399  0.072492  0.082030  0.056879
s  0.065023  0.059571  0.066518  0.054380
t  0.046150  0.095488  0.061057  0.050208
u  0.027828  0.024967  0.042690  0.043260
v  0.004822  0.010731  0.015140  0.003003
w  0.005786  0.023872  0.000745  0.006233
x  0.000413  0.003066  0.005088  0.000308
y  0.033889  0.014893  0.004964  0.012921
z  0.003031  0.000657  0.001986  0.001136

image

image


웹 애플리케이션 만들기

애플리케이션을 실행시킬 때마다 새로 데이터를 학습시키는 일은 불필요한 일이다. 따라서 학습시킨 매개변수를 저장하고 활용을 하게끔 할 것이다.

from sklearn import svm
from sklearn.externals import joblib
import json

#각 언어의 출현 빈도 데이터 (JSON) 읽어 들이기
with open ("./language/freq.json","r",encoding="utf-8") as fp:
    d=json.load(fp)
    data=d[0]
    
#데이터 학습시키기
clf=svm.SVC()
clf.fit(data["freqs"], data["labels"])

#학습 데이터 저장하기
joblib.dump(clf, "./language/freq.pkl")
print("ok")

pkl은 주로 객체를 저장할 때 사용하는 확장자이다.


아래와 같은 폴더 구조를 만들어 보자.

<root>
|-<cgi-bin>
| |-webapp.py
| |-freq.pkl

root 폴더 안에 cgi-bin 폴더 안에 위의 코드를 통해 만든 freq.pkl 과 아래에서 만들 webapp.py 라는 파일이 들어가 있는 구조이다.


webapp.py 파일은 다음과 같다.

import cgi, os.path
from sklearn.externals import joblib

#학습 데이터 읽어들이기
pklfile=os.path.dirname(__file__)+"/freq.pkl"
clf=joblib.load(pklfile)

#텍스트 입력 양식 출력하기
def show_form(text, msg=""):
    print("Content-Type : text/html ; charset=utf-8")
    print("")
    print("""
        <html>
        <body><form>
        <textarea name="text" rows="8" cols="40">{0}</textarea>
        <p><input type="submit" value="판정"></p>
        <p>{1}</p>
        </form></body></html>
    """.format(cgi,escape(text), msg))

#판정하기
def detect_lang(text):
    text=text.lower()
    code_a, code_z = (ord("a"), ord("z"))
    cnt=[0 for i in range(26)]
    for char in text:
        n=ord(char)-code_a
        if 0<= n <= 26:
            cnt[n]+=1
    total=sum(cnt)
    if total ==0: return "입력이 없습니다"
    freq=list(map(lambda n: n/ total, cnt))
    
    #언어 예측하기
    res=clf.predict([freq])
    
    #언어코드 한국어로 변환
    lang_dic={"en":"영어", "fr":"프랑스어", "id":"인도네시아어", "tl": "타갈로그어"}
    return lang_dic[res[0]]

#입력 양식의 값 읽어 들이기
form=cgi.FieldStorage()
text=form.getvalue("text", default="")
msg=""
if text!="":
    lang=detect_lang(text)
    msg="판정 결과: "+ lang
    
#입력 양식 출력
show_form(text, msg)


  • 실행방법

    $ cd cgi-bin경로 # ex)/Users/Desktop/root/cgi-bin)
    $ chmod +x webapp.py # 파이썬 권한 부여
    $ cd ..
    $ python3 -m http.server --cgi 8080
    

    이후 웹 브라우저에서 아래의 URL로 접속하면 된다.

    http://localhost:8080/cgi-bin/webapp.py
    


    학습량이 부족해 너무 짧은 문장은 잘못 구분하는 경우가 있지만, 2문장 이상인 경우는 꽤 잘 구분했다.

image

img


시간을 내어 크롤링을 바탕으로 학습을 꽤 시켜볼 예정이다.





© 2018. by yeo0

Powered by yeo0