이번에 진행한 프로젝트는
수능 영어 지문 중에서 주제를 찾는 문제를 풀어보는 인공지능을 구현해 보겠습니다.
from nltk import download
from nltk import word_tokenize
from nltk.corpus import stopwords
from nltk.stem.lancaster import LancasterStemmer
from nltk.stem import WordNetLemmatizer
import nltk
download('stopwords')
stop_words = stopwords.words('english')
import pandas as pd
import numpy as np
먼저 기본적인 영어 NLP 를 처리해주는 패키지를 사용했습니다.
def _preprocess(doc):
doc = doc.lower() # Lower the text.
doc = word_tokenize(doc) # Split into words.
doc = [w for w in doc if not w in stop_words] # Remove stopwords.
doc = [w for w in doc if w.isalpha()] # Remove numbers and punctuation.
return doc
def _lemmatize(tags):
"""단어 리스트를 받아 품사를 구분하여 원형으로 바꿔줍니다."""
result = list()
n=WordNetLemmatizer()
for word , tag in tags:
if tag in ['VB', 'VBD', 'VBG', 'VBN', 'VBP']:
result.append(n.lemmatize(word,'v'))
else:
result.append(n.lemmatize(word))
return result
그리고 전처리를 해주는 함수와 lemmatize 함수를 선언했습니다.
전처리 함수는 문장을 받아 소문자로 변환해주고 토큰화해서 불용어를 제거해 줍니다.
lemmatize 함수는 prices 와 같은 단어를 원형 price 로 바꿔주는 패키지로
동사와 동사가 아닌 것들을 구별하여 원형변환을 진행했습니다.
Q. 문제
For their own benefit, companies have various ways of offering lower prices. One way of doing this is a trade discount. It is offered to the shops or businesses that buy goods on a large scale and sell them. There is also a quantity discount, which is offered to individuals who order quantities of a product. The company gives a price break to these buyers because they help cut the costs of selling, storing, shipping, and billing. Finally, a cash discount is a lower price offered to people who pay in cash. |
보기
1. "Types of Discount Pricing", 2. "Ways to Improve Products", 3. "How to Buy Products in Cash", 4. "Locations of Discount Stores", 5. "How to Start a Business" |
여기서 Word2vec 모델을 사용하는 데 이미 학습이 되어있는 모델 중 최적화된 모델인
GoogleNews-vectors-negative300-SLIM 모델을 사용했습니다. ( 검색하시면 다운받으실 수 있습니다.)
import gensim.models.keyedvectors as word2vec
path = './GoogleNews-vectors-negative300-SLIM.bin.gz'
w2vModel = word2vec.KeyedVectors.load_word2vec_format(path, binary=True)
패키지를 임포트 해준 뒤
TextTokens = _preprocess(originText)
textTags = nltk.pos_tag(TextTokens)
text_words = _lemmatize(textTags)
문제의 대한 텍스트를 전처리 해줍니다. 문제 본문 ( originText )
#정답 리스트를 단어별로 쪼개서 원형으로 바꿔줍니다.
resultChoice = list()
for choice in options:
tempChoice = _lemmatize(nltk.pos_tag(_preprocess(choice)))
resultChoice.append(tempChoice)
정답도 리스트로 받아 각각의 단어들로 토큰화 해주고 원형으로 바꿔줍니다.
# 본문에 있는 단어들과 보기단어들의 wmd의 거리를 계산하여 distanceList에 추가합니다.
# tuple 형식으로 번호를 추가하고 거리가 가까운 순으로 정렬합니다.
distanceList = []
for i in range(len(options)):
distance = w2vModel.wmdistance(text_words,resultChoice[i])
distanceList.append((distance , i))
distanceList.sort(key=lambda x : x[0])
여기서 불러온 모델의 wmdistance 를 사용해서 본문과 지문 사이의 거리를 받아옵니다.
여기서 WMD 를 간략하게 설명하자면
Word2vec 는 단어들간의 임베딩이라면 WMD 는 문장과 문장간의 유사도를 구하는 알고리즘입니다.
문장들 속 단어들의 유클리디안 거리를 구해서 문장간의 유사도를 측정한다고 보면됩니다.
위 예시 문제는 틀렸네요 . 정답은 1번인데 3번이라고 예측했네요.. ㅠㅠ
이렇게 해서 이 모든 과정을 함수로 만듭니다.
def question_solving(originText , options ,answer):
"""
Type : orginText(str) , options(list) , answer(int)
0. args 의 전처리 과정을 거칩니다.
1. 선택보기들과 본문단어들의 거리값들을 모아둔 distanceList에서 가장 가까운거리의 인덱스를 정답이라고 판단합니다.
2. picked 와 answer를 비교하여 정답이면 1을 리턴 아니면 0을 리턴합니다.
"""
text_words = _lemmatize(nltk.pos_tag(_preprocess(originText)))
resultChoice = list()
for choice in options:
tempChoice = _lemmatize(nltk.pos_tag(_preprocess(choice)))
resultChoice.append(tempChoice)
distanceList = list()
for choiceNum in range(len(options)):
distance = w2vModel.wmdistance(text_words,resultChoice[choiceNum])
distanceList.append(distance)
picked = distanceList.index(min(distanceList)) + 1
print("정답은 {}번 입니다.".format(picked))
if(answer == picked):
print("정답을 맞췄습니다.")
return 1
elif(answer != picked):
print("틀렸습니다. 실제정답은 {} 입니다".format(answer))
return 0
실제로 수집한 데이터들을 테스트 해보겠습니다.
data = pd.read_excel('text_data.xlsx')
data = data.replace(np.NaN, 'None')
#question_soving 함수가 정답이면 1을 리턴 하기때문에 맞은갯수 / 문제갯수를 통해 정확도를 계산합니다.
accuracy = 0
columns = ['#1','#2',"#3",'#4','#5','answer']
for i in range(len(data['text'])):
origin_text = data['text'][i]
options = data[columns].loc[i].values[:5]
answer = data[columns].loc[i].values[-1]
accuracy += question_solving(origin_text,options , answer)
print("정확도 : {}".format((accuracy / len(data['text']))))
print("{} 문제중 {}개의 정답을 맞췄습니다.".format(len(data['text']), accuracy ))
56.25% 의 정확도를 가지고 있네요.
56점이면 5~6등급인가요? 수능을 본지 오래되서 모르겠습니다.
그럼 한 번 말고 두 번 혹은 세 번만의 문제를 맞춘다면
얼마정도의 정확도를 가지고 있는지 테스트해 보았습니다.
def question_solving_generous(originText , options ,answer):
"""
question_solving_generous 함수는 몇번시도만에 정답을 맞췄는지를 체크합니다.
"""
text_words = _lemmatize(nltk.pos_tag(_preprocess(originText)))
resultChoice = list()
for choice in options:
tempChoice = _lemmatize(nltk.pos_tag(_preprocess(choice)))
resultChoice.append(tempChoice)
distanceList = list()
for choiceNum in range(len(options)):
distance = w2vModel.wmdistance(text_words,resultChoice[choiceNum])
distanceList.append((distance , choiceNum))
distanceList.sort(key=lambda x : x[0])
picked = distanceList[0][1] + 1
picked2 = distanceList[1][1] + 1
picked3 = distanceList[2][1] + 1
tryOne = 0; tryTwo = 0; tryThree = 0;
if(answer == picked): tryOne = 1
elif(answer == picked2): tryTwo = 1
elif(answer == picked3): tryThree = 1
return tryOne , tryTwo , tryThree
try1 = 0
try2 = 0
try3 = 0
columns = ['#1','#2',"#3",'#4','#5','answer']
for i in range(len(data['text'])):
origin_text = data['text'][i]
options = data[columns].loc[i].values[:5]
answer = data[columns].loc[i].values[-1]
tryOne , tryTwo , tryThree = question_solving_generous(origin_text,options , answer)
try1 += tryOne
try2 += tryOne + tryTwo
try3 += tryOne + tryTwo + tryThree
avg_score = round(try1 / len(data['text']),2) * 100
gen_score = round(try2 / len(data['text']),2) * 100
m_gen_score = round(try3 / len(data['text']),2) * 100
print(f'정답률은 {avg_score}%입니다.')
print(f'너그러운 정답률은 {gen_score}%입니다.' )
print(f'더 너그러운 정답률은 {m_gen_score}%입니다.')
세 번 만의 맞추면 89%의 정확도를 가지고 있네요.
수능문제 특성상 문제간의 서로 너무 비슷하지만 아주 쉬운 문제라면 잘 풀 수 있을 것 같네요.
수능 영어 문제 풀이 봇 이였습니다.
'Machine learning > NLP' 카테고리의 다른 글
[NLP]. Transformer : Structure - part1 (0) | 2021.08.19 |
---|---|
[NLP] Sequence to Sequence(시퀀스 투 시퀀스) 코드 (0) | 2021.08.19 |
[NLP] Sequence to Sequence (시퀀스 투 시퀀스), Attention(어텐션) 개념 (0) | 2021.08.18 |
[NLP] Word Encoding & Embedding (0) | 2021.01.13 |
[NLP] TF-IDF 를 활용한 제목별 추천 시스템. (0) | 2021.01.07 |