3. 튜토리얼/금융 분석 프로그래밍 응용

파이썬 주식 시장 동향 분석 - 자연어처리 감성분석

swsong 2023. 1. 12. 07:04

주식 시장에는 기본적으로 주식을 팔고자 하는 사람과 사고자 하는 사람, 이렇게 크게 두 유형이 있다. 팽팽한 줄다리기 끝에 팔고자 하는 사람이 더 많으면 공급 초과로 가격은 하락하게 된다.

주가에 영향을 주는 요소는 금융 애널리스트의 예측, 기관 예측, 경제 위기, 그리고 이를 전달하는 뉴스 매체 등으로 셀 수 없이 많지만 결국 그 종착지에는 대중의 움직임이 있다. 그러한 대중의 움직임이 파는 쪽에 더 치우치면 가격은 떨어지고, 사는 쪽에 치우치면 가격은 상승한다.

그렇다면 우리는 지금 이 시간, 대중의 움직임이 어느 방향으로 기울고 있는가를 분석해 볼 수 있다. 그 대상은 특정 종목이나 특정 국가가 될 수도 있고 주식 시장 자체가 될 수 있다. 본 분석은 후자, 주식 시장 자체에 대해 사람들이 어떻게 평가하고 있는가를 분석해 볼 것이며 분석 대상이 되는 기간은 1주일이다. 즉, '이번 주 주식 시장에 대한 사람들의 평가는 어떠한가?'에 대한 분석이 되겠다.(2022-10-12 기준)

Step 1. 블로그 정보 수집

1-1. 웹사이트 구조 및 데이터 호출 정보 확인

파이썬으로 포스팅을 긁어올 것이므로 타깃을 명확히 확인하자. '네이버 블로그'(https://section.blog.naver.com/) 사이트에 진입해서 '주가 전망'이라는 키워드를 검색 후 개발자 모드를 열어 네트워크 탭을 살펴본다.

미리 보기를 통해 반환된 문서들을 살펴보면 그중 SearchList.naver에 우리가 찾는 데이터가 있는 것을 확인할 수 있다. 이제 파이썬으로 해당 자료를 가져올 수 있으면 된다.

먼저 해당 데이터가 어떤 방식(GET, POST)으로, 어떤 url 값으로 반환받을 수 있는지 살펴본 다음, 하단 쿼리 문자열 매개변수를 확인함으로써 넘겨줄 요청 값들을 지정해 줄 수 있다.

1-2. 단일 페이지 데이터 호출

currentPage는 현재 페이지 번호, countPerPage는 페이지당 포함하는 포스팅 수, endDate와 startDate는 지난 7일간을 지정해 주고 keyword는 검색한 값을 보여준다. 이렇게 파라미터 파악이 끝났으면 코드 작성을 위해 필요한 라이브러리를 호출하겠다.

endDate, startDate는 직접 문자열로 입력해 줘도 되지만 스크래퍼를 개발할 때에는 항상 자동화를 염두에 두어야 한다. 따라서 기준 날짜로부터 지난 7일을 계산할 수 있도록 다음과 같이 함수를 사용하겠다.

이제, url, header, date, params를 정의하고 파라미터가 잘 세팅되는지 확인한다.

requests 모듈을 통해 데이터를 불러온다. 결괏값을 확인했을 때 우리가 앞서 봤던 SearchList.naver의 미리 보기 값과 형태가 동일해야 한다. 텍스트 앞부분만 짧게 출력하자.

데이터를 json 형태로 변환해 주면 좋겠다. 그전에 앞부분의 불필요한 문자열을 잘라줘야 정상적으로 변환이 될 것이다.

1-3. 다중 페이지 데이터 호출(함수)

이제, 앞서 구현한 코드들을 종합하여 키워드와 페이지 번호만 넣어주면 지난 7일간의 포스팅 정보를 긁어올 수 있는 함수를 정의하겠다.

총 10개 페이지, 70개 포스팅 정보가 수집되었다.

 

Step 2. 블로그 내용 수집

2-1. HTML 태그 정보 확인

이렇게 수집한 포스팅 정보로 각 url을 순회하며 텍스트만 뽑아올 것다. 수집한 url 중 하나에 진입해서 html 태그를 살펴보자.

본문 텍스트는 se-main-continer라는 div class가 지정되어 있다.

2-2. 데이터 호출 정보 확인

데이터 호출을 위해 이번에는 네트워크 탭의 '문서'를 살펴보면 PostView.naver에 우리가 찾는 본문 텍스트가 있다. 해당 데이터를 어떻게 호출할 수 있을지 url과 매개변수를 통해 살펴본다.

2-3. 단일 페이지 데이터 호출(함수)

이번에는 함수로 바로 만들어주겠다. select_one 함수로 본문을 가져오고 select('p')를 해줌으로써 본문 내 모든 문장들을 리스트로 추출한다. 또한, list comprehension 문법을 통해 각 리스트 내 요소에서 텍스트만 추출해 다시 리스트로 저장하고, 제어문자(u200b는 폭 없는 공백이다. 네이버 블로그 포스팅 시 자동으로 문자 사이에 포함됩니다.)는 없애준다.

계속해서 list comprehension 문법을 사용하며 줄바꿈은 분리해 주고 공백 요소는 제거해 깔끔한 형태의 문자열만 담은 리스트로 만든다.

Step 3. 감성 분류

이제, 이렇게 얻은 문자열에 대해 감성 분류를 실시할 수 있다. 텍스트 클렌징 작업은 최대한 정교하게 할수록 좋다. 텍스트 수집에 정도가 없듯 텍스트 전처리 방법에도 역시 정도가 없다.

이번 튜토리얼에서는 간단하게 전처리한 문장을 활용해 감성 분류를 해보자.

3-1. 허깅페이스 모델 검색

우선, 임의의 텍스트 한 줄을 가져다 잘 분류해 줄 수 있는 모델을 찾아보자.

언어 모델의 경우 대규모로 사전학습된 모델을 먼저 리서치해 보는 것이 좋다. 간단한 분석을 위해 몇 주간의 학습 시간을 소진하기에는 현실적으로 어렵기 때문에, 언어 모델 사용 시 다운스트림 테스크로 우리의 데이터 셋에 조금 더 확률 값을 높여주고 우리가 목표로 하는 결괏값을 출력해 내도록 구조를 맞춰주는 정도의 파인튜닝이 일반적이다.

감성 분류, 그중에서도 금융 텍스트에 특화된 한국어 모델이 이미 허깅페이스에 올라와 있다. 해당 모델을 테스트해 보고 사용 여부를 판단한다.

부정적 어감의 문장을 가져다 넣으니 negative로 잘 분류해 준다. 중립은 어떨까?

'엔비디아 전망'이라는 텍스트는 97.6%의 확률로 neutral으로 분류를 해준다. 만약 우리가 사용하는 모델이 긍/부정으로만 분류할 수 있다면 중립에 해당하는 문장은 최대한 걸러내야 할 것이다.

여기서는 우리가 수집한 전체 문장에 대해 중립, 부정, 긍정 각각의 비중을 살펴보고 주식시장의 동향을 파악해 보고자 한다.

우리는 특정 블로그 포스팅 자체에 대해 '긍정 포스팅이다.', '부정 포스팅이다.' 하고 분류하기보다 위 방식처럼 하나의 글 내에서도 여러 문장으로 분리하고 각각을 분류 대상으로 삼을 것이다. 한 명이 작성한 하나의 블로그 포스팅 안에서도 긍정적인 문장과 부정적인 문장이 혼재해있다. 해당 블로그 포스팅으로 누군가는 부정적인 정보를 획득해갈 것이고 누군가는 반대로 긍정적인 정보를 획득해간다. 사람마다 받아들이는 정보는 매우 주관적이므로 블로그 포스팅 내 모든 문장을 정보 혹은 대중의 판단 근거라 가정하는 것이다.

물론 이러한 분석 방식 역시 주관과 의도가 강하게 개입되어 있으며, 유일한 정답일 수 없다.

3-2. 추론 모델 인스턴스 생성 및 테스트

위 코드는 허깅페이스를 통해 우리가 사용하려는 모델과 토크나이저를 불러와 빠르게 추론 모델을 만든다.

해당 모델은 뉴스 기사 텍스트로 1차 파인튜닝된 모델이다. 그렇기 때문에 추가 학습 없이 우리 테스크에 바로 적용할 수 있다.

다만, 경우에 따라 같은 유형의 테스크라 하더라도 모델이 우리 데이터에 완전히 맞지는 않을 수 있다. 따라서 몇 가지 단어를 넣어 적절한 출력이 나오는지 한 번 더 확인하겠다.

주가 관련 텍스트를 학습한 탓에 상승이라는 키워드에는 positive를, 하락이라는 키워드에는 negative를, 애플과 같은 고유명사에는 neutral을 출력해 준다. 우리가 기대했던 결과값이다.

그럼, 이제 지난 7일간의 주식 시장에 대한 평가를 네이버 블로그에 한정해서 살펴보도록 하겠다.

3-3. 단일 포스팅 내 텍스트 감성 분류

우리가 분석하려는 총 포스트 수는 총 70개다. 첫 번째 포스팅을 먼저 분석해 보겠다.

3-4. 전체 7일간 포스팅 감성 분류

서론, 혹은 이야기를 풀어나가기 위한 담화용 텍스트는 neutral로 분류되고 그 외 주가 판단에 대한 내용은 positive 혹은 negative로 분류되는 모습을 볼 수 있다.

이번에는 전체 70개 포스팅에 대해 모두 분류하고 분류 비중을 살펴보도록 하겠다.

포스팅을 먼저 모두 수집하고 전체 데이터에 대해 한 번에 추론하는 방식 혹은 매 포스팅마다 수집과 추론을 반복해서 결괏값을 합치는 방식을 취할 수 있다.

속도 및 작업 편의를 고려하면 전자가 좋겠지만 각 포스팅에 수십 개의 문장이 있기 때문에 전체 분류 타깃이 메모리에 한 번에 올라가게 되면 중간에 멈출 수도 있다. 따라서 배치 작업으로 안정적인 추론이 이루어질 수 있도록 후자 형태로 코드를 구성하겠다.

3-5. 결과

결과는 중립 73%, 긍정 18%, 부정 8%로 현재 불안정한 시장 상황을 충분히 반영하고 있다.

사실, 이는 해석하기 나름이다. '여전히 긍정적인 전망을 하는 사람들이 더 많기 때문에 아직 바닥이 아니다'라고 할 수도 있고, '이제 바닥을 찍고 상승하려는 움직임이 보인다'라고 할 수도 있다.

따라서 이렇게 하나의 지표나 결과를 보고 전체 주식시장을 판단하는 것은 무리가 있겠지만 이러한 비율을 매주 추적하며 시계열로 분석한다면 더 유의미한 추세적 지표로 활용할 수 있을 것이다.