다른 패키지 없이 Python 만을 사용하여 한 뉴스에 대해 비슷한 뉴스를 찾아내는 News Categorization을 진행하려 한다.

진행 하기 앞서 알아야하는 것이 있다.


컴퓨터는 문자를 그대로 이해하지 못함

문자 -> 숫자

 

숫자간에 유사하다는 표현을 어떻게 할까?

유사하다 = 가깝다(거리 계산) - 유클리드 or Cosine distance

 

따라서

문자 -> 숫자 -> Vector

 

문자를 Vector로 바꿔주는 One-Hot Encoding 사용!

 

One-Hot Encoding

  • Vector Space를 만들어 하나의 문자에 대한 Vector 표현을 가능하게 해준다.
  • 하나의 문자(단어)를 Vector의 index로 인식, 단어 존재시 1 없으면 0 

One-Hot Encoding
Bag of words

 

 


News Categorization Process

  1. 파일을 불러오기 (80개의 기사를 스크럽한 txt 파일)
  2. 파일을 읽기
  3. 단어사전(corpus) 만들기, 단어별 Index 만들기
  4. 만들어진 인덱스로 문서별 Bag of words vector 생성
  5. 비교하고자 하는 문서 비교하기
  6. 얼마나 맞는지 측정하기

 

 

1. 파일을 불러오기

import os

def get_file_list(dir_name):
    return os.listdir(dir_name)  # listdir 이라는 Python bulit-in module 사용
    
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]

get_file_list : news_data 에 해당하는 폴더 내의 파일 명을 모두 가져와서 return 해주는 함수

os.path.join : Python에서 폴더-파일명을 연결을 해줄 때, 각 os별로 폴더 구분 방법이 다르므로 os에 맞게 자동으로 설정해줌

 

 

2. 파일별로 내용 읽기

def get_contents(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")  # 수집경로 windows -> cp949, Linux&Mac -> UTF
            category = int(file_name.split(os.sep)[1].split("_")[0])
            y_class.append(class_dict[category])
            X_text.append(f.read())
            f.close()
        expect UnicodeDecodeError as e:
            print(e)
            print(file_name)
        return X_text, y_class

os.sep : 파일 폴더 기호(\)를 나누는 것을 의미. os.sep[1] = only 파일명  이 작업은 ->방향으로 수행된다.

(파일명 예시 : 1_Dae-Ho Lee walk-off homer gives Mariners 4-2 win over Rangers)

X_text 에는 문서 내용이, y_class 에는 카테고리의 값들이 있다.

 

 

3. Corpus + 단어별 index 만들기

def get_cleaned_text(word):
    import re
    p_word = re.sub('\W+','', text.lower())
    return p_word
    
def get_corpus_dict(text):
    text = [sentence.split() for sentence in text]  # text -> 2-Dimensional List
    cleaned_words = [get_cleaned_text(word) for words in text for word in words] # 1-Dimensional List

    from collections import Ordereddict
    corpus_dict = OrderedDict()
    for i, v in enumerate(set(cleaned_words)):
        corpus_dict[v] = i
    return corpus_dict

re : regular expression

courpus_dict[v] : 동일한 단어들이 없으므로(set) 단어가 key 값이 될 수 있다.

 

4. 문서별 Bag of words vector 생성

def get_count_vector(text, corpus):
    text = [word.split() for word in text]
    word_number_list = [[corpus[get_cleaned_text(word)] for word in words] for words in text]
    X_vector = [[0 for _ in range(len(corpus))] for _ in range(len(text))]  # Vector 초기화
    
    for i, text in enumerate(word_number_list):
        for word_number in text:
            X_vector[i][word_number] += 1
    return X_vector

word_number_list 는 80개의 기사로 이루어진 2-Dimensional List이고, 해당 단어의 value 값을 가짐

 

 

5. 비교하기

import math
def get_cosine_sililarity(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
        sumxy += x*y
        sumyy += y*y
    return sumxy/math.sqrt(sumxx*sumyy)

단순 cosine distance 공식을 구현한 것

 

6. 비교결과 정리하기

# source : 대상 문서
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
    
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]

get_cosine_similarity : 총 80번 반복하고, source 의 경우 값이 1이므로, 1인 것은 제외한다.

OrderedDict 자료형이므로 sorted가 가능하다.

 

 

전체코드

import os

def get_file_list(dir_name):
    return os.listdir(dir_name)  # listdir 이라는 Python bulit-in module 사용
    

def get_contents(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")  # 수집경로 windows -> cp949, Linux&Mac -> UTF
            category = int(file_name.split(os.sep)[1].split("_")[0])
            y_class.append(class_dict[category])
            X_text.append(f.read())           
            f.close()
        except UnicodeDecodeError as e:
            print(e)
            print(file_name)
    return X_text, y_class

def get_cleaned_text(word):
    import re
    p_word = re.sub('\W+','', word.lower())
    return p_word
    
def get_corpus_dict(text):
    text = [sentence.split() for sentence in text]  # text -> 2-Dimensional List
    cleaned_words = [get_cleaned_text(word) for words in text for word in words] # 1-Dimensional List

    from collections import OrderedDict
    corpus_dict = OrderedDict()
    for i, v in enumerate(set(cleaned_words)):
        corpus_dict[v] = i
    return corpus_dict

def get_count_vector(text, corpus):
    text = [word.split() for word in text]
    word_number_list = [[corpus[get_cleaned_text(word)] for word in words] for words in text]
    X_vector = [[0 for _ in range(len(corpus))] for _ in range(len(text))]  # Vector 초기화
    
    for i, text in enumerate(word_number_list):
        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
        sumxy += x*y
        sumyy += y*y
    return sumxy/math.sqrt(sumxx*sumyy)

# source : 대상 문서
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
    
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]

    X_text, y_class = get_contents(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)
        #print(similarity_news)
        accuracy_score = get_accuracy(similarity_news, y_class, source_number)
        result.append(accuracy_score)
    print(sum(result) / 80)

 

 

마치며

사실 이렇게 잘 안한다. . .

이미 만들어진 모듈을 사용하지. . .

모듈을 사용할 때, 어떻게 작동하지?? 라는 기술적 부채를 덜기위해 low level에서 진행해 보았다.

'✍🏻Language & FrameWork > Python' 카테고리의 다른 글

[Python for ML] Numpy - 1  (2) 2024.01.08
[Python for ML] Linear Algebra Codes - 2  (2) 2024.01.04
[Python for ML] Linear Algebra Codes - 1  (2) 2024.01.02
[Python for ML] Asterisk  (0) 2024.01.02
[Python for ML] Reduce 함수  (0) 2024.01.01

Vector representation of python

  • Vector를 파이썬으로 표현하는 다양한 방법 존재
vector_a = [1, 2, 10]  # List로 표현했을 경우
vector_b = (1, 2, 10)  # Tuple로 표현했을 경우
vector_c = {'x':1, 'y':2, 'z':10}  # dict로 표현했을 경우
  • 최선의 방법은 없고, 값의 변경 유무, 속성값 유무에 따라 선택가능

 

1. Vector 덧셈

Ex 1) 파이썬 답지 못한 벡터 표현 (X)

u = [2, 2]
v = [2, 3]
z = [3, 5]
result = []
for i in range(len(u)):
    result.append(u[i] + v[i] + z[i])
    
print(result)

 

 

Ex 2) zip을 사용하여 Ex 1) 표현 

u = [2, 2]
v = [2, 3]
z = [3, 5]

result = [sum(i) for i in zip(u,v,z)]
print(result)

[7, 10]

 

 

2. Vector 곱셈

Ex) Scalar-Vector Product

u = [1, 2, 3]
v = [4, 5, 6]
a = 5
result = [a * sum(z) for z in zip(u,v)]

print(result)

[25, 35, 45]

 

 

Matrix representation of python

  • Matrix도 파이썬으로 표현하는 다양한 방법 존재
matrix_a = [[3, 6], [4, 5]]  # List로 표현했을 경우
matirx_b = [(3, 6), (4, 5)]  # Tuple로 표현했을 경우
matrix_c = {(0, 0):3, (0, 1):6, (1, 0):4, (1, 1):5}  # dict로 표현했을 경우
  • dict 로 표현할 때는 무궁무진한 방법이 있음
  • 기본적으로 2-Dimensional List 형태로 표현함
  • [[1번째 row], [2번째 row], [3번째 row]]

 

1. Matrix 덧셈

Ex) List comprehension 사용

matrix_a = [[3, 6], [4, 5]]
matrix_b = [[5, 8], [6, 7]]
result = [[sum(row) for row in zip(*t)] for t in zip(matrix_a, matrix_b)]

print(result)

[[8,14], [10, 12]]

 

 

2. Matrix 곱셈(Scalar)

Ex) Scalar-Matrix Product

matrix_a = [[3,6], [4,5]]
alpha = 4
result = [[alpha * element for element in t] for t in matrix_a]

print(result)

[[12, 24], [16, 20]]

 

 

3. Matrix Transpose

Ex) List comprehension + zip

matrix_a = [[1, 2, 3], [4, 5, 6]]
result = [[row for row in t] for t in zip(*matrix_a)]

print(result)

 

 

4. Matrix Product

Ex) matrix_b에서 zip(*matrix_b)을 사용하여 column_b을 만들어줌

matrix_a = [[1, 2, 3], [4, 5, 6]]
matrix_b = [[1, 4], [2, 5], [3, 6]]
result = [[a * b for a, b in zip(row_a, column_b) for column_b in zip(*matrix_b)] for row_a in matrix_a]

print(result)

 

'✍🏻Language & FrameWork > Python' 카테고리의 다른 글

[Python for ML] Linear Algebra Codes - 2  (2) 2024.01.04
[Python for ML] News Categorization  (0) 2024.01.02
[Python for ML] Asterisk  (0) 2024.01.02
[Python for ML] Reduce 함수  (0) 2024.01.01
[Python for ML] Map 함수  (0) 2024.01.01

Asterisk(*)

  • 단순 곱셈, 제곱 연산, 가변 인자(여러 개 값들을 한 번에 받을 수 있음)등 다양하게 사용됨
  • tuple, dict 등의 자료형에 들어가 있는 값을 unpacking 하기 편리함
  • 함수의 입력값, zip 등에 유용하게 사용가능

 

Ex 1) 가변 인자 사용 예

  • a = 1, *args = (2, 3, 4, 5, 6)
  • 가변 인자는 Tuple 형태
def asterisk_test(a, *args):
    print(a, args)
    print(type(args))
    
asterisk_test(1, 2, 3, 4, 5, 6)

1 (2, 3, 4, 5, 6)

<class 'tuple'>

 

 

Ex 2) 키워드 인자 사용 예

  • a = 1, **kargs = {'b':2, 'c':3, 'd':4, 'e':5, 'f':6}
  • 키워드 인자는 Dict 형태
def asterisk_test(a, **kargs):
    print(a, kargs)
    print(type(kargs))
    
asterisk_test(1, b=2, c=3, d=4, e=5, f=6)

1 {'b':2, 'c':3, 'd':4, 'e':5, 'f':6}

<class 'dict'>

 

 

Ex 3) unpacking 예 

  • 함수 선언의 args는 하나의 tuple을 받음
  • 이것을 *args를 하여 unpacking 
def asterisk_test(a, args):
    print(a, *args)
    print(type(args))
    
asterisk_test(1, (2,3,4,5,6))

1 2 3 4 5 6     (unpacking 된 결과)

<class 'tuple'>

 

 

Ex 3-1) unpacking 예 2

a, b, c = ([1, 2], [3, 4], [5, 6])
print(a, b, c)

data = ([1, 2], [3, 4], [5, 6])
print(*data)

[1, 2], [3, 4], [5, 6]

[1, 2], [3, 4], [5, 6]

 

 

Ex 3-2) unpacking 예 3

def asterisk_test(a, b, c, d, e):
    print(a, b, c, d, e)
    
data = {"d":1, "c":2, "b":3, "e":56}
asterisk_test(10, **data)

10 3 2 1 56

 

 

Ex 3-3) unpacking 예 4

for data in zip(*([1, 2], [3, 4], [5, 6])):
    print(sum(data))

9    (1 + 3+ 5)

12    (2+ 4 + 6)

 

'✍🏻Language & FrameWork > Python' 카테고리의 다른 글

[Python for ML] News Categorization  (0) 2024.01.02
[Python for ML] Linear Algebra Codes - 1  (2) 2024.01.02
[Python for ML] Reduce 함수  (0) 2024.01.01
[Python for ML] Map 함수  (0) 2024.01.01
[Python for ML] Lambda 함수  (0) 2024.01.01

Reduce 함수

  • map 함수와 달리 list에 똑같은 함수를 적용해서 통합(이전의 계산 결과를 하나의 입력으로 넣는다)
  • 사용을 위해 from functools import reduce 호출
  • Legacy library나 다양한 머신러닝 코드에서 여전히 사용중

 

 

Ex 1) 기본적인 사용 예

from functools import reduce

print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))

15 -> (처음 계산 1 + 2 = 3을 x에 넣고, 다음의 계산 3 + 3 = 6 을 x에 넣고. . . 반복)



Ex 2) 팩토리얼 계산

def factorial(n):
    return reduce(
            lambda x, y: x * y, range(1, n+1))
            
print(factorial(5))

120 -> (1 * 2 * 3 * 4* 5)

 

 

'✍🏻Language & FrameWork > Python' 카테고리의 다른 글

[Python for ML] Linear Algebra Codes - 1  (2) 2024.01.02
[Python for ML] Asterisk  (0) 2024.01.02
[Python for ML] Map 함수  (0) 2024.01.01
[Python for ML] Lambda 함수  (0) 2024.01.01
[Python for ML] Zip 함수  (0) 2024.01.01