본문 바로가기
Programming/Python

파이썬 내장 모듈로만 벡터 연산 실습

by Renechoi 2022. 10. 14.

부스트코스의 "머신러닝을 위한 파이썬" 강좌 필기 내용 

 

https://www.boostcourse.org/ai222/lecture/253409/?isDesc=false

 

 

텍스트 마이닝 실습 코드로 

 

문서의 유사도를 측정하는 모듈 작성 

 

 

 

import os


''' 
로직 : 

문자 => 벡터와 숫자로 표현
방식은 원핫인코딩 / bag of words => 각각의 단어별로 인덱스를 부여해서 한 문장의 단어 개수를 표현할 수 있다 

벡터화된 문자들의 유사도를 거리로 측정할 수 있다 

유사도 측정에는 유클리드 거리, 코사인 유사도 측정 
=> 코사인 유사도가 더 많이 쓰이고 적중률이 높음 


프로세스 : 

- 파일을 불러오기 
- 파일 읽어서 단어 사전 만들기 
- 단어별로 index 만들기
- 만들어진 인덱스로 문별로 bag of words vector를 생성 
- 비교하고자 하는 문서 비교하기
- 얼마나 맞는지 측정하기 

=> 첫번째 문서와 나머지 문서의 유사도 측정 
=> 같은거 축구 혹은 야구에 대한 유사한 문서 뽑기 



'''



def get_file_list(dir_name):
    return os.listdir(dir_name)

def get_conetents(file_list):
    y_class = []
    X_text = []
    class_dict = {              
        1: "0", 2: "0", 3:"0", 4:"0", 5:"1", 6:"1", 7:"1", 8:"1"}               # 0은 야구 , 1은 축구 

    for file_name in file_list:
        try:
            f = open(file_name, "r",  encoding="cp949")         # cp949 = 윈도우로 작성된 파일 열 때 
            category = int(file_name.split(os.sep)[1].split("_")[0])        # 카테고리 값은 파일명 제일 앞에 있는 숫자 
            y_class.append(class_dict[category])                # 0과 1일때 구분해서 y값으로 넣어준다 
            X_text.append(f.read())         
            f.close()
        except UnicodeDecodeError as e:
            print(e)
            print(file_name)
    return X_text, y_class                                      # 각각 40개, 총 80개 반환해줌 = > x에는 텍스트, y에는 숫자 = 카테고리 값이 들어가게 됨 



def get_cleaned_text(text):                                 # 의미 없는 문장 부호등은 제거한다 
    import re
    text = re.sub('\W+','', text.lower() )                  # 문장부호를 없애고 소문자로 만드는 함수 
    return text


def get_corpus_dict(text):
    text = [sentence.split() for sentence in text]                                  # 80개의 document가 들어있는데 단어 개수대로 잘려져 있음 = 2 dimentional 
    clenad_words = [get_cleaned_text(word) for words in text for word in words]     # 텍스트에서 단어들을 뽑아 오고, 그 단어들에서 word를 다시 한글자씩 뽑아서 1차원 array로 바꿔준다 

    from collections import OrderedDict
    corpus_dict = OrderedDict()
    for i, v in enumerate(set(clenad_words)):               # 단어와 인덱스 값을 각각 뽑아줌 
        corpus_dict[v] = i
    return corpus_dict


def get_count_vector(text, corpus):
    text = [sentence.split() for sentence in text]
    word_number_list = [[corpus[get_cleaned_text(word)] for word in words] for words in text]           # 똑 같은 방식으로 전처리 해서 다시 corupus에 넘겨서 인덱스와 밸류 값으로 받을 수 있게 됨
    X_vector = [[0 for _ in range(len(corpus))] for x in range(len(text))]                              # 80개의 문서에 대해서 4302 벡터 스페이스를 만들어주는 방식
                     # 언더바를 쓰면 이 변수를 쓰지 않고 모두 0으로 채워주는 코드


    for i, text in enumerate(word_number_list):     # 모두 0으로 채워진 행렬에 인덱스 값을 가져오면서 올려줌 
        for word_number in text:
            X_vector[i][word_number] += 1
    return X_vector

import math
def get_cosine_similarity(v1,v2):                                           #첫번째 문서와 두번째 문서에 대한 유사도 계산 
    "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):
    source_vector = X_vector[source]
    similarity_list = []
    for target_vector in X_vector:
        similarity_list.append(
            get_cosine_similarity(source_vector, target_vector))
    return similarity_list      # 1개의 문서와 80개를 각각 비교한 값 


def get_top_n_similarity_news(similarity_score, 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]

def get_accuracy(similarity_list, y_class, source_news):
    source_class = y_class[source_news]

    return sum([source_class == y_class[i[0]] for i in similarity_list]) / len(similarity_list)


if __name__ == "__main__":
    dir_name = "news_data"                                                       # 뉴스데이터 파일에서 가져옴 
    file_list = get_file_list(dir_name)                                          # 파일 리스트라는 리스트 타입 변수에 넣어둔다 
    file_list = [os.path.join(dir_name, file_name) for file_name in file_list]   # os.path로 조인을 해주어야 슬래쉬와 역슬래쉬를 운영체제에 맞춰서 조인을 해줌 => 폴더명과 파일명 합쳐서 나옴 => 80개 

    X_text, y_class = get_conetents(file_list)

    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)           # 가장 유사한 거 10개를 뽑아오게 => accuracy 변수로 연결해줌 
        accuracy_score = get_accuracy(similarity_news, y_class, source_number)
        result.append(accuracy_score)
    print(sum(result) / 80)

 

 

하지만 실전에서는 이와 같이 코드를 짜서 하는 일은 거의 없고 

 

Numpy와 Pandas만 이용하여도 쉽게 구현할 수 있다. 

 

Scikit-learn의 Countvectorizer 를 이용하면 손쉽게 구현 가능. 

 

from sklearn.feature_extraction.text import Countvectorizer 

vectorizer = CountVectorizer()
corpus = [ 
	'This is the first document.',
    'This is the second second document.',
    'And the third one.',
    'Is this the first document?',
]

X = vectorizer.fit_transform(corpus)
X.toarray()

.
.
.
.
.
.

 

 

 

반응형