프로그래밍공부/파이썬
코퍼스 검색툴
중랑구보안관
2020. 6. 30. 11:19
NLU업무중를 하다보면 훈련된 데이터가 실제 코퍼스파일에 어디있는지 찾거나 혹은 문장이 어떤식으로 훈련되어 있는지 액션과 익스퍼트를 직접 찾아야 하는일이 많은데, 사실 txt파일을 일일이 찾는게 좀 번거로워서 tkinter를 이용해서 간단한 검색툴을 만들었었다.
사실 만든지는 몇 달 되었고 이거 말고도 업무용으로 만든게 몇개 있는데 계약기간이 끝나기전에 기록으로 남겨둘려고 올려 놓는다 ^^ 이 자료가 누군가에게 도움이 됬으면 좋겠다.
참고로 실행이 정상적으로 되려면 config.txt파일이 아래와 같은 형식으로 같은 폴더에 있어야한다.
TRAINING_PATH:C:/nlpworkspace/training/training
SERVICE_PATH:C:/nlpworkspace/service/service
EXPERT_PATH:models/ENG/expert/ha
DB_FILE_PATH:DB/EN/ARCH
PATTERNTEMP_PATH:ha_application/EN/domain/svm
그리고 이거는 나름대로 짧게 설계?한 것이다.
문장 입력 하면 코퍼스에서 액션 ne 찾아주기.+ 줄수. 파일이름.
마지막 : DB.txt 혹은 패턴템프에서 찾아주기.
1.config 파일에서 training경로를 찾아서 코퍼스를 읽어온다.
key(문장):data_list(클래스로 만들자)
1.코퍼스를 한줄 씩 읽는다.
2.#처리된것과 줄띄어쓰기를 넘긴다.
3.한줄을 \t으로 잘라서 액션을 가지고 와서 class에 넣어준다.
4.문장에서 반복문으로 find(사용 해당 문자가 없으면 -1 출력)
을 사용하여 <>안에 있는 NE와 Lexical을 가져와서 class에 넣어준다.
5.NE태깅을 뺸 문장 만 추출하여 class에 넣어준다.
6.위 데이터를 dictionary자료형에 넣어준다.
data_list : 액션명,익스퍼트명,줄수,코퍼스이름,원래 코퍼스 형태
2. Corpus = { 'sentenes':data_list, 반복.... }
3. 문장을 입력하면 dictionary에서 key값으로 value를 찾아와서 화면에 출력.
from tkinter import *
from tkinter.ttk import *
import sys
import os
import re
#코퍼스를 읽은 데이터를 저장하기 위한 클래스
#참조문제로 미사용.
#class Corpus():
#
# #액션명
# actionName=''
# #익스퍼트명
# expertName=''
# #줄수
# line=-1
# #NE이름(리스트)
# neList=[]
# #NE개수
# neCount=0
# #원래 문장 형태
# originSentence=''
# #ne를 뺀 문장 형태
# sentenceNotNeTagging=''
# #생성자
# def __init__(self,line):
# self.line = line
# pass
#
# #getter와 setter
# def getActionName(self):
# return self.actionName
# def setActionName(self, actionName):
# self.actionName=actionName
# def getExpertName(self):
# return self.expertName
# def setExpertName(self, expertName):
# self.expertName = expertName
# def getLine(self):
# return self.line
# def setLine(self,line):
# self.line=line
# def getNeList(self):
# return self.neList
# def setNeList(self,ne,lexical):
# neData={ne:lexical}
# self.neList.append(neData)
# self.neCount+=1
# def getNeCount(self):
# return self.neCount
# def getOriginSentence(self):
# return self.originSentence
# def setOriginSentence(self,originSentence):
# self.originSentence=originSentence
# def getSentenceNotNeTagging(self):
# return self.sentenceNotNeTagging
# def setSentenceNotNeTagging(self,sentenceNotNeTagging):
# self.sentenceNotNeTagging=sentenceNotNeTagging
#프레임 클래스
class MainFrame(Frame):
#경로들을 저장할 변수
training_path=''
service_path=''
db_file_path=''
expert_path=''
patternTemp_path=''
#코퍼스를 읽은 데이터를 저장할 딕셔너리. Corpus = { 'sentenes':data_list, 반복.... }
global corpus_datas
corpus_datas={}
#익스퍼트 정보를 저장할 딕셔너리
global expert_datas
expert_datas={}
#패턴템프를 읽은 정보를 저장할 딕셔너리
global pattern_datas
pattern_datas={}
def __init__(self, master):
#설정파일 읽는다.
self.readConfig()
#expert.list를 읽어온다.
self.readExpertList()
#코퍼스파일을 읽는다
self.readCorpus()
#patternTemp를 읽는다.
self.readPatternTemp()
#print(corpus_datas)
#GUI설계부분.
Frame.__init__(self, master)
self.master = master
self.master.title('searchTool v.0.11')
self.master.geometry('1200x600+100+100')
self.pack(fill=BOTH, expand=True)
#액션입력창
topFrame = Frame(self)
topFrame.pack(fill=X)
lblAction = Label(topFrame, text='액션입력 ', width=10)
lblAction.pack(side=LEFT, padx=10,pady=10)
entryAction = Entry(topFrame)
entryAction.pack(fill=X, padx=10,expand=True)
#검색하기
def searchStart():
readSentence=str(entryAction.get())
readSentence=re.sub('(\?|\,|\.|\-|\_)','',readSentence.lower())
data=corpus_datas.get(readSentence)
#0 액션명 1 줄수 2 원래 문장형태 3 파일 이름 4 익스퍼트명 5 이후..NE
#기존창에 입력되어있던것 삭제
actionNameTxt.delete(1.0,END)
neNameTxt.delete(1.0,END)
sentenceTxt.delete(1.0,END)
fileNameTxt.delete(1.0,END)
patternTmpTxt.delete(1.0,END)
#입력된액션에 대한 정보가 없는경우
if data == None:
actionNameTxt.delete(1.0,END)
actionNameTxt.insert(1.0,'없는 문장.')
return
#2개이상의 데이터가 있는 경우
if len(data) >= 2:
row=0.0
for d in data:
row+=1.0
actionNameTxt.insert(row,d[0]+'\t'+str(d[4])+'\n')
totalLen = len(d)
neData = ''
for i in range(5,totalLen):
neData += str(d[i])+','
neNameTxt.insert(row,neData.rstrip(',')+'\n')
sentenceTxt.insert(row,d[2]+'\n')
fileNameTxt.insert(row,d[3]+' / '+str(d[1])+'\n')
patternTmpTxt.insert(row,str(pattern_datas[d[2]])+'\n')
else:
actionNameTxt.insert(1.0,data[0]+'\t'+str(data[4]))
totalLen = len(data)
neData = ''
for i in range(5,totalLen):
neData += str(data[i])+','
neNameTxt.insert(1.0,neData.rstrip(','))
sentenceTxt.insert(1.0,data[2])
fileNameTxt.insert(1.0,data[3]+' / '+str(data[1]))
patternTmpTxt.insert(1.0,str(pattern_datas[data[2]])+'\n')
#검색버튼
btnFrame = Frame(self)
btnFrame.pack(fill=X)
btnSearch = Button(btnFrame, text="검색", command = searchStart)
btnSearch.pack(side=LEFT, padx=10, pady=10)
#결과창
middleFrame = Frame(self)
middleFrame.pack(fill=X)
lblActionName = Label(middleFrame, text='액션 / 익스퍼트', width=15)
lblActionName.pack(side=LEFT, padx=10,pady=10)
actionNameTxt = Text(middleFrame, height=6)
actionNameTxt.pack(fill=X, padx=10)
middleFrame2 = Frame(self)
middleFrame2.pack(fill=X)
lblNeName = Label(middleFrame2, text='NE ', width=15)
lblNeName.pack(side=LEFT, padx=10,pady=10)
neNameTxt = Text(middleFrame2, height=6)
neNameTxt.pack(fill=X, padx=10,expand=True)
middleFrame3 = Frame(self)
middleFrame3.pack(fill=X)
lblSentence = Label(middleFrame3, text='원래 문장 ', width=15)
lblSentence.pack(side=LEFT, padx=10,pady=10)
sentenceTxt = Text(middleFrame3, height=6)
sentenceTxt.pack(fill=X, padx=10,expand=True)
middleFrame4 = Frame(self)
middleFrame4.pack(fill=X)
lblFileName = Label(middleFrame4, text='파일이름 / 줄 수 ', width=15)
lblFileName.pack(side=LEFT, padx=10,pady=10)
fileNameTxt = Text(middleFrame4, height=6)
fileNameTxt.pack(fill=X, padx=10,expand=True)
middleFrame5 = Frame(self)
middleFrame5.pack(fill=X)
lblFileName = Label(middleFrame5, text='패턴템프 ', width=15)
lblFileName.pack(side=LEFT, padx=10,pady=10)
patternTmpTxt = Text(middleFrame5, height=12)
patternTmpTxt.pack(fill=X, padx=10,expand=True)
#설정파일 읽기.
def readConfig(self):
config_file_name = 'config.txt'
config = open(config_file_name,'r',encoding='UTF8')
lines = config.readlines()
for line in lines:
config_data=line.split(':')
if config_data[0] == 'TRAINING_PATH' :
global training_path
training_path = config_data[1].rstrip('\n')+':'+config_data[2].rstrip('\n')
elif config_data[0] == 'DB_FILE_PATH':
global db_file_path
db_file_path = config_data[1].rstrip('\n')
elif config_data[0] == 'EXPERT_PATH':
global expert_path
expert_path = config_data[1].rstrip('\n')
elif config_data[0] == 'SERVICE_PATH':
global service_path
service_path = config_data[1].rstrip('\n')+':'+config_data[2].rstrip('\n')
elif config_data[0] == 'PATTERNTEMP_PATH':
global patternTemp_path
patternTemp_path = config_data[1].rstrip('\n')
config.close()
#코퍼스파일 읽기
def readCorpus(self):
#DB디렉토리내의 코퍼스파일 이름들을 읽어옴.
total_path=training_path+'/'+db_file_path
corpus_list= os.listdir(total_path)
#print('corpus_list : '+str(corpus_list))
for file_name in corpus_list:
#코퍼스파일이 아닌경우 패스.
if file_name.find('.txt') == -1:
continue
#코퍼스파일들을 하나씩 읽어줌.
lines=''
try:
corpus_file = open(total_path+'/'+file_name,'r', encoding='UTF8')
lines = corpus_file.readlines()
except UnicodeDecodeError as e:
print(e)
print(corpus_file)
finally:
lineNumber=0
for line in lines:
lineNumber+=1
#주석이나 줄띄어쓰기 한것은 넘기기
if line.find('#') == 0 or line.find('\t') == -1:
continue
#corpus = Corpus(lineNumber)
innerData=[]
dataInCorpus=line.split('\t')
#읽은 줄에서 액션명, 문장, ne리스트를 읽어와야 한다.
#ne리스트를 읽어와 보자.
tempSentence=dataInCorpus[5]
while tempSentence.find('<')!=-1:
firstIndex=tempSentence.find('<')
lastIndex=tempSentence.find('>')
firstIndex+=1
try:
#neDatas[0] = Lexical, neDatas[1] = Ne
neData=tempSentence[firstIndex:lastIndex]
neDatas=neData.split(':')
#ne를 corpus클래스에 넣어준다.
#corpus.setNeList(neDatas[1],neDatas[0])
innerData.append([neDatas[1],neDatas[0]])
firstIndex-=1
middleIndex = tempSentence.find(':')
#검색한 ne를 빼준다.
tempSentence=tempSentence[:firstIndex]+tempSentence[firstIndex+1:middleIndex]+tempSentence[lastIndex+1:]
except IndexError as e:
print(e)
print(tempSentence)
print(neData)
#액션명
innerData.insert(0,dataInCorpus[4].strip())
#줄수
innerData.insert(1,lineNumber)
#원래 문장 형태
innerData.insert(2,dataInCorpus[5])
#파일 이름
innerData.insert(3,str(file_name))
#익스퍼트 이름
expertName=expert_datas.get(dataInCorpus[4])
innerData.insert(4,expertName)
key = tempSentence.lower()
key = re.sub('(\?|\,|\.|\-|\_)','',key)
#중복문장에 대한 해결책
if key in corpus_datas:
ret=corpus_datas[key]
newData=[]
if str(type(ret[0])) != "<class 'str'>":
for data in ret:
newData.append(data)
else:
newData.append(ret)
newData.append(innerData)
corpus_datas[key] = newData
else:
corpus_datas[key] = innerData
corpus_file.close()
#익스퍼트리스트 읽기
def readExpertList(self):
#자료구조 딕셔너리 {액션명 : 익스퍼트명}
total_path=service_path+'/'+expert_path
expert_file = open(total_path+'/expert.list','r',encoding='UTF8')
lines = expert_file.readlines()
#expert.list에서 액션-익스퍼트 읽기
for line in lines:
#exact,공백,주석은 제외
if line.find('#') == 0 or line.find('\t') == -1 or line.find('exact') > 0 :
continue
experts=line.split('\t')
expert_datas[experts[0]]=experts[1]
expert_file.close()
#패턴템프읽기
def readPatternTemp(self):
pass
#자료구조 딕셔너리 {문장 : [[레벨,훈련된형태,줄수],[레벨,훈련된형태,줄수],[레벨,훈련된형태,줄수]],....}
total_path=training_path+'/'+patternTemp_path
patternTmp_file=open(total_path+'/total.patternTmp','r',encoding='UTF8')
lines = patternTmp_file.readlines()
lineNumber=0
for line in lines:
lineNumber+=1
innerData=[]
#공백,주석은 제외
if line.find('#') == 0 or line.find('\t') == -1:
continue
patternDatas=line.split('\t')
#삽입할 데이터
innerData.append(patternDatas[0])
innerData.append(patternDatas[3])
innerData.append(lineNumber)
#키값 = ne태깅 뺴기전의 문장
key = patternDatas[5]
#중복되는 문장일 경우
if key in pattern_datas:
ret=pattern_datas[key]
newData=[]
if str(type(ret[0])) != "<class 'str'>":
for data in ret:
newData.append(data)
else:
newData.append(ret)
newData.append(innerData)
pattern_datas[key] = newData
else:
pattern_datas[key] = innerData
def main(self):
root = Tk()
mainFrame = MainFrame(root)
root.mainloop()
if __name__ == '__main__':
main(sys.argv)