데이터과학, 기계학습, 데이터마이닝, 인공지능, 통계 등에 항상 따라붙는 수학이 선형대수학입니다. 이와 관련된 채용 공고에도 선형대수학에 대한 기본 또는 충분한 이해가 요건으로 붙는 경우가 많았습니다.
데이터과학을 하려면 선형대수를 꼭 배워야 하나?
이 질문에 대한 대부분의 전문가들의 답은 “그렇다”일 것입니다.
어쩌면 여러분은 “데이터과학을 하는데 선형대수학은 필요없다”는 말을 듣고 싶어서 이 포스트를 보고 있는지도 모르겠습니다.
솔직히 누군가 그렇게 말한 사람이 있다면 그건 “개뻥”입니다. 또는 속성 과정으로 금방 배우고 마스터링 할 수 있다고 말한다면 그것도 “개뻥”입니다.
“얼마만큼 깊이 또는 많이 하느냐”의 차이는 있겠지만 선형대수의 기초 지식은 반드시 필요하며 데이터과학, 데이터분석, 통계, 기계학습, 데이터마이닝과 같은 데이터 관련 고급 기술을 배울 때 선형대수는 필수적입니다. 못하면 멍텅구리가 되거나 결국에는 아예 아무것도 이해하지 못하게 됩니다.
선형대수학은 무엇인가?
무시무시한 어감을 가진 선형대수는 대수학의 한 갈래로 영어로는 “Linear Algebra”이고 “리니어 앨지브라”라고 읽습니다. 발음이 조금 까다롭기 때문에 한국인들은 “선형대수” 또는 “선대”라고 읽습니다.
선형대수학이 무엇인지 쉽게 설명하면
선형대수는 짧게 말하면 벡터와 행렬을 다루는 수학입니다.
벡터는 관련이 있는 숫자들을 묶어 놓은 것이고
행렬은 벡터 여러 개를 묶어서 사각형 깍두기로 만들어 놓은 것입니다.
이렇게 모양을 만들어 놓는 것은 안 중요한데 이렇게 서로 뭔가 관련이 있는 숫자들을 묶어서 뭉치로 만들고 이것들을 서로 곱하고 나누고 빼고 줄이고 바꾸고 이것저것 하면 숫자와 숫자의 관계나 숫자 압축, 숫자 분해이 가능하고 문제를 풀 수 있는 경우가 많아집니다.
즉 선형대수학과 미적분을 사용하면 현실에서 정답을 찾는 것이 아니라 쓸만한 결과를 거의 항상 얻어낼 수 있습니다.
선형대수의 큰 매력은 대부분의 경우에 문제를 해결할 수 있다는 것입니다. 정답이 있다는 것이 아니라 “문제를 해결”한다는 것을 이해해야 합니다.
선형대수는 수학적으로 답이 없는 문제가 있을지라도 현실적으로는 억지로라도 답을 찾거나 가장 그럴듯한 것을 찾아낼 수 있습니다.
답이 없다고 포기할 것이 아니라 가장 그럴듯하고 가장 쓸만한 것을 찾아낸다면 그것이 답인 것인데 선형대수는 그런 것을 하는데 가장 도움이 되는 수학입니다.
왜 이름이 선형대수인가?
선형은 “선의 형태”라는 뜻인데 벡터와 행렬을 다룬다고 하면서 여기에 갑자기 “선”은 왜 나오는가?
선형대수가 있으면 원형대수나 곡형대수 같은 것도 있는가? 이런 의문도 들텐데요.
쪼금 어렵게 말하면 벡터를 다차원공간상에 매핑하고 이 벡터를 여러 연산으로 바꾸게 되면 이 벡터가 차원안에서 직선으로만 움직이기 때문입니다. 벡터는 공간상의 점이라고 하면 이 벡터는 직선운동만 가능합니다. 회전해서 움직일 수 없고 직선이 아니게 바꾸는 유일한 방법은 내적이라는 계산밖에 없습니다.
쉽게 말하면 벡터를 공간에 있는 점으로 표현해서 그림을 그려놓고 이런저런 계산을 해서 이 벡터를 다시 공간상에 점을 찍어 놓고 보면 직선으로만 움직이더라는 것이 이렇게 직선으로만 움직이게 해서 숫자로 부터 어떤 의미나 관계를 찾아내기 때문에 선형대수라고 합니다.
그래서 삼각형과 원의 관계를 다루는 삼각함수가 선형대수에서는 잘 보이지 않습니다. 물론 수학자들이 하는 고등수학에는 선형대수나 삼각함수나 뭐 이것저것 섞여서 빈번하게 나오지만 그건 그 사람들 알아서 할 일이구요.
데이터과학을 위해서선형대수를 꼭 배워하는 이유?
선형대수학을 모르고 데이터과학, 통계분석, 데이터마이닝, 기계학습, 인공지능을 한다면 어떤 알고리즘이나 방법(프레임워크나 스킴)의 원리를 이해하지 못하게 됩니다.
이해하지 못하면 다룰 수 없고 알고리즘같은 것이 압도당하고 지배당하게 됩니다. 샘플 코드 복붙해서 실행하는 것인 모델 빌드나 데이터 과학의 전부가 아닙니다.
간단한 예로 선형회귀(Linear Regression)에 대해서 얘기해보면
선형회귀는 처음보면 단순하고 쉬워 보이지만 기저를 다 이해하기 상당히 까다롭고 어렵습니다. 기본을 이해하지 못하면 결국 제대로 다루지 못하고 그 보다 더 복잡하고 난해한 알고리즘은 사용하지 못합니다.
선형회귀를 이해하기 위해서는 OLS를 알아야하고 그러려면 행렬연산이나 이것저것 선형대수와 미적분을 알아야합니다. 제대로 이해하지 못한채 경험적으로 암기한 내용으로만 선형회귀를 하고 나서는 엉뚱한 선형회귀 모델을 만들어 놓고 “이게 왜이러지?”하고 고민하는 경우를 많이 봤습니다.
숫자묶음에서 특징을 추출하는 특이값분해 같은 기법도 선형대수를 배우지 않으면 1도 이해하지 못합니다.
인공지능에서 숱하게 사용하는 경사하강(Gradient Desecnt)과 역전파(Back propagation)같은 것은 선형대수와 미적분의 컬래버입니다.
어떻게 배우면 될까? 문과도 가능할까?
선형대수는 대수학 중에서 매우 쉬운 수학 중에 하나입니다.
대수학들 (미적분, 선형대수학, 위상수학 등…)중에서 쉽다는 말이지 쉽다는 것이 절대 아닙니다.
문과든 이과든 이 선형대수학은 쉬운것 같지만 그렇게 쉽지 않습니다.
그렇다고 수포자가 못할 정도의 것은 아닙니다. 이것도 역시 사람이 만든 것이기 때문입니다.
선형대수학 공부를 쉽게 접근하려면
온라인 강의를 많이 들으면 좋습니다. 요즘 유튜브에 강의가 참 많이 있습니다. 하지만 시간이 많이 든다는 것을 알아야 하고 이해가 될 때까지 반복해서 듣거나 이해가 안되면 다른 강의를 찾는 것이 좋습니다.
가장 유명한 강의이고 추천할 강의는 길버트 스트랭 MIT 교수님의 선형대수학 강의이고 자막이 있습니다.
온라인 강의가 싫으면 결국 대학교재 중에 좋은 것을 하나 읽어야 하는데 “공업수학”교재를 찾으면 됩니다. 크레이그 공업수학 책이 가장 잘 알려진 편이지만 기초가 없으면 독학하기에 조금 어렵지만 못할 것도 아닙니다.
이런 책은 이해가 안되더라도 처음부터 끝까지 다 읽어야 좋은데 고등학교 이과 수준의 수학을 다 알고 대학에서 배우는 고등수학도 일부 알아야 한다는 점입니다.
늦지 않았을까?
늦었다고 시작했을 때는 늦은 겁니다. 하지만 더 늦으면 계속 늦어지기만 할 뿐입니다. 조금씩 하다보면 언젠가는 선형대수가 만만해지는 시기가 옵니다. 물론 이럴때가 또 위험하 시기이긴 하지만 말이죠.
중요한 것은 “지금부터 해도 나는 안될꺼야” 라는생각과 “책 한 권만 떼면 금방 마스터 할 수있을꺼야” 라는 생각을 버리는 것입니다.
자연어처리, 크롤한 데이터 정제 등을 할 때 특수문자를 제거하거나 클린징을 해야 할 때 많이 하는 작업입니다.
특히 비정형 데이터 중에서 텍스트(문자열)을 다루다보면 계속 해야 하는 그런 작업입니다.
짧게 먼저 요약하면
가장 빠른 것은 translate() 메서드를 사용하는 것이고
그 다음은 string.replace() 메서드를 사용하는 것이고
가장 느린 것은 정규표현식을 사용하는 것입니다.
속도가 문제되지 않으면 (느려도 되면) 정규표현식을 사용하는 것이 가장 유연하고 좋습니다. 특정 문자를 넣고 빼거나 숫자를 포함하거나 하는 여러가지 작업을 할 수 있습니다.
3가지 방법의 소스코드를 참고하세요.
translate() 함수 사용하기
# strings 패키지의 translate() 함수를 사용하여 특수기호를 제거하는 예제
import string
input_string = '!hi. wh?at is the weat[h]er lik?e. !@##$%%^^&*)_+{}|?"'
output_string = input_string.translate(str.maketrans('', '', string.punctuation))
print(output_string)
# Returns: hi what is the weather like
# 제거되는 특수기호는 아래와 같다.
print(string.punctuation)
# Returns: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
정규표현식 regular expression 사용하기
# 정규표현식을 사용하여 특수기호를 제거하는 예제
import re
input_string = '!hi. wh?at is the weat[h]er lik?e. !@##$%%^^&*)_+{}|?"'
output_string = re.sub(r'[^\w\s]', '', input_string)
print(output_string)
# Returns: hi what is the weather like
# 좀더 빨리 하려면 정규표현식을 컴파일하는 것이 좋다.
pattern_punctuation = re.compile(r'[^\w\s]')
output_string = pattern_punctuation.sub('', input_string)
print(output_string)
string.replace() 사용하기
# string.replace() 함수를 사용하여 특수기호를 제거하는 예제
import string
input_string = '!hi. wh?at is the weat[h]er lik?e. !@##$%%^^&*)_+{}|?"'
for character in string.punctuation:
input_string = input_string.replace(character, '')
print(input_string)
# Returns: hi what is the weather like
이런 것은 KNN (K-nearest-neighbor) 와 같은 기계학습 모델에 사용하는 것입니다. KNN은 판별 모델에서 사용할때 매우 강력한 알고리즘이지만 검색할 때 너무 느리고 자원을 많이 사용하는 문제로 인해서 실제로는 거의 사용을 못하는 알고리즘이지만 Faiss를 이용하면 이걸 쓸 수 있습니다.
Faiss 색인을 생성할 때 벡터의 차원을 지정해주고, Index의 유형도 결정을 해줘야 하는 것이 중요합니다. 검색은 입력한 k의 갯수만큼 리턴하게 되어 있고 벡터의 색인 번호와 거리를 리턴하게 되어 있늡니다.
색인 번호는 그냥 입력한 입력한 벡터의 순번입니다.
import faiss
import numpy as np
import random
# Euclidean distance 기반으로 가장 가까운 벡터를 찾는다.
# 랜덤으로 10차원 벡터를 10개 생성
vectors = [[random.uniform(0, 1) for _ in range(10)] for _ in range(10)]
# 10차원짜리 벡터를 검색하기 위한 Faiss index 생성
index = faiss.IndexFlatL2(10)
# Vector를 numpy array로 바꾸기
vectors = np.array(vectors).astype(np.float32)
# 아까 만든 10x10 벡터를 Faiss index에 넣기
index.add(vectors)
# query vector를 하나 만들기
query_vector = np.array([[random.uniform(0, 1) for x in range(10)]]).astype(np.float32)
print("query vector: {}".format(query_vector))
# 가장 가까운 것 10개 찾기
distances, indices = index.search(query_vector, 10)
# 결과룰 출력하자
idx = 0
for i in indices:
print("v{}: {}, distance={}".format(idx+1, vectors[i], distances[idx]))
idx += 1
Faiss로 코사인 유사도로 검색하기
유클리디안 거리(Euclidean Distance)로 가장 가까운 벡터를 찾으면 특정 차원의 양적 수치에 따라는 거리가 가깝다고 판별되는 편향의 문제가 있습니다. 이게 문제가 될 때가 있고 그렇지 않을 때가 있는데 이것은 문제의 도메인에 따라 다릅니다. 그러니까 문제가 주어진 환경에 따라 그때그때 다르다는 뜻입니다.
이런 문제를 피하는 방법은 유사도를 계산할 때 거리측정 방법을 유클리디안 거리를 사용하지 않고 코사인 유사도를 사용해서 벡터의 방향이 가까운 것을 찾는 것입니다. 보통 검색엔진들도 이 방법을 기본으로 사용합니다.
Faiss도 이걸 지원하는데 예제는 아래 코드를 보시면 되고 앞서 설명했던 유클리디안 거리 기반의 검색과 다른 점은 index를 생성할 때 타입을 다르게 생성해야 하고 벡터를 노말라이즈 해줘야 한다는 것입니다. 벡터가 이미 노말라이즈되어 있다면 안해도 됩니다.
import faiss
import numpy as np
import random
# 코사인 유사도 (Cosine Similarity) 를 이용해서 가장 가까운 벡터를 찾으려면 몇가지를 바꿔줘야 한다.
# 코사인 유사도 (Cosine Similarity) 를 사용하려면 벡터 내적으로 색인하는 index를 만들면 된다.
# 코사인 유사도를 계산하라면 벡터 내적을 필연적으로 계산해야 하기 때문이다.
# 랜덤으로 10차원 벡터를 10개 생성
vectors = [[random.uniform(0, 1) for _ in range(10)] for _ in range(100)]
# 10차원짜리 벡터를 검색하기 위한 Faiss index를 생성
# 생성할 때 Inner Product을 검색할 수 있는 index를 생성한다.
index = faiss.IndexFlatIP(10)
# 아래는 위와 동일하다.
# index = faiss.index_factory(300, "Flat", faiss.METRIC_INNER_PRODUCT)
# Vector를 numpy array로 바꾸기
vectors = np.array(vectors).astype(np.float32)
# vectors를 노말라이즈 해준다.
faiss.normalize_L2(vectors)
# 아까 만든 10x10 벡터를 Faiss index에 넣기
index.add(vectors)
# query vector를 하나 만들기
query_vector = np.array([[random.uniform(0, 1) for x in range(10)]]).astype(np.float32)
print("query vector: {}".format(query_vector))
# 가장 가까운 것 10개 찾기
distances, indices = index.search(query_vector, 50)
# 결과룰 출력하자.
idx = 0
for i in indices:
print("v{}: {}, distance={}".format(idx+1, vectors[i], distances[idx]))
idx += 1
먼저 스타벅스 매장 주소 데이터는 스타벅스 웹사이트에서 가져오면 되는데 csv나 엑셀 파일로 다운로드하게 지원하지 않으니 웹페이지의 내용물을 읽어서 파싱(parsing)해야 합니다.
이 작업은 웹 스크랩이라고 하는데 흔히 크롤이라고 부릅니다. 사실 원래 크롤과 스크랩은 서로 차이가 많습니다만 이 글에서는 그냥 널리 알려진 대로 크롤이라고 하겠습니다.
또 스타벅스 웹페이지는 지도상에 매장의 위치를 표시하기 위해서 Ajax로 데이터를 호출하는 방식을 사용하는데 이것의 방식을 알아내서 어떻게 데이터를 가져와야 하는지도 알고 있어야 합니다.
보통 크롬이나 파이어폭스의 개발자도구를 사용해서 알아내는데 연습과 방법을 익히는데 시간과 노력이 필요합니다. IT활용능력, 웹개발능력과 관련이 있는 것이라서 이것까지 설명하려면 설명이 길어지므로 이것을 알아내는 방법은 여기서는 생략합니다. 다음 기회에 다른 포스트에서 하겠습니다.
이 글에 소스코드를 넣어 두었으니 이미 완성된 코드를 참고 참고하세요.
인구데이터는 국가통계포털 웹사이트에서 가져오면 됩니다. csv나 excel로 다운로드 할 수 있게 지원하므로 매우 편합니다만 그래도 역시 데이터 정제, 정돈 작업은 좀 필요합니다.
인구데이터는 여기 말고도 받을 수 있는 곳이 더 있습니다. 더 편한곳이 있으면 그냥 거기를 이용하세요.
데이터를 크롤하는 법
R언어는 텍스트 파일을 파싱하거나 json 데이터를 처리하는 것이 조금 복잡한 편에 속합니다. R언어가 수치와 벡터 계산에 중점을 두기 때문에 이런 텍스트 처리에는 매우 약합니다.
하기가 까다롭다는 말입니다.
이런 것을 하려면 Python이나 Javascript가 더 나은 선택이지만
R언어로 이걸 하면 한 코드에서 데이터 크롤, 정제, 분석, 시각화까지 한 번에 할 수 있어 관리가 편하고 나중에 코드를 다시 볼 때 전체 흐름을 이해하기 편하다는 장점이 있습니다.
데이터랭글링이라는 것을 할 텐데 이런 작업은 tidyverse 패키지에 포함된 여러 패키지를 이용하는 것이 가장 세련되고 좋은 방법입니다.
그리고 시각화는 ggplot2를 사용하겠습니다.
결과
스타벅스 전체 매장수: 1658개
1658개로 나옵니다. 스타벅스는 모두 신세계에서 운영하는 직영점이라고 알려져 있고 관리가 웹사이트 관리가 잘 되고 있을 것이라고 생각돼서 이 수치는 맞을 것입니다.
수천개는 될 것 같지만 제 예상보다는 적습니다.
시도별 매장수
sido n
<chr> <int>
1 서울특별시 571
2 경기도 383
3 부산광역시 128
4 대구광역시 71
5 인천광역시 67
6 경상남도 66
7 광주광역시 59
8 대전광역시 59
9 경상북도 48
10 충청남도 36
11 울산광역시 29
12 전라북도 29
13 강원도 28
14 충청북도 26
15 전라남도 25
16 제주특별자치도 22
17 세종특별자치시 11
시도별 매장수는 역시 서울이 가장 많고 그 다음은 경기도, 부산 순입니다. 인구수가 많은 시도에 매장이 더 많은 것을 알 수 있습니다.
네 당연하죠. 물론 지역별로 로컬 카페나 다른 프렌차이즈, 브랜드의 커피매장이 더 인기가 있는 지역이 있을 수도 있으니 이 가설이 틀렸을 여지는 있습니다.
차트로 보며 시도별 매장수의 규모를 더 직관적으로 확인할 수 있는데 서울특별시와 경기도를 합치면 다른 시도를 다 합친 것 보다 더 많습니다.
우리나라 스타벅스 매장은 수도권에 크게 집중되어 있습니다.
구군별 매장수
sido gugun n sido_gugun
<chr> <chr> <int> <chr>
1 서울특별시 강남구 88 서울특별시 강남구
2 서울특별시 중구 52 서울특별시 중구
3 서울특별시 서초구 48 서울특별시 서초구
4 경기도 성남시 47 경기도 성남시
5 경기도 고양시 42 경기도 고양시
6 서울특별시 영등포구 40 서울특별시 영등포구
7 서울특별시 종로구 39 서울특별시 종로구
8 경기도 수원시 35 경기도 수원시
9 경기도 용인시 34 경기도 용인시
10 서울특별시 송파구 34 서울특별시 송파구
구군별로 매장수를 살펴보면 역시 서울 강남구가 가장 많습니다. 한국에서 아마 인구밀도가 가장 높은 지역이며 유동인구와 일과시간에 직장인이 가장 많이 있는 곳입니다.
그 다음은 역시 유동인구가 가장 많고 오피스밀집 지역이며 인구밀도가 높은 서울 중구입니다. 중구에는 명동, 충무로, 을지로가 있습니다.
그 다음은 서울 서초구인데 서초구는 강남구와 인구밀도는 비슷하지만 오피스공간 보다는 주거지역이 훨씬 많기 때문에 강남구나 중구에 비해서 낮시간에 직장인이 아주 많지는 않습니다. 그래도 괘나 많습니다.
그리고 그 다음은 경기도 성남시인데 성남에는 분당과 판교가 있으며 성남전체의 인구도 많으므로 이것도 당연합니다.
서울-강남구에 스타벅스가 압도적으로 많으며 다른 대형 지역구들도 제법 많습니다. 앞서 말했듯이 이 지역은 직장인들이 주중에 많이 머무는 지역들입니다. 물론 여기 나온 인구수는 거주 인구수이기 때문에 직장인이 주중에 많다는 것과의 상관관계를 다시 확인해야 할 필요가 있습니다만 알려진 바로는 매우 특별한 경우를 빼면 거주인구가 많은 지역에 직장도 많습니다.
인구수와 매장수의 상관관계
Pearson's product-moment correlation
data: sido_tbl$n and sido_tbl$total
t = 7.6707, df = 15, p-value = 1.439e-06
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7216524 0.9609928
sample estimates:
cor
0.8926678
시도별 인구수와 스타벅스 매장수의 상관관계를 보면 상관계수가 0.89로 강한 상관이며 검정에서 대립가설 채택으로 상관이 있다는 결과가 나옵니다.
더 해볼 것
상관관계를 볼 때 단순 인구수가 아닌 면적을 인구수로 나눠서 인구밀집도를 구한 다음에 상관관계를 보는 것이 더 합리적이겠지만 면적데이터를 가져와서 붙여야 하니 귀찮아서 생략하겠습니다.
또 구군별로도 면적데이터를 가져와서 구군별로도 인구수와 스타벅스 매장수가 상관이 있는지 확인해 보는 것도 필요합니다만 이것도 생략하겠습니다.
혹시 해보고 싶으시면 아래 소스를 보시고 고쳐서 직접 한 번 해보세요.
결론 및 인사이트
매장수와 지역의 인구밀집도는 관계가 있습니다.
시도별, 구군별로 모두 상관관계가 있습니다.
따라서 스타벅스매장이 많은 곳은 인구밀집도가 높은 지역일 가능성이 큽니다.
인구밀집도는 유동인구수와 상관관계가 있는 것으로 알려져 있습니다.
스타벅스 매장이 많은 곳(있는 곳)은 유동인구가 많은 곳입니다.
마약 누군가 많은 유동인구가 필요한 점포를 개업하려고 한다면 스타벅스 부근에 개점하면 됩니다.
길거리 음식을 팔거나 판매하려면 패션 팟업스토어, 푸드 트럭을 열려면 스타벅스 근처에 하면 잘 될 것입니다. 물론 그게 가능하다면 말이죠.