티스토리 뷰
주어진 문제
현재는 퍼펫티어를 사용하여 뉴스를 크롤링 해왔지만 해당 뉴스에서 문맥을 파악하지못해
실제 사건사고가 아님에도 인식이 되는 문제가 발생.
자연어 처리와 기계학습을 통하여 최대한 문맥을 파악할수 있도록 하려면 파이썬을 사용해야 했기 때문에
파이썬의 셀리니움과 뷰티풀숲을 사용하여 크롤링으로 전환하게 됨.
selenium, BeautifulSoup 사용이유
selenium도 크롤링 가능, BuautifulSoup도 크롤링 가능 근데 왜 같이써?
조금더 효과적으로 사용하기 위해 함께 사용하게 되었다.
selenium은 동적인 웹페이지에서 데이터를 수집하는것에 유용하다.
페이지를 옮겨 다닌다거나, javascript를 사용하는것에 있어서 브라우저를 제어하면서 스크랩을 할수가 있다.
하지만 정적페이지를 가져오는것은 BeautifulSoup이 훨씬 유용하다.
일단 내가 해야할것은 뉴스의 더보기버튼만 끝까지 누른다고 가정하면 모두 정적인 페이지 이기 때문에
selenium으로는 동적인 작업만, BeautifulSoup은 정적인 데이터들만 가져오도록 하였다.
나이브 베이즈 알고리즘을 선택한 이유
1. 로지스틱 회귀 2. 나이브 베이즈 3. 결정트리(예츨력과 성능으로 따지자면 결정트리 모델을 사용할 일은 없다.)
4. 랜덤 포레스트 5. XGBoost(Extreme Gradient Boosting) 6. lightGBM
이렇게 시도해볼만한 것들을 리스트로 만들어 놨었다.
이중에서 나이브 베이즈를 결정한 이유는
일단 우리의 기간이 많지가 않았으며 기계학습에 대한 이해도가 그렇게 높지가 않기 때문에 다루기 쉬웠어야 했다.
이중에 가장 간단하기도하고 빠르기도 하며 우리가 원하는 기능인 텍스트 분류에 효과적이기도 해서
가장 좋다고 생각하여 해당 알고리즘을 선택하였다.
여기서 이제 우리는 실제 데이터를 다뤄야 하는데 파이썬과 연동되는것들을 테스트 해보아야 했다.
가장 중요한 db
로컬 db와는 연결이 잘되나?
pip install pymysql
import pymysql
mysql에 flasktest를 만들어준다.
테이블에 유저도 넣어주었다.
# 데이터에 접근
cursor = db.cursor() # 데이터베이스 연결 객체에서 커서 객체를 생성하는 코드
sql = "select * from users"
cursor.execute(sql) # SQL 쿼리를 데이터베이스에 실행하는데 사용되는 Python 코드
users = cursor.fetchall() # 실행된 쿼리의 모든 결과를 가져옴
print(users)
로컬 db의 연결이 되었다.
하지만 우리는 팀프로젝트이브로 이제 도커에서 사용하는 db와 연결해야한다.
pip install psycopg2 # 파이썬에서 postgreSQL을 사용할 수 있게 해주는 커넥터(라이브러리). postgreSQL에 접근해서 데이터 읽고 쓰기 가능해짐.
import psycopg2
conn = psycopg2.connect(host='localhost',dbname='',user='',password='',port='5432')
cur = conn.cursor()
cur.execute('SELECT * FROM news')
info = cur.fetchall()
print(info)
이렇게 같은 방식으로 현재 사용하고 있던 db와 연결을 해주니
[
(1, '‘은평구 오피스텔 살인’ 40대 남성 구속 기소', 'https://n.news.naver.com/mnews/article/032/0003290117', '서울 은평구의 한 오피스텔에서 20대 여성을 살해한 혐의를 받는 40대 남성이 11일 구속 상태로 재판에 넘겨졌다. 서울서부지검 형사3부(부장검사 권내건)는 살인 등 혐의를 받는 40대 남성 김모씨를 구속기소 했다고', '경향신문', datetime.datetime(2024, 4, 11, 14, 25, 4, 650076)),
(2, '채권 거래 불발에 격분한 증권맨…상대 증권맨 찾아가 폭행', 'https://n.news.naver.com/mnews/article/018/0005711992', '채권 거래가 불발됐다는 이유로 일면식도 없는 다른 증권사 직원을 찾아가 폭행한 증권사 직원이 검찰에 넘겨졌다. 경찰(사진=뉴스1) 9일 경찰에 따르면 서울 영등포경찰서는 폭행 혐의로 서울 여의도의 한 증권사 채권 거', '이데일리', datetime.datetime(2024, 4, 11, 14, 25, 4, 650076)),
.
.
.
(10, '성신여대입구역 인근 마을버스 중앙선 넘어 5중 충돌…5명 부상', 'https://n.news.naver.com/mnews/article/421/0007483151', '지하철 4호선 성신여대입구역 인근 도로에서 마을버스와 승용차 등 차량 5대가 충돌해 5명이 다쳤다. 16일 성북소방서에 따르면 전날 낮 12시 36분쯤 서울 성북구 동소문동 성신여대입구역 인근 도로에서 경사길을 내려', '뉴스1', datetime.datetime(2024, 4, 16, 2, 55, 3, 408342))
]
원래 들어있던 10개의 데이터를 가지고 온건 볼수가 있었다.
그럼 이제 여기서 해야하는것은 환경변수 설정이다.
nest와는 다르게 여기서는 .env가 설정이 되지 않았다. config.를 설정해야하는건가 싶었지만 아니였다.
python의 라이브러리를 설치하면 해결이 되었다.
pip install python-dotenv
import os
import psycopg2
# .env 파일을 로드하여 환경 변수 설정
from dotenv import load_dotenv
load_dotenv()
def create_connection():
conn = psycopg2.connect(
host=os.getenv('DB_HOST'),
dbname=os.getenv('DB_NAME'),
user=os.getenv('DB_USERNAME'),
password=os.getenv('DB_PASSWORD'),
port=os.getenv('DB_PORT')
)
return conn
conn = create_connection()
cur = conn.cursor()
cur.execute('SELECT * FROM news')
info = cur.fetchall()
print(info)
마찬가지로 db안에 있는 정보가 잘뜨는걸 확인할수가 있다!!
이제 실제 프로젝트에 적용시키면된다.
파이썬을 깔아주고 현재 프로젝트에 가상 환경을 만들어줬다.
이유는 다수의 패키지를 사용하면 패키지 버전이나 의존성때문에 충돌이 일어나는 경우가 있는데 이를 해결하기 위해서 패키지를 독립적으로 관리하기 위해서 이다.
원하는 경로에서 아래 명령어를 입력 하면 다음과 같이 가상환경 파일들이 들어가게 된다.
python -m venv env
이제 가상환경을 적용시키기 위해서 스크립트를 실행하기 위한 명령어를 적어야한다.
env\Scripts\activate.ps1
# commend터미널이라면 .bat확장자, powerShell이라면 .ps1 확장자를 붙여주면된다.
이렇게 되면 가상환경이 적용된 상태가 된다.
가상환경이 적용된 상태에서 새로운 창을 띄우면
또한 터미널은 켜보면
이러한 알림문구가 잘뜨는걸 확인할수가 있다.
가상 환경이 잘 설정되었다.
이제 우리 프로젝트를 진행하면된다.
서버를 가동하기 위한 flask 설치
pip install flask
이런 문구가 나와야 한다.
#app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return '안녕'
서버가 잘 돌아가는지 확인하기 위한 코드를 적어주고 서버를 가동한다.
nest의 npm start처럼 flask에서는 flask run을 하면 서버가 돌아간다.
해당 경로로 요청을 보내면
서버가 작동되는걸 확인할수가 있다.
이제 여기서 우리가 원하는 작업을 시작하면된다.
뉴스 크롤링하기
node의 package_lock.json과 같은 역할을 하는 requirements.txt설치
설치 이유 : 협업을 위한 테스트를 하기 위해서는 동일한 버전과 동일한 라이브러리로 해야하기 때문.
pip install pipreqs
pip list --format=freeze > requirements.txt # 의존성 txt파일에 넣기
이제 동일한 환경에서 모두 테스트를 해볼수가 있다.
이걸 깔아서 테스트 하려면
pip install -r requirements.txt # 의존성 설치하기
이렇게 설치를 하면된다.
selenium, BeautifulSoup으로 크롤링 해오기
def crawling():
options = webdriver.ChromeOptions()
options.add_argument('--headless') # headless 모드 활성화
service = ChromeService(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
try:
driver.get("https://news.naver.com/breakingnews/section/102/249")
calendar_xpath ='//*[@id="newsct"]/div[1]/div[1]/div/button[2]'
calendar_element = driver.find_element(By.XPATH, calendar_xpath)
calendar_element.click()
time.sleep(1)
date_selector = ".is_today"
date_element = driver.find_element(By.CSS_SELECTOR, date_selector)
date_element.click()
time.sleep(1)
while True:
try:
show_more_button_selector = ".section_more_inner"
button_element = driver.find_element(By.CSS_SELECTOR, show_more_button_selector)
if button_element.is_displayed():
button_element.click()
time.sleep(1)
else:
break
except:
break
page_source = driver.page_source
soup = BeautifulSoup(page_source, 'html.parser')
title_elements = soup.find_all(class_='sa_text_strong')
mini_context_elements = soup.find_all(class_='sa_text_lede')
media_elements = soup.find_all(class_='sa_text_press')
url_elements = soup.find_all('a',class_='sa_text_title')
seoul_articles = []
key_word=['사고','폭행','화재','낙뢰','지진','재난','홍수','폭발','둔기','살인','절도','충돌','칼부림','묻지마','긴급체포','부상','사망','산불',]
for i in range(len(title_elements)):
title = title_elements[i].text
mini_context = mini_context_elements[i].text
media = media_elements[i].text
url = url_elements[i]['href']
if "서울" in title or "서울" in mini_context:
for keyword in key_word:
if keyword in title or keyword in mini_context:
seoul_articles.append({"title": title, "mini_context": mini_context,"media":media, "url":url})
break
copy_seoul_articles = seoul_articles[0:]
i = 0
while i < len(copy_seoul_articles):
j = i + 1
while j < len(copy_seoul_articles):
title1 = copy_seoul_articles[i]["title"]
title2 = copy_seoul_articles[j]["title"]
similarity = jellyfish.jaro_winkler_similarity(title1, title2)
if similarity > 0.53 :
del copy_seoul_articles[j]
else:
j += 1
i += 1
return copy_seoul_articles
except Exception as e:
error_message = f"데이터베이스 작업 중 오류 발생: {type(e).__name__} - {e}"
print(error_message)
return error_message
finally:
driver.close()
언어가 바뀌면서 느낀것은 솔직히 문법만 다를뿐이지 로직자체는 nest에서 했던 방식과 다른게 없다.
다만 여기서 볼만한것은 xpath이다.
selenium, BeautifulSoup 사용하기
xpath라는 단어를 처음 볼 뿐더러 path라고 하니 경로인것 같긴한데 뭔지 이해가 안갔다.
xpath
• XML문서의 요소나 속성을 선택하기 위한 경로 표현 언어이다.
• XML문서에서 원하는 요소를 찾거나 추출하기 위해 사용한다.
• XML문서의 구조를 탐색하고 검색하기 위한 강력한 도구로, 특정 요소를 선택하거나 필터링 하는데 사용한다.
이렇게 설명이 나와있다.
그러면 HTML은 XML과 같은 것 인가? 라는 의문이 생긴다.
조금만 신경써서 보면 ML이 같은걸 볼수있다.
Hyper Text Markup Language → 웹사이트의 모습을 기술하기 위한 마크업 언어.
Extensible Markup Language → 데이터를 정의하는 규칙을 제공하는 마크업 언어.
즉, 같은 마크업 언어이기 때문에 HTML에서도 사용할수가 있는것이다.
근데 어떻게 쓰지?
해당하는 사진의 xpath를 얻고 싶을때
원하는 부분을 우클릭한후 copy를 들어가 XPath를 눌러준다.
그러면 해당하는 부분의
//*[@id="ct"]/div/section[1]/div[2]/div/div[1]/div[2]/div/div/div[3]/ul[1]/li[1]/a/div[1]
이러한 xpath가 나온다.
즉 이러한 경로를 사용하여 원하는 작업을 할수가 있게 되었다.(검색, 버튼 클릭 등등)
while True:
try:
show_more_button_selector = ".section_more_inner"
button_element = driver.find_element(By.CSS_SELECTOR, show_more_button_selector)
if button_element.is_displayed():
button_element.click()
time.sleep(1)
else:
break
except:
break
.이 붙은 변수명은 클래스를 뜻하며 css선택자로서 사용하므로 알맞게 설정을 해주어 요소를 가져왔다.
그런뒤 그요소가 있다면 클릭을 하도록 설정을 하였다.
page_source = driver.page_source
soup = BeautifulSoup(page_source, 'html.parser')
이 두 부분은 웹페이지의 HTML소스코드를 가져와서 BeautifulSoup라이브러리를 사용하여 파싱하는 부분이다.
이걸 사용함으로써 정보를 추출할수 있는것이다.
page_source는 현재 페이지 전체 HTML소스 코드를 문자열로 반환한다.
그런뒤 BeautifulSoup이 HTML을 파싱하여 BeautifulSoup객체가 생성되는 것인데
이것을 사용하여 웹페이지의 구조를 이해하고 필요 정보를 추출할수가 있다.
크롤링 자동화하기
@scheduler.task('interval', id='do_save_news_5minutes', minutes=5)
def saveNews():
result = crawling()
cursor = conn.cursor()
cursor.execute('select title from news')
newsTitle = cursor.fetchall()
flat_newsTitle = list(itertools.chain(*newsTitle))
addNews_article=[]
for news in result:
if news['title'] not in flat_newsTitle:
addNews_article.append(news)
values = [(article['title'], article['mini_context'], article['media'], article['url']) for article in addNews_article]
cursor.executemany("INSERT INTO news (title , text, media, url) VALUES (%s, %s,%s, %s)", values)
conn.commit()
cursor.close()
if __name__ == '__main__':
scheduler.start()
app.run()
우리는 실시간으로 정보를 받아와야 하기 때문에 일정한 간격으로 정보를 계속 해서 받아와야했다.
만약 클라이언트 즉, 사용자가 버튼을 누르는 시점을 기준으로 정보를 받아오고 저장하고 보여주고 한다면
오랜 시간이 걸릴것이다.
그렇게 되면 클라이언트는 점점 해당 웹앱에 대한 만족도가 낮아질것이며 점차 사용을 안하게 될것이다.
또한 1시간 마다 업데이트를 한다고 했을때 업데이트하기 5분전에 사용자가 들어왔다면 그 사용자 기준에서는
절대 실시간이 라고 볼수가 없을것이다.
본인은 뉴스의 정보를 업데이트하는 기준을 5분으로 잡았다.
뉴스가 올라오는 주기가 3분정도 되는것으로 보아 그정보를 가져오는것을 5분으로 잡는다면 실시간으로 볼수 있을것이다.
@scheduler.task('interval', id='do_save_news_5minutes', minutes=5)
if __name__ == '__main__':
scheduler.start()
app.run()
테스크 스케줄링을 위한 데코레이터 형식이다.
interval : 간격, minutes : 분 , 즉 5분간격으로 계속 실행하겠다는 것이다. id는 단순 별칭 같은 느낌이다.
여기서 마주친 문제가 있었다.
일단 저 name과 main이 구분이 되지 않았었다.
이름을 지정해주지 않으면 app.py의 이름을 따라간다고 했었는데 거기에 main이라는 이름을 준다는건 어떤걸 실행한다는거지?
그리고 이 조건문을 들어가야 스케줄러가 작동하는건데 어떻게 해야 들어가는거지?
일단 조건문의 의미부터 알아야했다.
저 부분은 python 스크립트가 직접 실행될때만 코드가 실행되도록하는 구문이였다.
__name__ : 직접 스크립트를 실행한다면 __main__이라는 이름을 가지고 그것이 아니라면 모듈의 이름을 가지는것이다.
__main__ : Python스크립트가 직접 실행 될 때의 이름.
즉, 파이썬 스크립트를 직접 실행해야 스케줄러가 작동하는 것이였다.
파이썬 스크립트란 ?
python app.py
와 같이 직접 명령어로 적는 것이다.
지금까지 flask run으로 쉽게 실행해주는 방법만 썻었다.
그래서 위와 같은 방법을 아예 생각도 못하고 있었던 것이다.
나중에 안 사실이지만 스케줄러를 사용하려면 스크립트 파일을 직접 실행해야 한다고 한다. 잊지말자
flat_newsTitle = list(itertools.chain(*newsTitle))
이터러블 객체인 newsTitle의 모든 요소를 하나의 이터레이터로 연결하는 로직이다.
이것으로 2차원 리스트들을 1차원 리스트로 펼칠수 있는것인데
쉽게 말하자면 2차원 배열을 1차원 배열로 합치는 것이다.
[['사과', '배'], ['바나나', '딸기']]
↓
↓
['사과', '배', '바나나', '딸기']
이런식으로 합치면 하나의 배열이 되어 버리니 그곳에서 일치하는 문장을 찾기위하여 1차원 리스트로 만들어주었다.
이터러블
- 요소들의 시퀀스를 나타내는 객체
- 반복문을 통해 요소에 순차적으로 접근할 수 있는 객체
- 리스트, 튜플, 세트, 문자열등이 이에 해당.
이터레이터
- 이터러블의 요소에 순차적으로 접근하는 방법을 제공하는 객체
- __iter__(), __next__()메서드가 있으며 iter를 사용하여 이터레이터로 변환, next를 사용하여 순차적접근을 한다.
values = [(article['title'], article['mini_context'], article['media'], article['url']) for article in addNews_article]
리스트 컴프리헨션이라고 한다.
말로 설명해보자면 "addNews_article에서 하나씩 꺼내온 article에서 각각의 요소 값들을 추출하여 튜플로 만든뒤
한번에 모아 values에 넣겠다" 이다.
즉 bulk insert를 하기 위한 밑 작업이라고 보면된다.
이렇게 한뒤
cursor.executemany("INSERT INTO news (title , text, media, url) VALUES (%s, %s,%s, %s)", values)
여기에 값을 넣어주면 한번에 모든 정보들이 들어기 때문에 이런식으로 db에 부하를 줄일수 있도록 고려를 해보았다.
기계학습 시키기
pip install pandas
pip install scikit-learn
import pandas as pd
import string
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, confusion_matrix
import joblib
우리는 문맥을 파악해야 한다.
그러기 위해서는 인간의 문맥을 파악하는 능력을 조금이라도 더 확률이 높을수 있도록 학습을 기계에게 시켜야 한다.
즉 사람의 일을 대신할 모델이 있어야 한다.
file_url = 'code_red_study.csv'
data = pd.read_csv(file_url)
def remove_punc(x):
new_string=[]
for i in x:
if i not in string.punctuation:
new_string.append(i)
new_string = ''.join(new_string)
return new_string
data['text'] = data['text'].apply(remove_punc)
stop_words_list=[]
sw_file = open('stop_words.txt', 'r', encoding = 'utf-8')
for line in sw_file.readlines():
stop_words_list.append(line.rstrip())
def stop_word(x):
new_string = []
for i in x:
if i not in stop_words_list:
new_string.append(i)
new_string = ''.join(new_string)
return new_string
data['text'] = data['text'].apply(stop_word)
data['target'] = data['target'].map({'accident':1, 'etc':0})
x = data['text']
y = data['target']
cv = CountVectorizer()
cv.fit(x)
cv.vocabulary_
x = cv.transform(x)
x_train,x_test,y_train,y_test = train_test_split(x, y, test_size=0.2, random_state=100) #학습셋, 시험셋 분할
model = MultinomialNB()
model.fit(x_train, y_train)
pred = model.predict(x_test)
# accuracy_score(y_test,pred) # 정확도 계산
print(accuracy_score(y_test,pred))
print(confusion_matrix(y_test,pred))
joblib.dump((model, cv), 'model.pkl')
이부분이 우리대신에 일을 해줄 모델에게 학습을 시키는 부분이다.
file_url = 'code_red_study.csv'
data = pd.read_csv(file_url)
이 csv파일을 우리가 우리가 모델에게 학습을 시켜줄 정보가 담겨있는 파일이다.
# csv파일
target,text
etc,"졸음운전 일 평균 5.9건…'나들이 많은 봄철, 교통사고 유의해야'. 경찰청은 나들이 차량이 많은 봄철 졸음운전 등 교통사고에 특히 유의해달라고 당부했 다. 2일 오후 서울 여의서로 벚꽃길이 꽃을 감상하는 시민들로 북적이고 있다. 한편, 서울 영등포구는 여의서로의 벚꽃이 이번 주부터 본"
accident,"동부간선도로서 가로등 들이받고 화재…운전자 사망 서울 성동구 동부간선도로에서 차량이 가로등을 들이받고 불이 나 40대 여성 운전자가 숨졌다. 4일 서울 성동소방서에 따르면 이날 오전 3시10분쯤 성동구 동부간선도로 군자교 인근에서 차량 1대가 가로등을 들이받고 화"
이처럼 타겟과 text라고 구분을 주고
해당하는 text의 타겟은 어떠한 성격을 가지고 있는지 미리 정의 해놓는것이다.
이부분에서 좋은 데이터가 들어가야 우리가 학습시킬 모델은 좋은 성능을 가지기 때문에 세심하게 구분해서 자료를 넣었다.
자연어 전처리 과정1 - 특수문자 제거
def remove_punc(x):
new_string=[]
for i in x:
if i not in string.punctuation:
new_string.append(i)
new_string = ''.join(new_string)
return new_string
data['text'] = data['text'].apply(remove_punc)
특수문자를 없애는 부분이다.
기계가 문맥을 파악할때는 특수문자는 큰 의미를 가지지 못한다.
왜냐하면 특수문자는 문장간의 유사성을 계산할 때 이상적이지 않기 때문이다.
또한 특수문자가 있는 경우에 모델의 성능을 저하시키거나 분류 알고리즘에 왜곡을 일으킬수가 있어 꼭 제거해야한다.
자연어 전처리 과정2 - 불용어 제거
stop_words_list=[]
sw_file = open('stop_words.txt', 'r', encoding = 'utf-8')
for line in sw_file.readlines():
stop_words_list.append(line.rstrip())
def stop_word(x):
new_string = []
for i in x:
if i not in stop_words_list:
new_string.append(i)
new_string = ''.join(new_string)
return new_string
data['text'] = data['text'].apply(stop_word)
불용어도 자연어 처리 과정에서 마찬가지로 문장내에서 반복적으로 나타나지만 문맥을 이해하는 것에 있어서 도움이 되지 않는 단어들을 제거해 주어야 한다.
텍스트 데이터의 크기를 줄이고 모델의 성능을 향상 시킬수가 있으며 텍스트 데이터의 노이즈를 줄이고 좀더 중요한 단어들에 집중을 할수가 있기 때문이다.
예를 들면 "은", "는", "이", "가", "우리","나","너"같은 단어들을 제거하는것이다.
자연어 전처리 과정3 - 목표 컬럼 형태 변경하기
data['target'] = data['target'].map({'accident':1, 'etc':0})
문자열로 인식을 하도록 하는것도 좋지만 컴퓨터의 언어는 0과 1로 되어있기도 하고 조금더 나은 성능과 학습을 기대하며
숫자로 매핑을 해주었다.
1은 사건사고에 해당, 0은 그외의 일들에 해당한다고 정해두면 1과 0으로 구분할수가 있기 때문에 이렇게 하였다.
카운트 기반 백터화하기
x = data['text'] # 독립변수 ~~~
y = data['target'] # 종속변수 0, 1
cv = CountVectorizer()
cv.fit(x)
cv.vocabulary_
벡터화 : 수학적 의미로 행렬을 세로 벡터로 바꾸는 선형변환의 하나이다.
위 말은 너무 어렵다.
쉽게말해 카운트 기반으로 벡터화란 단어의 빈도를 계산하는 과정이다.
각각의 단어의 토큰을 생성하고 각 단어의 빈도를 계산하는데에 사용한다.
cv = CountVectorizer()
→ 객체 생성
cv.fit(x)
→ x를 인자로 넘기면 fit메서드는 단어 사전을 만들고 각 단어에 고유한 인덱스를 부여한다.
cv.vocabulary_
→ 생성된 단어 사전을 확인하여 {단어 : 인덱스}형태의 딕셔너리를 반환.
{'go': 3791, 'jurong': 4687, 'point': 6433, 'crazy': 2497, 'available': 1414,
'bugis': 1881, 'great': 3888, 'world': 9184, 'la': 4847, 'buffet': 1879, 'cine': 2214 }
즉, 이런식으로 반환된 형태를 볼수가 있다.
모델 생성하기
x = cv.transform(x)
x_train,x_test,y_train,y_test = train_test_split(x, y, test_size=0.2, random_state=100) #학습셋, 시험셋 분할
model = MultinomialNB()
model.fit(x_train, y_train)
pred = model.predict(x_test)
# accuracy_score(y_test,pred) # 정확도 계산
print(accuracy_score(y_test,pred))
print(confusion_matrix(y_test,pred))
joblib.dump((model, cv), 'model.pkl')
CountVectorizer는 fit 메서드로 학습된 단어 사전을 사용하여 transform 메서드로 입력 데이터를 단어 빈도 벡터로 변환한다.
단어 빈도 벡터는 행렬의 형태로 표현된다.
x_train,x_test,y_train,y_test = train_test_split(x, y, test_size=0.2, random_state=100) #학습셋, 시험셋 분할
train_test_split(x, y, test_size=0.2, random_state=100) : 입력 데이터와 레이블을 학습 및 테스트 세트로 무작위로 분할
- x: 입력 데이터 (feature)
- y: 레이블 (target)
- test_size: 테스트 세트의 비율을 나타내는 부동소수점 값 또는 정수. 부동소수점 값인 경우 0.0에서 1.0 사이의 비율을 나타낸다. 정수 값인 경우 전체 샘플의 개수를 나타냄.
- random_state: 데이터를 분할할 때 무작위성을 조절하기 위한 시드 값.
x_train,x_test,y_train,y_test
- 함수의 반환값으로, 각각 학습 데이터, 테스트 데이터, 학습 레이블, 테스트 레이블을 나타냄
모델 생성하기
model = MultinomialNB()
model.fit(x_train, y_train)
pred = model.predict(x_test)
위의 정보를 가지고 이제 모델을 생성하면된다.
모델은 다항분포 나이브베이즈 모델을 사용하였다.
나이브베이즈 모델은 주어진 텍스트 데이터에 대해 다항 분포를 사용하여 각 클래스(레이블)의 확률을 예측하는데 사용할수 있다.
MultinomialNB 모델 객체의 fit 메서드는 주어진 학습 데이터와 해당 데이터의 레이블을 사용하여 모델을 학습시킨다.
즉, 입력 데이터와 레이블을 모델에 적합하게 만드는 과정이다.
predict는 학습된 모델을 사용하여 주어진 입력 데이터에 대한 예측을 수행하는 메서드이다.
여기서는 x_test에 대한 예측을 수행한다.
이렇게 기계를 학습 시켰으면 이제 우리가 원하는 작업을 하면 된다.
실제 사건/사고만 추출하기
@app.route('/accident',methods=['GET'])
def accident():
today = date.today()
start_of_today = datetime.combine(today, datetime.min.time())
end_of_today = datetime.combine(today, datetime.max.time())
cursor = conn.cursor()
loaded_data = joblib.load('model.pkl')
loaded_model, loaded_cv = loaded_data
cursor.execute('select * from news where created_at BETWEEN %s AND %s', (start_of_today, end_of_today))
news = cursor.fetchall()
if not news :
return 'Not Found Error : 현재 뉴스가 존재하지 않습니다.', 404
filtered_articles = []
key_words = ['무죄','선고','구속','검거','법원','항소','징역','구형','법원','수사','공판','판사','기소','송치','지난','전날','작년','체포','단속','법정','고속도로']
for article in news:
if not any(keyword in article[1] for keyword in key_words) and not any(keyword in article[3] for keyword in key_words):
filtered_articles.append(article)
combined_text = []
for item in filtered_articles:
title = item[1]
mini_context = item[3]
combined_text.append(title + ' ' + mini_context)
new_text_processed = loaded_cv.transform(combined_text)
pred_new_text = loaded_model.predict(new_text_processed)
selected_texts = []
for i in range(len(pred_new_text)):
if pred_new_text[i] == 1:
selected_texts.append(filtered_articles[i])
return selected_texts
사건사고와 맞지 않는 키워드 들은 모두 걸러주고 그곳에서 최대한 필터링 한것을 가져와서 문맥파악을 모델에게 맞긴다.
그렇게 되면 모델은 이미 학습한 데이터를 기준으로 이 문맥을 파악하여 0 또는 1을 반환하게 된다.
우리는 그것을 파악하여 사건사고를 분류하기만 하면 된다.
'project > sparta' 카테고리의 다른 글
[최종 프로젝트]시간에 따른 거리 증가 및 알림 보내기 (0) | 2024.04.21 |
---|---|
[최종 프로젝트]실시간 구조 요청 보내기3 (위치 기반 서비스) (0) | 2024.04.14 |
[최종 프로젝트]실시간 구조 요청 보내기2 (위치 기반 서비스) (0) | 2024.04.11 |
[최종 프로젝트]실시간 구조 요청 보내기1 (위치 기반 서비스) (0) | 2024.04.08 |
[최종 프로젝트] Puppeteer 크롤링하기2<문자열 유사성 알고리즘> (0) | 2024.04.05 |