Machine Learning in Action ch.4.5 Naive Bayes
4.5 파이썬으로 텍스트 분류하기
텍스트 문서에서 속성들을 추출하기 위해서는 텍스트를 분할해야 한다. 속성은 텍스트 문서에서 얻을 수 있는 토큰들이 될 것이다. 하나의 토큰은 문자의 조합을 구성된다. 택스트의 모든 조각을 토큰 벡터로 줄일 것이다.
이번 실습에서는 한 전자 게시판의 필터를 만들어 볼 것이다. 작성자가 부정적이거나 폭력적인 언어를 사용할 경우 부적합하다는 메시지를 표시한다.
1은 폭력적인것을 나타내고 0은 폭력적이지 않은 것을 나타낸다.
4.5.1 준비 : 텍스트로 단어 벡터 만들기
우리는 하나의 문장을 하나의 벡터로 변환한 단어 벡터 또는 토큰 벡터의 형태로 텍스트 문서를 보기 시작할 것이다.
def loadDataSet():
postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0,1,0,1,0,1] #1 is abusive, 0 not
return postingList,classVec
def createVocabList(dataSet):
vocabSet = set([]) #create empty set
for document in dataSet:
vocabSet = vocabSet | set(document) #union of the two sets
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else: print "the word: %s is not in my Vocabulary!" % word
return returnVec
2번째 함수인 createVocabList() 함수는 굉장히 유용하게 다른 알고리즘에도 쓰일 것 같다.
왜냐하면 여러 리스트들을 갖고있는 리스트를 하나의 리스트로 통합시키기 때문이다.
결과적으로
postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
라는 리스트가['cute','dalmation','worthless','love','garbage','problems','posting','how','not','stop','quit','my','I','so','dog','help','to','flea','stupid','licks','food','is','him','steak','please','has','take','maybe','ate','park','mr','buying'] 처럼 되었다. 중복된 단어는 하나가 되었다.
3번째 함수인 setOfWords2Vec()는 하나로 통합된 리스트에 문자의 단어들이 있는지 확인하는 함수이다.
4.5.2 훈련 : 단어 벡터로 확률 계산하기
이제 단어를 단어의 횟수로 변환하고, 이 단어의 횟수를 가지고 확률을 계산하는 방법을 살펴보도록 하자. 우리는 4.5.1 에서 단어가 있는지 없는지 확인 했으며, 또한 문서가 어떤 분류 항목에 속하는 지 알고 있다.
p(ci | w) = p(w | ci) * p(ci) / p(w)
w를 볼드체로 표현한 것은 벡터라는 것을 의미하기 위해서다. 즉, 이것은 어휘집에 있는 단어들처럼 많은 값들을 가지고 있다. 그래서 어휘집을 다 펼쳐보면
p(w | ci) = p(w0,w1,w2,w3,w4 ...| ci )로 표현할 수 있다.
여기서 우리는 모든 단어들이 서로 독립적이라고 가정하며 이를 조건부 독립이라고 한다. 따라서 이에 대한 확률은 p(w0 | ci)*p(w1 | ci)*p(w2 | ci)* ...로 쉽게 계산할 수 있다.
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = ones(numWords); p1Num = ones(numWords) #change to ones()
p0Denom = 2.0; p1Denom = 2.0 #change to 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num/p1Denom) #change to log()
p0Vect = log(p0Num/p0Denom) #change to log()
return p0Vect,p1Vect,pAbusive
p(ci | w) = p(w | ci) * p(ci) / p(w)
w를 볼드체로 표현한 것은 벡터라는 것을 의미하기 위해서다. 즉, 이것은 어휘집에 있는 단어들처럼 많은 값들을 가지고 있다. 그래서 어휘집을 다 펼쳐보면
p(w | ci) = p(w0,w1,w2,w3,w4 ...| ci )로 표현할 수 있다.
여기서 우리는 모든 단어들이 서로 독립적이라고 가정하며 이를 조건부 독립이라고 한다. 따라서 이에 대한 확률은 p(w0 | ci)*p(w1 | ci)*p(w2 | ci)* ...로 쉽게 계산할 수 있다.
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = ones(numWords); p1Num = ones(numWords) #change to ones()
p0Denom = 2.0; p1Denom = 2.0 #change to 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num/p1Denom) #change to log()
p0Vect = log(p0Num/p0Denom) #change to log()
return p0Vect,p1Vect,pAbusive
4.5.3 검사 : 검사 조건을 반영하기 위해 분류기 수정하기
문서 분류를 시작할 때, 주어진 분류 항목에 속하는 문서의 확률을 구하기 위해 많은 양의 확률들을 가지고 곱하기 한다. 하지만 아까 말했듯이 각각의 확률이 독립이기 때문에 곱하는데 하나의 확률이 0이면 모두 0이 되므로 1로 초기화해야한다.
다른 문제로 언더플로우가 있다. 이 문제는 작은 수들끼리 너무 많이 곱하면 생기는 문제로 부정확한 답을 산출하게 된다. 이를 해결하기 위한 한 가지 방법으로 산출 결과에 대해 자연로그를 계산하는 것이다. 산출 결과에 대한 또 다른 부정확한 값을 도출하는 게 아닌가라는 의문을 가질 수 있겠지만 오히려 더 정확한 값을 낸다.
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts,listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)
testEntry = ['stupid', 'garbage']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)
댓글
댓글 쓰기