본문 바로가기

Python/부스트코스_노트

Pythonic Code - news categorization

네이버 부스트코스 강좌 정리입니다.

 

  • 문서 간 유사도(코사인 유사도)를 통해 특정 문서의 범주를 분류해보기
    • 총 80개의 문서, 야구선수/축구선수 기사로의 분류
import os

##########################################################################################################

def get_file_list(dir_name): # file name들을 가져오는 함수 # 폴더명 인자 # 폴더가 위치한 경로를 인자로
    return os.listdir(dir_name) # 폴더 내 파일명을 리스트 형태로 반환 

##########################################################################################################

# 현재 8명의 선수에 대해 각각 10개씩의 기사 (야구4,축구4)
# 기사 카테고리의 경우 선수별로 1_~~~.txt 형식으로 맨 앞 숫자로 누구의 기사인지 구분

def get_conetents(file_list): # 모든 파일의 상대경로 리스트를 인자로
    y_class = []
    X_text = []
    class_dict = { # 야구선수와 축구선수에 대해 0,1의 클래스로 분류할것
        1: "0", 2: "0", 3:"0", 4:"0", 5:"1", 6:"1", 7:"1", 8:"1"} # 파일명의 맨 앞 숫자로 분류

    for file_name in file_list: 
        try:
            f = open(file_name, "r",  encoding="cp949") # winodw => cp949, 파일 읽기
            category = int(file_name.split(os.sep)[1].split("_")[0]) # 파일명 맨앞 숫자 빼내어 int형으로 
            # os.sep => \,/
            y_class.append(class_dict[category]) # 해당파일이 0,1의 클래스 중 어디에 속하는지 (class_dict에 맞추어 분류됨)
            X_text.append(f.read()) # 해당파일의 text 
            f.close()
        except UnicodeDecodeError as e:
            print(e)
            print(file_name)
    return X_text, y_class # 모든 파일들에 대해 각각의 text와 종목클래스 정보 반환



def get_cleaned_text(words): #
    import re
    words = re.sub('\W+','', words.lower() ) # 각각의 단어들을 소문자로 바꾸고, 문장부호를 없에고 반환
    return words


def get_corpus_dict(text): # 모든 text를 인자로 받음(80개)
    text = [sentence.split() for sentence in text] # 각 글별로 split을 진행하여 text라는 리스트에 저장
    # split()은 결과물을 리스트로 반환함
    # 2차원 리스트로 저장됨. # [[글1의 단어들],[글2의 단어들],[글3의 단어들]....]
    cleand_words = [get_cleaned_text(word) for words in text for word in words] 
    # [글1의 단어들]에서 단어들을 하나씩 뽑은 후 다시 글자 하나 단위로 뽑아 get_cleaned함수를 적용
    
    # list comprehension에서 for문이 나란히 두개인경우 앞에 for문부터 실행. 아래와 같다
    # for words in text:
    #     for word in words:
    #         get_cleaned_text(word)

    from collections import OrderedDict # dict의 값을 순서대로 사용가능. # 원래 dict는 순서 없음.
    corpus_dict = OrderedDict()
    for i, v in enumerate(set(cleand_words)): # 중복제거하여 단어사전을 만듬
        corpus_dict[v] = i # 각 단어별로 index를 지정
    return corpus_dict # 각 단어를 key값으로 갖는 dict를 반환


def get_count_vector(text, corpus): # 문서별 단어빈도를 vector로 
    text = [sentence.split() for sentence in text]
    word_number_list = [[corpus[get_cleaned_text(word)] for word in words] for words in text]
    # get_corpus_dict와 같은 원리이지만 2차원 리스트로 반환함. # 각 문서별로 구분하기위해
    X_vector = [[0 for _ in range(len(corpus))] for x in range(len(text))]
    # 80 4030? 의 matrix 생성 # _ => 변수를 사용하지 않는다.(0으로 행렬을 채워넣는다)

    for i, text in enumerate(word_number_list): # 각 문서별로 corpus_dict에 맞추어 빈도수 구해줌
        for word_number in text:
            X_vector[i][word_number] += 1
    return X_vector # 문서별 단어빈도수까지 추가

import math
def get_cosine_similarity(v1,v2): # 문서 2개의 벡터를 넣어 코사인 유사도 계산
    "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
    sumxx, sumxy, sumyy = 0, 0, 0
    for i in range(len(v1)):
        x = v1[i]; y = v2[i]
        sumxx += x*x
        sumyy += y*y
        sumxy += x*y
    return sumxy/math.sqrt(sumxx*sumyy)
 
def get_similarity_score(X_vector, source): # X_vector => 전체(80개)문서 벡터,source => target text vector
    source_vector = X_vector[source] # target text를 몇번째 문서로 할것인가
    similarity_list = []
    for target_vector in X_vector:
        similarity_list.append(get_cosine_similarity(source_vector, target_vector))
        # ex) 10번째 문서와 나머지 문서간의 코사인 유사도를 계산하여 리스트형태로 반환
        # 여기서는 1개와 나머지 80개(자기자신포함)의 유사도 계산 => 자기자신과의 코사인 유사도는 1 
    return similarity_list


def get_top_n_similarity_news(similarity_score, n): # 가장 높은 유사도를 가진 문서 n개 
    import operator
    x = {i:v for i, v in enumerate(similarity_score)}
    sorted_x = sorted(x.items(), key=operator.itemgetter(1))

    return list(reversed(sorted_x))[1:n+1] # 유사도 1은 제외

def get_accuracy(similarity_list, y_class, source_news): 
    source_class = y_class[source_news]
#   target과 유사도가 가장 높은 n개의 문서들의 클래스를 이용하여 정확도 계산
#   ex) 10개중 0이 8개면 80% 
    return sum([source_class == y_class[i[0]] for i in similarity_list]) / len(similarity_list)
# 실행
if __name__ == "__main__":
    dir_name = "C:/Users/KIHyuk/Desktop/부스트코스/AI-python-connect-master/codes/ch_1/news/news_data" # 폴더명 * 폴더위치
    file_list = get_file_list(dir_name) # 폴더 내에 파일명을 리스트로 return
    file_list = [os.path.join(dir_name, file_name) for file_name in file_list] # 파일의 상대경로까지
    # os.path => 각 os방식에 맞추어 경로 연결 (window => \, mac => /)

    X_text, y_class = get_conetents(file_list)
#   X_text => 모든 text 리스트
#   y_class => 각 text 별 0,1 클래스

    corpus = get_corpus_dict(X_text)
    print("Number of words : {0}".format(len(corpus)))
    X_vector = get_count_vector(X_text, corpus)
    source_number = 10

    result = []

    for i in range(80): 
        source_number = i # 각 기사별로 나머지 기사와의 유사도 계산하여 

        similarity_score = get_similarity_score(X_vector, source_number)
        similarity_news = get_top_n_similarity_news(similarity_score, 10)
        accuracy_score = get_accuracy(similarity_news, y_class, source_number)
        result.append(accuracy_score)
    print(sum(result) / 80) # 전체 평균 정확도

'Python > 부스트코스_노트' 카테고리의 다른 글

Pythonic Code - Asterisk  (0) 2020.02.23
Pythonic Code - map & reduce  (0) 2020.02.22
Pythonic Code - enumerate & zip  (0) 2020.02.22